wip min
This commit is contained in:
parent
a38d704498
commit
a8e17cda3b
BIN
board/Body1.3mf
Normal file
BIN
board/Body1.3mf
Normal file
Binary file not shown.
BIN
board/modules/MPPT/battery-charging.png
Normal file
BIN
board/modules/MPPT/battery-charging.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
6
board/modules/MPPT/battery-charging.svg
Normal file
6
board/modules/MPPT/battery-charging.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-battery-charging" viewBox="0 0 16 16">
|
||||||
|
<path d="M9.585 2.568a.5.5 0 0 1 .226.58L8.677 6.832h1.99a.5.5 0 0 1 .364.843l-5.334 5.667a.5.5 0 0 1-.842-.49L5.99 9.167H4a.5.5 0 0 1-.364-.843l5.333-5.667a.5.5 0 0 1 .616-.09z"/>
|
||||||
|
<path d="M2 4h4.332l-.94 1H2a1 1 0 0 0-1 1v4a1 1 0 0 0 1 1h2.38l-.308 1H2a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2"/>
|
||||||
|
<path d="M2 6h2.45L2.908 7.639A1.5 1.5 0 0 0 3.313 10H2zm8.595-2-.308 1H12a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H9.276l-.942 1H12a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2z"/>
|
||||||
|
<path d="M12 10h-1.783l1.542-1.639q.146-.156.241-.34zm0-3.354V6h-.646a1.5 1.5 0 0 1 .646.646M16 8a1.5 1.5 0 0 1-1.5 1.5v-3A1.5 1.5 0 0 1 16 8"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 738 B |
4
board/modules/Sensors_can/Sensors/sym-lib-table
Normal file
4
board/modules/Sensors_can/Sensors/sym-lib-table
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
(sym_lib_table
|
||||||
|
(version 7)
|
||||||
|
(lib (name "Modules")(type "KiCad")(uri "/home/empire/workspace/PlantCtrl/board/modules/Modules.kicad_sym")(options "")(descr ""))
|
||||||
|
)
|
14
board/modules/Sensors_can/ch32-sensor/.cargo/config.toml
Normal file
14
board/modules/Sensors_can/ch32-sensor/.cargo/config.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[build]
|
||||||
|
target = "riscv32imc-unknown-none-elf"
|
||||||
|
|
||||||
|
[target."riscv32imc-unknown-none-elf"]
|
||||||
|
rustflags = [
|
||||||
|
# "-C", "link-arg=-Tlink.x",
|
||||||
|
]
|
||||||
|
# runner = "riscv64-unknown-elf-gdb -q -x openocd.gdb"
|
||||||
|
# runner = "riscv-none-embed-gdb -q -x openocd.gdb"
|
||||||
|
# runner = "gdb -q -x openocd.gdb"
|
||||||
|
# runner = "wlink -v flash"
|
||||||
|
|
||||||
|
runner = "wlink -v flash --enable-sdi-print --watch-serial --erase"
|
||||||
|
# runner = "wlink -v flash"
|
7
board/modules/Sensors_can/ch32-sensor/.gdbinit
Normal file
7
board/modules/Sensors_can/ch32-sensor/.gdbinit
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
target extended-remote :3333
|
||||||
|
set remotetimeout 2000
|
||||||
|
|
||||||
|
#symbol-file target/riscv32imc-unknown-none-elf/release/ch32v203-examples
|
||||||
|
file target/riscv32imc-unknown-none-elf/release/bms
|
||||||
|
|
||||||
|
monitor reset halt
|
1
board/modules/Sensors_can/ch32-sensor/.gitignore
vendored
Normal file
1
board/modules/Sensors_can/ch32-sensor/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
target
|
43
board/modules/Sensors_can/ch32-sensor/Cargo.toml
Normal file
43
board/modules/Sensors_can/ch32-sensor/Cargo.toml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
[package]
|
||||||
|
name = "bms"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ch32-hal = { git = "https://github.com/ch32-rs/ch32-hal", features = [
|
||||||
|
"ch32v203c8t6",
|
||||||
|
"memory-x",
|
||||||
|
"embassy",
|
||||||
|
"rt",
|
||||||
|
"time-driver-tim2",
|
||||||
|
], default-features = false }
|
||||||
|
|
||||||
|
embassy-executor = { version = "0.7.0", features = [
|
||||||
|
"arch-riscv32",
|
||||||
|
"executor-thread",
|
||||||
|
] }
|
||||||
|
|
||||||
|
embassy-time = { version = "0.4.0" }
|
||||||
|
embassy-usb = { version = "0.3.0" }
|
||||||
|
embassy-futures = { version = "0.1.0" }
|
||||||
|
|
||||||
|
# This is okay because we should automatically use whatever ch32-hal uses
|
||||||
|
qingke-rt = "*"
|
||||||
|
qingke = "*"
|
||||||
|
|
||||||
|
panic-halt = "1.0"
|
||||||
|
|
||||||
|
embedded-hal = "1.0.0"
|
||||||
|
heapless = "0.8.0"
|
||||||
|
micromath = { version = "2.1.0", features = ["num-traits"] }
|
||||||
|
embedded-can = "0.4.1"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
#lto = true
|
||||||
|
opt-level = 1
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = false # symbols are not flashed to the microcontroller, so don't strip them.
|
||||||
|
#lto = true
|
||||||
|
debug = true
|
||||||
|
opt-level = "z" # Optimize for size.
|
72
board/modules/Sensors_can/ch32-sensor/README.md
Normal file
72
board/modules/Sensors_can/ch32-sensor/README.md
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
|
||||||
|
# ch32v203-bms
|
||||||
|
|
||||||
|
A simple battery management controller software.
|
||||||
|
|
||||||
|
## CAN bus and hardware address
|
||||||
|
|
||||||
|
This firmware exposes a CAN interface on the CH32V203 and uses 4 hardware address pins to allow up to 16 sensors on the same bus.
|
||||||
|
|
||||||
|
- CAN pins (default mapping):
|
||||||
|
- CAN RX: PA11
|
||||||
|
- CAN TX: PA12
|
||||||
|
- Address select pins (with internal pull-ups):
|
||||||
|
- A0: PA0
|
||||||
|
- A1: PA1
|
||||||
|
- A2: PA2
|
||||||
|
- A3: PA3
|
||||||
|
|
||||||
|
Wire each address pin to GND to set its corresponding bit to 1. The 4-bit address range is 0..15. The node’s CAN Standard ID is `0x100 | addr`, i.e. 0x100..0x10F. The CAN acceptance filter is configured to only accept frames with the node’s own ID.
|
||||||
|
|
||||||
|
Adjust the pins above if your PCB routes CAN or address lines to different pads.
|
||||||
|
|
||||||
|
## 555 timer (software) emulation mode
|
||||||
|
|
||||||
|
To save the BOM cost of a classic NE555 in simple oscillator applications, this firmware implements a minimal 555-like Schmitt trigger using the MCU’s ADC and a GPIO, approximating the behavior when the capacitor is charged/discharged via Q through a resistor, and the combined Trigger/Threshold senses the capacitor node.
|
||||||
|
|
||||||
|
- Pins used:
|
||||||
|
- Q output: PB2
|
||||||
|
- Combined Trigger/Threshold (ADC input): PA0
|
||||||
|
- Wiring:
|
||||||
|
- PB2 (Q) -> series resistor R -> capacitor node
|
||||||
|
- Capacitor node -> capacitor to GND
|
||||||
|
- Capacitor node -> PA0 (ADC input)
|
||||||
|
- Behavior:
|
||||||
|
- When ADC(PA0) <= ~1/3 Vref, PB2 is driven High.
|
||||||
|
- When ADC(PA0) >= ~2/3 Vref, PB2 is driven Low.
|
||||||
|
- Hysteresis avoids chatter; the actual charge/discharge dynamics follow your chosen R and C.
|
||||||
|
- Notes:
|
||||||
|
- Use an appropriate resistor from PB2 to the capacitor to set oscillation frequency. Start with 10k..100k and adjust with C.
|
||||||
|
- Ensure PA0 is routed to the capacitor node and left high impedance (no strong pull-ups/downs) so the ADC can sense the analog voltage.
|
||||||
|
- PB2 drives the on-board LED (if present), so the LED might blink at the oscillation frequency.
|
||||||
|
|
||||||
|
This mode is implemented in `src/main.rs` using `hal::adc::Adc::convert(&mut pin, SampleTime::...)` to take periodic samples and a simple state machine to toggle the Q output based on ~1/3 and ~2/3 Vref thresholds.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
``` sh
|
||||||
|
cargo build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
## Flash
|
||||||
|
|
||||||
|
``` sh
|
||||||
|
wchisp config reset
|
||||||
|
wchip wchisp flash target/riscv32imc-unknown-none-elf/release/bms
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
For debugging purposes a container file is provided together with wrapper scripts to start the containerized `openocd` and `riscv-gdb` transparently. The wrapper scripts assume that `podman` is setup.
|
||||||
|
|
||||||
|
Starting Debug server
|
||||||
|
|
||||||
|
```
|
||||||
|
./bin/openocd
|
||||||
|
```
|
||||||
|
|
||||||
|
Connecting with gdb for interactive debugging
|
||||||
|
|
||||||
|
```
|
||||||
|
./bin/gdb -f target/riscv32imc-unknown-none-elf/release/bms
|
||||||
|
```
|
10
board/modules/Sensors_can/ch32-sensor/bin/build-wch-tools-container.sh
Executable file
10
board/modules/Sensors_can/ch32-sensor/bin/build-wch-tools-container.sh
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CONTAINER_NAME="localhost/wch-dev-tools:latest"
|
||||||
|
CONTAINER_TOOLS_BASEDIR="$(dirname "$(readlink -f "$0")")"
|
||||||
|
|
||||||
|
pushd "$CONTAINER_TOOLS_BASEDIR"
|
||||||
|
podman build -t "$CONTAINER_NAME" -f "../wch-tools.Containerfile" .
|
||||||
|
popd
|
29
board/modules/Sensors_can/ch32-sensor/bin/gdb
Executable file
29
board/modules/Sensors_can/ch32-sensor/bin/gdb
Executable file
@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CONTAINER_IMAGE="localhost/wch-dev-tools:latest"
|
||||||
|
CONTAINER_TOOLS_BASEDIR="$(dirname "$(readlink -f "$0")")"
|
||||||
|
|
||||||
|
function _fatal {
|
||||||
|
echo -e "\e[31mERROR\e[0m $(</dev/stdin)$*" 1>&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
declare -a PODMAN_ARGS=(
|
||||||
|
"--rm" "-i" "--log-driver=none"
|
||||||
|
"--network=host"
|
||||||
|
"--pid=host"
|
||||||
|
"-v" "$PWD:$PWD:rw"
|
||||||
|
"-w" "$PWD"
|
||||||
|
)
|
||||||
|
|
||||||
|
[[ -t 1 ]] && PODMAN_ARGS+=("-t")
|
||||||
|
|
||||||
|
if ! podman image exists "$CONTAINER_IMAGE"; then
|
||||||
|
#attempt to build container
|
||||||
|
"$CONTAINER_TOOLS_BASEDIR/build-wch-tools-container.sh" 1>&2 ||
|
||||||
|
_fatal "faild to build local image, cannot continue! … please ensure you have an internet connection"
|
||||||
|
fi
|
||||||
|
|
||||||
|
podman run "${PODMAN_ARGS[@]}" --entrypoint riscv-none-elf-gdb-py3 "$CONTAINER_IMAGE" "$@"
|
44
board/modules/Sensors_can/ch32-sensor/bin/openocd
Executable file
44
board/modules/Sensors_can/ch32-sensor/bin/openocd
Executable file
@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CONTAINER_IMAGE="localhost/wch-dev-tools:latest"
|
||||||
|
CONTAINER_TOOLS_BASEDIR="$(dirname "$(readlink -f "$0")")"
|
||||||
|
|
||||||
|
function _fatal {
|
||||||
|
echo -e "\e[31mERROR\e[0m $(</dev/stdin)$*" 1>&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
declare -a PODMAN_ARGS=(
|
||||||
|
"--rm" "-i" "--log-driver=none"
|
||||||
|
"--network=host"
|
||||||
|
"-v" "$PWD:$PWD:rw"
|
||||||
|
"-w" "$PWD"
|
||||||
|
)
|
||||||
|
|
||||||
|
for device in /dev/bus/usb/*/*; do
|
||||||
|
if udevadm info "$device" | grep -q "ID_VENDOR=wch.cn" && \
|
||||||
|
udevadm info "$device" | grep -q "ID_MODEL=WCH-Link"; then
|
||||||
|
DEBUGGER_DEV_PATH="$device"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z "${DEBUGGER_DEV_PATH:-}" ]]; then
|
||||||
|
echo "Could not find hardware debugger … Exiting!" 1>&2
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
# add jlink to podman device
|
||||||
|
PODMAN_ARGS+=("--device=$DEBUGGER_DEV_PATH")
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ -t 1 ]] && PODMAN_ARGS+=("-t")
|
||||||
|
|
||||||
|
if ! podman image exists "$CONTAINER_IMAGE"; then
|
||||||
|
#attempt to build container
|
||||||
|
"$CONTAINER_TOOLS_BASEDIR/build-wch-tools-container.sh" 1>&2 ||
|
||||||
|
_fatal "faild to build local image, cannot continue! … please ensure you have an internet connection"
|
||||||
|
fi
|
||||||
|
|
||||||
|
podman run "${PODMAN_ARGS[@]}" --entrypoint openocd "$CONTAINER_IMAGE" "$@"
|
11
board/modules/Sensors_can/ch32-sensor/build.rs
Normal file
11
board/modules/Sensors_can/ch32-sensor/build.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fn main() {
|
||||||
|
// println!("cargo:rustc-link-arg-bins=--nmagic");
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
||||||
|
// println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
|
||||||
|
|
||||||
|
let out_dir = std::env::var("OUT_DIR").unwrap();
|
||||||
|
let out_dir = std::path::PathBuf::from(out_dir);
|
||||||
|
std::fs::write(out_dir.join("memory.x"), include_bytes!("memory.x")).unwrap();
|
||||||
|
println!("cargo:rustc-link-search={}", out_dir.display());
|
||||||
|
println!("cargo:rerun-if-changed=memory.x");
|
||||||
|
}
|
125
board/modules/Sensors_can/ch32-sensor/memory.x
Normal file
125
board/modules/Sensors_can/ch32-sensor/memory.x
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/* CH32V203c8t6 */
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x00000000, LENGTH = 64K /* BANK_1 */
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
}
|
||||||
|
REGION_ALIAS("REGION_TEXT", FLASH);
|
||||||
|
REGION_ALIAS("REGION_RODATA", FLASH);
|
||||||
|
REGION_ALIAS("REGION_DATA", RAM);
|
||||||
|
REGION_ALIAS("REGION_BSS", RAM);
|
||||||
|
REGION_ALIAS("REGION_HEAP", RAM);
|
||||||
|
REGION_ALIAS("REGION_STACK", RAM);
|
||||||
|
|
||||||
|
/* fault handlers */
|
||||||
|
|
||||||
|
PROVIDE(InstructionMisaligned = ExceptionHandler);
|
||||||
|
PROVIDE(InstructionFault = ExceptionHandler);
|
||||||
|
PROVIDE(IllegalInstruction = ExceptionHandler);
|
||||||
|
PROVIDE(Breakpoint = ExceptionHandler);
|
||||||
|
PROVIDE(LoadMisaligned = ExceptionHandler);
|
||||||
|
PROVIDE(LoadFault = ExceptionHandler);
|
||||||
|
PROVIDE(StoreMisaligned = ExceptionHandler);
|
||||||
|
PROVIDE(StoreFault = ExceptionHandler);;
|
||||||
|
PROVIDE(UserEnvCall = ExceptionHandler);
|
||||||
|
PROVIDE(SupervisorEnvCall = ExceptionHandler);
|
||||||
|
PROVIDE(MachineEnvCall = ExceptionHandler);
|
||||||
|
PROVIDE(InstructionPageFault = ExceptionHandler);
|
||||||
|
PROVIDE(LoadPageFault = ExceptionHandler);
|
||||||
|
PROVIDE(StorePageFault = ExceptionHandler);
|
||||||
|
|
||||||
|
/* core interrupt handlers */
|
||||||
|
|
||||||
|
PROVIDE(NonMaskableInt = DefaultHandler);
|
||||||
|
PROVIDE(Software = DefaultHandler);
|
||||||
|
|
||||||
|
/* external interrupt handlers */
|
||||||
|
|
||||||
|
PROVIDE(WWDG = DefaultHandler);
|
||||||
|
PROVIDE(PVD = DefaultHandler);
|
||||||
|
PROVIDE(TAMPER = DefaultHandler);
|
||||||
|
PROVIDE(RTC = DefaultHandler);
|
||||||
|
PROVIDE(FLASH = DefaultHandler);
|
||||||
|
PROVIDE(RCC = DefaultHandler);
|
||||||
|
PROVIDE(EXTI0 = DefaultHandler);
|
||||||
|
PROVIDE(EXTI1 = DefaultHandler);
|
||||||
|
PROVIDE(EXTI2 = DefaultHandler);
|
||||||
|
PROVIDE(EXTI3 = DefaultHandler);
|
||||||
|
PROVIDE(EXTI4 = DefaultHandler);
|
||||||
|
PROVIDE(DMA1_CHANNEL1 = DefaultHandler);
|
||||||
|
PROVIDE(DMA1_CHANNEL2 = DefaultHandler);
|
||||||
|
PROVIDE(DMA1_CHANNEL3 = DefaultHandler);
|
||||||
|
PROVIDE(DMA1_CHANNEL4 = DefaultHandler);
|
||||||
|
PROVIDE(DMA1_CHANNEL5 = DefaultHandler);
|
||||||
|
PROVIDE(DMA1_CHANNEL6 = DefaultHandler);
|
||||||
|
PROVIDE(DMA1_CHANNEL7 = DefaultHandler);
|
||||||
|
PROVIDE(ADC = DefaultHandler);
|
||||||
|
PROVIDE(USB_HP_CAN1_TX = DefaultHandler);
|
||||||
|
/*PROVIDE(USB_LP_CAN1_RX0 = DefaultHandler);*/
|
||||||
|
PROVIDE(CAN1_RX1 = DefaultHandler);
|
||||||
|
PROVIDE(CAN1_SCE = DefaultHandler);
|
||||||
|
PROVIDE(EXTI9_5 = DefaultHandler);
|
||||||
|
PROVIDE(TIM1_BRK = DefaultHandler);
|
||||||
|
PROVIDE(TIM1_UP_ = DefaultHandler);
|
||||||
|
PROVIDE(TIM1_TRG_COM = DefaultHandler);
|
||||||
|
PROVIDE(TIM1_CC = DefaultHandler);
|
||||||
|
PROVIDE(TIM2 = DefaultHandler);
|
||||||
|
PROVIDE(TIM3 = DefaultHandler);
|
||||||
|
PROVIDE(TIM4 = DefaultHandler);
|
||||||
|
PROVIDE(I2C1_EV = DefaultHandler);
|
||||||
|
PROVIDE(I2C1_ER = DefaultHandler);
|
||||||
|
PROVIDE(I2C2_EV = DefaultHandler);
|
||||||
|
PROVIDE(I2C2_ER = DefaultHandler);
|
||||||
|
PROVIDE(SPI1 = DefaultHandler);
|
||||||
|
PROVIDE(SPI2 = DefaultHandler);
|
||||||
|
PROVIDE(USART1 = DefaultHandler);
|
||||||
|
PROVIDE(USART2 = DefaultHandler);
|
||||||
|
PROVIDE(USART3 = DefaultHandler);
|
||||||
|
PROVIDE(EXTI15_10 = DefaultHandler);
|
||||||
|
PROVIDE(RTCALARM = DefaultHandler);
|
||||||
|
PROVIDE(USBWAKE_UP = DefaultHandler);
|
||||||
|
PROVIDE(TIM8_BRK = DefaultHandler);
|
||||||
|
PROVIDE(TIM8_UP_ = DefaultHandler);
|
||||||
|
PROVIDE(TIM8_TRG_COM = DefaultHandler);
|
||||||
|
PROVIDE(TIM8_CC = DefaultHandler);
|
||||||
|
PROVIDE(RNG = DefaultHandler);
|
||||||
|
PROVIDE(FSMC = DefaultHandler);
|
||||||
|
PROVIDE(SDIO = DefaultHandler);
|
||||||
|
PROVIDE(TIM5 = DefaultHandler);
|
||||||
|
PROVIDE(SPI3 = DefaultHandler);
|
||||||
|
PROVIDE(UART4 = DefaultHandler);
|
||||||
|
PROVIDE(UART5 = DefaultHandler);
|
||||||
|
PROVIDE(TIM6 = DefaultHandler);
|
||||||
|
PROVIDE(TIM7 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL1 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL2 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL3 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL4 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL5 = DefaultHandler);
|
||||||
|
PROVIDE(ETH = DefaultHandler);
|
||||||
|
PROVIDE(ETH_WKUP = DefaultHandler);
|
||||||
|
PROVIDE(CAN2_TX = DefaultHandler);
|
||||||
|
PROVIDE(CAN2_RX0 = DefaultHandler);
|
||||||
|
PROVIDE(CAN2_RX1 = DefaultHandler);
|
||||||
|
PROVIDE(CAN2_SCE = DefaultHandler);
|
||||||
|
PROVIDE(OTG_FS = DefaultHandler);
|
||||||
|
PROVIDE(USBHSWAKEUP = DefaultHandler);
|
||||||
|
PROVIDE(USBHS = DefaultHandler);
|
||||||
|
PROVIDE(DVP = DefaultHandler);
|
||||||
|
PROVIDE(UART6 = DefaultHandler);
|
||||||
|
PROVIDE(UART7 = DefaultHandler);
|
||||||
|
PROVIDE(UART8 = DefaultHandler);
|
||||||
|
PROVIDE(TIM9_BRK = DefaultHandler);
|
||||||
|
PROVIDE(TIM9_UP_ = DefaultHandler);
|
||||||
|
PROVIDE(TIM9_TRG_COM = DefaultHandler);
|
||||||
|
PROVIDE(TIM9_CC = DefaultHandler);
|
||||||
|
PROVIDE(TIM10_BRK = DefaultHandler);
|
||||||
|
PROVIDE(TIM10_UP_ = DefaultHandler);
|
||||||
|
PROVIDE(TIM10_TRG_COM = DefaultHandler);
|
||||||
|
PROVIDE(TIM10_CC = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL6 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL7 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL8 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL9 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL10 = DefaultHandler);
|
||||||
|
PROVIDE(DMA2_CHANNEL11 = DefaultHandler);
|
17
board/modules/Sensors_can/ch32-sensor/openocd.cfg
Normal file
17
board/modules/Sensors_can/ch32-sensor/openocd.cfg
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
set _CHIPNAME ch32v203
|
||||||
|
set _TARGETNAME $_CHIPNAME.cpu
|
||||||
|
|
||||||
|
#bindto 0.0.0.0
|
||||||
|
|
||||||
|
adapter driver wlinke
|
||||||
|
adapter speed 6000
|
||||||
|
transport select sdi
|
||||||
|
|
||||||
|
sdi newtap $_CHIPNAME cpu -irlen 5 --expected-id 0x00001
|
||||||
|
target create $_TARGETNAME.0 wch_riscv -chain-position $_TARGETNAME
|
||||||
|
$_TARGETNAME.0 configure -work-area-phys 0x20000000 -work-area-size 10000 -work-area-backup 1
|
||||||
|
set _FLASHNAME $_CHIPNAME.flash
|
||||||
|
|
||||||
|
flash bank $_FLASHNAME wch_rsicv 0x00000000 0 0 0 $_TARGETNAME.0
|
||||||
|
|
||||||
|
init
|
@ -0,0 +1,2 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
61
board/modules/Sensors_can/ch32-sensor/src/main.rs
Normal file
61
board/modules/Sensors_can/ch32-sensor/src/main.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
#![feature(impl_trait_in_assoc_type)]
|
||||||
|
|
||||||
|
// Simple 555-like oscillator implemented in firmware.
|
||||||
|
// - Q output: PB2 (also drives the on-board LED if present)
|
||||||
|
// - Combined Trigger/Threshold analog input: PA0 (capacitor node)
|
||||||
|
// Wiring suggestion:
|
||||||
|
// Q (PB2) --[R]--+-- C -- GND
|
||||||
|
// |
|
||||||
|
// PA0 (ADC input)
|
||||||
|
// The firmware toggles Q high when PA0 <= 1/3 Vref and low when PA0 >= 2/3 Vref.
|
||||||
|
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::Timer;
|
||||||
|
use hal::gpio::{Level, Output};
|
||||||
|
use {ch32_hal as hal, panic_halt as _};
|
||||||
|
|
||||||
|
use hal::adc::{Adc, SampleTime};
|
||||||
|
|
||||||
|
#[embassy_executor::main(entry = "qingke_rt::entry")]
|
||||||
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
|
let p = hal::init(Default::default());
|
||||||
|
|
||||||
|
// Q output on PB2
|
||||||
|
let mut q = Output::new(p.PB2, Level::Low, Default::default());
|
||||||
|
|
||||||
|
// ADC on PA0 for combined Trigger/Threshold input
|
||||||
|
let mut adc = Adc::new(p.ADC1, Default::default());
|
||||||
|
let mut trig_thres = p.PA0; // analog-capable pin used as ADC channel
|
||||||
|
|
||||||
|
// ADC characteristics: assume 12-bit if HAL doesn't expose it.
|
||||||
|
// If the HAL provides a method to query resolution, prefer that.
|
||||||
|
let full_scale: u16 = 4095; // 12-bit default
|
||||||
|
let thr_low: u16 = (full_scale as u32 / 3) as u16; // ~1/3 Vref
|
||||||
|
let thr_high: u16 = ((full_scale as u32 * 2) / 3) as u16; // ~2/3 Vref
|
||||||
|
|
||||||
|
// Start with Q low. State variable to avoid redundant toggles.
|
||||||
|
let mut q_high = false;
|
||||||
|
q.set_low();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Read capacitor node voltage via ADC
|
||||||
|
let sample: u16 = adc.convert(&mut trig_thres, SampleTime::CYCLES239_5);
|
||||||
|
|
||||||
|
// Implement Schmitt trigger behavior like NE555 using thresholds
|
||||||
|
if !q_high && sample <= thr_low {
|
||||||
|
// Trigger: voltage fell below 1/3 Vref -> set output high
|
||||||
|
q.set_high();
|
||||||
|
q_high = true;
|
||||||
|
} else if q_high && sample >= thr_high {
|
||||||
|
// Threshold: voltage rose above 2/3 Vref -> set output low
|
||||||
|
q.set_low();
|
||||||
|
q_high = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Small delay to reduce CPU usage; adjust for responsiveness/noise
|
||||||
|
Timer::after_micros(200).await;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
FROM debian:bookworm
|
||||||
|
|
||||||
|
RUN apt update -y && apt upgrade -y && apt install git libjaylink-dev libusb-1.0-0 unzip curl libhidapi-hidraw0 xz-utils -y
|
||||||
|
|
||||||
|
RUN cd /root && \
|
||||||
|
curl -L -o mrs-toolchain.tar.xz "https://github.com/ch32-riscv-ug/MounRiver_Studio_Community_miror/releases/download/1.92-toolchain/MRS_Toolchain_Linux_x64_V1.92.tar.xz" && \
|
||||||
|
mkdir mrs-toolchain && \
|
||||||
|
tar -xvf mrs-toolchain.tar.xz -C mrs-toolchain --strip-components=1 && \
|
||||||
|
mv mrs-toolchain/OpenOCD/bin/openocd /usr/local/bin && \
|
||||||
|
mv mrs-toolchain/OpenOCD/share/openocd /usr/local/share && \
|
||||||
|
# mv mrs-toolchain/RISC-V_Embedded_GCC12/bin/riscv-none-elf-gdb /usr/local/bin && \ # both toolchains in MRS are to old to work with emacs dape
|
||||||
|
# mv mrs-toolchain/RISC-V_Embedded_GCC12/libexec /usr/local && \ # both toolchains in MRS are to old to work with emacs dape
|
||||||
|
rm -rf mrs-toolchain mrs-toolchain.tar.xz && \
|
||||||
|
# Use up to date xpack toolchains for gdb
|
||||||
|
curl -L -o xpack-riscv-toolchain.tar.gz "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v14.2.0-3/xpack-riscv-none-elf-gcc-14.2.0-3-linux-x64.tar.gz" && \
|
||||||
|
mkdir xpack-toolchain && \
|
||||||
|
tar -xvf xpack-riscv-toolchain.tar.gz -C xpack-toolchain --strip-components=1 && \
|
||||||
|
mv xpack-toolchain/bin/* /usr/local/bin && \
|
||||||
|
mv xpack-toolchain/lib/ /usr/local && \
|
||||||
|
mv xpack-toolchain/lib64/ /usr/local && \
|
||||||
|
mv xpack-toolchain/libexec /usr/local && \
|
||||||
|
mv xpack-toolchain/riscv-none-elf /usr/local && \
|
||||||
|
rm -rf xpack-toolchain xpack-riscv-toolchain.tar.gz
|
||||||
|
|
||||||
|
RUN mkdir -p /root/.config/gdb && echo "set auto-load safe-path /" >> /root/.config/gdb/gdbinit
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/usr/bin/bash" ]
|
4
board/modules/Sensors_simplified/Sensors/fp-lib-table
Normal file
4
board/modules/Sensors_simplified/Sensors/fp-lib-table
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
(fp_lib_table
|
||||||
|
(version 7)
|
||||||
|
(lib (name "Sensor")(type "KiCad")(uri "${KIPRJMOD}/Sensor.pretty")(options "")(descr ""))
|
||||||
|
)
|
@ -58,7 +58,7 @@ pub struct TankConfig {
|
|||||||
pub tank_warn_percent: u8,
|
pub tank_warn_percent: u8,
|
||||||
pub tank_empty_percent: u8,
|
pub tank_empty_percent: u8,
|
||||||
pub tank_full_percent: u8,
|
pub tank_full_percent: u8,
|
||||||
pub ml_per_pulse: f32
|
pub ml_per_pulse: f32,
|
||||||
}
|
}
|
||||||
impl Default for TankConfig {
|
impl Default for TankConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -111,6 +111,7 @@ pub struct PlantControllerConfig {
|
|||||||
pub struct PlantConfig {
|
pub struct PlantConfig {
|
||||||
pub mode: PlantWateringMode,
|
pub mode: PlantWateringMode,
|
||||||
pub target_moisture: f32,
|
pub target_moisture: f32,
|
||||||
|
pub min_moisture: f32,
|
||||||
pub pump_time_s: u16,
|
pub pump_time_s: u16,
|
||||||
pub pump_limit_ml: u16,
|
pub pump_limit_ml: u16,
|
||||||
pub pump_cooldown_min: u16,
|
pub pump_cooldown_min: u16,
|
||||||
@ -131,6 +132,7 @@ impl Default for PlantConfig {
|
|||||||
Self {
|
Self {
|
||||||
mode: PlantWateringMode::OFF,
|
mode: PlantWateringMode::OFF,
|
||||||
target_moisture: 40.,
|
target_moisture: 40.,
|
||||||
|
min_moisture: 30.,
|
||||||
pump_time_s: 30,
|
pump_time_s: 30,
|
||||||
pump_limit_ml: 5000,
|
pump_limit_ml: 5000,
|
||||||
pump_cooldown_min: 60,
|
pump_cooldown_min: 60,
|
||||||
|
@ -22,6 +22,7 @@ use bq34z100::Bq34z100g1Driver;
|
|||||||
use ds323x::{DateTimeAccess, Ds323x};
|
use ds323x::{DateTimeAccess, Ds323x};
|
||||||
use eeprom24x::{Eeprom24x, SlaveAddr, Storage};
|
use eeprom24x::{Eeprom24x, SlaveAddr, Storage};
|
||||||
use embedded_hal_bus::i2c::MutexDevice;
|
use embedded_hal_bus::i2c::MutexDevice;
|
||||||
|
use esp_idf_hal::pcnt::PCNT1;
|
||||||
use esp_idf_hal::{
|
use esp_idf_hal::{
|
||||||
adc::ADC1,
|
adc::ADC1,
|
||||||
delay::Delay,
|
delay::Delay,
|
||||||
@ -47,7 +48,6 @@ use once_cell::sync::Lazy;
|
|||||||
use std::result::Result::Ok as OkStd;
|
use std::result::Result::Ok as OkStd;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use esp_idf_hal::pcnt::PCNT1;
|
|
||||||
|
|
||||||
//Only support for 8 right now!
|
//Only support for 8 right now!
|
||||||
pub const PLANT_COUNT: usize = 8;
|
pub const PLANT_COUNT: usize = 8;
|
||||||
|
@ -132,7 +132,7 @@ pub(crate) fn create_v3(
|
|||||||
peripherals.gpio5,
|
peripherals.gpio5,
|
||||||
tank_power_pin,
|
tank_power_pin,
|
||||||
flow_sensor_pin,
|
flow_sensor_pin,
|
||||||
peripherals.pcnt1
|
peripherals.pcnt1,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut signal_counter = PcntDriver::new(
|
let mut signal_counter = PcntDriver::new(
|
||||||
|
@ -160,7 +160,7 @@ pub(crate) fn create_v4(
|
|||||||
peripherals.gpio5,
|
peripherals.gpio5,
|
||||||
tank_power_pin,
|
tank_power_pin,
|
||||||
flow_sensor_pin,
|
flow_sensor_pin,
|
||||||
peripherals.pcnt1
|
peripherals.pcnt1,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut signal_counter = PcntDriver::new(
|
let mut signal_counter = PcntDriver::new(
|
||||||
|
@ -6,7 +6,9 @@ use esp_idf_hal::adc::oneshot::{AdcChannelDriver, AdcDriver};
|
|||||||
use esp_idf_hal::adc::{attenuation, Resolution, ADC1};
|
use esp_idf_hal::adc::{attenuation, Resolution, ADC1};
|
||||||
use esp_idf_hal::delay::Delay;
|
use esp_idf_hal::delay::Delay;
|
||||||
use esp_idf_hal::gpio::{AnyIOPin, AnyInputPin, Gpio5, InputOutput, PinDriver, Pull};
|
use esp_idf_hal::gpio::{AnyIOPin, AnyInputPin, Gpio5, InputOutput, PinDriver, Pull};
|
||||||
use esp_idf_hal::pcnt::{PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex, PCNT1};
|
use esp_idf_hal::pcnt::{
|
||||||
|
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex, PCNT1,
|
||||||
|
};
|
||||||
use esp_idf_sys::EspError;
|
use esp_idf_sys::EspError;
|
||||||
use one_wire_bus::OneWire;
|
use one_wire_bus::OneWire;
|
||||||
|
|
||||||
@ -25,7 +27,7 @@ impl<'a> TankSensor<'a> {
|
|||||||
gpio5: Gpio5,
|
gpio5: Gpio5,
|
||||||
tank_power_pin: AnyIOPin,
|
tank_power_pin: AnyIOPin,
|
||||||
flow_sensor_pin: AnyIOPin,
|
flow_sensor_pin: AnyIOPin,
|
||||||
pcnt1: PCNT1
|
pcnt1: PCNT1,
|
||||||
) -> anyhow::Result<TankSensor<'a>> {
|
) -> anyhow::Result<TankSensor<'a>> {
|
||||||
let mut one_wire_pin =
|
let mut one_wire_pin =
|
||||||
PinDriver::input_output_od(one_wire_pin).expect("Failed to configure pin");
|
PinDriver::input_output_od(one_wire_pin).expect("Failed to configure pin");
|
||||||
@ -73,7 +75,6 @@ impl<'a> TankSensor<'a> {
|
|||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
||||||
Ok(TankSensor {
|
Ok(TankSensor {
|
||||||
one_wire_bus,
|
one_wire_bus,
|
||||||
tank_channel,
|
tank_channel,
|
||||||
|
@ -591,16 +591,31 @@ pub fn do_secure_pump(
|
|||||||
let mut first_error = true;
|
let mut first_error = true;
|
||||||
let mut pump_time_s = 0;
|
let mut pump_time_s = 0;
|
||||||
if !dry_run {
|
if !dry_run {
|
||||||
board.board_hal.get_tank_sensor().unwrap().reset_flow_meter();
|
board
|
||||||
board.board_hal.get_tank_sensor().unwrap().start_flow_meter();
|
.board_hal
|
||||||
|
.get_tank_sensor()
|
||||||
|
.unwrap()
|
||||||
|
.reset_flow_meter();
|
||||||
|
board
|
||||||
|
.board_hal
|
||||||
|
.get_tank_sensor()
|
||||||
|
.unwrap()
|
||||||
|
.start_flow_meter();
|
||||||
board.board_hal.pump(plant_id, true)?;
|
board.board_hal.pump(plant_id, true)?;
|
||||||
Delay::new_default().delay_ms(10);
|
Delay::new_default().delay_ms(10);
|
||||||
for step in 0..plant_config.pump_time_s as usize {
|
for step in 0..plant_config.pump_time_s as usize {
|
||||||
let flow_value = board.board_hal.get_tank_sensor().unwrap().get_flow_meter_value();
|
let flow_value = board
|
||||||
|
.board_hal
|
||||||
|
.get_tank_sensor()
|
||||||
|
.unwrap()
|
||||||
|
.get_flow_meter_value();
|
||||||
flow_collector[step] = flow_value;
|
flow_collector[step] = flow_value;
|
||||||
let flow_value_ml = flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse;
|
let flow_value_ml = flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse;
|
||||||
|
|
||||||
println!("Flow value is {} ml, limit is {} ml raw sensor {}", flow_value_ml, plant_config.pump_limit_ml, flow_value);
|
println!(
|
||||||
|
"Flow value is {} ml, limit is {} ml raw sensor {}",
|
||||||
|
flow_value_ml, plant_config.pump_limit_ml, flow_value
|
||||||
|
);
|
||||||
if flow_value_ml > plant_config.pump_limit_ml as f32 {
|
if flow_value_ml > plant_config.pump_limit_ml as f32 {
|
||||||
println!("Flow value is reached, stopping");
|
println!("Flow value is reached, stopping");
|
||||||
break;
|
break;
|
||||||
@ -672,9 +687,16 @@ pub fn do_secure_pump(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
board.board_hal.get_tank_sensor().unwrap().stop_flow_meter();
|
board.board_hal.get_tank_sensor().unwrap().stop_flow_meter();
|
||||||
let final_flow_value = board.board_hal.get_tank_sensor().unwrap().get_flow_meter_value();
|
let final_flow_value = board
|
||||||
|
.board_hal
|
||||||
|
.get_tank_sensor()
|
||||||
|
.unwrap()
|
||||||
|
.get_flow_meter_value();
|
||||||
let flow_value_ml = final_flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse;
|
let flow_value_ml = final_flow_value as f32 * board.board_hal.get_config().tank.ml_per_pulse;
|
||||||
println!("Final flow value is {} with {} ml", final_flow_value, flow_value_ml);
|
println!(
|
||||||
|
"Final flow value is {} with {} ml",
|
||||||
|
final_flow_value, flow_value_ml
|
||||||
|
);
|
||||||
current_collector.sort();
|
current_collector.sort();
|
||||||
Ok(PumpResult {
|
Ok(PumpResult {
|
||||||
median_current_ma: current_collector[current_collector.len() / 2],
|
median_current_ma: current_collector[current_collector.len() / 2],
|
||||||
|
@ -78,6 +78,7 @@ impl PumpState {
|
|||||||
pub enum PlantWateringMode {
|
pub enum PlantWateringMode {
|
||||||
OFF,
|
OFF,
|
||||||
TargetMoisture,
|
TargetMoisture,
|
||||||
|
MinMoisture,
|
||||||
TimerOnly,
|
TimerOnly,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,6 +236,30 @@ impl PlantState {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PlantWateringMode::MinMoisture => {
|
||||||
|
let (moisture_percent, _) = self.plant_moisture();
|
||||||
|
if let Some(moisture_percent) = moisture_percent {
|
||||||
|
if self.pump_in_timeout(plant_conf, current_time) {
|
||||||
|
false
|
||||||
|
} else if !in_time_range(
|
||||||
|
current_time,
|
||||||
|
plant_conf.pump_hour_start,
|
||||||
|
plant_conf.pump_hour_end,
|
||||||
|
) {
|
||||||
|
false
|
||||||
|
} else if (true) {
|
||||||
|
//if not cooldown min and below max
|
||||||
|
true
|
||||||
|
} else if (true) {
|
||||||
|
//if below min disable cooldown min
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
PlantWateringMode::TimerOnly => !self.pump_in_timeout(plant_conf, current_time),
|
PlantWateringMode::TimerOnly => !self.pump_in_timeout(plant_conf, current_time),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,7 +293,9 @@ impl PlantState {
|
|||||||
.map(|t| t.with_timezone(¤t_time.timezone())),
|
.map(|t| t.with_timezone(¤t_time.timezone())),
|
||||||
next_pump: if matches!(
|
next_pump: if matches!(
|
||||||
plant_conf.mode,
|
plant_conf.mode,
|
||||||
PlantWateringMode::TimerOnly | PlantWateringMode::TargetMoisture
|
PlantWateringMode::TimerOnly
|
||||||
|
| PlantWateringMode::TargetMoisture
|
||||||
|
| PlantWateringMode::MinMoisture
|
||||||
) {
|
) {
|
||||||
self.pump.previous_pump.and_then(|last_pump| {
|
self.pump.previous_pump.and_then(|last_pump| {
|
||||||
last_pump
|
last_pump
|
||||||
|
@ -105,6 +105,7 @@ export interface PlantControllerConfig {
|
|||||||
export interface PlantConfig {
|
export interface PlantConfig {
|
||||||
mode: string,
|
mode: string,
|
||||||
target_moisture: number,
|
target_moisture: number,
|
||||||
|
min_moisture: number,
|
||||||
pump_time_s: number,
|
pump_time_s: number,
|
||||||
pump_cooldown_min: number,
|
pump_cooldown_min: number,
|
||||||
pump_hour_start: number,
|
pump_hour_start: number,
|
||||||
|
@ -55,6 +55,7 @@
|
|||||||
<select class="plantvalue" id="plant_${plantId}_mode">
|
<select class="plantvalue" id="plant_${plantId}_mode">
|
||||||
<option value="OFF">Off</option>
|
<option value="OFF">Off</option>
|
||||||
<option value="TargetMoisture">Target</option>
|
<option value="TargetMoisture">Target</option>
|
||||||
|
<option value="MinMoisture">Min Moisture</option>
|
||||||
<option value="TimerOnly">Timer</option>
|
<option value="TimerOnly">Timer</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
@ -63,6 +64,10 @@
|
|||||||
<div class="plantkey">Target Moisture:</div>
|
<div class="plantkey">Target Moisture:</div>
|
||||||
<input class="plantvalue" id="plant_${plantId}_target_moisture" type="number" min="0" max="100" placeholder="0">
|
<input class="plantvalue" id="plant_${plantId}_target_moisture" type="number" min="0" max="100" placeholder="0">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flexcontainer plantMinEnabledOnly_${plantId}">
|
||||||
|
<div class="plantkey">Minimum Moisture:</div>
|
||||||
|
<input class="plantvalue" id="plant_${plantId}_min_moisture" type="number" min="0" max="100" placeholder="0">
|
||||||
|
</div>
|
||||||
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
|
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
|
||||||
<div class="plantkey">Pump Time (s):</div>
|
<div class="plantkey">Pump Time (s):</div>
|
||||||
<input class="plantvalue" id="plant_${plantId}_pump_time_s" type="number" min="0" max="600" placeholder="30">
|
<input class="plantvalue" id="plant_${plantId}_pump_time_s" type="number" min="0" max="600" placeholder="30">
|
||||||
|
@ -59,6 +59,7 @@ export class PlantView {
|
|||||||
private readonly header: HTMLElement;
|
private readonly header: HTMLElement;
|
||||||
private readonly testButton: HTMLButtonElement;
|
private readonly testButton: HTMLButtonElement;
|
||||||
private readonly targetMoisture: HTMLInputElement;
|
private readonly targetMoisture: HTMLInputElement;
|
||||||
|
private readonly minMoisture: HTMLInputElement;
|
||||||
private readonly pumpTimeS: HTMLInputElement;
|
private readonly pumpTimeS: HTMLInputElement;
|
||||||
private readonly pumpCooldown: HTMLInputElement;
|
private readonly pumpCooldown: HTMLInputElement;
|
||||||
private readonly pumpHourStart: HTMLSelectElement;
|
private readonly pumpHourStart: HTMLSelectElement;
|
||||||
@ -118,6 +119,11 @@ export class PlantView {
|
|||||||
controller.configChanged()
|
controller.configChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.minMoisture = document.getElementById("plant_" + plantId + "_min_moisture")! as HTMLInputElement;
|
||||||
|
this.minMoisture.onchange = function () {
|
||||||
|
controller.configChanged()
|
||||||
|
}
|
||||||
|
|
||||||
this.pumpTimeS = document.getElementById("plant_" + plantId + "_pump_time_s") as HTMLInputElement;
|
this.pumpTimeS = document.getElementById("plant_" + plantId + "_pump_time_s") as HTMLInputElement;
|
||||||
this.pumpTimeS.onchange = function () {
|
this.pumpTimeS.onchange = function () {
|
||||||
controller.configChanged()
|
controller.configChanged()
|
||||||
@ -203,13 +209,15 @@ export class PlantView {
|
|||||||
let sensorOnly = document.getElementsByClassName("plantSensorEnabledOnly_"+ this.plantId)
|
let sensorOnly = document.getElementsByClassName("plantSensorEnabledOnly_"+ this.plantId)
|
||||||
let pumpOnly = document.getElementsByClassName("plantPumpEnabledOnly_"+ this.plantId)
|
let pumpOnly = document.getElementsByClassName("plantPumpEnabledOnly_"+ this.plantId)
|
||||||
let targetOnly = document.getElementsByClassName("plantTargetEnabledOnly_"+ this.plantId)
|
let targetOnly = document.getElementsByClassName("plantTargetEnabledOnly_"+ this.plantId)
|
||||||
|
let minOnly = document.getElementsByClassName("plantMinEnabledOnly_"+ this.plantId)
|
||||||
|
|
||||||
console.log("updateVisibility plantConfig: " + plantConfig.mode)
|
console.log("updateVisibility plantConfig: " + plantConfig.mode)
|
||||||
let showSensor = plantConfig.sensor_a || plantConfig.sensor_b
|
let showSensor = plantConfig.sensor_a || plantConfig.sensor_b
|
||||||
let showPump = plantConfig.mode !== "OFF"
|
let showPump = plantConfig.mode !== "OFF"
|
||||||
let showTarget = plantConfig.mode === "TargetMoisture"
|
let showTarget = plantConfig.mode === "TargetMoisture"
|
||||||
|
let showMin = plantConfig.mode === "MinMoisture"
|
||||||
|
|
||||||
console.log("updateVisibility showsensor: " + showSensor + " pump " + showPump + " target " +showTarget)
|
console.log("updateVisibility showsensor: " + showSensor + " pump " + showPump + " target " +showTarget + " min " + showMin)
|
||||||
|
|
||||||
for (const element of Array.from(sensorOnly)) {
|
for (const element of Array.from(sensorOnly)) {
|
||||||
if (showSensor) {
|
if (showSensor) {
|
||||||
@ -234,6 +242,14 @@ export class PlantView {
|
|||||||
element.classList.add("plantHidden_" + this.plantId)
|
element.classList.add("plantHidden_" + this.plantId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const element of Array.from(minOnly)) {
|
||||||
|
if (showMin) {
|
||||||
|
element.classList.remove("plantHidden_" + this.plantId)
|
||||||
|
} else {
|
||||||
|
element.classList.add("plantHidden_" + this.plantId)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setTestResult(result: PumpTestResult) {
|
setTestResult(result: PumpTestResult) {
|
||||||
@ -255,6 +271,7 @@ export class PlantView {
|
|||||||
setConfig(plantConfig: PlantConfig) {
|
setConfig(plantConfig: PlantConfig) {
|
||||||
this.mode.value = plantConfig.mode;
|
this.mode.value = plantConfig.mode;
|
||||||
this.targetMoisture.value = plantConfig.target_moisture.toString();
|
this.targetMoisture.value = plantConfig.target_moisture.toString();
|
||||||
|
this.minMoisture.value = plantConfig.min_moisture?.toString() || "";
|
||||||
this.pumpTimeS.value = plantConfig.pump_time_s.toString();
|
this.pumpTimeS.value = plantConfig.pump_time_s.toString();
|
||||||
this.pumpCooldown.value = plantConfig.pump_cooldown_min.toString();
|
this.pumpCooldown.value = plantConfig.pump_cooldown_min.toString();
|
||||||
this.pumpHourStart.value = plantConfig.pump_hour_start.toString();
|
this.pumpHourStart.value = plantConfig.pump_hour_start.toString();
|
||||||
@ -280,6 +297,7 @@ export class PlantView {
|
|||||||
let conv: PlantConfig = {
|
let conv: PlantConfig = {
|
||||||
mode: this.mode.value,
|
mode: this.mode.value,
|
||||||
target_moisture: this.targetMoisture.valueAsNumber,
|
target_moisture: this.targetMoisture.valueAsNumber,
|
||||||
|
min_moisture: this.minMoisture.valueAsNumber,
|
||||||
pump_time_s: this.pumpTimeS.valueAsNumber,
|
pump_time_s: this.pumpTimeS.valueAsNumber,
|
||||||
pump_cooldown_min: this.pumpCooldown.valueAsNumber,
|
pump_cooldown_min: this.pumpCooldown.valueAsNumber,
|
||||||
pump_hour_start: +this.pumpHourStart.value,
|
pump_hour_start: +this.pumpHourStart.value,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user