Concepts
The Compiler Pipeline
From flow JSON to firmware.bin — what happens when you click Flash.
Pipeline steps
The editor POSTs the full flow graph as JSON to POST /api/compile. The compiler returns a jobId immediately (202 Accepted) — compilation runs async.
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.
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.
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().
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.
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.
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 + includesHAL header always included. Library headers only for node types actually used in the flow — unused libraries are never included.
Forward declarationsEvery function and every output port is forward-declared before use. This ensures C++ can resolve calls regardless of definition order.
Wire connectionsOutput functions defined before node bodies. Fan-out (one → many) generates a single function calling all targets. Typed ports carry values via function parameters.
Node implementationsEach 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.tsAdd a template function that returns a C++ string. Receives the node context (config, id, allNodes, allEdges).
src/lib/libraries.tsAdd the PlatformIO lib_deps entry and #include line if your node needs an external library.
src/compiler/generator.tsAdd to SETUP_NODES if it needs a setup() call on boot. Add to TICK_NODES if it needs a tick() call every loop.