├── .gitignore ├── LICENSE ├── README.md ├── data └── ver ├── examples └── example_EHJO.cpp ├── include └── README ├── json ├── base.json ├── fs-esp32ehjo.json └── fw-esp32ehjo.json ├── lib └── README ├── manifest └── esp32HttpJsonOTA.json ├── platformio.ini ├── src ├── esp32HttpJsonOTA.cpp ├── esp32HttpJsonOTA.h └── example_EHJO.cpp └── test └── README /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Franck RONDOT 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp32HttpJsonOTA 2 | ESP32 HTTP OTA Firmware and SPIFFS update with Json config and version control 3 | Perform an OTA update of firmware or SPIFFS from a bin located on a webserver (HTTP Only) 4 | without server side script. Use simple webserver, Google Cloud Platform storage, and and others 5 | simple HTTP file share system. 6 | Date: Mars 2020 7 | Author: Franck RONDOT https://www.franck-rondot.com 8 | Based on esp32FOTA by Chris Joyce 9 | I changed somes codes and add SPIFFS update support 10 | 11 | ## Nota : 12 | If your use Google Cloud Platform for data storage of json and .bin change the datatype to : application/octet-stream 13 | If you have some trouble to connect deactivate the WAF on your sub domain, some WAF detect some user agent, cookies... 14 | 15 | You could try CURL to verify your connection ie : curl --head https://update.website.com/appname/check.php 16 | 17 | ## Example 18 | ### json file for sketch firmware 19 | ```json 20 | { 21 | "name": "ESP32APPFR", 22 | "type": "FIRMWARE", 23 | "version": 1, 24 | "host": "storage.googleapis.com", 25 | "port": 80, 26 | "bin": "/update/esp32ehjo/fw-esp32ehjo.bin" 27 | } 28 | ``` 29 | ### json file for SPIFFS 30 | ```json 31 | { 32 | "name": "ESP32APPFR", 33 | "type": "SPIFFS", 34 | "version": 1, 35 | "host": "storage.googleapis.com", 36 | "port": 80, 37 | "bin": "/update/esp32ehjo/fs-esp32ehjo.bin" 38 | } 39 | ``` 40 | ### Cpp example 41 | ```cpp 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include "esp32HttpJsonOTA.h" 48 | 49 | // Nom et version pour la mise a jour du FW 50 | #define SSID "WIFISSID" 51 | #define PASSWORD "WIFIPASSWORD" 52 | #define OTA_NAME "ESP32APPFR" 53 | #define OTA_VER 1 54 | #define FWOTA_JSONURL "http://update.website.com/update/esp32ehjo/fw-esp32ehjo.json" 55 | #define FSOTA_JSONURL "http://update.website.com/update/esp32ehjo/fs-esp32ehjo.json" 56 | 57 | esp32HttpJsonOTA majFW(OTA_NAME, "FIRMWARE", OTA_VER, FWOTA_JSONURL); 58 | esp32HttpJsonOTA majFS(OTA_NAME, "SPIFFS", OTA_VER, FSOTA_JSONURL); 59 | 60 | static const char* TAG = "EHJO"; 61 | 62 | int verFS() 63 | { 64 | File myFile; 65 | String ver = ""; 66 | 67 | // Init SPIFFS 68 | if(!SPIFFS.begin(true)) 69 | { 70 | ESP_LOGE(TAG, "Mount error of SPIFFS..."); 71 | return 0; 72 | } 73 | else 74 | { 75 | myFile = SPIFFS.open("/ver", "r"); 76 | 77 | if(myFile) 78 | { 79 | while(myFile.available()) 80 | { 81 | ver = myFile.readString(); 82 | } 83 | 84 | myFile.close(); 85 | ESP_LOGD(TAG, "SPIFFS version : %d", ver.toInt()); 86 | 87 | return ver.toInt(); 88 | } 89 | else 90 | { 91 | return 1; 92 | } 93 | } 94 | } 95 | 96 | bool newVerFW() 97 | { 98 | bool maj = majFW.execHTTPcheck(); 99 | ESP_LOGD(TAG, "Sketch update available : %s", maj ? "Yes" : "No"); 100 | return maj; 101 | } 102 | 103 | void updateFW() 104 | { 105 | majFW.execOTA(); 106 | } 107 | 108 | bool newVerFS() 109 | { 110 | majFS.setVer(verFS()); 111 | bool maj = majFS.execHTTPcheck(); 112 | ESP_LOGD(TAG, "SPIFFS update available : %s", maj ? "Yes" : "No"); 113 | return maj; 114 | } 115 | 116 | void updateFS() 117 | { 118 | majFS.execOTA(); 119 | } 120 | 121 | void setup_wifi() 122 | { 123 | WiFi.begin(SSID, PASSWORD); 124 | 125 | while (WiFi.status() != WL_CONNECTED) 126 | { 127 | Serial.print("."); 128 | delay(500); 129 | } 130 | 131 | ESP_LOGI(TAG, "IP address : %s", WiFi.localIP().toString().c_str()); 132 | } 133 | 134 | void forceUpd() 135 | { 136 | majFW.forceUpdate("192.168.0.100", 80, "/firmware.bin", "FIRMWARE"); 137 | } 138 | 139 | void checkDeviceID() 140 | { 141 | majFW.useDeviceID = true; 142 | bool updatedNeeded = majFW.execHTTPcheck(); 143 | if (updatedNeeded) 144 | { 145 | majFW.execOTA(); 146 | } 147 | } 148 | 149 | void setup() 150 | { 151 | // esp32 log lavel 152 | esp_log_level_set("*", ESP_LOG_VERBOSE); 153 | 154 | Serial.begin(115200); 155 | 156 | setup_wifi(); 157 | 158 | if (newVerFW()) 159 | { 160 | ESP_LOGI(TAG, "Sketch update available !"); 161 | updateFW(); 162 | } 163 | 164 | if (newVerFS()) 165 | { 166 | ESP_LOGI(TAG, "SPIFFS update available !"); 167 | updateFS(); 168 | } 169 | } 170 | 171 | void loop() 172 | { 173 | delay(100); 174 | } 175 | ``` 176 | -------------------------------------------------------------------------------- /data/ver: -------------------------------------------------------------------------------- 1 | 1 -------------------------------------------------------------------------------- /examples/example_EHJO.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Example for esp32HttpOTA 3 | esp32 http firmware/SPIFFS OTA 4 | Date: Mars 2020 5 | Author: Franck RONDOT 6 | www.franck-rondot.com 7 | Based on esp32FOTA by Chris Joyce 8 | I changed somes codes and add SPIFFS update support 9 | Purpose: Perform an OTA update of firmware or SPIFFS from a bin located on a webserver (HTTP Only) 10 | 11 | If your use Google Cloud Platform for data storage of json and .bin change the datatype to : application/octet-stream 12 | If you have some trouble to connect deactivate the WAF on your sub domain, some WAF detect some user agent, cookies... 13 | 14 | You could try CURL to verify your connection ie : curl --head https://update.website.com/appname/check.php 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "esp32HttpJsonOTA.h" 23 | 24 | // Nom et version pour la mise a jour du FW 25 | #define SSID "WIFISSID" 26 | #define PASSWORD "WIFIPASSWORD" 27 | #define OTA_NAME "ESP32APPFR" 28 | #define OTA_VER 1 29 | #define FWOTA_JSONURL "http://update.website.com/update/esp32ehjo/fw-esp32ehjo.json" 30 | #define FSOTA_JSONURL "http://update.website.com/update/esp32ehjo/fs-esp32ehjo.json" 31 | 32 | esp32HttpJsonOTA majFW(OTA_NAME, "FIRMWARE", OTA_VER, FWOTA_JSONURL); 33 | esp32HttpJsonOTA majFS(OTA_NAME, "SPIFFS", OTA_VER, FSOTA_JSONURL); 34 | 35 | static const char* TAG = "EHJO"; 36 | 37 | int verFS() 38 | { 39 | File myFile; 40 | String ver = ""; 41 | 42 | // Init SPIFFS 43 | if(!SPIFFS.begin(true)) 44 | { 45 | ESP_LOGE(TAG, "Mount error of SPIFFS..."); 46 | return 0; 47 | } 48 | else 49 | { 50 | myFile = SPIFFS.open("/ver", "r"); 51 | 52 | if(myFile) 53 | { 54 | while(myFile.available()) 55 | { 56 | ver = myFile.readString(); 57 | } 58 | 59 | myFile.close(); 60 | ESP_LOGD(TAG, "SPIFFS version : %d", ver.toInt()); 61 | 62 | return ver.toInt(); 63 | } 64 | else 65 | { 66 | return 1; 67 | } 68 | } 69 | } 70 | 71 | bool newVerFW() 72 | { 73 | bool maj = majFW.execHTTPcheck(); 74 | ESP_LOGD(TAG, "Sketch update available : %s", maj ? "Yes" : "No"); 75 | return maj; 76 | } 77 | 78 | void updateFW() 79 | { 80 | majFW.execOTA(); 81 | } 82 | 83 | bool newVerFS() 84 | { 85 | majFS.setVer(verFS()); 86 | bool maj = majFS.execHTTPcheck(); 87 | ESP_LOGD(TAG, "SPIFFS update available : %s", maj ? "Yes" : "No"); 88 | return maj; 89 | } 90 | 91 | void updateFS() 92 | { 93 | majFS.execOTA(); 94 | } 95 | 96 | void setup_wifi() 97 | { 98 | WiFi.begin(SSID, PASSWORD); 99 | 100 | while (WiFi.status() != WL_CONNECTED) 101 | { 102 | Serial.print("."); 103 | delay(500); 104 | } 105 | 106 | ESP_LOGI(TAG, "IP address : %s", WiFi.localIP().toString().c_str()); 107 | } 108 | 109 | void forceUpd() 110 | { 111 | majFW.forceUpdate("192.168.0.100", 80, "/firmware.bin", "FIRMWARE"); 112 | } 113 | 114 | void checkDeviceID() 115 | { 116 | majFW.useDeviceID = true; 117 | bool updatedNeeded = majFW.execHTTPcheck(); 118 | if (updatedNeeded) 119 | { 120 | majFW.execOTA(); 121 | } 122 | } 123 | 124 | void setup() 125 | { 126 | // esp32 log lavel 127 | esp_log_level_set("*", ESP_LOG_VERBOSE); 128 | 129 | Serial.begin(115200); 130 | 131 | setup_wifi(); 132 | 133 | if (newVerFW()) 134 | { 135 | ESP_LOGI(TAG, "Sketch update available !"); 136 | updateFW(); 137 | } 138 | 139 | if (newVerFS()) 140 | { 141 | ESP_LOGI(TAG, "SPIFFS update available !"); 142 | updateFS(); 143 | } 144 | } 145 | 146 | void loop() 147 | { 148 | delay(100); 149 | } 150 | -------------------------------------------------------------------------------- /include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the usual convention is to give header files names that end with `.h'. 29 | It is most portable to use only letters, digits, dashes, and underscores in 30 | header file names, and at most one dot. 31 | 32 | Read more about using header files in official GCC documentation: 33 | 34 | * Include Syntax 35 | * Include Operation 36 | * Once-Only Headers 37 | * Computed Includes 38 | 39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 40 | -------------------------------------------------------------------------------- /json/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "XXXXXXXXXXXXXXXXXXXX", 3 | "type": "XXXXXXXX", 4 | "version": 2, 5 | "host": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 6 | "port": 80, 7 | "bin": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" 8 | } -------------------------------------------------------------------------------- /json/fs-esp32ehjo.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ESP32APPFR", 3 | "type": "SPIFFS", 4 | "version": 1, 5 | "host": "storage.googleapis.com", 6 | "port": 80, 7 | "bin": "/update/esp32ehjo/fs-esp32ehjo.bin" 8 | } -------------------------------------------------------------------------------- /json/fw-esp32ehjo.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ESP32APPFR", 3 | "type": "FIRMWARE", 4 | "version": 1, 5 | "host": "storage.googleapis.com", 6 | "port": 80, 7 | "bin": "/update/esp32ehjo/fw-esp32ehjo.bin" 8 | } 9 | -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into executable file. 4 | 5 | The source code of each library should be placed in a an own separate directory 6 | ("lib/your_library_name/[here are source files]"). 7 | 8 | For example, see a structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | and a contents of `src/main.c`: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /manifest/esp32HttpJsonOTA.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esp32HttpJsonOTA", 3 | "keywords": "esp32, OTA, json, firmware, SPIFFS, HTTP", 4 | "description": "Perform an OTA update of firmware or SPIFFS from a bin located on a webserver (HTTP Only)", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/meditant/esp32HttpJsonOTA.git" 9 | }, 10 | "authors": 11 | [ 12 | { 13 | "name": "Franck RONDOT", 14 | "email": "contact@franck-rondot.com", 15 | "url": "https://www.franck-rondot.com", 16 | "maintainer": true 17 | } 18 | ], 19 | "dependencies": [ 20 | { 21 | "name": "ArduinoJson", 22 | "authors": "Benoît Blanchon", 23 | "frameworks": "arduino" 24 | } 25 | ], 26 | "version": "1.0", 27 | "frameworks": "arduino", 28 | "platforms": "*" 29 | } -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env:lolin32] 12 | platform = espressif32 13 | board = lolin32 14 | framework = arduino 15 | monitor_speed = 115200 16 | upload_speed = 230400 17 | ;upload_port = 18 | 19 | ;build_flags = -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE 20 | ;build_flags = -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_ERROR 21 | ;build_flags = -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_WARN 22 | ;build_flags = -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_INFO 23 | build_flags = -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG 24 | ;build_flags = -D CORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_VERBOSE 25 | 26 | lib_deps = 27 | ArduinoJson -------------------------------------------------------------------------------- /src/esp32HttpJsonOTA.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | esp32HttpOTA 3 | esp32 http firmware/SPIFFS OTA 4 | Date: Mars 2020 5 | Author: Franck RONDOT 6 | www.franck-rondot.com 7 | Based on esp32FOTA by Chris Joyce 8 | I changed somes codes and add SPIFFS update support 9 | Purpose: Perform an OTA update of firmware or SPIFFS from a bin located on a webserver (HTTP Only) 10 | 11 | If your use Google Cloud Platform for data storage of json and .bin change the datatype to : application/octet-stream 12 | If you have some trouble to connect deactivate the WAF on your sub domain, some WAF detect some user agent, cookies... 13 | 14 | You could try CURL to verify your connection ie : curl --head https://update.website.com/appname/check.php 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "esp32HttpJsonOTA.h" 24 | 25 | esp32HttpJsonOTA::esp32HttpJsonOTA(String firmwareName, String firmwareType, int firmwareVersion, String urlJson) 26 | { 27 | _firmwareName = firmwareName; 28 | strlcpy(_cnf.type, firmwareType.c_str(), sizeof(_cnf.type)); 29 | _firmwareVersion = firmwareVersion; 30 | _checkURL = urlJson; 31 | 32 | useDeviceID = false; 33 | } 34 | 35 | // Set the version after initialisation 36 | void esp32HttpJsonOTA::setVer(int ver) 37 | { 38 | _firmwareVersion = ver; 39 | } 40 | 41 | // Utility to extract header value from headers 42 | String esp32HttpJsonOTA::getHeaderValue(String header, String headerName) 43 | { 44 | return header.substring(strlen(headerName.c_str())); 45 | } 46 | 47 | // OTA updating of firmware or SPIFFS 48 | void esp32HttpJsonOTA::execOTA() 49 | { 50 | WiFiClient client; 51 | int contentLength = 0; 52 | bool isValidContentType = false; 53 | String header = ""; 54 | String get = ""; 55 | 56 | ESP_LOGI(OTATAG, "Connecting to: %s port %d", _cnf.host, _cnf.port); 57 | // Connect to Webserver 58 | if (client.connect(_cnf.host, _cnf.port)) 59 | { 60 | // Fecthing the bin 61 | ESP_LOGI(OTATAG, "Fetching Bin: %s", _cnf.bin); 62 | 63 | // Get the contents of the bin file 64 | get = String("GET ") + String(_cnf.bin) + String(" HTTP/1.1\r\nHost: ") + String(_cnf.host) + String("\r\nCache-Control: no-cache\r\nConnection: close\r\n\r\n"); 65 | ESP_LOGD(OTATAG, "GET : %s", get.c_str()); 66 | client.print(get.c_str()); 67 | 68 | unsigned long timeout = millis(); 69 | while (client.available() == 0) 70 | { 71 | if (millis() - timeout > 3000) 72 | { 73 | ESP_LOGI(OTATAG, "Client Timeout !"); 74 | client.stop(); // Free resources 75 | return; 76 | } 77 | } 78 | 79 | while (client.available()) 80 | { 81 | // Read line till /n 82 | String line = client.readStringUntil('\n'); 83 | // Remove space, to check if the line is end of headers 84 | line.trim(); 85 | 86 | if (!line.length()) 87 | { 88 | // Headers ended and get the OTA started 89 | break; 90 | } 91 | 92 | // Check if the HTTP Response is 200 else break and Exit Update 93 | if (line.startsWith("HTTP/1.1")) 94 | { 95 | if (line.indexOf("200") < 0) 96 | { 97 | ESP_LOGD(OTATAG, "Got a non 200 status code from server. Exiting OTA Update."); 98 | break; 99 | } 100 | } 101 | 102 | // Extract headers 103 | 104 | // Content length 105 | header = line.substring(0, 16); 106 | 107 | if (header.equalsIgnoreCase("Content-Length: ")) 108 | { 109 | contentLength = atoi((getHeaderValue(line, "Content-Length: ")).c_str()); 110 | ESP_LOGI(OTATAG, "Got %d bytes from server", contentLength); 111 | } 112 | 113 | // Content type 114 | header = line.substring(0, 14); 115 | if (header.equalsIgnoreCase("Content-Type: ")) 116 | { 117 | String contentType = getHeaderValue(line, header); 118 | ESP_LOGI(OTATAG, "Got %s payload.", contentType.c_str()); 119 | if (contentType == "application/octet-stream") 120 | { 121 | isValidContentType = true; 122 | } 123 | } 124 | } 125 | } 126 | else 127 | { 128 | // Connect to webserver failed 129 | ESP_LOGI(OTATAG, "Connection to %s failed. Please check your setup", _cnf.host); 130 | client.stop(); // Free resources 131 | return; 132 | } 133 | 134 | // Check what is the contentLength and if content type is `application/octet-stream` 135 | ESP_LOGD(OTATAG, "contentLength : %s , isValidContentType : %s", String(contentLength),String(isValidContentType)); 136 | 137 | // Check contentLength and content type 138 | if (contentLength && isValidContentType) 139 | { 140 | // Check if there is enough to OTA Update and set the type of update FIRMWARE or SPIFFS 141 | int cmd = (String(_cnf.type) == "SPIFFS") ? U_SPIFFS : U_FLASH; 142 | ESP_LOGI(OTATAG, "OTA type : %s", (cmd == U_SPIFFS) ? "SPIFFS" : "FIRMWARE"); 143 | bool canBegin = Update.begin(contentLength, cmd); 144 | 145 | // If yes, begin 146 | if (canBegin) 147 | { 148 | ESP_LOGI("Begin OTA. This may take 2 - 5 mins to complete. Things might be quite for a while.. Patience!"); 149 | // No activity would appear on the Serial monitor 150 | // So be patient. This may take 2 - 5mins to complete 151 | size_t written = Update.writeStream(client); 152 | 153 | if (written == contentLength) 154 | { 155 | ESP_LOGI(OTATAG, "Written : %s successfully", String(written)); 156 | } 157 | else 158 | { 159 | ESP_LOGE(OTATAG, "Written only : %s/%s retry !", String(written), String(contentLength)); 160 | } 161 | 162 | if (Update.end()) 163 | { 164 | ESP_LOGI(OTATAG, "OTA done!"); 165 | if (Update.isFinished()) 166 | { 167 | ESP_LOGI(OTATAG, "Update successfully completed. Rebooting."); 168 | ESP.restart(); 169 | } 170 | else 171 | { 172 | ESP_LOGE(OTATAG, "Update not finished? Something went wrong!"); 173 | } 174 | } 175 | else 176 | { 177 | ESP_LOGE(OTATAG, "Error Occurred. Error #: %s", String(Update.getError())); 178 | } 179 | } 180 | else 181 | { 182 | // Not enough space to begin OTA 183 | ESP_LOGE(OTATAG, "Not enough space to begin OTA"); 184 | } 185 | } 186 | else 187 | { 188 | ESP_LOGE(OTATAG, "There was no content in the response"); 189 | } 190 | 191 | client.stop(); // Free resources 192 | } 193 | 194 | bool esp32HttpJsonOTA::execHTTPcheck() 195 | { 196 | 197 | String useURL; 198 | 199 | if (useDeviceID) 200 | { 201 | // String deviceID = getDeviceID() ; 202 | useURL = _checkURL + "?id=" + getDeviceID(); 203 | } 204 | else 205 | { 206 | useURL = _checkURL; 207 | } 208 | 209 | _cnf.port = 80; 210 | 211 | ESP_LOGD(OTATAG, "Getting HTTP : %s", useURL.c_str()); 212 | 213 | if ((WiFi.status() == WL_CONNECTED)) 214 | { 215 | //Check the current connection status 216 | 217 | HTTPClient http; 218 | 219 | http.begin(useURL); // Specify the URL 220 | int httpCode = http.GET(); // Make the request 221 | 222 | // Check if the file is receveid 223 | if (httpCode == 200) 224 | { 225 | const size_t capacity = JSON_OBJECT_SIZE(6) + 200; 226 | DynamicJsonDocument JSONDocument(capacity); 227 | 228 | DeserializationError err = deserializeJson(JSONDocument, http.getStream()); 229 | 230 | if (err) 231 | { 232 | //Check for errors in parsing 233 | ESP_LOGD(OTATAG, "Parsing failed"); 234 | http.end(); //Free the resources 235 | return false; 236 | } 237 | 238 | const char* pName = JSONDocument["name"]; 239 | String jsName(pName); 240 | 241 | int jsVer = JSONDocument["version"]; 242 | 243 | strlcpy(_cnf.type, JSONDocument["type"], sizeof(_cnf.type)); 244 | strlcpy(_cnf.host, JSONDocument["host"], sizeof(_cnf.host)); 245 | strlcpy(_cnf.bin, JSONDocument["bin"], sizeof(_cnf.bin)); 246 | _cnf.port = JSONDocument["port"]; 247 | 248 | ESP_LOGD(OTATAG, "name : %s", jsName.c_str()); 249 | ESP_LOGD(OTATAG, "type : %s", _cnf.type); 250 | ESP_LOGD(OTATAG, "version : %d", jsVer); 251 | ESP_LOGD(OTATAG, "host : %s", _cnf.host); 252 | ESP_LOGD(OTATAG, "port : %d", _cnf.port); 253 | ESP_LOGD(OTATAG, "bin : %s", _cnf.bin); 254 | 255 | if (jsVer > _firmwareVersion && jsName == _firmwareName) 256 | { 257 | http.end(); //Free the resources 258 | return true; // Update available 259 | } 260 | else 261 | { 262 | http.end(); //Free the resources 263 | return false; // Unavailable update 264 | } 265 | } 266 | else 267 | { 268 | ESP_LOGE(OTATAG, "Error on HTTP request"); 269 | http.end(); //Free the resources 270 | return false; 271 | } 272 | 273 | http.end(); //Free the resources 274 | return false; 275 | } 276 | return false; 277 | } 278 | 279 | String esp32HttpJsonOTA::getDeviceID() 280 | { 281 | char deviceid[21]; 282 | uint64_t chipid; 283 | chipid = ESP.getEfuseMac(); 284 | sprintf(deviceid, "%" PRIu64, chipid); 285 | String thisID(deviceid); 286 | return thisID; 287 | } 288 | 289 | // Force a firmware update regartless on current version 290 | void esp32HttpJsonOTA::forceUpdate(String firmwareHost, int firmwarePort, String firmwarePath, String firmwareType) 291 | { 292 | strlcpy(_cnf.host, firmwareHost.c_str(), sizeof(_cnf.host)); 293 | strlcpy(_cnf.bin, firmwarePath.c_str(), sizeof(_cnf.bin)); 294 | strlcpy(_cnf.type, firmwareType.c_str(), sizeof(_cnf.type)); 295 | 296 | _cnf.port = firmwarePort; 297 | 298 | execOTA(); 299 | } 300 | -------------------------------------------------------------------------------- /src/esp32HttpJsonOTA.h: -------------------------------------------------------------------------------- 1 | /* 2 | esp32HttpOTA 3 | esp32 http firmware/SPIFFS OTA 4 | Date: Mars 2020 5 | Author: Franck RONDOT 6 | www.franck-rondot.com 7 | Based on esp32FOTA by Chris Joyce 8 | I changed somes codes and add SPIFFS update support 9 | Purpose: Perform an OTA update of firmware or SPIFFS from a bin located on a webserver (HTTP Only) 10 | 11 | If your use Google Cloud Platform for data storage of json and .bin change the datatype to : application/octet-stream 12 | If you have some trouble to connect deactivate the WAF on your sub domain, some WAF detect some user agent, cookies... 13 | 14 | You could try CURL to verify your connection ie : curl --head https://update.website.com/appname/check.php 15 | */ 16 | 17 | #ifndef esp32HttpJsonOTA_h 18 | #define esp32HttpJsonOTA_h 19 | 20 | #include 21 | 22 | #include 23 | 24 | static const char* OTATAG = "OTA"; // ESP_LOG TAG 25 | 26 | struct otaConfig { 27 | char host[50]; // hostname or ip of firmware serveur without HTTP and subdir just the hostname like update.server.com 28 | char bin[60]; // the path of bin file start with / like /ver1/firmware.bin 29 | char type[9]; // the type of firmware FIRMWARE or SPIFFS 30 | int port = 80; // the port of the server 31 | }; 32 | 33 | class esp32HttpJsonOTA 34 | { 35 | public: 36 | esp32HttpJsonOTA(String firmwareName, String firmwareType, int firmwareVersion, String urlJson); 37 | 38 | void forceUpdate(String firmwareHost, int firmwarePort, String firmwarePath, String firmwareType); 39 | void execOTA(); 40 | bool execHTTPcheck(); 41 | bool useDeviceID; 42 | void setVer(int); 43 | 44 | private: 45 | String getHeaderValue(String header, String headerName); // Return header value 46 | String getDeviceID(); // Device ID 47 | String _firmwareName = ""; // Name of firmware 48 | int _firmwareVersion = 0; // Version of firmware 49 | String _checkURL = ""; // URL of json 50 | otaConfig _cnf; // struct otaConfig 51 | }; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /src/example_EHJO.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Example for esp32HttpOTA 3 | esp32 http firmware/SPIFFS OTA 4 | Date: Mars 2020 5 | Author: Franck RONDOT 6 | www.franck-rondot.com 7 | Based on esp32FOTA by Chris Joyce 8 | I changed somes codes and add SPIFFS update support 9 | Purpose: Perform an OTA update of firmware or SPIFFS from a bin located on a webserver (HTTP Only) 10 | 11 | If your use Google Cloud Platform for data storage of json and .bin change the datatype to : application/octet-stream 12 | If you have some trouble to connect deactivate the WAF on your sub domain, some WAF detect some user agent, cookies... 13 | 14 | You could try CURL to verify your connection ie : curl --head https://update.website.com/appname/check.php 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "esp32HttpJsonOTA.h" 23 | 24 | // Nom et version pour la mise a jour du FW 25 | #define SSID "WIFISSID" 26 | #define PASSWORD "WIFIPASSWORD" 27 | #define OTA_NAME "ESP32APPFR" 28 | #define OTA_VER 1 29 | #define FWOTA_JSONURL "http://update.website.com/update/esp32ehjo/fw-esp32ehjo.json" 30 | #define FSOTA_JSONURL "http://update.website.com/update/esp32ehjo/fs-esp32ehjo.json" 31 | 32 | esp32HttpJsonOTA majFW(OTA_NAME, "FIRMWARE", OTA_VER, FWOTA_JSONURL); 33 | esp32HttpJsonOTA majFS(OTA_NAME, "SPIFFS", OTA_VER, FSOTA_JSONURL); 34 | 35 | static const char* TAG = "EHJO"; 36 | 37 | int verFS() 38 | { 39 | File myFile; 40 | String ver = ""; 41 | 42 | // Init SPIFFS 43 | if(!SPIFFS.begin(true)) 44 | { 45 | ESP_LOGE(TAG, "Mount error of SPIFFS..."); 46 | return 0; 47 | } 48 | else 49 | { 50 | myFile = SPIFFS.open("/ver", "r"); 51 | 52 | if(myFile) 53 | { 54 | while(myFile.available()) 55 | { 56 | ver = myFile.readString(); 57 | } 58 | 59 | myFile.close(); 60 | ESP_LOGD(TAG, "SPIFFS version : %d", ver.toInt()); 61 | 62 | return ver.toInt(); 63 | } 64 | else 65 | { 66 | return 1; 67 | } 68 | } 69 | } 70 | 71 | bool newVerFW() 72 | { 73 | bool maj = majFW.execHTTPcheck(); 74 | ESP_LOGD(TAG, "Sketch update available : %s", maj ? "Yes" : "No"); 75 | return maj; 76 | } 77 | 78 | void updateFW() 79 | { 80 | majFW.execOTA(); 81 | } 82 | 83 | bool newVerFS() 84 | { 85 | majFS.setVer(verFS()); 86 | bool maj = majFS.execHTTPcheck(); 87 | ESP_LOGD(TAG, "SPIFFS update available : %s", maj ? "Yes" : "No"); 88 | return maj; 89 | } 90 | 91 | void updateFS() 92 | { 93 | majFS.execOTA(); 94 | } 95 | 96 | void setup_wifi() 97 | { 98 | WiFi.begin(SSID, PASSWORD); 99 | 100 | while (WiFi.status() != WL_CONNECTED) 101 | { 102 | Serial.print("."); 103 | delay(500); 104 | } 105 | 106 | ESP_LOGI(TAG, "IP address : %s", WiFi.localIP().toString().c_str()); 107 | } 108 | 109 | void forceUpd() 110 | { 111 | majFW.forceUpdate("192.168.0.100", 80, "/firmware.bin", "FIRMWARE"); 112 | } 113 | 114 | void checkDeviceID() 115 | { 116 | majFW.useDeviceID = true; 117 | bool updatedNeeded = majFW.execHTTPcheck(); 118 | if (updatedNeeded) 119 | { 120 | majFW.execOTA(); 121 | } 122 | } 123 | 124 | void setup() 125 | { 126 | // esp32 log lavel 127 | esp_log_level_set("*", ESP_LOG_VERBOSE); 128 | 129 | Serial.begin(115200); 130 | 131 | setup_wifi(); 132 | 133 | if (newVerFW()) 134 | { 135 | ESP_LOGI(TAG, "Sketch update available !"); 136 | updateFW(); 137 | } 138 | 139 | if (newVerFS()) 140 | { 141 | ESP_LOGI(TAG, "SPIFFS update available !"); 142 | updateFS(); 143 | } 144 | } 145 | 146 | void loop() 147 | { 148 | delay(100); 149 | } 150 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PIO Unit Testing and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PIO Unit Testing: 11 | - https://docs.platformio.org/page/plus/unit-testing.html 12 | --------------------------------------------------------------------------------