Connecting Rails and Arduino with SerialPort

Photo by Denys Nevozhai

I recently finished a project at the Turing School where my group created a Rails e-commerce website that featured multiple stores (multi-tenancy) with various levels of authorization. Fortunately, we were able to spend time refactoring our code base, so I wanted to add a feature using Arduino just for fun. When an order was placed, the Arduino would simply blink an LED.

I have played around with an Arduino on and off for about a year. I didn’t want to complicate matters since I didn’t know how to connect Rails (Ruby) to the Arduino in the first place, so I just went with blinking an LED.

From the research I did, I found there are two main routes you can take to tell the Arduino what you want to do using Ruby.

  1. Tell the Arduino each instruction in Ruby using a gem. If you want the LED to blink, then tell it the pin, when to blink, for how long, however many times, and when to stop.
  2. Give the Arduino a signal through the serial port to run the program that is compiled on its chip.

Route #1 is very cool. You can use Ruby gems like Dino or Artoo to write Ruby in your code base that gets translated to the Arduino language. For instance, if you want to continuously blink an LED on pin 12, then you can write this simple Ruby file using the Dino gem’s DSL:

board = Dino::Board.new(Dino::TxRx::Serial.new)
led = Dino::Components::Led.new(pin: 12, board: board)
[:on, :off].cycle do |switch|
led.send(switch)
sleep 0.5
end

The gem translates the Ruby code into instructions for the Arduino. You barely even have to learn the Arduino language this way! Which can be a blessing or a curse depending on what you want to do down the road. I usually find DSLs difficult to use unless the documentation is very good, but Dino’s has a bunch of great examples.

In the end, I chose to go with route #2. The main reason was because I was using Rails. If I used the Dino gem, then I had to wait for the Ruby code to finish executing before my Rails server could do anything else. So when an order came through the server, the Dino code blinked the LED for a couple seconds, the server waited for this to finish, and then the server was available again. I didn’t want this gap in server availability.

Using SerialPort

To solve this, I used the Rails server to send a single byte of information to the Arduino board via a serial port (usb). When the board got this byte, it ran the rest of the instructions already compiled on the Arduino. This way, the server needed to send only a very quick instruction to the Arduino and could go about its day without worrying about what was going on with the Arduino board.

Luckily, there is already a serial port Ruby gem called…you guessed it, SerialPort! My use case for this was simple:

  1. Initialize the class when I want the LED to blink.
  2. The class checks if there is anything connected to the usb port (the Arduino board).
  3. If there is, the server sends a byte (the letter a) to the Arduino board.
  4. The Arduino checks if it receives the correct byte, and then executes the LED blinker code.

I made the Ruby class as a service in my Rails app, which was initialized when a customer checks out and an order is placed.

class ArduinoService
  def initialize
port_str = "/dev/cu.usbmodem1421"
baud_rate = 4800
data_bits = 8
stop_bits = 1
parity = SerialPort::NONE
    if port_connected?(port_str)
sp = SerialPort.new(port_str, baud_rate, data_bits, stop_bits,
parity)
order_confirmation(sp)
end
end
  def order_confirmation(sp)
sp.write('a')
sp.flush
end
  def port_connected?(port)
return true if Dir.glob(port).count == 1
end
end

The port_str value is dependent on your system. You can see this value by connecting your Arduino to your machine and viewing the text in the bottom right corner of the Arduino IDE.

On the Arduino board, the embedded code was:

uint8_t c;
int i;
void setup() {
Serial.begin(4800); // set baud rate
pinMode(12, OUTPUT); // set pin 12 to be output for LED
digitalWrite(12, LOW); // start with LED off
}
void loop() {
while (Serial.available() > 0) { // check if serial port has data
c = Serial.read(); // read the byte
    if (c == 'a')
{
for (i=0; i<5; i++){
digitalWrite(12, HIGH); // turn on LED
delay(500); // wait 500 milliseconds
digitalWrite(12, LOW); // turn off LED
delay(500); // wait 500 milliseconds
}
}
}
}

The Arduino was wired using a simple LED connection as shown in the picture below.

In my Rails app, I added an item to my cart, checked out (the order was placed automatically), and success! The LED blinked five times as expected.

Of course, this wasn’t scalable or feasible for a production application, but it was a good proof of concept. Going forward, I would like to get an ethernet or wifi shield and have the same behavior by using the Arduino as a server. I’ll have to save that for part two.