├── 3D print files
└── Solar Powered Weather Station III by Rob Latour - Download free STL model - Printables.com.url
├── Images
├── AttemptIII.jpg
└── AttemptIandII.jpg
├── Node-Red
├── Node-Red-Flow.jpg
└── Report_To_PWSWeather_Flow.json
├── README.md
└── SolarWeatherStationIII
├── SolarWeatherStationIII.ino
├── general.h
└── secrets.h
/3D print files/Solar Powered Weather Station III by Rob Latour - Download free STL model - Printables.com.url:
--------------------------------------------------------------------------------
1 | [InternetShortcut]
2 | URL=https://www.printables.com/model/694171-solar-powered-weather-station-iii
3 |
--------------------------------------------------------------------------------
/Images/AttemptIII.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roblatour/SolarWeatherStationIII/211e758f30c93d589871cd3cdf31dac011ac5360/Images/AttemptIII.jpg
--------------------------------------------------------------------------------
/Images/AttemptIandII.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roblatour/SolarWeatherStationIII/211e758f30c93d589871cd3cdf31dac011ac5360/Images/AttemptIandII.jpg
--------------------------------------------------------------------------------
/Node-Red/Node-Red-Flow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roblatour/SolarWeatherStationIII/211e758f30c93d589871cd3cdf31dac011ac5360/Node-Red/Node-Red-Flow.jpg
--------------------------------------------------------------------------------
/Node-Red/Report_To_PWSWeather_Flow.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "disabled": false,
4 | "id": "164f144c.cb50cc",
5 | "info": "",
6 | "label": "Publish to PWSWeather.com",
7 | "type": "tab"
8 | },
9 | {
10 | "active": true,
11 | "complete": "payload",
12 | "console": false,
13 | "id": "2c7409a1.df33f6",
14 | "name": "Report to be sent",
15 | "statusType": "auto",
16 | "statusVal": "",
17 | "targetType": "msg",
18 | "tosidebar": true,
19 | "tostatus": false,
20 | "type": "debug",
21 | "wires": [
22 | ],
23 | "x": 1150,
24 | "y": 660,
25 | "z": "164f144c.cb50cc"
26 | },
27 | {
28 | "finalize": "",
29 | "func": "// https://pwsupdate.pwsweather.com/api/v1/submitwx?ID=xxx&PASSWORD=xxx%s&dateutc=now&tempf=%.1f&humidity=%.1f&baromin=%.2f&softwaretype=ESP32DIY&action=updateraw\"\n\nmsg.url = \"https://pwsupdate.pwsweather.com/api/v1/submitwx?\";\nmsg.stationname = \"YOUR_STATION_ID\";\nmsg.auth = \"YOUR_API_KEY\";\n\nmsg.payload = \"ID=\" + msg.stationname + \"&PASSWORD=\" + msg.auth + \"&dateutc=now&tempf=\" + msg.payload.temperature + \"&humidity=\" + msg.payload.humidity + \"&baromin=\" + msg.payload.pressure + \"&softwaretype=ESP32DIY&action=updateraw\";\nmsg.url = msg.url+msg.payload;\nreturn msg;\n\n\n\n",
30 | "id": "5ea0accc.247634",
31 | "initialize": "",
32 | "libs": [
33 | ],
34 | "name": "Formulate report",
35 | "noerr": 0,
36 | "outputs": 1,
37 | "timeout": "",
38 | "type": "function",
39 | "wires": [
40 | [
41 | "2c7409a1.df33f6",
42 | "e9828ed6.1bfec"
43 | ]
44 | ],
45 | "x": 820,
46 | "y": 660,
47 | "z": "164f144c.cb50cc"
48 | },
49 | {
50 | "accumulate": false,
51 | "build": "object",
52 | "count": "3",
53 | "id": "e867d58c.27fe98",
54 | "joiner": "\\n",
55 | "joinerType": "str",
56 | "key": "topic",
57 | "mode": "custom",
58 | "name": "Gather all readings",
59 | "property": "payload",
60 | "propertyType": "msg",
61 | "reduceExp": "",
62 | "reduceFixup": "",
63 | "reduceInit": "",
64 | "reduceInitType": "",
65 | "reduceRight": false,
66 | "timeout": "",
67 | "type": "join",
68 | "wires": [
69 | [
70 | "532a2709.1225e8",
71 | "2ea271a2.78a71e"
72 | ]
73 | ],
74 | "x": 820,
75 | "y": 440,
76 | "z": "164f144c.cb50cc"
77 | },
78 | {
79 | "active": true,
80 | "complete": "payload",
81 | "console": false,
82 | "id": "532a2709.1225e8",
83 | "name": "All readings",
84 | "statusType": "auto",
85 | "statusVal": "",
86 | "targetType": "msg",
87 | "tosidebar": true,
88 | "tostatus": false,
89 | "type": "debug",
90 | "wires": [
91 | ],
92 | "x": 1130,
93 | "y": 440,
94 | "z": "164f144c.cb50cc"
95 | },
96 | {
97 | "authType": "",
98 | "headers": [
99 | ],
100 | "id": "e9828ed6.1bfec",
101 | "insecureHTTPParser": false,
102 | "method": "GET",
103 | "name": "Publish report",
104 | "paytoqs": "ignore",
105 | "persist": false,
106 | "proxy": "",
107 | "ret": "txt",
108 | "senderr": false,
109 | "tls": "",
110 | "type": "http request",
111 | "url": "",
112 | "wires": [
113 | [
114 | "e02429dc.5cea98"
115 | ]
116 | ],
117 | "x": 840,
118 | "y": 760,
119 | "z": "164f144c.cb50cc"
120 | },
121 | {
122 | "active": true,
123 | "complete": "payload",
124 | "console": false,
125 | "id": "e02429dc.5cea98",
126 | "name": "Reply",
127 | "tosidebar": true,
128 | "tostatus": false,
129 | "type": "debug",
130 | "wires": [
131 | ],
132 | "x": 1110,
133 | "y": 760,
134 | "z": "164f144c.cb50cc"
135 | },
136 | {
137 | "active": false,
138 | "complete": "payload",
139 | "console": false,
140 | "id": "1587a23.d47655e",
141 | "name": "Temperature in Celcius",
142 | "statusType": "auto",
143 | "statusVal": "",
144 | "targetType": "msg",
145 | "tosidebar": true,
146 | "tostatus": false,
147 | "type": "debug",
148 | "wires": [
149 | ],
150 | "x": 520,
151 | "y": 100,
152 | "z": "164f144c.cb50cc"
153 | },
154 | {
155 | "finalize": "",
156 | "func": "msg.payload = msg.payload*0.029529983071445\nreturn msg;",
157 | "id": "4cf28efa.ffe1b",
158 | "initialize": "",
159 | "libs": [
160 | ],
161 | "name": "to inHG",
162 | "noerr": 0,
163 | "outputs": 1,
164 | "timeout": "",
165 | "type": "function",
166 | "wires": [
167 | [
168 | "63a3736a.36575c",
169 | "762de9e6.81d928"
170 | ]
171 | ],
172 | "x": 780,
173 | "y": 180,
174 | "z": "164f144c.cb50cc"
175 | },
176 | {
177 | "active": false,
178 | "complete": "payload",
179 | "console": false,
180 | "id": "63a3736a.36575c",
181 | "name": "Pressure in inHG",
182 | "statusType": "auto",
183 | "statusVal": "",
184 | "targetType": "msg",
185 | "tosidebar": true,
186 | "tostatus": false,
187 | "type": "debug",
188 | "wires": [
189 | ],
190 | "x": 990,
191 | "y": 180,
192 | "z": "164f144c.cb50cc"
193 | },
194 | {
195 | "active": false,
196 | "complete": "payload",
197 | "console": false,
198 | "id": "351f43e0.eb6acc",
199 | "name": "Humidity",
200 | "statusType": "auto",
201 | "statusVal": "",
202 | "targetType": "msg",
203 | "tosidebar": true,
204 | "tostatus": false,
205 | "type": "debug",
206 | "wires": [
207 | ],
208 | "x": 1460,
209 | "y": 100,
210 | "z": "164f144c.cb50cc"
211 | },
212 | {
213 | "action": "",
214 | "from": "",
215 | "id": "762de9e6.81d928",
216 | "name": "Pressure reading set",
217 | "property": "",
218 | "reg": false,
219 | "rules": [
220 | {
221 | "p": "topic",
222 | "pt": "msg",
223 | "t": "set",
224 | "to": "pressure",
225 | "tot": "str"
226 | }
227 | ],
228 | "to": "",
229 | "type": "change",
230 | "wires": [
231 | [
232 | "e867d58c.27fe98"
233 | ]
234 | ],
235 | "x": 820,
236 | "y": 280,
237 | "z": "164f144c.cb50cc"
238 | },
239 | {
240 | "action": "",
241 | "from": "",
242 | "id": "254194ed.7be4ec",
243 | "name": "temperature reading set",
244 | "property": "",
245 | "reg": false,
246 | "rules": [
247 | {
248 | "p": "topic",
249 | "pt": "msg",
250 | "t": "set",
251 | "to": "temperature",
252 | "tot": "str"
253 | }
254 | ],
255 | "to": "",
256 | "type": "change",
257 | "wires": [
258 | [
259 | "e867d58c.27fe98"
260 | ]
261 | ],
262 | "x": 310,
263 | "y": 280,
264 | "z": "164f144c.cb50cc"
265 | },
266 | {
267 | "action": "",
268 | "from": "",
269 | "id": "df92ff78.21a92",
270 | "name": "Humidity reading set",
271 | "property": "",
272 | "reg": false,
273 | "rules": [
274 | {
275 | "p": "topic",
276 | "pt": "msg",
277 | "t": "set",
278 | "to": "humidity",
279 | "tot": "str"
280 | }
281 | ],
282 | "to": "",
283 | "type": "change",
284 | "wires": [
285 | [
286 | "e867d58c.27fe98"
287 | ]
288 | ],
289 | "x": 1280,
290 | "y": 280,
291 | "z": "164f144c.cb50cc"
292 | },
293 | {
294 | "finalize": "",
295 | "func": "// List all the variables that you want rounded\nvar pressure = msg.payload.pressure;\nvar temperature = msg.payload.temperature;\nvar humidity = msg.payload.humidity;\n// Create a new payload with rounded numbers\nmsg.payload = {\n pressure: pressure.toFixed(2),\n temperature: temperature.toFixed(2),\n humidity: humidity.toFixed(2),\n};\nreturn msg;",
296 | "id": "2ea271a2.78a71e",
297 | "initialize": "",
298 | "libs": [
299 | ],
300 | "name": "Round",
301 | "noerr": 0,
302 | "outputs": 1,
303 | "timeout": "",
304 | "type": "function",
305 | "wires": [
306 | [
307 | "5ea0accc.247634",
308 | "cc13d797.7e88f8"
309 | ]
310 | ],
311 | "x": 830,
312 | "y": 560,
313 | "z": "164f144c.cb50cc"
314 | },
315 | {
316 | "active": true,
317 | "complete": "payload",
318 | "console": false,
319 | "id": "cc13d797.7e88f8",
320 | "name": "Rounded numbers",
321 | "statusType": "auto",
322 | "statusVal": "",
323 | "targetType": "msg",
324 | "tosidebar": true,
325 | "tostatus": false,
326 | "type": "debug",
327 | "wires": [
328 | ],
329 | "x": 1150,
330 | "y": 560,
331 | "z": "164f144c.cb50cc"
332 | },
333 | {
334 | "action": "",
335 | "from": "",
336 | "id": "e053342f.f87a78",
337 | "name": "",
338 | "property": "",
339 | "reg": false,
340 | "rules": [
341 | {
342 | "p": "payload",
343 | "pt": "msg",
344 | "t": "set",
345 | "to": "$number(payload)",
346 | "tot": "jsonata"
347 | }
348 | ],
349 | "to": "",
350 | "type": "change",
351 | "wires": [
352 | [
353 | "df92ff78.21a92"
354 | ]
355 | ],
356 | "x": 1260,
357 | "y": 180,
358 | "z": "164f144c.cb50cc"
359 | },
360 | {
361 | "broker": "09367f6ed870beea",
362 | "datatype": "auto-detect",
363 | "id": "2a285b1e3f5f1ca5",
364 | "inputs": 0,
365 | "name": "MQTT get temperature",
366 | "nl": false,
367 | "qos": "2",
368 | "rap": true,
369 | "rh": 0,
370 | "topic": "WeatherStationIII/temperature",
371 | "type": "mqtt in",
372 | "wires": [
373 | [
374 | "a71adadf.9db468",
375 | "1587a23.d47655e"
376 | ]
377 | ],
378 | "x": 220,
379 | "y": 100,
380 | "z": "164f144c.cb50cc"
381 | },
382 | {
383 | "broker": "09367f6ed870beea",
384 | "datatype": "auto-detect",
385 | "id": "7b595797c3f4ce2c",
386 | "inputs": 0,
387 | "name": "MQTT get pressure ",
388 | "nl": false,
389 | "qos": "2",
390 | "rap": true,
391 | "rh": 0,
392 | "topic": "WeatherStationIII/pressure",
393 | "type": "mqtt in",
394 | "wires": [
395 | [
396 | "4cf28efa.ffe1b",
397 | "cd9d36aff2fd6d59"
398 | ]
399 | ],
400 | "x": 770,
401 | "y": 100,
402 | "z": "164f144c.cb50cc"
403 | },
404 | {
405 | "broker": "09367f6ed870beea",
406 | "datatype": "auto-detect",
407 | "id": "a9f2e3940309176f",
408 | "inputs": 0,
409 | "name": "MQTT get humidity",
410 | "nl": false,
411 | "qos": "2",
412 | "rap": true,
413 | "rh": 0,
414 | "topic": "WeatherStationIII/humidity",
415 | "type": "mqtt in",
416 | "wires": [
417 | [
418 | "e053342f.f87a78",
419 | "351f43e0.eb6acc"
420 | ]
421 | ],
422 | "x": 1250,
423 | "y": 100,
424 | "z": "164f144c.cb50cc"
425 | },
426 | {
427 | "finalize": "",
428 | "func": "msg.payload = msg.payload*1.8+32\nreturn msg;",
429 | "id": "a71adadf.9db468",
430 | "initialize": "",
431 | "libs": [
432 | ],
433 | "name": "to F",
434 | "noerr": 0,
435 | "outputs": 1,
436 | "timeout": "",
437 | "type": "function",
438 | "wires": [
439 | [
440 | "254194ed.7be4ec",
441 | "c18c238021ddda08"
442 | ]
443 | ],
444 | "x": 250,
445 | "y": 180,
446 | "z": "164f144c.cb50cc"
447 | },
448 | {
449 | "active": false,
450 | "complete": "payload",
451 | "console": false,
452 | "id": "cd9d36aff2fd6d59",
453 | "name": "Pressure",
454 | "statusType": "auto",
455 | "statusVal": "",
456 | "targetType": "msg",
457 | "tosidebar": true,
458 | "tostatus": false,
459 | "type": "debug",
460 | "wires": [
461 | ],
462 | "x": 1020,
463 | "y": 100,
464 | "z": "164f144c.cb50cc"
465 | },
466 | {
467 | "active": false,
468 | "complete": "payload",
469 | "console": false,
470 | "id": "c18c238021ddda08",
471 | "name": "Temperature in Fahrenheit ",
472 | "statusType": "auto",
473 | "statusVal": "",
474 | "targetType": "msg",
475 | "tosidebar": true,
476 | "tostatus": false,
477 | "type": "debug",
478 | "wires": [
479 | ],
480 | "x": 500,
481 | "y": 180,
482 | "z": "164f144c.cb50cc"
483 | },
484 | {
485 | "autoConnect": true,
486 | "birthMsg": {
487 | },
488 | "birthPayload": "",
489 | "birthQos": "0",
490 | "birthTopic": "",
491 | "broker": "localhost",
492 | "cleansession": true,
493 | "clientid": "",
494 | "closeMsg": {
495 | },
496 | "closePayload": "",
497 | "closeQos": "0",
498 | "closeTopic": "",
499 | "id": "09367f6ed870beea",
500 | "keepalive": "60",
501 | "name": "MQTT",
502 | "port": "1883",
503 | "protocolVersion": "4",
504 | "sessionExpiry": "",
505 | "type": "mqtt-broker",
506 | "userProps": "",
507 | "usetls": false,
508 | "willMsg": {
509 | },
510 | "willPayload": "",
511 | "willQos": "0",
512 | "willTopic": ""
513 | }
514 | ]
515 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Solar Powered Weather Station III
2 |
3 | When it comes to solar powered weather stations, the third time was the charm for me!
4 |
5 | Here's a little about my first two attempts to put together a Solar Powered Weather Station:
6 |
7 | **Attempt I** is described in detail [here](https://hackaday.io/project/179917-open-source-solar-powered-weather-station) and featured: open source software for an ESP32 devkit v1, Real Time Clock, five independent sensors, a 110 mm x 136 mm solar panel, a [DFRobot Solar Power Manager 5V](https://www.dfrobot.com/product-1712.html) solar charger and a 8650 battery which together provided a regulated 5 volts to the solution. The weather station also had support for Over the Air (OTA) updating and direct weather services publishing. It ran well in through the summer, but ultimately failed in the fall as it was too power hungry for the short Canadian winter days and long winter nights.
8 |
9 | **Attempt II** is described in detail [here](https://github.com/roblatour/WeatherStation), and supplied power in the same way as the first attempt, but carved out some of the non-essential features such as multiple sensors and OTA updates (relying on a single BME680 sensor to report temperature, pressure and humidity), while also off loading the publishing of the weather status to an MQTT connected server inside my home. Also, this attempt was an earlier adopter of the ESP32-C6 dev board hoping to realize power savings using a Wi-Fi 6 Targeted Wait Times. When that didn't pan out, I also tried using a [Sparkfun TPL5100](https://www.sparkfun.com/products/15353) to reduce power consumption. The full solution can be viewed [here]( https://github.com/roblatour/WeatherStation). However, again, sadly while it lasted longer into the fall, it ultimately failed as the days grew shorter due to lack of power; likely also exasperated by poor battery efficiency in the cold.
10 |
11 | ... and now on to my latest attempt ...
12 |
13 | **Attempt III** has proven much more successful, and indeed has already survived December 21st (2023) - the shortest day of the year - along with below zero temperatures. The secret-sauce is in the hardware used to power the solution. For this the battery and charger from the first two attempts were replaced by a [750F Lithium Ion Capacitor](https://www.aliexpress.com/item/1005004881819434.html) and [one of these boards](https://www.tindie.com/products/jaspersikken/solar-harvesting-into-lithium-ion-capacitor/) supplying a regulated 3.3 volts. Additionally, using these two components allowed the size of the solar panel to be significantly reduced to 48 mm x 65 mm. The solution also leveraged the very low deep sleep power consumption of an [Unexpected Maker's FeatherS3](https://unexpectedmaker.com/shop.html#!/FeatherS3/p/577111310/category=154222511) board. With the FeatherS3 I also used the optional [External u.FL 2.4Ghz Antenna](https://unexpectedmaker.com/shop.html#!/Ext-u-FL-2-4Ghz-Antenna/p/578941059) to help ensure a strong Wi-Fi signal.
14 |
15 | Here's the size difference between the solar panel used in Attempts I & II (first picture below) vs III (second picture below):
16 |
17 | 
18 |
19 | 
20 |
21 | **Plans for a fourth attempt** are on the way. These include updating the design, and releasing the code, for more or less the same solution with the exception that I would like to try out the [Unexpected Maker's TinyC6](https://unexpectedmaker.com/shop.html#!/TinyC6/p/602208790/category=0) board in order to achieve a WiFi-6 solution. I do however plan to let Attempt III run a little longer, so that it will have time to deal with much colder outside temperatures, before moving ahead.
22 |
23 | So please stay tunned ...
24 |
25 | ## Edit: January 2025
26 | The fourth project has been delayed. Weather Station III succumbed when several insects got into the case this summer and short circuited the board. After a long delay, I finally got around to replacing the needed components but the replacement solution has been struggling with lack of daylight over the last couple months - something the original did not have issue with last winter. Perhaps the Lithium Ion Capacitor is getting too old – if so that is truly disappointing because it would only have been running for about a year now.
27 |
28 | ## Support
29 |
30 | [
](https://www.buymeacoffee.com/roblatour)
31 |
--------------------------------------------------------------------------------
/SolarWeatherStationIII/SolarWeatherStationIII.ino:
--------------------------------------------------------------------------------
1 | // Solar Weather Station III
2 | //
3 | // Copyright Rob Latour, 2024
4 | // License: MIT
5 | //
6 | // https://github.com/roblatour/SolarWeatherStationIII
7 | //
8 | // Compile and upload using Arduino IDE (2.2.2 or greater)
9 | //
10 | // Physical board: Feather S3
11 | //
12 | // Board in Arduino board manager: UM FeatherS3
13 | //
14 | // Arduino Tools settings:
15 | //
16 | // USB CDC On Boot: "Enabled"
17 | // CPU Frequency: "240MHz (WiFi)"
18 | // Core Debug Level: "None"
19 | // USB DFU On Boot: "Disabled"
20 | // Erase All Flash Before Sketch Upload: "Disabled"
21 | // Events Run On: "Core 1"
22 | // Flash Mode: "QI0"
23 | // Arduino Runs On: "Core 1"
24 | // USB Firmware MSCOn Boot: "Disabled"
25 | // Partition Scheme: "Default (6.25MB APP/3.43MB SPIFFS)"
26 | // PSRAM: "Enabled"
27 | // Upload Mode: "USB-OTG CDC (TinyUSB)"
28 | // Upload Speed: "921600"
29 | // USB Mode: "Hardware CDC and JTAG"
30 | //
31 | // ----------------------------------------------------------------
32 | // Programmer ESPTool
33 | // ----------------------------------------------------------------
34 |
35 | #include "secrets.h" // contains the MQTT and Wi-Fi credentials used in this sketch
36 |
37 | #include "general.h"
38 |
39 | // Feather S3 board
40 | #include
41 | UMS3 ums3;
42 |
43 |
44 | #include // https://github.com/plapointe6/EspMQTTClient
45 | // please also ensure this patch is applied: https://github.com/plapointe6/EspMQTTClient/pull/138/files
46 |
47 | #include
48 | #include
49 | #include
50 | #include "Adafruit_BME680.h" // https://github.com/adafruit/Adafruit_BME680
51 |
52 | #define SEALEVELPRESSURE_HPA (1013.25)
53 |
54 | Adafruit_BME680 bme;
55 |
56 | #include
57 |
58 | #define ProgramName "SolarWeatherStationIII"
59 |
60 | EspMQTTClient client(
61 | WiFiSSID,
62 | WIFIPassword,
63 | MQTTBroker,
64 | MQTTUser,
65 | MQTTPassword,
66 | MQTTClientName,
67 | MQTTPort);
68 |
69 | void serialPrintHMSFromMs(int32_t timeInMs) {
70 |
71 | unsigned long seconds = timeInMs / 1000;
72 | unsigned long minutes = seconds / 60;
73 | unsigned long hours = minutes / 60;
74 |
75 | seconds %= 60;
76 | minutes %= 60;
77 | hours %= 24;
78 |
79 | Serial.print(hours);
80 | Serial.print(":");
81 | Serial.print(minutes);
82 | Serial.print(":");
83 | Serial.print(seconds);
84 | }
85 |
86 | void publishStatus(String subtopic, String message) {
87 |
88 | String fullTopic;
89 | fullTopic = MQTTTopic;
90 | fullTopic.concat("/");
91 | fullTopic.concat(subtopic);
92 |
93 | if (outputToSerialMonitor)
94 | Serial.println("Publishing " + fullTopic + " " + message);
95 |
96 | client.publish(fullTopic, message);
97 | };
98 |
99 | void publishReadings(String subtopic, float value) {
100 |
101 | String fullTopic;
102 | fullTopic = MQTTTopic;
103 | fullTopic.concat("/");
104 | fullTopic.concat(subtopic);
105 |
106 | String valueAsAString = String(value, 2); // 2 decimal places
107 |
108 | if (outputToSerialMonitor)
109 | Serial.println("Publishing " + fullTopic + " " + valueAsAString);
110 |
111 | client.publish(fullTopic, valueAsAString);
112 | };
113 |
114 | void getAndPublishReadings() {
115 |
116 | // Power up the BME680 sensor
117 | ums3.setLDO2Power(true);
118 | delay(250);
119 |
120 | // Initialize the BME680 sensor
121 | if (bme.begin()) {
122 |
123 | float temperature = 0;
124 | float pressure = 0;
125 | float humidity = 0;
126 |
127 | bme.setTemperatureOversampling(BME680_OS_8X);
128 | bme.setPressureOversampling(BME680_OS_4X);
129 | bme.setHumidityOversampling(BME680_OS_2X);
130 |
131 | bool readingsPerformed = bme.performReading();
132 |
133 | if (readingsPerformed) {
134 |
135 | temperature = bme.temperature;
136 | if ((temperature >= -60.0) && (temperature <= 140.0))
137 | publishReadings("temperature", temperature);
138 | else
139 | publishReadings("error_temperature", temperature);
140 |
141 | pressure = bme.pressure / 100.0;
142 | if ((pressure >= 870.0) && (pressure <= 1090.0))
143 | publishReadings("pressure", pressure);
144 | else
145 | publishReadings("error_pressure", pressure);
146 |
147 | humidity = bme.humidity;
148 | if ((humidity >= 0.0) && (humidity <= 100.0))
149 | publishReadings("humidity", humidity);
150 | else
151 | publishReadings("error_humidity", humidity);
152 |
153 | } else {
154 |
155 | String errorMessage = "Failed to get a reading";
156 |
157 | if (outputToSerialMonitor)
158 | Serial.println(errorMessage);
159 |
160 | publishStatus("error_message", errorMessage);
161 | };
162 |
163 | ums3.setLDO2Power(false);
164 |
165 | } else {
166 |
167 | ums3.setLDO2Power(false);
168 |
169 | String errorMessage = "Could not find a valid BME680 sensor, check wiring!";
170 |
171 | if (outputToSerialMonitor)
172 | Serial.println(errorMessage);
173 |
174 | publishStatus("error_message", errorMessage);
175 | };
176 | };
177 |
178 |
179 | void getAndPublishRunTime() {
180 |
181 | if (publishLastCycleTime) {
182 | float lastCycleTime = ((float)millis() + 520.0) / 1000.0; // the 520 ms are added to account for the delays and processing below
183 | publishReadings("last_cycle_time", lastCycleTime);
184 | };
185 | };
186 |
187 | void goToSleep() {
188 |
189 | // Goto sleep
190 | if (outputToSerialMonitor)
191 | Serial.println("Going to sleep ...");
192 |
193 | // Calculate Time to Sleep (in uS) = minutes between readings (converted to uS)
194 | int32_t TimeToSleep = timeBetweenReportingReadingsInMinutes * 60 * 1000000;
195 |
196 | // Reduce the Time to Sleep by the time spent in processing through to this point
197 | // This adjust the timeing between publications to more accurately be the desired minutes between reported readings
198 |
199 | int32_t cycleTime = millis();
200 |
201 | TimeToSleep -= cycleTime * 1000;
202 |
203 | if (outputToSerialMonitor) {
204 | Serial.print("Cycle time: ");
205 | serialPrintHMSFromMs(cycleTime);
206 | Serial.println("");
207 |
208 | Serial.print("Sleep time: ");
209 | serialPrintHMSFromMs(TimeToSleep / 1000);
210 | Serial.println("");
211 |
212 | delay(20); // allow time to finishing outputting to serial monitor
213 | };
214 |
215 | if (turnOnBlueLEDWhenPocessing)
216 | ums3.setBlueLED(false);
217 |
218 | esp_sleep_enable_timer_wakeup(TimeToSleep);
219 | esp_deep_sleep_start();
220 | };
221 |
222 | void onConnectionEstablished() {
223 |
224 | if (outputToSerialMonitor)
225 | Serial.println("Connection established");
226 |
227 | getAndPublishReadings();
228 |
229 | getAndPublishRunTime();
230 |
231 | delay(500); // allow time for any remaining publishing to complete
232 |
233 | goToSleep();
234 | }
235 |
236 | void setup() {
237 |
238 | if (outputToSerialMonitor) {
239 | Serial.begin(115200);
240 | Serial.println("************************************************");
241 | Serial.println("Starting " + String(ProgramName));
242 | };
243 |
244 | // Initializes UM Feather S3 board peripherals
245 | ums3.begin();
246 | ums3.setLDO2Power(false);
247 |
248 | // re-initial EspMQTTClient (this is required to reset things after awaking from deep sleep)
249 | EspMQTTClient(
250 | WiFiSSID,
251 | WIFIPassword,
252 | MQTTBroker,
253 | MQTTUser,
254 | MQTTPassword,
255 | MQTTClientName,
256 | MQTTPort);
257 |
258 | client.enableDebuggingMessages(outputToSerialMonitor);
259 |
260 | if (turnOnBlueLEDWhenPocessing)
261 | ums3.setBlueLED(true);
262 | }
263 |
264 | void loop() {
265 |
266 | static bool reportWiFiConnectOnce = true;
267 | static bool reportMQTTConnectOnce = true;
268 | static wl_status_t lastWiFiStatus = WL_NO_SHIELD;
269 |
270 | client.loop();
271 |
272 | if (reportWiFiConnectOnce)
273 | if (client.isWifiConnected()) {
274 | reportWiFiConnectOnce = false;
275 | Serial.print("WiFi connected");
276 | };
277 |
278 | if (reportMQTTConnectOnce)
279 | if (client.isMqttConnected()) {
280 | reportMQTTConnectOnce = false;
281 | Serial.print("MQTT connected");
282 | };
283 |
284 | // if after a minute mqtt is still not connected then turn off the WiFi and go into deep sleep so as to try again in next cycle
285 |
286 | if ((millis() > 60000) && (!client.isMqttConnected())) {
287 |
288 | if (outputToSerialMonitor)
289 | Serial.println("Saftey sleep");
290 |
291 | goToSleep();
292 | };
293 |
294 | if (WiFi.status() != lastWiFiStatus) {
295 | lastWiFiStatus = WiFi.status();
296 | Serial.print("WiFi status: ");
297 | Serial.println(WiFi.status());
298 | };
299 |
300 | delay(25);
301 | }
302 |
--------------------------------------------------------------------------------
/SolarWeatherStationIII/general.h:
--------------------------------------------------------------------------------
1 | // Low power weather station
2 | //
3 | // Copyright Rob Latour, 2023
4 | // License: MIT
5 | //
6 | #define timeBetweenReportingReadingsInMinutes 15
7 | #define doubleTimeBetweenReportingReadingsWhenBatteryLevelIsBelow50Percent false
8 |
9 | #define outputToSerialMonitor true
10 | #define turnOnBlueLEDWhenPocessing false
11 | #define publishBatteryLevel false
12 | #define publishLastCycleTime true
13 |
14 | #define MQTTTopic "WeatherStationIII"
15 |
--------------------------------------------------------------------------------
/SolarWeatherStationIII/secrets.h:
--------------------------------------------------------------------------------
1 | // Low power weather station
2 | //
3 | // Copyright Rob Latour, 2023
4 | // License: MIT
5 | //
6 | // Wi-Fi credentials
7 | #define WiFiSSID "YourSSID"
8 | #define WIFIPassword "YourSSIDsPASSWORD"
9 |
10 | // MQTT Credentials
11 | #define MQTTBroker "192.168.1.173" // MQTT IP Address, may also be an IP address "mqtt.whatever.com"
12 | #define MQTTUser "MQTTUser" // MQTT User Name
13 | #define MQTTPassword "whatever" // MQTT Password
14 | #define MQTTClientName "WSIII" // MQTT Client Name
15 | #define MQTTPort 1883 // MQTTT Port Number
16 |
--------------------------------------------------------------------------------