Combining structs?

I have two structs on one Arduino and want to send them over serial to another one.
I need to receive it as one on the receiving side.
It seems like "memcpy" is how to do it but i can't figure it out.

Sender:

typedef struct {
  uint16_t nVcc;  // Battery voltage
  int16_t nTemp;  // Temperature reading
  uint8_t nID;    // Store this nodeId
  uint8_t nTx;    // Transmit level
} Payload;
Payload theData;

typedef struct {
  uint8_t gTx;    // Transmit level of this sender (gateway)
  int8_t gRSSI;   // This gateway's RSSI
  int8_t nRSSI;   // The node's RSSI
} serial_one_tx_payload_values ;
serial_one_tx_payload_values serialOneTxValues;

memcpy (&serialOneTxValues, &theData, sizeof (serialOneTxValues));

On the other side i want it like that:

typedef struct {
  uint16_t nVcc;  // Battery voltage
  int16_t nTemp;  // Temperature reading
  uint8_t nID;    // Store this nodeId
  uint8_t nTx;    // Transmit level
  uint8_t gTx;    // Transmit level of the gateway
  int8_t gRSSI;   // This gateway's RSSI
  int8_t nRSSI;   // The node's RSSI
} serial_two_rx_payload;
serial_two_rx_payload serialTwoRX;

There is a piece missing i guess.

memcpy() as you've used it will end up overwriting the data in 'serialOneTxValues'.
Why can't you just create a new struct with al 8 members, and copy the individual values into it one-by-one?

Just send the first structure, then the second.

On a serial channel there are only bytes, absolutely unstructured.

@quilkin, since the "theData Payload" is already there i thought it's stupid to the same thing twice.
Isn't it be wiser to use it if it is already there?

I saw this on stackoverflow:

If you want a easy copy without having to manually do each field but with the added bonus of non-shallow string copies, use strdup.
This will copy the entire contents of the structure, then deep-copy the string, effectively giving a separate string to each structure.

memcpy (dest_struct, source_struct, sizeof (dest_struct));
dest_struct->strptr = strdup (source_struct->strptr);

Can it be done on the Arduino?

@Whandall, i need to make sure the data comes in together to know to which node it belongs.
There is other data also coming in and if that would happen between the two structs then i don't know if for example it's the RSSI from node 1, 2, 3....

You control the sending of data.

Just do not send something between the structures.

That is over my head. How to i do this if i have 20 nodes all sending different data crisscross on different times and also other data (structs) in between?

You only have one thread running, so there is no criss-cross unless you mess up.

typedef struct {
  int bla;
} one;
typedef struct {
  int blub;
} two;
typedef struct {
  one first;
  two second;
} both;

one s1 = {0x3130};
two s2 = {0x3332};
both pack = { {0x3130}, {0x3332}, };

void setup() {
  Serial.begin(250000);

  Serial.write((byte*)&s1, sizeof(s1));
  Serial.write((byte*)&s2, sizeof(s2));

  Serial.println();

  Serial.write((byte*)&pack, sizeof(pack));

  Serial.println();
}
void loop() {}

You can not distinguish whether the sends are all in one or split.

0123
0123

What i forgot to say: I use EasyTransfer

So it can not happen that i do:

EToutTheData.sendData();   // Send struct 1
EToutValues.sendData();    // Send struct 2

And at the same time there is a error and

EToutError.sendData();      // Send struct 3

is send in between that i receive in this order:

struct 1
struct 3
struct 2

on the other side?

There is no "at the same time".

If you are sending data by radio from several different 'source' nodes to a single 'target' node, and the signals are sent asynchronously, then there is a chance the signals will clash (the more nodes, and the more often the radio packets are sent, the higher the chance of a clash). So I can understand you wanting to keep each data packet together.

We don't know what radio protocol you are using, so @Whandall's

  Serial.write((byte*)&s1, sizeof(s1));
  Serial.write((byte*)&s2, sizeof(s2));
  Serial.println();
  Serial.write((byte*)&pack, sizeof(pack));

could possibly end up sending several separate packets of data, each with its overhead of sync bytes and checksum data (those should be handled 'invisibly' by the radio library). If the library also needs a 'radio.start()' and 'radio.end()' or similar, my comments won't apply and @Whandall is quite correct.

I still don't understand your suggested use of 'memcpy' and 'strdup'; as I said I would just copy the variables individually into one struct and then send it.

quilkin:
If you are sending data by radio from several different 'source' nodes to a single 'target' node, and the signals are sent asynchronously, then there is a chance the signals will clash (the more nodes, and the more often the radio packets are sent, the higher the chance of a clash).

This is a totally different story and sending all in one shot does not help with it.

Most serial protocol data is collected in a buffer and sent asynchronously, byte by byte.

Just noticed that the OP is using EasyTransfer. That sends all the data in a single defined struct in a single radio packet. So my comments above are correct. (btw I have spend many years getting micros to send and receive data using 433MHz radio, before such libraries were available!).

So, @MrGlasspoole, you just want a single (but bigger) structure, into which you can copy the individual members of your existing two structures.

@quilkin, the serial part has nothing to do with the radio part when it comes to receiving.
One Arduino is the receiver with RFM69 and that data is transferred to another Arduino over serial.

For serial i use EasyTransfer.

I was searching for copying and combining structs and memcpy everywhere from Google:

http://forum.jeelabs.net/node/1135.html

@Whandall, ok got that. Need to remember that everything happens one after the other - no multithreading.

How do i check for the two payloads on the receiver?
Like this:

void processSerialTwoData() {
  unsigned long serialTwoRXInterval = 250; // How often to check
  if ((unsigned long)(millis() - prevMillisSerialTwoRX) >= serialTwoRXInterval) {
    prevMillisSerialTwoRX = millis();
    if(ETin.receiveData()) {   // Check and see if a data packet has come in
      #ifdef DEBUG
        Serial.print("Node:");
        Serial.print(serialTwoRX.nID);
        Serial.print(", Temp:");
        Serial.print(serialTwoRX.nTemp);
        Serial.print(", Vcc:");
        Serial.print(serialTwoRX.nVcc);
        Serial.print(", nTX Level:");
        Serial.print(serialTwoRX.nTx);
      #endif // END DEBUG
    }
    if(ETinLevel.receiveData()) {   // Check and see if a data packet has come in
      #ifdef DEBUG
        Serial.print(", nRSSI:");
        Serial.print(serialTwoRxLevel.nRSSI);
        Serial.print(", gTX Level:");
        Serial.print(serialTwoRxLevel.gTx);
        Serial.print(", gRSSI:");
        Serial.println(serialTwoRxLevel.gRSSI);
      #endif // END DEBUG
    }
  }
}

Are the 250ms always needed? They are in the EasyTransfer examples.
Without that library you normally work with CR to know when the stream is completely.

Edit:

quilkin:
You just want a single (but bigger) structure, into which you can copy the individual members of your existing two structures.

That is what I'm trying to find out. Insert data from one struct into the other one, or copy, or combine the data of the two structs.

I have not experience with EasyTransfer, and my suggestion of split transfer does not work in that context.

But I doubt that your using two instances of EasyTransfer will work either.

You could place both structures into the struct SEND_DATA_STRUCTURE and have them tranferred as one packet.

Whandall:
But I doubt that your using two instances of EasyTransfer will work either.

They do it in the example:
EasyTransfer Example - 2Way_wPot_Example

Whandall:
You could place both structures into the struct SEND_DATA_STRUCTURE and have them tranferred as one packet.

And how to do it is what I'm looking for since the beginning of this thread :slight_smile:

MrGlasspoole:
They do it in the example:
EasyTransfer Example - 2Way_wPot_Example

In the linked example there is one receive-EasyTransfer and one transmit-EasyTransfer,
you seemed to use two transmitters. How would the attached receiver distinguish between both?

I think you could place both structures into the struct SEND_DATA_STRUCTURE and have them tranferred as one packet like this

typedef struct {
  uint16_t nVcc;  // Battery voltage
  int16_t nTemp;  // Temperature reading
  uint8_t nID;    // Store this nodeId
  uint8_t nTx;    // Transmit level
} Payload;

typedef struct {
  uint8_t gTx;    // Transmit level of this sender (gateway)
  int8_t gRSSI;   // This gateway's RSSI
  int8_t nRSSI;   // The node's RSSI
} serial_one_tx_payload_values;

struct SEND_DATA_STRUCTURE {
  Payload pl;
  serial_one_tx_payload_values tx;
};

EasyTransfer ET(Serial);  // something like that

void setup() {

  mydata.pl.nId = 42;
  mydata.pl.nVcc = 500;
  mydata.tx.gTx = 5;
  
  ET.sendData();
}

void loop() {}

Untested.

Tried it and it does not work.

I tried two instances and i receive everything right.
From the creator of the library i found a comment:

Also i can't think of another way if you have multiple payloads (structs).
But what if you have just one value to send? Creating a objects, struct, instance for just value seems allot of work.
Also I'm not sure if the receiving code i showed is the way to do it.

Anyway, I'm still interested in how to copy two structs into one.

MrGlasspoole:
Tried it and it does not work.

Which gives no information at all.

I receive nothing on the other end.

The one where i send one struct after the other works most of the time.
But sometimes i receive:

Node:81, Temp:2500, Vcc:2969, nTX Level:0Node:81, Temp:2500, Vcc:2969, nTX Level:0Node:81, Temp:2500, Vcc:2969, nTX Level:0, nRSSI:-46, gTX Level:25, gRSSI:-27

That means the second struct sometimes is not send:

Node:81, Temp:2500, Vcc:2969, nTX Level:0
Node:81, Temp:2500, Vcc:2969, nTX Level:0
Node:81, Temp:2500, Vcc:2969, nTX Level:0, nRSSI:-46, gTX Level:25, gRSSI:-27

But i made another test with:

struct SEND_DATA_STRUCTURE {
  Payload pl;
  serial_one_tx_payload_values tx;
};

Everything is just "0"

Sender:

/**************************************************************
* RFM69 DATA STRUCTURE                     *
**************************************************************/
typedef struct {
  uint16_t nVcc;  // Battery voltage
  int16_t nTemp;  // Temperature reading
  uint8_t nID;    // Store this nodeId
  uint8_t nTx;    // Transmit level of the node
} Payload;
Payload rfData;

/**************************************************************
* SERIAL COMMUNICATION                          *
**************************************************************/
EasyTransfer EToutRfData;  // Create object
EasyTransfer EToutError;   // Create object

typedef struct {
  int16_t gRSSI;  // This gateway's RSSI
  int16_t nRSSI;  // The node's RSSI
  uint8_t gTx;    // Transmit level of this sender (gateway)
} serial_one_tx_rf_level ;
serial_one_tx_rf_level serialOneTxRfLevel;

typedef struct {
  Payload pl;
  serial_one_tx_rf_level tx;
} SEND_DATA_STRUCTURE;
SEND_DATA_STRUCTURE mydata;

typedef struct {
  uint8_t errorNode;  // Node with the error
  uint8_t error;      // Error number
} serial_one_tx_error;
serial_one_tx_error serialOneTxError;

void startSerialOne() {
  Serial1.begin(SERIAL_ONE_BAUD);
  EToutRfData.begin(details(mydata), &Serial1);
  EToutError.begin(details(serialOneTxError), &Serial1);
}

Receiver:

EasyTransfer ETinRfData;   // Create object
EasyTransfer ETinError;    // Create object

typedef struct {
  uint16_t nVcc;  // Battery voltage
  int16_t nTemp;  // Temperature reading
  uint8_t nID;    // Node Id
  uint8_t nTx;    // Transmit level of the node
  int16_t gRSSI;  // The gateway's RSSI
  int16_t nRSSI;  // The node's RSSI
  uint8_t gTx;    // Transmit level of the gateway
} serial_two_rx_rf_data;
serial_two_rx_rf_data serialTwoRxRfData;

typedef struct {
  uint8_t errorNode;  // Node with the error
  uint8_t error;      // Error number
} serial_two_rx_error;
serial_two_rx_error serialTwoRxError;

void startSerialTwo() {
  Serial2.begin(SERIAL_TWO_BAUD);
  ETinRfData.begin(details(serialTwoRxRfData), &Serial2);
  ETinError.begin(details(serialTwoRxError), &Serial2);
}

unsigned long prevMillisSerialTwoRX = 0;

void processSerialTwoData() {
  unsigned long serialTwoRXInterval = 250; // How often to check
  if ((unsigned long)(millis() - prevMillisSerialTwoRX) >= serialTwoRXInterval) {
    prevMillisSerialTwoRX = millis();
    if(ETinRfData.receiveData()) {   // Check and see if a data packet has come in
      #ifdef DEBUG
        Serial.print("Node:");
        Serial.print(serialTwoRxRfData.nID);
        Serial.print(", Temp:");
        Serial.print(serialTwoRxRfData.nTemp);
        Serial.print(", Vcc:");
        Serial.print(serialTwoRxRfData.nVcc);
        Serial.print(", nTX Level:");
        Serial.print(serialTwoRxRfData.nTx);
        Serial.print(", nRSSI:");
        Serial.print(serialTwoRxRfData.nRSSI);
        Serial.print(", gTX Level:");
        Serial.print(serialTwoRxRfData.gTx);
        Serial.print(", gRSSI:");
        Serial.println(serialTwoRxRfData.gRSSI);
      #endif // END DEBUG
    }
  }
}

Output:

Node:0, Temp:0, Vcc:0, nTX Level:0, nRSSI:0, gTX Level:0, gRSSI:0

Edit:
Found another comment:

Can EasyTransfer do 1-way comms with multiple structs in one direction? Sort of. EasyTransfer itself does not, but the nature of C++ Object Oriented Programing allows you to create multiple EasyTransfer objects with different structs but the same serial port. If you don’t understand what that means, just stick with once instance of the EasyTransfer library and put all your data in one struct.

It looks like it would be best to have just one instance and have some empty/dummy struct for EasyTransfer and copy the data into it before sending.

If somebody can tell me how, then how does the receiver look to know what data comes in?

I can't figure it out :frowning: