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.
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?
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, 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
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.
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();
}
you should not be managing interrupts, the library does that for you
you certainly don't need to manage interrupts to modify the pixel buffer:
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...
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.
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.
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)