LEGOS Firmware
This is the firmware for the LEGOS entities. It is written in Rust and builds on the Embassy async framework.
Installation
-
Install the Rust toolchain via rustup
-
Install
espupviacargo:cargo install espup -
Install the esp Rust toolchain:
espup install # Source the environment variables . $HOME/export-esp.sh -
Install
espflash:cargo install espflash -
Manipulate the configuration of e.g., WiFi in legos_cfg.toml
-
Build & flash the firmware:
# go to the entity directory cd substation # Build and flash cargo run --release -
Optional: modify espflash.toml to configure the flasher
Functional description
MQTT
Topic
JSON payload scheme
Development notes
Documentation can be found here: https://acs.pages.rwth-aachen.de/public/teaching/legos/legos-rs
There are three main data structures for most entities:
- A status struct (e.g.,
SubstationState). This is periodically updated with the sensor readings and is usually published via MQTT. - An external override struct (e.g.,
SubstationExternalControl). This is created by MQTT or HTTP and is used to override settings. This struct is passed to contoling task via a channel (CONTROL_CHANNEL). - A control structure (e.g.,
Substation). This contains the handles to all peripherals and controls them.
There is usually one task (run()), that owns and manages the control structure.
Callbacks for MQTT and HTTP
The init_legos function initialises a webserver and an MQTT client.
To interact with the entity, four callbacks should be passed as mqtt_config and http_config.
- MQTT publication callback: The MQTT client calls this function at the specified interval. The callback returns an optional String that contains the message to be published at the configured topic. If
Noneis returned, nothing will be published. - MQTT subscription callback: If specified, this function is called on every message for the specified topic. The content of the message is passed as an argument.
- HTTP status callback: The Javascript on the webpages triggers the user browsers to regularly do a
GETrequest onentity.local/status. If configured, this calls the callback to provide a string that is passed back to the Javascript, to display the current status on the webpage.
Static items
Often, 'static lifetimes are needed, for example when passing references to strings to a task. The suggested way to solve this is via a static_cell. A "global" static cell is declared and initialized at runtime. This produces a &'static that can be used for passing arguments to tasks:
Example:
static MQTT_TOPIC_PUB: StaticCell<String<64>> = StaticCell::new();
#[main]
async fn main(spawner: Spawner) {
// ...
let topic_pub = MQTT_TOPIC_PUB.init({
let mut s = String::<64>::new();
write!(s, "{}/substation", "mytopic").unwrap();
s
});
// topic_pub is now a &'static to MQTT_TOPIC_PUB
fn_that_requires_static(topic_pub);
// ...
}