0

I'm working on some code to read serial data from an Arduino. I'd like to wait for a serial data to come in, give an input in the terminal, then continue waiting for the next serial input or for the user to exit loop.

def assign_values(name, serial_connection):
    print(f"Waiting for signal from {name}...")
    try:
        dict = {}
        while True:
            key = serial_connection.readline().decode("utf-8").strip()
            if key and key not in dict:
                value = input(f"Detected {key}: ")
                dict[key] = value
    except KeyboardInterrupt as k:
        return dict

The above code works but has some problems that I'd like to clean up. First off, if I give multiple serial inputs before a user input, they get backed up. I'd rather it just ignore anything coming in while the user is typing their own input. The same problem also happens in reverse. Any user input is tracked while waiting for the serial data and immedietly populates the terminal when calling input(). Also I feel like the try -> while True isn't the best way to do this, but I can't find any other way to achieve my goal.

In case it is relevant, here is the Arduino code:

#include <IRremote.h>
#define IR_RECEIVE_PIN 7

void setup() {
  Serial.begin(9600);
  IrReceiver.begin(IR_RECEIVE_PIN);
}

void loop() {
  if (IrReceiver.decode()) {
    IrReceiver.resume();
    int cmd = IrReceiver.decodedIRData.command;
    Serial.println(cmd);
  }
}
Zelkins
  • 723
  • 1
  • 5
  • 22
  • 1
    Don't use the names of [built-ins](https://docs.python.org/3/library/functions.html) like `dict` as variable names. It shadows the built-in and can lead to problems later if you try to use the function. – MattDMo Aug 07 '22 at 04:18
  • The try/except to let a keyboard interrupt stop the loop is good. No need to change that. – tdelaney Aug 07 '22 at 04:43

1 Answers1

1

The serial inputs get backed up because they are in the buffer. So you will need to flush it and do a readline() to get only the latest input.

def assign_values(name, serial_connection):
    print(f"Waiting for signal from {name}...")
    try:
        dict = {}
        while True:
            serial_connection.flush_input() # this won't work, see the update
            key = serial_connection.readline().decode("utf-8").strip()
            if key and key not in dict:
                value = input(f"Detected {key}: ")
                dict[key] = value
    except KeyboardInterrupt as k:
        return dict

If you don't want to use try and while True to detect a keyboard interrupt to quit, then you could ask the user to type quit() to quit the program. Then do a check and break.

value = input(f"Detected {key}: ")
if value == "quit()":
    break

Update

  1. Take only the latest serial input: It is actually flushInput() and since version 3.0 it has been renamed to reset_input_buffer() as pointed out by @Zachary Elkins.
serial_connection.reset_input_buffer()
key = serial_connection.readline().decode("utf-8").strip()
  1. Ignore user input before input(): You can try using msvcrt for windows or termios to flush the input as shown in this answer.
  2. Quit program without KeyboardInterrupt: With pynput you could attach a listener that listens for a particular key like escape, then quits the program as shown here. For example:
from pynput import keyboard

keep_running = True

def flush_input():
    try:
        import msvcrt
        while msvcrt.kbhit():
            msvcrt.getch()
    except ImportError:
        import sys, termios    #for linux/unix
        termios.tcflush(sys.stdin, termios.TCIOFLUSH)

def on_press(key):
    global keep_running
    if key == keyboard.Key.esc:
        keep_running = False
listener =  keyboard.Listener(on_press=on_press)
listener.start()

def assign_values(name, serial_connection):
    print(f"Waiting for signal from {name}...")
    dict = {}
    while keep_running:
        serial_connection.reset_input_buffer()
        key = serial_connection.readline().decode("utf-8").strip()
        if key and key not in dict:
            flush_input()
            value = input(f"Detected {key}: ")
            dict[key] = value
    return dict
viggnah
  • 1,709
  • 1
  • 3
  • 12
  • `flush_input()` is actually `flush()` but neither work, the solution ended up being `reset_input_buffer()` rid of the try/except, but that wouldn't let the user exit unless they first give a serial input. – Zelkins Aug 07 '22 at 04:34
  • @Zelkins, sorry for the inaccuracies in my answer. I have updated the answer with your inputs. Also, included a couple of other things to flush user input, and quit on keyboard press instead of keyboard interrupt. Please see if it helps! Thanks – viggnah Aug 07 '22 at 06:04