pyserial

Pyserial reads empty byte before reading in serial data


I have an Arduino pumping out the characters 0 - 9 through a serial port

void setup() {

  Serial.begin(9600);

}

void loop() {

  for(int i = 0; i < 10; i++){

    Serial.print(i);
    delay(200);

  }

}

I'm using the following Python code to recieve the data on a PC,

import serial

usbport = 'COM3'
ser     = serial.Serial(usbport, 9600, timeout=1)

count = 0
while ser.in_waiting < 1 and count < 5:
    
    p = ser.read(10)
    p = p.decode()
    print(p, type(p))
    count += 1
    
ser.close()

The Arduino will reset whenever a serial communication is initiated (which is fine in this case) but I've notced that the Python code always picks up an empty byte prior to reading the serial data.

Example output,

 <class 'str'>
0123456789 <class 'str'>
0123456789 <class 'str'>
0123456789 <class 'str'>
0123456789 <class 'str'>

It always does this, anybody know why?

Also for a bonus point, can anyone tell me why I have to put while ser.in_waiting < 1 to get this to work & not just while ser.in_waiting?


Solution

  • The empty byte is because of the connecting with the serial port. Also generally, it would be much better if you add some code to your arduino, such that it starts the process when a command is sent:

    void setup() {
      Serial.begin(9600);
    }
    
    void loop() {
      // Wait for a command from the serial port
      while (!Serial.available()) {
        // Do nothing until a command is received
      }
    
      // Read the command from the serial port
      String command = Serial.readStringUntil('\n');
    
      // Check if the received command is "start"
      if (command == "start") {
        // Start sending numbers 0-9
        for (int i = 0; i < 10; i++) {
          Serial.print(i);
          delay(200);
        }
      }
    }
    

    In this case, when you write "start\n".encode() to the arduino port, it will start the process of looping, once.

    Furthermore, I usually flushInput and flushOutput before using the port, specifically to avoid such things:

    usbport = 'COM3'
    ser = serial.Serial(usbport, 9600, timeout=1)
    time.sleep(1) # sleep to give enough time for the arduino to initialize
    ser.flushInput() # flush input
    ser.flushOutput() # flush output
    

    And now, you can use your code, with the addition of writing the command:

    count = 0 # init count
    ser.write("start\n".encode()) # write the command to start
    while ser.in_waiting < 1 and count < 5:
        p = ser.read(10)
        p = p.decode()
        print(p, type(p))
        count += 1
    ser.close()
    

    Also, regarding your last question, using ser.in_waiting < 1, you are cchecking if there is less than one byte in the input buffer, which is equivalent to checking if the buffer is empty. I.e. it would be much more explicite if you just check !ser.in_waiting.