views:

3964

answers:

6

So I have an Arduino connected to my Windows XP computer. It's just running a loop, sending a value over the serial port back to the computer every 100ms.

I want to make a Python script that will read from the serial port only every few seconds, so I want it to just see the last thing sent from the Arduino.

How do you do this in Pyserial?

Here's the code I tried which does't work. It reads the lines sequentially.

import serial
import time

ser = serial.Serial('com4',9600,timeout=1)
while 1:
    time.sleep(10)
    print ser.readline() #how to get most recent line sent from device?
+1  A: 
from serial import *
from threading import Thread

last_received = ''

def receiving(ser):
    global last_received
    buffer = ''

    while True:
        # last_received = ser.readline()
        buffer += ser.read(ser.inWaiting())
        if '\n' in buffer:
            last_received, buffer = buffer.split('\n')[-2:]

if __name__ ==  '__main__':
    ser = Serial(
        port=None,
        baudrate=9600,
        bytesize=EIGHTBITS,
        parity=PARITY_NONE,
        stopbits=STOPBITS_ONE,
        timeout=0.1,
        xonxoff=0,
        rtscts=0,
        interCharTimeout=None
    )

    Thread(target=receiving, args=(ser,)).start()
mtasic
Well that reads in the sum total of what's in the receive buffer. My impression is the asker is delimiting what the arduino is sending by newlines so it probably won't match the receive buffer size.
JosefAssad
So last_received will always have what I need? Is there a way to do it with readline?
Greg
thats right, according to your question
mtasic
See my updated answer, `mtasic`'s code looks good apart from what I think is one little glitch.
Vinay Sajip
Your update is almost right. It sets a blank line if the buffer ends in a newline. See my further answer update.
Vinay Sajip
thanks so much for pointing it out, actually it is your answer ;)
mtasic
The code is good, just a quick comment. interCharTimeout is missing from older versions of python. You can omit that line and it should work fine on Python 2.5 or older.
Great Turtle
+7  A: 

Perhaps I'm misunderstanding your question, but as it's a serial line, you'll have to read everything sent from the Arduino sequentially - it'll be buffered up in the Arduino until you read it.

If you want to have a status display which shows the latest thing sent - use a thread which incorporates the code in your question (minus the sleep), and keep the last complete line read as the latest line from the Arduino.

Update: mtasic's example code is quite good, but if the Arduino has sent a partial line when inWaiting() is called, you'll get a truncated line. Instead, what you want to do is to put the last complete line into last_received, and keep the partial line in buffer so that it can be appended to the next time round the loop. Something like this:

def receiving(ser):
    global last_received

    buffer = ''
    while True:
        buffer = buffer + ser.read(ser.inWaiting())
        if '\n' in buffer:
            lines = buffer.split('\n') # Guaranteed to have at least 2 entries
            last_received = lines[-2]
            #If the Arduino sends lots of empty lines, you'll lose the
            #last filled line, so you could make the above statement conditional
            #like so: if lines[-2]: last_received = lines[-2]
            buffer = lines[-1]

Regarding use of readline(): Here's what the Pyserial documentation has to say (slightly edited for clarity):

Be careful when using "readline". Do specify a timeout when opening the serial port, otherwise it could block forever if no newline character is received. Also note that "readline" only works with a timeout. It depends on having a timeout and interprets that as EOF (end of file).

which seems quite reasonable to me!

Vinay Sajip
you are absolutely right, good point
mtasic
thanks, that works great! I gave you the answer button.
Greg
+1  A: 

You will need a loop to read everything sent, with the last call to readline() blocking until the timeout. So:

def readLastLine(ser):
    last_data=''
    while True:
        data=ser.readline()
        if data!='':
            last_data=data
        else:
            return last_data
quamrana
A: 

Slight modification to mtasic & Vinay Sajip's code:

While I found this code quite helpful to me for a similar application, I needed all the lines coming back from a serial device that would send information periodically.

I opted to pop the first element off the top, record it, and then rejoin the remaining elements as the new buffer and continue from there.

I realize that this is not what Greg was asking for, but I thought it was worth sharing as a side note.

def receiving(ser):
    global last_received

    buffer = ''
    while True:
        buffer = buffer + ser.read(ser.inWaiting())
        if '\n' in buffer:
            lines = buffer.split('\n')
            last_received = lines.pop(0)

            buffer = '\n'.join(lines)
Crazy Joe Malloy
A: 

These solutions will hog the CPU while waiting for characters.

You should do at least one blocking call to read(1)

while True:
    if '\n' in buffer: 
        pass # skip if a line already in buffer
    else:
        buffer += ser.read(1)  # this will block until one more char or timeout
    buffer = buffer + ser.read(ser.inWaiting()) # get remaining buffered chars

...and do the split thing as before.

Rufus
+1  A: 

Hello I a beginner to python. I interfaced kuka robort to pc. My boss told me to communicate with it.comunnication is via RS232 I wrote a program in controller. It sarts only when i send a string "NEXT" in pc i wrote a small program in python to send 'NEXT'.

import serial

import time

ser=serial.Serial(port='COM1',baudrate=19200,parity='E',timeout=None, xonxoff=0, rtscts=0)

ser.open()

x=ser.write("NEXT")

ser.close()

Then my robot moves correspondly and transmits a character '01'.
I would like to know how to read a character transmitted by controller. I used the above program but no use. Could please help me in finding this....

thanks in advance...

ram