使用 uint8 数组发送大于 255 的整数

sending integer greater than 255 with uint8 array

我想使用 uint8 数组通过蓝牙从移动设备向 Arduino 发送大于 255 的整数。

由于我使用的 BLE 模块不接受 Uint16Array,因此我只能使用 Uint8 数组。

我的应用代码:

var data = new Uint8Array(3);
data[0]= 1;
data[1]= 123;
data[2]= 555;

ble.writeWithoutResponse(app.connectedPeripheral.id, SERVICE_UUID, WRITE_UUID, data.buffer, success, failure);

我的设备特定代码:

void SimbleeBLE_onReceive(char *data, int len) {
    Serial.print(data[0]); // prints 1
    Serial.print(data[1]); // prints 123
    Serial.print(data[2]); // failed to print 555
}

由于 uint8 只允许最大为 255 的整数,我如何发送大于 255 的整数?

在发送端和接收端对数据进行某种形式的缩放,使其保持在 0 到 255 的范围内。例如除法和乘法,因此数据发送到 255 以下,然后在接收端按比例放大。

另一种方法,如果您知道数据的高低范围,则可以使用 Arduino 映射功能。

y = map(mapped value, actual lo, actual hi, mapped lo, mapped hi)

如果您不知道完整范围,可以使用 constrain() 函数。

你必须拆分它。您已经知道(或者您应该知道)int16 有 16 位(所以需要两个字节来存储它)。

现在关于字节序的非常小的题外话。对于字节序,您的意思是存储时字节的顺序。例如,如果您的值为 0x1234,则可以将其存储为 0x12 0x34(大端)或 0x34 0x12(小端)。

我不知道你用的是什么语言,所以...通常在 C++ 中你会这样做:

const int datalen = 3;
uint16_t data[datalen];
data[0]= 1;
data[1]= 123;
data[2]= 555;

uint8_t sendingData[] = new uint8_t[datalen * sizeof(uint16_t)];
for (int i = 0; i < datalen; i++)
{
    sendingData[i * 2] = (data[i] >> 8) & 0xFF;
    sendingData[i * 2 + 1] = data[i] & 0xFF;
}

functionToSendData(sendingData, datalen * sizeof(uint16_t));

这以大端格式发送。如果你更喜欢小端,写

    sendingData[i * 2] = data[i] & 0xFF;
    sendingData[i * 2 + 1] = (data[i] >> 8) & 0xFF;

更简单的版本可以是

const int datalen = 3;
uint16_t data[datalen];
data[0]= 1;
data[1]= 123;
data[2]= 555;

functionToSendData((uint8_t*)data, datalen * sizeof(uint16_t));

在第一种情况下,您知道传输的字节序(大小取决于您的编码方式),在第二种情况下,它取决于架构 and/or 编译器。

在JavaScript中你可以使用这个:

var sendingData = new Uint8Array(data.buffer)

然后发送这个新数组。学分转到 this answer

当你收​​到它时,你必须做这三件事之一来转换它

// Data is big endian
void SimbleeBLE_onReceive(char *receivedData, int len) {
    uint16_t data[] = new uint16_t[len/2];

    for (int i = 0; i < len/2; i++)
        data = (((uint16_t)receivedData[i * 2]) << 8) + receivedData[i * 2 + 1];

    Serial.print(data[0]);
    Serial.print(data[1]);
    Serial.print(data[2]);
}

// Data is little endian
void SimbleeBLE_onReceive(char *receivedData, int len) {
    uint16_t data[] = new uint16_t[len/2];

    for (int i = 0; i < len/2; i++)
        data = receivedData[i * 2] + (((uint16_t)receivedData[i * 2 + 1]) << 8);

    Serial.print(data[0]);
    Serial.print(data[1]);
    Serial.print(data[2]);
}

// Trust the compiler
void SimbleeBLE_onReceive(char *receivedData, int len) {
    uint16_t *data = receivedData;

    Serial.print(data[0]);
    Serial.print(data[1]);
    Serial.print(data[2]);
}

最后一种方法是最error-prone,因为你必须知道编译器使用的字节序是什么,它必须与sendong匹配。

如果字节顺序不匹配,您将收到您认为的 "random" 数字。不过,它确实很容易调试。例如,您发送值 156(十六进制 0x9C),并收到 39936(十六进制 0x9C00)。看?字节被反转。另一个例子:发送 8942(十六进制 0x22EE)并接收 60962(十六进制 0xEE22)。

最后,我想你会遇到问题,因为有时你不会收到字节 "in one block",而是分开的。例如,当您发送 1 123 555(十六进制,例如大端字节序,这将是六个字节,特别是 00 01 00 7B 02 2B)时,您可能会收到对 SimbleeBLE_onReceive 的调用,只有 3 或 4 个字节,然后接收其他人。所以你必须定义一种协议来标记数据包的开始and/or结束,并在缓冲区中累积字节直到准备好处理它们。