Send struct using Ethernet library

Hi all,

I’m in trouble trying to send a structure using the Ethernet library.

Hardware: Arduino Uno + EthShield.
Software: Arduino IDE 0022.

I’m using the Arduino as client and my notebook as server: communication happens via a tcp socket.

My example input is made up of three numbers (a byte, an int and a long, precisely).

Here is my code:

#include <stdint.h>
#include <SPI.h>
#include <Ethernet.h>

// connection port
const unsigned int tcp_port = 10001;
// baud rate
const unsigned long baud_rate = 115200;

struct numbers_t {
  byte first;
  int second;
  long third;
};

void send_message();

byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 53, 37 };
byte gateway[] = { 192, 168, 53, 33 };
byte server[] = { 192, 168, 53, 36 };  // notebook

Client client(server, tcp_port);

void setup() {
  Ethernet.begin(mac, ip, gateway);
  delay(1000);
  Serial.begin(baud_rate);
  
  Serial.println();
  
  if (client.connect()) {    
    Serial.println("connected");
    
    send_message();
  }
  else {
    Serial.println("connection failed");
  }
}

void loop () {
  
  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();

    for(;;)
      ;
  }
}

void send_message() 
{  
  int i;

  struct numbers_t numbers;
  
  numbers.first = 200;
  numbers.second = 30209;
  numbers.third = 67001;
  
  // here comes the instructions to send the structure, see below
}

Sending data this way works flawlessy:

  // without 'DEC', the '200' ascii char is written
  client.print(numbers.first, DEC);
  client.print(" ");
  client.print(numbers.second);
  client.print(" ");
  client.println(numbers.third);

Sending the entire structure with the println() function results in buggy output:

  // only 'strange' characters are received by the server
  client.println((const char *) &numbers);

And using write(), traversing the structure with a pointer as seen here: http://arduino.cc/forum/index.php/topic,42850.msg310501.html#msg310501, results in buggy output too:

  byte *ptr_num = (byte *) &numbers;
  for (i = 0; i < sizeof(struct numbers_t); i++) {
    // only "Hello, " is written (first string)
    client.write(*(ptr_num + i));
  }

Anybody knows how can I correctly send my structure?
Thanks in advance.

just let the pointer go through the memory of the struct

byte *ptr_num = (byte *) &numbers;
for (int i = 0; i < sizeof(struct numbers_t); i++) 
{
    client.write(*ptr);
    ptr++;
}

or shorter

byte *ptr = (byte *) &numbers;
for (int i = 0; i < sizeof(struct numbers_t); i++)  client.write(*ptr++);

In this case,

client.write(*ptr++);

and

client.write(*(ptr + i));

are the same thing.

... results in buggy output too:

Can you describe the buggy output in detail?

Can you tell more about the receiving code?

Can you post the receiving code?

Can you tell more about the receiving code?

The receiving code is a C socket-based tcp server.

Can you post the receiving code?

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>

//#pragma pack(1)
struct numbers_t {
  uint8_t first;
  uint16_t second;
  uint32_t third;
};
typedef struct numbers_t numbers;

void print_message(const char *);
void print_bytes(const char *, int);

numbers * parse_numbers(char *);
numbers * numbers_create(void);
void print_numbers(numbers *);

int main(int argc, char *argv[])
{
     int sockfd, newsockfd, portno, clilen;
     char *buffer = NULL;
     struct sockaddr_in serv_addr, cli_addr;
     int n;
     numbers *h = NULL;

     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided\n");
         exit(1);
     }
     sockfd = socket(AF_INET, SOCK_STREAM, 0);

     if (sockfd < 0) {
        printf("ERROR opening socket\n");
        exit(1);
     }
     memset((char *) &serv_addr, 0, sizeof(serv_addr));
     portno = atoi(argv[1]);
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(portno);

     if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
        printf("ERROR on binding\n");
        exit(2);
     }
     listen(sockfd, 5);
     clilen = sizeof(cli_addr);
     newsockfd = accept(sockfd, 
                 (struct sockaddr *) &cli_addr, 
                 (socklen_t *) &clilen);

     if (newsockfd < 0) {
        printf("ERROR on accept\n");
        exit(3);
     }

     buffer = (char *) malloc(sizeof(char) * sizeof(numbers));
     memset(buffer, 0, sizeof(numbers));
     
     sleep(1);
     n = read(newsockfd, buffer, sizeof(numbers));
     if (n < 0) {
        printf("ERROR reading from socket\n");
        exit(4);
     }
     // to see if all bytes are received
     if (n < sizeof(numbers)) {
        printf("Readed only %d bytes\n", n);
     }
     h = parse_numbers(buffer);

     print_message(buffer);
     print_bytes(buffer, n);

     if (h) {
       print_numbers(h);
     }

     return 0; 
}

numbers * parse_numbers(char *buf) {

  numbers *tmp = NULL;

  if (!buf) { return NULL; }

  tmp = numbers_create();
  if (tmp) {
    memcpy(tmp, buf, sizeof(numbers));
  }
  
  return tmp;
}

numbers * numbers_create() {

  numbers *tmp = NULL;

  tmp = (numbers *) malloc(sizeof(numbers));
  if (tmp) {
    memset(tmp, 0, sizeof(numbers));
  }

  return tmp;
}

void print_numbers(numbers *h) {

  if (h) {
    printf("Fields:\n");
    printf("first (8 bit) = %d\n", h->first);
    printf("second (16 bit) = %hi\n", h->second);
    printf("third (32 bit) = %d\n", h->third);
  }
}

void print_message(const char *buf) {

  if (buf) {
    printf("Message: %s", buf);
  }
  printf("\n");
}

void print_bytes(const char *buf, int len) {

  int i;

  if (buf) {
    for (i = 0; i < len; i++) {
      printf("%X", (int) buf[i]);
    }
  }
  printf("\n");
}

Can you describe the buggy output in detail?

I discovered that the buggy output comes from a totally different problem.

It’s a problem of data structure alignment.
Infact the data structure from the Arduino code is 7 bytes long, while the x86 structure was 8 bytes long.

I tried to use the #pragma pack(1) directive, but nothing happens.

I resolved using the -fpack-struct gcc compiling option. With it, also the x86 data structure is now 7 bytes long.

Now the output is consistent.

Here is the Arduino code (client) updated:

#include <stdint.h>
#include <SPI.h>
#include <Ethernet.h>

// connection port
const unsigned int tcp_port = 10001;
// baud rate
const unsigned long baud_rate = 115200;

//#pragma pack(1)
struct numbers_t {
  byte first;
  int second;
  long third;
};

void send_message(struct numbers_t *);
void print_message(struct numbers_t *);

byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 192, 168, 33, 17 };
byte gateway[] = { 192, 168, 33, 13 };
byte server[] = { 192, 168, 33, 16 };  // notebook

struct numbers_t numbers;

Client client(server, tcp_port);

void setup() {
  Ethernet.begin(mac, ip, gateway);
  delay(1000);
  Serial.begin(baud_rate);
  
  Serial.println();
  
  if (client.connect()) {    
    Serial.println("connected");
  
    numbers.first = 200;
    numbers.second = 30209;
    numbers.third = 67001;
    
    send_message(&numbers);
    print_message(&numbers);
  }
  else {
    Serial.println("connection failed");
  }  
}

void loop () {
  
  if (!client.connected()) {
    Serial.println();
    Serial.println("disconnecting.");
    client.stop();

    for(;;)
      ;
  }
}

void send_message(struct numbers_t *h) 
{  
  int i;
  
  byte *ptr = (byte *) h;
  for (i = 0; i < sizeof(struct numbers_t); i++) {
    //client.write(*(ptr + i));
    client.write(*ptr++);
  }
}

void print_message(struct numbers_t *h)
{
  if (h) {
    Serial.print("Size of the struct = ");
    Serial.println(sizeof(struct numbers_t));
    
    Serial.println(h->first, DEC);
    Serial.println(h->second);
    Serial.println(h->third);
  }
}

Thanx Ramo102,

So the lesson learned is that sending structs is not straightforward due to different memory alignment at the sending and receiving side. Your solution is to force memory alignment to be identical, is a good option as it works.

Another option would be explicit serialization and deserialization at both side. That means mapping the structmembers one by one on a bytestream and on the receiving side map the bytestream on the members. The advantage of serialization is portability for the price of some extra code.

The advantage of serialization is portability for the price of some extra code.

Yes, you are correct. But the "some extra code" is, at this moment, too much for me! :)

However, thanks for your previous questions: they pointed me to the correct way.

For the Arduino side it is not really complex, something like this

simple_send(uint8_t *p, uint8_t size)
{
  for(uint8_t i=0; i< size; i++) client.write(*p++);
}

send_struct_numbers(struct numbers_t *num)
{
  simple_send(&num.first, 1)
  simple_send(&num.second, 2);
  simple_send(&num.third, 4);
}

void loop()
{
  ...
  send_struct_numbers(&numbers);
  ...
}

What about if I have a struct member that use bit fields? For example:

struct example {
  unsigned char member1:4;
  unsigned int member2:12;
  uint8_t member3;
};

How can I serialize this?

map it on a standard type >= bitfield type is the simple way.

something like this

send_struct_example(struct example_t *ex)
{
  char c = ex.member1;
  int   i = ex.member2;

  simple_send(c, 1)
  simple_send(i, 2);
  simple_send(ex.member3, 1);
}

IN a more elaborate way the datatype and values is described in XML, and that XML is sent over the line. (watch out - data explosion!!! :)