An expression is the text in a field of the Telemetry mapping group of the sensor dialog. It tells the app how to compute that field from each message the device sends. This page lists everything an expression may contain. For what expressions are for and when to use one, see Sensor expressions.

| Part | Write it as | Example |
|---|---|---|
| Protocol parameter | The parameter key in braces | {in4} |
| Another sensor's field | Sensor name and field key in square brackets, joined by # |
[Ignition#v] |
| Number | Digits, with a dot for decimals | 37.5 |
| Arithmetic | + - * / % |
{ext_volt} / 1000 |
| Comparison | == != > >= < <= |
{status} > 0 |
| Logic | && (and), \|\| (or), ! (not) |
{speed} < 4 && {in4} == 1 |
| Choice | test ? value if true : value if false |
{status} > 0 ? 1 : 0 |
| Grouping | Round brackets | ({adc} * 15) - 37.5 |
Spaces between parts are optional. The app refuses anything else, such as words, units, quotes or other brackets.
A parameter reference reads one value from the message the device sent. Write the parameter key exactly as the device's protocol names it, inside braces: {fuel_lvl}, {can_rpm}.
{loc.coordinates.0} gives no value.input_status_X or trouble_codes_codeN. Any number in its place counts: {input_status_3} is a known key. When the entry's description gives an ID range, the number must fall inside it.fuel_lvl * 0.85 and accepts {fuel_lvl} * 0.85.A sensor reference reads the final value of a field of another sensor on the same device. Write the sensor's name and the field key, joined by #, inside square brackets: [Engine RPM#rpm].
[Ignition] alone reads nothing.[Fuel tank#level] gives litres, not raw probe counts.A parameter value is a number, text or true/false, depending on how the device's protocol sends it.
>, >=, < and <= need numbers on both sides. Text or a missing value there gives the field no value for that message.+ is the exception for text: it joins the two sides as text. If the device sends level as the text "12", {level} + 1 gives 121, not 13. Use a parameter that arrives as a number.== and != compare anything, but text never equals a number: {in4} == 1 is false when the device sends the text "1".{in4} passes the value through unchanged. The app reads numeric text such as "1" as a number later, so a bare reference works with text values too.0.85, not .85, 0,85 or 8.5e-1. Write a negative number with a minus sign: -37.5.The app works out an expression in this order, from first to last. Brackets change the order.
| Order | Operators | Meaning |
|---|---|---|
| 1 | -x, !x |
Negative value; not |
| 2 | *, /, % |
Multiply, divide, remainder after division |
| 3 | +, - |
Add, subtract |
| 4 | >, >=, <, <=, ==, != |
Compare two values; the result is true or false |
| 5 | && |
True when both sides are true |
| 6 | \|\| |
True when either side is true |
| 7 | ? : |
Choose between two values |
Write one comparison at a time: 0 < {x} < 10 does not work. Join comparisons with && instead: {x} > 0 && {x} < 10.
A conditional expression picks one of two values with test ? value if true : value if false. Chain them to pick from several: {x} > 100 ? 2 : ({x} > 50 ? 1 : 0).
? gives no value.<test> ? 1 : 0 to turn it into 1 or 0.Expressions have no functions. The fields accept convertToInt({param}, base), and the hint even shows a value for it. The server that computes sensor values has no such function, though. A field that uses it gets no value. Use a parameter that already arrives as a number, or ask the device's installer to change what the device sends.
The hint under the field checks the expression as you type. Save in the sensor dialog stays off while a mapped field is not valid.
| Hint or message | Meaning | Fix |
|---|---|---|
| "A bracket is still open — close it before this can be saved" | A {, [ or ( has no closing partner. |
Close the bracket. |
| "{parameter} is not in this device's protocol" | The key in braces is not one of the device's parameters, and the device's last message does not have it either. | Use the key the hint suggests after "Closest match", or pick one with Choose. |
| "This device has no [Sensor#field] — check the sensor name and field." | No sensor on this device has that name and field. | Correct the name or field key. |
| "Invalid expression: consecutive operators are not allowed." | Two operators in a row, such as * /. |
Remove one. |
| "Expression contains unrecognized identifiers." | A word outside braces or brackets. | Put parameter keys in braces. |
| "Leading + or - with a single variable reference is not allowed." | The whole field is -{x} or +[A#b]. |
Write 0 - {x}. |
| "This returns true or false, so the sensor reports nothing. Wrap it as: ? 1 : 0" | The result is a comparison, not a number. | Add ? 1 : 0. |
| "A condition over constants never evaluates. Reference a protocol parameter or another sensor." | The test uses only numbers. | Reference a parameter or a sensor. |
| "Valid expression · returns number · = 12.6" | The expression is valid. The number is what the device's last message gives. | — |
If an expression gets past the hint, the server checks it again on Save. For the cases above it refuses the sensor with "Expression must return a number", "Condition references nothing" or "Calibration table already set". It shows "The {field} filed value is invalid in {name}" for most other syntax errors. The last one also covers ^, **, ?? and square brackets without #, which the server does not accept.
The app computes every mapped field again for each message. A field has no value for a message, and the unit card and reports show nothing for it, when:
>, >=, <, <= is missing from the message or arrives as text.? : is not true or false.== and !=.convertToInt.A missing parameter inside == or != is different: the comparison is false, so a condition takes its "false" branch. {in4} == 1 ? 1 : 0 gives 0 when the message has no in4, but {in4} > 0 ? 1 : 0 gives no value.
A missing value in the branch a test picks also falls through to the else value. {in4} == 1 ? {fuel_lvl} : 0 gives 0 when the ignition is on and the message has no fuel_lvl.
If two sensors read each other, the app cannot order them and computes no sensor of that device. The hint blocks this when you edit in the app; check sensor files you upload.
These expressions run on the demo devices.
| Sensor | Field expression | What it does |
|---|---|---|
| Ignition | {in4} |
Passes digital input 4 through: 1 with the ignition on, 0 with it off. |
| Cargo door | {status}>0 ? 1 : 0 |
Turns any status above 0 into 1 (open) and everything else into 0 (closed). The sensor shows its own on and off texts for 1 and 0. |
| Cargo temperature | ({adc}*15)-37.5 |
Scales the analogue input into °C: a reading of 2.5 gives 0 °C, and 3 gives 7.5 °C. |
| Idling | [Ignition#v]==1 ? ([Engine RPM#rpm]<900 ? 1 : 0) : 0 |
Reads two other sensors: 1 when the ignition is on and the engine runs under 900 rpm, otherwise 0. |
| Power supply | {ext_volt} |
Passes the external voltage through. If a device reports millivolts, write {ext_volt} / 1000. |
[Engine RPM#rpm] > 300 ? {in4} : 0.