1

Suppose that one defined a data structure and wanted to send it through serial.

 struct Gyro_data_structure {
    char command_name[5];
    float gyro_X;
    float gyro_Y;
    float gyro_Z;
};

Gyro_data_structure Gyro_data;

int size_gyro=sizeof(struct Gyro_data_structure);

void setup() {
  Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}

void loop() {
  Gyro_data.command_name[0]='H';
  Gyro_data.gyro_X++;
  Serial.println(size_gyro);
  delay(500);
  Serial.write(Gyro_data,size_gyro);
}

However, it returned an error saying "no matching function for call to 'HardwareSerial:write(Gyro_data_structure&,int&)'". I googled and found Michaël Roy's answer here. However, it's not working: "can not convert ..."

How to send a structure through UART?

5
  • i would start with code that works and then make small changes to find out what works and what does not work .... use the code that you found as an example ... change your struct to all integers – jsotola Jan 29 '20 at 2:47
  • @jsotola currently, non of them were working, the only thing I haven't tried was union. Right now I'm trying the bitwise operator, but apparently int is singed. – ShoutOutAndCalculate Jan 29 '20 at 3:01
  • start with the exact same sketch as the link that you posted – jsotola Jan 29 '20 at 3:03
  • @jsotola That's the question, it didn't work. – ShoutOutAndCalculate Jan 29 '20 at 3:05
  • 1
    you can send the structure as binary only if the receiver has the exact same way of store binary data in a structure as the sender. basically it will work only if the receiver uses the same compiler – Juraj Jan 29 '20 at 5:43
4

I tried the code from the link you posted and it worked:

struct Gyro_data_structure
{
    char command_name[6];
    int gyro_X;
    int gyro_Y;
    int gyro_Z;
};

struct Gyro_data_structure Gyro_data = {"Hello", 48, 49 , 50};

int size_gyro = sizeof(struct Gyro_data_structure);

void setup() 
{
  Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}

void loop()
{
  send(&Gyro_data);
  Serial.println();
  delay(1000);
}

void send (const Gyro_data_structure* table)
{
  Serial.write((const char*)table, size_gyro);  // 2 bytes.
}

bool receive(Gyro_data_structure* table)
{
  return (Serial.readBytes((char*)table, sizeof(Gyro_data_structure)) == sizeof(Gyro_data_structure));
}

I changed the float type to int for easy representation. When you run the code the serial window will show: Hello012

Because Arduino IDE serial window shows ASCII characters. 0's ASCII value is 48 and so on.


Update

It depends on what you want from the read function. Let's break down what Michaël Roy's implementation is doing. First, he reads sizeof(Gyro_data_structure) number of serial bytes and put it in the variable that table points to:

Serial.readBytes((char*)table, sizeof(Gyro_data_structure))

The 2nd argument of readBytes() takes how many bytes you want to read. Also readBytes() return how many bytes it read. Let's say we stored that number in return_bytes. In the next part he did:

return (return_bytes == sizeof(Gyro_data_structure))

He is checking if the number of bytes readBytes() read is the same as the size of Gyro_data_structure. If it is not, then return false.

This just tells you if you read more or less than what you are supposed to read. Regardless of true or false, the actual bytes that were read will be in the variable that you passed to the receive() function.

If you don't want the function to not return anything this is how it should be:

void receive(Gyro_data_structure* table)
{
  Serial.readBytes((char*)table, sizeof(Gyro_data_structure));
}
4
  • Thank you soooooo much !!!!! That was really helpful! However, could you explain just a little bit what happened there? (char*) and send(&Gyro_data); receive(&Gyro_data); I want to learn and adjust the code for better noise resistance. – ShoutOutAndCalculate Jan 29 '20 at 5:52
  • 2
    send() takes a pointer of Gyro_data_structure. That's why in the loop() send() uses the address of the declared variable (Gyro_data). When you want to send the address of any variable you use '&' in front of that variable. Now, the address points to the first variable of the structure, which is command_name[0]. Serial.write() starts printing one byte at a time from that variable until it reaches size_gyro. – Fahad Jan 29 '20 at 6:08
  • could you explain a little bit of why receiving part used bool instead of void? and isn't second input of Serial.readBytes just 1?(say, 12==12 ) – ShoutOutAndCalculate Jan 29 '20 at 22:23
  • 1
    @ShoutOutAndCalculate I updated my answer to reflect your comment. – Fahad Jan 29 '20 at 23:45
3

First of all, you have to decide whether you want to send the data as raw bytes (“binary data”) or as an ASCII text representation. Binary tends to be more efficient: you can send a float with full precision in only four bytes, whereas you would typically need 8 to 9 significant digits to recover the full precision from an ASCII representation. Binary is, however, harder to handle: some types have different binary representations on your Arduino and your PC, and you may need to define a protocol of sorts in order to get the framing right. ASCII is easier, and you can usually use end-of-lines as a simple framing device. I generally recommend using ASCII unless you really need the extra efficiency of a binary protocol.

Sending as binary

You can easily send arbitrary binary data using the method write(const uint8_t *buffer, size_t size) from the Serial object:

Gyro_data_structure Gyro_data = {"Gyro", 0.0, 4.0, 5.0};

void loop() {
    Gyro_data.gyro_X++;
    Serial.write((uint8_t *) &Gyro_data, sizeof Gyro_data);
    delay(200);
}

Here is an hex dump of the output:

0000  47 79 72 6f 00 00 00 80  3f 00 00 80 40 00 00 a0  |Gyro....?...@...|
0010  40 47 79 72 6f 00 00 00  00 40 00 00 80 40 00 00  |@Gyro....@...@..|
[...]

which can be parsed as follows:

47 79 72 6f 00 = {'G', 'y', 'r', 'o', '\0'}
00 00 80 3f    = 0x3f800000 = 1.0f
00 00 80 40    = 0x40800000 = 4.0f
00 00 a0 40    = 0x40a00000 = 5.0f

Note that the floats are sent in little-endian order: lest significant byte first. The 32-bit hex numbers above are the binary representations of the floats.

Sending as text

Serial.print() is intended to print a text representation of the data you give it. But since it does not know how to represent a Gyro_data_structure, you have to Serial.print() each data member in turn, and then add some formatting around all that:

void print_gyro(const Gyro_data_structure &gyro) {
    Serial.print(F("Gyro{command = \""));
    Serial.print(gyro.command_name);
    Serial.print(F("\", X = "));
    Serial.print(gyro.gyro_X);
    Serial.print(F(", Y = "));
    Serial.print(gyro.gyro_Y);
    Serial.print(F(", Z = "));
    Serial.print(gyro.gyro_Z);
    Serial.print(F("}"));
}

void loop() {
    Gyro_data.gyro_X++;
    print_gyro(Gyro_data);
    Serial.println();
    delay(200);
}

The output is

Gyro{command = "Gyro", X = 1.00, Y = 4.00, Z = 5.00}
Gyro{command = "Gyro", X = 2.00, Y = 4.00, Z = 5.00}
[...]

Making your data printable

This is a variation on the previous technique. You can Serial.print() your object directly, if you tell the Arduino core how to print this type of objects. This is done by letting your class inherit from Printable and implementing the virtual method printTo(). But then the class is not an “aggregate” anymore, and thus needs an explicit constructor:

struct Gyro_data_structure : Printable
{
    char command_name[5];
    float gyro_X;
    float gyro_Y;
    float gyro_Z;
    Gyro_data_structure(const char *name, float x, float y, float z)
    : gyro_X(x), gyro_Y(y), gyro_Z(z) {
        strncpy(command_name, name, 4);
        command_name[4] = '\0';
    }
    size_t printTo(Print& p) const {
        size_t count = 0;
        count += p.print(F("Gyro{command = \""));
        count += p.print(command_name);
        count += p.print(F("\", X = "));
        count += p.print(gyro_X);
        count += p.print(F(", Y = "));
        count += p.print(gyro_Y);
        count += p.print(F(", Z = "));
        count += p.print(gyro_Z);
        count += p.print(F("}"));
        return count;
    }
};

Gyro_data_structure Gyro_data("Gyro", 0.0, 5.0, 5.0);

void loop() {
    Gyro_data.gyro_X++;
    Serial.println(Gyro_data);
    delay(200);
}

The output is identical as before. The definition of the class gets a little bit clumsier, but then you can very easily print to any serial port (be it an UART or a software implementation), to an LCD... or whatever understands print().

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.