#BLUEHASH
← Docs

Concepts

The Compiler Pipeline

From flow JSON to firmware.bin — what happens when you click Flash.

Pipeline steps

1
Flow JSON → Compiler

The editor POSTs the full flow graph as JSON to POST /api/compile. The compiler returns a jobId immediately (202 Accepted) — compilation runs async.

// POST /api/compile { "nodes": [ { "id": "abc", "data": { "type": "start", "config": {} } }, { "id": "def", "data": { "type": "show_text", "config": { "text": "hello" } } } ], "edges": [ { "source": "abc", "sourceHandle": "boot", "target": "def", "targetHandle": "trigger" } ] }
2
Graph walk → C++ templates

The generator walks every node, calls its C++ template function, and collects the output. Each template returns a self-contained C++ block with run(), setup(), and tick() functions.

// Template for show_text node: void node_def_run() { bh_display_clear(); bh_display_font(BH_FONT_SMALL); bh_display_print_center(28, "hello"); bh_display_show(); }
3
Wire builder → output functions

Each edge becomes a typed output function. Fan-out (one output wired to multiple targets) is handled by grouping edges and emitting a single function that calls all targets.

// Edge: abc.boot → def.trigger becomes: void node_abc_output_boot() { node_def_run(); }
4
flow.h assembly

All blocks are assembled into flow.h: includes, forward declarations, wire functions, node implementations, and the two entry points bh_flow_setup() and bh_flow_tick().

#pragma once #include "hal/bh_hal.h" // Forward declarations void node_abc_run(); void node_def_run(); // Wire connections void node_abc_output_boot() { node_def_run(); } // Node implementations void node_abc_run() { node_abc_output_boot(); } void node_def_run() { bh_display_print_center(28, "hello"); bh_display_show(); } // Entry points (called by main.ino) void bh_flow_setup() { node_abc_run(); } void bh_flow_tick() { }
5
PlatformIO compile

The compiler copies base firmware files from bluehash-firmware/, injects flow.h and any program .h files, then runs pio run. PlatformIO downloads missing libraries, compiles with GCC, and links the binary.

// pio run -e esp32s3 // → Downloads libraries from lib_deps // → Compiles main.ino + flow.h + hal/bh_hal.cpp // → Links → firmware.bin
6
Delivery → Flash

The editor polls GET /api/jobs/:id (or subscribes via WebSocket) for live progress. When done it fetches the firmware.bin and flashes it via esptool-js over Web Serial.

// WebSocket progress events: { "status": "compiling", "progress": 45 } { "status": "linking", "progress": 80 } { "status": "done", "progress": 100, "binUrl": "/api/jobs/xyz/download" }

Anatomy of flow.h

Every compile produces a single flow.h that replaces the template in the firmware source. It has six sections in order.

#pragma once + includes

HAL header always included. Library headers only for node types actually used in the flow — unused libraries are never included.

Forward declarations

Every function and every output port is forward-declared before use. This ensures C++ can resolve calls regardless of definition order.

Wire connections

Output functions defined before node bodies. Fan-out (one → many) generates a single function calling all targets. Typed ports carry values via function parameters.

Node implementations

Each node's C++ block: run(), setup(), tick(), set_val(), and no-op fallback output functions for unconnected ports.

bh_flow_setup()

Called once by main.ino on boot. Runs setup() for every node that needs hardware init (UART, I2C, SPI, BLE, etc), then calls the Start node.

bh_flow_tick()

Called every loop() iteration by main.ino. Polls all tick-based nodes (timers, buttons, WebSocket, UART receive, etc).

Adding a new node type

Three files to touch — nothing else changes.

src/templates/node.templates.ts

Add a template function that returns a C++ string. Receives the node context (config, id, allNodes, allEdges).

src/lib/libraries.ts

Add the PlatformIO lib_deps entry and #include line if your node needs an external library.

src/compiler/generator.ts

Add to SETUP_NODES if it needs a setup() call on boot. Add to TICK_NODES if it needs a tick() call every loop.