I've been using an Adafruit Huzzah ESP8266 Breakout to read data from an OpenTherm Gateway connected to my central heating unit, and publish that to MQTT. I'm programming the ESP8266 using the Arduino IDE.

The gateway publishes messages to the ESP8266 via serial. These messages look like this: T901812F3 or B501812F3

The first letter indicates whether the message comes from the thermostat or the boiler, while the remaining characters represent four bytes, hexadecimally encoded.

These four bytes are structured as follows:

enter image description here

I'm trying to extract the MSG-TYPE, DATA-ID, and DATA-VALUE from the four bytes, but I'm getting weird (to me) results. I'm new to Arduino and C in general.

I'm familiar with PHP, and extracting the data using that is easy:

$decimal = hexdec(substr("B501812F3", 1));      // 1343754995
$messageType = (($decimal & 0x70000000) >> 28); // 5
$dataId = (($decimal & 0x00FF0000) >> 16);      // 24
$dataValue = ($decimal & 0x0000FFFF);           // 4851

However when I try something similar in the Arduino IDE, and upload it an Arduino, I get a different result:

void setup() {


parseLine(char* message) {
    // Extract data
    char messageType;
    unsigned long data;
    int numberExtracted = sscanf(message, "%c%08x", &messageType, &data);

    // Decompose a single message
    int flag = (int) ((data & 0x70000000) >> 28);
    unsigned char openThermId = (unsigned char)((data & 0x00FF0000) >> 16);
    unsigned short openThermPayload = (unsigned short)(data & 0x0000FFFF);

    Serial.println(data);             // 4851
    Serial.println(flag);             // 0
    Serial.println(openThermId);      // 0
    Serial.println(openThermPayload); // 4851

I (perhaps incorrectly) assume that the effect will be the same when I upload the code to an ESP8266.

What am I missing here? I suspect it has something to do with the different max integer value on the Arduino, but I'm not really sure how to proceed.


You're best off using strtoul():

messageType = message[0];
uint32_t data = strtoul(message + 1, NULL, 16);

The trick here is that message is just an array, so you can get the first letter with message[0]. Also, since C arrays are themselves just pointers to blocks of memory, message + 1 actually points to the location in memory of message[1], so you start processing the numbers from the second character in the string, thus omitting the first letter.

strtoul() takes a third parameter which is the base to decode from - 16 in this case for hexadecimal.

From there you can then use your bit shifting and masking to slice up your 32-bit value into your required sub values.


A C/C++ struct with bit-fields can sometimes be an effective solution to this type of problem. The bit-field order depends on the endianness of the CPU. AVR uses little-endian order and thus the fields are from right to left.

union msg_t {
  struct {
    uint16_t value;
    uint8_t id;
    uint8_t spare:4;
    uint8_t type:3;
    uint8_t parity:1;
  uint32_t as_uint32;

A simple test sketch to verify:

void setup() {
  while (!Serial);
  msg_t msg;
  msg.uint32 = 0x501812f3;
  Serial.println(msg.parity);   // 0
  Serial.println(msg.type);     // 5
  Serial.println(msg.spare);    // 0
  Serial.println(msg.id);       // 24
  Serial.println(msg.value);    // 4851

void loop() {
  • I think you're missing the bit that mentions that the value is in a string prefixed with a character (B or T). msg.uint32 = 0xb501812f3; will overflow since you have 36 bits there, and 0xt501812f3 isn't valid hex.
    – Majenko
    Dec 14 '16 at 13:41
  • @Majenko Missed that but I am addressing methods of extracting bit-fields :). I do not want to event approach the whole idea of going for text notations. Dec 14 '16 at 14:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.