Send Data from Visual C++ to Arduino by UDP

Hi Everyone,

I'm started to develop a Plugin for X-Plane (a well known flight simulator) which should send data from instruments to my Arduino which then should display it. In a second step the Arduino also should send back data.

I took the existing XHSI X-Plane plugin as a base, because it contains everything i need for my plugin and some more stuff.
There i have the functions which get the data and sends it to an IP address by UDP.

The Arduino receives the data, thats good so far. But then i can't get out the data correctly.
Because the XHSI plugin used a struct, i wanted to keep that. It looks like this (in Visual C++):

struct SimDataPoint {
  int   id;
  float value;
};

struct SimDataPacket {
  char   packet_id[4];
  int    nb_of_sim_data_points;
  struct SimDataPoint sim_data_points[150];
};

And the code part which constructs the packet and sends it to Arduino:

struct SimDataPacket sim_packet;

int createAvionicsPacket(void) {
    int i = 0;
    int packet_size;

    strncpy(sim_packet.packet_id, "AVIO", 4);

    sim_packet.sim_data_points[i].id = custom_htoni(SIM_COCKPIT_AUTOPILOT_HEADING_MAG);      // id=154
    sim_packet.sim_data_points[i].value = custom_htonf(XPLMGetDataf(autopilot_heading_mag)); // 0-359
    i++;

    // now we know the number of datapoints
    sim_packet.nb_of_sim_data_points = custom_htoni( i );

    // packet size : char[4] + int + ( # * ( int + float) )
    packet_size = 8 + i * 8;

    return packet_size;
}


// This is sent to the Arduino in another function:
res = sendto(sockfd, (const char*)&sim_packet, packet_size, 0, (struct sockaddr *)&dest_sockaddr[i], sizeof(struct sockaddr));

The code to parse it on the Arduino:

// Defined the same struct
struct SimDataPoint {
  int   id;
  float value;
};

struct SimDataPacket {
  char   packet_id[4];
  int    nb_of_sim_data_points;
  struct SimDataPoint sim_data_points[150];
};

byte DATA_buf[500];

void loop() {
   // if there's data available, read a packet
  int packetSize = Udp.parsePacket();
  if(packetSize) {       //  UDP packet was received 
  
    Udp.read(DATA_buf,packetSize);

    // Output everything received
    for (int i = 0; i < packetSize; i++) {
      Serial.print(DATA_buf[i]);
    }
    Serial.println();

    // Read the received data into struct
    SimDataPacket* recv_data = (SimDataPacket*)DATA_buf;
    
    Serial.print("Type: ");
    Serial.print(recv_data->packet_id);
    Serial.print(" - Datapoints: ");
    Serial.print(recv_data->nb_of_sim_data_points);
    
    Serial.print(" - ID: ");
    Serial.print(recv_data->sim_data_points[0].id);
    Serial.print(" - Value: ");
    Serial.println(recv_data->sim_data_points[0].value);
  }
}

But if i look at the output, i get wrong values.
All values from the output below, except “Value”, should be the same on all packets:
Type: AVIO
Datapoints: 1
ID: 154
Value: 0-5

0 // The expected value
6586737900010001540000 // Received data
Type: AVIO - Datapoints: 0 - ID: 256 - Value: -0.00 // Values from struct (Type - Datapoints - ID - Value)

1
6586737900010001546312800
Type: AVIO - Datapoints: 0 - ID: 256 - Value: -0.00

2
65867379000100015464000
Type: AVIO - Datapoints: 0 - ID: 256 - Value: -0.00

3
658673790001000154646400
Type: AVIO - Datapoints: 0 - ID: 256 - Value: -0.00

4
6586737900010001546412800
Type: AVIO - Datapoints: 0 - ID: 256 - Value: -0.00

5
6586737900010001546416000
Type: AVIO - Datapoints: 0 - ID: 256 - Value: -0.00

The first value in the struct (the Type) is correct, but i even wasn't able to display the second value (Datapoints) correctly.

While searching around for many many hours, i saw posts that using structs on different systems to transfer data could be complicated. Also that f.ex. the number of bytes for an "int" is different on Visual C++ and the Arduino (also tried "long" instead of "int" on the Arduino, no success). Tried around a lot of stuff but nothing made it better.

Also recognised that the received data has different lengths depending on the value sent, no idea why...

Maybe there is a better and simpler way to submit data from Visual C++ to Arduino?

I have no more ideas at the moment and every hint would be appreciated.

Thank you very much
Urs

This loop:

    for (int i = 0; i < packetSize; i++) {
      Serial.print(DATA_buf[i]);
    }

prints each data byte as a decimal number which will be from one to three digits. But there's no spacing between the value for each character so there's no way to interpret those numbers.
I would have printed each character as a hexadecimal number, but this will at least allow you to figure out the value of each character:

    for (int i = 0; i < packetSize; i++) {
      Serial.print(DATA_buf[i]);
      Serial.print(",");
    }

Pete

Hi Pete,

Thanks for your answer.
Of course, haven't thought of that :~

Now it looks like this

0
65,86,73,79,0,0,0,1,0,0,0,154,0,0,0,0,
Type: AVIO - Datapoints: 0 - ID: 256 - Value: -0.00

1
65,86,73,79,0,0,0,1,0,0,0,154,63,128,0,0,
Type: AVIO - Datapoints: 0 - ID: 256 - Value: -0.00

2
65,86,73,79,0,0,0,1,0,0,0,154,64,0,0,0,
Type: AVIO - Datapoints: 0 - ID: 256 - Value: -0.00

So the "Datapoints" and "ID" has 4 bytes each. I changed the struct on the Arduino to long which is 4 bytes. but still no success:

struct SimDataPoint {
  long id;
  float value;
};

struct SimDataPacket {
  char  packet_id[4];
  long  nb_of_sim_data_points;
  struct SimDataPoint sim_data_points[150];
};

// if using long:
65,86,73,79,0,0,0,1,0,0,0,154,0,0,0,0,
Type: AVIO - Datapoints: 16777216 - ID: -1711276032 - Value: 0.00

// and using unsigned long:
65,86,73,79,0,0,0,1,0,0,0,154,65,248,0,0,
Type: AVIO - Datapoints: 16777216 - ID: 2583691264 - Value: 0.00

Decoding this packet:
65,86,73,79,0,0,0,1,0,0,0,154,0,0,0,0,

65,86,73,79 is the four ASCII characters AVIO
the next four bytes are the number of data packets which follow, and 0,0,0,1 is of course one packet.
That packet has a packet ID of 154 and the value is floating point zero.

Similarly, 65,86,73,79,0,0,0,1,0,0,0,154,63,128,0,0,
is AVIO with one packet with an ID of 154 and the floating point number is the 63,128,0,0 which would be easier to decode if it was printed by the program.

Pete

which would be easier to decode if it was printed by the program.

No decoding would be needed if a union was used. The only question is whether the 4 values are big-endian or little-endian.

No decoding would be needed if a union was used.

That's what I meant. The program could do the decoding instead of printing each byte as a decimal number and then decoding it by hand.

Pete

On the Visual C++ side little-endian is converted to big-endian to transfer over network:

#if BYTE_ORDER == LITTLE_ENDIAN
float custom_htonf(float x) {
    float r;
    unsigned char *s1 = (unsigned char *) &x;
    unsigned char *s2 = (unsigned char *) &r;
    s2[0] = s1[3];
    s2[1] = s1[2];
    s2[2] = s1[1];
    s2[3] = s1[0];
    return r;
}
int custom_htoni(int x) {
    int r;
    unsigned char *s1 = (unsigned char *) &x;
    unsigned char *s2 = (unsigned char *) &r;
    s2[0] = s1[3];
    s2[1] = s1[2];
    s2[2] = s1[1];
    s2[3] = s1[0];
    return r;
}
#endif

So i reversed it on the Arduino side and also changed "int" to "long" because of the 4 bytes. I'm getting closer:

struct SimDataPoint {
  unsigned long id;
  float value;
};

struct SimDataPacket {
  char  packet_id[4];
  unsigned long  nb_of_sim_data_points;
  struct SimDataPoint sim_data_points[150];
};


float custom_ntohf(float x) {
  float r;
  unsigned char *s1 = (unsigned char *) &x;
  unsigned char *s2 = (unsigned char *) &r;
  s2[0] = s1[3];
  s2[1] = s1[2];
  s2[2] = s1[1];
  s2[3] = s1[0];
  return r;
}
int custom_ntohi(int x) {
  int r;
  unsigned char *s1 = (unsigned char *) &x;
  unsigned char *s2 = (unsigned char *) &r;
  s2[0] = s1[3];
  s2[1] = s1[2];
  s2[2] = s1[1];
  s2[3] = s1[0];
  return r;
}


void loop() {
...
SimDataPacket* recv_data = (SimDataPacket*)DATA_buf;
    
    Serial.print("Type: ");
    Serial.print(recv_data->packet_id);
    Serial.print(" - Datapoints: ");
    Serial.print(custom_ntohi(recv_data->nb_of_sim_data_points));

    //Serial.println(sizeof(recv_data->sim_data_points));
    
    Serial.print(" - ID: ");
    Serial.print(custom_ntohi(recv_data->sim_data_points[0].id));
    Serial.print(" - Value: ");
    Serial.println(custom_ntohf(recv_data->sim_data_points[0].value));
}

And the output looks like this:

0
65,86,73,79,0,0,0,1,0,0,0,154,0,0,0,0,
Type: AVIO - Datapoints: 16 - ID: 16 - Value: 0.00

1
65,86,73,79,0,0,0,1,0,0,0,154,63,128,0,0,
Type: AVIO - Datapoints: 16 - ID: 16 - Value: 1.00

2
65,86,73,79,0,0,0,1,0,0,0,154,64,0,0,0,
Type: AVIO - Datapoints: 16 - ID: 16 - Value: 2.00

3
65,86,73,79,0,0,0,1,0,0,0,154,64,64,0,0,
Type: AVIO - Datapoints: 16 - ID: 16 - Value: 3.00

The Value is correct now \o/. But Datapoints should be 1 and ID: 154...

Added a second datapoint:

65,86,73,79,0,0,0,2,0,0,0,152,70,28,64,0,0,0,0,154,64,64,0,0,
Type: AVIO - Datapoints: 24 - ID: 24 - Value: 10000.00 - ID: 24 - Value: 3.00

Datapoints and both IDs show 24 now. Before, with one datapoint, it was 16. Looks like it shows the total number of bytes and not the value itself. I'm a little bit confused. But at least the values are fine.

OK, now i got it!

Forgot to change the custom_ntohf/custom_ntohi functions from int to long:

float custom_ntohf(float x) {
  float r;
  unsigned char *s1 = (unsigned char *) &x;
  unsigned char *s2 = (unsigned char *) &r;
  s2[0] = s1[3];
  s2[1] = s1[2];
  s2[2] = s1[1];
  s2[3] = s1[0];
  return r;
}
long custom_ntohi(long x) {
  long r;
  unsigned char *s1 = (unsigned char *) &x;
  unsigned char *s2 = (unsigned char *) &r;
  s2[0] = s1[3];
  s2[1] = s1[2];
  s2[2] = s1[1];
  s2[3] = s1[0];
  return r;
}

Many thanks for your help!
Urs