Controls - mitra42/frugal-iot GitHub Wiki
WORK IN PROGRESS
Frugal IoT - Controls
Frugal IoT intrinsically deals with the idea of a Control, an module that has inputs, wired to sensors, performs some calculations, and sets its outputs which are wired to actuators.
Some simple controls are included in the library, and in the examples, such as the Sonoff, allowing - without programming - to, for example deploy one device with a temperature sensor, and a Sonoff controlling a fan.
Its relatively easy to build your own control logic and include in an app, whether that is controlling, or sensing on the same device or on others.
There are different levels of engagement with Controls, depending on whether you want to use a control or write your own.
Using a Control
Some of the examples have Controls built in. For example if you have a Sonoff, or a SHT then examples/sonoff and examples/sht boith include a Control_Hysterisis (yes, this is miss-spelled, and awaiting a global fix across multiple repos! TODO)
To use this control, you can adjust the settings, so that for example when the value Now is greater than 25 then the Output turns on.
Hysteresis (or Hysterisis)
A switch that turns on above 25, and off below 25, is not very useful, as it will flicker on and off around the transition value. This is particularly true if for example, the output turns on a fan, that immediately drops the temperature below 57 and turns off.
In practice, you want Hysteresis, e.g. a switch that turns on at 27, and off at 23, which is how room thermostats etc work.
Wiring a Control
The power of the controls is that they can be wired to other devices.
Expand the Now and you'll see it is wired to this devices Temperature
Click on the current value, and you'll be offered a list of compatible sensors to connect to.
You can connect to other devices, so for example if you have both a SHT and a Sonoff in the same project,
you can connect the Out, to for example the Relay on the Sonoff.
Including an existing Control in an application
To include an existing control in an app, see examples/sht/sht.ino or examples/sonoff/sonoff.ino
// Create a new Control_Hysterisis, set its value, number of decimal points, min and max
Control_Hysterisis* cb = new Control_Hysterisis("Control_Hysterisis", "Control", 50, 1, 0, 100);
// Add it to the `controls` so it will get messages etc
frugal_iot.controls->add(cb);
It can either be wired to inputs or outputs in the app's code, or left to the user to change in the UI.
// Wire its output to the LED
cb->outputs[0]->wireTo(frugal_iot.messages->setPath("ledbuiltin/on"));
Note that the wired connections are remembered in the SPIFFS file system, and reloaded at boot, and also remembered on the MQTT server.
Developing your own Control Class
Lets say you want to build your own climate control system. With a bunch of sensors, and actuators (e.g. relay controlled fans & heaters). Including a number of Control_Hysteresis, and wiring them together, might not be sufficient or might add unwanted complexity. What is typically wanted is the ability to make decisions based on current, and possibly prior, data and then set outputs on this device on other.
Adding a custom Control class is the way to do this.
examples/climate
gives the basics.
In your main.cpp or climate.ino you'll see where the custom control is created, and wired to sensors and actuators.
Then in climate.cpp(https://github.com/mitra42/frugal-iot/blob/main/examples/climate/control_climate.cpp) you find the constructor
Control_Climate::Control_Climate(const char* const id, const char* const name,
float temp_setpoint, float temp_hysteresis,
float humidity_setpoint, float humidity_hysteresis)
which takes some standardized parameters (id, name) and custom parameters.
It then creates the inputs e.g.
new INfloat(id, "temperature_now", "Temperature", temp_setpoint, 1, -40, 80, "black", true),
and outputs e.g.
new OUTbool(id, "temperature_out", "Heating", false, "black", true),
The key function needed is act().
This function is called whenever any of the inputs have changed.
This is where any calculations are made, and can be as simple, or complex, as needed. Typically it will look at the inputs, make a decision, and set an output.
Usually, that is all that is needed, as the call to set will send the result wherever it is wired.
Adding to UX
If you have a user defined control on a device, then the UX will do its best to display it. At startup, your device sends some configuration data to the server, and that will be used for a best-effort to display this.
There are ways to improve this "best-effort" display.
In the frugal-iot-server repo's schema directory is the UX definition of each module (A control is a module).
Typically a control is defined in terms of some already existing UX elements, so for example the Control_Hysteresis is defined as
controlhysteresis:
name: Control
topics:
- leaf: now
leaf_from: controlfloat
name: Now
- leaf: greater
leaf_from: controlintoggle
name: Greater Than
- leaf: limit
leaf_from: controlfloat
name: Limit
- leaf: hysterisis
leaf_from: controlfloat
name: Hysterisis
max: 100
wireable: false
- leaf: out
leaf_from: controlouttoggle
name: Out
Notice how now is defined in terms of controlfloat which is defined in topics.yaml so controlfloat is defined as:
controlfloat:
#[leaf, name] should be overridden
min: 0
max: 100
type: float
display: text
color: black
wireable: true
rw: w
retain: true
In the modules.yaml file, we overrode the name and id of the field.
- leaf: now
leaf_from: controlfloat
name: Now
but we can also override other values such as its color, its range etc. Note that some of these values (such as min and max) are also defined on the device and will override the values in the UX.
If you have written your own control, feel free to submit a PR to add your control definition (or open an issue if you don't know how to submit a PR.
Controls and buttons (as in examples/sonoff)
TODO - write this
Time and Deep sleep for Controls
TODO - relationship to Deep Sleep TODO - relationship to time