2
\$\begingroup\$

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 def read_data(): data = [] start_time = time.time() arduino.write(b'S') time.sleep(360.0 - ((time.time() - start_time) % 360.0)) while True: temp = arduino.read().decode(encoding='UTF-8') if temp == "*": while True: temp = arduino.read().decode(encoding='UTF-8') 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() data_returned = read_data() # post-process data_returned here 
\$\endgroup\$
3
  • \$\begingroup\$What level of timing precision are you aiming for?\$\endgroup\$CommentedMar 24, 2016 at 18:23
  • \$\begingroup\$@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.\$\endgroup\$
    – Nogurenn
    CommentedMar 24, 2016 at 18:35
  • \$\begingroup\$Welcome to Code Review! Good job on your first question.\$\endgroup\$
    – SirPython
    CommentedMar 24, 2016 at 19:43

1 Answer 1

2
\$\begingroup\$
  • 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 != '*': temp = arduino.read()... while temp != '#': temp = arduino.read()... data.append(temp) return data[:-1] 
  • I am not sure why do you sleep() before reading the response.

\$\endgroup\$
4
  • \$\begingroup\$Clarification: the 'select.select()' would replace the lines inside the 'wait_until_sampling()' function? As for the sleep, I have to wait for the data to be written to the serial port since the Arduino will take samples over a 5-minute interval after receiving the command, and send the averaged results.\$\endgroup\$
    – Nogurenn
    CommentedMar 25, 2016 at 2:30
  • 1
    \$\begingroup\$@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.\$\endgroup\$
    – vnp
    CommentedMar 25, 2016 at 3:03
  • \$\begingroup\$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?\$\endgroup\$
    – Nogurenn
    CommentedMar 25, 2016 at 4:12
  • \$\begingroup\$@Nogurenn You got it right.\$\endgroup\$
    – vnp
    CommentedMar 25, 2016 at 5:32

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.