15 Commits

Author SHA1 Message Date
5bc20d312a Merge branch 'feature/enable-mqtt-login' into develop 2025-09-11 20:21:34 +02:00
4faae86a6b reduce amount of warnings 2025-09-11 20:17:18 +02:00
4fa1a05fc3 remove duplicate import 2025-09-11 20:04:27 +02:00
3a24dcec53 add release profile 2025-09-11 19:56:53 +02:00
e7895577ca update frontent to include inputs for mqtt auth 2025-09-11 19:56:50 +02:00
be9a076b33 add configuration options to enable mqtt user login 2025-09-11 19:56:48 +02:00
47ad4c70de Merge branch 'can' into develop 2025-09-03 19:08:55 +02:00
c4aa3a1450 1.3 baseplane 2025-08-29 21:11:04 +02:00
f0d89417b6 Merge branch 'premerge' into develop 2025-08-29 21:09:20 +02:00
a8e17cda3b wip min 2025-08-29 17:14:15 +02:00
a38d704498 new case and can sensor board 2025-08-25 10:15:36 +02:00
50b2e994ca can prepare stuff 2025-08-14 22:10:53 +02:00
7a7f000743 add missing sensor port labels 2025-08-08 23:24:05 +02:00
d650358bab Add flowsensor prototype, add canbus to backplane 2025-07-27 13:38:53 +02:00
9f113adf7e v4 case 2025-07-26 17:52:19 +02:00
55 changed files with 50378 additions and 4192 deletions

View File

View File

BIN
board/Body1.3mf Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"board": {
"active_layer": 4,
"active_layer": 0,
"active_layer_preset": "All Layers",
"auto_track_width": false,
"hidden_netclasses": [],

View File

@@ -462,8 +462,8 @@
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "error",
"power_pin_not_driven": "error",
"pin_to_pin": "ignore",
"power_pin_not_driven": "ignore",
"same_local_global_label": "warning",
"similar_label_and_power": "warning",
"similar_labels": "warning",
@@ -472,6 +472,7 @@
"single_global_label": "ignore",
"unannotated": "error",
"unconnected_wire_endpoint": "warning",
"undefined_netclass": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"

View File

@@ -4083,7 +4083,7 @@
(symbol "Sensor_1_1"
(rectangle
(start -5.08 -1.27)
(end 5.08 -29.21)
(end 5.08 -34.29)
(stroke
(width 0)
(type solid)
@@ -4110,7 +4110,7 @@
)
)
)
(pin no_connect line
(pin power_in line
(at 7.62 -5.08 180)
(length 2.54)
(name "VBAT"
@@ -4182,10 +4182,10 @@
)
)
)
(pin output line
(pin input line
(at 7.62 -15.24 180)
(length 2.54)
(name "GND"
(name "CAN_H"
(effects
(font
(size 1.27 1.27)
@@ -4200,10 +4200,10 @@
)
)
)
(pin output line
(pin input line
(at 7.62 -17.78 180)
(length 2.54)
(name "GND"
(name "CAN_L"
(effects
(font
(size 1.27 1.27)
@@ -4290,6 +4290,42 @@
)
)
)
(pin output line
(at 7.62 -30.48 180)
(length 2.54)
(name "GND"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "12"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin output line
(at 7.62 -33.02 180)
(length 2.54)
(name "GND"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "13"
(effects
(font
(size 1.27 1.27)
)
)
)
)
)
(embedded_fonts no)
)
@@ -5116,16 +5152,6 @@
)
(uuid f1cb09e6-74b4-43c4-8247-c9f441069dc5)
)
(text "free"
(exclude_from_sim no)
(at 180.848 72.39 0)
(effects
(font
(size 1.27 1.27)
)
)
(uuid "1556efd1-3aac-45ee-8113-03336077a83d")
)
(text "GPIO21 pulses high during flashing!\nGPIO19 is used for communication during flashing"
(exclude_from_sim no)
(at 202.692 102.362 0)
@@ -5427,14 +5453,6 @@
(at 31.75 200.66)
(uuid "133b7027-41a1-4e09-a1e6-24d8bb033b2a")
)
(no_connect
(at 184.15 72.39)
(uuid "5a1ba32e-b2ae-4014-aefc-1b75b4dc3c15")
)
(no_connect
(at 184.15 67.31)
(uuid "6d645bf1-339f-4b38-a26a-bdd168ca591e")
)
(no_connect
(at 41.91 134.62)
(uuid "6e3bf7f5-ac34-42e8-8ef5-a375f9ec4670")
@@ -6873,7 +6891,7 @@
)
(global_label "GND"
(shape input)
(at 125.73 176.53 0)
(at 125.73 181.61 0)
(fields_autoplaced yes)
(effects
(font
@@ -6883,7 +6901,7 @@
)
(uuid "2b9f7359-b644-478f-ba13-5ea4a3466855")
(property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 131.9315 176.53 0)
(at 131.9315 181.61 0)
(effects
(font
(size 1.27 1.27)
@@ -7005,7 +7023,7 @@
)
(global_label "GND"
(shape input)
(at 125.73 171.45 0)
(at 125.73 176.53 0)
(fields_autoplaced yes)
(effects
(font
@@ -7015,7 +7033,7 @@
)
(uuid "36226fb3-6951-4568-90f3-daa99e609121")
(property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 131.9315 171.45 0)
(at 131.9315 176.53 0)
(effects
(font
(size 1.27 1.27)
@@ -7027,7 +7045,7 @@
)
(global_label "GND"
(shape input)
(at 125.73 168.91 0)
(at 125.73 173.99 0)
(fields_autoplaced yes)
(effects
(font
@@ -7037,7 +7055,7 @@
)
(uuid "36c40c19-19ac-4e9a-9c4c-07e2994aa2a1")
(property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 131.9315 168.91 0)
(at 131.9315 173.99 0)
(effects
(font
(size 1.27 1.27)
@@ -7357,7 +7375,7 @@
)
(global_label "GND"
(shape input)
(at 125.73 166.37 0)
(at 125.73 171.45 0)
(fields_autoplaced yes)
(effects
(font
@@ -7367,7 +7385,7 @@
)
(uuid "51e33a9c-f244-46d5-81be-410c3572200f")
(property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 131.9315 166.37 0)
(at 131.9315 171.45 0)
(effects
(font
(size 1.27 1.27)
@@ -7401,7 +7419,7 @@
)
(global_label "GND"
(shape input)
(at 125.73 173.99 0)
(at 125.73 179.07 0)
(fields_autoplaced yes)
(effects
(font
@@ -7411,7 +7429,7 @@
)
(uuid "56e799dc-2d2c-42c8-a5ef-7cbac761326d")
(property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 131.9315 173.99 0)
(at 131.9315 179.07 0)
(effects
(font
(size 1.27 1.27)
@@ -7707,6 +7725,28 @@
)
)
)
(global_label "CAN+"
(shape input)
(at 184.15 67.31 180)
(fields_autoplaced yes)
(effects
(font
(size 1.27 1.27)
)
(justify right)
)
(uuid "6fca578f-ecf1-47ce-aa6b-29168a772f3c")
(property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 176.5575 67.31 0)
(effects
(font
(size 1.27 1.27)
)
(justify right)
(hide yes)
)
)
)
(global_label "ENABLE_TANK"
(shape input)
(at 274.32 114.3 180)
@@ -8037,6 +8077,28 @@
)
)
)
(global_label "CAN+"
(shape input)
(at 125.73 166.37 0)
(fields_autoplaced yes)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
(uuid "93024873-4376-41c4-a4ae-37490a45e129")
(property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 133.3225 166.37 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
(hide yes)
)
)
)
(global_label "GND"
(shape input)
(at 36.83 139.7 270)
@@ -9313,9 +9375,31 @@
)
)
)
(global_label "CAN-"
(shape input)
(at 184.15 72.39 180)
(fields_autoplaced yes)
(effects
(font
(size 1.27 1.27)
)
(justify right)
)
(uuid "f23eaf80-52f2-4322-aa76-67df8580ba2b")
(property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 176.5575 72.39 0)
(effects
(font
(size 1.27 1.27)
)
(justify right)
(hide yes)
)
)
)
(global_label "GND"
(shape input)
(at 125.73 179.07 0)
(at 125.73 184.15 0)
(fields_autoplaced yes)
(effects
(font
@@ -9325,7 +9409,7 @@
)
(uuid "f33ceed4-257c-4060-8932-54eca2a9ad88")
(property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 131.9315 179.07 0)
(at 131.9315 184.15 0)
(effects
(font
(size 1.27 1.27)
@@ -9467,6 +9551,28 @@
)
)
)
(global_label "CAN-"
(shape input)
(at 125.73 168.91 0)
(fields_autoplaced yes)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
(uuid "f9ee87c2-9190-47ad-bd27-1fb56a8f0ec3")
(property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 133.3225 168.91 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
(hide yes)
)
)
)
(global_label "ESP_TX"
(shape input)
(at 59.69 138.43 0)
@@ -14171,6 +14277,12 @@
(pin "7"
(uuid "3c606352-9f5a-4f2e-be79-76a5053cc312")
)
(pin "13"
(uuid "61b8597b-c106-4d1b-b452-b64557e910c2")
)
(pin "12"
(uuid "6432028b-d2db-43e4-9097-728a1d5b28eb")
)
(instances
(project ""
(path "/c26e8d55-0b6e-4c4e-b7c8-b1fed973201c"

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View 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

View File

@@ -428,7 +428,7 @@
(symbol "Sensor_1_1"
(rectangle
(start -5.08 -1.27)
(end 5.08 -29.21)
(end 5.08 -34.29)
(stroke
(width 0)
(type solid)
@@ -455,7 +455,7 @@
)
)
)
(pin no_connect line
(pin power_in line
(at 7.62 -5.08 180)
(length 2.54)
(name "VBAT"
@@ -527,10 +527,10 @@
)
)
)
(pin output line
(pin input line
(at 7.62 -15.24 180)
(length 2.54)
(name "GND"
(name "CAN_H"
(effects
(font
(size 1.27 1.27)
@@ -545,10 +545,10 @@
)
)
)
(pin output line
(pin input line
(at 7.62 -17.78 180)
(length 2.54)
(name "GND"
(name "CAN_L"
(effects
(font
(size 1.27 1.27)
@@ -635,6 +635,42 @@
)
)
)
(pin output line
(at 7.62 -30.48 180)
(length 2.54)
(name "GND"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "12"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin output line
(at 7.62 -33.02 180)
(length 2.54)
(name "GND"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "13"
(effects
(font
(size 1.27 1.27)
)
)
)
)
)
(embedded_fonts no)
)

View File

@@ -164,45 +164,61 @@
(remove_unused_layers no)
(uuid "eee06414-e977-45a8-a5e0-618644284d45")
)
(pad "7" thru_hole rect
(at -0.5 -12)
(size 0.85 0.85)
(drill 0.5)
(pad "7" thru_hole circle
(at -42.4 -0.06)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "536f0038-06c5-45e5-b1c8-898a364f6ec4")
(uuid "a794ee5a-cb4c-4bc2-9f30-7f38ae9862fc")
)
(pad "8" thru_hole rect
(at 39.5 -16)
(size 0.85 0.85)
(drill 0.5)
(pad "8" thru_hole circle
(at -42.4 2.48)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "3e0cdbaa-8219-48c4-bb0a-fd4f0e78a390")
(uuid "f9ed5486-c56c-4c91-b707-d09fae18d351")
)
(pad "9" thru_hole rect
(at 39.5 22.5)
(size 0.85 0.85)
(drill 0.5)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "f01565c2-eadd-4451-9dea-2ebe28d872f0")
)
(pad "10" thru_hole rect
(at -0.5 15)
(size 0.85 0.85)
(drill 0.5)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "56ec7c50-069f-4f46-9b83-ac18e4928930")
)
(pad "11" thru_hole rect
(at -43 22.5)
(size 0.85 0.85)
(drill 0.5)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "f423be21-13b8-46de-8e19-e48325411a29")
)
(pad "12" thru_hole rect
(at -0.5 -12)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "536f0038-06c5-45e5-b1c8-898a364f6ec4")
)
(pad "13" thru_hole rect
(at 39.5 -16)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "3e0cdbaa-8219-48c4-bb0a-fd4f0e78a390")
)
(embedded_fonts no)
)

View File

@@ -0,0 +1,208 @@
(footprint "Sensor"
(version 20241229)
(generator "pcbnew")
(generator_version "9.0")
(layer "F.Cu")
(property "Reference" "REF**"
(at 0 -0.5 0)
(unlocked yes)
(layer "F.SilkS")
(uuid "f71e9c26-7923-4e5c-828e-153927862740")
(effects
(font
(size 1 1)
(thickness 0.1)
)
)
)
(property "Value" "Sensor"
(at 0 1 0)
(unlocked yes)
(layer "F.Fab")
(uuid "d40c7203-0c06-49e1-8672-dbd216694fc8")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(property "Datasheet" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "6720cb18-0687-4d55-a6ad-3ccf0819eac2")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(property "Description" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "43905e6e-773d-4d5e-8d72-57c092c3495a")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(attr smd)
(fp_line
(start -45 -18)
(end 41 -18)
(stroke
(width 0.1)
(type default)
)
(layer "F.SilkS")
(uuid "e63ec799-95c4-407e-acc9-9c7e6d3e330c")
)
(fp_line
(start -45 24)
(end -45 -18)
(stroke
(width 0.1)
(type default)
)
(layer "F.SilkS")
(uuid "a66c286b-432b-493d-9619-2dd4fbfdb21c")
)
(fp_line
(start -44 24)
(end -45 24)
(stroke
(width 0.1)
(type default)
)
(layer "F.SilkS")
(uuid "71526e68-71d4-4b13-ab74-482657a06849")
)
(fp_line
(start 41 -18)
(end 41 24)
(stroke
(width 0.1)
(type default)
)
(layer "F.SilkS")
(uuid "55554342-bbff-4d1b-b931-67fb7eaa5895")
)
(fp_line
(start 41 24)
(end -44 24)
(stroke
(width 0.1)
(type default)
)
(layer "F.SilkS")
(uuid "2dc0ee59-36f6-407e-821a-c6acfe3ea887")
)
(fp_text user "${REFERENCE}"
(at 0 2.5 0)
(unlocked yes)
(layer "F.Fab")
(uuid "befc0725-b201-4f81-b0dc-b327becad9ba")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(pad "1" thru_hole rect
(at -42.4 -15.3)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "5538ac99-6e48-4111-a3df-8ff33be72ff3")
)
(pad "2" thru_hole circle
(at -42.4 -12.76)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "0c1d4a6c-7871-46ae-a262-a8223571975e")
)
(pad "3" thru_hole circle
(at -42.4 -10.22)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "70df4148-0415-45a2-8365-0977fcda45a1")
)
(pad "4" thru_hole circle
(at -42.4 -7.68)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "179dd332-2ab2-4917-91ec-35f232b45ce3")
)
(pad "5" thru_hole circle
(at -42.4 -5.14)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "17bc8f3a-4727-4e31-90f4-8252dff5ad1c")
)
(pad "6" thru_hole circle
(at -42.4 -2.6)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "eee06414-e977-45a8-a5e0-618644284d45")
)
(pad "7" thru_hole rect
(at -0.5 -12)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "536f0038-06c5-45e5-b1c8-898a364f6ec4")
)
(pad "8" thru_hole rect
(at 39.5 -16)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "3e0cdbaa-8219-48c4-bb0a-fd4f0e78a390")
)
(pad "9" thru_hole rect
(at 39.5 22.5)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "f01565c2-eadd-4451-9dea-2ebe28d872f0")
)
(pad "10" thru_hole rect
(at -0.5 15)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "56ec7c50-069f-4f46-9b83-ac18e4928930")
)
(pad "11" thru_hole rect
(at -43 22.5)
(size 1.7 1.7)
(drill 1)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "f423be21-13b8-46de-8e19-e48325411a29")
)
(embedded_fonts no)
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,136 @@
{
"board": {
"active_layer": 6,
"active_layer_preset": "All Layers",
"auto_track_width": false,
"hidden_netclasses": [],
"hidden_nets": [],
"high_contrast_mode": 0,
"net_color_mode": 1,
"opacity": {
"images": 0.6,
"pads": 1.0,
"shapes": 1.0,
"tracks": 1.0,
"vias": 1.0,
"zones": 0.6
},
"selection_filter": {
"dimensions": true,
"footprints": true,
"graphics": true,
"keepouts": true,
"lockedItems": false,
"otherItems": true,
"pads": true,
"text": true,
"tracks": true,
"vias": true,
"zones": true
},
"visible_items": [
"vias",
"footprint_text",
"footprint_anchors",
"ratsnest",
"grid",
"footprints_front",
"footprints_back",
"footprint_values",
"footprint_references",
"tracks",
"drc_errors",
"drawing_sheet",
"bitmaps",
"pads",
"zones",
"drc_warnings",
"locked_item_shadows",
"conflict_shadows",
"shapes"
],
"visible_layers": "ffffffff_ffffffff_ffffffff_ffffffff",
"zone_display_mode": 1
},
"git": {
"repo_type": "",
"repo_username": "",
"ssh_key": ""
},
"meta": {
"filename": "Sensors.kicad_prl",
"version": 5
},
"net_inspector_panel": {
"col_hidden": [
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false,
false
],
"col_order": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11
],
"col_widths": [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
"custom_group_rules": [],
"expanded_rows": [],
"filter_by_net_name": true,
"filter_by_netclass": true,
"filter_text": "",
"group_by_constraint": false,
"group_by_netclass": false,
"show_unconnected_nets": false,
"show_zero_pad_nets": false,
"sort_ascending": true,
"sorting_column": 0
},
"open_jobsets": [],
"project": {
"files": []
},
"schematic": {
"selection_filter": {
"graphics": true,
"images": true,
"labels": true,
"lockedItems": false,
"otherItems": true,
"pins": true,
"symbols": true,
"text": true,
"wires": true
}
}
}

View File

@@ -0,0 +1,674 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {
"apply_defaults_to_fp_fields": false,
"apply_defaults_to_fp_shapes": false,
"apply_defaults_to_fp_text": false,
"board_outline_line_width": 0.05,
"copper_line_width": 0.2,
"copper_text_italic": false,
"copper_text_size_h": 1.5,
"copper_text_size_v": 1.5,
"copper_text_thickness": 0.3,
"copper_text_upright": false,
"courtyard_line_width": 0.05,
"dimension_precision": 4,
"dimension_units": 3,
"dimensions": {
"arrow_length": 1270000,
"extension_offset": 500000,
"keep_text_aligned": true,
"suppress_zeroes": true,
"text_position": 0,
"units_format": 0
},
"fab_line_width": 0.1,
"fab_text_italic": false,
"fab_text_size_h": 1.0,
"fab_text_size_v": 1.0,
"fab_text_thickness": 0.15,
"fab_text_upright": false,
"other_line_width": 0.1,
"other_text_italic": false,
"other_text_size_h": 1.0,
"other_text_size_v": 1.0,
"other_text_thickness": 0.15,
"other_text_upright": false,
"pads": {
"drill": 0.8,
"height": 1.27,
"width": 2.54
},
"silk_line_width": 0.1,
"silk_text_italic": false,
"silk_text_size_h": 1.0,
"silk_text_size_v": 1.0,
"silk_text_thickness": 0.1,
"silk_text_upright": false,
"zones": {
"min_clearance": 0.5
}
},
"diff_pair_dimensions": [
{
"gap": 0.0,
"via_gap": 0.0,
"width": 0.0
}
],
"drc_exclusions": [],
"meta": {
"version": 2
},
"rule_severities": {
"annular_width": "error",
"clearance": "error",
"connection_width": "warning",
"copper_edge_clearance": "error",
"copper_sliver": "warning",
"courtyards_overlap": "error",
"creepage": "error",
"diff_pair_gap_out_of_range": "error",
"diff_pair_uncoupled_length_too_long": "error",
"drill_out_of_range": "error",
"duplicate_footprints": "warning",
"extra_footprint": "warning",
"footprint": "error",
"footprint_filters_mismatch": "ignore",
"footprint_symbol_mismatch": "warning",
"footprint_type_mismatch": "ignore",
"hole_clearance": "error",
"hole_to_hole": "warning",
"holes_co_located": "warning",
"invalid_outline": "error",
"isolated_copper": "warning",
"item_on_disabled_layer": "error",
"items_not_allowed": "error",
"length_out_of_range": "error",
"lib_footprint_issues": "warning",
"lib_footprint_mismatch": "warning",
"malformed_courtyard": "ignore",
"microvia_drill_out_of_range": "error",
"mirrored_text_on_front_layer": "warning",
"missing_courtyard": "ignore",
"missing_footprint": "warning",
"net_conflict": "warning",
"nonmirrored_text_on_back_layer": "warning",
"npth_inside_courtyard": "ignore",
"padstack": "warning",
"pth_inside_courtyard": "ignore",
"shorting_items": "error",
"silk_edge_clearance": "ignore",
"silk_over_copper": "warning",
"silk_overlap": "warning",
"skew_out_of_range": "error",
"solder_mask_bridge": "error",
"starved_thermal": "warning",
"text_height": "warning",
"text_on_edge_cuts": "error",
"text_thickness": "warning",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_angle": "error",
"track_dangling": "warning",
"track_segment_length": "error",
"track_width": "error",
"tracks_crossing": "error",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zones_intersect": "error"
},
"rules": {
"max_error": 0.005,
"min_clearance": 0.0,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.5,
"min_groove_width": 0.0,
"min_hole_clearance": 0.15,
"min_hole_to_hole": 0.25,
"min_microvia_diameter": 0.2,
"min_microvia_drill": 0.1,
"min_resolved_spokes": 2,
"min_silk_clearance": 0.0,
"min_text_height": 0.8,
"min_text_thickness": 0.08,
"min_through_hole_diameter": 0.25,
"min_track_width": 0.0,
"min_via_annular_width": 0.05,
"min_via_diameter": 0.25,
"solder_mask_to_copper_clearance": 0.005,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_onpthpad": true,
"td_onroundshapesonly": false,
"td_onsmdpad": true,
"td_ontrackend": false,
"td_onvia": true
}
],
"teardrop_parameters": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [
0.0,
0.1,
0.2,
0.5
],
"tuning_pattern_settings": {
"diff_pair_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 1.0
},
"diff_pair_skew_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
},
"single_track_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
}
},
"via_dimensions": [
{
"diameter": 0.0,
"drill": 0.0
},
{
"diameter": 0.3,
"drill": 0.25
}
],
"zones_allow_external_fillets": false
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_pairs": [],
"layer_presets": [],
"viewports": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
]
],
"rule_severities": {
"bus_definition_conflict": "error",
"bus_entry_needed": "error",
"bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"endpoint_off_grid": "warning",
"extra_units": "error",
"footprint_filter": "ignore",
"footprint_link_issues": "warning",
"four_way_junction": "ignore",
"global_label_dangling": "warning",
"hier_label_mismatch": "error",
"label_dangling": "error",
"label_multiple_wires": "warning",
"lib_symbol_issues": "warning",
"lib_symbol_mismatch": "warning",
"missing_bidi_pin": "warning",
"missing_input_pin": "warning",
"missing_power_pin": "error",
"missing_unit": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "warning",
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "ignore",
"power_pin_not_driven": "ignore",
"same_local_global_label": "warning",
"similar_label_and_power": "warning",
"similar_labels": "warning",
"similar_power": "warning",
"simulation_model_issue": "ignore",
"single_global_label": "ignore",
"unannotated": "error",
"unconnected_wire_endpoint": "warning",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "Sensors.kicad_pro",
"version": 3
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.15,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"priority": 2147483647,
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.2,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
}
],
"meta": {
"version": 4
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": []
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "",
"pos_files": "",
"specctra_dsn": "",
"step": "Sensors.step",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"bom_export_filename": "${PROJECTNAME}.csv",
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": true,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
},
{
"group_by": false,
"label": "Description",
"name": "Description",
"show": false
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": false,
"label": "#",
"name": "${ITEM_NUMBER}",
"show": false
},
{
"group_by": false,
"label": "LCSC_PART_NUMBER",
"name": "LCSC_PART_NUMBER",
"show": true
},
{
"group_by": false,
"label": "Sim.Device",
"name": "Sim.Device",
"show": false
},
{
"group_by": false,
"label": "Sim.Pins",
"name": "Sim.Pins",
"show": false
},
{
"group_by": false,
"label": "Sim.Type",
"name": "Sim.Type",
"show": false
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
},
{
"group_by": true,
"label": "Exclude from BOM",
"name": "${EXCLUDE_FROM_BOM}",
"show": true
},
{
"group_by": true,
"label": "Exclude from Board",
"name": "${EXCLUDE_FROM_BOARD}",
"show": true
}
],
"filter_string": "",
"group_symbols": true,
"include_excluded_from_bom": true,
"name": "",
"sort_asc": true,
"sort_field": "Reference"
},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"field_names": [],
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.375,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.15
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"page_layout_descr_file": "",
"plot_directory": "",
"space_save_all_events": true,
"spice_current_sheet_as_root": false,
"spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false,
"subpart_first_id": 65,
"subpart_id_separator": 0
},
"sheets": [
[
"46346c04-8bed-48b4-837b-9342dd403232",
"Root"
]
],
"text_variables": {}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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 ""))
)

View 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"

View 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

View File

@@ -0,0 +1 @@
target

View 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.

View 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 nodes CAN Standard ID is `0x100 | addr`, i.e. 0x100..0x10F. The CAN acceptance filter is configured to only accept frames with the nodes 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 MCUs 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
```

View 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

View 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" "$@"

View 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" "$@"

View 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");
}

View 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);

View 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

View File

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

View 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;
}
}

View File

@@ -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" ]

View File

@@ -0,0 +1,4 @@
(fp_lib_table
(version 7)
(lib (name "Sensor")(type "KiCad")(uri "${KIPRJMOD}/Sensor.pretty")(options "")(descr ""))
)

Binary file not shown.

View File

@@ -1,17 +1,45 @@
P CODE 00
P UNITS CUST 0
P arrayDim N
317CONFIG2 VIA MD0157PA00X+060394Y-009724X0315Y0000R000S3
317GND VIA MD0157PA00X+058465Y-005591X0315Y0000R000S3
317GND VIA MD0157PA00X+060433Y-005591X0315Y0000R000S3
317GND VIA MD0157PA00X+050827Y-007402X0315Y0000R000S3
317SDA VIA MD0157PA00X+050134Y-014937X0315Y0000R000S3
317SCL VIA MD0157PA00X+051535Y-013898X0315Y0000R000S3
317CHARGE VIA MD0157PA00X+071575Y-006732X0315Y0000R000S3
317CAN- VIA MD0157PA00X+070354Y-006220X0315Y0000R000S3
317CAN+ VIA MD0157PA00X+064606Y-009213X0315Y0000R000S3
317FLOW VIA MD0157PA00X+048543Y-021063X0315Y0000R000S3
317TEMP VIA MD0157PA00X+048898Y-023268X0315Y0000R000S3
317EN VIA MD0157PA00X+062697Y-009154X0315Y0000R000S3
317EN VIA MD0157PA00X+062500Y-010827X0315Y0000R000S3
317VBAT VIA MD0157PA00X+043130Y-036024X0315Y0000R000S3
317VBAT VIA MD0157PA00X+043130Y-035039X0315Y0000R000S3
3173_3V VIA MD0157PA00X+064646Y-014567X0315Y0000R000S3
3173_3V VIA MD0157PA00X+075906Y-014331X0315Y0000R000S3
3173_3V VIA MD0157PA00X+074094Y-014331X0315Y0000R000S3
3173_3V VIA MD0157PA00X+067874Y-014567X0315Y0000R000S3
317TEMP VIA MD0157PA00X+072343Y-012303X0315Y0000R000S3
317TANK_SENSOR VIA MD0157PA00X+063091Y-007776X0315Y0000R000S3
317TANK_SENSOR VIA MD0157PA00X+047244Y-017421X0315Y0000R000S3
317CHARGE VIA MD0157PA00X+071575Y-006732X0315Y0000R000S3
317SDA VIA MD0157PA00X+072441Y-010630X0315Y0000R000S3
317SDA VIA MD0157PA00X+050134Y-014937X0315Y0000R000S3
317SCL VIA MD0157PA00X+072441Y-011417X0315Y0000R000S3
317SCL VIA MD0157PA00X+051535Y-013898X0315Y0000R000S3
317NET-(CD1-A) VIA MD0157PA00X+058661Y-009843X0315Y0000R000S3
317NET-(CD1-A) VIA MD0157PA00X+045866Y-005906X0315Y0000R000S3
317CD_PROBE VIA MD0157PA00X+072638Y-009744X0315Y0000R000S3
317ISDAY VIA MD0157PA00X+066929Y-011024X0315Y0000R000S3
317LED_ENABLE VIA MD0157PA00X+064764Y-010728X0315Y0000R000S3
317WORKING VIA MD0157PA00X+058661Y-011417X0315Y0000R000S3
317WORKING VIA MD0157PA00X+069843Y-008858X0315Y0000R000S3
317ENABLE_TANK VIA MD0157PA00X+062894Y-011516X0315Y0000R000S3
317ENABLE_TANK VIA MD0157PA00X+050525Y-015584X0315Y0000R000S3
317USB_D- VIA MD0157PA00X+045276Y-010305X0315Y0000R000S3
317USB_D- VIA MD0157PA00X+063377Y-011752X0315Y0000R000S3
317FLOW VIA MD0157PA00X+063484Y-007382X0315Y0000R000S3
317USB_D+ VIA MD0157PA00X+045276Y-010719X0315Y0000R000S3
317USB_D+ VIA MD0157PA00X+063377Y-012165X0315Y0000R000S3
317BOOT_SEL VIA MD0157PA00X+064764Y-009744X0315Y0000R000S3
317BOOT_SEL VIA MD0157PA00X+070374Y-012205X0315Y0000R000S3
317WARN_LED VIA MD0157PA00X+069803Y-008465X0315Y0000R000S3
317WARN_LED VIA MD0157PA00X+044390Y-005807X0315Y0000R000S3
317CONFIG2 VIA MD0157PA00X+060394Y-009724X0315Y0000R000S3
317GND VIA MD1181PA00X+077500Y-007000X1575Y0000R000S3
317GND VIA MD0157PA00X+062402Y-010433X0315Y0000R000S3
317GND VIA MD0157PA00X+054449Y-012323X0315Y0000R000S3
@@ -24,6 +52,7 @@ P arrayDim N
317GND VIA MD0157PA00X+058150Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+054843Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+043228Y-012480X0315Y0000R000S3
317GND VIA MD0157PA00X+050827Y-007402X0315Y0000R000S3
317GND VIA MD0157PA00X+047244Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+060374Y-011594X0315Y0000R000S3
317GND VIA MD1181PA00X+041500Y-042900X1575Y0000R000S3
@@ -58,6 +87,7 @@ P arrayDim N
317GND VIA MD0157PA00X+048228Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+045669Y-007087X0315Y0000R000S3
317GND VIA MD0157PA00X+063386Y-011220X0315Y0000R000S3
317GND VIA MD0157PA00X+060433Y-005591X0315Y0000R000S3
317GND VIA MD0157PA00X+060472Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+045699Y-017156X0315Y0000R000S3
317GND VIA MD0157PA00X+048661Y-009646X0315Y0000R000S3
@@ -91,8 +121,8 @@ P arrayDim N
317GND VIA MD0157PA00X+047953Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+052205Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+053780Y-010315X0315Y0000R000S3
317GND VIA MD0157PA00X+058465Y-005591X0315Y0000R000S3
317GND VIA MD0157PA00X+046181Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+047283Y-023622X0315Y0000R000S3
317GND VIA MD0157PA00X+051496Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+053583Y-011378X0315Y0000R000S3
317GND VIA MD0157PA00X+049016Y-034154X0315Y0000R000S3
@@ -110,7 +140,6 @@ P arrayDim N
317GND VIA MD0157PA00X+060059Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+042283Y-013386X0315Y0000R000S3
317GND VIA MD0157PA00X+044764Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+047205Y-025709X0315Y0000R000S3
317GND VIA MD0157PA00X+055709Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+060827Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+054134Y-011969X0315Y0000R000S3
@@ -128,126 +157,24 @@ P arrayDim N
317GND VIA MD0157PA00X+048583Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+057185Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+042953Y-012756X0315Y0000R000S3
3173_3V VIA MD0157PA00X+064646Y-014567X0315Y0000R000S3
3173_3V VIA MD0157PA00X+075906Y-014331X0315Y0000R000S3
3173_3V VIA MD0157PA00X+074094Y-014331X0315Y0000R000S3
3173_3V VIA MD0157PA00X+067874Y-014567X0315Y0000R000S3
317TEMP VIA MD0157PA00X+072343Y-012303X0315Y0000R000S3
317TEMP VIA MD0157PA00X+049099Y-026344X0315Y0000R000S3
317TANK_SENSOR VIA MD0157PA00X+063091Y-007776X0315Y0000R000S3
317TANK_SENSOR VIA MD0157PA00X+047244Y-017421X0315Y0000R000S3
317SDA VIA MD0157PA00X+072441Y-010630X0315Y0000R000S3
317SCL VIA MD0157PA00X+072441Y-011417X0315Y0000R000S3
317NET-(CD1-A) VIA MD0157PA00X+058661Y-009843X0315Y0000R000S3
317NET-(CD1-A) VIA MD0157PA00X+045866Y-005906X0315Y0000R000S3
317CD_PROBE VIA MD0157PA00X+072638Y-009744X0315Y0000R000S3
317ISDAY VIA MD0157PA00X+066929Y-011024X0315Y0000R000S3
317LED_ENABLE VIA MD0157PA00X+064764Y-010728X0315Y0000R000S3
317WORKING VIA MD0157PA00X+058661Y-011417X0315Y0000R000S3
317WORKING VIA MD0157PA00X+069843Y-008858X0315Y0000R000S3
317ENABLE_TANK VIA MD0157PA00X+062894Y-011516X0315Y0000R000S3
317ENABLE_TANK VIA MD0157PA00X+050525Y-015584X0315Y0000R000S3
317USB_D- VIA MD0157PA00X+045276Y-010305X0315Y0000R000S3
317USB_D- VIA MD0157PA00X+063377Y-011752X0315Y0000R000S3
317FLOW VIA MD0157PA00X+048543Y-023228X0315Y0000R000S3
317FLOW VIA MD0157PA00X+063484Y-007382X0315Y0000R000S3
317USB_D+ VIA MD0157PA00X+045276Y-010719X0315Y0000R000S3
317USB_D+ VIA MD0157PA00X+063377Y-012165X0315Y0000R000S3
317BOOT_SEL VIA MD0157PA00X+064764Y-009744X0315Y0000R000S3
317BOOT_SEL VIA MD0157PA00X+070374Y-012205X0315Y0000R000S3
317WARN_LED VIA MD0157PA00X+069803Y-008465X0315Y0000R000S3
317WARN_LED VIA MD0157PA00X+044390Y-005807X0315Y0000R000S3
317WARN_LED J8 -1 D0394PA00X+055787Y-006386X0669Y0669R000S0
317GND J8 -2 D0394PA00X+055787Y-007386X0669Y0000R000S0
317CONFIG2 J8 -3 D0394PA00X+055787Y-008386X0669Y0000R000S0
317ESP_TX J3 -1 D0394PA00X+073051Y-006457X0669Y0669R270S0
317ESP_RX J3 -2 D0394PA00X+074051Y-006457X0669Y0000R270S0
327GND U2 -1 A01X+064094Y-005709X0591Y0354R000S2
3273_3V U2 -2 A01X+064094Y-006209X0591Y0354R000S2
327EN U2 -3 A01X+064094Y-006709X0591Y0354R000S2
327FLOW U2 -4 A01X+064094Y-007209X0591Y0354R000S2
327TANK_SENSOR U2 -5 A01X+064094Y-007709X0591Y0354R000S2
327EXTRA_1 U2 -6 A01X+064094Y-008209X0591Y0354R000S2
327ISDAY U2 -7 A01X+064094Y-008709X0591Y0354R000S2
327-(U2-IO0-PAD8) U2 -8 A01X+064094Y-009209X0591Y0354R000S2
327BOOT_SEL U2 -9 A01X+064094Y-009709X0591Y0354R000S2
327(U2-IO8-PAD10) U2 -10 A01X+064094Y-010209X0591Y0354R000S2
327LED_ENABLE U2 -11 A01X+064094Y-010709X0591Y0354R000S2
327ENABLE_TANK U2 -12 A01X+064094Y-011209X0591Y0354R000S2
327USB_D- U2 -13 A01X+064094Y-011709X0591Y0354R000S2
327USB_D+ U2 -14 A01X+064094Y-012209X0591Y0354R000S2
327BOOT_SEL U2 -15 A01X+070984Y-012209X0591Y0354R000S2
327TEMP U2 -16 A01X+070984Y-011709X0591Y0354R000S2
327SCL U2 -17 A01X+070984Y-011209X0591Y0354R000S2
327SDA U2 -18 A01X+070984Y-010709X0591Y0354R000S2
327WORKING U2 -19 A01X+070984Y-010209X0591Y0354R000S2
327CD_PROBE U2 -20 A01X+070984Y-009709X0591Y0354R000S2
327WARN_LED U2 -21 A01X+070984Y-009209X0591Y0354R000S2
327-(U2-NC-PAD22) U2 -22 A01X+070984Y-008709X0591Y0354R000S2
327EXTRA_2 U2 -23 A01X+070984Y-008209X0591Y0354R000S2
327ESP_RX U2 -24 A01X+070984Y-007709X0591Y0354R000S2
327ESP_TX U2 -25 A01X+070984Y-007209X0591Y0354R000S2
327CHARGE U2 -26 A01X+070984Y-006709X0591Y0354R000S2
327(U2-IO2-PAD27) U2 -27 A01X+070984Y-006209X0591Y0354R000S2
327GND U2 -28 A01X+070984Y-005709X0591Y0354R000S2
327GND U2 -29_5 A01X+066947Y-007961X0315Y0315R000S2
327GND U2 -29_1 A01X+066455Y-007469X0315Y0315R000S2
327GND U2 -29_2 A01X+066947Y-007469X0315Y0315R000S2
327GND U2 -29_3 A01X+067439Y-007469X0315Y0315R000S2
327GND U2 -29_4 A01X+066455Y-007961X0315Y0315R000S2
327GND U2 -29_6 A01X+067439Y-007961X0315Y0315R000S2
327GND U2 -29_7 A01X+066455Y-008453X0315Y0315R000S2
327GND U2 -29_8 A01X+066947Y-008453X0315Y0315R000S2
327GND U2 -29_9 A01X+067439Y-008453X0315Y0315R000S2
317GND U2 -30_1 D0098PA00X+066701Y-007469X0138Y0000R000S3
317GND U2 -30_2 D0098PA00X+067193Y-007469X0138Y0000R000S3
317GND U2 -30_3 D0098PA00X+066455Y-007715X0138Y0000R000S3
317GND U2 -30_4 D0098PA00X+066947Y-007715X0138Y0000R000S3
317GND U2 -30_5 D0098PA00X+067439Y-007715X0138Y0000R000S3
317GND U2 -30_6 D0098PA00X+066701Y-007961X0138Y0000R000S3
317GND U2 -30_7 D0098PA00X+067193Y-007961X0138Y0000R000S3
317GND U2 -30_8 D0098PA00X+066455Y-008207X0138Y0000R000S3
317GND U2 -30_9 D0098PA00X+066947Y-008207X0138Y0000R000S3
317GND U2 -30_1 D0098PA00X+067439Y-008207X0138Y0000R000S3
317GND U2 -30_1 D0098PA00X+066701Y-008453X0138Y0000R000S3
317GND U2 -30_1 D0098PA00X+067193Y-008453X0138Y0000R000S3
317VBAT U7 -1 D0433PA00X+042795Y-037835X0750Y0787R180S0
317GND U7 -2 D0433PA00X+041795Y-037835X0750Y0787R180S0
3173_3V U7 -3 D0433PA00X+040795Y-037835X0750Y0787R180S0
327USB_D+ U1 -1 A01X+044124Y-010945X0522Y0236R180S2
327GND U1 -2 A01X+044124Y-010571X0522Y0236R180S2
327USB_D- U1 -3 A01X+044124Y-010197X0522Y0236R180S2
327SLASH}O2-PAD4) U1 -4 A01X+043228Y-010197X0522Y0236R180S2
327USB_BUS U1 -5 A01X+043228Y-010571X0522Y0236R180S2
327SLASH}O1-PAD6) U1 -6 A01X+043228Y-010945X0522Y0236R180S2
3273_3V R19 -2 A01X+048898Y-022037X0315Y0374R270S2
327TEMP R19 -1 A01X+048898Y-022687X0315Y0374R270S2
3173_3V U5 -1 D0394PA00X+046339Y-019764X0669Y0669R000S0
317VBAT U5 -2 D0394PA00X+046339Y-020764X0669Y0000R000S0
317SDA U5 -3 D0394PA00X+046339Y-021764X0669Y0000R000S0
317SCL U5 -4 D0394PA00X+046339Y-022764X0669Y0000R000S0
317CD_PROBE U5 -5 D0394PA00X+046339Y-023764X0669Y0000R000S0
317CAN+ U5 -6 D0394PA00X+046339Y-024764X0669Y0000R000S0
317CAN- U5 -7 D0394PA00X+046339Y-025764X0669Y0000R000S0
317GND U5 -8 D0394PA00X+046339Y-026764X0669Y0000R000S0
317GND U5 -9 D0197PA00X+078583Y-034646X0335Y0335R000S0
317GND U5 -10 D0197PA00X+062835Y-031693X0335Y0335R000S0
317GND U5 -11 D0197PA00X+046102Y-034646X0335Y0335R000S0
317GND U5 -12 D0197PA00X+062835Y-021063X0335Y0335R000S0
317GND U5 -13 D0197PA00X+078583Y-019488X0335Y0335R000S0
327GND D11 -1 A01X+046506Y-030000X0581Y0236R180S2
327VBAT D11 -2 A01X+046506Y-029252X0581Y0236R180S2
327NET-(D10-K) D11 -3 A01X+045768Y-029626X0581Y0236R180S2
327GND D5 -1 A01X+048027Y-023602X0581Y0236R180S2
3273_3V D5 -2 A01X+048027Y-022854X0581Y0236R180S2
327FLOW D5 -3 A01X+047288Y-023228X0581Y0236R180S2
327GND D6 -1 A01X+046043Y-016811X0581Y0236R180S2
3273_3V D6 -2 A01X+046043Y-016063X0581Y0236R180S2
327TANK_SENSOR D6 -3 A01X+045305Y-016437X0581Y0236R180S2
327GND D7 -1 A01X+048027Y-025669X0581Y0236R180S2
3273_3V D7 -2 A01X+048027Y-024921X0581Y0236R180S2
327TEMP D7 -3 A01X+047288Y-025295X0581Y0236R180S2
327GND D3 -1 A01X+051102Y-007992X0581Y0236R000S2
3273_3V D3 -2 A01X+051102Y-008740X0581Y0236R000S2
327SCL D3 -3 A01X+051841Y-008366X0581Y0236R000S2
327GND D9 -1 A01X+046486Y-033760X0581Y0236R180S2
327VBAT D9 -2 A01X+046486Y-033012X0581Y0236R180S2
327NET-(D8-K) D9 -3 A01X+045748Y-033386X0581Y0236R180S2
327GND D1 -1 A01X+046033Y-007933X0581Y0236R000S2
3273_3V D1 -2 A01X+046033Y-008681X0581Y0236R000S2
327SDA D1 -3 A01X+046772Y-008307X0581Y0236R000S2
327GND D4 -1 A01X+045305Y-017520X0581Y0236R000S2
3273_3V D4 -2 A01X+045305Y-018268X0581Y0236R000S2
327NET-(J4-PIN_1) D4 -3 A01X+046043Y-017894X0581Y0236R000S2
3173_3V C9 -1 D0315PA00X+046165Y-012441X0630Y0630R000S0
3173_3V C9 -1 D0315PA00X+046429Y-013071X0630Y0630R000S0
317GND C9 -2 D0315PA00X+047413Y-013071X0630Y0000R000S0
317GND C9 -2 D0315PA00X+047677Y-013701X0630Y0000R000S0
317VBAT U3 -1 D0394PA00X+078189Y-017373X0669Y0669R000S0
317ISDAY U3 -2 D0394PA00X+078189Y-016373X0669Y0669R000S0
317CHARGE U3 -3 D0394PA00X+078189Y-015373X0669Y0669R000S0
@@ -259,8 +186,8 @@ P arrayDim N
317GND U3 -9 D0394PA00X+050827Y-017869X0669Y0669R000S0
317GND U3 -10 D0394PA00X+059685Y-010782X0669Y0669R000S0
317GND U3 -11 D0394PA00X+059685Y-017869X0669Y0669R000S0
327CONFIG2 Reset1-2 A01X+060394Y-009094X0591Y0591R090S2
327GND Reset1-1 A01X+060394Y-006024X0591Y0591R090S2
327CONFIG2 Reset1-2 A01X+060394Y-009094X0591Y0591R090S2
317GND U6 -1 D0394PA00X+078465Y-036024X0669Y0669R090S0
317GND U6 -2 D0394PA00X+071378Y-044016X0669Y0669R000S0
317GND U6 -3 D0394PA00X+047756Y-044016X0669Y0669R000S0
@@ -293,8 +220,14 @@ P arrayDim N
327GND R16 -2 A01X+048091Y-016043X0315Y0374R180S2
327NET-(Q2-G) R22 -1 A01X+047805Y-034843X0315Y0374R000S2
327GND R22 -2 A01X+048455Y-034843X0315Y0374R000S2
327FLOW D5 -3 A01X+047288Y-020295X0581Y0236R180S2
3273_3V D5 -2 A01X+048027Y-019921X0581Y0236R180S2
327GND D5 -1 A01X+048027Y-020669X0581Y0236R180S2
327NET-(D2-K) D2 -1 A01X+043701Y-006570X0384Y0551R270S2
327WARN_LED D2 -2 A01X+043701Y-005832X0384Y0551R270S2
327GND D6 -1 A01X+046043Y-016811X0581Y0236R180S2
3273_3V D6 -2 A01X+046043Y-016063X0581Y0236R180S2
327TANK_SENSOR D6 -3 A01X+045305Y-016437X0581Y0236R180S2
327NET-(CD1-A) R9 -1 A01X+058661Y-010305X0315Y0374R090S2
327WORKING R9 -2 A01X+058661Y-010955X0315Y0374R090S2
317GND J6 -1 D0394PA00X+046906Y-006220X0669Y0669R270S0
@@ -307,6 +240,15 @@ P arrayDim N
327NET-(D8-A) D8 -2 A01X+040945Y-032505X0384Y0551R270S2
327NET-(J2-PIN_2) R2 -1 A01X+042943Y-014764X0315Y0374R000S2
327GND R2 -2 A01X+043593Y-014764X0315Y0374R000S2
327USB_D+ U1 -1 A01X+044124Y-010945X0522Y0236R180S2
327GND U1 -2 A01X+044124Y-010571X0522Y0236R180S2
327USB_D- U1 -3 A01X+044124Y-010197X0522Y0236R180S2
327SLASH}O2-PAD4) U1 -4 A01X+043228Y-010197X0522Y0236R180S2
327USB_BUS U1 -5 A01X+043228Y-010571X0522Y0236R180S2
327SLASH}O1-PAD6) U1 -6 A01X+043228Y-010945X0522Y0236R180S2
327TEMP D7 -3 A01X+047288Y-022362X0581Y0236R180S2
3273_3V D7 -2 A01X+048027Y-021988X0581Y0236R180S2
327GND D7 -1 A01X+048027Y-022736X0581Y0236R180S2
327VBAT C8 -1 A01X+042534Y-036024X0463Y0571R180S2
327GND C8 -2 A01X+041718Y-036024X0463Y0571R180S2
327NET-(R3-PAD1) R3 -1 A01X+057677Y-012933X0315Y0374R090S2
@@ -315,10 +257,18 @@ P arrayDim N
327ENABLE_TANK R14 -2 A01X+050089Y-016043X0315Y0374R000S2
327GND C1 -1 A01X+056299Y-011112X0423Y0374R270S2
327T-(BOOT1-PAD2) C1 -2 A01X+056299Y-010433X0423Y0374R270S2
327TEMP R19 -1 A01X+049099Y-025295X0315Y0374R000S2
3273_3V R19 -2 A01X+049749Y-025295X0315Y0374R000S2
327GND D3 -1 A01X+051102Y-007992X0581Y0236R000S2
3273_3V D3 -2 A01X+051102Y-008740X0581Y0236R000S2
327SCL D3 -3 A01X+051841Y-008366X0581Y0236R000S2
3173_3V C9 -1 D0315PA00X+046165Y-012441X0630Y0630R000S0
3173_3V C9 -1 D0315PA00X+046429Y-013071X0630Y0630R000S0
317GND C9 -2 D0315PA00X+047413Y-013071X0630Y0000R000S0
317GND C9 -2 D0315PA00X+047677Y-013701X0630Y0000R000S0
3273_3V C6 -1 A01X+062205Y-007392X0463Y0571R270S2
327GND C6 -2 A01X+062205Y-006575X0463Y0571R270S2
317WARN_LED J8 -1 D0394PA00X+055787Y-006386X0669Y0669R000S0
317GND J8 -2 D0394PA00X+055787Y-007386X0669Y0000R000S0
317CONFIG2 J8 -3 D0394PA00X+055787Y-008386X0669Y0000R000S0
327NET-(Q3-G) R21 -1 A01X+047805Y-031102X0315Y0374R000S2
327GND R21 -2 A01X+048455Y-031102X0315Y0374R000S2
317LED_ENABLE U4 -1 D0394PA00X+074063Y-009173X0669Y0000R090S0
@@ -330,6 +280,9 @@ P arrayDim N
327NET-(Q3-G) Q3 -1 A01X+047756Y-030413X0354Y0315R270S2
327GND Q3 -2 A01X+048504Y-030413X0354Y0315R270S2
327NET-(D10-K) Q3 -3 A01X+048130Y-029626X0354Y0315R270S2
327GND D9 -1 A01X+046486Y-033760X0581Y0236R180S2
327VBAT D9 -2 A01X+046486Y-033012X0581Y0236R180S2
327NET-(D8-K) D9 -3 A01X+045748Y-033386X0581Y0236R180S2
327VBAT R17 -1 A01X+046033Y-032185X0315Y0374R000S2
327NET-(D8-A) R17 -2 A01X+046683Y-032185X0315Y0374R000S2
327GND C5 -1 A01X+062992Y-010379X0423Y0374R270S2
@@ -340,8 +293,60 @@ P arrayDim N
327GND R1 -2 A01X+044488Y-007667X0315Y0374R270S2
327EXTRA_1 R20 -1 A01X+048455Y-031890X0315Y0374R180S2
327NET-(Q3-G) R20 -2 A01X+047805Y-031890X0315Y0374R180S2
327GND U2 -1 A01X+064094Y-005709X0591Y0354R000S2
3273_3V U2 -2 A01X+064094Y-006209X0591Y0354R000S2
327EN U2 -3 A01X+064094Y-006709X0591Y0354R000S2
327FLOW U2 -4 A01X+064094Y-007209X0591Y0354R000S2
327TANK_SENSOR U2 -5 A01X+064094Y-007709X0591Y0354R000S2
327EXTRA_1 U2 -6 A01X+064094Y-008209X0591Y0354R000S2
327ISDAY U2 -7 A01X+064094Y-008709X0591Y0354R000S2
327CAN+ U2 -8 A01X+064094Y-009209X0591Y0354R000S2
327BOOT_SEL U2 -9 A01X+064094Y-009709X0591Y0354R000S2
327(U2-IO8-PAD10) U2 -10 A01X+064094Y-010209X0591Y0354R000S2
327LED_ENABLE U2 -11 A01X+064094Y-010709X0591Y0354R000S2
327ENABLE_TANK U2 -12 A01X+064094Y-011209X0591Y0354R000S2
327USB_D- U2 -13 A01X+064094Y-011709X0591Y0354R000S2
327USB_D+ U2 -14 A01X+064094Y-012209X0591Y0354R000S2
327BOOT_SEL U2 -15 A01X+070984Y-012209X0591Y0354R000S2
327TEMP U2 -16 A01X+070984Y-011709X0591Y0354R000S2
327SCL U2 -17 A01X+070984Y-011209X0591Y0354R000S2
327SDA U2 -18 A01X+070984Y-010709X0591Y0354R000S2
327WORKING U2 -19 A01X+070984Y-010209X0591Y0354R000S2
327CD_PROBE U2 -20 A01X+070984Y-009709X0591Y0354R000S2
327WARN_LED U2 -21 A01X+070984Y-009209X0591Y0354R000S2
327-(U2-NC-PAD22) U2 -22 A01X+070984Y-008709X0591Y0354R000S2
327EXTRA_2 U2 -23 A01X+070984Y-008209X0591Y0354R000S2
327ESP_RX U2 -24 A01X+070984Y-007709X0591Y0354R000S2
327ESP_TX U2 -25 A01X+070984Y-007209X0591Y0354R000S2
327CHARGE U2 -26 A01X+070984Y-006709X0591Y0354R000S2
327CAN- U2 -27 A01X+070984Y-006209X0591Y0354R000S2
327GND U2 -28 A01X+070984Y-005709X0591Y0354R000S2
327GND U2 -29_1 A01X+066455Y-007469X0315Y0315R000S2
327GND U2 -29_2 A01X+066947Y-007469X0315Y0315R000S2
327GND U2 -29_3 A01X+067439Y-007469X0315Y0315R000S2
327GND U2 -29_4 A01X+066455Y-007961X0315Y0315R000S2
327GND U2 -29_5 A01X+066947Y-007961X0315Y0315R000S2
327GND U2 -29_6 A01X+067439Y-007961X0315Y0315R000S2
327GND U2 -29_7 A01X+066455Y-008453X0315Y0315R000S2
327GND U2 -29_8 A01X+066947Y-008453X0315Y0315R000S2
327GND U2 -29_9 A01X+067439Y-008453X0315Y0315R000S2
317GND U2 -30_1 D0098PA00X+066701Y-007469X0138Y0000R000S3
317GND U2 -30_2 D0098PA00X+067193Y-007469X0138Y0000R000S3
317GND U2 -30_3 D0098PA00X+066455Y-007715X0138Y0000R000S3
317GND U2 -30_4 D0098PA00X+066947Y-007715X0138Y0000R000S3
317GND U2 -30_5 D0098PA00X+067439Y-007715X0138Y0000R000S3
317GND U2 -30_6 D0098PA00X+066701Y-007961X0138Y0000R000S3
317GND U2 -30_7 D0098PA00X+067193Y-007961X0138Y0000R000S3
317GND U2 -30_8 D0098PA00X+066455Y-008207X0138Y0000R000S3
317GND U2 -30_9 D0098PA00X+066947Y-008207X0138Y0000R000S3
317GND U2 -30_1 D0098PA00X+067439Y-008207X0138Y0000R000S3
317GND U2 -30_1 D0098PA00X+066701Y-008453X0138Y0000R000S3
317GND U2 -30_1 D0098PA00X+067193Y-008453X0138Y0000R000S3
317NET-(D10-K) Extra_-1 D0394PA00X+042283Y-029616X0669Y0787R270S0
317VBAT Extra_-2 D0394PA00X+042283Y-028632X0669Y0787R270S0
327GND D1 -1 A01X+046033Y-007933X0581Y0236R000S2
3273_3V D1 -2 A01X+046033Y-008681X0581Y0236R000S2
327SDA D1 -3 A01X+046772Y-008307X0581Y0236R000S2
3273_3V R10 -1 A01X+073553Y-011417X0315Y0374R180S2
327SCL R10 -2 A01X+072904Y-011417X0315Y0374R180S2
327EXTRA_2 R23 -1 A01X+048455Y-035728X0315Y0374R180S2
@@ -356,10 +361,15 @@ P arrayDim N
317TANK_SENSOR J4 -2 D0295PA00X+042323Y-016850X0472Y0689R270S0
327NET-(CD2-K) CD2 -1 A01X+040945Y-017594X0384Y0551R270S2
3273_3V CD2 -2 A01X+040945Y-016855X0384Y0551R270S2
317ESP_TX J3 -1 D0394PA00X+073051Y-006457X0669Y0669R270S0
317ESP_RX J3 -2 D0394PA00X+074051Y-006457X0669Y0000R270S0
327BOOT_SEL R5 -1 A01X+057677Y-010305X0315Y0374R090S2
327T-(BOOT1-PAD2) R5 -2 A01X+057677Y-010955X0315Y0374R090S2
327NET-(J4-PIN_1) R13 -1 A01X+047047Y-018346X0315Y0374R090S2
327NET-(CD2-K) R13 -2 A01X+047047Y-018996X0315Y0374R090S2
327GND D4 -1 A01X+045305Y-017520X0581Y0236R000S2
3273_3V D4 -2 A01X+045305Y-018268X0581Y0236R000S2
327NET-(J4-PIN_1) D4 -3 A01X+046043Y-017894X0581Y0236R000S2
327GND CD1 -1 A01X+045177Y-006570X0384Y0551R270S2
327NET-(CD1-A) CD1 -2 A01X+045177Y-005832X0384Y0551R270S2
327GND C4 -1 A01X+060630Y-011166X0423Y0374R270S2
@@ -370,8 +380,8 @@ P arrayDim N
317USB_D- J2 -4 D0295PA00X+041260Y-012165X0472Y0689R090S0
317USB_D+ J2 -5 D0295PA00X+041260Y-012953X0472Y0689R090S0
317GND J2 -6 D0295PA00X+041260Y-013740X0472Y0689R090S0
3273_3V R15 -1 A01X+049680Y-023228X0315Y0374R180S2
327FLOW R15 -2 A01X+049031Y-023228X0315Y0374R180S2
3273_3V R15 -1 A01X+048858Y-019990X0315Y0374R090S2
327FLOW R15 -2 A01X+048858Y-020640X0315Y0374R090S2
327NET-(Q1-G) Q1 -1 A01X+048770Y-016831X0354Y0315R090S2
327GND Q1 -2 A01X+048022Y-016831X0354Y0315R090S2
327NET-(J4-PIN_1) Q1 -3 A01X+048396Y-017618X0354Y0315R090S2
@@ -380,20 +390,12 @@ P arrayDim N
327NET-(Q2-G) Q2 -1 A01X+047776Y-034142X0354Y0315R270S2
327GND Q2 -2 A01X+048524Y-034142X0354Y0315R270S2
327NET-(D8-K) Q2 -3 A01X+048150Y-033354X0354Y0315R270S2
317VBAT U7 -1 D0433PA00X+042795Y-037835X0750Y0787R180S0
317GND U7 -2 D0433PA00X+041795Y-037835X0750Y0787R180S0
3173_3V U7 -3 D0433PA00X+040795Y-037835X0750Y0787R180S0
317GND J1 -1 D0394PA00X+047913Y-008346X0669Y0669R270S0
317SDA J1 -2 D0394PA00X+048913Y-008346X0669Y0000R270S0
317SCL J1 -3 D0394PA00X+049913Y-008346X0669Y0000R270S0
327BOOT_SEL R6 -1 A01X+057677Y-011683X0315Y0374R090S2
327NET-(R3-PAD1) R6 -2 A01X+057677Y-012333X0315Y0374R090S2
3173_3V U5 -1 D0394PA00X+046339Y-019764X0669Y0669R000S0
317(U5-VBAT-PAD2) U5 -2 D0394PA00X+046339Y-020764X0669Y0000R000S0
317SDA U5 -3 D0394PA00X+046339Y-021764X0669Y0000R000S0
317SCL U5 -4 D0394PA00X+046339Y-022764X0669Y0000R000S0
317CD_PROBE U5 -5 D0394PA00X+046339Y-023764X0669Y0000R000S0
317GND U5 -6 D0394PA00X+046339Y-024764X0669Y0000R000S0
317GND U5 -7 D0394PA00X+062835Y-021063X0669Y0669R000S0
317GND U5 -8 D0394PA00X+078583Y-019488X0669Y0669R000S0
317GND U5 -9 D0394PA00X+078583Y-034646X0669Y0669R000S0
317GND U5 -10 D0394PA00X+062835Y-031693X0669Y0669R000S0
317GND U5 -11 D0394PA00X+046102Y-034646X0669Y0669R000S0
999

BIN
case/PlantCtrl Case v9.f3z Normal file

Binary file not shown.

Binary file not shown.

View File

@@ -17,6 +17,16 @@ incremental = true
opt-level = 2
[profile.release]
# Explicitly disable LTO which the Xtensa codegen backend has issues
lto = false
strip = true
debug = false
overflow-checks = false
panic = "abort"
incremental = true
opt-level = "z"
[package.metadata.cargo_runner]
# The string `$TARGET_FILE` will be replaced with the path from cargo.
command = [
@@ -86,6 +96,7 @@ pca9535 = { version = "2.0.0", features = ["std"] }
ina219 = { version = "0.2.0", features = ["std"] }
embedded-storage = "=0.3.1"
ekv = "1.0.0"
embedded-can = "0.4.1"
[patch.crates-io]

View File

@@ -11,6 +11,8 @@ pub struct NetworkConfig {
pub password: Option<heapless::String<64>>,
pub mqtt_url: Option<heapless::String<128>>,
pub base_topic: Option<heapless::String<64>>,
pub mqtt_user: Option<heapless::String<32>>,
pub mqtt_password: Option<heapless::String<64>>,
pub max_wait: u32,
}
impl Default for NetworkConfig {
@@ -21,6 +23,8 @@ impl Default for NetworkConfig {
password: None,
mqtt_url: None,
base_topic: None,
mqtt_user: None,
mqtt_password: None,
max_wait: 10000,
}
}
@@ -58,6 +62,7 @@ pub struct TankConfig {
pub tank_warn_percent: u8,
pub tank_empty_percent: u8,
pub tank_full_percent: u8,
pub ml_per_pulse: f32,
}
impl Default for TankConfig {
fn default() -> Self {
@@ -68,6 +73,7 @@ impl Default for TankConfig {
tank_warn_percent: 40,
tank_empty_percent: 5,
tank_full_percent: 95,
ml_per_pulse: 0.0,
}
}
}
@@ -109,7 +115,9 @@ pub struct PlantControllerConfig {
pub struct PlantConfig {
pub mode: PlantWateringMode,
pub target_moisture: f32,
pub min_moisture: f32,
pub pump_time_s: u16,
pub pump_limit_ml: u16,
pub pump_cooldown_min: u16,
pub pump_hour_start: u8,
pub pump_hour_end: u8,
@@ -128,7 +136,9 @@ impl Default for PlantConfig {
Self {
mode: PlantWateringMode::OFF,
target_moisture: 40.,
min_moisture: 30.,
pump_time_s: 30,
pump_limit_ml: 5000,
pump_cooldown_min: 60,
pump_hour_start: 9,
pump_hour_end: 20,

View File

@@ -419,6 +419,8 @@ impl Esp<'_> {
}),
client_id: Some("plantctrl"),
keep_alive_interval: Some(Duration::from_secs(60 * 60 * 2)),
username: network_config.mqtt_user.as_ref().map(|v| &**v),
password: network_config.mqtt_password.as_ref().map(|v| &**v),
//room for improvement
..Default::default()
};

View File

@@ -51,6 +51,7 @@ pub(crate) fn create_initial_board(
config: PlantControllerConfig,
esp: Esp<'static>,
) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
println!("Start initial");
let mut general_fault = PinDriver::input_output(free_pins.gpio6.downgrade())?;
general_fault.set_pull(Pull::Floating)?;
general_fault.set_low()?;

View File

@@ -5,6 +5,7 @@ mod rtc;
mod v3_hal;
mod v4_hal;
mod water;
mod v4_sensor;
use crate::hal::rtc::{DS3231Module, RTCModuleInteraction};
use crate::hal::water::TankSensor;
@@ -22,6 +23,7 @@ use bq34z100::Bq34z100g1Driver;
use ds323x::{DateTimeAccess, Ds323x};
use eeprom24x::{Eeprom24x, SlaveAddr, Storage};
use embedded_hal_bus::i2c::MutexDevice;
use esp_idf_hal::pcnt::PCNT1;
use esp_idf_hal::{
adc::ADC1,
delay::Delay,
@@ -47,10 +49,10 @@ use once_cell::sync::Lazy;
use std::result::Result::Ok as OkStd;
use std::sync::Mutex;
use std::time::Duration;
use esp_idf_hal::can::CAN;
//Only support for 8 right now!
pub const PLANT_COUNT: usize = 8;
const REPEAT_MOIST_MEASURE: usize = 1;
const TANK_MULTI_SAMPLE: usize = 11;
@@ -111,7 +113,7 @@ pub trait BoardInteraction<'a> {
impl dyn BoardInteraction<'_> {
//the counter is just some arbitrary number that increases whenever some progress was made, try to keep the updates < 10 per second for ux reasons
fn progress(&mut self, counter: u32) {
fn _progress(&mut self, counter: u32) {
let even = counter % 2 == 0;
let current = counter / (PLANT_COUNT as u32);
for led in 0..PLANT_COUNT {
@@ -154,7 +156,9 @@ pub struct FreePeripherals {
pub gpio29: Gpio29,
pub gpio30: Gpio30,
pub pcnt0: PCNT0,
pub pcnt1: PCNT1,
pub adc1: ADC1,
pub can: CAN,
}
impl PlantHal {
@@ -184,8 +188,10 @@ impl PlantHal {
boot_button.set_pull(Pull::Floating)?;
let free_pins = FreePeripherals {
can: peripherals.can,
adc1: peripherals.adc1,
pcnt0: peripherals.pcnt0,
pcnt1: peripherals.pcnt1,
gpio0: peripherals.pins.gpio0,
gpio1: peripherals.pins.gpio1,
gpio2: peripherals.pins.gpio2,

View File

@@ -1,7 +1,7 @@
use crate::hal::rtc::RTCModuleInteraction;
use crate::hal::water::TankSensor;
use crate::hal::{
deep_sleep, BoardInteraction, FreePeripherals, Sensor, PLANT_COUNT, REPEAT_MOIST_MEASURE,
deep_sleep, BoardInteraction, FreePeripherals, Sensor, PLANT_COUNT,
};
use crate::log::{log, LogMessage};
use crate::{
@@ -64,6 +64,9 @@ const FAULT_4: usize = 21;
const FAULT_1: usize = 22;
const FAULT_2: usize = 23;
const REPEAT_MOIST_MEASURE: usize = 1;
pub struct V3<'a> {
config: PlantControllerConfig,
battery_monitor: Box<dyn BatteryInteraction + Send>,
@@ -91,6 +94,7 @@ pub(crate) fn create_v3(
battery_monitor: Box<dyn BatteryInteraction + Send>,
rtc_module: Box<dyn RTCModuleInteraction + Send>,
) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
println!("Start v3");
let mut clock = PinDriver::input_output(peripherals.gpio15.downgrade())?;
clock.set_pull(Pull::Floating)?;
let mut latch = PinDriver::input_output(peripherals.gpio3.downgrade())?;
@@ -124,12 +128,16 @@ pub(crate) fn create_v3(
let one_wire_pin = peripherals.gpio18.downgrade();
let tank_power_pin = peripherals.gpio11.downgrade();
let flow_sensor_pin = peripherals.gpio4.downgrade();
let tank_sensor = TankSensor::create(
one_wire_pin,
peripherals.adc1,
peripherals.gpio5,
tank_power_pin,
);
flow_sensor_pin,
peripherals.pcnt1,
)?;
let mut signal_counter = PcntDriver::new(
peripherals.pcnt0,
@@ -202,7 +210,7 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
&self.config
}
fn get_battery_monitor(&mut self) -> &mut Box<(dyn BatteryInteraction + Send + 'static)> {
fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send + 'static> {
&mut self.battery_monitor
}

View File

@@ -2,16 +2,16 @@ use crate::config::PlantControllerConfig;
use crate::hal::battery::BatteryInteraction;
use crate::hal::esp::Esp;
use crate::hal::rtc::RTCModuleInteraction;
use crate::hal::v4_sensor::SensorImpl;
use crate::hal::v4_sensor::SensorInteraction;
use crate::hal::water::TankSensor;
use crate::hal::{
deep_sleep, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT,
REPEAT_MOIST_MEASURE,
deep_sleep, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT
};
use crate::log::{log, LogMessage};
use anyhow::bail;
use embedded_hal::digital::OutputPin;
use embedded_hal_bus::i2c::MutexDevice;
use esp_idf_hal::delay::Delay;
use esp_idf_hal::gpio::{AnyInputPin, IOPin, InputOutput, Output, PinDriver, Pull};
use esp_idf_hal::i2c::I2cDriver;
use esp_idf_hal::pcnt::{
@@ -25,13 +25,9 @@ use ina219::SyncIna219;
use measurements::{Current, Resistance, Voltage};
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
use std::result::Result::Ok as OkStd;
const MS0: u8 = 1_u8;
const MS1: u8 = 0_u8;
const MS2: u8 = 3_u8;
const MS3: u8 = 4_u8;
const MS4: u8 = 2_u8;
const SENSOR_ON: u8 = 5_u8;
use embedded_can::Frame;
use embedded_can::StandardId;
use esp_idf_hal::can;
pub enum Charger<'a> {
SolarMpptV1 {
@@ -119,13 +115,13 @@ pub struct V4<'a> {
rtc_module: Box<dyn RTCModuleInteraction + Send>,
battery_monitor: Box<dyn BatteryInteraction + Send>,
config: PlantControllerConfig,
signal_counter: PcntDriver<'a>,
awake: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
pump_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>,
pump_ina: Option<SyncIna219<MutexDevice<'a, I2cDriver<'a>>, UnCalibrated>>,
sensor_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>,
sensor: SensorImpl<'a>,
extra1: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
extra2: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
}
@@ -137,6 +133,7 @@ pub(crate) fn create_v4(
battery_monitor: Box<dyn BatteryInteraction + Send>,
rtc_module: Box<dyn RTCModuleInteraction + Send>,
) -> anyhow::Result<Box<dyn BoardInteraction<'static> + Send + 'static>> {
println!("Start v4");
let mut awake = PinDriver::output(peripherals.gpio21.downgrade())?;
awake.set_high()?;
@@ -152,14 +149,22 @@ pub(crate) fn create_v4(
let one_wire_pin = peripherals.gpio18.downgrade();
let tank_power_pin = peripherals.gpio11.downgrade();
let flow_sensor_pin = peripherals.gpio4.downgrade();
let tank_sensor = TankSensor::create(
one_wire_pin,
peripherals.adc1,
peripherals.gpio5,
tank_power_pin,
);
flow_sensor_pin,
peripherals.pcnt1,
)?;
let mut sensor_expander = Pca9535Immediate::new(MutexDevice::new(&I2C_DRIVER), 34);
let sensor = match sensor_expander.pin_into_output(GPIOBank::Bank0, 0) {
Ok(_) => {
println!("SensorExpander answered");
//pulse counter version
let mut signal_counter = PcntDriver::new(
peripherals.pcnt0,
Some(peripherals.gpio22),
@@ -182,6 +187,42 @@ pub(crate) fn create_v4(
},
)?;
for pin in 0..8 {
let _ = sensor_expander.pin_into_output(GPIOBank::Bank0, pin);
let _ = sensor_expander.pin_into_output(GPIOBank::Bank1, pin);
let _ = sensor_expander.pin_set_low(GPIOBank::Bank0, pin);
let _ = sensor_expander.pin_set_low(GPIOBank::Bank1, pin);
}
SensorImpl::PulseCounter {
signal_counter,
sensor_expander,
}
}
Err(_) => {
println!("Can bus mode ");
let timing = can::config::Timing::B25K;
let config = can::config::Config::new().timing(timing);
let can = can::CanDriver::new(peripherals.can, peripherals.gpio0, peripherals.gpio2, &config).unwrap();
let frame = StandardId::new(0x042).unwrap();
let tx_frame = Frame::new(frame, &[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
can.transmit(&tx_frame, 1000).unwrap();
if let Ok(rx_frame) = can.receive(1000) {
println!("rx {:}:", rx_frame);
}
//can bus version
SensorImpl::CanBus {
can
}
}
};
let mut solar_is_day = PinDriver::input(peripherals.gpio7.downgrade())?;
solar_is_day.set_pull(Pull::Floating)?;
@@ -200,13 +241,7 @@ pub(crate) fn create_v4(
let _ = pump_expander.pin_set_low(GPIOBank::Bank1, pin);
}
let mut sensor_expander = Pca9535Immediate::new(MutexDevice::new(&I2C_DRIVER), 34);
for pin in 0..8 {
let _ = sensor_expander.pin_into_output(GPIOBank::Bank0, pin);
let _ = sensor_expander.pin_into_output(GPIOBank::Bank1, pin);
let _ = sensor_expander.pin_set_low(GPIOBank::Bank0, pin);
let _ = sensor_expander.pin_set_low(GPIOBank::Bank1, pin);
}
let mppt_ina = SyncIna219::new(
MutexDevice::new(&I2C_DRIVER),
@@ -249,17 +284,16 @@ pub(crate) fn create_v4(
esp,
awake,
tank_sensor,
signal_counter,
light,
general_fault,
pump_ina,
pump_expander,
sensor_expander,
config,
battery_monitor,
charger,
extra1,
extra2,
sensor,
};
Ok(Box::new(v))
}
@@ -347,78 +381,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
}
fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> anyhow::Result<f32> {
let mut results = [0_f32; REPEAT_MOIST_MEASURE];
for repeat in 0..REPEAT_MOIST_MEASURE {
self.signal_counter.counter_pause()?;
self.signal_counter.counter_clear()?;
//Disable all
self.sensor_expander.pin_set_high(GPIOBank::Bank0, MS4)?;
let sensor_channel = match sensor {
Sensor::A => plant as u32,
Sensor::B => (15 - plant) as u32,
};
let is_bit_set = |b: u8| -> bool { sensor_channel & (1 << b) != 0 };
if is_bit_set(0) {
self.sensor_expander.pin_set_high(GPIOBank::Bank0, MS0)?;
} else {
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS0)?;
}
if is_bit_set(1) {
self.sensor_expander.pin_set_high(GPIOBank::Bank0, MS1)?;
} else {
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS1)?;
}
if is_bit_set(2) {
self.sensor_expander.pin_set_high(GPIOBank::Bank0, MS2)?;
} else {
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS2)?;
}
if is_bit_set(3) {
self.sensor_expander.pin_set_high(GPIOBank::Bank0, MS3)?;
} else {
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS3)?;
}
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS4)?;
self.sensor_expander
.pin_set_high(GPIOBank::Bank0, SENSOR_ON)?;
let delay = Delay::new_default();
let measurement = 100; // TODO what is this scaling factor? what is its purpose?
let factor = 1000f32 / measurement as f32;
//give some time to stabilize
delay.delay_ms(10);
self.signal_counter.counter_resume()?;
delay.delay_ms(measurement);
self.signal_counter.counter_pause()?;
self.sensor_expander.pin_set_high(GPIOBank::Bank0, MS4)?;
self.sensor_expander
.pin_set_low(GPIOBank::Bank0, SENSOR_ON)?;
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS0)?;
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS1)?;
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS2)?;
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS3)?;
delay.delay_ms(10);
let unscaled = self.signal_counter.get_counter_value()? as i32;
let hz = unscaled as f32 * factor;
log(
LogMessage::RawMeasure,
unscaled as u32,
hz as u32,
&plant.to_string(),
&format!("{sensor:?}"),
);
results[repeat] = hz;
}
results.sort_by(|a, b| a.partial_cmp(b).unwrap()); // floats don't seem to implement total_ord
let mid = results.len() / 2;
let median = results[mid];
anyhow::Ok(median)
self.sensor.measure_moisture_hz(plant, sensor)
}
fn general_fault(&mut self, enable: bool) {

115
rust/src/hal/v4_sensor.rs Normal file
View File

@@ -0,0 +1,115 @@
use embedded_hal_bus::i2c::MutexDevice;
use esp_idf_hal::can::CanDriver;
use esp_idf_hal::delay::Delay;
use esp_idf_hal::i2c::I2cDriver;
use esp_idf_hal::pcnt::PcntDriver;
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
use crate::hal::Sensor;
use crate::log::{log, LogMessage};
const REPEAT_MOIST_MEASURE: usize = 10;
pub trait SensorInteraction {
fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> anyhow::Result<f32>;
}
const MS0: u8 = 1_u8;
const MS1: u8 = 0_u8;
const MS2: u8 = 3_u8;
const MS3: u8 = 4_u8;
const MS4: u8 = 2_u8;
const SENSOR_ON: u8 = 5_u8;
pub enum SensorImpl<'a> {
PulseCounter{
signal_counter: PcntDriver<'a>,
sensor_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>,
},
CanBus{
can: CanDriver<'a>
}
}
impl SensorInteraction for SensorImpl<'_> {
fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> anyhow::Result<f32> {
match self {
SensorImpl::PulseCounter { signal_counter, sensor_expander, .. } => {
let mut results = [0_f32; REPEAT_MOIST_MEASURE];
for repeat in 0..REPEAT_MOIST_MEASURE {
signal_counter.counter_pause()?;
signal_counter.counter_clear()?;
//Disable all
sensor_expander.pin_set_high(GPIOBank::Bank0, MS4)?;
let sensor_channel = match sensor {
Sensor::A => plant as u32,
Sensor::B => (15 - plant) as u32,
};
let is_bit_set = |b: u8| -> bool { sensor_channel & (1 << b) != 0 };
if is_bit_set(0) {
sensor_expander.pin_set_high(GPIOBank::Bank0, MS0)?;
} else {
sensor_expander.pin_set_low(GPIOBank::Bank0, MS0)?;
}
if is_bit_set(1) {
sensor_expander.pin_set_high(GPIOBank::Bank0, MS1)?;
} else {
sensor_expander.pin_set_low(GPIOBank::Bank0, MS1)?;
}
if is_bit_set(2) {
sensor_expander.pin_set_high(GPIOBank::Bank0, MS2)?;
} else {
sensor_expander.pin_set_low(GPIOBank::Bank0, MS2)?;
}
if is_bit_set(3) {
sensor_expander.pin_set_high(GPIOBank::Bank0, MS3)?;
} else {
sensor_expander.pin_set_low(GPIOBank::Bank0, MS3)?;
}
sensor_expander.pin_set_low(GPIOBank::Bank0, MS4)?;
sensor_expander
.pin_set_high(GPIOBank::Bank0, SENSOR_ON)?;
let delay = Delay::new_default();
let measurement = 100; // TODO what is this scaling factor? what is its purpose?
let factor = 1000f32 / measurement as f32;
//give some time to stabilize
delay.delay_ms(10);
signal_counter.counter_resume()?;
delay.delay_ms(measurement);
signal_counter.counter_pause()?;
sensor_expander.pin_set_high(GPIOBank::Bank0, MS4)?;
sensor_expander
.pin_set_low(GPIOBank::Bank0, SENSOR_ON)?;
sensor_expander.pin_set_low(GPIOBank::Bank0, MS0)?;
sensor_expander.pin_set_low(GPIOBank::Bank0, MS1)?;
sensor_expander.pin_set_low(GPIOBank::Bank0, MS2)?;
sensor_expander.pin_set_low(GPIOBank::Bank0, MS3)?;
delay.delay_ms(10);
let unscaled = signal_counter.get_counter_value()? as i32;
let hz = unscaled as f32 * factor;
log(
LogMessage::RawMeasure,
unscaled as u32,
hz as u32,
&plant.to_string(),
&format!("{sensor:?}"),
);
results[repeat] = hz;
}
results.sort_by(|a, b| a.partial_cmp(b).unwrap()); // floats don't seem to implement total_ord
let mid = results.len() / 2;
let median = results[mid];
anyhow::Ok(median)
}
SensorImpl::CanBus { .. } => {
todo!()
}
}
}
}

View File

@@ -5,7 +5,10 @@ use esp_idf_hal::adc::oneshot::config::AdcChannelConfig;
use esp_idf_hal::adc::oneshot::{AdcChannelDriver, AdcDriver};
use esp_idf_hal::adc::{attenuation, Resolution, ADC1};
use esp_idf_hal::delay::Delay;
use esp_idf_hal::gpio::{AnyIOPin, 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_sys::EspError;
use one_wire_bus::OneWire;
@@ -13,6 +16,7 @@ pub struct TankSensor<'a> {
one_wire_bus: OneWire<PinDriver<'a, AnyIOPin, InputOutput>>,
tank_channel: AdcChannelDriver<'a, Gpio5, AdcDriver<'a, ADC1>>,
tank_power: PinDriver<'a, AnyIOPin, InputOutput>,
flow_counter: PcntDriver<'a>,
delay: Delay,
}
@@ -22,7 +26,9 @@ impl<'a> TankSensor<'a> {
adc1: ADC1,
gpio5: Gpio5,
tank_power_pin: AnyIOPin,
) -> TankSensor<'a> {
flow_sensor_pin: AnyIOPin,
pcnt1: PCNT1,
) -> anyhow::Result<TankSensor<'a>> {
let mut one_wire_pin =
PinDriver::input_output_od(one_wire_pin).expect("Failed to configure pin");
one_wire_pin
@@ -47,12 +53,53 @@ impl<'a> TankSensor<'a> {
let one_wire_bus =
OneWire::new(one_wire_pin).expect("OneWire bus did not pull up after release");
TankSensor {
let mut flow_counter = PcntDriver::new(
pcnt1,
Some(flow_sensor_pin),
Option::<AnyInputPin>::None,
Option::<AnyInputPin>::None,
Option::<AnyInputPin>::None,
)?;
flow_counter.channel_config(
PcntChannel::Channel1,
PinIndex::Pin0,
PinIndex::Pin1,
&PcntChannelConfig {
lctrl_mode: PcntControlMode::Keep,
hctrl_mode: PcntControlMode::Keep,
pos_mode: PcntCountMode::Increment,
neg_mode: PcntCountMode::Hold,
counter_h_lim: i16::MAX,
counter_l_lim: 0,
},
)?;
Ok(TankSensor {
one_wire_bus,
tank_channel,
tank_power,
flow_counter,
delay: Default::default(),
})
}
pub fn reset_flow_meter(&mut self) {
self.flow_counter.counter_pause().unwrap();
self.flow_counter.counter_clear().unwrap();
}
pub fn start_flow_meter(&mut self) {
self.flow_counter.counter_resume().unwrap();
}
pub fn get_flow_meter_value(&mut self) -> i16 {
self.flow_counter.get_counter_value().unwrap()
}
pub fn stop_flow_meter(&mut self) -> i16 {
self.flow_counter.counter_pause().unwrap();
self.get_flow_meter_value()
}
pub fn water_temperature_c(&mut self) -> anyhow::Result<f32> {

View File

@@ -88,14 +88,9 @@ pub struct PumpResult {
max_current_ma: u16,
min_current_ma: u16,
error: bool,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
/// humidity sensor error
enum SensorError {
Unknown,
ShortCircuit { hz: f32, max: f32 },
OpenCircuit { hz: f32, min: f32 },
flow_value_ml: f32,
flow_value_count: i16,
pump_time_s: u16,
}
#[derive(Serialize, Debug, PartialEq)]
@@ -583,12 +578,41 @@ pub fn do_secure_pump(
dry_run: bool,
) -> anyhow::Result<PumpResult> {
let mut current_collector = vec![0_u16; plant_config.pump_time_s.into()];
let mut flow_collector = vec![0_i16; plant_config.pump_time_s.into()];
let mut error = false;
let mut first_error = true;
let mut pump_time_s = 0;
if !dry_run {
board
.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)?;
Delay::new_default().delay_ms(2);
Delay::new_default().delay_ms(10);
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();
flow_collector[step] = flow_value;
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
);
if flow_value_ml > plant_config.pump_limit_ml as f32 {
println!("Flow value is reached, stopping");
break;
}
let current = board.board_hal.pump_current(plant_id);
match current {
Ok(current) => {
@@ -651,13 +675,28 @@ pub fn do_secure_pump(
}
}
Delay::new_default().delay_ms(1000);
pump_time_s += 1;
}
}
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 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
);
current_collector.sort();
Ok(PumpResult {
median_current_ma: current_collector[current_collector.len() / 2],
max_current_ma: current_collector[current_collector.len() - 1],
min_current_ma: current_collector[0],
flow_value_ml,
flow_value_count: final_flow_value,
pump_time_s,
error,
})
}
@@ -820,7 +859,7 @@ fn pump_info(
median_current_ma: u16,
max_current_ma: u16,
min_current_ma: u16,
error: bool,
_error: bool,
) {
let pump_info = PumpInfo {
enabled: pump_active,

View File

@@ -78,6 +78,7 @@ impl PumpState {
pub enum PlantWateringMode {
OFF,
TargetMoisture,
MinMoisture,
TimerOnly,
}
@@ -235,6 +236,30 @@ impl PlantState {
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),
}
}
@@ -268,7 +293,9 @@ impl PlantState {
.map(|t| t.with_timezone(&current_time.timezone())),
next_pump: if matches!(
plant_conf.mode,
PlantWateringMode::TimerOnly | PlantWateringMode::TargetMoisture
PlantWateringMode::TimerOnly
| PlantWateringMode::TargetMoisture
| PlantWateringMode::MinMoisture
) {
self.pump.previous_pump.and_then(|last_pump| {
last_pump

View File

@@ -29,6 +29,8 @@ export interface NetworkConfig {
password: string,
mqtt_url: string,
base_topic: string,
mqtt_user: string | null,
mqtt_password: string | null,
max_wait: number
}
@@ -71,6 +73,7 @@ export interface TankConfig {
tank_warn_percent: number,
tank_empty_percent: number,
tank_full_percent: number,
ml_per_pulse: number
}
@@ -104,6 +107,7 @@ export interface PlantControllerConfig {
export interface PlantConfig {
mode: string,
target_moisture: number,
min_moisture: number,
pump_time_s: number,
pump_cooldown_min: number,
pump_hour_start: number,
@@ -122,6 +126,9 @@ export interface PumpTestResult {
median_current_ma: number,
max_current_ma: number,
min_current_ma: number,
flow_value_ml: number,
flow_value_count: number,
pump_time_s: number,
error: boolean,
}

View File

@@ -72,6 +72,18 @@
</div>
<input class="mqttvalue" type="text" id="base_topic" placeholder="plants/one">
</div>
<div class="flexcontainer">
<div class="mqttkey">
MQTT User
</div>
<input class="mqttvalue" type="text" id="mqtt_user" placeholder="">
</div>
<div class="flexcontainer">
<div class="mqttkey">
MQTT Password
</div>
<input class="mqttvalue" type="text" id="mqtt_password" placeholder="">
</div>
</div>
</div>

View File

@@ -16,6 +16,8 @@ export class NetworkConfigView {
private readonly mqtt_url: HTMLInputElement;
private readonly base_topic: HTMLInputElement;
private readonly max_wait: HTMLInputElement;
private readonly mqtt_user: HTMLInputElement;
private readonly mqtt_password: HTMLInputElement;
private readonly ssidlist: HTMLElement;
constructor(controller: Controller, publicIp: string) {
@@ -37,6 +39,10 @@ export class NetworkConfigView {
this.mqtt_url.onchange = controller.configChanged
this.base_topic = document.getElementById("base_topic") as HTMLInputElement;
this.base_topic.onchange = controller.configChanged
this.mqtt_user = document.getElementById("mqtt_user") as HTMLInputElement;
this.mqtt_user.onchange = controller.configChanged
this.mqtt_password = document.getElementById("mqtt_password") as HTMLInputElement;
this.mqtt_password.onchange = controller.configChanged
this.ssidlist = document.getElementById("ssidlist") as HTMLElement
@@ -52,6 +58,8 @@ export class NetworkConfigView {
this.password.value = network.password;
this.mqtt_url.value = network.mqtt_url;
this.base_topic.value = network.base_topic;
this.mqtt_user.value = network.mqtt_user ?? "";
this.mqtt_password.value = network.mqtt_password ?? "";
this.max_wait.value = network.max_wait.toString();
}
@@ -62,6 +70,8 @@ export class NetworkConfigView {
ssid: this.ssid.value ?? null,
password: this.password.value ?? null,
mqtt_url: this.mqtt_url.value ?? null,
mqtt_user: this.mqtt_user.value ? this.mqtt_user.value : null,
mqtt_password: this.mqtt_password.value ? this.mqtt_password.value : null,
base_topic: this.base_topic.value ?? null
}
}

View File

@@ -55,6 +55,7 @@
<select class="plantvalue" id="plant_${plantId}_mode">
<option value="OFF">Off</option>
<option value="TargetMoisture">Target</option>
<option value="MinMoisture">Min Moisture</option>
<option value="TimerOnly">Timer</option>
</select>
@@ -63,6 +64,10 @@
<div class="plantkey">Target Moisture:</div>
<input class="plantvalue" id="plant_${plantId}_target_moisture" type="number" min="0" max="100" placeholder="0">
</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="plantkey">Pump Time (s):</div>
<input class="plantvalue" id="plant_${plantId}_pump_time_s" type="number" min="0" max="600" placeholder="30">
@@ -129,8 +134,28 @@
<span class="plantsensorvalue" id="plant_${plantId}_moisture_b">not measured</span>
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="plantsensorkey">Test Current</div>
<span class="plantsensorvalue" id="plant_${plantId}_pump_current_result">not_tested</span>
<div class="plantsensorkey">Max Current</div>
<span class="plantsensorvalue" id="plant_${plantId}_pump_test_current_max">not_tested</span>
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="plantsensorkey">Min Current</div>
<span class="plantsensorvalue" id="plant_${plantId}_pump_test_current_min">not_tested</span>
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="plantsensorkey">Average</div>
<span class="plantsensorvalue" id="plant_${plantId}_pump_test_current_average">not_tested</span>
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="plantsensorkey">Pump Time</div>
<span class="plantsensorvalue" id="plant_${plantId}_pump_test_pump_time">not_tested</span>
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="plantsensorkey">Flow ml</div>
<span class="plantsensorvalue" id="plant_${plantId}_pump_test_flow_ml">not_tested</span>
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="plantsensorkey">Flow raw</div>
<span class="plantsensorvalue" id="plant_${plantId}_pump_test_flow_raw">not_tested</span>
</div>

View File

@@ -59,6 +59,7 @@ export class PlantView {
private readonly header: HTMLElement;
private readonly testButton: HTMLButtonElement;
private readonly targetMoisture: HTMLInputElement;
private readonly minMoisture: HTMLInputElement;
private readonly pumpTimeS: HTMLInputElement;
private readonly pumpCooldown: HTMLInputElement;
private readonly pumpHourStart: HTMLSelectElement;
@@ -68,12 +69,18 @@ export class PlantView {
private readonly mode: HTMLSelectElement;
private readonly moistureA: HTMLElement;
private readonly moistureB: HTMLElement;
private readonly pump_current_result: HTMLElement
private readonly maxConsecutivePumpCount: HTMLInputElement;
private readonly minPumpCurrentMa: HTMLInputElement;
private readonly maxPumpCurrentMa: HTMLInputElement;
private readonly ignoreCurrentError: HTMLInputElement;
private readonly pump_test_current_max: HTMLElement;
private readonly pump_test_current_min: HTMLElement;
private readonly pump_test_current_average: HTMLElement;
private readonly pump_test_pump_time: HTMLElement;
private readonly pump_test_flow_ml: HTMLElement;
private readonly pump_test_flow_raw: HTMLElement;
constructor(plantId: number, parent: HTMLDivElement, controller: Controller) {
this.plantId = plantId;
@@ -89,8 +96,13 @@ export class PlantView {
this.moistureA = document.getElementById("plant_" + plantId + "_moisture_a")! as HTMLElement;
this.moistureB = document.getElementById("plant_" + plantId + "_moisture_b")! as HTMLElement;
this.pump_current_result = document.getElementById("plant_" + plantId + "_pump_current_result")! as HTMLElement;
this.pump_test_current_max = document.getElementById("plant_" + plantId + "_pump_test_current_max")! as HTMLElement;
this.pump_test_current_min = document.getElementById("plant_" + plantId + "_pump_test_current_min")! as HTMLElement;
this.pump_test_current_average = document.getElementById("plant_" + plantId + "_pump_test_current_average")! as HTMLElement;
this.pump_test_pump_time = document.getElementById("plant_" + plantId + "_pump_test_pump_time")! as HTMLElement;
this.pump_test_flow_ml = document.getElementById("plant_" + plantId + "_pump_test_flow_ml")! as HTMLElement;
this.pump_test_flow_raw = document.getElementById("plant_" + plantId + "_pump_test_flow_raw")! as HTMLElement;
this.testButton = document.getElementById("plant_" + plantId + "_test")! as HTMLButtonElement;
this.testButton.onclick = function () {
@@ -107,6 +119,11 @@ export class PlantView {
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.onchange = function () {
controller.configChanged()
@@ -192,13 +209,15 @@ export class PlantView {
let sensorOnly = document.getElementsByClassName("plantSensorEnabledOnly_"+ this.plantId)
let pumpOnly = document.getElementsByClassName("plantPumpEnabledOnly_"+ this.plantId)
let targetOnly = document.getElementsByClassName("plantTargetEnabledOnly_"+ this.plantId)
let minOnly = document.getElementsByClassName("plantMinEnabledOnly_"+ this.plantId)
console.log("updateVisibility plantConfig: " + plantConfig.mode)
let showSensor = plantConfig.sensor_a || plantConfig.sensor_b
let showPump = plantConfig.mode !== "OFF"
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)) {
if (showSensor) {
@@ -223,10 +242,25 @@ export class PlantView {
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) {
this.pump_current_result.innerText = "Did abort " + result.error + " median current " + result.median_current_ma + " max current " + result.max_current_ma + " min current " + result.min_current_ma
this.pump_test_current_max.innerText = result.max_current_ma.toString()
this.pump_test_current_min.innerText = result.min_current_ma.toString()
this.pump_test_current_average.innerText = result.median_current_ma.toString()
this.pump_test_flow_raw.innerText = result.flow_value_count.toString()
this.pump_test_flow_ml.innerText = result.flow_value_ml.toString()
this.pump_test_pump_time.innerText = result.pump_time_s.toString()
}
setMeasurementResult(a: string, b: string) {
@@ -237,6 +271,7 @@ export class PlantView {
setConfig(plantConfig: PlantConfig) {
this.mode.value = plantConfig.mode;
this.targetMoisture.value = plantConfig.target_moisture.toString();
this.minMoisture.value = plantConfig.min_moisture?.toString() || "";
this.pumpTimeS.value = plantConfig.pump_time_s.toString();
this.pumpCooldown.value = plantConfig.pump_cooldown_min.toString();
this.pumpHourStart.value = plantConfig.pump_hour_start.toString();
@@ -262,6 +297,7 @@ export class PlantView {
let conv: PlantConfig = {
mode: this.mode.value,
target_moisture: this.targetMoisture.valueAsNumber,
min_moisture: this.minMoisture.valueAsNumber,
pump_time_s: this.pumpTimeS.valueAsNumber,
pump_cooldown_min: this.pumpCooldown.valueAsNumber,
pump_hour_start: +this.pumpHourStart.value,

View File

@@ -48,6 +48,10 @@
<div class="tankkey">Full at %</div>
<input class="tankvalue" type="number" min="0" max="100" id="tank_full_percent">
</div>
<div class="flexcontainer">
<div class="tankkey">Flow Sensor ml per pulse</div>
<input class="tankvalue" type="number" min="0" max="1000" step="0.01" id="ml_per_pulse">
</div>
<button id="tank_update">Update Tank</button>

View File

@@ -8,6 +8,7 @@ export class TankConfigView {
private readonly tank_warn_percent: HTMLInputElement;
private readonly tank_sensor_enabled: HTMLInputElement;
private readonly tank_allow_pumping_if_sensor_error: HTMLInputElement;
private readonly ml_per_pulse: HTMLInputElement;
private readonly tank_measure_error: HTMLLabelElement;
private readonly tank_measure_ml: HTMLLabelElement;
private readonly tank_measure_percent: HTMLLabelElement;
@@ -54,6 +55,8 @@ export class TankConfigView {
this.tank_sensor_enabled.onchange = controller.configChanged
this.tank_allow_pumping_if_sensor_error = document.getElementById("tank_allow_pumping_if_sensor_error") as HTMLInputElement;
this.tank_allow_pumping_if_sensor_error.onchange = controller.configChanged
this.ml_per_pulse = document.getElementById("ml_per_pulse") as HTMLInputElement;
this.ml_per_pulse.onchange = controller.configChanged
let tank_update = document.getElementById("tank_update") as HTMLInputElement;
tank_update.onclick = () => {
@@ -141,6 +144,7 @@ export class TankConfigView {
this.tank_full_percent.value = String(tank.tank_full_percent)
this.tank_sensor_enabled.checked = tank.tank_sensor_enabled
this.tank_useable_ml.value = String(tank.tank_useable_ml)
this.ml_per_pulse.value = String(tank.ml_per_pulse)
}
getConfig(): TankConfig {
return {
@@ -149,7 +153,8 @@ export class TankConfigView {
tank_full_percent: this.tank_full_percent.valueAsNumber,
tank_sensor_enabled: this.tank_sensor_enabled.checked,
tank_useable_ml: this.tank_useable_ml.valueAsNumber,
tank_warn_percent: this.tank_warn_percent.valueAsNumber
tank_warn_percent: this.tank_warn_percent.valueAsNumber,
ml_per_pulse: this.ml_per_pulse.valueAsNumber
}
}
}