Engineering Standalone Tale • September 13, 2026 • Embedded Systems, ESP32-C5, Firmware, Wi-Fi, Reliability, Open Source • 10 min read

Cirvane: From a Dusty Board to a Resilient Runtime

How a neglected XIAO ESP32-C5, a Wi-Fi scanner and a BLE homing experiment led to a bounded, recovery-focused runtime for connected devices.

Cirvane architecture showing bounded services, supervised recovery, transactional configuration and dual-slot firmware rollback

In March, I bought a Seeed Studio XIAO ESP32-C5.

Then I put it down.

The unopened package migrated to a corner of the house, where it gathered dust while other projects took priority. This was slightly absurd. Inside was a tiny RISC-V board with dual-band Wi-Fi 6, Bluetooth Low Energy, 8 MB of flash and 8 MB of PSRAM. It had more than enough capability to become something interesting. For several months, however, its main job was storing dust.

Eventually I picked the package up, wiped it clean and opened it.

The board was almost comically small. I connected it over USB and began with the obvious experiment: scan for nearby Wi-Fi networks. Once that worked, I moved to Bluetooth and built a simple BLE homing device. Those first sketches did exactly what I asked. They started, performed a task and responded to commands.

That was also the problem.

The board could react in real time, but my experiments did not give it a persistent operational shape. There was no small, always-ready runtime supervising services, bounding work and explaining what had happened after a failure. Each experiment was an application with its own loop and its own assumptions.

I wanted the board to remain understandable when something went wrong.

That is how Cirvane began.

The gap between a loop and a runtime

Embedded development makes it wonderfully easy to get a first result. Initialise a radio, register a callback, enter a loop and watch the serial output. A Wi-Fi scanner can be alive in an afternoon. A BLE experiment can feel like a complete system because the board responds immediately when a phone or terminal talks to it.

Responsiveness is not the same as resilience.

A connected device has to survive more than its successful path. A service can stop reporting health. A queue can fill. A configuration write can lose power halfway through. A firmware image can download correctly and still fail on its first boot. A malformed serial command can arrive at exactly the wrong time. A network can disappear after credentials have entered memory.

The interesting question was no longer, “Can this board do Wi-Fi or Bluetooth?” Seeed’s own documentation already establishes that the XIAO ESP32-C5 supports both. The question became, “What is the smallest runtime I can place around connected services so that their work and failure remain bounded?”

I deliberately resisted calling the answer a complete operating system.

Cirvane v0.1.0 is an ESP-IDF application powered by FreeRTOS. ESP-IDF owns the boot process, drivers, networking primitives, partitions and low-level update APIs. Cirvane owns a smaller but useful layer above them: service identity, supervision, bounded messaging, configuration recovery, update policy, diagnostics and the operator shell.

It is not quite a full OS. It is enough runtime to give one supported board a more predictable life.

Make the limits part of the design

The first rule of Cirvane is that important resources have a number attached to them.

The current service manager admits at most eight services. Messages come from a fixed pool of 32 slots, each carrying a payload of up to 24 bytes. If the pool or a destination queue is full, the runtime drops the message, increments a visible counter and returns an explicit error. It does not quietly grow a heap-backed queue until memory exhaustion makes the decision for it.

Supervision follows the same philosophy. Each service has a stable identity, a health deadline and a declared restart policy. An automatically restarted service receives no more than three restart attempts. Backoff begins at one second and increases with each attempt. Exhausting that budget moves the service into a degraded state that an operator can inspect.

Those bounds are intentionally unexciting. That is what makes them useful.

They turn questions such as “How much work can accumulate?” and “Will this retry forever?” into properties that can be read from the source and exercised in tests. On a small device, predictability is a feature. Refusing work and recording why is often safer than pretending every request can eventually succeed.

Configuration needs a last known good state

Configuration is another place where a happy-path implementation can become fragile.

Writing new settings over the current record creates an awkward failure window. If power disappears during the write, the device may wake with neither the old configuration nor a complete new one. Cirvane instead keeps two CRC-protected records. A change is written to the alternate record and verified before its generation becomes current.

At boot, the highest valid generation wins. If the newest record is corrupt or incomplete, the older valid record remains available. If neither record is valid, the runtime falls back to declared defaults.

This is not a general database transaction system. It is a narrow answer to a common embedded failure: preserve one known-good configuration while preparing its replacement.

The same pattern appears again in firmware updates.

An update is not successful when the download ends

Cirvane uses two application slots. A new image is written to the inactive slot while the current firmware remains available. The runtime does not select that slot merely because a network request returned bytes.

The remote update path begins with an exact version such as:

ota-update v0.1.0

Cirvane constructs a fixed GitHub Releases URL rather than accepting an arbitrary location. It fetches a small canonical manifest over HTTPS, checks the product and board identities, rejects stale release sequences, verifies an ECDSA-P256 signature, then streams the declared image through bounded buffers while hashing and writing it.

The inactive slot becomes the next boot target only after the transport, manifest, identity, version, origin, signature, image length, digest and application signature checks all succeed.

After reboot, the new application is still pending. The operator can inspect services and resources before running ota-confirm. If the image fails its first boot or restarts without confirmation, ESP-IDF’s application rollback mechanism returns the device to the previously verified image.

A failed update therefore leaves a useful answer: rejected at a named stage, current boot target preserved.

That is more valuable than a vague “update failed” message, particularly when the device is remote and the last working image may be the only recovery path available.

Wi-Fi became a product interaction

The project’s origin included a Wi-Fi scanner, so it felt fitting that connectivity became one of Cirvane’s most tangible product features.

From the privileged USB shell, an operator can run:

wifi connect

With no network name supplied, Cirvane scans for nearby access points, presents a bounded list and asks the operator to choose one. It then requests the password through masked input. A known or hidden network can be selected directly with wifi connect "<ssid>", but the password is never accepted as a command-line argument.

The distinction matters. Shell arguments tend to leak into history, logs and copied diagnostic evidence. Cirvane keeps credentials in the Wi-Fi driver’s RAM for the current session, clears the configuration after failure or disconnect and never writes the password to its own configuration journal. Rebooting returns the device to a disconnected state.

Scanning is capped at 20 results. SSIDs and passwords are length checked. Association and DHCP receive a 15-second deadline. Failure returns a useful reason rather than leaving the shell waiting indefinitely.

This is still a privileged local interface. There is no shell authentication in v0.1.0, and someone with physical or debugger access remains inside the trust boundary. Masking a password is good interaction design, not a substitute for hardware security.

Diagnostics are part of recovery

A restart policy is difficult to trust if it hides its decisions.

Cirvane’s USB shell exposes device identity, service state, resource use, message drops and update status through commands such as info, svc, res, bus and ota-status. A selftest command exercises the non-destructive checks available in the normal development image.

The runtime also keeps routine heartbeat telemetry below the default shell log level. That sounds minor, but it came from using the device rather than merely designing it. A prompt that is constantly interrupted by periodic logs is technically functional and practically unpleasant.

Hardware-in-the-loop testing has a different boundary. Commands that deliberately inject failure or alter low-level state are compile-gated and excluded from production images. A testing interface should not become an undocumented maintenance backdoor.

The result is a shell designed around two questions: what state is the device in, and what bounded action can I take next?

The board had to prove it

A runtime about recovery cannot rely on unit tests alone.

Cirvane has host-side tests for strict parsing, resource limits, generation arithmetic, Wi-Fi input policy, canonical update manifests and fail-closed OTA decisions. The complete local gate also builds the signed ESP32-C5 firmware and validates release, brand and public-source contracts.

The important evidence comes from the physical board.

On the supported XIAO ESP32-C5, the current evidence covers boot, shell operation, service supervision, malformed input, configuration corruption, Wi-Fi scanning and connection, signed update staging, first-boot confirmation and automatic rollback. The recorded Stage 3 measurements report a 20-sample restart median of 1,152.317 milliseconds and a 30-sample info command median of 2.122 milliseconds.

Those numbers are not universal performance claims. They belong to one board, one pinned ESP-IDF v6.0.2 toolchain and one recorded protocol. Energy use has not yet been measured with calibrated equipment. Hardware Secure Boot, flash encryption, irreversible eFuse provisioning, RF fuzzing, physical fault injection and independent penetration testing remain outside the v0.1.0 claim.

That boundary is not a footnote. It is part of the release.

Why Cirvane is not pretending to replace FreeRTOS

There is a clean-sheet Cirvane kernel research track in the repository. It explores kernel-owned scheduling, messaging, configuration and recovery without FreeRTOS. It has a portable core and an ESP32-C5 spike, but it is not the firmware released as Cirvane v0.1.0.

Keeping those identities separate matters.

It would be easy to let an ambitious research direction inflate the description of the working product. The product available today is Cirvane’s recovery-focused service runtime on ESP-IDF and FreeRTOS. The kernel work asks whether a later Cirvane image could own more of that stack. Until it does, the project says exactly which layer owns what.

That honesty made the smaller release more useful. Instead of holding the firmware back until it could satisfy every interpretation of “operating system”, I could release a coherent runtime with a narrow hardware target and evidence for its actual behaviour.

The package is no longer gathering dust

The XIAO ESP32-C5 that sat unopened for months now boots a signed Cirvane image.

It can scan for a network, guide a user through a connection, supervise a fixed set of services, bound its queued work, recover the last valid configuration and refuse an update before it changes the boot target. When a new image does become eligible to boot, it still has to prove itself before the old one is surrendered.

Cirvane does not make the board infallible. It makes failure smaller, more visible and more recoverable.

That was the missing piece in my first experiments. The Wi-Fi scanner and BLE homing device showed that the hardware could respond. Cirvane gives future experiments somewhere dependable to live.

The project is available under Apache 2.0 at github.com/kabudu/cirvane, with the v0.1.0 release, architecture, threat model, validation notes and hardware evidence open for inspection.

If you have a XIAO ESP32-C5 of your own, perhaps sitting unopened in a corner, the most useful first step may still be to run a Wi-Fi scan.

The second is to decide what should happen when it fails.