Servo controller 2040: reading user button and sys.stdin within a loop

What I want to achieve:

  1. I want to send commands via a serial port over USB to the servo 2040 controller. This works.
  2. I want to execute a servo sequence when the user button is pressed. This also works
  3. I want to do both within a while loop so that if the USB port is not connected I can press the user button to play a sequence but if the USB port is connected I can also upload servo sequences.

Problem: putting both into a single “while True” loop doesn’t work. The system won’t read the button press. The serial commands work well.

Description:
I have a while True loop that checks:

user_sw = Button(servo2040.USER_SW)
via if user_sw.read()

as given in Pimoroni micropython examples. This also works by itself.

I also listen to the sys.stdin, i.e. to a serial connection via USB to read commands from the PC.
This also works:

inCommand = sys.stdin.readline().strip()

But if both, the .read() command and the .readline() command are in one while True loop,
the button presses are not registered. The serial commands are still read. Commenting out the
serial commands will make the user button read work.

Can’t explain. Would love to either have this fixed or some alternative, e.g. not putting both in one loop.
Any ideas?

Piece of code given in attached image

I assume readline blocks (waits for data). You should check the uart if there is something to read, and if not, just carry on. Not using readline makes things a bit more complicated, but I don’t think you have a choice.

1 Like

Sounds plausible.
And does UART automatically refer to the USB serial port?
Or could I use the serial library? Like so:

import serial
ser = serial.Serial('/dev/ttyACM0', 9600, timeout=0.050)
...
while ser.available():
    print ser.readline()

This code looks like normal CPython. I don’t think this works on a microcontroller (no filesystem, no device files like /dev/ttyACM0).

I would first try

sys.stdin.any()

assuming that sys.stdin actually is an UART. (any() should return the number of available bytes). If this does not help, here is an alternative (copied from the raspberry-pi forum, I use very similar code on normal Linux to prevent blocking when reading from serial lines):

import micropython
import select
import sys
micropython.kbd_intr(-1)
while True:
  while sys.stdin in select.select([sys.stdin], [], [], 0)[0]:        
    ch = sys.stdin.read(1)
    print("Got " + hex(ord(ch)))

Select is rather powerful (as you can see, it has many strange arguments). I think you could also use readline once your within the second while loop, but this needs verification.