Uart.read(x) returns always one byte

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
User avatar
marfis
Posts: 215
Joined: Fri Oct 31, 2014 10:29 am
Location: Zurich / Switzerland

Uart.read(x) returns always one byte

Post by marfis » Wed Nov 05, 2014 8:50 pm

I'm using the UART in 9600,8,E,1 mode. With the latest updates, everything runs fine with the parity and bit settings. So far so good.

However... I was running into another issue that somebody might clarify / explain.

So I'm basically doing:

Code: Select all

self.serial = UART(port)
self.serial.init(9600, bits=8, parity = 0, timeout = 500)
self.serial.write(txdata)
ans = self.serial.read(1)
... check for acknowledge etc
txdata is an array of 40 bytes that is transmitted ok on my logic analyser.

The target board happily responds with 0x00 immediately afterwards as an acknowledge byte (confirmed on the logic analyser). After another 40msec I get the rest of the response (7 Bytes).

The script is written such that read(1) first checks the ack byte, then another read(3) reads the header etc. as it parses through the received data.

However, read(3) does not return 3 bytes but only one byte...

So if I do

Code: Select all

ans = self.serial.read(1)
print (ans)
ans = self.serial.read(1)
print (ans)
ans = self.serial.read(1)
print (ans)
the first three bytes of the frame are correctly printed on the REPL. But if I do

Code: Select all

ans = self.serial.read(1)
print (ans)
ans = self.serial.read(3)
print (ans)
ans = self.serial.read(2)
print (ans)
It prints out exactly the same. So it looks as the read(x) does not return x bytes but always one byte. readall by the way always returns the correct frame.

Am I missing something here? Has anybody experienced a similar thing?

Damien
Site Admin
Posts: 647
Joined: Mon Dec 09, 2013 5:02 pm

Re: Uart.read(x) returns always one byte

Post by Damien » Wed Nov 05, 2014 11:39 pm

Code: Select all

uart.read(n)
will read at most n bytes. It will not wait for exactly n bytes.

This behaviour follows the standard behaviour of reading functions (like socket recv). The reason is that you'd rather process data you know you have, rather than wait for data that may never come.

But, you can get your code working using the inter character timeout:

Code: Select all

uart = pyb.UART(port, 9600, 8, 0, timeout=500, timeout_char=2)
When reading from the uart, it'll now wait at most 2ms between characters (default is timeout_char=0). If it doesn't receive a new char after 2ms, then it returns everything read so far. If it reads enough characters then it'll return straight away. For 9600 baud you want about 2ms because that's the time a character takes to be read/written at 9600 baud (1/960 rounded up).

To explain a little more how the read works, consider

Code: Select all

data = uart.read(10)
This call can take between 0 and 500 + 9 * 2 = 518ms to complete (with the above timeout settings). It'll take 0ms (or be very quick) if there are 10 bytes waiting in the read buffer. It'll take the maximum time when it has to wait 500ms for the first char, then 2ms between each subsequent chars.

User avatar
marfis
Posts: 215
Joined: Fri Oct 31, 2014 10:29 am
Location: Zurich / Switzerland

Re: Uart.read(x) returns always one byte

Post by marfis » Thu Nov 06, 2014 9:49 pm

Ah well... I should have guessed something like that. I did see the timeout_char option but haven't thought of it as the solution.

Thanks for the clarification.

Post Reply