Documentation / Hardware

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:

FieldMeaning
NameHow formulas, bindings and automations refer to it. Letters, digits and underscores.
TypeFloat, Integer or Boolean. A Float becomes a float, an Integer an int32_t, a Boolean a bool.
SourceWhere the value comes from - see below.
ConstantThe value, when the source is Constant.
FormulaA conversion over raw. Empty means the raw value is used as it is.
Initial valueWhat it holds before the first reading, and what it keeps if the device is absent.
UnitA label, shown in the preview drawer and available to bindings.
DecimalsHow many the preview shows; Text bindings have their own format.
Min / Max, ClampThe expected range, and whether to limit the value to it.
The Data tab with a variable reading a BME280 temperature channel through a formula, and a constant setpoint below it.
The Data tab with a variable reading a BME280 temperature channel through a formula, and a constant setpoint below 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.

FunctionArgumentsMeaning
abs, round, floor, ceil, sqrt, log, exp, sin, cos, tan1The usual, as the float C versions.
pow2x to the power y.
min, max2The smaller or larger.
map5map(x, in_min, in_max, out_min, out_max) - Arduino's map, in floating point.
constrain, clamp3constrain(x, lo, hi).
prev1 nameThe named variable on the previous refresh.
delta1 nameIts 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:

FieldMeaning
Variable / ControlThe source - a variable, or another widget and its property.
DrivesThe property on this widget: Text, Value, Checked, Selected index, Hidden, Background colour, Text colour, Opacity, Push onto chart, Hue, Saturation.
SeriesFor Push onto chart: which of the chart's series receives the value.
DirectionVariable → widget, Widget → variable, or Both ways. Offered on controls only.
Prefix / Format / PostfixFor Text: a printf format such as %.1f, wrapped in fixed text.
FormulaA binding-level formula applied to the value on its way into the widget.
Data bindings on a label: the temperature variable driving Text, with a prefix, a format and a postfix.
Data bindings on a label: the temperature variable driving Text, with a prefix, a format and a postfix.

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.