Arduino serial communication python unstable

Hi, I've been testing arduino communication with serial.
during running it always connected , buy sometime the read is incorrect
python in run in version 3.9 in Debian 11

I'm using aioserial which is a asyncio wrapper for pyserial
This is the python code

import serial
import aioserial
import asyncio
import time
import sys
import logging


class SerialListener:
    def __init__(self, port="/dev/ttyUSB0", baudrate=9600):
        self._port = port
        self._baud_rate = baudrate
        self._run = True
        self._serial = None
        self._logger = logging.getLogger()

    def disconnect(self):
        self._logger.info("Cleanup serial {}".format(self._port))
        if self._serial is not None:
            try:
                self._serial.close()
            except:
                self._logger.info(sys.exc_info())
        self._serial = None

    def reconnect(self):
        self.disconnect()

        while self._serial is None:
            try:
                self._serial = aioserial.AioSerial(
                    port=self._port, baudrate=self._baud_rate,
                )
                self._logger.info("Connected to {}".format(self._port))
            except serial.serialutil.SerialException:
                self._logger.info("{} not ready".format(self._port))
            except:
                self._logger.info(sys.exc_info())
            time.sleep(1)

    async def listen(self):
        while self._run:
            try:
                data = await self._serial.read_until_async()
                code = data.decode(errors="ignore").strip()
                self._logger.info("Receive {}".format(code))
            except asyncio.CancelledError:
                break
            except serial.serialutil.SerialException:
                # Blocking call
                self.reconnect()
            except AttributeError:
                self.reconnect()
            except:
                self._logger.info(sys.exc_info())
            await asyncio.sleep(0.1)

    async def shutdown(self):
        self._run = False
        await asyncio.sleep(1)

def main():
    logging.basicConfig(
        level=logging.DEBUG,
        format="%(asctime)s %(levelname)s %(name)s : %(message)s",
        handlers=[logging.StreamHandler(sys.stdout)],
    )
    logger = logging.getLogger()

    serial_listener = SerialListener(port="/dev/ttyUSB0", baudrate=9600)

    loop = asyncio.get_event_loop()

    try:
        asyncio.ensure_future(serial_listener.listen())
        loop.run_forever()
    except asyncio.CancelledError:
        logger.info("Receive Cancel")
    except KeyboardInterrupt:
        logger.info("Receive Keyboard Interrupt")
    except:
        logger.info("Unknown Error")
        logger.info(sys.exc_info())
    finally:
        logger.info("Stop application")

    asyncio.ensure_future(serial_listener.shutdown())
    serial_listener.disconnect()

if __name__ == "__main__":
    main()

This is arduino part

const int buttonPin = 4;

int buttonState = 0;
int value = 0;

void setup() {
  Serial.begin(9600);
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop() {
  buttonState = digitalRead(4);

  if (buttonState == HIGH) 
  {
    value = 0x01;
  } else if (buttonState == LOW) 
  {
    value = 0x00;
  }

  if (Serial.available()) {
    // Sending value to python
    Serial.println(value);
  }

  delay(500);
}

It is very simple code, basically the arduino will send a value 0x01 when button is press and 0x00 when not every .5 second

When starting the python script, if the delay is .5 second ( set in arduino ) then all communication is responsive. But if the delay around .1s ( python kept printing without delay ) all the command from arduino are not sent correctly.

Testing using this method , all not consistent

  • Unplug the usb, start the python script , then plug the usb.
  • start the python script , replug the usb
  • usb kept plug, start and stop ( ctrl+c ) the script
  • running on separate terminal
  • rerun after fuser kill -TERM /dev/ttyUSB0

Testing same script in windows 10 with port COM3 ( using pyserial ) the problem doesn't seem to occur

Any idea what the cause ?

what do you expect Serial.available()) to be ?

(opening the Serial line reboots the arduino so sending data from Python to the Arduino immediately after open the port won't work, the Serial hardware is not configured yet on the other side )

I have this python example (it's in French but the code should be easy to read and google translate does an OK job on it) that uses queues to handle the communication. so far it has proved to be robust for me

what do you expect Serial.available()) to be ?

the previous version doesn't have this , but the problem still there. So I test whether this can help

(opening the Serial line reboots the arduino so sending data from Python to the Arduino immediately after open the port won't work, the Serial hardware is not configured yet on the other side )

Is it arduino side or python side ?

Is there a way to check on both side if it is ready ?

when python (or anything on your PC) opens the serial line, the arduino reboots

the python code is "always ready", what I do in the tutorial I posted is that the Arduino sends a message at the end of the setup() to say it's configured and ready to loop(). that's the cue for the python code to get really started

this is checking if python sent something to the Arduino (ie if there is something available to read), so if you did not sent anything, you'll never make it into the if.

Ok it works now, only tweak in arduino part.
The python will kept listening

void setup() {
  Serial.begin(9600);
  // This is required, empty string is ok
  Serial.println();
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop() {
   ...
   // comment out checking Serial.available()
   // if (Serial.available()) {
   Serial.println(value);
   // }
   ...
}

Depends on the board :wink: Boards with native USB don't reset when the port is opened.

Glad you got it going but this bit of code is very clumsy:-

If the button state is not HIGH then it has to be low so the

Is not required.

In fact the whole piece of code I posted could be written as just:-

value = 0x00;
 if (buttonState == HIGH) value = 0x01;

fair point indeed, I should have been more precise

Ah yes, there is actually another value need to be check instead of just LOW HIGH button. I forget to clean it up.

I updated the setup() to send OK and in python if not receive "OK" value then it will kept trying to reconnect.

It looks like there is problem with some arduino nano chip ( made in china ). Either the hardware broken or chip version problem. Still testing the hardware.

Sorry that doesn't make any sense. A LOW is the value of zero, a HIGH is any value that is not zero, so you can't check for other numbers once you start using HIGH.

Makes sense.

Hum, if the usb cable is there a the port activated then opening the connection will be successful. The Python code then needs to wait (not try to open again) until it’s gets the OK, indicating that the arduino has finished booting up.


@wpsd2006 have you tried the code in my python example?