How to convert hex character from SoftwareSerial string to int

Hello,

I am getting a string from a HC12 signal
message = hc12.readString();

To keep the communication small I decided to transmit numbers in hex.
Assuming the String is XYA where A represents hex 10, how do I convert this to a decimal int ?

this fails, I assume because the hex value isn't like 0x0A:
message.substring(3,4).toInt();

but this doesn't work either:

hexstring="0x0"+message.substring(3,4);
decimalval=hexstring.toInt()*1;

solution?

thanks!

What is the largest number that you want to transmit ?

16, 0 to F

Then why not transmit them as bytes ?

huh, how would I send anything >9 in one byte then? I am open for any solution as long as I get readable values and ints out of it at the end.

Don't use ascii.

Send 0 as a raw literal byte 0. 0b00000000
Send 1 as a raw literal byte 1. 0b00000001
Send 16 as a raw literal byte 16. 0b00010000

Ah okay, I just noticed that Serial.write is transforming anything in ascii to byte anyway.
Hmm that will probably make testing from the console a bit difficult.

But how does that look like then?
Serial.write(0b00010000) ?

Somehow this feels like I am getting in new problems then, how to turn this into a dec int ?
Besides don't have the number only, the transmitted string contains more parts.

Serial.write() always sends binary values as bytes.

To send the binary value equal to decimal 16, this works:

Serial.write(16);

Besides don't have the number only, the transmitted string contains more parts.

Use Serial.write to send a byte array. Example


byte array[3]={'X','Y',16};
Serial.write(array,3);

in a test program I transmit a structure

struct __attribute__((packed)) Struct1 {
  byte StructureID;  // identifies the structure type
  byte NodeID;       // ID of transmitting node
  int16_t seq;       // sequence number
  int32_t x;
  float y;
  char z[10];
  byte crc;
} struct1;

as hex values e.g.

01020300040000000000104168656c6c72000000000072

convert the hex back to bytes

  // convert 2 hex values into a byte
  for(int i=0;i<sizeof(struct1);i++) {
    sscanf(&text[i*2],"%02x",&((byte*)&struct1)[i]);
  }

gives a result

StructID 1 Node 2 seq 3 x = 4 y = 9.00 z = hellr crc = 0x72

The trouble with multi-byte binary transport is that there are no marker bytes. That may make synchronization a challenge. How do you tell start of message? What happens if you start processing bytes in the middle of a message? So, you need extra code to handle exceptions and special cases.

the hex values are terminated by '\n' - I check the number of bytes read is sizeof(struct1)*2 +1 otherwise I discard the data read and any other bytes in the stream
could transmit the original binary data as a frame with start and end markers and byte stuff the data - perhaps try that next

Ok these are valuable hints. Thanks, It just made me wonder if it even makes sense to do anything in hex or binary if it all is set to binary anyway.

XYA = 01011000 01011001 01000001 00001010
XY10= 01011000 01011001 00110001 00110000 00001010

00010000 is the binary code for ascii char 16 if I understand this right

I can't see the advantages yet, in all cases I still have the issue to convert the data back and forth. Except for a string containing decimals.

It just drives me mad why c++ got no direct hex to dec conversion option, I'd even put the 16 chars in an array but arrays can't have string keys. :roll_eyes:

No. 00010000 is the binary value 16. If you try to interpret it as ascii then it would be an unprintable character (Data Link Escape character).

If you want to be able to send data fast, then you need to forget ascii.

No conversion.

byte x = 16;
Serial.write(x);

on the other side:

byte x = Serial.read();
Serial.print(x);  // Prints 16.
1 Like

complete programs - transmitter

// ESP32 test of HC-12 wireless module - transmit structure as a HEX binary string

#define nodeID 2  //  <<< set up required node ID

// test structure
struct __attribute__((packed)) Struct1 {
  byte StructureID;  // identifies the structure type
  byte NodeID;       // ID of transmitting node
  int16_t seq;       // sequence number
  int32_t x;
  float y;
  char z[10];
  byte crc;
};

Struct1 struct1 = { 1, nodeID, 0, 1, 4.5, "hello", 0 };
#define RXD1 16
#define TXD1 17
#define HC12 Serial1

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println("\nESP32 test of HC-12 wireless module - transmit structure as a HEX string");
  HC12.begin(9600, SERIAL_8N1, RXD1, TXD1);
}

void loop() {
  Serial.print("Transmitting ");
  struct1.seq++;   // increment packet sequence number
  struct1.x += 1;  // and test data
  struct1.y += 1.5;
  struct1.z[4]++;
  struct1.crc = CRC((byte *)&struct1);  // calculate CRC
  // convert each structure byte into 2 hex digits
  char text[sizeof(struct1) * 2 + 2] = { 0 };
  for (int i = 0; i < sizeof(struct1); i++) {
    sprintf(&text[i * 2], "%02x", ((byte *)&struct1)[i]);
  }

  Serial.println(text);  // display the transmitted string
  HC12.println(text);    // transmit the string
  Serial.print(" StructureID ");
  Serial.print(struct1.StructureID);
  Serial.print(" Node ");
  Serial.print(nodeID);
  Serial.print(" seq number ");
  Serial.print(struct1.seq);
  Serial.print(" x = ");
  Serial.print(struct1.x);
  Serial.print(" y = ");
  Serial.print(struct1.y);
  Serial.print(" z = ");
  Serial.print(struct1.z);
  Serial.print(" crc = 0x");
  Serial.println(struct1.crc, HEX);
  delay(2000);
  // display any acknowledgement received
  while (HC12.available()) {    // If HC-12 has data
    Serial.write(HC12.read());  // Send the data to Serial monitor
  }
}

// simple checksum - replace with CRC8
byte CRC(byte *s) {
  byte crc = 0;
  for (int i = 0; i < sizeof(Struct1) - 1; i++) crc += s[i];
  return crc;
}

receiver

// ESP32 test of HC-12 wireless module - receive structure as a HEX binary string

#define nodeID 2  //  <<< set up required node ID

// test structure
struct __attribute__((packed)) Struct1 {
  byte StructureID;  // identifies the structure type
  byte NodeID;       // ID of transmitting node
  int16_t seq;       // sequence number
  int32_t x;
  float y;
  char z[10];
  byte crc;
} struct1;

#define RXD1 16
#define TXD1 17
#define HC12 Serial1

void setup() {
  Serial.begin(115200);
  delay(2000);
  Serial.println("\nESP32 test of HC-12 wireless module - receive structure as a HEX string");
  HC12.begin(9600, SERIAL_8N1, RXD1, TXD1);
}

void loop() {
  static int16_t node1seq = 0, node2seq = 0, seqErrors = 0, crcErrors = 0;
  char text[sizeof(struct1)*2+2]={0};                        // Set buffer to size of expected message
  if (HC12.available() == 0) return;     // if no HC-12 data return
  int length=HC12.readBytesUntil('\n', text, sizeof(struct1)*2+2);  // read string to newline
  Serial.printf("received %s\n", text);
  // check correct number of bytes received - if not discard 
  if(length != sizeof(struct1)*2+1) {
    Serial.printf("ERROR - number of bytes received %d incorrect !!\n", length);
    while(Serial.available()) Serial.read();     // flush input buffer
    return;
  }
  // convert 2 hex values into a byte
  for(int i=0;i<sizeof(struct1);i++) {
    sscanf(&text[i*2],"%02x",&((byte*)&struct1)[i]);
  }

  // Message received display data
  Serial.print("Received ");
  Serial.print(strlen(text));
  Serial.print("bytes StructID ");
  Serial.print(struct1.StructureID);
  Serial.print(" Node ");
  Serial.print(struct1.NodeID);
  Serial.print(" seq ");
  Serial.print(struct1.seq);
  Serial.print(" x = ");
  Serial.print(struct1.x);
  Serial.print(" y = ");
  Serial.print(struct1.y);
 Serial.print(" z = ");
  Serial.print(struct1.z);
  Serial.print(" crc = 0x");
  Serial.print(struct1.crc, HEX);
  if (struct1.NodeID == 1) {
    if (struct1.seq != node1seq) {
      Serial.print("\n  seq number error expected ");
      Serial.print(node1seq);
      node1seq = struct1.seq;
      ++seqErrors;
    }
    node1seq++;
  }
  if (struct1.NodeID == 2) {
    if (struct1.seq != node2seq) {
      Serial.print("\n  seq number error expected ");
      Serial.print(node2seq);
      ++seqErrors;
      node2seq = struct1.seq;
    }
    node2seq++;
  }
  // check CRC - note does not work if transmitting floats
  byte crc = CRC((byte*)&struct1);
  //crc++;                        // uncomment to display crc error message
  if (crc != struct1.crc) {
    Serial.print("\n  crc error calculated 0x");
    Serial.print(crc, HEX);
    crcErrors++;
  }
  Serial.print(" crc errors ");
  Serial.print(crcErrors);
  Serial.print(" seq errors ");
  Serial.println(seqErrors);
  HC12.printf("received seq %d OK\n", struct1.seq);
}


// simple checksum - replace with CRC8
byte CRC(byte* s) {
  byte crc = 0;
  for (int i = 0; i < sizeof(Struct1) - 1; i++) crc += s[i];
  return crc;
}

running on an ESP32 transmitter serial output


ESP32 test of HC-12 wireless module - transmit structure as a HEX string
Transmitting 01020100020000000000c04068656c6c7000000000001b
 StructureID 1 Node 2 seq number 1 x = 2 y = 6.00 z = hellp crc = 0x1B
Transmitting 01020200030000000000f04068656c6c7100000000004e
 StructureID 1 Node 2 seq number 2 x = 3 y = 7.50 z = hellq crc = 0x4E
Transmitting 01020300040000000000104168656c6c72000000000072
 StructureID 1 Node 2 seq number 3 x = 4 y = 9.00 z = hellr crc = 0x72
received seq 3 OK
Transmitting 01020400050000000000284168656c6c7300000000008d
 StructureID 1 Node 2 seq number 4 x = 5 y = 10.50 z = hells crc = 0x8D
received seq 4 OK
Transmitting 01020500060000000000404168656c6c740000000000a8
 StructureID 1 Node 2 seq number 5 x = 6 y = 12.00 z = hellt crc = 0xA8
received seq 5 OK
Transmitting 01020600070000000000584168656c6c750000000000c3
 StructureID 1 Node 2 seq number 6 x = 7 y = 13.50 z = hellu crc = 0xC3
received seq 6 OK
Transmitting 01020700080000000000704168656c6c760000000000de
 StructureID 1 Node 2 seq number 7 x = 8 y = 15.00 z = hellv crc = 0xDE
received seq 7 OK
Transmitting 01020800090000000000844168656c6c770000000000f5
 StructureID 1 Node 2 seq number 8 x = 9 y = 16.50 z = hellw crc = 0xF5

receiver serial output

ESP32 test of HC-12 wireless module - receive structure as a HEX string
received �56c6c7100000000004e
ERROR - number of bytes received 21 incorrect !!
received 01020300040000000000104168656c6c72000000000072
Received 47bytes StructID 1 Node 2 seq 3 x = 4 y = 9.00 z = hellr crc = 0x72
  seq number error expected 0 crc errors 0 seq errors 1
received 01020400050000000000284168656c6c7300000000008d
Received 47bytes StructID 1 Node 2 seq 4 x = 5 y = 10.50 z = hells crc = 0x8D crc errors 0 seq errors 1
received 01020500060000000000404168656c6c740000000000a8
Received 47bytes StructID 1 Node 2 seq 5 x = 6 y = 12.00 z = hellt crc = 0xA8 crc errors 0 seq errors 1
received 01020600070000000000584168656c6c750000000000c3
Received 47bytes StructID 1 Node 2 seq 6 x = 7 y = 13.50 z = hellu crc = 0xC3 crc errors 0 seq errors 1
received 01020700080000000000704168656c6c760000000000de
Received 47bytes StructID 1 Node 2 seq 7 x = 8 y = 15.00 z = hellv crc = 0xDE crc errors 0 seq errors 1
received 01020800090000000000844168656c6c770000000000f5
Received 47bytes StructID 1 Node 2 seq 8 x = 9 y = 16.50 z = hellw crc = 0xF5 crc errors 0 seq errors 1
received 010209000a0000000000904168656c6c78000000000004
Received 47bytes StructID 1 Node 2 seq 9 x = 10 y = 18.00 z = hellx crc = 0x4 crc errors 0 seq errors 1
received 01020a000b00000000009c4168656c6c79000000000013
Received 47bytes StructID 1 Node 2 seq 10 x = 11 y = 19.50 z = helly crc = 0x13 crc errors 0 seq errors 1
received 01020b000c0000000000a84168656c6c7a000000000022
Received 47bytes StructID 1 Node 2 seq 11 x = 12 y = 21.00 z = hellz crc = 0x22 crc errors 0 seq errors 1
received 01020c000d0000000000b44168656c6c7b000000000031
Received 47bytes StructID 1 Node 2 seq 12 x = 13 y = 22.50 z = hell{ crc = 0x31 crc errors 0 seq errors 1

initially received string of incorrect length - probably received end of last packet on startup
and got a sequence number error until seq was synchronised

there are several error checks

  1. check length of received hex string is sizeof(struct1)*2 +1 - in receiver output above the first line is discarded as it is only part of the transmitted text
  2. each packet is sequence numbered - at start of receive data the sequence number is invalid - it then synchronises
  3. a CRC check is performed on received data
2 Likes

@Delta_G : Ok, that looks easy then.
@horace : Thanks for the complete example, that will help me, when I got stuck somewhere again. I probably won't need a complex error checking since it is just a short identification (so I don't collect any garbage data from somewhere else), a status flag and an id. The response is a bit longer but a simple checksum at the end will do.

For anything that more than one symbol you will need a protocol with starting bytes, suffix and CRC checking

Yes, I am bit of a struggle right now.

The point is, I plan to use up to 16 senders. All senders must have the ability to send 3 different states. Not a problem and fast, BUT there is no identification if I just send one byte. So any garbage coming in could cause trouble, while it is still unlikely to disturb other things at 433MHz.

More than one byte could mix the receiptions, so I have to use at least a simple checksum like in @horace example which locks out the noise garbage (at least most likely) but requires at least 3 bytes, which I planned anyway with my original "ascii attempt".

if you have multiple senders I would poll them to prevent collisions and data corruption
do all send the same data structure?

Basically yes, but polling is not an option.
Not all senders are active all the time, might even be without power.
All senders send a "I am here", then receive some data from the receiver and respond "I am ready".
The third state is "sender n talked to me"

In theory I could reserve a range per sender and each sender could be identified by it. This just gives me some logic puzzle to describe 1-16 senders with the remaining numbers.

10 = sender one
11 = sender one sends "I am here"
12 = sender one sends "I am ready"

20 = sender two
21 = sender two sends "I am here"
22 = sender two sends "I am ready"

and so on...