Arduino Simulation Demo

GenesisNode running as a complete edge AI system on Arduino-class microcontrollers. The demo simulates a real-world predictive maintenance scenario: a DC fan with an accelerometer detects vibration imbalance and sends alerts over MQTT. Everything runs on-device.

The GenesisNode inference engine -- node structs, connection graph, signal propagation, tokenizer -- fits in as little as 1.1KB of SRAM and executes in ~4 microseconds on an ESP32-S3. There is no cloud inference, no WiFi round-trip for predictions, no TensorFlow Lite. The model trains and infers entirely on the microcontroller.

The browser-based simulation is a faithful JavaScript port of the C implementation (genesisnode_nano.h). The same node struct layout, the same signal propagation algorithm, the same tokenizer thresholds. Board-specific performance estimates are derived from cycle-accurate operation counting against real hardware profiles (clock speed, multiply latency, I2C bus timing, OLED refresh cost).

The simulation covers the full lifecycle: start a fan, collect accelerometer data, train a model to distinguish normal vs. imbalanced vibration, switch to inference mode, and watch the system detect faults in real time with MQTT alerts firing on every detection.


The Demo Flow

  1. Start the Fan

    Click Start Fan. The four-blade fan visualization begins spinning, and the simulated MPU-6050 accelerometer starts generating vibration readings at 200 Hz. The vibration chart shows low-amplitude, regular sinusoidal motion -- a healthy, balanced fan.

  2. Train the Normal Baseline

    Click Train: Normal. The system collects 100 accelerometer windows (50 samples each), tokenizes them into vibration descriptors (vib_low, vib_stable), and trains the GenesisNode model. Watch the node graph populate as nodes are created for each token and connected to the normal label. The progress bar tracks training completion. On an ESP32-S3, all 100 training steps would take about 1.7 ms total.

  3. Add Weight to a Blade

    Click Add Weight. A red dot appears on blade 1, and the fan animation switches from smooth rotation to a wobbling motion. The accelerometer chart immediately shows higher-amplitude, irregular vibration. RMS magnitude jumps from ~0.02g to ~0.15g.

  4. Train the Imbalance Pattern

    Click Train: Imbalance. The system collects 100 imbalanced vibration windows and trains them against the imbalance label. The node graph now shows both labels with weighted connections. Tokens like vib_high, vib_burst, and vib_spike get strong connections to imbalance, while vib_low and vib_stable connect strongly to normal.

  5. Switch to Inference Mode

    Click Inference Mode. The system begins classifying every vibration window in real time. The prediction, confidence score, and running accuracy appear in the Inference Results panel.

  6. Watch Real-Time Detection

    With the fan running imbalanced, the system detects imbalance and the OLED mockup shows !! IMBALANCE !! in red. Toggle the weight on and off to see detection switch between normal and imbalance. The accuracy chart tracks classification performance over time, typically reaching 100% after the initial window fills.

  7. See MQTT Alerts

    Every imbalance detection triggers an MQTT message. The MQTT panel shows timestamped alerts with confidence scores. The serial monitor shows the full JSON payload as it would appear on a real device's serial output at 115200 baud.


Board Selector

The board dropdown in the top bar changes all performance estimates to reflect real hardware characteristics. Six boards are available:

Arduino Nano V3 (ATmega328P)

XIAO SAMD21 (Cortex-M0+)

ESP32-S3 (Xtensa LX7 dual-core) -- TOP PICK

ESP32-C3 SuperMini (RISC-V)

Pi Pico 2 W (Cortex-M33 dual-core)

Teensy 4.0 (Cortex-M7)

Selecting a board updates all metrics in the Device Performance panel: SRAM usage percentage, maximum node capacity, inference time, inferences per second, full loop time (including I2C sensor reads, OLED refresh, and serial transmission), and total training time.


What the UI Shows

The interface is a three-panel layout simulating the full hardware stack.

Left Panel: Hardware Simulation

Center Panel: GenesisNode Engine

Right Panel: Output and Communication

LED Indicators

Two LED indicators in the top bar:


The GenesisNode Engine

The inference engine running in the browser is a direct JavaScript port of genesisnode_nano.h, the C header designed for microcontrollers. The core data structures are identical.

Node Structure (44 bytes in C)

typedef struct {
    char     token_id[16];        // "vib_high", "normal", etc.
    uint8_t  n_conns;             // number of connections (max 8)
    GN_Connection conns[8];       // {target_index, weight} pairs (3 bytes each)
    int16_t  threshold;           // firing threshold (default 200)
    uint8_t  maturity;            // lifecycle stage
} __attribute__((packed)) GN_Node;

Each connection is 3 bytes: a uint8_t target index and an int16_t weight (fixed-point, divided by 1000). The entire node is 44 bytes packed.

The GenesisNode Instance

typedef struct {
    GN_Node  nodes[24];           // 24 nodes max on ATmega328P
    uint8_t  n_nodes;
    GN_FireEntry firing_chain[16];
    uint8_t  chain_len;
    uint8_t  output_node;
    int16_t  output_score;
    bool     training_mode;
    uint16_t total_inferences;
    uint16_t total_train_steps;
} GenesisNode;

With 24 max nodes, the full struct is ~1,140 bytes. On an ATmega328P with 2KB SRAM, this leaves ~500 bytes for the Arduino core, Wire library, and Serial buffers. The fan vibration model typically uses 7-10 nodes, well within the 24-node limit.

Signal Propagation

Inference follows a two-pass algorithm:

  1. Input activation: Each input token is looked up by string match. Matching nodes get activation 1000. Their outgoing connections propagate signal: activation[target] += activation[source] * weight / 1000.
  2. Intermediate firing: All nodes above their threshold fire, propagating signal further through their connections.
  3. Label selection: The label node (normal/imbalance) with the highest activation wins.

The entire inference is integer arithmetic -- no floating point. The weight / 1000 division uses the fixed-point representation. This matters on AVR, which has no hardware floating-point unit.

Training

Training uses Hebbian-style weight updates:

Nodes and connections are created on demand via gn_get_or_create(). There are no pre-allocated layers or fixed topology.

Performance: Why It Is Fast

The fan demo model has ~7-10 nodes with ~8 connections each. Inference involves:

On an ESP32-S3 at 240 MHz with single-cycle multiply, this completes in ~4 microseconds. On an ATmega328P at 16 MHz with 17-cycle multiply, it takes ~115 microseconds. Either way, inference is a tiny fraction of the main loop -- I2C sensor reads (500 us on Nano) and OLED updates (20 ms) dominate.


MQTT Integration

Message Format

Every imbalance detection produces a JSON message:

{
  "device_id": "fan-demo-001",
  "event": "imbalance",
  "confidence": 12000,
  "tokens": ["vib_high", "vib_stable", "vib_burst"]
}

Fields:

Topic Structure

genesisnode/fan/alert     -- imbalance detections
genesisnode/fan/status    -- periodic heartbeat / health
genesisnode/fan/metrics   -- inference timing, accuracy stats

Transport

Compatibility

The JSON payload format is directly compatible with:


Building the Real Thing

Recommended Hardware (~$22 total)

Part Price Notes
XIAO ESP32-S3 x1 $7.49 Primary demo board with WiFi/BLE
ESP32-C3 SuperMini 5-pack $12-15 Fleet sensor nodes at ~$2.50 each
Total ~$20-22

You also need these sensors and peripherals (commonly available, ~$10-15 if you do not already have them):

Wiring Diagram

All sensors share the I2C bus (SDA/SCL). On an Arduino Nano, that is A4 (SDA) and A5 (SCL). On XIAO boards, use the labeled SDA/SCL pins.

MPU-6050:    SDA -> A4    SCL -> A5    VCC -> 5V     GND -> GND     (addr: 0x68)
BME280:      SDA -> A4    SCL -> A5    VCC -> 3.3V   GND -> GND     (addr: 0x76 or 0x77)
SSD1306:     SDA -> A4    SCL -> A5    VCC -> 5V     GND -> GND     (addr: 0x3C)
MT6701:      SDA -> A4    SCL -> A5                                  (addr: 0x06)
DC Motor:    D9 (PWM) -> MOSFET gate    Motor+ -> 12V    Motor- -> MOSFET drain
EC11 Enc:    CLK -> D2    DT -> D3    SW -> D4    VCC -> 5V    GND -> GND
Serial out:  USB (built-in) -> PC running MQTT bridge

All four I2C devices coexist on the same bus at different addresses. No address conflicts.

The C Code

The complete inference engine is a single C header file: arduino/genesisnode_nano.h. It requires only <stdint.h>, <string.h>, and <stdbool.h> -- no Arduino-specific dependencies, no external libraries beyond the standard C library.

To use it in an Arduino sketch:

#include "genesisnode_nano.h"

GenesisNode gn;
const char *labels[] = { "normal", "imbalance" };

void setup() {
    gn_init(&gn);
    // ... sensor setup, I2C init, OLED init ...
    // Run training (or load pre-trained weights)
}

void loop() {
    // 1. Read MPU-6050 accelerometer
    // 2. Fill 50-sample window
    // 3. Tokenize vibration pattern
    // 4. Run inference
    uint8_t result = gn_infer(&gn, tokens, n_tokens, labels, 2);
    // 5. Display on OLED
    // 6. Send MQTT alert if imbalance detected
}

The arduino/sim_hardware.c file provides a complete desktop simulation with cycle-accurate timing estimates, simulated sensors, tokenizer, OLED output, and serial JSON alerting. Compile and run it on any system with a C compiler to verify the model works before flashing hardware:

gcc -O2 -o fan_demo arduino/sim_hardware.c -lm
./fan_demo

Performance Numbers

All estimates are derived from cycle-accurate operation counting in sim_hardware.c, using measured instruction costs for each architecture (e.g., 17 cycles per multiply on AVR, 1 cycle on ARM/RISC-V).

Inference (7-10 node model, 2-3 input tokens)

Board Inference Time Inferences/sec Notes
Arduino Nano V3 115 us 8,700 16 MHz AVR, 17-cycle multiply
XIAO SAMD21 25.6 us 39,000 48 MHz ARM, single-cycle multiply
ESP32-C3 SuperMini 6-8 us 143,000 160 MHz RISC-V
Pi Pico 2 W 5-7 us 167,000 150 MHz ARM Cortex-M33
XIAO ESP32-S3 4.2 us 238,000 240 MHz Xtensa LX7
Teensy 4.0 1.1 us 909,000 600 MHz ARM Cortex-M7

Memory Usage (24-node config for Nano, larger for other boards)

Board SRAM Total GenesisNode Struct Available After Fits?
Arduino Nano V3 2,048 B ~1,140 B (55.6%) ~500 B Yes (tight)
XIAO SAMD21 32 KB ~1,140 B (3.5%) ~31 KB Yes
ESP32-C3 400 KB scales with nodes ~394 KB Yes
ESP32-S3 512 KB + 8 MB scales with nodes ~511 KB+ Yes
Pi Pico 2 W 520 KB scales with nodes ~519 KB Yes
Teensy 4.0 1 MB scales with nodes ~1 MB Yes

Training (200 samples, 2 labels)

Board Total Training Time Per Step
Arduino Nano V3 ~24 ms ~120 us
XIAO SAMD21 ~3.4 ms ~17 us
ESP32-S3 ~1.7 ms ~8.5 us

Full Loop Time (inference + 2x I2C read + OLED refresh + serial TX)

Board Inference I2C Reads OLED Update Serial TX Total Loop Hz
Arduino Nano V3 115 us 1,000 us 20,000 us 10,400 us 31.5 ms 32 Hz
XIAO SAMD21 25.6 us 400 us 8,000 us 10,400 us 18.8 ms 53 Hz
ESP32-S3 4.2 us 300 us 5,000 us 10,400 us 15.7 ms 64 Hz

Note: I2C sensor reads and OLED refresh dominate the loop time, not inference. GenesisNode inference is a tiny fraction of each cycle. On WiFi boards, MQTT publish replaces serial TX and runs asynchronously on the second core.