1.3.0
Over-the-air updates
Flash a sketch over the network. The board appears in the Arduino IDE under
Tools → Port as a network port, and PlatformIO takes upload_protocol = espota. It speaks the espota protocol, so tools/espota.py works by hand too.
ArduinoOTA and Updater are ported from arduino-pico, keeping their LGPL
headers and attribution, and adapted only where the storage differs.
arduino-pico stages a new image on LittleFS and lets its bootloader apply it.
This part has no bootloader and no second slot — the sketch region is 912 KB of
a 960 KB part — so the image is held in RAM until it is complete and its MD5
checks out, and only then is it committed over the running sketch by a routine
that runs from ITCM because it erases the flash it is stored in.
Two consequences worth knowing before building on it:
- An over-the-air image is capped by the heap, not the flash — around
430 KB against the 912 KB a probe can write.ArduinoOTA.maxImageSize()
reports what fits right now, and an oversized upload is refused at the invite
rather than two thirds of the way through. - The commit is not power-fail safe. Between the first erase and the last
program the sketch region is neither image. Verifying the MD5 before erasing
keeps that window as small as it can be; it cannot close it.
The build now also produces firmware_ota.bin — the sketch half of the image,
without the V3F boot stub — and the Updater rejects a full firmware.bin sent
in its place, which is the mistake that would otherwise put the boot stub where
the sketch belongs.
Verified end to end on hardware, repeatedly: transfer, MD5, commit, reboot,
running the new firmware with the network back up.
Next-line branch prediction is off, and that is a bug fix
corecfgr bit 15 makes the V5F mispredict and resume execution at an address
it was never sent to. It presented as a wild pointer inside lwIP on every
boot, and moved or vanished with any unrelated change — a compiler flag, an
added string, an enabled assert.
What it actually did: execution ran off from the middle of memp_malloc() into
the middle of memp_free(), 0xF8 further on, between two byte-identical
instructions — so lwIP's pool index was computed twice and the second result
indexed hundreds of kilobytes past the table into unprogrammed flash.
Measured three ways on one unchanged build:
| I-cache | NLP | result |
|---|---|---|
| on | on | faults, deterministically |
| off | on | runs — NLP does nothing without the cache |
| on | off | runs, at full cached speed |
The middle row is why this looks like a cache bug and is not one. The cache
stays on; the predictor does not. docs/hazards.md has the full account.
Other fixes
- A crash guard that wedged the debug probe. When the V5F faulted
repeatedly, the V3F printed "reflash, or reset twice to try again" and then
spun infor (;;) {}— which wedges the WCH-Link, so the escape hatch needed
the probe it had locked out. Now__WFI(), and the probe attaches to a
fault-guarded board. ltoa/utoa/ultoa, which the Arduino API requires a core to supply
and newlib does not have.String(millis())had never linked.-mno-save-restore, so nothing in ITCM calls into flash.-msave-restore
put__riscv_save_4into the OTA committer's prologue, and GCC turned its
deliberate word-at-a-time copy back into amemcpy— two calls into the
flash being erased, neither of them in the source.tools/check_itcm.pynow
fails the build on any call out of ITCM.- Fault records now capture
ra,a0anda1. The handler isnaked, so
these are the faulting context's real values; they are what identified the
predictor bug. - lwIP asserts and corruption detectors behind
-DCH32H4_LWIP_ASSERT_CONSOLE, routed to the raw UART.
Since 1.2.0 also
mDNS with _arduino._tcp advertisement; multicast reception that works
(hash-table filter with the bit-reversed CRC the MAC actually wants); a single
ITCM flash driver instead of three copies of the register sequences; automatic
parking of the other core on any flash write; Keyboard, Mouse and
Joystick over USB HID; a CH32H4 library with a cross-core mutex;
LED_BUILTIN, LED1 and LED2; examples for every library that had none;
mbedTLS and lwIP as ordinary libraries, so board_build.tls and
board_build.network are gone.