I want to access the serial port using Crystal lang.
I have following code in python. I want to write the equivalent Crystal-lang code for a pet project.
import serial
def readSerData():
s = ser.readline()
if s:
print(s)
result = something(s) #do other stuff
return result
if __name__ == '__main__':
ser = serial.Serial("/dev/ttyUSB0", 9600)
while True:
data = readSerData()
#do something with data
I couldn't find any library for accessing the serial port.
What is the proper way for accessing serial port in crystal-lang?
Thanks in advance.
It is easier to answer this question in multiple parts to really cover it all:
Q: How do I access a serial port on linux/bsd?
A: Open it as a file. On linux/bsd a serial connection is established the moment a device is plugged in, and is then listed somewhere under /dev/
(these days, usually as /dev/ttyUSB0). In order to access this connection you simply open it like you would a regular file. Sometimes this is actually good enough to start communicating with the device as modern hardware typically works with all baud rates and default flags.
Q: How do I configure a serial/tty device on linux/bsd?
A: Set termios flags on the file. If you do need to configure your connection to set things like baud rate, IXON/IXOFF etc, you can do it before even running your program using stty if it is available. Eg. to set the baud rate you could run: stty -F /dev/ttyUSB0 9600
. And after this is set up you can just open it as a file and start using it.
You can spawn stty
from crystal using Process.run
if you wanted an easy way to configure the device from your app. I would probably recommend this approach over the next solution..
Q: How do I set termios flags from crystal, without using stty?
A: Use the termios posix functions directly.
Crystal actually provides FileDescriptor handles with a few common termios settings such as cooked
, which means it has minimal termios bindings already. We can start by using the existing code for our inspiration:
require "termios" # See above link for contents
#Open the file
serial_file = File.open("/dev/ttyACM0")
raise "Oh no, not a TTY" unless serial_file.tty?
# Fetch the unix FD. It's just a number.
fd = serial_file.fd
# Fetch the file's existing TTY flags
raise "Can't access TTY?" unless LibC.tcgetattr(fd, out mode) == 0
# `mode` now contains a termios struct. Let's enable, umm.. ISTRIP and IXON
mode.c_iflag |= (Termios::InputMode::ISTRIP | Termios::InputMode::IXON).value
# Let's turn off IXOFF too.
mode.c_iflag &= ~Termios::InputMode::IXOFF.value
# Unfun discovery: Termios doesn't have cfset[io]speed available
# Let's add them so changing baud isn't so difficult.
lib LibC
fun cfsetispeed(termios_p : Termios*, speed : SpeedT) : Int
fun cfsetospeed(termios_p : Termios*, speed : SpeedT) : Int
end
# Use the above funcs to set the ispeed and ospeed to your nominated baud rate.
LibC.cfsetispeed(pointerof(mode), Termios::BaudRate::B9600)
LibC.cfsetospeed(pointerof(mode), Termios::BaudRate::B9600)
# Write your changes to the FD.
LibC.tcsetattr(fd, Termios::LineControl::TCSANOW, pointerof(mode))
# Done! Your serial_file handle is ready to use.
To set any other flags, refer to the termios manual, or this nice serial guide I just found.
Q: Is there a library to do all this for me?
A: No :( . Not that I can see, but it would be great if someone made it. It's probably not much work for someone to make one if they had a vested interest :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With