Wire Communication between DUE and UNO

I need to send a fair amount of data from a DUE being used as a sensor hub and an UNO connected to a display. In total approximately 20 variables - 10 of them INT, 6 of them DOUBLE and 4 BOOLEAN.

I started trying to use the Wire library to get the task done, and it turned out to be a huge mess, since Wire will only send individual bytes, characters or strings, as opposed to whole variables.

Does anyone have any suggestions? I was thinking of "adding" all the characters representing the values of all the variables into one long string and passing that through the wire, but it does not seem very elegant.

Any ideas would be most appreciated! Andrey

Why not just cluster all the variables into a struct, and send that?

I guess because I don't see the difference between a struct and a string. I thought that in either case I would have to have a bunch of subroutines to measure the length of each variable (for example, for temperatures with one decimal point I could either have three digits {v.g. 74.5°F or 102.2°F}), then separate everything into bytes, aggregate them into either a struct or a string, send that, and then de-code it on the other end. It just doesn't seem very efficient.

I also looked into using SPI instead of IIC, but could find literally zero examples to follow....

Oh, and also I am an MD and coding does not come naturally :)

I guess because I don't see the difference between a struct and a string

A struct will generally have the advantage of being more compact and more precise.

Sure. I get that, but still it seems like a whole mess. I guess my real question is: Is there an easy way to send whole variables - tried Nick Gammon's library and didn't work for me for whatever reason - where I don't have to mess around with "architecting" the struct or string? Any other protocol other than IIC? Would wireless be any different?

Thanks for your help AWOL!

Well, you haven't told us any of your resources or constraints or shown any of your code, or explained why you think things are "messy", so being specific is tough.

Fair enough. Here's a 30,000 ft view. The DUE is collecting data from a series of hall sensors and analog temperature, humidity and pressure sensors, as well as from 7 proximity sensors. Those result in the following variables:

Floats: extTemp (3 or 4 digits for all Temps, one decimal place) intTemp coolTemp exhTemp atmPres (5 or 6 digits, one decimal place) extHum (3 digits, one decimal place)

Ints: linSpeed (2 or 3 digits) outFlow (2 or 3 digits) retFlow (2 or 3 digits) linRevs (2 or 3 digits) xAngle (2 or 3 digits) yAngle (2 or 3 digits)

Booleans sensor1On sensor2On sensor3On sensor4On sensor5On warning1On warning2On

Data is collected by the DUE every 100 ms and I would like to transmit every 500 ms. Sending a bunch of random separate bytes that I believe represent the total length of the data stream took approximately 50 ms, so it should be doable.

The UNO would need to receive the data, do some minimal calculations and display the results on a TFT.

The code for data collection in the DUE and the display in the UNO is all written, tested and works independently (i.e., I can see all the data collected by the DUE in the Serial Monitor, and it's all correct and properly formatted, and the UNO can do the calculations and precisely drive the TFT displaying arbitrary values assigned to dummy variables).

I don't have code example for the transmission because I don't even know where to start arranging the data. I guess at this point i'm looking for pointers on how to even start thinking about the problem, rather than code correction.

And again, you are dealing with a really dumb coder. I can tell you whether a tumor is malignant or not a mile away, but I can't tell the difference between a float and a double unless I look it up!!!

Thanks again!

You have 43 (6x4 + 6x2 + 7x1) bytes of data. If you were to send them via the serial interface at 115200 bits per second using the standard 8N1 settings, they would take a shade under 4ms to transmit.

Sure. 4 ms, 50 ms, makes little difference if i'm updating every 500 ms. I still don't know how to deal with the fact that sometimes the variables will have 3 digits, some times 4; sometimes 5 or 6. Or how to convert a bunch of int and float variables to a string or a struct. ANd then how to decode them on the other side...

I still don't know how to deal with the fact that sometimes the variables will have 3 digits, some times 4;

All "float"s are the same size as each other, all "int"s are the same size as each other, all "bool"s. . . I'm sure you can see where this is going. How many digits they individually represent is irrelevant, as far as I can see.

Hi all
Following AWOL’s advice I gave up on trying to do string manipulations to arrange all my data onto a string and be able to send the string through the wire, and instead tried to use a struct to arrange the data and send. Clearly I should have done more research before starting!

I easily and successfully created the struct with my 20+ variables and within a single board I am able to access them, modify them, read them, etc. The issue - as I suspected from the beginning - is to how then send the struct to the other board through Wire.

Obviously the simple Wire.write(struct) doesn’t work. Why would it? Wire.write only can send one byte at a time. I was hoping that there would be some dark magic that would cause the Wire library to just send the bytes sequentially, but that does not happen. I have searched the forum wide and far and have not found a single example where Wire can send a struct.

So I’m back on square one, where I was a week ago.

Can anyone please try to help?
Here’s some simple sample code. I would like to send the three temperatures through the Wire. This is following AWOL’s struct idea. But obviously it contains the erroneous Wire.write(struct) instruction that will not compile.

Thanks in advance!!!

/*
  Adding struct together and wire send
*/

#include <Wire.h>

double Temp1=72.5;
double  Temp2=104.7;
double Temp3=1145.2;

double Temp1Read = 0;
double Temp2Read = 0;
double Temp3Read = 0;

struct Message {
  double T1;
  double T2;
  double T3;
};

  

void setup() {
  // initialize wire:
  Wire.begin();
  }

void loop() { 
  
  Wire.beginTransmission(4); // transmit to device #4
 
  Message message1 = {Temp1,Temp2,Temp3};
  Temp1Read = message1.T1;
  Temp2Read = message1.T2;
  Temp3Read = message1.T3; //This was done to test if i could extract the values out of the struct, which worked just fine.  Not needed anymore
  
  Wire.write(message1);              // sends the construct <-- This line does not compile bc the message1 struct is not what Wire.write expects
  Wire.endTransmission();    // stop transmitting
  
  delay(500);
}

This will get it to compile at least:

 Wire.write((byte*)&message1, sizeof message1);              // sends the construct

WildBill, Thanks for your reply. Yes. I had tried that and it does transmit, but obviously the entire structure is then lost. For example, on the other side of the Wire my code above will transmit/receive: "72.5104.71145.2" You can imagine that with 20 variables, including some of different types and different digit numbers on the ints and doubles, this would make it impossible to decode. What i had done before going on the struct wild chase was to add the values into a string so I would get something like: "72.5*104.7*1145.2*" and that way I could use the "*" as a field character to know one variable had ended and the other begun. The issue with this way of doing it is that then i receive the numbers as strings and not numbers, which would make any algebraic operations a whole mess.

Thanks though.

I had tried that and it does transmit, but obviously the entire structure is then lost.

The point of using the struct is that it needn't be. If you define the same struct on the receiving side you can receive the data from wire into a buffer and then cast a pointer to the struct to point to the buffer and then use the values just as you did on the transmitter.

Alternatively, you can cast a pointer to an instance of the struct to byte* and fill it directly as wire data is received.

Either way, you're just passing the bytes that make up the instance of the struct over the wire to populate an identical instance on the other side.

Just be aware that the size, format and endian-ness of the fields in the struct need to be the same on both arduinos - double on the Due is not the same as double on the Uno - try using float instead.

WildBill,

Yes, I tried that, but it would not work. Here's the issue: The number of meaningful digits in some of my variables change from cycle to cycle, for example: int Temp1 can range from 0 degrees farenheit to over 100 degrees (it represents ambient temperature), so sometimes it will have one digit, sometimes two, sometimes three (Boston temperatures, you know?). So, sometimes the string of bytes that goes over the wire will have one digit for Temp1, sometimes two digits, sometimes three digits. So, if i'm transferring four different temperature values i get a collection of numbers and have no clue where one variable ends and the next one begins, regardless of having the exact same definition for the struct on both sides. Does this make any sense to you or am I missing something?

You are indeed missing the point; the number of digits is an irrelevance, as AWOL said earlier.

You have a struct that contains three doubles. On the due those occupy eight bytes each, irrespective of what number they contain. Further, that number is not represented in the struct by its decimal digits, it's a binary representation of the mantissa and exponent, and you need not know nor care of the actual specifics.

All you need to do is transfer those 24 bytes from one arduino to another, byte by byte over the wire. A term sometimes used for this is serializing. Once you have those bytes transferred, you can reconstitute (or 'rehydrate') them on the receiver and you'll have the same struct with the same data that the sender had. No need for parsing or worrying about number of digits. What you had on the sender is now on the receiver and you can use it in the exact same way.

The only problem is that you have to ensure that the fields in the structs are the same size in bytes on both sides. If you were sending from uno to uno, they would be. With the Due in the picture, you will have to be careful with your struct declarations to ensure that they are.

OK. I ditched the DUE for now bc i think that may have been the issue with being unable to read the struct on the other side. So I now have two UNOs interconnected just so I can try to figure out.

The MasterWriter UNO is sending data over as individual bytes and the Slave is receiving also as individual bytes. So far so good.

Now, if I understand this correctly, I should be sending exactly 24 bytes over every time, regardless of teh value of the doubles, so on the Slave side i should be able to just read 24 bytes every time data is sent, correct?

The issue is that is not the case. I get a different number of bytes sent over depending on the values of the individual variables, ranging from 21 to 29.

I also have no clue how to "rehydrate" the struct, because all I have now on the slave side is a collection of bytes and no idea how to repopulate the original three doubles...

In fact, I'm now ditching the second UNO and just trying to see if i can deconstruct the struct locally into a chain of bytes and then re-assemble it. I figure if i can do that locally i should be able to transfer byte by byte and reconstruct on the other side...

So far, much more furstration than results. Any ideas?

azarur: So I now have two UNOs interconnected just so I can try to figure out.

That should make it easier.

Now, if I understand this correctly, I should be sending exactly 24 bytes over every time, regardless of teh value of the doubles, so on the Slave side i should be able to just read 24 bytes every time data is sent, correct?

Yes, except that on the uno, a double is actually a float, so it's only four bytes apiece

The issue is that is not the case. I get a different number of bytes sent over depending on the values of the individual variables, ranging from 21 to 29.

Please post the code that does this.

I also have no clue how to "rehydrate" the struct, because all I have now on the slave side is a collection of bytes and no idea how to repopulate the original three doubles...

All the struct is is twelve bytes. As they're transmitted, store them in a byte array of the same size. If you don't like casting a pointer to a struct to that buffer, memcpy those 12 bytes into an instance of your struct.

The critical insight is that all memory is just an array of bytes, but you can impose whatever interpretation you like on those bytes. So you can take a struct of three doubles and treat it as an array of bytes and vice versa.

Take a look at this example. Once you see what it's doing, imagine that the memcpy is actually pushing bytes across the wire into a buffer on a remote Uno instead.

float Temp1=72.5;
float  Temp2=104.7;
float Temp3=1145.2;

struct Message {
  float T1;
  float T2;
  float T3;
};

void setup() 
{
char buf[12];  
Serial.begin(115200);  
Message message1 = {Temp1,Temp2,Temp3};
Serial.println("Struct");  
Serial.println(message1.T1);  
Serial.println(message1.T2);  
Serial.println(message1.T3);  
memcpy(buf,(byte*)&message1,sizeof message1);
Message *ptr=(Message*)buf;
Serial.println("Struct pointer to copied data");  
Serial.println(ptr->T1);  
Serial.println(ptr->T2);  
Serial.println(ptr->T3);  
Message &ref=(Message &)buf;
Serial.println("Struct reference to copied data");  
Serial.println(ref.T1);  
Serial.println(ref.T2);  
Serial.println(ref.T3);  
}

void loop() 
{ 
}

void loop() 
{ 
}