├── LICENSE
├── README.md
├── esp32_ble_ota
├── esp32_ble_ota.ino
└── esp32_ble_ota.ino.doitESP32devkitV1.bin
├── esp32_ble_ota_lib_compact
├── .gitignore
├── .vscode
│ ├── extensions.json
│ └── settings.json
├── README.md
├── discover.py
├── include
│ └── README
├── lib
│ ├── README
│ └── ble_ota_dfu
│ │ ├── keywords.txt
│ │ ├── library.json
│ │ ├── library.properties
│ │ └── src
│ │ ├── ble_ota_dfu.cpp
│ │ ├── ble_ota_dfu.hpp
│ │ ├── freertos_utils.cpp
│ │ └── freertos_utils.hpp
├── ota_updater.py
├── platformio.ini
├── src
│ ├── main.cpp
│ └── main.hpp
└── test
│ └── README
└── esp32_nim_ble_ota
├── esp32_nim_ble_ota.ino
└── esp32_nim_ble_ota.ino.doitESP32devkitV1.bin
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Felix Biego
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 | # ESP32_BLE_OTA_Arduino
2 | OTA update on ESP32 via BLE
3 |
4 | - 1,038,544 bytes uploaded in 1min 25sec
5 | - Speed: ~ `12kb/s peak`
6 |
7 | [`esp32_ble_ota`](https://github.com/fbiego/ESP32_BLE_OTA_Arduino/tree/main/esp32_ble_ota) - 1,008,199 bytes
8 |
9 | [`esp32_nim_ble_ota`](https://github.com/fbiego/ESP32_BLE_OTA_Arduino/tree/main/esp32_nim_ble_ota) - 563,051 bytes
10 |
11 |
12 | ## Android app
13 |
14 |
15 |
16 | ## Python Script
17 | Update from your computer
18 |
19 | [`BLE_OTA_Python`](https://github.com/fbiego/BLE_OTA_Python)
20 |
21 | ## Video
22 | [`DIY ESP32 clock with BLE OTA`](https://youtu.be/TU_O4UPm00A) - 12kb/s peak (optimized)
23 |
24 | [`ESP32 OTA via BLE`](https://youtu.be/j4ELTS7QXFM) - 3kb/s (old)
25 |
--------------------------------------------------------------------------------
/esp32_ble_ota/esp32_ble_ota.ino:
--------------------------------------------------------------------------------
1 | /*
2 | MIT License
3 |
4 | Copyright (c) 2021 Felix Biego
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 | */
24 |
25 |
26 | #include
27 | #include "FS.h"
28 | #include "FFat.h"
29 | #include "SPIFFS.h"
30 | #include
31 | #include
32 | #include
33 | #include
34 |
35 | #define BUILTINLED 2
36 | #define FORMAT_SPIFFS_IF_FAILED true
37 | #define FORMAT_FFAT_IF_FAILED true
38 |
39 | #define USE_SPIFFS //comment to use FFat
40 |
41 | #ifdef USE_SPIFFS
42 | #define FLASH SPIFFS
43 | #define FASTMODE false //SPIFFS write is slow
44 | #else
45 | #define FLASH FFat
46 | #define FASTMODE true //FFat is faster
47 | #endif
48 |
49 | #define NORMAL_MODE 0 // normal
50 | #define UPDATE_MODE 1 // receiving firmware
51 | #define OTA_MODE 2 // installing firmware
52 |
53 | uint8_t updater[16384];
54 | uint8_t updater2[16384];
55 |
56 | #define SERVICE_UUID "fb1e4001-54ae-4a28-9f74-dfccb248601d"
57 | #define CHARACTERISTIC_UUID_RX "fb1e4002-54ae-4a28-9f74-dfccb248601d"
58 | #define CHARACTERISTIC_UUID_TX "fb1e4003-54ae-4a28-9f74-dfccb248601d"
59 |
60 | static BLECharacteristic* pCharacteristicTX;
61 | static BLECharacteristic* pCharacteristicRX;
62 |
63 | static bool deviceConnected = false, sendMode = false, sendSize = true;
64 | static bool writeFile = false, request = false;
65 | static int writeLen = 0, writeLen2 = 0;
66 | static bool current = true;
67 | static int parts = 0, next = 0, cur = 0, MTU = 0;
68 | static int MODE = NORMAL_MODE;
69 | unsigned long rParts, tParts;
70 |
71 | static void rebootEspWithReason(String reason) {
72 | Serial.println(reason);
73 | delay(1000);
74 | ESP.restart();
75 | }
76 |
77 | class MyServerCallbacks: public BLEServerCallbacks {
78 | void onConnect(BLEServer* pServer) {
79 | deviceConnected = true;
80 |
81 | }
82 | void onDisconnect(BLEServer* pServer) {
83 | deviceConnected = false;
84 | }
85 | };
86 |
87 | class MyCallbacks: public BLECharacteristicCallbacks {
88 |
89 | // void onStatus(BLECharacteristic* pCharacteristic, Status s, uint32_t code) {
90 | // Serial.print("Status ");
91 | // Serial.print(s);
92 | // Serial.print(" on characteristic ");
93 | // Serial.print(pCharacteristic->getUUID().toString().c_str());
94 | // Serial.print(" with code ");
95 | // Serial.println(code);
96 | // }
97 |
98 | void onNotify(BLECharacteristic *pCharacteristic) {
99 | uint8_t* pData;
100 | std::string value = pCharacteristic->getValue();
101 | int len = value.length();
102 | pData = pCharacteristic->getData();
103 | if (pData != NULL) {
104 | // Serial.print("Notify callback for characteristic ");
105 | // Serial.print(pCharacteristic->getUUID().toString().c_str());
106 | // Serial.print(" of data length ");
107 | // Serial.println(len);
108 | Serial.print("TX ");
109 | for (int i = 0; i < len; i++) {
110 | Serial.printf("%02X ", pData[i]);
111 | }
112 | Serial.println();
113 | }
114 | }
115 |
116 | void onWrite(BLECharacteristic *pCharacteristic) {
117 | uint8_t* pData;
118 | std::string value = pCharacteristic->getValue();
119 | int len = value.length();
120 | pData = pCharacteristic->getData();
121 | if (pData != NULL) {
122 | // Serial.print("Write callback for characteristic ");
123 | // Serial.print(pCharacteristic->getUUID().toString().c_str());
124 | // Serial.print(" of data length ");
125 | // Serial.println(len);
126 | // Serial.print("RX ");
127 | // for (int i = 0; i < len; i++) { // leave this commented
128 | // Serial.printf("%02X ", pData[i]);
129 | // }
130 | // Serial.println();
131 |
132 | if (pData[0] == 0xFB) {
133 | int pos = pData[1];
134 | for (int x = 0; x < len - 2; x++) {
135 | if (current) {
136 | updater[(pos * MTU) + x] = pData[x + 2];
137 | } else {
138 | updater2[(pos * MTU) + x] = pData[x + 2];
139 | }
140 | }
141 |
142 | } else if (pData[0] == 0xFC) {
143 | if (current) {
144 | writeLen = (pData[1] * 256) + pData[2];
145 | } else {
146 | writeLen2 = (pData[1] * 256) + pData[2];
147 | }
148 | current = !current;
149 | cur = (pData[3] * 256) + pData[4];
150 | writeFile = true;
151 | if (cur < parts - 1) {
152 | request = !FASTMODE;
153 | }
154 | } else if (pData[0] == 0xFD) {
155 | sendMode = true;
156 | if (FLASH.exists("/update.bin")) {
157 | FLASH.remove("/update.bin");
158 | }
159 | } else if (pData[0] == 0xFE) {
160 | rParts = 0;
161 | tParts = (pData[1] * 256 * 256 * 256) + (pData[2] * 256 * 256) + (pData[3] * 256) + pData[4];
162 |
163 | Serial.print("Available space: ");
164 | Serial.println(FLASH.totalBytes() - FLASH.usedBytes());
165 | Serial.print("File Size: ");
166 | Serial.println(tParts);
167 |
168 | } else if (pData[0] == 0xFF) {
169 | parts = (pData[1] * 256) + pData[2];
170 | MTU = (pData[3] * 256) + pData[4];
171 | MODE = UPDATE_MODE;
172 |
173 | } else if (pData[0] == 0xEF) {
174 | FLASH.format();
175 | sendSize = true;
176 | }
177 |
178 |
179 | }
180 |
181 | }
182 |
183 |
184 | };
185 |
186 | static void writeBinary(fs::FS &fs, const char * path, uint8_t *dat, int len) {
187 |
188 | //Serial.printf("Write binary file %s\r\n", path);
189 |
190 | File file = fs.open(path, FILE_APPEND);
191 |
192 | if (!file) {
193 | Serial.println("- failed to open file for writing");
194 | return;
195 | }
196 | file.write(dat, len);
197 | file.close();
198 | writeFile = false;
199 | rParts += len;
200 | }
201 |
202 | void sendOtaResult(String result) {
203 | pCharacteristicTX->setValue(result.c_str());
204 | pCharacteristicTX->notify();
205 | delay(200);
206 | }
207 |
208 |
209 | void performUpdate(Stream &updateSource, size_t updateSize) {
210 | char s1 = 0x0F;
211 | String result = String(s1);
212 | if (Update.begin(updateSize)) {
213 | size_t written = Update.writeStream(updateSource);
214 | if (written == updateSize) {
215 | Serial.println("Written : " + String(written) + " successfully");
216 | }
217 | else {
218 | Serial.println("Written only : " + String(written) + "/" + String(updateSize) + ". Retry?");
219 | }
220 | result += "Written : " + String(written) + "/" + String(updateSize) + " [" + String((written / updateSize) * 100) + "%] \n";
221 | if (Update.end()) {
222 | Serial.println("OTA done!");
223 | result += "OTA Done: ";
224 | if (Update.isFinished()) {
225 | Serial.println("Update successfully completed. Rebooting...");
226 | result += "Success!\n";
227 | }
228 | else {
229 | Serial.println("Update not finished? Something went wrong!");
230 | result += "Failed!\n";
231 | }
232 |
233 | }
234 | else {
235 | Serial.println("Error Occurred. Error #: " + String(Update.getError()));
236 | result += "Error #: " + String(Update.getError());
237 | }
238 | }
239 | else
240 | {
241 | Serial.println("Not enough space to begin OTA");
242 | result += "Not enough space for OTA";
243 | }
244 | if (deviceConnected) {
245 | sendOtaResult(result);
246 | delay(5000);
247 | }
248 | }
249 |
250 | void updateFromFS(fs::FS &fs) {
251 | File updateBin = fs.open("/update.bin");
252 | if (updateBin) {
253 | if (updateBin.isDirectory()) {
254 | Serial.println("Error, update.bin is not a file");
255 | updateBin.close();
256 | return;
257 | }
258 |
259 | size_t updateSize = updateBin.size();
260 |
261 | if (updateSize > 0) {
262 | Serial.println("Trying to start update");
263 | performUpdate(updateBin, updateSize);
264 | }
265 | else {
266 | Serial.println("Error, file is empty");
267 | }
268 |
269 | updateBin.close();
270 |
271 | // when finished remove the binary from spiffs to indicate end of the process
272 | Serial.println("Removing update file");
273 | fs.remove("/update.bin");
274 |
275 | rebootEspWithReason("Rebooting to complete OTA update");
276 | }
277 | else {
278 | Serial.println("Could not load update.bin from spiffs root");
279 | }
280 | }
281 |
282 | void initBLE() {
283 | BLEDevice::init("ESP32 OTA");
284 | BLEServer *pServer = BLEDevice::createServer();
285 | pServer->setCallbacks(new MyServerCallbacks());
286 |
287 | BLEService *pService = pServer->createService(SERVICE_UUID);
288 | pCharacteristicTX = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY );
289 | pCharacteristicRX = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_WRITE_NR);
290 | pCharacteristicRX->setCallbacks(new MyCallbacks());
291 | pCharacteristicTX->setCallbacks(new MyCallbacks());
292 | pCharacteristicTX->addDescriptor(new BLE2902());
293 | pCharacteristicTX->setNotifyProperty(true);
294 | pService->start();
295 |
296 |
297 | // BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility
298 | BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
299 | pAdvertising->addServiceUUID(SERVICE_UUID);
300 | pAdvertising->setScanResponse(true);
301 | pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
302 | pAdvertising->setMinPreferred(0x12);
303 | BLEDevice::startAdvertising();
304 | Serial.println("Characteristic defined! Now you can read it in your phone!");
305 | }
306 |
307 | void setup() {
308 | Serial.begin(115200);
309 | Serial.println("Starting BLE OTA sketch");
310 | pinMode(BUILTINLED, OUTPUT);
311 |
312 | #ifdef USE_SPIFFS
313 | if (!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)) {
314 | Serial.println("SPIFFS Mount Failed");
315 | return;
316 | }
317 | #else
318 | if (!FFat.begin()) {
319 | Serial.println("FFat Mount Failed");
320 | if (FORMAT_FFAT_IF_FAILED) FFat.format();
321 | return;
322 | }
323 | #endif
324 |
325 |
326 | initBLE();
327 |
328 | }
329 |
330 | void loop() {
331 |
332 | switch (MODE) {
333 |
334 | case NORMAL_MODE:
335 | if (deviceConnected) {
336 | digitalWrite(BUILTINLED, HIGH);
337 | if (sendMode) {
338 | uint8_t fMode[] = {0xAA, FASTMODE};
339 | pCharacteristicTX->setValue(fMode, 2);
340 | pCharacteristicTX->notify();
341 | delay(50);
342 | sendMode = false;
343 | }
344 |
345 | if (sendSize) {
346 | unsigned long x = FLASH.totalBytes();
347 | unsigned long y = FLASH.usedBytes();
348 | uint8_t fSize[] = {0xEF, (uint8_t) (x >> 16), (uint8_t) (x >> 8), (uint8_t) x, (uint8_t) (y >> 16), (uint8_t) (y >> 8), (uint8_t) y};
349 | pCharacteristicTX->setValue(fSize, 7);
350 | pCharacteristicTX->notify();
351 | delay(50);
352 | sendSize = false;
353 | }
354 |
355 | // your loop code here
356 | } else {
357 | digitalWrite(BUILTINLED, LOW);
358 | }
359 |
360 | // or here
361 |
362 | break;
363 |
364 | case UPDATE_MODE:
365 |
366 | if (request) {
367 | uint8_t rq[] = {0xF1, (cur + 1) / 256, (cur + 1) % 256};
368 | pCharacteristicTX->setValue(rq, 3);
369 | pCharacteristicTX->notify();
370 | delay(50);
371 | request = false;
372 | }
373 |
374 | if (cur + 1 == parts) { // received complete file
375 | uint8_t com[] = {0xF2, (cur + 1) / 256, (cur + 1) % 256};
376 | pCharacteristicTX->setValue(com, 3);
377 | pCharacteristicTX->notify();
378 | delay(50);
379 | MODE = OTA_MODE;
380 | }
381 |
382 | if (writeFile) {
383 | if (!current) {
384 | writeBinary(FLASH, "/update.bin", updater, writeLen);
385 | } else {
386 | writeBinary(FLASH, "/update.bin", updater2, writeLen2);
387 | }
388 | }
389 |
390 | break;
391 |
392 | case OTA_MODE:
393 |
394 | if (writeFile) {
395 | if (!current) {
396 | writeBinary(FLASH, "/update.bin", updater, writeLen);
397 | } else {
398 | writeBinary(FLASH, "/update.bin", updater2, writeLen2);
399 | }
400 | }
401 |
402 |
403 | if (rParts == tParts) {
404 | Serial.println("Complete");
405 | delay(5000);
406 | updateFromFS(FLASH);
407 | } else {
408 | writeFile = true;
409 | Serial.println("Incomplete");
410 | Serial.print("Expected: ");
411 | Serial.print(tParts);
412 | Serial.print("Received: ");
413 | Serial.println(rParts);
414 | delay(2000);
415 | }
416 | break;
417 |
418 | }
419 |
420 | }
421 |
--------------------------------------------------------------------------------
/esp32_ble_ota/esp32_ble_ota.ino.doitESP32devkitV1.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fbiego/ESP32_BLE_OTA_Arduino/905466b49eca9b7568bfba2b0ced75d3bb6d4bde/esp32_ble_ota/esp32_ble_ota.ino.doitESP32devkitV1.bin
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/.gitignore:
--------------------------------------------------------------------------------
1 | .pio
2 | .vscode/.browse.c_cpp.db*
3 | .vscode/c_cpp_properties.json
4 | .vscode/launch.json
5 | .vscode/ipch
6 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See http://go.microsoft.com/fwlink/?LinkId=827846
3 | // for the documentation about the extensions.json format
4 | "recommendations": [
5 | "platformio.platformio-ide"
6 | ],
7 | "unwantedRecommendations": [
8 | "ms-vscode.cpptools-extension-pack"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": [
3 | "LOGD",
4 | "LOGI",
5 | "LOGW",
6 | "ledc",
7 | "LEDC",
8 | ]
9 | }
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/README.md:
--------------------------------------------------------------------------------
1 | # Compact version of the BLE OTA example
2 |
3 | This directory contains a PlatformIO project which implement a compact version of the OTA. It also provides a library (in the lib folder) that allows to easily port BLE OTA to your own code.
4 |
5 | **Note:** this version of the code is not compatible with the mobile app for the moment (the update mechanism is not exactly the same).
6 |
7 | Furthermore, to use the FFAT mode you need to use another partition table (which you must configure in `platformio.ini`).
8 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/discover.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from bleak import BleakScanner
3 |
4 |
5 | async def run():
6 | for device in await BleakScanner.discover():
7 | print(f'{device.name} - {device.address}')
8 |
9 | if __name__ == '__main__':
10 | try:
11 | asyncio.run(run())
12 | except KeyboardInterrupt:
13 | pass
14 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/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 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/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 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/lib/ble_ota_dfu/keywords.txt:
--------------------------------------------------------------------------------
1 | # To be filled if you want to use it on Arduino
2 | #FUNCTIONS COLOR #D35400 - ORANGE KEYWORD1
3 | #FUNCTIONS COLOR #D35400 - ORANGE KEYWORD2
4 | #STRUCTURE COLOR #728E00 - GREEN KEYWORD3
5 | #VARIABLES COLOR #00979C - BLUE LITERAL1
6 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/lib/ble_ota_dfu/library.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ble_ota_dfuU",
3 | "keywords": "BLE OTA DFU",
4 | "description": "BLE OTA DFU",
5 | "repository":
6 | {
7 | "type": "email",
8 | "url": "vincent.stragier@outlook.com"
9 | },
10 | "version": "1.0.0",
11 | "exclude": "doc",
12 | "frameworks": "arduino",
13 | "platforms": ["esp32"]
14 | }
15 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/lib/ble_ota_dfu/library.properties:
--------------------------------------------------------------------------------
1 | name=ble_ota_dfu
2 | version=1.0.0
3 | author=Vincent STRAGIER
4 | maintainer=Vincent STRAGIER
5 | sentence=BLE OTA DFU
6 | paragraph=BLE OTA DFU
7 | category=Other
8 | url=mailto:vincent.stragier@outlook.com
9 | architectures=esp32
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/lib/ble_ota_dfu/src/ble_ota_dfu.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright 2022 Vincent Stragier */
2 | #include "ble_ota_dfu.hpp"
3 |
4 | QueueHandle_t start_update_queue;
5 | QueueHandle_t update_uploading_queue;
6 |
7 | void task_install_update(void *parameters) {
8 | FS file_system = FLASH;
9 | const char path[] = "/update.bin";
10 | uint8_t data = 0;
11 | BLE_OTA_DFU *OTA_DFU_BLE;
12 | OTA_DFU_BLE = reinterpret_cast(parameters);
13 | delay(100);
14 |
15 | // Wait for the upload to be completed
16 | bool start_update = false;
17 | xQueuePeek(start_update_queue, &start_update, portMAX_DELAY);
18 | while (!start_update) {
19 | delay(500);
20 | xQueuePeek(start_update_queue, &start_update, portMAX_DELAY);
21 | }
22 |
23 | ESP_LOGE(TAG, "Starting OTA update");
24 |
25 | // Open update.bin file.
26 | File update_binary = FLASH.open(path);
27 |
28 | // If the file cannot be loaded, return.
29 | if (!update_binary) {
30 | ESP_LOGE(TAG, "Could not load update.bin from spiffs root");
31 | vTaskDelete(NULL);
32 | }
33 |
34 | // Verify that the file is not a directory
35 | if (update_binary.isDirectory()) {
36 | ESP_LOGE(TAG, "Error, update.bin is not a file");
37 | update_binary.close();
38 | vTaskDelete(NULL);
39 | }
40 |
41 | // Get binary file size
42 | size_t update_size = update_binary.size();
43 |
44 | // Proceed to the update if the file is not empty
45 | if (update_size <= 0) {
46 | ESP_LOGE(TAG, "Error, update file is empty");
47 | update_binary.close();
48 | vTaskDelete(NULL);
49 | }
50 |
51 | ESP_LOGI(TAG, "Starting the update");
52 | // perform_update(update_binary, update_size);
53 | String result = (String) static_cast(0x0F);
54 |
55 | // Init update
56 | if (Update.begin(update_size)) {
57 | // Perform the update
58 | size_t written = Update.writeStream(update_binary);
59 |
60 | ESP_LOGI(TAG, "Written: %d/%d. %s", written, update_size,
61 | written == update_size ? "Success!" : "Retry?");
62 | result += "Written : " + String(written) + "/" + String(update_size) +
63 | " [" + String((written / update_size) * 100) + " %] \n";
64 |
65 | // Check update
66 | if (Update.end()) {
67 | ESP_LOGI(TAG, "OTA done!");
68 | result += "OTA Done: ";
69 |
70 | if (Update.isFinished()) {
71 | ESP_LOGI(TAG, "Update successfully completed. Rebooting...");
72 | } else {
73 | ESP_LOGE(TAG, "Update not finished? Something went wrong!");
74 | }
75 |
76 | result += Update.isFinished() ? "Success!\n" : "Failed!\n";
77 | } else {
78 | ESP_LOGE(TAG, "Error Occurred. Error #: %d", Update.getError());
79 | result += "Error #: " + String(Update.getError());
80 | }
81 | } else {
82 | ESP_LOGE(TAG, "Not enough space to begin BLE OTA DFU");
83 | result += "Not enough space to begin BLE OTA DFU";
84 | }
85 |
86 | update_binary.close();
87 |
88 | // When finished remove the binary from spiffs
89 | // to indicate the end of the process
90 | ESP_LOGI(TAG, "Removing update file");
91 | FLASH.remove(path);
92 |
93 | if (OTA_DFU_BLE->connected()) {
94 | // Return the result to the client (tells the client if the update was a
95 | // successfull or not)
96 | ESP_LOGI(TAG, "Sending result to client");
97 | OTA_DFU_BLE->send_OTA_DFU(result);
98 | // OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->setValue(result);
99 | // OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->notify();
100 | ESP_LOGE(TAG, "%s", result.c_str());
101 | ESP_LOGI(TAG, "Result sent to client");
102 | delay(5000);
103 | }
104 |
105 | ESP_LOGE(TAG, "Rebooting ESP32: complete OTA update");
106 | delay(5000);
107 | ESP.restart();
108 |
109 | // ESP_LOGI(TAG, "Installation is complete");
110 | vTaskDelete(NULL);
111 | }
112 |
113 | // void onStatus(BLECharacteristic* pCharacteristic, Status s, uint32_t
114 | // code) {
115 | // Serial.print("Status ");
116 | // Serial.print(s);
117 | // Serial.print(" on characteristic ");
118 | // Serial.print(pCharacteristic->getUUID().toString().c_str());
119 | // Serial.print(" with code ");
120 | // Serial.println(code);
121 | // }
122 |
123 | uint16_t BLEOverTheAirDeviceFirmwareUpdate::write_binary(fs::FS *file_system,
124 | const char *path,
125 | uint8_t *data,
126 | uint16_t length,
127 | bool keep_open) {
128 | static File file;
129 |
130 | // Append data to the file
131 |
132 | if (!file_open) {
133 | ESP_LOGI(TAG, "Opening binary file %s\r\n", path);
134 | file = file_system->open(path, FILE_WRITE);
135 | file_open = true;
136 | }
137 |
138 | if (!file) {
139 | ESP_LOGE(TAG, "Failed to open the file to write");
140 | return 0;
141 | }
142 |
143 | if (data != nullptr) {
144 | ESP_LOGI(TAG, "Write binary file %s\r\n", path);
145 | file.write(data, length);
146 | }
147 |
148 | if (keep_open) {
149 | return length;
150 | } else {
151 | file.close();
152 | file_open = false;
153 | return 0;
154 | }
155 | }
156 |
157 | void BLEOverTheAirDeviceFirmwareUpdate::onNotify(
158 | BLECharacteristic *pCharacteristic) {
159 | #ifdef DEBUG_BLE_OTA_DFU_TX
160 | // uint8_t *pData;
161 | std::string value = pCharacteristic->getValue();
162 | uint16_t len = value.length();
163 | // pData = pCharacteristic->getData();
164 | uint8_t *pData = (uint8_t *)value.data();
165 |
166 | if (pData != NULL) {
167 | ESP_LOGD(TAG, "Notify callback for characteristic %s of data length %d",
168 | pCharacteristic->getUUID().toString().c_str(), len);
169 |
170 | // Print transferred packets
171 | Serial.print("TX ");
172 | for (uint16_t i = 0; i < len; i++) {
173 | Serial.printf("%02X ", pData[i]);
174 | }
175 | Serial.println();
176 | }
177 | #endif
178 | }
179 |
180 | void BLEOverTheAirDeviceFirmwareUpdate::onWrite(
181 | BLECharacteristic *pCharacteristic) {
182 | // uint8_t *pData;
183 | std::string value = pCharacteristic->getValue();
184 | uint16_t len = value.length();
185 | // pData = pCharacteristic->getData();
186 | uint8_t *pData = (uint8_t *)value.data();
187 |
188 | // Check that data have been received
189 | if (pData != NULL) {
190 | // #define DEBUG_BLE_OTA_DFU_RX
191 | #ifdef DEBUG_BLE_OTA_DFU_RX
192 | ESP_LOGD(TAG, "Write callback for characteristic %s of data length %d",
193 | pCharacteristic->getUUID().toString().c_str(), len);
194 | Serial.print("RX ");
195 | for (uint16_t index = 0; index < len; index++) {
196 | Serial.printf("%02X ", pData[i]);
197 | }
198 | Serial.println();
199 | #endif
200 | switch (pData[0]) {
201 | // Send total and used sizes
202 | case 0xEF: {
203 | FLASH.format();
204 |
205 | // Send flash size
206 | uint16_t total_size = FLASH.totalBytes();
207 | uint16_t used_size = FLASH.usedBytes();
208 | uint8_t flash_size[] = {0xEF,
209 | static_cast(total_size >> 16),
210 | static_cast(total_size >> 8),
211 | static_cast(total_size),
212 | static_cast(used_size >> 16),
213 | static_cast(used_size >> 8),
214 | static_cast(used_size)};
215 | OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->setValue(flash_size, 7);
216 | OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->notify();
217 | delay(10);
218 | } break;
219 |
220 | // Write parts to RAM
221 | case 0xFB: {
222 | // pData[1] is the position of the next part
223 | for (uint16_t index = 0; index < len - 2; index++) {
224 | updater[!selected_updater][(pData[1] * MTU) + index] = pData[index + 2];
225 | }
226 | } break;
227 |
228 | // Write updater content to the flash
229 | case 0xFC: {
230 | selected_updater = !selected_updater;
231 | write_len[selected_updater] = (pData[1] * 256) + pData[2];
232 | current_progression = (pData[3] * 256) + pData[4];
233 |
234 | received_file_size +=
235 | write_binary(&FLASH, "/update.bin", updater[selected_updater],
236 | write_len[selected_updater]);
237 |
238 | if ((current_progression < parts - 1) && !FASTMODE) {
239 | uint8_t progression[] = {0xF1,
240 | (uint8_t)((current_progression + 1) / 256),
241 | (uint8_t)((current_progression + 1) % 256)};
242 | OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->setValue(progression, 3);
243 | OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->notify();
244 | delay(10);
245 | }
246 |
247 | ESP_LOGI(TAG, "Upload progress: %d/%d", current_progression + 1, parts);
248 | if (current_progression + 1 == parts) {
249 | // If all the file has been received, send the progression
250 | uint8_t progression[] = {0xF2,
251 | (uint8_t)((current_progression + 1) / 256),
252 | (uint8_t)((current_progression + 1) % 256)};
253 | OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->setValue(progression, 3);
254 | OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->notify();
255 | delay(10);
256 |
257 | if (received_file_size != expected_file_size) {
258 | // received_file_size += (pData[1] * 256) + pData[2];
259 | received_file_size +=
260 | write_binary(&FLASH, "/update.bin", updater[selected_updater],
261 | write_len[selected_updater]);
262 |
263 | if (received_file_size > expected_file_size) {
264 | ESP_LOGW(TAG, "Unexpected size:\n Expected: %d\nReceived: %d",
265 | expected_file_size, received_file_size);
266 | }
267 |
268 | } else {
269 | ESP_LOGI(TAG, "Installing update");
270 |
271 | // Start the installation
272 | write_binary(&FLASH, "/update.bin", nullptr, 0, false);
273 | bool start_update = true;
274 | xQueueOverwrite(start_update_queue, &start_update);
275 | }
276 | }
277 | } break;
278 |
279 | // Remove previous file and send transfer mode
280 | case 0xFD: {
281 | // Remove previous (failed?) update
282 | if (FLASH.exists("/update.bin")) {
283 | ESP_LOGI(TAG, "Removing previous update");
284 | FLASH.remove("/update.bin");
285 | }
286 |
287 | // Send mode ("fast" or "slow")
288 | uint8_t mode[] = {0xAA, FASTMODE};
289 | OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->setValue(mode, 2);
290 | OTA_DFU_BLE->pCharacteristic_BLE_OTA_DFU_TX->notify();
291 | delay(10);
292 | } break;
293 |
294 | // Keep track of the received file and of the expected file sizes
295 | case 0xFE:
296 | received_file_size = 0;
297 | expected_file_size = (pData[1] * 16777216) + (pData[2] * 65536) +
298 | (pData[3] * 256) + pData[4];
299 |
300 | ESP_LOGI(TAG, "Available space: %d\nFile Size: %d\n",
301 | FLASH.totalBytes() - FLASH.usedBytes(), expected_file_size);
302 | break;
303 |
304 | // Switch to update mode
305 | case 0xFF:
306 | parts = (pData[1] * 256) + pData[2];
307 | MTU = (pData[3] * 256) + pData[4];
308 | break;
309 |
310 | default:
311 | ESP_LOGW(TAG, "Unknown command: %02X", pData[0]);
312 | break;
313 | }
314 | // ESP_LOGE(TAG, "Not stuck in loop");
315 | }
316 | delay(1);
317 | }
318 |
319 | bool BLE_OTA_DFU::configure_OTA(NimBLEServer *pServer) {
320 | // Init FLASH
321 | #ifdef USE_SPIFFS
322 | if (!SPIFFS.begin(FORMAT_FLASH_IF_MOUNT_FAILED)) {
323 | ESP_LOGE(TAG, "SPIFFS Mount Failed");
324 | return false;
325 | }
326 | ESP_LOGI(TAG, "SPIFFS Mounted");
327 | #else
328 | if (!FFat.begin()) {
329 | ESP_LOGE(TAG, "FFat Mount Failed");
330 | if (FORMAT_FLASH_IF_MOUNT_FAILED)
331 | FFat.format();
332 | return false;
333 | }
334 | ESP_LOGI(TAG, "FFat Mounted");
335 | #endif
336 |
337 | // Get the pointer to the pServer
338 | this->pServer = pServer;
339 | // Create the BLE OTA DFU Service
340 | pServiceOTA = pServer->createService(SERVICE_OTA_BLE_UUID);
341 |
342 | if (pServiceOTA == nullptr) {
343 | return false;
344 | }
345 |
346 | BLECharacteristic *pCharacteristic_BLE_OTA_DFU_RX =
347 | pServiceOTA->createCharacteristic(CHARACTERISTIC_OTA_BL_UUID_RX,
348 | NIMBLE_PROPERTY::WRITE |
349 | NIMBLE_PROPERTY::WRITE_NR);
350 |
351 | if (pCharacteristic_BLE_OTA_DFU_RX == nullptr) {
352 | return false;
353 | }
354 |
355 | auto *bleOTACharacteristicCallbacksRX =
356 | new BLEOverTheAirDeviceFirmwareUpdate();
357 | bleOTACharacteristicCallbacksRX->OTA_DFU_BLE = this;
358 | pCharacteristic_BLE_OTA_DFU_RX->setCallbacks(bleOTACharacteristicCallbacksRX);
359 |
360 | pCharacteristic_BLE_OTA_DFU_TX = pServiceOTA->createCharacteristic(
361 | CHARACTERISTIC_OTA_BL_UUID_TX, NIMBLE_PROPERTY::NOTIFY);
362 |
363 | if (pCharacteristic_BLE_OTA_DFU_TX == nullptr) {
364 | return false;
365 | }
366 |
367 | // Start the BLE UART service
368 | pServiceOTA->start();
369 | return true;
370 | }
371 |
372 | void BLE_OTA_DFU::start_OTA() {
373 | bool state = false;
374 | initialize_queue(&start_update_queue, bool, &state, 1);
375 | initialize_queue(&update_uploading_queue, bool, &state, 1);
376 | // initialize_empty_queue(&update_queue, uint8_t, UPDATE_QUEUE_SIZE);
377 |
378 | ESP_LOGI(TAG, "Available heap memory: %d", ESP.getFreeHeap());
379 | // ESP_LOGE(TAG, "Available stack memory: %d",
380 |
381 | ESP_LOGI(TAG, "Available free space: %d",
382 | FLASH.totalBytes() - FLASH.usedBytes());
383 | // ESP_LOGE(TAG, "Available free blocks: %d", FLASH.blockCount());
384 | TaskHandle_t taskInstallUpdate = NULL;
385 |
386 | xTaskCreatePinnedToCoreAndAssert(task_install_update, "task_install_update",
387 | 5120, static_cast(this), 5,
388 | &taskInstallUpdate, 1);
389 |
390 | ESP_LOGI(TAG, "Available memory (after task started): %d", ESP.getFreeHeap());
391 | }
392 |
393 | bool BLE_OTA_DFU::begin(String local_name) {
394 | // Create the BLE Device
395 | BLEDevice::init(local_name.c_str());
396 |
397 | ESP_LOGI(TAG, "Starting BLE UART services");
398 |
399 | // Create the BLE Server
400 | pServer = BLEDevice::createServer();
401 |
402 | if (pServer == nullptr) {
403 | return false;
404 | }
405 |
406 | this->configure_OTA(pServer);
407 |
408 | // Start advertising
409 | pServer->getAdvertising()->addServiceUUID(pServiceOTA->getUUID());
410 | pServer->getAdvertising()->start();
411 |
412 | this->start_OTA();
413 | return true;
414 | }
415 |
416 | bool BLE_OTA_DFU::connected() {
417 | // True if connected
418 | return pServer->getConnectedCount() > 0;
419 | }
420 |
421 | void BLE_OTA_DFU::send_OTA_DFU(uint8_t value) {
422 | uint8_t _value = value;
423 | this->pCharacteristic_BLE_OTA_DFU_TX->setValue(&_value, 1);
424 | this->pCharacteristic_BLE_OTA_DFU_TX->notify();
425 | }
426 |
427 | void BLE_OTA_DFU::send_OTA_DFU(uint8_t *value, size_t size) {
428 | this->pCharacteristic_BLE_OTA_DFU_TX->setValue(value, size);
429 | this->pCharacteristic_BLE_OTA_DFU_TX->notify();
430 | }
431 |
432 | void BLE_OTA_DFU::send_OTA_DFU(String value) {
433 | this->pCharacteristic_BLE_OTA_DFU_TX->setValue(value.c_str());
434 | this->pCharacteristic_BLE_OTA_DFU_TX->notify();
435 | }
436 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/lib/ble_ota_dfu/src/ble_ota_dfu.hpp:
--------------------------------------------------------------------------------
1 | /* Copyright 2022 Vincent Stragier */
2 | // Highly inspired by https://github.com/fbiego/ESP32_BLE_OTA_Arduino
3 | #pragma once
4 | #ifndef SRC_BLE_OTA_DFU_HPP_
5 | #define SRC_BLE_OTA_DFU_HPP_
6 |
7 | #include "./freertos_utils.hpp"
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | // comment to use FFat
15 | #define USE_SPIFFS
16 |
17 | #ifdef USE_SPIFFS
18 | // SPIFFS write is slower
19 | #include
20 | #define FLASH SPIFFS
21 | const bool FASTMODE = false;
22 | #else
23 | // FFat is faster
24 | #include
25 | #define FLASH FFat
26 | const bool FASTMODE = true;
27 | #endif
28 | // #endif
29 |
30 | const char SERVICE_OTA_BLE_UUID[] = "fe590001-54ae-4a28-9f74-dfccb248601d";
31 | const char CHARACTERISTIC_OTA_BL_UUID_RX[] =
32 | "fe590002-54ae-4a28-9f74-dfccb248601d";
33 | const char CHARACTERISTIC_OTA_BL_UUID_TX[] =
34 | "fe590003-54ae-4a28-9f74-dfccb248601d";
35 |
36 | const bool FORMAT_FLASH_IF_MOUNT_FAILED = true;
37 | const uint32_t UPDATER_SIZE = 20000;
38 |
39 | /* Dummy class */
40 | class BLE_OTA_DFU;
41 |
42 | class BLEOverTheAirDeviceFirmwareUpdate : public BLECharacteristicCallbacks {
43 | private:
44 | bool selected_updater = true;
45 | bool file_open = false;
46 | uint8_t updater[2][UPDATER_SIZE];
47 | uint16_t write_len[2] = {0, 0};
48 | uint16_t parts = 0, MTU = 0;
49 | uint16_t current_progression = 0;
50 | uint32_t received_file_size, expected_file_size;
51 |
52 | public:
53 | friend class BLE_OTA_DFU;
54 | BLE_OTA_DFU *OTA_DFU_BLE;
55 |
56 | uint16_t write_binary(fs::FS *file_system, const char *path, uint8_t *data,
57 | uint16_t length, bool keep_open = true);
58 | void onNotify(BLECharacteristic *pCharacteristic);
59 | void onWrite(BLECharacteristic *pCharacteristic);
60 | };
61 |
62 | class BLE_OTA_DFU {
63 | private:
64 | BLEServer *pServer = nullptr;
65 | BLEService *pServiceOTA = nullptr;
66 | BLECharacteristic *pCharacteristic_BLE_OTA_DFU_TX = nullptr;
67 | friend class BLEOverTheAirDeviceFirmwareUpdate;
68 |
69 | public:
70 | BLE_OTA_DFU() = default;
71 | ~BLE_OTA_DFU() = default;
72 |
73 | bool configure_OTA(NimBLEServer *pServer);
74 | void start_OTA();
75 |
76 | bool begin(String local_name);
77 |
78 | bool connected();
79 |
80 | void send_OTA_DFU(uint8_t value);
81 | void send_OTA_DFU(uint8_t *value, size_t size);
82 | void send_OTA_DFU(String value);
83 |
84 | friend void task_install_update(void *parameters);
85 | };
86 |
87 | #endif /* SRC_BLE_OTA_DFU_HPP_ */
88 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/lib/ble_ota_dfu/src/freertos_utils.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright 2022 Vincent Stragier */
2 | #include "freertos_utils.hpp"
3 |
4 | BaseType_t xTaskCreatePinnedToCoreAndAssert(
5 | TaskFunction_t pvTaskCode, const char *pcName, uint32_t usStackDepth,
6 | void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pvCreatedTask,
7 | BaseType_t xCoreID) {
8 |
9 | BaseType_t xReturn =
10 | xTaskCreatePinnedToCore(pvTaskCode, pcName, usStackDepth, pvParameters,
11 | uxPriority, pvCreatedTask, xCoreID);
12 |
13 | configASSERT(pvCreatedTask);
14 |
15 | return xReturn;
16 | }
17 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/lib/ble_ota_dfu/src/freertos_utils.hpp:
--------------------------------------------------------------------------------
1 | /* Copyright 2022 Vincent Stragier */
2 | #pragma once
3 | #ifndef SRC_FREERTOS_UTILS_HPP_
4 | #define SRC_FREERTOS_UTILS_HPP_
5 |
6 | #include
7 |
8 | // Initialize the queue
9 | #define initialize_queue(queue, T, initial_value, size) \
10 | _initialize_queue((queue), (#queue), (size), (initial_value))
11 | #define initialize_empty_queue(queue, T, size) \
12 | _initialize_queue((queue), (#queue), (size))
13 |
14 | template
15 | void _initialize_queue(QueueHandle_t *queue, const char *queue_name,
16 | size_t size, T *initial_value = nullptr) {
17 | *queue = xQueueCreate(size, sizeof(T));
18 |
19 | ESP_LOGI(TAG, "Creating the queue \"%s\"", queue_name);
20 | // Init start update queue
21 | if (*queue == NULL || *queue == nullptr) {
22 | ESP_LOGE(TAG, "Error creating the queue \"%s\"", queue_name);
23 | }
24 |
25 | if (initial_value == nullptr || initial_value == NULL) {
26 | return;
27 | }
28 |
29 | if (!xQueueSend(*queue, initial_value, portMAX_DELAY)) {
30 | ESP_LOGE(TAG, "Error initializing the queue \"%s\"", queue_name);
31 | }
32 | }
33 |
34 | // Create a task and assert it's creation
35 | BaseType_t xTaskCreatePinnedToCoreAndAssert(
36 | TaskFunction_t pvTaskCode, const char *pcName, uint32_t usStackDepth,
37 | void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pvCreatedTask,
38 | BaseType_t xCoreID);
39 |
40 | #endif /* SRC_FREERTOS_UTILS_HPP_ */
41 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/ota_updater.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | import os
3 | import asyncio
4 | import sys
5 | import re
6 | from time import sleep
7 |
8 | from bleak import BleakClient, BleakScanner
9 | # from bleak.exc import BleakError
10 |
11 | header = """#####################################################################
12 | ------------------------BLE OTA update---------------------
13 | Arduino code @ https://github.com/fbiego/ESP32_BLE_OTA_Arduino
14 | #####################################################################"""
15 |
16 | UART_SERVICE_UUID = "fe590001-54ae-4a28-9f74-dfccb248601d"
17 | UART_RX_CHAR_UUID = "fe590002-54ae-4a28-9f74-dfccb248601d"
18 | UART_TX_CHAR_UUID = "fe590003-54ae-4a28-9f74-dfccb248601d"
19 |
20 | PART = 19000
21 | MTU = 250
22 |
23 | ble_ota_dfu_end = False
24 |
25 |
26 | async def start_ota(ble_address: str, filename: str):
27 | device = await BleakScanner.find_device_by_address(ble_address, timeout=20.0)
28 | disconnected_event = asyncio.Event()
29 | total = 0
30 | file_content = None
31 | client = None
32 |
33 | def handle_disconnect(_: BleakClient):
34 | print("Device disconnected !")
35 | disconnected_event.set()
36 | sleep(1)
37 | # sys.exit(0)
38 |
39 | async def handle_rx(_: int, data: bytearray):
40 | # print(f'\nReceived: {data = }\n')
41 | match data[0]:
42 | case 0xAA:
43 | print("Starting transfer, mode:", data[1])
44 | print_progress_bar(0, total, prefix='Upload progress:',
45 | suffix='Complete', length=50)
46 |
47 | match data[1]:
48 | case 0: # Slow mode
49 | # Send first part
50 | await send_part(0, file_content, client)
51 | case 1: # Fast mode
52 | for index in range(file_parts):
53 | await send_part(index, file_content, client)
54 | print_progress_bar(index + 1, total,
55 | prefix='Upload progress:',
56 | suffix='Complete', length=50)
57 |
58 | case 0xF1: # Send next part and update progress bar
59 | next_part_to_send = int.from_bytes(
60 | data[2:3], byteorder='little')
61 | # print("Next part:", next_part_to_send, "\n")
62 | await send_part(next_part_to_send, file_content, client)
63 | print_progress_bar(next_part_to_send + 1, total,
64 | prefix='Upload progress:',
65 | suffix='Complete', length=50)
66 |
67 | case 0xF2: # Install firmware
68 | # ins = 'Installing firmware'
69 | # print("Installing firmware")
70 | pass
71 |
72 | case 0x0F:
73 | print("OTA result: ", str(data[1:], 'utf-8'))
74 | global ble_ota_dfu_end
75 | ble_ota_dfu_end = True
76 |
77 | def print_progress_bar(iteration: int, total: int, prefix: str = '', suffix: str = '', decimals: int = 1, length: int = 100, filler: str = '█', print_end: str = "\r"):
78 | """
79 | Call in a loop to create terminal progress bar
80 | @params:
81 | iteration - Required : current iteration (Int)
82 | total - Required : total iterations (Int)
83 | prefix - Optional : prefix string (Str)
84 | suffix - Optional : suffix string (Str)
85 | decimals - Optional : positive number of decimals in percent complete (Int)
86 | length - Optional : character length of bar (Int)
87 | filler - Optional : bar fill character (Str)
88 | print_end - Optional : end character (e.g. "\r", "\r\n") (Str)
89 | """
90 | percent = ("{0:." + str(decimals) + "f}").format(100 *
91 | (iteration / float(total)))
92 | filled_length = (length * iteration) // total
93 | bar = filler * filled_length + '-' * (length - filled_length)
94 | print(f'\r{prefix} |{bar}| {percent} % {suffix}', end=print_end)
95 | # Print new line upon complete
96 | if iteration == total:
97 | print()
98 |
99 | async def send_part(position: int, data: bytearray, client: BleakClient):
100 | start = position * PART
101 | end = (position + 1) * PART
102 | # print(locals())
103 | if len(data) < end:
104 | end = len(data)
105 |
106 | data_length = end - start
107 | parts = data_length // MTU
108 | for part_index in range(parts):
109 | to_be_sent = bytearray([0xFB, part_index])
110 | for mtu_index in range(MTU):
111 | to_be_sent.append(
112 | data[(position*PART)+(MTU * part_index) + mtu_index])
113 | await send_data(client, to_be_sent)
114 |
115 | if data_length % MTU:
116 | remaining = data_length % MTU
117 | to_be_sent = bytearray([0xFB, parts])
118 | for index in range(remaining):
119 | to_be_sent.append(
120 | data[(position*PART)+(MTU * parts) + index])
121 | await send_data(client, to_be_sent)
122 |
123 | await send_data(client, bytearray([0xFC, data_length//256, data_length %
124 | 256, position//256, position % 256]), True)
125 |
126 | async def send_data(client: BleakClient, data: bytearray, response: bool = False):
127 | # print(f'{locals()["data"]}')
128 | await client.write_gatt_char(UART_RX_CHAR_UUID, data, response)
129 |
130 | if not device:
131 | print("-----------Failed--------------")
132 | print(f"Device with address {ble_address} could not be found.")
133 | return
134 | #raise BleakError(f"A device with address {ble_address} could not be found.")
135 |
136 | async with BleakClient(device, disconnected_callback=handle_disconnect) as local_client:
137 | client = local_client
138 |
139 | # Load file
140 | print("Reading from: ", filename)
141 | file_content = open(filename, "rb").read()
142 | file_parts = -(len(file_content) // -PART)
143 | total = file_parts
144 | file_length = len(file_content)
145 | print(f'File size: {len(file_content)}')
146 |
147 | # Set the UUID of the service you want to connect to and the callback
148 | await client.start_notify(UART_TX_CHAR_UUID, handle_rx)
149 | await asyncio.sleep(1.0)
150 |
151 | # Send file length
152 | await send_data(client, bytearray([0xFE,
153 | file_length >> 24 & 0xFF,
154 | file_length >> 16 & 0xFF,
155 | file_length >> 8 & 0xFF,
156 | file_length & 0xFF]))
157 |
158 | # Send number of parts and MTU value
159 | await send_data(client, bytearray([0xFF,
160 | file_parts//256,
161 | file_parts % 256,
162 | MTU // 256,
163 | MTU % 256]))
164 |
165 | # Remove previous update and receive transfer mode (start the update)
166 | await send_data(client, bytearray([0xFD]))
167 |
168 | # Wait til the update is complete
169 | while not ble_ota_dfu_end:
170 | await asyncio.sleep(1.0)
171 |
172 | print("Waiting for disconnect... ", end="")
173 |
174 | await disconnected_event.wait()
175 | print("-----------Complete--------------")
176 |
177 |
178 | def is_valid_address(value: str = None) -> bool:
179 | # Regex to check valid MAC address
180 | regex_0 = (r"^([0-9A-Fa-f]{2}[:-])"
181 | r"{5}([0-9A-Fa-f]{2})|"
182 | r"([0-9a-fA-F]{4}\\."
183 | r"[0-9a-fA-F]{4}\\."
184 | r"[0-9a-fA-F]{4}){17}$")
185 | regex_1 = (r"^[{]?[0-9a-fA-F]{8}"
186 | r"-([0-9a-fA-F]{4}-)"
187 | r"{3}[0-9a-fA-F]{12}[}]?$")
188 |
189 | # Compile the ReGex
190 | regex_0 = re.compile(regex_0)
191 | regex_1 = re.compile(regex_1)
192 |
193 | # If the string is empty return false
194 | if value is None:
195 | return False
196 |
197 | # Return if the string matched the ReGex
198 | if re.search(regex_0, value) and len(value) == 17:
199 | return True
200 |
201 | return re.search(regex_1, value) and len(value) == 36
202 |
203 |
204 | if __name__ == "__main__":
205 | print(header)
206 | # Check if the user has entered enough arguments
207 | # sys.argv.append("C8:C9:A3:D2:60:8E")
208 | # sys.argv.append("firmware.bin")
209 |
210 | if len(sys.argv) < 3:
211 | print("Specify the device address and firmware file")
212 | import sys
213 | import os
214 | filename = os.path.join(os.path.dirname(
215 | __file__), '.pio', 'build', 'esp32doit-devkit-v1', 'firmware.bin')
216 | filename = filename if os.path.exists(filename) else "firmware.bin"
217 | print(
218 | f"$ {sys.executable} \"{__file__}\" \"C8:C9:A3:D2:60:8E\" \"{filename}\"")
219 | exit(1)
220 |
221 | print("Trying to start OTA update")
222 | ble_address = sys.argv[1]
223 | filename = sys.argv[2]
224 |
225 | # Check if the address is valid
226 | if not is_valid_address(ble_address):
227 | print(f"Invalid Address: {ble_address}")
228 | exit(2)
229 |
230 | # Check if the file exists
231 | if not os.path.exists(filename):
232 | print(f"File not found: {filename}")
233 | exit(3)
234 |
235 | try:
236 | # Start the OTA update
237 | asyncio.run(start_ota(ble_address, filename))
238 | except KeyboardInterrupt:
239 | print("\nExiting...")
240 | exit(0)
241 | except OSError:
242 | print("\nExiting (OSError)...")
243 | exit(1)
244 | except Exception:
245 | import traceback
246 | traceback.print_exc()
247 | exit(2)
248 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/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:esp32doit-devkit-v1]
12 | platform = espressif32
13 | board = esp32dev
14 | framework = arduino
15 | ; ESP_LOGE - error (lowest) = 1
16 | ; ESP_LOGW - warning = 2
17 | ; ESP_LOGI - info = 3
18 | ; ESP_LOGD - debug = 4
19 | ; ESP_LOGV - verbose (highest) = 5
20 | ; build_flags = -DCORE_DEBUG_LEVEL=3
21 | ; build_type = debug
22 |
23 | ; board_build.partitions = default_ffat.csv
24 | board_build.partitions = default.csv
25 |
26 | ; Serial Monitor options
27 | ; https://docs.platformio.org/en/latest/core/userguide/device/cmd_monitor.html
28 | monitor_speed = 115200
29 | monitor_rts = 0
30 | monitor_dtr = 0
31 |
32 | monitor_filters =
33 | ; time
34 | ; send_on_enter
35 | esp32_exception_decoder
36 | ; hexlify
37 | ; colorize
38 |
39 | ; Libraries
40 | lib_deps =
41 | https://github.com/h2zero/NimBLE-Arduino.git
42 |
43 | ; Run before compilation
44 | ; extra_scripts =
45 | ; pre:some_script.py
46 |
47 | ; Configure checking tool
48 | ;, pvs-studio,
49 | check_tool = cppcheck, clangtidy
50 | check_flags =
51 | cppcheck: --addon=cert.py
52 | check_skip_packages = yes
53 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/src/main.cpp:
--------------------------------------------------------------------------------
1 | /* Copyright 2022 Vincent Stragier */
2 | #include "main.hpp"
3 |
4 | ///////////////////
5 | /// Setup ///
6 | ///////////////////
7 | void setup() { ota_dfu_ble.begin("Test OTA DFU"); }
8 |
9 | //////////////////
10 | /// Loop ///
11 | //////////////////
12 | void loop() {
13 | // Kill the holly loop()
14 | // Delete the task, comment if you want to keep the loop()
15 | vTaskDelete(NULL);
16 | }
17 |
18 | ///////////////////////
19 | /// Functions ///
20 | ///////////////////////
21 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/src/main.hpp:
--------------------------------------------------------------------------------
1 | /* Copyright 2022 Vincent Stragier */
2 |
3 | #ifndef SRC_MAIN_HPP_
4 | #define SRC_MAIN_HPP_
5 | ///////////////////////
6 | /// Libraries ///
7 | ///////////////////////
8 |
9 | #include
10 | #include
11 |
12 | ///////////////////////
13 | /// Variables ///
14 | ///////////////////////
15 | BLE_OTA_DFU ota_dfu_ble;
16 |
17 | #endif /* SRC_MAIN_HPP_ */
18 |
--------------------------------------------------------------------------------
/esp32_ble_ota_lib_compact/test/README:
--------------------------------------------------------------------------------
1 |
2 | This directory is intended for PlatformIO 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 PlatformIO Unit Testing:
11 | - https://docs.platformio.org/page/plus/unit-testing.html
12 |
--------------------------------------------------------------------------------
/esp32_nim_ble_ota/esp32_nim_ble_ota.ino:
--------------------------------------------------------------------------------
1 | /*
2 | MIT License
3 | Copyright (c) 2021 Felix Biego
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18 | SOFTWARE.
19 | */
20 |
21 | #include
22 | #include "FS.h"
23 | #include "FFat.h"
24 | #include "SPIFFS.h"
25 | #include
26 | //#include // For OTA with SD Card
27 |
28 | #define BUILTINLED 2
29 | #define FORMAT_SPIFFS_IF_FAILED true
30 | #define FORMAT_FFAT_IF_FAILED true
31 |
32 | #define USE_SPIFFS //comment to use FFat
33 |
34 | #ifdef USE_SPIFFS
35 | #define FLASH SPIFFS
36 | #define FASTMODE false //SPIFFS write is slow
37 | #else
38 | #define FLASH FFat
39 | #define FASTMODE true //FFat is faster
40 | #endif
41 |
42 | #define NORMAL_MODE 0 // normal
43 | #define UPDATE_MODE 1 // receiving firmware
44 | #define OTA_MODE 2 // installing firmware
45 |
46 | uint8_t updater[16384];
47 | uint8_t updater2[16384];
48 |
49 | #define SERVICE_UUID "fb1e4001-54ae-4a28-9f74-dfccb248601d"
50 | #define CHARACTERISTIC_UUID_RX "fb1e4002-54ae-4a28-9f74-dfccb248601d"
51 | #define CHARACTERISTIC_UUID_TX "fb1e4003-54ae-4a28-9f74-dfccb248601d"
52 |
53 | static BLECharacteristic* pCharacteristicTX;
54 | static BLECharacteristic* pCharacteristicRX;
55 |
56 | static bool deviceConnected = false, sendMode = false, sendSize = true;
57 | static bool writeFile = false, request = false;
58 | static int writeLen = 0, writeLen2 = 0;
59 | static bool current = true;
60 | static int parts = 0, next = 0, cur = 0, MTU = 0;
61 | static int MODE = NORMAL_MODE;
62 | unsigned long rParts, tParts;
63 |
64 | static void rebootEspWithReason(String reason) {
65 | Serial.println(reason);
66 | delay(1000);
67 | ESP.restart();
68 | }
69 |
70 | class MyServerCallbacks: public BLEServerCallbacks {
71 | void onConnect(BLEServer* pServer) {
72 | Serial.println("Connected");
73 | deviceConnected = true;
74 |
75 | }
76 | void onDisconnect(BLEServer* pServer) {
77 | Serial.println("disconnected");
78 | deviceConnected = false;
79 | }
80 | };
81 |
82 | class MyCallbacks: public BLECharacteristicCallbacks {
83 |
84 | // void onStatus(BLECharacteristic* pCharacteristic, Status s, uint32_t code) {
85 | // Serial.print("Status ");
86 | // Serial.print(s);
87 | // Serial.print(" on characteristic ");
88 | // Serial.print(pCharacteristic->getUUID().toString().c_str());
89 | // Serial.print(" with code ");
90 | // Serial.println(code);
91 | // }
92 |
93 | void onRead(BLECharacteristic* pCharacteristic) {
94 | Serial.print(pCharacteristic->getUUID().toString().c_str());
95 | Serial.print(": onRead(), value: ");
96 | Serial.println(pCharacteristic->getValue().c_str());
97 | };
98 |
99 | void onNotify(BLECharacteristic *pCharacteristic) {
100 | //uint8_t* pData;
101 | std::string pData = pCharacteristic->getValue();
102 | int len = pData.length();
103 | // Serial.print("Notify callback for characteristic ");
104 | // Serial.print(pCharacteristic->getUUID().toString().c_str());
105 | // Serial.print(" of data length ");
106 | // Serial.println(len);
107 | Serial.print("TX ");
108 | for (int i = 0; i < len; i++) {
109 | Serial.printf("%02X ", pData[i]);
110 | }
111 | Serial.println();
112 | }
113 |
114 | void onWrite(BLECharacteristic *pCharacteristic) {
115 | //uint8_t* pData;
116 | std::string pData = pCharacteristic->getValue();
117 | int len = pData.length();
118 | // Serial.print("Write callback for characteristic ");
119 | // Serial.print(pCharacteristic->getUUID().toString().c_str());
120 | // Serial.print(" of data length ");
121 | // Serial.println(len);
122 | // Serial.print("RX ");
123 | // for (int i = 0; i < len; i++) { // leave this commented
124 | // Serial.printf("%02X ", pData[i]);
125 | // }
126 | // Serial.println();
127 |
128 | if (pData[0] == 0xFB) {
129 | int pos = pData[1];
130 | for (int x = 0; x < len - 2; x++) {
131 | if (current) {
132 | updater[(pos * MTU) + x] = pData[x + 2];
133 | } else {
134 | updater2[(pos * MTU) + x] = pData[x + 2];
135 | }
136 | }
137 |
138 | } else if (pData[0] == 0xFC) {
139 | if (current) {
140 | writeLen = (pData[1] * 256) + pData[2];
141 | } else {
142 | writeLen2 = (pData[1] * 256) + pData[2];
143 | }
144 | current = !current;
145 | cur = (pData[3] * 256) + pData[4];
146 | writeFile = true;
147 | if (cur < parts - 1) {
148 | request = !FASTMODE;
149 | }
150 | } else if (pData[0] == 0xFD) {
151 | sendMode = true;
152 | if (FLASH.exists("/update.bin")) {
153 | FLASH.remove("/update.bin");
154 | }
155 | } else if (pData[0] == 0xFE) {
156 | rParts = 0;
157 | tParts = (pData[1] * 256 * 256 * 256) + (pData[2] * 256 * 256) + (pData[3] * 256) + pData[4];
158 |
159 | Serial.print("Available space: ");
160 | Serial.println(FLASH.totalBytes() - FLASH.usedBytes());
161 | Serial.print("File Size: ");
162 | Serial.println(tParts);
163 |
164 | } else if (pData[0] == 0xFF) {
165 | parts = (pData[1] * 256) + pData[2];
166 | MTU = (pData[3] * 256) + pData[4];
167 | MODE = UPDATE_MODE;
168 |
169 | } else if (pData[0] == 0xEF) {
170 | FLASH.format();
171 | sendSize = true;
172 | }
173 |
174 |
175 |
176 | }
177 |
178 |
179 | };
180 |
181 | static void writeBinary(fs::FS &fs, const char * path, uint8_t *dat, int len) {
182 |
183 | //Serial.printf("Write binary file %s\r\n", path);
184 |
185 | File file = fs.open(path, FILE_APPEND);
186 |
187 | if (!file) {
188 | Serial.println("- failed to open file for writing");
189 | return;
190 | }
191 | file.write(dat, len);
192 | file.close();
193 | writeFile = false;
194 | rParts += len;
195 | }
196 |
197 | void sendOtaResult(String result) {
198 | byte arr[result.length()];
199 | result.getBytes(arr, result.length());
200 | pCharacteristicTX->setValue(arr, result.length());
201 | pCharacteristicTX->notify();
202 | delay(200);
203 | }
204 |
205 |
206 | void performUpdate(Stream &updateSource, size_t updateSize) {
207 | char s1 = 0x0F;
208 | String result = String(s1);
209 | if (Update.begin(updateSize)) {
210 | size_t written = Update.writeStream(updateSource);
211 | if (written == updateSize) {
212 | Serial.println("Written : " + String(written) + " successfully");
213 | }
214 | else {
215 | Serial.println("Written only : " + String(written) + "/" + String(updateSize) + ". Retry?");
216 | }
217 | result += "Written : " + String(written) + "/" + String(updateSize) + " [" + String((written / updateSize) * 100) + "%] \n";
218 | if (Update.end()) {
219 | Serial.println("OTA done!");
220 | result += "OTA Done: ";
221 | if (Update.isFinished()) {
222 | Serial.println("Update successfully completed. Rebooting...");
223 | result += "Success!\n";
224 | }
225 | else {
226 | Serial.println("Update not finished? Something went wrong!");
227 | result += "Failed!\n";
228 | }
229 |
230 | }
231 | else {
232 | Serial.println("Error Occurred. Error #: " + String(Update.getError()));
233 | result += "Error #: " + String(Update.getError());
234 | }
235 | }
236 | else
237 | {
238 | Serial.println("Not enough space to begin OTA");
239 | result += "Not enough space for OTA";
240 | }
241 | if (deviceConnected) {
242 | sendOtaResult(result);
243 | delay(5000);
244 | }
245 | }
246 |
247 | void updateFromFS(fs::FS &fs) {
248 | File updateBin = fs.open("/update.bin");
249 | if (updateBin) {
250 | if (updateBin.isDirectory()) {
251 | Serial.println("Error, update.bin is not a file");
252 | updateBin.close();
253 | return;
254 | }
255 |
256 | size_t updateSize = updateBin.size();
257 |
258 | if (updateSize > 0) {
259 | Serial.println("Trying to start update");
260 | performUpdate(updateBin, updateSize);
261 | }
262 | else {
263 | Serial.println("Error, file is empty");
264 | }
265 |
266 | updateBin.close();
267 |
268 | // when finished remove the binary from spiffs to indicate end of the process
269 | Serial.println("Removing update file");
270 | fs.remove("/update.bin");
271 |
272 | rebootEspWithReason("Rebooting to complete OTA update");
273 | }
274 | else {
275 | Serial.println("Could not load update.bin from spiffs root");
276 | }
277 | }
278 |
279 | void initBLE() {
280 | BLEDevice::init("NimBLE OTA");
281 | BLEDevice::setMTU(517);
282 | BLEDevice::setPower(ESP_PWR_LVL_P9);
283 | BLEServer *pServer = BLEDevice::createServer();
284 | pServer->setCallbacks(new MyServerCallbacks());
285 |
286 | BLEService *pService = pServer->createService(SERVICE_UUID);
287 | pCharacteristicTX = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ );
288 | pCharacteristicRX = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE_NR);
289 | pCharacteristicRX->setCallbacks(new MyCallbacks());
290 | pCharacteristicTX->setCallbacks(new MyCallbacks());
291 | pService->start();
292 | pServer->start();
293 |
294 | // BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility
295 | NimBLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
296 | pAdvertising->addServiceUUID(SERVICE_UUID);
297 | pAdvertising->setScanResponse(true);
298 | pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
299 | pAdvertising->setMinPreferred(0x12);
300 | pAdvertising->start();
301 | //BLEDevice::startAdvertising();
302 | Serial.println("Characteristic defined! Now you can read it in your phone!");
303 | }
304 |
305 | void setup() {
306 | Serial.begin(115200);
307 | Serial.println("Starting BLE OTA sketch");
308 | pinMode(BUILTINLED, OUTPUT);
309 |
310 | //SPI.begin(18, 22, 23, 5); // For OTA with SD Card
311 | //SD.begin(5); // For OTA with SD Card
312 |
313 | #ifdef USE_SPIFFS
314 | if (!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)) {
315 | Serial.println("SPIFFS Mount Failed");
316 | return;
317 | }
318 | #else
319 | if (!FFat.begin()) {
320 | Serial.println("FFat Mount Failed");
321 | if (FORMAT_FFAT_IF_FAILED) FFat.format();
322 | return;
323 | }
324 | #endif
325 |
326 |
327 | initBLE();
328 |
329 | }
330 |
331 | void loop() {
332 |
333 | switch (MODE) {
334 |
335 | case NORMAL_MODE:
336 | if (deviceConnected) {
337 | digitalWrite(BUILTINLED, HIGH);
338 | if (sendMode) {
339 | uint8_t fMode[] = {0xAA, FASTMODE};
340 | pCharacteristicTX->setValue(fMode, 2);
341 | pCharacteristicTX->notify();
342 | delay(50);
343 | sendMode = false;
344 | }
345 | if (sendSize) {
346 | unsigned long x = FLASH.totalBytes();
347 | unsigned long y = FLASH.usedBytes();
348 | uint8_t fSize[] = {0xEF, (uint8_t) (x >> 16), (uint8_t) (x >> 8), (uint8_t) x, (uint8_t) (y >> 16), (uint8_t) (y >> 8), (uint8_t) y};
349 | pCharacteristicTX->setValue(fSize, 7);
350 | pCharacteristicTX->notify();
351 | delay(50);
352 | sendSize = false;
353 | }
354 |
355 | // your loop code here
356 | } else {
357 | digitalWrite(BUILTINLED, LOW);
358 | }
359 |
360 | // or here
361 |
362 | break;
363 |
364 | case UPDATE_MODE:
365 |
366 | if (request) {
367 | uint8_t rq[] = {0xF1, (cur + 1) / 256, (cur + 1) % 256};
368 | pCharacteristicTX->setValue(rq, 3);
369 | pCharacteristicTX->notify();
370 | delay(50);
371 | request = false;
372 | }
373 |
374 | if (writeFile) {
375 | if (!current) {
376 | writeBinary(FLASH, "/update.bin", updater, writeLen);
377 | } else {
378 | writeBinary(FLASH, "/update.bin", updater2, writeLen2);
379 | }
380 | writeFile = false;
381 | }
382 |
383 | if (cur + 1 == parts) { // received complete file
384 | uint8_t com[] = {0xF2, (cur + 1) / 256, (cur + 1) % 256};
385 | pCharacteristicTX->setValue(com, 3);
386 | pCharacteristicTX->notify();
387 | delay(50);
388 | MODE = OTA_MODE;
389 | }
390 |
391 | break;
392 |
393 | case OTA_MODE:
394 | if (writeFile) {
395 | if (!current) {
396 | writeBinary(FLASH, "/update.bin", updater, writeLen);
397 | } else {
398 | writeBinary(FLASH, "/update.bin", updater2, writeLen2);
399 | }
400 | }
401 |
402 |
403 | if (rParts == tParts) {
404 | Serial.println("Complete");
405 | delay(5000);
406 | updateFromFS(FLASH);
407 | } else {
408 | writeFile = true;
409 | Serial.println("Incomplete");
410 | Serial.print("Expected: ");
411 | Serial.print(tParts);
412 | Serial.print("Received: ");
413 | Serial.println(rParts);
414 | delay(2000);
415 | }
416 | break;
417 |
418 | }
419 |
420 | }
421 |
--------------------------------------------------------------------------------
/esp32_nim_ble_ota/esp32_nim_ble_ota.ino.doitESP32devkitV1.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fbiego/ESP32_BLE_OTA_Arduino/905466b49eca9b7568bfba2b0ced75d3bb6d4bde/esp32_nim_ble_ota/esp32_nim_ble_ota.ino.doitESP32devkitV1.bin
--------------------------------------------------------------------------------