elixir-circuits / circuits_uart

Discover and use UARTs and serial ports in Elixir
Apache License 2.0
189 stars 48 forks source link

Readline and/or Fixed packet size modes #5

Closed mattneel closed 8 years ago

mattneel commented 8 years ago

In my project, data is sent over serial in the following format:

node-id;child-sensor-id;message-type;ack;sub-type;payload\n

In it's current implementation, Nerves.UART makes this a little hard to work with, so I was looking for some method of reading things line by line.

The implementation I've worked out (in active mode, anyways) was to pass a string buffer around in state (initially empty), and on each serial callback I check if the new data contains a \n, and if it does, I take the existing buffer, append the new data, split it in 2 parts at the \n character, stick the remainder back in the buffer, and GenServer.call my own {:readline} event.

It would make it easier for people to adopt the library if we could set a delimiter to split packets in the buffer by, and even if we could define a fixed packet size, and buffer until we get that many bytes, and then send a message when data is ready.

fhunleth commented 8 years ago

FYI - I'm working on this feature the lines branch. Hopefully I'll get it merged to master and make a release soon. The framing of packets is done via a behaviour, but there's a built-in one for lines. Just add the :framing parameter to your Nerves.UART.open or Nerves.UART.configure calls.

Here's an example for passive mode, but active mode works similarly:

iex> {:ok, uart}= Nerves.UART.start_link      
{:ok, #PID<0.186.0>}
iex> Nerves.UART.open(uart, "ttyUSB0", speed: 9600, active: false)
:ok
iex> Nerves.UART.read(uart)
{:ok,
 "$PGTOP,11,2*6E\r\n$GPGGA,000209.800,,,,,0,0,,,M,,M,,*4B\r\n$GPGSA,A,1,,,,,,,,,,,,,,,*1E\r\n$GPRMC,000209.800,V,,,,,0.00,0.00,060180,,,N*41\r\n$GPVTG,0.00,T,,M,0.00,N,0.00,K,N*32\r\n$PGTOP,11,2*6E\r\n$GPGGA,000210.800,,,,,0,0,,,M,,M,,*43\r\n$GPGSA,A,1,,,,,,,,,,,,,,,*1E\r\n$GPRMC,000210.800,V,,,,,0.00,0.00,060180,,,N*49\r\n"}
iex> Nerves.UART.configure(uart, framing: {Nerves.UART.Framing.Line, separator: "\r\n"})
:ok
iex> Nerves.UART.read(uart)
{:ok, "$GPGGA,000226.800,,,,,0,0,,,M,,M,,*46"}
iex> Nerves.UART.read(uart)
{:ok, "$GPRMC,000226.800,V,,,,,0.00,0.00,060180,,,N*4C"}

Since you're breaking on "\n" characters, you don't need to specify the separator. I.e. adding framing: Nerves.UART.Framing.Line to your Nerves.UART.open call is sufficient.

fhunleth commented 8 years ago

The v0.1.0 release has this feature now. Closing this issue. If there are limitations with what I added, then let's open new issues up so that they can be addressed.

@mattneel, thanks for bring this issue up! I think this will help many people.