Search code examples
carduinobufferarraybufferuint8t

sending integer greater than 255 with uint8 array


I want to send integer greater than 255 using uint8 array from mobile to Arduino over bluetooth.

Since BLE module that I'm using does not accept Uint16Array, I'm restricted to use Uint8 array only.

My App code :

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);

My Device Specific Code :

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
}

Since uint8 only allows integer upto 255, How do I send greater values than that ?


Solution

  • You have to split it. You already know (or you should) that an int16 has, well, 16 bits (so it takes two bytes to store it).

    Now very small digression about endianness. With endianness you mean the order of the bytes when stored. For instance, if you have the value 0x1234, you can either store it as 0x12 0x34 (big endian) or as 0x34 0x12 (little endian).

    I don't know what language you use, so... Normally in C++ you do something like this:

    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));
    

    This sends in big endian format. If you prefer the little endian one, write

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

    A simpler version can be

    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));
    

    In the first case you know the endianness of the transmission (it is little or big according to how you code), in the second it depends on the architecture and/or the compiler.

    In JavaScript you can use this:

    var sendingData = new Uint8Array(data.buffer)
    

    and then send this new array. Credits go to this answer

    When you receive it, you will have to do one of these three things to convert it

    // 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]);
    }
    

    The last method is the most error-prone, since you have to know what endianness uses the compiler and it has to match the sendong one.

    If the endianness mismatches you will receive what you think are "random" numbers. It is really easily debugged, though. For instance, you send the value 156 (hexadecimal 0x9C), and receive the 39936 (hexadecimal 0x9C00). See? The bytes are inverted. Another example: sending 8942 (hex 0x22EE) and receiving 60962 (hex 0xEE22).

    Just to finish, I think you are going to have problems with this, because sometimes you will not receive the bytes "in one block", but separated. For instance, when you send 1 123 555 (in hex and, for instance, big endian this will be six bytes, particularly 00 01 00 7B 02 2B) you may get a call to SimbleeBLE_onReceive with just 3 or 4 bytes, then receive the others. So you will have to define a sort of protocol to mark the start and/or end of the packet, and accumulate the bytes in a buffer until ready to process them all.