Uploading over Bluetooth Low Energy

I'm working on sketches uploading over BLE. I had to write my own uploader (looking at avrdude sources with one eye) so i can change uploading behaviour as needed and it's working over USB just perfect but it does not get response from bootloader if trying over Bluetooth LE.

My hardware:

  • Arduino Uno (and i'm having Arduino Mega2560)
  • HM-10 BLE module

As HM-10 does not support DTR/RTS i had to add my own software reboot:

In brief: it reads incoming bytes on interrupt event and compares to reset command passed in ble_setup. If it's equal then it uses watchdog timer to reboot the board. If it's not equal it stores byte in byffer, then sketch loop() just returns incoming byes back ("echo").

#include 

const char *ble_reset_cmd;
int ble_reset_index = 0;
int ble_available = 0;       // chars available
int ble_byte;                // income buffer as 1 char
int *ble_bytes = &ble_byte;  // income buffer
boolean delete_ble_bytes = false;

//#define BLE_DEBUG

// reboot Arduino board using Watchdog timer
void reboot() {
//    #ifdef BLE_DEBUG
    Serial.println("rebooting ...");
//    #endif

//    digitalWrite(13, HIGH);
//    delay(2000);
    
    wdt_enable(WDTO_15MS);
    while(1) { };
}

void ble_setup(int baud, const char *reset_cmd) {
    Serial.begin(baud);
    ble_reset_cmd = reset_cmd;
}

int Serial_available() {
    return ble_available;
}

int Serial_read() {
    if (ble_available < 0)
        return -1;
  
    int inByte = ble_bytes[--ble_available];
    if (ble_available == 0 && delete_ble_bytes) {
        // restore buffer as 1 char buffer
        delete[] ble_bytes;
        ble_bytes = &ble_byte;
        delete_ble_bytes = false;
    }
    return inByte;
}

void serialEvent() {
    if (Serial.available() <= 0)
        return;
        
    int inByte = Serial.read();
    if (inByte == ble_reset_cmd[ble_reset_index]) {
        // entered char is cmd character

        if (ble_reset_cmd[++ble_reset_index] == 0)
            reboot();
      
        #ifdef BLE_DEBUG  
        Serial.write("ble_index = ");
        Serial.print((byte)ble_reset_index);
        Serial.write('\n');
        #endif 
     
    } else {
        // entered char is NOT cmd character
      
        if (ble_reset_index > 0) {
            // got character not from cmd, so we should to return characters
            // starting ble_reset_cmd until current index and then this character
            
            ble_available = ble_reset_index + 1;
            ble_reset_index = 0;
            
            #ifdef BLE_DEBUG
            Serial.print("Copy ");
            Serial.print((byte)(ble_available - 1));
            Serial.println(" bytes");
            #endif
            
            // recreate input buffer using cmd characters
            ble_bytes = new int[ble_available + 1];
            delete_ble_bytes = true;
            ble_bytes[0] = inByte;
            for (int i=0; i<(ble_available-1); i++) {
              ble_bytes[i + 1] = ble_reset_cmd[ble_available - i - 2];
            }
            ble_bytes[ble_available] = 0; // null-terminated array
            
            #ifdef BLE_DEBUG
            Serial.print("input buffer now contains ");
            Serial.print(ble_available);
            Serial.println(" bytes");
            #endif
            
        } else {
        
            #ifdef BLE_DEBUG
            Serial.println("ble_index = 0");
            #endif
      
            // pass inByte next ...
            ble_available++;
            ble_byte = inByte;
        }
    
    }
} 

// ----------------------------------- 

int led = 13;
int ledHIGH = 0;

void setup() {
  ble_setup(9600, "AT+RESET");
  Serial.print("ready\n");
  pinMode(led, OUTPUT);
}

void loop() {
  while (Serial_available() > 0) {
    int inByte = Serial_read();
    
//    Serial.write('>');
//    Serial.write(inByte);
//    Serial.write('\n');
    ledHIGH = 1- ledHIGH;
    digitalWrite(led, ledHIGH);
    Serial.write(inByte); // echo
  }
}

As i said it's working good for USB uploading: right after rebooting bootloader waits for upload commands and start to reply:

2015-04-07 21:22:10.860 Apploader[11226:303] Apploader v0.1 2015-04-07 21:22:12.772 Apploader[11226:303] DTR/RTS supported by Serial, resetting 2015-04-07 21:22:13.075 Apploader[11226:303] draining ... 2015-04-07 21:22:13.327 Apploader[11226:303] drain done 2015-04-07 21:22:13.328 Apploader[11226:303] Send: 0 [0x30] [0x20] 2015-04-07 21:22:13.328 Apploader[11226:303] draining ... 2015-04-07 21:22:13.723 Apploader[11226:303] drain done 2015-04-07 21:22:13.724 Apploader[11226:303] Send: 0 [0x30] [0x20] 2015-04-07 21:22:13.724 Apploader[11226:303] draining ... 2015-04-07 21:22:13.977 Apploader[11226:303] drain done 2015-04-07 21:22:13.978 Apploader[11226:303] Send: 0 [0x30] [0x20] 2015-04-07 21:22:13.979 Apploader[11226:303] Recv: . [0x14] 2015-04-07 21:22:13.980 Apploader[11226:303] Recv: . [0x10] 2015-04-07 21:22:13.980 Apploader[11226:303] Send: A [0x41] . [0x81] [0x20] 2015-04-07 21:22:13.984 Apploader[11226:303] Recv: . [0x14] 2015-04-07 21:22:13.984 Apploader[11226:303] Recv: . [0x04] 2015-04-07 21:22:13.985 Apploader[11226:303] Recv: . [0x10] 2015-04-07 21:22:13.985 Apploader[11226:303] Send: A [0x41] . [0x82] [0x20] 2015-04-07 21:22:13.988 Apploader[11226:303] Recv: . [0x14] 2015-04-07 21:22:13.988 Apploader[11226:303] Recv: . [0x04] 2015-04-07 21:22:13.989 Apploader[11226:303] Recv: . [0x10] 2015-04-07 21:22:13.989 Apploader[11226:303] Send: B [0x42] . [0x86] . [0x00] . [0x01] . [0x00] . [0x01] . [0x01] . [0x01] . [0x03] . [0xff] . [0xff] . [0xff] . [0xff] . [0x00] . [0x80] . [0x04] . [0x00] . [0x00] . [0x00] . [0x80] . [0x00] [0x20] 2015-04-07 21:22:13.996 Apploader[11226:303] Recv: . [0x14] 2015-04-07 21:22:13.997 Apploader[11226:303] Recv: . [0x10] 2015-04-07 21:22:13.997 Apploader[11226:303] Send: E [0x45] . [0x05] . [0x04] . [0xd7] . [0xc2] . [0x00] [0x20] 2015-04-07 21:22:14.000 Apploader[11226:303] Recv: . [0x14] 2015-04-07 21:22:14.001 Apploader[11226:303] Recv: . [0x10] 2015-04-07 21:22:14.003 Apploader[11226:303] Send: P [0x50] [0x20] 2015-04-07 21:22:14.004 Apploader[11226:303] Recv: . [0x14] 2015-04-07 21:22:14.008 Apploader[11226:303] Recv: . [0x10]

But when trying over BLE bootloader stops listening in 200ms and then launches sketch, i can see sketch setup() started as it prints "ready". Also i can see it actually reboots the board as it sends "rebooting".

2015-04-07 21:23:30.287 Apploader[11253:303] Apploader v0.1 2015-04-07 21:23:30.296 Apploader[11253:1503] CBCentralManager state is 5 2015-04-07 21:23:31.297 Apploader[11253:303] BleSerialDevice: open() 2015-04-07 21:23:31.298 Apploader[11253:303] Waiting for peripheral to be discovered ... 2015-04-07 21:23:31.591 Apploader[11253:1503] Found peripheral: 2015-04-07 21:23:31.593 Apploader[11253:1503] Stop scanning 2015-04-07 21:23:31.595 Apploader[11253:1503] Connecting to 2015-04-07 21:23:32.025 Apploader[11253:1503] Connected to peripheral 2015-04-07 21:23:32.041 Apploader[11253:1503] Discovered services for peripheral 2015-04-07 21:23:32.042 Apploader[11253:1503] Discovered service 2015-04-07 21:23:32.043 Apploader[11253:1503] Discovered service 2015-04-07 21:23:32.043 Apploader[11253:1503] Discovered service 2015-04-07 21:23:32.044 Apploader[11253:1503] Discovered characteristics for service 2015-04-07 21:23:32.044 Apploader[11253:1503] Discovered characteristic 2015-04-07 21:23:32.045 Apploader[11253:1503] Subscribed to Rx value 2015-04-07 21:23:32.046 Apploader[11253:303] Connected successfully 2015-04-07 21:23:33.342 Apploader[11253:303] BleSerialDevice: open() 2015-04-07 21:23:33.343 Apploader[11253:303] already connected, exiting 2015-04-07 21:23:33.343 Apploader[11253:303] DTR/RTS supported by Serial, resetting 2015-04-07 21:23:33.594 Apploader[11253:303] Within interval 2015-04-07 21:23:33.595 Apploader[11253:303] Perform reset 2015-04-07 21:23:33.596 Apploader[11253:303] Send: A [0x41] T [0x54] + [0x2b] R [0x52] E [0x45] S [0x53] E [0x45] T [0x54] 2015-04-07 21:23:33.647 Apploader[11253:303] Skip draining after reset 2015-04-07 21:23:33.648 Apploader[11253:303] Send: 0 [0x30] [0x20] 2015-04-07 21:23:33.648 Apploader[11253:303] Draining for 200 ms ... 2015-04-07 21:23:33.649 Apploader[11253:303] Start reading 2015-04-07 21:23:33.712 Apploader[11253:1503] Rx value received 15 bytes: r [0x72] e [0x65] b [0x62] o [0x6f] o [0x6f] t [0x74] i [0x69] n [0x6e] g [0x67] [0x20] . [0x2e] . [0x2e] . [0x2e] . [0x0d] . [0x0a] 2015-04-07 21:23:33.713 Apploader[11253:1503] _characterRead = true 2015-04-07 21:23:33.780 Apploader[11253:1503] Rx value received 6 bytes: r [0x72] e [0x65] a [0x61] d [0x64] y [0x79] . [0x0a] 2015-04-07 21:23:33.780 Apploader[11253:1503] _characterRead = true 2015-04-07 21:23:33.850 Apploader[11253:303] Finish reading 2015-04-07 21:23:33.851 Apploader[11253:303] Drained 21 bytes 2015-04-07 21:23:33.852 Apploader[11253:303] Send: 0 [0x30] [0x20] 2015-04-07 21:23:33.853 Apploader[11253:303] Draining for 200 ms ... 2015-04-07 21:23:33.853 Apploader[11253:303] Start reading 2015-04-07 21:23:33.982 Apploader[11253:1503] Rx value received 2 bytes: 0 [0x30] [0x20] 2015-04-07 21:23:33.983 Apploader[11253:1503] _characterRead = true 2015-04-07 21:23:34.054 Apploader[11253:303] Finish reading 2015-04-07 21:23:34.055 Apploader[11253:303] Drained 2 bytes 2015-04-07 21:23:34.056 Apploader[11253:303] Send: 0 [0x30] [0x20] 2015-04-07 21:23:34.056 Apploader[11253:303] Reading 1 bytes ... 2015-04-07 21:23:34.057 Apploader[11253:303] Start reading 2015-04-07 21:23:34.057 Apploader[11253:303] _characterRead = false 2015-04-07 21:23:34.184 Apploader[11253:1503] Rx value received 1 bytes: . [0x00] 2015-04-07 21:23:34.185 Apploader[11253:1503] _characterRead = true 2015-04-07 21:23:34.186 Apploader[11253:303] Finish reading 2015-04-07 21:23:34.186 Apploader[11253:1503] Rx value received 2 bytes: 0 [0x30] [0x20] 2015-04-07 21:23:34.186 Apploader[11253:1503] _characterRead = true 2015-04-07 21:23:34.186 Apploader[11253:303] Recv: . [0x00] 2015-04-07 21:23:34.187 Apploader[11253:303] stk500_getsync() attempt 1 of 5: not in sync: resp=0x00

Once again - bootloader stops listening in 200 ms instead of 1-1.5 secons as over USB:

2015-04-07 21:23:33.596 Apploader[11253:303] Send: A [0x41] T [0x54] + [0x2b] R [0x52] E [0x45] S [0x53] E [0x45] T [0x54] ... 2015-04-07 21:23:33.780 Apploader[11253:1503] Rx value received 6 bytes: r [0x72] e [0x65] a [0x61] d [0x64] y [0x79] . [0x0a]

I expect bootloader to reply with 0x14 0x10 as over USB:

2015-04-07 21:22:13.978 Apploader[11226:303] Send: 0 [0x30] [0x20] 2015-04-07 21:22:13.979 Apploader[11226:303] Recv: . [0x14] 2015-04-07 21:22:13.980 Apploader[11226:303] Recv: . [0x10]

As you can see later communication over Bluetooth is working good, as i receive exactly what was sent (0x30 0x20)

2015-04-07 21:23:33.852 Apploader[11253:303] Send: 0 [0x30] [0x20] 2015-04-07 21:23:33.853 Apploader[11253:303] Draining for 200 ms ... 2015-04-07 21:23:33.853 Apploader[11253:303] Start reading 2015-04-07 21:23:33.982 Apploader[11253:1503] Rx value received 2 bytes: 0 [0x30] [0x20]

So when sketch is launched it's ok.

My ideas what's wrong: 1. baud rate

Since serial.begin(N) is invoked after bootloader stops and launch sketch bootloader by default uses another baud rate? Is it correct? HM-10 default daud rate is 9600 and in sketch i also use 9600.

  1. wrong wire connection? Bluetooth-arduino: rx -> tx tx -> rx Since its working for sketch i believe it's ok. Do i need any capacitors or resistors?

Any thoughts are highly appreciated!

It seems i've found Uno bootloader source code and bootloader baud rate is 19200 by default?

/* set the UART baud rate */
/* 20060803: hacked by DojoCorp */
//#define BAUD_RATE   115200
#ifndef BAUD_RATE
#define BAUD_RATE   19200
#endif

So i should just set HM-10 baud rate to 19200 using 'AT+BAUD' and that's it?

I'm in stuck as in this tutorial 115200 is used: http://makezine.com/projects/diy-arduino-bluetooth-programming-shield/

in step 8. "build the programmer circuit" R1 2.2K is set on arduino rx pin. Why? Another thing i can't understand is that SoftwareSerial baud rate is set to 38400 but BT module baud rate is set 115200 using "AT" command. why?

I've set HM-10 baud rate to 19200 (using "AT+BAUD1") in order to have the same baud rate in bootloader and in BT module. Unfortunately it did not help.

I'm still having some strange characters from bootloader and bootloader does not wait for 1-1.5 second:

2015-04-08 22:32:29.788 Apploader[16529:303] Send: A [0x41] T [0x54] + [0x2b] R [0x52] E [0x45] S [0x53] E [0x45] T [0x54] 2015-04-08 22:32:29.839 Apploader[16529:303] Draining for 3000 ms ... 2015-04-08 22:32:29.840 Apploader[16529:303] Start reading 2015-04-08 22:32:29.841 Apploader[16529:1503] Rx value received 15 bytes: r [0x72] e [0x65] b [0x62] o [0x6f] o [0x6f] t [0x74] i [0x69] n [0x6e] g [0x67] [0x20] . [0x2e] . [0x2e] . [0x2e] . [0x0d] . [0x0a] 2015-04-08 22:32:29.841 Apploader[16529:1503] _characterRead = true 2015-04-08 22:32:29.916 Apploader[16529:1503] Rx value received 6 bytes: r [0x72] e [0x65] a [0x61] d [0x64] y [0x79] . [0x0a]

I had to increase drain interval to 3 seconds to make uploader doing nothing and just receiving bytes from bootloader. Any thoughts what is:

[0x20] . [0x2e] . [0x2e] . [0x2e] . [0x0d] . [0x0a]

?

It seems that’s the reason why bootloader does not wait :wink:
https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/bootloaders/atmega/ATmegaBOOT_168.c#L286

// Check if the WDT was used to reset, in which case we dont bootload and skip straight to the code. woot.
if (! (ch & _BV(EXTRF))) // if its a not an external reset…
app_start(); // skip bootloader

Finally i was able to do it and you upload over BLE just like over USB now. Check out the latest Apploader app release 2.0

Hello Anton, Do you still support your Apploader iOS app? I was trying to use it with no luck.

Appreciate your support.

Thanks