Reference
Node Reference
Every node available in the BLUEHASH editor — ports, config fields, notes, and examples.
Flow control
Sequencing, timing, branching, and repeating
Entry point of every flow. Fires boot on power-up. Every flow must have exactly one Start node.
| Name | Dir | Type | Description |
|---|---|---|---|
| boot | OUT → | trigger | Fires once when the device powers on |
| wake | OUT → | trigger | Fires when waking from deep sleep |
- Every flow must have exactly one Start node
- boot and wake are separate — use wake to restore state after deep sleep
Terminates a flow branch. Has no effect at runtime — used as a visual terminator.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Receives any trigger and does nothing |
Blocks execution for a fixed duration, then fires done.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Start the delay |
| done | OUT → | trigger | Fires after duration has elapsed |
| Key | Widget | Default | Description |
|---|---|---|---|
| duration | number | 1000 | Wait time in milliseconds |
Fires tick on a repeating interval. Non-blocking — runs alongside other nodes each loop iteration.
| Name | Dir | Type | Description |
|---|---|---|---|
| start | ← IN | trigger | Enable the timer |
| stop | ← IN | trigger | Disable the timer |
| tick | OUT → | trigger | Fires every interval ms |
| Key | Widget | Default | Description |
|---|---|---|---|
| interval | number | 1000 | Time between ticks in milliseconds |
| autoStart | toggle | true | Start ticking immediately on boot without needing a start trigger |
- Timer runs in tick() — it is non-blocking and never delays other nodes
- autoStart=true means the timer fires from boot with no wiring needed
Compares two float values (A op B) and routes flow to true or false output.
| Name | Dir | Type | Description |
|---|---|---|---|
| run | ← IN | trigger | Evaluate the condition |
| a | ← IN | number | Left-hand value |
| b | ← IN | number | Right-hand value |
| true | OUT → | trigger | Fires when condition is true |
| false | OUT → | trigger | Fires when condition is false |
| Key | Widget | Default | Description |
|---|---|---|---|
| operator | select | == | Comparison: ==, !=, >, <, >=, <= |
Fires each(index) N times in sequence, then fires done.
| Name | Dir | Type | Description |
|---|---|---|---|
| run | ← IN | trigger | Start the loop |
| each | OUT → | number | Fires for each iteration with current index (0-based) |
| done | OUT → | trigger | Fires after all iterations complete |
| Key | Widget | Default | Description |
|---|---|---|---|
| count | number | 10 | Number of iterations |
- Loop is blocking — the device cannot do anything else during iteration
- Use timer + counter for non-blocking repeat
Display
OLED text, menus, values, and notifications
Prints static text at a pixel position on the OLED. Optionally clears the screen first.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Draw the text |
| done | OUT → | trigger | Fires after text is drawn |
| Key | Widget | Default | Description |
|---|---|---|---|
| text | text | Text to display | |
| x | number | 0 | X pixel position (0 = left edge) |
| y | number | 0 | Y pixel position (0 = top edge) |
| fontSize | select | small | small (5×7) | medium (8×8) | large (10×20) |
| clear | toggle | false | Clear screen before drawing |
Displays a labelled numeric value in large font. Good for live sensor readings.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Refresh the display with the current value |
| value | ← IN | number | Float value to display — triggers refresh automatically |
| done | OUT → | trigger | Fires after display is updated |
| Key | Widget | Default | Description |
|---|---|---|---|
| label | text | Value | Label shown above the value |
| unit | text | Unit suffix shown after the number (e.g. °C, V, %) | |
| decimals | number | 1 | Decimal places to display |
Draws a progress bar 0–100%. Fires full when value reaches 100.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Refresh the bar |
| value | ← IN | number | 0–100 — triggers refresh automatically when wired |
| done | OUT → | trigger | Fires after drawing |
| full | OUT → | trigger | Fires when value reaches 100 |
| Key | Widget | Default | Description |
|---|---|---|---|
| label | text | Label shown above the bar | |
| height | number | 8 | Bar height in pixels |
Shows a popup message and waits for OK/Back or timeout. Blocking.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Show the notification |
| ok | OUT → | trigger | Fires when user presses OK |
| dismissed | OUT → | trigger | Fires when user presses Back or timeout expires |
| Key | Widget | Default | Description |
|---|---|---|---|
| message | text | — | Message text to display |
| duration | number | 2000 | Auto-dismiss after ms (0 = wait forever) |
Blanks the OLED.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Clear the screen |
| done | OUT → | trigger | Fires after clearing |
| Key | Widget | Default | Description |
|---|---|---|---|
| fill | select | black | black (blank) | white (all pixels on) |
Input
Physical button presses
GPIO / Hardware
Digital, analog, PWM, IR, RF, camera
Sets a GPIO pin HIGH or LOW.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Apply the pin state |
| done | OUT → | trigger | Fires after pin is set |
| Key | Widget | Default | Description |
|---|---|---|---|
| pin | pin | — | GPIO pin number |
| state | select | HIGH | HIGH or LOW |
Reads a GPIO pin and fires high or low output.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Perform the read |
| state | OUT → | boolean | Current pin state (true=HIGH) |
| high | OUT → | trigger | Fires if pin is HIGH |
| low | OUT → | trigger | Fires if pin is LOW |
| Key | Widget | Default | Description |
|---|---|---|---|
| pin | pin | — | GPIO pin number |
| pullMode | select | INPUT_PULLUP | INPUT | INPUT_PULLUP | INPUT_PULLDOWN |
Reads an ADC pin. Returns both raw 12-bit value (0–4095) and converted voltage (0–3.3V).
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Perform the read |
| raw | OUT → | number | 0–4095 ADC value |
| voltage | OUT → | number | 0.0–3.3 calculated voltage |
| Key | Widget | Default | Description |
|---|---|---|---|
| pin | pin | — | ADC-capable GPIO pin |
- Uses 11dB attenuation — full 0–3.3V range
- ESP32 ADC is non-linear near 0V and 3.3V — use map_scale for calibration
Outputs a PWM signal — for LED dimming, motor speed, servo control.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Apply PWM settings |
| done | OUT → | trigger | Fires after PWM is applied |
| Key | Widget | Default | Description |
|---|---|---|---|
| pin | pin | — | PWM-capable GPIO pin |
| duty | number | 128 | Duty cycle 0–255 |
| freq | number | 1000 | Frequency in Hz |
Fires on GPIO edge via hardware interrupt. Non-blocking — works alongside everything else.
| Name | Dir | Type | Description |
|---|---|---|---|
| rising | OUT → | trigger | Fires on LOW→HIGH edge |
| falling | OUT → | trigger | Fires on HIGH→LOW edge |
| change | OUT → | trigger | Fires on any edge |
| state | OUT → | boolean | Current pin state |
| Key | Widget | Default | Description |
|---|---|---|---|
| pin | pin | — | GPIO pin to watch |
| mode | select | CHANGE | RISING | FALLING | CHANGE |
- Uses hardware interrupt (IRAM_ATTR) — minimal latency
- Sets INPUT_PULLUP automatically
Decodes IR signals from any remote. Outputs the protocol name and raw code.
| Name | Dir | Type | Description |
|---|---|---|---|
| received | OUT → | trigger | Fires when a valid IR code is received |
| protocol | OUT → | string | Protocol name e.g. NEC, SONY, RC5 |
| code | OUT → | number | Raw decoded IR code |
| Key | Widget | Default | Description |
|---|---|---|---|
| pin | pin | — | IR receiver data pin (connect to TSOP382x) |
| timeout | number | 15 | Gap timeout in ms — default suits most remotes |
- Supports NEC, SONY, RC5, RC6, Samsung, LG, and 20+ more protocols via IRremoteESP8266
Transmits an IR code at 38kHz carrier.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Send the IR code |
| sent | OUT → | trigger | Fires after transmission completes |
| Key | Widget | Default | Description |
|---|---|---|---|
| pin | pin | — | IR LED pin (via NPN transistor for range) |
| protocol | select | NEC | NEC | SONY | RC5 | RC6 | Samsung | LG |
| code | text | — | IR code in hex e.g. 0x20DF10EF |
| bits | number | 32 | Code bit length |
Receives 433MHz RF codes from remote controls and switches.
| Name | Dir | Type | Description |
|---|---|---|---|
| received | OUT → | trigger | Valid code received |
| code | OUT → | number | Decoded RF code |
| protocol | OUT → | number | RF protocol number |
| bitlength | OUT → | number | Code bit length |
| Key | Widget | Default | Description |
|---|---|---|---|
| pin | pin | — | RF receiver DATA pin |
Transmits a 433MHz RF code — controls remote sockets, gates, alarm panels.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Transmit the code |
| sent | OUT → | trigger | Fires after transmission |
| Key | Widget | Default | Description |
|---|---|---|---|
| pin | pin | — | RF transmitter DATA pin |
| code | number | — | RF code to send |
| bitLength | number | 24 | Code bit length |
| protocol | number | 1 | RF protocol (1 is most common) |
| pulseLength | number | 350 | Pulse width in µs |
| repeat | number | 10 | Transmission repeat count |
Initializes the OV2640/OV5640 camera and captures a single JPEG frame into memory.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Capture a frame |
| done | OUT → | trigger | Frame captured successfully |
| imagedata | OUT → | any | BHFrame* pointer — connect to SD write or custom_code |
| width | OUT → | number | Frame width in pixels |
| height | OUT → | number | Frame height in pixels |
| error | OUT → | error | Camera not ready or capture failed |
| Key | Widget | Default | Description |
|---|---|---|---|
| resolution | select | QVGA (320x240) | QQVGA | QVGA | VGA | SVGA | XGA | UXGA |
| format | select | JPEG | JPEG | RGB565 | Grayscale |
| quality | number | 12 | JPEG quality 4–63 (lower = smaller file) |
| hmirror | toggle | false | Flip image horizontally |
| vflip | toggle | false | Flip image vertically |
- Cannot be used in the same flow as cam_web — both call bh_cam_init() and will conflict
- Use camera_capture to save frames to SD card
- Use cam_web to serve frames over HTTP to a browser
- PSRAM required for VGA and above — ESP32-CAM has PSRAM, plain ESP32 does not
Connectivity
WiFi, web server, HTTP, MQTT
Connects to an existing WiFi access point (STA mode). Shows progress on OLED.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Start connecting |
| connected | OUT → | ok | Successfully connected |
| ip | OUT → | string | Assigned IP address string |
| failed | OUT → | error | Connection failed or timed out |
| timeout | OUT → | neutral | Timeout elapsed without connecting |
| Key | Widget | Default | Description |
|---|---|---|---|
| ssid | text | — | WiFi network name |
| password | text | — | WiFi password |
| timeout | number | 10000 | Connection timeout in ms |
- Cannot be used in the same flow as wifi_ap — choose STA or AP mode
- Both failed and timeout fire on any connection failure — wire both or just failed
Creates a WiFi hotspot. Phones and laptops can connect directly — no router needed. Shows SSID and IP on OLED.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Start the access point |
| ready | OUT → | ok | AP is up and accepting connections |
| ip | OUT → | string | Gateway IP (always 192.168.4.1) |
| Key | Widget | Default | Description |
|---|---|---|---|
| ssid | text | BLUEHASH | Hotspot name visible to devices |
| password | text | Password — leave empty for open network | |
| channel | number | 1 | WiFi channel 1–13 |
- Gateway is always 192.168.4.1 — users open this in their browser
- Open network (no password) is simplest for demos and local use
- Always wire ready → web_server to start the HTTP server
Starts an HTTP server on port 80. Must be running for any browser requests to be served. Runs handleClient() every loop.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Start the web server |
| started | OUT → | ok | Server is live and accepting connections |
| Key | Widget | Default | Description |
|---|---|---|---|
| port | number | 80 | HTTP port (80 = no port needed in browser URL) |
- html_page, web_button, cam_web register their routes during setup() — before web_server starts. Routes are queued and applied automatically when the server starts.
- Keep port 80 — any other port requires users to type 192.168.4.1:PORT
- tick() runs every loop iteration automatically
Serves a fully styled HTML page at "/". The page includes a dark theme, button styling, and JS helper functions. No wiring needed — presence in the flow is enough.
| Key | Widget | Default | Description |
|---|---|---|---|
| title | text | BLUEHASH | Browser tab title |
| body | textarea | HTML for the <body> tag. Use single quotes in onclick to avoid escaping. |
- No edges needed — the node registers GET "/" in setup() automatically
- Use single quotes inside onclick attributes: onclick='bh_img("/snap")'
- Built-in JS: bh_call(url) fetches a route and shows response in <div id="out">
- Built-in JS: bh_img(url) fetches a route and shows result in <img id="preview">
- Built-in JS: bh_poll(url, ms) auto-refreshes <img id="preview"> every ms milliseconds
Initializes the camera and serves JPEG snapshots at /snap. Each browser request captures a fresh frame.
| Name | Dir | Type | Description |
|---|---|---|---|
| ready | OUT → | ok | Camera initialized and /snap is live |
| error | OUT → | error | Camera initialization failed |
| Key | Widget | Default | Description |
|---|---|---|---|
| resolution | select | QVGA (320x240) | QQVGA (160×120) | QVGA (320×240) | VGA (640×480) | SVGA (800×600) |
| quality | number | 12 | JPEG quality 4–63 (lower = smaller, faster) |
- No edges needed to register /snap — just add the node
- Cannot be in the same flow as camera_capture — both call bh_cam_init() and will conflict
- In html_page body: <img id="preview"> + bh_img("/snap") shows a snapshot on demand
- For live view: bh_poll("/snap", 800) auto-refreshes every 800ms
- QVGA is best for ESP32-CAM — fast capture, good quality, loads quickly on phone
Makes a blocking HTTP GET request to a URL and returns the response body.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Make the request |
| success | OUT → | ok | Request completed with 2xx status |
| body | OUT → | string | Response body text |
| statuscode | OUT → | number | HTTP status code |
| error | OUT → | error | Connection failed or negative code |
| Key | Widget | Default | Description |
|---|---|---|---|
| url | text | — | Full URL including https:// |
| timeout | number | 5000 | Request timeout in ms |
- Requires wifi_connect first
- Blocks the loop for the duration of the request
Makes a blocking HTTP POST request with a body.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Send the request |
| body | ← IN | string | Request body — triggers send automatically when wired |
| success | OUT → | ok | Request succeeded |
| response | OUT → | string | Response body text |
| error | OUT → | error | Request failed |
| Key | Widget | Default | Description |
|---|---|---|---|
| url | text | — | Endpoint URL |
| contentType | select | application/json | Content-Type header |
| timeout | number | 5000 | Timeout in ms |
Publishes a message to an MQTT broker topic.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Publish the message |
| payload | ← IN | string | Message payload — triggers publish when wired |
| sent | OUT → | ok | Message published |
| failed | OUT → | error | Broker unreachable or publish failed |
| Key | Widget | Default | Description |
|---|---|---|---|
| broker | text | — | Broker IP or hostname |
| port | number | 1883 | MQTT port |
| topic | text | — | Topic to publish to e.g. bluehash/sensor |
| retain | toggle | false | Retain message on broker |
Data / Logic
Variables, math, storage, scaling
Stores a string value in memory and passes it downstream.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Output the stored value |
| value | ← IN | string | New value to store — triggers output automatically |
| done | OUT → | trigger | Fires after outputting |
| value | OUT → | string | Current stored value |
| Key | Widget | Default | Description |
|---|---|---|---|
| value | text | — | Initial/default value |
Applies an arithmetic operator to two float values (A op B).
| Name | Dir | Type | Description |
|---|---|---|---|
| run | ← IN | trigger | Calculate and output result |
| a | ← IN | number | Left operand |
| b | ← IN | number | Right operand |
| result | OUT → | number | Calculation result |
| divbyzero | OUT → | error | Fires on division by zero |
| Key | Widget | Default | Description |
|---|---|---|---|
| operator | select | — | + − × ÷ % ** abs min max round floor ceil |
Linearly maps a value from one range to another and clamps the output.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Map the current value |
| value | ← IN | number | Value to map — triggers automatically when wired |
| mapped | OUT → | number | Mapped output value |
| Key | Widget | Default | Description |
|---|---|---|---|
| inMin | number | 0 | Input range minimum |
| inMax | number | 4095 | Input range maximum |
| outMin | number | 0 | Output range minimum |
| outMax | number | 100 | Output range maximum |
Saves a string value to non-volatile flash storage. Survives reboots and deep sleep.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Save the value |
| value | ← IN | string | Value to save — triggers save automatically |
| saved | OUT → | ok | Value saved |
| Key | Widget | Default | Description |
|---|---|---|---|
| namespace | text | bluehash | NVS namespace (max 15 chars) |
| key | text | — | Key name (max 15 chars) |
- NVS has limited write cycles (~100,000) — do not write on every timer tick
Reads a value from non-volatile flash storage.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Read the value |
| value | OUT → | string | Retrieved value or default |
| found | OUT → | ok | Key existed in storage |
| useddefault | OUT → | neutral | Key not found — default returned |
| Key | Widget | Default | Description |
|---|---|---|---|
| namespace | text | bluehash | NVS namespace |
| key | text | — | Key name |
| default | text | — | Value to return if key not found |
System
Sleep, restart, logging, custom code
Puts the ESP32 into deep sleep, consuming ~10µA. Wakes on timer or external pin.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Enter deep sleep |
| Key | Widget | Default | Description |
|---|---|---|---|
| duration | number | 0 | Wake-up time in ms (0 = no timer wake, use external pin) |
- RAM contents are lost — save state to NVS before sleeping
- Wakes via start node — connect start → wake for post-sleep logic
Reboots the ESP32 after an optional delay.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Restart the device |
| Key | Widget | Default | Description |
|---|---|---|---|
| delay | number | 500 | Delay before restart in ms |
Plays a tone on the buzzer pin.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Play the tone |
| done | OUT → | trigger | Fires after tone completes |
| Key | Widget | Default | Description |
|---|---|---|---|
| pin | pin | — | Buzzer pin |
| freq | number | 1000 | Tone frequency in Hz |
| duration | number | 200 | Tone duration in ms |
Prints a message to Serial monitor at the configured log level.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Print the message |
| done | OUT → | trigger | Fires after printing |
| Key | Widget | Default | Description |
|---|---|---|---|
| message | text | — | Message to print |
| level | select | INFO | INFO | WARN | ERROR | DEBUG |
Runs arbitrary C++ code inside the generated flow. Full access to bh_hal.h API.
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Execute the code |
| done | OUT → | trigger | Fires after execution |
| returnval | OUT → | number | Float return value (call node_xxx_output_returnval(v)) |
| error | OUT → | error | Error path (call node_xxx_output_error()) |
| Key | Widget | Default | Description |
|---|---|---|---|
| code | code | — | C++ code to run. Use bh_* functions from the HAL. |
- Code runs inside a void function — use return to exit early
- bh_hal.h is already included — call bh_display_print(), bh_led(), etc. directly
Launches a community program from the library. The program runs until it calls bh_program_exit().
| Name | Dir | Type | Description |
|---|---|---|---|
| trigger | ← IN | trigger | Launch the program |
| done | OUT → | trigger | Fires when program exits cleanly |
| returnval | OUT → | number | Value returned by the program |
| error | OUT → | error | Program not found or crashed |
| Key | Widget | Default | Description |
|---|---|---|---|
| programId | text | — | Library program ID (from the community library) |
- Programs are embedded at compile time — the program ID must exist in the compiler's library
- Long-pressing BACK during a program triggers bh_exit_requested() which programs must handle
Web panel recipes
Common patterns combining wifi_ap + web_server + html_page + cam_web + web_button.
Phone connects, taps a button, sees a fresh JPEG.
Image refreshes automatically every 800ms — no button needed.
Two buttons control the LED via browser. No camera.
Click to read a sensor and see the value in the browser.