====== Arduino without a bootloader ======
===== Blessed is the bootloader =====
One of the things that simplifies Arduino development is its [[https://www.arduino.cc/en/Hacking/Bootloader|bootloader]]. A bootloader is a small program that resides at the code start point of a microcontroller and manages what happens immediately on startup or reset of the device.
In the case of the Arduino, the bootloader lets you upload new programs over a standard serial interface. This is done in conjunction with the serial-to-USB converter included on the Uno, Nano, and others, or with an external serial-to-USB converter in the case of the Pro Mini and similar. When you power up or reset one of these Arduino boards, the bootloader starts running and checks to see if a new program is coming in on the serial line. If it is, it writes the new program to the "user program" area of flash memory, that is, the area of flash not reserved for the bootloader. If no new program is incoming, it jumps execution to the start of the "user program" area.
But a bootloader isn't the only way to program the microcontrollers on these boards. Instead, you can use an external programming device (a.k.a, //programmer//) that connects your computer to multi-function programming pins on the microcontroller and uploads code directly into flash storage. In fact, the bootloader is itself nothing more than a small program that needs to be written into the microcontroller's flash. Programming factory-fresh ATmega microcontrollers with an Arduino bootloader is typically done with such a programmer.
So while it's entirely possible to program many Arduino boards without using a bootloader, you would have to tie up one USB port on your computer for the programmer and another for serial communication (if you are using serial communication). You would also need separate hardware for both of these. So for this and other reasons, the bootloader is a great convenience.
===== Cursed is the bootloader =====
In spite of the convenience a bootloader offers, there are times when you might want to forego this and program a microcontroller directly. The most common reason is that you want to be able to use //all// of the available flash storage for your program. Bootloaders in ATmega chips are stored in the same flash memory as "user programs." While the amount of required memory is small---for example, the [[https://github.com/Optiboot/optiboot|Optiboot]] bootloader used in common Arduinos is a mere 512 bytes---sometimes you just need to use every last byte of flash on the micro for your program.
Another reason to not use a bootloader is that the startup time is critical. The Arduino bootloader introduces about a second and a half delay between power-up/reset and running your program. Without the bootloader, your code starts running almost instantaneously.
Yet another situation where you might not want a bootloader is if you have lowered or disabled the [[https://microchipdeveloper.com/8avr:bod|Brown-Out Detection]] so you can use every last ounce of battery juice in a portable application.((Disabling brown-out detection or lowering the threshold of detection involves [[arduino:atmega328p_arduinos_and_custom_fuse_settings|custom fuse settings]] and is beyond the scope of this article.)) Without brown-out detection, the erratic system behavior you can expect at very low voltages could cause a program jump to an arbitrary part of the code in flash storage. This could be a jump into bootloader code if the micro is using a bootloader. This then has the potential to start a cascade of events that leads to the bootloader overwriting your program code. If you're not using a bootloader, the code you have programmed can't be corrupted this way.((Not using a bootloader in this scenario will keep the program memory form becoming corrupted, but EEPROM and other things can still be corrupted when the voltage falls below the minimum for a given clock frequency. The risks are described more completely in the [[http://ww1.microchip.com/downloads/en/Appnotes/doc1051.pdf|AVR180: External Brown-out Protection]] application note.))
There are situations apart from these where you wouldn't want to use a bootloader that you might discover as you become more experienced with Arduino and microcontroller programming. For whatever reason, if you want to explore how to upload programs to ATmega-based Arduinos without a bootloader, then read on.
===== Hardware =====
To program an ATmega328P without a bootloader, you're going to need a device you want to program (e.g., an Arduino board) and a device with which you will do the programming (i.e., the programmer). You can see the complete list of programmers supported by the Arduino framework by looking at the list available under //Tools > Programmer: "xxxx"//. At the time of writing this, the available options were:
{{ :arduino:arduino-programmer-options.png?direct |}}
That's a lot of options!
==== USBtinyISP ====
Below, I cover a few cases to get you started. Most of these use the USBtinyISP.((As near as I can tell, the original source of the USBtinyISP design is Dick Streefland's [[https://dicks.home.xs4all.nl/avr/usbtiny/|USBtiny]]. Additional development and refinement leading to the [[https://learn.adafruit.com/usbtinyisp|USBtinyISP]] was done by Limor Fried. The software and hardware are open source (GPL and CC BY-SA 2.5), leading to many clones and derivatives.)) Owing to its low cost and wide availability, the USBtinyISP is one of the most popular options. The [[https://learn.adafruit.com/usbtinyisp|original USBtinyISP]] is available sporadically through Adafruit, and clones derived from its open source design along the lines of [[https://www.ebay.com/itm/400368767664|this one]] are widely available at auction sites, Amazon, Banggood, etc. All my testing was done using USBtinyISP clones from two different suppliers.
The original USBtinyISP has a built-in 3x2 IDC cable that carries the signals needed for ISP (in-system programming) and a 5x2 IDC cable that carries the same signals but in a different layout. If you're using a USBtinyISP clone, there's a good chance it has a keyed 3x2 and 5x2 headers instead of build-in cables. The following assumes this is the case and that you are using a 3x2 keyed IDC cable. Such a cable is often included with USBtinyISP clone purchases. Finally, I'm assuming the USBtinyISP works at 5VDC, which is the defacto standard.
Depending on the board you are programming, you may find it easier to use Dupont wires (M to F or F to F) instead of the IDC cable.
If you are a Linux user, see [[arduino:arduino_on_linux|Arduino on Linux]] for important information related to using the USBtinyISP.
===== Procedures =====
As is the case when uploading a program to your Arduino using a bootloader, when uploading a program using a programmer you must specify what the target board is. So when doing the following, make sure //Tools > Board// and //Tools > Processor// (or other board options) are set correctly for your board.
Now is also a good time to mention a significant caveat about using a programmer as described here.
Using a programmer involves making connections between the programmer and the Arduino's RST, SCK, MOSI, and MISO pins. If these pins are not used in your application, then programming should be a breeze. However, if these pins //are// used (e.g. for SPI), then you will probably need to disconnect those connections when you are uploading programs.
This can be bit of a drag and is yet another way the bootloader makes your life simpler.
With that out of the way, let's move on to some specific cases.
==== Case 1: Uno or Nano with USBtinyISP ====
Tested with an Uno R3.
Directly uploading a sketch to an Uno or Nano using a USBtinyISP is pretty straightforward:
- Configure your IDE to use the USBtinyISP: //Tools > Programmer "xxx"// and select //USBtinyISP//
- Unplug the USBtinyISP from the computer if it's connected and power down the Uno/Nano if it's powered up.
- Connect USBtinyISP's ISP interface to the Uno/Nano's ICSP header.
* The ICSP header is the 3x2 header on the end of the board opposite the USB jack. Connect the USBtinyISP's IDC cable to that header, but make sure it's the right way around. The easiest way to confirm the orientation is with all power off to connect the cable one way, and then test that the two boards' GNDs are connected with a multimeter. If not, flip the plug around and test again.
- Plug the USBtinyISP into your computer. This will power the Uno/Nano (and everything else) from the USBtinyISP's 5V VCC.
- Use the Arduino IDE to //Sketch > Upload Using Programmer//.
- Power down everything and disconnect the USBtinyISP from the Uno/Nano.
- Power up the Uno/Nano and enjoy.
- To re-program, power down the Uno/Nano and go back to **step 3**.
A caution about power is in order. The 5V VCC supplied from the USBtinyISP to the Arduino is good for about 100mA. This is enough for many situations, but if you have peripherals or other things sucking a lot of power, it may not be. In this case, be prepared to do a fair amount of hoop-jumping to disconnect the power sucking devices during programming or power the Arduino from a source other than the USBtinyISP during programming.
==== Case 2: Pro Mini 5V/16MHz with USBtinyISP ====
Tested.
The Pro Mini lacks the ISP header found in the Uno and Nano, so the process is a little more involved, but not much.
To connect the USBtinyISP to the Pro Mini you can use Dupont cables, M to F or F to F depending on your programmer. While Dupont cables work fine, you will probably end up connecting and disconnecting the Arduino to and from the USBtinyISP quite a lot. So you might want to build up a custom 3x2 header that you can leave connected to your system and then use the IDC cable to connect and disconnect the two devices. It's your choice.
In either case, the process is the same:
- Configure your IDE to use the USBtinyISP: //Tools > Programmer "xxx"// and select //USBtinyISP//
- Unplug the USBtinyISP from the computer if it's connected and power down the Pro Mini if it's powered up.
- Connect USBtinyISP's ISP header to the Pro Mini:
* Connect GND on the header to the Pro Mini's GND.
* Connect VCC on the header to the Pro Mini's VCC. //IMPORTANT: Do not connect this to the Pro Mini's RAW input.//
* Connect RST to the Pro Mini's RST
* Connect SCK to the Pro Mini's D13
* Connect MISO to the Pro Mini's D12
* Connect MOSI to the Pro Mini's D11
- Plug the USBtinyISP into your computer. This will power the Pro Mini (and everything else) from the USBtinyISP's 5V VCC.
- Use the Arduino IDE to //Sketch > Upload Using Programmer//.
- Power down everything and disconnect the USBtinyISP from the Pro Mini.
- Power up the Pro Mini and enjoy.
- To re-program, power down the Pro Mini and go back to **step 3**.
The caveats mentioned in Case 1 about powering an entire system from the USBtinyISP apply here as well.
==== Case 3: Pro Mini 3.3V/8MHz with USBtinyISP ====
Most programmers based on the USBtinyISP assume you are working at 5VDC. This presents a bit of an issue if you are using a 3.3V/8MHz Pro Mini because the I/O of the ATmega328P is not 5V tolerant when the VCC is 3.3V. In spite of this, there are a couple approaches to using an USBtinyISP to program a 3.3V Pro Mini, covered below.
As was the case in Case 2, to connect the USBtinyISP to the Pro Mini you can use Dupont cables, M to F or F to F depending on your programmer. While Dupont cables work fine, you will probably end up connecting and disconnecting the Arduino to and from the USBtinyISP quite a lot. So you might want to build up a custom 3x2 header that you can leave connected to your system and then use the IDC cable to connect and disconnect the two devices.
=== Method 1: Leverage the jumper ===
Tested.
Version 2 of the [[https://learn.adafruit.com/usbtinyisp|Adafruit USBtinyISP]] as well as many clones have a jumper that disconnects the internal 5V VCC from the programming headers //and// shifts the level of the programmer's I/O to the VCC applied at the ISP header. This means you can connect the 3.3V VCC used by your 3.3V Pro Mini to the programmer and all will be fine.
**Make sure your USBtinyISP level shifts the I/O before attempting this method.**
Version 1 of the Adafruit USBtinyISP has a jumper that disconnects the internal VCC from the programming headers, but it does //not// level-shift the I/O. All the thrid-party USBtinyISP clones I've encountered have the VCC jumper as well as the needed circuitry to level shift the I/O. But it's entirely likely that there are some that don't.
Assuming you have a USBtinyISP that has working level-shifting on the I/O, remove the VCC jumper from the USBtinyISP. This is J3 on the Adafruit USBtinyISP. On typical third party USBtinyISP boards it's adjacent to the 3x2 ISP header.
Then here's what to do:
- Configure your IDE to use the USBtinyISP: //Tools > Programmer "xxx"// and select //USBtinyISP//
- Unplug the USBtinyISP from the computer if it's connected and power down the Pro Mini if it's powered up.
- Connect USBtinyISP's ISP header to the Pro Mini:
* Connect GND on the header to the Pro Mini's GND.
* Connect VCC on the header to the Pro Mini's VCC. //IMPORTANT: Do not connect this to the Pro Mini's RAW input.//
* Connect RST to the Pro Mini's RST
* Connect SCK to the Pro Mini's D13
* Connect MISO to the Pro Mini's D12
* Connect MOSI to the Pro Mini's D11
- Plug the USBtinyISP into your computer.
- Power up the Pro Mini (either 3.3V to the VCC pin or something higher to the RAW pin).
- Use the Arduino IDE to //Sketch > Upload Using Programmer//.
- Power down everything and disconnect the USBtinyISP from the Pro Mini.
- Power up the Pro Mini and enjoy.
- To re-program, power down the Pro Mini and go back to **step 3**.
Because the Pro Mini and peripherals are powered by the whatever supply will be used in normal use, caveats mentioned in Case 1 about powering an entire system from the USBtinyISP do not apply.
=== Method 2: Use 5V anyway ===
Tested.
If your USBtinyISP doesn't have the option of shifting the I/O to the target's VCC (or if you're not sure), you might still be able to use a 5V USBtinyISP. What we are going to do is program the Pro Mini at a VCC or 5V but operate it at 3.3V. This approach only works fine if everything else connected in your system is OK with 5V VCC. So if you have a peripheral that's 3.3V only, then this approach will not work.
Assuming you're in the green zone for this approach, then here's what you do:
- Configure your IDE to use the USBtinyISP: //Tools > Programmer "xxx"// and select //USBtinyISP//
- Unplug the USBtinyISP from the computer if it's connected and power down the Pro Mini if it's powered up.
- Connect USBtinyISP's ISP header to the Pro Mini:
* Connect GND on the header to the Pro Mini's GND.
* Connect VCC on the header to the Pro Mini's VCC. //IMPORTANT: Do not connect this to the Pro Mini's RAW input.//
* Connect RST to the Pro Mini's RST
* Connect SCK to the Pro Mini's D13
* Connect MISO to the Pro Mini's D12
* Connect MOSI to the Pro Mini's D11
- Plug the USBtinyISP into your computer. This will power the Pro Mini (and everything else) from the USBtinyISP's 5V VCC.
- Use the Arduino IDE to //Sketch > Upload Using Programmer//.
- Power down everything and disconnect the USBtinyISP from the Pro Mini.
- Power up the Pro Mini with 3.3V and enjoy.
- To re-program, power down the Pro Mini and go back to **step 3**.
The caveats mentioned in Case 1 about powering an entire system from the USBtinyISP apply here as well and are made more complicated than the "program at 5V, use at 3.3V" protocol. This makes this approach pretty fraught, but it can be a lifesaver if you need it.
==== Case 4: Pro Mini 3.3V/8MHz with Pro Mini 3.3V/8MHz as programmer ====
I have not tested this case yet.
You can use an Arduino board as a programmer using the "Arduino as ISP" sketch. The process of configuring and connecting an Arduino as a programmer is described in the [[https://www.arduino.cc/en/tutorial/arduinoISP|Arduino as ISP and Arduino Bootloaders]] tutorial.
You should be able to use a 3.3V/8MHz Pro Mini as the "Arduino as ISP", thereby providing your target 3.3V/8MHz Pro Mini with voltage levels it will be happy with.
===== Getting your bootloader back =====
When you upload a program using a programmer, you overwrite the bootloader. This means your board will no longer work with the conventional //Sketch > Upload// command in the IDE. However, getting the bootloader back is straightforward.
Uploading a bootloader to your micro is almost identical to uploading a program using a programmer. The only difference is that instead of uploading a program using //Sketch > Upload Using Programmer//, you upload the appropriate bootloader for your board using //Tools > Burn Bootloader//. As was the case when uploading programs, before you upload the bootloader, you must confirm that //Tools > Board// and //Tools > Processor// or other options are set appropriately for your board.
After uploading the bootloader, you should be able to use //Sketch > Upload// again just as you always have.