43 Commits

Author SHA1 Message Date
7f3910bcd0 sensor sweep tester 2025-10-07 21:50:33 +02:00
712e8c8b8f cleanups 2025-10-06 16:44:46 +02:00
4ba68182e5 start of plantsensor 2025-10-06 13:18:37 +02:00
a3cdd92af8 fix ota abort/invalid switching 2025-10-06 02:43:37 +02:00
894be7c373 fix some ota stuff 2025-10-04 03:05:11 +02:00
0ddf6a6886 stuff 2025-10-04 01:24:00 +02:00
27b18df78e ota is back 2025-10-01 21:56:16 +02:00
9c6dcc465e canbus sensor stub and pcnt impl 2025-10-01 00:36:15 +02:00
cf58486cf5 we should be feature compatible with main version now! 2025-09-30 22:37:45 +02:00
cfe23c8a09 mqtt via mcutie 2025-09-29 01:00:11 +02:00
3d18b0dbf6 split webserver into submodules 2025-09-28 19:08:43 +02:00
7ebc147f51 v3 wip 2025-09-28 13:42:30 +02:00
f0bda32d7a use rom linked functions for pin keeping 2025-09-26 23:32:41 +02:00
76f59b093d enable deepsleep and gpio1 + timer wake 2025-09-26 22:07:48 +02:00
7fc8d0c882 sta config mode 2025-09-26 01:13:08 +02:00
6d5bb5b966 sntp and wifi sta mode working 2025-09-26 00:44:40 +02:00
336961f0a0 not working wip of station mode 2025-09-25 22:32:10 +02:00
e20b474dfd bring selftest online, bring tz online, fix set_config/get_config race 2025-09-24 22:29:58 +02:00
5b009f50e5 tanksensor and rtc sync 2025-09-23 22:59:08 +02:00
d010c5d12a get more functions online 2025-09-23 20:51:59 +02:00
b594a02870 fix progress display 2025-09-23 00:43:19 +02:00
1f3349c348 add pump expander 2025-09-23 00:26:05 +02:00
5b0e2b6797 i2c working, rtc working, eeprom working 2025-09-22 23:44:33 +02:00
1791f463b7 remove anyhow 2025-09-22 01:49:25 +02:00
c94f5bdb45 get log to work, make time accessible 2025-09-20 11:31:51 +02:00
584d6df2d0 file up & download and delete 2025-09-18 01:39:32 +02:00
cd63e76469 set read config initial somewhat ready 2025-09-17 03:50:21 +02:00
4c54edbcea littlefs2 impl stuff 2025-09-17 01:36:53 +02:00
8b938e7687 small adjustments 2025-09-16 22:41:45 +02:00
1c84cd00da rework wifi controller share 2025-09-16 02:24:03 +02:00
1397f5d775 keep ota around for alter queries to it 2025-09-16 01:41:38 +02:00
65f6670ca4 adda lot of basic webserver back 2025-09-15 01:21:50 +02:00
049a9d027c use ssid 2025-09-14 13:50:46 +02:00
4aa25c687b minimal startup running 2025-09-14 13:19:30 +02:00
b3cc313139 initial webserver stub running 2025-09-14 05:29:23 +02:00
be3c4a5095 startup and ota state detection working, now wifi_ap next 2025-09-13 20:56:11 +02:00
4160202cdc more changes and linking 2025-09-13 02:47:53 +02:00
9de85b6e37 it's alive 2025-09-13 01:39:47 +02:00
79087c9353 more async migration 2025-09-12 16:30:35 +02:00
0d495d0f56 probalby found most of the import related stuff, now the real refactor begins 2025-09-12 00:05:52 +02:00
242e748ca0 started cleanup and moving from std to no_std 2025-09-11 22:47:11 +02:00
f853b6f2b2 removed all std dependencies … WIP move to embassy pure rust code 2025-09-11 21:42:17 +02:00
5bc20d312a Merge branch 'feature/enable-mqtt-login' into develop 2025-09-11 20:21:34 +02:00
155 changed files with 11786 additions and 3671 deletions

2
board/.gitignore vendored
View File

@@ -34,3 +34,5 @@ _autosave-*
# Autorouter files (exported from Pcbnew)
fp-info-cache
*.zip
netlist.ipc

View File

@@ -0,0 +1 @@
{"EXTRA_LAYERS": "", "ALL_ACTIVE_LAYERS": false, "EXTEND_EDGE_CUT": false, "ALTERNATIVE_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": false}

View File

@@ -0,0 +1 @@
{"EXTRA_LAYERS": "", "ALL_ACTIVE_LAYERS": false, "EXTEND_EDGE_CUT": false, "ALTERNATIVE_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": false}

View File

@@ -0,0 +1 @@
{"EXTRA_LAYERS": "", "ALL_ACTIVE_LAYERS": false, "EXTEND_EDGE_CUT": false, "ALTERNATIVE_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": false}

View File

@@ -0,0 +1 @@
{"EXTRA_LAYERS": "", "ALL_ACTIVE_LAYERS": false, "EXTEND_EDGE_CUT": false, "ALTERNATIVE_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": false}

View File

@@ -0,0 +1 @@
{"EXTRA_LAYERS": "", "ALL_ACTIVE_LAYERS": false, "EXTEND_EDGE_CUT": false, "ALTERNATIVE_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": false}

View File

@@ -0,0 +1,4 @@
(fp_lib_table
(version 7)
(lib (name "Sensor")(type "KiCad")(uri "/home/empire/workspace/PlantCtrl/board/modules/Sensors/Sensors/Sensor.pretty")(options "")(descr ""))
)

View File

@@ -0,0 +1,71 @@
P CODE 00
P UNITS CUST 0
P arrayDim N
317GND VIA MD0118PA00X+040551Y-024902X0236Y0000R000S3
317GND VIA MD0118PA00X+054232Y-022933X0236Y0000R000S3
317GND VIA MD0118PA00X+053346Y-021949X0236Y0000R000S3
317GND VIA MD0118PA00X+040846Y-019980X0236Y0000R000S3
317GND VIA MD0118PA00X+041043Y-025787X0236Y0000R000S3
317GND VIA MD0118PA00X+040059Y-019980X0236Y0000R000S3
317GND VIA MD0118PA00X+044291Y-025787X0236Y0000R000S3
317GND VIA MD0118PA00X+046654Y-025000X0236Y0000R000S3
317GND VIA MD0118PA00X+041240Y-021949X0236Y0000R000S3
317GND VIA MD0118PA00X+043307Y-020768X0236Y0000R000S3
317GND VIA MD0118PA00X+039961Y-022539X0236Y0000R000S3
317GND VIA MD0118PA00X+049606Y-021260X0236Y0000R000S3
317GND VIA MD0118PA00X+043898Y-024803X0236Y0000R000S3
317GND VIA MD0118PA00X+044587Y-023917X0236Y0000R000S3
317GND VIA MD0118PA00X+041142Y-024016X0236Y0000R000S3
317GND VIA MD0118PA00X+052461Y-022835X0236Y0000R000S3
317GND VIA MD0118PA00X+053642Y-024016X0236Y0000R000S3
317GND VIA MD0118PA00X+047835Y-026378X0236Y0000R000S3
317GND VIA MD0118PA00X+042618Y-020669X0236Y0000R000S3
317GND VIA MD0118PA00X+049409Y-022047X0236Y0000R000S3
317GND VIA MD0118PA00X+042520Y-022441X0236Y0000R000S3
317GND VIA MD0118PA00X+048622Y-022835X0236Y0000R000S3
327GND R_slop-1 A01X+053967Y-020177X0315Y0374R180S2
327NET-(U1-RS) R_slop-2 A01X+053317Y-020177X0315Y0374R180S2
3173_3V U6 -1 D0394PA00X+039016Y-019370X0669Y0669R000S0
317(U6-VBAT-PAD2) U6 -2 D0394PA00X+039016Y-020370X0669Y0000R000S0
317-(U6-SDA-PAD3) U6 -3 D0394PA00X+039016Y-021370X0669Y0000R000S0
317-(U6-SCL-PAD4) U6 -4 D0394PA00X+039016Y-022370X0669Y0000R000S0
317ENABLE_CAN U6 -5 D0394PA00X+039016Y-023370X0669Y0000R000S0
317CAN+ U6 -6 D0394PA00X+039016Y-024370X0669Y0000R000S0
317CAN- U6 -7 D0394PA00X+039016Y-025370X0669Y0000R000S0
317GND U6 -8 D0394PA00X+039016Y-026370X0669Y0000R000S0
317GND U6 -9 D0394PA00X+071260Y-034252X0669Y0669R000S0
317GND U6 -10 D0394PA00X+055512Y-031299X0669Y0669R000S0
317GND U6 -11 D0394PA00X+038780Y-034252X0669Y0669R000S0
317GND U6 -12 D0394PA00X+055512Y-020669X0669Y0669R000S0
317GND U6 -13 D0394PA00X+071260Y-019094X0669Y0669R000S0
327NET-(QP_1-G) QP_1 -1 A01X+047835Y-021280X0354Y0315R000S2
3273_3V QP_1 -2 A01X+047835Y-022028X0354Y0315R000S2
327CAN_POWER QP_1 -3 A01X+048622Y-021654X0354Y0315R000S2
327CAN_POWER R5 -1 A01X+047835Y-022904X0315Y0374R090S2
327NET-(I1-A) R5 -2 A01X+047835Y-023553X0315Y0374R090S2
327NET-(QP_1-G) R6 -1 A01X+046654Y-021329X0315Y0374R090S2
3273_3V R6 -2 A01X+046654Y-021978X0315Y0374R090S2
327NET-(QP_1-G) R7 -1 A01X+045669Y-021329X0315Y0374R090S2
327NET-(Q1-D) R7 -2 A01X+045669Y-021978X0315Y0374R090S2
317CAN_POWER J1 -1 D0374PA00X+055197Y-025394X0669Y0768R270S0
317NET-(J1-PIN_2) J1 -2 D0374PA00X+055197Y-024409X0669Y0768R270S0
317NET-(J1-PIN_3) J1 -3 D0374PA00X+055197Y-023425X0669Y0768R270S0
317GND J1 -4 D0374PA00X+055197Y-022441X0669Y0768R270S0
327NET-(Q1-G) Q1 -1 A01X+043819Y-021280X0354Y0315R000S2
327GND Q1 -2 A01X+043819Y-022028X0354Y0315R000S2
327NET-(Q1-D) Q1 -3 A01X+044606Y-021654X0354Y0315R000S2
327CAN+ U1 -1 A01X+050295Y-020707X0768Y0236R000S2
327GND U1 -2 A01X+050295Y-021207X0768Y0236R000S2
327CAN_POWER U1 -3 A01X+050295Y-021707X0768Y0236R000S2
327CAN- U1 -4 A01X+050295Y-022207X0768Y0236R000S2
327(U1-VREF-PAD5) U1 -5 A01X+052244Y-022207X0768Y0236R000S2
327NET-(J1-PIN_2) U1 -6 A01X+052244Y-021707X0768Y0236R000S2
327NET-(J1-PIN_3) U1 -7 A01X+052244Y-021207X0768Y0236R000S2
327NET-(U1-RS) U1 -8 A01X+052244Y-020707X0768Y0236R000S2
327NET-(Q1-G) R8 -1 A01X+042520Y-021329X0315Y0374R090S2
327GND R8 -2 A01X+042520Y-021978X0315Y0374R090S2
327GND I1 -1 A01X+047835Y-025541X0384Y0551R270S2
327NET-(I1-A) I1 -2 A01X+047835Y-024803X0384Y0551R270S2
327NET-(Q1-G) R4 -1 A01X+040846Y-023346X0315Y0374R180S2
327ENABLE_CAN R4 -2 A01X+040197Y-023346X0315Y0374R180S2
999

View File

@@ -0,0 +1,32 @@
;;; .doomrc --- doom runtime config -*- mode: emacs-lisp; lexical-binding: t; -*-
;;; Commentary:
;;; Code:
(require 'doom) ; be silent, byte-compiler
(after! dape
(add-to-list
'dape-configs
`(gdb-dap-openocd
ensure (lambda (config)
(dape-ensure-command config)
(let* ((default-directory
(or (dape-config-get config 'command-cwd)
default-directory))
(command (dape-config-get config 'command))
(output (shell-command-to-string (format "%s --version" command)))
(version (save-match-data
(when (string-match "GNU gdb \\(?:(.*) \\)?\\([0-9.]+\\)" output)
(string-to-number (match-string 1 output))))))
(unless (>= version 14.1)
(user-error "Requires gdb version >= 14.1"))))
modes ()
command-cwd dape-command-cwd
command "gdb"
command-args ("--interpreter=dap")
:request nil
:program nil
:args []
:stopAtBeginningOfMainSubprogram nil))
)
;;; .doomrc ends here

View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -460,6 +460,7 @@
"single_global_label": "ignore",
"unannotated": "error",
"unconnected_wire_endpoint": "warning",
"undefined_netclass": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"

10
bootloader/.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
# ESP-IDF build artifacts
build/
.sdkconfig*
CMakeFiles/
CMakeCache.txt
cmake-build-*/
*.log
*.bin
*.elf
*.map

8
bootloader/.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

8
bootloader/.idea/bootloader.iml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
bootloader/.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/bootloader.iml" filepath="$PROJECT_DIR$/.idea/bootloader.iml" />
</modules>
</component>
</project>

7
bootloader/.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
<mapping directory="$PROJECT_DIR$/../website/themes/blowfish" vcs="Git" />
</component>
</project>

11
bootloader/CMakeLists.txt Normal file
View File

@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.16)
# Minimal ESP-IDF project to build only the bootloader
# You must have ESP-IDF installed and IDF_PATH exported.
# Pin the target to ESP32-C6 to ensure correct bootloader build
# (must be set before including project.cmake)
set(IDF_TARGET "esp32c6")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(custom_bootloader)

43
bootloader/README.md Normal file
View File

@@ -0,0 +1,43 @@
Custom ESP-IDF Bootloader (Rollback Enabled)
This minimal project builds a custom ESP-IDF bootloader with rollback support enabled.
You can flash it later alongside a Rust firmware using `espflash`.
What this provides
- A minimal ESP-IDF project (CMake) that can build just the bootloader.
- Rollback support enabled via sdkconfig.defaults (CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y).
- A sample OTA partition table (partitions.csv) suitable for OTA and rollback (otadata + two OTA slots).
- A convenience script to build the bootloader for the desired target.
Requirements
- ESP-IDF installed and set up (IDF_PATH exported, Python env activated).
- A selected target (esp32, esp32s3, esp32c3, etc.).
Build
1) Ensure ESP-IDF is set up:
source "$IDF_PATH/export.sh"
2) Pick a target (examples):
idf.py set-target esp32
# or use the script:
./build_bootloader.sh esp32
3) Build only the bootloader:
idf.py bootloader
# or using the script (which also supports setting target):
./build_bootloader.sh esp32
Artifacts
- build/bootloader/bootloader.bin
Using with espflash (Rust)
- For a no_std Rust firmware, you can pass this custom bootloader to espflash:
espflash flash --bootloader build/bootloader/bootloader.bin \
--partition-table partitions.csv \
<your-app-binary-or-elf>
Notes
- Rollback logic requires an OTA layout (otadata + at least two OTA app partitions). The provided partitions.csv is a starting point; adjust sizes/offsets to match your needs.
- This project doesnt build an application; it exists solely to produce a bootloader with the right configuration.
- If you need different log verbosity or features, run `idf.py menuconfig` and then diff/port the changes back into sdkconfig.defaults.
- Targets supported depend on your ESP-IDF version. Use `idf.py set-target <chip>` or `./build_bootloader.sh <chip>`.

41
bootloader/build_bootloader.sh Executable file
View File

@@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -euo pipefail
# Build script for custom ESP-IDF bootloader with rollback enabled.
# Requirements:
# - ESP-IDF installed
# - IDF_PATH exported
# - Python env prepared (the usual ESP-IDF setup)
# Usage:
# ./build_bootloader.sh [esp32|esp32s3|esp32c3|esp32s2|esp32c2|esp32c6|esp32h2]
# If target is omitted, the last configured target will be used.
TARGET=${1:-}
if [[ -z "${IDF_PATH:-}" ]]; then
echo "ERROR: IDF_PATH is not set. Please install ESP-IDF and export the environment (source export.sh)." >&2
exit 1
fi
# shellcheck source=/dev/null
source "$IDF_PATH/export.sh"
if [[ -n "$TARGET" ]]; then
idf.py set-target "$TARGET"
fi
# Ensure sdkconfig.defaults is considered (ESP-IDF does this automatically).
# Build only the bootloader.
idf.py bootloader
echo
BOOTLOADER_BIN="build/bootloader/bootloader.bin"
if [[ -f "$BOOTLOADER_BIN" ]]; then
echo "Bootloader built: $BOOTLOADER_BIN"
echo "You can use this with espflash via:"
echo " espflash flash --bootloader $BOOTLOADER_BIN [--partition-table partitions.csv] <your-app-binary>"
else
echo "ERROR: Bootloader binary not found. Check build logs above." >&2
exit 2
fi
cp build/bootloader/bootloader.bin ../rust/bootloader.bin

View File

@@ -0,0 +1 @@
idf_component_register(SRCS "dummy.c" INCLUDE_DIRS ".")

4
bootloader/main/dummy.c Normal file
View File

@@ -0,0 +1,4 @@
// This file intentionally left almost empty.
// ESP-IDF expects at least one component; the bootloader build does not use this.
void __unused_dummy_symbol(void) {}

View File

@@ -0,0 +1,6 @@
nvs, data, nvs, , 16k,
otadata, data, ota, , 8k,
phy_init, data, phy, , 4k,
ota_0, app, ota_0, , 3968k,
ota_1, app, ota_1, , 3968k,
storage, data, littlefs,, 8M,
1 nvs data nvs 16k
2 otadata data ota 8k
3 phy_init data phy 4k
4 ota_0 app ota_0 3968k
5 ota_1 app ota_1 3968k
6 storage data littlefs 8M

2385
bootloader/sdkconfig Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
# Target can be set with: idf.py set-target esp32|esp32s3|esp32c3|...
# If not set via idf.py, ESP-IDF may default to a target; it's recommended to set it explicitly.
# Explicitly pin target to ESP32-C6
CONFIG_IDF_TARGET="esp32c6"
CONFIG_IDF_TARGET_ESP32C6=y
CONFIG_IDF_TARGET_ARCH_RISCV=y
# Bootloader configuration
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y
# Slightly faster boot by skipping GPIO checks unless you need that feature
CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y
# Partition table config is not required to build bootloader, but shown for clarity when you build full app later
# CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
# CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"

2214
bootloader/sdkconfig.old Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,31 @@
[build]
#target = "xtensa-esp32-espidf"
target = "riscv32imac-esp-espidf"
rustflags = [
# Required to obtain backtraces (e.g. when using the "esp-backtrace" crate.)
# NOTE: May negatively impact performance of produced code
"-C", "force-frame-pointers",
"-Z", "stack-protector=all",
"-C", "link-arg=-Tlinkall.x",
]
[target.riscv32imac-esp-espidf]
linker = "ldproxy"
target = "riscv32imac-unknown-none-elf"
[target.riscv32imac-unknown-none-elf]
#runner = "espflash flash --monitor --bootloader bootloader.bin --chip esp32c6 --baud 921600 --partition-table partitions.csv"
#runner = "espflash flash --monitor --baud 921600 --partition-table partitions.csv -b no-reset" # Select this runner in case of usb ttl
runner = "espflash flash --monitor"
#runner = "espflash flash --monitor"
#runner = "cargo runner"
#runner = "espflash flash --monitor --partition-table partitions.csv -b no-reset" # create upgrade image file for webupload
# runner = espflash erase-parts otadata //ensure flash is clean
rustflags = ["--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110
[unstable]
build-std = ["std", "panic_abort"]
[env]
MCU = "esp32c6"
# Note: this variable is not used by the pio builder (`cargo build --features pio`)
ESP_IDF_VERSION = "v5.2.1"
CHRONO_TZ_TIMEZONE_FILTER = "UTC|America/New_York|America/Chicago|America/Los_Angeles|Europe/London|Europe/Berlin|Europe/Paris|Asia/Tokyo|Asia/Shanghai|Asia/Kolkata|Australia/Sydney|America/Sao_Paulo|Africa/Johannesburg|Asia/Dubai|Pacific/Auckland"
CARGO_WORKSPACE_DIR = { value = "", relative = true }
RUST_BACKTRACE = "full"
ESP_LOG = "info"
[unstable]
build-std = ["alloc", "core"]

View File

@@ -1,14 +1,18 @@
<component name="ProjectDictionaryState">
<dictionary name="project">
<words>
<w>boardtest</w>
<w>buildtime</w>
<w>deepsleep</w>
<w>githash</w>
<w>lamptest</w>
<w>lightstate</w>
<w>mppt</w>
<w>plantstate</w>
<w>pumptest</w>
<w>sntp</w>
<w>vergen</w>
<w>wifiscan</w>
</words>
</dictionary>
</component>

View File

@@ -7,5 +7,6 @@
</Languages>
</inspection_tool>
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NewCrateVersionAvailable" enabled="true" level="INFORMATION" enabled_by_default="true" />
</profile>
</component>

View File

@@ -1,113 +1,160 @@
[package]
name = "plant-ctrl2"
version = "0.1.0"
authors = ["Empire Phoenix"]
edition = "2021"
resolver = "2"
#rust-version = "1.71"
name = "plant-ctrl2"
rust-version = "1.86"
version = "0.1.0"
# Explicitly configure the binary target and disable building it as a test/bench.
[[bin]]
name = "plant-ctrl2"
path = "src/main.rs"
# Prevent IDEs/Cargo from trying to compile a test harness for this no_std binary.
test = false
bench = false
doc = false
#this strips the bootloader, we need that tho
#strip = true
[profile.dev]
# Explicitly disable LTO which the Xtensa codegen backend has issues
lto = false
strip = false
debug = true
overflow-checks = true
panic = "abort"
incremental = true
opt-level = 2
[profile.release]
# Explicitly disable LTO which the Xtensa codegen backend has issues
lto = false
strip = true
lto = "fat"
debug = false
overflow-checks = false
overflow-checks = true
panic = "abort"
incremental = true
opt-level = "z"
[package.metadata.cargo_runner]
# The string `$TARGET_FILE` will be replaced with the path from cargo.
command = [
"cargo",
"espflash",
"save-image",
"--chip",
"esp32c6",
"image.bin",
"--partition-table",
"partitions.csv"
]
[profile.release]
lto = "fat"
#debug = false
overflow-checks = true
panic = "abort"
incremental = false
opt-level = "z"
[package.metadata.espflash]
partition_table = "partitions.csv"
[features]
default = ["std", "esp-idf-svc/native"]
pio = ["esp-idf-svc/pio"]
std = ["alloc", "esp-idf-svc/binstart", "esp-idf-svc/std"]
alloc = ["esp-idf-svc/alloc"]
nightly = ["esp-idf-svc/nightly"]
experimental = ["esp-idf-svc/experimental"]
#embassy = ["esp-idf-svc/embassy-sync", "esp-idf-svc/critical-section", "esp-idf-svc/embassy-time-driver"]
[dependencies]
#ESP stuff
embedded-svc = { version = "0.28.1", features = ["experimental"] }
esp-idf-hal = "0.45.2"
esp-idf-sys = { version = "0.36.1", features = ["binstart", "native"] }
esp-idf-svc = { version = "0.51.0", default-features = false }
esp-bootloader-esp-idf = { version = "0.2.0", features = ["esp32c6"] }
esp-hal = { version = "=1.0.0-rc.0", features = [
"esp32c6",
"log-04",
"unstable",
"rt"
] }
log = "0.4.27"
embassy-net = { version = "0.7.1", default-features = false, features = [
"dhcpv4",
"log",
"medium-ethernet",
"tcp",
"udp",
"proto-ipv4",
"dns"
] }
embedded-io = "0.6.1"
embedded-io-async = "0.6.1"
esp-alloc = "0.8.0"
esp-backtrace = { version = "0.17.0", features = [
"esp32c6",
"exception-handler",
"panic-handler",
"println",
"colors",
"custom-halt"
] }
esp-println = { version = "0.15.0", features = ["esp32c6", "log-04"] }
# for more networking protocol support see https://crates.io/crates/edge-net
embassy-executor = { version = "0.7.0", features = [
"log",
"task-arena-size-64",
"nightly"
] }
embassy-time = { version = "0.5.0", features = ["log"], default-features = false }
esp-hal-embassy = { version = "0.9.0", features = ["esp32c6", "log-04"] }
esp-storage = { version = "0.7.0", features = ["esp32c6"] }
esp-wifi = { version = "0.15.0", features = [
"builtin-scheduler",
"esp-alloc",
"esp32c6",
"log-04",
"smoltcp",
"wifi",
] }
smoltcp = { version = "0.12.0", default-features = false, features = [
"alloc",
"log",
"medium-ethernet",
"multicast",
"proto-dhcpv4",
"proto-ipv6",
"proto-dns",
"proto-ipv4",
"socket-dns",
"socket-icmp",
"socket-raw",
"socket-tcp",
"socket-udp",
] }
#static_cell = "2.1.1"
embedded-hal = "1.0.0"
heapless = { version = "0.8", features = ["serde"] }
embedded-hal-bus = { version = "0.3.0", features = ["std"] }
embedded-hal-bus = { version = "0.3.0" }
#Hardware additional driver
ds18b20 = "0.1.1"
bq34z100 = { version = "0.3.0", features = ["flashstream"] }
one-wire-bus = "0.1.1"
#bq34z100 = { version = "0.3.0", default-features = false }
onewire = "0.4.0"
#strum = { version = "0.27.0", default-feature = false, features = ["derive"] }
measurements = "0.11.0"
ds323x = "0.6.0"
#pure code dependencies
once_cell = "1.19.0"
anyhow = { version = "1.0.75", features = ["std", "backtrace"] }
strum = { version = "0.27.0", features = ["derive"] }
measurements = "0.11.0"
#json
serde = { version = "1.0.192", features = ["derive"] }
serde_json = "1.0.108"
serde = { version = "1.0.219", features = ["derive", "alloc"], default-features = false }
serde_json = { version = "1.0.143", default-features = false, features = ["alloc"] }
#timezone
chrono = { version = "0.4.23", default-features = false, features = ["iana-time-zone", "alloc", "serde"] }
chrono-tz = { version = "0.10.3", default-features = false, features = ["filter-by-regex"] }
chrono = { version = "0.4.42", default-features = false, features = ["iana-time-zone", "alloc", "serde"] }
chrono-tz = { version = "0.10.4", default-features = false, features = ["filter-by-regex"] }
eeprom24x = "0.7.2"
url = "2.5.3"
crc = "3.2.1"
bincode = "2.0.1"
ringbuffer = "0.15.0"
text-template = "0.1.0"
strum_macros = "0.27.0"
esp-ota = { version = "0.2.2", features = ["log"] }
unit-enum = "1.4.1"
pca9535 = { version = "2.0.0", features = ["std"] }
ina219 = { version = "0.2.0", features = ["std"] }
pca9535 = { version = "2.0.0" }
ina219 = { version = "0.2.0" }
embedded-storage = "=0.3.1"
ekv = "1.0.0"
portable-atomic = "1.11.1"
embassy-sync = { version = "0.7.2", features = ["log"] }
async-trait = "0.1.89"
bq34z100 = { version = "0.4.0", default-features = false }
edge-dhcp = "0.6.0"
edge-nal = "0.5.0"
edge-nal-embassy = "0.6.0"
static_cell = "2.1.1"
edge-http = { version = "0.6.1", features = ["log"] }
littlefs2 = { version = "0.6.1", features = ["c-stubs", "alloc"] }
littlefs2-core = "0.1.1"
bytemuck = { version = "1.23.2", features = ["derive", "min_const_generics", "pod_saturating", "extern_crate_alloc"] }
deranged = "0.5.3"
embassy-embedded-hal = "0.5.0"
bincode = { version = "2.0.1", default-features = false, features = ["derive"] }
sntpc = { version = "0.6.0", default-features = false, features = ["log", "embassy-socket", "embassy-socket-ipv6"] }
option-lock = { version = "0.3.1", default-features = false }
#stay in sync with mcutie version here!
heapless = { version = "0.7.17", features = ["serde"] }
mcutie = { version = "0.3.0", default-features = false, features = ["log", "homeassistant"] }
nb = "1.1.0"
embedded-can = "0.4.1"
[patch.crates-io]
#esp-idf-hal = { git = "https://github.com/esp-rs/esp-idf-hal.git" }
#esp-idf-hal = { git = "https://github.com/empirephoenix/esp-idf-hal.git" }
#esp-idf-sys = { git = "https://github.com/empirephoenix/esp-idf-sys.git" }
#esp-idf-sys = { git = "https://github.com/esp-rs/esp-idf-sys.git" }
#esp-idf-svc = { git = "https://github.com/esp-rs/esp-idf-svc.git" }
mcutie = { git = 'https://github.com/empirephoenix/mcutie.git' }
#bq34z100 = { path = "../../bq34z100_rust" }
[build-dependencies]
cc = "=1.1.30"
embuild = { version = "0.32.0", features = ["espidf"] }
vergen = { version = "8.2.6", features = ["build", "git", "gitcl"] }

12
rust/all.sh Executable file
View File

@@ -0,0 +1,12 @@
rm ./src/webserver/index.html.gz
rm ./src/webserver/bundle.js.gz
set -e
cd ./src_webpack/
npx webpack build
cp index.html.gz ../src/webserver/index.html.gz
cp bundle.js.gz ../src/webserver/bundle.js.gz
cd ../
cargo build --release
espflash save-image --bootloader bootloader.bin --partition-table partitions.csv --chip esp32c6 target/riscv32imac-unknown-none-elf/release/plant-ctrl2 image.bin
espflash flash --monitor --bootloader bootloader.bin --chip esp32c6 --baud 921600 --partition-table partitions.csv target/riscv32imac-unknown-none-elf/release/plant-ctrl2

BIN
rust/bootloader.bin Normal file

Binary file not shown.

View File

@@ -1,10 +1,64 @@
use std::process::Command;
use vergen::EmitBuilder;
fn linker_be_nice() {
let args: Vec<String> = std::env::args().collect();
if args.len() > 1 {
let kind = &args[1];
let what = &args[2];
match kind.as_str() {
"undefined-symbol" => match what.as_str() {
"_defmt_timestamp" => {
eprintln!();
eprintln!("💡 `defmt` not found - make sure `defmt.x` is added as a linker script and you have included `use defmt_rtt as _;`");
eprintln!();
}
"_stack_start" => {
eprintln!();
eprintln!("💡 Is the linker script `linkall.x` missing?");
eprintln!();
}
"esp_wifi_preempt_enable"
| "esp_wifi_preempt_yield_task"
| "esp_wifi_preempt_task_create" => {
eprintln!();
eprintln!("💡 `esp-wifi` has no scheduler enabled. Make sure you have the `builtin-scheduler` feature enabled, or that you provide an external scheduler.");
eprintln!();
}
"embedded_test_linker_file_not_added_to_rustflags" => {
eprintln!();
eprintln!("💡 `embedded-test` not found - make sure `embedded-test.x` is added as a linker script for tests");
eprintln!();
}
_ => (),
},
// we don't have anything helpful for "missing-lib" yet
_ => {
std::process::exit(1);
}
}
std::process::exit(0);
}
println!(
"cargo:rustc-link-arg=--error-handling-script={}",
std::env::current_exe().unwrap().display()
);
}
fn main() {
println!("cargo:rerun-if-changed=./src/src_webpack");
//webpack();
linker_be_nice();
let _ = EmitBuilder::builder().all_git().all_build().emit();
}
fn webpack() {
//println!("cargo:rerun-if-changed=./src/src_webpack");
Command::new("rm")
.arg("./src/webserver/bundle.js")
.arg("./src/webserver/bundle.js.gz")
.output()
.unwrap();
@@ -27,14 +81,14 @@ fn main() {
let _ = Command::new("cmd")
.arg("/K")
.arg("move")
.arg("./src_webpack/bundle.js")
.arg("./src_webpack/bundle.js.gz")
.arg("./src/webserver")
.output()
.unwrap();
let _ = Command::new("cmd")
.arg("/K")
.arg("move")
.arg("./src_webpack/index.html")
.arg("./src_webpack/index.html.gz")
.arg("./src/webserver")
.output()
.unwrap();
@@ -53,18 +107,15 @@ fn main() {
// move webpack results to rust webserver src
let _ = Command::new("mv")
.arg("./src_webpack/bundle.js")
.arg("./src_webpack/bundle.js.gz")
.arg("./src/webserver")
.output()
.unwrap();
let _ = Command::new("mv")
.arg("./src_webpack/index.html")
.arg("./src_webpack/index.html.gz")
.arg("./src/webserver")
.output()
.unwrap();
}
}
embuild::espidf::sysenv::output();
let _ = EmitBuilder::builder().all_git().all_build().emit();
}

View File

@@ -1,5 +1,8 @@
partition_table = "partitions.csv"
[connection]
serial = "/dev/ttyACM0"
[[usb_device]]
vid = "303a"
pid = "1001"
[flash]
size = "16MB"
size = "16MB"

11
rust/flash.sh Executable file
View File

@@ -0,0 +1,11 @@
rm ./src/webserver/index.html.gz
rm ./src/webserver/bundle.js.gz
set -e
cd ./src_webpack/
npx webpack build
cp index.html.gz ../src/webserver/index.html.gz
cp bundle.js.gz ../src/webserver/bundle.js.gz
cd ../
cargo build --release
espflash flash --monitor --bootloader bootloader.bin --chip esp32c6 --baud 921600 --partition-table partitions.csv target/riscv32imac-unknown-none-elf/release/plant-ctrl2

13
rust/image_build.sh Executable file
View File

@@ -0,0 +1,13 @@
rm image.bin
rm ./src/webserver/index.html.gz
rm ./src/webserver/bundle.js.gz
set -e
cd ./src_webpack/
npx webpack build
cp index.html.gz ../src/webserver/index.html.gz
cp bundle.js.gz ../src/webserver/bundle.js.gz
cd ../
set -e
cargo build --release
espflash save-image --bootloader bootloader.bin --partition-table partitions.csv --chip esp32c6 target/riscv32imac-unknown-none-elf/release/plant-ctrl2 image.bin

View File

@@ -3,4 +3,4 @@ otadata, data, ota, , 8k,
phy_init, data, phy, , 4k,
ota_0, app, ota_0, , 3968k,
ota_1, app, ota_1, , 3968k,
storage, data, spiffs, , 8M,
storage, data, littlefs,, 8M,
1 nvs data nvs 16k
3 phy_init data phy 4k
4 ota_0 app ota_0 3968k
5 ota_1 app ota_1 3968k
6 storage data spiffs littlefs 8M

View File

@@ -1,3 +1,2 @@
[toolchain]
channel = "nightly"
toolchain = "esp"

View File

@@ -1,7 +1,8 @@
use crate::hal::PLANT_COUNT;
use crate::plant_state::PlantWateringMode;
use alloc::string::String;
use core::str::FromStr;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(default)]
@@ -9,10 +10,10 @@ pub struct NetworkConfig {
pub ap_ssid: heapless::String<32>,
pub ssid: Option<heapless::String<32>>,
pub password: Option<heapless::String<64>>,
pub mqtt_url: Option<heapless::String<128>>,
pub mqtt_url: Option<String>,
pub base_topic: Option<heapless::String<64>>,
pub mqtt_user: Option<heapless::String<32>>,
pub mqtt_password: Option<heapless::String<64>>,
pub mqtt_user: Option<String>,
pub mqtt_password: Option<String>,
pub max_wait: u32,
}
impl Default for NetworkConfig {

318
rust/src/fat_error.rs Normal file
View File

@@ -0,0 +1,318 @@
use alloc::format;
use alloc::string::{String, ToString};
use core::convert::Infallible;
use core::fmt;
use core::str::Utf8Error;
use embassy_embedded_hal::shared_bus::I2cDeviceError;
use embassy_executor::SpawnError;
use embassy_sync::mutex::TryLockError;
use embedded_storage::nor_flash::NorFlashErrorKind;
use esp_hal::i2c::master::ConfigError;
use esp_hal::pcnt::unit::{InvalidHighLimit, InvalidLowLimit};
use esp_hal::twai::EspTwaiError;
use esp_wifi::wifi::WifiError;
use ina219::errors::{BusVoltageReadError, ShuntVoltageReadError};
use littlefs2_core::PathError;
use onewire::Error;
use pca9535::ExpanderError;
//All error superconstruct
#[derive(Debug)]
pub enum FatError {
OneWireError {
error: Error<Infallible>,
},
String {
error: String,
},
LittleFSError {
error: littlefs2_core::Error,
},
PathError {
error: PathError,
},
TryLockError {
error: TryLockError,
},
WifiError {
error: WifiError,
},
SerdeError {
error: serde_json::Error,
},
PreconditionFailed {
error: String,
},
NoBatteryMonitor,
SpawnError {
error: SpawnError,
},
PartitionError {
error: esp_bootloader_esp_idf::partitions::Error,
},
I2CConfigError {
error: ConfigError,
},
DS323 {
error: String,
},
Eeprom24x {
error: String,
},
ExpanderError {
error: String,
},
CanBusError {
error: EspTwaiError,
},
SNTPError {
error: sntpc::Error,
},
}
pub type FatResult<T> = Result<T, FatError>;
impl fmt::Display for FatError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FatError::SpawnError { error } => {
write!(f, "SpawnError {:?}", error.to_string())
}
FatError::OneWireError { error } => write!(f, "OneWireError {:?}", error),
FatError::String { error } => write!(f, "{}", error),
FatError::LittleFSError { error } => write!(f, "LittleFSError {:?}", error),
FatError::PathError { error } => write!(f, "PathError {:?}", error),
FatError::TryLockError { error } => write!(f, "TryLockError {:?}", error),
FatError::WifiError { error } => write!(f, "WifiError {:?}", error),
FatError::SerdeError { error } => write!(f, "SerdeError {:?}", error),
FatError::PreconditionFailed { error } => write!(f, "PreconditionFailed {:?}", error),
FatError::PartitionError { error } => {
write!(f, "PartitionError {:?}", error)
}
FatError::NoBatteryMonitor => {
write!(f, "No Battery Monitor")
}
FatError::I2CConfigError { error } => write!(f, "I2CConfigError {:?}", error),
FatError::DS323 { error } => write!(f, "DS323 {:?}", error),
FatError::Eeprom24x { error } => write!(f, "Eeprom24x {:?}", error),
FatError::ExpanderError { error } => write!(f, "ExpanderError {:?}", error),
FatError::CanBusError { error } => {
write!(f, "CanBusError {:?}", error)
}
FatError::SNTPError { error } => write!(f, "SNTPError {:?}", error),
}
}
}
#[macro_export]
macro_rules! bail {
($msg:literal $(,)?) => {
return $crate::fat_error::fat_bail($msg)
};
($fmt:literal, $($arg:tt)*) => {
return $crate::fat_error::fat_bail(&alloc::format!($fmt, $($arg)*))
};
}
pub fn fat_bail<X>(message: &str) -> Result<X, FatError> {
Err(FatError::String {
error: message.to_string(),
})
}
pub trait ContextExt<T> {
fn context<C>(self, context: C) -> Result<T, FatError>
where
C: AsRef<str>;
}
impl<T> ContextExt<T> for Option<T> {
fn context<C>(self, context: C) -> Result<T, FatError>
where
C: AsRef<str>,
{
match self {
Some(value) => Ok(value),
None => Err(FatError::PreconditionFailed {
error: context.as_ref().to_string(),
}),
}
}
}
impl From<Error<Infallible>> for FatError {
fn from(error: Error<Infallible>) -> Self {
FatError::OneWireError { error }
}
}
impl From<littlefs2_core::Error> for FatError {
fn from(value: littlefs2_core::Error) -> Self {
FatError::LittleFSError { error: value }
}
}
impl From<PathError> for FatError {
fn from(value: PathError) -> Self {
FatError::PathError { error: value }
}
}
impl From<TryLockError> for FatError {
fn from(value: TryLockError) -> Self {
FatError::TryLockError { error: value }
}
}
impl From<WifiError> for FatError {
fn from(value: WifiError) -> Self {
FatError::WifiError { error: value }
}
}
impl From<serde_json::error::Error> for FatError {
fn from(value: serde_json::Error) -> Self {
FatError::SerdeError { error: value }
}
}
impl From<SpawnError> for FatError {
fn from(value: SpawnError) -> Self {
FatError::SpawnError { error: value }
}
}
impl From<esp_bootloader_esp_idf::partitions::Error> for FatError {
fn from(value: esp_bootloader_esp_idf::partitions::Error) -> Self {
FatError::PartitionError { error: value }
}
}
impl From<Utf8Error> for FatError {
fn from(value: Utf8Error) -> Self {
FatError::String {
error: value.to_string(),
}
}
}
impl<E: core::fmt::Debug> From<edge_http::io::Error<E>> for FatError {
fn from(value: edge_http::io::Error<E>) -> Self {
FatError::String {
error: format!("{:?}", value),
}
}
}
impl<E: core::fmt::Debug> From<ds323x::Error<E>> for FatError {
fn from(value: ds323x::Error<E>) -> Self {
FatError::DS323 {
error: format!("{:?}", value),
}
}
}
impl<E: core::fmt::Debug> From<eeprom24x::Error<E>> for FatError {
fn from(value: eeprom24x::Error<E>) -> Self {
FatError::Eeprom24x {
error: format!("{:?}", value),
}
}
}
impl<E: core::fmt::Debug> From<ExpanderError<I2cDeviceError<E>>> for FatError {
fn from(value: ExpanderError<I2cDeviceError<E>>) -> Self {
FatError::ExpanderError {
error: format!("{:?}", value),
}
}
}
impl From<bincode::error::DecodeError> for FatError {
fn from(value: bincode::error::DecodeError) -> Self {
FatError::Eeprom24x {
error: format!("{:?}", value),
}
}
}
impl From<bincode::error::EncodeError> for FatError {
fn from(value: bincode::error::EncodeError) -> Self {
FatError::Eeprom24x {
error: format!("{:?}", value),
}
}
}
impl From<ConfigError> for FatError {
fn from(value: ConfigError) -> Self {
FatError::I2CConfigError { error: value }
}
}
impl<E: core::fmt::Debug> From<I2cDeviceError<E>> for FatError {
fn from(value: I2cDeviceError<E>) -> Self {
FatError::String {
error: format!("{:?}", value),
}
}
}
impl<E: core::fmt::Debug> From<BusVoltageReadError<I2cDeviceError<E>>> for FatError {
fn from(value: BusVoltageReadError<I2cDeviceError<E>>) -> Self {
FatError::String {
error: format!("{:?}", value),
}
}
}
impl<E: core::fmt::Debug> From<ShuntVoltageReadError<I2cDeviceError<E>>> for FatError {
fn from(value: ShuntVoltageReadError<I2cDeviceError<E>>) -> Self {
FatError::String {
error: format!("{:?}", value),
}
}
}
impl From<Infallible> for FatError {
fn from(value: Infallible) -> Self {
panic!("Infallible error: {:?}", value)
}
}
impl From<InvalidLowLimit> for FatError {
fn from(value: InvalidLowLimit) -> Self {
FatError::String {
error: format!("{:?}", value),
}
}
}
impl From<InvalidHighLimit> for FatError {
fn from(value: InvalidHighLimit) -> Self {
FatError::String {
error: format!("{:?}", value),
}
}
}
impl From<nb::Error<EspTwaiError>> for FatError {
fn from(value: nb::Error<EspTwaiError>) -> Self {
match value {
nb::Error::Other(can_error) => FatError::CanBusError { error: can_error },
nb::Error::WouldBlock => FatError::String {
error: "Would block".to_string(),
},
}
}
}
impl From<NorFlashErrorKind> for FatError {
fn from(value: NorFlashErrorKind) -> Self {
FatError::String {
error: value.to_string(),
}
}
}
impl From<sntpc::Error> for FatError {
fn from(value: sntpc::Error) -> Self {
FatError::SNTPError { error: value }
}
}

View File

@@ -1,22 +1,28 @@
use anyhow::anyhow;
use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver};
use embedded_hal_bus::i2c::MutexDevice;
use esp_idf_hal::delay::Delay;
use esp_idf_hal::i2c::{I2cDriver, I2cError};
use crate::fat_error::{FatError, FatResult};
use crate::hal::Box;
use alloc::string::String;
use async_trait::async_trait;
use bq34z100::{Bq34z100g1, Bq34z100g1Driver, Flags};
use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use esp_hal::delay::Delay;
use esp_hal::i2c::master::I2c;
use esp_hal::Blocking;
use measurements::Temperature;
use serde::Serialize;
#[async_trait]
pub trait BatteryInteraction {
fn state_charge_percent(&mut self) -> Result<f32, BatteryError>;
fn remaining_milli_ampere_hour(&mut self) -> Result<u16, BatteryError>;
fn max_milli_ampere_hour(&mut self) -> Result<u16, BatteryError>;
fn design_milli_ampere_hour(&mut self) -> Result<u16, BatteryError>;
fn voltage_milli_volt(&mut self) -> Result<u16, BatteryError>;
fn average_current_milli_ampere(&mut self) -> Result<i16, BatteryError>;
fn cycle_count(&mut self) -> Result<u16, BatteryError>;
fn state_health_percent(&mut self) -> Result<u16, BatteryError>;
fn bat_temperature(&mut self) -> Result<u16, BatteryError>;
fn get_battery_state(&mut self) -> Result<BatteryState, BatteryError>;
async fn state_charge_percent(&mut self) -> FatResult<f32>;
async fn remaining_milli_ampere_hour(&mut self) -> FatResult<u16>;
async fn max_milli_ampere_hour(&mut self) -> FatResult<u16>;
async fn design_milli_ampere_hour(&mut self) -> FatResult<u16>;
async fn voltage_milli_volt(&mut self) -> FatResult<u16>;
async fn average_current_milli_ampere(&mut self) -> FatResult<i16>;
async fn cycle_count(&mut self) -> FatResult<u16>;
async fn state_health_percent(&mut self) -> FatResult<u16>;
async fn bat_temperature(&mut self) -> FatResult<u16>;
async fn get_battery_state(&mut self) -> FatResult<BatteryState>;
}
#[derive(Debug, Serialize)]
@@ -37,14 +43,6 @@ pub enum BatteryError {
CommunicationError(String),
}
impl From<Bq34Z100Error<esp_idf_hal::i2c::I2cError>> for BatteryError {
fn from(err: Bq34Z100Error<esp_idf_hal::i2c::I2cError>) -> Self {
BatteryError::CommunicationError(
anyhow!("failed to communicate with battery monitor: {:?}", err).to_string(),
)
}
}
#[derive(Debug, Serialize)]
pub enum BatteryState {
Unknown,
@@ -53,45 +51,46 @@ pub enum BatteryState {
/// If no battery monitor is installed this implementation will be used
pub struct NoBatteryMonitor {}
#[async_trait]
impl BatteryInteraction for NoBatteryMonitor {
fn state_charge_percent(&mut self) -> Result<f32, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
async fn state_charge_percent(&mut self) -> FatResult<f32> {
// No monitor configured: assume full battery for lightstate logic
Ok(100.0)
}
fn remaining_milli_ampere_hour(&mut self) -> Result<u16, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
async fn remaining_milli_ampere_hour(&mut self) -> FatResult<u16> {
Err(FatError::NoBatteryMonitor)
}
fn max_milli_ampere_hour(&mut self) -> Result<u16, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
async fn max_milli_ampere_hour(&mut self) -> FatResult<u16> {
Err(FatError::NoBatteryMonitor)
}
fn design_milli_ampere_hour(&mut self) -> Result<u16, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
async fn design_milli_ampere_hour(&mut self) -> FatResult<u16> {
Err(FatError::NoBatteryMonitor)
}
fn voltage_milli_volt(&mut self) -> Result<u16, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
async fn voltage_milli_volt(&mut self) -> FatResult<u16> {
Err(FatError::NoBatteryMonitor)
}
fn average_current_milli_ampere(&mut self) -> Result<i16, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
async fn average_current_milli_ampere(&mut self) -> FatResult<i16> {
Err(FatError::NoBatteryMonitor)
}
fn cycle_count(&mut self) -> Result<u16, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
async fn cycle_count(&mut self) -> FatResult<u16> {
Err(FatError::NoBatteryMonitor)
}
fn state_health_percent(&mut self) -> Result<u16, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
async fn state_health_percent(&mut self) -> FatResult<u16> {
Err(FatError::NoBatteryMonitor)
}
fn bat_temperature(&mut self) -> Result<u16, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
async fn bat_temperature(&mut self) -> FatResult<u16> {
Err(FatError::NoBatteryMonitor)
}
fn get_battery_state(&mut self) -> Result<BatteryState, BatteryError> {
async fn get_battery_state(&mut self) -> FatResult<BatteryState> {
Ok(BatteryState::Unknown)
}
}
@@ -100,115 +99,167 @@ impl BatteryInteraction for NoBatteryMonitor {
#[allow(dead_code)]
pub struct WchI2cSlave {}
pub struct BQ34Z100G1<'a> {
pub battery_driver: Bq34z100g1Driver<MutexDevice<'a, I2cDriver<'a>>, Delay>,
pub type I2cDev = I2cDevice<'static, CriticalSectionRawMutex, I2c<'static, Blocking>>;
pub struct BQ34Z100G1 {
pub battery_driver: Bq34z100g1Driver<I2cDev, Delay>,
}
impl BatteryInteraction for BQ34Z100G1<'_> {
fn state_charge_percent(&mut self) -> Result<f32, BatteryError> {
Ok(self.battery_driver.state_of_charge().map(f32::from)?)
#[async_trait]
impl BatteryInteraction for BQ34Z100G1 {
async fn state_charge_percent(&mut self) -> FatResult<f32> {
self.battery_driver
.state_of_charge()
.map(|v| v as f32)
.map_err(|e| FatError::String {
error: alloc::format!("{:?}", e),
})
}
fn remaining_milli_ampere_hour(&mut self) -> Result<u16, BatteryError> {
Ok(self.battery_driver.remaining_capacity()?)
async fn remaining_milli_ampere_hour(&mut self) -> FatResult<u16> {
self.battery_driver
.remaining_capacity()
.map_err(|e| FatError::String {
error: alloc::format!("{:?}", e),
})
}
fn max_milli_ampere_hour(&mut self) -> Result<u16, BatteryError> {
Ok(self.battery_driver.full_charge_capacity()?)
async fn max_milli_ampere_hour(&mut self) -> FatResult<u16> {
self.battery_driver
.full_charge_capacity()
.map_err(|e| FatError::String {
error: alloc::format!("{:?}", e),
})
}
fn design_milli_ampere_hour(&mut self) -> Result<u16, BatteryError> {
Ok(self.battery_driver.design_capacity()?)
async fn design_milli_ampere_hour(&mut self) -> FatResult<u16> {
self.battery_driver
.design_capacity()
.map_err(|e| FatError::String {
error: alloc::format!("{:?}", e),
})
}
fn voltage_milli_volt(&mut self) -> Result<u16, BatteryError> {
Ok(self.battery_driver.voltage()?)
async fn voltage_milli_volt(&mut self) -> FatResult<u16> {
self.battery_driver.voltage().map_err(|e| FatError::String {
error: alloc::format!("{:?}", e),
})
}
fn average_current_milli_ampere(&mut self) -> Result<i16, BatteryError> {
Ok(self.battery_driver.average_current()?)
async fn average_current_milli_ampere(&mut self) -> FatResult<i16> {
self.battery_driver
.average_current()
.map_err(|e| FatError::String {
error: alloc::format!("{:?}", e),
})
}
fn cycle_count(&mut self) -> Result<u16, BatteryError> {
Ok(self.battery_driver.cycle_count()?)
async fn cycle_count(&mut self) -> FatResult<u16> {
self.battery_driver
.cycle_count()
.map_err(|e| FatError::String {
error: alloc::format!("{:?}", e),
})
}
fn state_health_percent(&mut self) -> Result<u16, BatteryError> {
Ok(self.battery_driver.state_of_health()?)
async fn state_health_percent(&mut self) -> FatResult<u16> {
self.battery_driver
.state_of_health()
.map_err(|e| FatError::String {
error: alloc::format!("{:?}", e),
})
}
fn bat_temperature(&mut self) -> Result<u16, BatteryError> {
Ok(self.battery_driver.temperature()?)
async fn bat_temperature(&mut self) -> FatResult<u16> {
self.battery_driver
.temperature()
.map_err(|e| FatError::String {
error: alloc::format!("{:?}", e),
})
}
fn get_battery_state(&mut self) -> Result<BatteryState, BatteryError> {
async fn get_battery_state(&mut self) -> FatResult<BatteryState> {
Ok(BatteryState::Info(BatteryInfo {
voltage_milli_volt: self.voltage_milli_volt()?,
average_current_milli_ampere: self.average_current_milli_ampere()?,
cycle_count: self.cycle_count()?,
design_milli_ampere_hour: self.design_milli_ampere_hour()?,
remaining_milli_ampere_hour: self.remaining_milli_ampere_hour()?,
state_of_charge: self.state_charge_percent()?,
state_of_health: self.state_health_percent()?,
temperature: self.bat_temperature()?,
voltage_milli_volt: self.voltage_milli_volt().await?,
average_current_milli_ampere: self.average_current_milli_ampere().await?,
cycle_count: self.cycle_count().await?,
design_milli_ampere_hour: self.design_milli_ampere_hour().await?,
remaining_milli_ampere_hour: self.remaining_milli_ampere_hour().await?,
state_of_charge: self.state_charge_percent().await?,
state_of_health: self.state_health_percent().await?,
temperature: self.bat_temperature().await?,
}))
}
}
pub fn print_battery_bq34z100(
battery_driver: &mut Bq34z100g1Driver<MutexDevice<I2cDriver<'_>>, Delay>,
) -> anyhow::Result<(), Bq34Z100Error<I2cError>> {
println!("Try communicating with battery");
battery_driver: &mut Bq34z100g1Driver<I2cDevice<CriticalSectionRawMutex, I2c<Blocking>>, Delay>,
) -> FatResult<()> {
log::info!("Try communicating with battery");
let fwversion = battery_driver.fw_version().unwrap_or_else(|e| {
println!("Firmware {:?}", e);
log::info!("Firmware {:?}", e);
0
});
println!("fw version is {}", fwversion);
log::info!("fw version is {}", fwversion);
let design_capacity = battery_driver.design_capacity().unwrap_or_else(|e| {
println!("Design capacity {:?}", e);
log::info!("Design capacity {:?}", e);
0
});
println!("Design Capacity {}", design_capacity);
log::info!("Design Capacity {}", design_capacity);
if design_capacity == 1000 {
println!("Still stock configuring battery, readouts are likely to be wrong!");
log::info!("Still stock configuring battery, readouts are likely to be wrong!");
}
let flags = battery_driver.get_flags_decoded()?;
println!("Flags {:?}", flags);
let flags = battery_driver.get_flags_decoded().unwrap_or(Flags {
fast_charge_allowed: false,
full_chage: false,
charging_not_allowed: false,
charge_inhibit: false,
bat_low: false,
bat_high: false,
over_temp_discharge: false,
over_temp_charge: false,
discharge: false,
state_of_charge_f: false,
state_of_charge_1: false,
cf: false,
ocv_taken: false,
});
log::info!("Flags {:?}", flags);
let chem_id = battery_driver.chem_id().unwrap_or_else(|e| {
println!("Chemid {:?}", e);
log::info!("Chemid {:?}", e);
0
});
let bat_temp = battery_driver.internal_temperature().unwrap_or_else(|e| {
println!("Bat Temp {:?}", e);
log::info!("Bat Temp {:?}", e);
0
});
let temp_c = Temperature::from_kelvin(bat_temp as f64 / 10_f64).as_celsius();
let voltage = battery_driver.voltage().unwrap_or_else(|e| {
println!("Bat volt {:?}", e);
log::info!("Bat volt {:?}", e);
0
});
let current = battery_driver.current().unwrap_or_else(|e| {
println!("Bat current {:?}", e);
log::info!("Bat current {:?}", e);
0
});
let state = battery_driver.state_of_charge().unwrap_or_else(|e| {
println!("Bat Soc {:?}", e);
log::info!("Bat Soc {:?}", e);
0
});
let charge_voltage = battery_driver.charge_voltage().unwrap_or_else(|e| {
println!("Bat Charge Volt {:?}", e);
log::info!("Bat Charge Volt {:?}", e);
0
});
let charge_current = battery_driver.charge_current().unwrap_or_else(|e| {
println!("Bat Charge Current {:?}", e);
log::info!("Bat Charge Current {:?}", e);
0
});
println!("ChemId: {} Current voltage {} and current {} with charge {}% and temp {} CVolt: {} CCur {}", chem_id, voltage, current, state, temp_c, charge_voltage, charge_current);
log::info!("ChemId: {} Current voltage {} and current {} with charge {}% and temp {} CVolt: {} CCur {}", chem_id, voltage, current, state, temp_c, charge_voltage, charge_current);
let _ = battery_driver.unsealed();
let _ = battery_driver.it_enable();
anyhow::Result::Ok(())
Ok(())
}

Some files were not shown because too many files have changed in this diff Show More