-
-
Save gjlawran/0488d55901a89a45d2460bacad02fb2e to your computer and use it in GitHub Desktop.
ESP8266 Arduino project template with optional OTA firmware update
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* ESP8266 project template with optional: | |
* - WiFi config portal - auto or manual trigger | |
* - OTA update - Arduino or web server | |
* - Deep sleep | |
* - Process timeout watchdog | |
* | |
* Copyright (c) 2016 Dean Cording <[email protected]> | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
* THE SOFTWARE. | |
*/ | |
/** | |
* ESP8266 Pin Connections | |
* | |
* GPIO0/SPI-CS2/_FLASH_ - Pull low for Flash download by UART | |
* GPIO1/TXD0/SPI-CS1 - _LED_ | |
* GPIO2/TXD1/I2C-SDA/I2SO-WS - | |
* GPIO3/RXD0/I2SO-DATA - | |
* GPIO4 - | |
* GPIO5/IR-Rx - | |
* GPIO6/SPI-CLK - Flash CLK | |
* GPIO7/SPI-MISO - Flash DI | |
* GPIO8/SPI-MOSI/RXD1 - Flash DO | |
* GPIO9/SPI-HD - Flash _HD_ | |
* GPIO10/SPI-WP - Flash _WP_ | |
* GPIO11/SPI-CS0 - Flash _CS_ | |
* GPIO12/MTDI/HSPI-MISO/I2SI-DATA/IR-Tx - | |
* GPIO13/MTCK/CTS0/RXD2/HSPI-MOSI/I2S-BCK - | |
* GPIO14/MTMS/HSPI-CLK/I2C-SCL/I2SI_WS - | |
* GPIO15/MTDO/RTS0/TXD2/HSPI-CS/SD-BOOT/I2SO-BCK - Pull low for Flash boot | |
* GPIO16/WAKE - | |
* ADC - | |
* EN - | |
* RST - | |
* GND - | |
* VCC - | |
*/ | |
#include <Arduino.h> | |
String reqString; // define a request string to build and use to make OTA request | |
// Optional functionality. Comment out defines to disable feature | |
#define WIFI_PORTAL // Enable WiFi config portal | |
//#define ARDUINO_OTA // Enable Arduino IDE OTA updates | |
#define HTTP_OTA // Enable OTA updates from http server | |
#define LED_STATUS_FLASH // Enable flashing LED status | |
//#define DEEP_SLEEP_SECONDS 5 // Define for sleep period between process repeats. No sleep if not defined | |
#define STATUS_LED 2 // Built-in blue LED on pin 2 | |
#include <ESP8266WiFi.h> | |
#ifdef WIFI_PORTAL | |
#include <DNSServer.h> // Local DNS Server used for redirecting all requests to the configuration portal | |
#include <ESP8266WebServer.h> // Local WebServer used to serve the configuration portal | |
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager WiFi Configuration Magic | |
WiFiManager wifiManager; | |
#define WIFI_PORTAL_TRIGGER_PIN 4 // A low input on this pin will trigger the Wifi Manager Console at boot. Comment out to disable. | |
#else | |
#define WIFI_SSID "SSID" | |
#define WIFI_PASSWORD "password" | |
#endif | |
#ifdef ARDUINO_OTA | |
/* Over The Air updates directly from Arduino IDE */ | |
#include <ESP8266mDNS.h> | |
#include <WiFiUdp.h> | |
#include <ArduinoOTA.h> | |
#define ARDUINO_OTA_PORT 8266 | |
#define ARDUINO_OTA_HOSTNAME "esp8266" | |
#define ARDUINO_OTA_PASSWD "123" | |
#endif | |
#ifdef HTTP_OTA | |
/* Over The Air automatic firmware update from a web server. ESP8266 will contact the | |
* server on every boot and check for a firmware update. If available, the update will | |
* be downloaded and installed. Server can determine the appropriate firmware for this | |
* device from any combination of HTTP_OTA_VERSION, MAC address, and firmware MD5 checksums. | |
*/ | |
#include <ESP8266HTTPClient.h> | |
#include <ESP8266httpUpdate.h> | |
#define HTTP_OTA_ADDRESS F("sub.domain.com") // Address of OTA update server | |
#define HTTP_OTA_PATH F("/esp8266-ota/update") // Path to update firmware | |
#define HTTP_OTA_PORT 1880 // Port of update server | |
// Name of firmware | |
// #define HTTP_OTA_VERSION String(__FILE__).substring(String(__FILE__).lastIndexOf('/')+1) + ".generic" //unix | |
#define HTTP_OTA_VERSION String(__FILE__).substring(String(__FILE__).lastIndexOf('\\')+1) + ".generic" //windows | |
#endif | |
const char* SSID = (String("ESP") + String(ESP.getChipId())).c_str(); | |
/* Watchdog to guard against the ESP8266 wasting battery power looking for | |
* non-responsive wifi networks and servers. Expiry of the watchdog will trigger | |
* either a deep sleep cycle or a delayed reboot. The ESP8266 OS has another built-in | |
* watchdog to protect against infinite loops and hangups in user code. | |
*/ | |
#include <Ticker.h> | |
Ticker watchdog; | |
#define WATCHDOG_SETUP_SECONDS 30 // Setup should complete well within this time limit | |
#define WATCHDOG_LOOP_SECONDS 20 // Loop should complete well within this time limit | |
void timeout_cb() { | |
// This sleep happened because of timeout. Do a restart after a sleep | |
Serial.println(F("Watchdog timeout...")); | |
#ifdef DEEP_SLEEP_SECONDS | |
// Enter DeepSleep so that we don't exhaust our batteries by countinuously trying to | |
// connect to a network that isn't there. | |
ESP.deepSleep(DEEP_SLEEP_SECONDS * 1000, WAKE_RF_DEFAULT); | |
// Do nothing while we wait for sleep to overcome us | |
while(true){}; | |
#else | |
delay(1000); | |
ESP.restart(); | |
#endif | |
} | |
#ifdef LED_STATUS_FLASH | |
Ticker flasher; | |
void flash() { | |
digitalWrite(STATUS_LED, !digitalRead(STATUS_LED)); | |
} | |
#endif | |
#ifdef WIFI_PORTAL | |
// Callback for entering config mode | |
void configModeCallback (WiFiManager *myWiFiManager) { | |
// Config mode has its own timeout | |
watchdog.detach(); | |
#ifdef LED_STATUS_FLASH | |
flasher.attach(0.2, flash); | |
#endif | |
} | |
#endif | |
////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
// Put any project specific initialisation here | |
////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
void setup() { | |
Serial.begin(115200); | |
Serial.println(F("Booting")); | |
#ifdef LED_STATUS_FLASH | |
pinMode(STATUS_LED, OUTPUT); | |
flasher.attach(0.6, flash); | |
#endif | |
// Watchdog timer - resets if setup takes longer than allocated time | |
watchdog.once(WATCHDOG_SETUP_SECONDS, &timeout_cb); | |
// Set up WiFi connection | |
// Previous connection details stored in eeprom | |
#ifdef WIFI_PORTAL | |
#ifdef WIFI_PORTAL_TRIGGER_PIN | |
pinMode(WIFI_PORTAL_TRIGGER_PIN, INPUT_PULLUP); | |
delay(100); | |
if ( digitalRead(WIFI_PORTAL_TRIGGER_PIN) == LOW ) { | |
watchdog.detach(); | |
if (!wifiManager.startConfigPortal(SSID, NULL)) { | |
Serial.println(F("Config Portal Failed!")); | |
timeout_cb(); | |
} | |
} else { | |
#endif | |
wifiManager.setConfigPortalTimeout(180); | |
wifiManager.setAPCallback(configModeCallback); | |
if (!wifiManager.autoConnect()) { | |
Serial.println(F("Connection Failed!")); | |
timeout_cb(); | |
} | |
#ifdef WIFI_PORTAL_TRIGGER_PIN | |
} | |
#endif | |
#else | |
// Save boot up time by not configuring them if they haven't changed | |
if (WiFi.SSID() != WIFI_SSID) { | |
Serial.println(F("Initialising Wifi...")); | |
WiFi.mode(WIFI_STA); | |
WiFi.begin(WIFI_SSID, WIFI_PASSWORD); | |
WiFi.persistent(true); | |
WiFi.setAutoConnect(true); | |
WiFi.setAutoReconnect(true); | |
} | |
#endif | |
if (WiFi.waitForConnectResult() != WL_CONNECTED) { | |
Serial.println(F("Connection Failed!")); | |
timeout_cb(); | |
} | |
Serial.print(F("IP address: ")); | |
Serial.println(WiFi.localIP()); | |
#ifdef LED_STATUS_FLASH | |
flasher.detach(); | |
digitalWrite(STATUS_LED, HIGH); | |
#endif | |
Serial.println(); // show the MAC address of the device | |
Serial.print("MAC: "); | |
Serial.println(WiFi.macAddress()); | |
Serial.println(); // show the version updating | |
Serial.print("VER: "); | |
Serial.println( HTTP_OTA_VERSION); | |
#ifdef HTTP_OTA | |
// Check server for firmware updates | |
Serial.print("Checking for firmware updates from server "); | |
reqString = String("http://"); | |
reqString = reqString + String(HTTP_OTA_ADDRESS); | |
reqString = reqString + String(':') + HTTP_OTA_PORT ; | |
reqString = reqString + HTTP_OTA_PATH; | |
reqString = reqString + HTTP_OTA_VERSION; | |
Serial.println(reqString); | |
//review code below | |
/* | |
Serial.print("Checking for firmware updates from server "); | |
Serial.print(HTTP_OTA_ADDRESS); | |
Serial.print(":"); | |
Serial.print(HTTP_OTA_PORT); | |
Serial.println(HTTP_OTA_PATH); | |
switch(ESPhttpUpdate.update(HTTP_OTA_ADDRESS, HTTP_OTA_PORT, HTTP_OTA_PATH, HTTP_OTA_VERSION)) { | |
case HTTP_UPDATE_FAILED: | |
Serial.printf("HTTP update failed: Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); | |
break; | |
*/ | |
switch (ESPhttpUpdate.update(reqString)) { | |
case HTTP_UPDATE_FAILED: | |
Serial.printf("HTTP update failed: Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); | |
break; | |
case HTTP_UPDATE_NO_UPDATES: | |
Serial.println(F("No updates")); | |
break; | |
case HTTP_UPDATE_OK: | |
Serial.println(F("Update OK")); | |
break; | |
} | |
#endif | |
#ifdef ARDUINO_OTA | |
// Arduino OTA Initalisation | |
ArduinoOTA.setPort(ARDUINO_OTA_PORT); | |
ArduinoOTA.setHostname(SSID); | |
ArduinoOTA.setPassword(ARDUINO_OTA_PASSWD); | |
ArduinoOTA.onStart([]() { | |
watchdog.detach(); | |
Serial.println("Start"); | |
}); | |
ArduinoOTA.onEnd([]() { | |
Serial.println("\nEnd"); | |
}); | |
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { | |
Serial.printf("Progress: %u%%\r", (progress / (total / 100))); | |
}); | |
ArduinoOTA.onError([](ota_error_t error) { | |
Serial.printf("Error[%u]: ", error); | |
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); | |
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); | |
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); | |
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); | |
else if (error == OTA_END_ERROR) Serial.println("End Failed"); | |
}); | |
ArduinoOTA.begin(); | |
#endif | |
////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
// Put any project specific setup code here | |
////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
Serial.println(F("Ready")); | |
watchdog.detach(); | |
} | |
void loop() { | |
// Watchdog timer - resets if setup takes longer than allocated time | |
watchdog.once(WATCHDOG_LOOP_SECONDS, &timeout_cb); | |
////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
// put your main code here, to run repeatedly: | |
////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
delay(10000); | |
watchdog.detach(); | |
#ifdef ARDUINO_OTA | |
// Handle any OTA upgrade | |
ArduinoOTA.handle(); | |
#endif | |
#ifdef DEEP_SLEEP_SECONDS | |
// Enter DeepSleep | |
Serial.println(F("Sleeping...")); | |
ESP.deepSleep(DEEP_SLEEP_SECONDS * 1000000, WAKE_RF_DEFAULT); | |
// Do nothing while we wait for sleep to overcome us | |
while(true){}; | |
#endif | |
} |
added a couple of blocks to make finding the initialization, setup and loop blocks easier to find
change the request method to build string
made it possible to use arduino names from windows
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
While the node-red server software can take an MD5 hash and the MAC address of the board this script is not writtent to send these.
This script if unmodifed sends a call to http://hostname/esp8266-ota/update/ plus HTTP_OTA_VERSION - which is a string of the directory and filename of the location of the firmware itself.
Looking at the node-red server software with debug turned on requests made by the ESP8266 to update URL are not being logged by the debug node. So the request is not actually making it to the OTA update - likely because of the slashes included in the filename. I think I need to change what this script is sending to the update node - so that it is consistent with the filename logged in the firmware directory