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 ofmessage
, 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
:
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(?)):
So maybe I can use the casting to turn my two byte number into a c string and pass in the length of 16?
1 Answer 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 :|
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!!
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.
-
Note that in
char buffer[3] = {0, GAMEBOARD, REGISTER_CLIENT};
, the0
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– Edgar Bonet2021年02月20日 19:25:15 +00:00Commented Feb 20, 2021 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– Chris Schmitz2021年02月21日 13:56:23 +00:00Commented Feb 21, 2021 at 13:56
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.Serial.write()
has the overloadwrite(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. Yoursomeclass
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.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.