Fork me on GitHub

Project Notes

#334 Serial Communications with Ruby

Options for serial communications in Ruby as they stand in mid-2025.

Notes

In the past I’d been accustomed to using the old serialport gem for serial communications.

It has since fallen into a bit of disrepair: failing to keep up with ruby changes, macOS compatibility, and looking for a new maintainer.

The following notes take some alternatives for a test drive, and also look at getting serialport working again.

TLDR:

The serialport gem - older but still widely used

The current released version of the serialport gem does not install on my macOS 15.5 on Apple Silicon with Ruby 3.3.3. Then native extensions cannot compile - see for example gem_make.out.txt.

There is an update that addressed these issues, made by larskanis but not yet released. This is the version I am using, as specified in the Gemfile.

Key aspects of the serialport gem:

  • inherits from the IO class, and implements the corresponding input/output stream handling methods
    • read/write in binary or character mode
    • read/write text by character, line, or multiple lines
  • provides access to all serial port signals:
    • read: DCD, DSR, RI
    • read/write: DTR, RTS

The rubyserial is a more portable library, it does not require any native compilation thanks to using using Ruby-FFI.

Key aspects of the rubyserial gem:

  • very simple interface
  • all read/write calls are blocking
  • does not directly support timeouts
  • no access or control over serial port signals

Because of these limitations, in most cases it is probably important to have predictable termination indicators in the incoming data, and probably run the blocking call in a separate thread (with timeout).

The uart gem - even cleaner

Aaron Patterson developed a pure Ruby alternative: uart.

Key aspects of the uart gem:

  • No C code
  • No FFI code
  • 5 second timeout by default

Testing

I have an Arduino running a simple SerialControl sketch, attached to my mac and showing up as a device as follows:

$ ls -1 /dev/*serial*
/dev/cu.usbserial-2430
/dev/cu.wchusbserial2430
/dev/tty.usbserial-2430
/dev/tty.wchusbserial2430

Testing the serialport gem

Taking a very basic approach to send a command and read the response:

require 'serialport'
...
socket = SerialPort.new(port_name, 'baud' => baud_rate, 'data_bits' => 8, 'stop_bits' => 1, 'parity' => SerialPort::NONE)
socket.timeout = 1
socket.write "#{command}\r\n"
begin
  data = socket.gets rescue nil
  puts data if data
end while data

See serialport_example/example.rb:

$ cd serialport_example
$ bundle exec example.rb /dev/cu.wchusbserial2430 s32
# SerialPort demonstration
# client initialised for : /dev/cu.wchusbserial2430
#     connection options : {"baud"=>115200, "data_bits"=>8, "stop_bits"=>1, "parity"=>0}
#                signals : {"rts"=>1, "dtr"=>1, "cts"=>1, "dsr"=>1, "dcd"=>1, "ri"=>1}
Random String with length=32:
kPJnkGR7Q0B0MwbPNRB7BJI6dqpRz1gl
$

Testing the rubyserial gem

Taking a very basic approach to send a command and read the response:

require 'rubyserial'
...
socket = Serial.new(port_name, baud_rate, 8, :none, 1)
socket.write "#{command}\r\n"
begin
  data = socket.gets
  puts data unless data.chomp.empty?
end until data.chomp.empty?
socket.close

See rubyserial_example/example.rb:

$ cd rubyserial_example
$ bundle exec example.rb /dev/cu.wchusbserial2430 i
# RubySerial demonstration
# client initialised for : /dev/cu.wchusbserial2430
Random Integer:
45
$

Testing the uart gem

Taking a very basic approach to send a command and read the response:

require 'uart'
...
UART.open port_name, baud_rate, '8N1' do |socket|
  socket.write "#{command}\r\n"
  begin
    data = socket.gets
    puts data unless data.chomp.empty?
  end until data.chomp.empty?
end

See uart_example/example.rb:

$ cd uart_example
$ bundle exec example.rb /dev/cu.wchusbserial2430 s28
# UART demonstration
# client initialised for : /dev/cu.wchusbserial2430
Random String with length=28:
sDClAyQ67Rr71R83pEVYSS5RyWOL
$ bundle exec example.rb /dev/cu.wchusbserial2430 t
# UART demonstration
# client initialised for : /dev/cu.wchusbserial2430
LED on pin 13: turning LED ON
$

Credits and References

About LCK#334 ruby

This page is a web-friendly rendering of my project notes shared in the LittleCodingKata GitHub repository.

Project Source on GitHub Return to the LittleCodingKata Catalog
About LittleCodingKata

LittleCodingKata is my collection of programming exercises, research and code toys broadly spanning things that relate to programming and software development (languages, frameworks and tools).

These range from the trivial to the complex and serious. Many are inspired by existing work and I'll note credits and references where applicable. The focus is quite scattered, as I variously work on things new and important in the moment, or go back to revisit things from the past.

This is primarily a personal collection for my own edification and learning, but anyone who stumbles by is welcome to borrow, steal or reference the work here. And if you spot errors or issues I'd really appreciate some feedback - create an issue, send me an email or even send a pull-request.

Follow the Blog follow projects and notes as they are published in your favourite feed reader