Skip to content

Commit 2d05c97

Browse files
committed
Documenting safeboot.py script
1 parent 69cace8 commit 2d05c97

File tree

2 files changed

+44
-297
lines changed

2 files changed

+44
-297
lines changed

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ Factory image generated: /Users/mat/Data/Workspace/me/MycilaSafeBoot/examples/Ap
202202

203203
the `factory.py` script generates a complete factory image named `firmware.factory.bin` with all this content.
204204

205+
It can be downloaded from [https://github.com/mathieucarbou/MycilaSafeBoot/releases](https://github.com/mathieucarbou/MycilaSafeBoot/releases).
206+
205207
Flash this factory image on an ESP32:
206208

207209
```bash
@@ -301,3 +303,26 @@ Disabling mDNS saves about 24 kbytes. Enable both [...]\_NO_DNS options in `plat
301303
| wemos_d1_uno32 ||||
302304
| wipy3 ||||
303305
| wt32-eth01 ||||
306+
307+
## How to automatically update a firmware from PlatformIO
308+
309+
First make sure you created an HTTP endpoint that can be called to restart the app in SafeBoot mode.
310+
See [How to reboot in SafeBoot mode from the app](#how-to-reboot-in-safeboot-mode-from-the-app).
311+
312+
Then add to your PlatformIO `platformio.ini` file:
313+
314+
```ini
315+
upload_protocol = espota
316+
upload_port = 192.168.125.99
317+
custom_safeboot_restart_path = /api/system/safeboot
318+
extra_scripts =
319+
tools/safeboot.py
320+
```
321+
322+
The `safeboot.py` script can be downloaded from teh release page: [https://github.com/mathieucarbou/MycilaSafeBoot/releases](https://github.com/mathieucarbou/MycilaSafeBoot/releases).
323+
324+
- `upload_protocol = espota` tells PlatformIO to use Arduono OTA to upload the firmware
325+
- `upload_port` is the IP address of the ESP32
326+
- `custom_safeboot_restart_path` is the path to call to restart the app in SafeBoot mode
327+
328+
Once done, just run a `pio run -t upload` or `pio run -t uploadfs` for example and you will see the app automatically restarting in SafeBoot mode, then upload will be achieved, then the ESP will be restarted with your new app.

docs/index.md

Lines changed: 19 additions & 297 deletions
Original file line numberDiff line numberDiff line change
@@ -202,262 +202,7 @@ Factory image generated: /Users/mat/Data/Workspace/me/MycilaSafeBoot/examples/Ap
202202

203203
the `factory.py` script generates a complete factory image named `firmware.factory.bin` with all this content.
204204

205-
Flash this factory image on an ESP32:
206-
207-
```bash
208-
esptool.py write_flash 0x0 .pio/build/esp32dev/firmware.factory.bin
209-
```
210-
211-
Restart the ESP.
212-
The app loads, shows a button to restart in SafeBoot mode.
213-
After clicking on it, the ESP will reboot into SafeBoot mode.
214-
From there, you can access the web page to flash a new firmware, even from another application.
215-
216-
## How to reboot in SafeBoot mode from the app
217-
218-
You can use [MycilaSystem](https://github.com/mathieucarbou/MycilaSystem):
219-
220-
```cpp
221-
#include <MycilaSystem.h>
222-
223-
espConnect.saveConfiguration(); // if you want to save ESPConnect settings for network
224-
Mycila::System::restartFactory("safeboot");
225-
```
226-
227-
or this custom code:
228-
229-
```cpp
230-
#include <esp_ota_ops.h>
231-
#include <esp_partition.h>
232-
233-
const esp_partition_t* partition = esp_partition_find_first(esp_partition_type_t::ESP_PARTITION_TYPE_APP, esp_partition_subtype_t::ESP_PARTITION_SUBTYPE_APP_FACTORY, partitionName);
234-
if (partition) {
235-
esp_ota_set_boot_partition(partition);
236-
esp_restart();
237-
return true;
238-
} else {
239-
ESP_LOGE("SafeBoot", "SafeBoot partition not found");
240-
return false;
241-
}
242-
```
243-
244-
## Configuration options to manage build size
245-
246-
Squezing everything into the SafeBoot partition (655360 bytes only) is a tight fit especially on ethernet enabled boards.
247-
248-
Disabling the logging capabilites saves about 12 kbytes in the final build. Just comment out `MYCILA_SAFEBOOT_LOGGING` in `platformio.ini`.
249-
250-
```ini
251-
; -D MYCILA_SAFEBOOT_LOGGING
252-
```
253-
254-
Disabling mDNS saves about 24 kbytes. Enable both [...]\_NO_DNS options in `platformio.ini` to reduce the build size:
255-
256-
```ini
257-
-D ESPCONNECT_NO_MDNS
258-
-D MYCILA_SAFEBOOT_NO_MDNS# MycilaSafeBoot
259-
260-
[![Latest Release](https://img.shields.io/github/release/mathieucarbou/MycilaSafeBoot.svg)](https://GitHub.com/mathieucarbou/MycilaSafeBoot/releases/)
261-
[![Download](https://img.shields.io/badge/Download-safeboot-green.svg)](https://github.com/mathieucarbou/MycilaSafeBoot/releases)
262-
263-
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
264-
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](code_of_conduct.md)
265-
266-
[![Build](https://github.com/mathieucarbou/MycilaSafeBoot/actions/workflows/build.yml/badge.svg)](https://github.com/mathieucarbou/MycilaSafeBoot/actions/workflows/build.yml)
267-
[![GitHub latest commit](https://badgen.net/github/last-commit/mathieucarbou/MycilaSafeBoot)](https://GitHub.com/mathieucarbou/MycilaSafeBoot/commit/)
268-
[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/mathieucarbou/MycilaSafeBoot)
269-
270-
MycilaSafeBoot is a Web OTA recovery partition for ESP32 / Arduino.
271-
272-
It allows to have only one application partition to use the maximum available flash size.
273-
274-
The idea is not new: [Tasmota also uses a SafeBoot partition](https://tasmota.github.io/docs/Safeboot/).
275-
276-
- [Overview](#overview)
277-
- [How it works](#how-it-works)
278-
- [How to integrate the SafeBoot in your project](#how-to-integrate-the-safeboot-in-your-project)
279-
- [How to build the SafeBoot firmware image](#how-to-build-the-safeboot-firmware-image)
280-
- [SafeBoot Example](#safeboot-example)
281-
- [How to reboot in SafeBoot mode from the app](#how-to-reboot-in-safeboot-mode-from-the-app)
282-
- [Configuration options to manage build size](#configuration-options-to-manage-build-size)
283-
- [Options matrix](#options-matrix)
284-
- [Default board options](#default-board-options)
285-
286-
[![](https://mathieu.carbou.me/MycilaSafeBoot/safeboot-ssid.jpeg)](https://mathieu.carbou.me/MycilaSafeBoot/safeboot-ssid.jpeg)
287-
288-
## Overview
289-
290-
Usually, a normal partition table when supporting OTA updates on a 4MB ESP32 looks like this:
291-
292-
```
293-
# Name, Type, SubType, Offset, Size, Flags
294-
nvs, data, nvs, 0x9000, 0x5000,
295-
otadata, data, ota, 0xE000, 0x2000,
296-
app0, app, ota_0, 0x10000, 0x1F0000,
297-
app1, app, ota_1, 0x200000, 0x1F0000,
298-
spiffs, data, spiffs, 0x3F0000, 0x10000,
299-
```
300-
301-
which can also be written as:
302-
303-
```
304-
# Name ,Type ,SubType ,Offset ,Size ,Flags
305-
nvs ,data ,nvs ,36K ,20K ,
306-
otadata ,data ,ota ,56K ,8K ,
307-
app0 ,app ,ota_0 ,64K ,1984K ,
308-
app1 ,app ,ota_1 ,2048K ,1984K ,
309-
spiffs ,data ,spiffs ,4032K ,64K ,
310-
```
311-
312-
Because of the need to have 2 partitions with the same size, the firmware is then limited to only 2MB in this case when the ESP has 4MB flash.
313-
2MB is left unused (the OTA process will switch to the updated partition once completed).
314-
315-
**A SafeBoot partition is a small bootable recovery partition allowing you to flash the firmware.**
316-
Consequently, the firmware can take all the remaining space on the flash.
317-
318-
**The SafeBoot partition is 655360 bytes only.**
319-
320-
**Example for 4MB partition** with a SafeBoot partition and an application size of 3MB:
321-
322-
```
323-
# Name, Type, SubType, Offset, Size, Flags
324-
nvs, data, nvs, 0x9000, 0x5000,
325-
otadata, data, ota, 0xE000, 0x2000,
326-
safeboot, app, factory, 0x10000, 0xA0000,
327-
app, app, ota_0, 0xB0000, 0x330000,
328-
spiffs, data, spiffs, 0x3E0000, 0x10000,
329-
coredump, data, coredump, 0x3F0000, 0x10000,
330-
```
331-
332-
which can also be written as:
333-
334-
```
335-
# Name ,Type ,SubType ,Offset ,Size ,Flags
336-
nvs ,data ,nvs ,36K ,20K ,
337-
otadata ,data ,ota ,56K ,8K ,
338-
safeboot ,app ,factory ,64K ,640K ,
339-
app ,app ,ota_0 ,704K ,3264K ,
340-
spiffs ,data ,spiffs ,3968K ,64K ,
341-
coredump ,data ,coredump ,4032K ,64K ,
342-
```
343-
344-
**Example for 8Mb partition** with a SafeBoot partition and an application size of 7MB:
345-
346-
```
347-
# Name, Type, SubType, Offset, Size, Flags
348-
nvs, data, nvs, 0x9000, 0x5000,
349-
otadata, data, ota, 0xE000, 0x2000,
350-
safeboot, app, factory, 0x10000, 0xA0000,
351-
app, app, ota_0, 0xB0000, 0x730000,
352-
spiffs data, spiffs, 0x7E0000, 0x10000,
353-
coredump, data, coredump, 0x7F0000, 0x10000,
354-
```
355-
356-
which can also be written as:
357-
358-
```
359-
# Name ,Type ,SubType ,Offset ,Size ,Flags
360-
nvs ,data ,nvs ,36K ,20K ,
361-
otadata ,data ,ota ,56K ,8K ,
362-
safeboot ,app ,factory ,64K ,640K ,
363-
app ,app ,ota_0 ,704K ,7312K ,
364-
spiffs ,data ,spiffs ,8128K ,64K ,
365-
coredump ,data ,coredump ,8192K ,64K ,
366-
```
367-
368-
The SafeBoot partition is also automatically booted when the firmware is missing.
369-
370-
## How it works
371-
372-
1. When a user wants to update the app firmware, we have to tell the app to reboot in recovery mode.
373-
374-
2. Once booted in recovery mode, an Access Point is created with the SSID `SafeBoot`.
375-
376-
[![](https://mathieu.carbou.me/MycilaSafeBoot/safeboot-ssid.jpeg)](https://mathieu.carbou.me/MycilaSafeBoot/safeboot-ssid.jpeg)
377-
378-
3. Connect to the Access Point.
379-
380-
4. Now, you can flash the new firmware, either with `ArduinoOTA` or from the web page by going to `http://192.168.4.1`
381-
382-
5. After the flash is successful, the ESP will reboot in the new firmware.
383-
384-
SafeBoot partition also supports [MycilaESPConnect](https://github.com/mathieucarbou/MycilaESPConnect), which means if your application saves some network settings (WiFi SSID, Ethernet or WiFi static IP, etc), they will be reused.
385-
386-
## How to integrate the SafeBoot in your project
387-
388-
In the PIO file, some settings are added to specify the partition table and the SafeBoot location and the script to generate the factory image.
389-
390-
```ini
391-
extra_scripts = post:factory.py
392-
board_build.partitions = partitions-4MB-safeboot.csv
393-
board_build.app_partition_name = app
394-
custom_safeboot_url = https://github.com/mathieucarbou/MycilaSafeBoot/releases/download/latest/safeboot-esp32dev.bin
395-
```
396-
397-
It is also possible to point to a folder if you download the SafeBoot project locally:
398-
399-
```ini
400-
custom_safeboot_dir = ../../tools/SafeBoot
401-
```
402-
403-
It is also possible to point to a pre-downloaded safeoot image:
404-
405-
```ini
406-
custom_safeboot_file = safeboot.bin
407-
```
408-
409-
You can find in the [Project Releases](https://github.com/mathieucarbou/MycilaSafeBoot/releases) the list of available SafeBoot images, with the Python script to add to your build.
410-
411-
## How to build the SafeBoot firmware image
412-
413-
Go inside `tools/SafeBoot` and run:
414-
415-
```bash
416-
> pio run -e esp32dev
417-
```
418-
419-
If your board does not exist, you can specify it like this:
420-
421-
```bash
422-
> SAFEBOOT_BOARD=my-board pio run -e safeboot
423-
```
424-
425-
`SAFEBOOT_BOARD` is the environment variable to specify the board to build the SafeBoot firmware for.
426-
427-
At the end you should see these lines:
428-
429-
```
430-
Firmware size valid: 619744 <= 655360
431-
SafeBoot firmware created: /Users/mat/Data/Workspace/me/MycilaSafeBoot/.pio/build/dev/safeboot.bin
432-
```
433-
434-
## SafeBoot Example
435-
436-
Go inside `examples/App` and execute:
437-
438-
```bash
439-
> pio run
440-
```
441-
442-
You should see at the end of the build something like:
443-
444-
```
445-
Generating factory image for serial flashing
446-
Downloading SafeBoot image from https://github.com/mathieucarbou/MycilaSafeBoot/releases/download/latest/safeboot-esp32dev.bin
447-
Offset | File
448-
- 0x1000 | /Users/mat/Data/Workspace/me/MycilaSafeBoot/examples/App/.pio/build/esp32dev/bootloader.bin
449-
- 0x8000 | /Users/mat/Data/Workspace/me/MycilaSafeBoot/examples/App/.pio/build/esp32dev/partitions.bin
450-
- 0xe000 | /Users/mat/.platformio/packages/framework-arduinoespressif32@src-17df1753722b7b9e1913598420d4e038/tools/partitions/boot_app0.bin
451-
- 0x10000 | /Users/mat/Data/Workspace/me/MycilaSafeBoot/examples/App/.pio/build/esp32dev/safeboot.bin
452-
- 0xb0000 | /Users/mat/Data/Workspace/me/MycilaSafeBoot/examples/App/.pio/build/esp32dev/firmware.bin
453-
454-
[...]
455-
456-
Wrote 0x1451a0 bytes to file /Users/mat/Data/Workspace/me/MycilaSafeBoot/examples/App/.pio/build/esp32dev/firmware.factory.bin, ready to flash to offset 0x0
457-
Factory image generated: /Users/mat/Data/Workspace/me/MycilaSafeBoot/examples/App/.pio/build/esp32dev/firmware.factory.bin
458-
```
459-
460-
the `factory.py` script generates a complete factory image named `firmware.factory.bin` with all this content.
205+
It can be downloaded from [https://github.com/mathieucarbou/MycilaSafeBoot/releases](https://github.com/mathieucarbou/MycilaSafeBoot/releases).
461206

462207
Flash this factory image on an ESP32:
463208

@@ -559,48 +304,25 @@ Disabling mDNS saves about 24 kbytes. Enable both [...]\_NO_DNS options in `plat
559304
| wipy3 ||||
560305
| wt32-eth01 ||||
561306

562-
```
307+
## How to automatically update a firmware from PlatformIO
563308

564-
### Options matrix
309+
First make sure you created an HTTP endpoint that can be called to restart the app in SafeBoot mode.
310+
See [How to reboot in SafeBoot mode from the app](#how-to-reboot-in-safeboot-mode-from-the-app).
565311

566-
| Board | mDNS: on, logger: on | mDNS: on, logger: off | mDNS: off, logger: off |
567-
| -------------------- | -------------------- | --------------------- | ---------------------- |
568-
| denky_d4 | NOT SUPPORTED | OK | OK |
569-
| esp32-c3-devkitc-02 | OK | OK | OK |
570-
| esp32-c6-devkitc-1 | NOT SUPPORTED | NOT SUPPORTED | OK |
571-
| esp32-gateway | NOT SUPPORTED | OK | OK |
572-
| esp32-poe | NOT SUPPORTED | NOT SUPPORTED | OK |
573-
| esp32-poe-iso | NOT SUPPORTED | NOT SUPPORTED | OK |
574-
| esp32-s2-saola-1 | OK | OK | OK |
575-
| esp32-s3-devkitc-1 | OK | OK | OK |
576-
| esp32-solo1 | OK | OK | OK |
577-
| esp32dev | OK | OK | OK |
578-
| esp32s3box | OK | OK | OK |
579-
| lilygo-t-eth-lite-s3 | OK | OK | OK |
580-
| lolin_s2_mini | OK | OK | OK |
581-
| tinypico | NOT SUPPORTED | OK | OK |
582-
| wemos_d1_uno32 | OK | OK | OK |
583-
| wipy3 | NOT SUPPORTED | OK | OK |
584-
| wt32-eth01 | NOT SUPPORTED | NOT SUPPORTED | OK |
312+
Then add to your PlatformIO `platformio.ini` file:
585313

586-
## Default board options
314+
```ini
315+
upload_protocol = espota
316+
upload_port = 192.168.125.99
317+
custom_safeboot_restart_path = /api/system/safeboot
318+
extra_scripts =
319+
tools/safeboot.py
320+
```
321+
322+
The `safeboot.py` script can be downloaded from teh release page: [https://github.com/mathieucarbou/MycilaSafeBoot/releases](https://github.com/mathieucarbou/MycilaSafeBoot/releases).
323+
324+
- `upload_protocol = espota` tells PlatformIO to use Arduono OTA to upload the firmware
325+
- `upload_port` is the IP address of the ESP32
326+
- `custom_safeboot_restart_path` is the path to call to restart the app in SafeBoot mode
587327

588-
| Board | mDNS | Logging |
589-
| :------------------- | :--: | :-----: |
590-
| denky_d4 | ✅ | ❌ |
591-
| esp32-c3-devkitc-02 | ✅ | ✅ |
592-
| esp32-c6-devkitc-1 | ❌ | ❌ |
593-
| esp32-gateway | ✅ | ❌ |
594-
| esp32-poe | ❌ | ❌ |
595-
| esp32-poe-iso | ❌ | ❌ |
596-
| esp32-s2-saola-1 | ✅ | ✅ |
597-
| esp32-s3-devkitc-1 | ✅ | ✅ |
598-
| esp32-solo1 | ✅ | ✅ |
599-
| esp32dev | ✅ | ✅ |
600-
| esp32s3box | ✅ | ✅ |
601-
| lilygo-t-eth-lite-s3 | ✅ | ✅ |
602-
| lolin_s2_mini | ✅ | ✅ |
603-
| tinypico | ✅ | ❌ |
604-
| wemos_d1_uno32 | ✅ | ✅ |
605-
| wipy3 | ✅ | ❌ |
606-
| wt32-eth01 | ❌ | ❌ |
328+
Once done, just run a `pio run -t upload` or `pio run -t uploadfs` for example and you will see the app automatically restarting in SafeBoot mode, then upload will be achieved, then the ESP will be restarted with your new app.

0 commit comments

Comments
 (0)