bitgamma / elixir_serial Goto Github PK
View Code? Open in Web Editor NEWSerial communication through Elixir ports
License: MIT License
Serial communication through Elixir ports
License: MIT License
I'm running into an issue where the port process isn't exiting after Elixir terminates. Unless I kill the offending process, my computer will crash if I unplug the usb connection.
I'm using the Sparkfun WRL-08687.
$ ps aux | grep serial
rockwood 5061 0.0 0.0 2445080 788 s004 S+ 3:18PM 0:00.00 grep serial
I can then run the following:
{:ok, serial} = Serial.start_link
Serial.open(serial, "/dev/tty.usbserial-AM01VFAJ")
Serial.set_speed(serial, 9600)
Serial.connect(serial)
Serial.send_data(serial, "+++")
Serial.close(serial)
Serial.disconnect(serial)
And the process remains after elixir has exited.
$ ps aux | grep serial
rockwood 5110 0.0 0.0 2434840 752 s004 S+ 3:21PM 0:00.00 grep serial
rockwood 5098 0.0 0.0 2434824 632 ?? Ss 3:21PM 0:00.00 /Users/rockwood/devel/exbee/_build/dev/lib/serial/priv/serial -erlang
I've tried modifying serial.ex to manually Port.close(port)
on exit, but the process still remains.
Any ideas?
I encountered next problem and posted on stackowerflow, according to new readme I connected correctly to serialport my device added handle_info listener, but it doesn't recieve any data. I've tested the device with the same parameters in ruby serialport and it is working well. Do you have any idea?
I'm wondering if there's any settings I can apply to the serial process to have it look for a line break before sending over a message to the elixir process. right now, calling flush() gives me this output.
One message per character.
{:elixir_serial, #PID<0.160.0>, "M"} {:elixir_serial, #PID<0.160.0>, "O"} {:elixir_serial, #PID<0.160.0>, "V"} {:elixir_serial, #PID<0.160.0>, "I"} {:elixir_serial, #PID<0.160.0>, "E"} {:elixir_serial, #PID<0.160.0>, "v"} {:elixir_serial, #PID<0.160.0>, "e"} {:elixir_serial, #PID<0.160.0>, "n"} {:elixir_serial, #PID<0.160.0>, "t"} {:elixir_serial, #PID<0.160.0>, "["} {:elixir_serial, #PID<0.160.0>, "1"} {:elixir_serial, #PID<0.160.0>, "5"} {:elixir_serial, #PID<0.160.0>, "0"} {:elixir_serial, #PID<0.160.0>, "]"} {:elixir_serial, #PID<0.160.0>, ":"} {:elixir_serial, #PID<0.160.0>, " "} {:elixir_serial, #PID<0.160.0>, "S"} {:elixir_serial, #PID<0.160.0>, "P"} {:elixir_serial, #PID<0.160.0>, "E"} {:elixir_serial, #PID<0.160.0>, "A"} {:elixir_serial, #PID<0.160.0>, "K"} {:elixir_serial, #PID<0.160.0>, "I"} {:elixir_serial, #PID<0.160.0>, "N"} {:elixir_serial, #PID<0.160.0>, "G"} {:elixir_serial, #PID<0.160.0>, "\r"} {:elixir_serial, #PID<0.160.0>, "\n"} {:elixir_serial, #PID<0.160.0>, "M"} {:elixir_serial, #PID<0.160.0>, "O"} {:elixir_serial, #PID<0.160.0>, "V"} {:elixir_serial, #PID<0.160.0>, "I"} {:elixir_serial, #PID<0.160.0>, "E"} {:elixir_serial, #PID<0.160.0>, "v"} {:elixir_serial, #PID<0.160.0>, "e"} {:elixir_serial, #PID<0.160.0>, "n"} {:elixir_serial, #PID<0.160.0>, "t"} {:elixir_serial, #PID<0.160.0>, "["} {:elixir_serial, #PID<0.160.0>, "1"} {:elixir_serial, #PID<0.160.0>, "5"} {:elixir_serial, #PID<0.160.0>, "1"} {:elixir_serial, #PID<0.160.0>, "]"} {:elixir_serial, #PID<0.160.0>, ":"} {:elixir_serial, #PID<0.160.0>, " "} {:elixir_serial, #PID<0.160.0>, "E"} {:elixir_serial, #PID<0.160.0>, "N"} {:elixir_serial, #PID<0.160.0>, "D"} {:elixir_serial, #PID<0.160.0>, " "} {:elixir_serial, #PID<0.160.0>, "S"} {:elixir_serial, #PID<0.160.0>, "P"} {:elixir_serial, #PID<0.160.0>, "E"} {:elixir_serial, #PID<0.160.0>, "A"} {:elixir_serial, #PID<0.160.0>, "K"} {:elixir_serial, #PID<0.160.0>, "I"} {:elixir_serial, #PID<0.160.0>, "N"} {:elixir_serial, #PID<0.160.0>, "G"} {:elixir_serial, #PID<0.160.0>, "\r"} {:elixir_serial, #PID<0.160.0>, "\n"}
I'm not sure if I can add an accumulator for the handle_info call to build up the message myself.
I'm trying to handle RFID SEN-09963 ids via serialport and I've encountered difficulty that handle_info()
receives id not entirely. It may be associated with Serial setting?
In documentation I've read that device has none parity, data bits is 8 and stop bits is 1.
How to workaround this?
setup:
{:ok, serial} = Serial.start_link
Serial.open(serial, "/dev/cu.usbserial-A5026NYN")
Serial.set_speed(serial, 9600)
Serial.connect(serial)
handle_info():
def handle_info({:elixir_serial, serial, data}, state) do
Logger.debug "received :data #{data}"
{:noreply, state}
end
log:
[debug] received :data
[debug] received :data 7A005AFA518B
[debug] received :data
[debug] received :data 7
[debug] received :data A005AFA518B
[debug] received :data
[debug] received :data 7A005AFA518B
[debug] received :data
[debug] received :data 7A005AFA518
[debug] received :data B
I've tryed to figure out why I get multiple messages, this is not related with bits, I set manually in set_tty_speed() and result is the same:
ttymodes.c_cflag |= CS8; /* enable eight bit chars */
ttymodes.c_cflag &= ~PARENB; /* disable input parity check */
ttymodes.c_cflag &= ~CSTOPB; /* enable 1 stop bit */
I've compiled serial.c, and it seems that it isn't waiting for whole message, it cut ids as in handle_info()
./compiledserial
7AB
AFA518BA518B
5AFA518B
8B
7A005AF
As I asked in stackoverflow, do you have some basic example how can I use this library?
Hi.
Thanks for this package I've been using it successfully on my Mac for working with an Arduino.
On the Beaglebone, however, with /dev/ttyACM0
as the Arduino connected via USB, I get this error and crash when trying to call Serial.open/2
.
Maybe you have an idea?
Thanks
iex(branch@nerves)3> {:ok, serial} = Serial.start_link
{:ok, #PID<0.115.0>}
iex(branch@nerves)4>
nil
iex(branch@nerves)5> Serial.open(serial, "/dev/ttyACM0")
** (EXIT from #PID<0.110.0>) an exception was raised:
** (ArgumentError) argument error
:erlang.port_command(#Port<0.1278>, [3, "/dev/ttyACM0"])
(serial) lib/serial.ex:99: Serial.handle_call/3
(stdlib) gen_server.erl:629: :gen_server.try_handle_call/4
(stdlib) gen_server.erl:661: :gen_server.handle_msg/5
(stdlib) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Interactive Elixir (1.1.1) - press Ctrl+C to exit (type h() ENTER for help)
00:03:37.171 [error] GenServer #PID<0.115.0> terminating
** (ArgumentError) argument error
:erlang.port_command(#Port<0.1278>, [3, "/dev/ttyACM0"])
(serial) lib/serial.ex:99: Serial.handle_call/3
(stdlib) gen_server.erl:629: :gen_server.try_handle_call/4
(stdlib) gen_server.erl:661: :gen_server.handle_msg/5
(stdlib) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Last message: {:open, "/dev/ttyACM0"}
State: {#PID<0.110.0>, #Port<0.1278>}
=CRASH REPORT==== 1-Jan-1970::00:03:37 ===
crasher:
initial call: Elixir.Serial:init/1
pid: <0.115.0>
registered_name: []
exception exit: {badarg,
[{erlang,port_command,
[#Port<0.1278>,[3,<<"/dev/ttyACM0">>]],
[]},
{'Elixir.Serial',handle_call,3,
[{file,"lib/serial.ex"},{line,99}]},
{gen_server,try_handle_call,4,
[{file,"gen_server.erl"},{line,629}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,661}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,240}]}]}
in function gen_server:terminate/7 (gen_server.erl, line 826)
ancestors: [<0.110.0>]
messages: []
links: [<0.110.0>]
dictionary: []
trap_exit: false
status: running
heap_size: 610
stack_size: 27
reductions: 141
neighbours:
neighbour: [{pid,<0.110.0>},
{registered_name,[]},
{initial_call,{erlang,apply,2}},
{current_function,{gen,do_call,4}},
{ancestors,[]},
{messages,[]},
{links,[<0.115.0>]},
{dictionary,
[{iex_history,
#{'__struct__' => 'Elixir.IEx.History.State',
queue => {[{4,"\n",nil},
{3,"{:ok, serial} = Serial.start_link\n",
{ok,<0.115.0>}},
{2,"\n",nil}],
[{1,"\n",nil}]},
size => 4,
start => 1}}]},
{trap_exit,false},
{status,waiting},
{heap_size,4185},
{stack_size,50},
{reductions,6273}]
iex(branch@nerves)1>
Currently if I call Serial.connect
and there is a problem there is no way of knowing from the calling process. I see a message in iex
but the Serial.connect
call always return :ok
and a failed command does not crash the Serial
GenServer
.
To reproduce just try to open a resource that does not exist.
Ideally one of three things would happen:
# Option 1
Serial.open("/dev/myport")
Serial.set_speed(9600)
case Serial.connect(pid) do
:ok -> Logger.info("Yay! we have a serial port")
{:error, _reason} -> Logger.warn(":( no serial port")
end
#Option 2
:ok = Serial.connect(pid)
# Serial GenServer crashes on failure to open
# this is what erlang-serial does
#Option 3
try do
:ok = Serial.connect(pid)
Logger.info("Yay!! serial port")
rescue
RuntimeError -> Logger.warn(":'( no serial port")
Personally I like options 1 or 3. Option 2 relies on running both Serial
and the calling server under the same Sup, or monitoring the Serial
GenServer
from your calling server. Which is still workable though.
This is a strange problem, but very reproducible for me. When I first run the app on a new system, I have to run mix deps.compile
twice. If I run it only once, the deps compile without warning or error, but when I start the app, it fails with this message:
{failed_to_start_child,'Elixir.Gatekeeper.DoorInterface',{enoent,[{erlang,open_port,[{spawn_executable,\"/var/www/gatekeeper/_build/prod/lib/serial/priv/serial\"}
Simply running mix deps.compile
again "fixes" the issue. Here's the abbreviated output
$ mix deps.compile
# ... snip
==> serial
-- The C compiler identification is GNU 4.9.2
-- The CXX compiler identification is GNU 4.9.2
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /var/www/gatekeeper/deps/serial
Scanning dependencies of target serial
[100%] Building C object CMakeFiles/serial.dir/src/serial.c.o
Linking C executable priv/serial
[100%] Built target serial
Compiled lib/serial.ex
Generated serial app
# ... snip
$ mix phoenix.server
21:25:36.360 [info] Running Gatekeeper.Endpoint with Cowboy on http://localhost:4000
{"Kernel pid terminated",application_controller,"{application_start_failure,gatekeeper,{{shutdown,{failed_to_start_child,'Elixir.Gatekeeper.DoorInterface',{enoent,[{erlang,open_port,[{spawn_executable,\"/var/www/gatekeeper/_build/prod/lib/serial/priv/serial\"},[{args,[\"-erlang\"]},binary,{packet,2}]],[]},{'Elixir.Serial',init,1,[{file,\"lib/serial.ex\"},{line,94}]},{gen_server,init_it,6,[{file,\"gen_server.erl\"},{line,328}]},{proc_lib,init_p_do_apply,3,[{file,\"proc_lib.erl\"},{line,240}]}]}}},{'Elixir.Gatekeeper',start,[normal,[]]}}}"}
Crash dump is being written to: erl_crash.dump...done
Kernel pid terminated (application_controller) ({application_start_failure,gatekeeper,{{shutdown,{failed_to_start_child,'Elixir.Gatekeeper.DoorInterface',{enoent,[{erlang,open_port,[{spawn_executabl
$ mix deps.compile
# ... snip
==> serial
-- Configuring done
-- Generating done
-- Build files have been written to: /var/www/gatekeeper/deps/serial
[100%] Built target serial
$ mix phoenix.server
21:27:01.147 [info] Running Gatekeeper.Endpoint with Cowboy on http://localhost:4000
21:27:01.380 [info] RFID listener process started for reader /dev/ttyUSB0
Full logs: https://gist.github.com/bklang/c1435f247d73fc81a37c
This is probably related to #4, but the error looks a bit different in Raspberry Pi:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.