Variables and bindings
From a sensor channel to a number on the screen, and from a slider back into the firmware.
A variable is a value the firmware holds; a binding connects it to a widget. Together they are
how a reading on a sensor becomes a number on the screen, and how a slider on the screen becomes
a setpoint the firmware acts on. Both live in app_logic.cpp, which the generator
writes only when the project uses them.
Variables
The Data tab lists them. Each has:
| Field | Meaning |
|---|---|
| Name | How formulas, bindings and automations refer to it. Letters, digits and underscores. |
| Type | Float, Integer or Boolean. A Float becomes a float, an Integer an int32_t, a Boolean a bool. |
| Source | Where the value comes from - see below. |
| Constant | The value, when the source is Constant. |
| Formula | A conversion over raw. Empty means the raw value is used as it is. |
| Initial value | What it holds before the first reading, and what it keeps if the device is absent. |
| Unit | A label, shown in the preview drawer and available to bindings. |
| Decimals | How many the preview shows; Text bindings have their own format. |
| Min / Max, Clamp | The expected range, and whether to limit the value to it. |

The source menu is grouped: every readable channel of every device on the Devices tab, every analog input, every digital input, then Constant, and (set by events only) for a variable written by actions and bindings rather than read from hardware.
Constants
A constant is held at its value every refresh. It is the setpoint case - a target a chart can draw as a line, a threshold an automation compares against - and, with a formula over it, the way to express a value computed purely from other variables.
Formulas
A formula converts the raw reading. It is parsed, not evaluated: the editor shows the result against a sample value as you type, names the exact error if there is one, and the generator writes the same expression tree out as C. A malformed formula is reported before export, never emitted as broken code.
raw * 9 / 5 + 32
map(raw, 0, 4095, 0, 100)
constrain(round(raw / 10), 0, 99)
(temp_in - temp_out) * 1.8
delta(pressure) * 60
raw is the source value. Any other variable can be referenced by name. The
operators are + - * / % and parentheses, with comparisons < <= > >= ==
!= and && || ! for Boolean results.
| Function | Arguments | Meaning |
|---|---|---|
| abs, round, floor, ceil, sqrt, log, exp, sin, cos, tan | 1 | The usual, as the float C versions. |
| pow | 2 | x to the power y. |
| min, max | 2 | The smaller or larger. |
| map | 5 | map(x, in_min, in_max, out_min, out_max) - Arduino's map, in floating point. |
| constrain, clamp | 3 | constrain(x, lo, hi). |
| prev | 1 name | The named variable on the previous refresh. |
| delta | 1 name | Its change since the previous refresh. |
prev(name) and delta(name) take a variable name rather than a value:
what it held on the previous refresh, and how far it moved since. They are how a rate of change
is expressed without an automation. They cannot be applied to raw, and cannot be
used in an analog input's own formula, which runs inside hw_poll() before variables
exist.
Bindings
Select a widget and scroll the Inspector to Data bindings. A binding is either From a variable or From another control (the value of a slider driving a label directly, with no variable in between). Fields:
| Field | Meaning |
|---|---|
| Variable / Control | The source - a variable, or another widget and its property. |
| Drives | The property on this widget: Text, Value, Checked, Selected index, Hidden, Background colour, Text colour, Opacity, Push onto chart, Hue, Saturation. |
| Series | For Push onto chart: which of the chart's series receives the value. |
| Direction | Variable → widget, Widget → variable, or Both ways. Offered on controls only. |
| Prefix / Format / Postfix | For Text: a printf format such as %.1f, wrapped in fixed text. |
| Formula | A binding-level formula applied to the value on its way into the widget. |

Which properties are offered depends on the widget - Value for a slider, bar or arc; Checked for a switch or checkbox; Selected index for a dropdown or roller; Text for a label or button; Push onto chart for a chart; Hue and Saturation for a colorwheel; Hidden, colours and opacity for anything.
Direction
A binding on a control - slider, arc, switch, checkbox, dropdown, roller, spinbox, colorwheel, textarea - can run Variable → widget, Widget → variable or Both ways. A two-way binding is a setpoint: the firmware shows the variable, and touching the widget writes it back, where automations and other bindings see it on the next refresh.
One consequence to know about: a read-only binding overwrites what a finger did every
refresh, because bindings_apply() runs unconditionally. A slider that should stay
where the user put it wants Both ways. The preview behaves
identically, so you will see it before the device does.
Text formatting
For a Text binding the value is formatted with a printf format (%.1f by default
for a Float, %d for an Integer) between a prefix and a postfix:
Temp: + %.1f + °C. A Boolean bound to Text shows
1 or 0 unless a binding formula turns it into something else.
The refresh loop
app_poll() runs every pass through loop(). Every 100 ms it calls
vars_update(), which evaluates each variable's formula over the current cached reading
(saving the previous value first, so prev() and delta() work), then
bindings_apply(), which pushes every variable into every widget bound to it, and then
the automation checks. Hardware is read separately by
hw_poll() at each device's own interval, so the 100 ms tick is the rate at which
the screen updates, not the rate sensors are hammered.
Writing variables from events
The Set variable action, on a widget event or an automation, writes a variable in one of five ways: a fixed value, a formula, an amount added to it, a toggle for a Boolean, or the value of a widget. It marks the variable so bindings pick it up on the next refresh. Between a two-way binding and this action, most control logic needs no hand-written code at all.
Validation
The validator errors on two variables with one name, a formula that does not parse, a formula referencing a name that does not exist, a variable pointing at a peripheral that was removed, a binding with no control or variable selected, a binding to a variable that was removed, a chart binding onto a series that does not exist, and a widget bound to itself. Each message names the widget or variable.