Extracting Data from SoftwareSerial

If I have an array of values being sent to me through the Software Serial library, and I want to extract the array so that I can use those values (i.e. for something such as mapping them to a servo), how would I go about doing that?

The Serial class has methods to accomplish parsing from a delimited ASCII stream:

See: parseInt() and parseFloat()

There are numerous other ways to accomplish this, too.

ahhreeyell:
I have an array of values being sent to me through the Software Serial library

You need to know how the values are encoded, and then read the bytes from SoftwareSerial and decode them to get back the original values. How you do this would depend how they have been encoded.

It would be easier to help if you could supply some example input..

I agree with PeterH

You need to know how the values are encoded, and then read the bytes from SoftwareSerial and decode them to get back the original values.

By convention, the only way this makes sense is if the receiving array matches the type/size of the sending array. Then an agreement via handshaking dumps the array from the sending side to the receiving side over serial, looping element by element. Delimiting each element allows the receiver to extract and store the received value. One can only surmise that the array positions are meaningful to the receiver; ex. Array[5] perhaps representing a servo movement.

If the array is of type int, then Serial.parseInt() will suffice as would Serial.parseFloat if the array element were of type Float.

Sending arbitrary updates from array to array only requires a syntax such as <element, delimiter, value>

The value add here would be enhancement for checksums or advanced handshaking prior to the received value being stored and utilized.

Ray

tigerbomb8:

To answer your question, I'm using two Arduinos, and each one has a GPRS shield. The first Arduino has an FSR (force-sensing resistor—basically a pressure sensor) attached to it, and I'm storing the values it produces when it's squeezed into an array. I then send the array of values to the other Arduino, using the SoftwareSerial library. I will need to parse those values so that I can map them to a servo and make the servo rotate a corresponding number of degrees.

Did all that make sense? If I wasn't clear, or you need more information, please tell me.

In terms of an output example, the values are between 125 and 250, and they are separated by a newline character, so they are all on separate lines. So an output example is always going to be a list of values between those two numbers, and it will never be more than 100 values, because I have the maximum value of the array set to 100.

mrbrunette: Unless I am mistaken, it seems as though that library is for Serial, not SoftwareSerial. Do you know of a similar library/commands for SoftwareSerial?

@ahhreeyell:

According to PaulS on this post:'class SoftwareSerial' has no member named 'parseInt' - #3 by system - Programming Questions - Arduino Forum

Re: 'class SoftwareSerial' has no member named 'parseInt'
« Reply #2 on: August 29, 2013, 04:28:30 am »

What version of the IDE are you using? Prior to 1.0, SoftwareSerial derived from a different base class that did not have a parseInt() method.

So, I'm betting with PaulS that it will work... Op on the other post indicated it worked on ATtiny with the arduino-tiny core. But, I have not gone down to the Lab this morning to recompile a test to verify... maybe after another cup of coffee... maybe.

Ray

I'm not sure if this will help, but I only figured it out yesterday - my knowledge of C, C++ is very limited. It would have saved me a lot of time with several sketches if I had a found a tutorial about this.

I define a struct with the variables I am expecting to receive - for example

struct testStruct {
  int val1;
  int val2;
  long val3;
  int val4[8];
};

This struct will take up 2 + 2 + 4 + 2*8 = 24 bytes

I then create a union that "connects" that struct with an array of bytes (make it a bit bigger for safety)

union testUnion {
  byte dataReceived[26];
  testStruct dataVals;
};

Now I can receive the data (in my case from my PC) into the dataReceived array and read it properly formatted from the struct, for example

int recVal1 = dataVals.val1;

By the way I haven't tested this code, but it's just like my working code.

...R

When receiving messages like that, make sure you have some way to determine where each messages starts and ends. It's entirely possible for the serial interface to lose a byte, and if this happened all the subsequent messages would be misaligned with your data structure. If you can identify a byte sequence which you know will not appear in the message, you could use that as a marker. But, however you do it, you need to have a way to work out where each message starts and ends which does not rely on the sender and receiver staying perfectly synchronised.

Agreed.

I should have said that I prefix my data with a "C" (just because I felt like it) so the Arduino doesn't start saving bytes until the C is received. Then I send an "F" after the data. Because it's binary data there is no single character that will never ever appear in the data so my Arduino code reads the expected number of data bytes and expects the next byte after that to be an "F". If it's not an F the whole transmission is ignored.

If I was sending ascii characters I would probably use the byte 254 to mark the start and 255 to mark the end knowing that neither of these would ever be part of the ascii data.

...R

This struct will take up 2 + 2 + 4 + 2*8 = 24 bytes

No. It will take up AT LEAST 24 bytes. The compiler is free to pad the struct as much as it wants. That probably won't happen on the Arduino, but it is never safe to assume that the struct size matches the size of the objects in the struct.

Robin2:
I should have said that I prefix my data with a "C" (just because I felt like it) so the Arduino doesn't start saving bytes until the C is received. Then I send an "F" after the data. Because it's binary data there is no single character that will never ever appear in the data so my Arduino code reads the expected number of data bytes and expects the next byte after that to be an "F". If it's not an F the whole transmission is ignored.

That's a good scheme.

Thanks for this explanation. Based on experience I did suggest in my earlier post that the byte array be made longer than the struct as I got stung with very strange behaviour when the data going into the array overwrote the variable after the Union - however I thought (when the problem occurred) that I actually (accidentally) had the array smaller than the struct. The facts are now lost in the great cloud of deleted code.

...R

PaulS:

This struct will take up 2 + 2 + 4 + 2*8 = 24 bytes

No. It will take up AT LEAST 24 bytes. The compiler is free to pad the struct as much as it wants. That probably won't happen on the Arduino, but it is never safe to assume that the struct size matches the size of the objects in the struct.

Robin2:
Agreed.

I should have said that I prefix my data with a "C" (just because I felt like it) so the Arduino doesn't start saving bytes until the C is received. Then I send an "F" after the data. Because it's binary data there is no single character that will never ever appear in the data so my Arduino code reads the expected number of data bytes and expects the next byte after that to be an "F". If it's not an F the whole transmission is ignored.

Since 'C' is 67 in ASCII, let's hope val1, val2, val3 or val4 never contain the number 67. And don't get me started on the 'F' part. :slight_smile:

I wrote a library for sending serial data with a prefix and suffix, including a sumcheck:

The struct idea is sound enough, but you should use "sizeof" to let the compiler work out the length, rather than doing it yourself.

Nick, I think it's a little more robust than your comment implies.

If the initial C is not detected but one of the Vals "accidentally" contains a C it would ALSO be necessary for an F to "accidentally" exist after the end of the proper data in a position so that it is the right number of characters after the "incorrect" C.

It would be an easy matter to include a CRC character at the end of the data (before the F) as an additional safeguard.

...R

Let's say you start listening in the middle of the data stream. The "wrong" 'C' arrives and you then read 24 bytes and don't get an 'F'. So you read again. Meanwhile the other end has started sending again (quite likely since you might have only received 10 bytes before the "real" 'F'). Since the other end is retransmitting you get the wrong 'C' again, and so on. Nothing would really break you out of this loop.

A simple solution would be to send in ASCII and not binary, take the slight performance hit, and then put non-numeric characters at each end, eg.

<123,456,789,22,33,44,55,66,77,88,99>

If the numbers are small, something like "77," is actually less bytes than a long, anyway.

Nick, Thanks again for your insightful comments.

I should have said that the system I'm developing only allows the PC to send bytes when it is requested to by the Arduino so my sketch won't face a continuous flow of uncoordinated data. And whether the data is good or bad the Arduino dumps all characters after the specified quantity so the receive buffer is empty ready for the next PC transmission.

Which all goes to demonstrate that the logic of sending data is complex even if the code to implement it isn't very long.

By sending Longs and Ints to it the code on the Arduino is very much simpler and there is no time cost for converting ascii numbers to binary. And 857333 takes 6 bytes as ascii but only 4 as a Long. And writing and testing PC code in JRuby is much easier than writing and testing Arduino code.

...R

Robin2:
By sending Longs and Ints to it the code on the Arduino is very much simpler and there is no time cost for converting ascii numbers to binary.

Better get the endian-ness right then. It might be by default, and then again it might not.