gryffin13
Posts: 9
Joined: Tue May 30, 2017 3:42 am

Help reading in serial strings into pi from arduino?

Fri May 24, 2019 3:33 am

I have an arduino that is sending a serial string to my pi based on the status of 4 different sensors. How I have it set up right now is that the arduino is sending a 4 character string based on which sensors are on. T for on, F for off. So it might send something like "FTFF". Then I have the pi reading that string and displaying an image based on those values. Even though I feel confident this setup is not the most elegant/efficient/correct method, it seems to work. But here's the problem I'm now running into:

If the sensor input changes (e.g. from FTFF to FTTF) I want to change which image the pi displays. This works. But if the sensor input doesn't change in the time it takes to loop through my function, i just want it to keep the existing image displayed. They way it is working right now, it will close out the image and reopen it. I can't figure out where the issue is exactly. But I've tracked the issue down to the fact that it seems like each loop it seems to think that it didn't get a serial input (initially), so it enters the "close image" else clause before getting the serial input. I think this is related to having/not having a buffer for serial set up correctly, but I don't understand the serial read well enough. Any help would be much appreciated. Code below, let me know if I left any important details out. Thanks!

Code: Select all

def touchPic (sensor1, sensor2, sensor3, sensor4, sensorCase, picPath):
	import os
	import time
	import serial

#check sensor values
	if sensor1 and sensor2 and sensor3 and sensor4:
		if sensorCase != 1: #only enter the image open function if status is different than previous status
			os.system("sudo pkill fbi")
			os.system("sudo fbi -a -T 2" + picPath + "testpic" + "all" + ".png")
			sensorCase = 1
	elif sensor1 and sensor2 and sensor3 and not sensor4:
		if sensorCase != 2:
			os.system("sudo pkill fbi")
			os.system("sudo fbi -a -T 2" + picPath + "testpic" + "123" + ".png")
			sensorCase = 2
	elif sensor1 and sensor2 and sensor4 and not sensor3:
		if sensorCase != 3:
			os.system("sudo pkill fbi")
			os.system("sudo fbi -a -T 2" + picPath + "testpic" + "124" + ".png")
			sensorCase = 3
#... continue for all cases...


	time.sleep(.3)
	picPath = " /home/pi/Pictures/"
	ser = serial.Serial('/dev/ttyUSB0', 9600)

	if (ser.in_waiting > 0): #read the serial data from arduino. Set cases based on T/F inputs
		line = str(ser.readline().strip())
		sensor1status = line[0]
		sensor2status = line[1]
		sensor3status = line[2]
		sensor4status = line[3]
		if sensor1status == "T":
			sensor1 = True
		elif sensor1status == "F":
			sensor1 = False
		if sensor2status == "T":
			sensor2 = True
		elif sensor2status == "F":
			sensor2 = False
		if sensor3status == "T":
			sensor3 = True
		elif sensor3status == "F":
			sensor3 = False
		if sensor4status == "T":
			sensor4 = True
		elif sensor4status == "F":
			sensor4 = False
		if sensor1 or sensor2 or sensor3 or sensor4:
			(sensor1, sensor2, sensor3, sensor4) = escapeFunctions.touchPic(sensor1, sensor2, sensor3, sensor4, sensorCase,picPath)
	else: #if no serial data to read, then close out of the image, set all values to false, and exit function
		#code seems to run this every loop even though serial data should be coming in constantly...
		os.system("sudo pkill fbi")

		time.sleep(5)
		sensorCase = 0
		sensor1 = False
		sensor2 = False
		sensor3 = False
		sensor4 = False




	return (sensor1, sensor2, sensor3, sensor4)
	#time.sleep(1)
	#os.system("sudo pkill fbi")

ghp
Posts: 1420
Joined: Wed Jun 12, 2013 12:41 pm
Location: Stuttgart Germany
Contact: Website

Re: Help reading in serial strings into pi from arduino?

Fri May 24, 2019 6:21 pm

This code is only a part of the system and it is almost impossible to help without the full picture.
A few remarks are possible:
- in the function, the serial line is opened, but never closed. On the other side, as it is never known when the arduino is sending, the line should be kept open.
- instead of

Code: Select all

        sensor1status = line[0]
        if sensor1status == "T":
            sensor1 = True
        elif sensor1status == "F":
            sensor1 = False

you can write

Code: Select all

        sensor1 =  line[0] == 'T'
which makes the code better readable.

There is a common pattern for problems where data are polled continuously from a serial device and processing is taking longer time: Use a queue to separate the reading of data and the processing of data.
- read data in a loop from the serial device. Do this in a thread. Pack the result data sensor1, sensor2, etc together and put them into a queue. You can use an array, json or whatever is appropriate. Perhaps you want to add a timestamp, when not sending for some time is an important case.
- read the result data from the queue. Do this in another loop, run this in main code or in a second thread.
- In this second loop, you can check if data have changed and process them accordingly. Something like

Code: Select all

last_sensor1 = None
while True:
    # ... read from queue, say an array [ sensor1, sensor2, .... .]
    events = queue.get()
    
    sensor1 = events[0]
    
    if last_sensor1 != sensor_1:
        if sensor_1:
            print("sensor 1 active")
        else:
            print("sensor 1 now inactive")
        last_sensor1 = sensor_1

     

User avatar
scruss
Posts: 2615
Joined: Sat Jun 09, 2012 12:25 pm
Location: Toronto, ON
Contact: Website

Re: Help reading in serial strings into pi from arduino?

Fri May 24, 2019 7:51 pm

You should also have a timeout in your serial open command. You program may try to wait forever without one.
‘Remember the Golden Rule of Selling: “Do not resort to violence.”’ — McGlashan.

gryffin13
Posts: 9
Joined: Tue May 30, 2017 3:42 am

Re: Help reading in serial strings into pi from arduino?

Fri May 24, 2019 8:24 pm

scruss wrote:
Fri May 24, 2019 7:51 pm
You should also have a timeout in your serial open command. You program may try to wait forever without one.
Valid point. I've added that to my code.

gryffin13
Posts: 9
Joined: Tue May 30, 2017 3:42 am

Re: Help reading in serial strings into pi from arduino?

Fri May 24, 2019 8:30 pm

ghp wrote:
Fri May 24, 2019 6:21 pm
A few remarks are possible:
- in the function, the serial line is opened, but never closed. On the other side, as it is never known when the arduino is sending, the line should be kept open.
- instead of

Code: Select all

        sensor1status = line[0]
        if sensor1status == "T":
            sensor1 = True
        elif sensor1status == "F":
            sensor1 = False

you can write

Code: Select all

        sensor1 =  line[0] == 'T'
which makes the code better readable.

There is a common pattern for problems where data are polled continuously from a serial device and processing is taking longer time: Use a queue to separate the reading of data and the processing of data.
- read data in a loop from the serial device. Do this in a thread. Pack the result data sensor1, sensor2, etc together and put them into a queue. You can use an array, json or whatever is appropriate. Perhaps you want to add a timestamp, when not sending for some time is an important case.
- read the result data from the queue. Do this in another loop, run this in main code or in a second thread.
- In this second loop, you can check if data have changed and process them accordingly. Something like

Code: Select all

last_sensor1 = None
while True:
    # ... read from queue, say an array [ sensor1, sensor2, .... .]
    events = queue.get()
    
    sensor1 = events[0]
    
    if last_sensor1 != sensor_1:
        if sensor_1:
            print("sensor 1 active")
        else:
            print("sensor 1 now inactive")
        last_sensor1 = sensor_1

     
I appreciate all the info. Some of it is hard for me to wrap my head around especially since I'm not near my computer right now. I will read this in detail later and see if I can implement any of this into my function. Thanks for taking the time to respond

gryffin13
Posts: 9
Joined: Tue May 30, 2017 3:42 am

Re: Help reading in serial strings into pi from arduino?

Sat May 25, 2019 3:32 am

ghp wrote:
Fri May 24, 2019 6:21 pm

- instead of

Code: Select all

        sensor1status = line[0]
        if sensor1status == "T":
            sensor1 = True
        elif sensor1status == "F":
            sensor1 = False

you can write

Code: Select all

        sensor1 =  line[0] == 'T'
which makes the code better readable.

/quote]

I spent a while looking at this trying to figure out how it did the same thing. I'm not great with python so this confused me a bit, but once I understood it, it is very compact. Thank you for that tip.

It sounds like for the other stuff that the solution is much more advanced than I was expecting. I was hoping that since the general function was working like I wanted that clearing out the one loop of closing a window wouldn't be much different. I've tried threading with a previous part of this project and it didn't go well. I will keep your comments in mind for the future as I keep learning more about python. Thanks.

ankith26
Posts: 103
Joined: Mon Mar 25, 2019 11:08 am
Location: Earth
Contact: Website

Re: Help reading in serial strings into pi from arduino?

Sat Jun 01, 2019 5:48 am

Code: Select all

from serial import Serial
from os import system # lets import only what we want

data = "" #initialisation(only for first loop)

while True:
    prevdata = data # lets put the data of prev loop in a new variable
    arduino = Serial('/dev/ttyACM0', 115200)
    #
    #this block gets data from arduino and waits for it
    running = True
    while running:
        data = arduino.readline().decode('ascii')
        if data != "":
            running = False
    #
    #this block assigns values to variables
    dat = data.strip()
    sensor1 =  dat[0] == 'T'
    sensor2 =  dat[1] == 'T'
    sensor3 =  dat[2] == 'T'
    sensor4 =  dat[3] == 'T'
    #
    #This displayes pics
    picPath = " /home/pi/Pictures/"
    if data != prevdata:# this is the important bit which makes sure the pic remains same if data is same        
        if sensor1 and sensor2 and sensor3 and sensor4:
            system("sudo pkill fbi")
            system("sudo fbi -a -T 2" + picPath + "testpic" + "all" + ".png")
        elif sensor1 and sensor2 and sensor3 and not sensor4:
            system("sudo pkill fbi")
            system("sudo fbi -a -T 2" + picPath + "testpic" + "123" + ".png")
        elif sensor1 and sensor2 and not sensor3 and sensor4:
            system("sudo pkill fbi")
            system("sudo fbi -a -T 2" + picPath + "testpic" + "124" + ".png")
        elif sensor1 and not sensor2 and sensor3 and sensor4:
            system("sudo pkill fbi")
            system("sudo fbi -a -T 2" + picPath + "testpic" + "134" + ".png")
        elif not sensor1 and sensor2 and sensor3 and sensor4:
            system("sudo pkill fbi")
            system("sudo fbi -a -T 2" + picPath + "testpic" + "234" + ".png")
        # more combinations posssible
Here you go, this code works perfectly for me and I got the code to do exactly what you want after 2 hrs of thinking
I have tried to made it compact and simple although i am not the best at it
My arduino sends data every second and i recieve it perfectly.
if you are interested, here is arduino code.

Code: Select all

void setup() {
  Serial.begin(115200); // begin transmission
}
void loop(){
  String sensor1,sensor2,sensor3,sensor4;
  String val;
  //default
  sensor1 = sensor2 = sensor3 = sensor4 = "F";
  //Do your sensor thing and give T,F value to A B C D
  val = sensor1 + sensor2 + sensor3 + sensor4 + '\n';// Absolutely vital to use \n at the end of the string
  Serial.print(val);// just use serial.print function
  delay(1000);
}
My website is at https://pratt.ml
Hope it runs (which it wont)

Return to “Python”