#BLUEHASH
← Docs

Reference

Node Reference

Every node available in the BLUEHASH editor — ports, config fields, notes, and examples.

triggerstringnumberfloatbooleanokerrorneutralany

Flow control

Sequencing, timing, branching, and repeating

start
Start

Entry point of every flow. Fires boot on power-up. Every flow must have exactly one Start node.

Ports
NameDirTypeDescription
bootOUT →triggerFires once when the device powers on
wakeOUT →triggerFires when waking from deep sleep
Notes
  • Every flow must have exactly one Start node
  • boot and wake are separate — use wake to restore state after deep sleep
end
End

Terminates a flow branch. Has no effect at runtime — used as a visual terminator.

Ports
NameDirTypeDescription
trigger← INtriggerReceives any trigger and does nothing
delay
Delay

Blocks execution for a fixed duration, then fires done.

Ports
NameDirTypeDescription
trigger← INtriggerStart the delay
doneOUT →triggerFires after duration has elapsed
Config
KeyWidgetDefaultDescription
durationnumber1000Wait time in milliseconds
Example
// Blink: wait 500ms between on/off [digital_write HIGH] → done → [delay 500] → done → [digital_write LOW]
timer
Timer

Fires tick on a repeating interval. Non-blocking — runs alongside other nodes each loop iteration.

Ports
NameDirTypeDescription
start← INtriggerEnable the timer
stop← INtriggerDisable the timer
tickOUT →triggerFires every interval ms
Config
KeyWidgetDefaultDescription
intervalnumber1000Time between ticks in milliseconds
autoStarttoggletrueStart ticking immediately on boot without needing a start trigger
Notes
  • 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
Example
// Read sensor every 2 seconds [timer interval=2000] → tick → [analog_read]
condition
Condition

Compares two float values (A op B) and routes flow to true or false output.

Ports
NameDirTypeDescription
run← INtriggerEvaluate the condition
a← INnumberLeft-hand value
b← INnumberRight-hand value
trueOUT →triggerFires when condition is true
falseOUT →triggerFires when condition is false
Config
KeyWidgetDefaultDescription
operatorselect==Comparison: ==, !=, >, <, >=, <=
Example
// Alert if voltage > 3.0V [analog_read] → voltage → [condition A>3.0] → true → [notification "High voltage"]
loop
Loop

Fires each(index) N times in sequence, then fires done.

Ports
NameDirTypeDescription
run← INtriggerStart the loop
eachOUT →numberFires for each iteration with current index (0-based)
doneOUT →triggerFires after all iterations complete
Config
KeyWidgetDefaultDescription
countnumber10Number of iterations
Notes
  • 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

show_text
Show text

Prints static text at a pixel position on the OLED. Optionally clears the screen first.

Ports
NameDirTypeDescription
trigger← INtriggerDraw the text
doneOUT →triggerFires after text is drawn
Config
KeyWidgetDefaultDescription
texttextText to display
xnumber0X pixel position (0 = left edge)
ynumber0Y pixel position (0 = top edge)
fontSizeselectsmallsmall (5×7) | medium (8×8) | large (10×20)
cleartogglefalseClear screen before drawing
Example
// Show IP on boot [wifi_connect] → ip → [show_text "IP: " x=0 y=0]
show_value
Show value

Displays a labelled numeric value in large font. Good for live sensor readings.

Ports
NameDirTypeDescription
trigger← INtriggerRefresh the display with the current value
value← INnumberFloat value to display — triggers refresh automatically
doneOUT →triggerFires after display is updated
Config
KeyWidgetDefaultDescription
labeltextValueLabel shown above the value
unittextUnit suffix shown after the number (e.g. °C, V, %)
decimalsnumber1Decimal places to display
Example
// Live temperature display [timer 2s] → tick → [analog_read] → voltage → [show_value label="Temp" unit="V"]
progress_bar
Progress bar

Draws a progress bar 0–100%. Fires full when value reaches 100.

Ports
NameDirTypeDescription
trigger← INtriggerRefresh the bar
value← INnumber0–100 — triggers refresh automatically when wired
doneOUT →triggerFires after drawing
fullOUT →triggerFires when value reaches 100
Config
KeyWidgetDefaultDescription
labeltextLabel shown above the bar
heightnumber8Bar height in pixels
notification
Notification

Shows a popup message and waits for OK/Back or timeout. Blocking.

Ports
NameDirTypeDescription
trigger← INtriggerShow the notification
okOUT →triggerFires when user presses OK
dismissedOUT →triggerFires when user presses Back or timeout expires
Config
KeyWidgetDefaultDescription
messagetextMessage text to display
durationnumber2000Auto-dismiss after ms (0 = wait forever)
clear_screen
Clear screen

Blanks the OLED.

Ports
NameDirTypeDescription
trigger← INtriggerClear the screen
doneOUT →triggerFires after clearing
Config
KeyWidgetDefaultDescription
fillselectblackblack (blank) | white (all pixels on)

Input

Physical button presses

button_press
Button press

Detects physical button presses on board-defined pins and fires per-button outputs.

Ports
NameDirTypeDescription
okOUT →triggerOK button pressed
backOUT →triggerBack button pressed
upOUT →triggerUp button pressed
downOUT →triggerDown button pressed
btn1OUT →triggerButton 1 pressed
btn2OUT →triggerButton 2 pressed
Config
KeyWidgetDefaultDescription
debouncenumber50Debounce window in ms
Notes
  • Pin assignments come from bh_hal.h — they are board-specific
  • Pins defined as -1 for a board are silently skipped
  • Do NOT use button_press alongside a menu node — menu reads buttons itself
  • ESP32-CAM only has OK (GPIO 13) and Back (GPIO 0 / IO0 button)

GPIO / Hardware

Digital, analog, PWM, IR, RF, camera

digital_write
Digital write

Sets a GPIO pin HIGH or LOW.

Ports
NameDirTypeDescription
trigger← INtriggerApply the pin state
doneOUT →triggerFires after pin is set
Config
KeyWidgetDefaultDescription
pinpinGPIO pin number
stateselectHIGHHIGH or LOW
digital_read
Digital read

Reads a GPIO pin and fires high or low output.

Ports
NameDirTypeDescription
trigger← INtriggerPerform the read
stateOUT →booleanCurrent pin state (true=HIGH)
highOUT →triggerFires if pin is HIGH
lowOUT →triggerFires if pin is LOW
Config
KeyWidgetDefaultDescription
pinpinGPIO pin number
pullModeselectINPUT_PULLUPINPUT | INPUT_PULLUP | INPUT_PULLDOWN
analog_read
Analog read

Reads an ADC pin. Returns both raw 12-bit value (0–4095) and converted voltage (0–3.3V).

Ports
NameDirTypeDescription
trigger← INtriggerPerform the read
rawOUT →number0–4095 ADC value
voltageOUT →number0.0–3.3 calculated voltage
Config
KeyWidgetDefaultDescription
pinpinADC-capable GPIO pin
Notes
  • Uses 11dB attenuation — full 0–3.3V range
  • ESP32 ADC is non-linear near 0V and 3.3V — use map_scale for calibration
pwm_output
PWM output

Outputs a PWM signal — for LED dimming, motor speed, servo control.

Ports
NameDirTypeDescription
trigger← INtriggerApply PWM settings
doneOUT →triggerFires after PWM is applied
Config
KeyWidgetDefaultDescription
pinpinPWM-capable GPIO pin
dutynumber128Duty cycle 0–255
freqnumber1000Frequency in Hz
pin_trigger
Pin trigger

Fires on GPIO edge via hardware interrupt. Non-blocking — works alongside everything else.

Ports
NameDirTypeDescription
risingOUT →triggerFires on LOW→HIGH edge
fallingOUT →triggerFires on HIGH→LOW edge
changeOUT →triggerFires on any edge
stateOUT →booleanCurrent pin state
Config
KeyWidgetDefaultDescription
pinpinGPIO pin to watch
modeselectCHANGERISING | FALLING | CHANGE
Notes
  • Uses hardware interrupt (IRAM_ATTR) — minimal latency
  • Sets INPUT_PULLUP automatically
ir_receive
IR receive

Decodes IR signals from any remote. Outputs the protocol name and raw code.

Ports
NameDirTypeDescription
receivedOUT →triggerFires when a valid IR code is received
protocolOUT →stringProtocol name e.g. NEC, SONY, RC5
codeOUT →numberRaw decoded IR code
Config
KeyWidgetDefaultDescription
pinpinIR receiver data pin (connect to TSOP382x)
timeoutnumber15Gap timeout in ms — default suits most remotes
Notes
  • Supports NEC, SONY, RC5, RC6, Samsung, LG, and 20+ more protocols via IRremoteESP8266
ir_send
IR send

Transmits an IR code at 38kHz carrier.

Ports
NameDirTypeDescription
trigger← INtriggerSend the IR code
sentOUT →triggerFires after transmission completes
Config
KeyWidgetDefaultDescription
pinpinIR LED pin (via NPN transistor for range)
protocolselectNECNEC | SONY | RC5 | RC6 | Samsung | LG
codetextIR code in hex e.g. 0x20DF10EF
bitsnumber32Code bit length
rf_receive
RF receive

Receives 433MHz RF codes from remote controls and switches.

Ports
NameDirTypeDescription
receivedOUT →triggerValid code received
codeOUT →numberDecoded RF code
protocolOUT →numberRF protocol number
bitlengthOUT →numberCode bit length
Config
KeyWidgetDefaultDescription
pinpinRF receiver DATA pin
rf_send
RF send

Transmits a 433MHz RF code — controls remote sockets, gates, alarm panels.

Ports
NameDirTypeDescription
trigger← INtriggerTransmit the code
sentOUT →triggerFires after transmission
Config
KeyWidgetDefaultDescription
pinpinRF transmitter DATA pin
codenumberRF code to send
bitLengthnumber24Code bit length
protocolnumber1RF protocol (1 is most common)
pulseLengthnumber350Pulse width in µs
repeatnumber10Transmission repeat count
camera_capture
Camera capture

Initializes the OV2640/OV5640 camera and captures a single JPEG frame into memory.

Ports
NameDirTypeDescription
trigger← INtriggerCapture a frame
doneOUT →triggerFrame captured successfully
imagedataOUT →anyBHFrame* pointer — connect to SD write or custom_code
widthOUT →numberFrame width in pixels
heightOUT →numberFrame height in pixels
errorOUT →errorCamera not ready or capture failed
Config
KeyWidgetDefaultDescription
resolutionselectQVGA (320x240)QQVGA | QVGA | VGA | SVGA | XGA | UXGA
formatselectJPEGJPEG | RGB565 | Grayscale
qualitynumber12JPEG quality 4–63 (lower = smaller file)
hmirrortogglefalseFlip image horizontally
vfliptogglefalseFlip image vertically
Notes
  • 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

wifi_connect
WiFi connect

Connects to an existing WiFi access point (STA mode). Shows progress on OLED.

Ports
NameDirTypeDescription
trigger← INtriggerStart connecting
connectedOUT →okSuccessfully connected
ipOUT →stringAssigned IP address string
failedOUT →errorConnection failed or timed out
timeoutOUT →neutralTimeout elapsed without connecting
Config
KeyWidgetDefaultDescription
ssidtextWiFi network name
passwordtextWiFi password
timeoutnumber10000Connection timeout in ms
Notes
  • 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
wifi_ap
WiFi AP

Creates a WiFi hotspot. Phones and laptops can connect directly — no router needed. Shows SSID and IP on OLED.

Ports
NameDirTypeDescription
trigger← INtriggerStart the access point
readyOUT →okAP is up and accepting connections
ipOUT →stringGateway IP (always 192.168.4.1)
Config
KeyWidgetDefaultDescription
ssidtextBLUEHASHHotspot name visible to devices
passwordtextPassword — leave empty for open network
channelnumber1WiFi channel 1–13
Notes
  • 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
Example
[start] → boot → [wifi_ap "BLUEHASH"] → ready → [web_server port=80]
web_server
Web server

Starts an HTTP server on port 80. Must be running for any browser requests to be served. Runs handleClient() every loop.

Ports
NameDirTypeDescription
trigger← INtriggerStart the web server
startedOUT →okServer is live and accepting connections
Config
KeyWidgetDefaultDescription
portnumber80HTTP port (80 = no port needed in browser URL)
Notes
  • 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
Example
[wifi_ap] → ready → [web_server] → started → [show_text "Server live"]
html_page
HTML page

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.

Config
KeyWidgetDefaultDescription
titletextBLUEHASHBrowser tab title
bodytextareaHTML for the <body> tag. Use single quotes in onclick to avoid escaping.
Notes
  • 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
Example
// Snap button + live preview <button onclick='bh_img("/snap")'>📷 Capture</button> <img id='preview'> // Auto-refresh every 800ms (live view) <script>bh_poll("/snap", 800)</script> <img id='preview'> // Button triggers flow + shows result text <button onclick='bh_call("/btn/0")'>Read sensor</button> <div id='out'></div>
web_button
Web button

Registers GET /btn/{id} on the web server. When a browser calls that URL, fires the pressed output on the next loop tick.

Ports
NameDirTypeDescription
pressedOUT →okFires when browser calls GET /btn/{btnId}
Config
KeyWidgetDefaultDescription
labeltextButtonFor reference only — not served automatically
btnIdtext0URL segment — registers route /btn/{btnId}
Notes
  • No edges needed to register the route — just add the node
  • Wire pressed to any flow node: cam_capture, ir_send, led toggle, etc.
  • Browser response is always 200 "ok" — show result by calling bh_img("/snap") separately
  • Use unique btnId for each web_button in the same flow: 0, 1, 2…
  • Cannot be used with cam_web if you also want camera — cam_web registers /snap, wire web_button.pressed to ir_send, led, etc.
Example
// HTML: two buttons <button onclick='bh_call("/btn/0")'>💡 LED ON</button> <button onclick='bh_call("/btn/1")'>💡 LED OFF</button> <div id='out'></div> // Flow wiring: [web_button id=0] → pressed → [digital_write pin=2 HIGH] [web_button id=1] → pressed → [digital_write pin=2 LOW]
cam_web
Camera → web

Initializes the camera and serves JPEG snapshots at /snap. Each browser request captures a fresh frame.

Ports
NameDirTypeDescription
readyOUT →okCamera initialized and /snap is live
errorOUT →errorCamera initialization failed
Config
KeyWidgetDefaultDescription
resolutionselectQVGA (320x240)QQVGA (160×120) | QVGA (320×240) | VGA (640×480) | SVGA (800×600)
qualitynumber12JPEG quality 4–63 (lower = smaller, faster)
Notes
  • 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
Example
// Minimal camera web flow: Nodes: start → wifi_ap → web_server html_page (body below) cam_web // html_page body: <h1>Camera</h1> <button onclick='bh_img("/snap")'>📷 Snap</button> <img id='preview'>
http_get
HTTP GET

Makes a blocking HTTP GET request to a URL and returns the response body.

Ports
NameDirTypeDescription
trigger← INtriggerMake the request
successOUT →okRequest completed with 2xx status
bodyOUT →stringResponse body text
statuscodeOUT →numberHTTP status code
errorOUT →errorConnection failed or negative code
Config
KeyWidgetDefaultDescription
urltextFull URL including https://
timeoutnumber5000Request timeout in ms
Notes
  • Requires wifi_connect first
  • Blocks the loop for the duration of the request
http_post
HTTP POST

Makes a blocking HTTP POST request with a body.

Ports
NameDirTypeDescription
trigger← INtriggerSend the request
body← INstringRequest body — triggers send automatically when wired
successOUT →okRequest succeeded
responseOUT →stringResponse body text
errorOUT →errorRequest failed
Config
KeyWidgetDefaultDescription
urltextEndpoint URL
contentTypeselectapplication/jsonContent-Type header
timeoutnumber5000Timeout in ms
mqtt_publish
MQTT publish

Publishes a message to an MQTT broker topic.

Ports
NameDirTypeDescription
trigger← INtriggerPublish the message
payload← INstringMessage payload — triggers publish when wired
sentOUT →okMessage published
failedOUT →errorBroker unreachable or publish failed
Config
KeyWidgetDefaultDescription
brokertextBroker IP or hostname
portnumber1883MQTT port
topictextTopic to publish to e.g. bluehash/sensor
retaintogglefalseRetain message on broker

Data / Logic

Variables, math, storage, scaling

set_variable
Set variable

Stores a string value in memory and passes it downstream.

Ports
NameDirTypeDescription
trigger← INtriggerOutput the stored value
value← INstringNew value to store — triggers output automatically
doneOUT →triggerFires after outputting
valueOUT →stringCurrent stored value
Config
KeyWidgetDefaultDescription
valuetextInitial/default value
math
Math

Applies an arithmetic operator to two float values (A op B).

Ports
NameDirTypeDescription
run← INtriggerCalculate and output result
a← INnumberLeft operand
b← INnumberRight operand
resultOUT →numberCalculation result
divbyzeroOUT →errorFires on division by zero
Config
KeyWidgetDefaultDescription
operatorselect+ − × ÷ % ** abs min max round floor ceil
map_scale
Map / scale

Linearly maps a value from one range to another and clamps the output.

Ports
NameDirTypeDescription
trigger← INtriggerMap the current value
value← INnumberValue to map — triggers automatically when wired
mappedOUT →numberMapped output value
Config
KeyWidgetDefaultDescription
inMinnumber0Input range minimum
inMaxnumber4095Input range maximum
outMinnumber0Output range minimum
outMaxnumber100Output range maximum
Example
// ADC → percentage [analog_read] → raw → [map_scale 0–4095 → 0–100] → mapped → [progress_bar]
nvs_store
NVS store

Saves a string value to non-volatile flash storage. Survives reboots and deep sleep.

Ports
NameDirTypeDescription
trigger← INtriggerSave the value
value← INstringValue to save — triggers save automatically
savedOUT →okValue saved
Config
KeyWidgetDefaultDescription
namespacetextbluehashNVS namespace (max 15 chars)
keytextKey name (max 15 chars)
Notes
  • NVS has limited write cycles (~100,000) — do not write on every timer tick
nvs_read
NVS read

Reads a value from non-volatile flash storage.

Ports
NameDirTypeDescription
trigger← INtriggerRead the value
valueOUT →stringRetrieved value or default
foundOUT →okKey existed in storage
useddefaultOUT →neutralKey not found — default returned
Config
KeyWidgetDefaultDescription
namespacetextbluehashNVS namespace
keytextKey name
defaulttextValue to return if key not found

System

Sleep, restart, logging, custom code

deep_sleep
Deep sleep

Puts the ESP32 into deep sleep, consuming ~10µA. Wakes on timer or external pin.

Ports
NameDirTypeDescription
trigger← INtriggerEnter deep sleep
Config
KeyWidgetDefaultDescription
durationnumber0Wake-up time in ms (0 = no timer wake, use external pin)
Notes
  • RAM contents are lost — save state to NVS before sleeping
  • Wakes via start node — connect start → wake for post-sleep logic
restart
Restart

Reboots the ESP32 after an optional delay.

Ports
NameDirTypeDescription
trigger← INtriggerRestart the device
Config
KeyWidgetDefaultDescription
delaynumber500Delay before restart in ms
beep
Beep

Plays a tone on the buzzer pin.

Ports
NameDirTypeDescription
trigger← INtriggerPlay the tone
doneOUT →triggerFires after tone completes
Config
KeyWidgetDefaultDescription
pinpinBuzzer pin
freqnumber1000Tone frequency in Hz
durationnumber200Tone duration in ms
log
Log

Prints a message to Serial monitor at the configured log level.

Ports
NameDirTypeDescription
trigger← INtriggerPrint the message
doneOUT →triggerFires after printing
Config
KeyWidgetDefaultDescription
messagetextMessage to print
levelselectINFOINFO | WARN | ERROR | DEBUG
custom_code
Custom code

Runs arbitrary C++ code inside the generated flow. Full access to bh_hal.h API.

Ports
NameDirTypeDescription
trigger← INtriggerExecute the code
doneOUT →triggerFires after execution
returnvalOUT →numberFloat return value (call node_xxx_output_returnval(v))
errorOUT →errorError path (call node_xxx_output_error())
Config
KeyWidgetDefaultDescription
codecodeC++ code to run. Use bh_* functions from the HAL.
Notes
  • 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
Example
// Flash LED 3 times bh_led_blink(3, 100, 100); bh_display_print_center(20, "Done!"); bh_display_show();
program
Program

Launches a community program from the library. The program runs until it calls bh_program_exit().

Ports
NameDirTypeDescription
trigger← INtriggerLaunch the program
doneOUT →triggerFires when program exits cleanly
returnvalOUT →numberValue returned by the program
errorOUT →errorProgram not found or crashed
Config
KeyWidgetDefaultDescription
programIdtextLibrary program ID (from the community library)
Notes
  • 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.

Camera snap on button click
wifi_apweb_serverhtml_pagecam_web

Phone connects, taps a button, sees a fresh JPEG.

// Edges: start→wifi_ap, wifi_ap.ready→web_server // html_page body: <h1>Camera</h1> <button onclick='bh_img("/snap")'>📷 Snap</button> <img id='preview'>
Live view (auto-refresh)
wifi_apweb_serverhtml_pagecam_web

Image refreshes automatically every 800ms — no button needed.

// html_page body: <h1>Live View</h1> <img id='preview'> <script>bh_poll("/snap", 800)</script>
Remote control panel
wifi_apweb_serverhtml_pageweb_button ×2

Two buttons control the LED via browser. No camera.

// web_button id=0 → pressed → digital_write HIGH // web_button id=1 → pressed → digital_write LOW // html_page body: <h1>LED Control</h1> <button onclick='bh_call("/btn/0")'>💡 ON</button> <button onclick='bh_call("/btn/1")'>💡 OFF</button> <div id='out'></div>
Sensor dashboard
wifi_apweb_serverhtml_pageweb_buttonanalog_read

Click to read a sensor and see the value in the browser.

// web_button id=0 → pressed → analog_read → (result shown via separate GET) // html_page body: <h1>Sensors</h1> <button onclick='bh_call("/btn/0")'>📊 Read ADC</button> <div id='out'>Press to read</div>