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
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.
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
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.
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
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!
9 Comments
7 years ago
Hello great article, i rebuild you project to make it work some sensor with arduino and pass data to BBB.
I seem to have a good serial communication without level shifting GND and VCC,
i left only RX and TX attached ... and the transmission is still reliable, do you think there my be any issue with that?
Albert
Reply 7 years ago
if you're putting 5V TTL into the BB's serial pins you're going to have longevity issues for the whole board.
8 years ago on Step 5
Great article! Just what I was looking for. I do have one question, however.
I am using an ATMega328P on a custom BBB cape as a Watchdog for my BBB server. I want to be able to do remote SW updates to the ATMega328P and this seems like a good approach with one exception: I don't want to use a single GPIO to reset the watchdog - it would be too exposed to a rogue program and might be susceptible if the BBB is power cycled by the Watchdog.
What I'd like to do is use the serial command channel (AKA UART4) to permit the BBB to 'properly' tell the Watchdog to reboot to accept a SW update using avrdude. As I understand it, the Watchdog can use its own watchdog timer to force a self reset that will bring up the bootloader. Is this correct and do you see any pitfalls with this approach?
Tom
Reply 8 years ago on Step 5
hm, interesting plan. timing might be tricky, but that's tricky anyway. you'd need to send the arduino a message that would trigger something that causes the watchdog to trigger a reset, then time the avrdude command so it happens as it resets.
the "proper" way is to have a gpio pin assigned as DTR for that uart and let avrdude handle it, and this should be possible but I've only seen people fail to make it work.
another option is to forget the bootloader and use ICSP and program it directly.
Reply 8 years ago on Step 5
Thanks for the fast response!
Just how tricky is the timing? ie, how long does the bootloader look for 'new code' before it gives up and starts the old code? Also, does avrdude have a timeout parameter?
My thinking is that the operation would be more or less atomic. The BBB has new code to load and runs a script that does the following:
1. Sends a request to UART4 to effectively say, "I want to load new code"
2. Watchdog replies with "OK" or not...
3. If "OK", then there might be one more level of handshake if necessary, but otherwise, the Watchdog starts rebooting while avrdude gets launched in BBB.
I'm not planning on using this for inital program load, but only for updates to the Watchdog that already has some sort of handshake code in place.
Tom
Reply 8 years ago on Step 5
good question, so I looked it up. https://code.google.com/p/optiboot/wiki/HowOptiboo...
on watchdog reset it doesn't even try to look for commands, so that won't work. possibly driving reset low with an IO would do it. then you get 1 second. avrdude tries a reset 3 times before giving up.
of course you could always modify optiboot to pause longer...
9 years ago on Introduction
Have you tried to run Arduino IDE on the BeagleBone?
Reply 9 years ago on Introduction
I only have the original, no video output, so no. but that would also require USB, and this is about not using USB.
Reply 9 years ago on Introduction
My bad. I didn't catch that little detail. I am dreaming of a super mini MCU programming station. I bought a super cheap outdated netbook ($40) running android, turns out it doesn't have the right kind of usb connection. My next thought is a BBB or RPi.