Android与单片机 | 开发板 | 智能硬件 | 智能设备 | 数据协议 |开发总结

news2025/3/1 9:58:36

在这里插入图片描述

一、缘起

工作中经常遇到需要Android程序与各式各样的板子、智能设备进行交互,通信的方式也基本上都是Ble或者Socket tcp/udp等等…其中最重要的一点就是通信的协议协议协议重要的是说三遍;通信协议就是用来定义与设备交互的方式和交互过程中数据包的格式 如:(包头—数据包长度—数据—校验位—包尾)

二、这里先来理理各种数据类型、所占的字节数等

1、我们写的代码最终都是转化成各种机器所能识别的二进制或者字节码,不同的编程语言具有不同的数据类型基本的也好不基本的也好、当然有不同的也就有相同的byte(字节)就是其中的一个; 2、日常开发中我们进行通信发送的内容最终都会以字节的形式进行发送,这里以JavaSocket为例,我们来看下源码

  • 创建一个socket连接,发送数据
Socket socket = new Socket(ip, port);
OutputStream outputStream = socket.getOutputStream();
//发送数据
outputStream.write("Hello World!".getBytes());
outputStream.flush();
//关闭连接
outputStream.close();
socket.close();
  • 我们来看下OutputStreamwirte(byte[] b)函数
  • OutputStream # write(byte[] b)
//1 接着又调用了write(byte b[], int off, int len)
public void write(byte b[]) throws IOException {
    write(b, 0, b.length);
}

//2 最后又调用了write(byte b)
public void write(byte b[], int off, int len) throws IOException {
    if (b == null) {
        throw new NullPointerException();
    } else if ((off < 0) || (off > b.length) || (len < 0) ||
               ((off + len) > b.length) || ((off + len) < 0)) {
        throw new IndexOutOfBoundsException();
    } else if (len == 0) {
        return;
    }
    //3 这里就是讲我们发送的一个bye[]进行for循环一个个写入了
    for (int i = 0 ; i < len ; i++) {
        write(b[off + i]);
    }
}

//4 end
public abstract void write(int b) throws IOException;

小结:无论定义的通讯格式是什么样的最终肯定要转成byte[](字节数组)进行发送,所以只要将数据转成字节数组即可,下面进入数据类型科普时间

三、Java中的数据类型所占的字节数和bit数

数据类型所占字节数所占bit数取值范围
byte18-128 ~ 127
char216‘\u0000’ ~ ‘\uFFFF’
short216-2^15 ~ 2^15 - 1
int432-2^31 ~ 2^31 - 1
float4322^-149 ~ 2^128 -1
long864-2^63 ~ 2^63 - 1
double8642^-1074 ~ 2^1024 - 1
boolean/1true or false
  • String在Java中不属于基本数据类型,一个汉字占2个字节,一个英文字母占1个字节
  • 小结:1 byte = 8 bit

3.1 什么是bit呢?什么又是高低位呢?在Java中又怎么写代码呢?

  • bit就是

    也就是

    二进制
    

    数据,取值只有

    0
    

    ,

    1
    

    -

    高位在左,低位在右

    • 这里以byte 123为例:
    • byte b =123 转为bit
    • 高位在0,低位在1 0111 1011
  • 在Java中获取byte的8个bit

/**
 * byte转8 bit
 *
 * @param b byte
 * @return 高位到低位顺序, 以byte123 为例: 0111 1011
 */
public static byte[] byte2Bit(byte b) {
    byte[] arr = new byte[8];
    for (int i = 7; i >= 0; i--) {
        arr[i] = (byte) (b & 1);
        b = (byte) (b >> 1);
    }
    return arr;
}
  • 既然把byte转为了8个bit位,那我们又怎么再把bit转回为byte呢?
/**
 * 8个bit位转为byte
 */
public static byte bit2Byte(byte[] bytes) {
    if (bytes.length != 8) return 0;
    String binary = "";
    byte result;
    for (byte b : bytes) {
        binary += b;
    }
    if (bytes[0] == 0) {
        // 正数
        result = (byte) Integer.parseInt(binary, 2);
    } else {
        // 负数
        result = (byte) (Integer.parseInt(binary, 2) - 256);
    }
    return result;
}

3.2 上面已经说了byte与bit的相互转化,现在就轮到int

  • 上面已经说了一个int4个字节32bit
  • Integer类已经为我们封装好了转bit的方法,如下:
String s = Integer.toBinaryString(35235);
//输出结果
1000100110100011
  • 可以看到没有32位,这是为什么呢?这是因为高位都是为0所以就直接省略了,当然我们也可以主动补齐32位只需要在高位补0即可。
  • bit再转回为int
int result = Integer.parseInt("1000100110100011", 2);
//输出结果
35235
  • 这里需要注意的是Integer.toBinaryString()可以将负数转化为二进制,但是Integer.parseInt(“”, 2)不能直接将负数的二进制转为int,如下:
String radix = Integer.toBinaryString(-35235);
System.out.println(radix);
int result = Integer.parseInt(radix, 2);
System.out.println(result);

程序执行会报一个java.lang.NumberFormatException: For input string:"11111111111111110111011001011101"异常,那我们怎么将负数的转回为int呢?当然是有方法的啦,如下:

//需要借助 BigInteger类
String radix = Integer.toBinaryString(-3535);
BigInteger integer = new BigInteger(radix, 2);
System.out.println(integer.intValue());
//输出结果
-3535

3.3当然我们可以通过电脑的计算器来计算二进制

img

3.4 上面我们说了一个int32个字节也就是4byte,那理所当然一个int可以转成2个byte或者4byte,如下:

/**
 * 一个int转2个字节的byte数组
 * 由低位到高位的转换
 *
 * @param value
 * @return
 */
public static byte[] intTo2Bytes(int value) {
    byte[] src = new byte[2];
    src[0] = (byte) (value & 0xFF);
    src[1] = (byte) ((value >> 8) & 0xFF);
    return src;
}

/**
 * 一个int转4个字节的byte数组
 * 由低位到高位的转换
 *
 * @param value
 * @return
 */
public static byte[] intTo4Bytes(int value) {
    byte[] src = new byte[4];
    src[0] = (byte) (value & 0xFF);
    src[1] = (byte) ((value >> 8) & 0xFF);
    src[2] = (byte) ((value >> 16) & 0xFF);
    src[3] = (byte) ((value >> 24) & 0xFF);
    return src;
}

这里需要注意的是int转byte[]的时候是高位数组的0下标 还是低位数组的0下标,上面的两个方法都是低位在数组的0下标

四、上面bb了一大堆,现在我们通过一个具体的协议来深入了解这些内容

4.1 协议如下:

img

这里需要解释下ucharuint是什么意思?uchar = unsigned char 、uint = unsigned int,也就是无符号的数据,也就是表示了这个数据是正数

  • 1、对协议进行分析可以得知:整个数据包是由两部分组成的包头+扩展数据包,其中包头占固定的32个字节

  • 2、首先我们得分析包头里面的每一个字段所占了多少个

    字节
    
    • uchar 占1个字节
    • uint 占4个字节
  • 2、那我们重点就是得来分析包头的数据需要怎么封装,通过协议我们可以看出

    包头
    

    内一共包含6个字段,分别表示如下:

    • 第一个为固定的"DH",总共占2个字节
    • 第二个为版本1.0 ,总共占2个字节
    • 第三个为扩展数据长度"extlen" ,总共占4个字节
    • 第四个为扩展数据类型取值0或1 ,总共占1个字节
    • 第五个为保留字段不使用就0补齐,总共占3个字节
    • 第六个为保留字段不使用就0补齐,总共占20个字节
  • 3、通过上面一顿分析,我们就轻松的理清了每个字段所占的字节了

4.2 Talk is cheap. Show me the code.

//magic
byte[] magicB = {'D', 'H'};
//协议版本1.0转成int也就是1
byte[] versionB = {0, 1};
//扩展数据长度,这里假定扩展数据的长度为67
byte[] extLenB = intTo4Bytes(67);
//扩展数据类型 0:JSON、1:二进制数据;这里使用JSON
byte[] extType = {0};
//两个保留字段,直接0补齐,上面已经分析了两个字段一共占23个字节
byte[] reserved = new byte[23];
//这里将上面的多个数据合并至一个byte[]
byte[] data = byteMergerAll(magicB, versionB, extLenB, extType, reserved);

到这里包头的数据就已经处理好了,还可以进一步对它进行封装

  • 这里提供一个多个数据数组合并的工具方法
/**
 * 多个数组合并一个
 *
 * @return
 */
public static byte[] byteMergerAll(byte[]... bytes) {
    int allLength = 0;
    for (byte[] b : bytes) {
        allLength += b.length;
    }
    byte[] allByte = new byte[allLength];
    int countLength = 0;
    for (byte[] b : bytes) {
        System.arraycopy(b, 0, allByte, countLength, b.length);
        countLength += b.length;
    }
    return allByte;
}

4.3 封装包头数据

/**
 * 封装包头数据
 * 固定32个字节,其余的0补齐
 *
 * @param extLen 扩展数据长度
 */
public static byte[] getPkgHead(int extLen) {
    //magic
    byte[] magicB = {'D', 'H'};
    //协议版本1.0转成int也就是1
    byte[] versionB = {0, 1};
    //扩展数据长度,这里假定扩展数据的长度为67
    byte[] extLenB = intTo4Bytes(extLen);
    //扩展数据类型 0:JSON、1:二进制数据;这里使用JSON
    byte[] extType = {0};
    //两个保留字段,直接0补齐,上面已经分析了两个字段一共占23个字节
    byte[] reserved = new byte[23];
    //这里将上面的多个数据合并至一个byte[]
    return byteMergerAll(magicB, versionB, extLenB, extType, reserved);
}

4.4上面已经把包头处理好了那现在就可以发送命令了

//扩展数据:这里就需要根据实际的文档来生成了,我这里就随便写一个了
String extData = "{\"id\":12,\"cmd\":\"open\"}";
 byte[] extDataB = extData.getBytes();
 //获取包头
 byte[] pkgHead = getPkgHead(extDataB.length);
 //一个完整的数据包
 byte[] sendData = byteMergerAll(pkgHead, extDataB);

到这里一个完整的数据包就愉快的结束了也就实现了与设备的通信了;重点:以后再拿到一个协议首先研究一下由多少个部分组成,每个组成的部分占多少个字节,是高位到低位还是低位到高位

最后

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

img

更多Android进阶指南 可以扫码 解锁 《Android十大板块文档》

1.Android车载应用开发系统学习指南(附项目实战)

2.Android Framework学习指南,助力成为系统级开发高手

3.2023最新Android中高级面试题汇总+解析,告别零offer

4.企业级Android音视频开发学习路线+项目实战(附源码)

5.Android Jetpack从入门到精通,构建高质量UI界面

6.Flutter技术解析与实战,跨平台首要之选

7.Kotlin从入门到实战,全方面提升架构基础

8.高级Android插件化与组件化(含实战教程和源码)

9.Android 性能优化实战+360°全方面性能调优

10.Android零基础入门到精通,高手进阶之路

敲代码不易,关注一下吧。ღ( ´・ᴗ・` ) 🤔

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/998282.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

企业架构LNMP学习笔记37

1、能够理解读写分离的目的&#xff1b; 2、能够描述读写分离的常见实现方式&#xff1b; 3、能够通过项目框架配置文件实现读写分离&#xff1b; 4、能够通过中间件实现读写分离&#xff1b; 业务背景描述&#xff1a; 时间&#xff1a;2014.6.-2015.9 发布产品类型&#x…

【数据结构】线性表的知识点全面总结

目录 1.线性表的顺序表示 1.1顺序表的基本概念 1.2顺序表的基本操作 1.2.1插入 1.2.2删除 1.2.3查找 2.线性表的链式表示 2.1单链表 单链表的基本概念 2.1.1基本操作 2.1.1.1单链表的建立 2.1.1.2插入 2.1.1.3删除 2.1.1.4查找 2.2双链表 2.2.1基本操作 2.2.1.1插入 2.2.…

MySQL高可用九种方案

有的时候博客内容会有变动&#xff0c;首发博客是最新的&#xff0c;其他博客地址可能会未同步,认准https://blog.zysicyj.top 首发博客地址 参考视频 MMM方案&#xff08;单主&#xff09; MySQL高可用方案之MMM&#xff08;Multi-Master Replication Manager&#xff09;是一…

langchain-chatchat或者说fastchat启动访问404的问题

虽然很白痴&#xff0c;但是我还是记录一下我遇到的问题。 1.问题 最近想测试看看langchain的本地知识库效果&#xff0c;看到一个很完备的库&#xff0c;就是lanchain-chatchat。但是我启动了项目后进行对话回答是乱码的。 经过我的排查&#xff0c;问题不出在webui层&…

XSS入门 XSS Challenges

level1(直接注入) <script>alert(xss)</script>level2(双引号闭合标签) 测试 <sCr<ScRiPt>IPT>OonN"\/(hrHRefEF)</sCr</ScRiPt>IPT>发现<>"被转换&#xff0c;构造新的语句 "><script>alert(/xss/)</…

MySQL高可用搭建方案之(MHA)

有的时候博客内容会有变动&#xff0c;首发博客是最新的&#xff0c;其他博客地址可能会未同步,认准https://blog.zysicyj.top 首发博客地址 原文地址 MHA架构介绍 MHA是Master High Availability的缩写&#xff0c;它是目前MySQL高可用方面的一个相对成熟的解决方案&#xff0…

MyBatis 框架入门理论与实践

文章目录 1. MyBatis 框架介绍1.1 MyBatis优点1.2 MyBatis 不足 2. MyBatis 框架整体架构3. MyBatis 的 ORM 介绍4. MyBatis 框架入门开发4.1 入门案例的搭建4.1.1 准备SQL数据4.1.2 新建 SpringBoot 项目4.1.3 pom文件&#xff1a;4.1.4 配置文件4.1.5 mapper 和 mapper.xml4.…

C语言经典100例题(51-54)--学习使用按位与 ,按位或 |,按位异或 ^和按位取反~

目录 题目 问题分析 按位与操作符&#xff08;&&#xff09; 按位或操作符&#xff08;|&#xff09; 按位异或操作符&#xff08;^&#xff09; 按位取反操作符&#xff08;~&#xff09; 代码及运行结果 题目 学习使用按位与& ,按位或 |,按位异或 ^和按位取反…

Android 10.0 禁用插入耳机时弹出的保护听力对话框

1.前言 在10.0的系统开发中,在某些产品中会对耳机音量调节过高限制,在调高到最大音量的70%的时候,会弹出音量过高弹出警告,所以产品 开发的需要要求去掉这个音量弹窗警告功能 2.禁用插入耳机时弹出的保护听力对话框的核心类 frameworks\base\packages\SystemUI\src\com\an…

宋浩高等数学笔记(十二)无穷级数

完结&#xff0c;宋浩笔记系列的最后一更~ 之后会出一些武忠祥老师的错题&笔记总结&#xff0c;10月份就要赶紧做真题了

windows在gem下安装jekyll的问题

项目场景&#xff1a; 安装jekyll时抛出错误&#xff1a; ERROR: While executing gem … (Gem::RemoteFetcher::FetchError) IO::TimeoutError: Failed to open TCP connection to gems.ruby-china.com:443 (https://gems.ruby-china.com/quick/Marshal.4.8/jekyll-0.1.6.ge…

excl在建模语言中的运用

目录 1.表格的定位 2.数学函数 3.自动填充功能 4.数据透视表的应用 5.切片器 6. Date(),time(),now()&#xff0c;today() 7.文本转日期 8.分裂 9.sumif函数 10.数字转换为文本的方法 11.SUMIFS()函数&#xff1a;多个条件筛选 12.宏 13.提取多个表中&#xff0c;…

【随想】每日两题Day.4

题目&#xff1a;LeetCode 203.移除列表元素 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2…

项目部署之持续集成

1. 什么是持续集成 持续集成&#xff08;Continuous integration&#xff0c; 简称CI&#xff09;指的是&#xff0c;频繁地&#xff08;一天多次&#xff09;将代码集成到主干。 持续集成的组成要素 一个自动构建过程&#xff0c; 从检出代码、 编译构建、 运行测试、 结果记…

TDesign 点击高亮显示=》点击切换class类名

1. wx:for遍历数组 2. 在一行显示 2. 点击高亮

NPM 常用命令(七)

目录 1、npm help 1.1 命令使用 1.2 描述 1.3 配置 viewer 2、npm help-search 2.1 命令使用 2.2 描述 2.3 配置 long 3、npm hook 3.1 命令使用 3.2 描述 3.3 示例 3.4 配置 registry otp 4、npm init 4.1 命令使用 4.2 转发附加选项 4.3 示例 4.4 工作…

基于51单片机DS18B20温度及电流检测-proteus仿真-源程序

一、系统方案 本设计采用52单片机作为主控器&#xff0c;液晶1602显示&#xff0c;DS18B20检测温度&#xff0c;电流检测。 二、硬件设计 原理图如下&#xff1a; 三、单片机软件设计 1、首先是系统初始化 void lcd_init() //lcd 初始化设置子函数&#xff0c;不带参数 ,0x…

Matlab图像处理-边缘提取

基本概念 如果一个像素落在图像中某一个物体的边界上&#xff0c;那么它的邻域将成为一个灰度级变化的带。对这种变化最有用的两个特征是灰度的变化率和方向&#xff0c;它们分别以梯度向量的幅度和方向来表示。 边缘检测算子检查每一个像素的邻域并对灰度变化率进行量化&…

100天精通Python(可视化篇)——第100天:Pyecharts绘制多种炫酷漏斗图参数说明+代码实战

文章目录 专栏导读一、漏斗图介绍1. 说明2. 应用场景 二、漏斗图类说明1. 导包2. add函数 三、漏斗图实战1. 基础漏斗图2. 标签内漏斗图3. 百分比漏斗图4. 向上排序漏斗图5. 标准漏斗图 专栏导读 &#x1f525;&#x1f525;本文已收录于《100天精通Python从入门到就业》&…

特殊矩阵的压缩存储(对称矩阵,三角矩阵和三对角矩阵)

目录 1.对阵矩阵 2.三角矩阵 3.三对角矩阵&#xff08;带状矩阵&#xff09; 均假设数组的下标从0开始 1.对阵矩阵 定义&#xff1a;若对一个n阶矩阵A中的任意一个元素 aᵢ,ⱼ 都有aᵢ,ⱼaⱼ,ᵢ &#xff08;1≤i,j≤n&#xff09;&#xff0c;则称其为对称矩阵。 存储策略…