Compare commits
3 Commits
containeri
...
50a052df9a
| Author | SHA1 | Date | |
|---|---|---|---|
|
50a052df9a
|
|||
|
c4820e8035
|
|||
|
31b6f8633c
|
14
.gitignore
vendored
14
.gitignore
vendored
@@ -1,19 +1,13 @@
|
|||||||
*.sch-bak
|
*.sch-bak
|
||||||
|
PlantCtrlESP32-backups/
|
||||||
|
board/production/PlantCtrlESP32_2023-11-08_00-45-35/PlantCtrlESP32.zip
|
||||||
|
board/production/PlantCtrlESP32_2023-11-08_00-45-35/netlist.ipc
|
||||||
.vscode
|
.vscode
|
||||||
.embuild/
|
.embuild/
|
||||||
target/
|
target
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
node_modules/
|
node_modules/
|
||||||
rust/src/webserver/bundle.js
|
rust/src/webserver/bundle.js
|
||||||
rust/src/webserver/index.html
|
rust/src/webserver/index.html
|
||||||
rust/build/
|
rust/build/
|
||||||
rust/image.bin
|
rust/image.bin
|
||||||
*.zip
|
|
||||||
fp-info-cache
|
|
||||||
production/
|
|
||||||
_autosave*
|
|
||||||
~_autosave*
|
|
||||||
\#auto_saved_files#
|
|
||||||
*.lck
|
|
||||||
build/
|
|
||||||
image.bin
|
|
||||||
|
|||||||
Binary file not shown.
@@ -1,4 +0,0 @@
|
|||||||
(fp_lib_table
|
|
||||||
(version 7)
|
|
||||||
(lib (name "Modules")(type "KiCad")(uri "../_Shared/footprint/Modules.pretty")(options "")(descr ""))
|
|
||||||
)
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
(sym_lib_table
|
|
||||||
(version 7)
|
|
||||||
(lib (name "Light")(type "KiCad")(uri "/home/empire/workspace/PlantCtrl/Hardware/_Shared/symbols/Light.kicad_sym")(options "")(descr ""))
|
|
||||||
)
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
(footprint SOIC127P600X175-8N (layer F.Cu) (tedit 68EBCB5D)
|
|
||||||
(descr "")
|
|
||||||
(attr smd)
|
|
||||||
(fp_text reference REF** (at -0.268 -3.2064 0) (layer F.SilkS)
|
|
||||||
(effects (font (size 0.64 0.64) (thickness 0.15)))
|
|
||||||
)
|
|
||||||
(fp_text value SOIC127P600X175-8N (at 5.0152 3.1936 0) (layer F.Fab)
|
|
||||||
(effects (font (size 0.64 0.64) (thickness 0.15)))
|
|
||||||
)
|
|
||||||
(pad 1 smd roundrect (roundrect_rratio 0.25) (at -2.475 -1.905) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(pad 2 smd roundrect (roundrect_rratio 0.25) (at -2.475 -0.635) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(pad 3 smd roundrect (roundrect_rratio 0.25) (at -2.475 0.635) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(pad 4 smd roundrect (roundrect_rratio 0.25) (at -2.475 1.905) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(pad 5 smd roundrect (roundrect_rratio 0.25) (at 2.475 1.905) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(pad 6 smd roundrect (roundrect_rratio 0.25) (at 2.475 0.635) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(pad 7 smd roundrect (roundrect_rratio 0.25) (at 2.475 -0.635) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(pad 8 smd roundrect (roundrect_rratio 0.25) (at 2.475 -1.905) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(fp_line (start -2.0 2.5) (end -2.0 -2.5) (layer F.Fab) (width 0.127))
|
|
||||||
(fp_line (start -2.0 -2.5) (end 2.0 -2.5) (layer F.Fab) (width 0.127))
|
|
||||||
(fp_line (start 2.0 -2.5) (end 2.0 2.5) (layer F.Fab) (width 0.127))
|
|
||||||
(fp_line (start 2.0 2.5) (end -2.0 2.5) (layer F.Fab) (width 0.127))
|
|
||||||
(fp_line (start -2.0 -2.519) (end 2.0 -2.519) (layer F.SilkS) (width 0.127))
|
|
||||||
(fp_line (start -2.0 2.519) (end 2.0 2.519) (layer F.SilkS) (width 0.127))
|
|
||||||
(fp_line (start -3.71 -2.46) (end -2.25 -2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start -2.25 -2.46) (end -2.25 -2.75) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start -2.25 -2.75) (end 2.25 -2.75) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start 2.25 -2.75) (end 2.25 -2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start 2.25 -2.46) (end 3.71 -2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start 3.71 2.46) (end 2.25 2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start 2.25 2.46) (end 2.25 2.75) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start 2.25 2.75) (end -2.25 2.75) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start -2.25 2.75) (end -2.25 2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start -2.25 2.46) (end -3.71 2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start -3.71 2.46) (end -3.71 -2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start 3.71 -2.46) (end 3.71 2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_circle (center -4.1 -1.9) (end -4.0 -1.9) (layer F.SilkS) (width 0.2))
|
|
||||||
(fp_circle (center -4.1 -1.9) (end -4.0 -1.9) (layer F.Fab) (width 0.2))
|
|
||||||
)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
(fp_lib_table
|
|
||||||
(version 7)
|
|
||||||
(lib (name "amass")(type "KiCad")(uri "${KIPRJMOD}/footprints/amass")(options "")(descr ""))
|
|
||||||
(lib (name "Modules")(type "KiCad")(uri "${KIPRJMOD}/../_Shared/footprint/Modules.pretty")(options "")(descr ""))
|
|
||||||
(lib (name "4435")(type "KiCad")(uri "${KIPRJMOD}/footprints/4435")(options "")(descr ""))
|
|
||||||
)
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
|
|
||||||
(kicad_symbol_lib (version 20211014) (generator kicad_symbol_editor)
|
|
||||||
(symbol "AO4435" (pin_names (offset 1.016)) (in_bom yes) (on_board yes)
|
|
||||||
(property "Reference" "Q" (id 0) (at -11.43 8.89 0)
|
|
||||||
(effects (font (size 1.27 1.27)) (justify bottom left))
|
|
||||||
)
|
|
||||||
(property "Value" "AO4435" (id 1) (at -11.43 6.35 0)
|
|
||||||
(effects (font (size 1.27 1.27)) (justify bottom left))
|
|
||||||
)
|
|
||||||
(property "Footprint" "AO4435:SOIC127P600X175-8N" (id 2) (at 0 0 0)
|
|
||||||
(effects (font (size 1.27 1.27)) (justify bottom) hide)
|
|
||||||
)
|
|
||||||
(property "MF" "Alpha &" (id 4) (at 0 0 0)
|
|
||||||
(effects (font (size 1.27 1.27)) (justify bottom) hide)
|
|
||||||
)
|
|
||||||
(property "Description" "\n \n P-Channel 30 V 10.5A (Ta) 3.1W (Ta) Surface Mount 8-SOIC\n \n" (id 5) (at 0 0 0)
|
|
||||||
(effects (font (size 1.27 1.27)) (justify bottom) hide)
|
|
||||||
)
|
|
||||||
(property "Package" "SOIC-8 Alpha & Omega Semiconductor" (id 6) (at 0 0 0)
|
|
||||||
(effects (font (size 1.27 1.27)) (justify bottom) hide)
|
|
||||||
)
|
|
||||||
(property "Price" "None" (id 7) (at 0 0 0)
|
|
||||||
(effects (font (size 1.27 1.27)) (justify bottom) hide)
|
|
||||||
)
|
|
||||||
(property "Check_prices" "https://www.snapeda.com/parts/AO4435/Alpha/view-part/?ref=eda" (id 8) (at 0 0 0)
|
|
||||||
(effects (font (size 1.27 1.27)) (justify bottom) hide)
|
|
||||||
)
|
|
||||||
(property "STANDARD" "IPC 7351B" (id 9) (at 0 0 0)
|
|
||||||
(effects (font (size 1.27 1.27)) (justify bottom) hide)
|
|
||||||
)
|
|
||||||
(property "SnapEDA_Link" "https://www.snapeda.com/parts/AO4435/Alpha/view-part/?ref=snap" (id 10) (at 0 0 0)
|
|
||||||
(effects (font (size 1.27 1.27)) (justify bottom) hide)
|
|
||||||
)
|
|
||||||
(property "MP" "AO4435" (id 11) (at 0 0 0)
|
|
||||||
(effects (font (size 1.27 1.27)) (justify bottom) hide)
|
|
||||||
)
|
|
||||||
(property "Availability" "In Stock" (id 12) (at 0 0 0)
|
|
||||||
(effects (font (size 1.27 1.27)) (justify bottom) hide)
|
|
||||||
)
|
|
||||||
(property "MANUFACTURER" "ALPHA & OMEGA SEMICONDUCTOR" (id 13) (at 0 0 0)
|
|
||||||
(effects (font (size 1.27 1.27)) (justify bottom) hide)
|
|
||||||
)
|
|
||||||
(symbol "AO4435_0_0"
|
|
||||||
(polyline
|
|
||||||
(pts (xy -1.778 0.762) (xy -1.778 0.0)) (stroke (width 0.254))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts (xy -1.778 0.0) (xy -1.778 -0.762)) (stroke (width 0.254))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts (xy -1.778 3.175) (xy -1.778 2.54)) (stroke (width 0.254))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts (xy -1.778 2.54) (xy -1.778 1.905)) (stroke (width 0.254))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts (xy -1.778 0.0) (xy 0.0 0.0)) (stroke (width 0.1524))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts (xy 0.0 0.0) (xy 0.0 -2.54)) (stroke (width 0.1524))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts (xy -1.778 -1.905) (xy -1.778 -2.54)) (stroke (width 0.254))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts (xy -1.778 -2.54) (xy -1.778 -3.175)) (stroke (width 0.254))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts (xy -2.54 2.54) (xy -2.54 -2.54)) (stroke (width 0.254))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts (xy 0.0 -2.54) (xy -1.778 -2.54)) (stroke (width 0.1524))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts (xy 1.27 2.54) (xy 1.27 -0.508)) (stroke (width 0.1524))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts (xy 1.27 -0.508) (xy 1.27 -2.54)) (stroke (width 0.1524))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts (xy 0.0 -2.54) (xy 1.27 -2.54)) (stroke (width 0.1524))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts (xy -1.778 2.54) (xy 1.27 2.54)) (stroke (width 0.1524))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts (xy 0.762 -0.508) (xy 1.27 -0.508)) (stroke (width 0.1524))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts (xy 1.27 -0.508) (xy 1.778 -0.508)) (stroke (width 0.1524))
|
|
||||||
)
|
|
||||||
(circle (center 0.0 -2.54) (radius 0.3592)
|
|
||||||
(stroke (width 0.0)) (fill (type none))
|
|
||||||
)
|
|
||||||
(circle (center 0.0 2.54) (radius 0.3592)
|
|
||||||
(stroke (width 0.0)) (fill (type none))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts
|
|
||||||
(xy 1.27 -0.508)
|
|
||||||
(xy 1.778 0.254)
|
|
||||||
(xy 0.762 0.254)
|
|
||||||
(xy 1.27 -0.508)
|
|
||||||
)
|
|
||||||
(stroke (width 0.1524)) (fill (type outline))
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts
|
|
||||||
(xy 0.0 0.0)
|
|
||||||
(xy -1.016 -0.762)
|
|
||||||
(xy -1.016 0.762)
|
|
||||||
(xy 0.0 0.0)
|
|
||||||
)
|
|
||||||
(stroke (width 0.1524)) (fill (type outline))
|
|
||||||
)
|
|
||||||
(pin passive line (at 0.0 -5.08 90.0) (length 2.54)
|
|
||||||
(name "~"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
(number "1"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin passive line (at 0.0 -5.08 90.0) (length 2.54)
|
|
||||||
(name "~"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
(number "2"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin passive line (at 0.0 -5.08 90.0) (length 2.54)
|
|
||||||
(name "~"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
(number "3"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin passive line (at -5.08 -2.54 0) (length 2.54)
|
|
||||||
(name "~"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
(number "4"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin passive line (at 0.0 5.08 270.0) (length 2.54)
|
|
||||||
(name "~"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
(number "5"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin passive line (at 0.0 5.08 270.0) (length 2.54)
|
|
||||||
(name "~"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
(number "6"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin passive line (at 0.0 5.08 270.0) (length 2.54)
|
|
||||||
(name "~"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
(number "7"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin passive line (at 0.0 5.08 270.0) (length 2.54)
|
|
||||||
(name "~"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
(number "8"
|
|
||||||
(effects (font (size 1.016 1.016)))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
(kicad_symbol_lib
|
|
||||||
(version 20241209)
|
|
||||||
(generator "kicad_symbol_editor")
|
|
||||||
(generator_version "9.0")
|
|
||||||
)
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
(fp_lib_table
|
|
||||||
(version 7)
|
|
||||||
(lib (name "esp32c6")(type "KiCad")(uri "${KIPRJMOD}/../_Shared/footprint/esp32c6.pretty")(options "")(descr ""))
|
|
||||||
(lib (name "Modules")(type "KiCad")(uri "${KIPRJMOD}/../_Shared/footprint/Modules.pretty")(options "")(descr ""))
|
|
||||||
)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
(sym_lib_table
|
|
||||||
(version 7)
|
|
||||||
(lib (name "ESP32-C6-WROOM-1-N8")(type "KiCad")(uri "${KIPRJMOD}/../_Shared/symbols/ESP32-C6-WROOM-1-N8.kicad_sym")(options "")(descr ""))
|
|
||||||
(lib (name "Sensor_Module")(type "KiCad")(uri "${KIPRJMOD}/../_Shared/symbols/Sensor_Module.kicad_sym")(options "")(descr ""))
|
|
||||||
(lib (name "Pump")(type "KiCad")(uri "${KIPRJMOD}/../_Shared/symbols/Pump.kicad_sym")(options "")(descr ""))
|
|
||||||
(lib (name "Light")(type "KiCad")(uri "${KIPRJMOD}/../_Shared/symbols/Light.kicad_sym")(options "")(descr ""))
|
|
||||||
(lib (name "MPPT")(type "KiCad")(uri "${KIPRJMOD}/../_Shared/symbols/MPPT.kicad_sym")(options "")(descr ""))
|
|
||||||
)
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"EXTRA_LAYERS": "", "ALL_ACTIVE_LAYERS": false, "EXTEND_EDGE_CUT": false, "ALTERNATIVE_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": false}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
(fp_lib_table
|
|
||||||
(version 7)
|
|
||||||
(lib (name "Modules")(type "KiCad")(uri "/home/empire/workspace/PlantCtrl/Hardware/_Shared/footprint/Modules.pretty")(options "")(descr ""))
|
|
||||||
(lib (name "4435")(type "KiCad")(uri "${KIPRJMOD}/hardware/footprints/4435")(options "")(descr ""))
|
|
||||||
)
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
(footprint SOIC127P600X175-8N (layer F.Cu) (tedit 68EBCB5D)
|
|
||||||
(descr "")
|
|
||||||
(attr smd)
|
|
||||||
(fp_text reference REF** (at -0.268 -3.2064 0) (layer F.SilkS)
|
|
||||||
(effects (font (size 0.64 0.64) (thickness 0.15)))
|
|
||||||
)
|
|
||||||
(fp_text value SOIC127P600X175-8N (at 5.0152 3.1936 0) (layer F.Fab)
|
|
||||||
(effects (font (size 0.64 0.64) (thickness 0.15)))
|
|
||||||
)
|
|
||||||
(pad 1 smd roundrect (roundrect_rratio 0.25) (at -2.475 -1.905) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(pad 2 smd roundrect (roundrect_rratio 0.25) (at -2.475 -0.635) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(pad 3 smd roundrect (roundrect_rratio 0.25) (at -2.475 0.635) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(pad 4 smd roundrect (roundrect_rratio 0.25) (at -2.475 1.905) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(pad 5 smd roundrect (roundrect_rratio 0.25) (at 2.475 1.905) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(pad 6 smd roundrect (roundrect_rratio 0.25) (at 2.475 0.635) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(pad 7 smd roundrect (roundrect_rratio 0.25) (at 2.475 -0.635) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(pad 8 smd roundrect (roundrect_rratio 0.25) (at 2.475 -1.905) (size 1.97 0.6) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
|
|
||||||
(fp_line (start -2.0 2.5) (end -2.0 -2.5) (layer F.Fab) (width 0.127))
|
|
||||||
(fp_line (start -2.0 -2.5) (end 2.0 -2.5) (layer F.Fab) (width 0.127))
|
|
||||||
(fp_line (start 2.0 -2.5) (end 2.0 2.5) (layer F.Fab) (width 0.127))
|
|
||||||
(fp_line (start 2.0 2.5) (end -2.0 2.5) (layer F.Fab) (width 0.127))
|
|
||||||
(fp_line (start -2.0 -2.519) (end 2.0 -2.519) (layer F.SilkS) (width 0.127))
|
|
||||||
(fp_line (start -2.0 2.519) (end 2.0 2.519) (layer F.SilkS) (width 0.127))
|
|
||||||
(fp_line (start -3.71 -2.46) (end -2.25 -2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start -2.25 -2.46) (end -2.25 -2.75) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start -2.25 -2.75) (end 2.25 -2.75) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start 2.25 -2.75) (end 2.25 -2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start 2.25 -2.46) (end 3.71 -2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start 3.71 2.46) (end 2.25 2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start 2.25 2.46) (end 2.25 2.75) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start 2.25 2.75) (end -2.25 2.75) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start -2.25 2.75) (end -2.25 2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start -2.25 2.46) (end -3.71 2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start -3.71 2.46) (end -3.71 -2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_line (start 3.71 -2.46) (end 3.71 2.46) (layer F.CrtYd) (width 0.05))
|
|
||||||
(fp_circle (center -4.1 -1.9) (end -4.0 -1.9) (layer F.SilkS) (width 0.2))
|
|
||||||
(fp_circle (center -4.1 -1.9) (end -4.0 -1.9) (layer F.Fab) (width 0.2))
|
|
||||||
)
|
|
||||||
@@ -1,278 +0,0 @@
|
|||||||
(kicad_symbol_lib
|
|
||||||
(version 20241209)
|
|
||||||
(generator "kicad_symbol_editor")
|
|
||||||
(generator_version "9.0")
|
|
||||||
(symbol "SL2300"
|
|
||||||
(pin_names
|
|
||||||
(offset 1.016)
|
|
||||||
)
|
|
||||||
(exclude_from_sim no)
|
|
||||||
(in_bom yes)
|
|
||||||
(on_board yes)
|
|
||||||
(property "Reference" "Q"
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Value" "SL2300"
|
|
||||||
(at 7.62 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Footprint" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(hide yes)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Datasheet" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(hide yes)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Description" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(hide yes)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(symbol "SL2300_1_1"
|
|
||||||
(polyline
|
|
||||||
(pts
|
|
||||||
(xy -1.016 1.905) (xy -1.016 -1.905)
|
|
||||||
)
|
|
||||||
(stroke
|
|
||||||
(width 0.254)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type none)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts
|
|
||||||
(xy -1.016 0) (xy -3.81 0)
|
|
||||||
)
|
|
||||||
(stroke
|
|
||||||
(width 0)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type none)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts
|
|
||||||
(xy -0.508 2.286) (xy -0.508 1.27)
|
|
||||||
)
|
|
||||||
(stroke
|
|
||||||
(width 0.254)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type none)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts
|
|
||||||
(xy -0.508 0.508) (xy -0.508 -0.508)
|
|
||||||
)
|
|
||||||
(stroke
|
|
||||||
(width 0.254)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type none)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts
|
|
||||||
(xy -0.508 -1.27) (xy -0.508 -2.286)
|
|
||||||
)
|
|
||||||
(stroke
|
|
||||||
(width 0.254)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type none)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts
|
|
||||||
(xy -0.508 -1.778) (xy 2.032 -1.778) (xy 2.032 1.778) (xy -0.508 1.778)
|
|
||||||
)
|
|
||||||
(stroke
|
|
||||||
(width 0)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type none)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts
|
|
||||||
(xy -0.254 0) (xy 0.762 0.381) (xy 0.762 -0.381) (xy -0.254 0)
|
|
||||||
)
|
|
||||||
(stroke
|
|
||||||
(width 0)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type outline)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(circle
|
|
||||||
(center 0.381 0)
|
|
||||||
(radius 2.794)
|
|
||||||
(stroke
|
|
||||||
(width 0.254)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type none)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts
|
|
||||||
(xy 1.27 2.54) (xy 1.27 1.778)
|
|
||||||
)
|
|
||||||
(stroke
|
|
||||||
(width 0)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type none)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(circle
|
|
||||||
(center 1.27 1.778)
|
|
||||||
(radius 0.254)
|
|
||||||
(stroke
|
|
||||||
(width 0)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type outline)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(circle
|
|
||||||
(center 1.27 -1.778)
|
|
||||||
(radius 0.254)
|
|
||||||
(stroke
|
|
||||||
(width 0)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type outline)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts
|
|
||||||
(xy 1.27 -2.54) (xy 1.27 0) (xy -0.508 0)
|
|
||||||
)
|
|
||||||
(stroke
|
|
||||||
(width 0)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type none)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts
|
|
||||||
(xy 1.524 0.508) (xy 1.651 0.381) (xy 2.413 0.381) (xy 2.54 0.254)
|
|
||||||
)
|
|
||||||
(stroke
|
|
||||||
(width 0)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type none)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(polyline
|
|
||||||
(pts
|
|
||||||
(xy 2.032 0.381) (xy 1.651 -0.254) (xy 2.413 -0.254) (xy 2.032 0.381)
|
|
||||||
)
|
|
||||||
(stroke
|
|
||||||
(width 0)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type none)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin input line
|
|
||||||
(at -6.35 0 0)
|
|
||||||
(length 2.54)
|
|
||||||
(name "G"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "1"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin passive line
|
|
||||||
(at 1.27 5.08 270)
|
|
||||||
(length 2.54)
|
|
||||||
(name "D"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "3"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin passive line
|
|
||||||
(at 1.27 -5.08 90)
|
|
||||||
(length 2.54)
|
|
||||||
(name "S"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "2"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(embedded_fonts no)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
(sym_lib_table
|
|
||||||
(version 7)
|
|
||||||
(lib (name "Pump")(type "KiCad")(uri "/home/empire/workspace/PlantCtrl/Hardware/_Shared/symbols/Pump.kicad_sym")(options "")(descr ""))
|
|
||||||
(lib (name "SL2300")(type "KiCad")(uri "${KIPRJMOD}/hardware/symbols/SL2300.kicad_sym")(options "")(descr ""))
|
|
||||||
)
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,175 +0,0 @@
|
|||||||
(kicad_symbol_lib
|
|
||||||
(version 20241209)
|
|
||||||
(generator "kicad_symbol_editor")
|
|
||||||
(generator_version "9.0")
|
|
||||||
(symbol "LightPowerSupply"
|
|
||||||
(exclude_from_sim no)
|
|
||||||
(in_bom yes)
|
|
||||||
(on_board yes)
|
|
||||||
(property "Reference" "U"
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Value" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Footprint" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(hide yes)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Datasheet" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(hide yes)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Description" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(hide yes)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(symbol "LightPowerSupply_1_1"
|
|
||||||
(rectangle
|
|
||||||
(start -6.35 -1.27)
|
|
||||||
(end 6.35 -19.05)
|
|
||||||
(stroke
|
|
||||||
(width 0)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type background)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin input line
|
|
||||||
(at -8.89 -2.54 0)
|
|
||||||
(length 2.54)
|
|
||||||
(name "LED_ENABLE"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "1"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin power_in line
|
|
||||||
(at -8.89 -5.08 0)
|
|
||||||
(length 2.54)
|
|
||||||
(name "VBAT"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "2"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin input line
|
|
||||||
(at -8.89 -7.62 0)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "3"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin input line
|
|
||||||
(at -8.89 -10.16 0)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "4"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin input line
|
|
||||||
(at -8.89 -12.7 0)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "5"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin input line
|
|
||||||
(at -8.89 -15.24 0)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "6"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(embedded_fonts no)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -1,211 +0,0 @@
|
|||||||
(kicad_symbol_lib
|
|
||||||
(version 20241209)
|
|
||||||
(generator "kicad_symbol_editor")
|
|
||||||
(generator_version "9.0")
|
|
||||||
(symbol "Pump_Output"
|
|
||||||
(exclude_from_sim no)
|
|
||||||
(in_bom yes)
|
|
||||||
(on_board yes)
|
|
||||||
(property "Reference" "U"
|
|
||||||
(at -6.35 15.24 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Value" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Footprint" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(hide yes)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Datasheet" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(hide yes)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Description" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(hide yes)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(symbol "Pump_Output_1_1"
|
|
||||||
(rectangle
|
|
||||||
(start -12.7 12.7)
|
|
||||||
(end 10.16 -15.24)
|
|
||||||
(stroke
|
|
||||||
(width 0)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type background)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin bidirectional line
|
|
||||||
(at -15.24 1.27 0)
|
|
||||||
(length 2.54)
|
|
||||||
(name "SCL"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "5"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin bidirectional line
|
|
||||||
(at -15.24 -1.27 0)
|
|
||||||
(length 2.54)
|
|
||||||
(name "SDA"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "6"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin power_in line
|
|
||||||
(at -3.81 15.24 270)
|
|
||||||
(length 2.54)
|
|
||||||
(name "3V3"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "7"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin power_in line
|
|
||||||
(at -1.27 15.24 270)
|
|
||||||
(length 2.54)
|
|
||||||
(name "VCC"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "8"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin power_in line
|
|
||||||
(at -1.27 -17.78 90)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "1"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin power_in line
|
|
||||||
(at 1.27 -17.78 90)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "2"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin power_in line
|
|
||||||
(at 3.81 -17.78 90)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "3"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin power_in line
|
|
||||||
(at 6.35 -17.78 90)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "4"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(embedded_fonts no)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -1,265 +0,0 @@
|
|||||||
(kicad_symbol_lib
|
|
||||||
(version 20241209)
|
|
||||||
(generator "kicad_symbol_editor")
|
|
||||||
(generator_version "9.0")
|
|
||||||
(symbol "sensor"
|
|
||||||
(exclude_from_sim no)
|
|
||||||
(in_bom yes)
|
|
||||||
(on_board yes)
|
|
||||||
(property "Reference" "U"
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Value" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Footprint" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(hide yes)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Datasheet" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(hide yes)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Description" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(hide yes)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(symbol "sensor_1_1"
|
|
||||||
(rectangle
|
|
||||||
(start -5.08 -1.27)
|
|
||||||
(end 5.08 -30.48)
|
|
||||||
(stroke
|
|
||||||
(width 0)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type background)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin power_in line
|
|
||||||
(at 7.62 -2.54 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "3V3"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "1"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin power_in line
|
|
||||||
(at 7.62 -5.08 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "VBAT"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "2"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin power_in line
|
|
||||||
(at 7.62 -7.62 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "SDA"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "3"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin power_in line
|
|
||||||
(at 7.62 -10.16 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "SCL"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "4"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin output line
|
|
||||||
(at 7.62 -12.7 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "Signal"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "5"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin output line
|
|
||||||
(at 7.62 -15.24 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "8"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin output line
|
|
||||||
(at 7.62 -17.78 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "9"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin output line
|
|
||||||
(at 7.62 -20.32 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "10"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin output line
|
|
||||||
(at 7.62 -22.86 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "11"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin output line
|
|
||||||
(at 7.62 -25.4 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 -27.94 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)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -1,247 +0,0 @@
|
|||||||
(kicad_symbol_lib
|
|
||||||
(version 20241209)
|
|
||||||
(generator "kicad_symbol_editor")
|
|
||||||
(generator_version "9.0")
|
|
||||||
(symbol "sensor"
|
|
||||||
(exclude_from_sim no)
|
|
||||||
(in_bom yes)
|
|
||||||
(on_board yes)
|
|
||||||
(property "Reference" "U"
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Value" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Footprint" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(hide yes)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Datasheet" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(hide yes)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(property "Description" ""
|
|
||||||
(at 0 0 0)
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(hide yes)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(symbol "sensor_1_1"
|
|
||||||
(rectangle
|
|
||||||
(start -5.08 -1.27)
|
|
||||||
(end 5.08 -26.67)
|
|
||||||
(stroke
|
|
||||||
(width 0)
|
|
||||||
(type solid)
|
|
||||||
)
|
|
||||||
(fill
|
|
||||||
(type background)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin power_in line
|
|
||||||
(at 7.62 -2.54 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "3V3"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "1"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin power_in line
|
|
||||||
(at 7.62 -5.08 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "VBAT"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "2"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin power_in line
|
|
||||||
(at 7.62 -7.62 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "SDA"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "3"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin power_in line
|
|
||||||
(at 7.62 -10.16 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "SCL"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "4"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin output line
|
|
||||||
(at 7.62 -12.7 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "8"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin output line
|
|
||||||
(at 7.62 -15.24 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "9"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin output line
|
|
||||||
(at 7.62 -17.78 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "10"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin output line
|
|
||||||
(at 7.62 -20.32 180)
|
|
||||||
(length 2.54)
|
|
||||||
(name "GND"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(number "11"
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin output line
|
|
||||||
(at 7.62 -22.86 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 -25.4 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)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"EXTRA_LAYERS": "", "ALL_ACTIVE_LAYERS": false, "EXTEND_EDGE_CUT": false, "ALTERNATIVE_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": false}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"EXTRA_LAYERS": "", "ALL_ACTIVE_LAYERS": false, "EXTEND_EDGE_CUT": false, "ALTERNATIVE_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": false}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"EXTRA_LAYERS": "", "ALL_ACTIVE_LAYERS": false, "EXTEND_EDGE_CUT": false, "ALTERNATIVE_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": false}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
(fp_lib_table
|
|
||||||
(version 7)
|
|
||||||
(lib (name "Sensor")(type "KiCad")(uri "/home/empire/workspace/PlantCtrl/board/modules/Sensors/Sensors/Sensor.pretty")(options "")(descr ""))
|
|
||||||
)
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
;;; .doomrc --- doom runtime config -*- mode: emacs-lisp; lexical-binding: t; -*-
|
|
||||||
;;; Commentary:
|
|
||||||
;;; Code:
|
|
||||||
(require 'doom) ; be silent, byte-compiler
|
|
||||||
|
|
||||||
(after! dape
|
|
||||||
(add-to-list
|
|
||||||
'dape-configs
|
|
||||||
`(gdb-dap-openocd
|
|
||||||
ensure (lambda (config)
|
|
||||||
(dape-ensure-command config)
|
|
||||||
(let* ((default-directory
|
|
||||||
(or (dape-config-get config 'command-cwd)
|
|
||||||
default-directory))
|
|
||||||
(command (dape-config-get config 'command))
|
|
||||||
(output (shell-command-to-string (format "%s --version" command)))
|
|
||||||
(version (save-match-data
|
|
||||||
(when (string-match "GNU gdb \\(?:(.*) \\)?\\([0-9.]+\\)" output)
|
|
||||||
(string-to-number (match-string 1 output))))))
|
|
||||||
(unless (>= version 14.1)
|
|
||||||
(user-error "Requires gdb version >= 14.1"))))
|
|
||||||
modes ()
|
|
||||||
command-cwd dape-command-cwd
|
|
||||||
command "gdb"
|
|
||||||
command-args ("--interpreter=dap")
|
|
||||||
:request nil
|
|
||||||
:program nil
|
|
||||||
:args []
|
|
||||||
:stopAtBeginningOfMainSubprogram nil))
|
|
||||||
)
|
|
||||||
|
|
||||||
;;; .doomrc ends here
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="EMPTY_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/ch32-sensor.iml" filepath="$PROJECT_DIR$/.idea/ch32-sensor.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$/../../../.." vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/../../../../website/themes/blowfish" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"EXTRA_LAYERS": "", "ALL_ACTIVE_LAYERS": false, "EXTEND_EDGE_CUT": false, "ALTERNATIVE_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": false}
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 13.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="1325px"
|
|
||||||
height="675px" viewBox="0 0 1325 675" enable-background="new 0 0 1325 675" xml:space="preserve">
|
|
||||||
<g id="back">
|
|
||||||
<rect width="1325" height="675"/>
|
|
||||||
</g>
|
|
||||||
<g id="logo">
|
|
||||||
<g>
|
|
||||||
<path fill="#FFFFFF" d="M109.818,477.621H75.827v34.299h33.992v-11.638l12.612,2.614v6.409c0,4.956-0.932,8.331-2.794,10.126
|
|
||||||
c-1.864,1.794-5.409,2.691-10.639,2.691H76.647c-5.264,0-8.818-0.897-10.664-2.691c-1.846-1.795-2.769-5.17-2.769-10.126v-28.558
|
|
||||||
c0-4.922,0.923-8.287,2.769-10.1c1.846-1.812,5.4-2.717,10.664-2.717h32.351c5.229,0,8.775,0.905,10.639,2.717
|
|
||||||
c1.862,1.813,2.794,5.178,2.794,10.1v5.127l-12.612,2V477.621z"/>
|
|
||||||
<path fill="#FFFFFF" d="M148.424,522.123v-54.191h12.612v21.02h39.375v-21.02h12.715v54.191h-12.715v-23.328h-39.375v23.328
|
|
||||||
H148.424z"/>
|
|
||||||
<path fill="#FFFFFF" d="M230.456,522.123l30.659-54.191h12.151l31.069,54.191h-14.15l-6.511-11.844h-34.351l-6.306,11.844H230.456
|
|
||||||
z M254.706,500.487h23.738l-11.689-22.046L254.706,500.487z"/>
|
|
||||||
<path fill="#FFFFFF" d="M318.793,480.748c0-4.922,0.923-8.287,2.769-10.1c1.846-1.812,5.4-2.717,10.664-2.717h39.888
|
|
||||||
c5.263,0,8.818,0.905,10.664,2.717c1.846,1.813,2.769,5.178,2.769,10.1v28.558c0,4.956-0.923,8.331-2.769,10.126
|
|
||||||
c-1.846,1.794-5.401,2.691-10.664,2.691h-39.888c-5.264,0-8.818-0.897-10.664-2.691c-1.846-1.795-2.769-5.17-2.769-10.126V480.748
|
|
||||||
z M331.405,511.92h41.58v-34.299h-41.58V511.92z"/>
|
|
||||||
<path fill="#FFFFFF" d="M459.066,477.416h-35.479v11.639h33.787c5.297,0,8.877,0.905,10.741,2.717
|
|
||||||
c1.863,1.813,2.794,5.178,2.794,10.1v7.435c0,4.922-0.932,8.289-2.794,10.101c-1.863,1.812-5.443,2.717-10.741,2.717h-33.479
|
|
||||||
c-5.298,0-8.878-0.905-10.741-2.717c-1.863-1.812-2.794-5.179-2.794-10.101v-1.487l11.125-2.307v6.408h38.298v-12.355h-33.787
|
|
||||||
c-5.264,0-8.818-0.905-10.664-2.717s-2.769-5.179-2.769-10.101v-5.999c0-4.922,0.923-8.287,2.769-10.1
|
|
||||||
c1.846-1.812,5.4-2.717,10.664-2.717h30.762c5.092,0,8.604,0.871,10.536,2.614c1.931,1.743,2.897,4.837,2.897,9.28v1.127
|
|
||||||
l-11.125,2.615V477.416z"/>
|
|
||||||
<path fill="#FFFFFF" d="M537.714,522.123v-54.191h12.612v54.191H537.714z"/>
|
|
||||||
<path fill="#FFFFFF" d="M577.909,522.123v-54.191h10.203l25.532,32.761l24.968-32.761h10.1v54.191h-11.689v-33.479
|
|
||||||
c0-0.649,0.042-1.47,0.128-2.461c0.084-0.991,0.196-2.103,0.333-3.333c-0.684,1.402-1.282,2.556-1.794,3.461
|
|
||||||
c-0.513,0.906-0.974,1.649-1.384,2.229l-20.354,27.07h-2.512l-20.405-27.172c-0.752-1.025-1.393-1.991-1.923-2.897
|
|
||||||
c-0.53-0.905-0.966-1.785-1.307-2.64c0.103,1.127,0.179,2.152,0.231,3.076c0.051,0.922,0.077,1.812,0.077,2.666v33.479H577.909z"
|
|
||||||
/>
|
|
||||||
<path fill="#FFFFFF" d="M714.748,480.748c0-4.922,0.923-8.287,2.769-10.1c1.846-1.812,5.4-2.717,10.664-2.717h39.888
|
|
||||||
c5.263,0,8.818,0.905,10.664,2.717c1.846,1.813,2.769,5.178,2.769,10.1v28.558c0,4.956-0.923,8.331-2.769,10.126
|
|
||||||
c-1.846,1.794-5.401,2.691-10.664,2.691h-5.845l6.819,13.945h-12.51l-6.102-13.945h-22.251c-5.264,0-8.818-0.897-10.664-2.691
|
|
||||||
c-1.846-1.795-2.769-5.17-2.769-10.126V480.748z M727.36,511.92h18.56l-5.024-11.74l9.126-3.691l7.332,15.432h11.586v-34.299
|
|
||||||
H727.36V511.92z"/>
|
|
||||||
<path fill="#FFFFFF" d="M820.67,467.932v43.988h35.939v-43.988h12.562v41.374c0,4.956-0.923,8.331-2.769,10.126
|
|
||||||
c-1.846,1.794-5.401,2.691-10.664,2.691H821.49c-5.229,0-8.776-0.897-10.639-2.691c-1.863-1.795-2.794-5.17-2.794-10.126v-41.374
|
|
||||||
H820.67z"/>
|
|
||||||
<path fill="#FFFFFF" d="M884.244,522.123l30.659-54.191h12.151l31.068,54.191h-14.15l-6.511-11.844h-34.351l-6.307,11.844H884.244
|
|
||||||
z M908.494,500.487h23.738l-11.689-22.046L908.494,500.487z"/>
|
|
||||||
<path fill="#FFFFFF" d="M975.556,522.123v-54.191h34.863c9.946,0,17.594,2.324,22.943,6.973
|
|
||||||
c5.349,4.648,8.023,11.245,8.023,19.789c0,5.06-1.043,9.613-3.128,13.664c-2.085,4.05-5.023,7.237-8.818,9.562
|
|
||||||
c-2.357,1.47-5.289,2.538-8.792,3.204c-3.504,0.667-7.99,1-13.458,1H975.556z M988.168,511.92h16.304
|
|
||||||
c8.682,0,14.834-1.35,18.457-4.05c3.622-2.699,5.435-7.16,5.435-13.382c0-5.537-1.505-9.731-4.512-12.586
|
|
||||||
c-3.008-2.854-7.452-4.281-13.33-4.281h-22.354V511.92z"/>
|
|
||||||
<path fill="#FFFFFF" d="M1064.509,522.123v-54.191h46.348c4.307,0,7.425,0.829,9.356,2.486c1.931,1.658,2.896,4.332,2.896,8.023
|
|
||||||
v12.715c0,3.658-0.966,6.324-2.896,7.998c-1.932,1.675-5.05,2.512-9.356,2.512h-7.075l25.02,20.457h-18.098l-21.842-20.457
|
|
||||||
h-11.945v20.457H1064.509z M1105.268,477.313h-28.352v15.073h28.352c2.188,0,3.683-0.315,4.486-0.948
|
|
||||||
c0.803-0.632,1.205-1.751,1.205-3.358v-6.512c0-1.571-0.402-2.674-1.205-3.307C1108.95,477.63,1107.455,477.313,1105.268,477.313z
|
|
||||||
"/>
|
|
||||||
<path fill="#FFFFFF" d="M1134.594,522.123l30.659-54.191h12.151l31.068,54.191h-14.15l-6.511-11.844h-34.351l-6.307,11.844
|
|
||||||
H1134.594z M1158.844,500.487h23.738l-11.689-22.046L1158.844,500.487z"/>
|
|
||||||
<path fill="#FFFFFF" d="M1244.003,477.621v44.502h-12.612v-44.502h-24.865v-9.689h62.445v9.689H1244.003z"/>
|
|
||||||
<path fill="#FFFFFF" d="M71.188,626.523l-17.29-44.902h10.79l11.385,30.119l11.937-30.119h7.307l12.149,30.119l10.875-30.119
|
|
||||||
h9.346l-17.332,44.902h-7.052l-12.362-31.053l-12.532,31.053H71.188z"/>
|
|
||||||
<path fill="#FFFFFF" d="M151.73,626.523l-17.29-44.902h10.79l11.385,30.119l11.937-30.119h7.307l12.149,30.119l10.875-30.119
|
|
||||||
h9.346l-17.332,44.902h-7.052l-12.362-31.053l-12.532,31.053H151.73z"/>
|
|
||||||
<path fill="#FFFFFF" d="M232.273,626.523l-17.29-44.902h10.79l11.385,30.119l11.937-30.119h7.307l12.149,30.119l10.875-30.119
|
|
||||||
h9.346l-17.332,44.902h-7.052l-12.362-31.053l-12.532,31.053H232.273z"/>
|
|
||||||
<path fill="#FFFFFF" d="M303.301,613.057h1.572c0.765,0,1.302,0.22,1.614,0.659c0.312,0.439,0.467,1.281,0.467,2.527v7.052
|
|
||||||
c0,1.359-0.17,2.237-0.51,2.634s-1.388,0.595-3.144,0.595h-7.774c-0.765,0-1.303-0.227-1.614-0.68
|
|
||||||
c-0.312-0.453-0.467-1.303-0.467-2.549v-7.052c0-1.331,0.17-2.194,0.51-2.591s1.387-0.596,3.144-0.596H303.301z"/>
|
|
||||||
<path fill="#FFFFFF" d="M363.75,589.65h-28.165v28.42h28.165v-9.644l10.45,2.167v5.31c0,4.106-0.772,6.903-2.315,8.39
|
|
||||||
c-1.544,1.487-4.482,2.23-8.815,2.23h-26.805c-4.361,0-7.307-0.743-8.836-2.23c-1.529-1.486-2.294-4.283-2.294-8.39v-23.661
|
|
||||||
c0-4.078,0.765-6.868,2.294-8.369s4.475-2.252,8.836-2.252h26.805c4.333,0,7.271,0.751,8.815,2.252
|
|
||||||
c1.543,1.501,2.315,4.291,2.315,8.369v4.248l-10.45,1.656V589.65z"/>
|
|
||||||
<path fill="#FFFFFF" d="M433.418,589.65h-28.165v28.42h28.165v-9.644l10.45,2.167v5.31c0,4.106-0.772,6.903-2.315,8.39
|
|
||||||
c-1.544,1.487-4.482,2.23-8.815,2.23h-26.805c-4.361,0-7.307-0.743-8.836-2.23c-1.529-1.486-2.294-4.283-2.294-8.39v-23.661
|
|
||||||
c0-4.078,0.765-6.868,2.294-8.369s4.475-2.252,8.836-2.252h26.805c4.333,0,7.271,0.751,8.815,2.252
|
|
||||||
c1.543,1.501,2.315,4.291,2.315,8.369v4.248l-10.45,1.656V589.65z"/>
|
|
||||||
<path fill="#FFFFFF" d="M503.086,589.65h-28.165v28.42h28.165v-9.644l10.45,2.167v5.31c0,4.106-0.772,6.903-2.315,8.39
|
|
||||||
c-1.544,1.487-4.482,2.23-8.815,2.23h-26.805c-4.361,0-7.307-0.743-8.836-2.23c-1.529-1.486-2.294-4.283-2.294-8.39v-23.661
|
|
||||||
c0-4.078,0.765-6.868,2.294-8.369s4.475-2.252,8.836-2.252h26.805c4.333,0,7.271,0.751,8.815,2.252
|
|
||||||
c1.543,1.501,2.315,4.291,2.315,8.369v4.248l-10.45,1.656V589.65z"/>
|
|
||||||
<path fill="#FFFFFF" d="M527.768,607.832v-8.963h20.263v8.963H527.768z"/>
|
|
||||||
<path fill="#FFFFFF" d="M563.706,626.523v-44.902h8.454l21.155,27.146l20.688-27.146h8.369v44.902h-9.686v-27.74
|
|
||||||
c0-0.537,0.035-1.217,0.106-2.039c0.071-0.82,0.163-1.741,0.276-2.761c-0.567,1.161-1.062,2.117-1.487,2.867
|
|
||||||
c-0.425,0.751-0.807,1.367-1.147,1.849l-16.865,22.43h-2.082l-16.907-22.516c-0.623-0.85-1.154-1.649-1.593-2.399
|
|
||||||
c-0.439-0.75-0.801-1.479-1.083-2.188c0.085,0.936,0.149,1.785,0.191,2.549c0.043,0.766,0.064,1.502,0.064,2.209v27.74H563.706z"
|
|
||||||
/>
|
|
||||||
<path fill="#FFFFFF" d="M635.753,626.523l25.403-44.902h10.068l25.742,44.902h-11.725l-5.395-9.813h-28.462l-5.225,9.813H635.753z
|
|
||||||
M655.846,608.597h19.668l-9.686-18.267L655.846,608.597z"/>
|
|
||||||
<path fill="#FFFFFF" d="M711.326,626.523v-44.902h7.137l31.605,26.466c1.076,0.878,2.024,1.706,2.846,2.485s1.572,1.579,2.252,2.4
|
|
||||||
c-0.17-2.521-0.283-4.305-0.34-5.354c-0.057-1.047-0.085-1.854-0.085-2.421v-23.577h9.388v44.902h-7.137l-32.965-27.74
|
|
||||||
c-0.793-0.707-1.486-1.345-2.081-1.911s-1.147-1.147-1.657-1.741c0.142,1.388,0.248,2.697,0.319,3.929
|
|
||||||
c0.07,1.232,0.106,2.302,0.106,3.208v24.256H711.326z"/>
|
|
||||||
<path fill="#FFFFFF" d="M786.941,626.523v-44.902h7.137l31.605,26.466c1.076,0.878,2.024,1.706,2.846,2.485s1.572,1.579,2.252,2.4
|
|
||||||
c-0.17-2.521-0.283-4.305-0.34-5.354c-0.057-1.047-0.085-1.854-0.085-2.421v-23.577h9.388v44.902h-7.137l-32.965-27.74
|
|
||||||
c-0.793-0.707-1.486-1.345-2.081-1.911s-1.147-1.147-1.657-1.741c0.142,1.388,0.248,2.697,0.319,3.929
|
|
||||||
c0.07,1.232,0.106,2.302,0.106,3.208v24.256H786.941z"/>
|
|
||||||
<path fill="#FFFFFF" d="M862.642,626.523v-44.902h10.45v17.418h32.625v-17.418h10.535v44.902h-10.535v-19.328h-32.625v19.328
|
|
||||||
H862.642z"/>
|
|
||||||
<path fill="#FFFFFF" d="M939.19,626.523v-44.902h45.964v7.646h-35.514v10.025h20.73v7.646h-20.73v11.131h35.938v8.453H939.19z"/>
|
|
||||||
<path fill="#FFFFFF" d="M1004.866,626.523v-44.902h10.45v44.902H1004.866z"/>
|
|
||||||
<path fill="#FFFFFF" d="M1038.17,626.523v-44.902h8.453l21.156,27.146l20.688-27.146h8.369v44.902h-9.686v-27.74
|
|
||||||
c0-0.537,0.035-1.217,0.105-2.039c0.07-0.82,0.163-1.741,0.276-2.761c-0.566,1.161-1.062,2.117-1.487,2.867
|
|
||||||
c-0.424,0.751-0.807,1.367-1.146,1.849l-16.865,22.43h-2.081l-16.907-22.516c-0.623-0.85-1.154-1.649-1.593-2.399
|
|
||||||
c-0.439-0.75-0.801-1.479-1.083-2.188c0.084,0.936,0.148,1.785,0.19,2.549c0.043,0.766,0.063,1.502,0.063,2.209v27.74H1038.17z"/>
|
|
||||||
<path fill="#FFFFFF" d="M1124.83,613.057h1.572c0.764,0,1.302,0.22,1.613,0.659s0.468,1.281,0.468,2.527v7.052
|
|
||||||
c0,1.359-0.17,2.237-0.51,2.634s-1.388,0.595-3.144,0.595h-7.773c-0.766,0-1.304-0.227-1.615-0.68s-0.467-1.303-0.467-2.549
|
|
||||||
v-7.052c0-1.331,0.17-2.194,0.51-2.591s1.388-0.596,3.144-0.596H1124.83z"/>
|
|
||||||
<path fill="#FFFFFF" d="M1147.601,626.523v-44.902h28.887c8.241,0,14.577,1.927,19.01,5.777c4.433,3.853,6.648,9.318,6.648,16.398
|
|
||||||
c0,4.191-0.864,7.965-2.591,11.32c-1.729,3.356-4.164,5.997-7.307,7.923c-1.955,1.218-4.383,2.103-7.286,2.655
|
|
||||||
c-2.903,0.552-6.62,0.828-11.151,0.828H1147.601z M1158.051,618.07h13.509c7.193,0,12.291-1.119,15.293-3.356
|
|
||||||
s4.503-5.933,4.503-11.087c0-4.588-1.246-8.064-3.738-10.43c-2.492-2.364-6.174-3.547-11.045-3.547h-18.521V618.07z"/>
|
|
||||||
<path fill="#FFFFFF" d="M1221.304,626.523v-44.902h45.964v7.646h-35.514v10.025h20.73v7.646h-20.73v11.131h35.938v8.453H1221.304z
|
|
||||||
"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<polygon fill="#FFFFFF" points="1050.25,150.292 1050.25,100.292 1000.25,100.292 1000.25,49.792 799.75,49.792 799.75,100.25
|
|
||||||
749.75,100.25 749.75,349.791 799.75,349.791 799.75,400.292 1000.25,400.292 1000.25,349.791 1050.25,349.791 1050.25,300.292
|
|
||||||
950.25,300.292 950.25,349.791 849.75,349.791 849.75,100.292 950.25,100.292 950.25,150.292 "/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path fill="#FFFFFF" d="M1100.25,49.792v174.75h175V49.792H1100.25z M1251,124.042h-24.75v25h-50.75v24.75h75.5v25.125h-125.75
|
|
||||||
v-25.125h25.25v-24.75h25v-25h50.75v-25h-75.75v25h-25.25v-25h25.25v-25.25h75.75v25.25H1251V124.042z"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<polygon fill="#FFFFFF" points="700.25,150.292 700.25,100.292 650.25,100.292 650.25,49.792 449.75,49.792 449.75,100.25
|
|
||||||
399.75,100.25 399.75,349.791 449.75,349.791 449.75,400.292 650.25,400.292 650.25,349.791 700.25,349.791 700.25,300.292
|
|
||||||
600.25,300.292 600.25,349.791 499.75,349.791 499.75,100.292 600.25,100.292 600.25,150.292 "/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<polygon fill="#FFFFFF" points="350.25,150.292 350.25,100.292 300.25,100.292 300.25,49.792 99.75,49.792 99.75,100.25
|
|
||||||
49.75,100.25 49.75,349.791 99.75,349.791 99.75,400.292 300.25,400.292 300.25,349.791 350.25,349.791 350.25,300.292
|
|
||||||
250.25,300.292 250.25,349.791 149.75,349.791 149.75,100.292 250.25,100.292 250.25,150.292 "/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,131 +0,0 @@
|
|||||||
{
|
|
||||||
"board": {
|
|
||||||
"active_layer": 0,
|
|
||||||
"active_layer_preset": "",
|
|
||||||
"auto_track_width": true,
|
|
||||||
"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",
|
|
||||||
"drc_exclusions",
|
|
||||||
"locked_item_shadows",
|
|
||||||
"conflict_shadows",
|
|
||||||
"shapes"
|
|
||||||
],
|
|
||||||
"visible_layers": "ffffffff_ffffffff_ffffffff_ffffffff",
|
|
||||||
"zone_display_mode": 0
|
|
||||||
},
|
|
||||||
"git": {
|
|
||||||
"repo_type": "",
|
|
||||||
"repo_username": "",
|
|
||||||
"ssh_key": ""
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"filename": "sensor.kicad_prl",
|
|
||||||
"version": 5
|
|
||||||
},
|
|
||||||
"net_inspector_panel": {
|
|
||||||
"col_hidden": [
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
],
|
|
||||||
"col_order": [
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
4,
|
|
||||||
5,
|
|
||||||
6,
|
|
||||||
7,
|
|
||||||
8,
|
|
||||||
9
|
|
||||||
],
|
|
||||||
"col_widths": [
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,877 +0,0 @@
|
|||||||
{
|
|
||||||
"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": [],
|
|
||||||
"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": "error",
|
|
||||||
"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": "warning",
|
|
||||||
"silk_over_copper": "warning",
|
|
||||||
"silk_overlap": "warning",
|
|
||||||
"skew_out_of_range": "error",
|
|
||||||
"solder_mask_bridge": "error",
|
|
||||||
"starved_thermal": "error",
|
|
||||||
"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.25,
|
|
||||||
"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.3,
|
|
||||||
"min_track_width": 0.0,
|
|
||||||
"min_via_annular_width": 0.1,
|
|
||||||
"min_via_diameter": 0.5,
|
|
||||||
"solder_mask_to_copper_clearance": 0.0,
|
|
||||||
"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": [],
|
|
||||||
"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": [],
|
|
||||||
"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": "warning",
|
|
||||||
"power_pin_not_driven": "error",
|
|
||||||
"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",
|
|
||||||
"undefined_netclass": "error",
|
|
||||||
"unit_value_mismatch": "error",
|
|
||||||
"unresolved_variable": "error",
|
|
||||||
"wire_dangling": "error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"libraries": {
|
|
||||||
"pinned_footprint_libs": [],
|
|
||||||
"pinned_symbol_libs": []
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"filename": "sensor.kicad_pro",
|
|
||||||
"version": 3
|
|
||||||
},
|
|
||||||
"net_settings": {
|
|
||||||
"classes": [
|
|
||||||
{
|
|
||||||
"bus_width": 12,
|
|
||||||
"clearance": 0.2,
|
|
||||||
"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": "",
|
|
||||||
"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": false,
|
|
||||||
"label": "Qty",
|
|
||||||
"name": "${QUANTITY}",
|
|
||||||
"show": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": true,
|
|
||||||
"label": "Value",
|
|
||||||
"name": "Value",
|
|
||||||
"show": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": true,
|
|
||||||
"label": "Footprint",
|
|
||||||
"name": "Footprint",
|
|
||||||
"show": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Datasheet",
|
|
||||||
"name": "Datasheet",
|
|
||||||
"show": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Actuator/Cap Color",
|
|
||||||
"name": "Actuator/Cap Color",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Attrition Qty",
|
|
||||||
"name": "Attrition Qty",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Capacitance",
|
|
||||||
"name": "Capacitance",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Category",
|
|
||||||
"name": "Category",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Circuit",
|
|
||||||
"name": "Circuit",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Class",
|
|
||||||
"name": "Class",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Contact Current",
|
|
||||||
"name": "Contact Current",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Diode Configuration",
|
|
||||||
"name": "Diode Configuration",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Forward Voltage (Vf@If)",
|
|
||||||
"name": "Forward Voltage (Vf@If)",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Insulation Resistance",
|
|
||||||
"name": "Insulation Resistance",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "LCSC",
|
|
||||||
"name": "LCSC",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Manufacturer",
|
|
||||||
"name": "Manufacturer",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Mechanical Life",
|
|
||||||
"name": "Mechanical Life",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Minimum Qty",
|
|
||||||
"name": "Minimum Qty",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Mounting Style",
|
|
||||||
"name": "Mounting Style",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Operating Force",
|
|
||||||
"name": "Operating Force",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Operating Temperature",
|
|
||||||
"name": "Operating Temperature",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Operating Temperature Range",
|
|
||||||
"name": "Operating Temperature Range",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Overload Voltage (Max)",
|
|
||||||
"name": "Overload Voltage (Max)",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Part",
|
|
||||||
"name": "Part",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Pin Style",
|
|
||||||
"name": "Pin Style",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Power(Watts)",
|
|
||||||
"name": "Power(Watts)",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Price",
|
|
||||||
"name": "Price",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Process",
|
|
||||||
"name": "Process",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Rectified Current",
|
|
||||||
"name": "Rectified Current",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Resistance",
|
|
||||||
"name": "Resistance",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Reverse Leakage Current",
|
|
||||||
"name": "Reverse Leakage Current",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Reverse Voltage (Vr)",
|
|
||||||
"name": "Reverse Voltage (Vr)",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Sim.Pins",
|
|
||||||
"name": "Sim.Pins",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Stock",
|
|
||||||
"name": "Stock",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Strike Gundam",
|
|
||||||
"name": "Strike Gundam",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Switch Height",
|
|
||||||
"name": "Switch Height",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Switch Length",
|
|
||||||
"name": "Switch Length",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Switch Width",
|
|
||||||
"name": "Switch Width",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Temperature Coefficient",
|
|
||||||
"name": "Temperature Coefficient",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Tolerance",
|
|
||||||
"name": "Tolerance",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Type",
|
|
||||||
"name": "Type",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Voltage Rated",
|
|
||||||
"name": "Voltage Rated",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Voltage Rating (Dc)",
|
|
||||||
"name": "Voltage Rating (Dc)",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "With Lamp",
|
|
||||||
"name": "With Lamp",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Actuator Style",
|
|
||||||
"name": "Actuator Style",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "Description",
|
|
||||||
"name": "Description",
|
|
||||||
"show": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"group_by": false,
|
|
||||||
"label": "#",
|
|
||||||
"name": "${ITEM_NUMBER}",
|
|
||||||
"show": false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"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": [
|
|
||||||
[
|
|
||||||
"a6275404-53d1-4c49-9128-131a51db8de5",
|
|
||||||
"Root"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
"text_variables": {}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +0,0 @@
|
|||||||
[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 = "wchisp flash"
|
|
||||||
#runner = "wlink -v flash --enable-sdi-print --watch-serial --erase"
|
|
||||||
# runner = "wlink -v flash"
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
;;; .doomrc --- doom runtime config -*- mode: emacs-lisp; lexical-binding: t; -*-
|
|
||||||
;;; Commentary:
|
|
||||||
;;; Code:
|
|
||||||
(require 'doom) ; be silent, byte-compiler
|
|
||||||
|
|
||||||
(after! dape
|
|
||||||
(add-to-list
|
|
||||||
'dape-configs
|
|
||||||
`(gdb-dap-openocd
|
|
||||||
ensure (lambda (config)
|
|
||||||
(dape-ensure-command config)
|
|
||||||
(let* ((default-directory
|
|
||||||
(or (dape-config-get config 'command-cwd)
|
|
||||||
default-directory))
|
|
||||||
(command (dape-config-get config 'command))
|
|
||||||
(output (shell-command-to-string (format "%s --version" command)))
|
|
||||||
(version (save-match-data
|
|
||||||
(when (string-match "GNU gdb \\(?:(.*) \\)?\\([0-9.]+\\)" output)
|
|
||||||
(string-to-number (match-string 1 output))))))
|
|
||||||
(unless (>= version 14.1)
|
|
||||||
(user-error "Requires gdb version >= 14.1"))))
|
|
||||||
modes ()
|
|
||||||
command-cwd dape-command-cwd
|
|
||||||
command "gdb"
|
|
||||||
command-args ("--interpreter=dap")
|
|
||||||
:request nil
|
|
||||||
:program nil
|
|
||||||
:args []
|
|
||||||
:stopAtBeginningOfMainSubprogram nil))
|
|
||||||
)
|
|
||||||
|
|
||||||
;;; .doomrc ends here
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
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
|
|
||||||
2
Software/CAN_Sensor/.gitignore
vendored
2
Software/CAN_Sensor/.gitignore
vendored
@@ -1,2 +0,0 @@
|
|||||||
target
|
|
||||||
vendor
|
|
||||||
8
Software/CAN_Sensor/.idea/.gitignore
generated
vendored
8
Software/CAN_Sensor/.idea/.gitignore
generated
vendored
@@ -1,8 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
11
Software/CAN_Sensor/.idea/ch32-sensor.iml
generated
11
Software/CAN_Sensor/.idea/ch32-sensor.iml
generated
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="EMPTY_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
8
Software/CAN_Sensor/.idea/modules.xml
generated
8
Software/CAN_Sensor/.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/ch32-sensor.iml" filepath="$PROJECT_DIR$/.idea/ch32-sensor.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
9
Software/CAN_Sensor/.idea/vcs.xml
generated
9
Software/CAN_Sensor/.idea/vcs.xml
generated
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/../../website/themes/blowfish" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
904
Software/CAN_Sensor/Cargo.lock
generated
904
Software/CAN_Sensor/Cargo.lock
generated
@@ -1,904 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 4
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ahash"
|
|
||||||
version = "0.8.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"once_cell",
|
|
||||||
"version_check",
|
|
||||||
"zerocopy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bincode"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740"
|
|
||||||
dependencies = [
|
|
||||||
"bincode_derive",
|
|
||||||
"unty",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bincode_derive"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09"
|
|
||||||
dependencies = [
|
|
||||||
"virtue",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bit_field"
|
|
||||||
version = "0.10.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitfield"
|
|
||||||
version = "0.14.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2d7e60934ceec538daadb9d8432424ed043a904d8e0243f3c6446bce549a46ac"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bms"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"canapi",
|
|
||||||
"ch32-hal",
|
|
||||||
"embassy-executor",
|
|
||||||
"embassy-futures",
|
|
||||||
"embassy-sync 0.6.2",
|
|
||||||
"embassy-time",
|
|
||||||
"embassy-usb",
|
|
||||||
"embedded-alloc",
|
|
||||||
"embedded-can",
|
|
||||||
"heapless",
|
|
||||||
"log",
|
|
||||||
"panic-halt",
|
|
||||||
"qingke",
|
|
||||||
"qingke-rt",
|
|
||||||
"static_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "byteorder"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "canapi"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"bincode",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ch32-hal"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "git+https://github.com/empirephoenix/ch32-hal.git#8f0fc9a36a603cfc130295a79dbb336c504f659c"
|
|
||||||
dependencies = [
|
|
||||||
"ch32-metapac",
|
|
||||||
"critical-section",
|
|
||||||
"embassy-futures",
|
|
||||||
"embassy-hal-internal",
|
|
||||||
"embassy-sync 0.6.2",
|
|
||||||
"embassy-time",
|
|
||||||
"embassy-time-driver",
|
|
||||||
"embassy-time-queue-utils",
|
|
||||||
"embassy-usb-driver",
|
|
||||||
"embedded-can",
|
|
||||||
"embedded-hal 0.2.7",
|
|
||||||
"embedded-hal 1.0.0",
|
|
||||||
"embedded-hal-async",
|
|
||||||
"embedded-hal-nb",
|
|
||||||
"futures",
|
|
||||||
"nb 1.1.0",
|
|
||||||
"proc-macro2",
|
|
||||||
"qingke",
|
|
||||||
"qingke-rt",
|
|
||||||
"quote",
|
|
||||||
"rand_core",
|
|
||||||
"sdio-host",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ch32-metapac"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "git+https://github.com/ch32-rs/ch32-metapac?rev=b1cbc7a98e43af3fd3170821654784e2c01cb26b#b1cbc7a98e43af3fd3170821654784e2c01cb26b"
|
|
||||||
dependencies = [
|
|
||||||
"riscv 0.11.1",
|
|
||||||
"vcell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "critical-section"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "darling"
|
|
||||||
version = "0.20.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
|
|
||||||
dependencies = [
|
|
||||||
"darling_core",
|
|
||||||
"darling_macro",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "darling_core"
|
|
||||||
version = "0.20.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
|
|
||||||
dependencies = [
|
|
||||||
"fnv",
|
|
||||||
"ident_case",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"strsim",
|
|
||||||
"syn 2.0.106",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "darling_macro"
|
|
||||||
version = "0.20.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
|
||||||
dependencies = [
|
|
||||||
"darling_core",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.106",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "document-features"
|
|
||||||
version = "0.2.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
|
|
||||||
dependencies = [
|
|
||||||
"litrs",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embassy-executor"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "90327bcc66333a507f89ecc4e2d911b265c45f5c9bc241f98eee076752d35ac6"
|
|
||||||
dependencies = [
|
|
||||||
"critical-section",
|
|
||||||
"document-features",
|
|
||||||
"embassy-executor-macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embassy-executor-macros"
|
|
||||||
version = "0.6.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3577b1e9446f61381179a330fc5324b01d511624c55f25e3c66c9e3c626dbecf"
|
|
||||||
dependencies = [
|
|
||||||
"darling",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.106",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embassy-futures"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embassy-hal-internal"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0ef3bac31ec146321248a169e9c7b5799f1e0b3829c7a9b324cb4600a7438f59"
|
|
||||||
dependencies = [
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embassy-net-driver"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "524eb3c489760508f71360112bca70f6e53173e6fe48fc5f0efd0f5ab217751d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embassy-net-driver-channel"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b7b2739fbcf6cd206ae08779c7d709087b16577d255f2ea4a45bc4bbbf305b3f"
|
|
||||||
dependencies = [
|
|
||||||
"embassy-futures",
|
|
||||||
"embassy-net-driver",
|
|
||||||
"embassy-sync 0.7.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embassy-sync"
|
|
||||||
version = "0.6.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8d2c8cdff05a7a51ba0087489ea44b0b1d97a296ca6b1d6d1a33ea7423d34049"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"critical-section",
|
|
||||||
"embedded-io-async",
|
|
||||||
"futures-sink",
|
|
||||||
"futures-util",
|
|
||||||
"heapless",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embassy-sync"
|
|
||||||
version = "0.7.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "73974a3edbd0bd286759b3d483540f0ebef705919a5f56f4fc7709066f71689b"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"critical-section",
|
|
||||||
"embedded-io-async",
|
|
||||||
"futures-core",
|
|
||||||
"futures-sink",
|
|
||||||
"heapless",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embassy-time"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f820157f198ada183ad62e0a66f554c610cdcd1a9f27d4b316358103ced7a1f8"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"critical-section",
|
|
||||||
"document-features",
|
|
||||||
"embassy-time-driver",
|
|
||||||
"embedded-hal 0.2.7",
|
|
||||||
"embedded-hal 1.0.0",
|
|
||||||
"embedded-hal-async",
|
|
||||||
"futures-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embassy-time-driver"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a0a244c7dc22c8d0289379c8d8830cae06bb93d8f990194d0de5efb3b5ae7ba6"
|
|
||||||
dependencies = [
|
|
||||||
"document-features",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embassy-time-queue-utils"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc55c748d16908a65b166d09ce976575fb8852cf60ccd06174092b41064d8f83"
|
|
||||||
dependencies = [
|
|
||||||
"embassy-executor",
|
|
||||||
"heapless",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embassy-usb"
|
|
||||||
version = "0.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8d0b882133fa684b9d4652351cd7aac5afe8a2c2bf4a7da59f442ff61087cda2"
|
|
||||||
dependencies = [
|
|
||||||
"embassy-futures",
|
|
||||||
"embassy-net-driver-channel",
|
|
||||||
"embassy-sync 0.6.2",
|
|
||||||
"embassy-usb-driver",
|
|
||||||
"heapless",
|
|
||||||
"ssmarshal",
|
|
||||||
"usbd-hid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embassy-usb-driver"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "340c5ce591ef58c6449e43f51d2c53efe1bf0bb6a40cbf80afa0d259c7d52c76"
|
|
||||||
dependencies = [
|
|
||||||
"embedded-io-async",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-alloc"
|
|
||||||
version = "0.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8f2de9133f68db0d4627ad69db767726c99ff8585272716708227008d3f1bddd"
|
|
||||||
dependencies = [
|
|
||||||
"critical-section",
|
|
||||||
"linked_list_allocator",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-can"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e9d2e857f87ac832df68fa498d18ddc679175cf3d2e4aa893988e5601baf9438"
|
|
||||||
dependencies = [
|
|
||||||
"nb 1.1.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-hal"
|
|
||||||
version = "0.2.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
|
|
||||||
dependencies = [
|
|
||||||
"nb 0.1.3",
|
|
||||||
"void",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-hal"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-hal-async"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
|
|
||||||
dependencies = [
|
|
||||||
"embedded-hal 1.0.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-hal-nb"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fba4268c14288c828995299e59b12babdbe170f6c6d73731af1b4648142e8605"
|
|
||||||
dependencies = [
|
|
||||||
"embedded-hal 1.0.0",
|
|
||||||
"nb 1.1.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-io"
|
|
||||||
version = "0.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "embedded-io-async"
|
|
||||||
version = "0.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f"
|
|
||||||
dependencies = [
|
|
||||||
"embedded-io",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "encode_unicode"
|
|
||||||
version = "0.3.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fnv"
|
|
||||||
version = "1.0.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
|
||||||
dependencies = [
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-io",
|
|
||||||
"futures-sink",
|
|
||||||
"futures-task",
|
|
||||||
"futures-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-channel"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-sink",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-core"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-io"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-macro"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.106",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-sink"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-task"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-util"
|
|
||||||
version = "0.3.31"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-macro",
|
|
||||||
"futures-sink",
|
|
||||||
"futures-task",
|
|
||||||
"pin-project-lite",
|
|
||||||
"pin-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hash32"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hashbrown"
|
|
||||||
version = "0.13.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
|
||||||
dependencies = [
|
|
||||||
"ahash",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "heapless"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
|
||||||
dependencies = [
|
|
||||||
"hash32",
|
|
||||||
"portable-atomic",
|
|
||||||
"stable_deref_trait",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ident_case"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "linked_list_allocator"
|
|
||||||
version = "0.10.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "litrs"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "log"
|
|
||||||
version = "0.4.28"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nb"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
|
|
||||||
dependencies = [
|
|
||||||
"nb 1.1.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nb"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-traits"
|
|
||||||
version = "0.2.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "once_cell"
|
|
||||||
version = "1.21.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "panic-halt"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a513e167849a384b7f9b746e517604398518590a9142f4846a32e3c2a4de7b11"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "paste"
|
|
||||||
version = "1.0.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project-lite"
|
|
||||||
version = "0.2.16"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-utils"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "portable-atomic"
|
|
||||||
version = "1.11.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
|
||||||
dependencies = [
|
|
||||||
"critical-section",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro-error"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro-error-attr",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro-error-attr"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.101"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "qingke"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c0230c5310b68c08a3cf8b59fbeec3e9d8e352bc6500f62cbaf9c677f42c8dfc"
|
|
||||||
dependencies = [
|
|
||||||
"bit_field",
|
|
||||||
"critical-section",
|
|
||||||
"riscv 0.12.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "qingke-rt"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b955c60adac70c6d40205b1dbe9f57e1151d06aa842069cdbaef7bc07ad283fd"
|
|
||||||
dependencies = [
|
|
||||||
"qingke",
|
|
||||||
"qingke-rt-macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "qingke-rt-macros"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5f2ed46d18953ea5765ab26a07d1f092dffac2da1b4830c4397e02c3cec08501"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro-error",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.41"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.6.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "riscv"
|
|
||||||
version = "0.11.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2f5c1b8bf41ea746266cdee443d1d1e9125c86ce1447e1a2615abd34330d33a9"
|
|
||||||
dependencies = [
|
|
||||||
"critical-section",
|
|
||||||
"embedded-hal 1.0.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "riscv"
|
|
||||||
version = "0.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5ea8ff73d3720bdd0a97925f0bf79ad2744b6da8ff36be3840c48ac81191d7a7"
|
|
||||||
dependencies = [
|
|
||||||
"critical-section",
|
|
||||||
"embedded-hal 1.0.0",
|
|
||||||
"paste",
|
|
||||||
"riscv-macros",
|
|
||||||
"riscv-pac",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "riscv-macros"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f265be5d634272320a7de94cea15c22a3bfdd4eb42eb43edc528415f066a1f25"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.106",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "riscv-pac"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8188909339ccc0c68cfb5a04648313f09621e8b87dc03095454f1a11f6c5d436"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sdio-host"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f93c025f9cfe4c388c328ece47d11a54a823da3b5ad0370b22d95ad47137f85a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde"
|
|
||||||
version = "1.0.228"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
|
||||||
dependencies = [
|
|
||||||
"serde_core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_core"
|
|
||||||
version = "1.0.228"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
|
||||||
dependencies = [
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_derive"
|
|
||||||
version = "1.0.228"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.106",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ssmarshal"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f3e6ad23b128192ed337dfa4f1b8099ced0c2bf30d61e551b65fda5916dbb850"
|
|
||||||
dependencies = [
|
|
||||||
"encode_unicode",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "stable_deref_trait"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "static_cell"
|
|
||||||
version = "2.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0530892bb4fa575ee0da4b86f86c667132a94b74bb72160f58ee5a4afec74c23"
|
|
||||||
dependencies = [
|
|
||||||
"portable-atomic",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strsim"
|
|
||||||
version = "0.11.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "1.0.109"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.106"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unty"
|
|
||||||
version = "0.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "usb-device"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6"
|
|
||||||
dependencies = [
|
|
||||||
"heapless",
|
|
||||||
"portable-atomic",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "usbd-hid"
|
|
||||||
version = "0.8.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e6f291ab53d428685cc780f08a2eb9d5d6ff58622db2b36e239a4f715f1e184c"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"ssmarshal",
|
|
||||||
"usb-device",
|
|
||||||
"usbd-hid-macros",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "usbd-hid-descriptors"
|
|
||||||
version = "0.8.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0eee54712c5d778d2fb2da43b1ce5a7b5060886ef7b09891baeb4bf36910a3ed"
|
|
||||||
dependencies = [
|
|
||||||
"bitfield",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "usbd-hid-macros"
|
|
||||||
version = "0.8.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bb573c76e7884035ac5e1ab4a81234c187a82b6100140af0ab45757650ccda38"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder",
|
|
||||||
"hashbrown",
|
|
||||||
"log",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"serde",
|
|
||||||
"syn 1.0.109",
|
|
||||||
"usbd-hid-descriptors",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "vcell"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "version_check"
|
|
||||||
version = "0.9.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "virtue"
|
|
||||||
version = "0.0.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "void"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy"
|
|
||||||
version = "0.8.27"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
|
|
||||||
dependencies = [
|
|
||||||
"zerocopy-derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zerocopy-derive"
|
|
||||||
version = "0.8.27"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.106",
|
|
||||||
]
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "bms"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
# Shared CAN API
|
|
||||||
canapi = { path = "../Shared/canapi" }
|
|
||||||
ch32-hal = { git = "https://github.com/empirephoenix/ch32-hal.git", features = [
|
|
||||||
"ch32v203c8t6",
|
|
||||||
"memory-x",
|
|
||||||
"embassy",
|
|
||||||
"rt",
|
|
||||||
"time-driver-tim2",
|
|
||||||
|
|
||||||
], default-features = false }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
embassy-executor = { version = "0.7.0", features = [
|
|
||||||
# "arch-riscv32",
|
|
||||||
"arch-spin", # TODO: Required for USBD to connect properl
|
|
||||||
"executor-thread",
|
|
||||||
] }
|
|
||||||
|
|
||||||
#embassy-time = { version = "0.3.2" }
|
|
||||||
embassy-usb = { version = "0.3.0" }
|
|
||||||
embassy-futures = { version = "0.1.0" }
|
|
||||||
embassy-sync = { version = "0.6.0" }
|
|
||||||
embedded-can = "0.4.1"
|
|
||||||
embedded-alloc = { version = "0.6.0", default-features = false, features = ["llff"] }
|
|
||||||
|
|
||||||
# This is okay because we should automatically use whatever ch32-hal uses
|
|
||||||
qingke-rt = "*"
|
|
||||||
qingke = "*"
|
|
||||||
|
|
||||||
panic-halt = "1.0"
|
|
||||||
|
|
||||||
|
|
||||||
heapless = { version = "0.8.0", features = ["portable-atomic-critical-section"] }
|
|
||||||
embassy-time = { version = "0.4.0" }
|
|
||||||
static_cell = "2.1.1"
|
|
||||||
log = "0.4.28"
|
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
#lto = true
|
|
||||||
opt-level = 1
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
strip = true # symbols are not flashed to the microcontroller, so don't strip them.
|
|
||||||
lto = true
|
|
||||||
debug = false
|
|
||||||
opt-level = "z" # Optimize for size.
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
# ch32v203-bms
|
|
||||||
|
|
||||||
A simple battery management controller software.
|
|
||||||
|
|
||||||
## WeActStudio BluePill Plus CH32 Pin Labels
|
|
||||||
|
|
||||||
The BluePill Plus CH32 board from WeActStudio uses standard MCU port naming printed on the PCB silkscreen:
|
|
||||||
|
|
||||||
- PAx: GPIO Port A pins, labeled PA0 .. PA15
|
|
||||||
- PBx: GPIO Port B pins, labeled PB0 .. PB15
|
|
||||||
- PCx: GPIO Port C pins, commonly PC13 .. PC15 are broken out
|
|
||||||
- Other labels typically present: 3V3, 5V, G (GND), NRST (reset), BOOT (BOOT0), and SWD/RVSWD pads for programming/debug (SWCLK/SWDIO or similar).
|
|
||||||
|
|
||||||
For the exact header layout and picture of the silkscreen labels, please refer to the official WeActStudio documentation:
|
|
||||||
|
|
||||||
- https://github.com/WeActStudio/WeActStudio.BluePill-Plus-CH32
|
|
||||||
|
|
||||||
Pins used by this firmware (as referenced in `src/main.rs`):
|
|
||||||
|
|
||||||
- PA1: ADC analog input (combined Trigger/Threshold in the 555-timer example)
|
|
||||||
- PB0: Digital output (Q in the 555-timer example)
|
|
||||||
|
|
||||||
If you need to map a label to code, use the same letter+number as in the silkscreen. For example, `p.PA1` in code corresponds to pin labeled "PA1" on the PCB header, and `p.PB0` corresponds to "PB0".
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
``` sh
|
|
||||||
cargo build --release
|
|
||||||
```
|
|
||||||
|
|
||||||
- Wire the MCU’s USB pins to a USB connector:
|
|
||||||
- D+ (PA12)
|
|
||||||
- D− (PA11)
|
|
||||||
- GND and 5V (as appropriate for your board; ensure you have a data-capable cable)
|
|
||||||
|
|
||||||
After flashing and powering via USB, your OS should enumerate a virtual serial port (e.g., /dev/ttyACM0 on Linux, COMx on Windows, /dev/tty.usbmodem* on macOS). Open it with any terminal program (baud setting is ignored by CDC but 115200 is fine).
|
|
||||||
|
|
||||||
Example:
|
|
||||||
- Linux: `screen /dev/ttyACM0 115200`
|
|
||||||
- macOS: `screen /dev/tty.usbmodemXXXX 115200`
|
|
||||||
- Windows: Use PuTTY on the shown COM port.
|
|
||||||
|
|
||||||
You can flash the built ELF using wchisp (WCH ISP tool):
|
|
||||||
|
|
||||||
``` sh
|
|
||||||
wchisp flash target/riscv32imc-unknown-none-elf/release/bms
|
|
||||||
# or, if using a wrapper on your system/container, the command may be:
|
|
||||||
# wchip wchisp flash target/riscv32imc-unknown-none-elf/release/bms
|
|
||||||
```
|
|
||||||
|
|
||||||
## Unlock / Remove MCU Protection (fix "checksum error")
|
|
||||||
|
|
||||||
Some CH32 devices ship with flash protection enabled. When protected, tools may report a checksum error and refuse to program. You can clear the protection by performing a full chip erase with wchisp. This will erase all flash contents.
|
|
||||||
|
|
||||||
Steps:
|
|
||||||
|
|
||||||
- Ensure WCH-Link/WCH-LinkE is connected to the target and your OS has permissions to access it.
|
|
||||||
- Verify connection and current status:
|
|
||||||
|
|
||||||
``` sh
|
|
||||||
wchisp info
|
|
||||||
```
|
|
||||||
|
|
||||||
- Mass erase the chip (this also clears protection/lock bits):
|
|
||||||
|
|
||||||
``` sh
|
|
||||||
* start the device (while pressing boot)
|
|
||||||
wchisp erase
|
|
||||||
* powercycle the device (while pressing boot)
|
|
||||||
wchisp config reset
|
|
||||||
* powercycle the device again (while pressing boot), flash should not work
|
|
||||||
```
|
|
||||||
|
|
||||||
- Flash your firmware again:
|
|
||||||
|
|
||||||
``` sh
|
|
||||||
wchisp flash target/riscv32imc-unknown-none-elf/release/bms
|
|
||||||
```
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
- The target used in this project is CH32V203C8T6; wchisp detects it automatically with WCH-Link.
|
|
||||||
- If your wchisp version differs, run `wchisp --help`, `wchisp erase --help`, or consult the tool's README for the exact flag name.
|
|
||||||
- If the tool still reports protection, look for commands named `unprotect` or `protect --off` in `wchisp --help`. The mass/chip erase is the typical way to clear protection.
|
|
||||||
|
|
||||||
## 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
|
|
||||||
```
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#!/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
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#!/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" "$@"
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
#!/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" "$@"
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
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");
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
/* 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);
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,298 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
use crate::hal::peripherals::CAN1;
|
|
||||||
use core::fmt::Write as _;
|
|
||||||
use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET, MOISTURE_DATA_OFFSET};
|
|
||||||
use canapi::SensorSlot;
|
|
||||||
use ch32_hal::gpio::{Level, Output, Speed};
|
|
||||||
use ch32_hal::adc::{Adc, SampleTime, ADC_MAX};
|
|
||||||
use ch32_hal::can;
|
|
||||||
use ch32_hal::can::{Can, CanFifo, CanFilter, CanFrame, CanMode};
|
|
||||||
use ch32_hal::mode::{NonBlocking};
|
|
||||||
use ch32_hal::peripherals::USBD;
|
|
||||||
use embassy_executor::{Spawner, task};
|
|
||||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
|
||||||
use embassy_usb::{Builder, UsbDevice};
|
|
||||||
use embassy_futures::yield_now;
|
|
||||||
use hal::usbd::{Driver};
|
|
||||||
use hal::{bind_interrupts};
|
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
|
||||||
use embassy_sync::channel::{Channel};
|
|
||||||
use embassy_time::{Instant, Duration, Delay, Timer};
|
|
||||||
use embedded_can::{Id, StandardId};
|
|
||||||
use {ch32_hal as hal, panic_halt as _};
|
|
||||||
|
|
||||||
macro_rules! mk_static {
|
|
||||||
($t:ty,$val:expr) => {{
|
|
||||||
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
|
|
||||||
#[deny(unused_attributes)]
|
|
||||||
let x = STATIC_CELL.uninit().write(($val));
|
|
||||||
x
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
|
||||||
USB_LP_CAN1_RX0 => hal::usbd::InterruptHandler<hal::peripherals::USBD>;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
use embedded_alloc::LlffHeap as Heap;
|
|
||||||
use embedded_can::nb::Can as nb_can;
|
|
||||||
use qingke::riscv::asm::delay;
|
|
||||||
use log::log;
|
|
||||||
|
|
||||||
#[global_allocator]
|
|
||||||
static HEAP: Heap = Heap::empty();
|
|
||||||
|
|
||||||
|
|
||||||
static LOG_CH: Channel<CriticalSectionRawMutex, heapless::String<128>, 8> = Channel::new();
|
|
||||||
|
|
||||||
#[embassy_executor::main(entry = "qingke_rt::entry")]
|
|
||||||
async fn main(spawner: Spawner) {
|
|
||||||
ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2));
|
|
||||||
//
|
|
||||||
unsafe {
|
|
||||||
static mut HEAP_SPACE: [u8; 4096] = [0; 4096]; // 4 KiB heap, adjust as needed
|
|
||||||
HEAP.init(HEAP_SPACE.as_ptr() as usize, HEAP_SPACE.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
let p = hal::init(hal::Config {
|
|
||||||
rcc: hal::rcc::Config::SYSCLK_FREQ_144MHZ_HSI,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Build driver and USB stack using 'static buffers
|
|
||||||
let driver = Driver::new(p.USBD, Irqs, p.PA12, p.PA11);
|
|
||||||
|
|
||||||
let mut config = embassy_usb::Config::new(0xC0DE, 0xCAFE);
|
|
||||||
config.manufacturer = Some("Embassy");
|
|
||||||
config.product = Some("USB-serial example");
|
|
||||||
config.serial_number = Some("12345678");
|
|
||||||
config.max_power = 100;
|
|
||||||
config.max_packet_size_0 = 64;
|
|
||||||
|
|
||||||
// Windows compatibility requires these; CDC-ACM
|
|
||||||
config.device_class = 0x02;
|
|
||||||
config.device_sub_class = 0x02;
|
|
||||||
config.device_protocol = 0x00;
|
|
||||||
config.composite_with_iads = false;
|
|
||||||
|
|
||||||
let mut builder = Builder::new(
|
|
||||||
driver,
|
|
||||||
config,
|
|
||||||
mk_static!([u8;256], [0; 256]),
|
|
||||||
mk_static!([u8;256], [0; 256]),
|
|
||||||
&mut [], // no msos descriptors
|
|
||||||
mk_static!([u8;64], [0; 64]),
|
|
||||||
);
|
|
||||||
// Initialize CDC state and create CDC-ACM class
|
|
||||||
let class = mk_static!(CdcAcmClass<'static, Driver<'static, hal::peripherals::USBD>>,
|
|
||||||
CdcAcmClass::new(
|
|
||||||
&mut builder,
|
|
||||||
mk_static!(State, State::new()),
|
|
||||||
64
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Build USB device
|
|
||||||
let usb = mk_static!(UsbDevice<Driver<USBD>>, builder.build()) ;
|
|
||||||
|
|
||||||
// Create GPIO for 555 Q output (PB0)
|
|
||||||
let q_out = Output::new(p.PB0, Level::Low, Speed::Low);
|
|
||||||
// Built-in LED on PB2 mirrors Q state
|
|
||||||
let led = Output::new(p.PB2, Level::Low, Speed::Low);
|
|
||||||
|
|
||||||
let info = Output::new(p.PA3, Level::Low, Speed::Low);
|
|
||||||
|
|
||||||
// Create ADC on ADC1 and use PA1 as analog input (Threshold/Trigger)
|
|
||||||
let adc = Adc::new(p.ADC1, Default::default());
|
|
||||||
let ain = p.PA1;
|
|
||||||
let config = can::can::Config::default();
|
|
||||||
let can: Can<CAN1, NonBlocking> = Can::new_nb(p.CAN1, p.PB8, p.PB9, CanFifo::Fifo0, CanMode::Normal, 125_000, config).expect("Valid");
|
|
||||||
ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2));
|
|
||||||
|
|
||||||
spawner.spawn(usb_task(usb)).unwrap();
|
|
||||||
spawner.spawn(usb_writer(class)).unwrap();
|
|
||||||
// move Q output, LED, ADC and analog input into worker task
|
|
||||||
spawner.spawn(worker(q_out, led, adc, ain, can)).unwrap();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Prevent main from exiting
|
|
||||||
core::future::pending::<()>().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task]
|
|
||||||
async fn worker(
|
|
||||||
mut q: Output<'static>,
|
|
||||||
mut led: Output<'static>,
|
|
||||||
mut adc: Adc<'static, hal::peripherals::ADC1>,
|
|
||||||
mut ain: hal::peripherals::PA1,
|
|
||||||
mut can: Can<'static, CAN1, NonBlocking>,
|
|
||||||
) {
|
|
||||||
// 555 emulation state: Q initially Low
|
|
||||||
let mut q_high = false;
|
|
||||||
let low_th: u16 = (ADC_MAX as u16) / 3; // ~1/3 Vref
|
|
||||||
let high_th: u16 = ((ADC_MAX as u32 * 2) / 3) as u16; // ~2/3 Vref
|
|
||||||
|
|
||||||
|
|
||||||
let moisture_address = StandardId::new(plant_id(MOISTURE_DATA_OFFSET, SensorSlot::A, 0)).unwrap();
|
|
||||||
let identity_address = StandardId::new(plant_id(IDENTIFY_CMD_OFFSET, SensorSlot::A, 0)).unwrap();
|
|
||||||
|
|
||||||
let mut filter = CanFilter::new_id_list();
|
|
||||||
|
|
||||||
filter
|
|
||||||
.get(0)
|
|
||||||
.unwrap()
|
|
||||||
.set(identity_address.into(), Default::default());
|
|
||||||
|
|
||||||
can.add_filter(filter);
|
|
||||||
//can.add_filter(CanFilter::accept_all());
|
|
||||||
|
|
||||||
|
|
||||||
loop {
|
|
||||||
// Count rising edges of Q in a 100 ms window
|
|
||||||
let start = Instant::now();
|
|
||||||
let mut pulses: u32 = 0;
|
|
||||||
let mut last_q = q_high;
|
|
||||||
|
|
||||||
while Instant::now().checked_duration_since(start).unwrap_or(Duration::from_millis(0))
|
|
||||||
< Duration::from_millis(1000)
|
|
||||||
{
|
|
||||||
// Sample the analog input (Threshold/Trigger on A1)
|
|
||||||
let val: u16 = adc.convert(&mut ain, SampleTime::CYCLES28_5);
|
|
||||||
|
|
||||||
// 555 core behavior:
|
|
||||||
// - If input <= 1/3 Vref => set Q high (trigger)
|
|
||||||
// - If input >= 2/3 Vref => set Q low (threshold)
|
|
||||||
// - Otherwise keep previous Q state (hysteresis)
|
|
||||||
if val <= low_th {
|
|
||||||
q_high = true;
|
|
||||||
} else if val >= high_th {
|
|
||||||
q_high = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Drive output pin accordingly
|
|
||||||
if q_high {
|
|
||||||
q.set_high();
|
|
||||||
} else {
|
|
||||||
q.set_low();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count rising edges
|
|
||||||
if !last_q && q_high {
|
|
||||||
pulses = pulses.saturating_add(1);
|
|
||||||
}
|
|
||||||
last_q = q_high;
|
|
||||||
|
|
||||||
// Yield to allow USB and other tasks to run
|
|
||||||
yield_now().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute frequency from 100 ms window
|
|
||||||
let freq_hz = pulses; // pulses per 0.1s => Hz
|
|
||||||
|
|
||||||
let mut msg: heapless::String<128> = heapless::String::new();
|
|
||||||
let _ = write!(
|
|
||||||
&mut msg,
|
|
||||||
"555 window=100ms pulses={} freq={} Hz (A1->Q on PB0)\r\n",
|
|
||||||
pulses, freq_hz
|
|
||||||
);
|
|
||||||
log(msg);
|
|
||||||
|
|
||||||
|
|
||||||
let mut moisture = CanFrame::new(moisture_address, &[freq_hz as u8]).unwrap();
|
|
||||||
match can.transmit(&mut moisture){
|
|
||||||
Ok(..) => {
|
|
||||||
let mut msg: heapless::String<128> = heapless::String::new();
|
|
||||||
let _ = write!(
|
|
||||||
&mut msg,
|
|
||||||
"Send to canbus"
|
|
||||||
);
|
|
||||||
log(msg);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
let mut msg: heapless::String<128> = heapless::String::new();
|
|
||||||
let _ = write!(
|
|
||||||
&mut msg,
|
|
||||||
"err {:?}"
|
|
||||||
,err
|
|
||||||
);
|
|
||||||
log(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
yield_now().await;
|
|
||||||
match can.receive() {
|
|
||||||
Ok(frame) => {
|
|
||||||
match frame.id() {
|
|
||||||
Id::Standard(s_frame) => {
|
|
||||||
let mut msg: heapless::String<128> = heapless::String::new();
|
|
||||||
let _ = write!(
|
|
||||||
&mut msg,
|
|
||||||
"Received from canbus: {:?} ident is {:?} \r\n",
|
|
||||||
s_frame.as_raw(),
|
|
||||||
identity_address.as_raw()
|
|
||||||
);
|
|
||||||
log(msg);
|
|
||||||
if s_frame.as_raw() == identity_address.as_raw() {
|
|
||||||
for _ in 0..10 {
|
|
||||||
Timer::after_millis(250).await;
|
|
||||||
led.toggle();
|
|
||||||
}
|
|
||||||
led.set_low();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Id::Extended(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn log(message: heapless::String<128>) {
|
|
||||||
match LOG_CH.try_send(message) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task]
|
|
||||||
async fn usb_task(usb: &'static mut UsbDevice<'static, Driver<'static, hal::peripherals::USBD>>) {
|
|
||||||
usb.run().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[task]
|
|
||||||
async fn usb_writer(
|
|
||||||
class: &'static mut CdcAcmClass<'static, Driver<'static, hal::peripherals::USBD>>
|
|
||||||
) {
|
|
||||||
loop {
|
|
||||||
|
|
||||||
class.wait_connection().await;
|
|
||||||
printer(class).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn printer(class: &mut CdcAcmClass<'static, Driver<'static, USBD>>) {
|
|
||||||
loop {
|
|
||||||
let msg = LOG_CH.receive().await;
|
|
||||||
match class.write_packet(msg.as_bytes()).await {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(_) => {
|
|
||||||
// Disconnected or endpoint disabled
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
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" ]
|
|
||||||
10
Software/MainBoard/bootloader/.gitignore
vendored
10
Software/MainBoard/bootloader/.gitignore
vendored
@@ -1,10 +0,0 @@
|
|||||||
# ESP-IDF build artifacts
|
|
||||||
build/
|
|
||||||
.sdkconfig*
|
|
||||||
CMakeFiles/
|
|
||||||
CMakeCache.txt
|
|
||||||
cmake-build-*/
|
|
||||||
*.log
|
|
||||||
*.bin
|
|
||||||
*.elf
|
|
||||||
*.map
|
|
||||||
8
Software/MainBoard/bootloader/.idea/.gitignore
generated
vendored
8
Software/MainBoard/bootloader/.idea/.gitignore
generated
vendored
@@ -1,8 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="EMPTY_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$" />
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
8
Software/MainBoard/bootloader/.idea/modules.xml
generated
8
Software/MainBoard/bootloader/.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/bootloader.iml" filepath="$PROJECT_DIR$/.idea/bootloader.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.16)
|
|
||||||
|
|
||||||
# Minimal ESP-IDF project to build only the bootloader
|
|
||||||
# You must have ESP-IDF installed and IDF_PATH exported.
|
|
||||||
|
|
||||||
# Pin the target to ESP32-C6 to ensure correct bootloader build
|
|
||||||
# (must be set before including project.cmake)
|
|
||||||
set(IDF_TARGET "esp32c6")
|
|
||||||
|
|
||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
|
||||||
project(custom_bootloader)
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
Custom ESP-IDF Bootloader (Rollback Enabled)
|
|
||||||
|
|
||||||
This minimal project builds a custom ESP-IDF bootloader with rollback support enabled.
|
|
||||||
You can flash it later alongside a Rust firmware using `espflash`.
|
|
||||||
|
|
||||||
What this provides
|
|
||||||
- A minimal ESP-IDF project (CMake) that can build just the bootloader.
|
|
||||||
- Rollback support enabled via sdkconfig.defaults (CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y).
|
|
||||||
- A sample OTA partition table (partitions.csv) suitable for OTA and rollback (otadata + two OTA slots).
|
|
||||||
- A convenience script to build the bootloader for the desired target.
|
|
||||||
|
|
||||||
Requirements
|
|
||||||
- ESP-IDF installed and set up (IDF_PATH exported, Python env activated).
|
|
||||||
- A selected target (esp32, esp32s3, esp32c3, etc.).
|
|
||||||
|
|
||||||
Build
|
|
||||||
1) Ensure ESP-IDF is set up:
|
|
||||||
source "$IDF_PATH/export.sh"
|
|
||||||
|
|
||||||
2) Pick a target (examples):
|
|
||||||
idf.py set-target esp32
|
|
||||||
# or use the script:
|
|
||||||
./build_bootloader.sh esp32
|
|
||||||
|
|
||||||
3) Build only the bootloader:
|
|
||||||
idf.py bootloader
|
|
||||||
# or using the script (which also supports setting target):
|
|
||||||
./build_bootloader.sh esp32
|
|
||||||
|
|
||||||
Artifacts
|
|
||||||
- build/bootloader/bootloader.bin
|
|
||||||
|
|
||||||
Using with espflash (Rust)
|
|
||||||
- For a no_std Rust firmware, you can pass this custom bootloader to espflash:
|
|
||||||
espflash flash --bootloader build/bootloader/bootloader.bin \
|
|
||||||
--partition-table partitions.csv \
|
|
||||||
<your-app-binary-or-elf>
|
|
||||||
|
|
||||||
Notes
|
|
||||||
- Rollback logic requires an OTA layout (otadata + at least two OTA app partitions). The provided partitions.csv is a starting point; adjust sizes/offsets to match your needs.
|
|
||||||
- This project doesn’t build an application; it exists solely to produce a bootloader with the right configuration.
|
|
||||||
- If you need different log verbosity or features, run `idf.py menuconfig` and then diff/port the changes back into sdkconfig.defaults.
|
|
||||||
- Targets supported depend on your ESP-IDF version. Use `idf.py set-target <chip>` or `./build_bootloader.sh <chip>`.
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Build script for custom ESP-IDF bootloader with rollback enabled.
|
|
||||||
# Requirements:
|
|
||||||
# - ESP-IDF installed
|
|
||||||
# - IDF_PATH exported
|
|
||||||
# - Python env prepared (the usual ESP-IDF setup)
|
|
||||||
# Usage:
|
|
||||||
# ./build_bootloader.sh [esp32|esp32s3|esp32c3|esp32s2|esp32c2|esp32c6|esp32h2]
|
|
||||||
# If target is omitted, the last configured target will be used.
|
|
||||||
|
|
||||||
TARGET=${1:-}
|
|
||||||
|
|
||||||
if [[ -z "${IDF_PATH:-}" ]]; then
|
|
||||||
echo "ERROR: IDF_PATH is not set. Please install ESP-IDF and export the environment (source export.sh)." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# shellcheck source=/dev/null
|
|
||||||
source "$IDF_PATH/export.sh"
|
|
||||||
|
|
||||||
if [[ -n "$TARGET" ]]; then
|
|
||||||
idf.py set-target "$TARGET"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ensure sdkconfig.defaults is considered (ESP-IDF does this automatically).
|
|
||||||
# Build only the bootloader.
|
|
||||||
idf.py bootloader
|
|
||||||
|
|
||||||
echo
|
|
||||||
BOOTLOADER_BIN="build/bootloader/bootloader.bin"
|
|
||||||
if [[ -f "$BOOTLOADER_BIN" ]]; then
|
|
||||||
echo "Bootloader built: $BOOTLOADER_BIN"
|
|
||||||
echo "You can use this with espflash via:"
|
|
||||||
echo " espflash flash --bootloader $BOOTLOADER_BIN [--partition-table partitions.csv] <your-app-binary>"
|
|
||||||
else
|
|
||||||
echo "ERROR: Bootloader binary not found. Check build logs above." >&2
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
cp build/bootloader/bootloader.bin ../rust/bootloader.bin
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
idf_component_register(SRCS "dummy.c" INCLUDE_DIRS ".")
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
// This file intentionally left almost empty.
|
|
||||||
// ESP-IDF expects at least one component; the bootloader build does not use this.
|
|
||||||
|
|
||||||
void __unused_dummy_symbol(void) {}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,17 +0,0 @@
|
|||||||
# Target can be set with: idf.py set-target esp32|esp32s3|esp32c3|...
|
|
||||||
# If not set via idf.py, ESP-IDF may default to a target; it's recommended to set it explicitly.
|
|
||||||
|
|
||||||
# Explicitly pin target to ESP32-C6
|
|
||||||
CONFIG_IDF_TARGET="esp32c6"
|
|
||||||
CONFIG_IDF_TARGET_ESP32C6=y
|
|
||||||
CONFIG_IDF_TARGET_ARCH_RISCV=y
|
|
||||||
|
|
||||||
# Bootloader configuration
|
|
||||||
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
|
|
||||||
CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y
|
|
||||||
# Slightly faster boot by skipping GPIO checks unless you need that feature
|
|
||||||
CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y
|
|
||||||
|
|
||||||
# Partition table config is not required to build bootloader, but shown for clarity when you build full app later
|
|
||||||
# CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
|
||||||
# CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
|
||||||
File diff suppressed because it is too large
Load Diff
8
Software/MainBoard/rust/.idea/.gitignore
generated
vendored
8
Software/MainBoard/rust/.idea/.gitignore
generated
vendored
@@ -1,8 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
7
Software/MainBoard/rust/.idea/vcs.xml
generated
7
Software/MainBoard/rust/.idea/vcs.xml
generated
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$/../website/themes/blowfish" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
rm ./src/webserver/index.html.gz
|
|
||||||
rm ./src/webserver/bundle.js.gz
|
|
||||||
set -e
|
|
||||||
|
|
||||||
pushd ./src_webpack/
|
|
||||||
npm install
|
|
||||||
npx webpack build
|
|
||||||
cp index.html.gz ../src/webserver/index.html.gz
|
|
||||||
cp bundle.js.gz ../src/webserver/bundle.js.gz
|
|
||||||
popd
|
|
||||||
|
|
||||||
cargo build --release
|
|
||||||
espflash save-image --bootloader bootloader.bin --partition-table partitions.csv --chip esp32c6 target/riscv32imac-unknown-none-elf/release/plant-ctrl2 image.bin
|
|
||||||
espflash flash --monitor --bootloader bootloader.bin --chip esp32c6 --baud 921600 --partition-table partitions.csv target/riscv32imac-unknown-none-elf/release/plant-ctrl2
|
|
||||||
Binary file not shown.
@@ -1,53 +0,0 @@
|
|||||||
use vergen::EmitBuilder;
|
|
||||||
|
|
||||||
fn linker_be_nice() {
|
|
||||||
let args: Vec<String> = std::env::args().collect();
|
|
||||||
if args.len() > 1 {
|
|
||||||
let kind = &args[1];
|
|
||||||
let what = &args[2];
|
|
||||||
|
|
||||||
match kind.as_str() {
|
|
||||||
"undefined-symbol" => match what.as_str() {
|
|
||||||
"_defmt_timestamp" => {
|
|
||||||
eprintln!();
|
|
||||||
eprintln!("💡 `defmt` not found - make sure `defmt.x` is added as a linker script and you have included `use defmt_rtt as _;`");
|
|
||||||
eprintln!();
|
|
||||||
}
|
|
||||||
"_stack_start" => {
|
|
||||||
eprintln!();
|
|
||||||
eprintln!("💡 Is the linker script `linkall.x` missing?");
|
|
||||||
eprintln!();
|
|
||||||
}
|
|
||||||
"esp_wifi_preempt_enable"
|
|
||||||
| "esp_wifi_preempt_yield_task"
|
|
||||||
| "esp_wifi_preempt_task_create" => {
|
|
||||||
eprintln!();
|
|
||||||
eprintln!("💡 `esp-wifi` has no scheduler enabled. Make sure you have the `builtin-scheduler` feature enabled, or that you provide an external scheduler.");
|
|
||||||
eprintln!();
|
|
||||||
}
|
|
||||||
"embedded_test_linker_file_not_added_to_rustflags" => {
|
|
||||||
eprintln!();
|
|
||||||
eprintln!("💡 `embedded-test` not found - make sure `embedded-test.x` is added as a linker script for tests");
|
|
||||||
eprintln!();
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
},
|
|
||||||
// we don't have anything helpful for "missing-lib" yet
|
|
||||||
_ => {
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::process::exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"cargo:rustc-link-arg=--error-handling-script={}",
|
|
||||||
std::env::current_exe().unwrap().display()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
linker_be_nice();
|
|
||||||
let _ = EmitBuilder::builder().all_git().all_build().emit();
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
rm ./src/webserver/index.html.gz
|
|
||||||
rm ./src/webserver/bundle.js.gz
|
|
||||||
set -e
|
|
||||||
|
|
||||||
pushd ./src_webpack/
|
|
||||||
npm install
|
|
||||||
npx webpack build
|
|
||||||
cp index.html.gz ../src/webserver/index.html.gz
|
|
||||||
cp bundle.js.gz ../src/webserver/bundle.js.gz
|
|
||||||
popd
|
|
||||||
|
|
||||||
cargo build --release
|
|
||||||
espflash flash --monitor --bootloader bootloader.bin --chip esp32c6 --baud 921600 --partition-table partitions.csv target/riscv32imac-unknown-none-elf/release/plant-ctrl2
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
rm image.bin
|
|
||||||
rm ./src/webserver/index.html.gz
|
|
||||||
rm ./src/webserver/bundle.js.gz
|
|
||||||
set -e
|
|
||||||
|
|
||||||
pushd ./src_webpack/
|
|
||||||
npm install
|
|
||||||
npx webpack build
|
|
||||||
cp index.html.gz ../src/webserver/index.html.gz
|
|
||||||
cp bundle.js.gz ../src/webserver/bundle.js.gz
|
|
||||||
popd
|
|
||||||
|
|
||||||
set -e
|
|
||||||
cargo build --release
|
|
||||||
espflash save-image --bootloader bootloader.bin --partition-table partitions.csv --chip esp32c6 target/riscv32imac-unknown-none-elf/release/plant-ctrl2 image.bin
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
nvs, data, nvs, , 16k,
|
|
||||||
otadata, data, ota, , 8k,
|
|
||||||
phy_init, data, phy, , 4k,
|
|
||||||
ota_0, app, ota_0, , 3968k,
|
|
||||||
ota_1, app, ota_1, , 3968k,
|
|
||||||
storage, data, littlefs,, 8M,
|
|
||||||
|
@@ -1,2 +0,0 @@
|
|||||||
[toolchain]
|
|
||||||
channel = "nightly"
|
|
||||||
@@ -1,994 +0,0 @@
|
|||||||
use crate::bail;
|
|
||||||
use crate::config::{NetworkConfig, PlantControllerConfig};
|
|
||||||
use crate::hal::{PLANT_COUNT, TIME_ACCESS};
|
|
||||||
use crate::log::{LogMessage, LOG_ACCESS};
|
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
use crate::fat_error::{ContextExt, FatError, FatResult};
|
|
||||||
use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem;
|
|
||||||
use alloc::string::ToString;
|
|
||||||
use alloc::sync::Arc;
|
|
||||||
use alloc::{format, string::String, vec, vec::Vec};
|
|
||||||
use core::net::{IpAddr, Ipv4Addr, SocketAddr};
|
|
||||||
use core::str::FromStr;
|
|
||||||
use core::sync::atomic::Ordering;
|
|
||||||
use embassy_executor::Spawner;
|
|
||||||
use embassy_net::udp::UdpSocket;
|
|
||||||
use embassy_net::{DhcpConfig, Ipv4Cidr, Runner, Stack, StackResources, StaticConfigV4};
|
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
|
||||||
use embassy_sync::mutex::{Mutex, MutexGuard};
|
|
||||||
use embassy_sync::once_lock::OnceLock;
|
|
||||||
use embassy_time::{Duration, Timer, WithTimeout};
|
|
||||||
use embedded_storage::nor_flash::{check_erase, NorFlash, ReadNorFlash};
|
|
||||||
use esp_bootloader_esp_idf::ota::OtaImageState::Valid;
|
|
||||||
use esp_bootloader_esp_idf::ota::{Ota, OtaImageState, Slot};
|
|
||||||
use esp_bootloader_esp_idf::partitions::FlashRegion;
|
|
||||||
use esp_hal::gpio::{Input, RtcPinWithResistors};
|
|
||||||
use esp_hal::rng::Rng;
|
|
||||||
use esp_hal::rtc_cntl::{
|
|
||||||
sleep::{TimerWakeupSource, WakeupLevel},
|
|
||||||
Rtc,
|
|
||||||
};
|
|
||||||
use esp_hal::system::software_reset;
|
|
||||||
use esp_println::println;
|
|
||||||
use esp_storage::FlashStorage;
|
|
||||||
use esp_wifi::wifi::{
|
|
||||||
AccessPointConfiguration, AccessPointInfo, AuthMethod, ClientConfiguration, Configuration,
|
|
||||||
ScanConfig, ScanTypeConfig, WifiController, WifiDevice, WifiState,
|
|
||||||
};
|
|
||||||
use littlefs2::fs::Filesystem;
|
|
||||||
use littlefs2_core::{FileType, PathBuf, SeekFrom};
|
|
||||||
use log::{info, warn};
|
|
||||||
use mcutie::{
|
|
||||||
Error, McutieBuilder, McutieReceiver, McutieTask, MqttMessage, PublishDisplay, Publishable,
|
|
||||||
QoS, Topic,
|
|
||||||
};
|
|
||||||
use portable_atomic::AtomicBool;
|
|
||||||
use smoltcp::socket::udp::PacketMetadata;
|
|
||||||
use smoltcp::wire::DnsQueryType;
|
|
||||||
use sntpc::{get_time, NtpContext, NtpTimestampGenerator};
|
|
||||||
|
|
||||||
#[esp_hal::ram(rtc_fast, persistent)]
|
|
||||||
static mut LAST_WATERING_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT];
|
|
||||||
#[esp_hal::ram(rtc_fast, persistent)]
|
|
||||||
static mut CONSECUTIVE_WATERING_PLANT: [u32; PLANT_COUNT] = [0; PLANT_COUNT];
|
|
||||||
#[esp_hal::ram(rtc_fast, persistent)]
|
|
||||||
static mut LOW_VOLTAGE_DETECTED: i8 = 0;
|
|
||||||
#[esp_hal::ram(rtc_fast, persistent)]
|
|
||||||
static mut RESTART_TO_CONF: i8 = 0;
|
|
||||||
|
|
||||||
const CONFIG_FILE: &str = "config.json";
|
|
||||||
const NTP_SERVER: &str = "pool.ntp.org";
|
|
||||||
|
|
||||||
static MQTT_CONNECTED_EVENT_RECEIVED: AtomicBool = AtomicBool::new(false);
|
|
||||||
static MQTT_ROUND_TRIP_RECEIVED: AtomicBool = AtomicBool::new(false);
|
|
||||||
pub static MQTT_STAY_ALIVE: AtomicBool = AtomicBool::new(false);
|
|
||||||
static MQTT_BASE_TOPIC: OnceLock<String> = OnceLock::new();
|
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
pub struct FileInfo {
|
|
||||||
filename: String,
|
|
||||||
size: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
pub struct FileList {
|
|
||||||
total: usize,
|
|
||||||
used: usize,
|
|
||||||
files: Vec<FileInfo>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default)]
|
|
||||||
struct Timestamp {
|
|
||||||
stamp: DateTime<Utc>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Minimal esp-idf equivalent for gpio_hold on esp32c6 via ROM functions
|
|
||||||
extern "C" {
|
|
||||||
fn gpio_pad_hold(gpio_num: u32);
|
|
||||||
fn gpio_pad_unhold(gpio_num: u32);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn hold_enable(gpio_num: u8) {
|
|
||||||
unsafe { gpio_pad_hold(gpio_num as u32) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn hold_disable(gpio_num: u8) {
|
|
||||||
unsafe { gpio_pad_unhold(gpio_num as u32) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NtpTimestampGenerator for Timestamp {
|
|
||||||
fn init(&mut self) {
|
|
||||||
self.stamp = DateTime::default();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn timestamp_sec(&self) -> u64 {
|
|
||||||
self.stamp.timestamp() as u64
|
|
||||||
}
|
|
||||||
|
|
||||||
fn timestamp_subsec_micros(&self) -> u32 {
|
|
||||||
self.stamp.timestamp_subsec_micros()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Esp<'a> {
|
|
||||||
pub fs: Arc<Mutex<CriticalSectionRawMutex, Filesystem<'static, LittleFs2Filesystem>>>,
|
|
||||||
pub rng: Rng,
|
|
||||||
//first starter (ap or sta will take these)
|
|
||||||
pub interface_sta: Option<WifiDevice<'static>>,
|
|
||||||
pub interface_ap: Option<WifiDevice<'static>>,
|
|
||||||
pub controller: Arc<Mutex<CriticalSectionRawMutex, WifiController<'static>>>,
|
|
||||||
|
|
||||||
pub boot_button: Input<'a>,
|
|
||||||
|
|
||||||
// RTC-capable GPIO used as external wake source (store the raw peripheral)
|
|
||||||
pub wake_gpio1: esp_hal::peripherals::GPIO1<'static>,
|
|
||||||
|
|
||||||
pub ota: Ota<'static, FlashStorage>,
|
|
||||||
pub ota_target: &'static mut FlashRegion<'static, FlashStorage>,
|
|
||||||
pub current: Slot,
|
|
||||||
pub slot0_state: OtaImageState,
|
|
||||||
pub slot1_state: OtaImageState,
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: On this target we never move Esp across OS threads; the firmware runs single-core
|
|
||||||
// cooperative tasks with Embassy. All interior mutability of non-Send peripherals is gated
|
|
||||||
// behind &mut self or embassy_sync Mutex with CriticalSectionRawMutex, which does not rely on
|
|
||||||
// thread scheduling. Therefore it is sound to mark Esp as Send to satisfy trait object bounds
|
|
||||||
// (e.g., Box<dyn BoardInteraction + Send>). If you add fields that are accessed from multiple
|
|
||||||
// CPU cores/threads, reconsider this.
|
|
||||||
unsafe impl Send for Esp<'_> {}
|
|
||||||
|
|
||||||
macro_rules! mk_static {
|
|
||||||
($t:ty,$val:expr) => {{
|
|
||||||
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
|
|
||||||
#[deny(unused_attributes)]
|
|
||||||
let x = STATIC_CELL.uninit().write(($val));
|
|
||||||
x
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Esp<'_> {
|
|
||||||
pub(crate) async fn delete_file(&self, filename: String) -> FatResult<()> {
|
|
||||||
let file = PathBuf::try_from(filename.as_str())?;
|
|
||||||
let access = self.fs.lock().await;
|
|
||||||
access.remove(&*file)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub(crate) async fn write_file(
|
|
||||||
&mut self,
|
|
||||||
filename: String,
|
|
||||||
offset: u32,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Result<(), FatError> {
|
|
||||||
let file = PathBuf::try_from(filename.as_str())?;
|
|
||||||
let access = self.fs.lock().await;
|
|
||||||
access.open_file_with_options_and_then(
|
|
||||||
|options| options.read(true).write(true).create(true),
|
|
||||||
&*file,
|
|
||||||
|file| {
|
|
||||||
file.seek(SeekFrom::Start(offset))?;
|
|
||||||
file.write(buf)?;
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_size(&mut self, filename: String) -> FatResult<usize> {
|
|
||||||
let file = PathBuf::try_from(filename.as_str())?;
|
|
||||||
let access = self.fs.lock().await;
|
|
||||||
let data = access.metadata(&*file)?;
|
|
||||||
Ok(data.len())
|
|
||||||
}
|
|
||||||
pub(crate) async fn get_file(
|
|
||||||
&mut self,
|
|
||||||
filename: String,
|
|
||||||
chunk: u32,
|
|
||||||
) -> FatResult<([u8; 512], usize)> {
|
|
||||||
use littlefs2::io::Error as lfs2Error;
|
|
||||||
|
|
||||||
let file = PathBuf::try_from(filename.as_str())?;
|
|
||||||
let access = self.fs.lock().await;
|
|
||||||
let mut buf = [0_u8; 512];
|
|
||||||
let mut read = 0;
|
|
||||||
let offset = chunk * buf.len() as u32;
|
|
||||||
access.open_file_with_options_and_then(
|
|
||||||
|options| options.read(true),
|
|
||||||
&*file,
|
|
||||||
|file| {
|
|
||||||
let length = file.len()? as u32;
|
|
||||||
if length == 0 {
|
|
||||||
Err(lfs2Error::IO)
|
|
||||||
} else if length > offset {
|
|
||||||
file.seek(SeekFrom::Start(offset))?;
|
|
||||||
read = file.read(&mut buf)?;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
//exactly at end, do nothing
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
Ok((buf, read))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn write_ota(&mut self, offset: u32, buf: &[u8]) -> Result<(), FatError> {
|
|
||||||
let _ = check_erase(self.ota_target, offset, offset + 4096);
|
|
||||||
self.ota_target.erase(offset, offset + 4096)?;
|
|
||||||
|
|
||||||
let mut temp = vec![0; buf.len()];
|
|
||||||
let read_back = temp.as_mut_slice();
|
|
||||||
//change to nor flash, align writes!
|
|
||||||
self.ota_target.write(offset, buf)?;
|
|
||||||
self.ota_target.read(offset, read_back)?;
|
|
||||||
if buf != read_back {
|
|
||||||
info!("Expected {:?} but got {:?}", buf, read_back);
|
|
||||||
bail!(
|
|
||||||
"Flash error, read back does not match write buffer at offset {:x}",
|
|
||||||
offset
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn finalize_ota(&mut self) -> Result<(), FatError> {
|
|
||||||
let current = self.ota.current_slot()?;
|
|
||||||
if self.ota.current_ota_state()? != OtaImageState::Valid {
|
|
||||||
info!(
|
|
||||||
"Validating current slot {:?} as it was able to ota",
|
|
||||||
current
|
|
||||||
);
|
|
||||||
self.ota.set_current_ota_state(Valid)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.ota.set_current_slot(current.next())?;
|
|
||||||
info!("switched slot");
|
|
||||||
self.ota.set_current_ota_state(OtaImageState::New)?;
|
|
||||||
info!("switched state for new partition");
|
|
||||||
let state_new = self.ota.current_ota_state()?;
|
|
||||||
info!("state on new partition now {:?}", state_new);
|
|
||||||
//determine nextslot crc
|
|
||||||
|
|
||||||
self.set_restart_to_conf(true);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn mode_override_pressed(&mut self) -> bool {
|
|
||||||
self.boot_button.is_low()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn sntp(
|
|
||||||
&mut self,
|
|
||||||
_max_wait_ms: u32,
|
|
||||||
stack: Stack<'_>,
|
|
||||||
) -> FatResult<DateTime<Utc>> {
|
|
||||||
println!("start sntp");
|
|
||||||
let mut rx_meta = [PacketMetadata::EMPTY; 16];
|
|
||||||
let mut rx_buffer = [0; 4096];
|
|
||||||
let mut tx_meta = [PacketMetadata::EMPTY; 16];
|
|
||||||
let mut tx_buffer = [0; 4096];
|
|
||||||
|
|
||||||
let mut socket = UdpSocket::new(
|
|
||||||
stack,
|
|
||||||
&mut rx_meta,
|
|
||||||
&mut rx_buffer,
|
|
||||||
&mut tx_meta,
|
|
||||||
&mut tx_buffer,
|
|
||||||
);
|
|
||||||
socket.bind(123).unwrap();
|
|
||||||
|
|
||||||
let context = NtpContext::new(Timestamp::default());
|
|
||||||
|
|
||||||
let ntp_addrs = stack
|
|
||||||
.dns_query(NTP_SERVER, DnsQueryType::A)
|
|
||||||
.await
|
|
||||||
.expect("Failed to resolve DNS");
|
|
||||||
if ntp_addrs.is_empty() {
|
|
||||||
bail!("Failed to resolve DNS");
|
|
||||||
}
|
|
||||||
info!("NTP server: {:?}", ntp_addrs);
|
|
||||||
|
|
||||||
let mut counter = 0;
|
|
||||||
loop {
|
|
||||||
let addr: IpAddr = ntp_addrs[0].into();
|
|
||||||
let timeout = get_time(SocketAddr::from((addr, 123)), &socket, context)
|
|
||||||
.with_timeout(Duration::from_millis((_max_wait_ms / 10) as u64))
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match timeout {
|
|
||||||
Ok(result) => {
|
|
||||||
let time = result?;
|
|
||||||
info!("Time: {:?}", time);
|
|
||||||
return DateTime::from_timestamp(time.seconds as i64, 0)
|
|
||||||
.context("Could not convert Sntp result");
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!("sntp timeout, retry: {:?}", err);
|
|
||||||
counter += 1;
|
|
||||||
if counter > 10 {
|
|
||||||
bail!("Failed to get time from NTP server");
|
|
||||||
}
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn wifi_scan(&mut self) -> FatResult<Vec<AccessPointInfo>> {
|
|
||||||
info!("start wifi scan");
|
|
||||||
let mut lock = self.controller.try_lock()?;
|
|
||||||
info!("start wifi scan lock");
|
|
||||||
let scan_config = ScanConfig {
|
|
||||||
ssid: None,
|
|
||||||
bssid: None,
|
|
||||||
channel: None,
|
|
||||||
show_hidden: false,
|
|
||||||
scan_type: ScanTypeConfig::Active {
|
|
||||||
min: Default::default(),
|
|
||||||
max: Default::default(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let rv = lock.scan_with_config_async(scan_config).await?;
|
|
||||||
info!("end wifi scan lock");
|
|
||||||
Ok(rv)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn last_pump_time(&self, plant: usize) -> Option<DateTime<Utc>> {
|
|
||||||
let ts = unsafe { LAST_WATERING_TIMESTAMP }[plant];
|
|
||||||
DateTime::from_timestamp_millis(ts)
|
|
||||||
}
|
|
||||||
pub(crate) fn store_last_pump_time(&mut self, plant: usize, time: DateTime<Utc>) {
|
|
||||||
unsafe {
|
|
||||||
LAST_WATERING_TIMESTAMP[plant] = time.timestamp_millis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub(crate) fn set_low_voltage_in_cycle(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
LOW_VOLTAGE_DETECTED = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub(crate) fn clear_low_voltage_in_cycle(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
LOW_VOLTAGE_DETECTED = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn low_voltage_in_cycle(&mut self) -> bool {
|
|
||||||
unsafe { LOW_VOLTAGE_DETECTED == 1 }
|
|
||||||
}
|
|
||||||
pub(crate) fn store_consecutive_pump_count(&mut self, plant: usize, count: u32) {
|
|
||||||
unsafe {
|
|
||||||
CONSECUTIVE_WATERING_PLANT[plant] = count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub(crate) fn consecutive_pump_count(&mut self, plant: usize) -> u32 {
|
|
||||||
unsafe { CONSECUTIVE_WATERING_PLANT[plant] }
|
|
||||||
}
|
|
||||||
pub(crate) fn get_restart_to_conf(&mut self) -> bool {
|
|
||||||
unsafe { RESTART_TO_CONF == 1 }
|
|
||||||
}
|
|
||||||
pub(crate) fn set_restart_to_conf(&mut self, to_conf: bool) {
|
|
||||||
unsafe {
|
|
||||||
if to_conf {
|
|
||||||
RESTART_TO_CONF = 1;
|
|
||||||
} else {
|
|
||||||
RESTART_TO_CONF = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn wifi_ap(&mut self) -> FatResult<Stack<'static>> {
|
|
||||||
let ssid = match self.load_config().await {
|
|
||||||
Ok(config) => config.network.ap_ssid.as_str().to_string(),
|
|
||||||
Err(_) => "PlantCtrl Emergency Mode".to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let spawner = Spawner::for_current_executor().await;
|
|
||||||
|
|
||||||
let device = self.interface_ap.take().unwrap();
|
|
||||||
let gw_ip_addr_str = "192.168.71.1";
|
|
||||||
let gw_ip_addr = Ipv4Addr::from_str(gw_ip_addr_str).expect("failed to parse gateway ip");
|
|
||||||
|
|
||||||
let config = embassy_net::Config::ipv4_static(StaticConfigV4 {
|
|
||||||
address: Ipv4Cidr::new(gw_ip_addr, 24),
|
|
||||||
gateway: Some(gw_ip_addr),
|
|
||||||
dns_servers: Default::default(),
|
|
||||||
});
|
|
||||||
|
|
||||||
let seed = (self.rng.random() as u64) << 32 | self.rng.random() as u64;
|
|
||||||
|
|
||||||
println!("init secondary stack");
|
|
||||||
// Init network stack
|
|
||||||
let (stack, runner) = embassy_net::new(
|
|
||||||
device,
|
|
||||||
config,
|
|
||||||
mk_static!(StackResources<4>, StackResources::<4>::new()),
|
|
||||||
seed,
|
|
||||||
);
|
|
||||||
let stack = mk_static!(Stack, stack);
|
|
||||||
|
|
||||||
let client_config = Configuration::AccessPoint(AccessPointConfiguration {
|
|
||||||
ssid: ssid.clone(),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
self.controller
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.set_configuration(&client_config)?;
|
|
||||||
|
|
||||||
println!("start new");
|
|
||||||
self.controller.lock().await.start()?;
|
|
||||||
println!("start net task");
|
|
||||||
spawner.spawn(net_task(runner)).ok();
|
|
||||||
println!("run dhcp");
|
|
||||||
spawner.spawn(run_dhcp(stack.clone(), gw_ip_addr_str)).ok();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if stack.is_link_up() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Timer::after(Duration::from_millis(500)).await;
|
|
||||||
}
|
|
||||||
while !stack.is_config_up() {
|
|
||||||
Timer::after(Duration::from_millis(100)).await
|
|
||||||
}
|
|
||||||
println!("Connect to the AP `${ssid}` and point your browser to http://{gw_ip_addr_str}/");
|
|
||||||
stack
|
|
||||||
.config_v4()
|
|
||||||
.inspect(|c| println!("ipv4 config: {c:?}"));
|
|
||||||
|
|
||||||
Ok(stack.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn wifi(
|
|
||||||
&mut self,
|
|
||||||
network_config: &NetworkConfig,
|
|
||||||
) -> FatResult<Stack<'static>> {
|
|
||||||
esp_wifi::wifi_set_log_verbose();
|
|
||||||
let ssid = network_config.ssid.clone();
|
|
||||||
match &ssid {
|
|
||||||
Some(ssid) => {
|
|
||||||
if ssid.is_empty() {
|
|
||||||
bail!("Wifi ssid was empty")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
bail!("Wifi ssid was empty")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let ssid = ssid.unwrap().to_string();
|
|
||||||
info!("attempting to connect wifi {ssid}");
|
|
||||||
let password = match network_config.password {
|
|
||||||
Some(ref password) => password.to_string(),
|
|
||||||
None => "".to_string(),
|
|
||||||
};
|
|
||||||
let max_wait = network_config.max_wait;
|
|
||||||
|
|
||||||
let spawner = Spawner::for_current_executor().await;
|
|
||||||
|
|
||||||
let device = self.interface_sta.take().unwrap();
|
|
||||||
let config = embassy_net::Config::dhcpv4(DhcpConfig::default());
|
|
||||||
|
|
||||||
let seed = (self.rng.random() as u64) << 32 | self.rng.random() as u64;
|
|
||||||
|
|
||||||
// Init network stack
|
|
||||||
let (stack, runner) = embassy_net::new(
|
|
||||||
device,
|
|
||||||
config,
|
|
||||||
mk_static!(StackResources<8>, StackResources::<8>::new()),
|
|
||||||
seed,
|
|
||||||
);
|
|
||||||
let stack = mk_static!(Stack, stack);
|
|
||||||
|
|
||||||
let client_config = Configuration::Client(ClientConfiguration {
|
|
||||||
ssid,
|
|
||||||
bssid: None,
|
|
||||||
auth_method: AuthMethod::WPA2Personal, //FIXME read from config, fill via scan
|
|
||||||
password,
|
|
||||||
channel: None,
|
|
||||||
});
|
|
||||||
self.controller
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.set_configuration(&client_config)?;
|
|
||||||
spawner.spawn(net_task(runner)).ok();
|
|
||||||
self.controller.lock().await.start_async().await?;
|
|
||||||
|
|
||||||
let timeout = {
|
|
||||||
let guard = TIME_ACCESS.get().await.lock().await;
|
|
||||||
guard.current_time_us()
|
|
||||||
} + max_wait as u64 * 1000;
|
|
||||||
loop {
|
|
||||||
let state = esp_wifi::wifi::sta_state();
|
|
||||||
match state {
|
|
||||||
WifiState::StaStarted => {
|
|
||||||
self.controller.lock().await.connect()?;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
if {
|
|
||||||
let guard = TIME_ACCESS.get().await.lock().await;
|
|
||||||
guard.current_time_us()
|
|
||||||
} > timeout
|
|
||||||
{
|
|
||||||
bail!("Timeout waiting for wifi sta ready")
|
|
||||||
}
|
|
||||||
Timer::after(Duration::from_millis(500)).await;
|
|
||||||
}
|
|
||||||
let timeout = {
|
|
||||||
let guard = TIME_ACCESS.get().await.lock().await;
|
|
||||||
guard.current_time_us()
|
|
||||||
} + max_wait as u64 * 1000;
|
|
||||||
loop {
|
|
||||||
let state = esp_wifi::wifi::sta_state();
|
|
||||||
match state {
|
|
||||||
WifiState::StaConnected => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
if {
|
|
||||||
let guard = TIME_ACCESS.get().await.lock().await;
|
|
||||||
guard.current_time_us()
|
|
||||||
} > timeout
|
|
||||||
{
|
|
||||||
bail!("Timeout waiting for wifi sta connected")
|
|
||||||
}
|
|
||||||
Timer::after(Duration::from_millis(500)).await;
|
|
||||||
}
|
|
||||||
let timeout = {
|
|
||||||
let guard = TIME_ACCESS.get().await.lock().await;
|
|
||||||
guard.current_time_us()
|
|
||||||
} + max_wait as u64 * 1000;
|
|
||||||
while !stack.is_link_up() {
|
|
||||||
if {
|
|
||||||
let guard = TIME_ACCESS.get().await.lock().await;
|
|
||||||
guard.current_time_us()
|
|
||||||
} > timeout
|
|
||||||
{
|
|
||||||
bail!("Timeout waiting for wifi link up")
|
|
||||||
}
|
|
||||||
Timer::after(Duration::from_millis(500)).await;
|
|
||||||
}
|
|
||||||
let timeout = {
|
|
||||||
let guard = TIME_ACCESS.get().await.lock().await;
|
|
||||||
guard.current_time_us()
|
|
||||||
} + max_wait as u64 * 1000;
|
|
||||||
while !stack.is_config_up() {
|
|
||||||
if {
|
|
||||||
let guard = TIME_ACCESS.get().await.lock().await;
|
|
||||||
guard.current_time_us()
|
|
||||||
} > timeout
|
|
||||||
{
|
|
||||||
bail!("Timeout waiting for wifi config up")
|
|
||||||
}
|
|
||||||
Timer::after(Duration::from_millis(100)).await
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("Connected WIFI, dhcp: {:?}", stack.config_v4());
|
|
||||||
Ok(stack.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deep_sleep(
|
|
||||||
&mut self,
|
|
||||||
duration_in_ms: u64,
|
|
||||||
mut rtc: MutexGuard<CriticalSectionRawMutex, Rtc>,
|
|
||||||
) -> ! {
|
|
||||||
// Configure and enter deep sleep using esp-hal. Also keep prior behavior where
|
|
||||||
// duration_in_ms == 0 triggers an immediate reset.
|
|
||||||
|
|
||||||
// Mark the current OTA image as valid if we reached here while in pending verify.
|
|
||||||
if let Ok(cur) = self.ota.current_ota_state() {
|
|
||||||
if cur == OtaImageState::PendingVerify {
|
|
||||||
self.ota
|
|
||||||
.set_current_ota_state(OtaImageState::Valid)
|
|
||||||
.expect("Could not set image to valid");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if duration_in_ms == 0 {
|
|
||||||
software_reset();
|
|
||||||
} else {
|
|
||||||
let timer = TimerWakeupSource::new(core::time::Duration::from_millis(duration_in_ms));
|
|
||||||
let mut wake_pins: [(&mut dyn RtcPinWithResistors, WakeupLevel); 1] =
|
|
||||||
[(&mut self.wake_gpio1, WakeupLevel::Low)];
|
|
||||||
let ext1 = esp_hal::rtc_cntl::sleep::Ext1WakeupSource::new(&mut wake_pins);
|
|
||||||
rtc.sleep_deep(&[&timer, &ext1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn load_config(&mut self) -> FatResult<PlantControllerConfig> {
|
|
||||||
let cfg = PathBuf::try_from(CONFIG_FILE)?;
|
|
||||||
let config_exist = self.fs.lock().await.exists(&cfg);
|
|
||||||
if !config_exist {
|
|
||||||
bail!("No config file stored")
|
|
||||||
}
|
|
||||||
let data = self.fs.lock().await.read::<4096>(&cfg)?;
|
|
||||||
let config: PlantControllerConfig = serde_json::from_slice(&data)?;
|
|
||||||
return Ok(config);
|
|
||||||
}
|
|
||||||
pub(crate) async fn save_config(&mut self, config: Vec<u8>) -> FatResult<()> {
|
|
||||||
let filesystem = self.fs.lock().await;
|
|
||||||
let cfg = PathBuf::try_from(CONFIG_FILE)?;
|
|
||||||
filesystem.write(&cfg, &*config)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub(crate) async fn list_files(&self) -> FatResult<FileList> {
|
|
||||||
let path = PathBuf::new();
|
|
||||||
|
|
||||||
let fs = self.fs.lock().await;
|
|
||||||
let free_size = fs.available_space()?;
|
|
||||||
let total_size = fs.total_space();
|
|
||||||
|
|
||||||
let mut result = FileList {
|
|
||||||
total: total_size,
|
|
||||||
used: total_size - free_size,
|
|
||||||
files: Vec::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
fs.read_dir_and_then(&path, |dir| {
|
|
||||||
for entry in dir {
|
|
||||||
let e = entry?;
|
|
||||||
if e.file_type() == FileType::File {
|
|
||||||
result.files.push(FileInfo {
|
|
||||||
filename: e.path().to_string(),
|
|
||||||
size: e.metadata().len(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn init_rtc_deepsleep_memory(
|
|
||||||
&self,
|
|
||||||
init_rtc_store: bool,
|
|
||||||
to_config_mode: bool,
|
|
||||||
) {
|
|
||||||
if init_rtc_store {
|
|
||||||
unsafe {
|
|
||||||
LAST_WATERING_TIMESTAMP = [0; PLANT_COUNT];
|
|
||||||
CONSECUTIVE_WATERING_PLANT = [0; PLANT_COUNT];
|
|
||||||
LOW_VOLTAGE_DETECTED = 0;
|
|
||||||
if to_config_mode {
|
|
||||||
RESTART_TO_CONF = 1
|
|
||||||
} else {
|
|
||||||
RESTART_TO_CONF = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
unsafe {
|
|
||||||
if to_config_mode {
|
|
||||||
RESTART_TO_CONF = 1;
|
|
||||||
}
|
|
||||||
LOG_ACCESS
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.log(
|
|
||||||
LogMessage::RestartToConfig,
|
|
||||||
RESTART_TO_CONF as u32,
|
|
||||||
0,
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
LOG_ACCESS
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.log(
|
|
||||||
LogMessage::LowVoltage,
|
|
||||||
LOW_VOLTAGE_DETECTED as u32,
|
|
||||||
0,
|
|
||||||
"",
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
for i in 0..PLANT_COUNT {
|
|
||||||
log::info!(
|
|
||||||
"LAST_WATERING_TIMESTAMP[{}] = UTC {}",
|
|
||||||
i,
|
|
||||||
LAST_WATERING_TIMESTAMP[i]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
for i in 0..PLANT_COUNT {
|
|
||||||
log::info!(
|
|
||||||
"CONSECUTIVE_WATERING_PLANT[{}] = {}",
|
|
||||||
i,
|
|
||||||
CONSECUTIVE_WATERING_PLANT[i]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn mqtt(
|
|
||||||
&mut self,
|
|
||||||
network_config: &'static NetworkConfig,
|
|
||||||
stack: Stack<'static>,
|
|
||||||
) -> FatResult<()> {
|
|
||||||
let base_topic = network_config
|
|
||||||
.base_topic
|
|
||||||
.as_ref()
|
|
||||||
.context("missing base topic")?;
|
|
||||||
if base_topic.is_empty() {
|
|
||||||
bail!("Mqtt base_topic was empty")
|
|
||||||
}
|
|
||||||
MQTT_BASE_TOPIC
|
|
||||||
.init(base_topic.to_string())
|
|
||||||
.map_err(|_| FatError::String {
|
|
||||||
error: "Error setting basetopic".to_string(),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let mqtt_url = network_config
|
|
||||||
.mqtt_url
|
|
||||||
.as_ref()
|
|
||||||
.context("missing mqtt url")?;
|
|
||||||
if mqtt_url.is_empty() {
|
|
||||||
bail!("Mqtt url was empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
let last_will_topic = format!("{}/state", base_topic);
|
|
||||||
let round_trip_topic = format!("{}/internal/roundtrip", base_topic);
|
|
||||||
let stay_alive_topic = format!("{}/stay_alive", base_topic);
|
|
||||||
|
|
||||||
let mut builder: McutieBuilder<'_, String, PublishDisplay<String, &str>, 0> =
|
|
||||||
McutieBuilder::new(stack, "plant ctrl", mqtt_url);
|
|
||||||
if network_config.mqtt_user.is_some() && network_config.mqtt_password.is_some() {
|
|
||||||
builder = builder.with_authentication(
|
|
||||||
network_config.mqtt_user.as_ref().unwrap().as_str(),
|
|
||||||
network_config.mqtt_password.as_ref().unwrap().as_str(),
|
|
||||||
);
|
|
||||||
info!("With authentification");
|
|
||||||
}
|
|
||||||
|
|
||||||
let lwt = Topic::General(last_will_topic);
|
|
||||||
let lwt = mk_static!(Topic<String>, lwt);
|
|
||||||
let lwt = lwt.with_display("lost").retain(true).qos(QoS::AtLeastOnce);
|
|
||||||
builder = builder.with_last_will(lwt);
|
|
||||||
//TODO make configurable
|
|
||||||
builder = builder.with_device_id("plantctrl");
|
|
||||||
|
|
||||||
let builder: McutieBuilder<'_, String, PublishDisplay<String, &str>, 2> = builder
|
|
||||||
.with_subscriptions([
|
|
||||||
Topic::General(round_trip_topic.clone()),
|
|
||||||
Topic::General(stay_alive_topic.clone()),
|
|
||||||
]);
|
|
||||||
|
|
||||||
let keep_alive = Duration::from_secs(60 * 60 * 2).as_secs() as u16;
|
|
||||||
let (receiver, task) = builder.build(keep_alive);
|
|
||||||
|
|
||||||
let spawner = Spawner::for_current_executor().await;
|
|
||||||
spawner.spawn(mqtt_incoming_task(
|
|
||||||
receiver,
|
|
||||||
round_trip_topic.clone(),
|
|
||||||
stay_alive_topic.clone(),
|
|
||||||
))?;
|
|
||||||
spawner.spawn(mqtt_runner(task))?;
|
|
||||||
|
|
||||||
LOG_ACCESS
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.log(LogMessage::StayAlive, 0, 0, "", &stay_alive_topic)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
LOG_ACCESS
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.log(LogMessage::MqttInfo, 0, 0, "", mqtt_url)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let mqtt_timeout = 15000;
|
|
||||||
let timeout = {
|
|
||||||
let guard = TIME_ACCESS.get().await.lock().await;
|
|
||||||
guard.current_time_us()
|
|
||||||
} + mqtt_timeout as u64 * 1000;
|
|
||||||
while !MQTT_CONNECTED_EVENT_RECEIVED.load(Ordering::Relaxed) {
|
|
||||||
let cur = TIME_ACCESS.get().await.lock().await.current_time_us();
|
|
||||||
if cur > timeout {
|
|
||||||
bail!("Timeout waiting MQTT connect event")
|
|
||||||
}
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
Topic::General(round_trip_topic.clone())
|
|
||||||
.with_display("online_text")
|
|
||||||
.publish()
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let timeout = {
|
|
||||||
let guard = TIME_ACCESS.get().await.lock().await;
|
|
||||||
guard.current_time_us()
|
|
||||||
} + mqtt_timeout as u64 * 1000;
|
|
||||||
while !MQTT_ROUND_TRIP_RECEIVED.load(Ordering::Relaxed) {
|
|
||||||
let cur = TIME_ACCESS.get().await.lock().await.current_time_us();
|
|
||||||
if cur > timeout {
|
|
||||||
//ensure we do not further try to publish
|
|
||||||
MQTT_CONNECTED_EVENT_RECEIVED.store(false, Ordering::Relaxed);
|
|
||||||
bail!("Timeout waiting MQTT roundtrip")
|
|
||||||
}
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn mqtt_inner(&mut self, subtopic: &str, message: &str) -> FatResult<()> {
|
|
||||||
if !subtopic.starts_with("/") {
|
|
||||||
bail!("Subtopic without / at start {}", subtopic);
|
|
||||||
}
|
|
||||||
if subtopic.len() > 192 {
|
|
||||||
bail!("Subtopic exceeds 192 chars {}", subtopic);
|
|
||||||
}
|
|
||||||
let base_topic = MQTT_BASE_TOPIC
|
|
||||||
.try_get()
|
|
||||||
.context("missing base topic in static!")?;
|
|
||||||
|
|
||||||
let full_topic = format!("{base_topic}{subtopic}");
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let result = Topic::General(full_topic.as_str())
|
|
||||||
.with_display(message)
|
|
||||||
.retain(true)
|
|
||||||
.publish()
|
|
||||||
.await;
|
|
||||||
match result {
|
|
||||||
Ok(()) => return Ok(()),
|
|
||||||
Err(err) => {
|
|
||||||
let retry = match err {
|
|
||||||
Error::IOError => false,
|
|
||||||
Error::TimedOut => true,
|
|
||||||
Error::TooLarge => false,
|
|
||||||
Error::PacketError => false,
|
|
||||||
Error::Invalid => false,
|
|
||||||
};
|
|
||||||
if !retry {
|
|
||||||
bail!(
|
|
||||||
"Error during mqtt send on topic {} with message {:#?} error is {:?}",
|
|
||||||
&full_topic,
|
|
||||||
message,
|
|
||||||
err
|
|
||||||
);
|
|
||||||
}
|
|
||||||
info!(
|
|
||||||
"Retransmit for {} with message {:#?} error is {:?} retrying {}",
|
|
||||||
&full_topic, message, err, retry
|
|
||||||
);
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub(crate) async fn mqtt_publish(&mut self, subtopic: &str, message: &str) {
|
|
||||||
let online = MQTT_CONNECTED_EVENT_RECEIVED.load(Ordering::Relaxed);
|
|
||||||
if !online {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let roundtrip_ok = MQTT_ROUND_TRIP_RECEIVED.load(Ordering::Relaxed);
|
|
||||||
if !roundtrip_ok {
|
|
||||||
info!("MQTT roundtrip not received yet, dropping message");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
match self.mqtt_inner(subtopic, message).await {
|
|
||||||
Ok(()) => {}
|
|
||||||
Err(err) => {
|
|
||||||
info!(
|
|
||||||
"Error during mqtt send on topic {} with message {:#?} error is {:?}",
|
|
||||||
subtopic, message, err
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::task]
|
|
||||||
async fn mqtt_runner(
|
|
||||||
task: McutieTask<'static, String, PublishDisplay<'static, String, &'static str>, 2>,
|
|
||||||
) {
|
|
||||||
task.run().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::task]
|
|
||||||
async fn mqtt_incoming_task(
|
|
||||||
receiver: McutieReceiver,
|
|
||||||
round_trip_topic: String,
|
|
||||||
stay_alive_topic: String,
|
|
||||||
) {
|
|
||||||
loop {
|
|
||||||
let message = receiver.receive().await;
|
|
||||||
match message {
|
|
||||||
MqttMessage::Connected => {
|
|
||||||
info!("Mqtt connected");
|
|
||||||
MQTT_CONNECTED_EVENT_RECEIVED.store(true, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
MqttMessage::Publish(topic, payload) => match topic {
|
|
||||||
Topic::DeviceType(_type_topic) => {}
|
|
||||||
Topic::Device(_device_topic) => {}
|
|
||||||
Topic::General(topic) => {
|
|
||||||
let subtopic = topic.as_str();
|
|
||||||
|
|
||||||
if subtopic.eq(round_trip_topic.as_str()) {
|
|
||||||
MQTT_ROUND_TRIP_RECEIVED.store(true, Ordering::Relaxed);
|
|
||||||
} else if subtopic.eq(stay_alive_topic.as_str()) {
|
|
||||||
let value = payload.eq_ignore_ascii_case("true".as_ref())
|
|
||||||
|| payload.eq_ignore_ascii_case("1".as_ref());
|
|
||||||
let a = match value {
|
|
||||||
true => 1,
|
|
||||||
false => 0,
|
|
||||||
};
|
|
||||||
LOG_ACCESS
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.log(LogMessage::MqttStayAliveRec, a, 0, "", "")
|
|
||||||
.await;
|
|
||||||
MQTT_STAY_ALIVE.store(value, Ordering::Relaxed);
|
|
||||||
} else {
|
|
||||||
LOG_ACCESS
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.log(LogMessage::UnknownTopic, 0, 0, "", &*topic)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
MqttMessage::Disconnected => {
|
|
||||||
MQTT_CONNECTED_EVENT_RECEIVED.store(false, Ordering::Relaxed);
|
|
||||||
info!("Mqtt disconnected");
|
|
||||||
}
|
|
||||||
MqttMessage::HomeAssistantOnline => {
|
|
||||||
info!("Home assistant is online");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::task(pool_size = 2)]
|
|
||||||
async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) {
|
|
||||||
runner.run().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::task]
|
|
||||||
async fn run_dhcp(stack: Stack<'static>, gw_ip_addr: &'static str) {
|
|
||||||
use core::net::{Ipv4Addr, SocketAddrV4};
|
|
||||||
|
|
||||||
use edge_dhcp::{
|
|
||||||
io::{self, DEFAULT_SERVER_PORT},
|
|
||||||
server::{Server, ServerOptions},
|
|
||||||
};
|
|
||||||
use edge_nal::UdpBind;
|
|
||||||
use edge_nal_embassy::{Udp, UdpBuffers};
|
|
||||||
|
|
||||||
let ip = Ipv4Addr::from_str(gw_ip_addr).expect("dhcp task failed to parse gw ip");
|
|
||||||
|
|
||||||
let mut buf = [0u8; 1500];
|
|
||||||
|
|
||||||
let mut gw_buf = [Ipv4Addr::UNSPECIFIED];
|
|
||||||
|
|
||||||
let buffers = UdpBuffers::<3, 1024, 1024, 10>::new();
|
|
||||||
let unbound_socket = Udp::new(stack, &buffers);
|
|
||||||
let mut bound_socket = unbound_socket
|
|
||||||
.bind(SocketAddr::V4(SocketAddrV4::new(
|
|
||||||
Ipv4Addr::UNSPECIFIED,
|
|
||||||
DEFAULT_SERVER_PORT,
|
|
||||||
)))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
_ = io::server::run(
|
|
||||||
&mut Server::<_, 64>::new_with_et(ip),
|
|
||||||
&ServerOptions::new(ip, Some(&mut gw_buf)),
|
|
||||||
&mut bound_socket,
|
|
||||||
&mut buf,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.inspect_err(|e| log::warn!("DHCP server error: {e:?}"));
|
|
||||||
Timer::after(Duration::from_millis(500)).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
use embedded_storage::nor_flash::{check_erase, NorFlash, ReadNorFlash};
|
|
||||||
use esp_bootloader_esp_idf::partitions::FlashRegion;
|
|
||||||
use esp_storage::FlashStorage;
|
|
||||||
use littlefs2::consts::U4096 as lfsCache;
|
|
||||||
use littlefs2::consts::U512 as lfsLookahead;
|
|
||||||
use littlefs2::driver::Storage as lfs2Storage;
|
|
||||||
use littlefs2::io::Error as lfs2Error;
|
|
||||||
use littlefs2::io::Result as lfs2Result;
|
|
||||||
use log::error;
|
|
||||||
|
|
||||||
pub struct LittleFs2Filesystem {
|
|
||||||
pub(crate) storage: &'static mut FlashRegion<'static, FlashStorage>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl lfs2Storage for LittleFs2Filesystem {
|
|
||||||
const READ_SIZE: usize = 4096;
|
|
||||||
const WRITE_SIZE: usize = 4096;
|
|
||||||
const BLOCK_SIZE: usize = 4096; //usually optimal for flash access
|
|
||||||
const BLOCK_COUNT: usize = 8 * 1000 * 1000 / 4096; //8Mb in 4k blocks + a little space for stupid calculation errors
|
|
||||||
const BLOCK_CYCLES: isize = 100;
|
|
||||||
type CACHE_SIZE = lfsCache;
|
|
||||||
type LOOKAHEAD_SIZE = lfsLookahead;
|
|
||||||
|
|
||||||
fn read(&mut self, off: usize, buf: &mut [u8]) -> lfs2Result<usize> {
|
|
||||||
let read_size: usize = Self::READ_SIZE;
|
|
||||||
if off % read_size != 0 {
|
|
||||||
error!("Littlefs2Filesystem read error: offset not aligned to read size offset: {} read_size: {}", off, read_size);
|
|
||||||
return Err(lfs2Error::IO);
|
|
||||||
}
|
|
||||||
if buf.len() % read_size != 0 {
|
|
||||||
error!("Littlefs2Filesystem read error: length not aligned to read size length: {} read_size: {}", buf.len(), read_size);
|
|
||||||
return Err(lfs2Error::IO);
|
|
||||||
}
|
|
||||||
match self.storage.read(off as u32, buf) {
|
|
||||||
Ok(..) => Ok(buf.len()),
|
|
||||||
Err(err) => {
|
|
||||||
error!("Littlefs2Filesystem read error: {:?}", err);
|
|
||||||
Err(lfs2Error::IO)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, off: usize, data: &[u8]) -> lfs2Result<usize> {
|
|
||||||
let write_size: usize = Self::WRITE_SIZE;
|
|
||||||
if off % write_size != 0 {
|
|
||||||
error!("Littlefs2Filesystem write error: offset not aligned to write size offset: {} write_size: {}", off, write_size);
|
|
||||||
return Err(lfs2Error::IO);
|
|
||||||
}
|
|
||||||
if data.len() % write_size != 0 {
|
|
||||||
error!("Littlefs2Filesystem write error: length not aligned to write size length: {} write_size: {}", data.len(), write_size);
|
|
||||||
return Err(lfs2Error::IO);
|
|
||||||
}
|
|
||||||
match self.storage.write(off as u32, data) {
|
|
||||||
Ok(..) => Ok(data.len()),
|
|
||||||
Err(err) => {
|
|
||||||
error!("Littlefs2Filesystem write error: {:?}", err);
|
|
||||||
Err(lfs2Error::IO)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn erase(&mut self, off: usize, len: usize) -> lfs2Result<usize> {
|
|
||||||
let block_size: usize = Self::BLOCK_SIZE;
|
|
||||||
if off % block_size != 0 {
|
|
||||||
error!("Littlefs2Filesystem erase error: offset not aligned to block size offset: {} block_size: {}", off, block_size);
|
|
||||||
return lfs2Result::Err(lfs2Error::IO);
|
|
||||||
}
|
|
||||||
if len % block_size != 0 {
|
|
||||||
error!("Littlefs2Filesystem erase error: length not aligned to block size length: {} block_size: {}", len, block_size);
|
|
||||||
return lfs2Result::Err(lfs2Error::IO);
|
|
||||||
}
|
|
||||||
|
|
||||||
match check_erase(self.storage, off as u32, (off+len) as u32) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(err) => {
|
|
||||||
error!("Littlefs2Filesystem check erase error: {:?}", err);
|
|
||||||
return lfs2Result::Err(lfs2Error::IO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match self.storage.erase(off as u32, (off + len) as u32) {
|
|
||||||
Ok(..) => lfs2Result::Ok(len),
|
|
||||||
Err(err) => {
|
|
||||||
error!("Littlefs2Filesystem erase error: {:?}", err);
|
|
||||||
lfs2Result::Err(lfs2Error::IO)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,450 +0,0 @@
|
|||||||
use crate::bail;
|
|
||||||
use crate::fat_error::FatError;
|
|
||||||
use crate::hal::esp::{hold_disable, hold_enable};
|
|
||||||
use crate::hal::rtc::RTCModuleInteraction;
|
|
||||||
use crate::hal::v3_shift_register::ShiftRegister40;
|
|
||||||
use crate::hal::water::TankSensor;
|
|
||||||
use crate::hal::{BoardInteraction, FreePeripherals, Moistures, Sensor, PLANT_COUNT, TIME_ACCESS};
|
|
||||||
use crate::log::{LogMessage, LOG_ACCESS};
|
|
||||||
use crate::{
|
|
||||||
config::PlantControllerConfig,
|
|
||||||
hal::{battery::BatteryInteraction, esp::Esp},
|
|
||||||
};
|
|
||||||
use alloc::boxed::Box;
|
|
||||||
use alloc::format;
|
|
||||||
use alloc::string::ToString;
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
|
||||||
use embassy_sync::mutex::Mutex;
|
|
||||||
use embassy_time::Timer;
|
|
||||||
use embedded_hal::digital::OutputPin as _;
|
|
||||||
use esp_hal::gpio::{Flex, Input, InputConfig, Level, Output, OutputConfig, Pull};
|
|
||||||
use esp_hal::pcnt::channel::CtrlMode::Keep;
|
|
||||||
use esp_hal::pcnt::channel::EdgeMode::{Hold, Increment};
|
|
||||||
use esp_hal::pcnt::unit::Unit;
|
|
||||||
use measurements::{Current, Voltage};
|
|
||||||
|
|
||||||
const PUMP8_BIT: usize = 0;
|
|
||||||
const PUMP1_BIT: usize = 1;
|
|
||||||
const PUMP2_BIT: usize = 2;
|
|
||||||
const PUMP3_BIT: usize = 3;
|
|
||||||
const PUMP4_BIT: usize = 4;
|
|
||||||
const PUMP5_BIT: usize = 5;
|
|
||||||
const PUMP6_BIT: usize = 6;
|
|
||||||
const PUMP7_BIT: usize = 7;
|
|
||||||
const MS_0: usize = 8;
|
|
||||||
const MS_4: usize = 9;
|
|
||||||
const MS_2: usize = 10;
|
|
||||||
const MS_3: usize = 11;
|
|
||||||
const MS_1: usize = 13;
|
|
||||||
const SENSOR_ON: usize = 12;
|
|
||||||
|
|
||||||
const SENSOR_A_1: u8 = 7;
|
|
||||||
const SENSOR_A_2: u8 = 6;
|
|
||||||
const SENSOR_A_3: u8 = 5;
|
|
||||||
const SENSOR_A_4: u8 = 4;
|
|
||||||
const SENSOR_A_5: u8 = 3;
|
|
||||||
const SENSOR_A_6: u8 = 2;
|
|
||||||
const SENSOR_A_7: u8 = 1;
|
|
||||||
const SENSOR_A_8: u8 = 0;
|
|
||||||
|
|
||||||
const SENSOR_B_1: u8 = 8;
|
|
||||||
const SENSOR_B_2: u8 = 9;
|
|
||||||
const SENSOR_B_3: u8 = 10;
|
|
||||||
const SENSOR_B_4: u8 = 11;
|
|
||||||
const SENSOR_B_5: u8 = 12;
|
|
||||||
const SENSOR_B_6: u8 = 13;
|
|
||||||
const SENSOR_B_7: u8 = 14;
|
|
||||||
const SENSOR_B_8: u8 = 15;
|
|
||||||
|
|
||||||
const CHARGING: usize = 14;
|
|
||||||
const AWAKE: usize = 15;
|
|
||||||
|
|
||||||
const FAULT_3: usize = 16;
|
|
||||||
const FAULT_8: usize = 17;
|
|
||||||
const FAULT_7: usize = 18;
|
|
||||||
const FAULT_6: usize = 19;
|
|
||||||
const FAULT_5: usize = 20;
|
|
||||||
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>,
|
|
||||||
rtc_module: Box<dyn RTCModuleInteraction + Send>,
|
|
||||||
esp: Esp<'a>,
|
|
||||||
shift_register:
|
|
||||||
Mutex<CriticalSectionRawMutex, ShiftRegister40<Output<'a>, Output<'a>, Output<'a>>>,
|
|
||||||
_shift_register_enable_invert: Output<'a>,
|
|
||||||
tank_sensor: TankSensor<'a>,
|
|
||||||
solar_is_day: Input<'a>,
|
|
||||||
light: Output<'a>,
|
|
||||||
main_pump: Output<'a>,
|
|
||||||
general_fault: Output<'a>,
|
|
||||||
pub signal_counter: Unit<'static, 0>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn create_v3(
|
|
||||||
peripherals: FreePeripherals<'static>,
|
|
||||||
esp: Esp<'static>,
|
|
||||||
config: PlantControllerConfig,
|
|
||||||
battery_monitor: Box<dyn BatteryInteraction + Send>,
|
|
||||||
rtc_module: Box<dyn RTCModuleInteraction + Send>,
|
|
||||||
) -> Result<Box<dyn BoardInteraction<'static> + Send + 'static>, FatError> {
|
|
||||||
log::info!("Start v3");
|
|
||||||
let clock = Output::new(peripherals.gpio15, Level::Low, OutputConfig::default());
|
|
||||||
let latch = Output::new(peripherals.gpio3, Level::Low, OutputConfig::default());
|
|
||||||
let data = Output::new(peripherals.gpio23, Level::Low, OutputConfig::default());
|
|
||||||
let shift_register = ShiftRegister40::new(clock, latch, data);
|
|
||||||
//disable all
|
|
||||||
for mut pin in shift_register.decompose() {
|
|
||||||
let _ = pin.set_low();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set always-on status bits
|
|
||||||
let _ = shift_register.decompose()[AWAKE].set_high();
|
|
||||||
let _ = shift_register.decompose()[CHARGING].set_high();
|
|
||||||
|
|
||||||
// Multiplexer defaults: ms0..ms3 low, ms4 high (disabled)
|
|
||||||
let _ = shift_register.decompose()[MS_0].set_low();
|
|
||||||
let _ = shift_register.decompose()[MS_1].set_low();
|
|
||||||
let _ = shift_register.decompose()[MS_2].set_low();
|
|
||||||
let _ = shift_register.decompose()[MS_3].set_low();
|
|
||||||
let _ = shift_register.decompose()[MS_4].set_high();
|
|
||||||
|
|
||||||
let one_wire_pin = Flex::new(peripherals.gpio18);
|
|
||||||
let tank_power_pin = Output::new(peripherals.gpio11, Level::Low, OutputConfig::default());
|
|
||||||
|
|
||||||
let flow_sensor_pin = Input::new(
|
|
||||||
peripherals.gpio4,
|
|
||||||
InputConfig::default().with_pull(Pull::Up),
|
|
||||||
);
|
|
||||||
|
|
||||||
let tank_sensor = TankSensor::create(
|
|
||||||
one_wire_pin,
|
|
||||||
peripherals.adc1,
|
|
||||||
peripherals.gpio5,
|
|
||||||
tank_power_pin,
|
|
||||||
flow_sensor_pin,
|
|
||||||
peripherals.pcnt1,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let solar_is_day = Input::new(peripherals.gpio7, InputConfig::default());
|
|
||||||
let light = Output::new(peripherals.gpio10, Level::Low, OutputConfig::default());
|
|
||||||
let mut main_pump = Output::new(peripherals.gpio2, Level::Low, OutputConfig::default());
|
|
||||||
main_pump.set_low();
|
|
||||||
let mut general_fault = Output::new(peripherals.gpio6, Level::Low, OutputConfig::default());
|
|
||||||
general_fault.set_low();
|
|
||||||
|
|
||||||
let mut shift_register_enable_invert =
|
|
||||||
Output::new(peripherals.gpio21, Level::Low, OutputConfig::default());
|
|
||||||
shift_register_enable_invert.set_low();
|
|
||||||
|
|
||||||
let signal_counter = peripherals.pcnt0;
|
|
||||||
|
|
||||||
signal_counter.set_high_limit(Some(i16::MAX))?;
|
|
||||||
|
|
||||||
let ch0 = &signal_counter.channel0;
|
|
||||||
let edge_pin = Input::new(peripherals.gpio22, InputConfig::default());
|
|
||||||
ch0.set_edge_signal(edge_pin.peripheral_input());
|
|
||||||
ch0.set_input_mode(Hold, Increment);
|
|
||||||
ch0.set_ctrl_mode(Keep, Keep);
|
|
||||||
signal_counter.listen();
|
|
||||||
|
|
||||||
Ok(Box::new(V3 {
|
|
||||||
config,
|
|
||||||
battery_monitor,
|
|
||||||
rtc_module,
|
|
||||||
esp,
|
|
||||||
shift_register: Mutex::new(shift_register),
|
|
||||||
_shift_register_enable_invert: shift_register_enable_invert,
|
|
||||||
tank_sensor,
|
|
||||||
solar_is_day,
|
|
||||||
light,
|
|
||||||
main_pump,
|
|
||||||
general_fault,
|
|
||||||
signal_counter,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
impl V3<'_> {
|
|
||||||
|
|
||||||
async fn inner_measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32, FatError> {
|
|
||||||
let mut results = [0_f32; REPEAT_MOIST_MEASURE];
|
|
||||||
for repeat in 0..REPEAT_MOIST_MEASURE {
|
|
||||||
self.signal_counter.pause();
|
|
||||||
self.signal_counter.clear();
|
|
||||||
//Disable all
|
|
||||||
{
|
|
||||||
let shift_register = self.shift_register.lock().await;
|
|
||||||
shift_register.decompose()[MS_4].set_high()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let sensor_channel = match sensor {
|
|
||||||
Sensor::A => match plant {
|
|
||||||
0 => SENSOR_A_1,
|
|
||||||
1 => SENSOR_A_2,
|
|
||||||
2 => SENSOR_A_3,
|
|
||||||
3 => SENSOR_A_4,
|
|
||||||
4 => SENSOR_A_5,
|
|
||||||
5 => SENSOR_A_6,
|
|
||||||
6 => SENSOR_A_7,
|
|
||||||
7 => SENSOR_A_8,
|
|
||||||
_ => bail!("Invalid plant id {}", plant),
|
|
||||||
},
|
|
||||||
Sensor::B => match plant {
|
|
||||||
0 => SENSOR_B_1,
|
|
||||||
1 => SENSOR_B_2,
|
|
||||||
2 => SENSOR_B_3,
|
|
||||||
3 => SENSOR_B_4,
|
|
||||||
4 => SENSOR_B_5,
|
|
||||||
5 => SENSOR_B_6,
|
|
||||||
6 => SENSOR_B_7,
|
|
||||||
7 => SENSOR_B_8,
|
|
||||||
_ => bail!("Invalid plant id {}", plant),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_bit_set = |b: u8| -> bool { sensor_channel & (1 << b) != 0 };
|
|
||||||
{
|
|
||||||
let shift_register = self.shift_register.lock().await;
|
|
||||||
let pin_0 = &mut shift_register.decompose()[MS_0];
|
|
||||||
let pin_1 = &mut shift_register.decompose()[MS_1];
|
|
||||||
let pin_2 = &mut shift_register.decompose()[MS_2];
|
|
||||||
let pin_3 = &mut shift_register.decompose()[MS_3];
|
|
||||||
if is_bit_set(0) {
|
|
||||||
pin_0.set_high()?;
|
|
||||||
} else {
|
|
||||||
pin_0.set_low()?;
|
|
||||||
}
|
|
||||||
if is_bit_set(1) {
|
|
||||||
pin_1.set_high()?;
|
|
||||||
} else {
|
|
||||||
pin_1.set_low()?;
|
|
||||||
}
|
|
||||||
if is_bit_set(2) {
|
|
||||||
pin_2.set_high()?;
|
|
||||||
} else {
|
|
||||||
pin_2.set_low()?;
|
|
||||||
}
|
|
||||||
if is_bit_set(3) {
|
|
||||||
pin_3.set_high()?;
|
|
||||||
} else {
|
|
||||||
pin_3.set_low()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
shift_register.decompose()[MS_4].set_low()?;
|
|
||||||
shift_register.decompose()[SENSOR_ON].set_high()?;
|
|
||||||
}
|
|
||||||
let measurement = 100; //how long to measure and then extrapolate to hz
|
|
||||||
let factor = 1000f32 / measurement as f32; //scale raw cound by this number to get hz
|
|
||||||
|
|
||||||
//give some time to stabilize
|
|
||||||
Timer::after_millis(10).await;
|
|
||||||
self.signal_counter.resume();
|
|
||||||
Timer::after_millis(measurement).await;
|
|
||||||
self.signal_counter.pause();
|
|
||||||
{
|
|
||||||
let shift_register = self.shift_register.lock().await;
|
|
||||||
shift_register.decompose()[MS_4].set_high()?;
|
|
||||||
shift_register.decompose()[SENSOR_ON].set_low()?;
|
|
||||||
}
|
|
||||||
Timer::after_millis(10).await;
|
|
||||||
let unscaled = self.signal_counter.value();
|
|
||||||
let hz = unscaled as f32 * factor;
|
|
||||||
LOG_ACCESS
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.log(
|
|
||||||
LogMessage::RawMeasure,
|
|
||||||
unscaled as u32,
|
|
||||||
hz as u32,
|
|
||||||
&plant.to_string(),
|
|
||||||
&format!("{sensor:?}"),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
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];
|
|
||||||
Ok(median)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl<'a> BoardInteraction<'a> for V3<'a> {
|
|
||||||
fn get_tank_sensor(&mut self) -> Result<&mut TankSensor<'a>, FatError> {
|
|
||||||
Ok(&mut self.tank_sensor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_esp(&mut self) -> &mut Esp<'a> {
|
|
||||||
&mut self.esp
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_config(&mut self) -> &PlantControllerConfig {
|
|
||||||
&self.config
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send> {
|
|
||||||
&mut self.battery_monitor
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_rtc_module(&mut self) -> &mut Box<dyn RTCModuleInteraction + Send> {
|
|
||||||
&mut self.rtc_module
|
|
||||||
}
|
|
||||||
async fn set_charge_indicator(&mut self, charging: bool) -> Result<(), FatError> {
|
|
||||||
let shift_register = self.shift_register.lock().await;
|
|
||||||
if charging {
|
|
||||||
let _ = shift_register.decompose()[CHARGING].set_high();
|
|
||||||
} else {
|
|
||||||
let _ = shift_register.decompose()[CHARGING].set_low();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
|
|
||||||
let _ = self.shift_register.lock().await.decompose()[AWAKE].set_low();
|
|
||||||
let guard = TIME_ACCESS.get().await.lock().await;
|
|
||||||
self.esp.deep_sleep(duration_in_ms, guard)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_day(&self) -> bool {
|
|
||||||
self.solar_is_day.is_high()
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn light(&mut self, enable: bool) -> Result<(), FatError> {
|
|
||||||
hold_disable(10);
|
|
||||||
if enable {
|
|
||||||
self.light.set_high();
|
|
||||||
} else {
|
|
||||||
self.light.set_low();
|
|
||||||
}
|
|
||||||
hold_enable(10);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
async fn pump(&mut self, plant: usize, enable: bool) -> Result<(), FatError> {
|
|
||||||
if enable {
|
|
||||||
self.main_pump.set_high();
|
|
||||||
}
|
|
||||||
|
|
||||||
let index = match plant {
|
|
||||||
0 => PUMP1_BIT,
|
|
||||||
1 => PUMP2_BIT,
|
|
||||||
2 => PUMP3_BIT,
|
|
||||||
3 => PUMP4_BIT,
|
|
||||||
4 => PUMP5_BIT,
|
|
||||||
5 => PUMP6_BIT,
|
|
||||||
6 => PUMP7_BIT,
|
|
||||||
7 => PUMP8_BIT,
|
|
||||||
_ => bail!("Invalid pump {plant}"),
|
|
||||||
};
|
|
||||||
let shift_register = self.shift_register.lock().await;
|
|
||||||
if enable {
|
|
||||||
let _ = shift_register.decompose()[index].set_high();
|
|
||||||
} else {
|
|
||||||
let _ = shift_register.decompose()[index].set_low();
|
|
||||||
}
|
|
||||||
|
|
||||||
if !enable {
|
|
||||||
self.main_pump.set_low();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn pump_current(&mut self, _plant: usize) -> Result<Current, FatError> {
|
|
||||||
bail!("Not implemented in v3")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn fault(&mut self, plant: usize, enable: bool) -> Result<(), FatError> {
|
|
||||||
let index = match plant {
|
|
||||||
0 => FAULT_1,
|
|
||||||
1 => FAULT_2,
|
|
||||||
2 => FAULT_3,
|
|
||||||
3 => FAULT_4,
|
|
||||||
4 => FAULT_5,
|
|
||||||
5 => FAULT_6,
|
|
||||||
6 => FAULT_7,
|
|
||||||
7 => FAULT_8,
|
|
||||||
_ => panic!("Invalid plant id {}", plant),
|
|
||||||
};
|
|
||||||
let shift_register = self.shift_register.lock().await;
|
|
||||||
if enable {
|
|
||||||
let _ = shift_register.decompose()[index].set_high();
|
|
||||||
} else {
|
|
||||||
let _ = shift_register.decompose()[index].set_low();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn measure_moisture_hz(&mut self) -> Result<Moistures, FatError> {
|
|
||||||
let mut result = Moistures::default();
|
|
||||||
for plant in 0..PLANT_COUNT {
|
|
||||||
let a = self.inner_measure_moisture_hz(plant, Sensor::A).await;
|
|
||||||
let b = self.inner_measure_moisture_hz(plant, Sensor::B).await;
|
|
||||||
let aa = a.unwrap_or_else(|_| u32::MAX as f32);
|
|
||||||
let bb = b.unwrap_or_else(|_| u32::MAX as f32);
|
|
||||||
LOG_ACCESS
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.log(LogMessage::TestSensor, aa as u32, bb as u32, &plant.to_string(), "")
|
|
||||||
.await;
|
|
||||||
result.sensor_a_hz[plant] = aa;
|
|
||||||
result.sensor_b_hz[plant] = bb;
|
|
||||||
}
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async fn general_fault(&mut self, enable: bool) {
|
|
||||||
hold_disable(6);
|
|
||||||
if enable {
|
|
||||||
self.general_fault.set_high();
|
|
||||||
} else {
|
|
||||||
self.general_fault.set_low();
|
|
||||||
}
|
|
||||||
hold_enable(6);
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn test(&mut self) -> Result<(), FatError> {
|
|
||||||
self.general_fault(true).await;
|
|
||||||
Timer::after_millis(100).await;
|
|
||||||
self.general_fault(false).await;
|
|
||||||
Timer::after_millis(100).await;
|
|
||||||
self.light(true).await?;
|
|
||||||
Timer::after_millis(500).await;
|
|
||||||
|
|
||||||
self.light(false).await?;
|
|
||||||
Timer::after_millis(500).await;
|
|
||||||
for i in 0..PLANT_COUNT {
|
|
||||||
self.fault(i, true).await?;
|
|
||||||
Timer::after_millis(500).await;
|
|
||||||
self.fault(i, false).await?;
|
|
||||||
Timer::after_millis(500).await;
|
|
||||||
}
|
|
||||||
for i in 0..PLANT_COUNT {
|
|
||||||
self.pump(i, true).await?;
|
|
||||||
Timer::after_millis(100).await;
|
|
||||||
self.pump(i, false).await?;
|
|
||||||
Timer::after_millis(100).await;
|
|
||||||
}
|
|
||||||
self.measure_moisture_hz().await?;
|
|
||||||
Timer::after_millis(10).await;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_config(&mut self, config: PlantControllerConfig) {
|
|
||||||
self.config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_mptt_voltage(&mut self) -> Result<Voltage, FatError> {
|
|
||||||
bail!("Not implemented in v3")
|
|
||||||
}
|
|
||||||
async fn get_mptt_current(&mut self) -> Result<Current, FatError> {
|
|
||||||
bail!("Not implemented in v3")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,310 +0,0 @@
|
|||||||
use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET};
|
|
||||||
use crate::bail;
|
|
||||||
use crate::fat_error::{ContextExt, FatError, FatResult};
|
|
||||||
use canapi::{SensorSlot};
|
|
||||||
use crate::hal::{DetectionResult, Moistures, Sensor};
|
|
||||||
use crate::hal::Box;
|
|
||||||
use crate::log::{LogMessage, LOG_ACCESS};
|
|
||||||
use alloc::format;
|
|
||||||
use alloc::string::ToString;
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use bincode::config;
|
|
||||||
use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice;
|
|
||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
|
||||||
use embassy_time::{Duration, Instant, Timer, WithTimeout};
|
|
||||||
use embedded_can::{Frame, Id};
|
|
||||||
use esp_hal::gpio::Output;
|
|
||||||
use esp_hal::i2c::master::I2c;
|
|
||||||
use esp_hal::pcnt::unit::Unit;
|
|
||||||
use esp_hal::twai::{EspTwaiFrame, StandardId, Twai, TwaiConfiguration};
|
|
||||||
use esp_hal::{Async, Blocking};
|
|
||||||
use log::{error, info, warn};
|
|
||||||
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
|
|
||||||
|
|
||||||
const REPEAT_MOIST_MEASURE: usize = 10;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
pub trait SensorInteraction {
|
|
||||||
async fn measure_moisture_hz(&mut self) -> FatResult<Moistures>;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
PulseCounter {
|
|
||||||
signal_counter: Unit<'static, 0>,
|
|
||||||
sensor_expander:
|
|
||||||
Pca9535Immediate<I2cDevice<'static, CriticalSectionRawMutex, I2c<'static, Blocking>>>,
|
|
||||||
},
|
|
||||||
CanBus {
|
|
||||||
twai_config: Option<TwaiConfiguration<'static, Blocking>>,
|
|
||||||
can_power: Output<'static>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl SensorInteraction for SensorImpl {
|
|
||||||
async fn measure_moisture_hz(&mut self) -> FatResult<Moistures> {
|
|
||||||
match self {
|
|
||||||
SensorImpl::PulseCounter {
|
|
||||||
signal_counter,
|
|
||||||
sensor_expander,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let mut result = Moistures::default();
|
|
||||||
for plant in 0..crate::hal::PLANT_COUNT{
|
|
||||||
result.sensor_a_hz[plant] = Self::inner_pulse(plant, Sensor::A, signal_counter, sensor_expander).await?;
|
|
||||||
info!("Sensor {} {:?}: {}", plant, Sensor::A, result.sensor_a_hz[plant]);
|
|
||||||
result.sensor_b_hz[plant] = Self::inner_pulse(plant, Sensor::B, signal_counter, sensor_expander).await?;
|
|
||||||
info!("Sensor {} {:?}: {}", plant, Sensor::B, result.sensor_b_hz[plant]);
|
|
||||||
}
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
SensorImpl::CanBus {
|
|
||||||
twai_config,
|
|
||||||
can_power,
|
|
||||||
} => {
|
|
||||||
can_power.set_high();
|
|
||||||
let config = twai_config.take().expect("twai config not set");
|
|
||||||
let mut twai = config.start();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let rec = twai.receive();
|
|
||||||
match rec {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(err) => {
|
|
||||||
info!("Error receiving CAN message: {:?}", err);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer::after_millis(10).await;
|
|
||||||
let can = Self::inner_can(&mut twai).await;
|
|
||||||
|
|
||||||
can_power.set_low();
|
|
||||||
|
|
||||||
let config = twai.stop();
|
|
||||||
twai_config.replace(config);
|
|
||||||
|
|
||||||
let value = can?;
|
|
||||||
Ok(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl SensorImpl {
|
|
||||||
pub async fn autodetect(&mut self) -> FatResult<DetectionResult> {
|
|
||||||
match self {
|
|
||||||
SensorImpl::PulseCounter { .. } => {
|
|
||||||
bail!("Only CAN bus implementation supports autodetection")
|
|
||||||
}
|
|
||||||
SensorImpl::CanBus {
|
|
||||||
twai_config,
|
|
||||||
can_power,
|
|
||||||
} => {
|
|
||||||
// Power on CAN transceiver and start controller
|
|
||||||
can_power.set_high();
|
|
||||||
let config = twai_config.take().expect("twai config not set");
|
|
||||||
let mut as_async = config.into_async().start();
|
|
||||||
// Give CAN some time to stabilize
|
|
||||||
Timer::after_millis(10).await;
|
|
||||||
|
|
||||||
// Send a few test messages per potential sensor node
|
|
||||||
for plant in 0..crate::hal::PLANT_COUNT {
|
|
||||||
for sensor in [Sensor::A, Sensor::B] {
|
|
||||||
let target = StandardId::new(plant_id(IDENTIFY_CMD_OFFSET, sensor.into(), plant as u16)).context(">> Could not create address for sensor! (plant: {}) <<")?;
|
|
||||||
let can_buffer = [0_u8; 0];
|
|
||||||
if let Some(frame) = EspTwaiFrame::new(target, &can_buffer) {
|
|
||||||
// Try a few times; we intentionally ignore rx here and rely on stub logic
|
|
||||||
let resu = as_async.transmit_async(&frame).await;
|
|
||||||
match resu {
|
|
||||||
Ok(_) => {
|
|
||||||
info!(
|
|
||||||
"Sent test message to plant {} sensor {:?}",
|
|
||||||
plant, sensor
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
info!("Error sending test message to plant {} sensor {:?}: {:?}", plant, sensor, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
info!("Error building CAN frame");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut result = DetectionResult::default();
|
|
||||||
// Wait for messages to arrive
|
|
||||||
let _ = Self::wait_for_can_measurements(&mut as_async, &mut result).with_timeout(Duration::from_millis(5000)).await;
|
|
||||||
|
|
||||||
let config = as_async.stop().into_blocking();
|
|
||||||
can_power.set_low();
|
|
||||||
twai_config.replace(config);
|
|
||||||
|
|
||||||
info!("Autodetection result: {:?}", result);
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn wait_for_can_measurements(as_async: &mut Twai<'_, Async>, result: &mut DetectionResult) {
|
|
||||||
loop {
|
|
||||||
match as_async.receive_async().await {
|
|
||||||
Ok(can_frame) => {
|
|
||||||
match can_frame.id() {
|
|
||||||
Id::Standard(id) => {
|
|
||||||
info!("Received CAN message: {:?}", id);
|
|
||||||
let rawid = id.as_raw();
|
|
||||||
match classify(rawid) {
|
|
||||||
None => {}
|
|
||||||
Some(msg) => {
|
|
||||||
info!("received message of kind {:?} (plant: {}, sensor: {:?})", msg.0, msg.1, msg.2);
|
|
||||||
if msg.0 == MessageKind::MoistureData {
|
|
||||||
let plant = msg.1 as usize;
|
|
||||||
let sensor = msg.2;
|
|
||||||
match sensor {
|
|
||||||
SensorSlot::A => {
|
|
||||||
result.plant[plant].sensor_a = true;
|
|
||||||
}
|
|
||||||
SensorSlot::B => {
|
|
||||||
result.plant[plant].sensor_b = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Id::Extended(ext) => {
|
|
||||||
warn!("Received extended ID: {:?}", ext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!("Error receiving CAN message: {:?}", err);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn inner_pulse(plant: usize, sensor: Sensor, signal_counter: &mut Unit<'_, 0>, sensor_expander: &mut Pca9535Immediate<I2cDevice<'static, CriticalSectionRawMutex, I2c<'static, Blocking>>>) -> FatResult<f32> {
|
|
||||||
|
|
||||||
let mut results = [0_f32; REPEAT_MOIST_MEASURE];
|
|
||||||
for repeat in 0..REPEAT_MOIST_MEASURE {
|
|
||||||
signal_counter.pause();
|
|
||||||
signal_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 measurement = 100; // TODO what is this scaling factor? what is its purpose?
|
|
||||||
let factor = 1000f32 / measurement as f32;
|
|
||||||
|
|
||||||
//give some time to stabilize
|
|
||||||
Timer::after_millis(10).await;
|
|
||||||
signal_counter.resume();
|
|
||||||
Timer::after_millis(measurement).await;
|
|
||||||
signal_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)?;
|
|
||||||
Timer::after_millis(10).await;
|
|
||||||
let unscaled = 1337; //signal_counter.get_counter_value()? as i32;
|
|
||||||
let hz = unscaled as f32 * factor;
|
|
||||||
LOG_ACCESS
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.log(
|
|
||||||
LogMessage::RawMeasure,
|
|
||||||
unscaled as u32,
|
|
||||||
hz as u32,
|
|
||||||
&plant.to_string(),
|
|
||||||
&format!("{sensor:?}"),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
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];
|
|
||||||
Ok(median)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn inner_can(
|
|
||||||
twai: &mut Twai<'static, Blocking>,
|
|
||||||
) -> FatResult<Moistures> {
|
|
||||||
[0_u8; 8];
|
|
||||||
config::standard();
|
|
||||||
|
|
||||||
let timeout = Instant::now()
|
|
||||||
.checked_add(embassy_time::Duration::from_millis(100))
|
|
||||||
.context("Timeout")?;
|
|
||||||
loop {
|
|
||||||
let answer = twai.receive();
|
|
||||||
match answer {
|
|
||||||
Ok(answer) => {
|
|
||||||
info!("Received CAN message: {:?}", answer);
|
|
||||||
}
|
|
||||||
Err(error) => match error {
|
|
||||||
nb::Error::Other(error) => {
|
|
||||||
return Err(FatError::CanBusError { error });
|
|
||||||
}
|
|
||||||
nb::Error::WouldBlock => {
|
|
||||||
if Instant::now() > timeout {
|
|
||||||
bail!("Timeout waiting for CAN answer");
|
|
||||||
}
|
|
||||||
Timer::after_millis(10).await;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
use crate::fat_error::{FatError, FatResult};
|
|
||||||
use crate::hal::rtc::X25;
|
|
||||||
use crate::BOARD_ACCESS;
|
|
||||||
use alloc::borrow::ToOwned;
|
|
||||||
use alloc::format;
|
|
||||||
use alloc::string::{String, ToString};
|
|
||||||
use chrono::DateTime;
|
|
||||||
use edge_http::io::server::Connection;
|
|
||||||
use embedded_io_async::{Read, Write};
|
|
||||||
use log::info;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
|
||||||
pub struct WebBackupHeader {
|
|
||||||
timestamp: String,
|
|
||||||
size: u16,
|
|
||||||
}
|
|
||||||
pub(crate) async fn get_backup_config<T, const N: usize>(
|
|
||||||
conn: &mut Connection<'_, T, { N }>,
|
|
||||||
) -> FatResult<Option<u32>>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
// First pass: verify checksum without sending data
|
|
||||||
let mut checksum = X25.digest();
|
|
||||||
let mut chunk = 0_usize;
|
|
||||||
loop {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
board.board_hal.progress(chunk as u32).await;
|
|
||||||
let (buf, len, expected_crc) = board
|
|
||||||
.board_hal
|
|
||||||
.get_rtc_module()
|
|
||||||
.get_backup_config(chunk)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Update checksum with the actual data bytes of this chunk
|
|
||||||
checksum.update(&buf[..len]);
|
|
||||||
|
|
||||||
let is_last = len == 0 || len < buf.len();
|
|
||||||
if is_last {
|
|
||||||
let actual_crc = checksum.finalize();
|
|
||||||
if actual_crc != expected_crc {
|
|
||||||
BOARD_ACCESS
|
|
||||||
.get()
|
|
||||||
.await
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.board_hal
|
|
||||||
.clear_progress()
|
|
||||||
.await;
|
|
||||||
conn.initiate_response(
|
|
||||||
409,
|
|
||||||
Some(
|
|
||||||
format!(
|
|
||||||
"Checksum mismatch expected {} got {}",
|
|
||||||
expected_crc, actual_crc
|
|
||||||
)
|
|
||||||
.as_str(),
|
|
||||||
),
|
|
||||||
&[],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
return Ok(Some(409));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
chunk += 1;
|
|
||||||
}
|
|
||||||
// Second pass: stream data
|
|
||||||
conn.initiate_response(
|
|
||||||
200,
|
|
||||||
Some("OK"),
|
|
||||||
&[
|
|
||||||
("Access-Control-Allow-Origin", "*"),
|
|
||||||
("Access-Control-Allow-Headers", "*"),
|
|
||||||
("Access-Control-Allow-Methods", "*"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let mut chunk = 0_usize;
|
|
||||||
loop {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
board.board_hal.progress(chunk as u32).await;
|
|
||||||
let (buf, len, _expected_crc) = board
|
|
||||||
.board_hal
|
|
||||||
.get_rtc_module()
|
|
||||||
.get_backup_config(chunk)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if len == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
conn.write_all(&buf[..len]).await?;
|
|
||||||
|
|
||||||
if len < buf.len() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
chunk += 1;
|
|
||||||
}
|
|
||||||
BOARD_ACCESS
|
|
||||||
.get()
|
|
||||||
.await
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.board_hal
|
|
||||||
.clear_progress()
|
|
||||||
.await;
|
|
||||||
Ok(Some(200))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn backup_config<T, const N: usize>(
|
|
||||||
conn: &mut Connection<'_, T, N>,
|
|
||||||
) -> FatResult<Option<String>>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
let mut offset = 0_usize;
|
|
||||||
let mut buf = [0_u8; 32];
|
|
||||||
|
|
||||||
let mut checksum = X25.digest();
|
|
||||||
|
|
||||||
let mut counter = 0;
|
|
||||||
loop {
|
|
||||||
let to_write = conn.read(&mut buf).await?;
|
|
||||||
if to_write == 0 {
|
|
||||||
info!("backup finished");
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
board.board_hal.progress(counter).await;
|
|
||||||
|
|
||||||
counter = counter + 1;
|
|
||||||
board
|
|
||||||
.board_hal
|
|
||||||
.get_rtc_module()
|
|
||||||
.backup_config(offset, &buf[0..to_write])
|
|
||||||
.await?;
|
|
||||||
checksum.update(&buf[0..to_write]);
|
|
||||||
}
|
|
||||||
offset = offset + to_write;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
board
|
|
||||||
.board_hal
|
|
||||||
.get_rtc_module()
|
|
||||||
.backup_config_finalize(checksum.finalize(), offset)
|
|
||||||
.await?;
|
|
||||||
board.board_hal.clear_progress().await;
|
|
||||||
conn.initiate_response(
|
|
||||||
200,
|
|
||||||
Some("OK"),
|
|
||||||
&[
|
|
||||||
("Access-Control-Allow-Origin", "*"),
|
|
||||||
("Access-Control-Allow-Headers", "*"),
|
|
||||||
("Access-Control-Allow-Methods", "*"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(Some("saved".to_owned()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn backup_info<T, const N: usize>(
|
|
||||||
_request: &mut Connection<'_, T, N>,
|
|
||||||
) -> Result<Option<String>, FatError>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
let header = board.board_hal.get_rtc_module().get_backup_info().await;
|
|
||||||
let json = match header {
|
|
||||||
Ok(h) => {
|
|
||||||
let timestamp = DateTime::from_timestamp_millis(h.timestamp).unwrap();
|
|
||||||
let wbh = WebBackupHeader {
|
|
||||||
timestamp: timestamp.to_rfc3339(),
|
|
||||||
size: h.size,
|
|
||||||
};
|
|
||||||
serde_json::to_string(&wbh)?
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
let wbh = WebBackupHeader {
|
|
||||||
timestamp: err.to_string(),
|
|
||||||
size: 0,
|
|
||||||
};
|
|
||||||
serde_json::to_string(&wbh)?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(Some(json))
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
use crate::fat_error::{FatError, FatResult};
|
|
||||||
use crate::webserver::read_up_to_bytes_from_request;
|
|
||||||
use crate::BOARD_ACCESS;
|
|
||||||
use alloc::borrow::ToOwned;
|
|
||||||
use alloc::format;
|
|
||||||
use alloc::string::String;
|
|
||||||
use edge_http::io::server::Connection;
|
|
||||||
use edge_http::Method;
|
|
||||||
use embedded_io_async::{Read, Write};
|
|
||||||
use log::info;
|
|
||||||
|
|
||||||
pub(crate) async fn list_files<T, const N: usize>(
|
|
||||||
_request: &mut Connection<'_, T, N>,
|
|
||||||
) -> FatResult<Option<String>> {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
let result = board.board_hal.get_esp().list_files().await?;
|
|
||||||
let file_list_json = serde_json::to_string(&result)?;
|
|
||||||
Ok(Some(file_list_json))
|
|
||||||
}
|
|
||||||
pub(crate) async fn file_operations<T, const N: usize>(
|
|
||||||
conn: &mut Connection<'_, T, { N }>,
|
|
||||||
method: Method,
|
|
||||||
path: &&str,
|
|
||||||
prefix: &&str,
|
|
||||||
) -> Result<Option<u32>, FatError>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
let filename = &path[prefix.len()..];
|
|
||||||
info!("file request for {} with method {}", filename, method);
|
|
||||||
Ok(match method {
|
|
||||||
Method::Delete => {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
board
|
|
||||||
.board_hal
|
|
||||||
.get_esp()
|
|
||||||
.delete_file(filename.to_owned())
|
|
||||||
.await?;
|
|
||||||
conn.initiate_response(
|
|
||||||
200,
|
|
||||||
Some("OK"),
|
|
||||||
&[
|
|
||||||
("Access-Control-Allow-Origin", "*"),
|
|
||||||
("Access-Control-Allow-Headers", "*"),
|
|
||||||
("Access-Control-Allow-Methods", "*"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Some(200)
|
|
||||||
}
|
|
||||||
Method::Get => {
|
|
||||||
let disposition = format!("attachment; filename=\"{filename}\"");
|
|
||||||
let size = {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
board
|
|
||||||
.board_hal
|
|
||||||
.get_esp()
|
|
||||||
.get_size(filename.to_owned())
|
|
||||||
.await?
|
|
||||||
};
|
|
||||||
|
|
||||||
conn.initiate_response(
|
|
||||||
200,
|
|
||||||
Some("OK"),
|
|
||||||
&[
|
|
||||||
("Content-Type", "application/octet-stream"),
|
|
||||||
("Content-Disposition", disposition.as_str()),
|
|
||||||
("Content-Length", &format!("{}", size)),
|
|
||||||
("Access-Control-Allow-Origin", "*"),
|
|
||||||
("Access-Control-Allow-Headers", "*"),
|
|
||||||
("Access-Control-Allow-Methods", "*"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let mut chunk = 0;
|
|
||||||
loop {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
board.board_hal.progress(chunk).await;
|
|
||||||
let read_chunk = board
|
|
||||||
.board_hal
|
|
||||||
.get_esp()
|
|
||||||
.get_file(filename.to_owned(), chunk)
|
|
||||||
.await?;
|
|
||||||
let length = read_chunk.1;
|
|
||||||
if length == 0 {
|
|
||||||
info!("file request for {} finished", filename);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let data = &read_chunk.0[0..length];
|
|
||||||
conn.write_all(data).await?;
|
|
||||||
if length < read_chunk.0.len() {
|
|
||||||
info!("file request for {} finished", filename);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
chunk = chunk + 1;
|
|
||||||
}
|
|
||||||
BOARD_ACCESS
|
|
||||||
.get()
|
|
||||||
.await
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.board_hal
|
|
||||||
.clear_progress()
|
|
||||||
.await;
|
|
||||||
Some(200)
|
|
||||||
}
|
|
||||||
Method::Post => {
|
|
||||||
{
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
//ensure the file is deleted first; otherwise we would need to truncate the file which will not work with streaming
|
|
||||||
let _ = board
|
|
||||||
.board_hal
|
|
||||||
.get_esp()
|
|
||||||
.delete_file(filename.to_owned())
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut offset = 0_usize;
|
|
||||||
let mut chunk = 0;
|
|
||||||
loop {
|
|
||||||
let buf = read_up_to_bytes_from_request(conn, Some(4096)).await?;
|
|
||||||
if buf.len() == 0 {
|
|
||||||
info!("file request for {} finished", filename);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
board.board_hal.progress(chunk as u32).await;
|
|
||||||
board
|
|
||||||
.board_hal
|
|
||||||
.get_esp()
|
|
||||||
.write_file(filename.to_owned(), offset as u32, &buf)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
offset = offset + buf.len();
|
|
||||||
chunk = chunk + 1;
|
|
||||||
}
|
|
||||||
BOARD_ACCESS
|
|
||||||
.get()
|
|
||||||
.await
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.board_hal
|
|
||||||
.clear_progress()
|
|
||||||
.await;
|
|
||||||
conn.initiate_response(
|
|
||||||
200,
|
|
||||||
Some("OK"),
|
|
||||||
&[
|
|
||||||
("Access-Control-Allow-Origin", "*"),
|
|
||||||
("Access-Control-Allow-Headers", "*"),
|
|
||||||
("Access-Control-Allow-Methods", "*"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Some(200)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,186 +0,0 @@
|
|||||||
use core::str::FromStr;
|
|
||||||
use crate::fat_error::{FatError, FatResult};
|
|
||||||
use crate::hal::{esp_time, PLANT_COUNT};
|
|
||||||
use crate::log::LogMessage;
|
|
||||||
use crate::plant_state::{MoistureSensorState, PlantState};
|
|
||||||
use crate::tank::determine_tank_state;
|
|
||||||
use crate::{get_version, BOARD_ACCESS};
|
|
||||||
use alloc::format;
|
|
||||||
use alloc::string::{String, ToString};
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use chrono_tz::Tz;
|
|
||||||
use edge_http::io::server::Connection;
|
|
||||||
use embedded_io_async::{Read, Write};
|
|
||||||
use log::info;
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
struct LoadData<'a> {
|
|
||||||
rtc: &'a str,
|
|
||||||
native: &'a str,
|
|
||||||
}
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
struct Moistures {
|
|
||||||
moisture_a: Vec<String>,
|
|
||||||
moisture_b: Vec<String>,
|
|
||||||
}
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
struct SolarState {
|
|
||||||
mppt_voltage: f32,
|
|
||||||
mppt_current: f32,
|
|
||||||
is_day: bool,
|
|
||||||
}
|
|
||||||
pub(crate) async fn get_live_moisture<T, const N: usize>(
|
|
||||||
_request: &mut Connection<'_, T, N>,
|
|
||||||
) -> FatResult<Option<String>>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
let moistures = board.board_hal.measure_moisture_hz().await?;
|
|
||||||
let mut plant_state = Vec::new();
|
|
||||||
for i in 0..PLANT_COUNT {
|
|
||||||
plant_state.push(PlantState::read_hardware_state(moistures, i, &mut board).await);
|
|
||||||
}
|
|
||||||
let a = Vec::from_iter(plant_state.iter().map(|s| match &s.sensor_a {
|
|
||||||
MoistureSensorState::Disabled => "disabled".to_string(),
|
|
||||||
MoistureSensorState::MoistureValue {
|
|
||||||
raw_hz,
|
|
||||||
moisture_percent,
|
|
||||||
} => {
|
|
||||||
format!("{moisture_percent:.2}% {raw_hz}hz",)
|
|
||||||
}
|
|
||||||
MoistureSensorState::SensorError(err) => format!("{err:?}"),
|
|
||||||
}));
|
|
||||||
let b = Vec::from_iter(plant_state.iter().map(|s| match &s.sensor_b {
|
|
||||||
MoistureSensorState::Disabled => "disabled".to_string(),
|
|
||||||
MoistureSensorState::MoistureValue {
|
|
||||||
raw_hz,
|
|
||||||
moisture_percent,
|
|
||||||
} => {
|
|
||||||
format!("{moisture_percent:.2}% {raw_hz}hz",)
|
|
||||||
}
|
|
||||||
MoistureSensorState::SensorError(err) => format!("{err:?}"),
|
|
||||||
}));
|
|
||||||
|
|
||||||
let data = Moistures {
|
|
||||||
moisture_a: a,
|
|
||||||
moisture_b: b,
|
|
||||||
};
|
|
||||||
let json = serde_json::to_string(&data)?;
|
|
||||||
|
|
||||||
Ok(Some(json))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn tank_info<T, const N: usize>(
|
|
||||||
_request: &mut Connection<'_, T, N>,
|
|
||||||
) -> Result<Option<String>, FatError>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
let tank_state = determine_tank_state(&mut board).await;
|
|
||||||
//should be multisampled
|
|
||||||
let sensor = board.board_hal.get_tank_sensor()?;
|
|
||||||
|
|
||||||
let water_temp: FatResult<f32> = sensor.water_temperature_c().await;
|
|
||||||
Ok(Some(serde_json::to_string(&tank_state.as_mqtt_info(
|
|
||||||
&board.board_hal.get_config().tank,
|
|
||||||
&water_temp,
|
|
||||||
))?))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn get_timezones() -> FatResult<Option<String>> {
|
|
||||||
// Get all timezones compiled into the binary from chrono-tz
|
|
||||||
let timezones: Vec<&'static str> = chrono_tz::TZ_VARIANTS.iter().map(|tz| tz.name()).collect();
|
|
||||||
let json = serde_json::to_string(&timezones)?;
|
|
||||||
Ok(Some(json))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn get_solar_state<T, const N: usize>(
|
|
||||||
_request: &mut Connection<'_, T, N>,
|
|
||||||
) -> FatResult<Option<String>> {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
let state = SolarState {
|
|
||||||
mppt_voltage: board.board_hal.get_mptt_voltage().await?.as_millivolts() as f32,
|
|
||||||
mppt_current: board.board_hal.get_mptt_current().await?.as_milliamperes() as f32,
|
|
||||||
is_day: board.board_hal.is_day(),
|
|
||||||
};
|
|
||||||
Ok(Some(serde_json::to_string(&state)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn get_version_web<T, const N: usize>(
|
|
||||||
_request: &mut Connection<'_, T, N>,
|
|
||||||
) -> FatResult<Option<String>> {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
Ok(Some(serde_json::to_string(&get_version(&mut board).await)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn get_config<T, const N: usize>(
|
|
||||||
_request: &mut Connection<'_, T, N>,
|
|
||||||
) -> FatResult<Option<String>> {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
let json = serde_json::to_string(&board.board_hal.get_config())?;
|
|
||||||
Ok(Some(json))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn get_battery_state<T, const N: usize>(
|
|
||||||
_request: &mut Connection<'_, T, N>,
|
|
||||||
) -> FatResult<Option<String>> {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
let battery_state = board
|
|
||||||
.board_hal
|
|
||||||
.get_battery_monitor()
|
|
||||||
.get_battery_state()
|
|
||||||
.await?;
|
|
||||||
Ok(Some(serde_json::to_string(&battery_state)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn get_time<T, const N: usize>(
|
|
||||||
_request: &mut Connection<'_, T, N>,
|
|
||||||
) -> FatResult<Option<String>> {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
let conf = board.board_hal.get_config();
|
|
||||||
|
|
||||||
let tz:Tz = match conf.timezone.as_ref(){
|
|
||||||
None => {
|
|
||||||
Tz::UTC
|
|
||||||
}
|
|
||||||
Some(tz_string) => {
|
|
||||||
match Tz::from_str(tz_string) {
|
|
||||||
Ok(tz) => {
|
|
||||||
tz
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
info!("failed parsing timezone {}", err);
|
|
||||||
Tz::UTC
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let native = esp_time().await.with_timezone(&tz).to_rfc3339();
|
|
||||||
|
|
||||||
let rtc = match board.board_hal.get_rtc_module().get_rtc_time().await {
|
|
||||||
Ok(time) => time.with_timezone(&tz).to_rfc3339(),
|
|
||||||
Err(err) => {
|
|
||||||
format!("Error getting time: {}", err)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = LoadData {
|
|
||||||
rtc: rtc.as_str(),
|
|
||||||
native: native.as_str(),
|
|
||||||
};
|
|
||||||
let json = serde_json::to_string(&data)?;
|
|
||||||
|
|
||||||
Ok(Some(json))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn get_log_localization_config<T, const N: usize>(
|
|
||||||
_request: &mut Connection<'_, T, N>,
|
|
||||||
) -> FatResult<Option<String>> {
|
|
||||||
Ok(Some(serde_json::to_string(
|
|
||||||
&LogMessage::to_log_localisation_config(),
|
|
||||||
)?))
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
use crate::fat_error::FatResult;
|
|
||||||
use crate::log::LOG_ACCESS;
|
|
||||||
use edge_http::io::server::Connection;
|
|
||||||
use embedded_io_async::{Read, Write};
|
|
||||||
|
|
||||||
pub(crate) async fn get_log<T, const N: usize>(
|
|
||||||
conn: &mut Connection<'_, T, N>,
|
|
||||||
) -> FatResult<Option<u32>>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
let log = LOG_ACCESS.lock().await.get();
|
|
||||||
conn.initiate_response(
|
|
||||||
200,
|
|
||||||
Some("OK"),
|
|
||||||
&[
|
|
||||||
("Content-Type", "text/javascript"),
|
|
||||||
("Access-Control-Allow-Origin", "*"),
|
|
||||||
("Access-Control-Allow-Headers", "*"),
|
|
||||||
("Access-Control-Allow-Methods", "*"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
conn.write_all("[".as_bytes()).await?;
|
|
||||||
let mut append = false;
|
|
||||||
for entry in log {
|
|
||||||
if append {
|
|
||||||
conn.write_all(",".as_bytes()).await?;
|
|
||||||
}
|
|
||||||
append = true;
|
|
||||||
let json = serde_json::to_string(&entry)?;
|
|
||||||
conn.write_all(json.as_bytes()).await?;
|
|
||||||
}
|
|
||||||
conn.write_all("]".as_bytes()).await?;
|
|
||||||
Ok(Some(200))
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
use crate::fat_error::FatError;
|
|
||||||
use edge_http::io::server::Connection;
|
|
||||||
use embedded_io_async::{Read, Write};
|
|
||||||
|
|
||||||
pub(crate) async fn serve_favicon<T, const N: usize>(
|
|
||||||
conn: &mut Connection<'_, T, { N }>,
|
|
||||||
) -> Result<Option<u32>, FatError>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
conn.initiate_response(200, Some("OK"), &[("Content-Type", "image/x-icon")])
|
|
||||||
.await?;
|
|
||||||
conn.write_all(include_bytes!("favicon.ico")).await?;
|
|
||||||
Ok(Some(200))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn serve_index<T, const N: usize>(
|
|
||||||
conn: &mut Connection<'_, T, { N }>,
|
|
||||||
) -> Result<Option<u32>, FatError>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
conn.initiate_response(
|
|
||||||
200,
|
|
||||||
Some("OK"),
|
|
||||||
&[("Content-Type", "text/html"), ("Content-Encoding", "gzip")],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
conn.write_all(include_bytes!("index.html.gz")).await?;
|
|
||||||
Ok(Some(200))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn serve_bundle<T, const N: usize>(
|
|
||||||
conn: &mut Connection<'_, T, { N }>,
|
|
||||||
) -> Result<Option<u32>, FatError>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
conn.initiate_response(
|
|
||||||
200,
|
|
||||||
Some("OK"),
|
|
||||||
&[
|
|
||||||
("Content-Type", "text/javascript"),
|
|
||||||
("Content-Encoding", "gzip"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
conn.write_all(include_bytes!("bundle.js.gz")).await?;
|
|
||||||
Ok(Some(200))
|
|
||||||
}
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
//offer ota and config mode
|
|
||||||
|
|
||||||
mod backup_manager;
|
|
||||||
mod file_manager;
|
|
||||||
mod get_json;
|
|
||||||
mod get_log;
|
|
||||||
mod get_static;
|
|
||||||
mod ota;
|
|
||||||
mod post_json;
|
|
||||||
|
|
||||||
use crate::fat_error::{FatError, FatResult};
|
|
||||||
use crate::webserver::backup_manager::{backup_config, backup_info, get_backup_config};
|
|
||||||
use crate::webserver::file_manager::{file_operations, list_files};
|
|
||||||
use crate::webserver::get_json::{
|
|
||||||
get_battery_state, get_config, get_live_moisture, get_log_localization_config, get_solar_state,
|
|
||||||
get_time, get_timezones, get_version_web, tank_info,
|
|
||||||
};
|
|
||||||
use crate::webserver::get_log::get_log;
|
|
||||||
use crate::webserver::get_static::{serve_bundle, serve_favicon, serve_index};
|
|
||||||
use crate::webserver::ota::ota_operations;
|
|
||||||
use crate::webserver::post_json::{
|
|
||||||
board_test, night_lamp_test, pump_test, set_config, wifi_scan, write_time, detect_sensors,
|
|
||||||
};
|
|
||||||
use crate::{bail, BOARD_ACCESS};
|
|
||||||
use alloc::borrow::ToOwned;
|
|
||||||
use alloc::string::{String, ToString};
|
|
||||||
use alloc::sync::Arc;
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use core::fmt::{Debug, Display};
|
|
||||||
use core::net::{IpAddr, Ipv4Addr, SocketAddr};
|
|
||||||
use core::result::Result::Ok;
|
|
||||||
use core::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use edge_http::io::server::{Connection, Handler, Server};
|
|
||||||
use edge_http::Method;
|
|
||||||
use edge_nal::TcpBind;
|
|
||||||
use edge_nal_embassy::{Tcp, TcpBuffers};
|
|
||||||
use embassy_net::Stack;
|
|
||||||
use embassy_time::Instant;
|
|
||||||
use embedded_io_async::{Read, Write};
|
|
||||||
use log::{error, info};
|
|
||||||
|
|
||||||
struct HTTPRequestRouter {
|
|
||||||
reboot_now: Arc<AtomicBool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handler for HTTPRequestRouter {
|
|
||||||
type Error<E: Debug> = FatError;
|
|
||||||
async fn handle<'a, T, const N: usize>(
|
|
||||||
&self,
|
|
||||||
_task_id: impl Display + Copy,
|
|
||||||
conn: &mut Connection<'a, T, N>,
|
|
||||||
) -> Result<(), FatError>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
let start = Instant::now();
|
|
||||||
let headers = conn.headers()?;
|
|
||||||
|
|
||||||
let method = headers.method;
|
|
||||||
let path = headers.path;
|
|
||||||
|
|
||||||
let prefix = "/file?filename=";
|
|
||||||
let status = if path.starts_with(prefix) {
|
|
||||||
file_operations(conn, method, &path, &prefix).await?
|
|
||||||
} else if path == "/ota" {
|
|
||||||
ota_operations(conn, method).await.map_err(|e| {
|
|
||||||
error!("Error handling ota: {}", e);
|
|
||||||
e
|
|
||||||
})?
|
|
||||||
} else {
|
|
||||||
match method {
|
|
||||||
Method::Get => match path {
|
|
||||||
"/favicon.ico" => serve_favicon(conn).await?,
|
|
||||||
"/" => serve_index(conn).await?,
|
|
||||||
"/bundle.js" => serve_bundle(conn).await?,
|
|
||||||
"/log" => get_log(conn).await?,
|
|
||||||
"/get_backup_config" => get_backup_config(conn).await?,
|
|
||||||
&_ => {
|
|
||||||
let json = match path {
|
|
||||||
"/version" => Some(get_version_web(conn).await),
|
|
||||||
"/time" => Some(get_time(conn).await),
|
|
||||||
"/battery" => Some(get_battery_state(conn).await),
|
|
||||||
"/solar" => Some(get_solar_state(conn).await),
|
|
||||||
"/get_config" => Some(get_config(conn).await),
|
|
||||||
"/files" => Some(list_files(conn).await),
|
|
||||||
"/log_localization" => Some(get_log_localization_config(conn).await),
|
|
||||||
"/tank" => Some(tank_info(conn).await),
|
|
||||||
"/backup_info" => Some(backup_info(conn).await),
|
|
||||||
"/timezones" => Some(get_timezones().await),
|
|
||||||
"/moisture" => Some(get_live_moisture(conn).await),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
match json {
|
|
||||||
None => None,
|
|
||||||
Some(json) => Some(handle_json(conn, json).await?),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Method::Post => {
|
|
||||||
let json = match path {
|
|
||||||
"/wifiscan" => Some(wifi_scan(conn).await),
|
|
||||||
"/set_config" => Some(set_config(conn).await),
|
|
||||||
"/time" => Some(write_time(conn).await),
|
|
||||||
"/backup_config" => Some(backup_config(conn).await),
|
|
||||||
"/pumptest" => Some(pump_test(conn).await),
|
|
||||||
"/lamptest" => Some(night_lamp_test(conn).await),
|
|
||||||
"/boardtest" => Some(board_test().await),
|
|
||||||
"/detect_sensors" => Some(detect_sensors().await),
|
|
||||||
"/reboot" => {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
board.board_hal.get_esp().set_restart_to_conf(true);
|
|
||||||
self.reboot_now.store(true, Ordering::Relaxed);
|
|
||||||
Some(Ok(None))
|
|
||||||
}
|
|
||||||
"/exit" => {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
board.board_hal.get_esp().set_restart_to_conf(false);
|
|
||||||
self.reboot_now.store(true, Ordering::Relaxed);
|
|
||||||
Some(Ok(None))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
match json {
|
|
||||||
None => None,
|
|
||||||
Some(json) => Some(handle_json(conn, json).await?),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Method::Options | Method::Delete | Method::Head | Method::Put => None,
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let code = match status {
|
|
||||||
None => {
|
|
||||||
conn.initiate_response(404, Some("Not found"), &[]).await?;
|
|
||||||
404
|
|
||||||
}
|
|
||||||
Some(code) => code,
|
|
||||||
};
|
|
||||||
|
|
||||||
conn.complete().await?;
|
|
||||||
let response_time = Instant::now().duration_since(start).as_millis();
|
|
||||||
|
|
||||||
info!("\"{method} {path}\" {code} {response_time}ms");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn read_up_to_bytes_from_request<T, const N: usize>(
|
|
||||||
request: &mut Connection<'_, T, N>,
|
|
||||||
limit: Option<usize>,
|
|
||||||
) -> FatResult<Vec<u8>>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
let max_read = limit.unwrap_or(1024);
|
|
||||||
let mut data_store = Vec::new();
|
|
||||||
let mut total_read = 0;
|
|
||||||
loop {
|
|
||||||
let left = max_read - total_read;
|
|
||||||
let mut buf = [0_u8; 64];
|
|
||||||
let s_buf = if buf.len() <= left {
|
|
||||||
&mut buf
|
|
||||||
} else {
|
|
||||||
&mut buf[0..left]
|
|
||||||
};
|
|
||||||
let read = request.read(s_buf).await?;
|
|
||||||
if read == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let actual_data = &s_buf[0..read];
|
|
||||||
total_read += read;
|
|
||||||
if total_read > max_read {
|
|
||||||
bail!("Request too large {total_read} > {max_read}");
|
|
||||||
}
|
|
||||||
data_store.push(actual_data.to_owned());
|
|
||||||
}
|
|
||||||
let final_buffer = data_store.concat();
|
|
||||||
Ok(final_buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::task]
|
|
||||||
pub async fn http_server(reboot_now: Arc<AtomicBool>, stack: Stack<'static>) {
|
|
||||||
let buffer: TcpBuffers<2, 1024, 1024> = TcpBuffers::new();
|
|
||||||
let tcp = Tcp::new(stack, &buffer);
|
|
||||||
let acceptor = tcp
|
|
||||||
.bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 80))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut server: Server<2, 512, 15> = Server::new();
|
|
||||||
server
|
|
||||||
.run(Some(5000), acceptor, HTTPRequestRouter { reboot_now })
|
|
||||||
.await
|
|
||||||
.expect("Tcp stack error");
|
|
||||||
info!("Webserver started and waiting for connections");
|
|
||||||
|
|
||||||
//TODO https if mbed_esp lands
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_json<'a, T, const N: usize>(
|
|
||||||
conn: &mut Connection<'a, T, N>,
|
|
||||||
chain: FatResult<Option<String>>,
|
|
||||||
) -> FatResult<u32>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
<T as embedded_io_async::ErrorType>::Error: Debug,
|
|
||||||
{
|
|
||||||
match chain {
|
|
||||||
Ok(answer) => match answer {
|
|
||||||
Some(json) => {
|
|
||||||
conn.initiate_response(
|
|
||||||
200,
|
|
||||||
Some("OK"),
|
|
||||||
&[
|
|
||||||
("Access-Control-Allow-Origin", "*"),
|
|
||||||
("Access-Control-Allow-Headers", "*"),
|
|
||||||
("Access-Control-Allow-Methods", "*"),
|
|
||||||
("Content-Type", "application/json"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
conn.write_all(json.as_bytes()).await?;
|
|
||||||
Ok(200)
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
conn.initiate_response(
|
|
||||||
200,
|
|
||||||
Some("OK"),
|
|
||||||
&[
|
|
||||||
("Access-Control-Allow-Origin", "*"),
|
|
||||||
("Access-Control-Allow-Headers", "*"),
|
|
||||||
("Access-Control-Allow-Methods", "*"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(200)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
let error_text = err.to_string();
|
|
||||||
info!("error handling process {}", error_text);
|
|
||||||
conn.initiate_response(
|
|
||||||
500,
|
|
||||||
Some("OK"),
|
|
||||||
&[
|
|
||||||
("Access-Control-Allow-Origin", "*"),
|
|
||||||
("Access-Control-Allow-Headers", "*"),
|
|
||||||
("Access-Control-Allow-Methods", "*"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
conn.write_all(error_text.as_bytes()).await?;
|
|
||||||
Ok(500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
use crate::fat_error::FatError;
|
|
||||||
use crate::webserver::read_up_to_bytes_from_request;
|
|
||||||
use crate::BOARD_ACCESS;
|
|
||||||
use edge_http::io::server::Connection;
|
|
||||||
use edge_http::Method;
|
|
||||||
use embedded_io_async::{Read, Write};
|
|
||||||
use log::info;
|
|
||||||
|
|
||||||
pub(crate) async fn ota_operations<T, const N: usize>(
|
|
||||||
conn: &mut Connection<'_, T, { N }>,
|
|
||||||
method: Method,
|
|
||||||
) -> Result<Option<u32>, FatError>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
Ok(match method {
|
|
||||||
Method::Options => {
|
|
||||||
conn.initiate_response(
|
|
||||||
200,
|
|
||||||
Some("OK"),
|
|
||||||
&[
|
|
||||||
("Access-Control-Allow-Origin", "*"),
|
|
||||||
("Access-Control-Allow-Headers", "*"),
|
|
||||||
("Access-Control-Allow-Methods", "*"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Some(200)
|
|
||||||
}
|
|
||||||
Method::Post => {
|
|
||||||
let mut offset = 0_usize;
|
|
||||||
let mut chunk = 0;
|
|
||||||
loop {
|
|
||||||
let buf = read_up_to_bytes_from_request(conn, Some(4096)).await?;
|
|
||||||
if buf.len() == 0 {
|
|
||||||
info!("file request for ota finished");
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
board.board_hal.get_esp().finalize_ota().await?;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
board.board_hal.progress(chunk as u32).await;
|
|
||||||
// Erase next block if we are at a 4K boundary (including the first block at offset 0)
|
|
||||||
info!("erasing and writing block 0x{offset:x}");
|
|
||||||
board
|
|
||||||
.board_hal
|
|
||||||
.get_esp()
|
|
||||||
.write_ota(offset as u32, &*buf)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
offset = offset + buf.len();
|
|
||||||
chunk = chunk + 1;
|
|
||||||
}
|
|
||||||
BOARD_ACCESS
|
|
||||||
.get()
|
|
||||||
.await
|
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.board_hal
|
|
||||||
.clear_progress()
|
|
||||||
.await;
|
|
||||||
conn.initiate_response(
|
|
||||||
200,
|
|
||||||
Some("OK"),
|
|
||||||
&[
|
|
||||||
("Access-Control-Allow-Origin", "*"),
|
|
||||||
("Access-Control-Allow-Headers", "*"),
|
|
||||||
("Access-Control-Allow-Methods", "*"),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Some(200)
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
use crate::config::PlantControllerConfig;
|
|
||||||
use crate::fat_error::FatResult;
|
|
||||||
use crate::hal::esp_set_time;
|
|
||||||
use crate::webserver::read_up_to_bytes_from_request;
|
|
||||||
use crate::{do_secure_pump, BOARD_ACCESS};
|
|
||||||
use alloc::string::{String, ToString};
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use chrono::DateTime;
|
|
||||||
use edge_http::io::server::Connection;
|
|
||||||
use embedded_io_async::{Read, Write};
|
|
||||||
use log::info;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct NightLampCommand {
|
|
||||||
active: bool,
|
|
||||||
}
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
struct SSIDList {
|
|
||||||
ssids: Vec<String>,
|
|
||||||
}
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct SetTime<'a> {
|
|
||||||
time: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
||||||
pub struct TestPump {
|
|
||||||
pump: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn wifi_scan<T, const N: usize>(
|
|
||||||
_request: &mut Connection<'_, T, N>,
|
|
||||||
) -> FatResult<Option<String>> {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
info!("start wifi scan");
|
|
||||||
let mut ssids: Vec<String> = Vec::new();
|
|
||||||
let scan_result = board.board_hal.get_esp().wifi_scan().await?;
|
|
||||||
scan_result
|
|
||||||
.iter()
|
|
||||||
.for_each(|s| ssids.push(s.ssid.to_string()));
|
|
||||||
let ssid_json = serde_json::to_string(&SSIDList { ssids })?;
|
|
||||||
info!("Sending ssid list {}", &ssid_json);
|
|
||||||
Ok(Some(ssid_json))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn board_test() -> FatResult<Option<String>> {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
board.board_hal.test().await?;
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn detect_sensors() -> FatResult<Option<String>> {
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
let result = board.board_hal.detect_sensors().await?;
|
|
||||||
let json = serde_json::to_string(&result)?;
|
|
||||||
Ok(Some(json))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn pump_test<T, const N: usize>(
|
|
||||||
request: &mut Connection<'_, T, N>,
|
|
||||||
) -> FatResult<Option<String>>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
let actual_data = read_up_to_bytes_from_request(request, None).await?;
|
|
||||||
let pump_test: TestPump = serde_json::from_slice(&actual_data)?;
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
|
|
||||||
let config = &board.board_hal.get_config().plants[pump_test.pump].clone();
|
|
||||||
let pump_result = do_secure_pump(&mut board, pump_test.pump, config, false).await;
|
|
||||||
//ensure it is disabled before unwrapping
|
|
||||||
board.board_hal.pump(pump_test.pump, false).await?;
|
|
||||||
|
|
||||||
Ok(Some(serde_json::to_string(&pump_result?)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn night_lamp_test<T, const N: usize>(
|
|
||||||
request: &mut Connection<'_, T, N>,
|
|
||||||
) -> FatResult<Option<String>>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
let actual_data = read_up_to_bytes_from_request(request, None).await?;
|
|
||||||
let light_command: NightLampCommand = serde_json::from_slice(&actual_data)?;
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
board.board_hal.light(light_command.active).await?;
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn write_time<T, const N: usize>(
|
|
||||||
request: &mut Connection<'_, T, N>,
|
|
||||||
) -> FatResult<Option<String>>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
let actual_data = read_up_to_bytes_from_request(request, None).await?;
|
|
||||||
let time: SetTime = serde_json::from_slice(&actual_data)?;
|
|
||||||
let parsed = DateTime::parse_from_rfc3339(time.time).unwrap();
|
|
||||||
esp_set_time(parsed).await?;
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn set_config<T, const N: usize>(
|
|
||||||
request: &mut Connection<'_, T, N>,
|
|
||||||
) -> FatResult<Option<String>>
|
|
||||||
where
|
|
||||||
T: Read + Write,
|
|
||||||
{
|
|
||||||
let all = read_up_to_bytes_from_request(request, Some(4096)).await?;
|
|
||||||
let length = all.len();
|
|
||||||
let config: PlantControllerConfig = serde_json::from_slice(&all)?;
|
|
||||||
|
|
||||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
|
||||||
board.board_hal.get_esp().save_config(all).await?;
|
|
||||||
info!("Wrote config config {:?} with size {}", config, length);
|
|
||||||
board.board_hal.set_config(config);
|
|
||||||
Ok(Some("Ok".to_string()))
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
index.html.gz
|
|
||||||
bundle.js.gz
|
|
||||||
index.html
|
|
||||||
bundle.js
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
class ToastService {
|
|
||||||
private container: HTMLElement;
|
|
||||||
private stylesInjected = false;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.container = this.ensureContainer();
|
|
||||||
this.injectStyles();
|
|
||||||
}
|
|
||||||
|
|
||||||
info(message: string, timeoutMs: number = 5000) {
|
|
||||||
const el = this.createToast(message, 'info');
|
|
||||||
this.container.appendChild(el);
|
|
||||||
// Auto-dismiss after timeout
|
|
||||||
const timer = window.setTimeout(() => this.dismiss(el), timeoutMs);
|
|
||||||
// Dismiss on click immediately
|
|
||||||
el.addEventListener('click', () => {
|
|
||||||
window.clearTimeout(timer);
|
|
||||||
this.dismiss(el);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
error(message: string) {
|
|
||||||
const el = this.createToast(message, 'error');
|
|
||||||
this.container.appendChild(el);
|
|
||||||
// Only dismiss on click
|
|
||||||
el.addEventListener('click', () => this.dismiss(el));
|
|
||||||
}
|
|
||||||
|
|
||||||
private dismiss(el: HTMLElement) {
|
|
||||||
if (!el.parentElement) return;
|
|
||||||
el.parentElement.removeChild(el);
|
|
||||||
}
|
|
||||||
|
|
||||||
private createToast(message: string, type: 'info' | 'error'): HTMLElement {
|
|
||||||
const div = document.createElement('div');
|
|
||||||
div.className = `toast ${type}`;
|
|
||||||
div.textContent = message;
|
|
||||||
div.setAttribute('role', 'status');
|
|
||||||
div.setAttribute('aria-live', 'polite');
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ensureContainer(): HTMLElement {
|
|
||||||
let container = document.getElementById('toast-container');
|
|
||||||
if (!container) {
|
|
||||||
container = document.createElement('div');
|
|
||||||
container.id = 'toast-container';
|
|
||||||
document.body.appendChild(container);
|
|
||||||
}
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
private injectStyles() {
|
|
||||||
if (this.stylesInjected) return;
|
|
||||||
const style = document.createElement('style');
|
|
||||||
style.textContent = `
|
|
||||||
#toast-container {
|
|
||||||
position: fixed;
|
|
||||||
top: 12px;
|
|
||||||
right: 12px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 8px;
|
|
||||||
z-index: 9999;
|
|
||||||
}
|
|
||||||
.toast {
|
|
||||||
max-width: 320px;
|
|
||||||
padding: 10px 12px;
|
|
||||||
border-radius: 6px;
|
|
||||||
box-shadow: 0 2px 6px rgba(0,0,0,0.2);
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
.toast.info {
|
|
||||||
background-color: #d4edda; /* green-ish */
|
|
||||||
color: #155724;
|
|
||||||
border-left: 4px solid #28a745;
|
|
||||||
}
|
|
||||||
.toast.error {
|
|
||||||
background-color: #f8d7da; /* red-ish */
|
|
||||||
color: #721c24;
|
|
||||||
border-left: 4px solid #dc3545;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
document.head.appendChild(style);
|
|
||||||
this.stylesInjected = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const toast = new ToastService();
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "canapi"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "canapi"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bincode = { version = "2.0.1", default-features = false, features = ["derive"] }
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
//! CAN bus API shared crate for PlantCtrl sensors and controller.
|
|
||||||
//! Addressing and messages are defined here to be reused by all bus participants.
|
|
||||||
|
|
||||||
use bincode::{Decode, Encode};
|
|
||||||
|
|
||||||
/// Total plants supported by addressing (0..=15)
|
|
||||||
pub const MAX_PLANTS: u8 = 16;
|
|
||||||
|
|
||||||
/// Sensors per plant: 0..=1 => A/B
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum SensorSlot {
|
|
||||||
A = 0,
|
|
||||||
B = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SensorSlot {
|
|
||||||
pub const fn from_index(idx: u8) -> Option<Self> {
|
|
||||||
match idx {
|
|
||||||
0 => Some(SensorSlot::A),
|
|
||||||
1 => Some(SensorSlot::B),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Legacy sensor base address kept for compatibility with existing code.
|
|
||||||
/// Each plant uses SENSOR_BASE_ADDRESS + plant_index (0..PLANT_COUNT-1).
|
|
||||||
/// 11-bit standard ID space, safe range.
|
|
||||||
pub const SENSOR_BASE_ADDRESS: u16 = 1000;
|
|
||||||
|
|
||||||
/// Typed topics within the SENSOR_BASE space.
|
|
||||||
/// Additional offsets allow distinct message semantics while keeping plant-indexed layout.
|
|
||||||
pub mod id {
|
|
||||||
use crate::{SensorSlot, MAX_PLANTS, SENSOR_BASE_ADDRESS};
|
|
||||||
|
|
||||||
/// Number of plants addressable per sensor slot group
|
|
||||||
pub const PLANTS_PER_GROUP: u16 = MAX_PLANTS as u16; // 16
|
|
||||||
/// Offset applied for SensorSlot::B within a message group
|
|
||||||
pub const B_OFFSET: u16 = PLANTS_PER_GROUP; // 16
|
|
||||||
|
|
||||||
// Message group base offsets relative to SENSOR_BASE_ADDRESS
|
|
||||||
pub const MOISTURE_DATA_OFFSET: u16 = 0; // periodic data from sensor (sensor -> controller)
|
|
||||||
pub const IDENTIFY_CMD_OFFSET: u16 = 32; // identify LED command (controller -> sensor)
|
|
||||||
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub const fn plant_id(message_type_offset: u16, sensor: SensorSlot, plant: u16) -> u16 {
|
|
||||||
match sensor {
|
|
||||||
SensorSlot::A => SENSOR_BASE_ADDRESS + message_type_offset + plant,
|
|
||||||
SensorSlot::B => SENSOR_BASE_ADDRESS + message_type_offset + B_OFFSET + plant,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Kinds of message spaces recognized by the addressing scheme.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum MessageKind {
|
|
||||||
MoistureData, // sensor -> controller
|
|
||||||
IdentifyCmd, // controller -> sensor
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Try to classify a received 11-bit standard ID into a known message kind and extract plant and sensor slot.
|
|
||||||
/// Returns (kind, plant, slot) on success.
|
|
||||||
#[inline]
|
|
||||||
pub const fn classify(id: u16) -> Option<(MessageKind, u8, SensorSlot)> {
|
|
||||||
// Ensure the ID is within our base space
|
|
||||||
if id < SENSOR_BASE_ADDRESS {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let rel = id - SENSOR_BASE_ADDRESS;
|
|
||||||
|
|
||||||
// Helper: decode within a given group offset
|
|
||||||
const fn decode_in_group(rel: u16, group_base: u16) -> Option<(u8, SensorSlot)> {
|
|
||||||
if rel < group_base { return None; }
|
|
||||||
let inner = rel - group_base;
|
|
||||||
if inner < PLANTS_PER_GROUP { // A slot
|
|
||||||
Some((inner as u8, SensorSlot::A))
|
|
||||||
} else if inner >= B_OFFSET && inner < B_OFFSET + PLANTS_PER_GROUP { // B slot
|
|
||||||
Some(((inner - B_OFFSET) as u8, SensorSlot::B))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check known groups in order
|
|
||||||
if let Some((plant, slot)) = decode_in_group(rel, MOISTURE_DATA_OFFSET) {
|
|
||||||
return Some((MessageKind::MoistureData, plant, slot));
|
|
||||||
}
|
|
||||||
if let Some((plant, slot)) = decode_in_group(rel, IDENTIFY_CMD_OFFSET) {
|
|
||||||
return Some((MessageKind::IdentifyCmd, plant, slot));
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns Some((plant, slot)) regardless of message kind, if the id falls into any known group; otherwise None.
|
|
||||||
#[inline]
|
|
||||||
pub const fn extract_plant_slot(id: u16) -> Option<(u8, SensorSlot)> {
|
|
||||||
match classify(id) {
|
|
||||||
Some((_kind, plant, slot)) => Some((plant, slot)),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if an id corresponds exactly to the given message kind, plant and slot.
|
|
||||||
#[inline]
|
|
||||||
pub const fn is_identify_for(id: u16, plant: u8, slot: SensorSlot) -> bool {
|
|
||||||
id == plant_id(IDENTIFY_CMD_OFFSET, slot, plant as u16)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub const fn is_moisture_data_for(id: u16, plant: u8, slot: SensorSlot) -> bool {
|
|
||||||
id == plant_id(MOISTURE_DATA_OFFSET, slot, plant as u16)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Periodic moisture data sent by sensors.
|
|
||||||
/// Fits into 5 bytes with bincode-v2 (no varint): u8 + u8 + u16 = 4, alignment may keep 4.
|
|
||||||
#[derive(Debug, Clone, Copy, Encode, Decode)]
|
|
||||||
pub struct MoistureData {
|
|
||||||
pub plant: u8, // 0..MAX_PLANTS-1
|
|
||||||
pub sensor: SensorSlot, // A/B
|
|
||||||
pub hz: u16, // measured frequency of moisture sensor
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Request a sensor to report immediately (controller -> sensor).
|
|
||||||
#[derive(Debug, Clone, Copy, Encode, Decode)]
|
|
||||||
pub struct MoistureRequest {
|
|
||||||
pub plant: u8,
|
|
||||||
pub sensor: SensorSlot, // target sensor (sensor filters by this)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Control a sensor's identify LED, if received by sensor, blink for a few seconds
|
|
||||||
#[derive(Debug, Clone, Copy, Encode, Decode)]
|
|
||||||
pub struct IdentifyLed {}
|
|
||||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user