IoT Architectures Under Pressure: Hosting a Portable Firmware (Part 3)
Adriano Repetti

Adriano Repetti @adriano-repetti

About: Software Engineer passionate about IoT and building connected solutions that make a difference.

Location:
Florence, Italy
Joined:
May 8, 2025

IoT Architectures Under Pressure: Hosting a Portable Firmware (Part 3)

Publish Date: May 16
11 0

As discussed in the previous post, shifting firmware from individual devices to a centralized hub offers significant benefits, pooling resources for multiple connected devices.

Key Obstacles

However, several challenges must be addressed:

  • Firmware isolation – Each device’s software must be securely separated.
  • Unknown hub architecture – The hub's OS, CPU, and other specifications are unpredictable.
  • Avoiding ABI/ISA fragmentation – Supporting multiple ABIs and ISAs would be impractical and expensive.

Potential Solutions

A few possible approaches come to mind, but each has drawbacks:

  • Virtual machines or lightweight containers – While they offer isolation, they don’t fully resolve compatibility issues. An emulation layer could help, but would increase cost and reduce efficiency in the long run.
  • Interpreted languages – Using Python or JavaScript could simplify portability but introduces severe limitations. Pairing them with containers might improve isolation, but would still fall short of a universal solution.
  • Bytecode virtual machines – Java VM offers better flexibility but restricts developers to supported languages, limiting versatility.

Seeking a Better Alternative

Ideally, we need a standardized, open, language-agnostic solution with a sound type system, enabling a Language-based System approach.

One promising option is WebAssembly (WASM)—a lightweight, efficient, and cross-platform execution model. WASM already supports an increasing number of programming languages, including C, which facilitates the porting of existing code.

Other options, such as BEAM (the Erlang VM), provide robustness but lack widespread adoption for general commercial consumer applications.

Solving Key Challenges with WebAssembly (WASM)

Embracing WASM as our virtual ISA presents several advantages:

  • Broad language support – Numerous programming languages target WASM with varying degrees of support. See Awesome WebAssembly Languages. This ensures familiarity for firmware developers and lowers the learning curve.
  • Sound Type System – WASM guarantees type safety and memory safety within its semantics, enhancing reliability (source).
  • Platform Agnostic Execution – As a virtual ISA, WASM can be compiled AOT, JIT, or interpreted, making it highly adaptable to present and future platforms.

Choosing a WASM Runtime

Several WASM runtimes are available to execute our code. In this example, we'll use Wasmer, though other options exist. If we compile AOT (Ahead-of-Time), we don’t even need a runtime at all!

An example

We're going to write a fictional (and minimal!) firmware for our smart thermostat, let's assume we decided to use AssemblyScript (but it could have been in C, Rust, Go or any other supported language):

import { Context } from "firmwareless/hosting"
import { Interval, IoStream } from "firmwareless/lib"

export function setup(context: Context) {
    context.status.register<i8>("current_temperature", "Temperature");
    context.status.register<i8>({
        id: "desired_temperature",
        label: "Desired temperature",
        type: "environment/temperature",
        unit: "Celsius",
        editable: true,
        editor: "Knob",
        indicator: "Ring",
        range: { nullable: true, minimum: 12, maximum: 30, step: 1 }
    });
    context.status.register<u8>({
        id: "furnace",
        label: "Active",
        type: "status/boolean",
        indicator: "Led",
    });

    context.schedule(main, { interval: Interval.FromMinutes(1) });
}

function main(context: Context) {
    const stream = IoStream.Open(context.associatedDeviceId);

    const currentTemperature = stream.readInt8();
    context.status.set("current_temperature", currentTemperature);

    const desiredTemperature = 
        context.status.get("desired_temperature");

    const furnaceIsActive = currentTemperature < desiredTemperature;
    stream.write(furnaceIsActive);
    context.status.set("furnace", furnaceIsActive);
}

Enter fullscreen mode Exit fullscreen mode

Note how we described our setup programmatically but it could have been done with a simpler JSON descriptor. To compile it:

asc ./thermostat.ts -o ./thermostat.wasm --optimize
Enter fullscreen mode Exit fullscreen mode

Now, the final step is uploading the firmware to a Public Firmware Repository (obviously it's not the only option, the device itself could provide its firmware to the host!).

When customers purchase our device, the hub will automatically download and install the firmware, simplifying onboarding and updates.
The hub (or a separate cloud service) is responsible for compiling the code into a native executable, if we choose this approach. With Wasmer, the process is straightforward:

wasmer create-exe thermostat.wasm -o ./thermostat --target=aarch64-linux-gnu
Enter fullscreen mode Exit fullscreen mode

This setup ensures seamless deployment while keeping the firmware adaptable across platforms.

Where Are We?

At its core, this approach mirrors the way traditional desktop applications or device drivers function. Instead of embedding complex firmware directly into a device, we're simply offloading it to a hub: just as a computer executes software or loads drivers to communicate with peripherals.

We've developed a smart device that:

  • Costs less than a traditional one—requiring only an I2C temperature sensor and a tiny relay.
  • Empowers customers to spend less while choosing the hub that best suits their needs, with full control over updates.
  • Supports multiple programming languages, allowing developers to use familiar tools without retraining.
  • Simplifies development and debugging: the firmware runs unchanged on a development machine or even in a browser.
  • Includes an up-to-date UI, security, and mobile app—all without writing a single line of code.
  • Future-proof design ensures compatibility across CPUs, hub OSes, smartphone OSes, and input methods.

Looking Ahead

While this solution addresses key challenges, there are still important decisions to make. Future posts will explore these aspects further.

Key Considerations:

  • Flexibility – Devices can range from firmware-free (as in this example) to high-powered computing units, tailored to specific needs. This will be covered in the next post.
  • Standardization – The necessary tools and protocols already exist; now, we need a common standard for vendors to adopt. Ideally we have Matter and everything should integrate together (old and new).

If you want to jump ahead, directly to a practical example then you can go to the 5th post of this series where we start to design a smart thermostat.

Comments 0 total

    Add comment