update: improve documentation and restructure code for modular hardware integration, add CAN communication to HAL, and update KiCad layouts

This commit is contained in:
2026-01-23 22:02:14 +01:00
parent 1de40085fb
commit 0c0b62e2ed
18 changed files with 486 additions and 219 deletions

View File

@@ -5,24 +5,47 @@ draft: false
description: "a description"
tags: ["firmeware", "upload"]
---
# Prebuild
1. Download image from
2. todo something espflash tool here
# From source
# From Source
## Preconditions
* rustup with current version
* espup with current version
* npm /npx
* Connect the board via usb to the computer
* Ensure the esp is running (eg not in deepsleep if prior version was installed)
# Compiling and uploading
1. Clone the gitea
2. go to the rust/src_webroot
3. npm -i
4. cd ..
5. cargo run
* **Rust:** Current version of `rustup`.
* **ESP32 Toolchain:** `espup` installed and configured.
* **espflash:** Installed via `cargo install espflash`.
* **Node.js:** `npm` installed (for the web interface).
Depending on the setup of the cargo runner, either a OTA image is build or the firmware is directly flashed to the esp.
## Flashing via USB
1. Connect the MainBoard to your computer via USB.
2. Ensure the board is powered and not in deep sleep.
3. Build the web interface (only required once or if you changed something in `src_webpack`):
```bash
cd Software/MainBoard/rust/src_webpack
npm install
npx webpack
cd ..
```
4. Flash the firmware:
```bash
espflash flash --monitor --partition-table partitions.csv
```
*Note: If the flashing fails, you might need to put the ESP32-C6 into bootloader mode by holding the BOOT button while resetting or connecting USB.*
Note: If the bootloader is currently booting from ota_2, flashing ota_1 via usb will not make it switch. You might need to manually erase it in this case prior.
### Simplified Flashing with Scripts
You can use the provided bash scripts to automate the build and flash process:
* **`./flash.sh`**: Cleans temporary files, builds the web interface, compiles the firmware, and flashes it to the board.
* **`./all.sh`**: Similar to `flash.sh`, but also saves a local `image.bin` of the firmware.
## OTA (Over-the-Air) Update
You can also update the firmware wirelessly if the system is already running and connected to your network.
1. Generate the OTA binary:
```bash
cargo build --release
```
2. The binary will be at `target/riscv32imac-unknown-none-elf/release/plant-ctrl2`.
3. Open the PlantCtrl web interface in your browser.
4. Navigate to the **OTA** section.
5. Upload the `plant-ctrl2` file.
6. The system will reboot into the new firmware once the upload and verification are complete.
## Troubleshooting
* **Bootloader Partition:** If the bootloader is currently booting from `ota_2`, flashing to the factory partition via USB might not automatically switch the active partition. You can use `espflash erase-parts otadata` to reset the OTA state.
* **Serial Terminal:** Use `espflash monitor` to view the serial logs for debugging.

View File

@@ -16,11 +16,11 @@ Connect to the AccessPoint and browse to http://192.168.71.1 to set it up
3. The ESP will now try to connect to a WIFI station if configured and start the webserver, if the connection to the station cannot be made, it will open an AccessPoint instead.
# MQTT
It is possible to use the topic /stay/alive to enter the config mode.
It is possible to use the MQTT topic `{base_topic}/stay_alive` to enter the config mode remotely.
If this topic contains a retained message with the value true, the ESP will enter ConfigMode during it's next cycle and stay there.
If this topic contains a retained message with the value `true` or `1`, the ESP will enter ConfigMode during its next cycle and stay there.
Note: You must set /stay/alive to retained false again after this, otherwise it will always enter configmode, even if you exist it via the webui.
Note: You must set `{base_topic}/stay_alive` to `false` or `0` (or clear the retained message) after this, otherwise it will always enter configmode, even if you exit it via the webui.
# USB reboot
By connecting to the USB Terminal via a Serial Monitor, the ESP is reset and will enter configmode as if the button for normal operation was pressed

View File

@@ -6,22 +6,84 @@ description: "a description"
tags: ["mqtt", "esp"]
---
# MQTT
An Mqtt server can be configured and will be used to dump all kinds of statistical data.
A configured MQTT server will receive statistical and status data from the controller.
|Topic|Example|Description|
|/firmware/address|
### Topics
| Topic | Example | Description |
|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------|
| firmware/address | 192.168.1.2 | The Ip address in station mode |
| firmware/githash | feature/esp32c6@1ce4d74a | The branch and hash during build time |
| firmware/buildtime | 2025-01-21T20:56:18.168163570Z | Compile time |
| firmware/last_online | 2025-01-22T08:56:46.664+01:00 | Last time this board was online |
| firmware/ota_state | Partition state is ESP_OTA_IMG_VALID | The OTA state, relevant for rollback |
| firmware/partition_address | 0x10000 | The OTA partition used, 0x10000 is ota_1 |
| state | online | Current State, expected are online or sleep |
| battery | {<br/>"voltage_milli_volt":"12860",<br/>"current_milli_ampere":"-16",<br/>"cycle_count":"12",<br/>"design_milli_ampere":"6000",<br/>"remaining_milli_ampere":"806",<br/>"state_of_charge":"15",<br/>"state_of_health":"93",<br/>"temperature":"2957"<br/>} | Dump of battery data |
| water | {<br/>"enough_water":true,<br/>"warn_level":false,<br/>"left_ml":1337,<br/>"sensor_error":false,<br/>"raw":0,"water_frozen":<br/>"tank sensor error"<br/>} | Water Status dump |
| plant1 | {<br/>"a":"disabled",<br/>"a_raw":"0",<br/>"b":"disabled",<br/>"b_raw":"0",<br/>"mode":"OFF",<br/>"consecutive_pump_count":0,<br/>"dry":false,<br/>"active":false,<br/>"pump_error":false,<br/>"not_effective":false,<br/>"cooldown":false,<br/>"out_of_work_hour":false,<br/>"last_pump":"N/A",<br/>"next_pump":"N/A"<br/>} | Plant status dump |
| light | {<br/>"active":false,<br/>"out_of_work_hour":true,<br/>"battery_low":true,<br/>"is_day":false<br/>} | Light status dump |
| deepsleep | night 1h | Why and how long the ESP will sleep |
| Topic | Example | Description |
|-------|---------|-------------|
| `firmware/address` | `192.168.1.2` | IP address in station mode |
| `firmware/state` | `VersionInfo { ... }` | Debug information about the current firmware and OTA slots |
| `firmware/last_online` | `2025-01-22T08:56:46.664+01:00` | Last time the board was online |
| `state` | `online` | Current state of the controller |
| `mppt` | `{"current_ma":1200,"voltage_ma":18500}` | MPPT charging metrics |
| `battery` | `{"Info":{"voltage_milli_volt":12860,"average_current_milli_ampere":-16,...}}` | Battery health and charge data |
| `water` | `{"enough_water":true,"warn_level":false,"left_ml":1337,...}` | Water tank status |
| `plant{1-8}` | `{"sensor_a":...,"sensor_b":...,"mode":"TargetMoisture",...}` | Detailed status for each plant slot |
| `pump{1-8}` | `{"enabled":true,"pump_ineffective":false,...}` | Metrics for the last pump activity |
| `light` | `{"enabled":true,"active":true,...}` | Night light status |
| `deepsleep` | `night 1h` | Why and how long the ESP will sleep |
### Data Structures
#### Firmware State (`firmware/state`)
Contains a debug dump of the `VersionInfo` struct:
- `git_hash`: Branch and commit hash
- `build_time`: Compilation timestamp
- `current`: Current running partition
- `slot0_state`: State of OTA slot 0
- `slot1_state`: State of OTA slot 1
#### MPPT (`mppt`)
- `current_ma`: Solar charging current in mA
- `voltage_ma`: Solar panel voltage in mV
#### Battery (`battery`)
Can be `"Unknown"` or an `Info` object:
- `voltage_milli_volt`: Battery voltage
- `average_current_milli_ampere`: Current draw/charge
- `design_milli_ampere_hour`: Battery capacity
- `remaining_milli_ampere_hour`: Remaining capacity
- `state_of_charge`: Charge percentage (0-100)
- `state_of_health`: Health percentage (0-100)
- `temperature`: Temperature in degrees Celsius
#### Water (`water`)
- `enough_water`: Boolean, true if level is above empty threshold
- `warn_level`: Boolean, true if level is below warning threshold
- `left_ml`: Estimated remaining water in ml
- `percent`: Estimated fill level in percent
- `raw`: Raw sensor voltage in mV
- `sensor_error`: Details if the level sensor fails
- `water_frozen`: Boolean, true if temperature is below freezing
- `water_temp`: Water temperature in degrees Celsius
- `temp_sensor_error`: Details if the temperature sensor fails
#### Plant (`plant{1-8}`)
- `sensor_a` / `sensor_b`: Moisture sensor status
- `Disabled`
- `{"MoistureValue":{"raw_hz":5000,"moisture_percent":65}}`
- `{"SensorError":{"ShortCircuit":{"hz":...,"max":...}}}`
- `mode`: Watering mode (`Off`, `TargetMoisture`, `MinMoisture`, `TimerOnly`)
- `do_water`: Boolean, true if watering is currently required
- `dry`: Boolean, true if moisture is below target
- `cooldown`: Boolean, true if the pump is in cooldown period
- `out_of_work_hour`: Boolean, true if currently outside allowed watering hours
- `consecutive_pump_count`: Number of pump cycles without reaching target
- `pump_error`: Details if the pump is failing
- `last_pump`: Timestamp of last activity
- `next_pump`: Estimated timestamp for next allowed activity
#### Pump (`pump{1-8}`)
- `enabled`: Boolean, pump was active
- `pump_ineffective`: Boolean, no flow detected during pumping
- `median_current_ma`: Median pump current
- `max_current_ma`: Peak pump current
- `min_current_ma`: Minimum pump current
#### Light (`light`)
- `enabled`: Boolean, is enabled in config
- `active`: Boolean, led is currently on
- `out_of_work_hour`: Boolean, led should not be on at this time of day
- `battery_low`: Boolean, battery is low so led usage is restricted
- `is_day`: Boolean, the sun is up (determined by solar panel voltage)

View File

@@ -5,37 +5,57 @@ draft: false
description: "How to compile the project"
tags: ["clone", "compile"]
---
# Preconditons:
* NPM is installed
* rustup is installed
* espup is installed
# Preconditions:
* **Rust:** `rustup` installed.
* **ESP32 Toolchain:** `espup` installed.
* **Build Utilities:** `ldproxy` and `espflash` installed.
* **Node.js:** `npm` installed (for the web interface).
# Cloning the Repository
Clone the repository including submodules:
```bash
git clone --recursive https://git.mannheim.ccc.de/C3MA/PlantCtrl.git
cd PlantCtrl/Software/MainBoard/rust
```
# Cloning Git
Clone the git via the tool of your choice to your local computer
```
git clone https://git.mannheim.ccc.de/C3MA/PlantCtrl.git
```
switch to the newly cloned folder
```
cd PlantCtrl/rust
```
# Install rust
rustup description
install ldproxy
```
cargo install ldproxy
```
# Esp Toolchain
espup description
# Webpack install
The buildin config website is currently build inline via npm - typescript - webpack and then directly embedded into the binary, so it is required to have webpack build working.
```
# Toolchain Setup
1. **Install Rust:** If not already done, visit [rustup.rs](https://rustup.rs/).
2. **Install ldproxy:**
```bash
cargo install ldproxy
```
3. **Install espup:**
```bash
cargo install espup
```
4. **Install ESP toolchain:**
```bash
espup install
```
5. **Install espflash:**
```bash
cargo install espflash
```
# Building the Web Interface
The configuration website is built using TypeScript and Webpack, then embedded into the Rust binary.
```bash
cd src_webpack/
npm install
```
Check the webpack build is working
```
npx webpack
cd ..
```
# Cargo Build
# Compiling the Firmware
Build the project using Cargo:
```bash
cargo build --release
```
The resulting binary will be located in `target/riscv32imac-unknown-none-elf/release/plant-ctrl2`.
# Using Build Scripts
To simplify the process, several bash scripts are provided in the `Software/MainBoard/rust` directory:
* **`image_build.sh`**: Automatically builds the web interface, compiles the Rust firmware in release mode, and creates a flashable `image.bin`.
* **`all.sh`**: Performs all steps from `image_build.sh` and additionally flashes the firmware to a connected device and starts the serial monitor.
* **`wokwi_build.sh`**: Builds a debug version of the firmware and creates a full 16MB flash image for use with the [Wokwi simulator](https://wokwi.com).

View File

@@ -2,13 +2,50 @@
title: "Software"
date: 2025-01-24
draft: false
description: "Information about the Firmware"
tags: ["software"]
description: "Information about the PlantCtrl Firmware"
tags: ["software", "rust", "esp32-c6", "firmware"]
---
* esp-idf-hal based rust project
* can be run standalon or with wifi
* MQTT
* SNTP to sync the RTC/Clock
* Abitrary file upload and download into SPIFS volume
* Flash Battery Controller from file in Flash
* Flash Firmware via OTA directly from website
The PlantCtrl firmware is a modern, reliable, and efficient system designed specifically for the ESP32-C6 RISC-V microcontroller. It is written entirely in **Rust**, leveraging the power of asynchronous programming to ensure low power consumption and high responsiveness.
## Core Technology Stack
* **Language:** [Rust](https://www.rust-lang.org/) (no_std)
* **Async Runtime:** [Embassy](https://embassy.dev/)
* **Hardware Abstraction:** `esp-hal` for ESP32-C6
* **Filesystem:** [LittleFS2](https://github.com/littlefs-project/littlefs) for robust persistent storage
* **Web Framework:** `edge-http` for the integrated web server
## Key Features
### Intelligent Watering
The software manages up to 8 independent watering zones.
* **Moisture-Based Control:** Automatically triggers pumps when soil moisture drops below a configurable threshold.
* **Safety Limits:** Includes pump time limits, volume limits (requires flow sensor), and mandatory cooldown periods.
* **Scheduling:** Define specific hours during which watering is allowed to occur.
* **Pump Protection:** Monitors pump current to detect dry running or blockages (using INA219).
### Modular Sensor Integration
The firmware communicates with the new **CAN bus-based sensor modules**.
* **Digital Accuracy:** Receives precise moisture data over the robust CAN bus protocol.
* **Multi-Sensor Support:** Supports A and B sensor pairs for each plant to provide better averaging in large containers.
### Networking & IoT
* **WiFi:** Supports both Station mode (connecting to your home network) and Access Point mode (for initial setup).
* **MQTT & Home Assistant:** Full integration with Home Assistant via the `mcutie` crate. Reports battery status, moisture levels, tank levels, and allows remote configuration.
* **Time Sync:** Uses **SNTP** to keep the internal Real-Time Clock (RTC) accurate.
* **OTA Updates:** Firmware can be updated wirelessly via the web interface.
### Power Management
* **Solar Optimized:** Works in tandem with the MPPT module to maximize solar charging.
* **Battery Aware:** Monitors battery State of Charge (SoC). It can automatically disable non-essential features (like the nightlight) or enter a deep-sleep "rescue mode" if the battery is critically low.
* **Low Power:** Uses ESP32-C6 power-saving features to ensure long autonomous operation.
### Integrated Web Interface
The firmware hosts a comprehensive web dashboard built with **TypeScript** and **Webpack**.
* **Real-time Monitoring:** View current moisture, battery health, and solar production.
* **Easy Configuration:** Change all system settings (WiFi, MQTT, watering rules) directly from your browser.
* **Diagnostics:** Access system logs and hardware status information.
## Emergency Rescue Mode
If the system detects a missing configuration or an invalid system time, it automatically enters **Emergency Rescue Mode**. In this mode, it opens a WiFi Access Point ("PlantCtrl Init") allowing you to perform initial setup or recover the system.