user1473877
user1473877

Reputation: 21

Bidirectional communication between microcontroller and pyserial

I am using Python 2.5.4 and Windows 7.

I am trying to create a program that transmits and receives info from a microcontroller using pygame. This code works fine when the microcontroller just writes data to pygame, but when the line is included in the microcontroller code that receives information (readline), the pygame window freezes (doesn't just close the window so I could see what the problem was). I was wondering whether anyone had any experience in a microcontroller and pygame/pyserial talking and listening to each other?

I have read a potentially similar post, but I did not understand the code and not certain if it is the same problem. I read on these forums the term 'flow control' and I am wondering whether this is my problem?

My code is:

import os, pygame, math, serial   
from pygame.locals import *
from pygame.compat import geterror
from time import clock, time
pygame.init()
w = 1100   #sets pygame screen width
h = 642   #sets pygame screen height
screen = pygame.display.set_mode((w, h),0,32)  #make and display screen

pygame.display.flip()   #Update screen
running = 1
font = pygame.font.Font(None, 36)
clock = pygame.time.Clock()
port = serial.Serial("COM2", 115200)

while running:          #Loop this
   for event in pygame.event.get():    #get user input
      if event.type == pygame.QUIT:    #if user clicks the close X
           running = 0                 #make running 0 to break out of loop

   temp = float(port.readline())
   clock.tick(100)
   value = font.render(str(temp), 1, (100, 100, 100))
   screen.blit(value, (280,165))
   pygame.display.flip()   #Update screen
   port.write('3')

Upvotes: 2

Views: 3128

Answers (2)

David Cary
David Cary

Reputation: 5500

What seems to be the most likely problem: deadlock

Expanding on Jeff Laughlin's post a little:

Many, many people have encountered this deadlock problem:

  • The PC can't send out any characters, because it is in the middle of the blocking readline() command waiting for the microcontroller to send a newline character.
  • The microcontroller can't send out any characters, because it is in the middle of a blocking readline() command waiting for the PC to send a newline character.

Both end up waiting forever for the other side to do something.

There are many ways for a bidirectional communication system to get stuck in this deadlock. Perhaps a newline character going over the serial cable gets one tiny little bit corrupted enough that it doesn't look like a newline any more. Perhaps a long string was transmitted faster than the receiver could handle it, overflowing some buffer, and one of the lost bytes was the newline character. It doesn't matter if this newline was going in the micro-to-PC or the PC-to-micro direction; we get stuck in the same deadlock either way.

Work-around 1: timeout

The documentation for the pyserial package that is loaded by "import serial" has one partial work-around for this problem: Set a timeout. The documentation specifically recommends:

Do specify a timeout when opening the serial port. Otherwise it could block forever if no newline character is received.

Can you implement that work-around on both ends, the PC and the micro?

Work-around 2: non-blocking readline

Many people implement a non-blocking readline() subroutine, that works something like this:

def try_readline():
  • Has a new byte come in the serial port, or is the serial buffer empty? (Perhaps check with something like inWaiting() ).
  • If it's still empty, try_readline() immediately returns with the null string.
  • (optional) Has it been a really long time since the last time we got a byte? If so, reset the buffer to the empty string.
  • Read a byte from the serial port, and append it to the end of an internal buffer.
  • Is the buffer (which is several bytes longer than any valid message) about to overflow? If so, reset to the buffer to the empty string.
  • Is the byte we just read the newline character?
  • If it's not the newline character, try_readline() immediately returns with the null string.
  • Hey, it's the newline character. Finally!
  • Copy the message from the internal buffer to the return string.
  • reset the buffer to the empty string.
  • try_readline() returns with the message in the return string.

Further discussion

With either workaround, set up your program such that it periodically and unconditionally -- whether or not the try_readline function ever returns anything other than the null string; and whether or not the readline() function times out or receives a valid message -- unconditionally sends out lines of information.

You might also think about what exactly should happen if you plug your serial cable in halfway through a message, and your software receives half-a-message followed by a newline. Also, what should happen if the transmitter sends a bunch of newlines in a row?

Two-way communication is one of those things that humans do pretty much unconsciously, and so we are surprised at how complicated it really is.

p.s.: Have you seen this little program that does PC-to-Arduino with Pyserial?

p.s.: Perhaps you could skim through the latest rough draft of the Serial Programming Wikibook and fill in one or two of the gaping holes, or at least help us by pointing out where the holes are that need to be filled?

Upvotes: 1

Jeff Laughlin
Jeff Laughlin

Reputation: 319

It's likely that readline is blocking while waiting for a line-termination char; some combination of carriage return and or line feed. Your microcontroller needs to send one or both of those chars before readline will return. You should check the pyserial docs to see what line term chars readline expects and verify your uC is sending them by looking in a terminal or even doing a hexdump on the serial stream.

Upvotes: 2

Related Questions