ESP: rBoot
Inleiding
Bijna de handigste feature van de ESP8266 is het kunnen programmeren via WiFi.
Er zijn door de tijd heen verschillende manieren geweest om de ESP remote te flashen. Maar 1 van de meest betrouwbare manieren is wel via rBoot.
rBoot is een bootloader die het mogelijk maakt om verschillende ROM's te booten.
Voor je met rBoot aan de slag kan dien je wel wat basis te begrijpen...
2 Images
Het is niet mogelijk om de flash te beschrijven terwijl de huidige rom van deze locatie draait (applicatie stopt en ESP zal niet booten zonder dat deze opnieuw geflashed wordt). Daarom is het noodzakelijk de flash te partitioneren.
Door 2 of meer locaties te defineren kan de applicatie vanuit de ene locatie draaien terwijl je een andere locatie overschrijft. Door vervolgens de config van de bootloader aan te passen kan je rebooten naar deze andere locaties.
Flash layout
De ESP8266 komt grofweg in 2 smaken. Een 1MB en 4MB uitvoering:
- ESP-01: 1MB, 2x GPIO en TX/RX
- ESP-07: 1MB, 9x GPIO, TX/RX en ADC
- ESP-12: 4MB, 15x GPIO, TX/RX e ADC
Een 1MB versie heeft dus flash geheugen van 0x000000 t/m 0x0FFFFF. Een 4MB versie gaat van 0x000000 t/m 0x3FFFFF.
De ESP is in staat om maximaal 1 MB geheugen tegelijk aan te spreken. Het is wel mogelijk om de rom en spiffs in separate 1MB "pagina's" te zetten maar een rom of spiffs mag nooit over de scheiding van 2 pagina's vallen.
Voor rBoot op een 4MB chip is dit veruit de eenvoudigste inrichting:
0x000000 rboot.bin len: ~2300 bytes (0x920) 0x001000 config.bin len: 32 bytes (0x20) 0x002000 rom0.bin len: max 1M - 0x2010 (0xFDFF0) 0x100000 spiffs_rom1.bin len: max 1M - 0x10 (0xFFFEF) 0x202000 rom0.bin len: max 1M - 0x2010 (0xFDFF0) 0x300000 spiffs_rom2.bin len: max 1M - 0x10 (0xFFFEF)
Tijdens het compilen en linken worden in de rom bestanden verwijzingen opgenomen naar absolute geheugenlocaties. Dat is ook de reden waarom in bovenstaand voorbeeld beide rom0's starten op 0x?02000. Per blok van 1MB vallen de absolute verwijzingen naar geheugen dan op dezelfde posities.
Verder zie je in dit voorbeeld dat ik 2x een spiffs rom upload. Per image is het zo mogelijk om een aparte pagina van 1MB toe te wijzen als storage. Dit is geen eis, je kan ook vanuit beide images dezelfde SPIFFS image benaderen.
Makefile-user.mk
MODULES = app SERIAL_BAUD_RATE = 115200 ESP_HOME = /opt/esp-open-sdk SMING_HOME = /opt/Sming/Sming SPI_SIZE = 512K SPIFF_FILES = files SPIFF_SIZE = 524288 RBOOT_ENABLED ?= 1 RBOOT_BIG_FLASH ?= 1 RBOOT_TWO_ROMS ?=0 SPI_SIZE ?= 4M
Bij het flashen kan je het beste de bootconfig (0x1000 t/m 0x1020) leeg flashen (overschrijven met nullen) zodat deze automatisch wordt gegenereerd.
Verder dien je de rboot.bin, rom0.bin en eventueel de spiffs_rom.bin te flashen op resp. 0x00000, 0x02000 en 0x100000. Daarna kan je rom0.bin opnieuw flashen op 0x202000 maar dit kan je ook via OTA flashen.
Een automatisch gegenereerde bootconfig is vaak prima voor een 4MB flash chip met 2 images, maar voor bijzondere situaties zal deze niet volstaan. Zou je de bootconfig zelf willen schrijven dan kan dit door de volgende struct naar een file te schrijven:
typedef struct { uint8 magic; // our magic uint8 version; // config struct version uint8 mode; // boot loader mode uint8 current_rom; // currently selected rom uint8 gpio_rom; // rom to use for gpio boot uint8 count; // number of roms in use uint8 unused[2]; // padding uint32 roms[MAX_ROMS]; // flash addresses of the roms #ifdef BOOT_CONFIG_CHKSUM uint8 chksum; // boot config chksum #endif } rboot_config;
Dit kan je doen door bijvoorbeeld een nieuwe file te maken in een hex editor, of je laat eerst een config genereren (0x1000-0x1020 overschrijven met nullen en rboot starten) welke je met esptool.py download en edit.
In dit voorbeeld willen we op een 1MB flash chip 2 images en 1 spiffs wegschrijven:
0x1000.bin
0x00 E1 01 08 00 00 02 00 00 0x08 00 20 00 00 00 20 06 00 0x10 00 00 00 00 00 00 00 00
Op adres 0x0b t/m 0x08 staat het adres van rom0.bin (achtersevoren) dus: 0x00002000
Op adres 0x0f t/m 0x0c staat het adres van rom1.bin dus: 0x00062000
rom0 mag dus 0x62000 - 0x02000 = 0x60000 groot zijn (=384K) Als we dan rom1 net zo groot nemen zou spiffs op (0x62000 + 0x60000 =) 0xC2000 mogen starten. Voor spiffs is dan 247K over (0xFFFFF - 0xC2000)
In dit scenario kan niet dezelfde rom0.bin worden weggeschreven naar beide applicatie blokken. De absolute adressering is namelijk niet identiek binnen de 1MB pagina. Daarom moeten in de Makefile-user.mk andere opties worden meegegeven:
Makefile-user.mk
MODULES = app SERIAL_BAUD_RATE = 115200 ESP_HOME = /opt/esp-open-sdk SMING_HOME = /opt/Sming/Sming SPI_SIZE = 247K SPIFF_FILES = files SPIFF_SIZE = 252928 RBOOT_ENABLED ?= 1 RBOOT_BIG_FLASH ?= 0 RBOOT_TWO_ROMS ?= 1 SPI_SIZE ?= 1M RBOOT_ROM_0 ?= rom0 RBOOT_LD_0 ?= rom0.33.ld RBOOT_ROM_1 ?= rom1 RBOOT_LD_1 ?= rom1.33.ld
RBOOT_ROM_X is de naam van het bestand waarin de rom wordt weggeschreven. In dit geval rom0.bin en rom1.bin.
RBOOT_LD_X is de naam van het link bestand. Hierin dien je aan te geven per rom waar deze in de 1MB pagina wordt geplaatst.
rom0.33.ld
/* Linker Script for rboot */ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40202010, len = 0x4FF00 } INCLUDE "../ld/common.ld"
Met irom0_0_seg geven we aan dat de rom start op 0x02010 met een maximale lengte van 0x4FF00 (waarom niet op 0x2000??? TODO uitzoeken). Vervolgens maken we ook een rom1.33.ld en daarin zetten we:
/* Linker Script for rboot */ MEMORY { dport0_0_seg : org = 0x3FF00000, len = 0x10 dram0_0_seg : org = 0x3FFE8000, len = 0x14000 iram1_0_seg : org = 0x40100000, len = 0x8000 irom0_0_seg : org = 0x40262010, len = 0x4FF00 } INCLUDE "../ld/common.ld"
Tenslotte flashen we het geheel met:
./esptool.py --port /dev/ttyUSB0 write_flash -fs 8m \ 0x0000 firmware/rboot.bin \ 0x1000 0x1000.bin \ 0x02000 firmware/rom0.bin \ 0x62000 firmware/rom1.bin \ 0xc5000 firmware/spiff_rom.bin