bmitc

bmitc

Using Circuits.UART, how do you read a specific amount of bytes?

For Circuits.UART, I am looking for a way to read a specific amount of bytes where the number of bytes to read may vary from call to call. This is a common use case needed for when certain messages return a specific amount of bytes in the response but the number of bytes in the response may differ from message to message.

For example, I expected to be able to do something like this:

iex> {:ok, pid} = Circuits.UART.start_link
iex> Circuits.UART.open(pid, "COM2", speed: 115200, active: false)
iex> Circuits.UART.write(pid, "some message expecting 3 bytes in response\r\n")
iex> {:ok, three_byte_response} = Circuits.UART.read(pid, 3, 3000)
iex> Circuits.UART.write(pid, "some message expecting 5 bytes in response\r\n")
iex> {:ok, five_byte_response} = Circuits.UART.read(pid, 5, 3000)

This isn’t possible right now because the read/2 of course doesn’t have a number of bytes to read parameter. I originally expected there to be something like read/3 which would accept a specific number of bytes to read.

It seems a possible alternative is to create a framer behaviour, but that has two downsides: (1) the data isn’t actually framed, (2) this would require constantly calling configure like Circuits.UART.configure(pid, number_of_bytes: rx_framing_timeout: 500) before every write and read pair. I’m not entirely sure of the consequences of doing (2), but it’s the only possible way I could see doing this with the framing feature (if it would even work).

I think the Circuits.UART module should have a read/3 function like read(pid, bytes_to_read, timeout). This is quite common in the serial communication world:

  1. LabVIEW VISA Read
  2. .NET SerialPort.Read
  3. pySerial

So I’m curious, how have people handled this with Circuits.UART?

I asked this question a while back on the Circuits GitHub repo, but got a non-answer.

First Post!

Sebb

Sebb

I do not use circuits, but from the docs it seems, that this is not supported. So you either have to change the other side to a framed protocol or change circuits.uart or write your own - which is not too hard if you do not aim to be as comfortable as circuits.uart.

Open a Port — Elixir v1.12.3 and just take the bytes from the uart and write them to stdout (and vice versa).

I did this for a project of mine and it seems to work but its not battle tested. If you feel up to it I could make the code public for you to get it.

Note: I use a framed protocol, so you’d have to add the functionality you need. But my uart-adapter has just 200loc and should be easy to handle.

To give an idea how this works:

...

static void
forward(int const from, int const to, int const err_code)
{
  static char   buf[BUFSIZ];
  ssize_t const size = read(from, buf, BUFSIZ);
  require(size > 0, err_code + ERR_READ_ERROR_);
  ssize_t const write_size = write(to, buf, size);
  require(write_size == size, err_code + ERR_WRITE_ERROR_);
}

int
main(int argc, char* argv[])
{
  f_uart = config_uart(argc, argv);

  int const maxfd = f_uart + 1;
  fd_set    fds;

  for (;;) {
    FD_ZERO(&fds);
    FD_SET(f_uart, &fds);
    FD_SET(STDIN_FILENO, &fds);

    select(maxfd, &fds, NULL, NULL, NULL);

    if (FD_ISSET(f_uart, &fds)) {
      forward(f_uart, STDOUT_FILENO, ERR_FWD_STDOUT_FAILED_);
    }
    if (FD_ISSET(STDIN_FILENO, &fds)) {
      forward(STDIN_FILENO, f_uart, ERR_FWD_STDIN_FAILED_);
    }
  }

and on the Elixir side:

 def handle_info({_port, {:data, data}}, state = %{subscriber: subscriber}) do

    if subscriber do
      send(subscriber, {:data, data})
    end

    {:noreply, state}
  end

...

  @impl true
  def handle_cast({:write, data}, %{port: port} = state) do
    Port.command(port, data)
    {:noreply, state}
  end

...

Where Next?

Popular in Questions Top

lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
tduccuong
Hi, is there any work on GUI with Elixir, that is similar to Electron/Javascript? My idea is to bundle Phoenix and BEAM into a single se...
New
sacepums
Hey guys. I'm new to elixir and im really stocked about it. But I ran into a bit of problem - I need to convert a date sting, for examp...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New

Other popular topics Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
magnetic
Hey :wave:t3: Elixir community, I’ve been learning Elixir, and working on some side projects. My editor of choice is VSCode, and althoug...
New

We're in Beta

About us Mission Statement