RPi script to periodically collect data from Arduino through serial port

I'm not used to Python.

The script opens serial communications with an Arduino unit and send commands to and receives information from the Arduino. It is intended to run indefinitely until the system is shut down or pulled out of field deployment/laboratory testing.

The time.sleep() calls with offsets are assurances that repeated calls won't eventually result to time drifts, as mentioned in this Stack Overflow Q/A. I don't trust myself with this. I need confirmation that I placed the start_time = time.time() lines in proper locations. This time drift issue is crucial because the script shall run for months.

read_data() sends a command to the Arduino, which tells it to give the Pi new data. Data sets written by the Arduino vary in length, so I can't use serial.read(expected_bytecount). Data sent between start and stop bytes (e.g. *data1,data2,data3#) is always valid, so I don't need to do checks there. Is my algorithm here enough for my task?

wait_until_sampling() keeps the Pi from being busy around the clock. Reading data occurs every 15th and 45th minutes of an hour. I thought of using minutes % 15 != 0, but there are the 0th and 30th marks. I also tried doing away with datetime but I can't get the minutes of the system time from time. Is there a simple way to improve this algorithm by dropping datetime?

import datetime
import time
import serial

data = []
start_time = time.time()
arduino.write(b'S')
time.sleep(360.0 - ((time.time() - start_time) % 360.0))
while True:
if temp  == "*":
while True:
if temp == "#":
break
else:
data.append(temp)
break
return data

def wait_until_sampling():
start_time = time.time()
dt_now = datetime.datetime.now().time()
while (dt_now.minute != 45) and (dt_now.minute != 15):
time_now = time.time()
dt_now = datetime.datetime.now().time()
time.sleep(1.0 - ((time_now - start_time) % 1.0))

arduino = serial.Serial('/dev/ttyACM0', 9600)
time.sleep(20.0)
data_returned = []
while True:
wait_until_sampling()
# post-process data_returned here

• What level of timing precision are you aiming for? – 200_success Mar 24 '16 at 18:23
• @200_success Not entirely sure how to answer that, but I'd like to keep up to the seconds precision intact since I'm checking for the 15th and 45th minute-marks for each second passed. Knowing that the script will run for months, I worry that the sub-seconds will eventually spill drifts to the seconds and upwards. – Nogurenn Mar 24 '16 at 18:35
• Welcome to Code Review! Good job on your first question. – SirPython Mar 24 '16 at 19:43

• A simple technique to avoid busy wait is to

import select
....
select.select([], [], [], timeout)


The timeout is simply target_time - current_time in seconds. Since select interprets it as a floating point, the granularity is in milliseconds at least.

I see no possibility for time drift.

• The nested loops in read_data do not look right. Consider instead

def read_data():
data = []

temp = None
while temp != '*':

• I am not sure why do you sleep() before reading the response.
• @Nogurenn Yes, wait_until_sampling() becomes timeout = compute_timeout(); select.select([], [], [], timeout);. As for sleep, the read() is blocking and your program wouldn't proceed until Arduino replies - with sleep you are only creating a chance of serial overrun. Both select and read end up in system calls, letting your system do other things. – vnp Mar 25 '16 at 3:03
• If I understand this correctly, I don't have to sleep() because the program already just waits for valid input? I intended for the RPi to not be busy while waiting for the ~5-minute sampling time of the Arduino. Can I just call another select.select(), or read as you said will let my system do other things? – Nogurenn Mar 25 '16 at 4:12