1

I know this is simple basic C stuff, but I can't quite figure it out or find a solution when searching and reading.

I have a method I want to call from a library that wants a const char pointer

someclass::send(const char *data)

And the value I want to send is actually an unsigned integer:

enum messageTypeEnum
{
  REGISTER_CLIENT = 0x04,
  CLIENT_REGISTERED,
  UPDATE_CREDENTIALS,
  ADD_BRICK,
  GAME_FRAME,
  ERROR
};

enum clientTypeEnum
{
  GAMEBOARD = 0x01,
  BRICK_CONTROLLER,
  PLAYER_CONTROLLER,
  TOUCH_CONTROLLER
};

uint16_t message = GAMEBOARD;
message <<= 8;
message += REGISTER_CLIENT;

So I want to convert it, but I can't quite figure it out.

I know fundamentally the char pointer is just a pointer to a memory address and that c strings are null terminated, so I should be able to create the pointer, add the enum's underlying bytes to the pointer array, and then add a 0 at the end to terminate it, but I don't know how I would do that exactly.

I tried doing it via casting:

const char *data = (char *)&message;

So here I'm saying (if I'm thinking about it correctly), is:

  • &message take the memory address of message, our uint16
  • `(char *) interpret it as a pointer for a character string
  • const char * store that address in this const pointer

Even typing that out I feel like I'm misunderstanding parts.

This compiles, but it crashes my esp32 and I'm sure it's because I don't have a null terminator at the end, but I'm not sure how to add it (I tried to add it via array index but that failed).

What's the proper way of doing this (I'm sure there's a non-casting way that's probably easier)?

Update

More detail re: PMF and Edgar's comments.

I definitely want to send the binary numbers behind the enums up. I'm building a websocket based interactive project where "controller" websocket clients can drive interactions on "gameboards". Websockets don't have a concept of differentiating between client types (I know socket.io does, but I'm wanting to write it all with regular websockets), so I built a registration system into the server that looks at the first two bytes of an array buffer or blob message to determine the client and message type (it seems overly complicated, but I'm building this to give a talk on binary operations and I'm doing it give functional examples of data parsing).

So yeah, all of that to say I definitely want to send the underlying number values from the enums.

The library I'm using is ArduinoWebsockets and they do have a client method called sendBinary that does have a signature like the Serial.write:

sendBinary signature

Though when I tried this with a non-null terminated c-string my esp32 crashed. That said, I was fumbling around in the code so it may have been something else that caused the crash.

When I follow the method calls down, the library ultimately uses std::string to pull the data out of the passed in c string and takes in the length (meaning it doesn't need to be null terminated(?)):

enter image description here

So maybe I can use the casting to turn my two byte number into a c string and pass in the length of 16?

4
  • Are you sure you want to send a binary 0x01 value? I guess that it's rather your library that crashes because of this instead of the calling function. Although it is incorrect (as you're correctly assuming) the message is actually binary 0x01 0x00 (the esp32 is little endian) and so it does have a terminating 0 as long as the value is smaller than 256. – PMF Feb 20 at 7:23
  • Serial.write() has the overload write(const uint8_t *buffer, size_t size) which lets you output an arbitrary array of bytes and doesn't require a NUL terminator (0 is a valid value to output). I have seen this kind of overload in other libraries. Your someclass may offer something similar for sending binary data. If it doesn't, it's probably not meant to send anything but text, in which case you have to serialize your data as text. – Edgar Bonet Feb 20 at 10:03
  • I do want to send the binary values up because I'm parsing the messages on the server as 8 bit array buffers or blobs and I have the same enums on the server and other clients to coordinate the message and client identification. – Chris Schmitz Feb 20 at 13:27
  • Ah @EdgarBonet yeah the library does have a sendBinary method that takes a c string and a length, though I ran into issues when trying to use it. That said, I'm probably understanding it wrong. I'll update my question with more detail. – Chris Schmitz Feb 20 at 13:29
1

CORRECTED ANSWER

I dug back into my code after a comment from Edgar and figured out some needed adjustments. My original solution was mostly correct, but there were some inaccuracies that I want to correct so I'm not goofing up anyone else in the future that may find this answer useful.


So I was overthinking things a bit. I still don't know that this is the best way of handling it, but it is working on the client side.

So, c-strings are just arrays of 8bit values with a null terminator at the end.

I want to end up my enum values, and my enums are define with byte enum-bases:

enum messageTypeEnum
{
  REGISTER_CLIENT = 0x04,
  CLIENT_REGISTERED,
  UPDATE_CREDENTIALS,
  ADD_BRICK,
  GAME_FRAME,
  ERROR
};

enum clientTypeEnum
{
  GAMEBOARD = 0x01,
  BRICK_CONTROLLER,
  PLAYER_CONTROLLER,
  TOUCH_CONTROLLER
};

So, to send up my bytes, I can just build a character array, set each byte in little endian order, and manually terminate it:

NOTE: this is the original assembly that was incorrect. I put the null terminator at the beginning of the buffer thinking "oh, it's little endian so the terminator needs to be at the beginning". Classic sleepy brain mistake :|

void registerAsAGameBoard() { char buffer[3] = {0, GAMEBOARD, REGISTER_CLIENT};

client.sendBinary((const char *)buffer, 3); }

void registerAsAGameBoard()
{
  char data[3] = {GAMEBOARD, REGISTER_CLIENT, 0};

  const char *buffer = data; 
  size_t size = strlen(buffer);

  client.sendBinary(buffer, size);
}

Now when I call sendBinary with my byte array and the length of the buffer my esp32 doesn't crash (hurray!) and the server receives the buffer correctly!!

success!

I need to make a couple of adjustments on either the client and/or server side to account for the fact that the ESP32 is little endian and the server is already coded to receive payloads in big endian, but that shouldn't be too much of a lift.

2
  • Note that in char buffer[3] = {0, GAMEBOARD, REGISTER_CLIENT};, the 0 byte is not a “null terminator”: It's the first byte of the buffer, whereas a “terminator” is by definition at the end. – Edgar Bonet Feb 20 at 19:25
  • 1
    @EdgarBonet, you are correct, and I'm glad you pointed that out. I didn't understand why this particular setup prevented the crash. I dug in further and found that when put the null terminator in the correct place the controller still crashed, but it crashed in a different portion of the code where we handle different messages from the server. I commented that part out and the controller stopped crashing at both spots. I then updated the code with the correct null terminator and got everything ironed out. I'm updating my answer. – Chris Schmitz Feb 21 at 13:56

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.