MeshCore Web Flash - analiza działania (nRF52)
: sob mar 21, 2026 8:46 pm
Witajcie,
MeshCore Web Flasher (https://flasher.meshcore.co.uk/) sprowokował mnie do małego ‘dochodzenia’ w temacie programowania urządzeń opartych na kontrolerach nrf52x – popularnie używanych w companionach MeshCore (i MeshTastic). Poniższe eksperymenty przeprowadzałem na posiadanym przeze mnie Elecrow ThinkNode M1 opartym na mikrokontrolerze nrf52840 (s140). Część z zamieszczonych informacji może mieć również zastosowanie dla esp32, chociaż szczegóły (jak mapa pamięci) mogą być rożne nawet dla innych modeli z rodziny nrf52x. Do linii komend używam powershella z windows 11.
Poniższe wiadomości zamieszczam wyłącznie w celach edukacyjnych. Jeżeli popełniłem gdzieś błędu lub przeinaczenia - proszę o sprostowanie.
Jaki mam bootloader?
Na M1 można sprawdzić wersję bootloadera wprowadzając go w tak zwany tryb ‘uf2’. UF2 jest właściwie nazwą formatu obrazów oprogramowania zdefiniowanym przez Microsoft (https://github.com/microsoft/uf2). W skrócie działa to tak, że po podłączeniu do komputera bootloader kontrolera zgłasza się jako wirtualny dysk USB. Wystarczy wtedy przeciągnąć na niego oprogramowanie w formie uf2. Po zakończeniu ładowania bootloader rozpocznie programowanie.
Dygresja...
Dokładniej chodzi tu o tzw. bootloader drugiego stopnia (second stage bootloader). Pierwszy booloader (first stage bootloader) znajduje się w ROM (Read Only Memory). Wypalany jest na etapie produkcji układu, nie da rady go zmienić. Inicjalizuje sam układ.
Dla M1 (i wielu nrf52x) możecie uruchomić tryb uf2:
UF2 Bootloader 0.9.2-dirty lib/nrfx (v2.0.0) lib/tinyusb (0.12.0-145-g9775e7691) lib/uf2 (remotes/origin/configupdate-9-gadbb8c7)
Model: ELECROW nRF52840
Board-ID: ELECROW nRF52840-pca10056-v1
Date: Dec 12 2024
SoftDevice: S140 6.1.1
Znalazłem, że UF2 Bootloader 0.9.2-dirty to najprawdopodobniej wersja bootloadera Adafruit, stąd: https://github.com/adafruit/Adafruit_nR ... r/releases. ‘-dirty’ oznacza, że nie jest to oficjalna kompilacja, być może po pewnych zmianach. Stąd flashowanie oficjalnego bootloadera adafruit… może mieć nieprzewidywalne konsekwencje.
Dygresja...
Softdevice zawiera implementację stosów komunikacji bezprzewodowej - jak Bluetooth Low Energy - BLE (ale nie tylko). Softdevice pochodzi z sdk (software development kit) Nordica. Softdevice jest dostarczany przez Nordica a jego kod nie został otwarty.
Web flasher
Sam web flasher napisano w javascript. ‘Programująca’ część skryptu wykonuje się po stronie klienta (czyli Waszego komputera) z użyciem biblioteki do komunikacji po porcie szeregowym. Kod web flashera znajdziecie tutaj: https://github.com/meshcore-dev/flasher.meshcore.dev. Ciekawy jest config.json, który zawiera listę plików przeznaczonych do:
Przycisk: Enter DFU mode
DFU (device firmware update) oznacza programowania kontrolera przez komunikację po USB. Procedura wprowadzania urządzenia (a właściwie bootloadera) jest zazwyczaj różna dla różnych rodzin kontrolerów. Na przykład dla esp32 (jednym z dostępnych sposobów) jest spięcie pinu G0 do masy i wciśnięcie resetu. Ale można inaczej, bez konieczności ‘fizycznego’ kontaktu z kontrolerem. Web flasher używa tzw. „1200bps touch”. Procedura jest następująca:
Dygresja...
Zauważcie, że port COM zmienia się po resecie. Po prostu – teraz Wasz komputer nie rozmawia z portem szeregowym dla aplikacji – ale tym stworzonym przez bootloader do programowania. Zmieniają się identyfikatory urządzenia USB (PID/VID), zmienia się przeznaczenie.
Teraz kontroler jest gotowy do programowania.
Przycisk: Erase flash
Flash to ta pamięć stała, w której zapisywane są programy (RAM to pamięć, gdzie programy się wykonują). nrf52840 posiada wbudowane 1MB pamięci wewnętrznej flash. W tej pamięci zapisany jest bootloader, softdevice i aplikacja MeshCore. Przed właściwym programowaniem, żeby upewnić się, że po poprzedniej instalacji nie ma pozostałości, pamięć się zeruje.
W przypadku nrf52x wymazywanie flasha jest trochę bardziej skomplikowane. Normalnie wymagałoby to programatora podłączonego bezpośrednio do płyty głównej. Sam bootloader adafruit wydaje się nie posiadać takiej funkcji (dfu_transport_serial.c w bootloaderze Adafruit: brak wsparcia dla pakietu o typie 0x06 - page erase). Web flasher ładuje więc do pamięci specjalną aplikację. Aplikacja wymazuje flash – a potem z powrotem resetuje kontroler i wprowadza go w tryb programowania DFU. Ale… co właściwie jest wymazywane?
Jak rozumiem komentarz, kod aplikacji czyszczącej znajdziecie tu https://github.com/recrof/QSPIFlash-Formatter. Zakładam, że tą właśnie aplikację pobieracie klikając przycisk ‘Download’ i wybierając ‘ ThinkNode_M1_QSPIFlash_Format-v1.2*’. Warto jej się przyjrzeć, bo zawiera ciekawe wskazówki (przydadzą się na przyszłość):
Podsumowując: funkcja ‘erase’ tak naprawdę czyści obszary flasha (wewnętrznego i zewnętrznego) przeznaczone na systemy plików, do składowania danych.
Dygresja
Przedrostek ‘0x’ oznacza liczbę szesnastkową. Zamiast cyferek 0..9 jakie używanym na co dzień w systemie dziesiętnym, system szesnastkowy ma 0..9 oraz A..F. Czyli szestnastkowa 0x0f to dziesiętne 15. Stąd 0x1000 to 0*16^0 + 0*16^1 + 0*16^2 + 1*16^3 czyli 4096. A ponieważ 1 kilo-bajt to 1024 bity (w odróżnieniu do kilo-metra, który ma ‘tylko’ 1000 metrów), 4096 bitów to 4 kilo-bajty. Oczywiste, no nie?
Przycisk: Flash!
Nasz kontroler jest teraz w trybie programowania. Otwiera port szeregowy i czeka na pakiety. Teraz web flasher wysyła kolejne pakiety nowego oprogramowania. Bootloader odbiera je, składa i zapisuje w docelowym miejscu flasha. Po odebraniu wszystkich zresetuje się. Wystartuje wtedy w normalnym trybie uruchamiając nową aplikację.
Dygresja
Ładowanie aplikacji do wymazywania flasha działa dokładnie tak samo!
Web flasher używa wsadów spakowanych do zip. Możecie je pobrać je za pomocą przycisku 'Download'. Dla M1 będzie miał nazwę jak ThinkNode_M1_companion_radio_ble.zip. Jeżeli go rozpakujecie, w środku znajdziecie:
{"manifest": {
"application": {
"bin_file": "firmware.bin",
"dat_file": "firmware.dat",
"init_packet_data": {
"application_version": 4294967295,
"device_revision": 65535,
"device_type": 82,
"firmware_crc16": 14057,
"softdevice_req": [
182
]}},
"dfu_version": 0.5}}
Dygresja...
Mógłby tu również być ‘manifest/bootloader’ – do uzupełniania bootloadera, lub ‘manifest/softdevice’ do uzupełniania softdevice. Web flasher wydaje się programować tylko aplikację.
Web flasher czyta z manifestu nazwy plików .dat i .bin, które znajdują się w archiwum zip.
Najpierw web flasher wysyła pakiet startowy (0x03). Pakiet ten mówi bootloaderowi, że będzie programowana aplikacja (mode==0x04) i jaki będzie jej rozmiar. Następnie web flasher wysyła pakiet inicjalizacyjny (0x01), który zapisano w pliku firmware.dat. Cały ten plik to właściwie pakiet inicjujący programowanie kontrolera. Gdzieś znalazłem, że zakodowano go w formie protobuf. O definicję prototypu można podejrzewać plik: dfu-cc.proto (https://github.com/NordicSemiconductor/ ... u-cc.proto). Niestety wszelkie próby dekodowania przez protoc (również --decode_raw) kończyły się porażką. Być może czegoś tu nie rozumiem.
Spróbujmy zdekodować firmware.dat w powershell zmieniając jego postać z binarnej na hex (niektóre narzędzia mogą wymagać instalacji):
> certutil -encodehex -f .\firmware.dat firmware.hex
Stąd mamy (MeshCore 1.14.1 dla M1) plik firmware.hex:
52 00 ff ff ff ff ff ff 01 00 b6 00 e9 36
Plik ten pasuje do struktury dfu_init_packet_t z bootloadera (plik dfu_init.h) i zawiera po kolei:
Po wysłaniu pakietu inicjalizującego następuje transfer pliku firmware.bin za pomocą pakietów o typie 0x04. Ciekawą kwestią jest pytanie pod jakim adresem zapisywana jest aplikacja? Dla nrf52840 i SDK 6.1.1 będzie to 0x00026000 – zaraz po softdevice.
Dygresja
Dla sdk 7.0.0+ Softdevice jest większy i aplikacje zaczynają się od 0x00027 0000. Organizacja flasha to jednak temat na osobny post.
Transfer zamyka pakiet stop o typie 0x05. Bootloader kończy zapisywanie aplikacji i resetuje kontroler.
Na koniec...
Mam nadzieję, że tym postem udało mi się trochę ‘odczarować’ web flashera. Zapraszam do dyskusji.
Źródła:
https://github.com/microsoft/uf2
https://deadbadger.cz/blog/nordic-mbr-replacement
https://docs.nordicsemi.com/bundle/sdk_ ... der_memory
https://devzone.nordicsemi.com/f/nordic ... r-nrf51822
https://learn.sparkfun.com/tutorials/sp ... ng-further
https://docs.nordicsemi.com/bundle/sds_ ... usage.html
https://devzone.nordicsemi.com/guides/s ... ash-memory
https://cdn-learn.adafruit.com/download ... -sense.pdf
https://judepereira.com/blog/nrf52840-flash-s340/
https://forum.seeedstudio.com/t/cant-up ... 0/290812/5
https://forum.seeedstudio.com/t/cant-up ... /290812/26
https://arduino.github.io/arduino-cli/1 ... ification/
https://docs.nordicsemi.com/bundle/sdk_ ... r_dfu_init
https://devzone.nordicsemi.com/f/nordic ... ion-s-fwid
MeshCore Web Flasher (https://flasher.meshcore.co.uk/) sprowokował mnie do małego ‘dochodzenia’ w temacie programowania urządzeń opartych na kontrolerach nrf52x – popularnie używanych w companionach MeshCore (i MeshTastic). Poniższe eksperymenty przeprowadzałem na posiadanym przeze mnie Elecrow ThinkNode M1 opartym na mikrokontrolerze nrf52840 (s140). Część z zamieszczonych informacji może mieć również zastosowanie dla esp32, chociaż szczegóły (jak mapa pamięci) mogą być rożne nawet dla innych modeli z rodziny nrf52x. Do linii komend używam powershella z windows 11.
Poniższe wiadomości zamieszczam wyłącznie w celach edukacyjnych. Jeżeli popełniłem gdzieś błędu lub przeinaczenia - proszę o sprostowanie.
Jaki mam bootloader?
Na M1 można sprawdzić wersję bootloadera wprowadzając go w tak zwany tryb ‘uf2’. UF2 jest właściwie nazwą formatu obrazów oprogramowania zdefiniowanym przez Microsoft (https://github.com/microsoft/uf2). W skrócie działa to tak, że po podłączeniu do komputera bootloader kontrolera zgłasza się jako wirtualny dysk USB. Wystarczy wtedy przeciągnąć na niego oprogramowanie w formie uf2. Po zakończeniu ładowania bootloader rozpocznie programowanie.
Dygresja...
Dokładniej chodzi tu o tzw. bootloader drugiego stopnia (second stage bootloader). Pierwszy booloader (first stage bootloader) znajduje się w ROM (Read Only Memory). Wypalany jest na etapie produkcji układu, nie da rady go zmienić. Inicjalizuje sam układ.
Dla M1 (i wielu nrf52x) możecie uruchomić tryb uf2:
- Podłączcie M1 do komputera kablem USB,
- Szybko wciśnijcie reset dwa razy (potrzeba coś cienkiego, jak spinacz, otworek jest na spodzie obudowy koło portu USB)
- bootloader wchodzi w tryb uf2 i na Waszym komputerze pojawi się nowy napęd.
UF2 Bootloader 0.9.2-dirty lib/nrfx (v2.0.0) lib/tinyusb (0.12.0-145-g9775e7691) lib/uf2 (remotes/origin/configupdate-9-gadbb8c7)
Model: ELECROW nRF52840
Board-ID: ELECROW nRF52840-pca10056-v1
Date: Dec 12 2024
SoftDevice: S140 6.1.1
Znalazłem, że UF2 Bootloader 0.9.2-dirty to najprawdopodobniej wersja bootloadera Adafruit, stąd: https://github.com/adafruit/Adafruit_nR ... r/releases. ‘-dirty’ oznacza, że nie jest to oficjalna kompilacja, być może po pewnych zmianach. Stąd flashowanie oficjalnego bootloadera adafruit… może mieć nieprzewidywalne konsekwencje.
Dygresja...
Softdevice zawiera implementację stosów komunikacji bezprzewodowej - jak Bluetooth Low Energy - BLE (ale nie tylko). Softdevice pochodzi z sdk (software development kit) Nordica. Softdevice jest dostarczany przez Nordica a jego kod nie został otwarty.
Web flasher
Sam web flasher napisano w javascript. ‘Programująca’ część skryptu wykonuje się po stronie klienta (czyli Waszego komputera) z użyciem biblioteki do komunikacji po porcie szeregowym. Kod web flashera znajdziecie tutaj: https://github.com/meshcore-dev/flasher.meshcore.dev. Ciekawy jest config.json, który zawiera listę plików przeznaczonych do:
- wymazywania flasha kontrolera, dla m1: ThinkNode_M1_QSPIFlash_Format-v1.2.zip – pod przyciskiem ‘Erase flash’
- programowania przez DFU (plik spod file/flash - *.zip) – po wciśnięciu przycisku ‘Flash!’
- programowania przez uf2, plik o rozszerzeniu spod file/download – uf2.
Przycisk: Enter DFU mode
DFU (device firmware update) oznacza programowania kontrolera przez komunikację po USB. Procedura wprowadzania urządzenia (a właściwie bootloadera) jest zazwyczaj różna dla różnych rodzin kontrolerów. Na przykład dla esp32 (jednym z dostępnych sposobów) jest spięcie pinu G0 do masy i wciśnięcie resetu. Ale można inaczej, bez konieczności ‘fizycznego’ kontaktu z kontrolerem. Web flasher używa tzw. „1200bps touch”. Procedura jest następująca:
- komputer (skrypt flashera) łączy się z portem szeregowym wystawianym przez kontroler (USB CDC – wirtualny port szeregowy, widziany w komputerze jako COMx) z szybkością 1200 bps,
- skrypt oczekuje chwilę połączony, tu 100ms,
- skrypt zamka port.
Dygresja...
Zauważcie, że port COM zmienia się po resecie. Po prostu – teraz Wasz komputer nie rozmawia z portem szeregowym dla aplikacji – ale tym stworzonym przez bootloader do programowania. Zmieniają się identyfikatory urządzenia USB (PID/VID), zmienia się przeznaczenie.
Teraz kontroler jest gotowy do programowania.
Przycisk: Erase flash
Flash to ta pamięć stała, w której zapisywane są programy (RAM to pamięć, gdzie programy się wykonują). nrf52840 posiada wbudowane 1MB pamięci wewnętrznej flash. W tej pamięci zapisany jest bootloader, softdevice i aplikacja MeshCore. Przed właściwym programowaniem, żeby upewnić się, że po poprzedniej instalacji nie ma pozostałości, pamięć się zeruje.
W przypadku nrf52x wymazywanie flasha jest trochę bardziej skomplikowane. Normalnie wymagałoby to programatora podłączonego bezpośrednio do płyty głównej. Sam bootloader adafruit wydaje się nie posiadać takiej funkcji (dfu_transport_serial.c w bootloaderze Adafruit: brak wsparcia dla pakietu o typie 0x06 - page erase). Web flasher ładuje więc do pamięci specjalną aplikację. Aplikacja wymazuje flash – a potem z powrotem resetuje kontroler i wprowadza go w tryb programowania DFU. Ale… co właściwie jest wymazywane?
Jak rozumiem komentarz, kod aplikacji czyszczącej znajdziecie tu https://github.com/recrof/QSPIFlash-Formatter. Zakładam, że tą właśnie aplikację pobieracie klikając przycisk ‘Download’ i wybierając ‘ ThinkNode_M1_QSPIFlash_Format-v1.2*’. Warto jej się przyjrzeć, bo zawiera ciekawe wskazówki (przydadzą się na przyszłość):
- od 0xED000 czyszczony jest system plików LFS (little file system), rozmiar: 0x7000 (czyli 28kB) na dane użytkownika,
- od 0xD4000 czyszczony jest kolejny system plików LFS o rozmiarze 190000 bajtów czyli 18kB na dane aplikacji.
Podsumowując: funkcja ‘erase’ tak naprawdę czyści obszary flasha (wewnętrznego i zewnętrznego) przeznaczone na systemy plików, do składowania danych.
Dygresja
Przedrostek ‘0x’ oznacza liczbę szesnastkową. Zamiast cyferek 0..9 jakie używanym na co dzień w systemie dziesiętnym, system szesnastkowy ma 0..9 oraz A..F. Czyli szestnastkowa 0x0f to dziesiętne 15. Stąd 0x1000 to 0*16^0 + 0*16^1 + 0*16^2 + 1*16^3 czyli 4096. A ponieważ 1 kilo-bajt to 1024 bity (w odróżnieniu do kilo-metra, który ma ‘tylko’ 1000 metrów), 4096 bitów to 4 kilo-bajty. Oczywiste, no nie?
Przycisk: Flash!
Nasz kontroler jest teraz w trybie programowania. Otwiera port szeregowy i czeka na pakiety. Teraz web flasher wysyła kolejne pakiety nowego oprogramowania. Bootloader odbiera je, składa i zapisuje w docelowym miejscu flasha. Po odebraniu wszystkich zresetuje się. Wystartuje wtedy w normalnym trybie uruchamiając nową aplikację.
Dygresja
Ładowanie aplikacji do wymazywania flasha działa dokładnie tak samo!
Web flasher używa wsadów spakowanych do zip. Możecie je pobrać je za pomocą przycisku 'Download'. Dla M1 będzie miał nazwę jak ThinkNode_M1_companion_radio_ble.zip. Jeżeli go rozpakujecie, w środku znajdziecie:
- manifest.json
- firmware.dat
- firmware.bin
{"manifest": {
"application": {
"bin_file": "firmware.bin",
"dat_file": "firmware.dat",
"init_packet_data": {
"application_version": 4294967295,
"device_revision": 65535,
"device_type": 82,
"firmware_crc16": 14057,
"softdevice_req": [
182
]}},
"dfu_version": 0.5}}
Dygresja...
Mógłby tu również być ‘manifest/bootloader’ – do uzupełniania bootloadera, lub ‘manifest/softdevice’ do uzupełniania softdevice. Web flasher wydaje się programować tylko aplikację.
Web flasher czyta z manifestu nazwy plików .dat i .bin, które znajdują się w archiwum zip.
Najpierw web flasher wysyła pakiet startowy (0x03). Pakiet ten mówi bootloaderowi, że będzie programowana aplikacja (mode==0x04) i jaki będzie jej rozmiar. Następnie web flasher wysyła pakiet inicjalizacyjny (0x01), który zapisano w pliku firmware.dat. Cały ten plik to właściwie pakiet inicjujący programowanie kontrolera. Gdzieś znalazłem, że zakodowano go w formie protobuf. O definicję prototypu można podejrzewać plik: dfu-cc.proto (https://github.com/NordicSemiconductor/ ... u-cc.proto). Niestety wszelkie próby dekodowania przez protoc (również --decode_raw) kończyły się porażką. Być może czegoś tu nie rozumiem.
Spróbujmy zdekodować firmware.dat w powershell zmieniając jego postać z binarnej na hex (niektóre narzędzia mogą wymagać instalacji):
> certutil -encodehex -f .\firmware.dat firmware.hex
Stąd mamy (MeshCore 1.14.1 dla M1) plik firmware.hex:
52 00 ff ff ff ff ff ff 01 00 b6 00 e9 36
Plik ten pasuje do struktury dfu_init_packet_t z bootloadera (plik dfu_init.h) i zawiera po kolei:
- Typ urządzenia: 0x0052 (2 bajty), czyli decymalnie 82 – co jest w manifeście pod device_type,
- wersję urządzenia (0xFFFF, 2 bajty) i wersję aplikacji (0xFFFFFFFF, 4 bajty),
- Jedyną (0x0001) zgodną wersją softdevice z tym urządzeniem jest 0x00b6 (dec 182) – co również pokrywa się z manifestem.
- 2 ostatnie bajty (0x36e9, dec 14057) to suma kontrolna crc16 pliku firmware.bin kodu aplikacji.
Po wysłaniu pakietu inicjalizującego następuje transfer pliku firmware.bin za pomocą pakietów o typie 0x04. Ciekawą kwestią jest pytanie pod jakim adresem zapisywana jest aplikacja? Dla nrf52840 i SDK 6.1.1 będzie to 0x00026000 – zaraz po softdevice.
Dygresja
Dla sdk 7.0.0+ Softdevice jest większy i aplikacje zaczynają się od 0x00027 0000. Organizacja flasha to jednak temat na osobny post.
Transfer zamyka pakiet stop o typie 0x05. Bootloader kończy zapisywanie aplikacji i resetuje kontroler.
Na koniec...
Mam nadzieję, że tym postem udało mi się trochę ‘odczarować’ web flashera. Zapraszam do dyskusji.
Źródła:
https://github.com/microsoft/uf2
https://deadbadger.cz/blog/nordic-mbr-replacement
https://docs.nordicsemi.com/bundle/sdk_ ... der_memory
https://devzone.nordicsemi.com/f/nordic ... r-nrf51822
https://learn.sparkfun.com/tutorials/sp ... ng-further
https://docs.nordicsemi.com/bundle/sds_ ... usage.html
https://devzone.nordicsemi.com/guides/s ... ash-memory
https://cdn-learn.adafruit.com/download ... -sense.pdf
https://judepereira.com/blog/nrf52840-flash-s340/
https://forum.seeedstudio.com/t/cant-up ... 0/290812/5
https://forum.seeedstudio.com/t/cant-up ... /290812/26
https://arduino.github.io/arduino-cli/1 ... ification/
https://docs.nordicsemi.com/bundle/sdk_ ... r_dfu_init
https://devzone.nordicsemi.com/f/nordic ... ion-s-fwid