Serial comms over hardware UART for Johnny-Five between Arduino and a Raspberry Pi
A Raspberry Pi is an interesting piece of equipment as it gives you a full linux stack on a piece of hardware that can be run from a battery and that is lightweight and is inexpensive. However the downside is that whilst the RPi does have a GPIO it's really not that awesome to work with as timing of things can be a little wonky given Linux and a single core.
Given this, using an Arduino for hardware control whilst talking to the RPi over serial, provides a very good balance and is still relatively inexpensive with low weight and power consumption (my robot can run both of these - including using WiFi - for several hours on a 3600mAh, 7.4V RC-oriented LiPo battery).
However, just plugging the arduino into the USB port of the RPi has come issues:
- You take up a USB port
- The USB subsystem on the RPi is not excellent and is prone to power issues with a lot of comms over it.
- If you're using a USB port for WiFi networking, putting other devices on there (especially ones doing a lot of data transfer) can cause contention problems.
- The arduino will attempt to draw power through the RPi, again leading to more power stability woes - and this is exacerbated when using a battery.
Hardware Serial UART to the rescue.
On the RPi, we have the ability to use a dedicated hardware serial port that is available via the GPIO. This means we can skip USB and run our comms directly to the UART on broadcomm chip which is also exposed to the linux kernal. This means it's faster, more stable, doesn't block anything else running over USB and keeps a second USB port available.
However it also means more work than simply plugging the arduino into the USB port and running code across it.
Notes / don't cry warning.
This approach is fairly straight forward, however there is a small but real chance of blowing a GPIO pin if you wire things wrong. The RPi GPIO has no protection and runs at 3.3V NOT the 5V your arduino runs at. This means any of the following can do damage:
- Touching 5V to any of the GPIO data pins on the RPi (watch what you're connecting to).
- Not level shifting the data lines so the Arduino is broadcasting serial data at 5V but RPi is expecting 3.3 (not good)
- Wiring the wrong pins thinking they are something else (usually you'll get away with this but not always).
So with that warning - if you don't know what you're doing, if you aren't familiar with creating isolated cabling for a header like the GPIO on an RPi or you plain don't follow instructions then don't cry to me about it and add a comment. Treat this stuff with care and triple check before connecting headers and definitely don't do it powered.
Getting it working
For the purposes of this gist I'm assuming you've done and you know the below:
- You have raspbian installed and it's a relatively new version (some time in 2013+)
- You are totally familiar with command line linux and you can already SSH onto your PI
- Your RPi and Arduino both have some sort of power supply that's regulated.
- Your arduino has standard firmata flashed onto it and you've tested it's working with firmata or Johnny-Five examples (eg blinking LED on Pin 13 using firmata or Johnny Five to do it).
- Your RPi user is a member of the dialout group.
So there's multiple parts we need to get working and each piece will be stepped through below but at a high level we're doing the following:
- Modifying the RPi config to allow use the hardware serial port
- Wiring the RPi and the Arduino
- Connecting over serial and checking it's working
- Testing firmata over hardware serial
By default the UART serial line is attached to a console (if you have a serial cable you can plug this into your computer and watch the boot sequence old-skool style via a remote terminal). We don't want that though, we want to free up that serial line for talking to our arduino.
So in /etc/inittab you'll find a line that looks something like this:
T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100
Just comment this out with a '#' - you'll probably need to be root to do this.
Next up we need to tell the boot sequence not to try and attach a console on that device either. So this time we're going to edit /boot/cmdline.txt
Remove the references to ttyAMA0 from the boot command. Specifically remove this string 'console=ttyAMA0,115200 kgdboc=ttyAMA0,115200'. If you want, copy the line then use a '#'' to comment out the old version. Note you can't have any blank lines in cmdline.txt
That's it - reboot your RPi, do a:
And you should see that the ttyAMA0 references, are indeed gone.
Grab a cup of tea and move on to the next bit.
To wire up your arduino to your RPi you'll need a couple of things. First off some jumper wires - ones with female connectors so you can attach them to the RPi. Secondly you need a level shifter.
You can do this with resistors but really, for $7 go get a proper level shifter and save the agg and potential blown pins.
Go order one, solder it up and come back. This will wait...
Okay good. So now you want to connect everything up.
Basically the level shifter has two sides, low and high (it should be marked). You want to connect the Arduino to the HIGH side because it's 5V and the RPi to the LOW side because it's 3.3V.
Reminder: Make sure the arduino has standard firmata flashed as you may not be able to flash it once your TX / RX pins are connected
The Arduino is simple:
- connect the 5V out pin to V (or Vcc)
- connect GND pin to GND
- connect Arduino RX pin to PIN 1 on the level shifter
- connect Arduino TX pin to PIN 2 on the level shifter
Now the arduino is done, it's time to do the RPi. Do this with the POWER OFF and triple check your wiring.
The RPi GPIO is laid out like this:
This is what you're going to wire up:
- 3.3v on RPi (P1-01) to V on the level shifter
- GND (P1-06) to GND
- RPi TX (P1-08) to PIN 1 on the level shifter
- RPi RX (P1-10) to PIN 2 on the level shifter
To summarise then. Grounds are connected, output voltages are given to the level shifter as references and then the arduino transmit pin is connected to the RPi recieve pin and vice versa.
Check, check and check again that you have got this the right way around.
If you're happy you are not injecting 5V in any way into the RPi GPIO then switch on your Raspberry Pi and your arduino and let's move on to testing it's working.
The first test is to check the device is there:
You should see a device called /dev/ttyAMA0. If you don't then go back and check you've done everything. If you do, proceed.
Firmata / johnny-five tests
If you haven't done it, now is the time to install the packages.
npm install serialport firmata johnny-five
Johnny five will install firmata and serialport but sometimes it's handy to have them separately for debugging purposes. Like I'm about to show you.
Before you start any code, the best test is to:
cd node_modules/firmata node repl.js
This will prompt you for the device you want to connect to. Enter:
If it connects then great. If it throws you an error, have a look and rectify, if it just hangs check the following:
- Do you have enough power to the arduino? (I had this issue with and EtherTen which really needs 9V not 5V)
- Have you wired things up properly?
- Are you running firmata on the Arduino?
If you're connected try the following:
That should switch the LED on pin 13 on the arduino on and off. If this all works then you're pretty much sorted.
From here you can run the files present in the rest of this gist.
- firmatatest.js does blink using just firmata
- j5test does blink using just johnny-five
If you've got these working then you can now build a nodebot or other JS hardware device using the UART serial connection and free up those USB connections. Congratulations!!