Serial Port : Speed problem

Hi,

I am developing a software in python which is linked to an Arduino Uno via a serial port and i have some problems.
This software permits to select a recording of several objects that change of colors during time.
The Goal is to display colors changements of all objects on digital Leds (WS2812, using Adafruit library on Arduino) at the same speed.
The number of objects (and so leds) is between 100 and 190.

The sent message:
For instance, here i have to display the recording of 167 objects.
The computer send a message that contain the statement of each object at a time t.
The sent message has a specific shape :

  • The first byte is the number of objects to display
  • The next 3 bytes are the statement of my first object [color Red, color Green, color Blue]
  • And so on
    With 167 object, each message will have a length of 502 bytes.

How i expect to display with the same speed of the recording :
The baud rate of the serial port is at 230400. So to send a 502 bytes message, it takes almost 22ms.
The program of my Arduino (see the end of this post) takes 26ms (receiving message and display it on leds)
So i decide to send the objects statement at each 30ms to Arduino.

Here you have the code that send a message to the arduino every 30 ms :

for t in TimeRecording : #TimeRecording is a list like [0,0.030,0.060,0.120,0.180  ...] to hundred seconds
    s=time.time() #Starting time in second
    # Here i build my message to send 
    serialPort.write(struct.pack('B'*(502), *message))
    e=time.time() #End time in second
    if (e-s)<0.030:
        time.sleep(0.030-(e-s))

And here Arduino Program:

const unsigned int MAX_INPUT =200;
#include <Adafruit_NeoPixel.h>
#define PIN 6
#define NBLED 220
// Init de Adafruit
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NBLED, PIN, NEO_GRB + NEO_KHZ800);

void setup ()
  {
  // Init Leds
   strip.begin();
   strip.show();
  // Init Serial port
  Serial.begin (230400);
  Serial.println("Ready");
  } // end of setup

  int bytesread=0, i=0;
  long couleur[4];
  uint32_t c=0;
  int currentLed=0;
  int nbled=0;
 
static char input_led [NBLED*3] ; 


void lightoff()
{
  c=strip.Color(0,0,0);
  for(i=0;i<nbled;i++)
    {
      strip.setPixelColor(i,c);
   }
   strip.show();
}

void lightfrombuffer()
{
  
  for(i=0;i<nbled;i++)
    {
      c=strip.Color(input_led[i*3],input_led[i*3+1],input_led[i*3+2]);
      strip.setPixelColor(i,c);
   }
   strip.show();
   
   
}


void loop()
  {
  // if serial data available, process it
  if (Serial.available ())
  {
    
    nbled=Serial.read();
    bytesread=Serial.readBytes(input_led,nbled*3);
    if (bytesread==nbled*3)
    {
      lightfrombuffer();
    }
    else 
    {
      lightoff();
    }
    
  }
}

I think that my program is right. Because If i display my recording with a speed x0.5 instead of x1, I have two time the recording time as expecting. But, at speed x1 it’s chaotic. My Arduino does not success to display correctly the informations that i send, it seems that the arduino mistreats received data. The maximum that i can do as speed is x0.8 (so sending a message every 42ms.)

I don’t understand why i can not send a message every 30ms without a beug from the Arduino. Can you help me ?

Thanks

I guess the first question is what is the longest acceptable time between updates of the LEDs?

I suspect you would get much better use of your Arduino if you send the data in chunks of less than 64 bytes so a chunk can fit within the Serial input buffer. That way the code that uses the received chunk of data can run (more or less) in parallel with the receipt of the next chunk of data. You can also modify the Serial library to increase the size of the input buffer.

Note, also, that Serial.readbytes() is a blocking function that will waste time waiting for data.

You may want to look at how, in Serial Input Basics all the data is collected into an array (without blocking) before any of it is used.

And you should be able to use 500,000 or 1,000,000 baud on an Uno or Mega. A Leonardo or Micro work at even higher speeds because they ignore the baud rate and use the full USB data transfer rate.

...R

Thanks a lot for your answer.

I used your third example of your post Serial Input basics, And it is much better. I still have some displayed artefacts.
I don’t think it is a software problem but an electronic problem.

Here my code on Arduino :

const byte numChars = 5;
char receivedChars[numChars];
boolean newData = false;
uint32_t c=0;
int idxled=0;
#include <Adafruit_NeoPixel.h>
#define PIN 6
#define NBLED 192
// Init de Adafruit
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NBLED, PIN, NEO_GRB + NEO_KHZ800);

void setup ()
  {
  // Init Leds
   strip.begin();
   strip.show();
  // Init Serial port
  Serial.begin (230400);
  Serial.println("PlasticBrain Prêt");
  } // end of setup

void loop() {
    recvWithStartEndMarkers();
    //showNewData();
}

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;
 
    while (Serial.available() > 0 && newData==false) {
        rc = Serial.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                if (ndx==2){
                  UpDateLed(idxled,receivedChars[0],receivedChars[1],receivedChars[2]);
                  idxled++;
                  ndx=0;
                }
                else ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                strip.show();
                //receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx=0;
                idxled = 0;
            }
        }

        else if (rc == startMarker) {
            idxled=0;
            recvInProgress = true;
            
        }
    }
}

//void showNewData() {
    //if (newData == true) {
        //Serial.print("This just in ... ");
        //Serial.println(receivedChars);
        //newData = false;
    //}
void UpDateLed(int nbled,int r,int g,int b) {
    c=strip.Color(r,g,b);
    strip.setPixelColor(nbled,c);
}

And my electronic system: I have a battery at 5v-5A which is relied to 3 64 Led rgb Matrix (Velleman) and a capacity(1000uF) in parallel. Maybe the capacity is too small … Or maybe there is just something wrong in my program.

Take out all the code you have added to the recvWithStartEndMarkers() function and put it somewhere else. The function is not designed to be used that way. The intention is to receive the complete message and save it in the array receivedChars[] (which could be changed to a byte array if needed). When a message is complete (as identified by the newData variable, then you can use the data for whatever.

Apart from anything else, adding other code into the function that is unrelated to receiving data just makes debugging much harder.

...R