Merged documents into ultrasonic sensor stuff
This commit is contained in:
commit
04bfaf4d94
31
README.md
31
README.md
@ -1,16 +1,25 @@
|
||||
# PlantCtrl
|
||||
## Documentation of Power-Modes
|
||||
https://lastminuteengineers.com/esp32-sleep-modes-power-consumption/#esp32-deep-sleep
|
||||
|
||||
The following problems shall be solved with this project:
|
||||
* Solar Powered
|
||||
* Low Powered
|
||||
* Plant monitoring
|
||||
* Plant watering
|
||||
* IoT
|
||||
|
||||
gpio 17 only out no hold
|
||||
gpio 16 only out no hold
|
||||
# Hardware
|
||||
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
|
||||
* 7 moist sensors
|
||||
* 7 pump
|
||||
* DC-DC convert (generating voltage from Lipo for pumps)
|
||||
* DS18B20 temperature sensors
|
||||
* water tank ultrasonic sensor (via HC_SR04)
|
||||
* general purpose expansion pin
|
||||
|
||||
solar charger 2A?
|
||||
https://www.aliexpress.com/item/4000238259949.html?spm=a2g0o.productlist.0.0.7e50231cCWGu0Z&algo_pvid=9ab7b0d3-5026-438b-972b-1d4a81d4dc56&algo_expid=9ab7b0d3-5026-438b-972b-1d4a81d4dc56-11&btsid=0b0a0ac215999246489888249e72a9&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_
|
||||
|
||||
MT3608 boost für pumpe
|
||||
https://www.aliexpress.com/item/32925951391.html?spm=a2g0o.productlist.0.0.39e21087nAzH9q&algo_pvid=7db0a849-62f7-4403-88e3-615ee4d99339&algo_expid=7db0a849-62f7-4403-88e3-615ee4d99339-0&btsid=0b0a0ac215999252934777876e7253&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_
|
||||
|
||||
DS18B20 one wire temp sensor
|
||||
# Software
|
||||
The firmware for the controller is stored in ***esp32*** sub directory.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,10 +4,10 @@ EELAYER END
|
||||
$Descr A4 11693 8268
|
||||
encoding utf-8
|
||||
Sheet 1 1
|
||||
Title ""
|
||||
Date ""
|
||||
Rev ""
|
||||
Comp ""
|
||||
Title "Plant Controller"
|
||||
Date "2020-11-11"
|
||||
Rev "0.3"
|
||||
Comp "C3MA"
|
||||
Comment1 ""
|
||||
Comment2 ""
|
||||
Comment3 ""
|
||||
@ -134,7 +134,7 @@ Text GLabel 1150 2050 0 50 Input ~ 0
|
||||
PLANT6_PUMP
|
||||
Text Notes 950 550 0 50 ~ 0
|
||||
Pump Control
|
||||
Text Notes 1100 5400 0 50 ~ 0
|
||||
Text Notes 2600 7500 0 50 ~ 0
|
||||
Lipo
|
||||
Text Notes 4050 2650 1 50 ~ 0
|
||||
Sensors
|
||||
@ -542,44 +542,44 @@ Wire Wire Line
|
||||
$Comp
|
||||
L Connector_Generic:Conn_01x04 J3
|
||||
U 1 1 5F837F50
|
||||
P 1750 3150
|
||||
F 0 "J3" H 1830 3142 50 0000 L CNN
|
||||
F 1 "Conn_01x04" H 1830 3051 50 0000 L CNN
|
||||
F 2 "misc_footprints:MT3608_module_SMT" H 1750 3150 50 0001 C CNN
|
||||
F 3 "~" H 1750 3150 50 0001 C CNN
|
||||
1 1750 3150
|
||||
P 1600 2750
|
||||
F 0 "J3" H 1680 2742 50 0000 L CNN
|
||||
F 1 "Conn_01x04" H 1680 2651 50 0000 L CNN
|
||||
F 2 "misc_footprints:MT3608_module_SMT" H 1600 2750 50 0001 C CNN
|
||||
F 3 "~" H 1600 2750 50 0001 C CNN
|
||||
1 1600 2750
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
Text GLabel 1550 3050 0 50 Input ~ 0
|
||||
Text GLabel 1400 2650 0 50 Input ~ 0
|
||||
PWR_PUMP_CONVERTER
|
||||
Text GLabel 1550 3150 0 50 Input ~ 0
|
||||
Text GLabel 1400 2750 0 50 Input ~ 0
|
||||
GND
|
||||
Text GLabel 1550 3250 0 50 Input ~ 0
|
||||
Text GLabel 1400 2850 0 50 Input ~ 0
|
||||
GND
|
||||
Text GLabel 1550 3350 0 50 Input ~ 0
|
||||
Text GLabel 1400 2950 0 50 Input ~ 0
|
||||
PUMP_PWR
|
||||
$Comp
|
||||
L LP38690DT-3.3:LP38690DT-3.3 U3
|
||||
U 1 1 5F84FA14
|
||||
P 1400 5300
|
||||
F 0 "U3" H 1400 5665 50 0000 C CNN
|
||||
F 1 "LP38690DT-3.3" H 1400 5574 50 0000 C CNN
|
||||
F 2 "ESP32:DPAK457P991X255-3N" H 1400 5300 50 0001 L BNN
|
||||
F 3 "IPC-7351B" H 1400 5300 50 0001 L BNN
|
||||
F 4 "Texas Instruments" H 1400 5300 50 0001 L BNN "Field4"
|
||||
F 5 "M" H 1400 5300 50 0001 L BNN "Field5"
|
||||
F 6 "2.55mm" H 1400 5300 50 0001 L BNN "Field6"
|
||||
1 1400 5300
|
||||
P 2900 7400
|
||||
F 0 "U3" H 2900 7765 50 0000 C CNN
|
||||
F 1 "LP38690DT-3.3" H 2900 7674 50 0000 C CNN
|
||||
F 2 "ESP32:DPAK457P991X255-3N" H 2900 7400 50 0001 L BNN
|
||||
F 3 "IPC-7351B" H 2900 7400 50 0001 L BNN
|
||||
F 4 "Texas Instruments" H 2900 7400 50 0001 L BNN "Field4"
|
||||
F 5 "M" H 2900 7400 50 0001 L BNN "Field5"
|
||||
F 6 "2.55mm" H 2900 7400 50 0001 L BNN "Field6"
|
||||
1 2900 7400
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
Text GLabel 2000 5400 2 50 Input ~ 0
|
||||
Text GLabel 3500 7500 2 50 Input ~ 0
|
||||
GND
|
||||
Text GLabel 2000 5200 2 50 Input ~ 0
|
||||
Text GLabel 3500 7300 2 50 Input ~ 0
|
||||
3_3V
|
||||
Text GLabel 4150 5700 0 50 Input ~ 0
|
||||
3_3V
|
||||
NoConn ~ 4200 7500
|
||||
Text GLabel 800 5200 0 50 Input ~ 0
|
||||
Text GLabel 2300 7300 0 50 Input ~ 0
|
||||
LIPO+
|
||||
Wire Wire Line
|
||||
10550 6100 10700 6100
|
||||
@ -1004,37 +1004,28 @@ CUSTOM_GPIO
|
||||
$Comp
|
||||
L Connector:Conn_01x02_Female J4
|
||||
U 1 1 5F8D742C
|
||||
P 1300 4100
|
||||
F 0 "J4" H 1328 4076 50 0000 L CNN
|
||||
F 1 "Conn_01x02_Female" H 1000 3900 50 0000 L CNN
|
||||
F 2 "misc_footprints:BatteryHolder_Keystone_1042_1x18650" H 1300 4100 50 0001 C CNN
|
||||
F 3 "~" H 1300 4100 50 0001 C CNN
|
||||
1 1300 4100
|
||||
P 3050 4300
|
||||
F 0 "J4" H 3078 4276 50 0000 L CNN
|
||||
F 1 "Conn_01x02_Female" H 2450 4100 50 0000 L CNN
|
||||
F 2 "misc_footprints:BatteryHolder_Keystone_1042_1x18650" H 3050 4300 50 0001 C CNN
|
||||
F 3 "~" H 3050 4300 50 0001 C CNN
|
||||
1 3050 4300
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
Text Notes 1200 4000 0 50 ~ 0
|
||||
Fuse for Lipo
|
||||
Text GLabel 900 4200 0 50 Input ~ 0
|
||||
GND
|
||||
Wire Wire Line
|
||||
900 4200 950 4200
|
||||
Text Notes 1300 3850 0 50 ~ 0
|
||||
Protection for Lipo
|
||||
$Comp
|
||||
L power:GND #PWR0120
|
||||
U 1 1 5F95011F
|
||||
P 950 4450
|
||||
F 0 "#PWR0120" H 950 4200 50 0001 C CNN
|
||||
F 1 "GND" H 955 4277 50 0000 C CNN
|
||||
F 2 "" H 950 4450 50 0001 C CNN
|
||||
F 3 "" H 950 4450 50 0001 C CNN
|
||||
1 950 4450
|
||||
P 2000 5200
|
||||
F 0 "#PWR0120" H 2000 4950 50 0001 C CNN
|
||||
F 1 "GND" H 2005 5027 50 0000 C CNN
|
||||
F 2 "" H 2000 5200 50 0001 C CNN
|
||||
F 3 "" H 2000 5200 50 0001 C CNN
|
||||
1 2000 5200
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
Wire Wire Line
|
||||
950 4450 950 4200
|
||||
Connection ~ 950 4200
|
||||
Wire Wire Line
|
||||
950 4200 1100 4200
|
||||
Text Notes 900 2950 0 50 ~ 0
|
||||
Text Notes 750 2550 0 50 ~ 0
|
||||
Pump Voltage Converter
|
||||
Wire Wire Line
|
||||
11050 6250 11050 6300
|
||||
@ -1049,21 +1040,21 @@ F 3 "~" H 3050 2550 50 0001 C CNN
|
||||
1 3050 2550
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
Text GLabel 2350 2750 0 50 Input ~ 0
|
||||
Text GLabel 2500 2750 0 50 Input ~ 0
|
||||
PWR_SENSORS
|
||||
Text GLabel 2350 2650 0 50 Input ~ 0
|
||||
Text GLabel 2500 2650 0 50 Input ~ 0
|
||||
TANK_TRIGGER
|
||||
Text GLabel 2350 2550 0 50 Input ~ 0
|
||||
Text GLabel 2500 2550 0 50 Input ~ 0
|
||||
TANK_ECHO
|
||||
$Comp
|
||||
L power:GND #PWR0121
|
||||
U 1 1 5F9F8100
|
||||
P 2850 2450
|
||||
F 0 "#PWR0121" H 2850 2200 50 0001 C CNN
|
||||
F 1 "GND" V 2855 2322 50 0000 R CNN
|
||||
F 2 "" H 2850 2450 50 0001 C CNN
|
||||
F 3 "" H 2850 2450 50 0001 C CNN
|
||||
1 2850 2450
|
||||
P 2500 2450
|
||||
F 0 "#PWR0121" H 2500 2200 50 0001 C CNN
|
||||
F 1 "GND" V 2505 2322 50 0000 R CNN
|
||||
F 2 "" H 2500 2450 50 0001 C CNN
|
||||
F 3 "" H 2500 2450 50 0001 C CNN
|
||||
1 2500 2450
|
||||
0 1 1 0
|
||||
$EndComp
|
||||
Wire Wire Line
|
||||
@ -1082,15 +1073,14 @@ $EndComp
|
||||
$Comp
|
||||
L power:PWR_FLAG #FLG0101
|
||||
U 1 1 5FA3662A
|
||||
P 950 4450
|
||||
F 0 "#FLG0101" H 950 4525 50 0001 C CNN
|
||||
F 1 "PWR_FLAG" V 950 4578 50 0000 L CNN
|
||||
F 2 "" H 950 4450 50 0001 C CNN
|
||||
F 3 "~" H 950 4450 50 0001 C CNN
|
||||
1 950 4450
|
||||
0 1 1 0
|
||||
P 2000 5200
|
||||
F 0 "#FLG0101" H 2000 5275 50 0001 C CNN
|
||||
F 1 "PWR_FLAG" H 1900 5350 50 0000 L CNN
|
||||
F 2 "" H 2000 5200 50 0001 C CNN
|
||||
F 3 "~" H 2000 5200 50 0001 C CNN
|
||||
1 2000 5200
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
Connection ~ 950 4450
|
||||
Text GLabel 5900 6800 2 50 Input ~ 0
|
||||
SENSORS_ENABLE
|
||||
$Comp
|
||||
@ -2391,44 +2381,31 @@ $EndComp
|
||||
Connection ~ 10700 5400
|
||||
Wire Wire Line
|
||||
10700 5400 10700 5550
|
||||
Wire Wire Line
|
||||
1000 3500 1550 3500
|
||||
Text GLabel 1550 3800 0 50 Input ~ 0
|
||||
Text GLabel 1650 3600 0 50 Input ~ 0
|
||||
SOLAR_IN
|
||||
Text GLabel 1550 3600 0 50 Input ~ 0
|
||||
GND
|
||||
Text GLabel 1550 3700 0 50 Input ~ 0
|
||||
GND
|
||||
$Comp
|
||||
L Connector_Generic:Conn_01x04 J2
|
||||
U 1 1 5F7E5709
|
||||
P 1750 3600
|
||||
F 0 "J2" H 1830 3592 50 0000 L CNN
|
||||
F 1 "Conn_01x04" H 1830 3501 50 0000 L CNN
|
||||
F 2 "Connector_PinSocket_2.54mm:PinSocket_1x04_P2.54mm_Horizontal" H 1750 3600 50 0001 C CNN
|
||||
F 3 "~" H 1750 3600 50 0001 C CNN
|
||||
1 1750 3600
|
||||
P 1850 3400
|
||||
F 0 "J2" H 1930 3392 50 0000 L CNN
|
||||
F 1 "Conn_01x04" H 1930 3301 50 0000 L CNN
|
||||
F 2 "Connector_PinSocket_2.54mm:PinSocket_1x04_P2.54mm_Horizontal" H 1850 3400 50 0001 C CNN
|
||||
F 3 "~" H 1850 3400 50 0001 C CNN
|
||||
1 1850 3400
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
Wire Wire Line
|
||||
1000 4100 1100 4100
|
||||
Connection ~ 1000 3800
|
||||
Wire Wire Line
|
||||
1000 3800 1000 3500
|
||||
$Comp
|
||||
L Device:Fuse F1
|
||||
U 1 1 5F8D8528
|
||||
P 1000 3950
|
||||
F 0 "F1" H 1060 3996 50 0000 L CNN
|
||||
F 1 "Fuse" H 1060 3905 50 0000 L CNN
|
||||
F 2 "Fuse:Fuse_Blade_ATO_directSolder" V 930 3950 50 0001 C CNN
|
||||
F 3 "~" H 1000 3950 50 0001 C CNN
|
||||
1 1000 3950
|
||||
1 0 0 -1
|
||||
P 1150 3300
|
||||
F 0 "F1" V 1200 3250 50 0000 L CNN
|
||||
F 1 "Fuse" V 1050 3200 50 0000 L CNN
|
||||
F 2 "Fuse:Fuse_Blade_ATO_directSolder" V 1080 3300 50 0001 C CNN
|
||||
F 3 "~" H 1150 3300 50 0001 C CNN
|
||||
1 1150 3300
|
||||
0 1 1 0
|
||||
$EndComp
|
||||
Wire Wire Line
|
||||
1000 3800 850 3800
|
||||
Text GLabel 850 3800 0 50 Input ~ 0
|
||||
Text GLabel 1250 3450 0 50 Input ~ 0
|
||||
LIPO+
|
||||
$Comp
|
||||
L power:GND #PWR0138
|
||||
@ -2696,29 +2673,29 @@ Text GLabel 8200 4850 1 50 Input ~ 0
|
||||
Wire Wire Line
|
||||
8200 4850 8200 4950
|
||||
Wire Wire Line
|
||||
2350 2550 2400 2550
|
||||
2500 2550 2550 2550
|
||||
Wire Wire Line
|
||||
2350 2650 2600 2650
|
||||
2500 2650 2750 2650
|
||||
$Comp
|
||||
L Device:D_Schottky D8
|
||||
U 1 1 5FA219F6
|
||||
P 2400 2300
|
||||
F 0 "D8" V 2446 2221 50 0000 R CNN
|
||||
F 1 "BAS40" V 2355 2221 50 0000 R CNN
|
||||
F 2 "Diode_SMD:D_1206_3216Metric_Pad1.42x1.75mm_HandSolder" H 2400 2300 50 0001 C CNN
|
||||
F 3 "~" H 2400 2300 50 0001 C CNN
|
||||
1 2400 2300
|
||||
P 2550 2250
|
||||
F 0 "D8" V 2596 2171 50 0000 R CNN
|
||||
F 1 "BAS40" V 2505 2171 50 0000 R CNN
|
||||
F 2 "Diode_SMD:D_1206_3216Metric_Pad1.42x1.75mm_HandSolder" H 2550 2250 50 0001 C CNN
|
||||
F 3 "~" H 2550 2250 50 0001 C CNN
|
||||
1 2550 2250
|
||||
0 1 1 0
|
||||
$EndComp
|
||||
$Comp
|
||||
L Device:D_Schottky D10
|
||||
U 1 1 5FA22159
|
||||
P 2600 2300
|
||||
F 0 "D10" V 2646 2221 50 0000 R CNN
|
||||
F 1 "BAS40" V 2555 2221 50 0000 R CNN
|
||||
F 2 "Diode_SMD:D_1206_3216Metric_Pad1.42x1.75mm_HandSolder" H 2600 2300 50 0001 C CNN
|
||||
F 3 "~" H 2600 2300 50 0001 C CNN
|
||||
1 2600 2300
|
||||
P 2750 2250
|
||||
F 0 "D10" V 2796 2171 50 0000 R CNN
|
||||
F 1 "BAS40" V 2705 2171 50 0000 R CNN
|
||||
F 2 "Diode_SMD:D_1206_3216Metric_Pad1.42x1.75mm_HandSolder" H 2750 2250 50 0001 C CNN
|
||||
F 3 "~" H 2750 2250 50 0001 C CNN
|
||||
1 2750 2250
|
||||
0 1 1 0
|
||||
$EndComp
|
||||
Wire Wire Line
|
||||
@ -2728,77 +2705,64 @@ Wire Wire Line
|
||||
Connection ~ 6200 5800
|
||||
Wire Wire Line
|
||||
6200 5800 6250 5800
|
||||
Wire Wire Line
|
||||
2850 2750 2350 2750
|
||||
$Comp
|
||||
L Device:D_Schottky D9
|
||||
U 1 1 5FA542CE
|
||||
P 2400 2950
|
||||
F 0 "D9" V 2446 2871 50 0000 R CNN
|
||||
F 1 "BAS40" V 2355 2871 50 0000 R CNN
|
||||
F 2 "Diode_SMD:D_1206_3216Metric_Pad1.42x1.75mm_HandSolder" H 2400 2950 50 0001 C CNN
|
||||
F 3 "~" H 2400 2950 50 0001 C CNN
|
||||
1 2400 2950
|
||||
P 2550 2950
|
||||
F 0 "D9" V 2596 2871 50 0000 R CNN
|
||||
F 1 "BAS40" V 2505 2871 50 0000 R CNN
|
||||
F 2 "Diode_SMD:D_1206_3216Metric_Pad1.42x1.75mm_HandSolder" H 2550 2950 50 0001 C CNN
|
||||
F 3 "~" H 2550 2950 50 0001 C CNN
|
||||
1 2550 2950
|
||||
0 1 1 0
|
||||
$EndComp
|
||||
$Comp
|
||||
L Device:D_Schottky D11
|
||||
U 1 1 5FA54A31
|
||||
P 2600 2950
|
||||
F 0 "D11" V 2646 2871 50 0000 R CNN
|
||||
F 1 "BAS40" V 2555 2871 50 0000 R CNN
|
||||
F 2 "Diode_SMD:D_1206_3216Metric_Pad1.42x1.75mm_HandSolder" H 2600 2950 50 0001 C CNN
|
||||
F 3 "~" H 2600 2950 50 0001 C CNN
|
||||
1 2600 2950
|
||||
P 2750 2950
|
||||
F 0 "D11" V 2796 2871 50 0000 R CNN
|
||||
F 1 "BAS40" V 2705 2871 50 0000 R CNN
|
||||
F 2 "Diode_SMD:D_1206_3216Metric_Pad1.42x1.75mm_HandSolder" H 2750 2950 50 0001 C CNN
|
||||
F 3 "~" H 2750 2950 50 0001 C CNN
|
||||
1 2750 2950
|
||||
0 1 1 0
|
||||
$EndComp
|
||||
Wire Wire Line
|
||||
2400 2550 2400 2800
|
||||
Connection ~ 2400 2550
|
||||
2550 2550 2550 2800
|
||||
Connection ~ 2550 2550
|
||||
Wire Wire Line
|
||||
2400 2550 2850 2550
|
||||
2550 2550 2850 2550
|
||||
Connection ~ 2750 2650
|
||||
Wire Wire Line
|
||||
2400 2450 2400 2550
|
||||
2750 2650 2850 2650
|
||||
Wire Wire Line
|
||||
2600 2450 2600 2650
|
||||
Connection ~ 2600 2650
|
||||
2750 2650 2750 2800
|
||||
Wire Wire Line
|
||||
2600 2650 2850 2650
|
||||
2550 3100 2550 3150
|
||||
Wire Wire Line
|
||||
2600 2650 2600 2800
|
||||
2550 3150 2650 3150
|
||||
Wire Wire Line
|
||||
2400 3100 2400 3150
|
||||
2750 3150 2750 3100
|
||||
Wire Wire Line
|
||||
2400 3150 2500 3150
|
||||
2550 2100 2550 2050
|
||||
Wire Wire Line
|
||||
2600 3150 2600 3100
|
||||
Wire Wire Line
|
||||
2400 2150 2400 2100
|
||||
Wire Wire Line
|
||||
2400 2100 2500 2100
|
||||
Wire Wire Line
|
||||
2600 2100 2600 2150
|
||||
2750 2050 2750 2100
|
||||
$Comp
|
||||
L power:GND #PWR0140
|
||||
U 1 1 5FAD1A1F
|
||||
P 2500 3150
|
||||
F 0 "#PWR0140" H 2500 2900 50 0001 C CNN
|
||||
F 1 "GND" V 2505 3022 50 0000 R CNN
|
||||
F 2 "" H 2500 3150 50 0001 C CNN
|
||||
F 3 "" H 2500 3150 50 0001 C CNN
|
||||
1 2500 3150
|
||||
P 2650 3150
|
||||
F 0 "#PWR0140" H 2650 2900 50 0001 C CNN
|
||||
F 1 "GND" V 2655 3022 50 0000 R CNN
|
||||
F 2 "" H 2650 3150 50 0001 C CNN
|
||||
F 3 "" H 2650 3150 50 0001 C CNN
|
||||
1 2650 3150
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
Connection ~ 2500 3150
|
||||
Connection ~ 2650 3150
|
||||
Wire Wire Line
|
||||
2500 3150 2600 3150
|
||||
Text GLabel 2500 2100 1 50 Input ~ 0
|
||||
2650 3150 2750 3150
|
||||
Text GLabel 2650 2000 1 50 Input ~ 0
|
||||
3_3V
|
||||
Wire Wire Line
|
||||
2500 2050 2500 2100
|
||||
Connection ~ 2500 2100
|
||||
Wire Wire Line
|
||||
2500 2100 2600 2100
|
||||
Text Notes 500 6650 0 105 ~ 0
|
||||
External Fix Power
|
||||
$Comp
|
||||
@ -2861,8 +2825,6 @@ Wire Wire Line
|
||||
10150 800 10200 800
|
||||
Wire Wire Line
|
||||
10200 800 10200 950
|
||||
Wire Wire Line
|
||||
10200 800 10650 800
|
||||
Wire Wire Line
|
||||
10650 800 10650 950
|
||||
Connection ~ 10200 800
|
||||
@ -2886,4 +2848,171 @@ Text GLabel 10150 800 0 50 Input ~ 0
|
||||
PWR_SENSORS
|
||||
Text GLabel 10500 2100 2 50 Input ~ 0
|
||||
PWR_SENSORS
|
||||
$Comp
|
||||
L DW01:DW01 IC1
|
||||
U 1 1 5FB36987
|
||||
P 1100 4200
|
||||
F 0 "IC1" H 1600 4467 50 0000 C CNN
|
||||
F 1 "DW01" H 1600 4376 50 0000 C CNN
|
||||
F 2 "SOT95P280X135-6N" H 1100 4200 50 0001 L BNN
|
||||
F 3 "" H 1100 4200 50 0001 L BNN
|
||||
F 4 "1.35mm" H 1100 4200 50 0001 L BNN "HEIGHT"
|
||||
F 5 "ic" H 1100 4200 50 0001 L BNN "DESCRIPTION"
|
||||
F 6 "" H 1100 4200 50 0001 L BNN "LCSC_PRICE-STOCK"
|
||||
F 7 "DW01" H 1100 4200 50 0001 L BNN "MANUFACTURER_PART_NUMBER"
|
||||
F 8 "" H 1100 4200 50 0001 L BNN "LCSC_PART_NUMBER"
|
||||
F 9 "Slkor" H 1100 4200 50 0001 L BNN "MANUFACTURER_NAME"
|
||||
1 1100 4200
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
$Comp
|
||||
L ESP32-DEVKITC-32D:SL2300 Q12
|
||||
U 1 1 5FB3ECD2
|
||||
P 1750 5250
|
||||
F 0 "Q12" V 2042 5250 50 0000 C CNN
|
||||
F 1 "SL2300" V 1951 5250 50 0000 C CNN
|
||||
F 2 "" H 1750 5250 50 0001 C CNN
|
||||
F 3 "" H 1750 5250 50 0001 C CNN
|
||||
1 1750 5250
|
||||
0 -1 -1 0
|
||||
$EndComp
|
||||
Wire Wire Line
|
||||
1550 5200 1400 5200
|
||||
Wire Wire Line
|
||||
1650 3500 1650 3400
|
||||
$Comp
|
||||
L Device:R R52
|
||||
U 1 1 5FC59C43
|
||||
P 950 4300
|
||||
F 0 "R52" V 950 4300 50 0000 C CNN
|
||||
F 1 "1K" V 800 4300 50 0000 C CNN
|
||||
F 2 "" V 880 4300 50 0001 C CNN
|
||||
F 3 "~" H 950 4300 50 0001 C CNN
|
||||
1 950 4300
|
||||
0 -1 -1 0
|
||||
$EndComp
|
||||
$Comp
|
||||
L Device:R R53
|
||||
U 1 1 5FC908BA
|
||||
P 2550 4300
|
||||
F 0 "R53" V 2343 4300 50 0000 C CNN
|
||||
F 1 "100Ohm" V 2434 4300 50 0000 C CNN
|
||||
F 2 "" V 2480 4300 50 0001 C CNN
|
||||
F 3 "~" H 2550 4300 50 0001 C CNN
|
||||
1 2550 4300
|
||||
0 1 1 0
|
||||
$EndComp
|
||||
Wire Wire Line
|
||||
1950 5200 2000 5200
|
||||
$Comp
|
||||
L Device:C C10
|
||||
U 1 1 5FCD9EFE
|
||||
P 2250 4150
|
||||
F 0 "C10" H 2365 4196 50 0000 L CNN
|
||||
F 1 "C" H 2365 4105 50 0000 L CNN
|
||||
F 2 "" H 2288 4000 50 0001 C CNN
|
||||
F 3 "~" H 2250 4150 50 0001 C CNN
|
||||
1 2250 4150
|
||||
1 0 0 -1
|
||||
$EndComp
|
||||
Text GLabel 1650 3500 0 50 Input ~ 0
|
||||
GND
|
||||
Text GLabel 2100 5200 2 50 Input ~ 0
|
||||
GND
|
||||
Text GLabel 2700 4400 0 50 Input ~ 0
|
||||
GND_BATT
|
||||
Text GLabel 1000 5200 0 50 Input ~ 0
|
||||
GND_BATT
|
||||
Connection ~ 2000 5200
|
||||
Wire Wire Line
|
||||
2000 5200 2100 5200
|
||||
Wire Wire Line
|
||||
2100 4300 2250 4300
|
||||
Text GLabel 2300 4000 2 50 Input ~ 0
|
||||
GND_BATT
|
||||
Text GLabel 800 4300 0 50 Input ~ 0
|
||||
GND
|
||||
Connection ~ 2250 4300
|
||||
Wire Wire Line
|
||||
2250 4300 2400 4300
|
||||
Wire Wire Line
|
||||
2100 4200 2100 4000
|
||||
Wire Wire Line
|
||||
2100 4000 2250 4000
|
||||
Wire Wire Line
|
||||
2250 4000 2300 4000
|
||||
Connection ~ 2250 4000
|
||||
Wire Wire Line
|
||||
2700 4300 2850 4300
|
||||
Wire Wire Line
|
||||
2700 4400 2850 4400
|
||||
Text GLabel 2800 4300 1 50 Input ~ 0
|
||||
VCC_BATT
|
||||
Text GLabel 1000 3300 0 50 Input ~ 0
|
||||
VCC_BATT
|
||||
Wire Wire Line
|
||||
1300 3300 1350 3300
|
||||
Wire Wire Line
|
||||
1350 3450 1250 3450
|
||||
Wire Wire Line
|
||||
1350 3300 1350 3450
|
||||
Connection ~ 1350 3300
|
||||
Wire Wire Line
|
||||
1350 3300 1650 3300
|
||||
Wire Wire Line
|
||||
2850 2750 2500 2750
|
||||
Wire Wire Line
|
||||
2550 2400 2550 2550
|
||||
Wire Wire Line
|
||||
2750 2400 2750 2650
|
||||
Wire Wire Line
|
||||
2850 2450 2500 2450
|
||||
Wire Wire Line
|
||||
2550 2050 2650 2050
|
||||
Wire Wire Line
|
||||
2650 2000 2650 2050
|
||||
Connection ~ 2650 2050
|
||||
Wire Wire Line
|
||||
2650 2050 2750 2050
|
||||
Text GLabel 850 4200 0 50 Input ~ 0
|
||||
LIPO_OD
|
||||
Text GLabel 1200 4850 1 50 Input ~ 0
|
||||
LIPO_OD
|
||||
Text GLabel 850 4400 0 50 Input ~ 0
|
||||
LIPO_OC
|
||||
Wire Wire Line
|
||||
850 4200 1100 4200
|
||||
Wire Wire Line
|
||||
850 4400 1100 4400
|
||||
Text GLabel 1750 5600 3 50 Input ~ 0
|
||||
LIPO_OC
|
||||
Wire Wire Line
|
||||
1750 5600 1750 5500
|
||||
$Comp
|
||||
L ESP32-DEVKITC-32D:SL2300 Q11
|
||||
U 1 1 5FB813FB
|
||||
P 1200 5150
|
||||
F 0 "Q11" V 1399 5150 50 0000 C CNN
|
||||
F 1 "SL2300" V 1490 5150 50 0000 C CNN
|
||||
F 2 "" H 1200 5150 50 0001 C CNN
|
||||
F 3 "" H 1200 5150 50 0001 C CNN
|
||||
1 1200 5150
|
||||
0 1 1 0
|
||||
$EndComp
|
||||
Wire Wire Line
|
||||
1200 4850 1200 4900
|
||||
NoConn ~ 2100 4400
|
||||
$Comp
|
||||
L power:PWR_FLAG #FLG0102
|
||||
U 1 1 6013ADBD
|
||||
P 10700 5400
|
||||
F 0 "#FLG0102" H 10700 5475 50 0001 C CNN
|
||||
F 1 "PWR_FLAG" H 10600 5550 50 0000 L CNN
|
||||
F 2 "" H 10700 5400 50 0001 C CNN
|
||||
F 3 "~" H 10700 5400 50 0001 C CNN
|
||||
1 10700 5400
|
||||
0 -1 -1 0
|
||||
$EndComp
|
||||
Wire Wire Line
|
||||
10200 800 10650 800
|
||||
$EndSCHEMATC
|
||||
|
@ -2,4 +2,5 @@
|
||||
(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 PlantCtrlESP32-rescue)(type Legacy)(uri ${KIPRJMOD}/PlantCtrlESP32-rescue.lib)(options "")(descr ""))
|
||||
(lib (name DW01)(type Legacy)(uri ${KIPRJMOD}/kicad-stuff/DW01.lib)(options "")(descr ""))
|
||||
)
|
||||
|
1
esp32/.gitignore
vendored
1
esp32/.gitignore
vendored
@ -3,3 +3,4 @@
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
||||
doc/
|
||||
|
2522
esp32/Doxyfile
Normal file
2522
esp32/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
@ -20,6 +20,23 @@ Uses ESP32MiniKit
|
||||
* Temperature
|
||||
* Custom GPIO
|
||||
|
||||
## Documentation of Power-Modes
|
||||
https://lastminuteengineers.com/esp32-sleep-modes-power-consumption/#esp32-deep-sleep
|
||||
|
||||
|
||||
gpio 17 only out no hold
|
||||
gpio 16 only out no hold
|
||||
|
||||
## Additional hardware
|
||||
solar charger 2A?
|
||||
https://www.aliexpress.com/item/4000238259949.html?spm=a2g0o.productlist.0.0.7e50231cCWGu0Z&algo_pvid=9ab7b0d3-5026-438b-972b-1d4a81d4dc56&algo_expid=9ab7b0d3-5026-438b-972b-1d4a81d4dc56-11&btsid=0b0a0ac215999246489888249e72a9&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_
|
||||
|
||||
MT3608 boost für pumpe
|
||||
https://www.aliexpress.com/item/32925951391.html?spm=a2g0o.productlist.0.0.39e21087nAzH9q&algo_pvid=7db0a849-62f7-4403-88e3-615ee4d99339&algo_expid=7db0a849-62f7-4403-88e3-615ee4d99339-0&btsid=0b0a0ac215999252934777876e7253&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_
|
||||
|
||||
DS18B20 one wire temp sensor
|
||||
|
||||
|
||||
# Features
|
||||
## Empires Wunschliste
|
||||
* Pflanze
|
||||
|
@ -12,7 +12,6 @@ For further details have a look at the Readme.md one level above.
|
||||
|
||||
# Remote Upload
|
||||
|
||||
|
||||
This script will allow you to send an OTA update to your device.
|
||||
|
||||
## Installation
|
||||
@ -22,6 +21,12 @@ Requirements are:
|
||||
|
||||
## Usage
|
||||
|
||||
See ***upload-via-mqtt.sh***
|
||||
|
||||
# Remote Upload - Backend
|
||||
|
||||
## Usage
|
||||
|
||||
```text
|
||||
usage: ota_updater.py [-h] -l BROKER_HOST -p BROKER_PORT [-u BROKER_USERNAME]
|
||||
[-d BROKER_PASSWORD] [-t BASE_TOPIC] -i DEVICE_ID
|
||||
|
27
esp32/host/upload-via-mqtt.sh
Executable file
27
esp32/host/upload-via-mqtt.sh
Executable file
@ -0,0 +1,27 @@
|
||||
#!//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
|
||||
firmwareFile=../.pio/build/esp32doit-devkit-v1/firmware.bin
|
||||
|
||||
if [ ! -f $firmwareFile ]; then
|
||||
echo "the script $0 must be started in host/ sub directory"
|
||||
exit 2
|
||||
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
|
||||
|
||||
mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/stay/alive/set" -m "0" -r
|
||||
exit 0
|
@ -6,18 +6,55 @@
|
||||
* @date 2020-05-30
|
||||
*
|
||||
* @copyright Copyright (c) 2020
|
||||
*
|
||||
* \mainpage Configuration of the controller
|
||||
* @{
|
||||
* Describe the used PINs of the controller
|
||||
*
|
||||
* @subpage Controller
|
||||
*
|
||||
* @subpage Homie
|
||||
*
|
||||
* @subpage Configuration
|
||||
*
|
||||
* There are several modes in the controller
|
||||
* \dot
|
||||
* digraph Operationmode {
|
||||
* ranksep=.75;
|
||||
* poweroff [ label="off" ];
|
||||
* mode1 [ label="Mode 1 - Sensor only", shape=box, width=2 ];
|
||||
* mode2 [ label="Mode 2 - Wifi enabled", shape=box ];
|
||||
* mode3 [ label="Mode 3 - Stay alive", shape=box ];
|
||||
* mode1 -> mode2 [ label="wakeup reason", fontsize=10 ];
|
||||
* mode1 -> mode2 [ label="Time duration", fontsize=10 ];
|
||||
* mode2 -> mode3 [ label="Over the Air Update", fontsize=10 ];
|
||||
* mode3 -> mode2 [ label="Over the Air Finished", fontsize=10 ];
|
||||
* mode3 -> mode2 [ label="Mqtt Command", fontsize=10 ];
|
||||
* mode2 -> mode3 [ label="Mqtt Command", fontsize=10 ];
|
||||
* poweroff -> mode1 [ label="deep sleep wakeup", fontsize=10 ];
|
||||
* mode1 -> poweroff [ label="enter deep sleep", fontsize=10 ];
|
||||
* mode2 -> poweroff [ label="Mqtt queue empty", fontsize=10 ];
|
||||
* }
|
||||
* \enddot
|
||||
*
|
||||
* Before entering Deep sleep the controller is configured with an wakeup time.
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
#ifndef CONTROLLER_CONFIG_H
|
||||
#define CONTROLLER_CONFIG_H
|
||||
|
||||
#define FIRMWARE_VERSION "1.0.6"
|
||||
/** \addtogroup Configuration
|
||||
* @{
|
||||
*/
|
||||
#define FIRMWARE_VERSION "1.0.9"
|
||||
|
||||
#define ADC_TO_VOLT(adc) ((adc) * 3.3 ) / 4095)
|
||||
#define ADC_TO_VOLT_WITH_MULTI(adc, multi) (((adc) * 3.3 * (multi)) / 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)
|
||||
|
||||
#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 */
|
||||
#define ADC_5V_TO_3V3(adc) ADC_TO_VOLT_WITH_MULTI(adc, 1.69) /**< 33k and 47k8 voltage dividor */
|
||||
#define MS_TO_S 1000
|
||||
|
||||
#define SENSOR_LIPO 34 /**< GPIO 34 (ADC1) */
|
||||
@ -41,16 +78,16 @@
|
||||
#define OUTPUT_SENSOR 16 /**< GPIO 16 - Enable Sensors */
|
||||
#define OUTPUT_PUMP 13 /**< GPIO 13 - Enable Pumps */
|
||||
|
||||
#define SENSOR_DS18B20 2 /**< GPIO 2 */
|
||||
#define BUTTON 0 /**< GPIO 0 */
|
||||
#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.6f /**< 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
|
||||
#define SOLAR_CHARGE_MAX_VOLTAGE 9
|
||||
#define SOLAR_CHARGE_MIN_VOLTAGE 7 /**< Sun is rising (morning detected) */
|
||||
#define SOLAR_CHARGE_MAX_VOLTAGE 9 /**< Sun is shining (noon) */
|
||||
|
||||
#define HC_SR04 /**< Ultrasonic distance sensor to measure water level */
|
||||
#define SENSOR_SR04_ECHO 17 /**< GPIO 17 - Echo */
|
||||
@ -58,7 +95,13 @@
|
||||
|
||||
#define MAX_CONFIG_SETTING_ITEMS 50 /**< Parameter, that can be configured in Homie */
|
||||
|
||||
#define PANIK_MODE_DEEPSLEEP (60*60*5U) /**< 5 hours in usecond */
|
||||
#define PANIK_MODE_DEEPSLEEP_US (PANIK_MODE_DEEPSLEEP*1000*1000)
|
||||
#define PANIK_MODE_DEEPSLEEP (60 * 60 * 5U) /**< 5 hours in usecond */
|
||||
#define PANIK_MODE_DEEPSLEEP_US (PANIK_MODE_DEEPSLEEP * 1000 * 1000)
|
||||
|
||||
#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 */
|
||||
|
||||
/* @} */
|
||||
#endif
|
@ -21,16 +21,20 @@
|
||||
|
||||
#include <OneWire.h>
|
||||
|
||||
class Ds18B20 {
|
||||
private:
|
||||
OneWire* mDs;
|
||||
class Ds18B20
|
||||
{
|
||||
private:
|
||||
OneWire *mDs;
|
||||
int foundDevices;
|
||||
public:
|
||||
Ds18B20(int pin) {
|
||||
|
||||
public:
|
||||
Ds18B20(int pin)
|
||||
{
|
||||
this->mDs = new OneWire(pin);
|
||||
}
|
||||
|
||||
~Ds18B20() {
|
||||
~Ds18B20()
|
||||
{
|
||||
delete this->mDs;
|
||||
}
|
||||
/**
|
||||
@ -47,6 +51,6 @@ class Ds18B20 {
|
||||
* @param maxTemperatures size of the given array
|
||||
* @return int amount of read temperature values
|
||||
*/
|
||||
int readAllTemperatures(float* pTemperatures, int maxTemperatures);
|
||||
int readAllTemperatures(float *pTemperatures, int maxTemperatures);
|
||||
};
|
||||
#endif
|
||||
|
@ -1,4 +1,6 @@
|
||||
/**
|
||||
/** \addtogroup Homie
|
||||
* @{
|
||||
*
|
||||
* @file HomieConfiguration.h
|
||||
* @author your name (you@domain.com)
|
||||
* @brief
|
||||
@ -7,6 +9,7 @@
|
||||
*
|
||||
* @copyright Copyright (c) 2020
|
||||
* All Settings, configurable in Homie
|
||||
*
|
||||
*/
|
||||
#ifndef HOMIE_PLANT_CONFIG_H
|
||||
#define HOMIE_PLANT_CONFIG_H
|
||||
@ -16,37 +19,52 @@
|
||||
#define MAX_PLANTS 7
|
||||
|
||||
/**
|
||||
*********************************** Attributes *******************************
|
||||
*/
|
||||
* @name Attributes
|
||||
* generated Information
|
||||
* @{
|
||||
**/
|
||||
|
||||
HomieNode plant0("plant0", "Plant 0", "Plant");
|
||||
|
||||
HomieNode plant1("plant1", "Plant 1", "Plant");
|
||||
HomieNode plant2("plant2", "Plant 2", "Plant");
|
||||
HomieNode plant3("plant3", "Plant 3", "Plant");
|
||||
HomieNode plant4("plant4", "Plant 4", "Plant");
|
||||
HomieNode plant5("plant5", "Plant 5", "Plant");
|
||||
HomieNode plant6("plant6", "Plant 6", "Plant");
|
||||
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 sensorLipo("lipo", "Battery Status", "Lipo");
|
||||
HomieNode sensorSolar("solar", "Solar Status", "Solarpanel");
|
||||
HomieNode sensorWater("water", "WaterSensor", "Water");
|
||||
HomieNode sensorTemp("temperature", "Temperature", "temperature");
|
||||
HomieNode stayAlive("stay", "alive", "alive");
|
||||
HomieNode stayAlive("stay", "alive", "alive"); /**< Necessary for Mqtt Active Command */
|
||||
|
||||
/* @} */
|
||||
|
||||
/**
|
||||
*********************************** Settings *******************************
|
||||
* @name Settings
|
||||
* General settings for the controller
|
||||
* @{
|
||||
*/
|
||||
HomieSetting<long> deepSleepTime("deepsleep", "time in milliseconds to sleep (0 deactivats it)");
|
||||
HomieSetting<long> deepSleepNightTime("nightsleep", "time in milliseconds to sleep (0 uses same setting: deepsleep at night, too)");
|
||||
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> 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<const char *> ntpServer("ntpServer", "NTP server (pool.ntp.org as default)");
|
||||
|
||||
/** Plant specific ones */
|
||||
/**
|
||||
*@}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @name Plant specific ones
|
||||
* Setting for one plant
|
||||
* @{
|
||||
**/
|
||||
|
||||
#define GENERATE_PLANT(plant, strplant) \
|
||||
HomieSetting<long> mSensorDry##plant = HomieSetting<long>("moistdry" strplant, "Plant " strplant "- Moist sensor dry threshold"); \
|
||||
@ -54,16 +72,22 @@ HomieSetting<long> waterLevelVol("waterVolume", "(ml) between minimum and maximu
|
||||
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 };
|
||||
PlantSettings_t mSetting##plant = {&mSensorDry##plant, &mPumpAllowedHourRangeStart##plant, &mPumpAllowedHourRangeEnd##plant, &mPumpOnlyWhenLowLight##plant, &mPumpCooldownInHours##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}
|
||||
*/
|
||||
|
||||
GENERATE_PLANT(0, "0");
|
||||
GENERATE_PLANT(1, "1");
|
||||
GENERATE_PLANT(2, "2");
|
||||
GENERATE_PLANT(3, "3");
|
||||
GENERATE_PLANT(4, "4");
|
||||
GENERATE_PLANT(5, "5");
|
||||
GENERATE_PLANT(6, "6");
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
GENERATE_PLANT(0, "0"); /**< Homie settings for first plant */
|
||||
GENERATE_PLANT(1, "1"); /**< Homie settings for second Plant */
|
||||
GENERATE_PLANT(2, "2"); /**< Homie settings for third plant */
|
||||
GENERATE_PLANT(3, "3"); /**< Homie settings for fourth plant */
|
||||
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 */
|
||||
#endif /* HOMIE_PLANT_CONFIG_H @} */
|
@ -14,13 +14,15 @@
|
||||
#include <Homie.h>
|
||||
|
||||
#define DEACTIVATED_PLANT 5000
|
||||
#define MISSING_SENSOR 5001
|
||||
|
||||
typedef struct PlantSettings_t {
|
||||
HomieSetting<long>* pSensorDry;
|
||||
HomieSetting<long>* pPumpAllowedHourRangeStart;
|
||||
HomieSetting<long>* pPumpAllowedHourRangeEnd;
|
||||
HomieSetting<bool>* pPumpOnlyWhenLowLight;
|
||||
HomieSetting<long>* pPumpCooldownInHours;
|
||||
typedef struct PlantSettings_t
|
||||
{
|
||||
HomieSetting<long> *pSensorDry;
|
||||
HomieSetting<long> *pPumpAllowedHourRangeStart;
|
||||
HomieSetting<long> *pPumpAllowedHourRangeEnd;
|
||||
HomieSetting<bool> *pPumpOnlyWhenLowLight;
|
||||
HomieSetting<long> *pPumpCooldownInHours;
|
||||
} PlantSettings_t;
|
||||
|
||||
#endif
|
@ -15,17 +15,18 @@
|
||||
#include "HomieTypes.h"
|
||||
#include "RunningMedian.h"
|
||||
|
||||
class Plant {
|
||||
class Plant
|
||||
{
|
||||
|
||||
private:
|
||||
RunningMedian moistureRaw = RunningMedian(5);
|
||||
HomieNode* mPlant = NULL;
|
||||
int mPinSensor=0; /**< Pin of the moist sensor */
|
||||
int mPinPump=0; /**< Pin of the pump */
|
||||
PlantSettings_t* mSetting;
|
||||
HomieNode *mPlant = NULL;
|
||||
int mPinSensor = 0; /**< Pin of the moist sensor */
|
||||
int mPinPump = 0; /**< Pin of the pump */
|
||||
bool mConnected = false;
|
||||
|
||||
public:
|
||||
PlantSettings_t *mSetting;
|
||||
/**
|
||||
* @brief Construct a new Plant object
|
||||
*
|
||||
@ -34,8 +35,8 @@ public:
|
||||
*/
|
||||
Plant(int pinSensor, int pinPump,
|
||||
int plantId,
|
||||
HomieNode* plant,
|
||||
PlantSettings_t* setting);
|
||||
HomieNode *plant,
|
||||
PlantSettings_t *setting);
|
||||
|
||||
void postMQTTconnection(void);
|
||||
|
||||
@ -47,8 +48,6 @@ public:
|
||||
*/
|
||||
void addSenseValue(void);
|
||||
|
||||
int getSensorValue() { return moistureRaw.getMedian(); }
|
||||
|
||||
void deactivatePump(void);
|
||||
|
||||
void activatePump(void);
|
||||
@ -59,16 +58,42 @@ public:
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool isPumpRequired() {
|
||||
return (this->mSetting->pSensorDry != NULL)
|
||||
&& (this->moistureRaw.getMedian() > this->mSetting->pSensorDry->get())
|
||||
&& (this->mSetting->pSensorDry->get() != DEACTIVATED_PLANT);
|
||||
bool isPumpRequired()
|
||||
{
|
||||
bool isDry = getCurrentMoisture() > getSettingsMoisture();
|
||||
bool isActive = isPumpTriggerActive();
|
||||
return isDry && isActive;
|
||||
}
|
||||
|
||||
HomieInternals::SendingPromise& setProperty(const String& property) const {
|
||||
bool isPumpTriggerActive()
|
||||
{
|
||||
return this->mSetting->pSensorDry->get() != DEACTIVATED_PLANT;
|
||||
}
|
||||
|
||||
float getCurrentMoisture()
|
||||
{
|
||||
if(moistureRaw.getCount()==0){
|
||||
return MISSING_SENSOR;
|
||||
}
|
||||
return this->moistureRaw.getMedian();
|
||||
}
|
||||
long getSettingsMoisture()
|
||||
{
|
||||
if (this->mSetting->pSensorDry != NULL)
|
||||
{
|
||||
return this->mSetting->pSensorDry->get();
|
||||
}
|
||||
else
|
||||
{
|
||||
return DEACTIVATED_PLANT;
|
||||
}
|
||||
}
|
||||
|
||||
HomieInternals::SendingPromise &setProperty(const String &property) const
|
||||
{
|
||||
return mPlant->setProperty(property);
|
||||
}
|
||||
bool switchHandler(const HomieRange& range, const String& value);
|
||||
bool switchHandler(const HomieRange &range, const String &value);
|
||||
|
||||
void init(void);
|
||||
|
||||
@ -76,16 +101,23 @@ public:
|
||||
* @brief determine, if the plant was recently casted
|
||||
* @param sinceLastActivation timestamp of last time
|
||||
*/
|
||||
bool isInCooldown(long sinceLastActivation) {
|
||||
bool isInCooldown(long sinceLastActivation)
|
||||
{
|
||||
/* if the time difference is greater than one month, we know these are initial values */
|
||||
if (sinceLastActivation > (60 * 60 * 24 * 30)) {
|
||||
if (sinceLastActivation > (60 * 60 * 24 * 30))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (this->mSetting->pPumpCooldownInHours->get() > sinceLastActivation / 3600);
|
||||
return (getCooldownInSeconds() > sinceLastActivation);
|
||||
}
|
||||
|
||||
bool isAllowedOnlyAtLowLight(void) {
|
||||
long getCooldownInSeconds(){
|
||||
return this->mSetting->pPumpCooldownInHours->get()*60*60;
|
||||
}
|
||||
|
||||
bool isAllowedOnlyAtLowLight(void)
|
||||
{
|
||||
return this->mSetting->pPumpOnlyWhenLowLight->get();
|
||||
}
|
||||
};
|
||||
|
@ -17,14 +17,12 @@
|
||||
// not tested ==> use at own risk :)
|
||||
// #define RUNNING_MEDIAN_USE_MALLOC
|
||||
|
||||
|
||||
// should at least be 5 to be practical,
|
||||
// odd sizes results in a 'real' middle element and will be a bit faster.
|
||||
// even sizes takes the average of the two middle elements as median
|
||||
#define MEDIAN_MIN_SIZE 5
|
||||
#define MEDIAN_MAX_SIZE 19
|
||||
|
||||
|
||||
class RunningMedian
|
||||
{
|
||||
public:
|
||||
@ -58,7 +56,6 @@ public:
|
||||
// returns current used elements, getCount() <= getSize()
|
||||
uint8_t getCount() { return _cnt; };
|
||||
|
||||
|
||||
protected:
|
||||
boolean _sorted;
|
||||
uint8_t _size;
|
||||
@ -66,8 +63,8 @@ protected:
|
||||
uint8_t _idx;
|
||||
|
||||
#ifdef RUNNING_MEDIAN_USE_MALLOC
|
||||
float * _ar;
|
||||
uint8_t * _p;
|
||||
float *_ar;
|
||||
uint8_t *_p;
|
||||
#else
|
||||
float _ar[MEDIAN_MAX_SIZE];
|
||||
uint8_t _p[MEDIAN_MAX_SIZE];
|
||||
|
@ -1,202 +0,0 @@
|
||||
/**
|
||||
arduino-timer - library for delaying function calls
|
||||
|
||||
Copyright (c) 2018, Michael Contreras
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _CM_ARDUINO_TIMER_H__
|
||||
#define _CM_ARDUINO_TIMER_H__
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include <Arduino.h>
|
||||
#else
|
||||
#include <WProgram.h>
|
||||
#endif
|
||||
|
||||
#ifndef TIMER_MAX_TASKS
|
||||
#define TIMER_MAX_TASKS 0x10
|
||||
#endif
|
||||
|
||||
template <
|
||||
size_t max_tasks = TIMER_MAX_TASKS, /* max allocated tasks */
|
||||
unsigned long (*time_func)() = millis, /* time function for timer */
|
||||
typename T = void * /* handler argument type */
|
||||
>
|
||||
class Timer {
|
||||
public:
|
||||
|
||||
typedef uintptr_t Task; /* public task handle */
|
||||
typedef bool (*handler_t)(T opaque); /* task handler func signature */
|
||||
|
||||
/* Calls handler with opaque as argument in delay units of time */
|
||||
Task
|
||||
in(unsigned long delay, handler_t h, T opaque = T())
|
||||
{
|
||||
return task_id(add_task(time_func(), delay, h, opaque));
|
||||
}
|
||||
|
||||
/* Calls handler with opaque as argument at time */
|
||||
Task
|
||||
at(unsigned long time, handler_t h, T opaque = T())
|
||||
{
|
||||
const unsigned long now = time_func();
|
||||
return task_id(add_task(now, time - now, h, opaque));
|
||||
}
|
||||
|
||||
/* Calls handler with opaque as argument every interval units of time */
|
||||
Task
|
||||
every(unsigned long interval, handler_t h, T opaque = T())
|
||||
{
|
||||
return task_id(add_task(time_func(), interval, h, opaque, interval));
|
||||
}
|
||||
|
||||
/* Cancel the timer task */
|
||||
void
|
||||
cancel(Task &task)
|
||||
{
|
||||
if (!task) return;
|
||||
|
||||
for (size_t i = 0; i < max_tasks; ++i) {
|
||||
struct task * const t = &tasks[i];
|
||||
|
||||
if (t->handler && (t->id ^ task) == (uintptr_t)t) {
|
||||
remove(t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
task = (Task)NULL;
|
||||
}
|
||||
|
||||
/* Ticks the timer forward - call this function in loop() */
|
||||
unsigned long
|
||||
tick()
|
||||
{
|
||||
unsigned long ticks = (unsigned long)-1;
|
||||
|
||||
for (size_t i = 0; i < max_tasks; ++i) {
|
||||
struct task * const task = &tasks[i];
|
||||
|
||||
if (task->handler) {
|
||||
const unsigned long t = time_func();
|
||||
const unsigned long duration = t - task->start;
|
||||
|
||||
if (duration >= task->expires) {
|
||||
task->repeat = task->handler(task->opaque) && task->repeat;
|
||||
|
||||
if (task->repeat) task->start = t;
|
||||
else remove(task);
|
||||
} else {
|
||||
const unsigned long remaining = task->expires - duration;
|
||||
ticks = remaining < ticks ? remaining : ticks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ticks == (unsigned long)-1 ? 0 : ticks;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
size_t ctr;
|
||||
|
||||
struct task {
|
||||
handler_t handler; /* task handler callback func */
|
||||
T opaque; /* argument given to the callback handler */
|
||||
unsigned long start,
|
||||
expires; /* when the task expires */
|
||||
size_t repeat, /* repeat task */
|
||||
id;
|
||||
} tasks[max_tasks];
|
||||
|
||||
inline
|
||||
void
|
||||
remove(struct task *task)
|
||||
{
|
||||
task->handler = NULL;
|
||||
task->opaque = T();
|
||||
task->start = 0;
|
||||
task->expires = 0;
|
||||
task->repeat = 0;
|
||||
task->id = 0;
|
||||
}
|
||||
|
||||
inline
|
||||
Task
|
||||
task_id(const struct task * const t)
|
||||
{
|
||||
const Task id = (Task)t;
|
||||
|
||||
return id ? id ^ t->id : id;
|
||||
}
|
||||
|
||||
inline
|
||||
struct task *
|
||||
next_task_slot()
|
||||
{
|
||||
for (size_t i = 0; i < max_tasks; ++i) {
|
||||
struct task * const slot = &tasks[i];
|
||||
if (slot->handler == NULL) return slot;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline
|
||||
struct task *
|
||||
add_task(unsigned long start, unsigned long expires,
|
||||
handler_t h, T opaque, bool repeat = 0)
|
||||
{
|
||||
struct task * const slot = next_task_slot();
|
||||
|
||||
if (!slot) return NULL;
|
||||
|
||||
if (++ctr == 0) ++ctr; // overflow
|
||||
|
||||
slot->id = ctr;
|
||||
slot->handler = h;
|
||||
slot->opaque = opaque;
|
||||
slot->start = start;
|
||||
slot->expires = expires;
|
||||
slot->repeat = repeat;
|
||||
|
||||
return slot;
|
||||
}
|
||||
};
|
||||
|
||||
/* create a timer with the default settings */
|
||||
inline Timer<>
|
||||
timer_create_default()
|
||||
{
|
||||
return Timer<>();
|
||||
}
|
||||
|
||||
#endif
|
@ -15,6 +15,8 @@ framework = arduino
|
||||
build_flags = -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
|
||||
board_build.partitions = defaultWithSmallerSpiffs.csv
|
||||
|
||||
upload_port=/dev/ttyUSB0
|
||||
|
||||
; the latest development brankitchen-lightch (convention V3.0.x)
|
||||
lib_deps = ArduinoJson@6.16.1
|
||||
https://github.com/homieiot/homie-esp8266.git#v3.0
|
||||
|
@ -21,36 +21,35 @@
|
||||
//Printf debugging
|
||||
//#define DS_DEBUG
|
||||
|
||||
int Ds18B20::readDevices() {
|
||||
int Ds18B20::readDevices()
|
||||
{
|
||||
byte addr[8];
|
||||
|
||||
int amount = -1;
|
||||
while (this->mDs->search(addr)) {
|
||||
while (this->mDs->search(addr))
|
||||
{
|
||||
amount++;
|
||||
}
|
||||
this->mDs->reset_search();
|
||||
return amount;
|
||||
}
|
||||
|
||||
int Ds18B20::readAllTemperatures(float* pTemperatures, int maxTemperatures) {
|
||||
int Ds18B20::readAllTemperatures(float *pTemperatures, int maxTemperatures)
|
||||
{
|
||||
byte addr[8];
|
||||
uint8_t scratchPad[SCRATCHPADSIZE];
|
||||
int currentTemp = 0;
|
||||
#ifdef DS_DEBUG
|
||||
int i;
|
||||
#endif
|
||||
|
||||
while (this->mDs->search(addr)) {
|
||||
#ifdef DS_DEBUG
|
||||
Serial.print(" ROM =");
|
||||
for (i = 0; i < 8; i++) {
|
||||
Serial.write(' ');
|
||||
Serial.print(addr[i], HEX);
|
||||
}
|
||||
#endif
|
||||
while (this->mDs->search(addr))
|
||||
{
|
||||
this->mDs->reset();
|
||||
this->mDs->select(addr);
|
||||
this->mDs->write(STARTCONV);
|
||||
}
|
||||
delay(750);
|
||||
|
||||
while (this->mDs->search(addr))
|
||||
{
|
||||
this->mDs->reset();
|
||||
this->mDs->select(addr);
|
||||
this->mDs->write(READSCRATCH);
|
||||
@ -68,31 +67,27 @@ int Ds18B20::readAllTemperatures(float* pTemperatures, int maxTemperatures) {
|
||||
// byte 7: DS18S20: COUNT_PER_C
|
||||
// DS18B20 & DS1822: store for crc
|
||||
// byte 8: SCRATCHPAD_CRC
|
||||
#ifdef DS_DEBUG
|
||||
Serial.write("\r\nDATA:");
|
||||
for (uint8_t i = 0; i < 9; i++) {
|
||||
Serial.print(scratchPad[i], HEX);
|
||||
}
|
||||
#else
|
||||
delay(50);
|
||||
#endif
|
||||
for (uint8_t i = 0; i < 9; i++) {
|
||||
for (uint8_t i = 0; i < 9; i++)
|
||||
{
|
||||
scratchPad[i] = this->mDs->read();
|
||||
}
|
||||
uint8_t crc8 = this->mDs->crc8(scratchPad, 8);
|
||||
|
||||
/* Only work an valid data */
|
||||
if (crc8 == scratchPad[OFFSET_CRC8]) {
|
||||
int16_t fpTemperature = (((int16_t) scratchPad[TEMP_MSB]) << 11)
|
||||
| (((int16_t) scratchPad[TEMP_LSB]) << 3);
|
||||
float celsius = (float) fpTemperature * 0.0078125;
|
||||
if (crc8 == scratchPad[OFFSET_CRC8])
|
||||
{
|
||||
int16_t fpTemperature = (((int16_t)scratchPad[TEMP_MSB]) << 11) | (((int16_t)scratchPad[TEMP_LSB]) << 3);
|
||||
float celsius = (float)fpTemperature * 0.0078125;
|
||||
#ifdef DS_DEBUG
|
||||
Serial.printf("\r\nTemp%d %f °C (Raw: %d, %x =? %x)\r\n", (currentTemp+1), celsius, fpTemperature, crc8, scratchPad[8]);
|
||||
Serial.printf("\r\nTemp%d %f °C (Raw: %d, %x =? %x)\r\n", (currentTemp + 1), celsius, fpTemperature, crc8, scratchPad[8]);
|
||||
#endif
|
||||
/* check, if the buffer as some space for our data */
|
||||
if (currentTemp < maxTemperatures) {
|
||||
if (currentTemp < maxTemperatures)
|
||||
{
|
||||
pTemperatures[currentTemp] = celsius;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -11,32 +11,35 @@
|
||||
*/
|
||||
|
||||
#include "PlantCtrl.h"
|
||||
#include "ControllerConfiguration.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)
|
||||
{
|
||||
this->mPinSensor = pinSensor;
|
||||
this->mPinPump = pinPump;
|
||||
this->mPlant = plant;
|
||||
this->mSetting = setting;
|
||||
}
|
||||
|
||||
void Plant::init(void) {
|
||||
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->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->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->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->pPumpCooldownInHours->setValidator([](long candidate) {
|
||||
return ((candidate >= 0) && (candidate <= 1024));
|
||||
});
|
||||
|
||||
/* Initialize Hardware */
|
||||
@ -45,43 +48,50 @@ void Plant::init(void) {
|
||||
digitalWrite(this->mPinPump, LOW);
|
||||
}
|
||||
|
||||
void Plant::addSenseValue(void) {
|
||||
this->moistureRaw.add( analogRead(this->mPinSensor) );
|
||||
void Plant::addSenseValue(void)
|
||||
{
|
||||
int raw = analogRead(this->mPinSensor);
|
||||
if(raw < MOIST_SENSOR_MAX_ADC && raw > MOIST_SENSOR_MIN_ADC){
|
||||
this->moistureRaw.add(raw);
|
||||
}
|
||||
}
|
||||
|
||||
void Plant::postMQTTconnection(void) {
|
||||
void Plant::postMQTTconnection(void)
|
||||
{
|
||||
const String OFF = String("OFF");
|
||||
this->mConnected=true;
|
||||
this->mConnected = true;
|
||||
this->mPlant->setProperty("switch").send(OFF);
|
||||
}
|
||||
|
||||
void Plant::deactivatePump(void) {
|
||||
void Plant::deactivatePump(void)
|
||||
{
|
||||
digitalWrite(this->mPinPump, LOW);
|
||||
if (this->mConnected) {
|
||||
if (this->mConnected)
|
||||
{
|
||||
const String OFF = String("OFF");
|
||||
this->mPlant->setProperty("switch").send(OFF);
|
||||
}
|
||||
}
|
||||
|
||||
void Plant::activatePump(void) {
|
||||
void Plant::activatePump(void)
|
||||
{
|
||||
digitalWrite(this->mPinPump, HIGH);
|
||||
if (this->mConnected) {
|
||||
if (this->mConnected)
|
||||
{
|
||||
const String OFF = String("ON");
|
||||
this->mPlant->setProperty("switch").send(OFF);
|
||||
}
|
||||
}
|
||||
|
||||
void Plant::advertise(void) {
|
||||
void Plant::advertise(void)
|
||||
{
|
||||
// Advertise topics
|
||||
this->mPlant->advertise("switch").setName("Pump 1")
|
||||
.setDatatype("boolean");
|
||||
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("moist").setName("Percent").setDatatype("number").setUnit("%");
|
||||
this->mPlant->advertise("moistraw").setName("adc").setDatatype("number").setUnit("3.3/4096V");
|
||||
}
|
||||
|
||||
|
||||
/* FIXME
|
||||
bool Plant::switchHandler(const HomieRange& range, const String& value) {
|
||||
if (range.isRange) return false; // only one switch is present
|
||||
|
@ -31,8 +31,8 @@ RunningMedian::RunningMedian(const uint8_t size)
|
||||
_size = constrain(size, MEDIAN_MIN_SIZE, MEDIAN_MAX_SIZE);
|
||||
|
||||
#ifdef RUNNING_MEDIAN_USE_MALLOC
|
||||
_ar = (float *) malloc(_size * sizeof(float));
|
||||
_p = (uint8_t *) malloc(_size * sizeof(uint8_t));
|
||||
_ar = (float *)malloc(_size * sizeof(float));
|
||||
_p = (uint8_t *)malloc(_size * sizeof(uint8_t));
|
||||
#endif
|
||||
|
||||
clear();
|
||||
@ -63,16 +63,20 @@ void RunningMedian::clear()
|
||||
void RunningMedian::add(float value)
|
||||
{
|
||||
_ar[_idx++] = value;
|
||||
if (_idx >= _size) _idx = 0; // wrap around
|
||||
if (_cnt < _size) _cnt++;
|
||||
if (_idx >= _size)
|
||||
_idx = 0; // wrap around
|
||||
if (_cnt < _size)
|
||||
_cnt++;
|
||||
_sorted = false;
|
||||
}
|
||||
|
||||
float RunningMedian::getMedian()
|
||||
{
|
||||
if (_cnt == 0) return NAN;
|
||||
if (_cnt == 0)
|
||||
return NAN;
|
||||
|
||||
if (_sorted == false) sort();
|
||||
if (_sorted == false)
|
||||
sort();
|
||||
|
||||
if (_cnt & 0x01) // is it odd sized?
|
||||
{
|
||||
@ -83,7 +87,8 @@ float RunningMedian::getMedian()
|
||||
|
||||
float RunningMedian::getAverage()
|
||||
{
|
||||
if (_cnt == 0) return NAN;
|
||||
if (_cnt == 0)
|
||||
return NAN;
|
||||
|
||||
float sum = 0;
|
||||
for (uint8_t i = 0; i < _cnt; i++)
|
||||
@ -95,13 +100,16 @@ float RunningMedian::getAverage()
|
||||
|
||||
float RunningMedian::getAverage(uint8_t nMedians)
|
||||
{
|
||||
if ((_cnt == 0) || (nMedians == 0)) return NAN;
|
||||
if ((_cnt == 0) || (nMedians == 0))
|
||||
return NAN;
|
||||
|
||||
if (_cnt < nMedians) nMedians = _cnt; // when filling the array for first time
|
||||
if (_cnt < nMedians)
|
||||
nMedians = _cnt; // when filling the array for first time
|
||||
uint8_t start = ((_cnt - nMedians) / 2);
|
||||
uint8_t stop = start + nMedians;
|
||||
|
||||
if (_sorted == false) sort();
|
||||
if (_sorted == false)
|
||||
sort();
|
||||
|
||||
float sum = 0;
|
||||
for (uint8_t i = start; i < stop; i++)
|
||||
@ -113,7 +121,8 @@ float RunningMedian::getAverage(uint8_t nMedians)
|
||||
|
||||
float RunningMedian::getElement(const uint8_t n)
|
||||
{
|
||||
if ((_cnt == 0) || (n >= _cnt)) return NAN;
|
||||
if ((_cnt == 0) || (n >= _cnt))
|
||||
return NAN;
|
||||
|
||||
uint8_t pos = _idx + n;
|
||||
if (pos >= _cnt) // faster than %
|
||||
@ -125,16 +134,19 @@ float RunningMedian::getElement(const uint8_t n)
|
||||
|
||||
float RunningMedian::getSortedElement(const uint8_t n)
|
||||
{
|
||||
if ((_cnt == 0) || (n >= _cnt)) return NAN;
|
||||
if ((_cnt == 0) || (n >= _cnt))
|
||||
return NAN;
|
||||
|
||||
if (_sorted == false) sort();
|
||||
if (_sorted == false)
|
||||
sort();
|
||||
return _ar[_p[n]];
|
||||
}
|
||||
|
||||
// n can be max <= half the (filled) size
|
||||
float RunningMedian::predict(const uint8_t n)
|
||||
{
|
||||
if ((_cnt == 0) || (n >= _cnt / 2)) return NAN;
|
||||
if ((_cnt == 0) || (n >= _cnt / 2))
|
||||
return NAN;
|
||||
|
||||
float med = getMedian(); // takes care of sorting !
|
||||
if (_cnt & 0x01)
|
||||
@ -162,7 +174,8 @@ void RunningMedian::sort()
|
||||
flag = false;
|
||||
}
|
||||
}
|
||||
if (flag) break;
|
||||
if (flag)
|
||||
break;
|
||||
}
|
||||
_sorted = true;
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
/**
|
||||
/** \addtogroup Controller
|
||||
* @{
|
||||
*
|
||||
* @file main.cpp
|
||||
* @author Ollo
|
||||
* @brief PlantControl
|
||||
@ -6,7 +8,6 @@
|
||||
* @date 2020-05-01
|
||||
*
|
||||
* @copyright Copyright (c) 2020
|
||||
*
|
||||
*/
|
||||
#include "PlantCtrl.h"
|
||||
#include "ControllerConfiguration.h"
|
||||
@ -16,8 +17,8 @@
|
||||
#include "time.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "RunningMedian.h"
|
||||
#include <arduino-timer.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
const unsigned long TEMPREADCYCLE = 30000; /**< Check temperature all half minutes */
|
||||
|
||||
@ -26,44 +27,36 @@ const unsigned long TEMPREADCYCLE = 30000; /**< Check temperature all half minut
|
||||
#define SOLAR4SENSORS 6.0f
|
||||
#define TEMP_INIT_VALUE -999.0f
|
||||
#define TEMP_MAX_VALUE 85.0f
|
||||
#define HalfHour 60
|
||||
|
||||
typedef struct
|
||||
{
|
||||
long lastActive; /**< Timestamp, a pump was activated */
|
||||
long moistTrigger; /**< Trigger value of the moist sensor */
|
||||
long moisture; /**< last measured moist value */
|
||||
|
||||
} rtc_plant_t;
|
||||
|
||||
/********************* non volatile enable after deepsleep *******************************/
|
||||
|
||||
RTC_DATA_ATTR rtc_plant_t rtcPlant[MAX_PLANTS];
|
||||
RTC_DATA_ATTR long gotoMode2AfterThisTimestamp = 0;
|
||||
RTC_DATA_ATTR long rtcDeepSleepTime = 0; /**< Time, when the microcontroller shall be up again */
|
||||
RTC_DATA_ATTR long rtcLastActive0 = 0;
|
||||
RTC_DATA_ATTR long rtcMoistureTrigger0 = 0; /**<Level for the moisture sensor */
|
||||
RTC_DATA_ATTR long rtcLastActive1 = 0;
|
||||
RTC_DATA_ATTR long rtcMoistureTrigger1 = 0; /**<Level for the moisture sensor */
|
||||
RTC_DATA_ATTR long rtcLastActive2 = 0;
|
||||
RTC_DATA_ATTR long rtcMoistureTrigger2 = 0; /**<Level for the moisture sensor */
|
||||
RTC_DATA_ATTR long rtcLastActive3 = 0;
|
||||
RTC_DATA_ATTR long rtcMoistureTrigger3 = 0; /**<Level for the moisture sensor */
|
||||
RTC_DATA_ATTR long rtcLastActive4 = 0;
|
||||
RTC_DATA_ATTR long rtcMoistureTrigger4 = 0; /**<Level for the moisture sensor */
|
||||
RTC_DATA_ATTR long rtcLastActive5 = 0;
|
||||
RTC_DATA_ATTR long rtcMoistureTrigger5 = 0; /**<Level for the moisture sensor */
|
||||
RTC_DATA_ATTR long rtcLastActive6 = 0;
|
||||
RTC_DATA_ATTR long rtcMoistureTrigger6 = 0; /**<Level for the moisture sensor */
|
||||
RTC_DATA_ATTR int lastPumpRunning = 0;
|
||||
RTC_DATA_ATTR long lastWaterValue = 0;
|
||||
|
||||
const char *ntpServer = "pool.ntp.org";
|
||||
RTC_DATA_ATTR float rtcLastTemp1 = 0.0f;
|
||||
RTC_DATA_ATTR float rtcLastTemp2 = 0.0f;
|
||||
RTC_DATA_ATTR int gBootCount = 0;
|
||||
RTC_DATA_ATTR int gCurrentPlant = 0; /**< Value Range: 1 ... 7 (0: no plant needs water) */
|
||||
|
||||
bool warmBoot = true;
|
||||
bool mode3Active = false; /**< Controller must not sleep */
|
||||
bool mDeepsleep = false;
|
||||
bool volatile mode3Active = false; /**< Controller must not sleep */
|
||||
bool volatile mDeepsleep = false;
|
||||
|
||||
int plantSensor1 = 0;
|
||||
|
||||
int readCounter = 0;
|
||||
bool mConfigured = false;
|
||||
|
||||
auto wait4sleep = timer_create_default(); // create a timer with default settings
|
||||
|
||||
RTC_DATA_ATTR int gBootCount = 0;
|
||||
RTC_DATA_ATTR int gCurrentPlant = 0; /**< Value Range: 1 ... 7 (0: no plant needs water) */
|
||||
|
||||
RunningMedian lipoRawSensor = RunningMedian(5);
|
||||
RunningMedian solarRawSensor = RunningMedian(5);
|
||||
RunningMedian waterRawSensor = RunningMedian(5);
|
||||
@ -91,10 +84,41 @@ float getSolarVoltage()
|
||||
return SOLAR_VOLT(solarRawSensor.getAverage());
|
||||
}
|
||||
|
||||
void setMoistureTrigger(int plantId, long value)
|
||||
{
|
||||
if ((plantId >= 0) && (plantId < MAX_PLANTS))
|
||||
{
|
||||
rtcPlant[plantId].moistTrigger = value;
|
||||
}
|
||||
}
|
||||
|
||||
void setLastMoisture(int plantId, long value)
|
||||
{
|
||||
if ((plantId >= 0) && (plantId < MAX_PLANTS))
|
||||
{
|
||||
rtcPlant[plantId].moisture = value;
|
||||
}
|
||||
}
|
||||
|
||||
long getLastMoisture(int plantId)
|
||||
{
|
||||
if ((plantId >= 0) && (plantId < MAX_PLANTS))
|
||||
{
|
||||
return rtcPlant[plantId].moisture;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void readSystemSensors()
|
||||
{
|
||||
for (int i=0; i < 5; i++) {
|
||||
lipoRawSensor.add(analogRead(SENSOR_LIPO));
|
||||
solarRawSensor.add(analogRead(SENSOR_SOLAR));
|
||||
}
|
||||
Serial << "Lipo " << lipoRawSensor.getAverage() << " -> " << getBatteryVoltage() << endl;
|
||||
}
|
||||
|
||||
int determineNextPump();
|
||||
@ -107,39 +131,42 @@ long getCurrentTime()
|
||||
return tv_now.tv_sec;
|
||||
}
|
||||
|
||||
//wait till homie flushed mqtt ect.
|
||||
bool prepareSleep(void *)
|
||||
{
|
||||
//FIXME wait till pending mqtt is done, then start sleep via event or whatever
|
||||
//Homie.disableResetTrigger();
|
||||
|
||||
bool queueIsEmpty = true;
|
||||
if (queueIsEmpty)
|
||||
{
|
||||
mDeepsleep = true;
|
||||
}
|
||||
return false; // repeat? true there is something in the queue to be done
|
||||
}
|
||||
|
||||
void espDeepSleepFor(long seconds, bool activatePump = false)
|
||||
{
|
||||
delay(1500);
|
||||
if (mode3Active)
|
||||
{
|
||||
Serial << "abort deepsleep, mode3Active" << endl;
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
long cTime = getCurrentTime();
|
||||
if (cTime < 100000)
|
||||
{
|
||||
Serial << "Wait for ntp" << endl;
|
||||
delay(100);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF);
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_ON);
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON);
|
||||
if (activatePump)
|
||||
{
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_ON);
|
||||
gpio_deep_sleep_hold_en();
|
||||
gpio_hold_en(GPIO_NUM_13); //pump pwr
|
||||
}
|
||||
else
|
||||
{
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_OFF);
|
||||
gpio_hold_dis(GPIO_NUM_13); //pump pwr
|
||||
gpio_deep_sleep_hold_dis();
|
||||
digitalWrite(OUTPUT_PUMP, LOW);
|
||||
digitalWrite(OUTPUT_SENSOR, LOW);
|
||||
for (int i = 0; i < MAX_PLANTS; i++)
|
||||
{
|
||||
mPlants[i].deactivatePump();
|
||||
@ -148,19 +175,17 @@ void espDeepSleepFor(long seconds, bool activatePump = false)
|
||||
//gpio_hold_en(GPIO_NUM_23); //p0
|
||||
//FIXME fix for outher outputs
|
||||
|
||||
Serial.print("Going to sleep for ");
|
||||
Serial.print("Trying to sleep for ");
|
||||
Serial.print(seconds);
|
||||
Serial.println(" seconds");
|
||||
esp_sleep_enable_timer_wakeup((seconds * 1000U * 1000U));
|
||||
wait4sleep.in(500, prepareSleep);
|
||||
mDeepsleep = true;
|
||||
}
|
||||
|
||||
void mode2MQTT()
|
||||
{
|
||||
readSystemSensors();
|
||||
|
||||
configTime(0, 0, ntpServer);
|
||||
|
||||
digitalWrite(OUTPUT_PUMP, LOW);
|
||||
for (int i = 0; i < MAX_PLANTS; i++)
|
||||
{
|
||||
@ -169,7 +194,7 @@ void mode2MQTT()
|
||||
|
||||
if (deepSleepTime.get())
|
||||
{
|
||||
Serial << "sleeping for " << deepSleepTime.get() << endl;
|
||||
Serial << "deepsleep time is configured to " << deepSleepTime.get() << endl;
|
||||
}
|
||||
/* Publish default values */
|
||||
|
||||
@ -180,7 +205,23 @@ void mode2MQTT()
|
||||
}
|
||||
for (int i = 0; i < MAX_PLANTS; i++)
|
||||
{
|
||||
mPlants[i].setProperty("moist").send(String(100 * mPlants[i].getSensorValue() / 4095));
|
||||
long raw = mPlants[i].getCurrentMoisture();
|
||||
long pct = 100 - map(raw, MOIST_SENSOR_MIN_ADC, MOIST_SENSOR_MAX_ADC, 0, 100);
|
||||
if (raw == MISSING_SENSOR)
|
||||
{
|
||||
pct = 0;
|
||||
}
|
||||
if (pct < 0)
|
||||
{
|
||||
pct = 0;
|
||||
}
|
||||
if (pct > 100)
|
||||
{
|
||||
pct = 100;
|
||||
}
|
||||
|
||||
mPlants[i].setProperty("moist").send(String(pct));
|
||||
mPlants[i].setProperty("moistraw").send(String(raw));
|
||||
}
|
||||
sensorWater.setProperty("remaining").send(String(waterLevelMax.get() - waterRawSensor.getAverage()));
|
||||
Serial << "W : " << waterRawSensor.getAverage() << " cm (" << String(waterLevelMax.get() - waterRawSensor.getAverage()) << "%)" << endl;
|
||||
@ -191,34 +232,31 @@ void mode2MQTT()
|
||||
sensorSolar.setProperty("percent").send(String((100 * solarRawSensor.getAverage()) / 4095));
|
||||
sensorSolar.setProperty("volt").send(String(getSolarVoltage()));
|
||||
|
||||
float temp[2] = {TEMP_INIT_VALUE, TEMP_INIT_VALUE};
|
||||
float *pFloat = temp;
|
||||
int devices = dallas.readAllTemperatures(pFloat, 2);
|
||||
if (devices < 2)
|
||||
float t1 = temp1.getMedian();
|
||||
if (t1 != NAN)
|
||||
{
|
||||
if ((pFloat[0] > TEMP_INIT_VALUE) && (pFloat[0] < TEMP_MAX_VALUE))
|
||||
{
|
||||
sensorTemp.setProperty("control").send(String(pFloat[0]));
|
||||
sensorTemp.setProperty("control").send(String(t1));
|
||||
}
|
||||
}
|
||||
else if (devices >= 2)
|
||||
float t2 = temp2.getMedian();
|
||||
if (t2 != NAN)
|
||||
{
|
||||
if ((pFloat[0] > TEMP_INIT_VALUE) && (pFloat[0] < TEMP_MAX_VALUE))
|
||||
{
|
||||
sensorTemp.setProperty("temp").send(String(pFloat[0]));
|
||||
}
|
||||
if ((pFloat[1] > TEMP_INIT_VALUE) && (pFloat[1] < TEMP_MAX_VALUE))
|
||||
{
|
||||
sensorTemp.setProperty("control").send(String(pFloat[1]));
|
||||
}
|
||||
sensorTemp.setProperty("temp").send(String(t2));
|
||||
}
|
||||
|
||||
bool lipoTempWarning = abs(temp[0] - temp[1]) > 5;
|
||||
//give mqtt time, use via publish callback instead?
|
||||
delay(100);
|
||||
|
||||
bool lipoTempWarning = t1 != 85 && t2 != 85 && abs(t1 - t2) > 10;
|
||||
if (lipoTempWarning)
|
||||
{
|
||||
Serial.println("Lipo temp incorrect, panic mode deepsleep");
|
||||
espDeepSleepFor(PANIK_MODE_DEEPSLEEP);
|
||||
return;
|
||||
Serial.println("Lipo temp incorrect, panic mode deepsleep TODO");
|
||||
//espDeepSleepFor(PANIK_MODE_DEEPSLEEP);
|
||||
//return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_PLANTS; i++)
|
||||
{
|
||||
setMoistureTrigger(i, mPlants[i].mSetting->pSensorDry->get());
|
||||
}
|
||||
|
||||
bool hasWater = true; //FIXMEmWaterGone > waterLevelMin.get();
|
||||
@ -229,24 +267,33 @@ void mode2MQTT()
|
||||
Serial.println("Want to pump but no water");
|
||||
}
|
||||
if (lastPumpRunning != -1 && hasWater)
|
||||
{
|
||||
if (mode3Active)
|
||||
{
|
||||
Serial.println("Mode 3 active, ignoring pump request");
|
||||
}
|
||||
else
|
||||
{
|
||||
digitalWrite(OUTPUT_PUMP, HIGH);
|
||||
setLastActivationForPump(lastPumpRunning, getCurrentTime());
|
||||
mPlants[lastPumpRunning].activatePump();
|
||||
}
|
||||
}
|
||||
if (lastPumpRunning == -1 || !hasWater)
|
||||
{
|
||||
if (getSolarVoltage() < SOLAR_CHARGE_MIN_VOLTAGE)
|
||||
{
|
||||
gotoMode2AfterThisTimestamp = getCurrentTime() + deepSleepNightTime.get();
|
||||
gotoMode2AfterThisTimestamp = getCurrentTime() + maxTimeBetweenMQTTUpdates.get();
|
||||
Serial.println("No pumps to activate and low light, deepSleepNight");
|
||||
espDeepSleepFor(deepSleepNightTime.get());
|
||||
rtcDeepSleepTime = deepSleepNightTime.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
gotoMode2AfterThisTimestamp = getCurrentTime() + deepSleepTime.get();
|
||||
gotoMode2AfterThisTimestamp = getCurrentTime() + maxTimeBetweenMQTTUpdates.get();
|
||||
Serial.println("No pumps to activate, deepSleep");
|
||||
espDeepSleepFor(deepSleepTime.get());
|
||||
rtcDeepSleepTime = deepSleepTime.get();
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -257,109 +304,47 @@ void mode2MQTT()
|
||||
}
|
||||
}
|
||||
|
||||
void setMoistureTrigger(int plantId, long value)
|
||||
long getMoistureTrigger(int plantId)
|
||||
{
|
||||
if (plantId == 0)
|
||||
if ((plantId >= 0) && (plantId < MAX_PLANTS))
|
||||
{
|
||||
rtcMoistureTrigger0 = value;
|
||||
return rtcPlant[plantId].moistTrigger;
|
||||
}
|
||||
if (plantId == 1)
|
||||
else
|
||||
{
|
||||
rtcMoistureTrigger1 = value;
|
||||
}
|
||||
if (plantId == 2)
|
||||
{
|
||||
rtcMoistureTrigger2 = value;
|
||||
}
|
||||
if (plantId == 3)
|
||||
{
|
||||
rtcMoistureTrigger3 = value;
|
||||
}
|
||||
if (plantId == 4)
|
||||
{
|
||||
rtcMoistureTrigger4 = value;
|
||||
}
|
||||
if (plantId == 5)
|
||||
{
|
||||
rtcMoistureTrigger5 = value;
|
||||
}
|
||||
if (plantId == 6)
|
||||
{
|
||||
rtcMoistureTrigger6 = value;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void setLastActivationForPump(int plantId, long value)
|
||||
{
|
||||
if (plantId == 0)
|
||||
if ((plantId >= 0) && (plantId < MAX_PLANTS))
|
||||
{
|
||||
rtcLastActive0 = value;
|
||||
}
|
||||
if (plantId == 1)
|
||||
{
|
||||
rtcLastActive1 = value;
|
||||
}
|
||||
if (plantId == 2)
|
||||
{
|
||||
rtcLastActive2 = value;
|
||||
}
|
||||
if (plantId == 3)
|
||||
{
|
||||
rtcLastActive3 = value;
|
||||
}
|
||||
if (plantId == 4)
|
||||
{
|
||||
rtcLastActive4 = value;
|
||||
}
|
||||
if (plantId == 5)
|
||||
{
|
||||
rtcLastActive5 = value;
|
||||
}
|
||||
if (plantId == 6)
|
||||
{
|
||||
rtcLastActive6 = value;
|
||||
rtcPlant[plantId].lastActive = value;
|
||||
}
|
||||
}
|
||||
|
||||
long getLastActivationForPump(int plantId)
|
||||
{
|
||||
if (plantId == 0)
|
||||
if ((plantId >= 0) && (plantId < MAX_PLANTS))
|
||||
{
|
||||
return rtcLastActive0;
|
||||
return rtcPlant[plantId].lastActive;
|
||||
}
|
||||
if (plantId == 1)
|
||||
else
|
||||
{
|
||||
return rtcLastActive1;
|
||||
}
|
||||
if (plantId == 2)
|
||||
{
|
||||
return rtcLastActive2;
|
||||
}
|
||||
if (plantId == 3)
|
||||
{
|
||||
return rtcLastActive3;
|
||||
}
|
||||
if (plantId == 4)
|
||||
{
|
||||
return rtcLastActive4;
|
||||
}
|
||||
if (plantId == 5)
|
||||
{
|
||||
return rtcLastActive5;
|
||||
}
|
||||
if (plantId == 6)
|
||||
{
|
||||
return rtcLastActive6;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sensors, that are connected to GPIOs, mandatory for WIFI.
|
||||
* These sensors (ADC2) can only be read when no Wifi is used.
|
||||
*/
|
||||
void readSensors()
|
||||
bool readSensors()
|
||||
{
|
||||
float temp[2] = {TEMP_MAX_VALUE, TEMP_MAX_VALUE};
|
||||
float *pFloat = temp;
|
||||
bool leaveMode1 = false;
|
||||
Serial << "Read Sensors" << endl;
|
||||
|
||||
readSystemSensors();
|
||||
@ -368,7 +353,7 @@ void readSensors()
|
||||
pinMode(OUTPUT_SENSOR, OUTPUT);
|
||||
digitalWrite(OUTPUT_SENSOR, HIGH);
|
||||
|
||||
delay(100);
|
||||
delay(20);
|
||||
/* wait before reading something */
|
||||
for (int readCnt = 0; readCnt < AMOUNT_SENOR_QUERYS; readCnt++)
|
||||
{
|
||||
@ -376,6 +361,19 @@ void readSensors()
|
||||
{
|
||||
mPlants[i].addSenseValue();
|
||||
}
|
||||
delay(10);
|
||||
}
|
||||
for (int i = 0; i < MAX_PLANTS; i++)
|
||||
{
|
||||
long current = mPlants[i].getCurrentMoisture();
|
||||
long delta = abs(getLastMoisture(i) - current);
|
||||
bool tmp = (delta > MOIST_DELTA_TRIGGER_ADC);
|
||||
setLastMoisture(i, current);
|
||||
if (tmp)
|
||||
{
|
||||
leaveMode1 = true;
|
||||
Serial.printf("Mode2 start due to moist delta in plant %d with %ld \r\n", i, delta);
|
||||
}
|
||||
}
|
||||
|
||||
Serial << "DS18B20" << endl;
|
||||
@ -384,23 +382,33 @@ void readSensors()
|
||||
delay(200);
|
||||
|
||||
/* Required to read the temperature once */
|
||||
float temp[2] = {0, 0};
|
||||
float *pFloat = temp;
|
||||
for (int i = 0; i < 10; i++)
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
if (dallas.readAllTemperatures(pFloat, 2) > 0)
|
||||
int sensors = dallas.readAllTemperatures(pFloat, 2);
|
||||
if (sensors > 0)
|
||||
{
|
||||
Serial << "t1: " << String(temp[0]) << endl;
|
||||
Serial << "t2: " << String(temp[1]) << endl;
|
||||
// first read returns crap, ignore result and read again
|
||||
if (i <= 2)
|
||||
{
|
||||
temp1.add(temp[0]);
|
||||
}
|
||||
if (sensors > 1)
|
||||
{
|
||||
Serial << "t2: " << String(temp[1]) << endl;
|
||||
temp2.add(temp[1]);
|
||||
}
|
||||
delay(50);
|
||||
}
|
||||
delay(200);
|
||||
|
||||
if ((temp1.getAverage() - rtcLastTemp1 > TEMPERATURE_DELTA_TRIGGER_IN_C) ||
|
||||
(rtcLastTemp1 - temp1.getAverage() > TEMPERATURE_DELTA_TRIGGER_IN_C)) {
|
||||
leaveMode1 = true;
|
||||
}
|
||||
if ((temp2.getAverage() - rtcLastTemp2 > TEMPERATURE_DELTA_TRIGGER_IN_C) ||
|
||||
(rtcLastTemp2 - temp2.getAverage() > TEMPERATURE_DELTA_TRIGGER_IN_C)) {
|
||||
leaveMode1 = true;
|
||||
}
|
||||
|
||||
rtcLastTemp1 = temp1.getAverage();
|
||||
rtcLastTemp2 = temp2.getAverage();
|
||||
|
||||
/* Use the Ultrasonic sensor to measure waterLevel */
|
||||
for (int i = 0; i < 5; i++)
|
||||
@ -417,6 +425,7 @@ void readSensors()
|
||||
}
|
||||
/* deactivate the sensors */
|
||||
digitalWrite(OUTPUT_SENSOR, LOW);
|
||||
return leaveMode1;
|
||||
}
|
||||
|
||||
//Homie.getMqttClient().disconnect();
|
||||
@ -429,9 +438,11 @@ void onHomieEvent(const HomieEvent &event)
|
||||
Homie.getLogger() << "My statistics" << endl;
|
||||
break;
|
||||
case HomieEventType::MQTT_READY:
|
||||
Serial.printf("NTP Setup with server %s\r\n", ntpServer.get());
|
||||
configTime(0, 0, ntpServer.get());
|
||||
//wait for rtc sync?
|
||||
rtcDeepSleepTime = deepSleepTime.get();
|
||||
Serial << "MQTT ready " << rtcDeepSleepTime << " ms ds" << endl;
|
||||
Serial << "Setup plants" << endl;
|
||||
for (int i = 0; i < MAX_PLANTS; i++)
|
||||
{
|
||||
mPlants[i].postMQTTconnection();
|
||||
@ -444,8 +455,9 @@ void onHomieEvent(const HomieEvent &event)
|
||||
esp_deep_sleep_start();
|
||||
break;
|
||||
case HomieEventType::OTA_STARTED:
|
||||
Homie.getLogger() << "OTA started" << endl;
|
||||
digitalWrite(OUTPUT_SENSOR, HIGH);
|
||||
digitalWrite(OUTPUT_PUMP, LOW);
|
||||
digitalWrite(OUTPUT_PUMP, HIGH);
|
||||
gpio_hold_dis(GPIO_NUM_13); //pump pwr
|
||||
gpio_deep_sleep_hold_dis();
|
||||
for (int i = 0; i < MAX_PLANTS; i++)
|
||||
@ -455,6 +467,7 @@ void onHomieEvent(const HomieEvent &event)
|
||||
mode3Active = true;
|
||||
break;
|
||||
case HomieEventType::OTA_SUCCESSFUL:
|
||||
Homie.getLogger() << "OTA successfull" << endl;
|
||||
digitalWrite(OUTPUT_SENSOR, LOW);
|
||||
digitalWrite(OUTPUT_PUMP, LOW);
|
||||
ESP.restart();
|
||||
@ -470,32 +483,46 @@ int determineNextPump()
|
||||
bool isLowLight = (solarValue > SOLAR_CHARGE_MIN_VOLTAGE || solarValue < SOLAR_CHARGE_MAX_VOLTAGE);
|
||||
|
||||
//FIXME instead of for, use sorted by last activation index to ensure equal runtime?
|
||||
|
||||
int pumpToUse = -1;
|
||||
for (int i = 0; i < MAX_PLANTS; i++)
|
||||
{
|
||||
Plant plant = mPlants[i];
|
||||
long lastActivation = getLastActivationForPump(i);
|
||||
long sinceLastActivation = getCurrentTime() - lastActivation;
|
||||
//this pump is in cooldown skip it and disable low power mode trigger for it
|
||||
if (mPlants[i].isInCooldown(sinceLastActivation))
|
||||
if (plant.isInCooldown(sinceLastActivation))
|
||||
{
|
||||
Serial.printf("%d Skipping due to cooldown\r\n", i);
|
||||
Serial.printf("%d Skipping due to cooldown %ld / %ld \r\n", i, sinceLastActivation, plant.getCooldownInSeconds());
|
||||
setMoistureTrigger(i, DEACTIVATED_PLANT);
|
||||
continue;
|
||||
}
|
||||
//skip as it is not low light
|
||||
if (!isLowLight && mPlants[i].isAllowedOnlyAtLowLight())
|
||||
if (!isLowLight && plant.isAllowedOnlyAtLowLight())
|
||||
{
|
||||
Serial.println("Skipping due to light");
|
||||
Serial.printf("%d No pump required: due to light\r\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mPlants->isPumpRequired())
|
||||
if (plant.getCurrentMoisture() == MISSING_SENSOR && plant.isPumpTriggerActive())
|
||||
{
|
||||
Serial.printf("%d No pump possible: missing sensor \r\n", i);
|
||||
continue;
|
||||
}
|
||||
if (plant.isPumpRequired())
|
||||
{
|
||||
Serial.printf("%d Requested pumping\r\n", i);
|
||||
return i;
|
||||
pumpToUse = i;
|
||||
}
|
||||
Serial.printf("%d No pump required\r\n", i);
|
||||
else if (plant.isPumpTriggerActive())
|
||||
{
|
||||
Serial.printf("%d No pump required: moisture acceptable %f / %ld\r\n", i, plant.getCurrentMoisture(), plant.getSettingsMoisture());
|
||||
}
|
||||
return -1;
|
||||
else
|
||||
{
|
||||
Serial.printf("%d No pump required: disabled pump trigger \r\n", i);
|
||||
}
|
||||
}
|
||||
return pumpToUse;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -510,7 +537,6 @@ bool aliveHandler(const HomieRange &range, const String &value)
|
||||
{
|
||||
if (range.isRange)
|
||||
return false; // only one controller is present
|
||||
Serial << value << endl;
|
||||
if (value.equals("ON") || value.equals("On") || value.equals("1"))
|
||||
{
|
||||
mode3Active = true;
|
||||
@ -536,9 +562,11 @@ void systemInit()
|
||||
// Set default values
|
||||
|
||||
//in seconds
|
||||
deepSleepTime.setDefaultValue(10);
|
||||
deepSleepNightTime.setDefaultValue(30);
|
||||
maxTimeBetweenMQTTUpdates.setDefaultValue(120);
|
||||
deepSleepTime.setDefaultValue(60);
|
||||
deepSleepNightTime.setDefaultValue(600);
|
||||
wateringDeepSleep.setDefaultValue(5);
|
||||
ntpServer.setDefaultValue("pool.ntp.org");
|
||||
|
||||
/* waterLevelMax 1000 */ /* 100cm in mm */
|
||||
waterLevelMin.setDefaultValue(50); /* 5cm in mm */
|
||||
@ -547,6 +575,7 @@ void systemInit()
|
||||
|
||||
Homie.setLoopFunction(homieLoop);
|
||||
Homie.onEvent(onHomieEvent);
|
||||
//Homie.disableLogging();
|
||||
Homie.setup();
|
||||
|
||||
mConfigured = Homie.isConfigured();
|
||||
@ -589,63 +618,46 @@ void systemInit()
|
||||
|
||||
bool mode1()
|
||||
{
|
||||
Serial.println("m1");
|
||||
Serial.println("==== Mode 1 ====");
|
||||
Serial << getCurrentTime() << " curtime" << endl;
|
||||
|
||||
/* Disable all sleeping stuff before reading sensors */
|
||||
gpio_deep_sleep_hold_dis();
|
||||
|
||||
readSensors();
|
||||
bool deltaTrigger = readSensors();
|
||||
//queue sensor values for
|
||||
|
||||
if ((rtcDeepSleepTime == 0) ||
|
||||
(rtcMoistureTrigger0 == 0) ||
|
||||
(rtcMoistureTrigger1 == 0) ||
|
||||
(rtcMoistureTrigger2 == 0) ||
|
||||
(rtcMoistureTrigger3 == 0) ||
|
||||
(rtcMoistureTrigger4 == 0) ||
|
||||
(rtcMoistureTrigger5 == 0) ||
|
||||
(rtcMoistureTrigger6 == 0))
|
||||
if (deltaTrigger)
|
||||
{
|
||||
Serial.println("RTCm2");
|
||||
Serial.println("1 delta triggered, going to mode2");
|
||||
return true;
|
||||
}
|
||||
if (rtcDeepSleepTime == 0)
|
||||
{
|
||||
Serial.println("1 missing rtc value, going to mode2");
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < MAX_PLANTS; i++)
|
||||
{
|
||||
long trigger = getMoistureTrigger(i);
|
||||
if (trigger == 0)
|
||||
{
|
||||
Serial << "Missing rtc trigger " << i << endl;
|
||||
return true;
|
||||
}
|
||||
if (trigger == DEACTIVATED_PLANT)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
long raw = mPlants[i].getCurrentMoisture();
|
||||
if (raw == MISSING_SENSOR)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (raw > trigger)
|
||||
{
|
||||
Serial << "plant " << i << " dry " << raw << " / " << trigger << " starting mode 2" << endl;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((rtcMoistureTrigger0 != DEACTIVATED_PLANT) && (mPlants[0].getSensorValue() < rtcMoistureTrigger0))
|
||||
{
|
||||
Serial.println("mt0");
|
||||
return true;
|
||||
}
|
||||
if ((rtcMoistureTrigger1 != DEACTIVATED_PLANT) && (mPlants[1].getSensorValue() < rtcMoistureTrigger1))
|
||||
{
|
||||
Serial.println("mt1");
|
||||
return true;
|
||||
}
|
||||
if ((rtcMoistureTrigger2 != DEACTIVATED_PLANT) && (mPlants[2].getSensorValue() < rtcMoistureTrigger2))
|
||||
{
|
||||
Serial.println("mt2");
|
||||
return true;
|
||||
}
|
||||
if ((rtcMoistureTrigger3 != DEACTIVATED_PLANT) && (mPlants[3].getSensorValue() < rtcMoistureTrigger3))
|
||||
{
|
||||
Serial.println("mt3");
|
||||
return true;
|
||||
}
|
||||
if ((rtcMoistureTrigger4 != DEACTIVATED_PLANT) && (mPlants[4].getSensorValue() < rtcMoistureTrigger4))
|
||||
{
|
||||
Serial.println("mt4");
|
||||
return true;
|
||||
}
|
||||
if ((rtcMoistureTrigger5 != DEACTIVATED_PLANT) && (mPlants[5].getSensorValue() < rtcMoistureTrigger5))
|
||||
{
|
||||
Serial.println("mt5");
|
||||
return true;
|
||||
}
|
||||
if ((rtcMoistureTrigger6 != DEACTIVATED_PLANT) && (mPlants[6].getSensorValue() < rtcMoistureTrigger6))
|
||||
{
|
||||
Serial.println("mt6");
|
||||
return true;
|
||||
}
|
||||
//check how long it was already in mode1 if to long goto mode2
|
||||
|
||||
long cTime = getCurrentTime();
|
||||
@ -660,18 +672,22 @@ bool mode1()
|
||||
Serial.println("Starting mode 2 after specified mode1 time");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial << "Mode2 Timer " << gotoMode2AfterThisTimestamp << " curtime " << cTime << endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void mode2()
|
||||
{
|
||||
Serial.println("m2");
|
||||
Serial.println("==== Mode 2 ====");
|
||||
systemInit();
|
||||
|
||||
/* Jump into Mode 3, if not configured */
|
||||
if (!mConfigured)
|
||||
{
|
||||
Serial.println("m3");
|
||||
Serial.println("==== Mode 3 ====");
|
||||
mode3Active = true;
|
||||
}
|
||||
}
|
||||
@ -732,8 +748,7 @@ void setup()
|
||||
else
|
||||
{
|
||||
Serial.println("nop");
|
||||
Serial.flush();
|
||||
esp_deep_sleep_start();
|
||||
espDeepSleepFor(rtcDeepSleepTime);
|
||||
}
|
||||
}
|
||||
|
||||
@ -741,22 +756,36 @@ void setup()
|
||||
* @brief Cyclic call
|
||||
* Executs the Homie base functionallity or triggers sleeping, if requested.
|
||||
*/
|
||||
|
||||
long nextBlink = 0;
|
||||
void loop()
|
||||
{
|
||||
if (!mDeepsleep)
|
||||
if (!mDeepsleep || mode3Active)
|
||||
{
|
||||
Homie.loop();
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial << "Bye" << endl;
|
||||
Serial.flush();
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
|
||||
if (millis() > 30000 && !mode3Active)
|
||||
{
|
||||
Serial << (millis() / 1000) << "s alive" << endl;
|
||||
Serial << (millis() / 1000) << "not terminated watchdog putting to sleep" << endl;
|
||||
Serial.flush();
|
||||
esp_deep_sleep_start();
|
||||
espDeepSleepFor(rtcDeepSleepTime);
|
||||
}
|
||||
|
||||
/* Toggel Senor LED to visualize mode 3 */
|
||||
if (mode3Active)
|
||||
{
|
||||
if (nextBlink < millis())
|
||||
{
|
||||
nextBlink = millis() + 500;
|
||||
digitalWrite(OUTPUT_SENSOR, !digitalRead(OUTPUT_SENSOR));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @}*/
|
Loading…
Reference in New Issue
Block a user