Serial commutation handling more than btye int

Adr

New member
Looking to get a handle on how to deal with short ints when sending 3 ints.

Right now my code looks something like this:

Code:
#define RX_SIZE 6
int8_t rx[RX_SIZE];

void handle_host_commands() {
  while (Serial1.available()) {
    Serial1.readBytes((uint8_t*) rx, RX_SIZE);
    
    if (rx[0] == -2) {

This works great for single bytes, but has a hard cap on the number I am needing to send to the teensy device. I know short ints are int16 and they are 2 bytes wide. How they are put together later I have no idea:)

The data I am sending is over python. I read teensy uses little-endian so I have < follow by the hhh for short:
Code:
pack( '<hhh', -2, 123, 1123 ) # b'\xfe\xff{\x00c\x04'

I assume I need to join the different bytes to create the short int - so I guess what do I need to do to make that happen?
 
It often depends on if the source and destination both use the same format for things like numbers: As you mentioned about big versus little...

For more complex things, some use the library: https://github.com/madsci1016/Arduino-EasyTransfer

If you are guaranteed that the source and destination handle the same way... Like two teensy boards..
You might get away with: doing stuff like sender doing
uint16_t my_array[3]);
sender: SerialX.write((char*)my_array, sizeof(my_array);
receiver: SerialX.readBytes((char*)my_array, sizeof(my_array);

But for example if you have some protocol for example dynamixel servos who sends out 16 bit serial data LSB MSB...

You will often see code like:
For output:
Code:
    uint16_t goal_position:
    ....
   Serial.write(goal_position & 0xff) ; // output the lsb
   Serial.write(goal_position >> 8);  // MSB

For input you might see:
Code:
    uint16_t present_position:
    ....
    present_position = Serial.read();
   present_position |= (Serial.read() << 8);
 
Many different protocols exist for packing and transmitting longer messages into 8 bit bytes. Often these protocols can become quite complex, so I'd agree with Kurt's recommendation, best to use a library which is already written and known to work well. That is, unless you really want to dive into the details. If so, here's a little info.

Most protocols utilize less than the full 8 bits of every byte for message data, so they can reserve certain bytes or data patterns to discern the start and length or end or each message. For example, MIDI uses 7 of the 8 bits for message info. The top bit in each byte is 1 for the first byte of a message and 0 in all following bytes of that message. Some protocols are even less efficient, packing only 4 or 6 bits of message data into each byte, so all possible data is encoded as printable ASCII characters. The base64 encoding used for attaching files to email messages is one example. The other characters or bytes can be used to detect the begin and end of this encoded data. It's also possible to reserve just 1 or 2 bytes, or even sequences of bytes, to mark the beginning of a message and then substitute something else in their place for the occasions where those occur in the message data. But whatever type of encoding the transmitting does to guarantee a unique pattern to detect the messages must be undone by the receiver. Usually the more efficient the use of bits, the more complex the encoding & decoding process becomes.

But some protocols use all 8 bits and delineate messages based on timing of the transmission, rather than using special encoding. Modbus RTU is probably the most commonly used timing-based protocol. This approach requires careful design of the transmitting code, as extra delay between bytes will be interpreted by the receiver as the end of 1 message and beginning of another. Receiving code also has to capture the relative timing of the bytes as they arrive.

Still other protocols "think outside the byte", like using a 9th bit to mark the beginning of each message. Modern high speed protocols like USB and PCIe do something similar with special signal patterns or bitwise encoding built into the hardware, so the hardware can deliver data as complete messages rather than individual bytes where software has to parse the message boundaries.

Some protocols add metadata to each message, such as a length field, a message type byte, and a checksum or CRC to verify if the message was received intact. Designing a protocol and creating a practical implementation which detects data errors and recovers quickly and gracefully can be much harder than you might initially think. Especially for protocols where messages can be any size and possibly very long, it can be difficult to reliably recover from certain types of data corruption and avoid mistaking subsequent short messages as a continuation of a corrupted or truncated long message.

Unless this sounds like your idea of a fun time, usually it's best to either use a well established protocol with well tested library code, or use a very simple approach, like MIDI or ASCII printing. But you did ask how this sort of thing is done, so hopefully this helps explain the general ways.
 
Last edited:
I had similar challenges with sending data between 2 different MCU's via serial (wirelessly but still on a serial bus). I have an application where a teensy 3.2 sends data to an Arduino NANO and ESP32. My data is a struct with 29 uint16_t's and is sent every second. Packing / unpacking destroyed the transmitted data, but this library was able to send data such that it could be received.


https://github.com/madsci1016/Arduino-EasyTransfer

Hope this helps.


I
 
Back
Top