Compare commits

..

No commits in common. "develop" and "housekeeping" have entirely different histories.

24 changed files with 7127 additions and 7944 deletions

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1102,15 +1102,6 @@
(hide yes) (hide yes)
) )
) )
(property "Sim.Pins" "1=K 2=A"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "ki_keywords" "LED diode" (property "ki_keywords" "LED diode"
(at 0 0 0) (at 0 0 0)
(effects (effects
@ -4751,26 +4742,6 @@
) )
(uuid f1cb09e6-74b4-43c4-8247-c9f441069dc5) (uuid f1cb09e6-74b4-43c4-8247-c9f441069dc5)
) )
(text "free"
(exclude_from_sim no)
(at 180.848 72.39 0)
(effects
(font
(size 1.27 1.27)
)
)
(uuid "1556efd1-3aac-45ee-8113-03336077a83d")
)
(text "GPIO21 pulses high during flashing!\nGPIO19 is used for communication during flashing"
(exclude_from_sim no)
(at 202.692 102.362 0)
(effects
(font
(size 1.27 1.27)
)
)
(uuid "2195adc7-ab0b-4ee3-aa2e-b2786571de43")
)
(text "To Allow Wakeup via interrupt" (text "To Allow Wakeup via interrupt"
(exclude_from_sim no) (exclude_from_sim no)
(at 157.734 70.104 0) (at 157.734 70.104 0)
@ -5050,10 +5021,6 @@
(at 31.75 200.66) (at 31.75 200.66)
(uuid "133b7027-41a1-4e09-a1e6-24d8bb033b2a") (uuid "133b7027-41a1-4e09-a1e6-24d8bb033b2a")
) )
(no_connect
(at 184.15 72.39)
(uuid "5a1ba32e-b2ae-4014-aefc-1b75b4dc3c15")
)
(no_connect (no_connect
(at 184.15 67.31) (at 184.15 67.31)
(uuid "6d645bf1-339f-4b38-a26a-bdd168ca591e") (uuid "6d645bf1-339f-4b38-a26a-bdd168ca591e")
@ -6184,7 +6151,7 @@
) )
(global_label "WORKING" (global_label "WORKING"
(shape input) (shape input)
(at 224.79 87.63 0) (at 224.79 72.39 0)
(fields_autoplaced yes) (fields_autoplaced yes)
(effects (effects
(font (font
@ -6194,7 +6161,7 @@
) )
(uuid "0af53f6a-ea11-4d73-a8d1-f63ebcdc2e54") (uuid "0af53f6a-ea11-4d73-a8d1-f63ebcdc2e54")
(property "Intersheetrefs" "${INTERSHEET_REFS}" (property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 235.6482 87.63 0) (at 235.6482 72.39 0)
(effects (effects
(font (font
(size 1.27 1.27) (size 1.27 1.27)
@ -6468,6 +6435,28 @@
) )
) )
) )
(global_label "PUMP_ENABLE"
(shape input)
(at 184.15 72.39 180)
(fields_autoplaced yes)
(effects
(font
(size 1.27 1.27)
)
(justify right)
)
(uuid "2e795b18-a106-4105-bbb4-8c45273e4987")
(property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 168.5143 72.39 0)
(effects
(font
(size 1.27 1.27)
)
(justify right)
(hide yes)
)
)
)
(global_label "SDA" (global_label "SDA"
(shape input) (shape input)
(at 224.79 85.09 0) (at 224.79 85.09 0)
@ -6712,22 +6701,44 @@
) )
(global_label "EXTRA_1" (global_label "EXTRA_1"
(shape input) (shape input)
(at 184.15 82.55 180) (at 224.79 92.71 0)
(fields_autoplaced yes) (fields_autoplaced yes)
(effects (effects
(font (font
(size 1.27 1.27) (size 1.27 1.27)
) )
(justify right) (justify left)
) )
(uuid "4635eda0-e395-42c7-9764-c7b7fc3019ec") (uuid "4635eda0-e395-42c7-9764-c7b7fc3019ec")
(property "Intersheetrefs" "${INTERSHEET_REFS}" (property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 173.9572 82.55 0) (at 234.9828 92.71 0)
(effects (effects
(font (font
(size 1.27 1.27) (size 1.27 1.27)
) )
(justify right) (justify left)
(hide yes)
)
)
)
(global_label "GND"
(shape input)
(at 59.69 135.89 0)
(fields_autoplaced yes)
(effects
(font
(size 1.27 1.27)
)
(justify left)
)
(uuid "472614c1-a2b1-4366-9d8e-904ae5de7b8a")
(property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 65.8915 135.89 0)
(effects
(font
(size 1.27 1.27)
)
(justify left)
(hide yes) (hide yes)
) )
) )
@ -7064,22 +7075,22 @@
) )
(global_label "WARN_LED" (global_label "WARN_LED"
(shape input) (shape input)
(at 224.79 92.71 0) (at 184.15 82.55 180)
(fields_autoplaced yes) (fields_autoplaced yes)
(effects (effects
(font (font
(size 1.27 1.27) (size 1.27 1.27)
) )
(justify left) (justify right)
) )
(uuid "63850aa5-2eca-4827-9a2f-c5aed597072f") (uuid "63850aa5-2eca-4827-9a2f-c5aed597072f")
(property "Intersheetrefs" "${INTERSHEET_REFS}" (property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 236.6762 92.71 0) (at 172.2638 82.55 0)
(effects (effects
(font (font
(size 1.27 1.27) (size 1.27 1.27)
) )
(justify left) (justify right)
(hide yes) (hide yes)
) )
) )
@ -7328,7 +7339,7 @@
) )
(global_label "EXTRA_2" (global_label "EXTRA_2"
(shape input) (shape input)
(at 224.79 72.39 0) (at 224.79 87.63 0)
(fields_autoplaced yes) (fields_autoplaced yes)
(effects (effects
(font (font
@ -7338,7 +7349,7 @@
) )
(uuid "79c7218b-c7f0-4a6c-8b1d-8ae92200b937") (uuid "79c7218b-c7f0-4a6c-8b1d-8ae92200b937")
(property "Intersheetrefs" "${INTERSHEET_REFS}" (property "Intersheetrefs" "${INTERSHEET_REFS}"
(at 234.9828 72.39 0) (at 234.9828 87.63 0)
(effects (effects
(font (font
(size 1.27 1.27) (size 1.27 1.27)
@ -12065,7 +12076,7 @@
(hide yes) (hide yes)
) )
) )
(property "Description" "Light emitting diode" (property "Description" ""
(at 142.24 115.57 0) (at 142.24 115.57 0)
(effects (effects
(font (font
@ -12099,7 +12110,7 @@
) )
) )
) )
(property "Sim.Pins" "1=K 2=A" (property "Sim.Pins" ""
(at 142.24 115.57 0) (at 142.24 115.57 0)
(effects (effects
(font (font
@ -12287,7 +12298,7 @@
(hide yes) (hide yes)
) )
) )
(property "Description" "Light emitting diode" (property "Description" ""
(at 74.93 337.82 0) (at 74.93 337.82 0)
(effects (effects
(font (font
@ -12321,7 +12332,7 @@
) )
) )
) )
(property "Sim.Pins" "1=K 2=A" (property "Sim.Pins" ""
(at 74.93 337.82 0) (at 74.93 337.82 0)
(effects (effects
(font (font
@ -13620,7 +13631,7 @@
(hide yes) (hide yes)
) )
) )
(property "Description" "Light emitting diode" (property "Description" ""
(at 113.03 107.95 0) (at 113.03 107.95 0)
(effects (effects
(font (font
@ -13654,7 +13665,7 @@
) )
) )
) )
(property "Sim.Pins" "1=K 2=A" (property "Sim.Pins" ""
(at 113.03 107.95 0) (at 113.03 107.95 0)
(effects (effects
(font (font
@ -14377,7 +14388,7 @@
(hide yes) (hide yes)
) )
) )
(property "Description" "Light emitting diode" (property "Description" ""
(at 35.56 337.82 0) (at 35.56 337.82 0)
(effects (effects
(font (font
@ -14411,7 +14422,7 @@
) )
) )
) )
(property "Sim.Pins" "1=K 2=A" (property "Sim.Pins" ""
(at 35.56 337.82 0) (at 35.56 337.82 0)
(effects (effects
(font (font
@ -14928,7 +14939,7 @@
(hide yes) (hide yes)
) )
) )
(property "Description" "Light emitting diode" (property "Description" ""
(at 281.94 101.6 0) (at 281.94 101.6 0)
(effects (effects
(font (font
@ -14962,7 +14973,7 @@
) )
) )
) )
(property "Sim.Pins" "1=K 2=A" (property "Sim.Pins" ""
(at 281.94 101.6 0) (at 281.94 101.6 0)
(effects (effects
(font (font

View File

@ -166,40 +166,40 @@
) )
(pad "7" thru_hole rect (pad "7" thru_hole rect
(at -0.5 -12) (at -0.5 -12)
(size 1.7 1.7) (size 0.85 0.85)
(drill 1) (drill 0.5)
(layers "*.Cu" "*.Mask") (layers "*.Cu" "*.Mask")
(remove_unused_layers no) (remove_unused_layers no)
(uuid "536f0038-06c5-45e5-b1c8-898a364f6ec4") (uuid "536f0038-06c5-45e5-b1c8-898a364f6ec4")
) )
(pad "8" thru_hole rect (pad "8" thru_hole rect
(at 39.5 -16) (at 39.5 -16)
(size 1.7 1.7) (size 0.85 0.85)
(drill 1) (drill 0.5)
(layers "*.Cu" "*.Mask") (layers "*.Cu" "*.Mask")
(remove_unused_layers no) (remove_unused_layers no)
(uuid "3e0cdbaa-8219-48c4-bb0a-fd4f0e78a390") (uuid "3e0cdbaa-8219-48c4-bb0a-fd4f0e78a390")
) )
(pad "9" thru_hole rect (pad "9" thru_hole rect
(at 39.5 22.5) (at 39.5 22.5)
(size 1.7 1.7) (size 0.85 0.85)
(drill 1) (drill 0.5)
(layers "*.Cu" "*.Mask") (layers "*.Cu" "*.Mask")
(remove_unused_layers no) (remove_unused_layers no)
(uuid "f01565c2-eadd-4451-9dea-2ebe28d872f0") (uuid "f01565c2-eadd-4451-9dea-2ebe28d872f0")
) )
(pad "10" thru_hole rect (pad "10" thru_hole rect
(at -0.5 15) (at -0.5 15)
(size 1.7 1.7) (size 0.85 0.85)
(drill 1) (drill 0.5)
(layers "*.Cu" "*.Mask") (layers "*.Cu" "*.Mask")
(remove_unused_layers no) (remove_unused_layers no)
(uuid "56ec7c50-069f-4f46-9b83-ac18e4928930") (uuid "56ec7c50-069f-4f46-9b83-ac18e4928930")
) )
(pad "11" thru_hole rect (pad "11" thru_hole rect
(at -43 22.5) (at -43 22.5)
(size 1.7 1.7) (size 0.85 0.85)
(drill 1) (drill 0.5)
(layers "*.Cu" "*.Mask") (layers "*.Cu" "*.Mask")
(remove_unused_layers no) (remove_unused_layers no)
(uuid "f423be21-13b8-46de-8e19-e48325411a29") (uuid "f423be21-13b8-46de-8e19-e48325411a29")

View File

@ -738,7 +738,7 @@
(type default) (type default)
) )
(layer "F.SilkS") (layer "F.SilkS")
(uuid "23d58535-2840-4798-8fe6-a41e5dd82cfc") (uuid "87f61a83-c9a2-45b6-a235-fe6e83f3dbe3")
) )
(fp_line (fp_line
(start -45 24) (start -45 24)
@ -748,7 +748,7 @@
(type default) (type default)
) )
(layer "F.SilkS") (layer "F.SilkS")
(uuid "ed3c0755-343c-4d19-9481-c2c33348ef82") (uuid "f24175c8-21e2-4899-af70-89e48457e9ba")
) )
(fp_line (fp_line
(start -44 24) (start -44 24)
@ -758,7 +758,7 @@
(type default) (type default)
) )
(layer "F.SilkS") (layer "F.SilkS")
(uuid "cd2e639b-4e20-40a4-8976-6223f2753aeb") (uuid "a884c661-0353-48a0-9bc6-995e02652e36")
) )
(fp_line (fp_line
(start 41 -18) (start 41 -18)
@ -768,7 +768,7 @@
(type default) (type default)
) )
(layer "F.SilkS") (layer "F.SilkS")
(uuid "f34b006e-828a-45d3-b0f5-daad16782ed8") (uuid "675d135e-52c1-4dd1-b434-983f40a2b544")
) )
(fp_line (fp_line
(start 41 24) (start 41 24)
@ -778,7 +778,7 @@
(type default) (type default)
) )
(layer "F.SilkS") (layer "F.SilkS")
(uuid "03fcb85f-a42f-4eab-bf16-6ff7087e0441") (uuid "fbfb2621-73ae-4c15-8403-abdec5cc45cd")
) )
(fp_text user "${REFERENCE}" (fp_text user "${REFERENCE}"
(at 0 2.5 0) (at 0 2.5 0)
@ -860,8 +860,8 @@
) )
(pad "7" thru_hole rect (pad "7" thru_hole rect
(at -0.5 -12) (at -0.5 -12)
(size 1.7 1.7) (size 0.85 0.85)
(drill 1) (drill 0.5)
(layers "*.Cu" "*.Mask") (layers "*.Cu" "*.Mask")
(remove_unused_layers no) (remove_unused_layers no)
(net 1 "GND") (net 1 "GND")
@ -871,8 +871,8 @@
) )
(pad "8" thru_hole rect (pad "8" thru_hole rect
(at 39.5 -16) (at 39.5 -16)
(size 1.7 1.7) (size 0.85 0.85)
(drill 1) (drill 0.5)
(layers "*.Cu" "*.Mask") (layers "*.Cu" "*.Mask")
(remove_unused_layers no) (remove_unused_layers no)
(net 1 "GND") (net 1 "GND")
@ -882,8 +882,8 @@
) )
(pad "9" thru_hole rect (pad "9" thru_hole rect
(at 39.5 22.5) (at 39.5 22.5)
(size 1.7 1.7) (size 0.85 0.85)
(drill 1) (drill 0.5)
(layers "*.Cu" "*.Mask") (layers "*.Cu" "*.Mask")
(remove_unused_layers no) (remove_unused_layers no)
(net 1 "GND") (net 1 "GND")
@ -893,8 +893,8 @@
) )
(pad "10" thru_hole rect (pad "10" thru_hole rect
(at -0.5 15) (at -0.5 15)
(size 1.7 1.7) (size 0.85 0.85)
(drill 1) (drill 0.5)
(layers "*.Cu" "*.Mask") (layers "*.Cu" "*.Mask")
(remove_unused_layers no) (remove_unused_layers no)
(net 1 "GND") (net 1 "GND")
@ -904,8 +904,8 @@
) )
(pad "11" thru_hole rect (pad "11" thru_hole rect
(at -43 22.5) (at -43 22.5)
(size 1.7 1.7) (size 0.85 0.85)
(drill 1) (drill 0.5)
(layers "*.Cu" "*.Mask") (layers "*.Cu" "*.Mask")
(remove_unused_layers no) (remove_unused_layers no)
(net 1 "GND") (net 1 "GND")

Binary file not shown.

View File

@ -7,123 +7,54 @@ P arrayDim N
317VBAT VIA MD0157PA00X+043130Y-035039X0315Y0000R000S3 317VBAT VIA MD0157PA00X+043130Y-035039X0315Y0000R000S3
317GND VIA MD1181PA00X+077500Y-007000X1575Y0000R000S3 317GND VIA MD1181PA00X+077500Y-007000X1575Y0000R000S3
317GND VIA MD0157PA00X+062402Y-010433X0315Y0000R000S3 317GND VIA MD0157PA00X+062402Y-010433X0315Y0000R000S3
317GND VIA MD0157PA00X+054449Y-012323X0315Y0000R000S3 317GND VIA MD0157PA00X+054134Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+044606Y-010551X0315Y0000R000S3 317GND VIA MD0157PA00X+062205Y-012303X0315Y0000R000S3
317GND VIA MD0157PA00X+054921Y-011417X0315Y0000R000S3 317GND VIA MD0157PA00X+058169Y-012303X0315Y0000R000S3
317GND VIA MD0157PA00X+050787Y-009646X0315Y0000R000S3 317GND VIA MD0157PA00X+051772Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+042126Y-008976X0315Y0000R000S3
317GND VIA MD0157PA00X+062205Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+059291Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+058150Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+054843Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+043228Y-012480X0315Y0000R000S3
317GND VIA MD0157PA00X+047244Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+060374Y-011594X0315Y0000R000S3
317GND VIA MD1181PA00X+041500Y-042900X1575Y0000R000S3 317GND VIA MD1181PA00X+041500Y-042900X1575Y0000R000S3
317GND VIA MD0157PA00X+047165Y-011260X0315Y0000R000S3 317GND VIA MD0157PA00X+059685Y-012402X0315Y0000R000S3
317GND VIA MD0157PA00X+042717Y-013031X0315Y0000R000S3 317GND VIA MD0157PA00X+054429Y-011909X0315Y0000R000S3
317GND VIA MD0157PA00X+057008Y-012323X0315Y0000R000S3 317GND VIA MD0157PA00X+053346Y-010925X0315Y0000R000S3
317GND VIA MD0157PA00X+062205Y-011614X0315Y0000R000S3 317GND VIA MD0157PA00X+062205Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+058917Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+040945Y-035630X0315Y0000R000S3 317GND VIA MD0157PA00X+040945Y-035630X0315Y0000R000S3
317GND VIA MD0157PA00X+059606Y-011575X0315Y0000R000S3
317GND VIA MD1181PA00X+077500Y-042900X1575Y0000R000S3 317GND VIA MD1181PA00X+077500Y-042900X1575Y0000R000S3
317GND VIA MD0157PA00X+046890Y-009646X0315Y0000R000S3 317GND VIA MD0157PA00X+059055Y-012402X0315Y0000R000S3
317GND VIA MD0157PA00X+054016Y-010591X0315Y0000R000S3 317GND VIA MD0157PA00X+056594Y-012303X0315Y0000R000S3
317GND VIA MD0157PA00X+046457Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+045039Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+045748Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+051850Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+051260Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+054646Y-011142X0315Y0000R000S3
317GND VIA MD0157PA00X+043819Y-011929X0315Y0000R000S3
317GND VIA MD0157PA00X+046102Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+059646Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+050079Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+056594Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+045118Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+061122Y-011614X0315Y0000R000S3 317GND VIA MD0157PA00X+061122Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+043346Y-009646X0315Y0000R000S3 317GND VIA MD0157PA00X+054626Y-010827X0315Y0000R000S3
317GND VIA MD0157PA00X+056693Y-011614X0315Y0000R000S3 317GND VIA MD0157PA00X+056693Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+044055Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+052559Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+050197Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+048228Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+045669Y-007087X0315Y0000R000S3 317GND VIA MD0157PA00X+045669Y-007087X0315Y0000R000S3
317GND VIA MD0157PA00X+054724Y-012205X0315Y0000R000S3
317GND VIA MD0157PA00X+063386Y-011220X0315Y0000R000S3 317GND VIA MD0157PA00X+063386Y-011220X0315Y0000R000S3
317GND VIA MD0157PA00X+060472Y-012323X0315Y0000R000S3 317GND VIA MD0157PA00X+059685Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+045699Y-017156X0315Y0000R000S3 317GND VIA MD0157PA00X+045699Y-017156X0315Y0000R000S3
317GND VIA MD0157PA00X+048661Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+049724Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+048091Y-016535X0315Y0000R000S3 317GND VIA MD0157PA00X+048091Y-016535X0315Y0000R000S3
317GND VIA MD0157PA00X+053740Y-011220X0315Y0000R000S3
317GND VIA MD0157PA00X+048917Y-031102X0315Y0000R000S3 317GND VIA MD0157PA00X+048917Y-031102X0315Y0000R000S3
317GND VIA MD0157PA00X+056299Y-005591X0315Y0000R000S3 317GND VIA MD0157PA00X+056299Y-005591X0315Y0000R000S3
317GND VIA MD0157PA00X+053858Y-011693X0315Y0000R000S3
317GND VIA MD0157PA00X+049843Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+059232Y-011575X0315Y0000R000S3
317GND VIA MD0157PA00X+055709Y-011220X0315Y0000R000S3 317GND VIA MD0157PA00X+055709Y-011220X0315Y0000R000S3
317GND VIA MD0157PA00X+058169Y-011614X0315Y0000R000S3 317GND VIA MD0157PA00X+058169Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+044094Y-011654X0315Y0000R000S3
317GND VIA MD0157PA00X+061654Y-008071X0315Y0000R000S3 317GND VIA MD0157PA00X+061654Y-008071X0315Y0000R000S3
317GND VIA MD0157PA00X+050551Y-011260X0315Y0000R000S3 317GND VIA MD0157PA00X+054035Y-010138X0315Y0000R000S3
317GND VIA MD0157PA00X+049370Y-009646X0315Y0000R000S3 317GND VIA MD0157PA00X+054331Y-010531X0315Y0000R000S3
317GND VIA MD0157PA00X+042402Y-009213X0315Y0000R000S3 317GND VIA MD0157PA00X+055217Y-012303X0315Y0000R000S3
317GND VIA MD0157PA00X+059980Y-011594X0315Y0000R000S3 317GND VIA MD0157PA00X+056102Y-012303X0315Y0000R000S3
317GND VIA MD0157PA00X+056181Y-011614X0315Y0000R000S3 317GND VIA MD0157PA00X+055315Y-011516X0315Y0000R000S3
317GND VIA MD0157PA00X+044409Y-009646X0315Y0000R000S3 317GND VIA MD0157PA00X+055709Y-012303X0315Y0000R000S3
317GND VIA MD0157PA00X+055276Y-012323X0315Y0000R000S3 317GND VIA MD0157PA00X+054921Y-011220X0315Y0000R000S3
317GND VIA MD0157PA00X+056181Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+045394Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+060748Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+044685Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+055276Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+051142Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+055748Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+045827Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+052835Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+053189Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+047953Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+052205Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+053780Y-010315X0315Y0000R000S3
317GND VIA MD0157PA00X+046181Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+047283Y-023622X0315Y0000R000S3
317GND VIA MD0157PA00X+051496Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+059843Y-005591X0315Y0000R000S3 317GND VIA MD0157PA00X+059843Y-005591X0315Y0000R000S3
317GND VIA MD0157PA00X+053583Y-011378X0315Y0000R000S3 317GND VIA MD0157PA00X+050787Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+049016Y-034154X0315Y0000R000S3 317GND VIA MD0157PA00X+049016Y-034154X0315Y0000R000S3
317GND VIA MD0157PA00X+045472Y-009646X0315Y0000R000S3 317GND VIA MD0157PA00X+051772Y-008858X0315Y0000R000S3
317GND VIA MD0157PA00X+058543Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+054331Y-010866X0315Y0000R000S3
317GND VIA MD0157PA00X+046811Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+046555Y-030512X0315Y0000R000S3 317GND VIA MD0157PA00X+046555Y-030512X0315Y0000R000S3
317GND VIA MD0157PA00X+052913Y-009646X0315Y0000R000S3 317GND VIA MD0157PA00X+061122Y-012303X0315Y0000R000S3
317GND VIA MD0157PA00X+043701Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+048307Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+061181Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+049488Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+047520Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+060059Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+042283Y-013386X0315Y0000R000S3
317GND VIA MD0157PA00X+044764Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+047205Y-025709X0315Y0000R000S3
317GND VIA MD0157PA00X+055709Y-011614X0315Y0000R000S3 317GND VIA MD0157PA00X+055709Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+060827Y-012323X0315Y0000R000S3 317GND VIA MD0157PA00X+059055Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+054134Y-011969X0315Y0000R000S3
317GND VIA MD0157PA00X+046535Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+043504Y-012205X0315Y0000R000S3
317GND VIA MD1181PA00X+041575Y-007087X1575Y0000R000S3 317GND VIA MD1181PA00X+041575Y-007087X1575Y0000R000S3
317GND VIA MD0157PA00X+050906Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+042992Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+040945Y-034646X0315Y0000R000S3 317GND VIA MD0157PA00X+040945Y-034646X0315Y0000R000S3
317GND VIA MD0157PA00X+063386Y-012598X0315Y0000R000S3 317GND VIA MD0157PA00X+063386Y-012598X0315Y0000R000S3
317GND VIA MD0157PA00X+047598Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+047874Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+042756Y-009291X0315Y0000R000S3
317GND VIA MD0157PA00X+050433Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+048583Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+048031Y-014567X0315Y0000R000S3 317GND VIA MD0157PA00X+048031Y-014567X0315Y0000R000S3
317GND VIA MD0157PA00X+057185Y-011614X0315Y0000R000S3 317GND VIA MD0157PA00X+057185Y-011516X0315Y0000R000S3
317GND VIA MD0157PA00X+042953Y-012756X0315Y0000R000S3
3173_3V VIA MD0157PA00X+064646Y-014567X0315Y0000R000S3 3173_3V VIA MD0157PA00X+064646Y-014567X0315Y0000R000S3
3173_3V VIA MD0157PA00X+075906Y-014331X0315Y0000R000S3 3173_3V VIA MD0157PA00X+075906Y-014331X0315Y0000R000S3
3173_3V VIA MD0157PA00X+074094Y-014331X0315Y0000R000S3 3173_3V VIA MD0157PA00X+074094Y-014331X0315Y0000R000S3
@ -145,19 +76,79 @@ P arrayDim N
317ISDAY VIA MD0157PA00X+066929Y-011024X0315Y0000R000S3 317ISDAY VIA MD0157PA00X+066929Y-011024X0315Y0000R000S3
317LED_ENABLE VIA MD0157PA00X+064764Y-010728X0315Y0000R000S3 317LED_ENABLE VIA MD0157PA00X+064764Y-010728X0315Y0000R000S3
317WORKING VIA MD0157PA00X+058661Y-011417X0315Y0000R000S3 317WORKING VIA MD0157PA00X+058661Y-011417X0315Y0000R000S3
317WORKING VIA MD0157PA00X+069843Y-008858X0315Y0000R000S3 317WORKING VIA MD0157PA00X+071949Y-008169X0315Y0000R000S3
317ENABLE_TANK VIA MD0157PA00X+062894Y-011516X0315Y0000R000S3 317ENABLE_TANK VIA MD0157PA00X+062894Y-011516X0315Y0000R000S3
317ENABLE_TANK VIA MD0157PA00X+050525Y-015584X0315Y0000R000S3 317ENABLE_TANK VIA MD0157PA00X+050525Y-015584X0315Y0000R000S3
317USB_D- VIA MD0157PA00X+045276Y-010305X0315Y0000R000S3
317USB_D- VIA MD0157PA00X+063377Y-011752X0315Y0000R000S3 317USB_D- VIA MD0157PA00X+063377Y-011752X0315Y0000R000S3
317USB_D- VIA MD0157PA00X+052902Y-009500X0315Y0000R000S3
317FLOW VIA MD0157PA00X+048543Y-023228X0315Y0000R000S3 317FLOW VIA MD0157PA00X+048543Y-023228X0315Y0000R000S3
317FLOW VIA MD0157PA00X+063484Y-007382X0315Y0000R000S3 317FLOW VIA MD0157PA00X+063484Y-007382X0315Y0000R000S3
317USB_D+ VIA MD0157PA00X+045276Y-010719X0315Y0000R000S3
317USB_D+ VIA MD0157PA00X+063377Y-012165X0315Y0000R000S3 317USB_D+ VIA MD0157PA00X+063377Y-012165X0315Y0000R000S3
317USB_D+ VIA MD0157PA00X+052610Y-009792X0315Y0000R000S3
317BOOT_SEL VIA MD0157PA00X+064764Y-009744X0315Y0000R000S3 317BOOT_SEL VIA MD0157PA00X+064764Y-009744X0315Y0000R000S3
317BOOT_SEL VIA MD0157PA00X+070374Y-012205X0315Y0000R000S3 317BOOT_SEL VIA MD0157PA00X+070374Y-012205X0315Y0000R000S3
317WARN_LED VIA MD0157PA00X+069803Y-008465X0315Y0000R000S3
317WARN_LED VIA MD0157PA00X+044390Y-005807X0315Y0000R000S3 317WARN_LED VIA MD0157PA00X+044390Y-005807X0315Y0000R000S3
317WARN_LED VIA MD0157PA00X+063386Y-008268X0315Y0000R000S3
327GND U2 -1 A01X+064094Y-005709X0591Y0354R000S2
3273_3V U2 -2 A01X+064094Y-006209X0591Y0354R000S2
327EN U2 -3 A01X+064094Y-006709X0591Y0354R000S2
327FLOW U2 -4 A01X+064094Y-007209X0591Y0354R000S2
327TANK_SENSOR U2 -5 A01X+064094Y-007709X0591Y0354R000S2
327WARN_LED U2 -6 A01X+064094Y-008209X0591Y0354R000S2
327ISDAY U2 -7 A01X+064094Y-008709X0591Y0354R000S2
327-(U2-IO0-PAD8) U2 -8 A01X+064094Y-009209X0591Y0354R000S2
327BOOT_SEL U2 -9 A01X+064094Y-009709X0591Y0354R000S2
327(U2-IO8-PAD10) U2 -10 A01X+064094Y-010209X0591Y0354R000S2
327LED_ENABLE U2 -11 A01X+064094Y-010709X0591Y0354R000S2
327ENABLE_TANK U2 -12 A01X+064094Y-011209X0591Y0354R000S2
327USB_D- U2 -13 A01X+064094Y-011709X0591Y0354R000S2
327USB_D+ U2 -14 A01X+064094Y-012209X0591Y0354R000S2
327BOOT_SEL U2 -15 A01X+070984Y-012209X0591Y0354R000S2
327TEMP U2 -16 A01X+070984Y-011709X0591Y0354R000S2
327SCL U2 -17 A01X+070984Y-011209X0591Y0354R000S2
327SDA U2 -18 A01X+070984Y-010709X0591Y0354R000S2
327EXTRA_2 U2 -19 A01X+070984Y-010209X0591Y0354R000S2
327CD_PROBE U2 -20 A01X+070984Y-009709X0591Y0354R000S2
327EXTRA_1 U2 -21 A01X+070984Y-009209X0591Y0354R000S2
327-(U2-NC-PAD22) U2 -22 A01X+070984Y-008709X0591Y0354R000S2
327WORKING U2 -23 A01X+070984Y-008209X0591Y0354R000S2
327ESP_RX U2 -24 A01X+070984Y-007709X0591Y0354R000S2
327ESP_TX U2 -25 A01X+070984Y-007209X0591Y0354R000S2
327CHARGE U2 -26 A01X+070984Y-006709X0591Y0354R000S2
327PUMP_ENABLE U2 -27 A01X+070984Y-006209X0591Y0354R000S2
327GND U2 -28 A01X+070984Y-005709X0591Y0354R000S2
327GND U2 -29_5 A01X+066947Y-007961X0315Y0315R000S2
327GND U2 -29_1 A01X+066455Y-007469X0315Y0315R000S2
327GND U2 -29_2 A01X+066947Y-007469X0315Y0315R000S2
327GND U2 -29_3 A01X+067439Y-007469X0315Y0315R000S2
327GND U2 -29_4 A01X+066455Y-007961X0315Y0315R000S2
327GND U2 -29_6 A01X+067439Y-007961X0315Y0315R000S2
327GND U2 -29_7 A01X+066455Y-008453X0315Y0315R000S2
327GND U2 -29_8 A01X+066947Y-008453X0315Y0315R000S2
327GND U2 -29_9 A01X+067439Y-008453X0315Y0315R000S2
317GND U2 -30_1 D0098PA00X+066701Y-007469X0138Y0000R000S3
317GND U2 -30_2 D0098PA00X+067193Y-007469X0138Y0000R000S3
317GND U2 -30_3 D0098PA00X+066455Y-007715X0138Y0000R000S3
317GND U2 -30_4 D0098PA00X+066947Y-007715X0138Y0000R000S3
317GND U2 -30_5 D0098PA00X+067439Y-007715X0138Y0000R000S3
317GND U2 -30_6 D0098PA00X+066701Y-007961X0138Y0000R000S3
317GND U2 -30_7 D0098PA00X+067193Y-007961X0138Y0000R000S3
317GND U2 -30_8 D0098PA00X+066455Y-008207X0138Y0000R000S3
317GND U2 -30_9 D0098PA00X+066947Y-008207X0138Y0000R000S3
317GND U2 -30_1 D0098PA00X+067439Y-008207X0138Y0000R000S3
317GND U2 -30_1 D0098PA00X+066701Y-008453X0138Y0000R000S3
317GND U2 -30_1 D0098PA00X+067193Y-008453X0138Y0000R000S3
3173_3V U5 -1 D0394PA00X+046339Y-019764X0669Y0669R000S0
317(U5-VBAT-PAD2) U5 -2 D0394PA00X+046339Y-020764X0669Y0000R000S0
317SDA U5 -3 D0394PA00X+046339Y-021764X0669Y0000R000S0
317SCL U5 -4 D0394PA00X+046339Y-022764X0669Y0000R000S0
317CD_PROBE U5 -5 D0394PA00X+046339Y-023764X0669Y0000R000S0
317GND U5 -6 D0394PA00X+046339Y-024764X0669Y0000R000S0
317GND U5 -7 D0197PA00X+062835Y-021063X0335Y0335R000S0
317GND U5 -8 D0197PA00X+078583Y-019488X0335Y0335R000S0
317GND U5 -9 D0197PA00X+078583Y-034646X0335Y0335R000S0
317GND U5 -10 D0197PA00X+062835Y-031693X0335Y0335R000S0
317GND U5 -11 D0197PA00X+046102Y-034646X0335Y0335R000S0
327GND D11 -1 A01X+046506Y-030000X0581Y0236R180S2 327GND D11 -1 A01X+046506Y-030000X0581Y0236R180S2
327VBAT D11 -2 A01X+046506Y-029252X0581Y0236R180S2 327VBAT D11 -2 A01X+046506Y-029252X0581Y0236R180S2
327NET-(D10-K) D11 -3 A01X+045768Y-029626X0581Y0236R180S2 327NET-(D10-K) D11 -3 A01X+045768Y-029626X0581Y0236R180S2
@ -206,9 +197,9 @@ P arrayDim N
327GND R16 -2 A01X+048091Y-016043X0315Y0374R180S2 327GND R16 -2 A01X+048091Y-016043X0315Y0374R180S2
327NET-(Q2-G) R22 -1 A01X+047805Y-034843X0315Y0374R000S2 327NET-(Q2-G) R22 -1 A01X+047805Y-034843X0315Y0374R000S2
327GND R22 -2 A01X+048455Y-034843X0315Y0374R000S2 327GND R22 -2 A01X+048455Y-034843X0315Y0374R000S2
327GND D5 -1 A01X+048027Y-023602X0581Y0236R180S2
3273_3V D5 -2 A01X+048027Y-022854X0581Y0236R180S2
327FLOW D5 -3 A01X+047288Y-023228X0581Y0236R180S2 327FLOW D5 -3 A01X+047288Y-023228X0581Y0236R180S2
3273_3V D5 -2 A01X+048027Y-022854X0581Y0236R180S2
327GND D5 -1 A01X+048027Y-023602X0581Y0236R180S2
327NET-(D2-K) D2 -1 A01X+043701Y-006570X0384Y0551R270S2 327NET-(D2-K) D2 -1 A01X+043701Y-006570X0384Y0551R270S2
327WARN_LED D2 -2 A01X+043701Y-005832X0384Y0551R270S2 327WARN_LED D2 -2 A01X+043701Y-005832X0384Y0551R270S2
327GND D6 -1 A01X+046043Y-016811X0581Y0236R180S2 327GND D6 -1 A01X+046043Y-016811X0581Y0236R180S2
@ -216,25 +207,25 @@ P arrayDim N
327TANK_SENSOR D6 -3 A01X+045305Y-016437X0581Y0236R180S2 327TANK_SENSOR D6 -3 A01X+045305Y-016437X0581Y0236R180S2
327NET-(CD1-A) R9 -1 A01X+058661Y-010305X0315Y0374R090S2 327NET-(CD1-A) R9 -1 A01X+058661Y-010305X0315Y0374R090S2
327WORKING R9 -2 A01X+058661Y-010955X0315Y0374R090S2 327WORKING R9 -2 A01X+058661Y-010955X0315Y0374R090S2
317GND J6 -1 D0394PA00X+051921Y-008228X0669Y0669R090S0 317GND J6 -1 D0394PA00X+041319Y-014661X0669Y0669R180S0
3173_3V J6 -2 D0394PA00X+050921Y-008228X0669Y0000R090S0 3173_3V J6 -2 D0394PA00X+041319Y-013661X0669Y0000R180S0
317SDA J6 -3 D0394PA00X+049921Y-008228X0669Y0000R090S0 317SDA J6 -3 D0394PA00X+041319Y-012661X0669Y0000R180S0
317SCL J6 -4 D0394PA00X+048921Y-008228X0669Y0000R090S0 317SCL J6 -4 D0394PA00X+041319Y-011661X0669Y0000R180S0
317SQW J6 -5 D0394PA00X+047921Y-008228X0669Y0000R090S0 317SQW J6 -5 D0394PA00X+041319Y-010661X0669Y0000R180S0
31732K J6 -6 D0394PA00X+046921Y-008228X0669Y0000R090S0 31732K J6 -6 D0394PA00X+041319Y-009661X0669Y0000R180S0
327NET-(D8-K) D8 -1 A01X+040945Y-033243X0384Y0551R270S2 327NET-(D8-K) D8 -1 A01X+040945Y-033243X0384Y0551R270S2
327NET-(D8-A) D8 -2 A01X+040945Y-032505X0384Y0551R270S2 327NET-(D8-A) D8 -2 A01X+040945Y-032505X0384Y0551R270S2
327NET-(J2-PIN_2) R2 -1 A01X+042943Y-014764X0315Y0374R000S2 327NET-(J2-PIN_2) R2 -1 A01X+046457Y-005581X0315Y0374R090S2
327GND R2 -2 A01X+043593Y-014764X0315Y0374R000S2 327GND R2 -2 A01X+046457Y-006230X0315Y0374R090S2
327USB_D+ U1 -1 A01X+044124Y-010945X0522Y0236R180S2 327USB_D+ U1 -1 A01X+050251Y-009286X0522Y0236R180S2
327GND U1 -2 A01X+044124Y-010571X0522Y0236R180S2 327GND U1 -2 A01X+050251Y-008912X0522Y0236R180S2
327USB_D- U1 -3 A01X+044124Y-010197X0522Y0236R180S2 327USB_D- U1 -3 A01X+050251Y-008538X0522Y0236R180S2
327SLASH}O2-PAD4) U1 -4 A01X+043228Y-010197X0522Y0236R180S2 327SLASH}O2-PAD4) U1 -4 A01X+049355Y-008538X0522Y0236R180S2
327USB_BUS U1 -5 A01X+043228Y-010571X0522Y0236R180S2 327USB_BUS U1 -5 A01X+049355Y-008912X0522Y0236R180S2
327SLASH}O1-PAD6) U1 -6 A01X+043228Y-010945X0522Y0236R180S2 327SLASH}O1-PAD6) U1 -6 A01X+049355Y-009286X0522Y0236R180S2
327GND D7 -1 A01X+048027Y-025669X0581Y0236R180S2
3273_3V D7 -2 A01X+048027Y-024921X0581Y0236R180S2
327TEMP D7 -3 A01X+047288Y-025295X0581Y0236R180S2 327TEMP D7 -3 A01X+047288Y-025295X0581Y0236R180S2
3273_3V D7 -2 A01X+048027Y-024921X0581Y0236R180S2
327GND D7 -1 A01X+048027Y-025669X0581Y0236R180S2
327VBAT C8 -1 A01X+042534Y-036024X0463Y0571R180S2 327VBAT C8 -1 A01X+042534Y-036024X0463Y0571R180S2
327GND C8 -2 A01X+041718Y-036024X0463Y0571R180S2 327GND C8 -2 A01X+041718Y-036024X0463Y0571R180S2
327NET-(R3-PAD1) R3 -1 A01X+057677Y-012933X0315Y0374R090S2 327NET-(R3-PAD1) R3 -1 A01X+057677Y-012933X0315Y0374R090S2
@ -270,59 +261,10 @@ P arrayDim N
327BOOT_SEL C5 -2 A01X+062992Y-009700X0423Y0374R270S2 327BOOT_SEL C5 -2 A01X+062992Y-009700X0423Y0374R270S2
327TANK_SENSOR R12 -1 A01X+047215Y-016831X0404Y0551R270S2 327TANK_SENSOR R12 -1 A01X+047215Y-016831X0404Y0551R270S2
3273_3V R12 -2 A01X+047215Y-016112X0404Y0551R270S2 3273_3V R12 -2 A01X+047215Y-016112X0404Y0551R270S2
327NET-(D2-K) R1 -1 A01X+044488Y-008317X0315Y0374R270S2 327NET-(D2-K) R1 -1 A01X+045935Y-007874X0315Y0374R000S2
327GND R1 -2 A01X+044488Y-007667X0315Y0374R270S2 327GND R1 -2 A01X+046585Y-007874X0315Y0374R000S2
327EXTRA_1 R20 -1 A01X+048455Y-031890X0315Y0374R180S2 327EXTRA_1 R20 -1 A01X+048455Y-031890X0315Y0374R180S2
327NET-(Q3-G) R20 -2 A01X+047805Y-031890X0315Y0374R180S2 327NET-(Q3-G) R20 -2 A01X+047805Y-031890X0315Y0374R180S2
327GND U2 -1 A01X+064094Y-005709X0591Y0354R000S2
3273_3V U2 -2 A01X+064094Y-006209X0591Y0354R000S2
327EN U2 -3 A01X+064094Y-006709X0591Y0354R000S2
327FLOW U2 -4 A01X+064094Y-007209X0591Y0354R000S2
327TANK_SENSOR U2 -5 A01X+064094Y-007709X0591Y0354R000S2
327EXTRA_1 U2 -6 A01X+064094Y-008209X0591Y0354R000S2
327ISDAY U2 -7 A01X+064094Y-008709X0591Y0354R000S2
327-(U2-IO0-PAD8) U2 -8 A01X+064094Y-009209X0591Y0354R000S2
327BOOT_SEL U2 -9 A01X+064094Y-009709X0591Y0354R000S2
327(U2-IO8-PAD10) U2 -10 A01X+064094Y-010209X0591Y0354R000S2
327LED_ENABLE U2 -11 A01X+064094Y-010709X0591Y0354R000S2
327ENABLE_TANK U2 -12 A01X+064094Y-011209X0591Y0354R000S2
327USB_D- U2 -13 A01X+064094Y-011709X0591Y0354R000S2
327USB_D+ U2 -14 A01X+064094Y-012209X0591Y0354R000S2
327BOOT_SEL U2 -15 A01X+070984Y-012209X0591Y0354R000S2
327TEMP U2 -16 A01X+070984Y-011709X0591Y0354R000S2
327SCL U2 -17 A01X+070984Y-011209X0591Y0354R000S2
327SDA U2 -18 A01X+070984Y-010709X0591Y0354R000S2
327WORKING U2 -19 A01X+070984Y-010209X0591Y0354R000S2
327CD_PROBE U2 -20 A01X+070984Y-009709X0591Y0354R000S2
327WARN_LED U2 -21 A01X+070984Y-009209X0591Y0354R000S2
327-(U2-NC-PAD22) U2 -22 A01X+070984Y-008709X0591Y0354R000S2
327EXTRA_2 U2 -23 A01X+070984Y-008209X0591Y0354R000S2
327ESP_RX U2 -24 A01X+070984Y-007709X0591Y0354R000S2
327ESP_TX U2 -25 A01X+070984Y-007209X0591Y0354R000S2
327CHARGE U2 -26 A01X+070984Y-006709X0591Y0354R000S2
327PUMP_ENABLE U2 -27 A01X+070984Y-006209X0591Y0354R000S2
327GND U2 -28 A01X+070984Y-005709X0591Y0354R000S2
327GND U2 -29_1 A01X+066455Y-007469X0315Y0315R000S2
327GND U2 -29_2 A01X+066947Y-007469X0315Y0315R000S2
327GND U2 -29_3 A01X+067439Y-007469X0315Y0315R000S2
327GND U2 -29_4 A01X+066455Y-007961X0315Y0315R000S2
327GND U2 -29_5 A01X+066947Y-007961X0315Y0315R000S2
327GND U2 -29_6 A01X+067439Y-007961X0315Y0315R000S2
327GND U2 -29_7 A01X+066455Y-008453X0315Y0315R000S2
327GND U2 -29_8 A01X+066947Y-008453X0315Y0315R000S2
327GND U2 -29_9 A01X+067439Y-008453X0315Y0315R000S2
317GND U2 -30_1 D0098PA00X+066701Y-007469X0138Y0000R000S3
317GND U2 -30_2 D0098PA00X+067193Y-007469X0138Y0000R000S3
317GND U2 -30_3 D0098PA00X+066455Y-007715X0138Y0000R000S3
317GND U2 -30_4 D0098PA00X+066947Y-007715X0138Y0000R000S3
317GND U2 -30_5 D0098PA00X+067439Y-007715X0138Y0000R000S3
317GND U2 -30_6 D0098PA00X+066701Y-007961X0138Y0000R000S3
317GND U2 -30_7 D0098PA00X+067193Y-007961X0138Y0000R000S3
317GND U2 -30_8 D0098PA00X+066455Y-008207X0138Y0000R000S3
317GND U2 -30_9 D0098PA00X+066947Y-008207X0138Y0000R000S3
317GND U2 -30_1 D0098PA00X+067439Y-008207X0138Y0000R000S3
317GND U2 -30_1 D0098PA00X+066701Y-008453X0138Y0000R000S3
317GND U2 -30_1 D0098PA00X+067193Y-008453X0138Y0000R000S3
317NET-(D10-K) Extra_-1 D0394PA00X+042283Y-029616X0669Y0787R270S0 317NET-(D10-K) Extra_-1 D0394PA00X+042283Y-029616X0669Y0787R270S0
317VBAT Extra_-2 D0394PA00X+042283Y-028632X0669Y0787R270S0 317VBAT Extra_-2 D0394PA00X+042283Y-028632X0669Y0787R270S0
327GND D1 -1 A01X+047441Y-014469X0581Y0236R180S2 327GND D1 -1 A01X+047441Y-014469X0581Y0236R180S2
@ -334,19 +276,8 @@ P arrayDim N
327NET-(Q2-G) R23 -2 A01X+047805Y-035728X0315Y0374R180S2 327NET-(Q2-G) R23 -2 A01X+047805Y-035728X0315Y0374R180S2
327VBAT R18 -1 A01X+046033Y-028445X0315Y0374R000S2 327VBAT R18 -1 A01X+046033Y-028445X0315Y0374R000S2
327NET-(D10-A) R18 -2 A01X+046683Y-028445X0315Y0374R000S2 327NET-(D10-A) R18 -2 A01X+046683Y-028445X0315Y0374R000S2
3173_3V U5 -1 D0394PA00X+046339Y-019764X0669Y0669R000S0 327NET-(J2-PIN_5) R4 -1 A01X+053150Y-005581X0315Y0374R090S2
317(U5-VBAT-PAD2) U5 -2 D0394PA00X+046339Y-020764X0669Y0000R000S0 327GND R4 -2 A01X+053150Y-006230X0315Y0374R090S2
317SDA U5 -3 D0394PA00X+046339Y-021764X0669Y0000R000S0
317SCL U5 -4 D0394PA00X+046339Y-022764X0669Y0000R000S0
317CD_PROBE U5 -5 D0394PA00X+046339Y-023764X0669Y0000R000S0
317GND U5 -6 D0394PA00X+046339Y-024764X0669Y0000R000S0
317GND U5 -7 D0394PA00X+062835Y-021063X0669Y0669R000S0
317GND U5 -8 D0394PA00X+078583Y-019488X0669Y0669R000S0
317GND U5 -9 D0394PA00X+078583Y-034646X0669Y0669R000S0
317GND U5 -10 D0394PA00X+062835Y-031693X0669Y0669R000S0
317GND U5 -11 D0394PA00X+046102Y-034646X0669Y0669R000S0
327NET-(J2-PIN_3) R4 -1 A01X+042943Y-014134X0315Y0374R000S2
327GND R4 -2 A01X+043593Y-014134X0315Y0374R000S2
327NET-(D10-K) D10 -1 A01X+040945Y-029542X0384Y0551R270S2 327NET-(D10-K) D10 -1 A01X+040945Y-029542X0384Y0551R270S2
327NET-(D10-A) D10 -2 A01X+040945Y-028804X0384Y0551R270S2 327NET-(D10-A) D10 -2 A01X+040945Y-028804X0384Y0551R270S2
317NET-(J4-PIN_1) J4 -1 D0295PA00X+042323Y-017638X0472Y0689R270S0 317NET-(J4-PIN_1) J4 -1 D0295PA00X+042323Y-017638X0472Y0689R270S0
@ -366,12 +297,12 @@ P arrayDim N
327NET-(CD1-A) CD1 -2 A01X+045177Y-005832X0384Y0551R270S2 327NET-(CD1-A) CD1 -2 A01X+045177Y-005832X0384Y0551R270S2
327GND C4 -1 A01X+060630Y-011166X0423Y0374R270S2 327GND C4 -1 A01X+060630Y-011166X0423Y0374R270S2
327NET-(C4-PAD2) C4 -2 A01X+060630Y-010487X0423Y0374R270S2 327NET-(C4-PAD2) C4 -2 A01X+060630Y-010487X0423Y0374R270S2
317USB_BUS J2 -1 D0295PA00X+041260Y-009803X0472Y0689R090S0 317USB_BUS J2 -1 D0295PA00X+047835Y-006142X0472Y0689R000S0
317NET-(J2-PIN_2) J2 -2 D0295PA00X+041260Y-010591X0472Y0689R090S0 317NET-(J2-PIN_2) J2 -2 D0295PA00X+048622Y-006142X0472Y0689R000S0
317NET-(J2-PIN_3) J2 -3 D0295PA00X+041260Y-011378X0472Y0689R090S0 317USB_D+ J2 -3 D0295PA00X+049409Y-006142X0472Y0689R000S0
317USB_D- J2 -4 D0295PA00X+041260Y-012165X0472Y0689R090S0 317USB_D- J2 -4 D0295PA00X+050197Y-006142X0472Y0689R000S0
317USB_D+ J2 -5 D0295PA00X+041260Y-012953X0472Y0689R090S0 317NET-(J2-PIN_5) J2 -5 D0295PA00X+050984Y-006142X0472Y0689R000S0
317GND J2 -6 D0295PA00X+041260Y-013740X0472Y0689R090S0 317GND J2 -6 D0295PA00X+051772Y-006142X0472Y0689R000S0
3273_3V R15 -1 A01X+049680Y-023228X0315Y0374R180S2 3273_3V R15 -1 A01X+049680Y-023228X0315Y0374R180S2
327FLOW R15 -2 A01X+049031Y-023228X0315Y0374R180S2 327FLOW R15 -2 A01X+049031Y-023228X0315Y0374R180S2
327NET-(Q1-G) Q1 -1 A01X+048770Y-016831X0354Y0315R090S2 327NET-(Q1-G) Q1 -1 A01X+048770Y-016831X0354Y0315R090S2
@ -382,9 +313,9 @@ P arrayDim N
327NET-(Q2-G) Q2 -1 A01X+047776Y-034142X0354Y0315R270S2 327NET-(Q2-G) Q2 -1 A01X+047776Y-034142X0354Y0315R270S2
327GND Q2 -2 A01X+048524Y-034142X0354Y0315R270S2 327GND Q2 -2 A01X+048524Y-034142X0354Y0315R270S2
327NET-(D8-K) Q2 -3 A01X+048150Y-033354X0354Y0315R270S2 327NET-(D8-K) Q2 -3 A01X+048150Y-033354X0354Y0315R270S2
317VBAT U7 -1 D0433PA00X+042795Y-037835X0750Y0787R180S0 317VBAT U7 -1 D0433PA00X+042795Y-040315X0750Y0787R180S0
317GND U7 -2 D0433PA00X+041795Y-037835X0750Y0787R180S0 317GND U7 -2 D0433PA00X+041795Y-040315X0750Y0787R180S0
3173_3V U7 -3 D0433PA00X+040795Y-037835X0750Y0787R180S0 3173_3V U7 -3 D0433PA00X+040795Y-040315X0750Y0787R180S0
317GND J1 -1 D0394PA00X+044882Y-012047X0669Y0669R000S0 317GND J1 -1 D0394PA00X+044882Y-012047X0669Y0669R000S0
317SCL J1 -2 D0394PA00X+044882Y-013047X0669Y0000R000S0 317SCL J1 -2 D0394PA00X+044882Y-013047X0669Y0000R000S0
317SDA J1 -3 D0394PA00X+044882Y-014047X0669Y0000R000S0 317SDA J1 -3 D0394PA00X+044882Y-014047X0669Y0000R000S0

View File

@ -5,7 +5,7 @@ target = "riscv32imac-esp-espidf"
[target.riscv32imac-esp-espidf] [target.riscv32imac-esp-espidf]
linker = "ldproxy" linker = "ldproxy"
#runner = "espflash flash --monitor --baud 921600 --partition-table partitions.csv -b no-reset" # Select this runner in case of usb ttl #runner = "espflash flash --monitor --baud 921600 --partition-table partitions.csv -b no-reset" # Select this runner in case of usb ttl
runner = "espflash flash --monitor" runner = "espflash flash --monitor --baud 921600 --flash-size 16mb --partition-table partitions.csv"
#runner = "cargo runner" #runner = "cargo runner"

View File

@ -1,11 +1,6 @@
<component name="InspectionProjectProfileManager"> <component name="InspectionProjectProfileManager">
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
<Languages>
<language minSize="102" name="Rust" />
</Languages>
</inspection_tool>
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile> </profile>
</component> </component>

View File

@ -14,8 +14,11 @@ debug = true
overflow-checks = true overflow-checks = true
panic = "abort" panic = "abort"
incremental = true incremental = true
opt-level = 2 opt-level = "s"
[profile.dev.build-override]
opt-level = 1
incremental = true
[package.metadata.cargo_runner] [package.metadata.cargo_runner]
# The string `$TARGET_FILE` will be replaced with the path from cargo. # The string `$TARGET_FILE` will be replaced with the path from cargo.
@ -51,7 +54,7 @@ esp-idf-sys = { version = "0.36.1", features = ["binstart", "native"] }
esp-idf-svc = { version = "0.51.0", default-features = false } esp-idf-svc = { version = "0.51.0", default-features = false }
embedded-hal = "1.0.0" embedded-hal = "1.0.0"
heapless = { version = "0.8", features = ["serde"] } heapless = { version = "0.8", features = ["serde"] }
embedded-hal-bus = { version = "0.3.0", features = ["std"] } embedded-hal-bus = { version = "0.2.0", features = ["std"] }
#Hardware additional driver #Hardware additional driver
ds18b20 = "0.1.1" ds18b20 = "0.1.1"
@ -71,11 +74,11 @@ serde_json = "1.0.108"
#timezone #timezone
chrono = { version = "0.4.23", default-features = false , features = ["iana-time-zone" , "alloc", "serde"] } chrono = { version = "0.4.23", default-features = false , features = ["iana-time-zone" , "alloc", "serde"] }
chrono-tz = { version = "0.10.3", default-features = false, features = ["filter-by-regex"] } chrono-tz = {version="0.8.0", default-features = false , features = [ "filter-by-regex" ]}
eeprom24x = "0.7.2" eeprom24x = "0.7.2"
url = "2.5.3" url = "2.5.3"
crc = "3.2.1" crc = "3.2.1"
bincode = "2.0.1" bincode = "1.3.3"
ringbuffer = "0.15.0" ringbuffer = "0.15.0"
text-template = "0.1.0" text-template = "0.1.0"
strum_macros = "0.27.0" strum_macros = "0.27.0"
@ -83,7 +86,6 @@ esp-ota = { version = "0.2.2", features = ["log"] }
unit-enum = "1.4.1" unit-enum = "1.4.1"
pca9535 = { version = "2.0.0", features = ["std"] } pca9535 = { version = "2.0.0", features = ["std"] }
ina219 = { version = "0.2.0", features = ["std"] } ina219 = { version = "0.2.0", features = ["std"] }
embedded-storage = "=0.3.1"
[patch.crates-io] [patch.crates-io]

View File

@ -1,5 +0,0 @@
partition_table = "partitions.csv"
[connection]
serial = "/dev/ttyACM0"
[flash]
size = "16MB"

View File

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

View File

@ -240,6 +240,14 @@ impl Esp<'_> {
println!("Wrote config config {:?}", config); println!("Wrote config config {:?}", config);
anyhow::Ok(()) anyhow::Ok(())
} }
pub(crate) fn delete_config(&self) -> anyhow::Result<()> {
let config = Path::new(Self::CONFIG_FILE);
if config.exists() {
println!("Removing config");
fs::remove_file(config)?
}
anyhow::Ok(())
}
pub(crate) fn mount_file_system(&mut self) -> anyhow::Result<()> { pub(crate) fn mount_file_system(&mut self) -> anyhow::Result<()> {
log(LogMessage::MountingFilesystem, 0, 0, "", ""); log(LogMessage::MountingFilesystem, 0, 0, "", "");
let base_path = CString::new("/spiffs")?; let base_path = CString::new("/spiffs")?;
@ -300,7 +308,10 @@ impl Esp<'_> {
OkStd(file) => { OkStd(file) => {
let f = FileInfo { let f = FileInfo {
filename: file.file_name().into_string().unwrap(), filename: file.file_name().into_string().unwrap(),
size: file.metadata().map(|it| it.len()).unwrap_or_default() size: file
.metadata()
.map(|it| it.len())
.unwrap_or_default()
as usize, as usize,
}; };
result.push(f); result.push(f);

View File

@ -1,7 +1,5 @@
use crate::hal::esp::Esp; use crate::hal::esp::Esp;
use crate::hal::rtc::{BackupHeader, RTCModuleInteraction}; use crate::hal::{deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor};
use crate::hal::water::TankSensor;
use crate::hal::{deep_sleep, BoardInteraction, FreePeripherals, Sensor};
use crate::{ use crate::{
config::PlantControllerConfig, config::PlantControllerConfig,
hal::battery::{BatteryInteraction, NoBatteryMonitor}, hal::battery::{BatteryInteraction, NoBatteryMonitor},
@ -18,31 +16,6 @@ pub struct Initial<'a> {
pub(crate) esp: Esp<'a>, pub(crate) esp: Esp<'a>,
pub(crate) config: PlantControllerConfig, pub(crate) config: PlantControllerConfig,
pub(crate) battery: Box<dyn BatteryInteraction + Send>, pub(crate) battery: Box<dyn BatteryInteraction + Send>,
pub rtc: Box<dyn RTCModuleInteraction + Send>,
}
struct NoRTC {}
impl RTCModuleInteraction for NoRTC {
fn get_backup_info(&mut self) -> Result<BackupHeader> {
bail!("Please configure board revision")
}
fn get_backup_config(&mut self) -> Result<Vec<u8>> {
bail!("Please configure board revision")
}
fn backup_config(&mut self, _bytes: &[u8]) -> Result<()> {
bail!("Please configure board revision")
}
fn get_rtc_time(&mut self) -> Result<DateTime<Utc>> {
bail!("Please configure board revision")
}
fn set_rtc_time(&mut self, _time: &DateTime<Utc>) -> Result<()> {
bail!("Please configure board revision")
}
} }
pub(crate) fn create_initial_board( pub(crate) fn create_initial_board(
@ -63,16 +36,11 @@ pub(crate) fn create_initial_board(
config, config,
esp, esp,
battery: Box::new(NoBatteryMonitor {}), battery: Box::new(NoBatteryMonitor {}),
rtc: Box::new(NoRTC {}),
}; };
Ok(Box::new(v)) Ok(Box::new(v))
} }
impl<'a> BoardInteraction<'a> for Initial<'a> { impl<'a> BoardInteraction<'a> for Initial<'a> {
fn get_tank_sensor(&mut self) -> Option<&mut TankSensor<'a>> {
None
}
fn get_esp(&mut self) -> &mut Esp<'a> { fn get_esp(&mut self) -> &mut Esp<'a> {
&mut self.esp &mut self.esp
} }
@ -85,10 +53,6 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
&mut self.battery &mut self.battery
} }
fn get_rtc_module(&mut self) -> &mut Box<dyn RTCModuleInteraction + Send> {
&mut self.rtc
}
fn set_charge_indicator(&mut self, _charging: bool) -> Result<()> { fn set_charge_indicator(&mut self, _charging: bool) -> Result<()> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }
@ -96,9 +60,30 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
fn deep_sleep(&mut self, duration_in_ms: u64) -> ! { fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
deep_sleep(duration_in_ms) deep_sleep(duration_in_ms)
} }
fn get_backup_info(&mut self) -> Result<BackupHeader> {
bail!("Please configure board revision")
}
fn get_backup_config(&mut self) -> Result<Vec<u8>> {
bail!("Please configure board revision")
}
fn backup_config(&mut self, _bytes: &[u8]) -> Result<()> {
bail!("Please configure board revision")
}
fn is_day(&self) -> bool { fn is_day(&self) -> bool {
false false
} }
fn water_temperature_c(&mut self) -> Result<f32> {
bail!("Please configure board revision")
}
fn tank_sensor_voltage(&mut self) -> Result<f32> {
bail!("Please configure board revision")
}
fn light(&mut self, _enable: bool) -> Result<()> { fn light(&mut self, _enable: bool) -> Result<()> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }
@ -106,11 +91,9 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
fn pump(&mut self, _plant: usize, _enable: bool) -> Result<()> { fn pump(&mut self, _plant: usize, _enable: bool) -> Result<()> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }
fn fault(&mut self, _plant: usize, _enable: bool) -> Result<()> { fn fault(&mut self, _plant: usize, _enable: bool) -> Result<()> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }
fn measure_moisture_hz(&mut self, _plant: usize, _sensor: Sensor) -> Result<f32> { fn measure_moisture_hz(&mut self, _plant: usize, _sensor: Sensor) -> Result<f32> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }
@ -119,6 +102,17 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
let _ = self.general_fault.set_state(enable.into()); let _ = self.general_fault.set_state(enable.into());
} }
fn factory_reset(&mut self) -> Result<()> {
bail!("Please configure board revision")
}
fn get_rtc_time(&mut self) -> Result<DateTime<Utc>> {
bail!("Please configure board revision")
}
fn set_rtc_time(&mut self, _time: &DateTime<Utc>) -> Result<()> {
bail!("Please configure board revision")
}
fn test_pump(&mut self, _plant: usize) -> Result<()> { fn test_pump(&mut self, _plant: usize) -> Result<()> {
bail!("Please configure board revision") bail!("Please configure board revision")
} }

View File

@ -1,13 +1,9 @@
pub(crate) mod battery; pub(crate) mod battery;
mod esp; mod esp;
mod initial_hal; mod initial_hal;
mod rtc;
mod v3_hal; mod v3_hal;
mod v4_hal; mod v4_hal;
mod water;
use crate::hal::rtc::{DS3231Module, RTCModuleInteraction};
use crate::hal::water::TankSensor;
use crate::{ use crate::{
config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig}, config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig},
hal::{ hal::{
@ -19,8 +15,7 @@ use crate::{
use anyhow::{Ok, Result}; use anyhow::{Ok, Result};
use battery::BQ34Z100G1; use battery::BQ34Z100G1;
use bq34z100::Bq34z100g1Driver; use bq34z100::Bq34z100g1Driver;
use ds323x::{DateTimeAccess, Ds323x}; use chrono::{DateTime, Utc};
use eeprom24x::{Eeprom24x, SlaveAddr, Storage};
use embedded_hal_bus::i2c::MutexDevice; use embedded_hal_bus::i2c::MutexDevice;
use esp_idf_hal::{ use esp_idf_hal::{
adc::ADC1, adc::ADC1,
@ -44,6 +39,7 @@ use esp_idf_sys::{
use esp_ota::mark_app_valid; use esp_ota::mark_app_valid;
use measurements::{Current, Voltage}; use measurements::{Current, Voltage};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use std::result::Result::Ok as OkStd; use std::result::Result::Ok as OkStd;
use std::sync::Mutex; use std::sync::Mutex;
use std::time::Duration; use std::time::Duration;
@ -56,6 +52,8 @@ const TANK_MULTI_SAMPLE: usize = 11;
pub static I2C_DRIVER: Lazy<Mutex<I2cDriver<'static>>> = Lazy::new(PlantHal::create_i2c); pub static I2C_DRIVER: Lazy<Mutex<I2cDriver<'static>>> = Lazy::new(PlantHal::create_i2c);
const X25: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC);
fn deep_sleep(duration_in_ms: u64) -> ! { fn deep_sleep(duration_in_ms: u64) -> ! {
unsafe { unsafe {
//if we don't do this here, we might just revert newly flashed firmware //if we don't do this here, we might just revert newly flashed firmware
@ -86,23 +84,35 @@ pub struct HAL<'a> {
pub board_hal: Box<dyn BoardInteraction<'a> + Send>, pub board_hal: Box<dyn BoardInteraction<'a> + Send>,
} }
#[derive(Serialize, Deserialize, PartialEq, Debug, Default)]
pub struct BackupHeader {
pub timestamp: i64,
crc16: u16,
pub size: usize,
}
pub trait BoardInteraction<'a> { pub trait BoardInteraction<'a> {
fn get_tank_sensor(&mut self) -> Option<&mut TankSensor<'a>>;
fn get_esp(&mut self) -> &mut Esp<'a>; fn get_esp(&mut self) -> &mut Esp<'a>;
fn get_config(&mut self) -> &PlantControllerConfig; fn get_config(&mut self) -> &PlantControllerConfig;
fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send>; fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send>;
fn get_rtc_module(&mut self) -> &mut Box<dyn RTCModuleInteraction + Send>;
fn set_charge_indicator(&mut self, charging: bool) -> Result<()>; fn set_charge_indicator(&mut self, charging: bool) -> Result<()>;
fn deep_sleep(&mut self, duration_in_ms: u64) -> !; fn deep_sleep(&mut self, duration_in_ms: u64) -> !;
fn get_backup_info(&mut self) -> Result<BackupHeader>;
fn get_backup_config(&mut self) -> Result<Vec<u8>>;
fn backup_config(&mut self, bytes: &[u8]) -> Result<()>;
fn is_day(&self) -> bool; fn is_day(&self) -> bool;
//should be multsampled //should be multsampled
fn water_temperature_c(&mut self) -> Result<f32>;
/// return median tank sensor value in milli volt
fn tank_sensor_voltage(&mut self) -> Result<f32>;
fn light(&mut self, enable: bool) -> Result<()>; fn light(&mut self, enable: bool) -> Result<()>;
fn pump(&mut self, plant: usize, enable: bool) -> Result<()>; fn pump(&mut self, plant: usize, enable: bool) -> Result<()>;
fn fault(&mut self, plant: usize, enable: bool) -> Result<()>; fn fault(&mut self, plant: usize, enable: bool) -> Result<()>;
fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32>; fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32>;
fn general_fault(&mut self, enable: bool); fn general_fault(&mut self, enable: bool);
fn factory_reset(&mut self) -> Result<()>;
fn get_rtc_time(&mut self) -> Result<DateTime<Utc>>;
fn set_rtc_time(&mut self, time: &DateTime<Utc>) -> Result<()>;
fn test_pump(&mut self, plant: usize) -> Result<()>; fn test_pump(&mut self, plant: usize) -> Result<()>;
fn test(&mut self) -> Result<()>; fn test(&mut self) -> Result<()>;
fn set_config(&mut self, config: PlantControllerConfig) -> Result<()>; fn set_config(&mut self, config: PlantControllerConfig) -> Result<()>;
@ -110,18 +120,6 @@ pub trait BoardInteraction<'a> {
fn get_mptt_current(&mut self) -> anyhow::Result<Current>; fn get_mptt_current(&mut self) -> anyhow::Result<Current>;
} }
impl dyn BoardInteraction<'_> {
//the counter is just some arbitrary number that increases whenever some progress was made, try to keep the updates < 10 per second for ux reasons
fn progress(&mut self, counter: u32) {
let even = counter % 2 == 0;
let current = counter / (PLANT_COUNT as u32);
for led in 0..PLANT_COUNT {
self.fault(led, current == led as u32).unwrap();
}
let _ = self.general_fault(even.into());
}
}
#[allow(dead_code)] #[allow(dead_code)]
pub struct FreePeripherals { pub struct FreePeripherals {
pub gpio0: Gpio0, pub gpio0: Gpio0,
@ -261,30 +259,6 @@ impl PlantHal {
let config = esp.load_config(); let config = esp.load_config();
println!("Init rtc driver");
let mut rtc = Ds323x::new_ds3231(MutexDevice::new(&I2C_DRIVER));
println!("Init rtc eeprom driver");
let eeprom = {
Eeprom24x::new_24x32(
MutexDevice::new(&I2C_DRIVER),
SlaveAddr::Alternative(true, true, true),
)
};
let rtc_time = rtc.datetime();
match rtc_time {
OkStd(tt) => {
println!("Rtc Module reports time at UTC {}", tt);
}
Err(err) => {
println!("Rtc Module could not be read {:?}", err);
}
}
let storage = Storage::new(eeprom, Delay::new(1000));
let rtc_module: Box<dyn RTCModuleInteraction + Send> =
Box::new(DS3231Module { rtc, storage }) as Box<dyn RTCModuleInteraction + Send>;
let hal = match config { let hal = match config {
Result::Ok(config) => { Result::Ok(config) => {
let battery_interaction: Box<dyn BatteryInteraction + Send> = let battery_interaction: Box<dyn BatteryInteraction + Send> =
@ -322,10 +296,10 @@ impl PlantHal {
initial_hal::create_initial_board(free_pins, fs_mount_error, config, esp)? initial_hal::create_initial_board(free_pins, fs_mount_error, config, esp)?
} }
BoardVersion::V3 => { BoardVersion::V3 => {
v3_hal::create_v3(free_pins, esp, config, battery_interaction, rtc_module)? v3_hal::create_v3(free_pins, esp, config, battery_interaction)?
} }
BoardVersion::V4 => { BoardVersion::V4 => {
v4_hal::create_v4(free_pins, esp, config, battery_interaction, rtc_module)? v4_hal::create_v4(free_pins, esp, config, battery_interaction)?
} }
}; };

View File

@ -1,132 +0,0 @@
use anyhow::{anyhow, bail};
use bincode::config::Configuration;
use bincode::{config, Decode, Encode};
use chrono::{DateTime, Utc};
use ds323x::{DateTimeAccess, Ds323x};
use eeprom24x::addr_size::TwoBytes;
use eeprom24x::page_size::B32;
use eeprom24x::unique_serial::No;
use eeprom24x::Storage;
use embedded_hal_bus::i2c::MutexDevice;
use embedded_storage::ReadStorage as embedded_storage_ReadStorage;
use embedded_storage::Storage as embedded_storage_Storage;
use esp_idf_hal::delay::Delay;
use esp_idf_hal::i2c::I2cDriver;
use serde::{Deserialize, Serialize};
use std::result::Result::Ok as OkStd;
const X25: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC);
const CONFIG: Configuration = config::standard();
pub trait RTCModuleInteraction {
fn get_backup_info(&mut self) -> anyhow::Result<BackupHeader>;
fn get_backup_config(&mut self) -> anyhow::Result<Vec<u8>>;
fn backup_config(&mut self, bytes: &[u8]) -> anyhow::Result<()>;
fn get_rtc_time(&mut self) -> anyhow::Result<DateTime<Utc>>;
fn set_rtc_time(&mut self, time: &DateTime<Utc>) -> anyhow::Result<()>;
}
const BACKUP_HEADER_MAX_SIZE: usize = 64;
#[derive(Serialize, Deserialize, PartialEq, Debug, Default, Encode, Decode)]
pub struct BackupHeader {
pub timestamp: i64,
crc16: u16,
pub size: u16,
}
pub struct DS3231Module<'a> {
pub(crate) rtc:
Ds323x<ds323x::interface::I2cInterface<MutexDevice<'a, I2cDriver<'a>>>, ds323x::ic::DS3231>,
pub(crate) storage: Storage<MutexDevice<'a, I2cDriver<'a>>, B32, TwoBytes, No, Delay>,
}
impl RTCModuleInteraction for DS3231Module<'_> {
fn get_backup_info(&mut self) -> anyhow::Result<BackupHeader> {
let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE];
self.storage
.read(0, &mut header_page_buffer)
.map_err(|err| anyhow!("Error reading eeprom header {:?}", err))?;
let (header, len): (BackupHeader, usize) =
bincode::decode_from_slice(&header_page_buffer[..], CONFIG)?;
println!("Raw header is {:?} with size {}", header_page_buffer, len);
anyhow::Ok(header)
}
fn get_backup_config(&mut self) -> anyhow::Result<Vec<u8>> {
let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE];
self.storage
.read(0, &mut header_page_buffer)
.map_err(|err| anyhow!("Error reading eeprom header {:?}", err))?;
let (header, _header_size): (BackupHeader, usize) =
bincode::decode_from_slice(&header_page_buffer[..], CONFIG)?;
let mut data_buffer = vec![0_u8; header.size as usize];
//read the specified number of bytes after the header
self.storage
.read(BACKUP_HEADER_MAX_SIZE as u32, &mut data_buffer)
.map_err(|err| anyhow!("Error reading eeprom data {:?}", err))?;
let checksum = X25.checksum(&data_buffer);
if checksum != header.crc16 {
bail!(
"Invalid checksum, got {} but expected {}",
checksum,
header.crc16
);
}
anyhow::Ok(data_buffer)
}
fn backup_config(&mut self, bytes: &[u8]) -> anyhow::Result<()> {
let mut header_page_buffer = [0_u8; BACKUP_HEADER_MAX_SIZE];
let time = self.get_rtc_time()?.timestamp_millis();
let checksum = X25.checksum(bytes);
let header = BackupHeader {
crc16: checksum,
timestamp: time,
size: bytes.len() as u16,
};
let config = config::standard();
let encoded = bincode::encode_into_slice(&header, &mut header_page_buffer, config)?;
println!(
"Raw header is {:?} with size {}",
header_page_buffer, encoded
);
self.storage
.write(0, &header_page_buffer)
.map_err(|err| anyhow!("Error writing header {:?}", err))?;
//write rest after the header
self.storage
.write(BACKUP_HEADER_MAX_SIZE as u32, &bytes)
.map_err(|err| anyhow!("Error writing body {:?}", err))?;
anyhow::Ok(())
}
fn get_rtc_time(&mut self) -> anyhow::Result<DateTime<Utc>> {
match self.rtc.datetime() {
OkStd(rtc_time) => anyhow::Ok(rtc_time.and_utc()),
Err(err) => {
bail!("Error getting rtc time {:?}", err)
}
}
}
fn set_rtc_time(&mut self, time: &DateTime<Utc>) -> anyhow::Result<()> {
let naive_time = time.naive_utc();
match self.rtc.set_datetime(&naive_time) {
OkStd(_) => anyhow::Ok(()),
Err(err) => {
bail!("Error getting rtc time {:?}", err)
}
}
}
}

View File

@ -1,21 +1,33 @@
use crate::hal::rtc::RTCModuleInteraction;
use crate::hal::water::TankSensor;
use crate::hal::{ use crate::hal::{
deep_sleep, BoardInteraction, FreePeripherals, Sensor, PLANT_COUNT, REPEAT_MOIST_MEASURE, deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT,
REPEAT_MOIST_MEASURE, TANK_MULTI_SAMPLE, X25,
}; };
use crate::log::{log, LogMessage}; use crate::log::{log, LogMessage};
use crate::{ use crate::{
config::PlantControllerConfig, config::PlantControllerConfig,
hal::{battery::BatteryInteraction, esp::Esp}, hal::{battery::BatteryInteraction, esp::Esp},
}; };
use anyhow::{bail, Ok, Result}; use anyhow::{anyhow, bail, Ok, Result};
use chrono::{DateTime, Utc};
use ds18b20::Ds18b20;
use ds323x::{DateTimeAccess, Ds323x};
use eeprom24x::{Eeprom24x, Eeprom24xTrait, SlaveAddr};
use embedded_hal::digital::OutputPin; use embedded_hal::digital::OutputPin;
use embedded_hal_bus::i2c::MutexDevice;
use esp_idf_hal::{ use esp_idf_hal::{
gpio::{AnyInputPin, IOPin, InputOutput, PinDriver, Pull}, adc::{
attenuation,
oneshot::{config::AdcChannelConfig, AdcChannelDriver, AdcDriver},
Resolution,
},
delay::Delay,
gpio::{AnyInputPin, Gpio5, IOPin, InputOutput, PinDriver, Pull},
i2c::I2cDriver,
pcnt::{PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex}, pcnt::{PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex},
}; };
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay}; use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError};
use measurements::{Current, Voltage}; use measurements::{Current, Voltage};
use one_wire_bus::OneWire;
use plant_ctrl2::sipo::ShiftRegister40; use plant_ctrl2::sipo::ShiftRegister40;
use std::result::Result::Ok as OkStd; use std::result::Result::Ok as OkStd;
@ -67,7 +79,6 @@ const FAULT_2: usize = 23;
pub struct V3<'a> { pub struct V3<'a> {
config: PlantControllerConfig, config: PlantControllerConfig,
battery_monitor: Box<dyn BatteryInteraction + Send>, battery_monitor: Box<dyn BatteryInteraction + Send>,
rtc_module: Box<dyn RTCModuleInteraction + Send>,
esp: Esp<'a>, esp: Esp<'a>,
shift_register: ShiftRegister40< shift_register: ShiftRegister40<
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
@ -76,12 +87,22 @@ pub struct V3<'a> {
>, >,
_shift_register_enable_invert: _shift_register_enable_invert:
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Output>, PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Output>,
tank_sensor: TankSensor<'a>, tank_channel: AdcChannelDriver<'a, Gpio5, AdcDriver<'a, esp_idf_hal::adc::ADC1>>,
solar_is_day: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>, solar_is_day: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
main_pump: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, main_pump: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
tank_power: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
signal_counter: PcntDriver<'a>, signal_counter: PcntDriver<'a>,
one_wire_bus: OneWire<PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>>,
rtc:
Ds323x<ds323x::interface::I2cInterface<MutexDevice<'a, I2cDriver<'a>>>, ds323x::ic::DS3231>,
eeprom: Eeprom24x<
MutexDevice<'a, I2cDriver<'a>>,
eeprom24x::page_size::B32,
eeprom24x::addr_size::TwoBytes,
eeprom24x::unique_serial::No,
>,
} }
pub(crate) fn create_v3( pub(crate) fn create_v3(
@ -89,7 +110,6 @@ pub(crate) fn create_v3(
esp: Esp<'static>, esp: Esp<'static>,
config: PlantControllerConfig, config: PlantControllerConfig,
battery_monitor: Box<dyn BatteryInteraction + Send>, battery_monitor: Box<dyn BatteryInteraction + Send>,
rtc_module: Box<dyn RTCModuleInteraction + Send>,
) -> Result<Box<dyn BoardInteraction<'static> + Send>> { ) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
let mut clock = PinDriver::input_output(peripherals.gpio15.downgrade())?; let mut clock = PinDriver::input_output(peripherals.gpio15.downgrade())?;
clock.set_pull(Pull::Floating)?; clock.set_pull(Pull::Floating)?;
@ -121,15 +141,39 @@ pub(crate) fn create_v3(
let ms4 = &mut shift_register.decompose()[MS_4]; let ms4 = &mut shift_register.decompose()[MS_4];
ms4.set_high()?; ms4.set_high()?;
let one_wire_pin = peripherals.gpio18.downgrade(); println!("Init battery driver");
let tank_power_pin = peripherals.gpio11.downgrade();
let tank_sensor = TankSensor::create( println!("Init rtc driver");
one_wire_pin, let mut rtc = Ds323x::new_ds3231(MutexDevice::new(&I2C_DRIVER));
peripherals.adc1,
peripherals.gpio5, println!("Init rtc eeprom driver");
tank_power_pin, let mut eeprom = {
); Eeprom24x::new_24x32(
MutexDevice::new(&I2C_DRIVER),
SlaveAddr::Alternative(true, true, true),
)
};
let mut one_wire_pin = PinDriver::input_output_od(peripherals.gpio18.downgrade())?;
one_wire_pin.set_pull(Pull::Floating)?;
let rtc_time = rtc.datetime();
match rtc_time {
OkStd(tt) => {
println!("Rtc Module reports time at UTC {}", tt);
}
Err(err) => {
println!("Rtc Module could not be read {:?}", err);
}
}
match eeprom.read_byte(0) {
OkStd(byte) => {
println!("Read first byte with status {}", byte);
}
Err(err) => {
println!("Eeprom could not read first byte {:?}", err);
}
}
let mut signal_counter = PcntDriver::new( let mut signal_counter = PcntDriver::new(
peripherals.pcnt0, peripherals.pcnt0,
@ -153,6 +197,15 @@ pub(crate) fn create_v3(
}, },
)?; )?;
let adc_config = AdcChannelConfig {
attenuation: attenuation::DB_11,
resolution: Resolution::Resolution12Bit,
calibration: esp_idf_hal::adc::oneshot::config::Calibration::Curve,
};
let tank_driver = AdcDriver::new(peripherals.adc1)?;
let tank_channel: AdcChannelDriver<Gpio5, AdcDriver<esp_idf_hal::adc::ADC1>> =
AdcChannelDriver::new(tank_driver, peripherals.gpio5, &adc_config)?;
let mut solar_is_day = PinDriver::input(peripherals.gpio7.downgrade())?; let mut solar_is_day = PinDriver::input(peripherals.gpio7.downgrade())?;
solar_is_day.set_pull(Pull::Floating)?; solar_is_day.set_pull(Pull::Floating)?;
@ -162,11 +215,15 @@ pub(crate) fn create_v3(
let mut main_pump = PinDriver::input_output(peripherals.gpio2.downgrade())?; let mut main_pump = PinDriver::input_output(peripherals.gpio2.downgrade())?;
main_pump.set_pull(Pull::Floating)?; main_pump.set_pull(Pull::Floating)?;
main_pump.set_low()?; main_pump.set_low()?;
let mut tank_power = PinDriver::input_output(peripherals.gpio11.downgrade())?;
tank_power.set_pull(Pull::Floating)?;
let mut general_fault = PinDriver::input_output(peripherals.gpio6.downgrade())?; let mut general_fault = PinDriver::input_output(peripherals.gpio6.downgrade())?;
general_fault.set_pull(Pull::Floating)?; general_fault.set_pull(Pull::Floating)?;
general_fault.set_low()?; general_fault.set_low()?;
let one_wire_bus = OneWire::new(one_wire_pin)
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
let mut shift_register_enable_invert = PinDriver::output(peripherals.gpio21.downgrade())?; let mut shift_register_enable_invert = PinDriver::output(peripherals.gpio21.downgrade())?;
unsafe { gpio_hold_dis(shift_register_enable_invert.pin()) }; unsafe { gpio_hold_dis(shift_register_enable_invert.pin()) };
@ -176,24 +233,43 @@ pub(crate) fn create_v3(
Ok(Box::new(V3 { Ok(Box::new(V3 {
config, config,
battery_monitor, battery_monitor,
rtc_module,
esp, esp,
shift_register, shift_register,
_shift_register_enable_invert: shift_register_enable_invert, _shift_register_enable_invert: shift_register_enable_invert,
tank_sensor, tank_channel,
solar_is_day, solar_is_day,
light, light,
main_pump, main_pump,
tank_power,
general_fault, general_fault,
signal_counter, signal_counter,
one_wire_bus,
rtc,
eeprom,
})) }))
} }
impl<'a> BoardInteraction<'a> for V3<'a> { impl<'a> BoardInteraction<'a> for V3<'a> {
fn get_tank_sensor(&mut self) -> Option<&mut TankSensor<'a>> { fn set_charge_indicator(&mut self, charging: bool) -> Result<()> {
Some(&mut self.tank_sensor) Ok(self.shift_register.decompose()[CHARGING].set_state(charging.into())?)
} }
fn is_day(&self) -> bool {
self.solar_is_day.get_level().into()
}
fn get_mptt_voltage(&mut self) -> Result<Voltage> {
//if working this is the hardware set mppt voltage
if self.is_day() {
Ok(Voltage::from_volts(15_f64))
} else {
Ok(Voltage::from_volts(0_f64))
}
}
fn get_mptt_current(&mut self) -> Result<Current> {
bail!("Board does not have current sensor")
}
fn get_esp(&mut self) -> &mut Esp<'a> { fn get_esp(&mut self) -> &mut Esp<'a> {
&mut self.esp &mut self.esp
} }
@ -206,28 +282,158 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
&mut self.battery_monitor &mut self.battery_monitor
} }
fn get_rtc_module(&mut self) -> &mut Box<dyn RTCModuleInteraction + Send> {
&mut self.rtc_module
}
fn set_charge_indicator(&mut self, charging: bool) -> Result<()> {
Ok(self.shift_register.decompose()[CHARGING].set_state(charging.into())?)
}
fn deep_sleep(&mut self, duration_in_ms: u64) -> ! { fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
let _ = self.shift_register.decompose()[AWAKE].set_low(); let _ = self.shift_register.decompose()[AWAKE].set_low();
deep_sleep(duration_in_ms) deep_sleep(duration_in_ms)
} }
fn is_day(&self) -> bool { fn get_backup_info(&mut self) -> Result<BackupHeader> {
self.solar_is_day.get_level().into() let store = bincode::serialize(&BackupHeader::default())?.len();
let mut header_page_buffer = vec![0_u8; store];
self.eeprom
.read_data(0, &mut header_page_buffer)
.map_err(|err| anyhow!("Error reading eeprom header {:?}", err))?;
println!("Raw header is {:?} with size {}", header_page_buffer, store);
let header: BackupHeader = bincode::deserialize(&header_page_buffer)?;
Ok(header)
} }
fn get_backup_config(&mut self) -> Result<Vec<u8>> {
let store = bincode::serialize(&BackupHeader::default())?.len();
let mut header_page_buffer = vec![0_u8; store];
self.eeprom
.read_data(0, &mut header_page_buffer)
.map_err(|err| anyhow!("Error reading eeprom header {:?}", err))?;
let header: BackupHeader = bincode::deserialize(&header_page_buffer)?;
//skip page 0, used by the header
let data_start_address = self.eeprom.page_size() as u32;
let mut data_buffer = vec![0_u8; header.size];
self.eeprom
.read_data(data_start_address, &mut data_buffer)
.map_err(|err| anyhow!("Error reading eeprom data {:?}", err))?;
let checksum = X25.checksum(&data_buffer);
if checksum != header.crc16 {
bail!(
"Invalid checksum, got {} but expected {}",
checksum,
header.crc16
);
}
Ok(data_buffer)
}
fn backup_config(&mut self, bytes: &[u8]) -> Result<()> {
let time = self.get_rtc_time()?.timestamp_millis();
let delay = Delay::new_default();
let checksum = X25.checksum(bytes);
let page_size = self.eeprom.page_size();
let header = BackupHeader {
crc16: checksum,
timestamp: time,
size: bytes.len(),
};
let encoded = bincode::serialize(&header)?;
if encoded.len() > page_size {
bail!(
"Size limit reached header is {}, but firest page is only {}",
encoded.len(),
page_size
)
}
let as_u8: &[u8] = &encoded;
match self.eeprom.write_page(0, as_u8) {
OkStd(_) => {}
Err(err) => bail!("Error writing eeprom {:?}", err),
};
delay.delay_ms(5);
let to_write = bytes.chunks(page_size);
let mut lastiter = 0;
let mut current_page = 1;
for chunk in to_write {
let address = current_page * page_size as u32;
self.eeprom
.write_page(address, chunk)
.map_err(|err| anyhow!("Error writing eeprom {:?}", err))?;
current_page += 1;
let iter = (current_page % 8) as usize;
if iter != lastiter {
for i in 0..PLANT_COUNT {
let _ = self.fault(i, iter == i);
}
lastiter = iter;
}
delay.delay_ms(5);
}
Ok(())
}
fn water_temperature_c(&mut self) -> Result<f32> {
self.one_wire_bus
.reset(&mut self.esp.delay)
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
let first = self.one_wire_bus.devices(false, &mut self.esp.delay).next();
if first.is_none() {
bail!("Not found any one wire Ds18b20");
}
let device_address = first
.unwrap()
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
let water_temp_sensor = Ds18b20::new::<EspError>(device_address)
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
water_temp_sensor
.start_temp_measurement(&mut self.one_wire_bus, &mut self.esp.delay)
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
ds18b20::Resolution::Bits12.delay_for_measurement_time(&mut self.esp.delay);
let sensor_data = water_temp_sensor
.read_data(&mut self.one_wire_bus, &mut self.esp.delay)
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
if sensor_data.temperature == 85_f32 {
bail!("Ds18b20 dummy temperature returned");
}
Ok(sensor_data.temperature / 10_f32)
}
fn tank_sensor_voltage(&mut self) -> Result<f32> {
self.tank_power.set_high()?;
//let stabilize
self.esp.delay.delay_ms(100);
let mut store = [0_u16; TANK_MULTI_SAMPLE];
for multisample in 0..TANK_MULTI_SAMPLE {
let value = self.tank_channel.read()?;
store[multisample] = value;
}
self.tank_power.set_low()?;
store.sort();
let median_mv = store[6] as f32 / 1000_f32;
Ok(median_mv)
}
fn light(&mut self, enable: bool) -> Result<()> { fn light(&mut self, enable: bool) -> Result<()> {
unsafe { gpio_hold_dis(self.light.pin()) }; unsafe { gpio_hold_dis(self.light.pin()) };
self.light.set_state(enable.into())?; self.light.set_state(enable.into())?;
unsafe { gpio_hold_en(self.light.pin()) }; unsafe { gpio_hold_en(self.light.pin()) };
Ok(()) Ok(())
} }
fn pump(&mut self, plant: usize, enable: bool) -> Result<()> { fn pump(&mut self, plant: usize, enable: bool) -> Result<()> {
if enable { if enable {
self.main_pump.set_high()?; self.main_pump.set_high()?;
@ -244,6 +450,7 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
7 => PUMP8_BIT, 7 => PUMP8_BIT,
_ => bail!("Invalid pump {plant}",), _ => bail!("Invalid pump {plant}",),
}; };
//currently infallible error, keep for future as result anyway
self.shift_register.decompose()[index].set_state(enable.into())?; self.shift_register.decompose()[index].set_state(enable.into())?;
if !enable { if !enable {
@ -330,8 +537,8 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
self.shift_register.decompose()[MS_4].set_low()?; self.shift_register.decompose()[MS_4].set_low()?;
self.shift_register.decompose()[SENSOR_ON].set_high()?; self.shift_register.decompose()[SENSOR_ON].set_high()?;
let measurement = 100; //how long to measure and then extrapolate to hz let measurement = 100; // TODO what is this scaling factor? what is its purpose?
let factor = 1000f32 / measurement as f32; //scale raw cound by this number to get hz let factor = 1000f32 / measurement as f32;
//give some time to stabilize //give some time to stabilize
self.esp.delay.delay_ms(10); self.esp.delay.delay_ms(10);
@ -365,6 +572,34 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
unsafe { gpio_hold_en(self.general_fault.pin()) }; unsafe { gpio_hold_en(self.general_fault.pin()) };
} }
fn factory_reset(&mut self) -> Result<()> {
println!("factory resetting");
self.esp.delete_config()?;
//destroy backup header
let dummy: [u8; 0] = [];
self.backup_config(&dummy)?;
Ok(())
}
fn get_rtc_time(&mut self) -> Result<DateTime<Utc>> {
match self.rtc.datetime() {
OkStd(rtc_time) => Ok(rtc_time.and_utc()),
Err(err) => {
bail!("Error getting rtc time {:?}", err)
}
}
}
fn set_rtc_time(&mut self, time: &DateTime<Utc>) -> Result<()> {
let naive_time = time.naive_utc();
match self.rtc.set_datetime(&naive_time) {
OkStd(_) => Ok(()),
Err(err) => {
bail!("Error getting rtc time {:?}", err)
}
}
}
fn test_pump(&mut self, plant: usize) -> Result<()> { fn test_pump(&mut self, plant: usize) -> Result<()> {
self.pump(plant, true)?; self.pump(plant, true)?;
unsafe { vTaskDelay(30000) }; unsafe { vTaskDelay(30000) };
@ -410,22 +645,9 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
Ok(()) Ok(())
} }
fn set_config(&mut self, config: PlantControllerConfig) -> Result<()> { fn set_config(&mut self, config: PlantControllerConfig) -> anyhow::Result<()> {
self.config = config; self.config = config;
self.esp.save_config(&self.config)?; self.esp.save_config(&self.config)?;
anyhow::Ok(()) anyhow::Ok(())
} }
fn get_mptt_voltage(&mut self) -> Result<Voltage> {
//assuming module to work, these are the hardware set values
if self.is_day() {
Ok(Voltage::from_volts(15_f64))
} else {
Ok(Voltage::from_volts(0_f64))
}
}
fn get_mptt_current(&mut self) -> Result<Current> {
bail!("Board does not have current sensor")
}
} }

View File

@ -1,29 +1,34 @@
use crate::config::PlantControllerConfig; use crate::config::PlantControllerConfig;
use crate::hal::battery::BatteryInteraction; use crate::hal::battery::BatteryInteraction;
use crate::hal::esp::Esp; use crate::hal::esp::Esp;
use crate::hal::rtc::RTCModuleInteraction;
use crate::hal::water::TankSensor;
use crate::hal::{ use crate::hal::{
deep_sleep, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT, deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT,
REPEAT_MOIST_MEASURE, REPEAT_MOIST_MEASURE, TANK_MULTI_SAMPLE, X25,
}; };
use crate::log::{log, LogMessage}; use crate::log::{log, LogMessage};
use anyhow::bail; use anyhow::{anyhow, bail};
use chrono::{DateTime, Utc};
use ds18b20::Ds18b20;
use ds323x::{DateTimeAccess, Ds323x};
use eeprom24x::{Eeprom24x, Eeprom24xTrait, SlaveAddr};
use embedded_hal::digital::OutputPin; use embedded_hal::digital::OutputPin;
use embedded_hal_bus::i2c::MutexDevice; use embedded_hal_bus::i2c::MutexDevice;
use esp_idf_hal::adc::oneshot::config::AdcChannelConfig;
use esp_idf_hal::adc::oneshot::{AdcChannelDriver, AdcDriver};
use esp_idf_hal::adc::{attenuation, Resolution};
use esp_idf_hal::delay::Delay; use esp_idf_hal::delay::Delay;
use esp_idf_hal::gpio::{AnyInputPin, IOPin, InputOutput, Output, PinDriver, Pull}; use esp_idf_hal::gpio::{AnyInputPin, Gpio5, IOPin, InputOutput, Output, PinDriver, Pull};
use esp_idf_hal::i2c::{I2cDriver, I2cError}; use esp_idf_hal::i2c::I2cDriver;
use esp_idf_hal::pcnt::{ use esp_idf_hal::pcnt::{
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex, PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
}; };
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en}; use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, EspError};
use ina219::address::{Address, Pin}; use ina219::address::{Address, Pin};
use ina219::calibration::UnCalibrated; use ina219::calibration::UnCalibrated;
use ina219::configuration::{Configuration, OperatingMode}; use ina219::configuration::{Configuration, OperatingMode};
use ina219::errors::InitializationError;
use ina219::SyncIna219; use ina219::SyncIna219;
use measurements::{Current, Resistance, Voltage}; use measurements::{Current, Resistance, Voltage};
use one_wire_bus::OneWire;
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface}; use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
use std::result::Result::Ok as OkStd; use std::result::Result::Ok as OkStd;
@ -40,7 +45,6 @@ pub enum Charger<'a> {
solar_is_day: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>, solar_is_day: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
charge_indicator: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, charge_indicator: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
}, },
ErrorInit {},
} }
impl Charger<'_> { impl Charger<'_> {
@ -63,7 +67,6 @@ impl Charger<'_> {
); );
}); });
} }
_ => {}
} }
} }
fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> { fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> {
@ -73,7 +76,6 @@ impl Charger<'_> {
} => { } => {
charge_indicator.set_state(charging.into())?; charge_indicator.set_state(charging.into())?;
} }
_ => {}
} }
Ok(()) Ok(())
} }
@ -81,7 +83,6 @@ impl Charger<'_> {
fn is_day(&self) -> bool { fn is_day(&self) -> bool {
match self { match self {
Charger::SolarMpptV1 { solar_is_day, .. } => solar_is_day.get_level().into(), Charger::SolarMpptV1 { solar_is_day, .. } => solar_is_day.get_level().into(),
_ => true,
} }
} }
@ -90,9 +91,6 @@ impl Charger<'_> {
Charger::SolarMpptV1 { mppt_ina, .. } => mppt_ina Charger::SolarMpptV1 { mppt_ina, .. } => mppt_ina
.bus_voltage() .bus_voltage()
.map(|v| Voltage::from_millivolts(v.voltage_mv() as f64))?, .map(|v| Voltage::from_millivolts(v.voltage_mv() as f64))?,
_ => {
bail!("hardware error during init")
}
}; };
Ok(voltage) Ok(voltage)
} }
@ -105,9 +103,6 @@ impl Charger<'_> {
let current = shunt_voltage.as_volts() / shut_value.as_ohms(); let current = shunt_voltage.as_volts() / shut_value.as_ohms();
Current::from_amperes(current) Current::from_amperes(current)
})?, })?,
_ => {
bail!("hardware error during init")
}
}; };
Ok(current) Ok(current)
} }
@ -115,19 +110,26 @@ impl Charger<'_> {
pub struct V4<'a> { pub struct V4<'a> {
esp: Esp<'a>, esp: Esp<'a>,
tank_sensor: TankSensor<'a>,
charger: Charger<'a>, charger: Charger<'a>,
rtc_module: Box<dyn RTCModuleInteraction + Send>,
battery_monitor: Box<dyn BatteryInteraction + Send>, battery_monitor: Box<dyn BatteryInteraction + Send>,
config: PlantControllerConfig, config: PlantControllerConfig,
tank_channel: AdcChannelDriver<'a, Gpio5, AdcDriver<'a, esp_idf_hal::adc::ADC1>>,
signal_counter: PcntDriver<'a>, signal_counter: PcntDriver<'a>,
awake: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>, awake: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
tank_power: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
one_wire_bus: OneWire<PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>>,
rtc:
Ds323x<ds323x::interface::I2cInterface<MutexDevice<'a, I2cDriver<'a>>>, ds323x::ic::DS3231>,
eeprom: Eeprom24x<
MutexDevice<'a, I2cDriver<'a>>,
eeprom24x::page_size::B32,
eeprom24x::addr_size::TwoBytes,
eeprom24x::unique_serial::No,
>,
general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
pump_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>, pump_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>,
sensor_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>, sensor_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>,
extra1: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
extra2: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
} }
pub(crate) fn create_v4( pub(crate) fn create_v4(
@ -135,30 +137,48 @@ pub(crate) fn create_v4(
esp: Esp<'static>, esp: Esp<'static>,
config: PlantControllerConfig, config: PlantControllerConfig,
battery_monitor: Box<dyn BatteryInteraction + Send>, battery_monitor: Box<dyn BatteryInteraction + Send>,
rtc_module: Box<dyn RTCModuleInteraction + Send>,
) -> anyhow::Result<Box<dyn BoardInteraction<'static> + Send + 'static>> { ) -> anyhow::Result<Box<dyn BoardInteraction<'static> + Send + 'static>> {
let mut awake = PinDriver::output(peripherals.gpio21.downgrade())?; let mut awake = PinDriver::output(peripherals.gpio15.downgrade())?;
awake.set_high()?; awake.set_high()?;
let mut general_fault = PinDriver::input_output(peripherals.gpio23.downgrade())?; let mut general_fault = PinDriver::input_output(peripherals.gpio6.downgrade())?;
general_fault.set_pull(Pull::Floating)?; general_fault.set_pull(Pull::Floating)?;
general_fault.set_low()?; general_fault.set_low()?;
let mut extra1 = PinDriver::output(peripherals.gpio6.downgrade())?; println!("Init rtc driver");
extra1.set_low()?; let mut rtc = Ds323x::new_ds3231(MutexDevice::new(&I2C_DRIVER));
let mut extra2 = PinDriver::output(peripherals.gpio15.downgrade())?; println!("Init rtc eeprom driver");
extra2.set_low()?; let mut eeprom = {
Eeprom24x::new_24x32(
MutexDevice::new(&I2C_DRIVER),
SlaveAddr::Alternative(true, true, true),
)
};
let one_wire_pin = peripherals.gpio18.downgrade(); let mut one_wire_pin = PinDriver::input_output_od(peripherals.gpio18.downgrade())?;
let tank_power_pin = peripherals.gpio11.downgrade(); one_wire_pin.set_pull(Pull::Floating)?;
let tank_sensor = TankSensor::create( let one_wire_bus = OneWire::new(one_wire_pin)
one_wire_pin, .map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
peripherals.adc1,
peripherals.gpio5, let rtc_time = rtc.datetime();
tank_power_pin, match rtc_time {
); OkStd(tt) => {
println!("Rtc Module reports time at UTC {}", tt);
}
Err(err) => {
println!("Rtc Module could not be read {:?}", err);
}
}
match eeprom.read_byte(0) {
OkStd(byte) => {
println!("Read first byte with status {}", byte);
}
Err(err) => {
println!("Eeprom could not read first byte {:?}", err);
}
}
let mut signal_counter = PcntDriver::new( let mut signal_counter = PcntDriver::new(
peripherals.pcnt0, peripherals.pcnt0,
@ -182,17 +202,31 @@ pub(crate) fn create_v4(
}, },
)?; )?;
let adc_config = AdcChannelConfig {
attenuation: attenuation::DB_11,
resolution: Resolution::Resolution12Bit,
calibration: esp_idf_hal::adc::oneshot::config::Calibration::Curve,
};
let tank_driver = AdcDriver::new(peripherals.adc1)?;
let tank_channel: AdcChannelDriver<Gpio5, AdcDriver<esp_idf_hal::adc::ADC1>> =
AdcChannelDriver::new(tank_driver, peripherals.gpio5, &adc_config)?;
let mut solar_is_day = PinDriver::input(peripherals.gpio7.downgrade())?; let mut solar_is_day = PinDriver::input(peripherals.gpio7.downgrade())?;
solar_is_day.set_pull(Pull::Floating)?; solar_is_day.set_pull(Pull::Floating)?;
let mut light = PinDriver::input_output(peripherals.gpio10.downgrade())?; let mut light = PinDriver::input_output(peripherals.gpio10.downgrade())?;
light.set_pull(Pull::Floating)?; light.set_pull(Pull::Floating)?;
let mut tank_power = PinDriver::input_output(peripherals.gpio11.downgrade())?;
tank_power.set_pull(Pull::Floating)?;
let mut charge_indicator = PinDriver::input_output(peripherals.gpio3.downgrade())?; let mut charge_indicator = PinDriver::input_output(peripherals.gpio3.downgrade())?;
charge_indicator.set_pull(Pull::Floating)?; charge_indicator.set_pull(Pull::Floating)?;
charge_indicator.set_low()?; charge_indicator.set_low()?;
let mut pump_expander = Pca9535Immediate::new(MutexDevice::new(&I2C_DRIVER), 32); let mut pump_expander = Pca9535Immediate::new(MutexDevice::new(&I2C_DRIVER), 32);
//todo error handing if init error
for pin in 0..8 { for pin in 0..8 {
let _ = pump_expander.pin_into_output(GPIOBank::Bank0, pin); let _ = pump_expander.pin_into_output(GPIOBank::Bank0, pin);
let _ = pump_expander.pin_into_output(GPIOBank::Bank1, pin); let _ = pump_expander.pin_into_output(GPIOBank::Bank1, pin);
@ -208,13 +242,12 @@ pub(crate) fn create_v4(
let _ = sensor_expander.pin_set_low(GPIOBank::Bank1, pin); let _ = sensor_expander.pin_set_low(GPIOBank::Bank1, pin);
} }
//TODO error handling is not done nicely here, should not break if ina is not responsive
let mut mppt_ina = SyncIna219::new( let mut mppt_ina = SyncIna219::new(
MutexDevice::new(&I2C_DRIVER), MutexDevice::new(&I2C_DRIVER),
Address::from_pins(Pin::Vcc, Pin::Gnd), Address::from_pins(Pin::Vcc, Pin::Gnd),
); )?;
let charger = match mppt_ina {
Ok(mut mppt_ina) => {
mppt_ina.set_configuration(Configuration { mppt_ina.set_configuration(Configuration {
reset: Default::default(), reset: Default::default(),
bus_voltage_range: Default::default(), bus_voltage_range: Default::default(),
@ -223,7 +256,6 @@ pub(crate) fn create_v4(
shunt_resolution: ina219::configuration::Resolution::Avg128, shunt_resolution: ina219::configuration::Resolution::Avg128,
operating_mode: Default::default(), operating_mode: Default::default(),
})?; })?;
//TODO this is probably already done until we are ready first time?, maybe add startup time comparison on access? //TODO this is probably already done until we are ready first time?, maybe add startup time comparison on access?
esp.delay.delay_ms( esp.delay.delay_ms(
mppt_ina mppt_ina
@ -233,39 +265,33 @@ pub(crate) fn create_v4(
.as_millis() as u32, .as_millis() as u32,
); );
Charger::SolarMpptV1 { let charger = Charger::SolarMpptV1 {
mppt_ina, mppt_ina,
solar_is_day, solar_is_day,
charge_indicator, charge_indicator,
}
}
Err(_) => Charger::ErrorInit {},
}; };
let v = V4 { let v = V4 {
rtc_module,
esp, esp,
awake, awake,
tank_sensor, tank_channel,
signal_counter, signal_counter,
light, light,
tank_power,
one_wire_bus,
rtc,
eeprom,
general_fault, general_fault,
pump_expander, pump_expander,
sensor_expander, sensor_expander,
config, config,
battery_monitor, battery_monitor,
charger, charger,
extra1,
extra2,
}; };
Ok(Box::new(v)) Ok(Box::new(v))
} }
impl<'a> BoardInteraction<'a> for V4<'a> { impl<'a> BoardInteraction<'a> for V4<'a> {
fn get_tank_sensor(&mut self) -> Option<&mut TankSensor<'a>> {
Some(&mut self.tank_sensor)
}
fn get_esp(&mut self) -> &mut Esp<'a> { fn get_esp(&mut self) -> &mut Esp<'a> {
&mut self.esp &mut self.esp
} }
@ -278,10 +304,6 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
&mut self.battery_monitor &mut self.battery_monitor
} }
fn get_rtc_module(&mut self) -> &mut Box<dyn RTCModuleInteraction + Send> {
&mut self.rtc_module
}
fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> { fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> {
self.charger.set_charge_indicator(charging) self.charger.set_charge_indicator(charging)
} }
@ -292,10 +314,151 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
deep_sleep(duration_in_ms); deep_sleep(duration_in_ms);
} }
fn get_backup_info(&mut self) -> anyhow::Result<BackupHeader> {
let store = bincode::serialize(&BackupHeader::default())?.len();
let mut header_page_buffer = vec![0_u8; store];
self.eeprom
.read_data(0, &mut header_page_buffer)
.map_err(|err| anyhow!("Error reading eeprom header {:?}", err))?;
println!("Raw header is {:?} with size {}", header_page_buffer, store);
let header: BackupHeader = bincode::deserialize(&header_page_buffer)?;
anyhow::Ok(header)
}
fn get_backup_config(&mut self) -> anyhow::Result<Vec<u8>> {
let store = bincode::serialize(&BackupHeader::default())?.len();
let mut header_page_buffer = vec![0_u8; store];
self.eeprom
.read_data(0, &mut header_page_buffer)
.map_err(|err| anyhow!("Error reading eeprom header {:?}", err))?;
let header: BackupHeader = bincode::deserialize(&header_page_buffer)?;
//skip page 0, used by the header
let data_start_address = self.eeprom.page_size() as u32;
let mut data_buffer = vec![0_u8; header.size];
self.eeprom
.read_data(data_start_address, &mut data_buffer)
.map_err(|err| anyhow!("Error reading eeprom data {:?}", err))?;
let checksum = X25.checksum(&data_buffer);
if checksum != header.crc16 {
bail!(
"Invalid checksum, got {} but expected {}",
checksum,
header.crc16
);
}
anyhow::Ok(data_buffer)
}
fn backup_config(&mut self, bytes: &[u8]) -> anyhow::Result<()> {
let time = self.get_rtc_time()?.timestamp_millis();
let delay = Delay::new_default();
let checksum = X25.checksum(bytes);
let page_size = self.eeprom.page_size();
let header = BackupHeader {
crc16: checksum,
timestamp: time,
size: bytes.len(),
};
let encoded = bincode::serialize(&header)?;
if encoded.len() > page_size {
bail!(
"Size limit reached header is {}, but firest page is only {}",
encoded.len(),
page_size
)
}
let as_u8: &[u8] = &encoded;
match self.eeprom.write_page(0, as_u8) {
OkStd(_) => {}
Err(err) => bail!("Error writing eeprom {:?}", err),
};
delay.delay_ms(5);
let to_write = bytes.chunks(page_size);
let mut lastiter = 0;
let mut current_page = 1;
for chunk in to_write {
let address = current_page * page_size as u32;
self.eeprom
.write_page(address, chunk)
.map_err(|err| anyhow!("Error writing eeprom {:?}", err))?;
current_page += 1;
let iter = (current_page % 8) as usize;
if iter != lastiter {
for i in 0..PLANT_COUNT {
let _ = self.fault(i, iter == i);
}
lastiter = iter;
}
delay.delay_ms(5);
}
anyhow::Ok(())
}
fn is_day(&self) -> bool { fn is_day(&self) -> bool {
self.charger.is_day() self.charger.is_day()
} }
fn water_temperature_c(&mut self) -> anyhow::Result<f32> {
self.one_wire_bus
.reset(&mut self.esp.delay)
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
let first = self.one_wire_bus.devices(false, &mut self.esp.delay).next();
if first.is_none() {
bail!("Not found any one wire Ds18b20");
}
let device_address = first
.unwrap()
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
let water_temp_sensor = Ds18b20::new::<EspError>(device_address)
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
water_temp_sensor
.start_temp_measurement(&mut self.one_wire_bus, &mut self.esp.delay)
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
ds18b20::Resolution::Bits12.delay_for_measurement_time(&mut self.esp.delay);
let sensor_data = water_temp_sensor
.read_data(&mut self.one_wire_bus, &mut self.esp.delay)
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
if sensor_data.temperature == 85_f32 {
bail!("Ds18b20 dummy temperature returned");
}
anyhow::Ok(sensor_data.temperature / 10_f32)
}
fn tank_sensor_voltage(&mut self) -> anyhow::Result<f32> {
self.tank_power.set_high()?;
//let stabilize
self.esp.delay.delay_ms(100);
let mut store = [0_u16; TANK_MULTI_SAMPLE];
for multisample in 0..TANK_MULTI_SAMPLE {
let value = self.tank_channel.read()?;
store[multisample] = value;
}
self.tank_power.set_low()?;
store.sort();
let median_mv = store[6] as f32 / 1000_f32;
anyhow::Ok(median_mv)
}
fn light(&mut self, enable: bool) -> anyhow::Result<()> { fn light(&mut self, enable: bool) -> anyhow::Result<()> {
unsafe { gpio_hold_dis(self.light.pin()) }; unsafe { gpio_hold_dis(self.light.pin()) };
self.light.set_state(enable.into())?; self.light.set_state(enable.into())?;
@ -406,6 +569,35 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
unsafe { gpio_hold_en(self.general_fault.pin()) }; unsafe { gpio_hold_en(self.general_fault.pin()) };
} }
fn factory_reset(&mut self) -> anyhow::Result<()> {
println!("factory resetting");
self.esp.delete_config()?;
//destroy backup header
let dummy: [u8; 0] = [];
self.backup_config(&dummy)?;
anyhow::Ok(())
}
fn get_rtc_time(&mut self) -> anyhow::Result<DateTime<Utc>> {
match self.rtc.datetime() {
OkStd(rtc_time) => anyhow::Ok(rtc_time.and_utc()),
Err(err) => {
bail!("Error getting rtc time {:?}", err)
}
}
}
fn set_rtc_time(&mut self, time: &DateTime<Utc>) -> anyhow::Result<()> {
let naive_time = time.naive_utc();
match self.rtc.set_datetime(&naive_time) {
OkStd(_) => anyhow::Ok(()),
Err(err) => {
bail!("Error getting rtc time {:?}", err)
}
}
}
fn test_pump(&mut self, plant: usize) -> anyhow::Result<()> { fn test_pump(&mut self, plant: usize) -> anyhow::Result<()> {
self.pump(plant, true)?; self.pump(plant, true)?;
self.esp.delay.delay_ms(30000); self.esp.delay.delay_ms(30000);

View File

@ -1,124 +0,0 @@
use crate::hal::TANK_MULTI_SAMPLE;
use anyhow::{anyhow, bail};
use ds18b20::Ds18b20;
use esp_idf_hal::adc::oneshot::config::AdcChannelConfig;
use esp_idf_hal::adc::oneshot::{AdcChannelDriver, AdcDriver};
use esp_idf_hal::adc::{attenuation, Resolution, ADC1};
use esp_idf_hal::delay::Delay;
use esp_idf_hal::gpio::{AnyIOPin, Gpio5, InputOutput, PinDriver, Pull};
use esp_idf_sys::EspError;
use one_wire_bus::OneWire;
pub struct TankSensor<'a> {
one_wire_bus: OneWire<PinDriver<'a, AnyIOPin, InputOutput>>,
tank_channel: AdcChannelDriver<'a, Gpio5, AdcDriver<'a, ADC1>>,
tank_power: PinDriver<'a, AnyIOPin, InputOutput>,
delay: Delay,
}
impl<'a> TankSensor<'a> {
pub(crate) fn create(
one_wire_pin: AnyIOPin,
adc1: ADC1,
gpio5: Gpio5,
tank_power_pin: AnyIOPin,
) -> TankSensor<'a> {
let mut one_wire_pin =
PinDriver::input_output_od(one_wire_pin).expect("Failed to configure pin");
one_wire_pin
.set_pull(Pull::Floating)
.expect("Failed to set pull");
let adc_config = AdcChannelConfig {
attenuation: attenuation::DB_11,
resolution: Resolution::Resolution12Bit,
calibration: esp_idf_hal::adc::oneshot::config::Calibration::Curve,
};
let tank_driver = AdcDriver::new(adc1).expect("Failed to configure ADC");
let tank_channel = AdcChannelDriver::new(tank_driver, gpio5, &adc_config)
.expect("Failed to configure ADC channel");
let mut tank_power =
PinDriver::input_output(tank_power_pin).expect("Failed to configure pin");
tank_power
.set_pull(Pull::Floating)
.expect("Failed to set pull");
let one_wire_bus =
OneWire::new(one_wire_pin).expect("OneWire bus did not pull up after release");
TankSensor {
one_wire_bus,
tank_channel,
tank_power,
delay: Default::default(),
}
}
pub fn water_temperature_c(&mut self) -> anyhow::Result<f32> {
//multisample should be moved to water_temperature_c
let mut attempt = 1;
let water_temp: Result<f32, anyhow::Error> = loop {
let temp = self.single_temperature_c();
match &temp {
Ok(res) => {
println!("Water temp is {}", res);
break temp;
}
Err(err) => {
println!("Could not get water temp {} attempt {}", err, attempt)
}
}
if attempt == 5 {
break temp;
}
attempt += 1;
};
water_temp
}
fn single_temperature_c(&mut self) -> anyhow::Result<f32> {
self.one_wire_bus
.reset(&mut self.delay)
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
let first = self.one_wire_bus.devices(false, &mut self.delay).next();
if first.is_none() {
bail!("Not found any one wire Ds18b20");
}
let device_address = first
.unwrap()
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
let water_temp_sensor = Ds18b20::new::<EspError>(device_address)
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
water_temp_sensor
.start_temp_measurement(&mut self.one_wire_bus, &mut self.delay)
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
ds18b20::Resolution::Bits12.delay_for_measurement_time(&mut self.delay);
let sensor_data = water_temp_sensor
.read_data(&mut self.one_wire_bus, &mut self.delay)
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
if sensor_data.temperature == 85_f32 {
bail!("Ds18b20 dummy temperature returned");
}
anyhow::Ok(sensor_data.temperature / 10_f32)
}
pub fn tank_sensor_voltage(&mut self) -> anyhow::Result<f32> {
self.tank_power.set_high()?;
//let stabilize
self.delay.delay_ms(100);
let mut store = [0_u16; TANK_MULTI_SAMPLE];
for multisample in 0..TANK_MULTI_SAMPLE {
let value = self.tank_channel.read()?;
store[multisample] = value;
}
self.tank_power.set_low()?;
store.sort();
let median_mv = store[6] as f32 / 1000_f32;
anyhow::Ok(median_mv)
}
}

View File

@ -3,7 +3,7 @@ use crate::{
hal::{PlantHal, HAL, PLANT_COUNT}, hal::{PlantHal, HAL, PLANT_COUNT},
webserver::httpd, webserver::httpd,
}; };
use anyhow::{bail, Context}; use anyhow::bail;
use chrono::{DateTime, Datelike, Timelike, Utc}; use chrono::{DateTime, Datelike, Timelike, Utc};
use chrono_tz::Tz::{self, UTC}; use chrono_tz::Tz::{self, UTC};
use esp_idf_hal::delay::Delay; use esp_idf_hal::delay::Delay;
@ -33,6 +33,8 @@ mod webserver;
pub static BOARD_ACCESS: Lazy<Mutex<HAL>> = Lazy::new(|| PlantHal::create().unwrap()); pub static BOARD_ACCESS: Lazy<Mutex<HAL>> = Lazy::new(|| PlantHal::create().unwrap());
pub static STAY_ALIVE: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false)); pub static STAY_ALIVE: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false));
#[derive(Serialize, Deserialize, Debug, PartialEq)] #[derive(Serialize, Deserialize, Debug, PartialEq)]
enum WaitType { enum WaitType {
MissingConfig, MissingConfig,
@ -40,12 +42,6 @@ enum WaitType {
MqttConfig, MqttConfig,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct Solar {
current_ma: u32,
voltage_ma: u32,
}
impl WaitType { impl WaitType {
fn blink_pattern(&self) -> u32 { fn blink_pattern(&self) -> u32 {
match self { match self {
@ -160,7 +156,6 @@ fn safe_main() -> anyhow::Result<()> {
let cur = board let cur = board
.board_hal .board_hal
.get_rtc_module()
.get_rtc_time() .get_rtc_time()
.or_else(|err| { .or_else(|err| {
println!("rtc module error: {:?}", err); println!("rtc module error: {:?}", err);
@ -264,7 +259,6 @@ fn safe_main() -> anyhow::Result<()> {
timezone_time, timezone_time,
); );
publish_battery_state(&mut board); publish_battery_state(&mut board);
let _ = publish_mppt_state(&mut board);
} }
log( log(
@ -333,12 +327,8 @@ fn safe_main() -> anyhow::Result<()> {
} }
let mut water_frozen = false; let mut water_frozen = false;
let water_temp = board
.board_hal
.get_tank_sensor()
.context("no sensor")
.and_then(|f| f.water_temperature_c());
let water_temp = obtain_tank_temperature(&mut board);
if let Ok(res) = water_temp { if let Ok(res) = water_temp {
if res < WATER_FROZEN_THRESH { if res < WATER_FROZEN_THRESH {
water_frozen = true; water_frozen = true;
@ -576,6 +566,28 @@ fn update_charge_indicator(board: &mut MutexGuard<HAL>) {
} }
} }
fn obtain_tank_temperature(board: &mut MutexGuard<HAL>) -> anyhow::Result<f32> {
//multisample should be moved to water_temperature_c
let mut attempt = 1;
let water_temp: Result<f32, anyhow::Error> = loop {
let temp = board.board_hal.water_temperature_c();
match &temp {
Ok(res) => {
println!("Water temp is {}", res);
break temp;
}
Err(err) => {
println!("Could not get water temp {} attempt {}", err, attempt)
}
}
if attempt == 5 {
break temp;
}
attempt += 1;
};
water_temp
}
fn publish_tank_state( fn publish_tank_state(
board: &mut MutexGuard<HAL>, board: &mut MutexGuard<HAL>,
tank_state: &TankState, tank_state: &TankState,
@ -668,7 +680,7 @@ fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<HAL>) -> NetworkMode {
let sntp_mode: SntpMode = match board.board_hal.get_esp().sntp(1000 * 10) { let sntp_mode: SntpMode = match board.board_hal.get_esp().sntp(1000 * 10) {
Ok(new_time) => { Ok(new_time) => {
println!("Using time from sntp"); println!("Using time from sntp");
let _ = board.board_hal.get_rtc_module().set_rtc_time(&new_time); let _ = board.board_hal.set_rtc_time(&new_time);
SntpMode::SYNC { current: new_time } SntpMode::SYNC { current: new_time }
} }
Err(err) => { Err(err) => {
@ -732,24 +744,6 @@ fn pump_info(
}; };
} }
fn publish_mppt_state(board: &mut MutexGuard<'_, HAL<'_>>) -> anyhow::Result<()> {
let current = board.board_hal.get_mptt_current()?;
let voltage = board.board_hal.get_mptt_voltage()?;
let solar_state = Solar {
current_ma: current.as_milliamperes() as u32,
voltage_ma: voltage.as_millivolts() as u32,
};
if let Ok(serialized_solar_state_bytes) =
serde_json::to_string(&solar_state).map(|s| s.into_bytes())
{
let _ = board
.board_hal
.get_esp()
.mqtt_publish("/mppt", &serialized_solar_state_bytes);
}
Ok(())
}
fn publish_battery_state(board: &mut MutexGuard<'_, HAL<'_>>) { fn publish_battery_state(board: &mut MutexGuard<'_, HAL<'_>>) {
let state = board.board_hal.get_battery_monitor().get_battery_state(); let state = board.board_hal.get_battery_monitor().get_battery_state();
if let Ok(serialized_battery_state_bytes) = if let Ok(serialized_battery_state_bytes) =

View File

@ -1,5 +1,4 @@
use crate::{config::TankConfig, hal::HAL}; use crate::{config::TankConfig, hal::HAL};
use anyhow::Context;
use serde::Serialize; use serde::Serialize;
const OPEN_TANK_VOLTAGE: f32 = 3.0; const OPEN_TANK_VOLTAGE: f32 = 3.0;
@ -152,12 +151,7 @@ impl TankState {
pub fn determine_tank_state(board: &mut std::sync::MutexGuard<'_, HAL<'_>>) -> TankState { pub fn determine_tank_state(board: &mut std::sync::MutexGuard<'_, HAL<'_>>) -> TankState {
if board.board_hal.get_config().tank.tank_sensor_enabled { if board.board_hal.get_config().tank.tank_sensor_enabled {
match board match board.board_hal.tank_sensor_voltage() {
.board_hal
.get_tank_sensor()
.context("no sensor")
.and_then(|f| f.tank_sensor_voltage())
{
Ok(raw_sensor_value_mv) => TankState::Present(raw_sensor_value_mv), Ok(raw_sensor_value_mv) => TankState::Present(raw_sensor_value_mv),
Err(err) => TankState::Error(TankError::BoardError(err.to_string())), Err(err) => TankState::Error(TankError::BoardError(err.to_string())),
} }

View File

@ -8,7 +8,7 @@ use crate::{
plant_state::{MoistureSensorState, PlantState}, plant_state::{MoistureSensorState, PlantState},
BOARD_ACCESS, BOARD_ACCESS,
}; };
use anyhow::{bail, Context}; use anyhow::bail;
use chrono::DateTime; use chrono::DateTime;
use core::result::Result::Ok; use core::result::Result::Ok;
use embedded_svc::http::Method; use embedded_svc::http::Method;
@ -60,7 +60,7 @@ pub struct TestPump {
#[derive(Serialize, Deserialize, PartialEq, Debug)] #[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct WebBackupHeader { pub struct WebBackupHeader {
timestamp: std::string::String, timestamp: std::string::String,
size: u16, size: usize,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -81,10 +81,7 @@ fn write_time(
tv_usec: 0, tv_usec: 0,
}; };
unsafe { settimeofday(&now, core::ptr::null_mut()) }; unsafe { settimeofday(&now, core::ptr::null_mut()) };
board board.board_hal.set_rtc_time(&parsed.to_utc())?;
.board_hal
.get_rtc_module()
.set_rtc_time(&parsed.to_utc())?;
anyhow::Ok(None) anyhow::Ok(None)
} }
@ -100,7 +97,6 @@ fn get_time(
.unwrap_or("error".to_string()); .unwrap_or("error".to_string());
let rtc = board let rtc = board
.board_hal .board_hal
.get_rtc_module()
.get_rtc_time() .get_rtc_time()
.map(|t| t.to_rfc3339()) .map(|t| t.to_rfc3339())
.unwrap_or("error".to_string()); .unwrap_or("error".to_string());
@ -174,9 +170,7 @@ fn backup_config(
) -> Result<Option<std::string::String>, anyhow::Error> { ) -> Result<Option<std::string::String>, anyhow::Error> {
let all = read_up_to_bytes_from_request(request, Some(3072))?; let all = read_up_to_bytes_from_request(request, Some(3072))?;
let mut board = BOARD_ACCESS.lock().expect("board access"); let mut board = BOARD_ACCESS.lock().expect("board access");
board.board_hal.backup_config(&all)?;
//TODO how to handle progress here? prior versions animated the fault leds while running
board.board_hal.get_rtc_module().backup_config(&all)?;
anyhow::Ok(Some("saved".to_owned())) anyhow::Ok(Some("saved".to_owned()))
} }
@ -184,7 +178,7 @@ fn get_backup_config(
_request: &mut Request<&mut EspHttpConnection>, _request: &mut Request<&mut EspHttpConnection>,
) -> Result<Option<std::string::String>, anyhow::Error> { ) -> Result<Option<std::string::String>, anyhow::Error> {
let mut board = BOARD_ACCESS.lock().expect("board access"); let mut board = BOARD_ACCESS.lock().expect("board access");
let json = match board.board_hal.get_rtc_module().get_backup_config() { let json = match board.board_hal.get_backup_config() {
Ok(config) => from_utf8(&config)?.to_owned(), Ok(config) => from_utf8(&config)?.to_owned(),
Err(err) => { Err(err) => {
println!("Error get backup config {:?}", err); println!("Error get backup config {:?}", err);
@ -198,7 +192,7 @@ fn backup_info(
_request: &mut Request<&mut EspHttpConnection>, _request: &mut Request<&mut EspHttpConnection>,
) -> Result<Option<std::string::String>, anyhow::Error> { ) -> Result<Option<std::string::String>, anyhow::Error> {
let mut board = BOARD_ACCESS.lock().expect("Should never fail"); let mut board = BOARD_ACCESS.lock().expect("Should never fail");
let header = board.board_hal.get_rtc_module().get_backup_info(); let header = board.board_hal.get_backup_info();
let json = match header { let json = match header {
Ok(h) => { Ok(h) => {
let timestamp = DateTime::from_timestamp_millis(h.timestamp).unwrap(); let timestamp = DateTime::from_timestamp_millis(h.timestamp).unwrap();
@ -208,9 +202,10 @@ fn backup_info(
}; };
serde_json::to_string(&wbh)? serde_json::to_string(&wbh)?
} }
Err(err) => { Err(_) => {
//TODO make better
let wbh = WebBackupHeader { let wbh = WebBackupHeader {
timestamp: err.to_string(), timestamp: "no backup".to_owned(),
size: 0, size: 0,
}; };
serde_json::to_string(&wbh)? serde_json::to_string(&wbh)?
@ -226,7 +221,7 @@ fn set_config(
let config: PlantControllerConfig = serde_json::from_slice(&all)?; let config: PlantControllerConfig = serde_json::from_slice(&all)?;
let mut board = BOARD_ACCESS.lock().expect("board access"); let mut board = BOARD_ACCESS.lock().expect("board access");
board.board_hal.set_config(config)?; let _ = board.board_hal.set_config(config);
anyhow::Ok(Some("saved".to_owned())) anyhow::Ok(Some("saved".to_owned()))
} }
@ -285,12 +280,7 @@ fn tank_info(
let mut board = BOARD_ACCESS.lock().unwrap(); let mut board = BOARD_ACCESS.lock().unwrap();
let tank_info = determine_tank_state(&mut board); let tank_info = determine_tank_state(&mut board);
//should be multsampled //should be multsampled
let water_temp = board.board_hal.water_temperature_c();
let water_temp = board
.board_hal
.get_tank_sensor()
.context("no sensor")
.and_then(|f| f.water_temperature_c());
Ok(Some(serde_json::to_string(&tank_info.as_mqtt_info( Ok(Some(serde_json::to_string(&tank_info.as_mqtt_info(
&board.board_hal.get_config().tank, &board.board_hal.get_config().tank,
&water_temp, &water_temp,
@ -351,7 +341,6 @@ fn ota(
let iter = (total_read / 1024) % 8; let iter = (total_read / 1024) % 8;
if iter != lastiter { if iter != lastiter {
board.board_hal.general_fault(iter % 5 == 0);
for i in 0..PLANT_COUNT { for i in 0..PLANT_COUNT {
let _ = board.board_hal.fault(i, iter == i); let _ = board.board_hal.fault(i, iter == i);
} }

View File

@ -56,7 +56,6 @@ export class Controller {
console.log(error); console.log(error);
}); });
} }
loadLog() { loadLog() {
return fetch(PUBLIC_URL + "/log") return fetch(PUBLIC_URL + "/log")
.then(response => response.json()) .then(response => response.json())
@ -68,7 +67,6 @@ export class Controller {
console.log(error); console.log(error);
}); });
} }
getBackupInfo() : Promise<void> { getBackupInfo() : Promise<void> {
return fetch(PUBLIC_URL + "/backup_info") return fetch(PUBLIC_URL + "/backup_info")
.then(response => response.json()) .then(response => response.json())
@ -102,7 +100,6 @@ export class Controller {
console.log(error); console.log(error);
}); });
} }
uploadFile(file: File, name:string) { uploadFile(file: File, name:string) {
var current = 0; var current = 0;
var max = 100; var max = 100;
@ -130,7 +127,6 @@ export class Controller {
ajax.open("POST", PUBLIC_URL + "/file?filename="+name); ajax.open("POST", PUBLIC_URL + "/file?filename="+name);
ajax.send(file); ajax.send(file);
} }
deleteFile(name:string) { deleteFile(name:string) {
controller.progressview.addIndeterminate("file_delete", "Deleting " + name); controller.progressview.addIndeterminate("file_delete", "Deleting " + name);
var ajax = new XMLHttpRequest(); var ajax = new XMLHttpRequest();
@ -165,8 +161,7 @@ export class Controller {
console.log(error); console.log(error);
}); });
} }
updateBatteryData() {
updateBatteryData(): Promise<void> {
return fetch(PUBLIC_URL + "/battery") return fetch(PUBLIC_URL + "/battery")
.then(response => response.json()) .then(response => response.json())
.then(json => json as BatteryState) .then(json => json as BatteryState)
@ -178,8 +173,7 @@ export class Controller {
console.log(error); console.log(error);
}) })
} }
updateSolarData() {
updateSolarData(): Promise<void> {
return fetch(PUBLIC_URL + "/solar") return fetch(PUBLIC_URL + "/solar")
.then(response => response.json()) .then(response => response.json())
.then(json => json as SolarState) .then(json => json as SolarState)
@ -191,7 +185,6 @@ export class Controller {
console.log(error); console.log(error);
}) })
} }
uploadNewFirmware(file: File) { uploadNewFirmware(file: File) {
var current = 0; var current = 0;
var max = 100; var max = 100;
@ -217,7 +210,6 @@ export class Controller {
ajax.open("POST", PUBLIC_URL + "/ota"); ajax.open("POST", PUBLIC_URL + "/ota");
ajax.send(file); ajax.send(file);
} }
version() : Promise<void> { version() : Promise<void> {
controller.progressview.addIndeterminate("version", "Getting buildVersion") controller.progressview.addIndeterminate("version", "Getting buildVersion")
return fetch(PUBLIC_URL + "/version") return fetch(PUBLIC_URL + "/version")
@ -250,11 +242,9 @@ export class Controller {
controller.configChanged(); controller.configChanged();
controller.progressview.removeProgress("get_config"); controller.progressview.removeProgress("get_config");
} }
setInitialConfig(currentConfig: PlantControllerConfig) { setInitialConfig(currentConfig: PlantControllerConfig) {
this.initialConfig = currentConfig this.initialConfig = currentConfig
} }
uploadConfig(json: string, statusCallback: (status: string) => void) { uploadConfig(json: string, statusCallback: (status: string) => void) {
controller.progressview.addIndeterminate("set_config", "Uploading Config") controller.progressview.addIndeterminate("set_config", "Uploading Config")
fetch(PUBLIC_URL + "/set_config", { fetch(PUBLIC_URL + "/set_config", {
@ -268,20 +258,22 @@ export class Controller {
controller.downloadConfig() controller.downloadConfig()
} }
backupConfig(json: string): Promise<string> { backupConfig(json: string, statusCallback: (status: string) => void) {
return fetch(PUBLIC_URL + "/backup_config", { controller.progressview.addIndeterminate("backup_config", "Backingup Config")
fetch(PUBLIC_URL + "/backup_config", {
method: "POST", method: "POST",
body: json, body: json,
}) })
.then(response => response.text()); .then(response => response.text())
.then(text => statusCallback(text))
controller.progressview.removeProgress("backup_config")
} }
syncRTCFromBrowser() { syncRTCFromBrowser() {
controller.progressview.addIndeterminate("write_rtc", "Writing RTC") controller.progressview.addIndeterminate("write_rtc", "Writing RTC")
const value: SetTime = { var value: SetTime = {
time: new Date().toISOString() time: new Date().toISOString()
}; }
const pretty = JSON.stringify(value, undefined, 1); var pretty = JSON.stringify(value, undefined, 1);
fetch(PUBLIC_URL + "/time", { fetch(PUBLIC_URL + "/time", {
method: "POST", method: "POST",
body: pretty body: pretty
@ -326,14 +318,12 @@ export class Controller {
controller.progressview.addProgress("test_pump", counter / limit * 100, "Testing pump " + (plantId + 1) + " for " + (limit - counter) + "s") controller.progressview.addProgress("test_pump", counter / limit * 100, "Testing pump " + (plantId + 1) + " for " + (limit - counter) + "s")
let timerId: string | number | NodeJS.Timeout | undefined let timerId: string | number | NodeJS.Timeout | undefined
function updateProgress() { function updateProgress() {
counter++; counter++;
controller.progressview.addProgress("test_pump", counter / limit * 100, "Testing pump " + (plantId + 1) + " for " + (limit - counter) + "s") controller.progressview.addProgress("test_pump", counter / limit * 100, "Testing pump " + (plantId + 1) + " for " + (limit - counter) + "s")
timerId = setTimeout(updateProgress, 1000); timerId = setTimeout(updateProgress, 1000);
} }
timerId = setTimeout(updateProgress, 1000); timerId = setTimeout(updateProgress, 1000);
var body: TestPump = { var body: TestPump = {
@ -371,14 +361,12 @@ export class Controller {
controller.progressview.addProgress("scan_ssid", counter / limit * 100, "Scanning for SSIDs for " + (limit - counter) + "s") controller.progressview.addProgress("scan_ssid", counter / limit * 100, "Scanning for SSIDs for " + (limit - counter) + "s")
let timerId: string | number | NodeJS.Timeout | undefined let timerId: string | number | NodeJS.Timeout | undefined
function updateProgress() { function updateProgress() {
counter++; counter++;
controller.progressview.addProgress("scan_ssid", counter / limit * 100, "Scanning for SSIDs for " + (limit - counter) + "s") controller.progressview.addProgress("scan_ssid", counter / limit * 100, "Scanning for SSIDs for " + (limit - counter) + "s")
timerId = setTimeout(updateProgress, 1000); timerId = setTimeout(updateProgress, 1000);
} }
timerId = setTimeout(updateProgress, 1000); timerId = setTimeout(updateProgress, 1000);
@ -415,14 +403,12 @@ export class Controller {
controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s") controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s")
let timerId: string | number | NodeJS.Timeout | undefined let timerId: string | number | NodeJS.Timeout | undefined
function updateProgress() { function updateProgress() {
counter++; counter++;
controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s") controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s")
timerId = setTimeout(updateProgress, 1000); timerId = setTimeout(updateProgress, 1000);
} }
timerId = setTimeout(updateProgress, 1000); timerId = setTimeout(updateProgress, 1000);
@ -496,7 +482,6 @@ export class Controller {
readonly solarView: SolarView; readonly solarView: SolarView;
readonly fileview: FileView; readonly fileview: FileView;
readonly logView: LogView readonly logView: LogView
constructor() { constructor() {
this.timeView = new TimeView(this) this.timeView = new TimeView(this)
this.plantViews = new PlantViews(this) this.plantViews = new PlantViews(this)
@ -521,7 +506,6 @@ export class Controller {
} }
} }
} }
const controller = new Controller(); const controller = new Controller();
controller.progressview.removeProgress("rebooting"); controller.progressview.removeProgress("rebooting");

View File

@ -30,12 +30,8 @@ export class SubmitView {
}); });
} }
this.backupBtn.onclick = () => { this.backupBtn.onclick = () => {
controller.progressview.addIndeterminate("backup", "Backup to EEPROM running") controller.backupConfig(this.json.textContent as string, (status: string) => {
controller.backupConfig(this.json.textContent as string).then(saveStatus => { this.submit_status.innerHTML = status;
controller.getBackupInfo().then(r => {
controller.progressview.removeProgress("backup")
this.submit_status.innerHTML = saveStatus;
});
}); });
} }
this.restoreBackupBtn.onclick = () => { this.restoreBackupBtn.onclick = () => {