Python script not reading from serial unless arduino/teensy is rebooted

Hi all,
I want to send some analog sensor data from a Teensy 3.2 to a mac running a python script (I am using the usual pyserial module and I send data using Serial.print() in the arduino sketch). I am using Teensy but I guess the same would be true for an Arduino Uno.
The teensy is correctly sending on the serial port the data, as I can see it using the serial monitor. However, only the first time that I run the python script the data are handled by the script itself, if I stop the script then no data seem to be read in the serial buffer. To make the script work I necessarily have to re-launch the Arduino IDE and then upload the sketch.

Indeed the second time I run the script (after having stopped it), I can see that the pyserial function serial_port.inWaiting() does not give any data.

if (serial_port.inWaiting() > 0):
   print("serial_port.inWaiting() > 0") # does not print anything the second time I launch the script

I have checked that the second time that I launch the script the serial connection is open (using self.serial_port.isOpen())

What is preventing the python script to get the serial data the second time? Why serial_port.inWaiting() is not seeing that there is data in the input buffer?

In the arduino sketch code I have added the line while(!Serial); in void setup()

when you open the Serial port by launching your script, you reboot your arduino. Is your code designed for this?

are you keeping a Serial Monitor open?

Hi,
no the code is not designed for handling the rebooting. What should I do? I am on a teensy 3.2 not on arduino though.

Of course I am not keeping the serial monitor open.

Building on reply #1, because the python script reboots the Arduino, it is customary for the Arduino to send some sort of “Hello, I’m alive” message in setup() which your python script should wait to receive. After it get this response, it can then expect the data to begin flowing (or use some sort of send/response protocol)

The Teensy 3.2 does NOT reboot when serial connects.

The problem is in the code you didn't post.

MorganS:
The Teensy 3.2 does NOT reboot when serial connects.

Good to know! I don't work with Teensies that much.

Sorry missed the information it was a Teensy. Indeed - Teensy is a native USB device which will connect / disconnect and not subject to the reboot behavior.

You need to handle the USB connect disconnect “properly” though in your Python code using a close() or the Serial port might remain in a weird state possibly.

Hi all, thanks. This is my arduino code:

int sensor = 0;

void setup() {
  Serial.begin(115200);
  while(!Serial);
}


void loop() {

  sensor = analogRead(0);
  Serial.print(sensor);
}

And this is the python code:

#!/usr/bin/env python3

import serial
import time
import signal
import sys


def keyboard_interrupt(signal, frame):
    serial_port.close()          
    time.sleep(2)
    exit(0)
        
              
serial_port = serial.Serial("/dev/tty.usbmodem1225061", 115200)

signal.signal(signal.SIGINT, keyboard_interrupt)
signal.signal(signal.SIGTERM, keyboard_interrupt)
    
    
if serial_port.isOpen():
    print(serial_port.name + ' is open...')


while True:
    if (serial_port.inWaiting() > 0):
        sensor = serial_port.read().decode("utf-8") 
        print('sensor: "{}" '.format(sensor))

What is wrong?

I can't comment on the Python, but the Teensy code is easy for me...

void loop() {

sensor = analogRead(0);
  Serial.print(sensor);
}

This will print really, really fast. Much faster than you are expecting. Slow it down a little. You also need some kind of seperator between numbers otherwise you will just get 1231231231231231 with no idea where each number starts and ends. println() makes a good seperator.

void loop() {
  const unsigned long samplePeriod = 20; //milliseconds, make this number smaller for faster
  static unsigned long lastSample;

  if(millis() - lastSample > samplePeriod) {  
    lastSample = millis();
    sensor = analogRead(0);
    Serial.println(sensor);
  }

}[/code]

Also get into the habit of naming your pins early. In this context "0" is a "magic number", which is to be avoided.

Hum - long time I've not played with pySerial but sensor = serial_port.read().decode("utf-8")feels weird as read()will only read 1 byte if called with no parameter, not your integer value in ASCII

You need to pass a size, or use read_until() where you can pass your end of line character and a max number of bytes.

behavior changed between Python 2.x and 3 If I remember well in terms of what is returned. Check the doc and your Python version

Hi I have actually posted a very simplified version just to allow you to reproduce the problem and understand it. All your suggestions a very valuable but do not address the problem. There is something wrong in the inWaiting() function, the data seem not to be present in the buffer.... but still the teensy is actually sending data, and the serial connection has been opened by the python script.
Any idea?

what happens on your computer if you run this python:

 import serial
serialport = serial.Serial("/dev/tty.usbmodem1225061", 115200, timeout=0.5)
while True:    
    command = serialport.read()
    print str(command)

It prints:
b'8'
b'3'
b'9'
b'8'
b'3'
b'9'
b'8'
b'4'
b'9'
b'8'
b'4'

And when I do CTL+C the python script restarts correctly.

Why in_waiting is returning 0 after the script is launched the second time? Any idea?

because no-one is buffering the Serial communication on the PC side if there is no-one opening this port and listening

using this loop in your Arduino code as proposed by MorganS

void loop() {
  const unsigned long samplePeriod = 200; //milliseconds, make this number smaller for faster
  static unsigned long lastSample;

  if(millis() - lastSample > samplePeriod) {  
    lastSample = millis();
    sensor = analogRead(0);
    Serial.println(sensor);
  }

and using this Python

import serial
serialport = serial.Serial("/dev/tty.usbmodem1225061", 115200, timeout=0.5)
while True:    
     line = serialport.readline()   
     print line

what do you get?

Sorry I don't really get your answer could you please give me some more details? When I relaunch the script I check that the serial connection is open (with the isOpen() function, which returns True). So the port is open. Now, the only way I got to have the in_waiting >0 is to place a serial_port.read(size=1) just after the creation of the serial connection. If I don't do this in_waiting will be always 0 the second time I launch the script. Do you think that this is a bug of the pyserial module? Or am I not fully understanding something here?

Sorry I don't really get your answer could you please give me some more details?

I'm asking you to test a code that is different than yours. there is no isOpen() or whatever. just the code I gave you

I was replying to what you wrote in the earlier post. The code you sent me work well. It prints:
3\r\n'
b'983\r\n'
b'983\r\n'
b'983\r\n'
b'983\r\n'
b'984\r\n'
b'983\r\n'
b'984\r\n'
b'983\r\n'

However, I don't intend to use that code (that is just a simplified version of what I am trying to do!), I do not use indeed readline(), but I want to use read() and I want to use in_waiting.

Any idea why I need to read() in order to unlock in_waiting the second time I launch the script?

read() will only read one byte, so you'll need to build a buffer to read the full number... readline would do that for you...

I already told you that nothing buffers Serial Data you throw at your PC if no program has an open Serial port attached to this serial port. So when you launch your Script, the buffer is always empty, wether it's the first time you launch it or not, in_waiting will be 0 (or whatever arrived between the time you opened the Serial port and tested in_waiting)