Compare commits
45 Commits
develop
...
45e948636b
| Author | SHA1 | Date | |
|---|---|---|---|
| 45e948636b | |||
| 083573de4a | |||
| e5c5f31112 | |||
| 7f3910bcd0 | |||
| 712e8c8b8f | |||
| 4ba68182e5 | |||
| a3cdd92af8 | |||
| 894be7c373 | |||
| 0ddf6a6886 | |||
| 27b18df78e | |||
| 9c6dcc465e | |||
| cf58486cf5 | |||
| cfe23c8a09 | |||
| 3d18b0dbf6 | |||
| 7ebc147f51 | |||
| f0bda32d7a | |||
| 76f59b093d | |||
| 7fc8d0c882 | |||
| 6d5bb5b966 | |||
| 336961f0a0 | |||
| e20b474dfd | |||
| 5b009f50e5 | |||
| d010c5d12a | |||
| b594a02870 | |||
| 1f3349c348 | |||
| 5b0e2b6797 | |||
| 1791f463b7 | |||
| c94f5bdb45 | |||
| 584d6df2d0 | |||
| cd63e76469 | |||
| 4c54edbcea | |||
| 8b938e7687 | |||
| 1c84cd00da | |||
| 1397f5d775 | |||
| 65f6670ca4 | |||
| 049a9d027c | |||
| 4aa25c687b | |||
| b3cc313139 | |||
| be3c4a5095 | |||
| 4160202cdc | |||
| 9de85b6e37 | |||
| 79087c9353 | |||
|
0d495d0f56
|
|||
|
242e748ca0
|
|||
|
f853b6f2b2
|
Binary file not shown.
2
board/.gitignore
vendored
2
board/.gitignore
vendored
@@ -34,3 +34,5 @@ _autosave-*
|
||||
|
||||
# Autorouter files (exported from Pcbnew)
|
||||
fp-info-cache
|
||||
*.zip
|
||||
netlist.ipc
|
||||
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
1
board/modules/3v3/fabrication-toolkit-options.json
Normal file
1
board/modules/3v3/fabrication-toolkit-options.json
Normal 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}
|
||||
1
board/modules/LightOut/fabrication-toolkit-options.json
Normal file
1
board/modules/LightOut/fabrication-toolkit-options.json
Normal 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}
|
||||
1
board/modules/MPPT/fabrication-toolkit-options.json
Normal file
1
board/modules/MPPT/fabrication-toolkit-options.json
Normal 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}
|
||||
@@ -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}
|
||||
@@ -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}
|
||||
4
board/modules/Sensors_can/Sensors/fp-lib-table
Normal file
4
board/modules/Sensors_can/Sensors/fp-lib-table
Normal 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 ""))
|
||||
)
|
||||
71
board/modules/Sensors_can/Sensors/production/netlist.ipc
Normal file
71
board/modules/Sensors_can/Sensors/production/netlist.ipc
Normal 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
|
||||
32
board/modules/Sensors_can/ch32-sensor/.doomrc
Normal file
32
board/modules/Sensors_can/ch32-sensor/.doomrc
Normal 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
|
||||
8
board/modules/Sensors_can/ch32-sensor/.idea/.gitignore
generated
vendored
Normal file
8
board/modules/Sensors_can/ch32-sensor/.idea/.gitignore
generated
vendored
Normal 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
|
||||
11
board/modules/Sensors_can/ch32-sensor/.idea/ch32-sensor.iml
generated
Normal file
11
board/modules/Sensors_can/ch32-sensor/.idea/ch32-sensor.iml
generated
Normal 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>
|
||||
@@ -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
10
bootloader/.gitignore
vendored
Normal 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
8
bootloader/.idea/.gitignore
generated
vendored
Normal 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
8
bootloader/.idea/bootloader.iml
generated
Normal 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
8
bootloader/.idea/modules.xml
generated
Normal 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
7
bootloader/.idea/vcs.xml
generated
Normal 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
11
bootloader/CMakeLists.txt
Normal 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
43
bootloader/README.md
Normal 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 doesn’t 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
41
bootloader/build_bootloader.sh
Executable 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
|
||||
1
bootloader/main/CMakeLists.txt
Normal file
1
bootloader/main/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
idf_component_register(SRCS "dummy.c" INCLUDE_DIRS ".")
|
||||
4
bootloader/main/dummy.c
Normal file
4
bootloader/main/dummy.c
Normal 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) {}
|
||||
6
bootloader/partitions.csv
Normal file
6
bootloader/partitions.csv
Normal 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,
|
||||
|
2385
bootloader/sdkconfig
Normal file
2385
bootloader/sdkconfig
Normal file
File diff suppressed because it is too large
Load Diff
17
bootloader/sdkconfig.defaults
Normal file
17
bootloader/sdkconfig.defaults
Normal 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
2214
bootloader/sdkconfig.old
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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"]
|
||||
|
||||
|
||||
4
rust/.idea/dictionaries/project.xml
generated
4
rust/.idea/dictionaries/project.xml
generated
@@ -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>
|
||||
@@ -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>
|
||||
203
rust/Cargo.toml
203
rust/Cargo.toml
@@ -1,113 +1,162 @@
|
||||
[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]
|
||||
# Shared CAN API
|
||||
canapi = { path = "canapi" }
|
||||
#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
12
rust/all.sh
Executable 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
BIN
rust/bootloader.bin
Normal file
Binary file not shown.
105
rust/build.rs
105
rust/build.rs
@@ -1,70 +1,53 @@
|
||||
use std::process::Command;
|
||||
|
||||
use vergen::EmitBuilder;
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=./src/src_webpack");
|
||||
Command::new("rm")
|
||||
.arg("./src/webserver/bundle.js")
|
||||
.output()
|
||||
.unwrap();
|
||||
|
||||
match Command::new("cmd").spawn() {
|
||||
Ok(_) => {
|
||||
println!("Assuming build on windows");
|
||||
let output = Command::new("cmd")
|
||||
.arg("/K")
|
||||
.arg("npx")
|
||||
.arg("webpack")
|
||||
.current_dir("./src_webpack")
|
||||
.output()
|
||||
.unwrap();
|
||||
println!("status: {}", output.status);
|
||||
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
|
||||
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
|
||||
assert!(output.status.success());
|
||||
fn linker_be_nice() {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
if args.len() > 1 {
|
||||
let kind = &args[1];
|
||||
let what = &args[2];
|
||||
|
||||
// move webpack results to rust webserver src
|
||||
let _ = Command::new("cmd")
|
||||
.arg("/K")
|
||||
.arg("move")
|
||||
.arg("./src_webpack/bundle.js")
|
||||
.arg("./src/webserver")
|
||||
.output()
|
||||
.unwrap();
|
||||
let _ = Command::new("cmd")
|
||||
.arg("/K")
|
||||
.arg("move")
|
||||
.arg("./src_webpack/index.html")
|
||||
.arg("./src/webserver")
|
||||
.output()
|
||||
.unwrap();
|
||||
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);
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
println!("Assuming build on linux");
|
||||
let output = Command::new("npx")
|
||||
.arg("webpack")
|
||||
.current_dir("./src_webpack")
|
||||
.output()
|
||||
.unwrap();
|
||||
println!("status: {}", output.status);
|
||||
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
|
||||
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
|
||||
assert!(output.status.success());
|
||||
|
||||
// move webpack results to rust webserver src
|
||||
let _ = Command::new("mv")
|
||||
.arg("./src_webpack/bundle.js")
|
||||
.arg("./src/webserver")
|
||||
.output()
|
||||
.unwrap();
|
||||
let _ = Command::new("mv")
|
||||
.arg("./src_webpack/index.html")
|
||||
.arg("./src/webserver")
|
||||
.output()
|
||||
.unwrap();
|
||||
}
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
embuild::espidf::sysenv::output();
|
||||
println!(
|
||||
"cargo:rustc-link-arg=--error-handling-script={}",
|
||||
std::env::current_exe().unwrap().display()
|
||||
);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
linker_be_nice();
|
||||
let _ = EmitBuilder::builder().all_git().all_build().emit();
|
||||
}
|
||||
|
||||
14
rust/canapi/Cargo.toml
Normal file
14
rust/canapi/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "canapi"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "canapi"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
bincode = { version = "2.0.1", default-features = false, features = ["derive"] }
|
||||
138
rust/canapi/src/lib.rs
Normal file
138
rust/canapi/src/lib.rs
Normal file
@@ -0,0 +1,138 @@
|
||||
#![no_std]
|
||||
//! CAN bus API shared crate for PlantCtrl sensors and controller.
|
||||
//! Addressing and messages are defined here to be reused by all bus participants.
|
||||
|
||||
use bincode::{Decode, Encode};
|
||||
|
||||
/// Total plants supported by addressing (0..=15)
|
||||
pub const MAX_PLANTS: u8 = 16;
|
||||
|
||||
/// Sensors per plant: 0..=1 => A/B
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
|
||||
#[repr(u8)]
|
||||
pub enum SensorSlot {
|
||||
A = 0,
|
||||
B = 1,
|
||||
}
|
||||
|
||||
impl SensorSlot {
|
||||
pub const fn from_index(idx: u8) -> Option<Self> {
|
||||
match idx {
|
||||
0 => Some(SensorSlot::A),
|
||||
1 => Some(SensorSlot::B),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Legacy sensor base address kept for compatibility with existing code.
|
||||
/// Each plant uses SENSOR_BASE_ADDRESS + plant_index (0..PLANT_COUNT-1).
|
||||
/// 11-bit standard ID space, safe range.
|
||||
pub const SENSOR_BASE_ADDRESS: u16 = 1000;
|
||||
|
||||
/// Typed topics within the SENSOR_BASE space.
|
||||
/// Additional offsets allow distinct message semantics while keeping plant-indexed layout.
|
||||
pub mod id {
|
||||
use crate::{SensorSlot, MAX_PLANTS, SENSOR_BASE_ADDRESS};
|
||||
|
||||
/// Number of plants addressable per sensor slot group
|
||||
pub const PLANTS_PER_GROUP: u16 = MAX_PLANTS as u16; // 16
|
||||
/// Offset applied for SensorSlot::B within a message group
|
||||
pub const B_OFFSET: u16 = PLANTS_PER_GROUP; // 16
|
||||
|
||||
// Message group base offsets relative to SENSOR_BASE_ADDRESS
|
||||
pub const MOISTURE_DATA_OFFSET: u16 = 0; // periodic data from sensor (sensor -> controller)
|
||||
pub const IDENTIFY_CMD_OFFSET: u16 = 32; // identify LED command (controller -> sensor)
|
||||
|
||||
// Convenience constants for per-slot base offsets
|
||||
pub const IDENTIFY_CMD_OFFSET_A: u16 = IDENTIFY_CMD_OFFSET + 0;
|
||||
pub const IDENTIFY_CMD_OFFSET_B: u16 = IDENTIFY_CMD_OFFSET + B_OFFSET;
|
||||
|
||||
#[inline]
|
||||
pub const fn plant_id(message_type_offset: u16, sensor: SensorSlot, plant: u16) -> u16 {
|
||||
match sensor {
|
||||
SensorSlot::A => SENSOR_BASE_ADDRESS + message_type_offset + plant,
|
||||
SensorSlot::B => SENSOR_BASE_ADDRESS + message_type_offset + B_OFFSET + plant,
|
||||
}
|
||||
}
|
||||
|
||||
/// Kinds of message spaces recognized by the addressing scheme.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum MessageKind {
|
||||
MoistureData, // sensor -> controller
|
||||
IdentifyCmd, // controller -> sensor
|
||||
}
|
||||
|
||||
/// Try to classify a received 11-bit standard ID into a known message kind and extract plant and sensor slot.
|
||||
/// Returns (kind, plant, slot) on success.
|
||||
#[inline]
|
||||
pub const fn classify(id: u16) -> Option<(MessageKind, u8, SensorSlot)> {
|
||||
// Ensure the ID is within our base space
|
||||
if id < SENSOR_BASE_ADDRESS {
|
||||
return None;
|
||||
}
|
||||
let rel = id - SENSOR_BASE_ADDRESS;
|
||||
|
||||
// Helper: decode within a given group offset
|
||||
const fn decode_in_group(rel: u16, group_base: u16) -> Option<(u8, SensorSlot)> {
|
||||
if rel < group_base { return None; }
|
||||
let inner = rel - group_base;
|
||||
if inner < PLANTS_PER_GROUP { // A slot
|
||||
Some((inner as u8, SensorSlot::A))
|
||||
} else if inner >= B_OFFSET && inner < B_OFFSET + PLANTS_PER_GROUP { // B slot
|
||||
Some(((inner - B_OFFSET) as u8, SensorSlot::B))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Check known groups in order
|
||||
if let Some((plant, slot)) = decode_in_group(rel, MOISTURE_DATA_OFFSET) {
|
||||
return Some((MessageKind::MoistureData, plant, slot));
|
||||
}
|
||||
if let Some((plant, slot)) = decode_in_group(rel, IDENTIFY_CMD_OFFSET) {
|
||||
return Some((MessageKind::IdentifyCmd, plant, slot));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns Some((plant, slot)) regardless of message kind, if the id falls into any known group; otherwise None.
|
||||
#[inline]
|
||||
pub const fn extract_plant_slot(id: u16) -> Option<(u8, SensorSlot)> {
|
||||
match classify(id) {
|
||||
Some((_kind, plant, slot)) => Some((plant, slot)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if an id corresponds exactly to the given message kind, plant and slot.
|
||||
#[inline]
|
||||
pub const fn is_identify_for(id: u16, plant: u8, slot: SensorSlot) -> bool {
|
||||
id == plant_id(IDENTIFY_CMD_OFFSET, slot, plant as u16)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn is_moisture_data_for(id: u16, plant: u8, slot: SensorSlot) -> bool {
|
||||
id == plant_id(MOISTURE_DATA_OFFSET, slot, plant as u16)
|
||||
}
|
||||
}
|
||||
|
||||
/// Periodic moisture data sent by sensors.
|
||||
/// Fits into 5 bytes with bincode-v2 (no varint): u8 + u8 + u16 = 4, alignment may keep 4.
|
||||
#[derive(Debug, Clone, Copy, Encode, Decode)]
|
||||
pub struct MoistureData {
|
||||
pub plant: u8, // 0..MAX_PLANTS-1
|
||||
pub sensor: SensorSlot, // A/B
|
||||
pub hz: u16, // measured frequency of moisture sensor
|
||||
}
|
||||
|
||||
/// Request a sensor to report immediately (controller -> sensor).
|
||||
#[derive(Debug, Clone, Copy, Encode, Decode)]
|
||||
pub struct MoistureRequest {
|
||||
pub plant: u8,
|
||||
pub sensor: SensorSlot, // target sensor (sensor filters by this)
|
||||
}
|
||||
|
||||
/// Control a sensor's identify LED, if received by sensor, blink for a few seconds
|
||||
#[derive(Debug, Clone, Copy, Encode, Decode)]
|
||||
pub struct IdentifyLed {}
|
||||
@@ -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
11
rust/flash.sh
Executable 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
13
rust/image_build.sh
Executable 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
|
||||
@@ -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,3 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "nightly"
|
||||
toolchain = "esp"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user