3D Print Server: How to Check for Marlin Firmware with Ruby on macOS

Have been starting to write my own 3D print server as a Ruby on Rails project. Yesterday I wrote the first draft Ruby code to read a gcode file and to stream the gcode to a 3D printer (running Marlin firmware) and print. It works OK (I could print a model with the code I wrote in Ruby); but I want to tweak it a bit more before posting that code.

Today, I wrote a Rails initializer get_usb_serial_devices.rb to get all the active USB serial devices on my mac and check each one (except for the bluetooth device) to see if it responds to a Marlin gcode command and if it does, set a config variable specifying the Marlin device:

get_usb_serial_devices.rb

##############################################
# get_usb_serial_devices.rb
# Author:  Neo
# URL:  https://community.unix.com
# Summary:  Get serial devices on macos and test for Marlin firmware connection
# Version: 0.1
# Date: 7 November 2021
##############################################
require 'serialport'

TIMEOUT = 250 #milliseconds
BAUD_RATE = 115200
files = Dir["/dev/cu*"]
marlin_device = ''
devices_to_test = []
files.each do |device|
   if device.downcase.include? "blue"
    puts "[#{Time.now}] Bluetooth Serial Device Skipped: #{device}"
    next
   end
   begin
        sp = SerialPort.new device
        if sp.present?
            puts "[#{Time.now}] Serial Device Available: #{device}"
            devices_to_test << device
            sp.close
        end
    rescue
        puts "[#{Time.now}] Serial Device Busy Or Not Available: #{device}"
    end
end

devices_to_test.each do |device|
    puts "[#{Time.now}] Testing For Marlin Firmware: #{device}"
    sp = SerialPort.new device
    reply = ''
    sp.baud=BAUD_RATE 
    sp.read_timeout = TIMEOUT
    # gcode M504: Validate EEPROM contents.
    sp.write "M504\r"
    # perform test one extra time to make sure we got all the data
    # normally the M504 command returns only two lines
    3.times do |n|
        begin
        test_line = sp.readline("\n")
        puts "[#{Time.now}] Testing For Marlin Firmware: #{test_line}"
        if test_line.downcase.chomp == 'ok'
            puts "[#{Time.now}] Passed Marlin Firmware Test: #{device}"
            Rails.application.config.marlin_device = device
        end
        rescue
            #exception occurs then nothing to read and read times out
            next
        end
    end
end

Results when initialized:

dev-macos:gcode-rails Tim$ rails c
[2021-11-07 09:43:21 +0700] Bluetooth Serial Device Skipped: /dev/cu.Bluetooth-Incoming-Port
[2021-11-07 09:43:21 +0700] Serial Device Available: /dev/cu.wchusbserial140
[2021-11-07 09:43:21 +0700] Serial Device Busy Or Not Available: /dev/cu.usbserial-140
[2021-11-07 09:43:21 +0700] Testing For Marlin Firmware: /dev/cu.wchusbserial140
[2021-11-07 09:43:21 +0700] Testing For Marlin Firmware: echo:EEPROM OK
[2021-11-07 09:43:21 +0700] Testing For Marlin Firmware: ok
[2021-11-07 09:43:21 +0700] Passed Marlin Firmware Test: /dev/cu.wchusbserial140
Loading development environment (Rails 6.0.4.1)

From the console: checking the Marlin device configured by the initalizer:

2.7.1 :001 > Rails.application.config.marlin_device
 => "/dev/cu.wchusbserial140" 
2.7.1 :002 > 

Using OctoPrint, which is written in Python, I noticed it is a bit "archaic" and the software architecture of OctoPrint is "less than ideal", politely speaking. Without getting into any details, OctoPrint has a huge following, is very popular, and it is very difficult to change the legacy architecture without breaking the plugin ecosystem. I looked at rewriting OctoPrint but after a few days of reviewing this possibility, I decided that redesigning OctoPrint was a can-of-worms better left unopened. However, I have noticed that I don't use many OctoPrint plugins and the plugin features which I do use, I can write this code in Ruby fairly easily.

So, after pondering this for a few weeks, yesterday I took the first step and actually wrote my first very rough working 3D print server. Today, I took a step back and wrote some code to get the active 3D printer serial device so I do not have to hard code that device into the code.

Next, I plan to rewrite my core streaming code and then start building a new Rails UI based on Bootstrap 5 and jQuery.

Free free to improve the code above if you have any ideas. I'm moving my attention back to the gcode streaming routine.

This is the only Rails 3D print server I could fine on the net. It's was hard for me to believe there was no modern Rails version of legacy OctoPrint, but I turned up nothing at all. That is why I have started this new project at the serial port IO. With the serial IO part to the 3D printer (basically) working, the rest should be fairly easy, I think.

More to follow.....


See Also:

Ruby Serial Port Class

Ruby IO Class

2 Likes