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);
}
}
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
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()
input()
: You can try using msvcrt
for windows or termios
to flush the input as shown in this answer.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