Building Your First WASM App

Create, build, and deploy a “Hello World” WebAssembly application on AkiraOS.

Prerequisites

  • AkiraOS firmware flashed and running on a device (or native_sim built)
  • WASI SDK installed (see below)
  • Basic C programming knowledge

Install WASI SDK

WASI_VERSION=24
wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_VERSION}/wasi-sdk-${WASI_VERSION}.0-x86_64-linux.tar.gz
sudo tar xvf wasi-sdk-${WASI_VERSION}.0-x86_64-linux.tar.gz -C /opt
sudo ln -sf /opt/wasi-sdk-${WASI_VERSION}.0 /opt/wasi-sdk

Note: Install to /opt/wasi-sdk (the default path all sample Makefiles expect) or set WASI_SDK=/your/path.


Hello World App

The AkiraSDK/ submodule contains ready-to-build sample apps in AkiraSDK/wasm_apps/. The fastest way to get started is to copy an existing app as a template.

Step 1: Create your project

cp -r ~/akira-workspace/AkiraOS/AkiraSDK/wasm_apps/hello_world ~/my_hello_world
cd ~/my_hello_world

Step 2: Write the code

Apps include a single header — all APIs are declared there:

main.c:

#include "akira_api.h"

int main(void) {
    printf("Hello from AkiraOS WASM!");

    display_clear(COLOR_BLACK);
    display_text(10, 10, "Hello AkiraOS!", COLOR_WHITE);
    display_flush();

    while (1) {
        delay(1000000);  // idle — yield every 1 second
    }
    return 0;
}

Key rules:

  • Use printf() from akira_api.h — do not include <stdio.h>
  • All API functions are extern — they are provided by AkiraOS at runtime, not linked from a library
  • Always call display_flush() after drawing, or the screen stays blank
  • Always delay() in your loop — spinning without yielding burns 100% CPU

Step 3: Create the manifest

manifest.json:

{
  "name": "hello_world",
  "version": "1.0.0",
  "capabilities": ["display.write"],
  "memory_quota": 65536
}

The manifest is automatically embedded into the .wasm binary by embed_manifest.py (called by the Makefile).

Step 4: Build

# Using the SDK build script (recommended)
cd ~/akira-workspace/AkiraOS/AkiraSDK/wasm_apps
./build.sh hello_world        # single app → bin/hello_world.wasm
./build.sh                    # all apps

# Or with Make directly from your app directory
cd ~/my_hello_world
make

Verify the output:

ls -lh bin/hello_world.wasm   # should be ~2–5 KB
file bin/hello_world.wasm     # "WebAssembly (wasm) binary module"

Deploy to Hardware

The device must be connected to WiFi. Find its IP from the serial console (net iface).

curl -X POST -F "file=@bin/hello_world.wasm" http://<device-ip>/upload

Run from the shell

# Connect to serial console
west espmonitor

# In the shell:
AkiraOS:~$ wasm load /apps/hello_world.wasm
AkiraOS:~$ wasm start hello_world

Expected output:

[00:00:10.523] <inf> wasm: Loading: /apps/hello_world.wasm
[00:00:10.687] <inf> wasm: App loaded: hello_world
[00:00:10.692] <inf> app: Hello from AkiraOS WASM!

Next Steps

Read a sensor

#include "akira_api.h"

int main(void) {
    int raw = sensor_read(SENSOR_CHAN_AMBIENT_TEMP);
    if (raw == AKIRA_SENSOR_ERROR) {
        printf("sensor unavailable");
    } else {
        // raw = 23500 → 23.5 °C; divide by 1000 for whole degrees
        printf("Temp: %d.%d C", raw / 1000, (raw % 1000) / 100);
    }
    return 0;
}

Capability required: "sensor.read"

Read a GPIO input

#include "akira_api.h"

#define BTN_PIN 0   // adjust to your hardware

int main(void) {
    gpio_configure(BTN_PIN, GPIO_INPUT | GPIO_PULL_DOWN);

    while (1) {
        if (gpio_read(BTN_PIN) == 1) {
            printf("button pressed!");
        }
        delay(10000);  // poll every 10 ms
    }
    return 0;
}

Capability required: "gpio.read"

Write to persistent storage

#include "akira_api.h"

int main(void) {
    int fd = storage_open("message.txt", STORAGE_O_WRITE);
    if (fd >= 0) {
        storage_write(fd, "Hello, filesystem!", 18);
        storage_close(fd);
        printf("saved");
    }
    return 0;
}

Capability required: "storage.write"


Troubleshooting

“Failed to load WASM” — check the file exists:

AkiraOS:~$ fs ls /apps

“capability denied” — add the required capability to manifest.json:

{ "capabilities": ["display.write", "sensor.read"] }

“Out of memory” — increase the quota in manifest.json:

{ "memory_quota": 131072 }

Screen stays black — you forgot display_flush() after your draw calls.

Function not found at build time — do not include <stdio.h> or other stdlib headers. All functions come from akira_api.h.



Copyright © 2025-2026 AkiraOS Project. Licensed under GNU GPL v3.