Compare commits

..

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

31 changed files with 12511 additions and 18197 deletions

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

Binary file not shown.

View File

@ -1,133 +1,60 @@
P CODE 00
P UNITS CUST 0
P arrayDim N
317CONFIG2 VIA MD0157PA00X+060394Y-009724X0315Y0000R000S3
317GND VIA MD0157PA00X+058465Y-005591X0315Y0000R000S3
317GND VIA MD0157PA00X+060433Y-005591X0315Y0000R000S3
317GND VIA MD0157PA00X+050827Y-007402X0315Y0000R000S3
317SDA VIA MD0157PA00X+050134Y-014937X0315Y0000R000S3
317SCL VIA MD0157PA00X+051535Y-013898X0315Y0000R000S3
317CHARGE VIA MD0157PA00X+071575Y-006732X0315Y0000R000S3
317EN VIA MD0157PA00X+062697Y-009154X0315Y0000R000S3
317EN VIA MD0157PA00X+062500Y-010827X0315Y0000R000S3
317VBAT VIA MD0157PA00X+043130Y-036024X0315Y0000R000S3
317VBAT VIA MD0157PA00X+043130Y-035039X0315Y0000R000S3
317GND VIA MD1181PA00X+077500Y-007000X1575Y0000R000S3
317GND VIA MD0157PA00X+062402Y-010433X0315Y0000R000S3
317GND VIA MD0157PA00X+054449Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+044606Y-010551X0315Y0000R000S3
317GND VIA MD0157PA00X+054921Y-011417X0315Y0000R000S3
317GND VIA MD0157PA00X+050787Y-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 MD0157PA00X+054134Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+062205Y-012303X0315Y0000R000S3
317GND VIA MD0157PA00X+058169Y-012303X0315Y0000R000S3
317GND VIA MD0157PA00X+051772Y-009646X0315Y0000R000S3
317GND VIA MD1181PA00X+041500Y-042900X1575Y0000R000S3
317GND VIA MD0157PA00X+047165Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+042717Y-013031X0315Y0000R000S3
317GND VIA MD0157PA00X+057008Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+059685Y-012402X0315Y0000R000S3
317GND VIA MD0157PA00X+054429Y-011909X0315Y0000R000S3
317GND VIA MD0157PA00X+053346Y-010925X0315Y0000R000S3
317GND VIA MD0157PA00X+062205Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+058917Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+040945Y-035630X0315Y0000R000S3
317GND VIA MD0157PA00X+059606Y-011575X0315Y0000R000S3
317GND VIA MD1181PA00X+077500Y-042900X1575Y0000R000S3
317GND VIA MD0157PA00X+046890Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+054016Y-010591X0315Y0000R000S3
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+059055Y-012402X0315Y0000R000S3
317GND VIA MD0157PA00X+056594Y-012303X0315Y0000R000S3
317GND VIA MD0157PA00X+061122Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+043346Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+054626Y-010827X0315Y0000R000S3
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+054724Y-012205X0315Y0000R000S3
317GND VIA MD0157PA00X+063386Y-011220X0315Y0000R000S3
317GND VIA MD0157PA00X+060472Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+059685Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+045699Y-017156X0315Y0000R000S3
317GND VIA MD0157PA00X+048661Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+049724Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+048091Y-016535X0315Y0000R000S3
317GND VIA MD0157PA00X+053740Y-011220X0315Y0000R000S3
317GND VIA MD0157PA00X+048917Y-031102X0315Y0000R000S3
317GND VIA MD0157PA00X+053858Y-011693X0315Y0000R000S3
317GND VIA MD0157PA00X+049843Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+059232Y-011575X0315Y0000R000S3
317GND VIA MD0157PA00X+056299Y-005591X0315Y0000R000S3
317GND VIA MD0157PA00X+055709Y-011220X0315Y0000R000S3
317GND VIA MD0157PA00X+058169Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+044094Y-011654X0315Y0000R000S3
317GND VIA MD0157PA00X+061654Y-008071X0315Y0000R000S3
317GND VIA MD0157PA00X+050551Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+049370Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+042402Y-009213X0315Y0000R000S3
317GND VIA MD0157PA00X+059980Y-011594X0315Y0000R000S3
317GND VIA MD0157PA00X+056181Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+044409Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+055276Y-012323X0315Y0000R000S3
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+053583Y-011378X0315Y0000R000S3
317GND VIA MD0157PA00X+054035Y-010138X0315Y0000R000S3
317GND VIA MD0157PA00X+054331Y-010531X0315Y0000R000S3
317GND VIA MD0157PA00X+055217Y-012303X0315Y0000R000S3
317GND VIA MD0157PA00X+056102Y-012303X0315Y0000R000S3
317GND VIA MD0157PA00X+055315Y-011516X0315Y0000R000S3
317GND VIA MD0157PA00X+055709Y-012303X0315Y0000R000S3
317GND VIA MD0157PA00X+054921Y-011220X0315Y0000R000S3
317GND VIA MD0157PA00X+059843Y-005591X0315Y0000R000S3
317GND VIA MD0157PA00X+050787Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+049016Y-034154X0315Y0000R000S3
317GND VIA MD0157PA00X+045472Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+058543Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+054331Y-010866X0315Y0000R000S3
317GND VIA MD0157PA00X+046811Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+051772Y-008858X0315Y0000R000S3
317GND VIA MD0157PA00X+046555Y-030512X0315Y0000R000S3
317GND VIA MD0157PA00X+052913Y-009646X0315Y0000R000S3
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+061122Y-012303X0315Y0000R000S3
317GND VIA MD0157PA00X+055709Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+060827Y-012323X0315Y0000R000S3
317GND VIA MD0157PA00X+054134Y-011969X0315Y0000R000S3
317GND VIA MD0157PA00X+046535Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+043504Y-012205X0315Y0000R000S3
317GND VIA MD0157PA00X+059055Y-011614X0315Y0000R000S3
317GND VIA MD1181PA00X+041575Y-007087X1575Y0000R000S3
317GND VIA MD0157PA00X+050906Y-011260X0315Y0000R000S3
317GND VIA MD0157PA00X+042992Y-009646X0315Y0000R000S3
317GND VIA MD0157PA00X+040945Y-034646X0315Y0000R000S3
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+057185Y-011614X0315Y0000R000S3
317GND VIA MD0157PA00X+042953Y-012756X0315Y0000R000S3
317GND VIA MD0157PA00X+048031Y-014567X0315Y0000R000S3
317GND VIA MD0157PA00X+057185Y-011516X0315Y0000R000S3
3173_3V VIA MD0157PA00X+064646Y-014567X0315Y0000R000S3
3173_3V VIA MD0157PA00X+075906Y-014331X0315Y0000R000S3
3173_3V VIA MD0157PA00X+074094Y-014331X0315Y0000R000S3
@ -136,7 +63,12 @@ P arrayDim N
317TEMP VIA MD0157PA00X+049099Y-026344X0315Y0000R000S3
317TANK_SENSOR VIA MD0157PA00X+063091Y-007776X0315Y0000R000S3
317TANK_SENSOR VIA MD0157PA00X+047244Y-017421X0315Y0000R000S3
317CHARGE VIA MD0157PA00X+071949Y-006693X0315Y0000R000S3
317ESP_RX VIA MD0157PA00X+071949Y-007776X0315Y0000R000S3
317ESP_TX VIA MD0157PA00X+071949Y-007283X0315Y0000R000S3
317SDA VIA MD0157PA00X+048031Y-014094X0315Y0000R000S3
317SDA VIA MD0157PA00X+072441Y-010630X0315Y0000R000S3
317SCL VIA MD0157PA00X+048819Y-013780X0315Y0000R000S3
317SCL VIA MD0157PA00X+072441Y-011417X0315Y0000R000S3
317NET-(CD1-A) VIA MD0157PA00X+058661Y-009843X0315Y0000R000S3
317NET-(CD1-A) VIA MD0157PA00X+045866Y-005906X0315Y0000R000S3
@ -144,30 +76,25 @@ P arrayDim N
317ISDAY VIA MD0157PA00X+066929Y-011024X0315Y0000R000S3
317LED_ENABLE VIA MD0157PA00X+064764Y-010728X0315Y0000R000S3
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+050525Y-015584X0315Y0000R000S3
317USB_D- VIA MD0157PA00X+045276Y-010305X0315Y0000R000S3
317USB_D- VIA MD0157PA00X+063377Y-011752X0315Y0000R000S3
317USB_D- VIA MD0157PA00X+052902Y-009500X0315Y0000R000S3
317FLOW VIA MD0157PA00X+048543Y-023228X0315Y0000R000S3
317FLOW VIA MD0157PA00X+063484Y-007382X0315Y0000R000S3
317USB_D+ VIA MD0157PA00X+045276Y-010719X0315Y0000R000S3
317USB_D+ VIA MD0157PA00X+063377Y-012165X0315Y0000R000S3
317USB_D+ VIA MD0157PA00X+052610Y-009792X0315Y0000R000S3
317BOOT_SEL VIA MD0157PA00X+064764Y-009744X0315Y0000R000S3
317BOOT_SEL VIA MD0157PA00X+070374Y-012205X0315Y0000R000S3
317WARN_LED VIA MD0157PA00X+069803Y-008465X0315Y0000R000S3
317WARN_LED VIA MD0157PA00X+044390Y-005807X0315Y0000R000S3
317WARN_LED J8 -1 D0394PA00X+055787Y-006386X0669Y0669R000S0
317GND J8 -2 D0394PA00X+055787Y-007386X0669Y0000R000S0
317CONFIG2 J8 -3 D0394PA00X+055787Y-008386X0669Y0000R000S0
317ESP_TX J3 -1 D0394PA00X+073051Y-006457X0669Y0669R270S0
317ESP_RX J3 -2 D0394PA00X+074051Y-006457X0669Y0000R270S0
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
327EXTRA_1 U2 -6 A01X+064094Y-008209X0591Y0354R000S2
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
@ -180,15 +107,15 @@ P arrayDim N
327TEMP U2 -16 A01X+070984Y-011709X0591Y0354R000S2
327SCL U2 -17 A01X+070984Y-011209X0591Y0354R000S2
327SDA U2 -18 A01X+070984Y-010709X0591Y0354R000S2
327WORKING U2 -19 A01X+070984Y-010209X0591Y0354R000S2
327EXTRA_2 U2 -19 A01X+070984Y-010209X0591Y0354R000S2
327CD_PROBE U2 -20 A01X+070984Y-009709X0591Y0354R000S2
327WARN_LED U2 -21 A01X+070984Y-009209X0591Y0354R000S2
327EXTRA_1 U2 -21 A01X+070984Y-009209X0591Y0354R000S2
327-(U2-NC-PAD22) U2 -22 A01X+070984Y-008709X0591Y0354R000S2
327EXTRA_2 U2 -23 A01X+070984Y-008209X0591Y0354R000S2
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
327(U2-IO2-PAD27) U2 -27 A01X+070984Y-006209X0591Y0354R000S2
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
@ -211,43 +138,20 @@ P arrayDim N
317GND U2 -30_1 D0098PA00X+067439Y-008207X0138Y0000R000S3
317GND U2 -30_1 D0098PA00X+066701Y-008453X0138Y0000R000S3
317GND U2 -30_1 D0098PA00X+067193Y-008453X0138Y0000R000S3
317VBAT U7 -1 D0433PA00X+042795Y-037835X0750Y0787R180S0
317GND U7 -2 D0433PA00X+041795Y-037835X0750Y0787R180S0
3173_3V U7 -3 D0433PA00X+040795Y-037835X0750Y0787R180S0
327USB_D+ U1 -1 A01X+044124Y-010945X0522Y0236R180S2
327GND U1 -2 A01X+044124Y-010571X0522Y0236R180S2
327USB_D- U1 -3 A01X+044124Y-010197X0522Y0236R180S2
327SLASH}O2-PAD4) U1 -4 A01X+043228Y-010197X0522Y0236R180S2
327USB_BUS U1 -5 A01X+043228Y-010571X0522Y0236R180S2
327SLASH}O1-PAD6) U1 -6 A01X+043228Y-010945X0522Y0236R180S2
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
327VBAT D11 -2 A01X+046506Y-029252X0581Y0236R180S2
327NET-(D10-K) D11 -3 A01X+045768Y-029626X0581Y0236R180S2
327GND D5 -1 A01X+048027Y-023602X0581Y0236R180S2
3273_3V D5 -2 A01X+048027Y-022854X0581Y0236R180S2
327FLOW D5 -3 A01X+047288Y-023228X0581Y0236R180S2
327GND D6 -1 A01X+046043Y-016811X0581Y0236R180S2
3273_3V D6 -2 A01X+046043Y-016063X0581Y0236R180S2
327TANK_SENSOR D6 -3 A01X+045305Y-016437X0581Y0236R180S2
327GND D7 -1 A01X+048027Y-025669X0581Y0236R180S2
3273_3V D7 -2 A01X+048027Y-024921X0581Y0236R180S2
327TEMP D7 -3 A01X+047288Y-025295X0581Y0236R180S2
327GND D3 -1 A01X+051102Y-007992X0581Y0236R000S2
3273_3V D3 -2 A01X+051102Y-008740X0581Y0236R000S2
327SCL D3 -3 A01X+051841Y-008366X0581Y0236R000S2
327GND D9 -1 A01X+046486Y-033760X0581Y0236R180S2
327VBAT D9 -2 A01X+046486Y-033012X0581Y0236R180S2
327NET-(D8-K) D9 -3 A01X+045748Y-033386X0581Y0236R180S2
327GND D1 -1 A01X+046033Y-007933X0581Y0236R000S2
3273_3V D1 -2 A01X+046033Y-008681X0581Y0236R000S2
327SDA D1 -3 A01X+046772Y-008307X0581Y0236R000S2
327GND D4 -1 A01X+045305Y-017520X0581Y0236R000S2
3273_3V D4 -2 A01X+045305Y-018268X0581Y0236R000S2
327NET-(J4-PIN_1) D4 -3 A01X+046043Y-017894X0581Y0236R000S2
3173_3V C9 -1 D0315PA00X+046165Y-012441X0630Y0630R000S0
3173_3V C9 -1 D0315PA00X+046429Y-013071X0630Y0630R000S0
317GND C9 -2 D0315PA00X+047413Y-013071X0630Y0000R000S0
317GND C9 -2 D0315PA00X+047677Y-013701X0630Y0000R000S0
317VBAT U3 -1 D0394PA00X+078189Y-017373X0669Y0669R000S0
317ISDAY U3 -2 D0394PA00X+078189Y-016373X0669Y0669R000S0
317CHARGE U3 -3 D0394PA00X+078189Y-015373X0669Y0669R000S0
@ -259,8 +163,8 @@ P arrayDim N
317GND U3 -9 D0394PA00X+050827Y-017869X0669Y0669R000S0
317GND U3 -10 D0394PA00X+059685Y-010782X0669Y0669R000S0
317GND U3 -11 D0394PA00X+059685Y-017869X0669Y0669R000S0
327CONFIG2 Reset1-2 A01X+060394Y-009094X0591Y0591R090S2
327GND Reset1-1 A01X+060394Y-006024X0591Y0591R090S2
327GND Reset1-1 A01X+059843Y-006024X0591Y0591R090S2
327NET-(C4-PAD2) Reset1-2 A01X+059843Y-009094X0591Y0591R090S2
317GND U6 -1 D0394PA00X+078465Y-036024X0669Y0669R090S0
317GND U6 -2 D0394PA00X+071378Y-044016X0669Y0669R000S0
317GND U6 -3 D0394PA00X+047756Y-044016X0669Y0669R000S0
@ -279,10 +183,10 @@ P arrayDim N
327SDA R11 -2 A01X+072904Y-010630X0315Y0374R180S2
327GND C2 -1 A01X+062062Y-008268X0423Y0374R000S2
327EN C2 -2 A01X+062741Y-008268X0423Y0374R000S2
327GND Boot1 -1 A01X+058425Y-006024X0591Y0591R090S2
327T-(BOOT1-PAD2) Boot1 -2 A01X+058425Y-009094X0591Y0591R090S2
327GND Boot1 -1 A01X+056299Y-006024X0591Y0591R090S2
327T-(BOOT1-PAD2) Boot1 -2 A01X+056299Y-009094X0591Y0591R090S2
327EN R7 -1 A01X+061614Y-011152X0315Y0374R270S2
327CONFIG2 R7 -2 A01X+061614Y-010502X0315Y0374R270S2
327NET-(C4-PAD2) R7 -2 A01X+061614Y-010502X0315Y0374R270S2
317GND J5 -1 D0295PA00X+042323Y-021457X0472Y0689R270S0
3173_3V J5 -2 D0295PA00X+042323Y-020669X0472Y0689R270S0
317FLOW J5 -3 D0295PA00X+042323Y-019882X0472Y0689R270S0
@ -293,20 +197,35 @@ P arrayDim N
327GND R16 -2 A01X+048091Y-016043X0315Y0374R180S2
327NET-(Q2-G) R22 -1 A01X+047805Y-034843X0315Y0374R000S2
327GND R22 -2 A01X+048455Y-034843X0315Y0374R000S2
327FLOW D5 -3 A01X+047288Y-023228X0581Y0236R180S2
3273_3V D5 -2 A01X+048027Y-022854X0581Y0236R180S2
327GND D5 -1 A01X+048027Y-023602X0581Y0236R180S2
327NET-(D2-K) D2 -1 A01X+043701Y-006570X0384Y0551R270S2
327WARN_LED D2 -2 A01X+043701Y-005832X0384Y0551R270S2
327GND D6 -1 A01X+046043Y-016811X0581Y0236R180S2
3273_3V D6 -2 A01X+046043Y-016063X0581Y0236R180S2
327TANK_SENSOR D6 -3 A01X+045305Y-016437X0581Y0236R180S2
327NET-(CD1-A) R9 -1 A01X+058661Y-010305X0315Y0374R090S2
327WORKING R9 -2 A01X+058661Y-010955X0315Y0374R090S2
317GND J6 -1 D0394PA00X+046906Y-006220X0669Y0669R270S0
3173_3V J6 -2 D0394PA00X+047906Y-006220X0669Y0000R270S0
317SDA J6 -3 D0394PA00X+048906Y-006220X0669Y0000R270S0
317SCL J6 -4 D0394PA00X+049906Y-006220X0669Y0000R270S0
317SQW J6 -5 D0394PA00X+050906Y-006220X0669Y0000R270S0
31732K J6 -6 D0394PA00X+051906Y-006220X0669Y0000R270S0
317GND J6 -1 D0394PA00X+041319Y-014661X0669Y0669R180S0
3173_3V J6 -2 D0394PA00X+041319Y-013661X0669Y0000R180S0
317SDA J6 -3 D0394PA00X+041319Y-012661X0669Y0000R180S0
317SCL J6 -4 D0394PA00X+041319Y-011661X0669Y0000R180S0
317SQW J6 -5 D0394PA00X+041319Y-010661X0669Y0000R180S0
31732K J6 -6 D0394PA00X+041319Y-009661X0669Y0000R180S0
327NET-(D8-K) D8 -1 A01X+040945Y-033243X0384Y0551R270S2
327NET-(D8-A) D8 -2 A01X+040945Y-032505X0384Y0551R270S2
327NET-(J2-PIN_2) R2 -1 A01X+042943Y-014764X0315Y0374R000S2
327GND R2 -2 A01X+043593Y-014764X0315Y0374R000S2
327NET-(J2-PIN_2) R2 -1 A01X+046457Y-005581X0315Y0374R090S2
327GND R2 -2 A01X+046457Y-006230X0315Y0374R090S2
327USB_D+ U1 -1 A01X+050251Y-009286X0522Y0236R180S2
327GND U1 -2 A01X+050251Y-008912X0522Y0236R180S2
327USB_D- U1 -3 A01X+050251Y-008538X0522Y0236R180S2
327SLASH}O2-PAD4) U1 -4 A01X+049355Y-008538X0522Y0236R180S2
327USB_BUS U1 -5 A01X+049355Y-008912X0522Y0236R180S2
327SLASH}O1-PAD6) U1 -6 A01X+049355Y-009286X0522Y0236R180S2
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
327GND C8 -2 A01X+041718Y-036024X0463Y0571R180S2
327NET-(R3-PAD1) R3 -1 A01X+057677Y-012933X0315Y0374R090S2
@ -315,6 +234,9 @@ P arrayDim N
327ENABLE_TANK R14 -2 A01X+050089Y-016043X0315Y0374R000S2
327GND C1 -1 A01X+056299Y-011112X0423Y0374R270S2
327T-(BOOT1-PAD2) C1 -2 A01X+056299Y-010433X0423Y0374R270S2
327GND D3 -1 A01X+046752Y-012106X0581Y0236R000S2
3273_3V D3 -2 A01X+046752Y-012854X0581Y0236R000S2
327SCL D3 -3 A01X+047490Y-012480X0581Y0236R000S2
327TEMP R19 -1 A01X+049099Y-025295X0315Y0374R000S2
3273_3V R19 -2 A01X+049749Y-025295X0315Y0374R000S2
3273_3V C6 -1 A01X+062205Y-007392X0463Y0571R270S2
@ -330,46 +252,57 @@ P arrayDim N
327NET-(Q3-G) Q3 -1 A01X+047756Y-030413X0354Y0315R270S2
327GND Q3 -2 A01X+048504Y-030413X0354Y0315R270S2
327NET-(D10-K) Q3 -3 A01X+048130Y-029626X0354Y0315R270S2
327GND D9 -1 A01X+046486Y-033760X0581Y0236R180S2
327VBAT D9 -2 A01X+046486Y-033012X0581Y0236R180S2
327NET-(D8-K) D9 -3 A01X+045748Y-033386X0581Y0236R180S2
327VBAT R17 -1 A01X+046033Y-032185X0315Y0374R000S2
327NET-(D8-A) R17 -2 A01X+046683Y-032185X0315Y0374R000S2
327GND C5 -1 A01X+062992Y-010379X0423Y0374R270S2
327BOOT_SEL C5 -2 A01X+062992Y-009700X0423Y0374R270S2
327TANK_SENSOR R12 -1 A01X+047215Y-016831X0404Y0551R270S2
3273_3V R12 -2 A01X+047215Y-016112X0404Y0551R270S2
327NET-(D2-K) R1 -1 A01X+044488Y-008317X0315Y0374R270S2
327GND R1 -2 A01X+044488Y-007667X0315Y0374R270S2
327NET-(D2-K) R1 -1 A01X+045935Y-007874X0315Y0374R000S2
327GND R1 -2 A01X+046585Y-007874X0315Y0374R000S2
327EXTRA_1 R20 -1 A01X+048455Y-031890X0315Y0374R180S2
327NET-(Q3-G) R20 -2 A01X+047805Y-031890X0315Y0374R180S2
317NET-(D10-K) Extra_-1 D0394PA00X+042283Y-029616X0669Y0787R270S0
317VBAT Extra_-2 D0394PA00X+042283Y-028632X0669Y0787R270S0
327GND D1 -1 A01X+047441Y-014469X0581Y0236R180S2
3273_3V D1 -2 A01X+047441Y-013720X0581Y0236R180S2
327SDA D1 -3 A01X+046703Y-014094X0581Y0236R180S2
3273_3V R10 -1 A01X+073553Y-011417X0315Y0374R180S2
327SCL R10 -2 A01X+072904Y-011417X0315Y0374R180S2
327EXTRA_2 R23 -1 A01X+048455Y-035728X0315Y0374R180S2
327NET-(Q2-G) R23 -2 A01X+047805Y-035728X0315Y0374R180S2
327VBAT R18 -1 A01X+046033Y-028445X0315Y0374R000S2
327NET-(D10-A) R18 -2 A01X+046683Y-028445X0315Y0374R000S2
327NET-(J2-PIN_3) R4 -1 A01X+042943Y-014134X0315Y0374R000S2
327GND R4 -2 A01X+043593Y-014134X0315Y0374R000S2
327NET-(J2-PIN_5) R4 -1 A01X+053150Y-005581X0315Y0374R090S2
327GND R4 -2 A01X+053150Y-006230X0315Y0374R090S2
327NET-(D10-K) D10 -1 A01X+040945Y-029542X0384Y0551R270S2
327NET-(D10-A) D10 -2 A01X+040945Y-028804X0384Y0551R270S2
317NET-(J4-PIN_1) J4 -1 D0295PA00X+042323Y-017638X0472Y0689R270S0
317TANK_SENSOR J4 -2 D0295PA00X+042323Y-016850X0472Y0689R270S0
327NET-(CD2-K) CD2 -1 A01X+040945Y-017594X0384Y0551R270S2
3273_3V CD2 -2 A01X+040945Y-016855X0384Y0551R270S2
317ESP_TX J3 -1 D0394PA00X+077559Y-005709X0669Y0669R270S0
317ESP_RX J3 -2 D0394PA00X+078559Y-005709X0669Y0000R270S0
327BOOT_SEL R5 -1 A01X+057677Y-010305X0315Y0374R090S2
327T-(BOOT1-PAD2) R5 -2 A01X+057677Y-010955X0315Y0374R090S2
327NET-(J4-PIN_1) R13 -1 A01X+047047Y-018346X0315Y0374R090S2
327NET-(CD2-K) R13 -2 A01X+047047Y-018996X0315Y0374R090S2
327GND D4 -1 A01X+045305Y-017520X0581Y0236R000S2
3273_3V D4 -2 A01X+045305Y-018268X0581Y0236R000S2
327NET-(J4-PIN_1) D4 -3 A01X+046043Y-017894X0581Y0236R000S2
327GND CD1 -1 A01X+045177Y-006570X0384Y0551R270S2
327NET-(CD1-A) CD1 -2 A01X+045177Y-005832X0384Y0551R270S2
327GND C4 -1 A01X+060630Y-011166X0423Y0374R270S2
327CONFIG2 C4 -2 A01X+060630Y-010487X0423Y0374R270S2
317USB_BUS J2 -1 D0295PA00X+041260Y-009803X0472Y0689R090S0
317NET-(J2-PIN_2) J2 -2 D0295PA00X+041260Y-010591X0472Y0689R090S0
317NET-(J2-PIN_3) J2 -3 D0295PA00X+041260Y-011378X0472Y0689R090S0
317USB_D- J2 -4 D0295PA00X+041260Y-012165X0472Y0689R090S0
317USB_D+ J2 -5 D0295PA00X+041260Y-012953X0472Y0689R090S0
317GND J2 -6 D0295PA00X+041260Y-013740X0472Y0689R090S0
327NET-(C4-PAD2) C4 -2 A01X+060630Y-010487X0423Y0374R270S2
317USB_BUS J2 -1 D0295PA00X+047835Y-006142X0472Y0689R000S0
317NET-(J2-PIN_2) J2 -2 D0295PA00X+048622Y-006142X0472Y0689R000S0
317USB_D+ J2 -3 D0295PA00X+049409Y-006142X0472Y0689R000S0
317USB_D- J2 -4 D0295PA00X+050197Y-006142X0472Y0689R000S0
317NET-(J2-PIN_5) J2 -5 D0295PA00X+050984Y-006142X0472Y0689R000S0
317GND J2 -6 D0295PA00X+051772Y-006142X0472Y0689R000S0
3273_3V R15 -1 A01X+049680Y-023228X0315Y0374R180S2
327FLOW R15 -2 A01X+049031Y-023228X0315Y0374R180S2
327NET-(Q1-G) Q1 -1 A01X+048770Y-016831X0354Y0315R090S2
@ -380,20 +313,12 @@ P arrayDim N
327NET-(Q2-G) Q2 -1 A01X+047776Y-034142X0354Y0315R270S2
327GND Q2 -2 A01X+048524Y-034142X0354Y0315R270S2
327NET-(D8-K) Q2 -3 A01X+048150Y-033354X0354Y0315R270S2
317GND J1 -1 D0394PA00X+047913Y-008346X0669Y0669R270S0
317SDA J1 -2 D0394PA00X+048913Y-008346X0669Y0000R270S0
317SCL J1 -3 D0394PA00X+049913Y-008346X0669Y0000R270S0
317VBAT U7 -1 D0433PA00X+042795Y-040315X0750Y0787R180S0
317GND U7 -2 D0433PA00X+041795Y-040315X0750Y0787R180S0
3173_3V U7 -3 D0433PA00X+040795Y-040315X0750Y0787R180S0
317GND J1 -1 D0394PA00X+044882Y-012047X0669Y0669R000S0
317SCL J1 -2 D0394PA00X+044882Y-013047X0669Y0000R000S0
317SDA J1 -3 D0394PA00X+044882Y-014047X0669Y0000R000S0
327BOOT_SEL R6 -1 A01X+057677Y-011683X0315Y0374R090S2
327NET-(R3-PAD1) R6 -2 A01X+057677Y-012333X0315Y0374R090S2
3173_3V U5 -1 D0394PA00X+046339Y-019764X0669Y0669R000S0
317(U5-VBAT-PAD2) U5 -2 D0394PA00X+046339Y-020764X0669Y0000R000S0
317SDA U5 -3 D0394PA00X+046339Y-021764X0669Y0000R000S0
317SCL U5 -4 D0394PA00X+046339Y-022764X0669Y0000R000S0
317CD_PROBE U5 -5 D0394PA00X+046339Y-023764X0669Y0000R000S0
317GND U5 -6 D0394PA00X+046339Y-024764X0669Y0000R000S0
317GND U5 -7 D0394PA00X+062835Y-021063X0669Y0669R000S0
317GND U5 -8 D0394PA00X+078583Y-019488X0669Y0669R000S0
317GND U5 -9 D0394PA00X+078583Y-034646X0669Y0669R000S0
317GND U5 -10 D0394PA00X+062835Y-031693X0669Y0669R000S0
317GND U5 -11 D0394PA00X+046102Y-034646X0669Y0669R000S0
999

View File

@ -5,20 +5,20 @@ target = "riscv32imac-esp-espidf"
[target.riscv32imac-esp-espidf]
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"
runner = "espflash flash --monitor --baud 921600 --flash-size 16mb --partition-table partitions.csv"
#runner = "cargo runner"
#runner = "espflash flash --monitor --partition-table partitions.csv -b no-reset" # create upgrade image file for webupload
# runner = espflash erase-parts otadata //ensure flash is clean
rustflags = ["--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110
rustflags = [ "--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110
[unstable]
build-std = ["std", "panic_abort"]
[env]
MCU = "esp32c6"
MCU="esp32c6"
# Note: this variable is not used by the pio builder (`cargo build --features pio`)
ESP_IDF_VERSION = "v5.2.1"
CHRONO_TZ_TIMEZONE_FILTER = "UTC|America/New_York|America/Chicago|America/Los_Angeles|Europe/London|Europe/Berlin|Europe/Paris|Asia/Tokyo|Asia/Shanghai|Asia/Kolkata|Australia/Sydney|America/Sao_Paulo|Africa/Johannesburg|Asia/Dubai|Pacific/Auckland"

View File

@ -1,11 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<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" />
</profile>
</component>

View File

@ -10,12 +10,15 @@ resolver = "2"
# Explicitly disable LTO which the Xtensa codegen backend has issues
lto = false
strip = false
debug = true
debug = true
overflow-checks = true
panic = "abort"
incremental = true
opt-level = 2
opt-level = "s"
[profile.dev.build-override]
opt-level = 1
incremental = true
[package.metadata.cargo_runner]
# 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 }
embedded-hal = "1.0.0"
heapless = { version = "0.8", features = ["serde"] }
embedded-hal-bus = { version = "0.3.0", features = ["std"] }
embedded-hal-bus = { version = "0.2.0", features = ["std"] }
#Hardware additional driver
ds18b20 = "0.1.1"
@ -66,17 +69,16 @@ strum = { version = "0.27.0", features = ["derive"] }
measurements = "0.11.0"
#json
serde = { version = "1.0.192", features = ["derive"] }
serde = { version = "1.0.192", features = ["derive"] }
serde_json = "1.0.108"
#timezone
chrono = { version = "0.4.23", default-features = false, features = ["iana-time-zone", "alloc", "serde"] }
chrono-tz = { version = "0.10.3", default-features = false, features = ["filter-by-regex"] }
chrono = { version = "0.4.23", default-features = false , features = ["iana-time-zone" , "alloc", "serde"] }
chrono-tz = {version="0.8.0", default-features = false , features = [ "filter-by-regex" ]}
eeprom24x = "0.7.2"
url = "2.5.3"
crc = "3.2.1"
bincode = "2.0.1"
bincode = "1.3.3"
ringbuffer = "0.15.0"
text-template = "0.1.0"
strum_macros = "0.27.0"
@ -84,8 +86,6 @@ esp-ota = { version = "0.2.2", features = ["log"] }
unit-enum = "1.4.1"
pca9535 = { version = "2.0.0", features = ["std"] }
ina219 = { version = "0.2.0", features = ["std"] }
embedded-storage = "=0.3.1"
ekv = "1.0.0"
[patch.crates-io]
@ -98,5 +98,5 @@ ekv = "1.0.0"
[build-dependencies]
cc = "=1.1.30"
embuild = { version = "0.32.0", features = ["espidf"] }
embuild = { version= "0.32.0", features = ["espidf"]}
vergen = { version = "8.2.6", features = ["build", "git", "gitcl"] }

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,
otadata, data, ota, , 8k,
phy_init, data, phy, , 4k,
ota_0, app, ota_0, , 3968k,
ota_1, app, ota_1, , 3968k,
storage, data, spiffs, , 8M,
ota_0, app, ota_0, , 5632k,
ota_1, app, ota_1, , 5632k,
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

@ -118,9 +118,6 @@ pub struct PlantConfig {
pub max_consecutive_pump_count: u8,
pub moisture_sensor_min_frequency: Option<f32>, // Optional min frequency
pub moisture_sensor_max_frequency: Option<f32>, // Optional max frequency
pub min_pump_current_ma: u16,
pub max_pump_current_ma: u16,
pub ignore_current_error: bool,
}
impl Default for PlantConfig {
@ -137,9 +134,6 @@ impl Default for PlantConfig {
max_consecutive_pump_count: 10,
moisture_sensor_min_frequency: None, // No override by default
moisture_sensor_max_frequency: None, // No override by default
min_pump_current_ma: 10,
max_pump_current_ma: 3000,
ignore_current_error: true,
}
}
}

View File

@ -240,6 +240,14 @@ impl Esp<'_> {
println!("Wrote config config {:?}", config);
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<()> {
log(LogMessage::MountingFilesystem, 0, 0, "", "");
let base_path = CString::new("/spiffs")?;
@ -300,7 +308,10 @@ impl Esp<'_> {
OkStd(file) => {
let f = FileInfo {
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,
};
result.push(f);

View File

@ -1,7 +1,5 @@
use crate::hal::esp::Esp;
use crate::hal::rtc::{BackupHeader, RTCModuleInteraction};
use crate::hal::water::TankSensor;
use crate::hal::{deep_sleep, BoardInteraction, FreePeripherals, Sensor};
use crate::hal::{deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor};
use crate::{
config::PlantControllerConfig,
hal::battery::{BatteryInteraction, NoBatteryMonitor},
@ -18,31 +16,6 @@ pub struct Initial<'a> {
pub(crate) esp: Esp<'a>,
pub(crate) config: PlantControllerConfig,
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(
@ -63,16 +36,11 @@ pub(crate) fn create_initial_board(
config,
esp,
battery: Box::new(NoBatteryMonitor {}),
rtc: Box::new(NoRTC {}),
};
Ok(Box::new(v))
}
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> {
&mut self.esp
}
@ -85,10 +53,6 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
&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<()> {
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) -> ! {
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 {
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<()> {
bail!("Please configure board revision")
}
@ -106,15 +91,9 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
fn pump(&mut self, _plant: usize, _enable: bool) -> Result<()> {
bail!("Please configure board revision")
}
fn pump_current(&mut self, _plant: usize) -> Result<Current> {
bail!("Please configure board revision")
}
fn fault(&mut self, _plant: usize, _enable: bool) -> Result<()> {
bail!("Please configure board revision")
}
fn measure_moisture_hz(&mut self, _plant: usize, _sensor: Sensor) -> Result<f32> {
bail!("Please configure board revision")
}
@ -123,6 +102,21 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
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<()> {
bail!("Please configure board revision")
}
fn test(&mut self) -> Result<()> {
bail!("Please configure board revision")
}

View File

@ -1,13 +1,9 @@
pub(crate) mod battery;
mod esp;
mod initial_hal;
mod rtc;
mod v3_hal;
mod v4_hal;
mod water;
use crate::hal::rtc::{DS3231Module, RTCModuleInteraction};
use crate::hal::water::TankSensor;
use crate::{
config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig},
hal::{
@ -19,8 +15,7 @@ use crate::{
use anyhow::{Ok, Result};
use battery::BQ34Z100G1;
use bq34z100::Bq34z100g1Driver;
use ds323x::{DateTimeAccess, Ds323x};
use eeprom24x::{Eeprom24x, SlaveAddr, Storage};
use chrono::{DateTime, Utc};
use embedded_hal_bus::i2c::MutexDevice;
use esp_idf_hal::{
adc::ADC1,
@ -44,6 +39,7 @@ use esp_idf_sys::{
use esp_ota::mark_app_valid;
use measurements::{Current, Voltage};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use std::result::Result::Ok as OkStd;
use std::sync::Mutex;
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);
const X25: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC);
fn deep_sleep(duration_in_ms: u64) -> ! {
unsafe {
//if we don't do this here, we might just revert newly flashed firmware
@ -86,41 +84,42 @@ pub struct HAL<'a> {
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> {
fn get_tank_sensor(&mut self) -> Option<&mut TankSensor<'a>>;
fn get_esp(&mut self) -> &mut Esp<'a>;
fn get_config(&mut self) -> &PlantControllerConfig;
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 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;
//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 pump(&mut self, plant: usize, enable: bool) -> Result<()>;
fn pump_current(&mut self, plant: usize) -> Result<Current>;
fn fault(&mut self, plant: usize, enable: bool) -> Result<()>;
fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32>;
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(&mut self) -> Result<()>;
fn set_config(&mut self, config: PlantControllerConfig) -> Result<()>;
fn get_mptt_voltage(&mut self) -> anyhow::Result<Voltage>;
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)]
pub struct FreePeripherals {
pub gpio0: Gpio0,
@ -260,30 +259,6 @@ impl PlantHal {
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 {
Result::Ok(config) => {
let battery_interaction: Box<dyn BatteryInteraction + Send> =
@ -321,10 +296,10 @@ impl PlantHal {
initial_hal::create_initial_board(free_pins, fs_mount_error, config, esp)?
}
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 => {
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::{
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::{
config::PlantControllerConfig,
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_bus::i2c::MutexDevice;
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},
};
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en};
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError};
use measurements::{Current, Voltage};
use one_wire_bus::OneWire;
use plant_ctrl2::sipo::ShiftRegister40;
use std::result::Result::Ok as OkStd;
@ -67,7 +79,6 @@ const FAULT_2: usize = 23;
pub struct V3<'a> {
config: PlantControllerConfig,
battery_monitor: Box<dyn BatteryInteraction + Send>,
rtc_module: Box<dyn RTCModuleInteraction + Send>,
esp: Esp<'a>,
shift_register: ShiftRegister40<
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
@ -76,12 +87,22 @@ pub struct V3<'a> {
>,
_shift_register_enable_invert:
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>,
light: 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>,
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(
@ -89,7 +110,6 @@ pub(crate) fn create_v3(
esp: Esp<'static>,
config: PlantControllerConfig,
battery_monitor: Box<dyn BatteryInteraction + Send>,
rtc_module: Box<dyn RTCModuleInteraction + Send>,
) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
let mut clock = PinDriver::input_output(peripherals.gpio15.downgrade())?;
clock.set_pull(Pull::Floating)?;
@ -121,15 +141,39 @@ pub(crate) fn create_v3(
let ms4 = &mut shift_register.decompose()[MS_4];
ms4.set_high()?;
let one_wire_pin = peripherals.gpio18.downgrade();
let tank_power_pin = peripherals.gpio11.downgrade();
println!("Init battery driver");
let tank_sensor = TankSensor::create(
one_wire_pin,
peripherals.adc1,
peripherals.gpio5,
tank_power_pin,
);
println!("Init rtc driver");
let mut rtc = Ds323x::new_ds3231(MutexDevice::new(&I2C_DRIVER));
println!("Init rtc eeprom driver");
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(
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())?;
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())?;
main_pump.set_pull(Pull::Floating)?;
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())?;
general_fault.set_pull(Pull::Floating)?;
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())?;
unsafe { gpio_hold_dis(shift_register_enable_invert.pin()) };
@ -176,24 +233,43 @@ pub(crate) fn create_v3(
Ok(Box::new(V3 {
config,
battery_monitor,
rtc_module,
esp,
shift_register,
_shift_register_enable_invert: shift_register_enable_invert,
tank_sensor,
tank_channel,
solar_is_day,
light,
main_pump,
tank_power,
general_fault,
signal_counter,
one_wire_bus,
rtc,
eeprom,
}))
}
impl<'a> BoardInteraction<'a> for V3<'a> {
fn get_tank_sensor(&mut self) -> Option<&mut TankSensor<'a>> {
Some(&mut self.tank_sensor)
fn set_charge_indicator(&mut self, charging: bool) -> Result<()> {
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> {
&mut self.esp
}
@ -206,28 +282,158 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
&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) -> ! {
let _ = self.shift_register.decompose()[AWAKE].set_low();
deep_sleep(duration_in_ms)
}
fn is_day(&self) -> bool {
self.solar_is_day.get_level().into()
fn get_backup_info(&mut self) -> 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)?;
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<()> {
unsafe { gpio_hold_dis(self.light.pin()) };
self.light.set_state(enable.into())?;
unsafe { gpio_hold_en(self.light.pin()) };
Ok(())
}
fn pump(&mut self, plant: usize, enable: bool) -> Result<()> {
if enable {
self.main_pump.set_high()?;
@ -244,6 +450,7 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
7 => PUMP8_BIT,
_ => bail!("Invalid pump {plant}",),
};
//currently infallible error, keep for future as result anyway
self.shift_register.decompose()[index].set_state(enable.into())?;
if !enable {
@ -252,10 +459,6 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
Ok(())
}
fn pump_current(&mut self, _plant: usize) -> Result<Current> {
bail!("Not implemented in v3")
}
fn fault(&mut self, plant: usize, enable: bool) -> Result<()> {
let index = match plant {
0 => FAULT_1,
@ -334,8 +537,8 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
self.shift_register.decompose()[MS_4].set_low()?;
self.shift_register.decompose()[SENSOR_ON].set_high()?;
let measurement = 100; //how long to measure and then extrapolate to hz
let factor = 1000f32 / measurement as f32; //scale raw cound by this number to get hz
let measurement = 100; // TODO what is this scaling factor? what is its purpose?
let factor = 1000f32 / measurement as f32;
//give some time to stabilize
self.esp.delay.delay_ms(10);
@ -369,6 +572,41 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
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<()> {
self.pump(plant, true)?;
unsafe { vTaskDelay(30000) };
self.pump(plant, false)?;
Ok(())
}
fn test(&mut self) -> Result<()> {
self.general_fault(true);
self.esp.delay.delay_ms(100);
@ -407,22 +645,9 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
Ok(())
}
fn set_config(&mut self, config: PlantControllerConfig) -> Result<()> {
fn set_config(&mut self, config: PlantControllerConfig) -> anyhow::Result<()> {
self.config = config;
self.esp.save_config(&self.config)?;
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,28 +1,34 @@
use crate::config::PlantControllerConfig;
use crate::hal::battery::BatteryInteraction;
use crate::hal::esp::Esp;
use crate::hal::rtc::RTCModuleInteraction;
use crate::hal::water::TankSensor;
use crate::hal::{
deep_sleep, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, 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 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_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::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;
use esp_idf_hal::pcnt::{
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::calibration::UnCalibrated;
use ina219::configuration::{Configuration, OperatingMode};
use ina219::SyncIna219;
use measurements::{Current, Resistance, Voltage};
use one_wire_bus::OneWire;
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
use std::result::Result::Ok as OkStd;
@ -39,7 +45,6 @@ pub enum Charger<'a> {
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>,
},
ErrorInit {},
}
impl Charger<'_> {
@ -62,7 +67,6 @@ impl Charger<'_> {
);
});
}
_ => {}
}
}
fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> {
@ -72,7 +76,6 @@ impl Charger<'_> {
} => {
charge_indicator.set_state(charging.into())?;
}
_ => {}
}
Ok(())
}
@ -80,7 +83,6 @@ impl Charger<'_> {
fn is_day(&self) -> bool {
match self {
Charger::SolarMpptV1 { solar_is_day, .. } => solar_is_day.get_level().into(),
_ => true,
}
}
@ -89,9 +91,6 @@ impl Charger<'_> {
Charger::SolarMpptV1 { mppt_ina, .. } => mppt_ina
.bus_voltage()
.map(|v| Voltage::from_millivolts(v.voltage_mv() as f64))?,
_ => {
bail!("hardware error during init")
}
};
Ok(voltage)
}
@ -104,9 +103,6 @@ impl Charger<'_> {
let current = shunt_voltage.as_volts() / shut_value.as_ohms();
Current::from_amperes(current)
})?,
_ => {
bail!("hardware error during init")
}
};
Ok(current)
}
@ -114,20 +110,26 @@ impl Charger<'_> {
pub struct V4<'a> {
esp: Esp<'a>,
tank_sensor: TankSensor<'a>,
charger: Charger<'a>,
rtc_module: Box<dyn RTCModuleInteraction + Send>,
battery_monitor: Box<dyn BatteryInteraction + Send>,
config: PlantControllerConfig,
tank_channel: AdcChannelDriver<'a, Gpio5, AdcDriver<'a, esp_idf_hal::adc::ADC1>>,
signal_counter: PcntDriver<'a>,
awake: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
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>,
pump_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>,
pump_ina: Option<SyncIna219<MutexDevice<'a, I2cDriver<'a>>, UnCalibrated>>,
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(
@ -135,30 +137,48 @@ pub(crate) fn create_v4(
esp: Esp<'static>,
config: PlantControllerConfig,
battery_monitor: Box<dyn BatteryInteraction + Send>,
rtc_module: Box<dyn RTCModuleInteraction + Send>,
) -> 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()?;
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_low()?;
let mut extra1 = PinDriver::output(peripherals.gpio6.downgrade())?;
extra1.set_low()?;
println!("Init rtc driver");
let mut rtc = Ds323x::new_ds3231(MutexDevice::new(&I2C_DRIVER));
let mut extra2 = PinDriver::output(peripherals.gpio15.downgrade())?;
extra2.set_low()?;
println!("Init rtc eeprom driver");
let mut eeprom = {
Eeprom24x::new_24x32(
MutexDevice::new(&I2C_DRIVER),
SlaveAddr::Alternative(true, true, true),
)
};
let one_wire_pin = peripherals.gpio18.downgrade();
let tank_power_pin = peripherals.gpio11.downgrade();
let mut one_wire_pin = PinDriver::input_output_od(peripherals.gpio18.downgrade())?;
one_wire_pin.set_pull(Pull::Floating)?;
let tank_sensor = TankSensor::create(
one_wire_pin,
peripherals.adc1,
peripherals.gpio5,
tank_power_pin,
);
let one_wire_bus = OneWire::new(one_wire_pin)
.map_err(|err| -> anyhow::Error { anyhow!("Missing attribute: {:?}", err) })?;
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(
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())?;
solar_is_day.set_pull(Pull::Floating)?;
let mut light = PinDriver::input_output(peripherals.gpio10.downgrade())?;
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())?;
charge_indicator.set_pull(Pull::Floating)?;
charge_indicator.set_low()?;
let mut pump_expander = Pca9535Immediate::new(MutexDevice::new(&I2C_DRIVER), 32);
//todo error handing if init error
for pin in 0..8 {
let _ = pump_expander.pin_into_output(GPIOBank::Bank0, pin);
let _ = pump_expander.pin_into_output(GPIOBank::Bank1, pin);
@ -208,67 +242,56 @@ pub(crate) fn create_v4(
let _ = sensor_expander.pin_set_low(GPIOBank::Bank1, pin);
}
let mppt_ina = SyncIna219::new(
//TODO error handling is not done nicely here, should not break if ina is not responsive
let mut mppt_ina = SyncIna219::new(
MutexDevice::new(&I2C_DRIVER),
Address::from_pins(Pin::Vcc, Pin::Gnd),
)?;
mppt_ina.set_configuration(Configuration {
reset: Default::default(),
bus_voltage_range: Default::default(),
shunt_voltage_range: Default::default(),
bus_resolution: Default::default(),
shunt_resolution: ina219::configuration::Resolution::Avg128,
operating_mode: Default::default(),
})?;
//TODO this is probably already done until we are ready first time?, maybe add startup time comparison on access?
esp.delay.delay_ms(
mppt_ina
.configuration()?
.conversion_time()
.unwrap()
.as_millis() as u32,
);
let charger = match mppt_ina {
Ok(mut mppt_ina) => {
mppt_ina.set_configuration(Configuration {
reset: Default::default(),
bus_voltage_range: Default::default(),
shunt_voltage_range: Default::default(),
bus_resolution: Default::default(),
shunt_resolution: ina219::configuration::Resolution::Avg128,
operating_mode: Default::default(),
})?;
Charger::SolarMpptV1 {
mppt_ina,
solar_is_day,
charge_indicator,
}
}
Err(_) => Charger::ErrorInit {},
};
let pump_ina = match SyncIna219::new(
MutexDevice::new(&I2C_DRIVER),
Address::from_pins(Pin::Gnd, Pin::Sda),
) {
Ok(pump_ina) => Some(pump_ina),
Err(err) => {
println!("Error creating pump ina: {:?}", err);
None
}
let charger = Charger::SolarMpptV1 {
mppt_ina,
solar_is_day,
charge_indicator,
};
let v = V4 {
rtc_module,
esp,
awake,
tank_sensor,
tank_channel,
signal_counter,
light,
tank_power,
one_wire_bus,
rtc,
eeprom,
general_fault,
pump_ina,
pump_expander,
sensor_expander,
config,
battery_monitor,
charger,
extra1,
extra2,
};
Ok(Box::new(v))
}
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> {
&mut self.esp
}
@ -281,10 +304,6 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
&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<()> {
self.charger.set_charge_indicator(charging)
}
@ -295,10 +314,151 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
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 {
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<()> {
unsafe { gpio_hold_dis(self.light.pin()) };
self.light.set_state(enable.into())?;
@ -317,24 +477,6 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
anyhow::Ok(())
}
fn pump_current(&mut self, _plant: usize) -> anyhow::Result<Current> {
//sensore is shared for all pumps, ignore plant id
match self.pump_ina.as_mut() {
None => {
bail!("pump current sensor not available");
}
Some(pump_ina) => {
let v = pump_ina.shunt_voltage().map(|v| {
let shunt_voltage = Voltage::from_microvolts(v.shunt_voltage_uv().abs() as f64);
let shut_value = Resistance::from_ohms(0.05_f64);
let current = shunt_voltage.as_volts() / shut_value.as_ohms();
Current::from_amperes(current)
})?;
Ok(v)
}
}
}
fn fault(&mut self, plant: usize, enable: bool) -> anyhow::Result<()> {
if enable {
self.pump_expander
@ -427,6 +569,42 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
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<()> {
self.pump(plant, true)?;
self.esp.delay.delay_ms(30000);
self.pump(plant, false)?;
anyhow::Ok(())
}
fn test(&mut self) -> anyhow::Result<()> {
self.general_fault(true);
self.esp.delay.delay_ms(100);

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

@ -93,7 +93,6 @@ pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &st
let serial_entry = template.fill_in(&values);
println!("{serial_entry}");
//TODO push to mqtt?
let entry = LogEntry {
timestamp: time,
@ -194,16 +193,6 @@ pub enum LogMessage {
serialize = "Pumped multiple times, but plant is still to try attempt: ${number_a} limit :: ${number_b} plant: ${txt_short}"
)]
ConsecutivePumpCountLimit,
#[strum(
serialize = "Pump Overcurrent error, pump: ${number_a} tripped overcurrent ${number_b} limit was ${txt_short} @s ${txt_long}"
)]
PumpOverCurrent,
#[strum(
serialize = "Pump Open loop error, pump: ${number_a} is low, ${number_b} limit was ${txt_short} @s ${txt_long}"
)]
PumpOpenLoopCurrent,
#[strum(serialize = "Pump Open current sensor required but did not work: ${number_a}")]
PumpMissingSensorCurrent,
}
#[derive(Serialize)]

View File

@ -1,10 +1,9 @@
use crate::config::PlantConfig;
use crate::{
config::BoardVersion::INITIAL,
hal::{PlantHal, HAL, PLANT_COUNT},
webserver::httpd,
};
use anyhow::{bail, Context};
use anyhow::bail;
use chrono::{DateTime, Datelike, Timelike, Utc};
use chrono_tz::Tz::{self, UTC};
use esp_idf_hal::delay::Delay;
@ -34,6 +33,8 @@ mod webserver;
pub static BOARD_ACCESS: Lazy<Mutex<HAL>> = Lazy::new(|| PlantHal::create().unwrap());
pub static STAY_ALIVE: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false));
#[derive(Serialize, Deserialize, Debug, PartialEq)]
enum WaitType {
MissingConfig,
@ -41,12 +42,6 @@ enum WaitType {
MqttConfig,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct Solar {
current_ma: u32,
voltage_ma: u32,
}
impl WaitType {
fn blink_pattern(&self) -> u32 {
match self {
@ -77,17 +72,6 @@ struct LightState {
struct PumpInfo {
enabled: bool,
pump_ineffective: bool,
median_current_ma: u16,
max_current_ma: u16,
min_current_ma: u16,
}
#[derive(Serialize)]
pub struct PumpResult {
median_current_ma: u16,
max_current_ma: u16,
min_current_ma: u16,
error: bool,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
@ -172,7 +156,6 @@ fn safe_main() -> anyhow::Result<()> {
let cur = board
.board_hal
.get_rtc_module()
.get_rtc_time()
.or_else(|err| {
println!("rtc module error: {:?}", err);
@ -237,8 +220,6 @@ fn safe_main() -> anyhow::Result<()> {
try_connect_wifi_sntp_mqtt(&mut board)
} else {
println!("No wifi configured");
//the current sensors require this amount to stabilize, in case of wifi this is already handles for sure;
board.board_hal.get_esp().delay.delay_ms(100);
NetworkMode::OFFLINE
};
@ -278,7 +259,6 @@ fn safe_main() -> anyhow::Result<()> {
timezone_time,
);
publish_battery_state(&mut board);
let _ = publish_mppt_state(&mut board);
}
log(
@ -347,12 +327,8 @@ fn safe_main() -> anyhow::Result<()> {
}
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 res < WATER_FROZEN_THRESH {
water_frozen = true;
@ -409,20 +385,14 @@ fn safe_main() -> anyhow::Result<()> {
board.board_hal.get_esp().last_pump_time(plant_id);
//state.active = true;
pump_info(&mut board, plant_id, true, pump_ineffective, 0, 0, 0, false);
pump_info(&mut board, plant_id, true, pump_ineffective);
let result = do_secure_pump(&mut board, plant_id, plant_config, dry_run)?;
board.board_hal.pump(plant_id, false)?;
pump_info(
&mut board,
plant_id,
false,
pump_ineffective,
result.median_current_ma,
result.max_current_ma,
result.min_current_ma,
result.error,
);
if !dry_run {
board.board_hal.pump(plant_id, true)?;
Delay::new_default().delay_ms(1000 * plant_config.pump_time_s as u32);
board.board_hal.pump(plant_id, false)?;
}
pump_info(&mut board, plant_id, false, pump_ineffective);
} else if !state.pump_in_timeout(plant_config, &timezone_time) {
// plant does not need to be watered and is not in timeout
// -> reset consecutive pump count
@ -576,92 +546,6 @@ fn safe_main() -> anyhow::Result<()> {
.deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64);
}
pub fn do_secure_pump(
board: &mut MutexGuard<HAL>,
plant_id: usize,
plant_config: &PlantConfig,
dry_run: bool,
) -> anyhow::Result<PumpResult> {
let mut current_collector = vec![0_u16; plant_config.pump_time_s.into()];
let mut error = false;
let mut first_error = true;
if !dry_run {
board.board_hal.pump(plant_id, true)?;
Delay::new_default().delay_ms(2);
for step in 0..plant_config.pump_time_s as usize {
let current = board.board_hal.pump_current(plant_id);
match current {
Ok(current) => {
let current_ma = current.as_milliamperes() as u16;
current_collector[step] = current_ma;
let high_current = current_ma > plant_config.max_pump_current_ma;
if high_current {
if first_error {
log(
LogMessage::PumpOverCurrent,
plant_id as u32 + 1,
current_ma as u32,
plant_config.max_pump_current_ma.to_string().as_str(),
step.to_string().as_str(),
);
board.board_hal.general_fault(true);
board.board_hal.fault(plant_id, true)?;
if !plant_config.ignore_current_error {
error = true;
break;
}
first_error = false;
}
}
let low_current = current_ma < plant_config.min_pump_current_ma;
if low_current {
if first_error {
log(
LogMessage::PumpOpenLoopCurrent,
plant_id as u32 + 1,
current_ma as u32,
plant_config.min_pump_current_ma.to_string().as_str(),
step.to_string().as_str(),
);
board.board_hal.general_fault(true);
board.board_hal.fault(plant_id, true)?;
if !plant_config.ignore_current_error {
error = true;
break;
}
first_error = false;
}
}
}
Err(err) => {
if !plant_config.ignore_current_error {
println!("Error getting pump current: {}", err);
log(
LogMessage::PumpMissingSensorCurrent,
plant_id as u32,
0,
"",
"",
);
error = true;
break;
} else {
//eg v3 without a sensor ends here, do not spam
}
}
}
Delay::new_default().delay_ms(1000);
}
}
current_collector.sort();
Ok(PumpResult {
median_current_ma: current_collector[current_collector.len() / 2],
max_current_ma: current_collector[current_collector.len() - 1],
min_current_ma: current_collector[0],
error,
})
}
fn update_charge_indicator(board: &mut MutexGuard<HAL>) {
//we have mppt controller, ask it for charging current
if let Ok(current) = board.board_hal.get_mptt_current() {
@ -682,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(
board: &mut MutexGuard<HAL>,
tank_state: &TankState,
@ -774,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) {
Ok(new_time) => {
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 }
}
Err(err) => {
@ -817,17 +723,10 @@ fn pump_info(
plant_id: usize,
pump_active: bool,
pump_ineffective: bool,
median_current_ma: u16,
max_current_ma: u16,
min_current_ma: u16,
error: bool,
) {
let pump_info = PumpInfo {
enabled: pump_active,
pump_ineffective,
median_current_ma: median_current_ma,
max_current_ma: max_current_ma,
min_current_ma: min_current_ma,
};
let pump_topic = format!("/pump{}", plant_id + 1);
match serde_json::to_string(&pump_info) {
@ -845,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<'_>>) {
let state = board.board_hal.get_battery_monitor().get_battery_state();
if let Ok(serialized_battery_state_bytes) =

View File

@ -1,5 +1,4 @@
use crate::{config::TankConfig, hal::HAL};
use anyhow::Context;
use serde::Serialize;
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 {
if board.board_hal.get_config().tank.tank_sensor_enabled {
match board
.board_hal
.get_tank_sensor()
.context("no sensor")
.and_then(|f| f.tank_sensor_voltage())
{
match board.board_hal.tank_sensor_voltage() {
Ok(raw_sensor_value_mv) => TankState::Present(raw_sensor_value_mv),
Err(err) => TankState::Error(TankError::BoardError(err.to_string())),
}

View File

@ -2,13 +2,13 @@
use crate::{
config::PlantControllerConfig,
determine_tank_state, do_secure_pump, get_version,
determine_tank_state, get_version,
hal::PLANT_COUNT,
log::LogMessage,
plant_state::{MoistureSensorState, PlantState},
BOARD_ACCESS,
};
use anyhow::{bail, Context};
use anyhow::bail;
use chrono::DateTime;
use core::result::Result::Ok;
use embedded_svc::http::Method;
@ -60,7 +60,7 @@ pub struct TestPump {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct WebBackupHeader {
timestamp: std::string::String,
size: u16,
size: usize,
}
#[derive(Deserialize)]
@ -81,10 +81,7 @@ fn write_time(
tv_usec: 0,
};
unsafe { settimeofday(&now, core::ptr::null_mut()) };
board
.board_hal
.get_rtc_module()
.set_rtc_time(&parsed.to_utc())?;
board.board_hal.set_rtc_time(&parsed.to_utc())?;
anyhow::Ok(None)
}
@ -100,7 +97,6 @@ fn get_time(
.unwrap_or("error".to_string());
let rtc = board
.board_hal
.get_rtc_module()
.get_rtc_time()
.map(|t| t.to_rfc3339())
.unwrap_or("error".to_string());
@ -174,9 +170,7 @@ fn backup_config(
) -> Result<Option<std::string::String>, anyhow::Error> {
let all = read_up_to_bytes_from_request(request, Some(3072))?;
let mut board = BOARD_ACCESS.lock().expect("board access");
//TODO how to handle progress here? prior versions animated the fault leds while running
board.board_hal.get_rtc_module().backup_config(&all)?;
board.board_hal.backup_config(&all)?;
anyhow::Ok(Some("saved".to_owned()))
}
@ -184,7 +178,7 @@ fn get_backup_config(
_request: &mut Request<&mut EspHttpConnection>,
) -> Result<Option<std::string::String>, anyhow::Error> {
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(),
Err(err) => {
println!("Error get backup config {:?}", err);
@ -198,7 +192,7 @@ fn backup_info(
_request: &mut Request<&mut EspHttpConnection>,
) -> Result<Option<std::string::String>, anyhow::Error> {
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 {
Ok(h) => {
let timestamp = DateTime::from_timestamp_millis(h.timestamp).unwrap();
@ -208,9 +202,10 @@ fn backup_info(
};
serde_json::to_string(&wbh)?
}
Err(err) => {
Err(_) => {
//TODO make better
let wbh = WebBackupHeader {
timestamp: err.to_string(),
timestamp: "no backup".to_owned(),
size: 0,
};
serde_json::to_string(&wbh)?
@ -222,11 +217,11 @@ fn backup_info(
fn set_config(
request: &mut Request<&mut EspHttpConnection>,
) -> Result<Option<std::string::String>, anyhow::Error> {
let all = read_up_to_bytes_from_request(request, Some(4096))?;
let all = read_up_to_bytes_from_request(request, Some(3072))?;
let config: PlantControllerConfig = serde_json::from_slice(&all)?;
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()))
}
@ -235,8 +230,8 @@ fn get_solar_state(
) -> Result<Option<std::string::String>, anyhow::Error> {
let mut board = BOARD_ACCESS.lock().expect("board access");
let state = SolarState {
mppt_voltage: board.board_hal.get_mptt_voltage()?.as_millivolts() as f32,
mppt_current: board.board_hal.get_mptt_current()?.as_milliamperes() as f32,
mppt_voltage: board.board_hal.get_mptt_voltage()?.as_volts() as f32,
mppt_current: board.board_hal.get_mptt_current()?.as_amperes() as f32,
is_day: board.board_hal.is_day(),
};
anyhow::Ok(Some(serde_json::to_string(&state)?))
@ -275,11 +270,8 @@ fn pump_test(
let actual_data = read_up_to_bytes_from_request(request, None)?;
let pump_test: TestPump = serde_json::from_slice(&actual_data)?;
let mut board = BOARD_ACCESS.lock().unwrap();
let config = &board.board_hal.get_config().plants[pump_test.pump].clone();
let pump_result = do_secure_pump(&mut board, pump_test.pump, config, false)?;
board.board_hal.pump(pump_test.pump, false)?;
anyhow::Ok(Some(serde_json::to_string(&pump_result)?))
board.board_hal.test_pump(pump_test.pump)?;
anyhow::Ok(None)
}
fn tank_info(
@ -288,12 +280,7 @@ fn tank_info(
let mut board = BOARD_ACCESS.lock().unwrap();
let tank_info = determine_tank_state(&mut board);
//should be multsampled
let water_temp = board
.board_hal
.get_tank_sensor()
.context("no sensor")
.and_then(|f| f.water_temperature_c());
let water_temp = board.board_hal.water_temperature_c();
Ok(Some(serde_json::to_string(&tank_info.as_mqtt_info(
&board.board_hal.get_config().tank,
&water_temp,
@ -354,7 +341,6 @@ fn ota(
let iter = (total_read / 1024) % 8;
if iter != lastiter {
board.board_hal.general_fault(iter % 5 == 0);
for i in 0..PLANT_COUNT {
let _ = board.board_hal.fault(i, iter == i);
}

View File

@ -1,185 +1,174 @@
export interface LogArray extends Array<LogEntry> {
}
export interface LogArray extends Array<LogEntry>{}
export interface LogEntry {
timestamp: string,
message_id: number,
a: number,
b: number,
txt_short: string,
txt_long: string
timestamp: string,
message_id: number,
a: number,
b: number,
txt_short: string,
txt_long: string
}
export interface LogLocalisation extends Array<LogLocalisationEntry> {
}
export interface LogLocalisation extends Array<LogLocalisationEntry>{}
export interface LogLocalisationEntry {
msg_type: string,
message: string
msg_type: string,
message: string
}
export interface BackupHeader {
timestamp: string,
size: number
timestamp: string,
size: number
}
export interface NetworkConfig {
ap_ssid: string,
ssid: string,
password: string,
mqtt_url: string,
base_topic: string,
max_wait: number
ap_ssid: string,
ssid: string,
password: string,
mqtt_url: string,
base_topic: string,
max_wait: number
}
export interface FileList {
total: number,
used: number,
files: FileInfo[],
file_system_corrupt: string,
iter_error: string,
total: number,
used: number,
files: FileInfo[],
file_system_corrupt: string,
iter_error: string,
}
export interface SolarState {
mppt_voltage: number,
mppt_current: number,
is_day: boolean
export interface SolarState{
mppt_voltage: number,
mppt_current: number,
is_day: boolean
}
export interface FileInfo {
filename: string,
size: number,
export interface FileInfo{
filename: string,
size: number,
}
export interface NightLampConfig {
enabled: boolean,
night_lamp_hour_start: number,
night_lamp_hour_end: number,
night_lamp_only_when_dark: boolean,
low_soc_cutoff: number,
low_soc_restore: number
enabled: boolean,
night_lamp_hour_start: number,
night_lamp_hour_end: number,
night_lamp_only_when_dark: boolean,
low_soc_cutoff: number,
low_soc_restore: number
}
export interface NightLampCommand {
active: boolean
active: boolean
}
export interface TankConfig {
tank_sensor_enabled: boolean,
tank_allow_pumping_if_sensor_error: boolean,
tank_useable_ml: number,
tank_warn_percent: number,
tank_empty_percent: number,
tank_full_percent: number,
tank_sensor_enabled: boolean,
tank_allow_pumping_if_sensor_error: boolean,
tank_useable_ml: number,
tank_warn_percent: number,
tank_empty_percent: number,
tank_full_percent: number,
}
export enum BatteryBoardVersion {
Disabled = "Disabled",
BQ34Z100G1 = "BQ34Z100G1",
WchI2cSlave = "WchI2cSlave"
Disabled = "Disabled",
BQ34Z100G1 = "BQ34Z100G1",
WchI2cSlave = "WchI2cSlave"
}
export enum BoardVersion {
export enum BoardVersion{
INITIAL = "INITIAL",
V3 = "V3",
V4 = "V4"
}
export interface BoardHardware {
board: BoardVersion,
battery: BatteryBoardVersion,
board: BoardVersion,
battery: BatteryBoardVersion,
}
export interface PlantControllerConfig {
hardware: BoardHardware,
hardware: BoardHardware,
network: NetworkConfig,
tank: TankConfig,
night_lamp: NightLampConfig,
plants: PlantConfig[]
timezone?: string,
network: NetworkConfig,
tank: TankConfig,
night_lamp: NightLampConfig,
plants: PlantConfig[]
timezone?: string,
}
export interface PlantConfig {
mode: string,
target_moisture: number,
pump_time_s: number,
pump_cooldown_min: number,
pump_hour_start: number,
pump_hour_end: number,
sensor_a: boolean,
sensor_b: boolean,
max_consecutive_pump_count: number,
moisture_sensor_min_frequency: number | null;
moisture_sensor_max_frequency: number | null;
min_pump_current_ma: number,
max_pump_current_ma: number,
ignore_current_error: boolean,
mode: string,
target_moisture: number,
pump_time_s: number,
pump_cooldown_min: number,
pump_hour_start: number,
pump_hour_end: number,
sensor_a: boolean,
sensor_b: boolean,
max_consecutive_pump_count: number,
moisture_sensor_min_frequency: number | null;
moisture_sensor_max_frequency: number | null;
}
export interface PumpTestResult {
median_current_ma: number,
max_current_ma: number,
min_current_ma: number,
error: boolean,
}
export interface SSIDList {
ssids: [string]
ssids: [string]
}
export interface TestPump {
pump: number
pump: number
}
export interface SetTime {
time: string
time: string
}
export interface GetTime {
rtc: string,
native: string
rtc: string,
native: string
}
export interface Moistures {
moisture_a: [string],
moisture_b: [string],
moisture_a: [string],
moisture_b: [string],
}
export interface VersionInfo {
git_hash: string,
build_time: string,
partition: string
git_hash: string,
build_time: string,
partition: string
}
export interface BatteryState {
temperature: string
voltage_milli_volt: string,
current_milli_ampere: string,
cycle_count: string,
design_milli_ampere: string,
remaining_milli_ampere: string,
state_of_charge: string,
state_of_health: string
temperature: string
voltage_milli_volt: string,
current_milli_ampere: string,
cycle_count: string,
design_milli_ampere: string,
remaining_milli_ampere: string,
state_of_charge: string,
state_of_health: string
}
export interface TankInfo {
/// is there enough water in the tank
enough_water: boolean,
/// warning that water needs to be refilled soon
warn_level: boolean,
/// estimation how many ml are still in tank
left_ml: number | null,
/// if there is was an issue with the water level sensor
sensor_error: string | null,
/// raw water sensor value
raw: number | null,
/// percent value
percent: number | null,
/// water in tank might be frozen
water_frozen: boolean,
/// water temperature
water_temp: number | null,
temp_sensor_error: string | null
/// is there enough water in the tank
enough_water: boolean,
/// warning that water needs to be refilled soon
warn_level: boolean,
/// estimation how many ml are still in tank
left_ml: number | null,
/// if there is was an issue with the water level sensor
sensor_error: string | null,
/// raw water sensor value
raw: number | null,
/// percent value
percent: number | null,
/// water in tank might be frozen
water_frozen: boolean,
/// water temperature
water_temp: number | null,
temp_sensor_error: string | null
}

View File

@ -75,15 +75,15 @@
.subcontainer {
min-width: 300px;
max-width: 900px;
flex-grow: 1;
border-style: solid;
border-width: 1px;
flex-grow: 1;
border-style: solid;
border-width: 1px;
padding: 8px;
}
.subcontainercontainer{
flex-grow: 1;
}
.plantcontainer {
flex-grow: 1;
min-width: 100%;
@ -120,15 +120,15 @@
}
.plantlist {
display: flex;
display: flex;
flex-wrap: wrap;
}
.subtitle {
flex-grow: 1;
text-align: center;
flex-grow: 1;
text-align: center;
font-weight: bold;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,53 +1,30 @@
<style>
.plantsensorkey {
min-width: 100px;
}
.plantsensorkey{
min-width: 100px;
}
.plantsensorvalue{
flex-grow: 1;
}
.plantsensorvalue {
flex-grow: 1;
}
.plantkey {
min-width: 195px;
}
.plantvalue {
flex-grow: 1;
}
.plantcheckbox {
min-width: 20px;
margin: 0;
}
.plantTargetEnabledOnly_${plantId}{
}
.plantPumpEnabledOnly_${plantId}{
}
.plantSensorEnabledOnly_${plantId}{
}
.plantHidden_${plantId} {
display: none;
}
.plantkey{
min-width: 175px;
}
.plantvalue{
flex-grow: 1;
}
.plantcheckbox{
min-width: 20px;
margin: 0;
}
</style>
<div>
<div class="subtitle"
id="plant_${plantId}_header">
id="plant_${plantId}_header">
Plant ${plantId}
</div>
<div class="flexcontainer">
<div class="plantkey">Sensor A installed:</div>
<input class="plantcheckbox" id="plant_${plantId}_sensor_a" type="checkbox">
</div>
<div class="flexcontainer">
<div class="plantkey">Sensor B installed:</div>
<input class="plantcheckbox" id="plant_${plantId}_sensor_b" type="checkbox">
</div>
<div class="flexcontainer">
<div class="plantkey">
Mode:
@ -59,78 +36,64 @@
</select>
</div>
<div class="flexcontainer plantTargetEnabledOnly_${plantId}">
<div class="flexcontainer">
<div class="plantkey">Target Moisture:</div>
<input class="plantvalue" id="plant_${plantId}_target_moisture" type="number" min="0" max="100" placeholder="0">
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="flexcontainer">
<div class="plantkey">Pump Time (s):</div>
<input class="plantvalue" id="plant_${plantId}_pump_time_s" type="number" min="0" max="600" placeholder="30">
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="flexcontainer">
<div class="plantkey">Pump Cooldown (m):</div>
<input class="plantvalue" id="plant_${plantId}_pump_cooldown_min" type="number" min="0" max="600"
placeholder="30">
<input class="plantvalue" id="plant_${plantId}_pump_cooldown_min" type="number" min="0" max="600" placeholder="30">
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="flexcontainer">
<div class="plantkey">"Pump Hour Start":</div>
<select class="plantvalue" id="plant_${plantId}_pump_hour_start">10</select>
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="flexcontainer">
<div class="plantkey">"Pump Hour End":</div>
<select class="plantvalue" id="plant_${plantId}_pump_hour_end">19</select>
</div>
<div class="flexcontainer plantTargetEnabledOnly_${plantId}">
<div class="flexcontainer">
<div class="plantkey">Warn Pump Count:</div>
<input class="plantvalue" id="plant_${plantId}_max_consecutive_pump_count" type="number" min="1" max="50"
placeholder="10">
<input class="plantvalue" id="plant_${plantId}_max_consecutive_pump_count" type="number" min="1" max="50"
placeholder="10">
</div>
<div class="flexcontainer plantSensorEnabledOnly_${plantId}">
<div class="flexcontainer">
<div class="plantkey">Min Frequency Override</div>
<input class="plantvalue" id="plant_${plantId}_min_frequency" type="number" min="1000" max="25000">
</div>
<div class="flexcontainer plantSensorEnabledOnly_${plantId}">
<div class="flexcontainer">
<div class="plantkey">Max Frequency Override</div>
<input class="plantvalue" id="plant_${plantId}_max_frequency" type="number" min="1000" max="25000">
<input class="plantvalue" id="plant_${plantId}_max_frequency" type="number" min="1000" max="25000" >
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<h2 class="plantkey">Current config:</h2>
<div class="flexcontainer">
<div class="plantkey">Sensor A installed:</div>
<input class="plantcheckbox" id="plant_${plantId}_sensor_a" type="checkbox">
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="plantkey">Min current</div>
<input class="plantvalue" id="plant_${plantId}_min_pump_current_ma" type="number" min="0" max="4500">
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="plantkey">Max current</div>
<input class="plantvalue" id="plant_${plantId}_max_pump_current_ma" type="number" min="0" max="4500">
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="plantkey">Ignore current sensor error</div>
<input class="plantcheckbox" id="plant_${plantId}_ignore_current_error" type="checkbox">
<div class="flexcontainer">
<div class="plantkey">Sensor B installed:</div>
<input class="plantcheckbox" id="plant_${plantId}_sensor_b" type="checkbox">
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="flexcontainer">
<button class="subtitle" id="plant_${plantId}_test">Test Pump</button>
</div>
<div class="flexcontainer plantSensorEnabledOnly_${plantId}">
<div class="flexcontainer">
<div class="subtitle">Live:</div>
</div>
<div class="flexcontainer plantSensorEnabledOnly_${plantId}">
<div class="flexcontainer">
<span class="plantsensorkey">Sensor A:</span>
<span class="plantsensorvalue" id="plant_${plantId}_moisture_a">not measured</span>
<span class="plantsensorvalue" id="plant_${plantId}_moisture_a">loading</span>
</div>
<div class="flexcontainer plantSensorEnabledOnly_${plantId}">
<div class="flexcontainer">
<div class="plantsensorkey">Sensor B:</div>
<span class="plantsensorvalue" id="plant_${plantId}_moisture_b">not measured</span>
</div>
<div class="flexcontainer plantPumpEnabledOnly_${plantId}">
<div class="plantsensorkey">Test Current</div>
<span class="plantsensorvalue" id="plant_${plantId}_pump_current_result">not_tested</span>
<span class="plantsensorvalue" id="plant_${plantId}_moisture_b">loading</span>
</div>

View File

@ -1,4 +1,4 @@
import {PlantConfig, PumpTestResult} from "./api";
import {PlantConfig} from "./api";
const PLANT_COUNT = 8;
@ -9,47 +9,39 @@ export class PlantViews {
private readonly measure_moisture: HTMLButtonElement;
private readonly plants: PlantView[] = []
private readonly plantsDiv: HTMLDivElement
constructor(syncConfig: Controller) {
this.measure_moisture = document.getElementById("measure_moisture") as HTMLButtonElement
this.measure_moisture.onclick = syncConfig.measure_moisture
this.plantsDiv = document.getElementById("plants") as HTMLDivElement;
for (let plantId = 0; plantId < PLANT_COUNT; plantId++) {
this.plants[plantId] = new PlantView(plantId, this.plantsDiv, syncConfig);
}
constructor(syncConfig:Controller) {
this.measure_moisture = document.getElementById("measure_moisture") as HTMLButtonElement
this.measure_moisture.onclick = syncConfig.measure_moisture
this.plantsDiv = document.getElementById("plants") as HTMLDivElement;
for (let plantId = 0; plantId < PLANT_COUNT; plantId++) {
this.plants[plantId] = new PlantView(plantId, this.plantsDiv, syncConfig);
}
}
getConfig(): PlantConfig[] {
const rv: PlantConfig[] = [];
for (let i = 0; i < PLANT_COUNT; i++) {
rv[i] = this.plants[i].getConfig();
}
return rv
const rv: PlantConfig[] = [];
for (let i = 0; i < PLANT_COUNT; i++) {
rv[i] = this.plants[i].getConfig();
}
return rv
}
update(moisture_a: [string], moisture_b: [string]) {
for (let plantId = 0; plantId < PLANT_COUNT; plantId++) {
const a = moisture_a[plantId]
const b = moisture_b[plantId]
this.plants[plantId].setMeasurementResult(a, b)
}
for (let plantId = 0; plantId < PLANT_COUNT; plantId++) {
const a = moisture_a[plantId]
const b = moisture_b[plantId]
this.plants[plantId].update(a,b)
}
}
setConfig(plants: PlantConfig[]) {
for (let plantId = 0; plantId < PLANT_COUNT; plantId++) {
const plantConfig = plants[plantId];
const plantView = this.plants[plantId];
plantView.setConfig(plantConfig)
}
}
setPumpTestCurrent(plantId: number, response: PumpTestResult) {
for (let plantId = 0; plantId < PLANT_COUNT; plantId++) {
const plantConfig = plants[plantId];
const plantView = this.plants[plantId];
plantView.setTestResult(response)
plantView.setConfig(plantConfig)
}
}
}
}
export class PlantView {
private readonly moistureSensorMinFrequency: HTMLInputElement;
@ -68,172 +60,109 @@ export class PlantView {
private readonly mode: HTMLSelectElement;
private readonly moistureA: HTMLElement;
private readonly moistureB: HTMLElement;
private readonly pump_current_result: HTMLElement
private readonly maxConsecutivePumpCount: HTMLInputElement;
private readonly minPumpCurrentMa: HTMLInputElement;
private readonly maxPumpCurrentMa: HTMLInputElement;
private readonly ignoreCurrentError: HTMLInputElement;
constructor(plantId: number, parent: HTMLDivElement, controller: Controller) {
constructor(plantId: number, parent:HTMLDivElement, controller:Controller) {
this.plantId = plantId;
this.plantDiv = document.createElement("div")! as HTMLDivElement
const template = require('./plant.html') as string;
this.plantDiv = document.createElement("div")! as HTMLDivElement
const template = require('./plant.html') as string;
this.plantDiv.innerHTML = template.replaceAll("${plantId}", String(plantId))
this.plantDiv.classList.add("plantcontainer")
parent.appendChild(this.plantDiv)
this.header = document.getElementById("plant_"+plantId+"_header")!
this.header.innerText = "Plant "+ (this.plantId+1)
this.moistureA = document.getElementById("plant_"+plantId+"_moisture_a")! as HTMLElement;
this.moistureB = document.getElementById("plant_"+plantId+"_moisture_b")! as HTMLElement;
this.plantDiv.classList.add("plantcontainer")
parent.appendChild(this.plantDiv)
this.testButton = document.getElementById("plant_"+plantId+"_test")! as HTMLButtonElement;
this.testButton.onclick = function(){
controller.testPlant(plantId)
}
this.header = document.getElementById("plant_" + plantId + "_header")!
this.header.innerText = "Plant " + (this.plantId + 1)
this.mode = document.getElementById("plant_"+plantId+"_mode") as HTMLSelectElement
this.mode.onchange = function(){
controller.configChanged()
}
this.moistureA = document.getElementById("plant_" + plantId + "_moisture_a")! as HTMLElement;
this.moistureB = document.getElementById("plant_" + plantId + "_moisture_b")! as HTMLElement;
this.pump_current_result = document.getElementById("plant_" + plantId + "_pump_current_result")! as HTMLElement;
this.targetMoisture = document.getElementById("plant_"+plantId+"_target_moisture")! as HTMLInputElement;
this.targetMoisture.onchange = function(){
controller.configChanged()
}
this.pumpTimeS = document.getElementById("plant_"+plantId+"_pump_time_s") as HTMLInputElement;
this.pumpTimeS.onchange = function(){
controller.configChanged()
}
this.testButton = document.getElementById("plant_" + plantId + "_test")! as HTMLButtonElement;
this.testButton.onclick = function () {
controller.testPlant(plantId)
this.pumpCooldown = document.getElementById("plant_"+plantId+"_pump_cooldown_min") as HTMLInputElement;
this.pumpCooldown.onchange = function(){
controller.configChanged()
}
this.pumpHourStart = document.getElementById("plant_"+plantId+"_pump_hour_start") as HTMLSelectElement;
this.pumpHourStart.onchange = function(){
controller.configChanged()
}
for (let i = 0; i < 24; i++) {
let option = document.createElement("option");
if (i == 10){
option.selected = true
}
option.innerText = i.toString();
this.pumpHourStart.appendChild(option);
}
this.mode = document.getElementById("plant_" + plantId + "_mode") as HTMLSelectElement
this.mode.onchange = function () {
controller.configChanged()
this.pumpHourEnd = document.getElementById("plant_"+plantId+"_pump_hour_end") as HTMLSelectElement;
this.pumpHourEnd.onchange = function(){
controller.configChanged()
}
for (let i = 0; i < 24; i++) {
let option = document.createElement("option");
if (i == 19){
option.selected = true
}
option.innerText = i.toString();
this.pumpHourEnd.appendChild(option);
}
this.targetMoisture = document.getElementById("plant_" + plantId + "_target_moisture")! as HTMLInputElement;
this.targetMoisture.onchange = function () {
controller.configChanged()
}
this.sensorAInstalled = document.getElementById("plant_"+plantId+"_sensor_a") as HTMLInputElement;
this.sensorAInstalled.onchange = function(){
controller.configChanged()
}
this.pumpTimeS = document.getElementById("plant_" + plantId + "_pump_time_s") as HTMLInputElement;
this.pumpTimeS.onchange = function () {
controller.configChanged()
}
this.sensorBInstalled = document.getElementById("plant_"+plantId+"_sensor_b") as HTMLInputElement;
this.sensorBInstalled.onchange = function(){
controller.configChanged()
}
this.pumpCooldown = document.getElementById("plant_" + plantId + "_pump_cooldown_min") as HTMLInputElement;
this.pumpCooldown.onchange = function () {
controller.configChanged()
}
this.maxConsecutivePumpCount = document.getElementById("plant_"+plantId+"_max_consecutive_pump_count") as HTMLInputElement;
this.maxConsecutivePumpCount.onchange = function(){
controller.configChanged()
}
this.pumpHourStart = document.getElementById("plant_" + plantId + "_pump_hour_start") as HTMLSelectElement;
this.pumpHourStart.onchange = function () {
controller.configChanged()
}
for (let i = 0; i < 24; i++) {
let option = document.createElement("option");
if (i == 10) {
option.selected = true
}
option.innerText = i.toString();
this.pumpHourStart.appendChild(option);
}
this.pumpHourEnd = document.getElementById("plant_" + plantId + "_pump_hour_end") as HTMLSelectElement;
this.pumpHourEnd.onchange = function () {
controller.configChanged()
}
for (let i = 0; i < 24; i++) {
let option = document.createElement("option");
if (i == 19) {
option.selected = true
}
option.innerText = i.toString();
this.pumpHourEnd.appendChild(option);
}
this.sensorAInstalled = document.getElementById("plant_" + plantId + "_sensor_a") as HTMLInputElement;
this.sensorAInstalled.onchange = function () {
controller.configChanged()
}
this.sensorBInstalled = document.getElementById("plant_" + plantId + "_sensor_b") as HTMLInputElement;
this.sensorBInstalled.onchange = function () {
controller.configChanged()
}
this.minPumpCurrentMa = document.getElementById("plant_" + plantId + "_min_pump_current_ma") as HTMLInputElement;
this.minPumpCurrentMa.onchange = function () {
controller.configChanged()
}
this.maxPumpCurrentMa = document.getElementById("plant_" + plantId + "_max_pump_current_ma") as HTMLInputElement;
this.maxPumpCurrentMa.onchange = function () {
controller.configChanged()
}
this.ignoreCurrentError = document.getElementById("plant_" + plantId + "_ignore_current_error") as HTMLInputElement;
this.ignoreCurrentError.onchange = function () {
controller.configChanged()
}
this.maxConsecutivePumpCount = document.getElementById("plant_" + plantId + "_max_consecutive_pump_count") as HTMLInputElement;
this.maxConsecutivePumpCount.onchange = function () {
controller.configChanged()
}
this.moistureSensorMinFrequency = document.getElementById("plant_" + plantId + "_min_frequency") as HTMLInputElement;
this.moistureSensorMinFrequency.onchange = function () {
this.moistureSensorMinFrequency = document.getElementById("plant_"+plantId+"_min_frequency") as HTMLInputElement;
this.moistureSensorMinFrequency.onchange = function(){
controller.configChanged()
}
this.moistureSensorMinFrequency.onchange = () => {
controller.configChanged();
};
this.moistureSensorMaxFrequency = document.getElementById("plant_" + plantId + "_max_frequency") as HTMLInputElement;
this.moistureSensorMaxFrequency = document.getElementById("plant_"+plantId+"_max_frequency") as HTMLInputElement;
this.moistureSensorMaxFrequency.onchange = () => {
controller.configChanged();
};
}
updateVisibility(plantConfig: PlantConfig) {
let sensorOnly = document.getElementsByClassName("plantSensorEnabledOnly_"+ this.plantId)
let pumpOnly = document.getElementsByClassName("plantPumpEnabledOnly_"+ this.plantId)
let targetOnly = document.getElementsByClassName("plantTargetEnabledOnly_"+ this.plantId)
console.log("updateVisibility plantConfig: " + plantConfig.mode)
let showSensor = plantConfig.sensor_a || plantConfig.sensor_b
let showPump = plantConfig.mode !== "OFF"
let showTarget = plantConfig.mode === "TargetMoisture"
console.log("updateVisibility showsensor: " + showSensor + " pump " + showPump + " target " +showTarget)
for (const element of Array.from(sensorOnly)) {
if (showSensor) {
element.classList.remove("plantHidden_" + this.plantId)
} else {
element.classList.add("plantHidden_" + this.plantId)
}
}
for (const element of Array.from(pumpOnly)) {
if (showPump) {
element.classList.remove("plantHidden_" + this.plantId)
} else {
element.classList.add("plantHidden_" + this.plantId)
}
}
for (const element of Array.from(targetOnly)) {
if (showTarget) {
element.classList.remove("plantHidden_" + this.plantId)
} else {
element.classList.add("plantHidden_" + this.plantId)
}
}
}
setTestResult(result: PumpTestResult) {
this.pump_current_result.innerText = "Did abort " + result.error + " median current " + result.median_current_ma + " max current " + result.max_current_ma + " min current " + result.min_current_ma
}
setMeasurementResult(a: string, b: string) {
update(a: string, b: string) {
this.moistureA.innerText = a
this.moistureB.innerText = b
}
setConfig(plantConfig: PlantConfig) {
this.mode.value = plantConfig.mode;
this.targetMoisture.value = plantConfig.target_moisture.toString();
@ -244,22 +173,16 @@ export class PlantView {
this.sensorBInstalled.checked = plantConfig.sensor_b;
this.sensorAInstalled.checked = plantConfig.sensor_a;
this.maxConsecutivePumpCount.value = plantConfig.max_consecutive_pump_count.toString();
this.minPumpCurrentMa.value = plantConfig.min_pump_current_ma.toString();
this.maxPumpCurrentMa.value = plantConfig.max_pump_current_ma.toString();
this.ignoreCurrentError.checked = plantConfig.ignore_current_error;
// Set new fields
this.moistureSensorMinFrequency.value =
plantConfig.moisture_sensor_min_frequency?.toString() || "";
this.moistureSensorMaxFrequency.value =
plantConfig.moisture_sensor_max_frequency?.toString() || "";
this.updateVisibility(plantConfig);
}
getConfig(): PlantConfig {
let conv: PlantConfig = {
return {
mode: this.mode.value,
target_moisture: this.targetMoisture.valueAsNumber,
pump_time_s: this.pumpTimeS.valueAsNumber,
@ -270,12 +193,7 @@ export class PlantView {
sensor_a: this.sensorAInstalled.checked,
max_consecutive_pump_count: this.maxConsecutivePumpCount.valueAsNumber,
moisture_sensor_min_frequency: this.moistureSensorMinFrequency.valueAsNumber || null,
moisture_sensor_max_frequency: this.moistureSensorMaxFrequency.valueAsNumber || null,
min_pump_current_ma: this.minPumpCurrentMa.valueAsNumber,
max_pump_current_ma: this.maxPumpCurrentMa.valueAsNumber,
ignore_current_error: this.ignoreCurrentError.checked,
moisture_sensor_max_frequency: this.moistureSensorMaxFrequency.valueAsNumber || null
};
this.updateVisibility(conv);
return conv;
}
}
}

View File

@ -33,8 +33,8 @@ export class SolarView{
this.solar_current_milli_ampere.innerText = "N/A"
this.solar_is_day.innerText = "N/A"
} else {
this.solar_voltage_milli_volt.innerText = solarState.mppt_voltage.toFixed(0)
this.solar_current_milli_ampere.innerText = solarState.mppt_current.toFixed(0)
this.solar_voltage_milli_volt.innerText = solarState.mppt_voltage.toFixed(2)
this.solar_current_milli_ampere.innerText = String(+solarState.mppt_current)
this.solar_is_day.innerText = solarState.is_day?"🌞":"🌙"
}

View File

@ -1,65 +1,61 @@
import {Controller} from "./main";
import { Controller } from "./main";
import {BackupHeader} from "./api";
export class SubmitView {
json: HTMLDivElement;
submitFormBtn: HTMLButtonElement;
submit_status: HTMLElement;
backupBtn: HTMLButtonElement;
restoreBackupBtn: HTMLButtonElement;
backuptimestamp: HTMLElement;
backupsize: HTMLElement;
backupjson: HTMLElement;
json: HTMLDivElement;
submitFormBtn: HTMLButtonElement;
submit_status: HTMLElement;
backupBtn: HTMLButtonElement;
restoreBackupBtn: HTMLButtonElement;
backuptimestamp: HTMLElement;
backupsize: HTMLElement;
backupjson: HTMLElement;
constructor(controller: Controller) {
(document.getElementById("submitview") as HTMLElement).innerHTML = require("./submitview.html")
constructor(controller: Controller) {
(document.getElementById("submitview") as HTMLElement).innerHTML = require("./submitview.html")
let showJson = document.getElementById('showJson') as HTMLButtonElement
let rawdata = document.getElementById('rawdata') as HTMLElement
this.json = document.getElementById('json') as HTMLDivElement
this.backupjson = document.getElementById('backupjson') as HTMLDivElement
this.submitFormBtn = document.getElementById("submit") as HTMLButtonElement
this.backupBtn = document.getElementById("backup") as HTMLButtonElement
this.restoreBackupBtn = document.getElementById("restorebackup") as HTMLButtonElement
this.backuptimestamp = document.getElementById("backuptimestamp") as HTMLElement
this.backupsize = document.getElementById("backupsize") as HTMLElement
this.submit_status = document.getElementById("submit_status") as HTMLElement
this.submitFormBtn.onclick = () => {
controller.uploadConfig(this.json.textContent as string, (status: string) => {
this.submit_status.innerHTML = status;
});
}
this.backupBtn.onclick = () => {
controller.progressview.addIndeterminate("backup", "Backup to EEPROM running")
controller.backupConfig(this.json.textContent as string).then(saveStatus => {
controller.getBackupInfo().then(r => {
controller.progressview.removeProgress("backup")
this.submit_status.innerHTML = saveStatus;
});
});
}
this.restoreBackupBtn.onclick = () => {
controller.getBackupConfig();
}
showJson.onclick = () => {
if (rawdata.style.display == "none") {
rawdata.style.display = "flex";
} else {
rawdata.style.display = "none";
}
}
let showJson = document.getElementById('showJson') as HTMLButtonElement
let rawdata = document.getElementById('rawdata') as HTMLElement
this.json = document.getElementById('json') as HTMLDivElement
this.backupjson = document.getElementById('backupjson') as HTMLDivElement
this.submitFormBtn = document.getElementById("submit") as HTMLButtonElement
this.backupBtn = document.getElementById("backup") as HTMLButtonElement
this.restoreBackupBtn = document.getElementById("restorebackup") as HTMLButtonElement
this.backuptimestamp = document.getElementById("backuptimestamp") as HTMLElement
this.backupsize = document.getElementById("backupsize") as HTMLElement
this.submit_status = document.getElementById("submit_status") as HTMLElement
this.submitFormBtn.onclick = () => {
controller.uploadConfig(this.json.textContent as string, (status: string) => {
this.submit_status.innerHTML = status;
});
}
setBackupInfo(header: BackupHeader) {
this.backuptimestamp.innerText = header.timestamp
this.backupsize.innerText = header.size.toString()
this.backupBtn.onclick = () => {
controller.backupConfig(this.json.textContent as string, (status: string) => {
this.submit_status.innerHTML = status;
});
}
setJson(pretty: string) {
this.json.textContent = pretty
this.restoreBackupBtn.onclick = () => {
controller.getBackupConfig();
}
setBackupJson(pretty: string) {
this.backupjson.textContent = pretty
showJson.onclick = () => {
if (rawdata.style.display == "none"){
rawdata.style.display = "flex";
} else {
rawdata.style.display = "none";
}
}
}
setBackupInfo(header: BackupHeader) {
this.backuptimestamp.innerText = header.timestamp
this.backupsize.innerText = header.size.toString()
}
setJson(pretty: string) {
this.json.textContent = pretty
}
setBackupJson(pretty: string) {
this.backupjson.textContent = pretty
}
}