Add new code for battery validation, updated probe node code added base read me
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
<b>User Projects</b>
|
||||||
|
<p>
|
||||||
|
This folder contains user Projects files.
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||||
@@ -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
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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