前言
距离上一次博客已经过去很久了,原因有三
- 其一:上一篇即将完成的博客未手动保存,系统亦未自动保存 导致热情降低
- 其二:国庆小假期放飞自我
- 其三:当前项目遇阻,进展缓慢
当前问题
说一说最近遇到的问题,之前实现了 Android 低功耗蓝牙的使用,为接入现有项目,需将数据先封装为 ModBus_RTU 协议帧,再通过蓝牙协议将 ModBus_RTU协议帧 透传至从机
项目整体架构如下:
熟悉嵌入式开发的朋友应该对 INT8U,INT16U 等类型非常熟悉,它们分别代表的是 8位无符号整型,16位无符号整型。
既然是 8位/16位,首先想到的办法就是在 Java 中找到同字节的数据类型去替代使用
我选择的是 byte 和 short
随后注意到 Java 的基础数据类型中居然没有无符号类型(char 除外),而透传模块传输过来的数据是无符号的
类型 | 默认值 | 占用存储空间 | 范围 | 范围说明 |
byte | 0 | 1byte(8bit) | -128~127 | -2 的 7 次方到 2 的7次方 - 1 |
short | 0 | 2byte(16bit) | -32768~32767 | -2 的 15 次方到 2 的 15 次方 - 1 |
int | 0 | 4byte(32bit) | -2147483648~2147483647 | -2 的 31 次方到 2 的 31 次方 - 1 |
long | 0 | 8byte(64bit) | -9223372036854774808~9223372036854774807 | -2 的 63 次方到 2 的 63 次方 - 1 |
float | 0.0 | 4byte(32bit) | 3.402823e+38 ~ 1.401298e-45 | e+38 表示是乘以 10 的 38 次方,同样,e-45 表示乘以 10 的负 45 次方 |
double | 0.0 | 8byte(64bit) | 1.797693e+308~ 4.9000000e-324 | |
char | 空 | 2byte(16bit) | \u0000~\uFFFF | char 类型变量是用来存储 Unicode 编码的字符,可以存储一个汉字,汉字在 Unicode编码中占用两个字节 |
boolean | FALSE | 1byte(8bit) | false、true |
关于 char 的编码,可以看看阮一峰老师的 字符编码笔记
那么蓝牙通讯时,协议封装和解析时就要特别注意到有符号数据和无符号数据之间的转换
否则可能出现以下情况:
Android 端接收到 INT8U 数据255(0xFF),若直接将一个 byte 变量赋值为0xFF,则将被 Java 解析为 -1。这种结果当然是我们不愿意看到的。
解决方法
接收数据时(无符号数据 ---> 有符号数据)
当前解决方法是在 Java 中用更高的存储数据类型
例如 需要接收 INT8U 数据,则需在 Java 中使用 16+bit 数据存储例如 short,int...
//Android 蓝牙接收到 byte[],byte[0] = 0xFF = 255(无符号数据)
byte a = (byte)0xFF;
System.out.println(UINT8toRead(a));
//...
public int UINT8toRead(byte a)
{
//部分编译器会把最高位当做符号位,因此写成 0x0FF
int read = a & 0x0FF;
//read = a;
//read = (read<<8)>>>8;
return read;
}
0xFF 在 Java 会被解析为 int 数据,因而
& 操作在这里返回的结果是一个 int 类型(int & byte)
<< 是左移
>>> 是无符号右移
结果:
此外我们还可以使用 Guava ,参见
发送数据时(有符号数据 ---> 无符号数据)
同样以 8位数据 为例,例如需要发送 255,那么协议中使用 byte 进行存储,如何操作?
//待发送数据为255
int write = 255;
System.out.println(WritetoUINT8(write));
//...
public byte WritetoUINT8(int write)
{
//在协议中该数据保存为byte a
byte a = (byte)(write & 0x0FF);
return a;
}
结果:
查看其二进制编码:
在 8bit 无符号类型中, 0xFF 将被解析为 255