BLE Central print out values of int characteristic

Let me try it this way... (and I edited my last post to correct it)

Your posted question is this:

"I did this using an ArrayToInteger union.
I don't really understand this union but copied it from examples and adjusted seemed to do the job"

The quick answer is that you will read 4 bytes that make up an unsigned long since the characteristic value is an unsigned long (that is how you defined it in the peripheral).

In Arduino, an unsigned long is 4 bytes, little endian, meaning the LSB first (byte 0) and MSB last (byte 3). When the central device reads the characteristic as 4 bytes, it has to pack the 4 bytes into an unsigned long variable and it has to do it in the correct order. The peripheral declared that the characteristic value is an unsigned long and the central has to treat it that way in order to read it correctly. That is the simple answer to your question.

Here is a WORKING example of a code fragment that I use with a SensorTag to read the humidity sensor that might help.

void read_HUM(BLEDevice peripheral) {
  uint8_t holdvalues[4]; // hold the characteristic's bytes

  if (peripheral.connected()) {
    // wake up sensor
    HUMConCharacteristic.writeValue(sensorOn);
    delay(1200); // wait for the sensor to do a read
    HUMValCharacteristic.readValue(holdvalues, 4);
    HUMConCharacteristic.writeValue(sensorOff); // sleep sensor
    unsigned int rawtem = (holdvalues[0]) + (holdvalues[1] * 256);
    unsigned int rawhum = (holdvalues[2]) + (holdvalues[3] * 256);
    // calculate final temperature and relative humidity values
    float temp = (rawtem / 65536.0) * 165.0 - 40.0;
    temp = ((temp * 9.0) / 5.0) + 32.0; // convert to F - comment out to leave at C
    float hum = ((double)rawhum / 65536.0) * 100.0;
    // save into the structure
    SensorData.tem = temp;
    SensorData.hum = hum;
  }
  else {
    Serial.println(" *not connected* ");
  }
}

In this case, the peripheral (the SensorTag) is going to send the MKR1010 as a central device, 4 bytes. But they are NOT an unsigned long. They are actually temperature values in two bytes and humidity values in the other two bytes.

Since I know (from SensorTag documentation) that I will be getting 4 bytes, I read those values into a byte array - HUMValCharacteristic.readValue(holdvalues, 4);

I could treat those 4 values as an unsigned long, literally packing them into an unsigned long variable (that is what you do in your case), but that would make no sense in this case. Instead, I process them as two variables first:

unsigned int rawtem = (holdvalues[0]) + (holdvalues[1] * 256);
    unsigned int rawhum = (holdvalues[2]) + (holdvalues[3] * 256);

and because of the SensorTag documentation I know which bytes are sent first (LSB/MSB wise) so I know how to get the correct value into the variable.

Then I process them further to get the final temperature and humidity values (again,from the SensorTag and the sensor's documentation).

So that is how I would answer your question.


ArduinoBLE is relatively new and I am also learning to use it. What I was trying to get at in my first attempt at answering your question is about using Characteristic.readValue(Val), where Val is a type that matches the characteristic value. As long as the endianess has been preserved then it is the convenient way to get the characteristic value in contrast to reading into a byte array and assembling the variable "by hand" so to speak.

So, if it does, then in your case, defining Val as an unsigned long would mean that .readValue(Val) would get Val all set up with none of the processing that you used from the example.

Here are tested working code fragment examples of what I mean using an unsigned int (which is 2 bytes) characteristic. Both of the code fragments below give the same final value for lux (from a light sensor).

The first code fragment reads the characteristic's value into an array (OPTValCharacteristic.readValue(holdvalues, 2) ); and then sticks the two bytes into a variable and then processes that variable.

The second code fragment reads the characteristic's value directly into a variable (OPTValCharacteristic.readValue(Val) ); that has been declared as an unsigned int (matching the peripheral's declaration), and then processes the variable in the same way as the first code fragment.

void read_OPT(BLEDevice peripheral) {
  uint8_t holdvalues[2]; // hold the characteristic's bytes

  if (peripheral.connected()) {
    // wake up the sensor
    OPTConCharacteristic.writeValue(sensorOn);
    delay(1200); // wait for the sensor to do a read
    OPTValCharacteristic.readValue(holdvalues, 2);
    unsigned int rawlux = (holdvalues[0]) + (holdvalues[1] * 256);
    OPTConCharacteristic.writeValue(sensorOff); // sleep sensor
    // calculate lux final value
    unsigned int m = rawlux & 0x0FFF;
    unsigned int e = (rawlux & 0xF000) >> 12;
    float lux = (m * (0.01 * pow(2.0, e)));
    // save into the structure
    SensorData.li = lux;
  }
  else {
    Serial.println(" *not connected* ");
  }
}
void read_OPT2(BLEDevice peripheral) {
  //uint8_t holdvalues[2]; // hold the characteristic's bytes
  uint16_t Val; // read the characteristic's bytes into the variable

  if (peripheral.connected()) {
    // wake up the sensor
    OPTConCharacteristic.writeValue(sensorOn);
    delay(1200); // wait for the sensor to do a read
    //OPTValCharacteristic.readValue(holdvalues, 2);
    //unsigned int rawlux = (holdvalues[0]) + (holdvalues[1] * 256);
    OPTValCharacteristic.readValue(Val);
    unsigned int rawlux =Val;
    OPTConCharacteristic.writeValue(sensorOff); // sleep sensor
    // calculate lux final value
    unsigned int m = rawlux & 0x0FFF;
    unsigned int e = (rawlux & 0xF000) >> 12;
    float lux = (m * (0.01 * pow(2.0, e)));
    // save into the structure
    SensorData.li = lux;
  }
  else {
    Serial.println(" *not connected* ");
  }
}

Hope this helps.