Go Down

Topic: Processing and Arduino - String to bytes (Read 146 times) previous topic - next topic

maurobarreca

Hi, I´m in the middle of a project, my first big one using Arduino, and need some advice with the topic of serial communications.

I have a Processing sketch that sends a string to an Arduino UNO R3 through serial. I'm using 250000 baud rate and I'm sending a long array of data (135 four-digit values). The string, generated in Processing, has this structure:

Code: [Select]
<4095 4095 4095 ... 4095 4095>

The string is made to send RGB info to 135 LEDs (45 RGB LEDs) at 12bit resolution once per frame. The problem is that because of the array size plus the limits of the Arduino serial port speeds, the code only works when the Processing sketch is running at 20fps, and my objective is to run at at least 25 (I'm very close!).

I was told that I can improve the communication speed by sending bytes instead of a string, but I don't know where to start to adapt my current code to do that.

This is my current code:

Code: [Select]
#include "Adafruit_TLC5947.h"

#define NUM_TLC5974 6

#define data   4
#define clock   5
#define latch   6
#define oe  -1  // set to -1 to not use the enable pin (its optional)

Adafruit_TLC5947 tlc = Adafruit_TLC5947(NUM_TLC5974, clock, data, latch);

char val; // Data received from the serial port
char receivedChars[674];
char arrayOfNums[135];
boolean started = false;
boolean ended = false;
int serialIn = 0;

void setup()
{
  //initialize serial communications
  Serial.begin(250000);
  tlc.begin();
  if (oe >= 0) {
    pinMode(oe, OUTPUT);
    digitalWrite(oe, LOW);
  }
}

void loop(){

  while(Serial.available() > 0) {
    int incomingByte = Serial.read();
    if(incomingByte == '<'){
      started = true;
      ended = false;
    } else if (incomingByte == '>') {
      ended = true;
      break;
    } else {
      receivedChars[serialIn] = incomingByte;
      serialIn++;
    }
  }

 
  if (started && ended) {
    int arrIndex = 0;   

    char * token = strtok(receivedChars," ");
    while (token != NULL){
      int aNum = atoi(token);
      if(arrIndex <= 134){
        //arrayOfNums[arrIndex] = aNum;
        tlc.setPWM(arrIndex, aNum);
        arrIndex++;
      }
      token = strtok (NULL, " ");
    }
    tlc.write();
    serialIn = 0;
    receivedChars[serialIn] = '\0';
   
    started = false;
    ended = false;
  }
 
}


Could you please help me with a little guidance to how to start?

Thank you.

Robin2

I don't know Processing so I can't help with that side of things.

Sending binary data will certainly result in shorter messages - 4095 can fit into an an int (2 bytes). But with binary data it is more difficult to know when the data starts and ends because the byte values used as start- and end-markers might legitimately appear in the data. That can be avoided by treating (say) byte values 253, 254 and 255 as special cases. See the code in the Original Post in this Thread. Note that there is a different version in Reply #4 in that Thread).

I like the idea of sending data in human readable form because it makes debugging easier. In my recent projects when I need extra communication speed I have been converting numbers into a sort-of base64 code. So 4095 would become
4095 / 64 = 63 (integer maths) with the remainder of 63. And then I add 48 (so that a byte value of 0 becomes 48) and 4095 encodes as the two chars "oo" (lower case O). That way I can still use < and > as start and end markers. This may sound complex but it actually decodes in about 1/10th of the time required by atoi().

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

maurobarreca

Hi. Thank you for your comments. Yes, I was aware of the difficulties of working with binary data, but can it be avoided with some synchronisation? For example: sending a request when the Arduino is ready to receive and send from Processing in that moment. Or would it be even slower?

I was reading too about compressed data (I think that's what you're talking about on the second half of your post). Maybe I should try that, seems to be a smoother transition from my current code. I do not understand, though, the conversion you describe. You mean using numbers 0 to o (lower case O)? What function can I use to make the conversion? I do not understand why you add 48. Sorry, I'm new on this kind of stuff.

Robin2

#3
Aug 14, 2017, 09:40 am Last Edit: Aug 14, 2017, 09:41 am by Robin2
In my "compressed" system I use the 64 characters from '0' (number 0) to 'o' (lower case O) to represent the values 0 to 63

48 is the ascii code for '0' (number 0) which is why I add 48

The encoding calculation is (in pseudo code)
Code: [Select]
highByte = (integer part of myNumber / 64) + 48
lowByte =  (remainder part of myNumber / 64) + 48


The deconding is
Code: [Select]
myNumber = (highByte - 48) * 64 + (lowByte - 48)

Check my calcs with your calculator in case I have made a silly mistake.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

AndreR241

You could use signed int16, for example. Use positive numbers for the data values and negative numbers as commands.

-1 is 1111 1111 1111 1111 (or 0xFFFF) in binary. You could easily use this as the start mark. 0xFF00 could be a as an end mark, because the first four bits could never be 1 in your operation range.

With that approach, you can directly write the data into a storage array without further conversion. It should be (nearly) the fastest you can get. If you want to improve speed even further, you could use a custom 12 bit format and pack the data even further. To speed it up even more, it would be possible to use a simple compression like the run-length encoding, but that highly depends on the data you are transmitting.

Robin2

You could easily use this as the start mark. 0xFF00 could be a as an end mark,
Using 2-byte start and end marks makes their detection very much more complex.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Go Up