252 Commits

Author SHA1 Message Date
e070c802d5 show build version in html 2024-03-02 14:01:48 +01:00
34066ee463 more stack slightly improved debugging 2024-03-02 13:21:12 +01:00
3110f25d80 Merge branch 'feature/12v' of https://git.mannheim.ccc.de/C3MA/PlantCtrl into feature/12v 2024-02-24 16:26:01 +01:00
5be6197f8c after pump fast restart cycle 2024-02-24 16:25:56 +01:00
27bde9cc87 fix tank calculation, update bq34 driver 2024-02-21 22:30:03 +01:00
3e31b4c041 Merge branch 'feature/12v' of ssh://git.mannheim.ccc.de:1337/C3MA/PlantCtrl into feature/12v 2024-02-21 20:00:40 +01:00
c6ef614221 doc erase-parts 2024-02-21 19:57:36 +01:00
bfee21796a fix rollback and windows build 2024-02-21 15:36:26 +01:00
57a0971c4b also print mode to mqtt 2024-02-17 18:43:36 +01:00
556e64740a allow build to run on windows as well 2024-02-17 18:40:11 +01:00
9d1a807805 allow to selectivly enable redundant sensors 2024-02-17 17:25:50 +01:00
680d1c3aaf fix rtc storage 2024-02-15 23:00:05 +01:00
060a1cc32d proceed with bq34z100 extraction 2024-02-02 21:35:04 +01:00
541f7e4471 Also ignore rust folder 2024-01-25 13:31:44 +01:00
7ea1486e2c made most battery code ready to work 2024-01-22 23:13:52 +01:00
b933516062 fix for bq34z100 led mode, added i2c header 2024-01-22 23:13:37 +01:00
b533739aa4 bq34z100 code improvements 2024-01-21 06:11:06 +01:00
12463c557b add i2c initial and pull configs 2024-01-17 21:25:01 +01:00
9473466feb set mppt output voltage to be less agressive 2024-01-10 19:49:57 +01:00
b5b5b25238 main pump handler 2024-01-09 00:16:13 +01:00
b9ec3247af clippy 2024-01-07 14:34:45 +01:00
a30dbe0759 go to stay alive 2024-01-07 14:33:02 +01:00
5724088780 remove boardaccess arc and replace with static lazy 2023-12-27 20:00:06 +01:00
4c02b99ea7 webserver improvements 2023-12-27 17:33:11 +01:00
1e40e2e3ba move to typescript, testmode, shiftregister revival 2023-12-23 01:59:00 +01:00
fd823217aa fix for reset of shift register 2023-12-23 01:56:07 +01:00
2541c83ebe more javascript shit 2023-12-22 17:26:00 +01:00
58801f870e initial config changes 2023-12-22 01:35:08 +01:00
066b3ec24f play with config page 2023-12-19 10:45:56 +01:00
303bdd131f fix reverse protection diodes 2023-12-19 10:45:22 +01:00
5d6871250e wifi config file handling added 2023-12-12 03:46:53 +01:00
e43538ec8a free size handling 2023-12-07 02:50:50 +01:00
fb0ad6b1dc file write test 2023-12-07 02:33:17 +01:00
f0f0e9f27e adjustments 2023-12-04 00:41:29 +01:00
4a8fffb3cc Merge branch 'feature/12v' of github.com:0110/PlantCtrl into feature/12v
# Conflicts:
#	rust/Cargo.toml
2023-11-30 19:26:03 +01:00
dd949a1be1 partition table 2023-11-29 18:28:09 +01:00
a9ab4ee99b fixes 2023-11-29 18:27:55 +01:00
8bfa27612d config start 2023-11-29 18:27:40 +01:00
55369e46a8 adjusted partitions 2023-11-29 18:27:23 +01:00
b2e5ccccee adjusted runner 2023-11-29 18:27:15 +01:00
031e8de99f use vergen 2023-11-27 01:07:07 +01:00
d35b814047 adjust compile stuff 2023-11-27 00:05:09 +01:00
0a0ac6babf added chrono-tz filter 2023-11-23 22:50:17 +01:00
b268466b89 adjustments 2023-11-21 23:45:15 +01:00
3c409ea339 proceeed further 2023-11-21 01:05:12 +01:00
825d389a8c design 2023-11-20 02:24:14 +01:00
17db1b8a45 adjust folder to be shorter 2023-11-20 01:46:19 +01:00
2092354bc9 ignore netlist 2023-11-20 00:14:29 +01:00
ae8ff86c1a rust init 2023-11-20 00:05:47 +01:00
ec67450a0a finalize layout for comparator 2023-11-01 22:44:32 +01:00
f72135a2b3 finalize 2023-10-26 23:08:03 +02:00
ba0fa2e732 added bettry manager 2023-10-26 07:49:56 +02:00
d3d6ad8ffb added batterymanagemt 2023-10-22 20:06:27 +02:00
ca74a716b7 updated layout 2023-10-22 01:20:17 +02:00
ba9fc2432b changes 2023-10-21 21:59:41 +02:00
fd08921bba layouted further 2023-10-16 01:10:03 +02:00
33082b7481 add charging circuit 2023-10-15 13:16:44 +02:00
8cc9b9d492 layouting continued 2023-10-11 00:01:17 +02:00
928dce949d Signed-off-by: Empire <empirephoenix@yahoo.de> 2023-10-10 20:20:48 +02:00
5f20ec17d0 12v layout tests 2023-09-27 00:55:39 +02:00
bdd9a17757 initial bus design 2023-09-26 01:20:07 +02:00
Empire
fbbbc5f5aa fix ota 2023-08-02 21:45:36 +02:00
Empire
5e16ca32e0 increase ota partition size for newer platform core 2023-08-02 21:44:54 +02:00
Empire
78193855a3 gpio 15 warnung 2023-08-02 21:44:17 +02:00
Empire
6d5f68fab5 Merge branch 'master' of github.com:0110/PlantCtrl 2023-07-20 20:11:58 +02:00
Ollo
9167da5a82 Refactoring 2023-06-20 18:06:29 +02:00
Ollo
f65132652c Optional features can be controlled via platformio.ini and are mentioned in firmware name on MQTT 2023-06-20 17:53:19 +02:00
Empire
703553beb4 Merge branch 'master' of github.com:0110/PlantCtrl 2023-06-15 21:44:00 +02:00
Ollo
2636e2defa Read more distances 2023-06-11 13:51:13 +02:00
Ollo
c6ddfea44b Handle unrealistic water values 2023-06-11 13:28:52 +02:00
Empire
85bcbd166c Fix remaining mqtt calculation 2023-06-04 19:21:53 +02:00
Ollo
6cb446735d Adapted default work time, as internal time is UTC 2023-05-28 10:53:43 +02:00
Ollo
ab17709435 More serial debuging (parsing 1WIRE Addresses failed) 2023-05-27 09:12:55 +02:00
Empire
e55b4abd49 added extension board 2023-05-18 11:06:12 +02:00
Empire
cf7ff9b0d1 added ota state debugging 2023-05-17 18:24:27 +02:00
Your Name
1cc46a360b fixed not watering when water is left 2023-04-26 08:33:12 +02:00
Ollo
9235e8a272 Changed to python3 2023-04-06 18:03:13 +02:00
Ollo
2a2a36b267 sleep at least several minutes, so the solar panel can charge the battery 2023-04-06 17:56:17 +02:00
Ollo
9f2e21d94c Sleep 3 times longer, if the battery voltage is low 2023-03-25 14:21:33 +01:00
Empire
a84344f978 Merge branch 'ollo-dev' 2023-03-24 19:55:14 +01:00
Ollo
5df69f20a8 Stop pumps due to water sensor 2023-03-24 18:40:57 +01:00
Ollo
704684d437 Fixed water distance and publish percentage 2023-03-24 18:06:21 +01:00
Ollo
90f2d821a7 Swapped water sensor pins 2023-03-22 22:04:49 +01:00
Empire
e8759435e7 frequency resistive sensor adjustment 2023-03-22 21:43:52 +01:00
Ollo
bd2e4d3da0 distance sensor is reset after powercycle 2023-03-22 21:06:14 +01:00
Ollo
9eb16637dc Reset as distance is > 8 meter 2023-03-22 21:01:24 +01:00
Ollo
66f61a69db Reset sensor on bad values 2023-03-22 20:50:25 +01:00
Ollo
939a5fe21a Print distance 2023-03-22 20:21:10 +01:00
Ollo
3a2ddb92e3 Set onewire bus 2023-03-22 20:06:35 +01:00
Ollo
1e79588e98 Merge branch 'ollo-dev' of github.com:0110/PlantCtrl into ollo-dev 2023-03-22 20:02:33 +01:00
Ollo
a0c657a418 VL53L0X test started 2023-03-22 20:02:23 +01:00
Ollo
ab480041b2 Removed timeout 2023-03-10 23:37:50 +01:00
Ollo
1fad63a84d Start I2C sensor after power was activated 2023-03-10 22:50:16 +01:00
Ollo
712ef73a30 Merged master into ollo-dev 2023-03-10 20:06:05 +01:00
Empire
63ed9afabc stuff changed 2023-03-03 19:58:55 +01:00
Empire
3835bd96a1 Revert "Revert "add resistive frequency sensor, updated dependencies""
This reverts commit 42ce4cdda2.
2023-02-12 12:51:52 +01:00
Empire
42ce4cdda2 Revert "add resistive frequency sensor, updated dependencies"
This reverts commit 17d16a6eef.
2023-02-12 12:40:38 +01:00
Empire
2b6d766492 fix water error code 2023-02-10 22:23:04 +01:00
Empire
68386ce02f Merge branch 'master' of github.com:0110/PlantCtrl 2023-02-10 22:12:57 +01:00
Empire
17d16a6eef add resistive frequency sensor, updated dependencies 2023-02-10 22:08:29 +01:00
Your Name
43e638a587 set more state retained 2022-12-20 00:07:25 +01:00
Your Name
0d15479268 remove leftover 2022-12-20 00:07:25 +01:00
Your Name
2914a0c7a1 slightly adjust range for better precision 2022-12-20 00:07:25 +01:00
37a08dede6 add frozen water mode 2022-12-20 00:04:57 +01:00
Ollo
195d3d3c1b Ignore voltages above 100V 2022-08-25 18:40:41 +02:00
Ollo
e820301490 Tweak state numbers 2022-08-23 20:26:11 +02:00
Ollo
29e3aa08cb Fixed unit description for cooldown 2022-08-22 18:08:19 +02:00
Ollo
791d6f04b0 Tweaked number 2022-08-21 12:12:07 +02:00
Ollo
79ffd8acff Version increased 2022-08-21 12:07:43 +02:00
Ollo
18b927bd59 Publish state as number and string 2022-08-21 12:05:43 +02:00
Ollo
c444117853 code refactoring: use the defined states of header file 2022-08-21 11:59:50 +02:00
Ollo
5d24a51bd9 Plant states defined in header 2022-08-21 11:52:15 +02:00
Ollo
71b9150279 Restore frequency boundaries (7a54065a5f) 2022-06-25 17:43:23 +02:00
Ollo
7eda866a91 Kept branch version of /esp32test/Esp32DeepSleepTest/src/main.cpp 2022-06-25 17:28:01 +02:00
Ollo
f18e4a7586 Show process in output (steps) 2022-06-25 17:20:11 +02:00
662d7bc853 add timer only mode, more sane default config 2022-05-20 01:00:04 +02:00
5dedc76727 formating only 2022-04-29 10:47:54 +02:00
15e96a4990 ensure compilation with bricking limits is not possible 2022-04-29 10:47:46 +02:00
0dd4553999 do not use pump for download indication anymore 2022-04-29 10:47:16 +02:00
7a841d423b use more sane default (pump always not only at night) 2022-04-29 10:45:59 +02:00
4a86d8c41c fixed analog probe being backwards, extended range for frequency one 2022-04-29 10:14:50 +02:00
Empire
c43da98245 Merge branch 'hydro'
# Conflicts:
#	esp32/PlantControl.code-workspace
#	esp32/include/ControllerConfiguration.h
#	esp32/platformio.ini
#	esp32/src/main.cpp
2022-04-27 19:19:26 +00:00
Ollo
f38cb6b564 Testsetup for frequency on GPIO17 2022-04-02 22:10:31 +02:00
Ollo
6c475a2ade Sensor types are defined in platformio.ini 2022-03-06 14:25:14 +01:00
Ollo
9a172c269e Swapped max and min for analog mapping 2022-03-06 14:05:54 +01:00
Ollo
868d3d912e Update the analog maximum according datasheet 2022-03-06 13:47:21 +01:00
Empire
defbcca8ae sht20 support 2022-03-05 09:31:43 +00:00
Empire
a9c4c32a22 Merge branch 'master' of github.com:0110/PlantCtrl 2022-02-19 17:47:00 +00:00
c3ma
1161c62a90 print into mqtt the sensor mode 2022-02-19 02:51:36 +01:00
c3ma
a0f8df7016 fix for capacitive sensors not working, enum introduction for sensormode 2022-02-19 02:24:19 +01:00
c3ma
d0320beaa7 allow sensor type selection per plant, changed calculations to use PCT values, due to different raw value meaning 2022-02-12 05:26:54 +01:00
Ollo
7a54065a5f Reduced frequrency boundery 2022-01-25 22:11:38 +01:00
Your Name
27cd6af02d cleanup power settings 2021-12-05 16:13:02 +01:00
Your Name
a745896643 added ulp-pwm logic for timedlight 2021-12-05 02:36:03 +01:00
Empire
80828eadd7 ulp tests 2021-12-04 16:04:18 +00:00
Empire
b9d8831dea Merge branch 'master' of github.com:0110/PlantCtrl
# Conflicts:
#	esp32/src/main.cpp
2021-12-03 18:42:37 +00:00
Empire
d69feb4143 basic pump ml calculation 2021-12-03 18:36:10 +00:00
Your Name
f131d9f39a hydro mode config and ignore low voltage light condition 2021-12-02 16:50:24 +01:00
Your Name
d7b854da75 allow disable light voltage range with -1 2021-12-02 14:40:06 +01:00
Ollo
16a722816c Use only Homie compliant datatypes: https://homieiot.github.io/specification/spec-core-v3_0_1/# 2021-11-29 17:25:08 +01:00
Ollo
9ff546b7b7 Remove Exception stuff 2021-11-28 16:02:08 +01:00
Empire
61fad8e14c Merge branch 'master' of https://github.com/0110/PlantCtrl 2021-11-19 18:58:26 +00:00
Empire
af618e73f3 pump refactor round 1 2021-11-19 18:48:14 +00:00
Ollo
c5bce25afe Added time into logs 2021-11-14 19:49:40 +01:00
Ollo
a457db4447 Fixed memory leak 2021-11-14 13:51:52 +01:00
c3ma
66d69eab6b Exceptions added 2021-11-13 17:26:39 +01:00
c3ma
f727971138 increased ota flash sizes 2021-10-27 22:04:44 +02:00
Your Name
d00517ec1e added epoxy form and sensor mod 2021-10-27 15:05:37 +02:00
Your Name
2ad351c41b remove selfTest causing sideeffects for pumploop trigger 2021-10-27 02:05:48 +02:00
Your Name
7d80d444cf use only seconds for time units, improve self test debug 2021-10-27 01:42:01 +02:00
Your Name
e70e467e9b properly set last run time, as it apparently was missing 2021-10-27 00:40:46 +02:00
Your Name
6a97215a04 fix for hydroponic not working at night 2021-10-27 00:40:26 +02:00
Your Name
2c8645121e added pump specific power level (requires controller that supports this or dc motor) 2021-10-26 21:50:55 +02:00
Your Name
dba39869d1 use shorter valid firmeware name 2021-10-26 21:12:00 +02:00
Your Name
9170a1fe05 some fixes for hydroponics mode 2021-10-26 20:46:40 +02:00
Empire
49664ba6f7 added initial support for hydroponic systems, changed cooldown to use minutes 2021-10-22 17:39:42 +00:00
Your Name
0bcef25528 Merge branch 'master' of https://github.com/0110/PlantCtrl 2021-10-22 15:53:58 +02:00
Your Name
f30a0a0c78 smooth battery sensor 2021-10-22 15:52:19 +02:00
Ollo
d819f7df4e Update documentation 2021-10-15 21:59:47 +02:00
c3ma
ecc03e9cb4 mqtt extraction 2021-10-06 22:00:17 +02:00
c3ma
2cd0a0f48b initial flowmeter logic and calculation 2021-10-06 21:24:34 +02:00
c3ma
b5569aa8ab Cleanup of last changes 2021-10-06 20:05:09 +02:00
Your Name
244dfb9b27 Merge branch 'master' of https://github.com/0110/PlantCtrl
# Conflicts:
#	esp32/src/main.cpp
2021-10-01 23:51:29 +02:00
Your Name
f4e13454e3 wip new pump system 2021-10-01 23:46:37 +02:00
Ollo
1619b93fa1 Updated ini.example 2021-10-01 23:00:24 +02:00
Ollo
58cb523e1b added determineTimeLightState in TIMED_LIGHT_PIN define 2021-10-01 22:53:40 +02:00
Ollo
e913ff462e Merge branch 'master' of github.com:0110/PlantCtrl 2021-10-01 21:40:41 +02:00
Your Name
f13a25b34d prevent light from being reenabled untill next day after low voltage trigger 2021-09-14 18:54:14 +02:00
Empire
88e4ceea8b fix for wrong validator 2021-08-30 08:02:12 +00:00
Your Name
f99f72b65a added timed light function 2021-08-29 20:45:50 +02:00
Your Name
e927fc8c70 add alarm if pumps did not change state to wet after X consecutive tries 2021-08-23 00:58:37 +02:00
Your Name
61098724b6 fixed wrong percent calculation, emit trigger value to mqtt 2021-08-17 16:55:57 +02:00
c3ma
2e052710e2 missing sensor error 2021-07-21 21:34:14 +02:00
c3ma
d043d873cc Reading sensors by frequency 2021-07-21 21:23:58 +02:00
Ollo
2d167c7393 adapted datatype 2021-07-14 21:43:03 +02:00
Ollo
f6c51f6d05 Updated example, more debug output in script 2021-07-14 21:33:47 +02:00
Ollo
02a882ebde Seperate setting itself into a local file 2021-07-14 21:21:49 +02:00
Ollo
697d470d29 Renamed file 2021-07-14 21:15:04 +02:00
Ollo
c448960415 Check values before publishing water stuff 2021-07-14 21:14:03 +02:00
c3ma
2d91f91290 react on switch from Mqtt 2021-07-09 22:51:50 +02:00
c3ma
df39c09c50 code cleanups 2021-07-09 21:50:51 +02:00
c3ma
99703e5ad1 Adjust moister sensor boundaries 2021-07-09 19:40:51 +02:00
Your Name
12d4c9950d fix cooldown memory without power, improved logging 2021-07-01 23:09:02 +02:00
Ollo
456f78042c Fixed concatination of strings in logging 2021-07-01 22:15:55 +02:00
Ollo
57d18af76d converted serial prints into MQTT status log messages 2021-07-01 22:06:50 +02:00
Your Name
f1d55ed603 extracte time stuff, started logger 2021-07-01 21:19:51 +02:00
Your Name
a1f2388c7f use mAlive instead of mqtt/homie varaibles 2021-07-01 20:50:47 +02:00
Your Name
d2010ddebb improved script reliability 2021-07-01 20:40:51 +02:00
Your Name
7c9a0bf6f1 clear averages for each run, ignore distances higher than tank depth 2021-07-01 20:39:51 +02:00
Your Name
11d29939f6 code stuff 2021-07-01 20:39:06 +02:00
Your Name
c32fd350c1 stuff 2021-06-29 23:50:41 +02:00
Your Name
a1de9ee2b9 config reduced, config json parser memory adjusted 2021-06-29 23:49:48 +02:00
Your Name
5568000d62 initial settings uploader added 2021-06-29 23:49:30 +02:00
Your Name
191e05b941 fixed config backup and restore 2021-06-29 22:09:30 +02:00
Your Name
c22c14a42b stop then script from sleeping the esp in case of an error 2021-06-29 20:14:36 +02:00
Your Name
54398b7bd6 adjust voltage divider to match 0.10 pcb layout 2021-06-29 20:14:19 +02:00
Your Name
0eeeb6dc60 set threshold for accumlator calculation 2021-06-29 20:14:03 +02:00
C3MA Werkstatt
e483b63400 Set 2k2 as pullup for I2C 2021-06-23 22:05:24 +02:00
Your Name
bc4d3ca271 0.10 rev final 2021-06-23 20:20:55 +02:00
Your Name
d08132c420 new extension header, improved labeling of connectors, optimized layout 2021-06-10 23:04:13 +02:00
Your Name
295673b6cb redesigned custom connector 2021-06-10 20:41:08 +02:00
Ollo
c400200e23 Merge branch 'master' of github.com:0110/PlantCtrl 2021-06-09 19:52:23 +02:00
Your Name
b5031124f4 Merge branch 'master' of https://github.com/0110/PlantCtrl
# Conflicts:
#	board/PlantCtrlESP32.kicad_pcb
2021-06-06 22:53:41 +02:00
3391dfb2c9 report raw distance for water sensor 2021-06-06 22:50:56 +02:00
c3ma
a8403a7c98 added distance sensor and adjusted pcb for it 2021-06-06 21:23:21 +02:00
c3ma
5fcf72602d fixed bootloader 2021-06-06 19:25:27 +02:00
c3ma
5980115298 added more correct pull down for download mode 2021-06-06 17:31:07 +02:00
e78f49b9da further reduced only once used parts 2021-06-04 12:52:11 +02:00
84690562b2 reduced odd resistors 2021-06-04 12:37:18 +02:00
7fcee1fb0d add high impendance pulldown to analog in 2021-06-04 12:31:00 +02:00
c3ma
2f21bd133e Signed-off-by: c3ma <c3ma@example.com> 2021-06-02 23:34:16 +02:00
Ollo
bb0eefa8bd Merge branch 'master' of github.com:0110/PlantCtrl 2021-06-02 22:20:26 +02:00
c3ma
89b982e59f Signed-off-by: c3ma <c3ma@example.com> 2021-06-02 22:20:05 +02:00
Ollo
8cbd96adda Merge branch 'master' of github.com:0110/PlantCtrl 2021-06-02 22:14:43 +02:00
c3ma
ac8e2bc4d4 use same pinout for moisture sensor as used on the sensor itself 2021-06-02 22:14:07 +02:00
Your Name
049246312e fixed labeling and spacing for sensors and pumps 2021-06-01 22:56:09 +02:00
Your Name
8c04bcbb7d fixed power on download mode, solar voltage sensor fixed 2021-06-01 22:44:31 +02:00
Your Name
dd58310fe9 make sensor address compare actually work 2021-05-29 22:07:47 +02:00
Your Name
ce1f75eae3 Merge branch 'master' of https://github.com/0110/PlantCtrl 2021-05-29 21:38:11 +02:00
Your Name
8e5a13d291 ensure DS18S20 are properly resolved via address 2021-05-29 21:37:27 +02:00
Your Name
200ff30f62 allow analog sensor some time to settle 2021-05-29 19:11:40 +02:00
c3ma
f275becb98 changed switches to cheaper alternative 2021-05-28 20:22:55 +02:00
Your Name
2c5d61a124 "improved" topic macro, added backup status and topic 2021-05-27 22:53:49 +02:00
Your Name
057e2b37c3 retry on crc error for onewire 2021-05-27 21:43:15 +02:00
Your Name
3d45a3fca3 config backup wip 2021-05-26 22:25:12 +02:00
Your Name
98799bd2d5 added testmode, fixed formating 2021-05-26 21:46:33 +02:00
Your Name
2c70ff1ed3 more capacitors, slight layout optimisation 2021-05-26 21:18:52 +02:00
Ollo
f0127921b1 Handle different start and end times to water plants 2021-05-26 20:19:27 +02:00
Your Name
cad836b070 fixed homie config fail mode 2021-05-24 20:35:41 +02:00
Your Name
72f8d36393 ignore bod while starting upstepper 2021-05-24 20:07:22 +02:00
Your Name
a90b59f69f use mqtt roundtrip to ensure all receiving messages are processed before ds 2021-05-24 19:37:27 +02:00
Your Name
50d07a3c02 fixed deepsleep procedure, homie shutdown, ensure pinhold is same as pin 2021-05-24 15:50:04 +02:00
Your Name
92005c1c33 control works again (fixed with wifi and fallback) 2021-05-24 14:58:35 +02:00
Ollo
5db0e2c82f Merged 2021-05-21 22:09:30 +02:00
Ollo
ad0ae88c3c More GPIO debug output added; checking GPIO15, too 2021-05-19 20:54:11 +02:00
c3ma
c7d98ef108 GPIO12 is evil 2021-05-16 22:24:14 +02:00
C3MA Werkstatt
3fffd41bcd Todos after building revision 2021-05-16 18:56:15 +02:00
C3MA Werkstatt
9af5a13e12 Nchannel is not Pchannel! 2021-05-16 18:18:11 +02:00
C3MA Werkstatt
758ae3ef57 Describe different buttons should be used 2021-05-16 17:49:12 +02:00
C3MA Werkstatt
04d7120adf Fix labels in pcb 2021-05-16 17:43:05 +02:00
Ollo
129b2b1478 Cleanup unused variables and code 2021-04-07 22:42:31 +02:00
Ollo
658bc7a389 Todos aufgerauemt 2021-04-07 22:21:58 +02:00
Ollo
0a1d755154 runtime optimization 2021-04-07 22:18:52 +02:00
Ollo
31229594fe One wire is initialized directly after start 2021-04-07 22:10:26 +02:00
Ollo
b8b8cf84d5 Reduce frequence of ESP32 to 80Mhz 2021-04-07 21:57:00 +02:00
Ollo
902c863873 Main logic introduced as seperate function. This is executed after MQTT is connected or a timeout occured 2021-04-07 21:54:53 +02:00
Ollo
47aba5387b Handle cooldown time for watering; add start and end time when pumping is allowed 2021-04-07 21:26:11 +02:00
Ollo
3932e82593 Address of tempsensors is published to MQTT 2021-04-07 20:27:42 +02:00
Ollo
dc5dc27ba5 Removed index to indentify temperatur sensors 2021-04-07 19:40:31 +02:00
Ollo
284fb4907d Renamed constants 2021-04-07 18:49:59 +02:00
Ollo
1eba578f20 Merge branch 'master' of github.com:0110/PlantCtrl 2020-11-11 22:16:30 +01:00
Ollo
060fa80efd read only once 2020-11-11 21:54:38 +01:00
113 changed files with 277042 additions and 17235 deletions

11
.gitignore vendored
View File

@@ -1 +1,12 @@
*.sch-bak
PlantCtrlESP32-backups/
board/production/PlantCtrlESP32_2023-11-08_00-45-35/PlantCtrlESP32.zip
board/production/PlantCtrlESP32_2023-11-08_00-45-35/netlist.ipc
.vscode
.embuild/
target
Cargo.lock
node_modules/
rust/src/webserver/bundle.js
rust/build/
rust/image.bin

View File

@@ -12,15 +12,15 @@ Open hardware design (powered by KiCAD).
The complete PCB is stored in the ***board*** sub directory.
There the following components are connected:
* ESP32 NodeMCU Module (the one of A-Z Delivery was used)
* Lipo
* ESP32 **16MB flash required**
* Lipo monitoring (DS2438)
* 7 moist sensors
* 7 pump
* DC-DC convert (generating voltage from Lipo for pumps)
* DS18B20 temperature sensors
* water tank ultrasonic sensor (via JSN-SR04T-2.0)
* water level via laser distance sensor (VL53L0X)
* DS2438 battery monitor
* general purpose expansion pin
* general purpose expansion pins
# Software
The firmware for the controller is stored in ***esp32*** sub directory.

46
board/4435.kicad_mod Normal file
View File

@@ -0,0 +1,46 @@
(footprint "4435" (version 20221018) (generator pcbnew)
(layer "F.Cu")
(attr smd)
(fp_text reference "REF**" (at -3.705 0 90) (layer "F.SilkS")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 888d4749-ec8d-48e0-b622-265ef152540e)
)
(fp_text value "4435" (at 0 0) (layer "F.Fab")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 92782e22-8381-4ed1-a11f-ef69c9601af0)
)
(fp_line (start -2.705 -1.9) (end 2.705 -1.9)
(stroke (width 0.12) (type default)) (layer "F.SilkS") (tstamp 3128977c-e0a0-4ca7-8f5b-79eab6c86d44))
(fp_line (start -2.705 1.1) (end -2.705 -1.9)
(stroke (width 0.12) (type default)) (layer "F.SilkS") (tstamp b5339e6e-bef0-40f8-ac0d-5ffe68b262f3))
(fp_line (start -1.905 1.9) (end -2.705 1.1)
(stroke (width 0.12) (type default)) (layer "F.SilkS") (tstamp 267dcc87-49da-46db-86da-4ce81b7d2468))
(fp_line (start 2.705 -1.9) (end 2.705 1.9)
(stroke (width 0.12) (type default)) (layer "F.SilkS") (tstamp 14aaa409-1cdb-4bf6-a811-f0da4dffe73a))
(fp_line (start 2.705 1.9) (end -1.905 1.9)
(stroke (width 0.12) (type default)) (layer "F.SilkS") (tstamp 1789de97-d192-4375-b67e-3ad05848eee2))
(fp_line (start -2.46 -4.85) (end 2.46 -4.85)
(stroke (width 0.05) (type default)) (layer "F.CrtYd") (tstamp f4d6fc79-0700-4ee7-b411-e8f033ff66d3))
(fp_line (start -2.46 4.85) (end -2.46 -4.85)
(stroke (width 0.05) (type default)) (layer "F.CrtYd") (tstamp 3cfe9046-c22e-44b7-8f86-5e90bf2c83b0))
(fp_line (start 2.46 -4.85) (end 2.46 4.85)
(stroke (width 0.05) (type default)) (layer "F.CrtYd") (tstamp 41eb9d06-d36a-4d1d-a098-b2c48b580c65))
(fp_line (start 2.46 4.85) (end -2.46 4.85)
(stroke (width 0.05) (type default)) (layer "F.CrtYd") (tstamp 05ce4f60-8c60-4fe2-86da-df65172ffb03))
(pad "1" smd rect (at -1.905 -3.5) (size 0.6 2.2) (layers "F.Cu" "F.Paste" "F.Mask")
(thermal_bridge_angle 45) (tstamp 239f0d43-7a93-43bf-abef-be78a7aed604))
(pad "1" smd rect (at -0.635 -3.5) (size 0.6 2.2) (layers "F.Cu" "F.Paste" "F.Mask")
(thermal_bridge_angle 45) (tstamp a9c370b9-178b-48cd-94a4-ca368fb8484d))
(pad "1" smd rect (at 0.635 -3.5) (size 0.6 2.2) (layers "F.Cu" "F.Paste" "F.Mask")
(thermal_bridge_angle 45) (tstamp 8d506357-8b26-4d9b-b778-52fd40f96071))
(pad "1" smd rect (at 1.905 -3.5) (size 0.6 2.2) (layers "F.Cu" "F.Paste" "F.Mask")
(thermal_bridge_angle 45) (tstamp 4f4e03c6-0964-4327-a6cb-2e71085bfef0))
(pad "2" smd rect (at 1.905 3.5) (size 0.6 2.2) (layers "F.Cu" "F.Paste" "F.Mask")
(thermal_bridge_angle 45) (tstamp 3f6b4cbd-12fd-4380-92b9-d204178e6014))
(pad "3" smd rect (at -1.905 3.5) (size 0.6 2.2) (layers "F.Cu" "F.Paste" "F.Mask")
(thermal_bridge_angle 45) (tstamp bb893791-93a0-4766-9b0e-f175da5df5f4))
(pad "3" smd rect (at -0.635 3.5) (size 0.6 2.2) (layers "F.Cu" "F.Paste" "F.Mask")
(thermal_bridge_angle 45) (tstamp 8487832e-80f2-4019-9519-9da7cbe24a96))
(pad "3" smd rect (at 0.635 3.5) (size 0.6 2.2) (layers "F.Cu" "F.Paste" "F.Mask")
(thermal_bridge_angle 45) (tstamp 4ecffbef-d0f1-4d07-af9a-0fb6030dccea))
)

82
board/CN3306.kicad_sym Normal file
View File

@@ -0,0 +1,82 @@
(kicad_symbol_lib (version 20220914) (generator kicad_symbol_editor)
(symbol "CN3306" (in_bom yes) (on_board yes)
(property "Reference" "U" (at 5.08 -5.08 0)
(effects (font (size 1.27 1.27)))
)
(property "Value" "" (at 0 0 0)
(effects (font (size 1.27 1.27)))
)
(property "Footprint" "" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "Datasheet" "" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(symbol "CN3306_1_1"
(rectangle (start -2.54 7.62) (end 12.7 -16.51)
(stroke (width 0) (type default))
(fill (type background))
)
(pin input line (at 15.24 -8.89 180) (length 2.54)
(name "FB" (effects (font (size 1.27 1.27))))
(number "1" (effects (font (size 1.27 1.27))))
)
(pin input line (at 5.08 10.16 270) (length 2.54)
(name "COME" (effects (font (size 1.27 1.27))))
(number "10" (effects (font (size 1.27 1.27))))
)
(pin input line (at 15.24 -13.97 180) (length 2.54)
(name "VCC" (effects (font (size 1.27 1.27))))
(number "11" (effects (font (size 1.27 1.27))))
)
(pin input line (at 15.24 -11.43 180) (length 2.54)
(name "VCC" (effects (font (size 1.27 1.27))))
(number "12" (effects (font (size 1.27 1.27))))
)
(pin input line (at 15.24 5.08 180) (length 2.54)
(name "DRV" (effects (font (size 1.27 1.27))))
(number "13" (effects (font (size 1.27 1.27))))
)
(pin input line (at 3.81 -19.05 90) (length 2.54)
(name "GND" (effects (font (size 1.27 1.27))))
(number "14" (effects (font (size 1.27 1.27))))
)
(pin input line (at 6.35 -19.05 90) (length 2.54)
(name "GND" (effects (font (size 1.27 1.27))))
(number "15" (effects (font (size 1.27 1.27))))
)
(pin input line (at 15.24 2.54 180) (length 2.54)
(name "ISW" (effects (font (size 1.27 1.27))))
(number "16" (effects (font (size 1.27 1.27))))
)
(pin input line (at -5.08 -10.16 0) (length 2.54)
(name "COMP" (effects (font (size 1.27 1.27))))
(number "2" (effects (font (size 1.27 1.27))))
)
(pin input line (at -5.08 -1.27 0) (length 2.54)
(name "MPPT" (effects (font (size 1.27 1.27))))
(number "3" (effects (font (size 1.27 1.27))))
)
(pin input line (at -5.08 -7.62 0) (length 2.54)
(name "SHDN" (effects (font (size 1.27 1.27))))
(number "5" (effects (font (size 1.27 1.27))))
)
(pin input line (at -5.08 3.81 0) (length 2.54)
(name "CHRG" (effects (font (size 1.27 1.27))))
(number "6" (effects (font (size 1.27 1.27))))
)
(pin input line (at -5.08 1.27 0) (length 2.54)
(name "DONE" (effects (font (size 1.27 1.27))))
(number "7" (effects (font (size 1.27 1.27))))
)
(pin input line (at 15.24 -3.81 180) (length 2.54)
(name "CSP" (effects (font (size 1.27 1.27))))
(number "8" (effects (font (size 1.27 1.27))))
)
(pin input line (at 15.24 -6.35 180) (length 2.54)
(name "ONE" (effects (font (size 1.27 1.27))))
(number "9" (effects (font (size 1.27 1.27))))
)
)
)
)

123
board/CN3795.kicad_sym Normal file
View File

@@ -0,0 +1,123 @@
(kicad_symbol_lib (version 20211014) (generator kicad_symbol_editor)
(symbol "CN3795" (pin_names (offset 1.016)) (in_bom yes) (on_board yes)
(property "Reference" "U" (id 0) (at 5.08 18.542 0)
(effects (font (size 1.27 1.27)) (justify bottom left))
)
(property "Value" "CN3795" (id 1) (at 5.08 -20.32 0)
(effects (font (size 1.27 1.27)) (justify bottom left))
)
(property "Footprint" "CN3795:SSOP10" (id 2) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "MF" "Consonance" (id 4) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "Description" "\n4A, Multi-Chemistry Battery Charger\nWith Photovoltaic Cell MPPT Function\n" (id 5) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "Package" "Package" (id 6) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "Price" "None" (id 7) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "SnapEDA_Link" "https://www.snapeda.com/parts/CN3795/Consonance/view-part/?ref=snap" (id 8) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "MP" "CN3795" (id 9) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "Availability" "Not in stock" (id 10) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "Check_prices" "https://www.snapeda.com/parts/CN3795/Consonance/view-part/?ref=eda" (id 11) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(symbol "CN3795_0_0"
(rectangle (start -12.7 -17.78) (end 12.7 17.78)
(stroke (width 0.254)) (fill (type background))
)
(pin bidirectional line (at -15.24 7.62 0) (length 2.54)
(name "VCC"
(effects (font (size 1.016 1.016)))
)
(number "9"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at -15.24 -2.54 0) (length 2.54)
(name "~{CHRG}"
(effects (font (size 1.016 1.016)))
)
(number "3"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at -15.24 -7.62 0) (length 2.54)
(name "MPPT"
(effects (font (size 1.016 1.016)))
)
(number "4"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at -2.54 20.32 270.0) (length 2.54)
(name "VG"
(effects (font (size 1.016 1.016)))
)
(number "1"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at 2.54 20.32 270.0) (length 2.54)
(name "DRV"
(effects (font (size 1.016 1.016)))
)
(number "10"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at 15.24 7.62 180.0) (length 2.54)
(name "CSP"
(effects (font (size 1.016 1.016)))
)
(number "8"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at 15.24 2.54 180.0) (length 2.54)
(name "BAT"
(effects (font (size 1.016 1.016)))
)
(number "7"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at 15.24 -2.54 180.0) (length 2.54)
(name "FB"
(effects (font (size 1.016 1.016)))
)
(number "6"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at 15.24 -7.62 180.0) (length 2.54)
(name "COM"
(effects (font (size 1.016 1.016)))
)
(number "5"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at 0.0 -20.32 90.0) (length 2.54)
(name "GND"
(effects (font (size 1.016 1.016)))
)
(number "2"
(effects (font (size 1.016 1.016)))
)
)
)
)
)

View File

@@ -0,0 +1,3 @@
EESchema-DOCLIB Version 2.0
#
#End Doc Library

11
board/PlantCtrlESP32.erc Normal file
View File

@@ -0,0 +1,11 @@
ERC report (Thu 10 Jun 2021 11:02:23 PM CEST, Encoding UTF8 )
***** Sheet /
ErrType(3): Pin connected to other pins, but not driven by any pin
@(476.25 mm, 236.22 mm): Pin 3 (Power input) of component U6 is not driven (Net 13).
ErrType(3): Pin connected to other pins, but not driven by any pin
@(71.12 mm, 154.94 mm): Pin 4 (Power input) of component U3 is not driven (Net 25).
ErrType(3): Pin connected to other pins, but not driven by any pin
@(558.80 mm, 274.32 mm): Pin 5 (Power input) of component U2 is not driven (Net 28).
** ERC messages: 3 Errors 0 Warnings 3

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
{
"board": {
"active_layer": 2,
"active_layer_preset": "",
"auto_track_width": false,
"hidden_netclasses": [],
"hidden_nets": [],
"high_contrast_mode": 0,
"net_color_mode": 1,
"opacity": {
"images": 0.6,
"pads": 1.0,
"tracks": 1.0,
"vias": 1.0,
"zones": 0.6
},
"ratsnest_display_mode": 0,
"selection_filter": {
"dimensions": true,
"footprints": true,
"graphics": true,
"keepouts": true,
"lockedItems": true,
"otherItems": true,
"pads": true,
"text": true,
"tracks": true,
"vias": true,
"zones": true
},
"visible_items": [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
32,
33,
34,
35,
36,
39,
40
],
"visible_layers": "fffffff_ffffffff",
"zone_display_mode": 1
},
"meta": {
"filename": "PlantCtrlESP32.kicad_prl",
"version": 3
},
"project": {
"files": []
}
}

File diff suppressed because it is too large Load Diff

24810
board/PlantCtrlESP32.kicad_sch Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,286 +0,0 @@
update=Fri 27 Nov 2020 08:17:47 PM CET
version=1
last_client=kicad
[general]
version=1
RootSch=
BoardNm=
[cvpcb]
version=1
NetIExt=net
[eeschema]
version=1
LibDir=
[eeschema/libraries]
[schematic_editor]
version=1
PageLayoutDescrFile=
PlotDirectoryName=/tmp/
SubpartIdSeparator=0
SubpartFirstId=65
NetFmtName=Pcbnew
SpiceAjustPassiveValues=0
LabSize=50
ERC_TestSimilarLabels=1
[pcbnew]
version=1
PageLayoutDescrFile=
LastNetListRead=PlantCtrlESP32.net
CopperLayerCount=2
BoardThickness=1.6
AllowMicroVias=0
AllowBlindVias=0
RequireCourtyardDefinitions=0
ProhibitOverlappingCourtyards=1
MinTrackWidth=0.2
MinViaDiameter=0.4
MinViaDrill=0.3
MinMicroViaDiameter=0.2
MinMicroViaDrill=0.09999999999999999
MinHoleToHole=0.25
TrackWidth1=1.2
TrackWidth2=0.2
TrackWidth3=0.5
TrackWidth4=1
ViaDiameter1=0.8
ViaDrill1=0.4
ViaDiameter2=4
ViaDrill2=3
dPairWidth1=0.2
dPairGap1=0.25
dPairViaGap1=0.25
SilkLineWidth=0.12
SilkTextSizeV=1
SilkTextSizeH=1
SilkTextSizeThickness=0.15
SilkTextItalic=0
SilkTextUpright=1
CopperLineWidth=0.2
CopperTextSizeV=1.5
CopperTextSizeH=1.5
CopperTextThickness=0.3
CopperTextItalic=0
CopperTextUpright=1
EdgeCutLineWidth=0.05
CourtyardLineWidth=0.05
OthersLineWidth=0.15
OthersTextSizeV=1
OthersTextSizeH=1
OthersTextSizeThickness=0.15
OthersTextItalic=0
OthersTextUpright=1
SolderMaskClearance=0.051
SolderMaskMinWidth=0.25
SolderPasteClearance=0
SolderPasteRatio=-0
[pcbnew/Layer.F.Cu]
Name=F.Cu
Type=0
Enabled=1
[pcbnew/Layer.In1.Cu]
Name=In1.Cu
Type=0
Enabled=0
[pcbnew/Layer.In2.Cu]
Name=In2.Cu
Type=0
Enabled=0
[pcbnew/Layer.In3.Cu]
Name=In3.Cu
Type=0
Enabled=0
[pcbnew/Layer.In4.Cu]
Name=In4.Cu
Type=0
Enabled=0
[pcbnew/Layer.In5.Cu]
Name=In5.Cu
Type=0
Enabled=0
[pcbnew/Layer.In6.Cu]
Name=In6.Cu
Type=0
Enabled=0
[pcbnew/Layer.In7.Cu]
Name=In7.Cu
Type=0
Enabled=0
[pcbnew/Layer.In8.Cu]
Name=In8.Cu
Type=0
Enabled=0
[pcbnew/Layer.In9.Cu]
Name=In9.Cu
Type=0
Enabled=0
[pcbnew/Layer.In10.Cu]
Name=In10.Cu
Type=0
Enabled=0
[pcbnew/Layer.In11.Cu]
Name=In11.Cu
Type=0
Enabled=0
[pcbnew/Layer.In12.Cu]
Name=In12.Cu
Type=0
Enabled=0
[pcbnew/Layer.In13.Cu]
Name=In13.Cu
Type=0
Enabled=0
[pcbnew/Layer.In14.Cu]
Name=In14.Cu
Type=0
Enabled=0
[pcbnew/Layer.In15.Cu]
Name=In15.Cu
Type=0
Enabled=0
[pcbnew/Layer.In16.Cu]
Name=In16.Cu
Type=0
Enabled=0
[pcbnew/Layer.In17.Cu]
Name=In17.Cu
Type=0
Enabled=0
[pcbnew/Layer.In18.Cu]
Name=In18.Cu
Type=0
Enabled=0
[pcbnew/Layer.In19.Cu]
Name=In19.Cu
Type=0
Enabled=0
[pcbnew/Layer.In20.Cu]
Name=In20.Cu
Type=0
Enabled=0
[pcbnew/Layer.In21.Cu]
Name=In21.Cu
Type=0
Enabled=0
[pcbnew/Layer.In22.Cu]
Name=In22.Cu
Type=0
Enabled=0
[pcbnew/Layer.In23.Cu]
Name=In23.Cu
Type=0
Enabled=0
[pcbnew/Layer.In24.Cu]
Name=In24.Cu
Type=0
Enabled=0
[pcbnew/Layer.In25.Cu]
Name=In25.Cu
Type=0
Enabled=0
[pcbnew/Layer.In26.Cu]
Name=In26.Cu
Type=0
Enabled=0
[pcbnew/Layer.In27.Cu]
Name=In27.Cu
Type=0
Enabled=0
[pcbnew/Layer.In28.Cu]
Name=In28.Cu
Type=0
Enabled=0
[pcbnew/Layer.In29.Cu]
Name=In29.Cu
Type=0
Enabled=0
[pcbnew/Layer.In30.Cu]
Name=In30.Cu
Type=0
Enabled=0
[pcbnew/Layer.B.Cu]
Name=B.Cu
Type=0
Enabled=1
[pcbnew/Layer.B.Adhes]
Enabled=1
[pcbnew/Layer.F.Adhes]
Enabled=1
[pcbnew/Layer.B.Paste]
Enabled=1
[pcbnew/Layer.F.Paste]
Enabled=1
[pcbnew/Layer.B.SilkS]
Enabled=1
[pcbnew/Layer.F.SilkS]
Enabled=1
[pcbnew/Layer.B.Mask]
Enabled=1
[pcbnew/Layer.F.Mask]
Enabled=1
[pcbnew/Layer.Dwgs.User]
Enabled=1
[pcbnew/Layer.Cmts.User]
Enabled=1
[pcbnew/Layer.Eco1.User]
Enabled=1
[pcbnew/Layer.Eco2.User]
Enabled=1
[pcbnew/Layer.Edge.Cuts]
Enabled=1
[pcbnew/Layer.Margin]
Enabled=1
[pcbnew/Layer.B.CrtYd]
Enabled=1
[pcbnew/Layer.F.CrtYd]
Enabled=1
[pcbnew/Layer.B.Fab]
Enabled=1
[pcbnew/Layer.F.Fab]
Enabled=1
[pcbnew/Layer.Rescue]
Enabled=0
[pcbnew/Netclasses]
[pcbnew/Netclasses/Default]
Name=Default
Clearance=0.2
TrackWidth=1.2
ViaDiameter=0.8
ViaDrill=0.4
uViaDiameter=0.3
uViaDrill=0.1
dPairWidth=0.2
dPairGap=0.25
dPairViaGap=0.25
[pcbnew/Netclasses/1]
Name=5V
Clearance=0.2
TrackWidth=1.4
ViaDiameter=0.8
ViaDrill=0.4
uViaDiameter=0.3
uViaDrill=0.1
dPairWidth=0.2
dPairGap=0.25
dPairViaGap=0.25
[pcbnew/Netclasses/2]
Name=Mini
Clearance=0.2
TrackWidth=1
ViaDiameter=0.8
ViaDrill=0.4
uViaDiameter=0.3
uViaDrill=0.1
dPairWidth=0.2
dPairGap=0.25
dPairViaGap=0.25
[pcbnew/Netclasses/3]
Name=Power
Clearance=0.2
TrackWidth=1.7
ViaDiameter=0.8
ViaDrill=0.4
uViaDiameter=0.3
uViaDrill=0.1
dPairWidth=0.2
dPairGap=0.25
dPairViaGap=0.25

View File

@@ -0,0 +1,7 @@
Default False 2.0 3
12V True 2.0 3
3V True 2.0 3
BAT+ True 2.0 3
BAT- True 2.0 3
GND True 2.0 3
False True False

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,63 @@
(footprint "R_Shunt_Vishay_WSK2512_6332Metric_T1.19mm" (version 20221018) (generator pcbnew)
(layer "F.Cu")
(descr "Shunt Resistor SMD 2512 (6332 Metric), 2.6mm thick, Vishay WKS2512, Terminal length (T) 1.19mm, 5 to 200 milli Ohm (http://http://www.vishay.com/docs/30108/wsk.pdf)")
(tags "resistor shunt WSK2512")
(attr smd)
(fp_text reference "REF**" (at 0 -2.65) (layer "F.SilkS")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp e64357ee-67ea-437d-842c-edd4e9b0e817)
)
(fp_text value "R_Shunt_Vishay_WSK2512_6332Metric_T1.19mm" (at 0 2.65) (layer "F.Fab")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 6d43eee8-d677-4710-a5b6-ed0a393d7e86)
)
(fp_text user "${REFERENCE}" (at 0 0) (layer "F.Fab")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 7af73258-6848-4c90-ada6-9916b21e0d54)
)
(fp_line (start -2.5 1.7) (end 1.67 1.7)
(stroke (width 0.12) (type solid)) (layer "F.SilkS") (tstamp 686b0a9f-f303-4b9c-97bc-13b268071b2a))
(fp_line (start -1.67 -1.7) (end 2.53 -1.7)
(stroke (width 0.12) (type solid)) (layer "F.SilkS") (tstamp 8ff3cd0c-2e69-49a7-948e-62e2af1cdeaf))
(fp_line (start -4.38 -1.9) (end 4.38 -1.9)
(stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp 97453d6f-90a6-4d0b-9533-4b9de0e70d67))
(fp_line (start -4.38 1.9) (end -4.38 -1.9)
(stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp 8d751a64-d963-4d3e-b652-c6367d221a4c))
(fp_line (start 4.38 -1.9) (end 4.38 1.9)
(stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp 5fc8cbbe-390d-45f2-b9e2-001b3b36ad53))
(fp_line (start 4.38 1.9) (end -4.38 1.9)
(stroke (width 0.05) (type solid)) (layer "F.CrtYd") (tstamp 85ea9322-734e-4f57-b7f4-e08d8559c109))
(fp_line (start -3.175 -1.59) (end 3.175 -1.59)
(stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp a616017c-8c30-4c24-8988-6ebc65a5a14c))
(fp_line (start -3.175 0.32) (end -3.175 -1.59)
(stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp 007035a4-df14-4627-b9dd-16c0816e9fc4))
(fp_line (start -3.175 0.32) (end -2.795 0.32)
(stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp 7a92ebc4-86fb-494b-8925-1512bf4598dd))
(fp_line (start -3.175 0.94) (end -3.175 1.59)
(stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp 8d1ed9db-8b9e-4c9a-b026-a147d8ba5a89))
(fp_line (start -3.175 0.94) (end -2.795 0.94)
(stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp 3238b0a3-1bbc-4ecd-9eca-d39a4563f46a))
(fp_line (start -2.795 0.94) (end -2.795 0.32)
(stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp 56e7214e-7e65-4896-9919-6dfa6ad242a1))
(fp_line (start 2.79 -0.94) (end 3.17 -0.94)
(stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp bf2fd8b5-dc07-4d36-93f4-c05d760e4ae8))
(fp_line (start 2.79 -0.32) (end 2.79 -0.94)
(stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp 2732ea3b-0a68-4014-9f1f-147b192cb827))
(fp_line (start 2.79 -0.32) (end 3.17 -0.32)
(stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp 7597632e-0fab-4537-9ef5-3d955ee8fe04))
(fp_line (start 3.175 -0.94) (end 3.175 -1.59)
(stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp be78e59b-9296-4843-849b-9aeaddf3ec90))
(fp_line (start 3.175 -0.32) (end 3.175 1.59)
(stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp f0529fdf-6a96-4afa-ae66-3987676f8cc1))
(fp_line (start 3.175 1.59) (end -3.175 1.59)
(stroke (width 0.1) (type solid)) (layer "F.Fab") (tstamp 5494f1f1-9934-4901-ae11-4f45356840a6))
(pad "1" smd roundrect (at -2.985 0.635) (size 2.29 2.03) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.12) (tstamp 7d665233-6376-4fd3-9d23-184556b0630c))
(pad "2" smd roundrect (at -3.43 -1.27) (size 1.4 0.76) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.12) (tstamp a060266a-0c38-4364-b2df-6f7cf4c69f0d))
(pad "3" smd roundrect (at 3.43 -1.27) (size 1.4 0.76) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25) (tstamp a1e31646-49e8-4836-bd67-1b6bed652414))
(pad "4" smd roundrect (at 2.985 0.635) (size 2.29 2.03) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.12) (tstamp 3f532dd2-fd5a-4fe7-9636-aa84f769e3b2))
(model "${KICAD6_3DMODEL_DIR}/Resistor_SMD.3dshapes/R_Shunt_Vishay_WSK2512_6332Metric_T1.19mm.wrl"
(offset (xyz 0 0 0))
(scale (xyz 1 1 1))
(rotate (xyz 0 0 0))
)
)

4562
board/bom/ibom.html Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,7 @@
(fp_lib_table
(lib (name misc_footprints)(type KiCad)(uri ${KIPRJMOD}/kicad-stuff/misc_footprints-master/misc_footprints.pretty)(options "")(descr ""))
(lib (name ESP32)(type KiCad)(uri ${KIPRJMOD}/kicad-stuff/ESP32)(options "")(descr ""))
(version 7)
(lib (name "misc_footprints")(type "KiCad")(uri "${KIPRJMOD}/kicad-stuff/misc_footprints-master/misc_footprints.pretty")(options "")(descr ""))
(lib (name "ESP32")(type "KiCad")(uri "${KIPRJMOD}/kicad-stuff/ESP32")(options "")(descr ""))
(lib (name "kicad-stuff")(type "KiCad")(uri "${KIPRJMOD}/kicad-stuff")(options "")(descr ""))
(lib (name "board")(type "KiCad")(uri "${KIPRJMOD}/")(options "")(descr ""))
)

View File

@@ -0,0 +1,158 @@
(kicad_symbol_lib (version 20211014) (generator kicad_symbol_editor)
(symbol "BQ34Z100PWR-G1" (pin_names (offset 1.016)) (in_bom yes) (on_board yes)
(property "Reference" "U" (id 0) (at -12.7 26.4 0.0)
(effects (font (size 1.27 1.27)) (justify bottom left))
)
(property "Value" "BQ34Z100PWR-G1" (id 1) (at -12.7 -29.4 0.0)
(effects (font (size 1.27 1.27)) (justify bottom left))
)
(property "Footprint" "BQ34Z100PWR-G1:SOP65P640X120-14N" (id 2) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "MF" "Texas Instruments" (id 4) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "Description" "\nMulti-chemistry Impedance Track™ standalone fuel gauge | battery gas gauge\n" (id 5) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "Package" "TSSOP-14 Texas Instruments" (id 6) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "Price" "None" (id 7) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "SnapEDA_Link" "https://www.snapeda.com/parts/BQ34Z100PWR-G1/Texas+Instruments/view-part/?ref=snap" (id 8) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "MP" "BQ34Z100PWR-G1" (id 9) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "Purchase-URL" "https://www.snapeda.com/api/url_track_click_mouser/?unipart_id=327977&manufacturer=Texas Instruments&part_name=BQ34Z100PWR-G1&search_term=None" (id 10) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "Availability" "In Stock" (id 11) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(property "Check_prices" "https://www.snapeda.com/parts/BQ34Z100PWR-G1/Texas+Instruments/view-part/?ref=eda" (id 12) (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify bottom) hide)
)
(symbol "BQ34Z100PWR-G1_0_0"
(rectangle (start -12.7 -25.4) (end 12.7 25.4)
(stroke (width 0.41)) (fill (type background))
)
(pin input line (at -17.78 22.86 0) (length 5.08)
(name "REGIN"
(effects (font (size 1.016 1.016)))
)
(number "6"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at -17.78 17.78 0) (length 5.08)
(name "BAT"
(effects (font (size 1.016 1.016)))
)
(number "4"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at -17.78 12.7 0) (length 5.08)
(name "CE"
(effects (font (size 1.016 1.016)))
)
(number "5"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at -17.78 7.62 0) (length 5.08)
(name "P1"
(effects (font (size 1.016 1.016)))
)
(number "3"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at -17.78 5.08 0) (length 5.08)
(name "P2"
(effects (font (size 1.016 1.016)))
)
(number "1"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at -17.78 2.54 0) (length 5.08)
(name "P3/SDA"
(effects (font (size 1.016 1.016)))
)
(number "14"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at -17.78 -2.54 0) (length 5.08)
(name "REG25"
(effects (font (size 1.016 1.016)))
)
(number "7"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at -17.78 -7.62 0) (length 5.08)
(name "SRN"
(effects (font (size 1.016 1.016)))
)
(number "10"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at -17.78 -12.7 0) (length 5.08)
(name "SRP"
(effects (font (size 1.016 1.016)))
)
(number "9"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at -17.78 -17.78 0) (length 5.08)
(name "VEN"
(effects (font (size 1.016 1.016)))
)
(number "2"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at 17.78 5.08 180.0) (length 5.08)
(name "P4/SCL"
(effects (font (size 1.016 1.016)))
)
(number "13"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at 17.78 2.54 180.0) (length 5.08)
(name "P5/HDQ"
(effects (font (size 1.016 1.016)))
)
(number "12"
(effects (font (size 1.016 1.016)))
)
)
(pin bidirectional line (at 17.78 1.77636e-15 180.0) (length 5.08)
(name "P6/TS"
(effects (font (size 1.016 1.016)))
)
(number "11"
(effects (font (size 1.016 1.016)))
)
)
(pin power_in line (at 17.78 -20.32 180.0) (length 5.08)
(name "VSS"
(effects (font (size 1.016 1.016)))
)
(number "8"
(effects (font (size 1.016 1.016)))
)
)
)
)
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,186 @@
(kicad_symbol_lib (version 20220914) (generator kicad_symbol_editor)
(symbol "CN61CN33" (in_bom yes) (on_board yes)
(property "Reference" "U" (at 2.54 12.7 0)
(effects (font (size 1.27 1.27)))
)
(property "Value" "CN61CN33" (at 5.08 10.16 0)
(effects (font (size 1.27 1.27)))
)
(property "Footprint" "Package_TO_SOT_SMD:SOT-23" (at 7.62 2.54 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "Datasheet" "http://www.ti.com/lit/ds/symlink/lm809.pdf" (at 7.62 2.54 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "ki_keywords" "reset supervisor" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "ki_description" "Microprocessor Reset (active-low) Circuit, SOT-23" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "ki_fp_filters" "SOT?23*" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(symbol "CN61CN33_0_1"
(rectangle (start 5.08 7.62) (end -5.08 -7.62)
(stroke (width 0.254) (type solid))
(fill (type background))
)
)
(symbol "CN61CN33_1_1"
(pin output line (at 0 -10.16 90) (length 2.54)
(name "RESET" (effects (font (size 1.27 1.27))))
(number "1" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at 7.62 0 180) (length 2.54)
(name "GND" (effects (font (size 1.27 1.27))))
(number "2" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at 0 10.16 270) (length 2.54)
(name "VCC" (effects (font (size 1.27 1.27))))
(number "3" (effects (font (size 1.27 1.27))))
)
)
)
(symbol "DS2438" (pin_names (offset 1.016)) (in_bom yes) (on_board yes)
(property "Reference" "U" (at 0 0 0)
(effects (font (size 1.27 1.27)))
)
(property "Value" "DS2438" (at 0 0 0)
(effects (font (size 1.27 1.27)))
)
(property "Footprint" "" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "Datasheet" "" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(symbol "DS2438_0_1"
(rectangle (start -11.43 -2.54) (end 12.7 -30.48)
(stroke (width 0) (type solid))
(fill (type background))
)
)
(symbol "DS2438_1_1"
(pin power_in line (at -13.97 -6.35 0) (length 2.54)
(name "GND" (effects (font (size 1.27 1.27))))
(number "1" (effects (font (size 1.27 1.27))))
)
(pin unspecified line (at -13.97 -12.7 0) (length 2.54)
(name "Vsens+" (effects (font (size 1.27 1.27))))
(number "2" (effects (font (size 1.27 1.27))))
)
(pin unspecified line (at -13.97 -19.05 0) (length 2.54)
(name "Vsense-" (effects (font (size 1.27 1.27))))
(number "3" (effects (font (size 1.27 1.27))))
)
(pin input line (at -13.97 -25.4 0) (length 2.54)
(name "Vad" (effects (font (size 1.27 1.27))))
(number "4" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at 15.24 -25.4 180) (length 2.54)
(name "Vdd" (effects (font (size 1.27 1.27))))
(number "5" (effects (font (size 1.27 1.27))))
)
(pin unspecified line (at 15.24 -19.05 180) (length 2.54)
(name "NC" (effects (font (size 1.27 1.27))))
(number "6" (effects (font (size 1.27 1.27))))
)
(pin unspecified line (at 15.24 -12.7 180) (length 2.54)
(name "NC" (effects (font (size 1.27 1.27))))
(number "7" (effects (font (size 1.27 1.27))))
)
(pin bidirectional line (at 15.24 -6.35 180) (length 2.54)
(name "DQ" (effects (font (size 1.27 1.27))))
(number "8" (effects (font (size 1.27 1.27))))
)
)
)
(symbol "DW01" (pin_names (offset 1.016)) (in_bom yes) (on_board yes)
(property "Reference" "IC" (at 21.59 7.62 0)
(effects (font (size 1.27 1.27)) (justify left bottom))
)
(property "Value" "DW01" (at 21.59 5.08 0)
(effects (font (size 1.27 1.27)) (justify left bottom))
)
(property "Footprint" "SOT95P280X135-6N" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "Datasheet" "" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "HEIGHT" "1.35mm" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "DESCRIPTION" "ic" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "MANUFACTURER_PART_NUMBER" "DW01" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "MANUFACTURER_NAME" "Slkor" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "ki_locked" "" (at 0 0 0)
(effects (font (size 1.27 1.27)))
)
(symbol "DW01_0_0"
(polyline
(pts
(xy 5.08 2.54)
(xy 5.08 -7.62)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 5.08 2.54)
(xy 20.32 2.54)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 20.32 -7.62)
(xy 5.08 -7.62)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 20.32 -7.62)
(xy 20.32 2.54)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(pin bidirectional line (at 0 0 0) (length 5.08)
(name "OD" (effects (font (size 1.016 1.016))))
(number "1" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 0 -2.54 0) (length 5.08)
(name "CSI" (effects (font (size 1.016 1.016))))
(number "2" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 0 -5.08 0) (length 5.08)
(name "OC" (effects (font (size 1.016 1.016))))
(number "3" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 25.4 -5.08 180) (length 5.08)
(name "TD" (effects (font (size 1.016 1.016))))
(number "4" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 25.4 -2.54 180) (length 5.08)
(name "VDD" (effects (font (size 1.016 1.016))))
(number "5" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 25.4 0 180) (length 5.08)
(name "VSS" (effects (font (size 1.016 1.016))))
(number "6" (effects (font (size 1.016 1.016))))
)
)
)
)

View File

@@ -0,0 +1,454 @@
(kicad_symbol_lib (version 20220914) (generator kicad_symbol_editor)
(symbol "ESP32-DEVKITC-32D" (pin_names (offset 1.016)) (in_bom yes) (on_board yes)
(property "Reference" "U" (at -15.2654 26.0604 0)
(effects (font (size 1.27 1.27)) (justify left bottom))
)
(property "Value" "ESP32-DEVKITC-32D" (at -15.2654 -27.9654 0)
(effects (font (size 1.27 1.27)) (justify left bottom))
)
(property "Footprint" "MODULE_ESP32-DEVKITC-32D" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "Datasheet" "4" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "Field4" "Espressif Systems" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "ki_locked" "" (at 0 0 0)
(effects (font (size 1.27 1.27)))
)
(symbol "ESP32-DEVKITC-32D_0_0"
(polyline
(pts
(xy -15.24 -25.4)
(xy -15.24 25.4)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -15.24 25.4)
(xy 15.24 25.4)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 15.24 -25.4)
(xy -15.24 -25.4)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 15.24 25.4)
(xy 15.24 -25.4)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(pin power_in line (at -20.32 22.86 0) (length 5.08)
(name "3V3" (effects (font (size 1.016 1.016))))
(number "1" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -20.32 0 0) (length 5.08)
(name "IO26" (effects (font (size 1.016 1.016))))
(number "10" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -20.32 -2.54 0) (length 5.08)
(name "IO27" (effects (font (size 1.016 1.016))))
(number "11" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -20.32 -5.08 0) (length 5.08)
(name "IO14" (effects (font (size 1.016 1.016))))
(number "12" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -20.32 -7.62 0) (length 5.08)
(name "IO12" (effects (font (size 1.016 1.016))))
(number "13" (effects (font (size 1.016 1.016))))
)
(pin power_in line (at -20.32 -10.16 0) (length 5.08)
(name "GND1" (effects (font (size 1.016 1.016))))
(number "14" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -20.32 -12.7 0) (length 5.08)
(name "IO13" (effects (font (size 1.016 1.016))))
(number "15" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -20.32 -15.24 0) (length 5.08)
(name "SD2" (effects (font (size 1.016 1.016))))
(number "16" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -20.32 -17.78 0) (length 5.08)
(name "SD3" (effects (font (size 1.016 1.016))))
(number "17" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -20.32 -20.32 0) (length 5.08)
(name "CMD" (effects (font (size 1.016 1.016))))
(number "18" (effects (font (size 1.016 1.016))))
)
(pin power_in line (at -20.32 -22.86 0) (length 5.08)
(name "EXT_5V" (effects (font (size 1.016 1.016))))
(number "19" (effects (font (size 1.016 1.016))))
)
(pin input line (at -20.32 20.32 0) (length 5.08)
(name "EN" (effects (font (size 1.016 1.016))))
(number "2" (effects (font (size 1.016 1.016))))
)
(pin power_in line (at 20.32 22.86 180) (length 5.08)
(name "GND3" (effects (font (size 1.016 1.016))))
(number "20" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 20.32 20.32 180) (length 5.08)
(name "IO23" (effects (font (size 1.016 1.016))))
(number "21" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 20.32 17.78 180) (length 5.08)
(name "IO22" (effects (font (size 1.016 1.016))))
(number "22" (effects (font (size 1.016 1.016))))
)
(pin output line (at 20.32 15.24 180) (length 5.08)
(name "TXD0" (effects (font (size 1.016 1.016))))
(number "23" (effects (font (size 1.016 1.016))))
)
(pin input line (at 20.32 12.7 180) (length 5.08)
(name "RXD0" (effects (font (size 1.016 1.016))))
(number "24" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 20.32 10.16 180) (length 5.08)
(name "IO21" (effects (font (size 1.016 1.016))))
(number "25" (effects (font (size 1.016 1.016))))
)
(pin power_in line (at 20.32 7.62 180) (length 5.08)
(name "GND2" (effects (font (size 1.016 1.016))))
(number "26" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 20.32 5.08 180) (length 5.08)
(name "IO19" (effects (font (size 1.016 1.016))))
(number "27" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 20.32 2.54 180) (length 5.08)
(name "IO18" (effects (font (size 1.016 1.016))))
(number "28" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 20.32 0 180) (length 5.08)
(name "IO5" (effects (font (size 1.016 1.016))))
(number "29" (effects (font (size 1.016 1.016))))
)
(pin input line (at -20.32 17.78 0) (length 5.08)
(name "SENSOR_VP" (effects (font (size 1.016 1.016))))
(number "3" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 20.32 -2.54 180) (length 5.08)
(name "IO17" (effects (font (size 1.016 1.016))))
(number "30" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 20.32 -5.08 180) (length 5.08)
(name "IO16" (effects (font (size 1.016 1.016))))
(number "31" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 20.32 -7.62 180) (length 5.08)
(name "IO4" (effects (font (size 1.016 1.016))))
(number "32" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 20.32 -10.16 180) (length 5.08)
(name "IO0" (effects (font (size 1.016 1.016))))
(number "33" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 20.32 -12.7 180) (length 5.08)
(name "IO2" (effects (font (size 1.016 1.016))))
(number "34" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 20.32 -15.24 180) (length 5.08)
(name "IO15" (effects (font (size 1.016 1.016))))
(number "35" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 20.32 -17.78 180) (length 5.08)
(name "SD1" (effects (font (size 1.016 1.016))))
(number "36" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 20.32 -20.32 180) (length 5.08)
(name "SD0" (effects (font (size 1.016 1.016))))
(number "37" (effects (font (size 1.016 1.016))))
)
(pin input clock (at 20.32 -22.86 180) (length 5.08)
(name "CLK" (effects (font (size 1.016 1.016))))
(number "38" (effects (font (size 1.016 1.016))))
)
(pin input line (at -20.32 15.24 0) (length 5.08)
(name "SENSOR_VN" (effects (font (size 1.016 1.016))))
(number "4" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -20.32 12.7 0) (length 5.08)
(name "IO34" (effects (font (size 1.016 1.016))))
(number "5" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -20.32 10.16 0) (length 5.08)
(name "IO35" (effects (font (size 1.016 1.016))))
(number "6" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -20.32 7.62 0) (length 5.08)
(name "IO32" (effects (font (size 1.016 1.016))))
(number "7" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -20.32 5.08 0) (length 5.08)
(name "IO33" (effects (font (size 1.016 1.016))))
(number "8" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -20.32 2.54 0) (length 5.08)
(name "IO25" (effects (font (size 1.016 1.016))))
(number "9" (effects (font (size 1.016 1.016))))
)
)
)
(symbol "SL2300" (pin_names (offset 1.016)) (in_bom yes) (on_board yes)
(property "Reference" "Q" (at 0 0 0)
(effects (font (size 1.27 1.27)))
)
(property "Value" "SL2300" (at 7.62 0 0)
(effects (font (size 1.27 1.27)))
)
(property "Footprint" "" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "Datasheet" "" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(symbol "SL2300_1_1"
(polyline
(pts
(xy -1.016 0)
(xy -3.81 0)
)
(stroke (width 0) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -1.016 1.905)
(xy -1.016 -1.905)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -0.508 -1.27)
(xy -0.508 -2.286)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -0.508 0.508)
(xy -0.508 -0.508)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -0.508 2.286)
(xy -0.508 1.27)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 1.27 2.54)
(xy 1.27 1.778)
)
(stroke (width 0) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 1.27 -2.54)
(xy 1.27 0)
(xy -0.508 0)
)
(stroke (width 0) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -0.508 -1.778)
(xy 2.032 -1.778)
(xy 2.032 1.778)
(xy -0.508 1.778)
)
(stroke (width 0) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -0.254 0)
(xy 0.762 0.381)
(xy 0.762 -0.381)
(xy -0.254 0)
)
(stroke (width 0) (type solid))
(fill (type outline))
)
(polyline
(pts
(xy 1.524 0.508)
(xy 1.651 0.381)
(xy 2.413 0.381)
(xy 2.54 0.254)
)
(stroke (width 0) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 2.032 0.381)
(xy 1.651 -0.254)
(xy 2.413 -0.254)
(xy 2.032 0.381)
)
(stroke (width 0) (type solid))
(fill (type none))
)
(circle (center 0.381 0) (radius 2.794)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(circle (center 1.27 -1.778) (radius 0.254)
(stroke (width 0) (type solid))
(fill (type outline))
)
(circle (center 1.27 1.778) (radius 0.254)
(stroke (width 0) (type solid))
(fill (type outline))
)
(pin input line (at -6.35 0 0) (length 2.54)
(name "G" (effects (font (size 1.27 1.27))))
(number "1" (effects (font (size 1.27 1.27))))
)
(pin passive line (at 1.27 -5.08 90) (length 2.54)
(name "S" (effects (font (size 1.27 1.27))))
(number "2" (effects (font (size 1.27 1.27))))
)
(pin passive line (at 1.27 5.08 270) (length 2.54)
(name "D" (effects (font (size 1.27 1.27))))
(number "3" (effects (font (size 1.27 1.27))))
)
)
)
(symbol "SR04M-2" (pin_names (offset 1.016)) (in_bom yes) (on_board yes)
(property "Reference" "H" (at 6.35 27.94 0)
(effects (font (size 1.27 1.27)))
)
(property "Value" "SR04M-2" (at 7.62 -21.59 0)
(effects (font (size 1.27 1.27)))
)
(property "Footprint" "" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "Datasheet" "" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(symbol "SR04M-2_0_1"
(rectangle (start -22.86 26.67) (end 41.91 -20.32)
(stroke (width 0.254) (type solid))
(fill (type none))
)
)
)
(symbol "SR04M-2-HeaderConn_01x04_Female" (pin_names (offset 1.016) hide) (in_bom yes) (on_board yes)
(property "Reference" "J" (at 0 5.08 0)
(effects (font (size 1.27 1.27)))
)
(property "Value" "SR04M-2-HeaderConn_01x04_Female" (at 0 -7.62 0)
(effects (font (size 1.27 1.27)))
)
(property "Footprint" "ESP32:SR04M-2PinHeader_1x04_P2.54mm_Vertical" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "Datasheet" "~" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "ki_keywords" "connector" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "ki_description" "Generic connector, single row, 01x04, script generated (kicad-library-utils/schlib/autogen/connector/)" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(property "ki_fp_filters" "Connector*:*_1x??_*" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(symbol "SR04M-2-HeaderConn_01x04_Female_1_1"
(arc (start 0 -4.572) (mid -0.5058 -5.08) (end 0 -5.588)
(stroke (width 0.1524) (type solid))
(fill (type none))
)
(arc (start 0 -2.032) (mid -0.5058 -2.54) (end 0 -3.048)
(stroke (width 0.1524) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -1.27 -5.08)
(xy -0.508 -5.08)
)
(stroke (width 0.1524) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -1.27 -2.54)
(xy -0.508 -2.54)
)
(stroke (width 0.1524) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -1.27 0)
(xy -0.508 0)
)
(stroke (width 0.1524) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -1.27 2.54)
(xy -0.508 2.54)
)
(stroke (width 0.1524) (type solid))
(fill (type none))
)
(arc (start 0 0.508) (mid -0.5058 0) (end 0 -0.508)
(stroke (width 0.1524) (type solid))
(fill (type none))
)
(arc (start 0 3.048) (mid -0.5058 2.54) (end 0 2.032)
(stroke (width 0.1524) (type solid))
(fill (type none))
)
(pin power_in line (at -5.08 2.54 0) (length 3.81)
(name "VCC" (effects (font (size 1.27 1.27))))
(number "1" (effects (font (size 1.27 1.27))))
)
(pin input line (at -5.08 0 0) (length 3.81)
(name "RX" (effects (font (size 1.27 1.27))))
(number "2" (effects (font (size 1.27 1.27))))
)
(pin output line (at -5.08 -2.54 0) (length 3.81)
(name "TX" (effects (font (size 1.27 1.27))))
(number "3" (effects (font (size 1.27 1.27))))
)
(pin power_in line (at -5.08 -5.08 0) (length 3.81)
(name "GND" (effects (font (size 1.27 1.27))))
(number "4" (effects (font (size 1.27 1.27))))
)
)
)
)

View File

@@ -0,0 +1,74 @@
(kicad_symbol_lib (version 20220914) (generator kicad_symbol_editor)
(symbol "LP38690DT-3.3" (pin_names (offset 1.016)) (in_bom yes) (on_board yes)
(property "Reference" "U" (at -10.16 5.08 0)
(effects (font (size 1.27 1.27)) (justify left bottom))
)
(property "Value" "LP38690DT-3.3" (at -10.16 -7.62 0)
(effects (font (size 1.27 1.27)) (justify left bottom))
)
(property "Footprint" "DPAK457P991X255-3N" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "Datasheet" "IPC-7351B" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "Field4" "Texas Instruments" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "Field5" "M" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "Field6" "2.55mm" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "ki_locked" "" (at 0 0 0)
(effects (font (size 1.27 1.27)))
)
(symbol "LP38690DT-3.3_0_0"
(polyline
(pts
(xy -10.16 -5.08)
(xy 10.16 -5.08)
)
(stroke (width 0.1524) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -10.16 5.08)
(xy -10.16 -5.08)
)
(stroke (width 0.1524) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 10.16 -5.08)
(xy 10.16 5.08)
)
(stroke (width 0.1524) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 10.16 5.08)
(xy -10.16 5.08)
)
(stroke (width 0.1524) (type solid))
(fill (type none))
)
(pin output line (at 15.24 2.54 180) (length 5.08)
(name "OUT" (effects (font (size 1.016 1.016))))
(number "1" (effects (font (size 1.016 1.016))))
)
(pin input line (at -15.24 2.54 0) (length 5.08)
(name "IN" (effects (font (size 1.016 1.016))))
(number "3" (effects (font (size 1.016 1.016))))
)
(pin power_in line (at 15.24 -2.54 180) (length 5.08)
(name "GND" (effects (font (size 1.016 1.016))))
(number "4" (effects (font (size 1.016 1.016))))
)
)
)
)

View File

@@ -0,0 +1,37 @@
(footprint SOP65P640X120-14N (layer F.Cu) (tedit 65346025)
(descr "")
(attr smd)
(fp_text reference REF** (at -0.595 -3.435 0) (layer F.SilkS)
(effects (font (size 1.0 1.0) (thickness 0.15)))
)
(fp_text value SOP65P640X120-14N (at 7.025 3.435 0) (layer F.Fab)
(effects (font (size 1.0 1.0) (thickness 0.15)))
)
(pad 1 smd roundrect (roundrect_rratio 0.05) (at -2.87 -1.95) (size 1.57 0.41) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 2 smd roundrect (roundrect_rratio 0.05) (at -2.87 -1.3) (size 1.57 0.41) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 3 smd roundrect (roundrect_rratio 0.05) (at -2.87 -0.65) (size 1.57 0.41) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 4 smd roundrect (roundrect_rratio 0.05) (at -2.87 0.0) (size 1.57 0.41) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 5 smd roundrect (roundrect_rratio 0.05) (at -2.87 0.65) (size 1.57 0.41) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 6 smd roundrect (roundrect_rratio 0.05) (at -2.87 1.3) (size 1.57 0.41) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 7 smd roundrect (roundrect_rratio 0.05) (at -2.87 1.95) (size 1.57 0.41) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 8 smd roundrect (roundrect_rratio 0.05) (at 2.87 1.95) (size 1.57 0.41) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 9 smd roundrect (roundrect_rratio 0.05) (at 2.87 1.3) (size 1.57 0.41) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 10 smd roundrect (roundrect_rratio 0.05) (at 2.87 0.65) (size 1.57 0.41) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 11 smd roundrect (roundrect_rratio 0.05) (at 2.87 0.0) (size 1.57 0.41) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 12 smd roundrect (roundrect_rratio 0.05) (at 2.87 -0.65) (size 1.57 0.41) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 13 smd roundrect (roundrect_rratio 0.05) (at 2.87 -1.3) (size 1.57 0.41) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 14 smd roundrect (roundrect_rratio 0.05) (at 2.87 -1.95) (size 1.57 0.41) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(fp_circle (center -4.24 -2.26) (end -4.14 -2.26) (layer F.SilkS) (width 0.2))
(fp_circle (center -4.24 -2.26) (end -4.14 -2.26) (layer F.Fab) (width 0.2))
(fp_line (start -2.2 -2.5) (end 2.2 -2.5) (layer F.Fab) (width 0.127))
(fp_line (start -2.2 2.5) (end 2.2 2.5) (layer F.Fab) (width 0.127))
(fp_line (start -2.2 -2.5) (end 2.2 -2.5) (layer F.SilkS) (width 0.127))
(fp_line (start -2.2 2.5) (end 2.2 2.5) (layer F.SilkS) (width 0.127))
(fp_line (start -2.2 -2.5) (end -2.2 2.5) (layer F.Fab) (width 0.127))
(fp_line (start 2.2 -2.5) (end 2.2 2.5) (layer F.Fab) (width 0.127))
(fp_line (start -3.905 -2.75) (end 3.905 -2.75) (layer F.CrtYd) (width 0.05))
(fp_line (start -3.905 2.75) (end 3.905 2.75) (layer F.CrtYd) (width 0.05))
(fp_line (start -3.905 -2.75) (end -3.905 2.75) (layer F.CrtYd) (width 0.05))
(fp_line (start 3.905 -2.75) (end 3.905 2.75) (layer F.CrtYd) (width 0.05))
)

View File

@@ -0,0 +1,29 @@
(footprint SSOP10 (layer F.Cu) (tedit 652971AF)
(descr "")
(attr smd)
(fp_text reference REF** (at -3.2004 0.0 900) (layer F.SilkS)
(effects (font (size 0.64 0.64) (thickness 0.15)))
)
(fp_text value SSOP10 (at 3.1496 -0.4064 900) (layer F.Fab)
(effects (font (size 0.64 0.64) (thickness 0.15)))
)
(pad 2 smd rect (at -1.0 2.8) (size 0.6 1.75) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 9 smd rect (at -1.0 -2.8) (size 0.6 1.75) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 1 smd rect (at -2.0 2.8) (size 0.6 1.75) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 3 smd rect (at 0.0 2.8) (size 0.6 1.75) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 4 smd rect (at 1.0 2.8) (size 0.6 1.75) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 10 smd rect (at -2.0 -2.8) (size 0.6 1.75) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 8 smd rect (at 0.0 -2.8) (size 0.6 1.75) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 7 smd rect (at 1.0 -2.8) (size 0.6 1.75) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 5 smd rect (at 2.0 2.8) (size 0.6 1.75) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(pad 6 smd rect (at 2.0 -2.8) (size 0.6 1.75) (layers F.Cu F.Mask F.Paste) (solder_mask_margin 0.102))
(fp_line (start 2.527 -1.9) (end 2.527 1.4) (layer F.Fab) (width 0.2032))
(fp_line (start 2.527 1.4) (end 2.527 1.9) (layer F.Fab) (width 0.2032))
(fp_line (start 2.527 1.9) (end -2.527 1.9) (layer F.Fab) (width 0.2032))
(fp_line (start -2.527 1.9) (end -2.527 1.4) (layer F.Fab) (width 0.2032))
(fp_line (start -2.527 1.4) (end -2.527 -1.9) (layer F.Fab) (width 0.2032))
(fp_line (start -2.527 -1.9) (end 2.527 -1.9) (layer F.Fab) (width 0.2032))
(fp_line (start 2.527 1.4) (end -2.527 1.4) (layer F.Fab) (width 0.2032))
(fp_line (start -1.905 1.016) (end -1.905 -1.016) (layer F.SilkS) (width 0.3048))
)

View File

@@ -0,0 +1,104 @@
(kicad_symbol_lib (version 20220914) (generator kicad_symbol_editor)
(symbol "SX1308" (pin_names (offset 1.016)) (in_bom yes) (on_board yes)
(property "Reference" "U" (at -17.78 20.32 0)
(effects (font (size 1.27 1.27)) (justify left bottom))
)
(property "Value" "SX1308" (at -17.8054 17.8054 0)
(effects (font (size 1.27 1.27)) (justify left bottom))
)
(property "Footprint" "SOT-23-6" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "Datasheet" "" (at 0 0 0)
(effects (font (size 1.27 1.27)) (justify left bottom) hide)
)
(property "ki_locked" "" (at 0 0 0)
(effects (font (size 1.27 1.27)))
)
(symbol "SX1308_0_0"
(polyline
(pts
(xy -17.78 -7.62)
(xy -17.78 15.24)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -17.78 15.24)
(xy 0 15.24)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -13.7922 0.1778)
(xy -11.2522 0.1778)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -11.303 1.6764)
(xy -8.763 1.6764)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy -11.2522 0.1778)
(xy -11.303 1.6764)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 0 -7.62)
(xy -17.78 -7.62)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 0 15.24)
(xy 0 -7.62)
)
(stroke (width 0.254) (type solid))
(fill (type none))
)
(text "ON/OFF" (at -10.8712 -0.0508 0)
(effects (font (size 0.8128 0.8128)) (justify left bottom))
)
(pin bidirectional line (at 5.08 12.7 180) (length 5.08)
(name "SW" (effects (font (size 1.016 1.016))))
(number "1" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -22.86 -5.08 0) (length 5.08)
(name "GND" (effects (font (size 1.016 1.016))))
(number "2" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 5.08 -2.54 180) (length 5.08)
(name "FB" (effects (font (size 1.016 1.016))))
(number "3" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -22.86 2.54 0) (length 5.08)
(name "EN" (effects (font (size 1.016 1.016))))
(number "4" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at -22.86 12.7 0) (length 5.08)
(name "IN" (effects (font (size 1.016 1.016))))
(number "5" (effects (font (size 1.016 1.016))))
)
(pin bidirectional line (at 5.08 5.08 180) (length 5.08)
(name "N/C" (effects (font (size 1.016 1.016))))
(number "6" (effects (font (size 1.016 1.016))))
)
)
)
)

View File

@@ -0,0 +1,168 @@
(kicad_symbol_lib (version 20220914) (generator kicad_symbol_editor)
(symbol "ds2438az+" (pin_names (offset 0.254)) (in_bom yes) (on_board yes)
(property "Reference" "U" (at 30.48 10.16 0)
(effects (font (size 1.524 1.524)))
)
(property "Value" "ds2438az+" (at 30.48 7.62 0)
(effects (font (size 1.524 1.524)))
)
(property "Footprint" "21-0041B_8_MXM" (at 30.48 6.096 0)
(effects (font (size 1.524 1.524)) hide)
)
(property "Datasheet" "" (at 0 0 0)
(effects (font (size 1.524 1.524)))
)
(property "ki_locked" "" (at 0 0 0)
(effects (font (size 1.27 1.27)))
)
(property "ki_fp_filters" "21-0041B_8_MXM 21-0041B_8_MXM-M 21-0041B_8_MXM-L" (at 0 0 0)
(effects (font (size 1.27 1.27)) hide)
)
(symbol "ds2438az+_1_1"
(polyline
(pts
(xy 7.112 -7.62)
(xy 6.0452 -8.128)
)
(stroke (width 0.127) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 7.112 -7.62)
(xy 6.0452 -7.112)
)
(stroke (width 0.127) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 7.112 -5.08)
(xy 6.0452 -5.588)
)
(stroke (width 0.127) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 7.112 -5.08)
(xy 6.0452 -4.572)
)
(stroke (width 0.127) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 7.112 -2.54)
(xy 6.0452 -3.048)
)
(stroke (width 0.127) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 7.112 -2.54)
(xy 6.0452 -2.032)
)
(stroke (width 0.127) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 7.62 -12.7)
(xy 53.34 -12.7)
)
(stroke (width 0.127) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 7.62 5.08)
(xy 7.62 -12.7)
)
(stroke (width 0.127) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 53.34 -12.7)
(xy 53.34 5.08)
)
(stroke (width 0.127) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 53.34 5.08)
(xy 7.62 5.08)
)
(stroke (width 0.127) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 53.848 0)
(xy 54.9148 -0.508)
)
(stroke (width 0.127) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 53.848 0)
(xy 54.9148 0.508)
)
(stroke (width 0.127) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 55.4228 -0.508)
(xy 56.4642 0)
)
(stroke (width 0.127) (type solid))
(fill (type none))
)
(polyline
(pts
(xy 55.4228 0.508)
(xy 56.4642 0)
)
(stroke (width 0.127) (type solid))
(fill (type none))
)
(pin power_in line (at 0 0 0) (length 7.62)
(name "GND" (effects (font (size 1.4986 1.4986))))
(number "1" (effects (font (size 1.4986 1.4986))))
)
(pin input line (at 0 -2.54 0) (length 7.62)
(name "VSENS+" (effects (font (size 1.4986 1.4986))))
(number "2" (effects (font (size 1.4986 1.4986))))
)
(pin input line (at 0 -5.08 0) (length 7.62)
(name "VSENS-" (effects (font (size 1.4986 1.4986))))
(number "3" (effects (font (size 1.4986 1.4986))))
)
(pin input line (at 0 -7.62 0) (length 7.62)
(name "VAD" (effects (font (size 1.4986 1.4986))))
(number "4" (effects (font (size 1.4986 1.4986))))
)
(pin power_in line (at 60.96 -7.62 180) (length 7.62)
(name "VDD" (effects (font (size 1.4986 1.4986))))
(number "5" (effects (font (size 1.4986 1.4986))))
)
(pin no_connect line (at 60.96 -5.08 180) (length 7.62)
(name "NC" (effects (font (size 1.4986 1.4986))))
(number "6" (effects (font (size 1.4986 1.4986))))
)
(pin no_connect line (at 60.96 -2.54 180) (length 7.62)
(name "NC" (effects (font (size 1.4986 1.4986))))
(number "7" (effects (font (size 1.4986 1.4986))))
)
(pin bidirectional line (at 60.96 0 180) (length 7.62)
(name "DQ" (effects (font (size 1.4986 1.4986))))
(number "8" (effects (font (size 1.4986 1.4986))))
)
)
)
)

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,11 @@
(sym_lib_table
(lib (name LP38690DT-3.3)(type Legacy)(uri ${KIPRJMOD}/kicad-stuff/LP38690DT-3.3.lib)(options "")(descr ""))
(lib (name ESP32-DEVKITC-32D)(type Legacy)(uri ${KIPRJMOD}/kicad-stuff/ESP32/ESP32-DEVKITC-32D.lib)(options "")(descr ""))
(lib (name DW01)(type Legacy)(uri ${KIPRJMOD}/kicad-stuff/DW01.lib)(options "")(descr ""))
(lib (name SX1308)(type Legacy)(uri ${KIPRJMOD}/kicad-stuff/SX1308.lib)(options "")(descr ""))
(lib (name ds2438)(type Legacy)(uri ${KIPRJMOD}/kicad-stuff/ds2438.lib)(options "")(descr ""))
(version 7)
(lib (name "LP38690DT-3.3")(type "KiCad")(uri "${KIPRJMOD}/kicad-stuff/LP38690DT-3.3.kicad_sym")(options "")(descr ""))
(lib (name "ESP32-DEVKITC-32D")(type "KiCad")(uri "${KIPRJMOD}/kicad-stuff/ESP32/ESP32-DEVKITC-32D.kicad_sym")(options "")(descr ""))
(lib (name "DW01")(type "KiCad")(uri "${KIPRJMOD}/kicad-stuff/DW01.kicad_sym")(options "")(descr ""))
(lib (name "SX1308")(type "KiCad")(uri "${KIPRJMOD}/kicad-stuff/SX1308.kicad_sym")(options "")(descr ""))
(lib (name "ds2438")(type "KiCad")(uri "${KIPRJMOD}/kicad-stuff/ds2438.kicad_sym")(options "")(descr ""))
(lib (name "CN3306")(type "KiCad")(uri "${KIPRJMOD}/CN3306.kicad_sym")(options "")(descr ""))
(lib (name "CN3795")(type "KiCad")(uri "${KIPRJMOD}/CN3795.kicad_sym")(options "")(descr ""))
(lib (name "BQ34Z100PWR-G1")(type "KiCad")(uri "${KIPRJMOD}/kicad-stuff/BQ34Z100PWR-G1.kicad_sym")(options "")(descr ""))
)

1
esp32/.gitignore vendored
View File

@@ -7,3 +7,4 @@
doc/
custom_platformio.ini
cppcheck-build-dir
host/settings.json

View File

@@ -3,5 +3,8 @@
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

3
esp32/CMakeLists.txt Normal file
View File

@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.16.0)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp32)

View File

@@ -18,7 +18,14 @@
"string": "cpp",
"typeinfo": "cpp",
"cmath": "cpp",
"iterator": "cpp"
"iterator": "cpp",
"array": "cpp",
"tuple": "cpp",
"utility": "cpp",
"fstream": "cpp",
"ostream": "cpp",
"sstream": "cpp"
"system_error": "cpp"
}
}
}

View File

@@ -1,7 +1,17 @@
# PlantControl
## Hardware
Uses ESP32MiniKit
Main processor
* ESP32 with 16MB Flash
One-Wire
* Temperatur Sensor (DS18B20)
* Lipo-Monitoring (DS2438)
Lipo Protection
* Open drain 3.3V detector (CN61CN33 @ jlcpcb parts)
### Used Pins:
* See '''include/ControllerConfiguration.h'''
@@ -16,9 +26,9 @@ Uses ESP32MiniKit
* 7 Pumps
* Sensors
* Solar powered (voltage)
* Lipo-Powered (voltage)
* Lipo-Powered (DS2438 for monitoring)
* Temperature
* Distance sensor [JSN-SR04T-2.0] (for waterlevel)
* Laser distance sensor [VL53L0X]
* Custom GPIO
## Documentation of Power-Modes
@@ -42,35 +52,18 @@ DS18B20 one wire temp sensor
## Empires Wunschliste
* Pflanze
* Pumpe
* Zeitspann (wann laufen darf)
* Helligkeitstrigger (Um den Morgen zum pumpen zu erkennen)
* Maximal Dauer zum Pumpen (als Zeit oder Milliliter)
* Zeitspanne zwischen zwei Pumpvorgängen
* [x] Zeitspann (wann laufen darf)
* [x] Helligkeitstrigger (Um den Morgen zum pumpen zu erkennen)
* [-] Maximal Dauer zum Pumpen (als Zeit oder Milliliter)
* [x] Zeitspanne zwischen zwei Pumpvorgängen
* Moister sensor
* Oberen
* Unteren Wert
* [x] Schwellwert für Pumpe
* Tank
* Füllstand Anzeige (in Liter)
* Minimum Wasserstand (in cm damit Pumpen nicht leer laufen; enspricht 0 nutzbaren Liter)
* Trigger-Erinnerungen um Wasser nachzufüllen
* Maximaler Wasserstand des Tanks (in cm & Liter)
* System
* Tiefentladungsschutz vom LIPO (fest im Controller die Spannung festlegen)
* 3.5V unterschritten, dann nur noch Deepsleep
* MQTT Topic, wenn Spannung unterschritten wurde
* Lipo innerhalb 24h nicht geladen -> MQTT Topic
* Deep-Sleep
* Mode1:
* Nur Sensor werte einsameln
* Wird verlassen bei Aktionen
* Pumpe schalten
* MQTT Nachrichten
* nach x Minuten nur in Mode1
* Mode2:
* WLAN aktivieren und Werte über MQTT raus hauen
* aktuelle Werte raushauen
* MQTT lesen
* Mode3:
* Deepsleep verboten (MQTT topic, retained)
* alle Pumpen & Sensoren deaktiviert
* [x] Maximaler Wasserstand des Tanks (in cm & Liter)
## Masterplan 2.0
* Partitionslayout

View File

@@ -5,13 +5,12 @@ framework = arduino
build_flags = -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
board_build.partitions = defaultWithSmallerSpiffs.csv
extra_configs = custom_platformio.ini
; the latest development brankitchen-lightch (convention V3.0.x)
lib_deps = ArduinoJson@6.16.1
https://github.com/homieiot/homie-esp8266.git#v3.0
OneWire
DallasTemperature
pololu/VL53L0X
https://github.com/homieiot/homie-esp8266.git#develop
; add additional parameter, like the upload port
upload_port=/dev/ttyUSB1

View File

@@ -1,6 +1,7 @@
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x150000,
app1, app, ota_1, 0x160000,0x150000,
spiffs, data, spiffs, 0x300000,0x17000,
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000
otadata, data, ota, 0xD000, 0x2000
phy_init, data, phy, 0xF000, 0x1000
ota_0, app, ota_0, 0x10000, 0x1E0000
ota_1, app, ota_1, 0x1F0000, 0x1E0000
spiffs, data, spiffs, 0x3D0000, 0x30000
1 # Name # Name, Type, SubType, Offset, Size, Flags Type SubType Offset Size Flags
2 nvs nvs, data, nvs, 0x9000, 0x4000 data nvs 0x9000 0x5000
3 otadata otadata, data, ota, 0xD000, 0x2000 data ota 0xe000 0x2000
4 app0 phy_init, data, phy, 0xF000, 0x1000 app ota_0 0x10000 0x150000
5 app1 ota_0, app, ota_0, 0x10000, 0x1E0000 app ota_1 0x160000 0x150000
6 spiffs ota_1, app, ota_1, 0x1F0000, 0x1E0000 data spiffs 0x300000 0x17000
7 spiffs, data, spiffs, 0x3D0000, 0x30000

View File

@@ -1,49 +0,0 @@
{
"name": "PlantControl",
"device_id": "PlantCtrl1",
"device_stats_interval": 60,
"wifi": {
"ssid": "SSID",
"bssid" : "BSSID",
"password": "mysecretPassword",
"channel": 1
},
"mqtt": {
"host": "[0-255].[0-255].[0-255].[0-255]",
"port": 1883,
"base_topic": "mqtt/topic/",
"auth": false
},
"ota": {
"enabled": true
},
"settings": {
"deepsleep" : 60000,
"nightsleep" : 60000,
"pumpdeepsleep": 1000,
"watermaxlevel": 50,
"watermin" : 5,
"plants" : 3,
"moist0" : 2000,
"moist1" : 2000,
"moist2" : 2000,
"moist3" : 2000,
"moist4" : 2000,
"moist5" : 2000,
"moist6" : 2000,
"plant0MaxPumpTime": 1000,
"plant1MaxPumpTime": 1000,
"plant2MaxPumpTime": 1000,
"plant3MaxPumpTime": 1000,
"plant4MaxPumpTime": 1000,
"plant5MaxPumpTime": 1000,
"plant6MaxPumpTime": 1000,
"plant0MinPumpIdle": 10000,
"plant1MinPumpIdle": 10000,
"plant2MinPumpIdle": 10000,
"plant3MinPumpIdle": 10000,
"plant4MinPumpIdle": 10000,
"plant5MinPumpIdle": 10000,
"plant6MinPumpIdle": 10000
}
}

View File

@@ -0,0 +1,49 @@
{
"settings": {
"sleep":600,
"nightsleep": 1200,
"pumpsleep": 5,
"tankmax": 1000,
"tankmin": 100,
"tankwarn": 200,
"tankVolume": 100,
"lipoDSAddr": "abcdefghijklmnop",
"tankDSAddr": "abcdefghijklmnop",
"ntpServer":"pool.ntp.org",
"dry0":50,
"hourstart0":6,
"hourend0":20,
"lowLight0": false,
"delay0": 30,
"dry1":-1,
"hourstart1":6,
"hourend1":20,
"lowLight1": false,
"delay1": 30,
"dry2":-1,
"hourstart2":6,
"hourend2":20,
"lowLight2": false,
"delay2": 30,
"dry3":-1,
"hourstart3":6,
"hourend3":20,
"lowLight3": false,
"delay3": 30,
"dry4":-1,
"hourstart4":6,
"hourend4":20,
"lowLight4": false,
"delay4": 30,
"dry5":-1,
"hourstart5":6,
"hourend5":20,
"lowLight5": false,
"delay5": 30,
"dry6":-1,
"hourstart6":6,
"hourend6":20,
"lowLight6": false,
"delay6": 30
}
}

41
esp32/host/upload-settings.sh Executable file
View File

@@ -0,0 +1,41 @@
#!//bin/bash
if [ $# -ne 3 ]; then
echo "Homie prefex and device index must be specified:"
echo "$0 <mqtt host> <prefix> <device index>"
echo "e.g."
echo "$0 192.168.0.2 test/ MyDeviceId"
exit 1
fi
mqttHost=$1
mqttPrefix=$2
homieId=$3
maxSteps=6
settingsFile=settings.json
if [ ! -f $settingsFile ]; then
echo "$settingsFile missing"
echo "check $settingsFile.example"
exit 1
fi
mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/stay/alive/set" -m "1" -r
echo "(1 / $maxSteps) Waiting ..."
mosquitto_sub -h $mqttHost -t "${mqttPrefix}${homieId}/#" -R -C 1
set -e
echo "(2 / $maxSteps) Waiting 30 seconds ..."
sleep 30
mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/\$implementation/config/set" -f $settingsFile
echo "(3 / $maxSteps) Waiting for reboot ..."
sleep 1
mosquitto_sub -h $mqttHost -t "${mqttPrefix}${homieId}/#" -R -C 1
echo "(4 / $maxSteps) Alive"
sleep 20
echo "(5 / $maxSteps) Create Backup ..."
mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/config/backup/set" -m "true" -r
sleep 5
echo "(6 / $maxSteps) Shutdown ..."
mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/stay/alive/set" -m "0" -r
exit 0

View File

@@ -21,7 +21,8 @@ fi
mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/stay/alive/set" -m "1" -r
echo "Waiting ..."
mosquitto_sub -h $mqttHost -t "${mqttPrefix}${homieId}/#" -R -C 1
python ota_updater.py -l $mqttHost -t "$mqttPrefix" -i "$homieId" $firmwareFile
set -e
python3 ota_updater.py -l $mqttHost -t "$mqttPrefix" -i "$homieId" $firmwareFile
mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/stay/alive/set" -m "0" -r
exit 0

View File

@@ -43,66 +43,94 @@
*/
#ifndef CONTROLLER_CONFIG_H
#define CONTROLLER_CONFIG_H
/** \addtogroup GPIO Settings
* @{
*/
#define SENSOR_PLANT0 GPIO_NUM_32 /**< GPIO 32 (ADC1) */
#define SENSOR_PLANT1 GPIO_NUM_33 /**< GPIO 33 (ADC1) */
#define SENSOR_PLANT2 GPIO_NUM_25 /**< GPIO 25 (ADC2) */
#define SENSOR_PLANT3 GPIO_NUM_26 /**< GPIO 26 (ADC2) */
#define SENSOR_PLANT4 GPIO_NUM_27 /**< GPIO 27 (ADC2) */
#define SENSOR_PLANT5 GPIO_NUM_39 /**< SENSOR_VIN */
#define SENSOR_PLANT6 GPIO_NUM_36 /**< SENSOR_VP */
#define OUTPUT_PUMP0 GPIO_NUM_15 /**< GPIO 15 */
#define OUTPUT_PUMP1 GPIO_NUM_5 /**< GPIO 5 */
#define OUTPUT_PUMP2 GPIO_NUM_18 /**< GPIO 18 */
#define OUTPUT_PUMP3 GPIO_NUM_19 /**< GPIO 19 */
#define OUTPUT_PUMP4 GPIO_NUM_21 /**< GPIO 21 */
#define OUTPUT_PUMP5 GPIO_NUM_22 /**< GPIO 22 */
#define OUTPUT_PUMP6 GPIO_NUM_23 /**< GPIO 23 */
#define OUTPUT_ENABLE_SENSOR GPIO_NUM_14 /**< GPIO 14 - Enable Sensors */
#define OUTPUT_ENABLE_PUMP GPIO_NUM_13 /**< GPIO 13 - Enable Pumps */
#define SENSOR_ONEWIRE GPIO_NUM_4 /**< GPIO 12 - Temperatur sensor, Battery and other cool onewire stuff */
#define SENSOR_TANK_SDA GPIO_NUM_17 /**< GPIO 17 - water sensor SDA */
#define SENSOR_TANK_SCL GPIO_NUM_16 /**< GPIO 16 - water sensor SCL */
#define BUTTON GPIO_NUM_0 /**< GPIO 0 - Fix button of NodeMCU */
#define CUSTOM1_PIN1 GPIO_NUM_34 /** direct gpio */
#define CUSTOM1_PIN3 GPIO_NUM_35 /** direct gpio */
#define CUSTOM1_PIN5 GPIO_NUM_2 /** mosfet controlled */
#define CUSTOM1_PIN7 GPIO_NUM_12 /** mosfet controlled */
/* @} */
/** \addtogroup Configuration
* @{
*/
#define FIRMWARE_VERSION "1.1.0"
#define ADC_TO_VOLT(adc) ((adc) * 3.3 ) / 4095)
#define ADC_TO_VOLT_WITH_MULTI(adc, multi) (((adc)*3.3 * (multi)) / 4095)
#define MOIST_SENSOR_MAX_ADC (85 * 4095 / 100)
#define MOIST_SENSOR_MIN_ADC (25 * 4095 / 100)
#ifdef FLOWMETER_PIN
#define FLOWMETER_PULSES_PER_ML 2.2
#define FIRMWARE_FEATURE1 "Flow"
#else
#define FIRMWARE_FEATURE1 ""
#endif
#define SOLAR_VOLT_FACTOR 2
#ifdef TIMED_LIGHT_PIN
#define FIRMWARE_FEATURE2 "Light"
#else
#define FIRMWARE_FEATURE2 ""
#endif
#define FIRMWARE_BASENAME "PlantControl"
#define FIRMWARE_NAME FIRMWARE_BASENAME FIRMWARE_FEATURE1 FIRMWARE_FEATURE2
#define FIRMWARE_VERSION "3.01 HW0.10b"
#define MOIST_SENSOR_MAX_FRQ 5200 // 60kHz (500Hz margin)
#define MOIST_SENSOR_MIN_FRQ 500 // 0.5kHz (500Hz margin)
#define ANALOG_SENSOR_MAX_MV 1300 //successive approximation of good range
#define ANALOG_SENSOR_MIN_MV 100 //successive approximation of good range
#define SOLAR_VOLT_FACTOR 11
#define BATTSENSOR_INDEX_SOLAR 0
#define BATTSENSOR_INDEX_BATTERY 1
#define MS_TO_S 1000
#define SENSOR_PLANT0 32 /**< GPIO 32 (ADC1) */
#define SENSOR_PLANT1 33 /**< GPIO 33 (ADC1) */
#define SENSOR_PLANT2 25 /**< GPIO 25 (ADC2) */
#define SENSOR_PLANT3 26 /**< GPIO 26 (ADC2) */
#define SENSOR_PLANT4 27 /**< GPIO 27 (ADC2) */
#define SENSOR_PLANT5 39 /**< SENSOR_VIN */
#define SENSOR_PLANT6 36 /**< SENSOR_VP */
#define MQTT_TIMEOUT (1000 * 60) /**< After 10 seconds, MQTT is expected to be connected */
#define ESP_STALE_TIMEOUT (MQTT_TIMEOUT+(700*1000))
#define OUTPUT_PUMP0 17 /**< GPIO 17 */
#define OUTPUT_PUMP1 5 /**< GPIO 5 */
#define OUTPUT_PUMP2 18 /**< GPIO 18 */
#define OUTPUT_PUMP3 19 /**< GPIO 19 */
#define OUTPUT_PUMP4 21 /**< GPIO 21 */
#define OUTPUT_PUMP5 22 /**< GPIO 22 */
#define OUTPUT_PUMP6 23 /**< GPIO 23 */
#define OUTPUT_SENSOR 16 /**< GPIO 16 - Enable Sensors */
#define OUTPUT_PUMP 13 /**< GPIO 13 - Enable Pumps */
#define SENSOR_DS18B20 2 /**< GPIO 2 - Temperatur sensor */
#define BUTTON 0 /**< GPIO 0 - Fix button of NodeMCU */
#define MIN_TIME_RUNNING 5UL /**< Amount of seconds the controller must stay awoken */
#define MAX_PLANTS 7
#define MINIMUM_LIPO_VOLT 3.2f /**< Minimum voltage of the Lipo, that must be present */
#define NO_LIPO_VOLT 2.0f /**< No Lipo connected */
#define MINIMUM_SOLAR_VOLT 4.0f /**< Minimum voltage of the sun, to detect daylight */
#define SOLAR_CHARGE_MIN_VOLTAGE 7 /**< Sun is rising (morning detected) */
#define SOLAR_CHARGE_MAX_VOLTAGE 9 /**< Sun is shining (noon) */
#define SOLAR_CHARGE_MIN_VOLTAGE 7 /**< Sun is rising (morning detected) */
#define SOLAR_CHARGE_MAX_VOLTAGE 9 /**< Sun is shining (noon) */
#define SOLAR_MAX_VOLTAGE_POSSIBLE 100 /**< higher values are treated as not connected sensor */
#define VOLT_MAX_BATT 4.2f
#define VOLT_MIN_BATT 3.0f /**< Minimum battery voltage for normal operation */
#define LOWVOLT_SLEEP_FACTOR 3 /**< Factor for nightsleep delay, if the battery drops below minimum (@see VOLT_MIN_BATT) */
#define LOWVOLT_SLEEP_MINIMUM 1800 /**< At low voltage sleep at least for 30 minutes */
#define MAX_CONFIG_SETTING_ITEMS 50 /**< Parameter, that can be configured in Homie */
#define MAX_CONFIG_SETTING_ITEMS 100 /**< Parameter, that can be configured in Homie */
#define MAX_JSON_CONFIG_FILE_SIZE_CUSTOM 2500
#define PANIK_MODE_DEEPSLEEP (60 * 60 * 5U) /**< 5 hours in usecond */
#define PANIK_MODE_DEEPSLEEP_US (PANIK_MODE_DEEPSLEEP * 1000 * 1000)
#define LIPO_MAX_TEMPERATUR 85
#define LIPO_MAX_TEMPERATUR_DIFF 10
#define TEMPERATUR_TIMEOUT 3000 /**< 3 Seconds timeout for the temperatures sensors */
#define WATERSENSOR_TIMEOUT 3000 /**< 3 Seconds timeout for the water distance sensor */
#define WATERSENSOR_CYCLE 10 /**< 5 sensor measurement are performed */
#define DS18B20_RESOLUTION 9 /**< 9bit temperature resolution -> 0.5°C steps */
#define TEMPERATURE_DELTA_TRIGGER_IN_C 1.0f
#define MOIST_DELTA_TRIGGER_ADC 10
#define SOLAR_DELTA_VOLT_ADC 3
#define LIPO_DELTA_VOLT_ADC 0.2 /**< trigger for lipo voltage */
#define TEMPERATUR_TIMEOUT 3000 /**< 3 Seconds timeout for the temperatur sensors */
#define TEMP_SENSOR_MEASURE_SERIES 5
#define UTC_OFFSET_DE 3600 /* UTC offset in seconds for Germany */
#define UTF_OFFSET_DE_DST 3600 /* offset in seconds if daylight saving time is active */
/* @} */
#endif

View File

@@ -1,18 +1,6 @@
/*
* DS2438.h
/**
* @file DS2438.h
*
* by Joe Bechter
*
* (C) 2012, bechter.com
*
* All files, software, schematics and designs are provided as-is with no warranty.
* All files, software, schematics and designs are for experimental/hobby use.
* Under no circumstances should any part be used for critical systems where safety,
* life or property depends upon it. You are responsible for all use.
* You are free to use, modify, derive or otherwise extend for your own non-commercial purposes provided
* 1. No part of this software or design may be used to cause injury or death to humans or animals.
* 2. Use is non-commercial.
* 3. Credit is given to the author (i.e. portions © bechter.com), and provide a link to the original source.
*
*/
@@ -21,6 +9,7 @@
#include <Arduino.h>
#include <OneWire.h>
#include "RunningMedian.h"
#define DS2438_TEMPERATURE_CONVERSION_COMMAND 0x44
#define DS2438_VOLTAGE_CONVERSION_COMMAND 0xb4
@@ -42,6 +31,9 @@
#define DS2438_TEMPERATURE_DELAY 10
#define DS2438_VOLTAGE_CONVERSION_DELAY 8
#define DS2438_MEDIAN_COUNT 5
#define DS2438_MEDIAN_DELAY 50
#define DEFAULT_PAGE0(var) uint8_t var[8] { \
0b00001011 /* X, ADB=0, NVB=0, TB=0, AD=1, EE=0, CA=1, IAD=1 */, \
0, /* Temperatur */ \
@@ -50,7 +42,7 @@
0, /* Voltage */ \
0, /* Current */ \
0, /* Current */ \
0 /* Threashold */ \
0b10000000 /* Threshold to 4LSB */ \
}
typedef struct PageOne {
@@ -79,11 +71,10 @@ typedef uint8_t DeviceAddress[8];
class DS2438 {
public:
DS2438(OneWire *ow, float currentShunt);
DS2438(OneWire *ow, uint8_t *address);
DS2438(OneWire *ow, float currentShunt, int retryOnCRCError);
void begin();
void update();
void updateMultiple();
double getTemperature();
float getVoltage(int channel=DS2438_CHA);
float getCurrent();
@@ -96,16 +87,17 @@ class DS2438 {
private:
bool validAddress(const uint8_t*);
bool validFamily(const uint8_t* deviceAddress);
void update(bool firstIteration);
bool deviceFound = false;
OneWire *_ow;
DeviceAddress _address;
uint8_t _mode;
double _temperature;
float _voltageA;
float _voltageB;
float _current;
RunningMedian _temperature = RunningMedian(DS2438_MEDIAN_COUNT*2);
RunningMedian _voltageA = RunningMedian(DS2438_MEDIAN_COUNT);
RunningMedian _voltageB = RunningMedian(DS2438_MEDIAN_COUNT);
RunningMedian _current = RunningMedian(DS2438_MEDIAN_COUNT);
float _currentShunt;
int _retryOnCRCError;
long _CCA;
long _DCA;
long _ICA;

View File

@@ -0,0 +1,9 @@
#ifndef FILEUTILS_H
#define FILEUTILS_H
bool doesFileExist(const char *source);
bool copyFile(const char *source, const char *target);
bool deleteFile(const char *source);
void printFile(const char *source);
#endif

View File

@@ -19,56 +19,81 @@
#define MAX_PLANTS 7
/**
* @name Attributes
* @name Homie Attributes
* generated Information
* @{
**/
#define NUMBER_TYPE "number"
#define TEMPERATUR_SENSOR_LIPO "lipo"
#define TEMPERATUR_SENSOR_WATER "water"
#define TEMPERATUR_SENSOR_OUTSIDE "temp"
#define TEMPERATUR_SENSOR_CHIP "chip"
#define NUMBER_TYPE "Float" /**< numberic information, published or read in Homie */
/**
* @}
*
* @name Temperatur Node
* @{
**/
#define TEMPERATURE_NAME "Temperature"
#define TEMPERATURE_UNIT "°C"
#define TEMPERATUR_SENSOR_LIPO "lipo" /**< Homie node: temperatur, setting: lipo temperatur (or close to it) */
#define TEMPERATUR_SENSOR_CHIP "chip" /**< Homie node: temperatur, setting: battery chip */
#define TEMPERATUR_SENSOR_WATER "water" /**< Homie node: temperatur, setting: water temperatur */
/** @}
*
* @name Plant Nodes
* @{
*/
HomieNode plant0("plant0", "Plant 0", "Plant"); /**< dynamic Homie information for first plant */
HomieNode plant1("plant1", "Plant 1", "Plant"); /**< dynamic Homie information for second plant */
HomieNode plant2("plant2", "Plant 2", "Plant"); /**< dynamic Homie information for first plant */
HomieNode plant3("plant3", "Plant 3", "Plant"); /**< dynamic Homie information for first plant */
HomieNode plant4("plant4", "Plant 4", "Plant"); /**< dynamic Homie information for first plant */
HomieNode plant5("plant5", "Plant 5", "Plant"); /**< dynamic Homie information for first plant */
HomieNode plant6("plant6", "Plant 6", "Plant"); /**< dynamic Homie information for first plant */
HomieNode plant2("plant2", "Plant 2", "Plant"); /**< dynamic Homie information for third plant */
HomieNode plant3("plant3", "Plant 3", "Plant"); /**< dynamic Homie information for fourth plant */
HomieNode plant4("plant4", "Plant 4", "Plant"); /**< dynamic Homie information for fivth plant */
HomieNode plant5("plant5", "Plant 5", "Plant"); /**< dynamic Homie information for sixth plant */
HomieNode plant6("plant6", "Plant 6", "Plant"); /**< dynamic Homie information for seventh plant */
#if defined(TIMED_LIGHT_PIN)
HomieNode timedLightNode("timedLight", "TimedLight", "Status");
#endif // TIMED_LIGHT_PIN
HomieNode sensorLipo("lipo", "Battery Status", "Lipo");
HomieNode sensorSolar("solar", "Solar Status", "Solarpanel");
HomieNode sensorWater("water", "WaterSensor", "Water");
HomieNode sensorTemp("temperature", "Temperature", "temperature");
HomieNode startupReason("startupReason", "startupReason", "startupReason");
HomieNode stayAlive("stay", "alive", "alive"); /**< Necessary for Mqtt Active Command */
/* @} */
/**
* @}
*/
/**
* @name Settings
* General settings for the controller
* @{
*/
HomieSetting<long> maxTimeBetweenMQTTUpdates("mqttSleep", "time in seconds to start into mode2");
HomieSetting<long> deepSleepTime("deepsleep", "time in seconds to sleep (0 deactivats it)");
HomieSetting<long> deepSleepTime("sleep", "time in seconds to sleep");
HomieSetting<long> deepSleepNightTime("nightsleep", "time in seconds to sleep (0 uses same setting: deepsleep at night, too)");
HomieSetting<long> wateringDeepSleep("pumpdeepsleep", "time seconds to sleep, while a pump is running");
HomieSetting<long> waterLevelMax("watermaxlevel", "distance (mm) at maximum water level");
HomieSetting<long> waterLevelMin("waterminlevel", "distance (mm) at minimum water level (pumps still covered)");
HomieSetting<long> waterLevelWarn("waterlevelwarn", "warn (mm) if below this water level %");
HomieSetting<long> waterLevelVol("waterVolume", "(ml) between minimum and maximum");
HomieSetting<long> lipoSensorIndex("lipoTempIndex", "index onwire bus for lipo temperature sensor");
HomieSetting<long> waterSensorIndex("waterTempIndex", "index onwire bus for water temperature sensor");
HomieSetting<long> pumpIneffectiveWarning("pumpConsecutiveWarn", "if the pump was triggered this amount directly after each cooldown, without resolving dryness, warn");
HomieSetting<long> waterLevelMax("tankmax", "distance (mm) at maximum water level");
HomieSetting<long> waterLevelMin("tankmin", "distance (mm) at minimum water level (pumps still covered)");
HomieSetting<long> waterLevelWarn("tankwarn", "warn (mm) if below this water level %");
HomieSetting<long> waterLevelVol("tankVolume", "(ml) between minimum and maximum");
HomieSetting<const char *> lipoSensorAddr("lipoDSAddr", "1wire address for lipo temperature sensor");
HomieSetting<const char *> waterSensorAddr("tankDSAddr", "1wire address for water temperature sensor");
HomieSetting<const char *> ntpServer("ntpServer", "NTP server (pool.ntp.org as default)");
#if defined(TIMED_LIGHT_PIN)
HomieSetting<double> timedLightVoltageCutoff("LightVoltageCutoff", "voltage at wich to disable light");
HomieSetting<long> timedLightStart("LightStart", "hour to start light");
HomieSetting<long> timedLightEnd("LightEnd", "hour to end light");
HomieSetting<bool> timedLightOnlyWhenDark("LightOnlyDark", "only enable light, if solar is low");
HomieSetting<long> timedLightPowerLevel("LightPowerLevel", "0-255 power level");
#endif // TIMED_LIGHT_PIN
/**
*@}
* @}
*/
/**
@@ -78,12 +103,15 @@ HomieSetting<const char *> ntpServer("ntpServer", "NTP server (pool.ntp.org as d
**/
#define GENERATE_PLANT(plant, strplant) \
HomieSetting<long> mSensorDry##plant = HomieSetting<long>("moistdry" strplant, "Plant " strplant "- Moist sensor dry threshold"); \
HomieSetting<long> mPumpAllowedHourRangeStart##plant = HomieSetting<long>("rangehourstart" strplant, "Plant" strplant " - Range pump allowed hour start (0-23)"); \
HomieSetting<long> mPumpAllowedHourRangeEnd##plant = HomieSetting<long>("rangehourend" strplant, "Plant" strplant " - Range pump allowed hour end (0-23)"); \
HomieSetting<bool> mPumpOnlyWhenLowLight##plant = HomieSetting<bool>("onlyWhenLowLightZ" strplant, "Plant" strplant " - Enable the Pump only, when there is light but not enought to charge battery"); \
HomieSetting<long> mPumpCooldownInHours##plant = HomieSetting<long>("cooldownpump" strplant, "Plant" strplant " - How long to wait until the pump is activated again (minutes)"); \
PlantSettings_t mSetting##plant = {&mSensorDry##plant, &mPumpAllowedHourRangeStart##plant, &mPumpAllowedHourRangeEnd##plant, &mPumpOnlyWhenLowLight##plant, &mPumpCooldownInHours##plant}; \
HomieSetting<double> mSensorDry##plant = HomieSetting<double>("dry" strplant, "Plant" strplant " - Moist sensor dry %"); \
HomieSetting<long> mPumpAllowedHourRangeStart##plant = HomieSetting<long>("hourstart" strplant, "Plant" strplant " - Range pump allowed hour start (0-23)"); \
HomieSetting<long> mPumpAllowedHourRangeEnd##plant = HomieSetting<long>("hourend" strplant, "Plant" strplant " - Range pump allowed hour end (0-23)"); \
HomieSetting<bool> mPumpOnlyWhenLowLight##plant = HomieSetting<bool>("lowLight" strplant, "Plant" strplant " - Enable the Pump only, when there is no sunlight"); \
HomieSetting<long> mPumpCooldownInSeconds##plant = HomieSetting<long>("delay" strplant, "Plant" strplant " - How long to wait until the pump is activated again (sec)"); \
HomieSetting<long> pPumpDuration##plant = HomieSetting<long>("pumpDuration" strplant, "Plant" strplant " - time seconds to water when pump is active"); \
HomieSetting<long> pPumpMl##plant = HomieSetting<long>("pumpAmount" strplant, "Plant" strplant " - ml (if using flowmeter) to water when pump is active"); \
HomieSetting<long> pPowerLevel##plant = HomieSetting<long>("powerLevel" strplant, "Plant" strplant " - pwm duty cycle in percent"); \
PlantSettings_t mSetting##plant = {&mSensorDry##plant, &mPumpAllowedHourRangeStart##plant, &mPumpAllowedHourRangeEnd##plant, &mPumpOnlyWhenLowLight##plant, &mPumpCooldownInSeconds##plant, &pPumpDuration##plant, &pPowerLevel##plant, &pPumpMl##plant}; \
/**< Generate all settings for one plant \
* \
* Feature to start pumping only at morning: @link{SOLAR_CHARGE_MIN_VOLTAGE} and @link{SOLAR_CHARGE_MAX_VOLTAGE} \
@@ -101,4 +129,7 @@ GENERATE_PLANT(4, "4"); /**< Homie settings for fifth plant */
GENERATE_PLANT(5, "5"); /**< Homie settings for sixth plant */
GENERATE_PLANT(6, "6"); /**< Homie settings for seventh plant */
#endif /* HOMIE_PLANT_CONFIG_H @} */

View File

@@ -13,16 +13,83 @@
#include <Homie.h>
#define DEACTIVATED_PLANT 5000
#define MISSING_SENSOR 5001
/**
* @name Sensor types
* possible sensors:
* @{
**/
#define FOREACH_SENSOR(SENSOR) \
SENSOR(NONE) \
SENSOR(FREQUENCY_MOD_RESISTANCE_PROBE) \
SENSOR(ANALOG_RESISTANCE_PROBE)
/**
* @}
*/
#define GENERATE_ENUM(ENUM) ENUM,
#define GENERATE_STRING(STRING) #STRING,
enum SENSOR_MODE {
FOREACH_SENSOR(GENERATE_ENUM)
};
static const char *SENSOR_STRING[] = {
FOREACH_SENSOR(GENERATE_STRING)
};
//plant pump is deactivated, but sensor values are still recorded and published
#define DEACTIVATED_PLANT -1
//special value to indicate a missing sensor when the plant is not deactivated but no valid sensor value was read
#define MISSING_SENSOR -2
//plant uses only cooldown and duration, moisture is measured but ignored, allowedHours is ignored (eg. make a 30min on 30min off cycle)
#define HYDROPONIC_MODE -3
//plant uses cooldown and duration and workhours, moisture is measured but ignored
#define TIMER_ONLY -4
//special value to indicate a shorted sensor when the plant is not deactivated but the sensor reads short circuit value
#define SHORT_CIRCUIT_MODE -5
/**
* @brief State of plants
*
*/
#define PLANTSTATE_NUM_DEACTIVATED -1
#define PLANTSTATE_NUM_NO_SENSOR -2
#define PLANTSTATE_NUM_WET 0x00
#define PLANTSTATE_NUM_SUNNY_ALARM 0x11
#define PLANTSTATE_NUM_ACTIVE_ALARM 0x41
#define PLANTSTATE_NUM_ACTIVE_SUPESSED -3
#define PLANTSTATE_NUM_ACTIVE 0x40
#define PLANTSTATE_NUM_SUNNY 0x10
#define PLANTSTATE_NUM_COOLDOWN_ALARM 0x21
#define PLANTSTATE_NUM_COOLDOWN 0x20
#define PLANTSTATE_NUM_AFTERWORK_ALARM 0x31
#define PLANTSTATE_NUM_AFTERWORK 0x30
#define PLANTSTATE_STR_DEACTIVATED "deactivated"
#define PLANTSTATE_STR_NO_SENSOR "nosensor"
#define PLANTSTATE_STR_WET "wet"
#define PLANTSTATE_STR_SUNNY_ALARM "sunny+alarm"
#define PLANTSTATE_STR_ACTIVE_ALARM "active+alarm"
#define PLANTSTATE_STR_ACTIVE_SUPESSED "active+supressed"
#define PLANTSTATE_STR_ACTIVE "active"
#define PLANTSTATE_STR_SUNNY "sunny"
#define PLANTSTATE_STR_COOLDOWN_ALARM "cooldown+alarm"
#define PLANTSTATE_STR_COOLDOWN "cooldown"
#define PLANTSTATE_STR_AFTERWORK_ALARM "after-work+alarm"
#define PLANTSTATE_STR_AFTERWORK "after-work"
typedef struct PlantSettings_t
{
HomieSetting<long> *pSensorDry;
HomieSetting<double> *pSensorDry;
HomieSetting<long> *pPumpAllowedHourRangeStart;
HomieSetting<long> *pPumpAllowedHourRangeEnd;
HomieSetting<bool> *pPumpOnlyWhenLowLight;
HomieSetting<long> *pPumpCooldownInHours;
HomieSetting<long> *pPumpCooldownInSeconds;
HomieSetting<long> *pPumpDuration;
HomieSetting<long> *pPumpPowerLevel;
HomieSetting<long> *pPumpMl;
} PlantSettings_t;
#endif

View File

@@ -0,0 +1,50 @@
#ifndef LOG_DEFINES_H
#define LOG_DEFINES_H
#define LOG_LEVEL_ERROR 0
#define LOG_LEVEL_WARN 1
#define LOG_LEVEL_INFO 2
#define LOG_LEVEL_DEBUG 3
#define LOG_TANKSENSOR_FAIL_DETECT "Failed to detect and initialize distance sensor!"
#define LOG_TANKSENSOR_FAIL_DETECT_CODE -1
#define LOG_BACKUP_SUCCESSFUL "Backup sucessful"
#define LOG_BACKUP_SUCCESSFUL_CODE 1
#define LOG_BACKUP_FAILED "Backup error"
#define LOG_BACKUP_FAILED_CODE -2
#define LOG_PUMP_BUTNOTANK_MESSAGE "Want to pump but no water"
#define LOG_PUMP_BUTNOTANK_CODE -3
#define LOG_HARDWARECOUNTER_ERROR_MESSAGE "PCNR returned error"
#define LOG_HARDWARECOUNTER_ERROR_CODE -4
#define LOG_SENSORMODE_UNKNOWN "Unknown sensor mode requested"
#define LOG_SENSORMODE_UNKNOWN_CODE -5
#define LOG_SENSOR_MISSING -6
#define LOG_PUMP_AND_DOWNLOADMODE "Download mode, ignoring pump request"
#define LOG_PUMP_AND_DOWNLOADMODE_CODE 2
#define LOG_VERY_COLD_WATER "Water potentially frozen, ignoring pump request"
#define LOG_VERY_COLD_WATER_CODE -7
#define LOG_PUMP_FULLTANK_MESSAGE "Water Sensor distance unrealistic"
#define LOG_PUMP_FULLTANK_CODE 3
//msg is dynamic defined
#define LOG_PUMP_INEFFECTIVE -4
#define LOG_PUMP_STARTED_CODE 10
#define LOG_DEBUG_CODE 1001
#define LOG_SLEEP_NIGHT 100
#define LOG_SLEEP_DAY 101
#define LOG_SLEEP_LOWVOLTAGE 502
#define LOG_SLEEP_CYCLE 102
#define LOG_MISSING_PUMP -4
#define LOG_BOOT_ERROR_DETECTION 10000
#define LOG_SOLAR_CHARGER_MISSING 300
#endif

28
esp32/include/MQTTUtils.h Normal file
View File

@@ -0,0 +1,28 @@
#ifndef MQTTUtils_h
#define MQTTUtils_h
#include <Homie.h>
#define LOG_TOPIC "log\0"
#define TEST_TOPIC "roundtrip\0"
#define BACKUP_TOPIC "$implementation/config/backup/set\0"
#define CONFIG_FILE "/homie/config.json"
#define CONFIG_FILE_BACKUP "/homie/config.json.bak"
#define getTopic(test, topic) \
char *topic = new char[strlen(Homie.getConfiguration().mqtt.baseTopic) + strlen(Homie.getConfiguration().deviceId) + 1 + strlen(test) + 1]; \
strcpy(topic, Homie.getConfiguration().mqtt.baseTopic); \
strcat(topic, Homie.getConfiguration().deviceId); \
strcat(topic, "/"); \
strcat(topic, test);
bool aliveWasRead();
bool mqttReady();
void startMQTTRoundtripTest();
void log(int level, String message, int code);
void mqttWrite(HomieNode* target,const char* key, String value);
void mqttWrite(HomieNode* target,String key, String value);
#endif

View File

@@ -0,0 +1,8 @@
#ifndef MATHUTILS_H
#define MATHUTILS_H
bool equalish(double x, double y);
double mapf(double x, double in_min, double in_max, double out_min, double out_max);
#endif

View File

@@ -13,18 +13,32 @@
#define PLANT_CTRL_H
#include "HomieTypes.h"
#include <HomieNode.hpp>
#include "ControllerConfiguration.h"
#include "RunningMedian.h"
#include "MathUtils.h"
#include "MQTTUtils.h"
#include "LogDefines.h"
#define ANALOG_REREADS 5
#define MOISTURE_MEASUREMENT_DURATION 400 /** ms */
#define PWM_FREQ 50000
#define PWM_BITS 8
class Plant
{
private:
RunningMedian moistureRaw = RunningMedian(5);
HomieNode *mPlant = NULL;
HomieInternals::PropertyInterface mPump;
RunningMedian mMoisture_raw = RunningMedian(ANALOG_REREADS);
RunningMedian mTemperature_degree = RunningMedian(ANALOG_REREADS);
int mPinSensor = 0; /**< Pin of the moist sensor */
int mPinPump = 0; /**< Pin of the pump */
bool mConnected = false;
int mPlantId = -1;
SENSOR_MODE mSensorMode;
public:
PlantSettings_t *mSetting;
@@ -37,22 +51,45 @@ public:
Plant(int pinSensor, int pinPump,
int plantId,
HomieNode *plant,
PlantSettings_t *setting);
PlantSettings_t *setting, SENSOR_MODE mode);
void postMQTTconnection(void);
void advertise(void);
/**
* @brief Measure a new analog moister value
*
*/
void addSenseValue(void);
// for sensor that might take any time
void blockingMoistureMeasurement(void);
// for sensor that need a start and a end in defined timing
void startMoistureMeasurement(void);
void stopMoistureMeasurement(void);
void deactivatePump(void);
void activatePump(void);
String getSensorModeString()
{
SENSOR_MODE mode = getSensorMode();
return SENSOR_STRING[mode];
}
bool isTimerOnly()
{
long current = this->mSetting->pSensorDry->get();
return equalish(current, TIMER_ONLY);
}
bool isHydroponic()
{
long current = this->mSetting->pSensorDry->get();
return equalish(current, HYDROPONIC_MODE);
}
SENSOR_MODE getSensorMode()
{
return mSensorMode;
}
/**
* @brief Check if a plant is too dry and needs some water.
*
@@ -61,26 +98,25 @@ public:
*/
bool isPumpRequired()
{
bool isDry = getCurrentMoisture() > getSettingsMoisture();
if (isHydroponic() || isTimerOnly())
{
// hydroponic only uses timer based controll
return true;
}
bool isDry = getCurrentMoisturePCT() < getTargetMoisturePCT();
bool isActive = isPumpTriggerActive();
return isDry && isActive;
}
bool isPumpTriggerActive()
{
return this->mSetting->pSensorDry->get() != DEACTIVATED_PLANT;
long current = this->mSetting->pSensorDry->get();
return !equalish(current, DEACTIVATED_PLANT);
}
float getCurrentMoisture()
float getTargetMoisturePCT()
{
if(moistureRaw.getCount()==0){
return MISSING_SENSOR;
}
return this->moistureRaw.getMedian();
}
long getSettingsMoisture()
{
if (this->mSetting->pSensorDry != NULL)
if (isPumpTriggerActive())
{
return this->mSetting->pSensorDry->get();
}
@@ -90,37 +126,95 @@ public:
}
}
float getCurrentMoisturePCT()
{
switch (getSensorMode())
{
case NONE:
return DEACTIVATED_PLANT;
case FREQUENCY_MOD_RESISTANCE_PROBE:
return mapf(mMoisture_raw.getMedian(), MOIST_SENSOR_MIN_FRQ, MOIST_SENSOR_MAX_FRQ, 0, 100);
case ANALOG_RESISTANCE_PROBE:
return mapf(mMoisture_raw.getMedian(), ANALOG_SENSOR_MAX_MV, ANALOG_SENSOR_MIN_MV, 0, 100);
}
return MISSING_SENSOR;
}
float getCurrentMoistureRaw()
{
if (getSensorMode() == FREQUENCY_MOD_RESISTANCE_PROBE)
{
if (mMoisture_raw.getMedian() < MOIST_SENSOR_MIN_FRQ)
{
return MISSING_SENSOR;
}
else if (mMoisture_raw.getMedian() > MOIST_SENSOR_MAX_FRQ)
{
return SHORT_CIRCUIT_MODE;
}
}
return mMoisture_raw.getMedian();
}
HomieInternals::SendingPromise &setProperty(const String &property) const
{
return mPlant->setProperty(property);
}
bool switchHandler(const HomieRange &range, const String &value);
void init(void);
void initSensors(void);
/** @fn bool isInCooldown(long sinceLastActivation)
* @brief determine, if the plant was recently casted
* @param sinceLastActivation timestamp of last time
*/
bool isInCooldown(long sinceLastActivation)
long getCooldownInSeconds()
{
/* if the time difference is greater than one month, we know these are initial values */
if (sinceLastActivation > (60 * 60 * 24 * 30))
{
return false;
}
return (getCooldownInSeconds() > sinceLastActivation);
return this->mSetting->pPumpCooldownInSeconds->get();
}
long getCooldownInSeconds(){
return this->mSetting->pPumpCooldownInHours->get()*60*60;
/**
* @brief Get the Hours when pumping should start
*
* @return hour
*/
int getHoursStart()
{
return this->mSetting->pPumpAllowedHourRangeStart->get();
}
/**
* @brief Get the Hours when pumping should end
*
* @return hour
*/
int getHoursEnd()
{
return this->mSetting->pPumpAllowedHourRangeEnd->get();
}
bool isAllowedOnlyAtLowLight(void)
{
if (this->isHydroponic())
{
return false;
}
return this->mSetting->pPumpOnlyWhenLowLight->get();
}
void publishState(int stateNumber, String stateString);
bool switchHandler(const HomieRange &range, const String &value);
void setSwitchHandler(HomieInternals::PropertyInputHandler f);
long getPumpDuration()
{
return this->mSetting->pPumpDuration->get();
}
long getPumpMl()
{
return this->mSetting->pPumpMl->get();
}
};
#endif

View File

@@ -0,0 +1,4 @@
#pragma once
long getCurrentTime(void);
int getCurrentHour(void);

177
esp32/include/ulp-pwm.h Normal file
View File

@@ -0,0 +1,177 @@
#ifndef ULP_PWM_h
#define ILP_PWM_h
#include <Arduino.h>
#include "driver/rtc_io.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc.h"
#include "esp32/ulp.h"
#include "ControllerConfiguration.h"
#define LBL_START 1
#define LBL_DELAY_ON 2
#define LBL_DELAY_OFF 3
#define LBL_SKIP_ON 4
#define LBL_SKIP_OFF 5
#define REGISTER_DELAY_LOOP_COUNTER R0
#define REGISTER_TICKS_ON R1
#define REGISTER_TICKS_OFF R2
#define TOTAL_TICKS_DELAY 255
#define PIN TIMED_LIGHT_PIN
//support 20 vars
const size_t ulp_var_offset = CONFIG_ULP_COPROC_RESERVE_MEM - 20;
//use the first for dimming
const size_t ulp_dimm_offset = ulp_var_offset + 1;
const size_t ulp_alive_offset = ulp_var_offset + 2;
//see https://github.com/perseus086/ESP32-notes
const uint32_t rtc_bit[40] = {
25, //gpio0
0, //gpio1
26, //gpio2
0, //gpio3
24, //gpio4
0, //gpio5
0, //gpio6
0, //gpio7
0, //gpio8
0, //gpio9
0, //gpio10
0, //gpio11
29, //gpio12
28, //gpio13
30, //gpio14
27, //gpio15
0, //gpio16
31, //gpio17
0, //gpio18
0, //gpio19
0, //gpio20
0, //gpio21
0, //gpio22
0, //gpio23
0, //gpio24
20, //gpio25
21, //gpio26
0, //gpio27
0, //gpio28
0, //gpio29
0, //gpio30
0, //gpio31
23, //gpio32
22, //gpio33
18, //gpio34
19, //gpio35
14, //gpio36
15, //gpio37
16, //gpio38
17 //gpio39
};
static inline void ulp_internal_data_write(size_t offset, uint16_t value)
{
if (offset >= CONFIG_ULP_COPROC_RESERVE_MEM || offset <= ulp_var_offset)
{
Serial.print("Invalid ULP offset detected, refusing write!");
Serial.print(offset);
Serial.print("-");
Serial.print(ulp_var_offset);
Serial.print("-");
Serial.println(CONFIG_ULP_COPROC_RESERVE_MEM);
return;
}
else
{
RTC_SLOW_MEM[offset] = value;
}
}
static inline uint16_t ulp_internal_data_read(size_t offset)
{
if (offset >= CONFIG_ULP_COPROC_RESERVE_MEM || offset <= ulp_var_offset)
{
Serial.print("Invalid ULP offset detected");
Serial.print(offset);
Serial.print("-");
Serial.print(ulp_var_offset);
Serial.print("-");
Serial.println(CONFIG_ULP_COPROC_RESERVE_MEM);
}
return RTC_SLOW_MEM[offset] & 0xffff;
}
void ulp_internal_start(void)
{
rtc_gpio_init(PIN);
rtc_gpio_set_direction(PIN, RTC_GPIO_MODE_OUTPUT_ONLY);
rtc_gpio_set_level(PIN, 0);
const uint32_t rtc_gpio = rtc_io_number_get(PIN);
// Define ULP program
const ulp_insn_t ulp_prog[] = {
M_LABEL(LBL_START),
I_MOVI(REGISTER_DELAY_LOOP_COUNTER, 1),
I_MOVI(REGISTER_TICKS_ON, 0),
I_ST(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_ON, ulp_alive_offset), //store 1 with 0 offset into alive
I_LD(REGISTER_TICKS_ON, REGISTER_TICKS_ON, ulp_dimm_offset), //REGISTER_TICKS_ON = RTC_DATA[0+ulp_dimm_offset]
//in total there is always 255 delay loop iterations, but in different duty cycle
I_MOVI(REGISTER_TICKS_OFF, TOTAL_TICKS_DELAY),
I_SUBR(REGISTER_TICKS_OFF, REGISTER_TICKS_OFF, REGISTER_TICKS_ON),
//on phase
I_MOVR(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_ON),
M_BL(LBL_SKIP_ON, 1), //if never on, skip on phase
I_WR_REG(RTC_GPIO_OUT_REG, rtc_gpio, rtc_gpio, HIGH), // on
M_LABEL(LBL_DELAY_ON),
I_DELAY(1), //wait 1 clock
I_SUBI(REGISTER_DELAY_LOOP_COUNTER, REGISTER_DELAY_LOOP_COUNTER, 1), // REGISTER_DELAY_LOOP_COUNTER--
M_BGE(LBL_DELAY_ON, 1), //if time left, goto start of on loop
M_LABEL(LBL_SKIP_ON),
//off phase
I_MOVR(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_OFF),
M_BL(LBL_SKIP_OFF, 1), //if never off, skip on phase
I_WR_REG(RTC_GPIO_OUT_REG, rtc_gpio, rtc_gpio, LOW), // on
M_LABEL(3),
I_DELAY(1), //wait 1 clock
I_SUBI(REGISTER_DELAY_LOOP_COUNTER, REGISTER_DELAY_LOOP_COUNTER, 1), // REGISTER_DELAY_LOOP_COUNTER--
M_BGE(3, 1), //if time left, goto start of on loop
M_LABEL(LBL_SKIP_OFF),
M_BX(LBL_START),
};
// Run ULP program
size_t size = sizeof(ulp_prog) / sizeof(ulp_insn_t);
assert(size < ulp_var_offset && "ULP_DATA_OFFSET needs to be greater or equal to the program size");
esp_err_t error = ulp_process_macros_and_load(0, ulp_prog, &size);
Serial.print("ULP bootstrap status ");
Serial.println(error);
//allow glitchless start
ulp_internal_data_write(ulp_alive_offset, 0);
error = ulp_run(0);
Serial.print("ULP start status ");
Serial.println(error);
}
static inline void ulp_pwm_set_level(uint8_t level)
{
ulp_internal_data_write(ulp_dimm_offset, level);
}
static inline void ulp_pwm_init()
{
ulp_internal_data_write(ulp_alive_offset, 0);
delay(10);
if (ulp_internal_data_read(ulp_alive_offset) == 0)
{
ulp_internal_start();
}
}
#endif

View File

@@ -9,17 +9,31 @@
; https://docs.platformio.org/page/projectconf.html
[env:esp32doit-devkit-v1]
platform = espressif32
platform = espressif32@6.3.2
board = esp32doit-devkit-v1
framework = arduino
build_flags = -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
-DPLANT0_SENSORTYPE=FREQUENCY_MOD_RESISTANCE_PROBE
-DPLANT1_SENSORTYPE=FREQUENCY_MOD_RESISTANCE_PROBE
-DPLANT2_SENSORTYPE=FREQUENCY_MOD_RESISTANCE_PROBE
-DPLANT3_SENSORTYPE=FREQUENCY_MOD_RESISTANCE_PROBE
-DPLANT4_SENSORTYPE=FREQUENCY_MOD_RESISTANCE_PROBE
-DPLANT5_SENSORTYPE=FREQUENCY_MOD_RESISTANCE_PROBE
-DPLANT6_SENSORTYPE=FREQUENCY_MOD_RESISTANCE_PROBE
-DTIMED_LIGHT_PIN=CUSTOM1_PIN5
-DFLOWMETER_PIN=CUSTOM1_PIN1
board_build.partitions = defaultWithSmallerSpiffs.csv
;#https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html
; the latest development brankitchen-lightch (convention V3.0.x)
lib_deps = ArduinoJson@6.16.1
https://github.com/homieiot/homie-esp8266.git#v3.0
OneWire
DallasTemperature
lib_deps = bblanchon/ArduinoJson@^6.20.1
paulstoffregen/OneWire@^2.3.6
milesburton/DallasTemperature@^3.11.0
pololu/VL53L0X@^1.3.1
https://github.com/homieiot/homie-esp8266.git#develop
[platformio]

File diff suppressed because it is too large Load Diff

6
esp32/src/CMakeLists.txt Normal file
View File

@@ -0,0 +1,6 @@
# This file was automatically generated for projects
# without default 'CMakeLists.txt' file.
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
idf_component_register(SRCS ${app_sources})

View File

@@ -24,9 +24,10 @@
#define DS2438MODEL 0x26
DS2438::DS2438(OneWire *ow, float currentShunt = 1.0f) {
DS2438::DS2438(OneWire *ow, float currentShunt, int retryOnCRCError) {
_ow = ow;
_currentShunt = currentShunt;
_retryOnCRCError = retryOnCRCError;
};
void DS2438::begin(){
@@ -34,9 +35,9 @@ void DS2438::begin(){
_ow->reset_search();
memset(searchDeviceAddress,0, 8);
_temperature = 0;
_voltageA = 0.0;
_voltageB = 0.0;
_temperature.clear();
_voltageA.clear();
_voltageB.clear();
_error = true;
_mode = (DS2438_MODE_CHA | DS2438_MODE_CHB | DS2438_MODE_TEMPERATURE);
@@ -71,10 +72,20 @@ bool DS2438::validFamily(const uint8_t* deviceAddress) {
}
}
void DS2438::update() {
uint8_t data[9];
void DS2438::updateMultiple(){
for(int i = 0;i< DS2438_MEDIAN_COUNT; i++){
update(i==0);
if(_error){
return;
}
delay(DS2438_MEDIAN_DELAY);
}
}
void DS2438::update(bool firstIteration) {
uint8_t data[9];
_error = true;
if(!isFound()){
return;
}
@@ -92,10 +103,10 @@ void DS2438::update() {
}
if (doTemperature) {
_temperature = (double)(((((int16_t)data[2]) << 8) | (data[1] & 0x0ff)) >> 3) * 0.03125;
_temperature.add((double)(((((int16_t)data[2]) << 8) | (data[1] & 0x0ff)) >> 3) * 0.03125);
}
if (_mode & DS2438_MODE_CHA) {
_voltageA = (((data[4] << 8) & 0x00300) | (data[3] & 0x0ff)) / 100.0;
_voltageA.add((((data[4] << 8) & 0x00300) | (data[3] & 0x0ff)) / 100.0);
}
}
if (_mode & DS2438_MODE_CHB) {
@@ -112,33 +123,35 @@ void DS2438::update() {
int16_t upperByte = ((int16_t)data[2]) << 8;
int16_t lowerByte = data[1] >> 3;
int16_t fullByte = (upperByte | lowerByte);
_temperature = ((double)fullByte) * 0.03125;
_temperature.add(((double)fullByte) * 0.03125);
}
_voltageB = (((data[4] << 8) & 0x00300) | (data[3] & 0x0ff)) / 100.0;
_voltageB.add((((data[4] << 8) & 0x00300) | (data[3] & 0x0ff)) / 100.0);
}
int16_t upperByte = ((int16_t)data[6]) << 8;
int16_t lowerByte = data[5];
int16_t fullByte = (int16_t)(upperByte | lowerByte);
float fullByteb = fullByte;
_current = (fullByteb) / ((4096.0f * _currentShunt));
_current.add((fullByteb) / ((4096.0f * _currentShunt)));
if(firstIteration){
if (readPage(1, data)){
PageOne_t *pOne = (PageOne_t *) data;
_ICA = pOne->ICA;
}
if (readPage(7, data)){
PageSeven_t *pSeven = (PageSeven_t *) data;
_CCA = pSeven->CCA0 | ((int16_t) pSeven->CCA1) << 8;
_DCA = pSeven->DCA0 | ((int16_t) pSeven->DCA1) << 8;
}
}
_error = false;
if (readPage(1, data)){
PageOne_t *pOne = (PageOne_t *) data;
_ICA = pOne->ICA;
}
if (readPage(7, data)){
PageSeven_t *pSeven = (PageSeven_t *) data;
_CCA = pSeven->CCA0 | ((int16_t) pSeven->CCA1) << 8;
_DCA = pSeven->DCA0 | ((int16_t) pSeven->DCA1) << 8;
}
}
double DS2438::getTemperature() {
return _temperature;
return _temperature.getMedian();
}
float DS2438::getAh(){
@@ -160,16 +173,16 @@ long DS2438::getCCA(){
float DS2438::getVoltage(int channel) {
if (channel == DS2438_CHA) {
return _voltageA;
return _voltageA.getMedian();
} else if (channel == DS2438_CHB) {
return _voltageB;
return _voltageB.getMedian();
} else {
return 0.0;
}
}
float DS2438::getCurrent() {
return _current;
return _current.getMedian();
}
boolean DS2438::isError() {
@@ -234,22 +247,26 @@ void DS2438::writePage(int page, uint8_t *data) {
}
boolean DS2438::readPage(int page, uint8_t *data) {
//TODO if all data is 0 0 is a valid crc, but most likly not as intended
_ow->reset();
_ow->select(_address);
_ow->write(DS2438_RECALL_MEMORY_COMMAND, 0);
if ((page >= PAGE_MIN) && (page <= PAGE_MAX)) {
bool valid = false;
for(int retry = 0;retry < this->_retryOnCRCError && !valid; retry ++){
//TODO if all data is 0 0 is a valid crc, but most likly not as intended
_ow->reset();
_ow->select(_address);
_ow->write(DS2438_RECALL_MEMORY_COMMAND, 0);
if ((page >= PAGE_MIN) && (page <= PAGE_MAX)) {
_ow->write(page, 0);
} else {
return false;
}
_ow->reset();
_ow->select(_address);
_ow->write(DS2438_READ_SCRATCHPAD_COMMAND, 0);
_ow->write(page, 0);
} else {
return false;
for (int i = 0; i < 9; i++){
data[i] = _ow->read();
}
valid = _ow->crc8(data, 8) == data[8];
}
_ow->reset();
_ow->select(_address);
_ow->write(DS2438_READ_SCRATCHPAD_COMMAND, 0);
_ow->write(page, 0);
for (int i = 0; i < 9; i++){
data[i] = _ow->read();
}
return _ow->crc8(data, 8) == data[8];
return valid;
}

81
esp32/src/FileUtils.cpp Normal file
View File

@@ -0,0 +1,81 @@
#include <Homie.h>
#include "FileUtils.h"
bool deleteFile(const char *source)
{
Serial << "deleting file " << source << endl;
if (!SPIFFS.begin())
{
return false;
}
bool deleted = SPIFFS.remove(source);
if (deleted)
{
Serial << "Deleted " << source << endl;
}
else
{
Serial << "Could not delete " << source << endl;
}
return deleted;
}
void printFile(const char *source)
{
Serial << "printing file " << source << endl;
if (!SPIFFS.begin())
{
Serial << "could not start spiffs " << source << endl;
return;
}
File file = SPIFFS.open(source, FILE_READ);
if (!file)
{
Serial << "could not start open " << source << endl;
return;
}
Serial << file.readString() << endl;
Serial << "Finished printing file " << source << endl;
file.close();
}
bool doesFileExist(const char *source)
{
Serial << "checking if file exist " << source << endl;
if (!SPIFFS.begin())
{
return false;
}
bool exists = SPIFFS.exists(source);
Serial << "File " << source << (exists ? "" : " not") << " found " << endl;
return exists;
}
bool copyFile(const char *source, const char *target)
{
Serial << "copy started " << source << " -> " << target << endl;
if (!SPIFFS.begin())
{
return false;
}
File file = SPIFFS.open(source, FILE_READ);
File file2 = SPIFFS.open(target, FILE_WRITE);
Serial.flush();
if (!file)
{
Serial << "There was an error opening " << source << " for reading" << endl;
return false;
}
if (!file2)
{
Serial << "There was an error opening " << target << " for reading" << endl;
file.close();
return false;
}
file2.println(file.readString());
Serial << "copy finished " << source << " -> " << target << endl;
file.close();
file2.close();
return true;
}

98
esp32/src/MQTTUtils.cpp Normal file
View File

@@ -0,0 +1,98 @@
#include "MQTTUtils.h"
#include "FileUtils.h"
#include "LogDefines.h"
bool volatile mAliveWasRead = false;
void log(int level, String message, int statusCode)
{
String buffer;
StaticJsonDocument<200> doc;
// Read the current time
time_t now; // this is the epoch
tm tm; // the structure tm holds time information in a more convient way
doc["level"] = level;
doc["message"] = message;
doc["statusCode"] = statusCode;
time(&now);
localtime_r(&now, &tm);
if (tm.tm_year > (2021 - 1970)) { /* Only add the time, if we have at least 2021 */
doc["time"] = String(String(1900 + tm.tm_year) + "-" + String(tm.tm_mon + 1) + "-" + String(tm.tm_mday) +
" " + String(tm.tm_hour) + ":" + String(tm.tm_min) + ":" + String(tm.tm_sec));
}
serializeJson(doc, buffer);
if (mAliveWasRead)
{
getTopic(LOG_TOPIC, logTopic)
Homie.getMqttClient()
.subscribe(logTopic, 2);
Homie.getMqttClient().publish(logTopic, 2, false, buffer.c_str());
delete logTopic;
}
Serial << statusCode << "@" << level << " : " << message << endl;
Serial.flush();
}
void mqttWrite(HomieNode* target,String key, String value){
if(mAliveWasRead){
target->setProperty(key).send(value);
}
}
void mqttWrite(HomieNode* target,const char* key, String value){
if(aliveWasRead()){
target->setProperty(key).send(value);
}
}
void onMQTTMessage(char *incoming, char *payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total)
{
getTopic(TEST_TOPIC, testTopic);
if (strcmp(incoming, testTopic) == 0)
{
mAliveWasRead = true;
}
delete testTopic;
getTopic(BACKUP_TOPIC, backupTopic);
if (strcmp(incoming, backupTopic) == 0)
{
if (strcmp(payload, "true") == 0)
{
bool backupSucessful = copyFile(CONFIG_FILE, CONFIG_FILE_BACKUP);
printFile(CONFIG_FILE_BACKUP);
if (backupSucessful)
{
log(LOG_LEVEL_INFO, LOG_BACKUP_SUCCESSFUL, LOG_BACKUP_SUCCESSFUL_CODE);
}
else
{
log(LOG_LEVEL_INFO, LOG_BACKUP_FAILED, LOG_BACKUP_FAILED_CODE);
}
Homie.getMqttClient().publish(backupTopic, 2, true, "false");
}
}
delete backupTopic;
}
bool aliveWasRead(){
return mAliveWasRead;
}
void startMQTTRoundtripTest(){
{
getTopic(TEST_TOPIC, testopic)
Homie.getMqttClient()
.subscribe(testopic, 2);
Homie.getMqttClient().publish(testopic, 2, false, "ping");
Homie.getMqttClient().onMessage(onMQTTMessage);
getTopic(BACKUP_TOPIC, backupTopic)
Homie.getMqttClient()
.subscribe(backupTopic, 2);
}
}

12
esp32/src/MathUtil.cpp Normal file
View File

@@ -0,0 +1,12 @@
#include "MathUtils.h"
#include <Arduino.h>
bool equalish(double x, double y)
{
return (abs(x - y) < 0.5);
}
double mapf(double x, double in_min, double in_max, double out_min, double out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

View File

@@ -9,54 +9,163 @@
*
*/
#include "PlantCtrl.h"
#include "ControllerConfiguration.h"
#include "TimeUtils.h"
#include "driver/pcnt.h"
#include "MQTTUtils.h"
Plant::Plant(int pinSensor, int pinPump, int plantId, HomieNode *plant, PlantSettings_t *setting)
Plant::Plant(int pinSensor, int pinPump, int plantId, HomieNode *plant, PlantSettings_t *setting, SENSOR_MODE mode)
{
this->mPinSensor = pinSensor;
this->mPinPump = pinPump;
this->mPlant = plant;
this->mSetting = setting;
this->mPlantId = plantId;
this->mSensorMode = mode;
}
void Plant::init(void)
{
/* Initialize Home Settings validator */
this->mSetting->pSensorDry->setDefaultValue(DEACTIVATED_PLANT);
this->mSetting->pSensorDry->setValidator([](long candidate) {
return (((candidate >= 0) && (candidate <= 4095)) || candidate == DEACTIVATED_PLANT);
});
this->mSetting->pPumpAllowedHourRangeStart->setDefaultValue(8); // start at 8:00
this->mSetting->pPumpAllowedHourRangeStart->setValidator([](long candidate) {
return ((candidate >= 0) && (candidate <= 23));
});
this->mSetting->pPumpAllowedHourRangeEnd->setDefaultValue(20); // stop pumps at 20:00
this->mSetting->pPumpAllowedHourRangeEnd->setValidator([](long candidate) {
return ((candidate >= 0) && (candidate <= 23));
});
this->mSetting->pPumpOnlyWhenLowLight->setDefaultValue(true);
this->mSetting->pPumpCooldownInHours->setDefaultValue(20); // minutes
this->mSetting->pPumpCooldownInHours->setValidator([](long candidate) {
return ((candidate >= 0) && (candidate <= 1024));
});
this->mSetting->pSensorDry->setValidator([](long candidate)
{ return (((candidate >= 0.0) && (candidate <= 100.0)) || equalish(candidate, DEACTIVATED_PLANT) || equalish(candidate, HYDROPONIC_MODE) || equalish(candidate, TIMER_ONLY)); });
this->mSetting->pPumpAllowedHourRangeStart->setDefaultValue(5); // start at 5:00 UTC or 7:00 ECST
this->mSetting->pPumpAllowedHourRangeStart->setValidator([](long candidate)
{ return ((candidate >= 0) && (candidate <= 23)); });
this->mSetting->pPumpAllowedHourRangeEnd->setDefaultValue(18); // stop pumps at 18 UTC or 20:00 ECST
this->mSetting->pPumpAllowedHourRangeEnd->setValidator([](long candidate)
{ return ((candidate >= 0) && (candidate <= 23)); });
this->mSetting->pPumpOnlyWhenLowLight->setDefaultValue(false);
this->mSetting->pPumpCooldownInSeconds->setDefaultValue(60 * 60); // 1 hour
this->mSetting->pPumpCooldownInSeconds->setValidator([](long candidate)
{ return (candidate >= 0); });
this->mSetting->pPumpDuration->setDefaultValue(30);
this->mSetting->pPumpDuration->setValidator([](long candidate)
{ return ((candidate >= 0) && (candidate <= 1000)); });
this->mSetting->pPumpMl->setDefaultValue(1000);
this->mSetting->pPumpMl->setValidator([](long candidate)
{ return ((candidate >= 0) && (candidate <= 5000)); });
this->mSetting->pPumpPowerLevel->setDefaultValue(100);
this->mSetting->pPumpPowerLevel->setValidator([](long candidate)
{ return ((candidate >= 0) && (candidate <= 100)); });
/* Initialize Hardware */
pinMode(this->mPinPump, OUTPUT);
pinMode(this->mPinSensor, ANALOG);
digitalWrite(this->mPinPump, LOW);
ledcSetup(this->mPlantId, PWM_FREQ, PWM_BITS);
ledcAttachPin(mPinPump, this->mPlantId);
ledcWrite(this->mPlantId, 0);
pinMode(this->mPinSensor, INPUT);
}
void Plant::addSenseValue(void)
void Plant::initSensors(void)
{
int raw = analogRead(this->mPinSensor);
if(raw < MOIST_SENSOR_MAX_ADC && raw > MOIST_SENSOR_MIN_ADC){
this->moistureRaw.add(raw);
} else {
int plantId = this->mPlantId;
Serial << "ignoring sensor " << plantId << " value due to being strange " << raw << endl;
switch (getSensorMode())
{
case FREQUENCY_MOD_RESISTANCE_PROBE:
{
pcnt_unit_t unit = (pcnt_unit_t)(PCNT_UNIT_0 + this->mPlantId);
pcnt_config_t pcnt_config = {}; // Instancia PCNT config
pcnt_config.pulse_gpio_num = this->mPinSensor; // Configura GPIO para entrada dos pulsos
pcnt_config.ctrl_gpio_num = PCNT_PIN_NOT_USED; // Configura GPIO para controle da contagem
pcnt_config.unit = unit; // Unidade de contagem PCNT - 0
pcnt_config.channel = PCNT_CHANNEL_0; // Canal de contagem PCNT - 0
pcnt_config.counter_h_lim = INT16_MAX; // Limite maximo de contagem - 20000
pcnt_config.pos_mode = PCNT_COUNT_INC; // Incrementa contagem na subida do pulso
pcnt_config.neg_mode = PCNT_COUNT_DIS; // Incrementa contagem na descida do pulso
pcnt_config.lctrl_mode = PCNT_MODE_KEEP; // PCNT - modo lctrl desabilitado
pcnt_config.hctrl_mode = PCNT_MODE_KEEP; // PCNT - modo hctrl - se HIGH conta incrementando
pcnt_unit_config(&pcnt_config); // Configura o contador PCNT
pcnt_counter_pause(unit); // Pausa o contador PCNT
pcnt_counter_clear(unit); // Zera o contador PCNT
break;
}
case ANALOG_RESISTANCE_PROBE:
{
adcAttachPin(this->mPinSensor);
break;
}
case NONE:
{
// do nothing
break;
}
}
}
void Plant::blockingMoistureMeasurement(void)
{
switch (getSensorMode())
{
case ANALOG_RESISTANCE_PROBE:
{
for (int i = 0; i < ANALOG_REREADS; i++)
{
this->mMoisture_raw.add(analogReadMilliVolts(this->mPinSensor));
delay(5);
}
break;
}
case FREQUENCY_MOD_RESISTANCE_PROBE:
case NONE:
{
// nothing to do here
break;
}
}
}
void Plant::startMoistureMeasurement(void)
{
switch (getSensorMode())
{
case FREQUENCY_MOD_RESISTANCE_PROBE:
{
pcnt_unit_t unit = (pcnt_unit_t)(PCNT_UNIT_0 + this->mPlantId);
pcnt_counter_resume(unit);
break;
}
case ANALOG_RESISTANCE_PROBE:
case NONE:
{
// do nothing here
}
}
}
void Plant::stopMoistureMeasurement(void)
{
switch (getSensorMode())
{
case FREQUENCY_MOD_RESISTANCE_PROBE:
{
int16_t pulses;
pcnt_unit_t unit = (pcnt_unit_t)(PCNT_UNIT_0 + this->mPlantId);
pcnt_counter_pause(unit);
esp_err_t result = pcnt_get_counter_value(unit, &pulses);
pcnt_counter_clear(unit);
if (result != ESP_OK)
{
log(LOG_LEVEL_ERROR, LOG_HARDWARECOUNTER_ERROR_MESSAGE, LOG_HARDWARECOUNTER_ERROR_CODE);
this->mMoisture_raw.clear();
this->mMoisture_raw.add(-1);
}
else
{
this->mMoisture_raw.add(pulses * (1000 / MOISTURE_MEASUREMENT_DURATION));
}
break;
}
case ANALOG_RESISTANCE_PROBE:
case NONE:
{
break;
}
}
}
@@ -65,11 +174,33 @@ void Plant::postMQTTconnection(void)
const String OFF = String("OFF");
this->mConnected = true;
this->mPlant->setProperty("switch").send(OFF);
float pct = getCurrentMoisturePCT();
float raw = getCurrentMoistureRaw();
if (equalish(raw, MISSING_SENSOR))
{
pct = 0;
}
if (pct < 0)
{
pct = 0;
}
if (pct > 100)
{
pct = 100;
}
this->mPlant->setProperty("moist").send(String(pct));
this->mPlant->setProperty("sensormode").send(getSensorModeString());
this->mPlant->setProperty("moistraw").send(String(raw));
this->mPlant->setProperty("moisttrigger").send(String(getTargetMoisturePCT()));
}
void Plant::deactivatePump(void)
{
digitalWrite(this->mPinPump, LOW);
int plantId = this->mPlantId;
Serial << "deactivating pump " << plantId << endl;
ledcWrite(this->mPlantId, 0);
if (this->mConnected)
{
const String OFF = String("OFF");
@@ -77,39 +208,69 @@ void Plant::deactivatePump(void)
}
}
void Plant::activatePump(void)
void Plant::publishState(int stateNumber, String stateString)
{
digitalWrite(this->mPinPump, HIGH);
String buffer;
StaticJsonDocument<200> doc;
if (this->mConnected)
{
const String OFF = String("ON");
this->mPlant->setProperty("switch").send(OFF);
doc["number"] = stateNumber;
doc["message"] = stateString;
serializeJson(doc, buffer);
this->mPlant->setProperty("state").send(buffer.c_str());
}
}
void Plant::activatePump(void)
{
int plantId = this->mPlantId;
Serial << "activating pump " << plantId << endl;
long desiredPowerLevelPercent = this->mSetting->pPumpPowerLevel->get();
ledcWrite(this->mPlantId, desiredPowerLevelPercent * PWM_BITS);
if (this->mConnected)
{
const String ON = String("ON");
this->mPlant->setProperty("switch").send(ON);
this->mPlant->setProperty("lastPump").send(String(getCurrentTime()));
}
}
bool Plant::switchHandler(const HomieRange &range, const String &value)
{
if (range.isRange)
{
return false; // only one switch is present
}
if ((value.equals("ON")) || (value.equals("On")) || (value.equals("on")) || (value.equals("true")))
{
this->activatePump();
return true;
}
else if ((value.equals("OFF")) || (value.equals("Off")) || (value.equals("off")) || (value.equals("false")))
{
this->deactivatePump();
return true;
}
else
{
return false;
}
}
void Plant::setSwitchHandler(HomieInternals::PropertyInputHandler f)
{
this->mPump.settable(f);
}
void Plant::advertise(void)
{
// Advertise topics
this->mPlant->advertise("switch").setName("Pump 1").setDatatype("boolean");
//FIXME add .settable(this->switchHandler)
this->mPlant->advertise("moist").setName("Percent").setDatatype("number").setUnit("%");
this->mPlant->advertise("moistraw").setName("adc").setDatatype("number").setUnit("3.3/4096V");
mPump = this->mPlant->advertise("switch").setName("Pump").setDatatype("Boolean");
this->mPlant->advertise("lastPump").setName("lastPump").setDatatype("Integer").setUnit("unixtime").setRetained(true);
this->mPlant->advertise("moist").setName("Percent").setDatatype("Float").setUnit("%").setRetained(true);
this->mPlant->advertise("moistraw").setName("frequency").setDatatype("Float").setUnit("hz").setRetained(true);
this->mPlant->advertise("state").setName("state").setDatatype("String").setRetained(true);
}
/* FIXME
bool Plant::switchHandler(const HomieRange& range, const String& value) {
if (range.isRange) return false; // only one switch is present
if ((value.equals("ON")) || (value.equals("On")) || (value.equals("on")) || (value.equals("true"))) {
this->activatePump();
return true;
} else if ((value.equals("OFF")) || (value.equals("Off")) || (value.equals("off")) || (value.equals("false")) ) {
this->deactivatePump();
return true;
} else {
return false;
}
}
}
*/

18
esp32/src/TimeUtils.cpp Normal file
View File

@@ -0,0 +1,18 @@
#include "TimeUtils.h"
#include <Homie.h>
long getCurrentTime()
{
struct timeval tv_now;
gettimeofday(&tv_now, NULL);
return tv_now.tv_sec;
}
int getCurrentHour()
{
struct tm info;
time_t now;
time(&now);
localtime_r(&now, &info);
return info.tm_hour;
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,5 +3,8 @@
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

View File

@@ -15,7 +15,14 @@
"functional": "cpp",
"string": "cpp",
"typeinfo": "cpp",
"cmath": "cpp"
"cmath": "cpp",
"array": "cpp",
"deque": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"vector": "cpp",
"initializer_list": "cpp",
"regex": "cpp"
}
}
}

View File

@@ -1,112 +0,0 @@
/*
* DS2438.h
*
* by Joe Bechter
*
* (C) 2012, bechter.com
*
* All files, software, schematics and designs are provided as-is with no warranty.
* All files, software, schematics and designs are for experimental/hobby use.
* Under no circumstances should any part be used for critical systems where safety,
* life or property depends upon it. You are responsible for all use.
* You are free to use, modify, derive or otherwise extend for your own non-commercial purposes provided
* 1. No part of this software or design may be used to cause injury or death to humans or animals.
* 2. Use is non-commercial.
* 3. Credit is given to the author (i.e. portions © bechter.com), and provide a link to the original source.
*
*/
#ifndef DS2438_h
#define DS2438_h
#include <Arduino.h>
#include <OneWire.h>
#define DS2438_TEMPERATURE_CONVERSION_COMMAND 0x44
#define DS2438_VOLTAGE_CONVERSION_COMMAND 0xb4
#define DS2438_WRITE_SCRATCHPAD_COMMAND 0x4e
#define DS2438_COPY_SCRATCHPAD_COMMAND 0x48
#define DS2438_READ_SCRATCHPAD_COMMAND 0xbe
#define DS2438_RECALL_MEMORY_COMMAND 0xb8
#define PAGE_MIN 0
#define PAGE_MAX 7
#define DS2438_CHA 0
#define DS2438_CHB 1
#define DS2438_MODE_CHA 0x01
#define DS2438_MODE_CHB 0x02
#define DS2438_MODE_TEMPERATURE 0x04
#define DS2438_TEMPERATURE_DELAY 10
#define DS2438_VOLTAGE_CONVERSION_DELAY 8
#define DEFAULT_PAGE0(var) uint8_t var[8] { \
0b00001011 /* X, ADB=0, NVB=0, TB=0, AD=1, EE=0, CA=1, IAD=1 */, \
0, /* Temperatur */ \
0, /* Temperatur */ \
0, /* Voltage */ \
0, /* Voltage */ \
0, /* Current */ \
0, /* Current */ \
0 /* Threashold */ \
}
typedef struct PageOne {
uint8_t eleapsedTimerByte0; /**< LSB of timestamp */
uint8_t eleapsedTimerByte1;
uint8_t eleapsedTimerByte2;
uint8_t eleapsedTimerByte3; /**< MSB of timestamp */
uint8_t ICA; /**< Integrated Current Accumulator (current flowing into and out of the battery) */
uint8_t offsetRegisterByte0; /**< Offset for ADC calibdation */
uint8_t offsetRegisterByte1; /**< Offset for ADC calibdation */
uint8_t reserved;
} PageOne_t;
typedef struct PageSeven {
uint8_t userByte0;
uint8_t userByte1;
uint8_t userByte2;
uint8_t userByte3;
uint8_t CCA0; /**< Charging Current Accumulator (CCA) */
uint8_t CCA1; /**< Charging Current Accumulator (CCA) */
uint8_t DCA0; /**< Discharge Current Accumulator (DCA) */
uint8_t DCA1; /**< Discharge Current Accumulator (DCA) */
} PageSeven_t;
typedef uint8_t DeviceAddress[8];
class DS2438 {
public:
DS2438(OneWire *ow, float currentShunt);
DS2438(OneWire *ow, uint8_t *address);
void begin();
void update();
double getTemperature();
float getVoltage(int channel=DS2438_CHA);
float getCurrent();
boolean isError();
boolean isFound();
private:
bool validAddress(const uint8_t*);
bool validFamily(const uint8_t* deviceAddress);
bool deviceFound = false;
OneWire *_ow;
DeviceAddress _address;
uint8_t _mode;
double _temperature;
float _voltageA;
float _voltageB;
float _current;
float _currentShunt;
boolean _error;
boolean startConversion(int channel, boolean doTemperature);
boolean selectChannel(int channel);
void writePage(int page, uint8_t *data);
boolean readPage(int page, uint8_t *data);
};
#endif

View File

@@ -0,0 +1,187 @@
#ifndef ULP_PWM_h
#define ILP_PWM_h
#include <Arduino.h>
#include "driver/rtc_io.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc.h"
#include "esp32/ulp.h"
#define LBL_START 1
#define LBL_DELAY_ON 2
#define LBL_DELAY_OFF 3
#define LBL_SKIP_ON 4
#define LBL_SKIP_OFF 5
#define REGISTER_DELAY_LOOP_COUNTER R0
#define REGISTER_TICKS_ON R1
#define REGISTER_TICKS_OFF R2
#define TOTAL_TICKS_DELAY 255
#define PIN GPIO_NUM_12
//support 20 vars
const size_t ulp_var_offset = CONFIG_ULP_COPROC_RESERVE_MEM - 20;
//use the first for dimming
const size_t ulp_dimm_offset = ulp_var_offset + 1;
const size_t ulp_alive_offset = ulp_var_offset + 2;
//see https://github.com/perseus086/ESP32-notes
const uint32_t rtc_bit[40] = {
25, //gpio0
0, //gpio1
26, //gpio2
0, //gpio3
24, //gpio4
0, //gpio5
0, //gpio6
0, //gpio7
0, //gpio8
0, //gpio9
0, //gpio10
0, //gpio11
29, //gpio12
28, //gpio13
30, //gpio14
27, //gpio15
0, //gpio16
31, //gpio17
0, //gpio18
0, //gpio19
0, //gpio20
0, //gpio21
0, //gpio22
0, //gpio23
0, //gpio24
20, //gpio25
21, //gpio26
0, //gpio27
0, //gpio28
0, //gpio29
0, //gpio30
0, //gpio31
23, //gpio32
22, //gpio33
18, //gpio34
19, //gpio35
14, //gpio36
15, //gpio37
16, //gpio38
17 //gpio39
};
static inline void ulp_data_write(size_t offset, uint16_t value)
{
if (offset >= CONFIG_ULP_COPROC_RESERVE_MEM || offset <= ulp_var_offset)
{
Serial.print("Invalid ULP offset detected, refusing write!");
Serial.print(offset);
Serial.print("-");
Serial.print(ulp_var_offset);
Serial.print("-");
Serial.println(CONFIG_ULP_COPROC_RESERVE_MEM);
return;
}
else
{
RTC_SLOW_MEM[offset] = value;
}
}
static inline uint16_t ulp_data_read(size_t offset)
{
if (offset >= CONFIG_ULP_COPROC_RESERVE_MEM || offset <= ulp_var_offset)
{
Serial.print("Invalid ULP offset detected");
Serial.print(offset);
Serial.print("-");
Serial.print(ulp_var_offset);
Serial.print("-");
Serial.println(CONFIG_ULP_COPROC_RESERVE_MEM);
}
return RTC_SLOW_MEM[offset] & 0xffff;
}
static inline uint32_t rtc_io_number_get(gpio_num_t gpio_num)
{
assert(rtc_gpio_is_valid_gpio(gpio_num) && "Invalid GPIO for RTC");
uint32_t bit = rtc_bit[gpio_num];
Serial.print("Resolved GPIO ");
Serial.print(gpio_num);
Serial.print(" to rtc bit ");
Serial.println(bit);
return bit;
}
void ulp_pwm_start(void)
{
rtc_gpio_init(PIN);
rtc_gpio_set_direction(PIN, RTC_GPIO_MODE_OUTPUT_ONLY);
rtc_gpio_set_level(PIN, 0);
const uint32_t rtc_gpio = rtc_io_number_get(PIN);
// Define ULP program
const ulp_insn_t ulp_prog[] = {
M_LABEL(LBL_START),
I_MOVI(REGISTER_DELAY_LOOP_COUNTER, 1),
I_MOVI(REGISTER_TICKS_ON, 0),
I_ST(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_ON, ulp_alive_offset), //store 1 with 0 offset into alive
I_LD(REGISTER_TICKS_ON, REGISTER_TICKS_ON, ulp_dimm_offset), //REGISTER_TICKS_ON = RTC_DATA[0+ulp_dimm_offset]
//in total there is always 255 delay loop iterations, but in different duty cycle
I_MOVI(REGISTER_TICKS_OFF, TOTAL_TICKS_DELAY),
I_SUBR(REGISTER_TICKS_OFF, REGISTER_TICKS_OFF, REGISTER_TICKS_ON),
//on phase
I_MOVR(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_ON),
M_BL(LBL_SKIP_ON, 1), //if never on, skip on phase
I_WR_REG(RTC_GPIO_OUT_REG, rtc_gpio, rtc_gpio, HIGH), // on
M_LABEL(LBL_DELAY_ON),
I_DELAY(1), //wait 1 clock
I_SUBI(REGISTER_DELAY_LOOP_COUNTER, REGISTER_DELAY_LOOP_COUNTER, 1), // REGISTER_DELAY_LOOP_COUNTER--
M_BGE(LBL_DELAY_ON, 1), //if time left, goto start of on loop
M_LABEL(LBL_SKIP_ON),
//off phase
I_MOVR(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_OFF),
M_BL(LBL_SKIP_OFF, 1), //if never off, skip on phase
I_WR_REG(RTC_GPIO_OUT_REG, rtc_gpio, rtc_gpio, LOW), // on
M_LABEL(3),
I_DELAY(1), //wait 1 clock
I_SUBI(REGISTER_DELAY_LOOP_COUNTER, REGISTER_DELAY_LOOP_COUNTER, 1), // REGISTER_DELAY_LOOP_COUNTER--
M_BGE(3, 1), //if time left, goto start of on loop
M_LABEL(LBL_SKIP_OFF),
M_BX(LBL_START),
};
// Run ULP program
size_t size = sizeof(ulp_prog) / sizeof(ulp_insn_t);
assert(size < ulp_var_offset && "ULP_DATA_OFFSET needs to be greater or equal to the program size");
esp_err_t error = ulp_process_macros_and_load(0, ulp_prog, &size);
Serial.print("ULP bootstrap status ");
Serial.println(error);
//allow glitchless start
ulp_data_write(ulp_alive_offset, 0);
error = ulp_run(0);
Serial.print("ULP start status ");
Serial.println(error);
}
static inline void ulp_pwm_set_level(uint8_t level)
{
ulp_data_write(ulp_dimm_offset, level);
}
static inline void ulp_pwm_init()
{
ulp_data_write(ulp_alive_offset, 0);
delay(10);
if (ulp_data_read(ulp_alive_offset) == 0)
{
ulp_pwm_start();
}
}
#endif

View File

@@ -12,6 +12,5 @@
platform = espressif32
board = esp32doit-devkit-v1
framework = arduino
build_flags = -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
lib_deps = OneWire
DallasTemperature
lib_deps = pololu/VL53L0X@^1.3.1
build_flags = -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY -DBOOTLOADER_LOG_LEVEL_VERBOSE -DLOG_DEFAULT_LEVEL_VERBOSE -DCORE_DEBUG_LEVEL=5

View File

@@ -1,286 +0,0 @@
/*
* DS2438.cpp
*
* by Joe Bechter
*
* (C) 2012, bechter.com
*
* All files, software, schematics and designs are provided as-is with no warranty.
* All files, software, schematics and designs are for experimental/hobby use.
* Under no circumstances should any part be used for critical systems where safety,
* life or property depends upon it. You are responsible for all use.
* You are free to use, modify, derive or otherwise extend for your own non-commercial purposes provided
* 1. No part of this software or design may be used to cause injury or death to humans or animals.
* 2. Use is non-commercial.
* 3. Credit is given to the author (i.e. portions © bechter.com), and provide a link to the original source.
*
*/
#include "DS2438.h"
// DSROM FIELDS
#define DSROM_FAMILY 0
#define DSROM_CRC 7
#define DS2438MODEL 0x26
DS2438::DS2438(OneWire *ow, float currentShunt = 1.0f) {
_ow = ow;
_currentShunt = currentShunt;
};
void DS2438::begin(){
DeviceAddress searchDeviceAddress;
_ow->reset_search();
memset(searchDeviceAddress,0, 8);
_temperature = 0;
_voltageA = 0.0;
_voltageB = 0.0;
_error = true;
_mode = (DS2438_MODE_CHA | DS2438_MODE_CHB | DS2438_MODE_TEMPERATURE);
deviceFound = false; // Reset the number of devices when we enumerate wire devices
while (_ow->search(searchDeviceAddress)) {
if (validAddress(searchDeviceAddress)) {
if (validFamily(searchDeviceAddress)) {
memcpy(_address,searchDeviceAddress,8);
DEFAULT_PAGE0(defaultConfig);
writePage(0, defaultConfig);
deviceFound = true;
}
}
}
}
bool DS2438::isFound(){
return deviceFound;
}
bool DS2438::validAddress(const uint8_t* deviceAddress) {
return (_ow->crc8(deviceAddress, 7) == deviceAddress[DSROM_CRC]);
}
bool DS2438::validFamily(const uint8_t* deviceAddress) {
switch (deviceAddress[DSROM_FAMILY]) {
case DS2438MODEL:
return true;
default:
return false;
}
}
void DS2438::update() {
uint8_t data[9];
_error = true;
if(!isFound()){
return;
}
if (_mode & DS2438_MODE_CHA || _mode == DS2438_MODE_TEMPERATURE) {
boolean doTemperature = _mode & DS2438_MODE_TEMPERATURE;
if (!startConversion(DS2438_CHA, doTemperature)) {
Serial.println("Error starting temp conversion ds2438 channel a");
return;
}
if (!readPage(0, data)){
Serial.println("Error reading zero page ds2438 channel a");
return;
}
Serial.print(data[0],16);
Serial.print(" ");
Serial.print(data[1],16);
Serial.print(" ");
Serial.print(data[2],16);
Serial.print(" ");
Serial.print(data[3],16);
Serial.print(" ");
Serial.print(data[4],16);
Serial.print(" ");
Serial.print(data[5],16);
Serial.print(" ");
Serial.print(data[6],16);
Serial.print(" ");
Serial.println(data[7],16);
if (doTemperature) {
_temperature = (double)(((((int16_t)data[2]) << 8) | (data[1] & 0x0ff)) >> 3) * 0.03125;
}
if (_mode & DS2438_MODE_CHA) {
_voltageA = (((data[4] << 8) & 0x00300) | (data[3] & 0x0ff)) / 100.0;
}
}
if (_mode & DS2438_MODE_CHB) {
boolean doTemperature = _mode & DS2438_MODE_TEMPERATURE && !(_mode & DS2438_MODE_CHA);
if (!startConversion(DS2438_CHB, doTemperature)) {
Serial.println("Error starting temp conversion channel b ds2438");
return;
}
if (!readPage(0, data)){
Serial.println("Error reading zero page ds2438 channel b");
return;
}
if (doTemperature) {
int16_t upperByte = ((int16_t)data[2]) << 8;
int16_t lowerByte = data[1] >> 3;
int16_t fullByte = (upperByte | lowerByte);
_temperature = ((double)fullByte) * 0.03125;
}
_voltageB = (((data[4] << 8) & 0x00300) | (data[3] & 0x0ff)) / 100.0;
}
int16_t upperByte = ((int16_t)data[6]) << 8;
int16_t lowerByte = data[5];
int16_t fullByte = (int16_t)(upperByte | lowerByte);
float fullByteb = fullByte;
_current = (fullByteb) / ((4096.0f * _currentShunt));
_error = false;
Serial.print(data[0],16);
Serial.print(" ");
Serial.print(data[1],16);
Serial.print(" ");
Serial.print(data[2],16);
Serial.print(" ");
Serial.print(data[3],16);
Serial.print(" ");
Serial.print(data[4],16);
Serial.print(" ");
Serial.print(data[5],16);
Serial.print(" ");
Serial.print(data[6],16);
Serial.print(" ");
Serial.println(data[7],16);
Serial.println("-");
uint16_t ICA = 0;
if (readPage(1, data)){
PageOne_t *pOne = (PageOne_t *) data;
Serial.println(pOne->ICA);
float Ah = pOne->ICA / (2048.0f * _currentShunt);
Serial.print("Ah=");
Serial.println(Ah);
ICA = pOne->ICA;
}
if (readPage(7, data)){
PageSeven_t *pSeven = (PageSeven_t *) data;
int16_t CCA = pSeven->CCA0 | ((int16_t) pSeven->CCA1) << 8;
int16_t DCA = pSeven->DCA0 | ((int16_t) pSeven->DCA1) << 8;
Serial.println("ICA, DCA, CCA");
Serial.print(ICA);
Serial.print(", ");
Serial.print(DCA);
Serial.print(", ");
Serial.println(CCA);
}
}
double DS2438::getTemperature() {
return _temperature;
}
float DS2438::getVoltage(int channel) {
if (channel == DS2438_CHA) {
return _voltageA;
} else if (channel == DS2438_CHB) {
return _voltageB;
} else {
return 0.0;
}
}
float DS2438::getCurrent() {
return _current;
}
boolean DS2438::isError() {
return _error;
}
boolean DS2438::startConversion(int channel, boolean doTemperature) {
if(!isFound()){
return false;
}
if (!selectChannel(channel)){
return false;
}
_ow->reset();
_ow->select(_address);
if (doTemperature) {
_ow->write(DS2438_TEMPERATURE_CONVERSION_COMMAND, 0);
delay(DS2438_TEMPERATURE_DELAY);
_ow->reset();
_ow->select(_address);
}
_ow->write(DS2438_VOLTAGE_CONVERSION_COMMAND, 0);
delay(DS2438_VOLTAGE_CONVERSION_DELAY);
return true;
}
boolean DS2438::selectChannel(int channel) {
if(!isFound()){
return false;
}
uint8_t data[9];
if (readPage(0, data)) {
if (channel == DS2438_CHB){
data[0] = data[0] | 0x08;
}
else {
data[0] = data[0] & 0xf7;
}
writePage(0, data);
return true;
}
Serial.println("Could not read page zero data");
return false;
}
void DS2438::writePage(int page, uint8_t *data) {
_ow->reset();
_ow->select(_address);
_ow->write(DS2438_WRITE_SCRATCHPAD_COMMAND, 0);
if ((page >= PAGE_MIN) && (page <= PAGE_MAX)) {
_ow->write(page, 0);
} else {
return;
}
for (int i = 0; i < 8; i++){
_ow->write(data[i], 0);
}
_ow->reset();
_ow->select(_address);
_ow->write(DS2438_COPY_SCRATCHPAD_COMMAND, 0);
_ow->write(page, 0);
}
boolean DS2438::readPage(int page, uint8_t *data) {
//TODO if all data is 0 0 is a valid crc, but most likly not as intended
_ow->reset();
_ow->select(_address);
_ow->write(DS2438_RECALL_MEMORY_COMMAND, 0);
if ((page >= PAGE_MIN) && (page <= PAGE_MAX)) {
_ow->write(page, 0);
} else {
return false;
}
_ow->reset();
_ow->select(_address);
_ow->write(DS2438_READ_SCRATCHPAD_COMMAND, 0);
_ow->write(page, 0);
for (int i = 0; i < 9; i++){
data[i] = _ow->read();
}
return _ow->crc8(data, 8) == data[8];
}

View File

@@ -1,141 +1,83 @@
#include <Arduino.h>
#include "esp_sleep.h"
#include "DallasTemperature.h"
#include "DS2438.h"
#include "driver/pcnt.h"
#include <VL53L0X.h>
#define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 5 /* Time ESP32 will go to sleep (in seconds) */
#define SENSOR_DS18B20 2 /**< GPIO 2 */
#define SENSOR_TANK_SDA GPIO_NUM_16 /**< GPIO 16 - echo feedback of water sensor */
#define SENSOR_TANK_SCL GPIO_NUM_17 /**< GPIO 17 - trigger for water sensor */
#define OUTPUT_PUMP0 17 /**< GPIO 23 */
#define OUTPUT_PUMP1 05 /**< GPIO 22 */
#define OUTPUT_PUMP2 18 /**< GPIO 21 */
#define OUTPUT_PUMP3 19 /**< GPIO 19 */
#define OUTPUT_PUMP4 21 /**< GPIO 18 */
#define OUTPUT_PUMP5 22 /**< GPIO 5 */
#define OUTPUT_PUMP6 23 /**< GPIO 15 */
#define OUTPUT_SENSOR 14
#define SENSOR_PLANT 17
#define OUTPUT_SENSOR 16 /**< GPIO 16 - Enable Sensors */
#define OUTPUT_PUMP 13 /**< GPIO 13 - Enable Pumps */
VL53L0X tankSensor;
bool distanceReady = false;
#define SENSOR_PLANT0 32 /**< GPIO 32 (ADC1) */
void initializeTanksensor() {
Wire.begin(SENSOR_TANK_SDA, SENSOR_TANK_SCL, 100000UL /* 100kHz */);
tankSensor.setBus(&Wire);
delay(100);
tankSensor.setTimeout(500);
long start = millis();
while (start + 500 > millis())
{
if (tankSensor.init())
{
distanceReady = true;
break;
}
else
{
delay(20);
}
}
#define ADC_TO_VOLT(adc) ((adc) * 3.3 ) / 4095)
#define ADC_TO_VOLT_WITH_MULTI(adc, multi) (((adc) * 3.3 * (multi)) / 4095)
#define SOLAR_VOLT(adc) ADC_TO_VOLT_WITH_MULTI(adc, 4.0306) /**< 100k and 33k voltage dividor */
#define ADC_5V_TO_3V3(adc) ADC_TO_VOLT_WITH_MULTI(adc, 1.7) /**< 33k and 47k8 voltage dividor */
RTC_DATA_ATTR int bootCount = 0;
RTC_DATA_ATTR int pumpActive = 0;
int secondBootCount = 0;
OneWire oneWire(SENSOR_DS18B20);
DallasTemperature temp(&oneWire);
DS2438 battery(&oneWire,0.1f);
void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason){
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
if ((distanceReady) && (!tankSensor.timeoutOccurred()))
{
Serial.println("Sensor init done");
tankSensor.setSignalRateLimit(0.1);
// increase laser pulse periods (defaults are 14 and 10 PCLKs)
tankSensor.setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18);
tankSensor.setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14);
tankSensor.setMeasurementTimingBudget(200000);
tankSensor.startContinuous();
} else {
Serial.println("Sensor init failed");
}
}
bool whatever = true;
void setAll2Off() {
digitalWrite(OUTPUT_PUMP0, LOW);
digitalWrite(OUTPUT_PUMP1, LOW);
digitalWrite(OUTPUT_PUMP2, LOW);
digitalWrite(OUTPUT_PUMP3, LOW);
digitalWrite(OUTPUT_PUMP4, LOW);
digitalWrite(OUTPUT_PUMP5, LOW);
digitalWrite(OUTPUT_PUMP6, LOW);
digitalWrite(OUTPUT_SENSOR, LOW);
digitalWrite(OUTPUT_PUMP, LOW);
}
void setup() {
pinMode(OUTPUT_PUMP0, OUTPUT);
pinMode(OUTPUT_PUMP1, OUTPUT);
pinMode(OUTPUT_PUMP2, OUTPUT);
pinMode(OUTPUT_PUMP3, OUTPUT);
pinMode(OUTPUT_PUMP4, OUTPUT);
pinMode(OUTPUT_PUMP5, OUTPUT);
pinMode(OUTPUT_PUMP6, OUTPUT);
void setup()
{
Serial.begin(115200);
pinMode(OUTPUT_SENSOR, OUTPUT);
pinMode(OUTPUT_PUMP, OUTPUT);
pinMode(SENSOR_PLANT0, ANALOG);
setAll2Off();
Serial.begin(9600);
//Increment boot number and print it every reboot
++bootCount;
++secondBootCount;
Serial.println("Boot number: " + String(bootCount) + " " + String(secondBootCount));
//Print the wakeup reason for ESP32
print_wakeup_reason();
/* activate power pump and pump 0 */
digitalWrite(OUTPUT_SENSOR, HIGH);
Serial.println("Nodemcu ESP32 Start done");
delay(1);
temp.begin();
battery.begin();
Serial.print("Battery");
Serial.print("\t");
Serial.print("Solar");
Serial.print("\t");
Serial.print("Bat I");
Serial.print("\t");
Serial.println("Temp/10");
initializeTanksensor();
}
void loop() {
whatever = !whatever;
digitalWrite(OUTPUT_PUMP, HIGH);
delay(500);
digitalWrite(OUTPUT_PUMP6, HIGH);
for(int j=0; j < 5 && temp.getDeviceCount() == 0; j++) {
delay(10);
// Serial.println("Reset 1wire temp");
temp.begin();
}
for(int j=0; j < 5 && (0 == battery.isFound()); j++) {
delay(10);
Serial.println("Reset 1wire bat");
battery.begin();
battery.update();
if ((distanceReady) && (!tankSensor.timeoutOccurred()))
{
uint16_t distance = tankSensor.readRangeSingleMillimeters();
if (distance > 8000) {
Serial.println("Reset due invalid distance: 8 meter");
Wire.end();
delay(1000);
initializeTanksensor();
} else {
Serial.print("Distance");
Serial.println(distance);
}
} else {
Serial.println("Timeout");
tankSensor.stopContinuous();
Wire.end();
delay(100);
initializeTanksensor();
}
battery.update();
Serial.print(battery.getVoltage(0)); //use define here, solar
Serial.print("\t");
Serial.print(battery.getVoltage(1)); //use define here, battery
Serial.print("\t");
Serial.print(battery.getCurrent());
Serial.print("\t");
Serial.println(battery.getTemperature()/10);
}

BIN
hardware/sensor_mod.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
{
"board": {
"active_layer": 31,
"active_layer_preset": "All Layers",
"auto_track_width": false,
"hidden_netclasses": [],
"hidden_nets": [],
"high_contrast_mode": 0,
"net_color_mode": 1,
"opacity": {
"images": 0.6,
"pads": 1.0,
"tracks": 1.0,
"vias": 1.0,
"zones": 0.6
},
"ratsnest_display_mode": 0,
"selection_filter": {
"dimensions": true,
"footprints": true,
"graphics": true,
"keepouts": true,
"lockedItems": true,
"otherItems": true,
"pads": true,
"text": true,
"tracks": true,
"vias": true,
"zones": true
},
"visible_items": [
0,
1,
2,
3,
4,
5,
8,
9,
10,
11,
12,
13,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
32,
33,
34,
35,
36,
39,
40
],
"visible_layers": "fffffff_ffffffff",
"zone_display_mode": 0
},
"meta": {
"filename": "plantctrl-extension.kicad_prl",
"version": 3
},
"project": {
"files": []
}
}

View File

@@ -0,0 +1,495 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {
"board_outline_line_width": 0.09999999999999999,
"copper_line_width": 0.19999999999999998,
"copper_text_italic": false,
"copper_text_size_h": 1.5,
"copper_text_size_v": 1.5,
"copper_text_thickness": 0.3,
"copper_text_upright": false,
"courtyard_line_width": 0.049999999999999996,
"dimension_precision": 4,
"dimension_units": 3,
"dimensions": {
"arrow_length": 1270000,
"extension_offset": 500000,
"keep_text_aligned": true,
"suppress_zeroes": false,
"text_position": 0,
"units_format": 1
},
"fab_line_width": 0.09999999999999999,
"fab_text_italic": false,
"fab_text_size_h": 1.0,
"fab_text_size_v": 1.0,
"fab_text_thickness": 0.15,
"fab_text_upright": false,
"other_line_width": 0.15,
"other_text_italic": false,
"other_text_size_h": 1.0,
"other_text_size_v": 1.0,
"other_text_thickness": 0.15,
"other_text_upright": false,
"pads": {
"drill": 0.762,
"height": 1.524,
"width": 1.524
},
"silk_line_width": 0.15,
"silk_text_italic": false,
"silk_text_size_h": 1.0,
"silk_text_size_v": 1.0,
"silk_text_thickness": 0.15,
"silk_text_upright": false,
"zones": {
"45_degree_only": false,
"min_clearance": 0.508
}
},
"diff_pair_dimensions": [
{
"gap": 0.0,
"via_gap": 0.0,
"width": 0.0
}
],
"drc_exclusions": [],
"meta": {
"version": 2
},
"rule_severities": {
"annular_width": "error",
"clearance": "error",
"connection_width": "warning",
"copper_edge_clearance": "error",
"copper_sliver": "warning",
"courtyards_overlap": "error",
"diff_pair_gap_out_of_range": "error",
"diff_pair_uncoupled_length_too_long": "error",
"drill_out_of_range": "error",
"duplicate_footprints": "warning",
"extra_footprint": "warning",
"footprint": "error",
"footprint_type_mismatch": "error",
"hole_clearance": "error",
"hole_near_hole": "error",
"invalid_outline": "error",
"isolated_copper": "warning",
"item_on_disabled_layer": "error",
"items_not_allowed": "error",
"length_out_of_range": "error",
"lib_footprint_issues": "warning",
"lib_footprint_mismatch": "warning",
"malformed_courtyard": "error",
"microvia_drill_out_of_range": "error",
"missing_courtyard": "ignore",
"missing_footprint": "warning",
"net_conflict": "warning",
"npth_inside_courtyard": "ignore",
"padstack": "error",
"pth_inside_courtyard": "ignore",
"shorting_items": "error",
"silk_edge_clearance": "warning",
"silk_over_copper": "warning",
"silk_overlap": "warning",
"skew_out_of_range": "error",
"solder_mask_bridge": "error",
"starved_thermal": "error",
"text_height": "warning",
"text_thickness": "warning",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_dangling": "warning",
"track_width": "error",
"tracks_crossing": "error",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zones_intersect": "error"
},
"rules": {
"allow_blind_buried_vias": false,
"allow_microvias": false,
"max_error": 0.005,
"min_clearance": 0.0,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.0,
"min_hole_clearance": 0.25,
"min_hole_to_hole": 0.25,
"min_microvia_diameter": 0.19999999999999998,
"min_microvia_drill": 0.09999999999999999,
"min_resolved_spokes": 2,
"min_silk_clearance": 0.0,
"min_text_height": 0.7999999999999999,
"min_text_thickness": 0.08,
"min_through_hole_diameter": 0.3,
"min_track_width": 0.19999999999999998,
"min_via_annular_width": 0.049999999999999996,
"min_via_diameter": 0.39999999999999997,
"solder_mask_clearance": 0.0,
"solder_mask_min_width": 0.0,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 5,
"td_on_pad_in_zone": false,
"td_onpadsmd": true,
"td_onroundshapesonly": false,
"td_ontrackend": false,
"td_onviapad": true
}
],
"teardrop_parameters": [
{
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [
0.0,
0.15,
0.5,
1.0
],
"via_dimensions": [
{
"diameter": 0.0,
"drill": 0.0
}
],
"zones_allow_external_fillets": false,
"zones_use_no_outline": true
},
"layer_presets": [],
"viewports": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
]
],
"rule_severities": {
"bus_definition_conflict": "error",
"bus_entry_needed": "error",
"bus_label_syntax": "error",
"bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"extra_units": "error",
"global_label_dangling": "warning",
"hier_label_mismatch": "error",
"label_dangling": "error",
"lib_symbol_issues": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "warning",
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "warning",
"power_pin_not_driven": "error",
"similar_labels": "warning",
"unannotated": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "plantctrl-extension.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.2,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.25,
"via_diameter": 0.8,
"via_drill": 0.4,
"wire_width": 6
}
],
"meta": {
"version": 3
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": []
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"specctra_dsn": "",
"step": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"drawing": {
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"field_names": [],
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.375,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.15
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"ngspice": {
"fix_include_paths": true,
"fix_passive_vals": false,
"meta": {
"version": 0
},
"model_mode": 0,
"workbook_filename": ""
},
"page_layout_descr_file": "",
"plot_directory": "",
"spice_adjust_passive_values": false,
"spice_external_command": "spice \"%I\"",
"subpart_first_id": 65,
"subpart_id_separator": 0
},
"sheets": [
[
"e63e39d7-6ac0-4ffd-8aa3-1841a4541b55",
""
]
],
"text_variables": {}
}

File diff suppressed because it is too large Load Diff

20
rust/.cargo/config.toml Normal file
View File

@@ -0,0 +1,20 @@
[build]
target = "xtensa-esp32-espidf"
[target.xtensa-esp32-espidf]
linker = "ldproxy"
#runner = "espflash flash --monitor --partition-table partitions.csv" # Select this runner for espflash v2.x.x
# runner = espflash erase-parts otadata
runner = "espflash flash --monitor --baud 921600 --partition-table partitions.csv" # Select this runner for espflash v2.x.x
#runner = "cargo runner"
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="esp32"
# Note: this variable is not used by the pio builder (`cargo build --features pio`)
ESP_IDF_VERSION = "v5.1.1"
CHRONO_TZ_TIMEZONE_FILTER="UTC|Europe/Berlin"
CARGO_WORKSPACE_DIR = { value = "", relative = true }

8
rust/.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

8
rust/.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/plant-ctrl2.iml" filepath="$PROJECT_DIR$/.idea/plant-ctrl2.iml" />
</modules>
</component>
</project>

11
rust/.idea/plant-ctrl2.iml generated Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
rust/.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

83
rust/Cargo.toml Normal file
View File

@@ -0,0 +1,83 @@
[package]
name = "plant-ctrl2"
version = "0.1.0"
authors = ["Empire Phoenix"]
edition = "2021"
resolver = "2"
rust-version = "1.71"
[profile.release]
# Explicitly disable LTO which the Xtensa codegen backend has issues
#lto = "thin"
opt-level = "s"
strip = false
#codegen-units = 1
debug = true
overflow-checks = true
panic = "unwind"
incremental = true
[profile.dev]
# Explicitly disable LTO which the Xtensa codegen backend has issues
#lto = "thin"
opt-level = "s"
strip = false
#codegen-units = 1
debug = true
overflow-checks = true
panic = "unwind"
incremental = true
[package.metadata.cargo_runner]
# The string `$TARGET_FILE` will be replaced with the path from cargo.
command = [
"cargo",
"espflash",
"save-image",
"--chip",
"esp32",
"image.bin"
]
[package.metadata.espflash]
partition_table = "partitions.csv"
[features]
default = ["std", "embassy", "esp-idf-svc/native"]
pio = ["esp-idf-svc/pio"]
std = ["alloc", "esp-idf-svc/binstart", "esp-idf-svc/std"]
alloc = ["esp-idf-svc/alloc"]
nightly = ["esp-idf-svc/nightly"]
experimental = ["esp-idf-svc/experimental"]
embassy = ["esp-idf-svc/embassy-sync", "esp-idf-svc/critical-section", "esp-idf-svc/embassy-time-driver"]
[dependencies]
log = { version = "0.4", default-features = false }
esp-idf-svc = { version = "0.48.0", default-features = false }
serde = { version = "1.0.192", features = ["derive"] }
average = { version = "0.14.1" , features = ["std"] }
#esp32 = "0.28.0"
bit_field = "0.10.2"
ds18b20 = "0.1.1"
embedded-svc = { version = "0.27.0", features = ["experimental"] }
esp-idf-hal = "0.43.0"
esp-idf-sys = { version = "0.34.0", features = ["binstart", "native"] }
esp_idf_build = "0.1.3"
chrono = { version = "0.4.23", default-features = false , features = ["iana-time-zone" , "alloc"] }
chrono-tz = {version="0.8.0", default-features = false , features = [ "filter-by-regex" ]}
embedded-hal = "1.0.0"
one-wire-bus = "0.1.1"
anyhow = { version = "1.0.75", features = ["std", "backtrace"] }
schemars = "0.8.16"
heapless = { version = "0.8", features = ["serde"] }
serde_json = "1.0.108"
strum = { version = "0.26.1", features = ["derive"] }
once_cell = "1.19.0"
measurements = "0.11.0"
bq34z100 = "0.2.1"
[build-dependencies]
embuild = "0.31.3"
vergen = { version = "8.2.6", features = ["build", "git", "gitcl"] }

42
rust/build.rs Normal file
View File

@@ -0,0 +1,42 @@
use std::process::Command;
use vergen::EmitBuilder;
fn main() {
println!("cargo:rerun-if-changed=./src/src_webpack");
Command::new("rm")
.arg("./src/webserver/bundle.js")
.output()
.unwrap();
match Command::new("cmd").spawn() {
Ok(_) => {
println!("Assuming build on windows");
let output = Command::new("cmd")
.arg("/K")
.arg("npx")
.arg("webpack")
.current_dir("./src_webpack")
.output()
.unwrap();
println!("status: {}", output.status);
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
assert!(output.status.success());
}
Err(_) => {
println!("Assuming build on linux");
let output = Command::new("npx")
.arg("webpack")
.current_dir("./src_webpack")
.output()
.unwrap();
println!("status: {}", output.status);
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
assert!(output.status.success());
}
}
embuild::espidf::sysenv::output();
let _ = EmitBuilder::builder().all_git().all_build().emit();
}

6
rust/partitions.csv Normal file
View File

@@ -0,0 +1,6 @@
nvs, data, nvs, , 16k,
otadata, data, ota, , 8k,
phy_init, data, phy, , 4k,
ota_0, app, ota_0, , 1792K,
ota_1, app, ota_1, , 1792K,
storage, data, spiffs, , 400K,
1 nvs data nvs 16k
2 otadata data ota 8k
3 phy_init data phy 4k
4 ota_0 app ota_0 1792K
5 ota_1 app ota_1 1792K
6 storage data spiffs 400K

3
rust/rust-toolchain.toml Normal file
View File

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

8
rust/sdkconfig.defaults Normal file
View File

@@ -0,0 +1,8 @@
# Rust often needs a bit of an extra main task stack size compared to C (the default is 3K)
CONFIG_ESP_MAIN_TASK_STACK_SIZE=25000
# Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default).
# This allows to use 1 ms granuality for thread sleeps (10 ms by default).
CONFIG_FREERTOS_HZ=1000
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y

18
rust/setup.txt Normal file
View File

@@ -0,0 +1,18 @@
cargo install cargo-generate
cargo install ldproxy
cargo install espup
cargo install espflash
cargo install cargo-espflash
cargo generate esp-rs/esp-idf-template cargo
export PATH="$PATH:$HOME/.cargo/bin"
espup install
rustup toolchain link esp ~/.rustup/toolchains/esp/
cargo install ldproxy
cargo espflash save-image --chip esp32 image.bin
esptool.py --chip ESP32-C3 elf2image --output my-app.bin target/release/my-app
$ espflash save-image ESP32-C3 target/release/my-app my-app.bin

90
rust/src/config.rs Normal file
View File

@@ -0,0 +1,90 @@
use std::{fmt, str::FromStr};
use serde::{Deserialize, Serialize};
use crate::PLANT_COUNT;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Config {
pub mqtt_url: heapless::String<128>,
pub base_topic: heapless::String<64>,
pub max_consecutive_pump_count: u8,
pub tank_allow_pumping_if_sensor_error: bool,
pub tank_sensor_enabled: bool,
pub tank_useable_ml: u32,
pub tank_warn_percent: u8,
pub tank_empty_percent: u8,
pub tank_full_percent: u8,
pub night_lamp_hour_start: u8,
pub night_lamp_hour_end: u8,
pub night_lamp_only_when_dark: bool,
pub plants: [Plant; PLANT_COUNT],
}
impl Default for Config {
fn default() -> Self {
Self {
base_topic: heapless::String::from_str("plant/one").unwrap(),
mqtt_url: heapless::String::from_str("mqtt://192.168.1.1:1883").unwrap(),
tank_allow_pumping_if_sensor_error: true,
tank_sensor_enabled: true,
tank_warn_percent: 50,
night_lamp_hour_start: 21,
night_lamp_hour_end: 2,
night_lamp_only_when_dark: true,
plants: [Plant::default(); PLANT_COUNT],
max_consecutive_pump_count: 15,
tank_useable_ml: 5000,
tank_empty_percent: 0_u8,
tank_full_percent: 100_u8,
}
}
}
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)]
pub enum Mode {
OFF,
TargetMoisture,
TimerOnly,
TimerAndDeadzone,
}
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)]
pub struct Plant {
pub mode: Mode,
pub target_moisture: u8,
pub pump_time_s: u16,
pub pump_cooldown_min: u16,
pub pump_hour_start: u8,
pub pump_hour_end: u8,
pub sensor_b: bool,
pub sensor_p: bool,
}
impl Default for Plant {
fn default() -> Self {
Self {
target_moisture: 40,
pump_time_s: 60,
pump_cooldown_min: 60,
pump_hour_start: 8,
pump_hour_end: 20,
mode: Mode::OFF,
sensor_b: false,
sensor_p: false,
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct WifiConfig {
pub ssid: heapless::String<32>,
pub password: Option<heapless::String<64>>,
}
impl fmt::Display for WifiConfig {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, ****)", self.ssid)
}
}

266
rust/src/espota.rs Normal file
View File

@@ -0,0 +1,266 @@
use core::fmt;
use core::mem;
use core::ptr;
use esp_idf_sys::{
esp_ota_abort, esp_ota_begin, esp_ota_end, esp_ota_get_next_update_partition, esp_ota_handle_t,
esp_ota_mark_app_invalid_rollback_and_reboot, esp_ota_mark_app_valid_cancel_rollback,
esp_ota_set_boot_partition, esp_ota_write, esp_partition_t, esp_restart, ESP_ERR_FLASH_OP_FAIL,
ESP_ERR_FLASH_OP_TIMEOUT, ESP_ERR_INVALID_ARG, ESP_ERR_INVALID_SIZE, ESP_ERR_INVALID_STATE,
ESP_ERR_NOT_FOUND, ESP_ERR_NO_MEM, ESP_ERR_OTA_PARTITION_CONFLICT, ESP_ERR_OTA_ROLLBACK_FAILED,
ESP_ERR_OTA_ROLLBACK_INVALID_STATE, ESP_ERR_OTA_SELECT_INFO_INVALID,
ESP_ERR_OTA_VALIDATE_FAILED, ESP_FAIL, ESP_OK, OTA_SIZE_UNKNOWN,
};
pub type Result<T> = core::result::Result<T, Error>;
/// An error that can happen during ESP OTA operations.
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
}
impl Error {
pub(crate) fn from_kind(kind: ErrorKind) -> Self {
Self { kind }
}
/// Returns the kind of error as an enum, that can be matched on.
pub fn kind(&self) -> ErrorKind {
self.kind
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.kind.fmt(f)
}
}
impl std::error::Error for Error {}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[non_exhaustive]
pub enum ErrorKind {
/// No suitable partition for writing OTA update to found.
NoOtaPartition,
/// Cannot allocate memory for OTA operation.
AllocFailed,
/// Rollback enabled, but the currently running application is still pending. The currently
/// running application must confirm itself before downloading and flashing a new app.
InvalidRollbackState,
/// First byte of image contains invalid app image magic byte.
InvalidMagicByte,
/// Flash write operation timed out.
FlashTimeout,
/// Flash write operation failed.
FlashFailed,
/// OTA data partition has invalid contents.
InvalidOtaPartitionData,
/// The [`OtaUpdate`] handle was finalized before any app image was written to it.
NothingWritten,
/// OTA image is invalid (either not a valid app image, or - if secure boot is enabled - signature failed to verify.)
InvalidImage,
/// If flash encryption is enabled, this result indicates an internal error writing the final encrypted bytes to flash.
WritingEncryptedFailed,
/// The rollback failed.
RollbackFailed,
/// The rollback is not possible due to flash does not have any apps.
RollbackFailedNoApps,
}
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ErrorKind::*;
match self {
NoOtaPartition => "No suitable partition for writing OTA update to found",
AllocFailed => "Cannot allocate memory for OTA operation",
InvalidRollbackState => {
"Rollback enabled, but the currently running application is still pending"
}
InvalidMagicByte => "First byte of image contains invalid app image magic byte",
FlashTimeout => "Flash write operation timed out",
FlashFailed => "Flash write operation failed",
InvalidOtaPartitionData => "OTA data partition has invalid contents",
NothingWritten => "OtaUpdate was never written to",
InvalidImage => "OTA image is invalid",
WritingEncryptedFailed => "Internal error writing the final encrypted bytes to flash",
RollbackFailed => "The rollback failed",
RollbackFailedNoApps => {
"The rollback is not possible due to flash does not have any apps"
}
}
.fmt(f)
}
}
/// Represents an ongoing OTA update.
///
/// Dropping this object before calling [`finalize`](OtaUpdate::finalize) will abort the update.
#[derive(Debug)]
pub struct OtaUpdate {
partition: *const esp_partition_t,
ota_handle: esp_ota_handle_t,
}
impl OtaUpdate {
/// Starts an OTA update to the next OTA compatible partition.
///
/// Finds next partition round-robin, starting from the current running partition.
/// The entire partition is erased.
pub fn begin() -> Result<Self> {
let partition = unsafe { esp_ota_get_next_update_partition(ptr::null()) };
if partition.is_null() {
return Err(Error::from_kind(ErrorKind::NoOtaPartition));
}
let mut ota_handle = 0;
match unsafe { esp_ota_begin(partition, OTA_SIZE_UNKNOWN as usize, &mut ota_handle) } {
ESP_OK => Ok(()),
ESP_ERR_INVALID_ARG => panic!("Invalid partition or out_handle"),
ESP_ERR_NO_MEM => Err(Error::from_kind(ErrorKind::AllocFailed)),
ESP_ERR_OTA_PARTITION_CONFLICT => Err(Error::from_kind(ErrorKind::NoOtaPartition)),
ESP_ERR_NOT_FOUND => panic!("Partition argument not found in partition table"),
ESP_ERR_OTA_SELECT_INFO_INVALID => {
Err(Error::from_kind(ErrorKind::InvalidOtaPartitionData))
}
ESP_ERR_INVALID_SIZE => panic!("Partition doesnt fit in configured flash size"),
ESP_ERR_FLASH_OP_TIMEOUT => Err(Error::from_kind(ErrorKind::FlashTimeout)),
ESP_ERR_FLASH_OP_FAIL => Err(Error::from_kind(ErrorKind::FlashFailed)),
ESP_ERR_OTA_ROLLBACK_INVALID_STATE => {
Err(Error::from_kind(ErrorKind::InvalidRollbackState))
}
code => panic!("Unexpected esp_ota_begin return code: {}", code),
}?;
Ok(Self {
partition,
ota_handle,
})
}
/// Write app image data to partition.
///
/// This method can be called multiple times as data is received during the OTA operation.
/// Data is written sequentially to the partition.
///
/// The format of the app image can be read about in the main README and crate documentation.
pub fn write(&mut self, app_image_chunk: &[u8]) -> Result<()> {
let chunk_ptr = app_image_chunk.as_ptr() as *const _;
let chunk_len = app_image_chunk.len();
match unsafe { esp_ota_write(self.ota_handle, chunk_ptr, chunk_len) } {
ESP_OK => Ok(()),
ESP_ERR_INVALID_ARG => panic!("Invalid OTA handle"),
ESP_ERR_OTA_VALIDATE_FAILED => Err(Error::from_kind(ErrorKind::InvalidMagicByte)),
ESP_ERR_FLASH_OP_TIMEOUT => Err(Error::from_kind(ErrorKind::FlashTimeout)),
ESP_ERR_FLASH_OP_FAIL => Err(Error::from_kind(ErrorKind::FlashFailed)),
ESP_ERR_OTA_SELECT_INFO_INVALID => {
Err(Error::from_kind(ErrorKind::InvalidOtaPartitionData))
}
code => panic!("Unexpected esp_ota_write return code: {code}"),
}
}
/// Finish OTA update and validate newly written app image.
///
/// Unless you also call [`set_as_boot_partition`](CompletedOtaUpdate::set_as_boot_partition) the new app will not
/// start.
pub fn finalize(self) -> Result<CompletedOtaUpdate> {
match unsafe { esp_ota_end(self.ota_handle) } {
ESP_OK => Ok(()),
ESP_ERR_NOT_FOUND => panic!("Invalid OTA handle"),
ESP_ERR_INVALID_ARG => Err(Error::from_kind(ErrorKind::NothingWritten)),
ESP_ERR_OTA_VALIDATE_FAILED => Err(Error::from_kind(ErrorKind::InvalidImage)),
ESP_ERR_INVALID_STATE => Err(Error::from_kind(ErrorKind::WritingEncryptedFailed)),
code => panic!("Unexpected esp_ota_end return code: {code}"),
}?;
let partition = self.partition;
mem::forget(self);
Ok(CompletedOtaUpdate { partition })
}
/// Returns a raw pointer to the partition that the new app is/will be written to.
pub fn raw_partition(&self) -> *const esp_partition_t {
self.partition
}
}
impl Drop for OtaUpdate {
fn drop(&mut self) {
#[cfg(feature = "log")]
log::debug!("Aborting OTA update");
let abort_result_code = unsafe { esp_ota_abort(self.ota_handle) };
if abort_result_code != ESP_OK {
#[cfg(feature = "log")]
log::error!(
"Aborting the OTA update returned an unexpected code: {}",
abort_result_code
)
}
}
}
pub struct CompletedOtaUpdate {
partition: *const esp_partition_t,
}
impl CompletedOtaUpdate {
/// Sets the boot partition to the newly flashed OTA partition.
pub fn set_as_boot_partition(&mut self) -> Result<()> {
match unsafe { esp_ota_set_boot_partition(self.partition) } {
ESP_OK => Ok(()),
ESP_ERR_INVALID_ARG => panic!("Invalid partition sent to esp_ota_set_boot_partition"),
ESP_ERR_OTA_VALIDATE_FAILED => Err(Error::from_kind(ErrorKind::InvalidImage)),
ESP_ERR_NOT_FOUND => panic!("OTA data partition not found"),
ESP_ERR_FLASH_OP_TIMEOUT => Err(Error::from_kind(ErrorKind::FlashTimeout)),
ESP_ERR_FLASH_OP_FAIL => Err(Error::from_kind(ErrorKind::FlashFailed)),
code => panic!("Unexpected esp_ota_set_boot_partition code: {}", code),
}
}
/// Restarts the CPU. If [`set_as_boot_partition`](CompletedOtaUpdate::set_as_boot_partition) was
/// called and completed successfully, the CPU will boot into the newly written app.
///
/// After successful restart, CPU reset reason will be SW_CPU_RESET. Peripherals
/// (except for WiFi, BT, UART0, SPI1, and legacy timers) are not reset.
pub fn restart(self) -> ! {
unsafe { esp_restart() }
}
/// Returns a raw pointer to the partition that the new app was written to.
pub fn raw_partition(&self) -> *const esp_partition_t {
self.partition
}
}
/// Call this function to indicate that the running app is working well.
///
/// Should be called (at least) the first time a new app starts up after
/// being flashed.
pub fn mark_app_valid() {
match unsafe { esp_ota_mark_app_valid_cancel_rollback() } {
ESP_OK => (),
code => panic!(
"Unexpected esp_ota_mark_app_valid_cancel_rollback code: {}",
code
),
}
}
/// Call this function to roll back to the previously workable app with reboot.
///
/// If rolling back failed, it returns an error, otherwise this function never returns,
/// as the CPU is rebooting.
pub fn rollback_and_reboot() -> Result<core::convert::Infallible> {
match unsafe { esp_ota_mark_app_invalid_rollback_and_reboot() } {
ESP_FAIL => Err(Error::from_kind(ErrorKind::RollbackFailed)),
ESP_ERR_OTA_ROLLBACK_FAILED => Err(Error::from_kind(ErrorKind::RollbackFailedNoApps)),
code => panic!(
"Unexpected esp_ota_mark_app_invalid_rollback_and_reboot code: {}",
code
),
}
}

4
rust/src/lib.rs Normal file
View File

@@ -0,0 +1,4 @@
#![allow(dead_code)]
extern crate embedded_hal as hal;
pub mod sipo;

1171
rust/src/main.rs Normal file

File diff suppressed because it is too large Load Diff

1046
rust/src/plant_hal.rs Normal file

File diff suppressed because it is too large Load Diff

153
rust/src/sipo.rs Normal file
View File

@@ -0,0 +1,153 @@
//! Serial-in parallel-out shift register
use core::cell::RefCell;
use core::mem::{self, MaybeUninit};
use std::convert::Infallible;
use hal::digital::OutputPin;
trait ShiftRegisterInternal {
fn update(&self, index: usize, command: bool) -> Result<(), ()>;
}
/// Output pin of the shift register
pub struct ShiftRegisterPin<'a> {
shift_register: &'a dyn ShiftRegisterInternal,
index: usize,
}
impl<'a> ShiftRegisterPin<'a> {
fn new(shift_register: &'a dyn ShiftRegisterInternal, index: usize) -> Self {
ShiftRegisterPin {
shift_register,
index,
}
}
}
impl embedded_hal::digital::ErrorType for ShiftRegisterPin<'_> {
type Error = Infallible;
}
impl OutputPin for ShiftRegisterPin<'_> {
fn set_low(&mut self) -> Result<(), Infallible> {
self.shift_register.update(self.index, false).unwrap();
Ok(())
}
fn set_high(&mut self) -> Result<(), Infallible> {
self.shift_register.update(self.index, true).unwrap();
Ok(())
}
}
macro_rules! ShiftRegisterBuilder {
($name: ident, $size: expr) => {
/// Serial-in parallel-out shift register
pub struct $name<Pin1, Pin2, Pin3>
where
Pin1: OutputPin,
Pin2: OutputPin,
Pin3: OutputPin,
{
clock: RefCell<Pin1>,
latch: RefCell<Pin2>,
data: RefCell<Pin3>,
output_state: RefCell<[bool; $size]>,
}
impl<Pin1, Pin2, Pin3> ShiftRegisterInternal for $name<Pin1, Pin2, Pin3>
where
Pin1: OutputPin,
Pin2: OutputPin,
Pin3: OutputPin,
{
/// Sets the value of the shift register output at `index` to value `command`
fn update(&self, index: usize, command: bool) -> Result<(), ()> {
self.output_state.borrow_mut()[index] = command;
let output_state = self.output_state.borrow();
self.latch.borrow_mut().set_low().map_err(|_e| ())?;
for i in 1..=output_state.len() {
if output_state[output_state.len() - i] {
self.data.borrow_mut().set_high().map_err(|_e| ())?;
} else {
self.data.borrow_mut().set_low().map_err(|_e| ())?;
}
self.clock.borrow_mut().set_high().map_err(|_e| ())?;
self.clock.borrow_mut().set_low().map_err(|_e| ())?;
}
self.latch.borrow_mut().set_high().map_err(|_e| ())?;
Ok(())
}
}
impl<Pin1, Pin2, Pin3> $name<Pin1, Pin2, Pin3>
where
Pin1: OutputPin,
Pin2: OutputPin,
Pin3: OutputPin,
{
/// Creates a new SIPO shift register from clock, latch, and data output pins
pub fn new(clock: Pin1, latch: Pin2, data: Pin3) -> Self {
$name {
clock: RefCell::new(clock),
latch: RefCell::new(latch),
data: RefCell::new(data),
output_state: RefCell::new([false; $size]),
}
}
/// Get embedded-hal output pins to control the shift register outputs
pub fn decompose(&self) -> [ShiftRegisterPin; $size] {
// Create an uninitialized array of `MaybeUninit`. The `assume_init` is
// safe because the type we are claiming to have initialized here is a
// bunch of `MaybeUninit`s, which do not require initialization.
let mut pins: [MaybeUninit<ShiftRegisterPin>; $size] =
unsafe { MaybeUninit::uninit().assume_init() };
// Dropping a `MaybeUninit` does nothing, so if there is a panic during this loop,
// we have a memory leak, but there is no memory safety issue.
for (index, elem) in pins.iter_mut().enumerate() {
elem.write(ShiftRegisterPin::new(self, index));
}
// Everything is initialized. Transmute the array to the
// initialized type.
unsafe { mem::transmute::<_, [ShiftRegisterPin; $size]>(pins) }
}
/// Consume the shift register and return the original clock, latch, and data output pins
pub fn release(self) -> (Pin1, Pin2, Pin3) {
let Self {
clock,
latch,
data,
output_state: _,
} = self;
(clock.into_inner(), latch.into_inner(), data.into_inner())
}
}
};
}
ShiftRegisterBuilder!(ShiftRegister8, 8);
ShiftRegisterBuilder!(ShiftRegister16, 16);
ShiftRegisterBuilder!(ShiftRegister24, 24);
ShiftRegisterBuilder!(ShiftRegister32, 32);
ShiftRegisterBuilder!(ShiftRegister40, 40);
ShiftRegisterBuilder!(ShiftRegister48, 48);
ShiftRegisterBuilder!(ShiftRegister56, 56);
ShiftRegisterBuilder!(ShiftRegister64, 64);
ShiftRegisterBuilder!(ShiftRegister72, 72);
ShiftRegisterBuilder!(ShiftRegister80, 80);
ShiftRegisterBuilder!(ShiftRegister88, 88);
ShiftRegisterBuilder!(ShiftRegister96, 96);
ShiftRegisterBuilder!(ShiftRegister104, 104);
ShiftRegisterBuilder!(ShiftRegister112, 112);
ShiftRegisterBuilder!(ShiftRegister120, 120);
ShiftRegisterBuilder!(ShiftRegister128, 128);
/// 8 output serial-in parallel-out shift register
pub type ShiftRegister<Pin1, Pin2, Pin3> = ShiftRegister8<Pin1, Pin2, Pin3>;

View File

@@ -0,0 +1,94 @@
<html>
<body>
<h2>Current Firmware</h2>
<div>
<div id="firmware_buildtime">Buildtime loading</div>
<div id="firmware_githash">Build githash loading</div>
</div>
<h2>firmeware OTA v3</h2>
<form id="upload_form" method="post">
<input type="file" name="file1" id="file1"><br>
<progress id="progressBar" value="0" max="100" style="width:300px;"></progress>
<h3 id="status"></h3>
<h3 id="answer"></h3>
<p id="loaded_n_total"></p>
</form>
<h2>config</h2>
<div id="configform">
<h3>Mqtt:</h3>
<div>
<input type="text" id="mqtt_url">
MQTT Url
</div>
<div>
<input type="text" id="base_topic">
Base Topic
</div>
<h3>Tank:</h3>
<div>
<input type="checkbox" id="tank_sensor_enabled">
Enable Tank Sensor
</div>
<div>
<input type="checkbox" id="tank_allow_pumping_if_sensor_error">
Allow Pumping if Sensor Error
</div>
<div>
<input type="number" min="2" max="500000" id="tank_useable_ml">
Tank Size mL
</div>
<div>
<input type="number" min="1" max="500000" id="tank_warn_percent">
Tank Warn Percent (mapped in relation to empty and full)
</div>
<div>
<input type="number" min="0" max="100" id="tank_empty_percent">
Tank Empty Percent (% max move)
</div>
<div>
<input type="number" min="0" max="100" id="tank_full_percent">
Tank Full Percent (% max move)
</div>
<h3>Light:</h3>
<div>
Start
<select type="time" id="night_lamp_time_start">
</select>
Stop
<select type="time" id="night_lamp_time_end">
</select>
</div>
<div>
<input type="checkbox" id="night_lamp_only_when_dark">
Light only when dark
</div>
<h3>Plants:</h3>
<div>
<input type="number" min="2" max="100" id="max_consecutive_pump_count">
Max consecutive pump count:
</div>
<div id="plants"></div>
</div>
<button id="submit">Submit</button>
<div id="submit_status"></div>
<br>
<textarea id="json" cols=50 rows=10></textarea>
<script src="bundle.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,37 @@
<html>
<body>
<input type="button" id="test" value="Test">
<h2>Current Firmware</h2>
<div>
<div id="firmware_buildtime">Buildtime loading</div>
<div id="firmware_githash">Build githash loading</div>
</div>
<div>
<h2>firmeware OTA v3</h2>
<form id="upload_form" method="post">
<input type="file" name="file1" id="file1"><br>
<progress id="progressBar" value="0" max="100" style="width:300px;"></progress>
<h3 id="status"></h3>
<h3 id="answer"></h3>
<p id="loaded_n_total"></p>
</form>
</div>
<div>
<h2>WIFI</h2>
<input type="button" id="scan" value="Scan">
<br>
<label for="ssid">SSID:</label>
<input type="text" id="ssid" list="ssidlist">
<datalist id="ssidlist">
<option value="Not scanned yet">
</datalist>
<label for="ssid">Password:</label>
<input type="text" id="password">
<input type="button" id="save" value="Save & Restart">
<div id="wifistatus"></div>
<br>
</div>
<script src="bundle.js"></script>
</body>
</html>

View File

@@ -0,0 +1,260 @@
//offer ota and config mode
use std::{
str::from_utf8,
sync::{atomic::AtomicBool, Arc},
};
use crate::{espota::OtaUpdate, BOARD_ACCESS};
use core::result::Result::Ok;
use embedded_svc::http::Method;
use esp_idf_svc::http::server::{Configuration, EspHttpServer};
use heapless::String;
use serde::Serialize;
use crate::{
config::{Config, WifiConfig},
plant_hal::PlantCtrlBoardInteraction,
};
#[derive(Serialize, Debug)]
struct SSIDList<'a> {
ssids: Vec<&'a String<32>>,
}
#[derive(Serialize, Debug)]
struct VersionInfo<'a> {
git_hash: &'a str,
build_time: &'a str
}
pub fn httpd_initial(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
let mut server = shared();
server
.fn_handler("/", Method::Get, move |request| {
let mut response = request.into_ok_response()?;
response.write(include_bytes!("initial_config.html"))?;
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/wifiscan", Method::Post, move |request| {
let mut response = request.into_ok_response()?;
let mut board = BOARD_ACCESS.lock().unwrap();
match board.wifi_scan() {
Err(error) => {
response.write(format!("Error scanning wifi: {}", error).as_bytes())?;
}
Ok(scan_result) => {
let mut ssids: Vec<&String<32>> = Vec::new();
scan_result.iter().for_each(|s| ssids.push(&s.ssid));
let ssid_json = serde_json::to_string(&SSIDList { ssids })?;
println!("Sending ssid list {}", &ssid_json);
response.write(ssid_json.as_bytes())?;
}
}
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/wifisave", Method::Post, move |mut request| {
let mut buf = [0_u8; 2048];
let read = request.read(&mut buf);
if read.is_err() {
let error_text = read.unwrap_err().to_string();
println!("Could not parse wificonfig {}", error_text);
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return anyhow::Ok(());
}
let actual_data = &buf[0..read.unwrap()];
println!("raw {:?}", actual_data);
println!("Raw data {}", from_utf8(actual_data).unwrap());
let wifi_config: Result<WifiConfig, serde_json::Error> =
serde_json::from_slice(actual_data);
if wifi_config.is_err() {
let error_text = wifi_config.unwrap_err().to_string();
println!("Could not parse wificonfig {}", error_text);
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return anyhow::Ok(());
}
let mut board = BOARD_ACCESS.lock().unwrap();
board.set_wifi(&wifi_config.unwrap())?;
let mut response = request.into_status_response(202)?;
response.write("saved".as_bytes())?;
reboot_now.store(true, std::sync::atomic::Ordering::Relaxed);
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/boardtest", Method::Post, move |_| {
let mut board = BOARD_ACCESS.lock().unwrap();
board.test()?;
anyhow::Ok(())
})
.unwrap();
server
}
pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
let mut server = shared();
server
.fn_handler("/", Method::Get, move |request| {
let mut response = request.into_ok_response()?;
response.write(include_bytes!("config.html"))?;
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/get_config", Method::Get, move |request| {
let mut response = request.into_ok_response()?;
let mut board = BOARD_ACCESS.lock().unwrap();
match board.get_config() {
Ok(config) => {
let config_json = serde_json::to_string(&config)?;
response.write(config_json.as_bytes())?;
}
Err(_) => {
let config_json = serde_json::to_string(&Config::default())?;
response.write(config_json.as_bytes())?;
}
}
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/set_config", Method::Post, move |mut request| {
let mut buf = [0_u8; 3072];
let read = request.read(&mut buf);
if read.is_err() {
let error_text = read.unwrap_err().to_string();
println!("Could not parse config {}", error_text);
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return anyhow::Ok(());
}
let actual_data = &buf[0..read.unwrap()];
println!("Raw data {}", from_utf8(actual_data).unwrap());
let config: Result<Config, serde_json::Error> = serde_json::from_slice(actual_data);
if config.is_err() {
let error_text = config.unwrap_err().to_string();
println!("Could not parse config {}", error_text);
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return Ok(());
}
let mut board = BOARD_ACCESS.lock().unwrap();
board.set_config(&config.unwrap())?;
let mut response = request.into_status_response(202)?;
response.write("saved".as_bytes())?;
reboot_now.store(true, std::sync::atomic::Ordering::Relaxed);
Ok(())
})
.unwrap();
server
}
pub fn shared() -> Box<EspHttpServer<'static>> {
let server_config = Configuration {
stack_size: 8192,
..Default::default()
};
let mut server: Box<EspHttpServer<'static>> =
Box::new(EspHttpServer::new(&server_config).unwrap());
server
.fn_handler("/version", Method::Get, |request| {
let mut response = request.into_ok_response()?;
let git_hash = env!("VERGEN_GIT_DESCRIBE");
let build_time = env!("VERGEN_BUILD_TIMESTAMP");
let version_info = VersionInfo{
git_hash,
build_time,
};
let version_info_json = serde_json::to_string(&version_info)?;
response.write(version_info_json.as_bytes())?;
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/bundle.js", Method::Get, |request| {
let mut response = request.into_ok_response()?;
response.write(include_bytes!("bundle.js"))?;
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/favicon.ico", Method::Get, |request| {
let mut response = request.into_ok_response()?;
response.write(include_bytes!("favicon.ico"))?;
anyhow::Ok(())
})
.unwrap();
server
.fn_handler("/ota", Method::Post, |mut request| {
let ota = OtaUpdate::begin();
if ota.is_err() {
let error_text = ota.unwrap_err().to_string();
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return anyhow::Ok(());
}
let mut ota = ota.unwrap();
println!("start ota");
//having a larger buffer is not really faster, requires more stack and prevents the progress bar from working ;)
const BUFFER_SIZE: usize = 512;
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
let mut total_read: usize = 0;
loop {
let read = request.read(&mut buffer).unwrap();
total_read += read;
println!("received {read} bytes ota {total_read}");
let to_write = &buffer[0..read];
let write_result = ota.write(to_write);
if write_result.is_err() {
let error_text = write_result.unwrap_err().to_string();
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return Ok(());
}
println!("wrote {read} bytes ota {total_read}");
if read == 0 {
break;
}
}
println!("finish ota");
let partition = ota.raw_partition();
println!("finalizing and changing boot partition to {partition:?}");
let finalizer = ota.finalize();
if finalizer.is_err() {
let error_text = finalizer.err().unwrap().to_string();
request
.into_status_response(500)?
.write(error_text.as_bytes())?;
return Ok(());
}
let mut finalizer = finalizer.unwrap();
println!("changing boot partition");
finalizer.set_as_boot_partition().unwrap();
finalizer.restart();
})
.unwrap();
server
}

1540
rust/src_webpack/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More