Merge pull request #2 from 0x1abin/master

update
This commit is contained in:
M5Stack
2017-05-11 11:18:53 +08:00
committed by GitHub
31 changed files with 4196 additions and 31 deletions
+31 -2
View File
@@ -11,10 +11,16 @@ void M5Stack::begin()
pinMode(BEEP_PIN, OUTPUT);
digitalWrite(BEEP_PIN, 0);
// LED init
pinMode(LED_PIN, OUTPUT);
// Setup the button with an internal pull-up
btn_pins[BTN_A] = BUTTON_A_PIN;
btn_pins[BTN_B] = BUTTON_B_PIN;
btn_pins[BTN_C] = BUTTON_C_PIN;
pinMode(BUTTON_A_PIN, INPUT_PULLUP);
pinMode(BUTTON_B_PIN, INPUT_PULLUP);
pinMode(BUTTON_C_PIN, INPUT_PULLUP);
// M5.lcd INIT
lcd.begin();
@@ -33,7 +39,7 @@ void M5Stack::loop()
{
// buttons reads each button btn_states and store it
for (uint8_t thisButton = 0; thisButton < NUM_BTN; thisButton++) {
pinMode(btn_pins[thisButton], INPUT_PULLUP); //enable internal pull up resistors
// pinMode(btn_pins[thisButton], INPUT_PULLUP); //enable internal pull up resistors
if (digitalRead(btn_pins[thisButton]) == LOW) { //if button pressed
btn_states[thisButton]++; //increase button hold time
} else {
@@ -44,7 +50,7 @@ void M5Stack::loop()
else
btn_states[thisButton] = 0xFF; //button just released
}
pinMode(btn_pins[thisButton], INPUT); //disable internal pull up resistors to save power
// pinMode(btn_pins[thisButton], INPUT); //disable internal pull up resistors to save power
}
// TFTLCD button update
@@ -82,6 +88,29 @@ uint8_t M5Stack::bootSetup()
return select_app_id;
}
/*
* Turn ON LED
*/
void M5Stack::ledOn() {
digitalWrite(LED_PIN, 1);
}
/*
* Turn OFF LED
*/
void M5Stack::ledOff() {
digitalWrite(LED_PIN, 0);
}
/*
* ledTrig LED
*/
void M5Stack::ledTrig() {
static bool tgbit;
digitalWrite(LED_PIN, tgbit);
tgbit = !tgbit;
}
/*
* Returns true when 'button' is pressed.
* The button has to be released for it to be triggered again.
+11
View File
@@ -16,12 +16,23 @@
#include "utility/bootmenu.h"
#include "utility/config.h"
#ifdef ENABLE_DEBUG
#define M5PUTLOG(X) Serial.printf(X)
#else
#define M5PUTLOG(X)
#endif
class M5Stack {
public:
void begin();
uint8_t bootSetup();
void loop();
//LED
void ledOn();
void ledOff();
void ledTrig();
// button API
bool pressed(uint8_t button);
bool released(uint8_t button);
BIN
View File
Binary file not shown.
@@ -0,0 +1,40 @@
#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
//needed for library
// #include <DNSServer.h>
#include <ESPmDNS.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
//WiFiManager
//Local intialization. Once its business is done, there is no need to keep it around
WiFiManager wifiManager;
//reset saved settings
//wifiManager.resetSettings();
//set custom ip for portal
//wifiManager.setAPStaticIPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0));
//fetches ssid and pass from eeprom and tries to connect
//if it does not connect it starts an access point with the specified name
//here "AutoConnectAP"
//and goes into a blocking loop awaiting configuration
wifiManager.autoConnect("AutoConnectAP");
//or use this for auto generated name ESP + ChipID
//wifiManager.autoConnect();
//if you get here you have connected to the WiFi
Serial.println("connected...yeey :)");
}
void loop() {
// put your main code here, to run repeatedly:
}
File diff suppressed because it is too large Load Diff
+195
View File
@@ -0,0 +1,195 @@
/**************************************************************
WiFiManager is a library for the ESP8266/Arduino platform
(https://github.com/esp8266/Arduino) to enable easy
configuration and reconfiguration of WiFi credentials using a Captive Portal
inspired by:
http://www.esp8266.com/viewtopic.php?f=29&t=2520
https://github.com/chriscook8/esp-arduino-apboot
https://github.com/esp8266/Arduino/tree/esp8266/hardware/esp8266com/esp8266/libraries/DNSServer/examples/CaptivePortalAdvanced
Built by AlexT https://github.com/tzapu
Licensed under MIT license
**************************************************************/
#ifndef WiFiManager_h
#define WiFiManager_h
#include <WiFi.h>
#include <WebServer.h>
// #include <DNSServer.h>
#include <ESPmDNS.h>
#include <memory>
// extern "C" {
// #include "user_interface.h"
// }
const char HTTP_HEAD[] PROGMEM = "<!DOCTYPE html><html lang=\"en\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\"/><title>{v}</title>";
const char HTTP_STYLE[] PROGMEM = "<style>.c{text-align: center;} div,input{padding:5px;font-size:1em;} input{width:95%;} body{text-align: center;font-family:verdana;} button{border:0;border-radius:0.3rem;background-color:#1fa3ec;color:#fff;line-height:2.4rem;font-size:1.2rem;width:100%;} .q{float: right;width: 64px;text-align: right;} .l{background: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAALVBMVEX///8EBwfBwsLw8PAzNjaCg4NTVVUjJiZDRUUUFxdiZGSho6OSk5Pg4eFydHTCjaf3AAAAZElEQVQ4je2NSw7AIAhEBamKn97/uMXEGBvozkWb9C2Zx4xzWykBhFAeYp9gkLyZE0zIMno9n4g19hmdY39scwqVkOXaxph0ZCXQcqxSpgQpONa59wkRDOL93eAXvimwlbPbwwVAegLS1HGfZAAAAABJRU5ErkJggg==\") no-repeat left center;background-size: 1em;}</style>";
const char HTTP_SCRIPT[] PROGMEM = "<script>function c(l){document.getElementById('s').value=l.innerText||l.textContent;document.getElementById('p').focus();}</script>";
const char HTTP_HEAD_END[] PROGMEM = "</head><body><div style='text-align:left;display:inline-block;min-width:260px;'>";
const char HTTP_PORTAL_OPTIONS[] PROGMEM = "<form action=\"/wifi\" method=\"get\"><button>Configure WiFi</button></form><br/><form action=\"/0wifi\" method=\"get\"><button>Configure WiFi (No Scan)</button></form><br/><form action=\"/i\" method=\"get\"><button>Info</button></form><br/><form action=\"/r\" method=\"post\"><button>Reset</button></form>";
const char HTTP_ITEM[] PROGMEM = "<div><a href='#p' onclick='c(this)'>{v}</a>&nbsp;<span class='q {i}'>{r}%</span></div>";
const char HTTP_FORM_START[] PROGMEM = "<form method='get' action='wifisave'><input id='s' name='s' length=32 placeholder='SSID'><br/><input id='p' name='p' length=64 type='password' placeholder='password'><br/>";
const char HTTP_FORM_PARAM[] PROGMEM = "<br/><input id='{i}' name='{n}' length={l} placeholder='{p}' value='{v}' {c}>";
const char HTTP_FORM_END[] PROGMEM = "<br/><button type='submit'>save</button></form>";
const char HTTP_SCAN_LINK[] PROGMEM = "<br/><div class=\"c\"><a href=\"/wifi\">Scan</a></div>";
const char HTTP_SAVED[] PROGMEM = "<div>Credentials Saved<br />Trying to connect ESP to network.<br />If it fails reconnect to AP to try again</div>";
const char HTTP_END[] PROGMEM = "</div></body></html>";
#define WIFI_MANAGER_MAX_PARAMS 10
class WiFiManagerParameter {
public:
WiFiManagerParameter(const char *custom);
WiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length);
WiFiManagerParameter(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom);
const char *getID();
const char *getValue();
const char *getPlaceholder();
int getValueLength();
const char *getCustomHTML();
private:
const char *_id;
const char *_placeholder;
char *_value;
int _length;
const char *_customHTML;
void init(const char *id, const char *placeholder, const char *defaultValue, int length, const char *custom);
friend class WiFiManager;
};
class WiFiManager
{
public:
WiFiManager();
boolean autoConnect();
boolean autoConnect(char const *apName, char const *apPassword = NULL);
//if you want to always start the config portal, without trying to connect first
boolean startConfigPortal();
boolean startConfigPortal(char const *apName, char const *apPassword = NULL);
// get the AP name of the config portal, so it can be used in the callback
String getConfigPortalSSID();
void resetSettings();
//sets timeout before webserver loop ends and exits even if there has been no setup.
//usefully for devices that failed to connect at some point and got stuck in a webserver loop
//in seconds setConfigPortalTimeout is a new name for setTimeout
void setConfigPortalTimeout(unsigned long seconds);
void setTimeout(unsigned long seconds);
//sets timeout for which to attempt connecting, usefull if you get a lot of failed connects
void setConnectTimeout(unsigned long seconds);
void setDebugOutput(boolean debug);
//defaults to not showing anything under 8% signal quality if called
void setMinimumSignalQuality(int quality = 8);
//sets a custom ip /gateway /subnet configuration
void setAPStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn);
//sets config for a static IP
void setSTAStaticIPConfig(IPAddress ip, IPAddress gw, IPAddress sn);
//called when AP mode and config portal is started
void setAPCallback( void (*func)(WiFiManager*) );
//called when settings have been changed and connection was successful
void setSaveConfigCallback( void (*func)(void) );
//adds a custom parameter
void addParameter(WiFiManagerParameter *p);
//if this is set, it will exit after config, even if connection is unsucessful.
void setBreakAfterConfig(boolean shouldBreak);
//if this is set, try WPS setup when starting (this will delay config portal for up to 2 mins)
//TODO
//if this is set, customise style
void setCustomHeadElement(const char* element);
//if this is true, remove duplicated Access Points - defaut true
void setRemoveDuplicateAPs(boolean removeDuplicates);
private:
std::unique_ptr<ESPmDNS> dnsServer;
std::unique_ptr<WebServer> server;
//const int WM_DONE = 0;
//const int WM_WAIT = 10;
//const String HTTP_HEAD = "<!DOCTYPE html><html lang=\"en\"><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/><title>{v}</title>";
void setupConfigPortal();
void startWPS();
const char* _apName = "no-net";
const char* _apPassword = NULL;
String _ssid = "";
String _pass = "";
unsigned long _configPortalTimeout = 0;
unsigned long _connectTimeout = 0;
unsigned long _configPortalStart = 0;
IPAddress _ap_static_ip;
IPAddress _ap_static_gw;
IPAddress _ap_static_sn;
IPAddress _sta_static_ip;
IPAddress _sta_static_gw;
IPAddress _sta_static_sn;
int _paramsCount = 0;
int _minimumQuality = -1;
boolean _removeDuplicateAPs = true;
boolean _shouldBreakAfterConfig = false;
boolean _tryWPS = false;
const char* _customHeadElement = "";
//String getEEPROMString(int start, int len);
//void setEEPROMString(int start, int len, String string);
int status = WL_IDLE_STATUS;
int connectWifi(String ssid, String pass);
uint8_t waitForConnectResult();
void handleRoot();
void handleWifi(boolean scan);
void handleWifiSave();
void handleInfo();
void handleReset();
void handleNotFound();
void handle204();
boolean captivePortal();
boolean configPortalHasTimeout();
// DNS server
const byte DNS_PORT = 53;
//helpers
int getRSSIasQuality(int RSSI);
boolean isIp(String str);
String toStringIp(IPAddress ip);
boolean connect;
boolean _debug = true;
void (*_apcallback)(WiFiManager*) = NULL;
void (*_savecallback)(void) = NULL;
WiFiManagerParameter* _params[WIFI_MANAGER_MAX_PARAMS];
template <typename Generic>
void DEBUG_WM(Generic text);
template <class T>
auto optionalIPFromString(T *obj, const char *s) -> decltype( obj->fromString(s) ) {
return obj->fromString(s);
}
auto optionalIPFromString(...) -> bool {
DEBUG_WM("NO fromString METHOD ON IPAddress, you need ESP8266 core 2.1.0 or newer for Custom IP configuration to work.");
return false;
}
};
#endif
@@ -0,0 +1,81 @@
#include <M5Stack.h>
#include <ArduinoJson.h>
bool getParamFromTFCard(const char* filename, const char* keyword, char* strout) {
// fs file read
File fsFile;
uint8_t fsbuffer[255];
// const char* filename = "/azure-config.json";
if(SD.exists(filename)) {
fsFile = SD.open(filename, "r");
if(fsFile) {
Serial.printf("Success open file:\r\n");
fsFile.read(fsbuffer, fsFile.size());
Serial.printf("\r\n");
fsbuffer[fsFile.size()] = 0;
Serial.printf("%s", fsbuffer);
} else {
Serial.printf("Fail open file!\r\n");
return false;
}
}
// Memory pool for JSON object tree.
//
// Inside the brackets, 200 is the size of the pool in bytes,
// If the JSON object is more complex, you need to increase that value.
StaticJsonBuffer<200> jsonBuffer;
// StaticJsonBuffer allocates memory on the stack, it can be
// replaced by DynamicJsonBuffer which allocates in the heap.
// It's simpler but less efficient.
//
// DynamicJsonBuffer jsonBuffer;
// Root of the object tree.
//
// It's a reference to the JsonObject, the actual bytes are inside the
// JsonBuffer with all the other nodes of the object tree.
// Memory is freed when jsonBuffer goes out of scope.
JsonObject& root = jsonBuffer.parseObject(fsbuffer);
// Test if parsing succeeds.
if (!root.success()) {
Serial.println("parseObject() failed");
return false;
}
// Fetch values.
//
// Most of the time, you can rely on the implicit casts.
// In other case, you can do root["time"].as<long>();
const char* value = root[keyword];
// Copy values.
strcpy((char*)strout, value);
Serial.printf("Keyword:%s\r\nValue:%s\r\n", keyword, strout);
return true;
}
// the setup routine runs once when M5Stack starts up
void setup(){
// initialize the M5Stack object
m5.begin();
// lcd display
m5.lcd.printf("JSON test");
char ssid[30];
if(getParamFromTFCard("/azure-config.json", "WiFi_SSID", ssid)) {
Serial.printf("Read Value:%s", ssid);
}
}
// the loop routine runs over and over again forever
void loop(){
m5.loop();
}
@@ -0,0 +1,6 @@
{
"WiFi_SSID":"MasterHax_2.4G",
"WiFi_PASSWORD":"wittyercheese551",
"AzureDeviceID":"test001",
"ConnectingString":"m5statck-abcde"
}
@@ -0,0 +1,18 @@
#include <M5Stack.h>
// the setup routine runs once when M5Stack starts up
void setup(){
// initialize the M5Stack object
m5.begin();
}
// the loop routine runs over and over again forever
void loop(){
m5.lcd.drawPicture("/ROBOT2.bmp");
m5.lcd.drawPicture("/ROBOT3.bmp");
m5.lcd.drawPicture("/ROBOT4.bmp");
m5.lcd.drawPicture("/ROBOT5.bmp");
m5.loop();
}
@@ -0,0 +1,303 @@
#include <M5Stack.h>
#include <WiFi.h>
// #include <DNSServer.h>
#include <ESPmDNS.h>
#include <WiFiClient.h>
// #include <EEPROM.h>
#include <WebServer.h>
#include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson
const IPAddress apIP(192, 168, 1, 1);
const char* apSSID = "M5STACK_SETUP";
boolean settingMode;
String ssidList;
char wifi_ssid[40];
char wifi_password[40];
// DNSServer dnsServer;
WebServer webServer(80);
void writeFile(fs::FS &fs, const char * path, const char * message) {
Serial.printf("Writing file: %s\n", path);
File file = fs.open(path, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (file.print(message)) {
Serial.println("File written");
} else {
Serial.println("Write failed");
}
}
void deleteFile(fs::FS &fs, const char * path) {
Serial.printf("Deleting file: %s\n", path);
if (fs.remove(path)) {
Serial.println("File deleted");
} else {
Serial.println("Delete failed");
}
}
void setup() {
m5.begin();
// EEPROM.begin(512);
delay(10);
if (restoreConfig()) {
if (checkConnection()) {
settingMode = false;
startWebServer();
return;
}
}
settingMode = true;
setupMode();
}
void loop() {
if (settingMode) {
// dnsServer.processNextRequest();
}
webServer.handleClient();
}
boolean restoreConfig() {
// char fsbuffer[200];
const char* filename = "/wifi-config.json";
Serial.printf("Reading %s\r\n", filename);
if (SD.exists(filename)) {
//file exists, reading and loading
Serial.println("reading config file");
File configFile = SD.open(filename, "r");
if (configFile) {
Serial.println("opened config file");
size_t size = configFile.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> fsbuffer(new char[size]);
configFile.readBytes(fsbuffer.get(), size);
// configFile.readBytes(fsbuffer, size);
fsbuffer[size] = 0;
DynamicJsonBuffer jsonBuffer;
// JsonObject& json = jsonBuffer.parseObject(fsbuffer);
JsonObject& json = jsonBuffer.parseObject(fsbuffer.get());
json.printTo(Serial);
if (json.success()) {
Serial.println("\nparsed json");
// const char* wifi = json["WiFi_SSID"];
// const char* passwd = json["WiFi_PASSWORD"];
strcpy(wifi_ssid, json["WiFi_SSID"]);
strcpy(wifi_password, json["WiFi_PASSWORD"]);
Serial.printf("SSID:%s\r\n", wifi_ssid);
Serial.printf("PASSWORD:%s\r\n", wifi_password);
WiFi.begin(wifi_ssid, wifi_password);
return true;
} else {
Serial.println("failed to load json config");
return false;
}
}
} else {
Serial.printf("file isn't exists.\r\n");
writeFile(SD, "/wifi-config.json", "{\"WiFi_SSID\":\"xMasterHax_2.4G\",\"WiFi_PASSWORD\":\"wittyercheese551\",\"AzureDeviceID\":\"test001\",\"ConnectingString\":\"m5statck-abcde\"}");
}
return false;
}
boolean checkConnection() {
int count = 0;
Serial.print("Waiting for Wi-Fi connection");
while ( count < 30 ) {
if (WiFi.status() == WL_CONNECTED) {
Serial.println();
Serial.println("Connected!");
return (true);
}
delay(500);
Serial.print(".");
count++;
}
Serial.println("Timed out.");
return false;
}
void startWebServer() {
if (settingMode) {
Serial.print("Starting Web Server at ");
Serial.println(WiFi.softAPIP());
webServer.on("/settings", []() {
String s = "<h1>Wi-Fi Settings</h1><p>Please enter your password by selecting the SSID.</p>";
s += "<form method=\"get\" action=\"setap\"><label>SSID: </label><select name=\"ssid\">";
s += ssidList;
s += "</select><br>Password: <input name=\"pass\" length=64 type=\"password\"><input type=\"submit\"></form>";
webServer.send(200, "text/html", makePage("Wi-Fi Settings", s));
});
webServer.on("/setap", []() {
// for (int i = 0; i < 96; ++i) {
// EEPROM.write(i, 0);
// }
String ssid = urlDecode(webServer.arg("ssid"));
Serial.print("SSID: ");
Serial.println(ssid);
String pass = urlDecode(webServer.arg("pass"));
Serial.print("Password: ");
Serial.println(pass);
Serial.println("Writing SSID to EEPROM...");
//----------------file--------------------
const char* filename = "/wifi-config.json";
if (SD.exists(filename)) {
//file exists, reading and loading
Serial.println("reading config file");
File configFile = SD.open(filename, "r");
if (configFile) {
Serial.println("opened config file");
size_t size = configFile.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> fsbuffer(new char[size]);
configFile.readBytes(fsbuffer.get(), size);
// configFile.readBytes(fsbuffer, size);
fsbuffer[size] = 0;
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(fsbuffer.get());
root[String("WiFi_SSID")] = ssid;
root[String("WiFi_PASSWORD")] = pass;
String output;
root.printTo(output);
Serial.printf("Save Json.\r\n");
Serial.print(output);
configFile.close();
File configFile = SD.open(filename, "w");
if (!configFile) {
Serial.println("Failed to open file for writing");
return;
}
if (configFile.print(output)) {
Serial.println("File written");
} else {
Serial.println("Write failed");
}
}
}
//---------------------------------------
for (int i = 0; i < ssid.length(); ++i) {
// EEPROM.write(i, ssid[i]);
}
Serial.println("Writing Password to EEPROM...");
for (int i = 0; i < pass.length(); ++i) {
// EEPROM.write(32 + i, pass[i]);
}
// EEPROM.commit();
Serial.println("Write EEPROM done!");
String s = "<h1>Setup complete.</h1><p>device will be connected to \"";
s += ssid;
s += "\" after the restart.";
webServer.send(200, "text/html", makePage("Wi-Fi Settings", s));
delay(3000);
ESP.restart();
});
webServer.onNotFound([]() {
String s = "<h1>AP mode</h1><p><a href=\"/settings\">Wi-Fi Settings</a></p>";
webServer.send(200, "text/html", makePage("AP mode", s));
});
}
else {
Serial.print("Starting Web Server at ");
Serial.println(WiFi.localIP());
webServer.on("/", []() {
String s = "<h1>STA mode</h1><p><a href=\"/reset\">Reset Wi-Fi Settings</a></p>";
webServer.send(200, "text/html", makePage("STA mode", s));
});
webServer.on("/reset", []() {
deleteFile(SD, "/wifi-config.json");
String s = "<h1>Wi-Fi settings was reset.</h1><p>Please reset device.</p>";
webServer.send(200, "text/html", makePage("Reset Wi-Fi Settings", s));
delay(3000);
ESP.restart();
});
}
webServer.begin();
}
void setupMode() {
WiFi.mode(WIFI_MODE_STA);
WiFi.disconnect();
delay(100);
int n = WiFi.scanNetworks();
delay(100);
Serial.println("");
for (int i = 0; i < n; ++i) {
ssidList += "<option value=\"";
ssidList += WiFi.SSID(i);
ssidList += "\">";
ssidList += WiFi.SSID(i);
ssidList += "</option>";
}
delay(100);
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
WiFi.softAP(apSSID);
WiFi.mode(WIFI_MODE_AP);
// WiFi.softAPConfig(IPAddress local_ip, IPAddress gateway, IPAddress subnet);
// WiFi.softAP(const char* ssid, const char* passphrase = NULL, int channel = 1, int ssid_hidden = 0);
// dnsServer.start(53, "*", apIP);
startWebServer();
Serial.print("Starting Access Point at \"");
Serial.print(apSSID);
Serial.println("\"");
}
String makePage(String title, String contents) {
String s = "<!DOCTYPE html><html><head>";
s += "<meta name=\"viewport\" content=\"width=device-width,user-scalable=0\">";
s += "<title>";
s += title;
s += "</title></head><body>";
s += contents;
s += "</body></html>";
return s;
}
String urlDecode(String input) {
String s = input;
s.replace("%20", " ");
s.replace("+", " ");
s.replace("%21", "!");
s.replace("%22", "\"");
s.replace("%23", "#");
s.replace("%24", "$");
s.replace("%25", "%");
s.replace("%26", "&");
s.replace("%27", "\'");
s.replace("%28", "(");
s.replace("%29", ")");
s.replace("%30", "*");
s.replace("%31", "+");
s.replace("%2C", ",");
s.replace("%2E", ".");
s.replace("%2F", "/");
s.replace("%2C", ",");
s.replace("%3A", ":");
s.replace("%3A", ";");
s.replace("%3C", "<");
s.replace("%3D", "=");
s.replace("%3E", ">");
s.replace("%3F", "?");
s.replace("%40", "@");
s.replace("%5B", "[");
s.replace("%5C", "\\");
s.replace("%5D", "]");
s.replace("%5E", "^");
s.replace("%5F", "-");
s.replace("%60", "`");
return s;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,228 @@
/**
* ESP8266HTTPClient.h
*
* Created on: 02.11.2015
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the ESP8266HTTPClient for Arduino.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef ESP8266HTTPClient_H_
#define ESP8266HTTPClient_H_
#include <memory>
#include <Arduino.h>
#include <WiFiClient.h>
#ifdef DEBUG_ESP_HTTP_CLIENT
#ifdef DEBUG_ESP_PORT
#define DEBUG_HTTPCLIENT(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ )
#endif
#endif
#ifndef DEBUG_HTTPCLIENT
#define DEBUG_HTTPCLIENT(...)
#endif
#define HTTPCLIENT_DEFAULT_TCP_TIMEOUT (5000)
/// HTTP client errors
#define HTTPC_ERROR_CONNECTION_REFUSED (-1)
#define HTTPC_ERROR_SEND_HEADER_FAILED (-2)
#define HTTPC_ERROR_SEND_PAYLOAD_FAILED (-3)
#define HTTPC_ERROR_NOT_CONNECTED (-4)
#define HTTPC_ERROR_CONNECTION_LOST (-5)
#define HTTPC_ERROR_NO_STREAM (-6)
#define HTTPC_ERROR_NO_HTTP_SERVER (-7)
#define HTTPC_ERROR_TOO_LESS_RAM (-8)
#define HTTPC_ERROR_ENCODING (-9)
#define HTTPC_ERROR_STREAM_WRITE (-10)
#define HTTPC_ERROR_READ_TIMEOUT (-11)
/// size for the stream handling
#define HTTP_TCP_BUFFER_SIZE (1460)
/// HTTP codes see RFC7231
typedef enum {
HTTP_CODE_CONTINUE = 100,
HTTP_CODE_SWITCHING_PROTOCOLS = 101,
HTTP_CODE_PROCESSING = 102,
HTTP_CODE_OK = 200,
HTTP_CODE_CREATED = 201,
HTTP_CODE_ACCEPTED = 202,
HTTP_CODE_NON_AUTHORITATIVE_INFORMATION = 203,
HTTP_CODE_NO_CONTENT = 204,
HTTP_CODE_RESET_CONTENT = 205,
HTTP_CODE_PARTIAL_CONTENT = 206,
HTTP_CODE_MULTI_STATUS = 207,
HTTP_CODE_ALREADY_REPORTED = 208,
HTTP_CODE_IM_USED = 226,
HTTP_CODE_MULTIPLE_CHOICES = 300,
HTTP_CODE_MOVED_PERMANENTLY = 301,
HTTP_CODE_FOUND = 302,
HTTP_CODE_SEE_OTHER = 303,
HTTP_CODE_NOT_MODIFIED = 304,
HTTP_CODE_USE_PROXY = 305,
HTTP_CODE_TEMPORARY_REDIRECT = 307,
HTTP_CODE_PERMANENT_REDIRECT = 308,
HTTP_CODE_BAD_REQUEST = 400,
HTTP_CODE_UNAUTHORIZED = 401,
HTTP_CODE_PAYMENT_REQUIRED = 402,
HTTP_CODE_FORBIDDEN = 403,
HTTP_CODE_NOT_FOUND = 404,
HTTP_CODE_METHOD_NOT_ALLOWED = 405,
HTTP_CODE_NOT_ACCEPTABLE = 406,
HTTP_CODE_PROXY_AUTHENTICATION_REQUIRED = 407,
HTTP_CODE_REQUEST_TIMEOUT = 408,
HTTP_CODE_CONFLICT = 409,
HTTP_CODE_GONE = 410,
HTTP_CODE_LENGTH_REQUIRED = 411,
HTTP_CODE_PRECONDITION_FAILED = 412,
HTTP_CODE_PAYLOAD_TOO_LARGE = 413,
HTTP_CODE_URI_TOO_LONG = 414,
HTTP_CODE_UNSUPPORTED_MEDIA_TYPE = 415,
HTTP_CODE_RANGE_NOT_SATISFIABLE = 416,
HTTP_CODE_EXPECTATION_FAILED = 417,
HTTP_CODE_MISDIRECTED_REQUEST = 421,
HTTP_CODE_UNPROCESSABLE_ENTITY = 422,
HTTP_CODE_LOCKED = 423,
HTTP_CODE_FAILED_DEPENDENCY = 424,
HTTP_CODE_UPGRADE_REQUIRED = 426,
HTTP_CODE_PRECONDITION_REQUIRED = 428,
HTTP_CODE_TOO_MANY_REQUESTS = 429,
HTTP_CODE_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
HTTP_CODE_INTERNAL_SERVER_ERROR = 500,
HTTP_CODE_NOT_IMPLEMENTED = 501,
HTTP_CODE_BAD_GATEWAY = 502,
HTTP_CODE_SERVICE_UNAVAILABLE = 503,
HTTP_CODE_GATEWAY_TIMEOUT = 504,
HTTP_CODE_HTTP_VERSION_NOT_SUPPORTED = 505,
HTTP_CODE_VARIANT_ALSO_NEGOTIATES = 506,
HTTP_CODE_INSUFFICIENT_STORAGE = 507,
HTTP_CODE_LOOP_DETECTED = 508,
HTTP_CODE_NOT_EXTENDED = 510,
HTTP_CODE_NETWORK_AUTHENTICATION_REQUIRED = 511
} t_http_codes;
typedef enum {
HTTPC_TE_IDENTITY,
HTTPC_TE_CHUNKED
} transferEncoding_t;
class TransportTraits;
typedef std::unique_ptr<TransportTraits> TransportTraitsPtr;
class HTTPClient
{
public:
HTTPClient();
~HTTPClient();
bool begin(String url);
bool begin(String url, String httpsFingerprint);
bool begin(String host, uint16_t port, String uri = "/");
bool begin(String host, uint16_t port, String uri, String httpsFingerprint);
// deprecated, use the overload above instead
bool begin(String host, uint16_t port, String uri, bool https, String httpsFingerprint) __attribute__ ((deprecated));
void end(void);
bool connected(void);
void setReuse(bool reuse); /// keep-alive
void setUserAgent(const String& userAgent);
void setAuthorization(const char * user, const char * password);
void setAuthorization(const char * auth);
void setTimeout(uint16_t timeout);
void useHTTP10(bool usehttp10 = true);
/// request handling
int GET();
int POST(uint8_t * payload, size_t size);
int POST(String payload);
int PUT(uint8_t * payload, size_t size);
int PUT(String payload);
int sendRequest(const char * type, String payload);
int sendRequest(const char * type, uint8_t * payload = NULL, size_t size = 0);
int sendRequest(const char * type, Stream * stream, size_t size = 0);
void addHeader(const String& name, const String& value, bool first = false, bool replace = true);
/// Response handling
void collectHeaders(const char* headerKeys[], const size_t headerKeysCount);
String header(const char* name); // get request header value by name
String header(size_t i); // get request header value by number
String headerName(size_t i); // get request header name by number
int headers(); // get header count
bool hasHeader(const char* name); // check if header exists
int getSize(void);
WiFiClient& getStream(void);
WiFiClient* getStreamPtr(void);
int writeToStream(Stream* stream);
String getString(void);
static String errorToString(int error);
protected:
struct RequestArgument {
String key;
String value;
};
bool beginInternal(String url, const char* expectedProtocol);
void clear();
int returnError(int error);
bool connect(void);
bool sendHeader(const char * type);
int handleHeaderResponse();
int writeToStreamDataBlock(Stream * stream, int len);
TransportTraitsPtr _transportTraits;
std::unique_ptr<WiFiClient> _tcp;
/// request handling
String _host;
uint16_t _port = 0;
bool _reuse = false;
uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT;
bool _useHTTP10 = false;
String _uri;
String _protocol;
String _headers;
String _userAgent = "ESP8266HTTPClient";
String _base64Authorization;
/// Response handling
RequestArgument* _currentHeaders = nullptr;
size_t _headerKeysCount = 0;
int _returnCode = 0;
int _size = -1;
bool _canReuse = false;
transferEncoding_t _transferEncoding = HTTPC_TE_IDENTITY;
};
#endif /* ESP8266HTTPClient_H_ */
@@ -0,0 +1,391 @@
/**
*
* @file ESP8266HTTPUpdate.cpp
* @date 21.06.2015
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the ESP8266 Http Updater.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "ESP8266httpUpdate.h"
#include <StreamString.h>
// extern "C" uint32_t _SPIFFS_start;
// extern "C" uint32_t _SPIFFS_end;
ESP8266HTTPUpdate::ESP8266HTTPUpdate(void)
{
}
ESP8266HTTPUpdate::~ESP8266HTTPUpdate(void)
{
}
HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion,
const String& httpsFingerprint, bool reboot)
{
rebootOnUpdate(reboot);
return update(url, currentVersion, httpsFingerprint);
}
HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion)
{
HTTPClient http;
http.begin(url);
return handleUpdate(http, currentVersion, false);
}
HTTPUpdateResult ESP8266HTTPUpdate::update(const String& url, const String& currentVersion,
const String& httpsFingerprint)
{
HTTPClient http;
http.begin(url, httpsFingerprint);
return handleUpdate(http, currentVersion, false);
}
HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint)
{
HTTPClient http;
http.begin(url, httpsFingerprint);
return handleUpdate(http, currentVersion, true);
}
HTTPUpdateResult ESP8266HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion)
{
HTTPClient http;
http.begin(url);
return handleUpdate(http, currentVersion, true);
}
HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri, const String& currentVersion,
bool https, const String& httpsFingerprint, bool reboot)
{
rebootOnUpdate(reboot);
if (httpsFingerprint.length() == 0) {
return update(host, port, uri, currentVersion);
} else {
return update(host, port, uri, currentVersion, httpsFingerprint);
}
}
HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& uri,
const String& currentVersion)
{
HTTPClient http;
http.begin(host, port, uri);
return handleUpdate(http, currentVersion, false);
}
HTTPUpdateResult ESP8266HTTPUpdate::update(const String& host, uint16_t port, const String& url,
const String& currentVersion, const String& httpsFingerprint)
{
HTTPClient http;
http.begin(host, port, url, httpsFingerprint);
return handleUpdate(http, currentVersion, false);
}
/**
* return error code as int
* @return int error code
*/
int ESP8266HTTPUpdate::getLastError(void)
{
return _lastError;
}
/**
* return error code as String
* @return String error
*/
String ESP8266HTTPUpdate::getLastErrorString(void)
{
if(_lastError == 0) {
return String(); // no error
}
// error from Update class
if(_lastError > 0) {
StreamString error;
Update.printError(error);
error.trim(); // remove line ending
return String(F("Update error: ")) + error;
}
// error from http client
if(_lastError > -100) {
return String(F("HTTP error: ")) + HTTPClient::errorToString(_lastError);
}
switch(_lastError) {
case HTTP_UE_TOO_LESS_SPACE:
return F("To less space");
case HTTP_UE_SERVER_NOT_REPORT_SIZE:
return F("Server not Report Size");
case HTTP_UE_SERVER_FILE_NOT_FOUND:
return F("File not Found (404)");
case HTTP_UE_SERVER_FORBIDDEN:
return F("Forbidden (403)");
case HTTP_UE_SERVER_WRONG_HTTP_CODE:
return F("Wrong HTTP code");
case HTTP_UE_SERVER_FAULTY_MD5:
return F("Faulty MD5");
case HTTP_UE_BIN_VERIFY_HEADER_FAILED:
return F("Verify bin header failed");
case HTTP_UE_BIN_FOR_WRONG_FLASH:
return F("bin for wrong flash size");
}
return String();
}
/**
*
* @param http HTTPClient *
* @param currentVersion const char *
* @return HTTPUpdateResult
*/
HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs)
{
HTTPUpdateResult ret = HTTP_UPDATE_FAILED;
// use HTTP/1.0 for update since the update handler not support any transfer Encoding
http.useHTTP10(true);
http.setTimeout(8000);
http.setUserAgent(F("ESP8266-http-Update"));
// http.addHeader(F("x-ESP8266-STA-MAC"), WiFi.macAddress());
// http.addHeader(F("x-ESP8266-AP-MAC"), WiFi.softAPmacAddress());
// http.addHeader(F("x-ESP8266-free-space"), String(ESP.getFreeSketchSpace()));
// http.addHeader(F("x-ESP8266-sketch-size"), String(ESP.getSketchSize()));
// http.addHeader(F("x-ESP8266-sketch-md5"), String(ESP.getSketchMD5()));
// http.addHeader(F("x-ESP8266-chip-size"), String(ESP.getFlashChipRealSize()));
// http.addHeader(F("x-ESP8266-sdk-version"), ESP.getSdkVersion());
if(spiffs) {
http.addHeader(F("x-ESP8266-mode"), F("spiffs"));
} else {
http.addHeader(F("x-ESP8266-mode"), F("sketch"));
}
if(currentVersion && currentVersion[0] != 0x00) {
http.addHeader(F("x-ESP8266-version"), currentVersion);
}
const char * headerkeys[] = { "x-MD5" };
size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
// track these headers
http.collectHeaders(headerkeys, headerkeyssize);
int code = http.GET();
int len = http.getSize();
if(code <= 0) {
DEBUG_HTTP_UPDATE("[httpUpdate] HTTP error: %s\n", http.errorToString(code).c_str());
_lastError = code;
http.end();
return HTTP_UPDATE_FAILED;
}
DEBUG_HTTP_UPDATE("[httpUpdate] Header read fin.\n");
DEBUG_HTTP_UPDATE("[httpUpdate] Server header:\n");
DEBUG_HTTP_UPDATE("[httpUpdate] - code: %d\n", code);
DEBUG_HTTP_UPDATE("[httpUpdate] - len: %d\n", len);
if(http.hasHeader("x-MD5")) {
DEBUG_HTTP_UPDATE("[httpUpdate] - MD5: %s\n", http.header("x-MD5").c_str());
}
DEBUG_HTTP_UPDATE("[httpUpdate] ESP8266 info:\n");
DEBUG_HTTP_UPDATE("[httpUpdate] - free Space: %d\n", ESP.getFreeSketchSpace());
DEBUG_HTTP_UPDATE("[httpUpdate] - current Sketch Size: %d\n", ESP.getSketchSize());
if(currentVersion && currentVersion[0] != 0x00) {
DEBUG_HTTP_UPDATE("[httpUpdate] - current version: %s\n", currentVersion.c_str() );
}
switch(code) {
case HTTP_CODE_OK: ///< OK (Start Update)
if(len > 0) {
bool startUpdate = true;
// if(spiffs) {
// size_t spiffsSize = ((size_t) &_SPIFFS_end - (size_t) &_SPIFFS_start);
// if(len > (int) spiffsSize) {
// DEBUG_HTTP_UPDATE("[httpUpdate] spiffsSize to low (%d) needed: %d\n", spiffsSize, len);
// startUpdate = false;
// }
// } else {
// if(0/*len > (int) ESP.getFreeSketchSpace()*/) {
// // DEBUG_HTTP_UPDATE("[httpUpdate] FreeSketchSpace to low (%d) needed: %d\n", ESP.getFreeSketchSpace(), len);
// startUpdate = false;
// }
// }
if(!startUpdate) {
_lastError = HTTP_UE_TOO_LESS_SPACE;
ret = HTTP_UPDATE_FAILED;
} else {
WiFiClient * tcp = http.getStreamPtr();
// WiFiUDP::stop();
// WiFiClient::stopAllExcept(tcp);
delay(100);
int command;
if(spiffs) {
command = U_SPIFFS;
DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate spiffs...\n");
} else {
command = U_FLASH;
DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate flash...\n");
Serial.print("[httpUpdate] runUpdate flash...\n");
}
// if(!spiffs) {
// uint8_t buf[4];
// if(tcp->peekBytes(&buf[0], 4) != 4) {
// DEBUG_HTTP_UPDATE("[httpUpdate] peekBytes magic header failed\n");
// _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED;
// http.end();
// return HTTP_UPDATE_FAILED;
// }
// check for valid first magic byte
// if(buf[0] != 0xE9) {
// DEBUG_HTTP_UPDATE("[httpUpdate] magic header not starts with 0xE9\n");
// _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED;
// http.end();
// return HTTP_UPDATE_FAILED;
// }
// uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4);
// check if new bin fits to SPI flash
// if(bin_flash_size > ESP.getFlashChipRealSize()) {
// DEBUG_HTTP_UPDATE("[httpUpdate] magic header, new bin not fits SPI Flash\n");
// _lastError = HTTP_UE_BIN_FOR_WRONG_FLASH;
// http.end();
// return HTTP_UPDATE_FAILED;
// }
// }
if(runUpdate(*tcp, len, http.header("x-MD5"), command)) {
ret = HTTP_UPDATE_OK;
DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n");
Serial.print("[httpUpdate] Update ok\n");
http.end();
if(_rebootOnUpdate && !spiffs) {
ESP.restart();
}
} else {
ret = HTTP_UPDATE_FAILED;
DEBUG_HTTP_UPDATE("[httpUpdate] Update failed\n");
}
}
} else {
_lastError = HTTP_UE_SERVER_NOT_REPORT_SIZE;
ret = HTTP_UPDATE_FAILED;
DEBUG_HTTP_UPDATE("[httpUpdate] Content-Length is 0 or not set by Server?!\n");
}
break;
case HTTP_CODE_NOT_MODIFIED:
///< Not Modified (No updates)
ret = HTTP_UPDATE_NO_UPDATES;
break;
case HTTP_CODE_NOT_FOUND:
_lastError = HTTP_UE_SERVER_FILE_NOT_FOUND;
ret = HTTP_UPDATE_FAILED;
break;
case HTTP_CODE_FORBIDDEN:
_lastError = HTTP_UE_SERVER_FORBIDDEN;
ret = HTTP_UPDATE_FAILED;
break;
default:
_lastError = HTTP_UE_SERVER_WRONG_HTTP_CODE;
ret = HTTP_UPDATE_FAILED;
DEBUG_HTTP_UPDATE("[httpUpdate] HTTP Code is (%d)\n", code);
//http.writeToStream(&Serial1);
break;
}
http.end();
return ret;
}
/**
* write Update to flash
* @param in Stream&
* @param size uint32_t
* @param md5 String
* @return true if Update ok
*/
bool ESP8266HTTPUpdate::runUpdate(Stream& in, uint32_t size, String md5, int command)
{
StreamString error;
if(!Update.begin(size, command)) {
_lastError = Update.getError();
Update.printError(error);
error.trim(); // remove line ending
DEBUG_HTTP_UPDATE("[httpUpdate] Update.begin failed! (%s)\n", error.c_str());
Serial.printf("[httpUpdate] Update.begin failed! (%s)\n", error.c_str());
return false;
}
if(md5.length()) {
if(!Update.setMD5(md5.c_str())) {
_lastError = HTTP_UE_SERVER_FAULTY_MD5;
DEBUG_HTTP_UPDATE("[httpUpdate] Update.setMD5 failed! (%s)\n", md5.c_str());
Serial.printf("[httpUpdate] Update.setMD5 failed! (%s)\n", md5.c_str());
return false;
}
}
if(Update.writeStream(in) != size) {
_lastError = Update.getError();
Update.printError(error);
error.trim(); // remove line ending
DEBUG_HTTP_UPDATE("[httpUpdate] Update.writeStream failed! (%s)\n", error.c_str());
Serial.printf("[httpUpdate] Update.writeStream failed! (%s)\n", error.c_str());
return false;
}
if(!Update.end()) {
_lastError = Update.getError();
Update.printError(error);
error.trim(); // remove line ending
DEBUG_HTTP_UPDATE("[httpUpdate] Update.end failed! (%s)\n", error.c_str());
Serial.printf("[httpUpdate] Update.end failed! (%s)\n", error.c_str());
return false;
}
return true;
}
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
ESP8266HTTPUpdate ESPhttpUpdate;
#endif
@@ -0,0 +1,116 @@
/**
*
* @file ESP8266HTTPUpdate.h
* @date 21.06.2015
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the ESP8266 Http Updater.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef ESP8266HTTPUPDATE_H_
#define ESP8266HTTPUPDATE_H_
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include "ESP8266HTTPClient.h"
#include "Update.h"
#include "WiFiUdp.h"
#define DEBUG_ESP_HTTP_UPDATE
#ifdef DEBUG_ESP_HTTP_UPDATE
#ifdef DEBUG_ESP_PORT
#define DEBUG_HTTP_UPDATE(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ )
#endif
#endif
#ifndef DEBUG_HTTP_UPDATE
#define DEBUG_HTTP_UPDATE(...)
#endif
/// note we use HTTP client errors too so we start at 100
#define HTTP_UE_TOO_LESS_SPACE (-100)
#define HTTP_UE_SERVER_NOT_REPORT_SIZE (-101)
#define HTTP_UE_SERVER_FILE_NOT_FOUND (-102)
#define HTTP_UE_SERVER_FORBIDDEN (-103)
#define HTTP_UE_SERVER_WRONG_HTTP_CODE (-104)
#define HTTP_UE_SERVER_FAULTY_MD5 (-105)
#define HTTP_UE_BIN_VERIFY_HEADER_FAILED (-106)
#define HTTP_UE_BIN_FOR_WRONG_FLASH (-107)
enum HTTPUpdateResult {
HTTP_UPDATE_FAILED,
HTTP_UPDATE_NO_UPDATES,
HTTP_UPDATE_OK
};
typedef HTTPUpdateResult t_httpUpdate_return; // backward compatibility
class ESP8266HTTPUpdate
{
public:
ESP8266HTTPUpdate(void);
~ESP8266HTTPUpdate(void);
void rebootOnUpdate(bool reboot)
{
_rebootOnUpdate = reboot;
}
// This function is deprecated, use rebootOnUpdate and the next one instead
t_httpUpdate_return update(const String& url, const String& currentVersion,
const String& httpsFingerprint, bool reboot) __attribute__((deprecated));
t_httpUpdate_return update(const String& url, const String& currentVersion = "");
t_httpUpdate_return update(const String& url, const String& currentVersion,
const String& httpsFingerprint);
// This function is deprecated, use one of the overloads below along with rebootOnUpdate
t_httpUpdate_return update(const String& host, uint16_t port, const String& uri, const String& currentVersion,
bool https, const String& httpsFingerprint, bool reboot) __attribute__((deprecated));
t_httpUpdate_return update(const String& host, uint16_t port, const String& uri = "/",
const String& currentVersion = "");
t_httpUpdate_return update(const String& host, uint16_t port, const String& url,
const String& currentVersion, const String& httpsFingerprint);
// This function is deprecated, use rebootOnUpdate and the next one instead
t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion,
const String& httpsFingerprint, bool reboot) __attribute__((deprecated));
t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion = "");
t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, const String& httpsFingerprint);
int getLastError(void);
String getLastErrorString(void);
protected:
t_httpUpdate_return handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs = false);
bool runUpdate(Stream& in, uint32_t size, String md5, int command = U_FLASH);
int _lastError;
bool _rebootOnUpdate = true;
};
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
extern ESP8266HTTPUpdate ESPhttpUpdate;
#endif
#endif /* ESP8266HTTPUPDATE_H_ */
+161
View File
@@ -0,0 +1,161 @@
#ifndef ESP8266UPDATER_H
#define ESP8266UPDATER_H
#include <Arduino.h>
#include <MD5Builder.h>
#include "esp_partition.h"
#define UPDATE_ERROR_OK (0)
#define UPDATE_ERROR_WRITE (1)
#define UPDATE_ERROR_ERASE (2)
#define UPDATE_ERROR_READ (3)
#define UPDATE_ERROR_SPACE (4)
#define UPDATE_ERROR_SIZE (5)
#define UPDATE_ERROR_STREAM (6)
#define UPDATE_ERROR_MD5 (7)
#define UPDATE_ERROR_MAGIC_BYTE (8)
#define UPDATE_ERROR_ACTIVATE (9)
#define UPDATE_ERROR_NO_PARTITION (10)
#define UPDATE_ERROR_BAD_ARGUMENT (11)
#define UPDATE_ERROR_ABORT (12)
#define UPDATE_SIZE_UNKNOWN 0xFFFFFFFF
#define U_FLASH 0
#define U_SPIFFS 100
#define U_AUTH 200
class UpdateClass {
public:
UpdateClass();
/*
Call this to check the space needed for the update
Will return false if there is not enough space
*/
bool begin(size_t size=UPDATE_SIZE_UNKNOWN, int command = U_FLASH);
/*
Writes a buffer to the flash and increments the address
Returns the amount written
*/
size_t write(uint8_t *data, size_t len);
/*
Writes the remaining bytes from the Stream to the flash
Uses readBytes() and sets UPDATE_ERROR_STREAM on timeout
Returns the bytes written
Should be equal to the remaining bytes when called
Usable for slow streams like Serial
*/
size_t writeStream(Stream &data);
/*
If all bytes are written
this call will write the config to eboot
and return true
If there is already an update running but is not finished and !evenIfRemainanig
or there is an error
this will clear everything and return false
the last error is available through getError()
evenIfRemaining is helpfull when you update without knowing the final size first
*/
bool end(bool evenIfRemaining = false);
/*
Aborts the running update
*/
void abort();
/*
Prints the last error to an output stream
*/
void printError(Stream &out);
/*
sets the expected MD5 for the firmware (hexString)
*/
bool setMD5(const char * expected_md5);
/*
returns the MD5 String of the sucessfully ended firmware
*/
String md5String(void){ return _md5.toString(); }
/*
populated the result with the md5 bytes of the sucessfully ended firmware
*/
void md5(uint8_t * result){ return _md5.getBytes(result); }
//Helpers
uint8_t getError(){ return _error; }
void clearError(){ _error = UPDATE_ERROR_OK; }
bool hasError(){ return _error != UPDATE_ERROR_OK; }
bool isRunning(){ return _size > 0; }
bool isFinished(){ return _progress == _size; }
size_t size(){ return _size; }
size_t progress(){ return _progress; }
size_t remaining(){ return _size - _progress; }
/*
Template to write from objects that expose
available() and read(uint8_t*, size_t) methods
faster than the writeStream method
writes only what is available
*/
template<typename T>
size_t write(T &data){
size_t written = 0;
if (hasError() || !isRunning())
return 0;
size_t available = data.available();
while(available) {
if(_bufferLen + available > remaining()){
available = remaining() - _bufferLen;
}
if(_bufferLen + available > 4096) {
size_t toBuff = 4096 - _bufferLen;
data.read(_buffer + _bufferLen, toBuff);
_bufferLen += toBuff;
if(!_writeBuffer())
return written;
written += toBuff;
} else {
data.read(_buffer + _bufferLen, available);
_bufferLen += available;
written += available;
if(_bufferLen == remaining()) {
if(!_writeBuffer()) {
return written;
}
}
}
if(remaining() == 0)
return written;
available = data.available();
}
return written;
}
private:
void _reset();
void _abort(uint8_t err);
bool _writeBuffer();
bool _verifyHeader(uint8_t data);
bool _verifyEnd();
uint8_t _error;
uint8_t *_buffer;
size_t _bufferLen;
size_t _size;
uint32_t _progress;
uint32_t _command;
const esp_partition_t* _partition;
String _target_md5;
MD5Builder _md5;
};
extern UpdateClass Update;
#endif
+279
View File
@@ -0,0 +1,279 @@
#include "Update.h"
#include "Arduino.h"
#include "esp_spi_flash.h"
#include "esp_ota_ops.h"
#include "esp_image_format.h"
static const char * _err2str(uint8_t _error){
if(_error == UPDATE_ERROR_OK){
return ("No Error");
} else if(_error == UPDATE_ERROR_WRITE){
return ("Flash Write Failed");
} else if(_error == UPDATE_ERROR_ERASE){
return ("Flash Erase Failed");
} else if(_error == UPDATE_ERROR_READ){
return ("Flash Read Failed");
} else if(_error == UPDATE_ERROR_SPACE){
return ("Not Enough Space");
} else if(_error == UPDATE_ERROR_SIZE){
return ("Bad Size Given");
} else if(_error == UPDATE_ERROR_STREAM){
return ("Stream Read Timeout");
} else if(_error == UPDATE_ERROR_MD5){
return ("MD5 Check Failed");
} else if(_error == UPDATE_ERROR_MAGIC_BYTE){
return ("Wrong Magic Byte");
} else if(_error == UPDATE_ERROR_ACTIVATE){
return ("Could Not Activate The Firmware");
} else if(_error == UPDATE_ERROR_NO_PARTITION){
return ("Partition Could Not be Found");
} else if(_error == UPDATE_ERROR_BAD_ARGUMENT){
return ("Bad Argument");
} else if(_error == UPDATE_ERROR_ABORT){
return ("Aborted");
}
return ("UNKNOWN");
}
UpdateClass::UpdateClass()
: _error(0)
, _buffer(0)
, _bufferLen(0)
, _size(0)
, _progress(0)
, _command(U_FLASH)
, _partition(NULL)
{
}
void UpdateClass::_reset() {
if (_buffer)
delete[] _buffer;
_buffer = 0;
_bufferLen = 0;
_progress = 0;
_size = 0;
_command = U_FLASH;
}
bool UpdateClass::begin(size_t size, int command) {
if(_size > 0){
log_w("already running");
return false;
}
_reset();
_error = 0;
if(size == 0) {
_error = UPDATE_ERROR_SIZE;
return false;
}
if (command == U_FLASH) {
_partition = esp_ota_get_next_update_partition(NULL);
if(!_partition){
_error = UPDATE_ERROR_NO_PARTITION;
return false;
}
log_d("OTA Partition: %s", _partition->label);
}
else if (command == U_SPIFFS) {
_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL);
if(!_partition){
_error = UPDATE_ERROR_NO_PARTITION;
return false;
}
}
else {
_error = UPDATE_ERROR_BAD_ARGUMENT;
log_e("bad command %u", command);
return false;
}
if(size == UPDATE_SIZE_UNKNOWN){
size = _partition->size;
} else if(size > _partition->size){
_error = UPDATE_ERROR_SIZE;
log_e("too large %u > %u", size, _partition->size);
return false;
}
//initialize
_buffer = (uint8_t*)malloc(SPI_FLASH_SEC_SIZE);
if(!_buffer){
log_e("malloc failed");
return false;
}
_size = size;
_command = command;
_md5.begin();
return true;
}
void UpdateClass::_abort(uint8_t err){
_reset();
_error = err;
}
void UpdateClass::abort(){
_abort(UPDATE_ERROR_ABORT);
}
bool UpdateClass::_writeBuffer(){
if(!ESP.flashEraseSector((_partition->address + _progress)/SPI_FLASH_SEC_SIZE)){
_abort(UPDATE_ERROR_ERASE);
return false;
}
if (!ESP.flashWrite(_partition->address + _progress, (uint32_t*)_buffer, _bufferLen)) {
_abort(UPDATE_ERROR_WRITE);
return false;
}
_md5.add(_buffer, _bufferLen);
_progress += _bufferLen;
_bufferLen = 0;
return true;
}
bool UpdateClass::_verifyHeader(uint8_t data) {
if(_command == U_FLASH) {
if(data != ESP_IMAGE_HEADER_MAGIC) {
_abort(UPDATE_ERROR_MAGIC_BYTE);
return false;
}
return true;
} else if(_command == U_SPIFFS) {
return true;
}
return false;
}
bool UpdateClass::_verifyEnd() {
if(_command == U_FLASH) {
uint8_t buf[4];
if(!ESP.flashRead(_partition->address, (uint32_t*)buf, 4)) {
_abort(UPDATE_ERROR_READ);
return false;
}
if(buf[0] != ESP_IMAGE_HEADER_MAGIC) {
_abort(UPDATE_ERROR_MAGIC_BYTE);
return false;
}
if(esp_ota_set_boot_partition(_partition)){
_abort(UPDATE_ERROR_ACTIVATE);
return false;
}
_reset();
return true;
} else if(_command == U_SPIFFS) {
return true;
}
return false;
}
bool UpdateClass::setMD5(const char * expected_md5){
if(strlen(expected_md5) != 32)
{
return false;
}
_target_md5 = expected_md5;
return true;
}
bool UpdateClass::end(bool evenIfRemaining){
if(hasError() || _size == 0){
return false;
}
if(!isFinished() && !evenIfRemaining){
log_e("premature end: res:%u, pos:%u/%u\n", getError(), progress(), _size);
_abort(UPDATE_ERROR_ABORT);
return false;
}
if(evenIfRemaining) {
if(_bufferLen > 0) {
_writeBuffer();
}
_size = progress();
}
_md5.calculate();
if(_target_md5.length()) {
if(_target_md5 != _md5.toString()){
_abort(UPDATE_ERROR_MD5);
return false;
}
}
return _verifyEnd();
}
size_t UpdateClass::write(uint8_t *data, size_t len) {
if(hasError() || !isRunning()){
return 0;
}
if(len > remaining()){
_abort(UPDATE_ERROR_SPACE);
return 0;
}
size_t left = len;
while((_bufferLen + left) > SPI_FLASH_SEC_SIZE) {
size_t toBuff = SPI_FLASH_SEC_SIZE - _bufferLen;
memcpy(_buffer + _bufferLen, data + (len - left), toBuff);
_bufferLen += toBuff;
if(!_writeBuffer()){
return len - left;
}
left -= toBuff;
}
memcpy(_buffer + _bufferLen, data + (len - left), left);
_bufferLen += left;
if(_bufferLen == remaining()){
if(!_writeBuffer()){
return len - left;
}
}
return len;
}
size_t UpdateClass::writeStream(Stream &data) {
size_t written = 0;
size_t toRead = 0;
if(hasError() || !isRunning())
return 0;
// if(!_verifyHeader(data.peek())) {
// Serial.print("_reset\r\n");
// _reset();
// return 0;
// }
while(remaining()) {
toRead = data.readBytes(_buffer + _bufferLen, (SPI_FLASH_SEC_SIZE - _bufferLen));
if(toRead == 0) { //Timeout
delay(100);
toRead = data.readBytes(_buffer + _bufferLen, (SPI_FLASH_SEC_SIZE - _bufferLen));
if(toRead == 0) { //Timeout
_abort(UPDATE_ERROR_STREAM);
return written;
}
}
_bufferLen += toRead;
if((_bufferLen == remaining() || _bufferLen == SPI_FLASH_SEC_SIZE) && !_writeBuffer())
return written;
written += toRead;
}
return written;
}
void UpdateClass::printError(Stream &out){
out.println(_err2str(_error));
}
UpdateClass Update;
@@ -0,0 +1,80 @@
/**
* httpUpdate.ino
*
* Created on: 27.11.2015
*
*/
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include "ESP8266HTTPClient.h"
#include "ESP8266httpUpdate.h"
#include <WiFiClientSecure.h>
#define USE_SERIAL Serial
WiFiMulti wifiMulti;
void setup() {
USE_SERIAL.begin(115200);
// USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
USE_SERIAL.println();
USE_SERIAL.println();
// Serial.print("update success!\r\n");
for(uint8_t t = 4; t > 0; t--) {
USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
USE_SERIAL.flush();
delay(100);
}
WiFi.begin("MasterHax_2.4G", "wittyercheese551");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
// wifiMulti.addAP("MasterHax_2.4G", "wittyercheese551");
// Serial.println("Connecting Wifi...");
// if(wifiMulti.run() == WL_CONNECTED) {
// Serial.println("");
// Serial.println("WiFi connected");
// Serial.println("IP address: ");
// Serial.println(WiFi.localIP());
// }
}
void loop() {
// wait for WiFi connection
// if((wifiMulti.run() == WL_CONNECTED)) {
if(1) {
Serial.printf("connect...\n");
t_httpUpdate_return ret = ESPhttpUpdate.update("http://olcunuug8.bkt.clouddn.com/httpUpdate.ino.esp32.bin");
//t_httpUpdate_return ret = ESPhttpUpdate.update("https://server/file.bin");
switch(ret) {
case HTTP_UPDATE_FAILED:
USE_SERIAL.printf("HTTP_UPDATE_FAILD Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
USE_SERIAL.println("HTTP_UPDATE_NO_UPDATES");
break;
case HTTP_UPDATE_OK:
USE_SERIAL.println("HTTP_UPDATE_OK");
break;
}
}
}

Some files were not shown because too many files have changed in this diff Show More