Neopixel Show() interrupting serial communication

I have an Arduino (Nano) communicating using Serial communication with my Raspberry Pi. Upon receiving a signal, the Arduino sends the signal back then lights the LED strip hooked up to pin 6.

The thing is that in my void loop function, the strip.show() disables any serial interrupts and the serial signal received back by the Pi is jumbled, random, and broken.

I understand that the strip.show() function contains the noInterrupts() call which is what is causing my serial signal to break. What I need is to either check if there are any more serial data to be transferred before the .show() is executed, or remove that noInterrupts() call entirely (given that this doesn't break any of the desired behavior).

Here is my void loop() function:

void loop() {
  recvWithStartEndMarkers(); //parse signal

  if (newData == true) {
    signalBuild();  //send new signal
    newData = false;
  }

  for(int j = 0; j < 19; j++)
        strip.setPixelColor(j, 255, 0, 0); 
   
  strip.show();  //this is breaking my signal sent back
}

I'm guessing the noInterrupts() is needed because of some sensitive timing required by the neopixels.

In your example, since the colors never change you could simply move the for loop and show() to the setup, but I'm assuming you will eventually do something useful with the neopixels inside the loop so lets move on.

Instead of calling show() every time through the loop, consider calling it just when there are new colors to show. That alone will greatly reduce the time noInterupts is active and therefore reduce missed characters.

Additionally, you could implement some kind of "flow control" to tell the Pi that you're busy and to not send data. The method is to send a "stop" command before show(), and a "start" command after. It could be software flow control like sending a special character to the Pi, or hardware flow control, like toggling an output. Googling "raspberry pi serial port flow control" should get you there on the Pi side.

Do you need to call strip.show() in every iteration of loop()? If it were only called every 50 millisecs (say) there would be more time for other stuff and the pattern would still update 20 times per second.

...R

So I think the noInterrupts() method would suit me best since the Pi is always polling the Arduino with a specific RGB colour. Should this be inserted before the strip.show() or before the serial communication?

darnegar:
So I think the noInterrupts() method would suit me best

I don't understand. Are you the same person as @pacebrian0 but logged in under a different username?

If not, why are you hi-jacking this Thread with your own problem? - you should start your own Thread and post your own code.

...R

Ah yes, we are working on the same project and I (pacebrian0) posted on his laptop by mistake.
I apologize for any confusion caused.

darnegar:
Ah yes, we are working on the same project and I (pacebrian0) posted on his laptop by mistake.
I apologize for any confusion caused.

No problem, now that we know.

...R

I have done everything from mutex for show() and noInterrupts as well as running it every 50ms; but to no avail, the signal always manages to get scrambled in the process. The project works flawlessly without the strip.show(), and it is really bugging me that I cannot bypass this problem. My setup is a number of daisy-chained arduinos and a single pi to send out colours to each, addressed by an index, so the signal flow is only one-way (i.e. I cannot tell the previous device my state). Is there anything else I can try?

darnegar:
I have done everything from mutex for show() and noInterrupts as well as running it every 50ms; but to no avail,

Post the program (in a new Reply) so we can see exactly what you have done.

...R

darnegar:
I have done everything from mutex for show() and noInterrupts as well as running it every 50ms; but to no avail, the signal always manages to get scrambled in the process. The project works flawlessly without the strip.show(), and it is really bugging me that I cannot bypass this problem. My setup is a number of daisy-chained arduinos and a single pi to send out colours to each, addressed by an index, so the signal flow is only one-way (i.e. I cannot tell the previous device my state). Is there anything else I can try?

How big is the data packet you are receiving and transmitting? how frequently? what Serial Speed?

Make it smaller, do it less frequently, do it faster

post your code if you really want help.

This is my serial function:

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

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        changecolours();  //this is breaking my signal sent back
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        strLength = ndx;
        ndx = 0;
        newData = true;

      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
  //interrupts();
}

This is my LED function

void changecolours() {

  byte count = 0;
  int diff_rgb = 0;
  for (int i = 0; i < 3; i++) {
    diff_rgb = signal_rgb[i] - permanent_rgb[i];

    if (diff_rgb > 0) {
      permanent_rgb[i] += 2;

    }
    else if (diff_rgb < 0) {
      permanent_rgb[i] -= 2;
    }
    else count++;

  }
  if (count == 3)return;
  noInterrupts();
  for (int j = 0; j < 18; j++) {
    strip.setPixelColor(j, permanent_rgb[0], permanent_rgb[1], permanent_rgb[2]);

  }
  
  strip.show();  //this is breaking my signal sent back


  interrupts();
  
}

Sometimes the arduino locks up (or restarts), causing the connection to fall for about 5 seconds. I suspect this would either be a buffer overflow of the serial or maybe the strip.show() hogging up the arduino. Funny thing is, removing the changecolour() function causes the arduino to function perfectly and without any hitches.

How about posting the ENTIRE code as requested?

darnegar:
This is my serial function:

I am not going to waste time looking at snippets. Post the complete program.

...R

  noInterrupts();
  for (int j = 0; j < 18; j++) {
    strip.setPixelColor(j, permanent_rgb[0], permanent_rgb[1], permanent_rgb[2]);

  }
  
  strip.show();  //this is breaking my signal sent back


  interrupts();
  
}
  1. you should not be managing interrupts, the library does that for you
  2. you certainly don't need to manage interrupts to modify the pixel buffer:
strip.setPixelColor(j, permanent_rgb[0], permanent_rgb[1], permanent_rgb[2]);

You also should not be mixing apples and oranges, or in your case updating pixel colors during your Serial data management...

this serial model you used from @Robin2's tutorial is a good start:

strip.setPixelColor(j, permanent_rgb[0], permanent_rgb[1], permanent_rgb[2]);

but you need to de-couple the two functions... I'd recommend that you switch to a function that returns something, call it in loop(), and call color change function if and only if a new something is received.

I suspect that your entire code is in need of a bit of work...

Entire arduino code as requested.
The code on the Pi is a simple write and read, I will post it too if necessary

//#include <LiquidCrystal.h>
#include <stdio.h>
#include <stdlib.h>
#include <Adafruit_NeoPixel.h>
#include <Wire.h>
#include <VL53L0X.h>
VL53L0X sensor;

#define PIN 6
Adafruit_NeoPixel strip = Adafruit_NeoPixel(18, PIN, NEO_GRB + NEO_KHZ800);
#define LONG_RANGE
//LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
char tmp[2] = "";
const int numChars = 500;
char receivedChars[numChars];
int init_index = 0;
int permanent_rgb[3] = {0, 0, 0};
int signal_rgb[3] = {0, 0, 0};
int sensor_location = 0;
bool chk = false;
boolean newData = false;
int strLength = 0;
int counter = 0;
bool mutex = true;
void setup() {

  strip.begin();

  //strip.show() causes I/O problems with serial -> interrupts
  strip.show(); // Initialize all pixels to 'off'
  Serial.begin(115200);
  Wire.begin();
  sensor.init();

  sensor.setTimeout(500);
  sensor.setMeasurementTimingBudget(33000);
  sensor.setSignalRateLimit(0.1 * 65536);
  sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18);
  sensor.setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14);

  sensor.startContinuous();
}
void changecolours() {

  byte count = 0;
  int diff_rgb = 0;
  for (int i = 0; i < 3; i++) {
    diff_rgb = signal_rgb[i] - permanent_rgb[i];

    if (diff_rgb > 0) {
      permanent_rgb[i] += 2;

    }
    else if (diff_rgb < 0) {
      permanent_rgb[i] -= 2;
    }
    else count++;

  }
  if (count == 3)return;
  noInterrupts();
  for (int j = 0; j < 18; j++) {
    strip.setPixelColor(j, permanent_rgb[0], permanent_rgb[1], permanent_rgb[2]);

  }
  
  strip.show();  //this is breaking my signal sent back


  interrupts();
  
}
void loop() {
  recvWithStartEndMarkers(); //parse signal

  if (newData == true) {
    signalBuild();  //send new signal
    newData = false;
  }
  /*
    if(counter>50){
    changecolours();  //this is breaking my signal sent back
    counter=0;
    }
    counter++;
  */




}

void parseSign() {  //parse the sign into a string

  //bool chk = false;

  sensor_location = init_index;

}


void buildIndex() { //building of index into new sign

  char tmp[3];

  tmp[0] = receivedChars[0];
  tmp[1] = receivedChars[1];

  init_index = atoi(tmp);

  itoa(init_index + 1, tmp, 10);

  if ((init_index + 1) < 10) {
    receivedChars[0] = '0';
    receivedChars[1] = tmp[0];
  } else {
    receivedChars[0] = tmp[0];
    receivedChars[1] = tmp[1];
  }
  //Serial.write(sign);
}


void buildSensor() { //building of sensor values into new sign
  char sens;
  int newValue;
  int range = sensor.readRangeContinuousMillimeters();

  //newValue = range;
  newValue = range / 26 + 97;
  //bring newValue to ASCII/Alphabet character
  sens = newValue;
  receivedChars[sensor_location] = sens;
}

void readRGB() {
  while (receivedChars[sensor_location] != '%') {
    sensor_location++;
  }
  sensor_location += 3 * init_index + 1;

  for (int i = 0; i < 3; i++) {
    if ((int)receivedChars[sensor_location + i] * 2 >= 0 && (int)receivedChars[sensor_location + i] * 2 < 255)
      signal_rgb[i] = ((int)receivedChars[sensor_location + i] * 2)%255;
    //Serial.write((int)receivedChars[sensor_location + i] * 2);
  }
  //rgb_values[sensor_location]='\0';
}


void signalBuild() {
  if (strLength <= 3) {
    buildIndex();
    Serial.write("<");
    Serial.write(receivedChars);
    Serial.write(">");
  }
  else {
    parseSign(); //parse signal string
    //buildIndex(); //build new index
    buildSensor();  //read and build new sensor
    Serial.write("<");
    Serial.write(receivedChars);
    Serial.write(">");    readRGB(); //read RGB values

  }
}

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

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        changecolours();  //this is breaking my signal sent back
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        strLength = ndx;
        ndx = 0;
        newData = true;

      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
  //interrupts();
}

void showNewData() {
  if (newData == true) {
    Serial.write("<");
    Serial.write(receivedChars);
    Serial.write(">");
    newData = false;
  }
}

That all seems to be very mixed up. I don't understand why there seem to be several separate parts for extracting information from the received data. Why not just do it all in one place?

Please post some examples of the data that is being sent to the Arduino - they may help to make things clearer.

The RPi has a great deal more power than an Arduino so it should be sending data in a format that requires the minimum effort by the Arduino.

Don't add calls to functions (such as changecolours() ) into recvWithStartEndMarkers(). As far as possible each function should just do one thing. You can just as easily call changecolours() from loop() and it would also make the code more obvious.

I know you have commented out the lines noInterrupts() and interrupts() in recvWithStartEndMarkers() but the thought of wanting to put them there is mind boggling.

...R

pacebrian0:
I have an Arduino (Nano) communicating using Serial communication with my Raspberry Pi. Upon receiving a signal, the Arduino sends the signal back then lights the LED strip hooked up to pin 6.

You can consider to use Serial.flush() after / as part of the echoing back to the Pi. That will force the data to be send to the Pi before continuing with the strip.

Note 1: I did not look at your complete code.
Note 2: flush will block till all serial data is send; alternatively you can use Serial.availableForWrite() to check if the buffer is empty; that would give the option for a non-blocking flush if needed.

sterretje:
the option for a non-blocking flush

Every plumber's dream :slight_smile:

...R

//#include <LiquidCrystal.h>
#include <stdio.h>
#include <stdlib.h>
#include <Adafruit_NeoPixel.h>
#include <Wire.h>
#include <VL53L0X.h>

Peeling the onion, I guess.

This is the pi running with 2 arduinos hooked up in a round robin network via rx and tx. In the first image, the pi pings the arduinos to see how many are there. The pi receives 02 and the pi generates the string to be read by the arduinos. Do not worry about the garbled text as that is ASCII chars representing RGB values (128 values are enough for my project). The real issue is in the second image, where all of a sudden the pi does not receive any signal, and the arduinos seem to freeze about 10 seconds with the LED lights on, then suddenly the lights switch off and the ping succeeds. Is this a memory issue, a buffer issue or maybe serial issue?

This code works seamlessly without freezing if I comment out the strip.show(), which means it's the source of this problem.

I say again, the garbled text is CORRECT and it is NOT the issue.


Image 1.


Image 2.

Pi code:

import serial
import datetime
import time
import sys
# RGB values for brown
colour=[254,50,0]

#
arduinos=0


# Returns the values from the string sent by the arduino
def parseString(str):

    sensor = str[0:(arduinos)]
    rgb = str[(arduinos)+1:len(str)]
    print("Sensor: %s" % sensor)
    print("RGB: %s" % rgb)
    return sensor,rgb
    
# Adjusts the RGB values retrieved from the arduino with the new values from sensor data
def newSignal(sensor, rgb):
    st = ""
    i = 0
    for s in sensor:
        changeColour(rgb)
        tmp_rgb = list(rgb)
        if ord(s)>= ord("c") and ord(s) <= ord('z'):
            for j in range(0, 3):
                if colour[j] == 0:
                    tmp_rgb[i*3+j] = chr(1)
    
                else:
                    tmp_rgb[i*3+j] = chr(colour[j]//2)
        else:
            for j in range(0, 3):
                tmp_rgb[i*3+j] = 'a'
        rgb = ''.join(tmp_rgb)
        i+=1
          
    return sensor + "%" + rgb


# Gets index from ping
def ping(port):
    print "Pinging..."
    ok=False
    while not ok:
        try:
            port.write("<00>")
            recv= port.read(4)
            recv=recv[1:-1]
            print "Ping: "+recv
            return int(recv)
        except:
            time.sleep(1)
            pass
    

# Gets the range from the sensor and turns the ascii character into an RGB value
def calc_rgb(sensor_vals):
    st=""
    for s in sensor_vals:
        if s is 'a' or '{':
            st+="aaa"
        else:
            if ord(s)-97<10:
                st+="0"+str(ord(s)-97)
            else:
                st+=str(ord(s)-97)

    return st

# Generates a test string       
def create_string(index,sensor_vals):
    spaga=""
    for i in range(index):
        spaga+="a"
    spaga+="%"
    spaga+=calc_rgb(sensor_vals)
       
    return spaga



# Uses the above classes to generate the I/0 signal
def main_loop(port,out):
    s=False
    t0 = datetime.datetime.now()
    t2=datetime.datetime.now()
    t1=datetime.datetime.now()
    s=""
    s1=""
    t1=datetime.datetime.now()
    print("Pre IO: "+ str((t1-t0).total_seconds()*1000)+"ms")
    port.write("<"+out+">")
    print("write IO: "+ str((t1-t0).total_seconds()*1000)+"ms")
    s = port.readline()
    s=s[1:-1]
    t2=datetime.datetime.now()
    print ("Received signal: "+s)
    if not len(s)==len(out):
        arduinos = ping(port)
        s=out
    # Get index, sensor and rgb values
    senso, rgb = parseString(s)
    newstr= newSignal(senso, rgb)
    print("New signal: %s" % newSignal(senso, rgb))

    # Displays the time taken for the IO to be read and total times
    print("read IO: "+ str((t2-t1).total_seconds()*1000)+"ms")
    print("End: "+ str((datetime.datetime.now()-t2).total_seconds()*1000)+"ms")
    print("Total: "+ str((datetime.datetime.now()-t0).total_seconds()*1000)+"ms")
    return newstr

# Serial import, definition, opening and closing ports & getting number of Arduinos attached
if __name__ == "__main__":
    try:
        with serial.Serial('/dev/ttyAMA0', baudrate=115200,rtscts=True, dsrdtr=True, timeout=0.05) as port:
            
            arduinos = ping(port)
            ss=""
            for j in range(arduinos):
                ss+="a"
            print str(arduinos)+" Arduino(s)"
            s1= create_string(arduinos,ss)

            while(1):

                print "-------------------------------------------------------------------------"
                print "generated signal: "+ s1
                s1=main_loop(port,s1)


    except serial.SerialException:
        port.close()
        port.open()
    except Exception as e:
        print(e)