Introduction: Program an Arduino Using BeagleBone, Without USB
If you seen my BeagleBone/Arduino serial communication guide, you may have been waiting for this one! If you haven't seen it yet, go read it first because we're 100% building on that framework. I'm not going to repeat those instruction here.
The end-goal is to be able to make an Arduino based gadget that gets permanently mounted somewhere and talks to a BeagleBone, and be able to update the Arduino's firmware without having to take anything apart. We'll be using avrdude on the BeagleBone, and a GPIO to control the Arduino's reset line. This has a secondary advantage of allowing the Arduino to be reset remotely from the BeagleBone any time it needs it.
Method:
- code in the Arduino gui as normal
- do a "Verify" compile, which creates a hex file and leaves it in a temp folder
- copy hex file to the BeagleBone
- toggle the reset line
- upload the hex with avrdude
Sounds easy right? well, turns out that there's some tricky timing involved to get avrdude and the Arduino boot loader to talk, and that's the point of this guide.
Things you'll need:
- Complete the BeagleBone/Arduino serial communication guide and have the demo programs working
- one more jumper wire
Assumptions:
- your serial_echo test works every time
- you know some basic file editing on linux
The end-goal is to be able to make an Arduino based gadget that gets permanently mounted somewhere and talks to a BeagleBone, and be able to update the Arduino's firmware without having to take anything apart. We'll be using avrdude on the BeagleBone, and a GPIO to control the Arduino's reset line. This has a secondary advantage of allowing the Arduino to be reset remotely from the BeagleBone any time it needs it.
Method:
- code in the Arduino gui as normal
- do a "Verify" compile, which creates a hex file and leaves it in a temp folder
- copy hex file to the BeagleBone
- toggle the reset line
- upload the hex with avrdude
Sounds easy right? well, turns out that there's some tricky timing involved to get avrdude and the Arduino boot loader to talk, and that's the point of this guide.
Things you'll need:
- Complete the BeagleBone/Arduino serial communication guide and have the demo programs working
- one more jumper wire
Assumptions:
- your serial_echo test works every time
- you know some basic file editing on linux
Step 1: The Reset Line
Connect the Arduino reset pin to P9 pin 23 on the BeagleBone.
The way this works is that the 3.3V the BB outputs is enough to keep the RST line "high", which is what the Arduino wants for "run".
Setting the pin "low" will connect to GND, which puts the Arduino in a reset state. Setting the pin high again starts the bootloader on the Arduino.
In the next step we'll configure the BeagleBone to set the Arduino in run mode.
The way this works is that the 3.3V the BB outputs is enough to keep the RST line "high", which is what the Arduino wants for "run".
Setting the pin "low" will connect to GND, which puts the Arduino in a reset state. Setting the pin high again starts the bootloader on the Arduino.
In the next step we'll configure the BeagleBone to set the Arduino in run mode.
Step 2: Setup the BB GPIO
I've chosen to have the BeagleBone set the GPIO pin high on boot, so that it will allow the Arduino to run all the time. You could also do this as part of your python script's setup, but I want my gadget to work even if the python script is never started.
I'm running Ubuntu on my BB, so if you're running Angstrom the only difference is to figure out how to run a bash script on boot up. For Ubuntu it's as easy as adding a line to /etc/rc.local. I've named my bash script "sysinit.sh", set it executable (chmod a+x), and stored it in /root/, so I add "/root/sysinit.sh" to my rc.local file on a new line. Be sure to reboot or just run the script once before moving on!
In the script, we have to use a different pin naming convention than we are used to. On the board, we are using P9 pin 23. Using the handy graphic on PyBBIO's wiki we can see that pin is also known as GPIO1_17. On linux, the pins are simply numbered, but luckily it's easy to translate the GPIO name to the simple number by using the following formula:
GPIOb_p = b * 32 + p = N
So we want GPIO1_17, which is 1*32+17=49. If you want to use a different pin, substitute 49 for your number in the script.
My script follows:
#!/bin/bash
#gpio1_17 for arduino reset pin, pull high to turn on
#GPIOb_p = b * 32 + p = N
#enable the pin
echo 49 > /sys/class/gpio/export
#set for output
echo "out" > /sys/class/gpio/gpio49/direction
#set HIGH
echo 1 > /sys/class/gpio/gpio49/value
I'm running Ubuntu on my BB, so if you're running Angstrom the only difference is to figure out how to run a bash script on boot up. For Ubuntu it's as easy as adding a line to /etc/rc.local. I've named my bash script "sysinit.sh", set it executable (chmod a+x), and stored it in /root/, so I add "/root/sysinit.sh" to my rc.local file on a new line. Be sure to reboot or just run the script once before moving on!
In the script, we have to use a different pin naming convention than we are used to. On the board, we are using P9 pin 23. Using the handy graphic on PyBBIO's wiki we can see that pin is also known as GPIO1_17. On linux, the pins are simply numbered, but luckily it's easy to translate the GPIO name to the simple number by using the following formula:
GPIOb_p = b * 32 + p = N
So we want GPIO1_17, which is 1*32+17=49. If you want to use a different pin, substitute 49 for your number in the script.
My script follows:
#!/bin/bash
#gpio1_17 for arduino reset pin, pull high to turn on
#GPIOb_p = b * 32 + p = N
#enable the pin
echo 49 > /sys/class/gpio/export
#set for output
echo "out" > /sys/class/gpio/gpio49/direction
#set HIGH
echo 1 > /sys/class/gpio/gpio49/value
Step 3: Compile the Hex
Back in the Arduino environment, we need to compile our sketch to a hex file that avrdude can upload. You can do this without uploading by clicking the Verify button. The hex file will be placed in a temp folder, along with a lot of other files needed for the compile. If you do not know where the temp files are kept, you can find out by enabling verbose output in the Arduino gui preferences. On my linux desktop, one of the final lines of the verbose Verify for the serial_echo demo looks like this:
/tmp/build5862458412917915972.tmp/serial_echo.cpp.elf /tmp/build5862458412917915972.tmp/serial_echo.cpp.hex
The file we want is /tmp/build5862458412917915972.tmp/serial_echo.cpp.hex. The big long number will change for every sketch, and probably change with every reboot, but using the basic path template you should be able to find your hex files from now on without enabling verbose compile.
Now, upload this serial_echo.cpp.hex file to your BeagleBone. I use my linux desktop's filemanager's stfp support, and on window I use Notepad++'s built in sftp client. I've also used WinSCP before and it works well. If a mac user would like to recommend a method, I'll add it here.
/tmp/build5862458412917915972.tmp/serial_echo.cpp.elf /tmp/build5862458412917915972.tmp/serial_echo.cpp.hex
The file we want is /tmp/build5862458412917915972.tmp/serial_echo.cpp.hex. The big long number will change for every sketch, and probably change with every reboot, but using the basic path template you should be able to find your hex files from now on without enabling verbose compile.
Now, upload this serial_echo.cpp.hex file to your BeagleBone. I use my linux desktop's filemanager's stfp support, and on window I use Notepad++'s built in sftp client. I've also used WinSCP before and it works well. If a mac user would like to recommend a method, I'll add it here.
Step 4: Create the Upload Script
Now we need the magic. How do you do all that fancy stuff the Arduino GUI does with a FTDI or the Arduino board's USB? The answer, as I've hinted, is avrdude, and you can install it with "apt-get install avrdude" on ubuntu and "ipgk install avrdude" on Angstrom (I think, please correct me if this is incorrect). But this isn't enough. If we use USB, avrdude will handle toggling that reset for us and take care of all the timing itself. TTL serial gives us no such luxury, so we have a nice 2 line script
Line 1:
(echo 0 > /sys/class/gpio/gpio49/value && sleep 0.9 && echo 1 > /sys/class/gpio/gpio49/value) &
This sets the pin low, sleeps for 0.9 seconds, then sets the pin high. The whole thing is in () so that it becomes is a single commandlet. The & at the end will background the commandlet, allowing it to run in its own thread/process, basically forking it. This allows the reset to happen timed so that avrdude can startup, read the hex file, and start pinging for the bootloader. The 0.9 second I arrived at via trial and error, you may need to adjust this.
Line 2:
avrdude -c arduino -p m328p -v -v -v -v -P /dev/ttyO2 -U flash:w:serial_echo.cpp.hex
There's a lot going on here, and I'll let you read the avrdude manual for the gritty details, but it's basically this:
program an arduino with a 328p, be very verbose, use serial port ttyO2 and serial_echo.cpp.hex.
once you get all this working, you can remove some -v's to reduce the verbosity.
In the previous guide I mentioned that the BB as 4 TTY serial ports. It actual has 6, ttyO0-5 (that's a capital letter O), but only 4 are usable on the GPIO pins. These are ttyO1, ttyO2, ttyO4, and ttyO5, which conveniently correspond to Serial1, Serial2, Serial4 and Serial5.
If you run this 2nd line all by itself, you'll see the output end with:
avrdude: Send: 0 [30] [20]
avrdude: Send: 0 [30] [20]
avrdude: Send: 0 [30] [20]
{long pause}
avrdude: ser_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude done. Thank you.
The key is to see the sleep time in the Line1 so that the Arduino bootloader responds to one of those 3 Sends. tricky stuff!
Below is my complete upload.sh script, put it in the same folder you put the hex file and mark it executable (chmod a+x upload.sh):
#!/bin/bash
(echo 0 > /sys/class/gpio/gpio49/value && sleep 0.9 && echo 1 > /sys/class/gpio/gpio49/value) &
avrdude -c arduino -p m328p -v -v -v -v -P /dev/ttyO2 -U flash:w:serial_echo.cpp.hex
Line 1:
(echo 0 > /sys/class/gpio/gpio49/value && sleep 0.9 && echo 1 > /sys/class/gpio/gpio49/value) &
This sets the pin low, sleeps for 0.9 seconds, then sets the pin high. The whole thing is in () so that it becomes is a single commandlet. The & at the end will background the commandlet, allowing it to run in its own thread/process, basically forking it. This allows the reset to happen timed so that avrdude can startup, read the hex file, and start pinging for the bootloader. The 0.9 second I arrived at via trial and error, you may need to adjust this.
Line 2:
avrdude -c arduino -p m328p -v -v -v -v -P /dev/ttyO2 -U flash:w:serial_echo.cpp.hex
There's a lot going on here, and I'll let you read the avrdude manual for the gritty details, but it's basically this:
program an arduino with a 328p, be very verbose, use serial port ttyO2 and serial_echo.cpp.hex.
once you get all this working, you can remove some -v's to reduce the verbosity.
In the previous guide I mentioned that the BB as 4 TTY serial ports. It actual has 6, ttyO0-5 (that's a capital letter O), but only 4 are usable on the GPIO pins. These are ttyO1, ttyO2, ttyO4, and ttyO5, which conveniently correspond to Serial1, Serial2, Serial4 and Serial5.
If you run this 2nd line all by itself, you'll see the output end with:
avrdude: Send: 0 [30] [20]
avrdude: Send: 0 [30] [20]
avrdude: Send: 0 [30] [20]
{long pause}
avrdude: ser_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
avrdude done. Thank you.
The key is to see the sleep time in the Line1 so that the Arduino bootloader responds to one of those 3 Sends. tricky stuff!
Below is my complete upload.sh script, put it in the same folder you put the hex file and mark it executable (chmod a+x upload.sh):
#!/bin/bash
(echo 0 > /sys/class/gpio/gpio49/value && sleep 0.9 && echo 1 > /sys/class/gpio/gpio49/value) &
avrdude -c arduino -p m328p -v -v -v -v -P /dev/ttyO2 -U flash:w:serial_echo.cpp.hex
Step 5: Let It Rip!
now, we run the script!
./upload.sh
if the output ends in something like:
avrdude: safemode read 3, efuse value: 0
avrdude: safemode: efuse reads as 0
avrdude: safemode: Fuses OK
avrdude: Send: Q [51] [20]
avrdude: Recv: . [14]
avrdude: Recv: . [10]
avrdude done. Thank you.
then you have success! the Arduino should reboot and start blinking, run the serial_echo.py from the other guide and make sure it still works.
If you get:
avrdude: ser_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
your sleep time may be off, try adjusting it a tenth of a second or 3 in each direction. If it still won't work, make sure the serial echo still works, you may have a wiring problem.
I did have one case where whatever code was on the chip before was somehow preventing this hack from working. pulling the chip and reflashing the normal way seemed to fix it, and I haven't yet had the problem repeat.
I've also found that super long wires (10 feet?) will cause problems. I've had my serial comms work fine at 9600 but programming fail every time. I've see that there's a way to reprogram the bootloader to run slower, but I have not tried yet.
I've attached a zip file with the 2 scripts I use.
Happy hacking!
./upload.sh
if the output ends in something like:
avrdude: safemode read 3, efuse value: 0
avrdude: safemode: efuse reads as 0
avrdude: safemode: Fuses OK
avrdude: Send: Q [51] [20]
avrdude: Recv: . [14]
avrdude: Recv: . [10]
avrdude done. Thank you.
then you have success! the Arduino should reboot and start blinking, run the serial_echo.py from the other guide and make sure it still works.
If you get:
avrdude: ser_recv(): programmer is not responding
avrdude: stk500_recv(): programmer is not responding
your sleep time may be off, try adjusting it a tenth of a second or 3 in each direction. If it still won't work, make sure the serial echo still works, you may have a wiring problem.
I did have one case where whatever code was on the chip before was somehow preventing this hack from working. pulling the chip and reflashing the normal way seemed to fix it, and I haven't yet had the problem repeat.
I've also found that super long wires (10 feet?) will cause problems. I've had my serial comms work fine at 9600 but programming fail every time. I've see that there's a way to reprogram the bootloader to run slower, but I have not tried yet.
I've attached a zip file with the 2 scripts I use.
Happy hacking!