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?
-
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 integersjsotola– jsotola2020年01月29日 02:47:36 +00:00Commented Jan 29, 2020 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– ShoutOutAndCalculate2020年01月29日 03:01:51 +00:00Commented Jan 29, 2020 at 3:01
-
start with the exact same sketch as the link that you postedjsotola– jsotola2020年01月29日 03:03:50 +00:00Commented Jan 29, 2020 at 3:03
-
@jsotola That's the question, it didn't work.ShoutOutAndCalculate– ShoutOutAndCalculate2020年01月29日 03:05:53 +00:00Commented Jan 29, 2020 at 3:05
-
1you 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 compilerJuraj– Juraj ♦2020年01月29日 05:43:22 +00:00Commented Jan 29, 2020 at 5:43
2 Answers 2
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));
}
-
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– ShoutOutAndCalculate2020年01月29日 05:52:33 +00:00Commented Jan 29, 2020 at 5:52
-
2send() 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– Fahad2020年01月29日 06:08:32 +00:00Commented Jan 29, 2020 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– ShoutOutAndCalculate2020年01月29日 22:23:13 +00:00Commented Jan 29, 2020 at 22:23
-
1@ShoutOutAndCalculate I updated my answer to reflect your comment.Fahad– Fahad2020年01月29日 23:45:44 +00:00Commented Jan 29, 2020 at 23:45
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()
.