Add new code for battery validation, updated probe node code added base read me
This commit is contained in:
3
Eagle/DESCRIPTION
Normal file
3
Eagle/DESCRIPTION
Normal file
@@ -0,0 +1,3 @@
|
||||
<b>User Projects</b>
|
||||
<p>
|
||||
This folder contains user Projects files.
|
||||
1908
Eagle/soilSensor.b#1
Normal file
1908
Eagle/soilSensor.b#1
Normal file
File diff suppressed because it is too large
Load Diff
1938
Eagle/soilSensor.b#2
Normal file
1938
Eagle/soilSensor.b#2
Normal file
File diff suppressed because it is too large
Load Diff
1965
Eagle/soilSensor.b#3
Normal file
1965
Eagle/soilSensor.b#3
Normal file
File diff suppressed because it is too large
Load Diff
1963
Eagle/soilSensor.b#4
Normal file
1963
Eagle/soilSensor.b#4
Normal file
File diff suppressed because it is too large
Load Diff
1904
Eagle/soilSensor.b#5
Normal file
1904
Eagle/soilSensor.b#5
Normal file
File diff suppressed because it is too large
Load Diff
1952
Eagle/soilSensor.b#6
Normal file
1952
Eagle/soilSensor.b#6
Normal file
File diff suppressed because it is too large
Load Diff
1904
Eagle/soilSensor.b#7
Normal file
1904
Eagle/soilSensor.b#7
Normal file
File diff suppressed because it is too large
Load Diff
1940
Eagle/soilSensor.b#8
Normal file
1940
Eagle/soilSensor.b#8
Normal file
File diff suppressed because it is too large
Load Diff
1937
Eagle/soilSensor.b#9
Normal file
1937
Eagle/soilSensor.b#9
Normal file
File diff suppressed because it is too large
Load Diff
1965
Eagle/soilSensor.brd
Normal file
1965
Eagle/soilSensor.brd
Normal file
File diff suppressed because it is too large
Load Diff
25
Eagle/soilSensor.pro
Normal file
25
Eagle/soilSensor.pro
Normal file
@@ -0,0 +1,25 @@
|
||||
EAGLE AutoRouter Statistics:
|
||||
|
||||
Job : C:/Users/ckoch/Documents/EAGLE/projects/soilSensor.brd
|
||||
|
||||
Start at : 16:06:15 (1/15/2025)
|
||||
End at : 16:06:22 (1/15/2025)
|
||||
Elapsed time : 00:00:03
|
||||
|
||||
Signals : 11 RoutingGrid: 11 mil Layers: 2
|
||||
Connections : 13 predefined: 2 ( 3 Vias )
|
||||
|
||||
Router memory : 107632
|
||||
|
||||
Passname : TopRouter Route Optimize1 Optimize2 Optimize3 Optimize4 Optimize5 Optimize6 Optimize7 Optimize8 Optimize9Optimize10Optimize11Optimize12
|
||||
|
||||
Time per pass : 00:00:02 00:00:00 00:00:00 00:00:00 00:00:01 00:00:00 00:00:00 00:00:00 00:00:00 00:00:00 00:00:00 00:00:00 00:00:00 00:00:00
|
||||
Number of Ripups : 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
max. Level : 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
max. Total : 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
|
||||
Routed : 0 11 11 11 11 11 11 11 11 11 11 11 11 11
|
||||
Vias : 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
Resolution : 15.4 % 100.0 % 100.0 % 100.0 % 100.0 % 100.0 % 100.0 % 100.0 % 100.0 % 100.0 % 100.0 % 100.0 % 100.0 % 100.0 %
|
||||
|
||||
Final : 100.0% finished
|
||||
5255
Eagle/soilSensor.s#1
Normal file
5255
Eagle/soilSensor.s#1
Normal file
File diff suppressed because it is too large
Load Diff
5255
Eagle/soilSensor.s#2
Normal file
5255
Eagle/soilSensor.s#2
Normal file
File diff suppressed because it is too large
Load Diff
5254
Eagle/soilSensor.s#3
Normal file
5254
Eagle/soilSensor.s#3
Normal file
File diff suppressed because it is too large
Load Diff
1699
Eagle/soilSensor.s#4
Normal file
1699
Eagle/soilSensor.s#4
Normal file
File diff suppressed because it is too large
Load Diff
1612
Eagle/soilSensor.s#5
Normal file
1612
Eagle/soilSensor.s#5
Normal file
File diff suppressed because it is too large
Load Diff
1636
Eagle/soilSensor.s#6
Normal file
1636
Eagle/soilSensor.s#6
Normal file
File diff suppressed because it is too large
Load Diff
1578
Eagle/soilSensor.s#7
Normal file
1578
Eagle/soilSensor.s#7
Normal file
File diff suppressed because it is too large
Load Diff
1530
Eagle/soilSensor.s#8
Normal file
1530
Eagle/soilSensor.s#8
Normal file
File diff suppressed because it is too large
Load Diff
1531
Eagle/soilSensor.s#9
Normal file
1531
Eagle/soilSensor.s#9
Normal file
File diff suppressed because it is too large
Load Diff
5245
Eagle/soilSensor.sch
Normal file
5245
Eagle/soilSensor.sch
Normal file
File diff suppressed because it is too large
Load Diff
275
Eagle/soilSensor/eagle.epf
Normal file
275
Eagle/soilSensor/eagle.epf
Normal file
@@ -0,0 +1,275 @@
|
||||
[Eagle]
|
||||
Version="09 06 02"
|
||||
Platform="Windows"
|
||||
Globals="Globals"
|
||||
Desktop="Desktop"
|
||||
|
||||
[Globals]
|
||||
AutoSaveProject=1
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:79"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:88"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:201"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:202"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:203"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:204"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:206"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:208"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:210"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:211"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:212"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:213"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:89"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:214"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:215"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:218"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:220"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:221"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:222"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:224"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:226"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:229"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:230"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:90"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:231"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:233"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:234"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:235"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:236"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:237"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:238"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:239"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:240"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:241"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:93"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:242"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:243"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:244"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:245"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:248"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:250"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:251"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:252"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:255"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:256"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:94"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:258"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:259"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:260"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:261"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:262"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:263"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:264"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:265"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:266"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:267"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:96"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:269"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:270"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:272"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:273"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:274"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:275"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:276"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:278"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:279"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:281"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:97"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:283"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:284"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:285"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:287"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:288"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:289"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:290"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:291"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:293"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:294"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:98"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:296"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:297"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:298"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:300"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:302"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:303"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:304"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:307"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:312"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:313"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:99"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:314"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:315"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:317"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:318"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:319"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:320"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:322"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:323"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:324"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:325"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:102"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:327"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:328"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:331"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:332"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:334"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:335"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:336"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:337"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:338"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:339"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:80"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:105"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:341"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:342"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:343"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:344"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:346"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:347"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:350"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:351"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:353"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:358"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:107"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:360"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:361"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:362"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:364"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:366"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:367"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:368"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:369"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:371"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:372"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:109"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:373"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:374"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:375"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:376"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:377"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:378"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:379"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:380"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:382"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:383"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:111"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:385"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:386"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:387"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:388"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:389"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:391"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:392"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:393"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:394"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:396"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:112"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:397"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:398"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:399"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:400"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:401"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:402"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:403"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:404"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:407"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:408"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:113"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:409"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:410"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:411"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:412"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:417"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:418"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:419"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:527439"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:5610454"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:10015156"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:116"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:117"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:118"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:119"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:81"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:121"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:122"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:123"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:124"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:125"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:126"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:127"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:128"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:129"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:130"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:82"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:132"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:133"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:134"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:135"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:136"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:138"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:139"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:140"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:141"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:143"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:83"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:144"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:145"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:147"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:148"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:149"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:150"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:151"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:152"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:153"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:154"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:84"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:155"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:156"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:157"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:158"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:159"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:160"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:161"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:162"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:163"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:164"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:85"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:165"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:166"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:167"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:169"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:170"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:171"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:172"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:173"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:174"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:175"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:86"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:176"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:177"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:179"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:180"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:181"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:182"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:183"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:184"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:188"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:189"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:87"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:190"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:191"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:192"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:193"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:194"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:195"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:196"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:197"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:198"
|
||||
UsedLibraryUrn="urn:adsk.eagle:library:199"
|
||||
|
||||
[Win_1]
|
||||
Type="Control Panel"
|
||||
Number=0
|
||||
|
||||
[Desktop]
|
||||
Screen="5120 1440"
|
||||
Window="Win_1"
|
||||
9
README.txt
Normal file
9
README.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
building and deploying:
|
||||
|
||||
docker build -t houseplant-client:latest .
|
||||
docker image tag houseplant-client 192.168.1.193:5000/houseplant-client
|
||||
docker push 192.168.1.193:5000/houseplant-client
|
||||
|
||||
docker build --no-cache -t houseplant-api:latest .
|
||||
docker image tag houseplant-api 192.168.1.193:5000/houseplant-api
|
||||
docker push 192.168.1.193:5000/houseplant-api
|
||||
61
batteryValidationTest/batteryValidationTest.ino
Normal file
61
batteryValidationTest/batteryValidationTest.ino
Normal file
@@ -0,0 +1,61 @@
|
||||
// XAIO-ESP32-C6 Battery Monitor with Averaged ADC Readings
|
||||
// Measures the battery voltage via a resistor divider (two 220 kΩ resistors),
|
||||
// averages 10 ADC readings, and reports the battery percentage.
|
||||
// Battery assumed: single-cell LiPo with 3.0V (0%) to 4.2V (100%).
|
||||
|
||||
// ----- CONFIGURATION -----
|
||||
const int analogPin = A0; // ADC pin connected to the resistor divider
|
||||
const float adcRefVoltage = 3.3; // ADC reference voltage (Volts)
|
||||
const int adcMaxValue = 4095; // Maximum ADC value (for 12-bit resolution)
|
||||
const float voltageDividerFactor = 2.0; // Divider factor: battery voltage is divided by 2
|
||||
|
||||
// Battery voltage range settings (adjust as necessary)
|
||||
const float batteryMinVoltage = 3.0; // Voltage corresponding to 0% battery
|
||||
const float batteryMaxVoltage = 4.2; // Voltage corresponding to 100% battery
|
||||
|
||||
const int numSamples = 10; // Number of ADC samples to average
|
||||
|
||||
// ----- SETUP -----
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
// If needed, additional ADC calibration or configuration can be added here
|
||||
}
|
||||
|
||||
// ----- MAIN LOOP -----
|
||||
void loop() {
|
||||
long totalADC = 0;
|
||||
|
||||
// Take numSamples readings
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
totalADC += analogRead(analogPin);
|
||||
delay(10); // Short delay between samples (adjust as needed)
|
||||
}
|
||||
|
||||
// Calculate the average ADC value
|
||||
float avgADC = totalADC / (float)numSamples;
|
||||
|
||||
// Convert the averaged ADC value to the measured voltage at the divider
|
||||
float measuredVoltage = (avgADC / adcMaxValue) * adcRefVoltage;
|
||||
|
||||
// Calculate the actual battery voltage using the divider factor
|
||||
float batteryVoltage = measuredVoltage * voltageDividerFactor;
|
||||
|
||||
// Map the battery voltage to a battery percentage using a linear mapping
|
||||
float batteryPercent = ((batteryVoltage - batteryMinVoltage) / (batteryMaxVoltage - batteryMinVoltage)) * 100.0;
|
||||
if (batteryPercent > 100.0) batteryPercent = 100.0;
|
||||
if (batteryPercent < 0.0) batteryPercent = 0.0;
|
||||
|
||||
// Print the results to the Serial Monitor
|
||||
Serial.print("Avg ADC Value: ");
|
||||
Serial.print(avgADC, 2);
|
||||
Serial.print(" | Divider Voltage: ");
|
||||
Serial.print(measuredVoltage, 2);
|
||||
Serial.print(" V | Battery Voltage: ");
|
||||
Serial.print(batteryVoltage, 2);
|
||||
Serial.print(" V | Battery Percent: ");
|
||||
Serial.print(batteryPercent, 1);
|
||||
Serial.println("%");
|
||||
|
||||
// Wait 2 seconds before the next reading
|
||||
delay(2000);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
]
|
||||
}
|
||||
@@ -1,405 +0,0 @@
|
||||
#include <Arduino.h>
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLE2902.h>
|
||||
#include <EEPROM.h>
|
||||
#include <esp_sleep.h>
|
||||
|
||||
// ======== BLE UUIDs ========
|
||||
#define SERVICE_UUID "12345678-1234-1234-1234-123456789abc"
|
||||
#define CHARACTERISTIC_UUID "abcd1234-5678-1234-5678-abcdef123456"
|
||||
|
||||
// ======== Hardware & ADC ========
|
||||
#define MOISTURE_PIN A0
|
||||
#define LED_PIN 2 // Example LED pin (built-in LED on many ESP32 boards)
|
||||
#define BUTTON_PIN D4 // GPIO 4 for calibration button (recommended over GPIO 0)
|
||||
|
||||
// ======== Constants ========
|
||||
const int DRY_VALUE_DEFAULT = 500;
|
||||
const int WET_VALUE_DEFAULT = 200;
|
||||
const int ADC_RESOLUTION = 10; // 10-bit ADC (0–1023)
|
||||
|
||||
// ======== Reading & Averaging ========
|
||||
const int NUM_SAMPLES = 5; // Number of samples to average
|
||||
const int SAMPLE_DELAY_MS = 50; // Delay between each sample (ms)
|
||||
|
||||
// ======== Connection Timeout Settings ========
|
||||
const unsigned long CONNECTION_TIMEOUT_MS = 120000; // 2 minutes to establish connection
|
||||
unsigned long connectionStartTime = 0;
|
||||
bool connectionTimerStarted = false;
|
||||
|
||||
// ======== Sleep Settings ========
|
||||
const uint64_t DEEP_SLEEP_DURATION_US = 60ULL * 60ULL * 1000000ULL; // 1 hour in microseconds
|
||||
|
||||
// ======== Calibration Variables ========
|
||||
int dryValue = DRY_VALUE_DEFAULT; // Initialize with default dry value
|
||||
int wetValue = WET_VALUE_DEFAULT; // Initialize with default wet value
|
||||
|
||||
// ======== Device Configuration ========
|
||||
const char* deviceName = "003-SoilSensor"; // Default device name
|
||||
|
||||
// ======== EEPROM Addresses ========
|
||||
const int EEPROM_SIZE = 512;
|
||||
const int EEPROM_DRY_ADDRESS = 0; // Bytes 0-3
|
||||
const int EEPROM_WET_ADDRESS = 4; // Bytes 4-7
|
||||
|
||||
// ======== BLE Objects ========
|
||||
BLEServer* pServer = nullptr;
|
||||
BLECharacteristic* pCharacteristic = nullptr;
|
||||
|
||||
// ======== Device State Flags ========
|
||||
bool isConnected = false;
|
||||
bool dataSent = false;
|
||||
|
||||
// ======== Calibration Variables ========
|
||||
enum DeviceState {
|
||||
STATE_NORMAL,
|
||||
STATE_CALIBRATE_NOT_IN_SOIL,
|
||||
STATE_CALIBRATE_IN_SOIL
|
||||
};
|
||||
|
||||
DeviceState currentState = STATE_NORMAL;
|
||||
int calibrationCount = 0;
|
||||
|
||||
// ======== Button Handling ========
|
||||
const unsigned long DEBOUNCE_DELAY = 50; // 50ms debounce delay
|
||||
bool lastButtonState = HIGH; // Assuming pull-up resistor
|
||||
bool buttonPressed = false;
|
||||
unsigned long lastDebounceTime = 0;
|
||||
|
||||
// ======== LED Blinking Parameters ========
|
||||
unsigned long previousBlinkTime = 0;
|
||||
const unsigned long BLINK_ON_DURATION = 200; // 200ms ON
|
||||
const unsigned long BLINK_OFF_DURATION = 100; // 100ms OFF
|
||||
bool ledState = false;
|
||||
|
||||
// ======== Function Prototypes ========
|
||||
int readSoilMoisture();
|
||||
int getAveragedMoisture(int numSamples);
|
||||
void enterDeepSleep();
|
||||
void handleCalibrationButtonPress();
|
||||
void readCalibrationValues();
|
||||
void writeCalibrationValues(int dry, int wet);
|
||||
void checkButtonPress();
|
||||
void updateLED();
|
||||
|
||||
// ======== Custom Server Callbacks ========
|
||||
class MyServerCallbacks : public BLEServerCallbacks {
|
||||
void onConnect(BLEServer* pServer) override {
|
||||
Serial.println("Client connected.");
|
||||
isConnected = true;
|
||||
|
||||
// Turn on LED to indicate active state
|
||||
digitalWrite(LED_PIN, HIGH);
|
||||
|
||||
// Stop connection timeout timer
|
||||
connectionTimerStarted = false;
|
||||
connectionStartTime = 0;
|
||||
}
|
||||
|
||||
void onDisconnect(BLEServer* pServer) override {
|
||||
Serial.println("Client disconnected.");
|
||||
isConnected = false;
|
||||
dataSent = false;
|
||||
|
||||
// Restart advertising to allow new connections
|
||||
pServer->startAdvertising();
|
||||
|
||||
// Restart connection timeout timer
|
||||
connectionTimerStarted = true;
|
||||
connectionStartTime = millis();
|
||||
|
||||
// Turn off LED to indicate sleep state
|
||||
digitalWrite(LED_PIN, LOW);
|
||||
}
|
||||
};
|
||||
|
||||
// ======== Sensor Reading Functions ========
|
||||
/**
|
||||
* Perform a single soil moisture reading (0–100%).
|
||||
*/
|
||||
int readSoilMoisture() {
|
||||
int rawValue = analogRead(MOISTURE_PIN);
|
||||
int percent = map(rawValue, dryValue, wetValue, 0, 100);
|
||||
return constrain(percent, 0, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers multiple samples, applies a small delay between each,
|
||||
* averages them, and returns the final moisture value.
|
||||
*/
|
||||
int getAveragedMoisture(int numSamples) {
|
||||
long sum = 0;
|
||||
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
sum += readSoilMoisture();
|
||||
delay(SAMPLE_DELAY_MS); // Small delay between samples
|
||||
}
|
||||
|
||||
int avg = (int)(sum / numSamples);
|
||||
return avg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter deep sleep mode for the defined duration.
|
||||
*/
|
||||
void enterDeepSleep() {
|
||||
Serial.println("Entering deep sleep for 1 hour...");
|
||||
|
||||
// Turn off LED to indicate sleep state
|
||||
digitalWrite(LED_PIN, LOW);
|
||||
|
||||
// Disable BLE before sleeping
|
||||
BLEDevice::deinit(true);
|
||||
|
||||
// Configure deep sleep wake-up time
|
||||
esp_sleep_enable_timer_wakeup(DEEP_SLEEP_DURATION_US);
|
||||
|
||||
// Enter deep sleep
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle calibration button presses to manage calibration steps.
|
||||
*/
|
||||
void handleCalibrationButtonPress() {
|
||||
calibrationCount++;
|
||||
|
||||
switch (calibrationCount) {
|
||||
case 1:
|
||||
// Calibration Step 1: Record Dry Value
|
||||
Serial.println("Calibration Step 1: Place device in dry soil and press button.");
|
||||
currentState = STATE_CALIBRATE_NOT_IN_SOIL;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Calibration Step 2: Record Wet Value
|
||||
Serial.println("Calibration Step 2: Place device in wet soil and press button.");
|
||||
currentState = STATE_CALIBRATE_IN_SOIL;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// Calibration Complete
|
||||
Serial.println("Calibration Complete. Returning to normal operation.");
|
||||
currentState = STATE_NORMAL;
|
||||
calibrationCount = 0; // Reset for next calibration
|
||||
break;
|
||||
|
||||
default:
|
||||
// Reset Calibration
|
||||
Serial.println("Calibration Reset. Returning to normal operation.");
|
||||
currentState = STATE_NORMAL;
|
||||
calibrationCount = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
updateLED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read calibration values from EEPROM.
|
||||
*/
|
||||
void readCalibrationValues() {
|
||||
EEPROM.get(EEPROM_DRY_ADDRESS, dryValue);
|
||||
EEPROM.get(EEPROM_WET_ADDRESS, wetValue);
|
||||
|
||||
// Validate calibration values
|
||||
if (dryValue == 0xFFFFFFFF || wetValue == 0xFFFFFFFF) {
|
||||
dryValue = DRY_VALUE_DEFAULT;
|
||||
wetValue = WET_VALUE_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write calibration values to EEPROM.
|
||||
*/
|
||||
void writeCalibrationValues(int dry, int wet) {
|
||||
EEPROM.put(EEPROM_DRY_ADDRESS, dry);
|
||||
EEPROM.put(EEPROM_WET_ADDRESS, wet);
|
||||
EEPROM.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for button presses with debouncing.
|
||||
*/
|
||||
void checkButtonPress() {
|
||||
bool reading = digitalRead(BUTTON_PIN);
|
||||
|
||||
if (reading != lastButtonState) {
|
||||
lastDebounceTime = millis();
|
||||
}
|
||||
|
||||
if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY) {
|
||||
if (reading == LOW && lastButtonState == HIGH) {
|
||||
buttonPressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
lastButtonState = reading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update LED based on the current state.
|
||||
*/
|
||||
void updateLED() {
|
||||
// Reset LED parameters
|
||||
previousBlinkTime = millis();
|
||||
ledState = false;
|
||||
|
||||
switch (currentState) {
|
||||
case STATE_NORMAL:
|
||||
// LED is handled by BLE connection callbacks
|
||||
digitalWrite(LED_PIN, LOW); // Ensure LED is off in normal mode
|
||||
break;
|
||||
|
||||
case STATE_CALIBRATE_NOT_IN_SOIL:
|
||||
// Solid LED to indicate calibration for dry soil
|
||||
digitalWrite(LED_PIN, HIGH);
|
||||
break;
|
||||
|
||||
case STATE_CALIBRATE_IN_SOIL:
|
||||
// Start blinking in 2:1 ratio for wet soil calibration
|
||||
digitalWrite(LED_PIN, HIGH); // Start with LED ON
|
||||
ledState = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize EEPROM and read calibration values.
|
||||
*/
|
||||
void initializeEEPROM() {
|
||||
if (!EEPROM.begin(EEPROM_SIZE)) {
|
||||
Serial.println("Failed to initialize EEPROM");
|
||||
// Handle EEPROM initialization failure if necessary
|
||||
}
|
||||
readCalibrationValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update mapping based on calibrated dry and wet values.
|
||||
*/
|
||||
int mapMoisture(int rawValue) {
|
||||
int percent = map(rawValue, dryValue, wetValue, 0, 100);
|
||||
return constrain(percent, 0, 100);
|
||||
}
|
||||
|
||||
// ======== Setup Function ========
|
||||
void setup() {
|
||||
// Initialize Serial
|
||||
Serial.begin(115200);
|
||||
delay(1000); // Allow time for serial to initialize
|
||||
|
||||
// Initialize LED pin
|
||||
pinMode(LED_PIN, OUTPUT);
|
||||
digitalWrite(LED_PIN, LOW); // LED off initially
|
||||
|
||||
// Initialize Button pin
|
||||
pinMode(BUTTON_PIN, INPUT_PULLUP); // Use INPUT_PULLUP if using internal resistor
|
||||
// If using external resistor, use INPUT instead:
|
||||
// pinMode(BUTTON_PIN, INPUT);
|
||||
|
||||
// Initialize EEPROM
|
||||
initializeEEPROM();
|
||||
Serial.printf("Calibrated Dry Value: %d\n", dryValue);
|
||||
Serial.printf("Calibrated Wet Value: %d\n", wetValue);
|
||||
|
||||
// Initialize BLE with deviceName variable
|
||||
BLEDevice::init(deviceName); // Initialize BLE with the variable device name
|
||||
pServer = BLEDevice::createServer();
|
||||
pServer->setCallbacks(new MyServerCallbacks());
|
||||
|
||||
// Create BLE service
|
||||
BLEService* pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
// Create BLE characteristic
|
||||
pCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID,
|
||||
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
|
||||
);
|
||||
|
||||
// Add descriptor for notifications
|
||||
pCharacteristic->addDescriptor(new BLE2902());
|
||||
|
||||
// Start the service
|
||||
pService->start();
|
||||
|
||||
// Start advertising
|
||||
pServer->getAdvertising()->addServiceUUID(SERVICE_UUID);
|
||||
pServer->getAdvertising()->setScanResponse(true);
|
||||
pServer->getAdvertising()->start();
|
||||
|
||||
Serial.println("BLE server is running...");
|
||||
|
||||
// Start connection timeout timer
|
||||
connectionTimerStarted = true;
|
||||
connectionStartTime = millis();
|
||||
|
||||
// Initialize state and LED
|
||||
currentState = STATE_NORMAL;
|
||||
calibrationCount = 0;
|
||||
updateLED();
|
||||
}
|
||||
|
||||
// ======== Loop Function ========
|
||||
void loop() {
|
||||
// Check for button presses
|
||||
checkButtonPress();
|
||||
|
||||
if (buttonPressed) {
|
||||
buttonPressed = false;
|
||||
if (currentState != STATE_NORMAL) {
|
||||
// Handle calibration step
|
||||
handleCalibrationButtonPress();
|
||||
} else {
|
||||
// Initiate calibration
|
||||
Serial.println("Button pressed. Initiating calibration mode.");
|
||||
currentState = STATE_CALIBRATE_NOT_IN_SOIL;
|
||||
calibrationCount = 1;
|
||||
updateLED();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long currentTime = millis();
|
||||
|
||||
// ======== Connection Timeout Logic ========
|
||||
if (connectionTimerStarted) {
|
||||
if (currentTime - connectionStartTime >= CONNECTION_TIMEOUT_MS) {
|
||||
Serial.println("No connection within 2 minutes. Entering deep sleep.");
|
||||
enterDeepSleep();
|
||||
}
|
||||
}
|
||||
|
||||
// ======== BLE Connected Logic ========
|
||||
if (isConnected && !dataSent && currentState == STATE_NORMAL) {
|
||||
// Take sensor readings
|
||||
int rawMoisture = analogRead(MOISTURE_PIN);
|
||||
int moistureAvg = mapMoisture(rawMoisture);
|
||||
Serial.printf("Averaged Soil Moisture: %d%%\n", moistureAvg);
|
||||
|
||||
// Update the characteristic and notify the client
|
||||
pCharacteristic->setValue(moistureAvg);
|
||||
pCharacteristic->notify();
|
||||
|
||||
dataSent = true;
|
||||
|
||||
// Immediately enter deep sleep after sending data
|
||||
enterDeepSleep();
|
||||
}
|
||||
|
||||
// ======== LED Update ========
|
||||
updateLED();
|
||||
|
||||
// ======== LED Blinking Logic ========
|
||||
if (currentState == STATE_CALIBRATE_IN_SOIL) {
|
||||
if (millis() - previousBlinkTime >= (ledState ? BLINK_ON_DURATION : BLINK_OFF_DURATION)) {
|
||||
ledState = !ledState;
|
||||
digitalWrite(LED_PIN, ledState ? HIGH : LOW);
|
||||
previousBlinkTime = millis();
|
||||
}
|
||||
}
|
||||
|
||||
// Short delay to prevent watchdog resets
|
||||
delay(100);
|
||||
}
|
||||
327
soilProbeNode/soilProbeNode.ino
Normal file
327
soilProbeNode/soilProbeNode.ino
Normal file
@@ -0,0 +1,327 @@
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
#include <EEPROM.h>
|
||||
#include "esp_sleep.h"
|
||||
|
||||
// ***** Configuration *****
|
||||
// Pins:
|
||||
#define BATTERY_PIN A0 // Battery voltage reading via a resistor divider
|
||||
#define SOIL_PIN A2 // Soil moisture sensor analog reading
|
||||
#define BUTTON_PIN D1 // Button on D1, active LOW (internal pull-up)
|
||||
|
||||
// EEPROM settings: 4 bytes (2 for dry, 2 for wet)
|
||||
#define EEPROM_SIZE 4
|
||||
#define EEPROM_DRY_ADDR 0
|
||||
#define EEPROM_WET_ADDR 2
|
||||
|
||||
// Sampling settings:
|
||||
#define BATTERY_SAMPLES 10
|
||||
#define SOIL_SAMPLES 20
|
||||
#define SAMPLE_INTERVAL 100 // 0.1 seconds between soil samples
|
||||
#define NORMAL_DELAY 2000 // 2 seconds delay between normal readings
|
||||
#define CAL_DELAY 10000 // 10-second delay before calibration reading
|
||||
|
||||
// Deep sleep settings:
|
||||
#define SLEEP_DURATION_HOURS 6
|
||||
#define AWAKE_MAX_MS 300000 // 5 minutes in milliseconds
|
||||
|
||||
// Battery voltage parameters (assuming 220kΩ divider, two equal resistors)
|
||||
#define ADC_MAX 4095.0
|
||||
#define VREF 3.3
|
||||
#define VOLTAGE_DIVIDER_FACTOR 2.0
|
||||
#define BATTERY_FULL 4.2
|
||||
#define BATTERY_EMPTY 3.0
|
||||
|
||||
// ***** BLE Settings *****
|
||||
#define SERVICE_UUID "12345678-1234-1234-1234-123456789abc"
|
||||
#define MOISTURE_CHAR_UUID "abcd1234-5678-1234-5678-abcdef123456"
|
||||
#define BATTERY_CHAR_UUID "fedcba98-7654-3210-0123-456789abcdef"
|
||||
|
||||
// ***** Default Calibration Values *****
|
||||
// These defaults are used if no valid calibration exists in EEPROM.
|
||||
#define DEFAULT_DRY_CAL 3000 // Higher raw reading for dry soil
|
||||
#define DEFAULT_WET_CAL 1000 // Lower raw reading for wet soil
|
||||
|
||||
// ***** Global Calibration Variables *****
|
||||
int dryCal = 0;
|
||||
int wetCal = 0;
|
||||
|
||||
// Calibration mode states
|
||||
enum CalState {
|
||||
CAL_OFF,
|
||||
CAL_DRY_PENDING,
|
||||
CAL_WET_PENDING,
|
||||
CAL_DONE
|
||||
};
|
||||
CalState calState = CAL_OFF;
|
||||
|
||||
// Debounce for button on BUTTON_PIN
|
||||
bool buttonPressed = false;
|
||||
unsigned long lastButtonPress = 0;
|
||||
const unsigned long debounceDelay = 50; // 50 ms
|
||||
|
||||
// Operation mode: NORMAL_MODE or CALIBRATION_MODE
|
||||
enum OperationMode {
|
||||
NORMAL_MODE,
|
||||
CALIBRATION_MODE
|
||||
};
|
||||
OperationMode opMode = NORMAL_MODE;
|
||||
|
||||
// ***** BLE Server Variables *****
|
||||
BLEServer* pServer = nullptr;
|
||||
BLECharacteristic* pMoistureCharacteristic = nullptr;
|
||||
BLECharacteristic* pBatteryCharacteristic = nullptr;
|
||||
|
||||
// ***** Time Tracking *****
|
||||
unsigned long awakeStartTime = 0;
|
||||
|
||||
// ***** Helper Functions *****
|
||||
|
||||
// Read battery voltage: average BATTERY_SAMPLES, convert reading to voltage, then map to a percentage.
|
||||
float readBatteryVoltage() {
|
||||
long total = 0;
|
||||
for (int i = 0; i < BATTERY_SAMPLES; i++) {
|
||||
total += analogRead(BATTERY_PIN);
|
||||
delay(10);
|
||||
}
|
||||
float avg = total / (float)BATTERY_SAMPLES;
|
||||
float v_adc = (avg * VREF) / ADC_MAX;
|
||||
float v_bat = v_adc * VOLTAGE_DIVIDER_FACTOR;
|
||||
return v_bat;
|
||||
}
|
||||
|
||||
int batteryPercentage(float voltage) {
|
||||
int percent = (int)(((voltage - BATTERY_EMPTY) / (BATTERY_FULL - BATTERY_EMPTY)) * 100);
|
||||
if (percent < 0) percent = 0;
|
||||
if (percent > 100) percent = 100;
|
||||
return percent;
|
||||
}
|
||||
|
||||
// Read soil moisture raw value: average SOIL_SAMPLES spaced by SAMPLE_INTERVAL ms.
|
||||
int readSoilRaw() {
|
||||
long sum = 0;
|
||||
for (int i = 0; i < SOIL_SAMPLES; i++) {
|
||||
sum += analogRead(SOIL_PIN);
|
||||
delay(SAMPLE_INTERVAL);
|
||||
}
|
||||
return sum / SOIL_SAMPLES;
|
||||
}
|
||||
|
||||
// Map raw soil moisture value to percentage using calibration values.
|
||||
int mapSoilMoisture(int raw) {
|
||||
if (dryCal == wetCal) return 0;
|
||||
int percent = map(raw, dryCal, wetCal, 0, 100);
|
||||
percent = constrain(percent, 0, 100);
|
||||
return percent;
|
||||
}
|
||||
|
||||
// Update BLE characteristics with current battery and soil moisture data.
|
||||
void updateBLE() {
|
||||
int soilRaw = readSoilRaw();
|
||||
int moisturePercent = 0;
|
||||
if (dryCal != 0 && wetCal != 0 && dryCal > wetCal) {
|
||||
moisturePercent = mapSoilMoisture(soilRaw);
|
||||
}
|
||||
|
||||
float v_bat = readBatteryVoltage();
|
||||
int batPercent = batteryPercentage(v_bat);
|
||||
|
||||
String moistureStr = String(moisturePercent);
|
||||
String batteryStr = String(batPercent);
|
||||
|
||||
pMoistureCharacteristic->setValue(moistureStr.c_str());
|
||||
pMoistureCharacteristic->notify();
|
||||
|
||||
pBatteryCharacteristic->setValue(batteryStr.c_str());
|
||||
pBatteryCharacteristic->notify();
|
||||
|
||||
Serial.print("Battery: ");
|
||||
Serial.print(v_bat);
|
||||
Serial.print(" V (");
|
||||
Serial.print(batPercent);
|
||||
Serial.println("%)");
|
||||
Serial.print("Soil raw: ");
|
||||
Serial.print(soilRaw);
|
||||
Serial.print(" Moisture: ");
|
||||
Serial.print(moisturePercent);
|
||||
Serial.println("%");
|
||||
}
|
||||
|
||||
// Enter deep sleep for SLEEP_DURATION_HOURS hours.
|
||||
void enterDeepSleep() {
|
||||
Serial.println("Entering deep sleep.");
|
||||
uint64_t sleepTimeUs = SLEEP_DURATION_HOURS * 3600ULL * 1000000ULL;
|
||||
esp_deep_sleep(sleepTimeUs);
|
||||
}
|
||||
|
||||
// ----- Calibration Functions -----
|
||||
// Wait CAL_DELAY ms then take SOIL_SAMPLES samples and return the average raw value.
|
||||
int calibrateSensor() {
|
||||
delay(CAL_DELAY);
|
||||
return readSoilRaw();
|
||||
}
|
||||
|
||||
// Check the state of the calibration button on BUTTON_PIN and update calibration state.
|
||||
void checkButton() {
|
||||
if (digitalRead(BUTTON_PIN) == LOW) { // Button pressed
|
||||
if (!buttonPressed && (millis() - lastButtonPress > debounceDelay)) {
|
||||
buttonPressed = true;
|
||||
lastButtonPress = millis();
|
||||
|
||||
// Cycle through calibration states
|
||||
if (calState == CAL_OFF) {
|
||||
calState = CAL_DRY_PENDING;
|
||||
Serial.println("Button pressed: Entering Dry Calibration Mode. Ensure sensor is in dry soil.");
|
||||
} else if (calState == CAL_DRY_PENDING) {
|
||||
calState = CAL_WET_PENDING;
|
||||
Serial.println("Button pressed: Entering Wet Calibration Mode. Place sensor in wet soil.");
|
||||
} else if (calState == CAL_WET_PENDING) {
|
||||
calState = CAL_DONE;
|
||||
Serial.println("Button pressed: Finalizing Calibration.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buttonPressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Runs the calibration sequence.
|
||||
// In Dry mode: after delay, take sample and assign dryCal.
|
||||
// In Wet mode: similarly assign wetCal.
|
||||
void runCalibration() {
|
||||
if (calState == CAL_DRY_PENDING) {
|
||||
Serial.println("Starting Dry Calibration...");
|
||||
int dryValue = calibrateSensor();
|
||||
dryCal = dryValue;
|
||||
Serial.print("Dry calibration value: ");
|
||||
Serial.println(dryCal);
|
||||
// Wait for next button press to proceed to wet calibration.
|
||||
while (calState == CAL_DRY_PENDING) {
|
||||
checkButton();
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
if (calState == CAL_WET_PENDING) {
|
||||
Serial.println("Starting Wet Calibration...");
|
||||
int wetValue = calibrateSensor();
|
||||
wetCal = wetValue;
|
||||
Serial.print("Wet calibration value: ");
|
||||
Serial.println(wetCal);
|
||||
while (calState == CAL_WET_PENDING) {
|
||||
checkButton();
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
if (calState == CAL_DONE) {
|
||||
// Save calibration values to EEPROM
|
||||
EEPROM.write(EEPROM_DRY_ADDR, lowByte(dryCal));
|
||||
EEPROM.write(EEPROM_DRY_ADDR + 1, highByte(dryCal));
|
||||
EEPROM.write(EEPROM_WET_ADDR, lowByte(wetCal));
|
||||
EEPROM.write(EEPROM_WET_ADDR + 1, highByte(wetCal));
|
||||
EEPROM.commit();
|
||||
Serial.println("Calibration saved. Exiting calibration mode.");
|
||||
calState = CAL_OFF;
|
||||
opMode = NORMAL_MODE;
|
||||
}
|
||||
}
|
||||
|
||||
// ----- BLE Server Setup -----
|
||||
void setupBLEServer() {
|
||||
BLEDevice::init("SoilSensor_XIAO");
|
||||
pServer = BLEDevice::createServer();
|
||||
BLEService* pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
pMoistureCharacteristic = pService->createCharacteristic(
|
||||
MOISTURE_CHAR_UUID,
|
||||
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
|
||||
);
|
||||
pMoistureCharacteristic->setValue("0");
|
||||
|
||||
pBatteryCharacteristic = pService->createCharacteristic(
|
||||
BATTERY_CHAR_UUID,
|
||||
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
|
||||
);
|
||||
pBatteryCharacteristic->setValue("0");
|
||||
|
||||
pService->start();
|
||||
|
||||
BLEAdvertising* pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(true);
|
||||
pAdvertising->setMinPreferred(0x06);
|
||||
pAdvertising->setMinPreferred(0x12);
|
||||
BLEDevice::startAdvertising();
|
||||
|
||||
Serial.println("BLE Server started and advertising.");
|
||||
}
|
||||
|
||||
// ----- Main Setup and Loop -----
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Booting Sensor Node...");
|
||||
|
||||
EEPROM.begin(EEPROM_SIZE);
|
||||
|
||||
pinMode(BATTERY_PIN, INPUT);
|
||||
pinMode(SOIL_PIN, INPUT);
|
||||
pinMode(BUTTON_PIN, INPUT_PULLUP);
|
||||
|
||||
// Initialize BLE Server so sensor values can be advertised
|
||||
setupBLEServer();
|
||||
|
||||
// Load calibration from EEPROM
|
||||
int storedDry = EEPROM.read(EEPROM_DRY_ADDR) | (EEPROM.read(EEPROM_DRY_ADDR + 1) << 8);
|
||||
int storedWet = EEPROM.read(EEPROM_WET_ADDR) | (EEPROM.read(EEPROM_WET_ADDR + 1) << 8);
|
||||
// Check validity: dry value should be higher than wet value and nonzero.
|
||||
if (storedDry > storedWet && storedDry > 0 && storedWet > 0) {
|
||||
dryCal = storedDry;
|
||||
wetCal = storedWet;
|
||||
Serial.print("Loaded Calibration - Dry: ");
|
||||
Serial.print(dryCal);
|
||||
Serial.print(" Wet: ");
|
||||
Serial.println(wetCal);
|
||||
opMode = NORMAL_MODE;
|
||||
} else {
|
||||
// Use default calibration values if none are valid.
|
||||
dryCal = DEFAULT_DRY_CAL;
|
||||
wetCal = DEFAULT_WET_CAL;
|
||||
Serial.println("No valid calibration found. Using default calibration values.");
|
||||
Serial.print("Default Dry: ");
|
||||
Serial.print(dryCal);
|
||||
Serial.print(" Default Wet: ");
|
||||
Serial.println(wetCal);
|
||||
opMode = NORMAL_MODE; // Allow device to be usable even without calibration.
|
||||
}
|
||||
|
||||
awakeStartTime = millis();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Check for button press to trigger calibration mode.
|
||||
checkButton();
|
||||
if (digitalRead(BUTTON_PIN) == LOW && opMode == NORMAL_MODE) {
|
||||
// If button is pressed while in normal mode, enter calibration mode.
|
||||
Serial.println("Button pressed while awake. Entering Calibration Mode.");
|
||||
opMode = CALIBRATION_MODE;
|
||||
calState = CAL_DRY_PENDING;
|
||||
}
|
||||
|
||||
if (opMode == CALIBRATION_MODE) {
|
||||
runCalibration();
|
||||
awakeStartTime = millis(); // reset awake time after calibration
|
||||
return;
|
||||
}
|
||||
|
||||
// NORMAL MODE
|
||||
updateBLE();
|
||||
|
||||
// If more than 5 minutes have elapsed, go to deep sleep for 6 hours.
|
||||
if (millis() - awakeStartTime >= AWAKE_MAX_MS) {
|
||||
Serial.println("5 minutes elapsed; going to deep sleep.");
|
||||
enterDeepSleep();
|
||||
}
|
||||
|
||||
delay(NORMAL_DELAY);
|
||||
}
|
||||
Reference in New Issue
Block a user