Parsing binary (Arduino Sensor) data using [serial]?

Rodrigo's icon

So I have two Arduino boards, with 9 analog sensors each, going over a pair of XBee (series 1) into another XBee connected to a Sparkfun USB Explorer Xbee shield.

It's 3axis gyro, 3axis accel, 3axis compas.

In Max I need to determine which arduino is sending the data, and for which sensor.

I started off using serial.print() in the Arduino code with a tweaked version of Sensor Box (https://cycling74.com/tools/sensorbox/) in Max, but I started getting buffer overflows when going over XBee. I've since switched to serial.write() in order to send the data as binary bytes, to send as little data as possible.

Here is one clump of the code from the Arduino:

Serial.write(0x11);
Serial.write(highByte(AccX));
Serial.write(lowByte(AccX));
Serial.write(0x99);

It's the same format for each sensor. The first byte is my header which tags the device and the sensor, so 0x11 = device 1, sensor 1. AccX is the accelerometer X axis, and 0x99 is my footer.

Now the sensor box parsing used Serial.println() and [select 10 13] to group each bit of data together. I'm not using serial.print but 0x99 instead as it's only one byte.
Also, I don't know how I would assemble the binary byte back together in max either.

I then need to route the sensor/device data out to different places. Before I was using [route] as I had plain text coming in, now it's a binary byte as the sensor/device tag, so not sure if I can use [route] with that, or if I turn the binary byte into something [route] would be happy with.

Lastly, is there anything I can do on the Max side of things to make sure the data from each device doesn't "cross streams" with the other one? I basically have two of these devices, all sending 9 sensors worth of data to [serial] at all times. If I figure out the binary parsing/routing, what's to stop the messages coming in mixed together?

So if device one is sending:

Serial.write(0x11);
Serial.write(highByte(AccX));
Serial.write(lowByte(AccX));
Serial.write(0x99);

and device two is sending:

Serial.write(0x25);
Serial.write(highByte(GyroY));
Serial.write(lowByte(GyroY));
Serial.write(0x99);

will [serial] potentially see:

Serial.write(0x11);
Serial.write(0x25);
Serial.write(highByte(AccX));
Serial.write(highByte(GyroY));
Serial.write(lowByte(AccX));
Serial.write(lowByte(GyroY));
Serial.write(0x99);
Serial.write(0x99);

Because that would obviously break the parsing routine.

Here is the max patch I'm using (though all the parsing objects are setup for my older Serial.print() code).

Max Patch
Copy patch and select New From Clipboard in Max.

Scott Fitzgerald's icon

To make sure you aren't "crossing the streams", try sending a byte to the sensor you want information from first, indicating you're ready to receive. Once you've received from sensor 1, send a byte to sensor 2 requesting data. This should help insure you know what you're getting.

Rodrigo's icon

Would that add considerable latency to the setup? (if every bit of sensor data makes it's own round trip)

brendan mccloskey's icon

Hi Rodrigo
Try different baud rate settings to determine the significance of adding call/response code to your setup.

I'm interested to know the reason for using two separate Arduinos...

Brendan

Rodrigo's icon

There's two sending devices.

It's for a sensor setup on a painter. So there's an Arduino/Sensorboard + Xbee + battery per arm/wrist. All of that data will end up in max and after some software filtering (kalman/marg? I've not gotten that far yet) I'll then use the angles to determine DSP/sampling stuff.

At the moment I'm at the maximum baud rate that the XBee's will do. 11500, so anything else would only increase the delay. I've not tested a call/response thing at 11500 though.

I still don't know how to make sense of the binary bytes coming in (particularly the sensor one which is split), so can't really setup a call response thing yet.

Scott Fitzgerald's icon

There shouldn't be a noticeable latency. One of the big benefits to a call and response method helps insure both the sender and the receiver are ready for new information, not backlogging the queue.

As an aside, I've found Xbees to be unreliable at 115,200 baud, you may want to try something slower like 57600. That should be more than sufficient for sending 4 bytes at a time from the sensors and processing it in Max.

Rodrigo's icon

So on the Max side of things do you just setup a [sel] type thing? So you send an initial byte to get it started, then once the footer tag is received, use sel to bang the next byte (sensor tag) and just having it go through a sel/bang/byte loop in max?

Scott Fitzgerald's icon

You've nailed it.

Look at the Arduino communication examples that come with the IDE. There's one for raw bytes found in file>examples>communication>serial call and response.

In that example, there's no need for a final footer byte either. Because you can expect the size of the payload, you can just bang out when the right number of bytes arrive.

Rodrigo's icon

Look at that! Very convenient. I shall take a look when I get home from work.

Rodrigo's icon

So there's a 10ms delay between each sensor... that could prove to be problematic as I have 9 sensors per unit, and 2 units, so 18 sensors total, so if I'm handshaking, I'll have 180ms latency (not counting actual processing/transmitting time).

Scott Fitzgerald's icon

You can decrease those delays to something closer to 2ms each. The 10ms is a large safety buffer.

Rodrigo's icon

It's also only between the first two (analog sensors), is it only needed after an ADC is read, but not before the end of the loop?

Actually, the read/sent loop only runs once (per byte received), so that delay is a blanked thing for reading ADCs?

A different version of my code (before I was printing with bytes and was using text as my header/footers) had all the ADCs being read at the same time and it seemed to work OK, it's only when I tried to go wireless with it that it broke.

I also noticed that you use Serial.print vs Serial.write, I was under the (mistaken?) impression that for binary printing you had to use Serial.write.

Scott Fitzgerald's icon

The delay should be done after each analogRead. The reason there is only 1 in that sketch is because there are only 2 analog sensors. It is in there in the first place is to let the onboard ADC 'settle' after each analogRead. So it's not necessary to have one after your final sensor has been read (depending on your sketch).

There are 2 examples in the Communication folder, one for ASCII and one for raw bytes. Make sure you're looking at the right one. You're correct that Serial.write() is for bytes, and Serial.print is ASCII (usually).

Rodrigo's icon

Does that just increase ADC accuracy? As it seems to work fine without the delay.

Both versions in IDE 022 use Serial.print:

From the ASCII one:
// send sensor values:
Serial.print(firstSensor, DEC);
Serial.print(",");
Serial.print(secondSensor, DEC);
Serial.print(",");
Serial.println(thirdSensor, DEC);

From the raw one:
// send sensor values:
Serial.print(firstSensor, BYTE);
Serial.print(secondSensor, BYTE);
Serial.print(thirdSensor, BYTE);

Scott Fitzgerald's icon

Yes, it does increase the accuracy. But if it's working for you, keep what you have.

In 022, Serial.print(value, BYTE) works, but as of version 1.0 (due out in a few weeks), that is no longer supported. Serial.write() will be the way to send bytes in the future.

jalool Salman's icon

I am working on a project that transmit several sensors data wirelessly between two xbee S2 and two arduino uno. the receiving xbee should use the received sensor data to control servo motors on a 6-dof robotic arm. I was able to transmit flex sensor readings and use them to control a servo motor, but how can I do that for several sensors, I mean how can I seperate between sensor readings such that each sensor is responsible for controlling a specific servo motor. Also, I am using a gyroscope with 3 dimentions (x,y,z), how can I transfer each dimention seperately such that the other end is able to know which is which.

brendan mccloskey's icon

This question has already been addressed in your other post here: