Over-the-Air Arduino firmware updates using Firebase, Part 1

We don’t need no stinking cables!

I mean, one cable is usually enough, but you get the idea.

In my last post, I talked about how you can use Google Firebase to control an Internet-connect Arduino project without running any servers at all — all of the data is hosted by Firebase. In this article, I’ll talk about how you can use Firebase to enable over-the-air updating of your Arduino firmware, allowing your devices to magically download new firmware and reflash themselves without plugging them into a laptop. This is a great party trick and is sure to impress all your friends. (Well, if your friends are anything like mine.)

Here’s the idea. Your Arduino code periodically calls home to Firebase over the Internet and asks it whether the firmware version it is currently running is the version it should be running. If it is, there’s nothing more to do (except re-poll sometime later). If not, it downloads a new firmware image, stores it to local flash, and once the download is complete (and verified) reboots into the new program. Voilà!

In this series of articles, I’m going to walk you through how I do this in my projects. There are no doubt other (and possibly better) ways to achieve the same result, but I’m pretty happy with it and hope others find this useful.

In this article, I’ll talk about the basic setup for using Firebase to host Arduino firmware images, and some sample code for over-the-air updates.

In Part 2, I’ll talk about supporting multiple firmware versions and automatically detecting when a firmware update is needed.

Prerequisites

I’m assuming that you’re using an Arduino device with some kind of Internet connectivity (WiFi, Ethernet cable, carrier pigeon, whatever). I use the HUZZAH32 Feather development board from Adafruit, which has built-in WiFi.

You need some library to flash a new firmware image onto the device and reboot into it. This is going to vary by device. The HUZZAH32 (based on the Espressif ESP32 chipset) has a nifty Arduino library that does just this. The library provides an interface where you write a stream of bytes representing the new firmware image. It takes care of staging the new firmware on the device’s flash, and computing an MD5 checksum to verify that the write has completed successfully.

Finally, you need to set up Firebase for your Arduino project. Check out my other tutorial on using Firebase for controlling Arduino devices for this — you just need to follow the steps under “Setting up Firebase”.

Uploading a firmware image to Firebase

First, we need a way to upload a new firmware binary to Firebase, so it can be fetched by your Arduino devices over the Internet.

One of Firebase’s key features is Firebase Cloud Storage, which lets you store arbitrary binary blobs which can be retrieved over the Internet using a simple HTTP request.

A manual way to upload a new binary to Firebase is to visit the Firebase console, click on “Storage” in the left nav bar, and then click on the “Upload file” button:

Using the Firebase console to upload files to Firebase Cloud Storage.

In this case, you want to upload the .ino.bin file that is generated by Arduino when you compile your sketch — the location of this is going to depend on your Arduino IDE setup. To simplify finding it, I recommend editing your Arduino preferences.txt file and setting the build.path parameter to a directory that is easy to find, so you don’t need to go hunting around your filesystem for the binary. For example,

build.path=/Users/mdw/arduino_builds

will store all compiled Arduino sketches to the directory /Users/mdw/arduino_builds.

Over-the-air firmware updates

Say you have a sketch called AwesomeProject.ino and have uploaded the binary to Firebase as AwesomeProject.ino.bin. Now you need your Arduino devices to pull down the new binary from Firebase and reboot into it.

First, we need to get the URL for the binary that we uploaded above. In the Firebase console, when you click on a file in the Cloud Storage list, look for the “Download URL” in the box that pops up to the right.

Getting the download URL for a file in Firebase Cloud Storage.

This is is going to give you a URL that looks something like this:

https://firebasestorage.googleapis.com/v0/b/your-project.appspot.com/o/AwesomeProject.ino.bin?alt=media&token=836b2515-2000-473f-a5ff-bf9c38b958c9

If you want, you can try downloading it by pasting the URL into your browser and confirming that the correct file is retrieved.

All we have to do is have our Arduino code pull the contents of this URL down over the Internet and use it to flash the firmware. On ESP32-based hardware like the Feather HUZZAH32, this code would look like the following:

/* Note that this code is specific to ESP32-based boards like
* the Feather HUZZAH32. Some modifications may be required
* for different boards and support libraries.
*/
#define FIRMWARE_URL \
"https://firebasestorage.googleapis.com/v0/b/your-project.appspot.com/o/AwesomeProject.ino.bin?alt=media&token=836b2515-2000-473f-a5ff-bf9c38b958c9"
#include "Update.h"
#include "WiFi.h"
#include "HTTPClient.h"
void updateFirmware() { // Start pulling down the firmware binary.
http.begin(FIRMWARE_URL);
int httpCode = http.GET();
if (httpCode <= 0) {
Serial.printf("HTTP failed, error: %s\n",
http.errorToString(httpCode).c_str());
return;
}
// Check that we have enough space for the new binary.
int contentLen = http.getSize();
Serial.printf("Content-Length: %d\n", contentLen);
bool canBegin = Update.begin(contentLen);
if (!canBegin) {
Serial.println("Not enough space to begin OTA");
return;
}
// Write the HTTP stream to the Update library.
WiFiClient* client = http.getStreamPtr();
size_t written = Update.writeStream(*client);
Serial.printf("OTA: %d/%d bytes written.\n", written, contentLen);
if (written != contentLen) {
Serial.println("Wrote partial binary. Giving up.");
return;
}
if (!Update.end()) {
Serial.println("Error from Update.end(): " +
String(Update.getError()));
return;
}
if (Update.isFinished()) {
Serial.println("Update successfully completed. Rebooting.");
// This line is specific to the ESP32 platform:
ESP.restart();
} else {
Serial.println("Error from Update.isFinished(): " +
String(Update.getError()));
return;
}
}

Summary

In this first installment, I’ve shown you how to manually upload an Arduino binary to Firebase, and how to write some Arduino code to pull that file down and do an over-the-air update.

In Part 2, I’ll talk about how to support multiple firmware versions, as well as how to modify your Arduino code so it can automatically detect that it needs to perform an update.

Let me know in the comments if you have any questions or suggestions!

--

--

--

Tutorials, deep-dives, and random musings from Firebase developers all around the world. Views expressed are those of the authors and don’t necessarily reflect those of Firebase or its parent companies.

Recommended from Medium

Building a Board Game in Unity

Looking Back at 2019 with IPinfo

Internet of Things  — from zero to DIY Bitcoin Ticker

Querying TB sized External Tables with Snowflake

SAP Application Migration to Azure Cloud: what should we consider ??

Why Is Everyone Talking About App Monitoring with deeper transaction traceability

Why is everyone talking about app monitoring with deeper transaction tracebility

A Brief Intro to AWS S3

Access Key ID and Secret Access Key

How to start using Curl and why: a hands-on introduction

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Matt Welsh

Matt Welsh

VP of Engineering at OctoML.ai, building compilers for fast AI. Ex-Google engineering director, Ex-Apple. Systems hacker and drinker of beer.

More from Medium

Coreflux and No Code — A full Edge IoT Management utility in 30 minutes

Configure MongoDB Realm to perform CRUD operations in our app using GraphQL

Heartbeat Newsletter: Volume 7

Introducing Firebase CTL