Skip to content

Reliable Bluetooth OTA upload of an Arduino pro using an HC-05

Notifications You must be signed in to change notification settings

mgurzixo/flashota

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

THE PROJECT

I wanted to be able to pilot and update OTA (Over The Air) an Arduino PRO mini using Bluetooth and a cheap HC-05 clone. Doing so allows to update easily a card located behind a dashboard, or sealed in a waterproof container, and use a phone as the user interface...

It seemed simple, but it turned out to be very difficult.

WARNING: This project is not for beginners; it involves electronics, Linux shell commands, soldering, interrupts and MCU programming at low level, recompiling a bootloader and patching the Arduino GUI. It is NOT packaged and is more a proof of concept and a collection of notes than a finished product. Do NOT try it if you do not understand any of those words!!!

A first try was to follow one of the many tutos like this one and write a small sketch which echoes received characters incremented (ie. an 'a' is echoed as 'b'), and test using a communication program such as the excellent CuteCom (I am a Linux user)

I needed to setup Bluetooth on Ubuntu, and create a serial communication. I installed a cheap Bluetooth dongle which was hopefully recognised, and wired the HC-05 to the Arduino as explained.

Bluetooth serial communication under Linux is not so simple, but following this tuto worked well. It involves discovering and pairing using bluetoothctl, then creating a device using rfcomm.

I reprogrammed the HC-05 by following this tuto (Do not forget to use CR+LF for line ends, and use UPPERCASE commands!) So that STATE mimicks the DTR line by going low when the connection is established. Here is the list of AT commands of the HC-05.

I then connected the Arduino to Linux using a serial dongle, flashed my simple sketch and validated that, when being connected to the card using the serial dongle and CuteCom on /dev/ttyUSBx, I got my characters echoed with 1 added (ie: 'aaa' -> 'bbb')

Then I connected the Arduino to the HC-05, and fired CuteCom onto /dev/rfcomm0. It worked and I was able get my characters echoed OTA :).

The real problems started when I tried to upload a sketch OTA: it simply did not work!

The first reason was that the STATE pin of the HC-05 was NOT connected! Very often, the HC-05 chinese clones are crappy, and the PIO9 pin (when looking at the module with the antenna on top, it is the 4th one from the top left of the piggyback module) of the tiny module is not soldered to the PCB. You have to cut the plastic wrapping and add a blob of solder to connect it to the PCB. PIO9 is pin4, as shown here:

IMG_20211130_124516

After that, the STATE pin went low when the connection was established; but it did NOT reset the MCU !

The reason is the STATE voltage: The module inside the HC-05 is alimented in 3.3V, but uses internally 1.8V, according to an old datasheet. That was effectively the case, and, when looking with a scope at the RESET signal, it went barely below 3.5V, which is out of specification for 5V logic. OK, so let's amplify the signal.

I found This schematic which seemed too complicated, and ended up using this very simple schema: image

The collector is directly connected to the DTR of the Arduino, saving a capacitor ;).

Obviously,this is an inverting amplifier, so I had to reprogram the HC-05 to its normal mode (AT+POLAR=0,0) and the MCU did reset reliabily each time the connection was established.

Unforunately OTA uploading worked only one time out of ten, on average!

When looking at TX and RX on the scope, triggered by RESET, I found that the handshake for uploading was not correct most of the time, and the arduino bootloader exited while AVRDUDE was still sending sync chars.

I suspect that this is caused by some line noise or an HC-05 "feature"...

Arduino uses the STK500 protocol for uploading. I needed more delay in the bootloader, so time to flash another bootloader!

Optiboot is a GREAT program, but the Arduino GUI is very complicated internally. This post was a great help for changing the timeout from 1 second to 8 seconds. As added benefits, I gained more programming space (Optiboot is only 512 bytes), and increased upload speed from 57600 to 115200 bauds.

Finally, upload worked reliably, but the price to pay was a startup time of 8 seconds in the normal case (no flashing) and some electronics.

Then I remembered that the ATMEGA328P has a little known feature called the analog comparator, and an internal Vref of about 1.1V, which sits nicely between the 2 values (1.8V and 0V) of STATE. In addition, Optiboot allows for activating the bootloader by software. There is even a demo program for testing this.

So I removed the electronics, connected directly the STATE pin to the AIN1 (pin 7) of the Arduino PRO, downloaded the nice analogComp library, added a couple of lines (analogComparator.setOn() + analogComparator.enableInterrupt()) and an ISR setting a flag when the connection was established, and it worked!

CAVEAT: WDT must be cleared in setup(), otherwise we have an infinite reboot loop.

Problem: the MCU did reset correctly, but did not entered into bootloading most of the time. I recompiled Optiboot, setting the timer to 8 seconds, and OTA bootloading worked reliably!

Time to have a look to Optiboot source code!

When calling reset, and if MCUSR == 0, we arrive directly at line 845, then after some initialisations and setting the watchdog to the timer value, we enter the bootloader main loop at line 923.

There, the function verifySpace() is called from lots of places, and, if something is even slightly wrong, resets the MCU and starts the user sketch.

This behaviour is even documented:

All other commands will result in a success response if they are immediate followed by CRC_EOP (0x20, ' ') (ie they are ignored), or a WDT reset (start applicatin) otherwise.

That's what is called a feature!!!

So I put back timer to 1 second and rewrote verifySpace() according to the STK500 protocol to tell avrdude that we have not understood the command:

void verifySpace() {
  if (getch() != CRC_EOP) {
#ifdef CLASSIC_VERIFY_SPACE
    watchdogConfig(WATCHDOG_16MS);    // shorten WD timeout
    while (1)            // and busy-loop so that WD causes
      ;              //  a reset and app start.
#else
    putch(STK_NOSYNC);
#endif
  }
  else {
    putch(STK_INSYNC);
  }
}

et voila! it worked perfectly, quickly and reliably.

As an added benefit, Optiboot is now 2 bytes smaller :)

I raised Optiboot issue #333 concerning this patch.

Now, time to integrate this in the Arduino GUI.

The simplest way to do it is to install the superb MiniCore package from MCUdude and patch it.

For doing that, patch the above function in ~/.arduino15/packages/MiniCore/hardware/avr/2.1.3/bootloaders/optiboot_flash/optiboot_flash.c, and recompile the 8977 !!! bootloaders using the makefile provided in that directory.

As a courtesy and for doing a quick experiment, I have copied my patched MiniCore bootloaders directory in patched_bootloaders .

I finally removed the analogComp library and programmed the analog comparator by hand (function setupAC() and ISR(ANALOG_COMP_vect) in order to have a smaller sketch.

The result is the flashota sketch.

This approach have some other PROs/CONs:

  • PRO: It is now possible to enable/disable in software the flash update (could be password protected)
  • PRO: The sketch is aware of the connection status and can act accordingly.
  • CON: Never forget to put the setupAC()/ISR, otherwise it will be impossible next time to reflash OTA !!!

CAVEAT: Do not forget to load this sketch the first time using a direct serial line ;)

Have fun!