logging_to_MMW.ino example

Example logging data and publishing to Monitor My Watershed.

Example logging data and publishing to Monitor My Watershed.=========================================================================

See the walkthrough page for detailed instructions.


1/** =========================================================================
2 * @example{lineno} logging_to_MMW.ino
3 * @copyright Stroud Water Research Center
4 * @license This example is published under the BSD-3 license.
5 * @author Sara Geleskie Damiano <sdamiano@stroudcenter.org>
6 *
7 * @brief Example logging data and publishing to Monitor My Watershed.
8 *
9 * See [the walkthrough page](@ref example_mmw) for detailed instructions.
10 *
11 * @m_examplenavigation{example_mmw,}
12 * ======================================================================= */
13
14// ==========================================================================
15// Defines for TinyGSM
16// ==========================================================================
17/** Start [defines] */
18#ifndef TINY_GSM_RX_BUFFER
19#define TINY_GSM_RX_BUFFER 64
20#endif
21#ifndef TINY_GSM_YIELD_MS
22#define TINY_GSM_YIELD_MS 2
23#endif
24/** End [defines] */
25
26// ==========================================================================
27// Include the libraries required for any data logger
28// ==========================================================================
29/** Start [includes] */
30// The Arduino library is needed for every Arduino program.
31#include <Arduino.h>
32
33// Include the main header for ModularSensors
34#include <ModularSensors.h>
35/** End [includes] */
36
37
38// ==========================================================================
39// Data Logging Options
40// ==========================================================================
41/** Start [logging_options] */
42// The name of this program file
43const char* sketchName = "logging_to MMW.ino";
44// Logger ID, also becomes the prefix for the name of the data file on SD card
45const char* LoggerID = "XXXXX";
46// How frequently (in minutes) to log data
47const uint8_t loggingInterval = 15;
48// Your logger's timezone.
49const int8_t timeZone = -5; // Eastern Standard Time
50// NOTE: Daylight savings time will not be applied! Please use standard time!
51
52// Set the input and output pins for the logger
53// NOTE: Use -1 for pins that do not apply
54const int32_t serialBaud = 115200; // Baud rate for debugging
55const int8_t greenLED = 8; // Pin for the green LED
56const int8_t redLED = 9; // Pin for the red LED
57const int8_t buttonPin = 21; // Pin for debugging mode (ie, button pin)
58const int8_t wakePin = 31; // MCU interrupt/alarm pin to wake from sleep
59// Mayfly 0.x D31 = A7
60// Set the wake pin to -1 if you do not want the main processor to sleep.
61// In a SAMD system where you are using the built-in rtc, set wakePin to 1
62const int8_t sdCardPwrPin = -1; // MCU SD card power pin
63const int8_t sdCardSSPin = 12; // SD card chip select/slave select pin
64const int8_t sensorPowerPin = 22; // MCU pin controlling main sensor power
65/** End [logging_options] */
66
67
68// ==========================================================================
69// Wifi/Cellular Modem Options
70// ==========================================================================
71/** Start [digi_xbee_cellular_transparent] */
72// For any Digi Cellular XBee's
73// NOTE: The u-blox based Digi XBee's (3G global and LTE-M global) can be used
74// in either bypass or transparent mode, each with pros and cons
75// The Telit based Digi XBees (LTE Cat1) can only use this mode.
76#include <modems/DigiXBeeCellularTransparent.h>
77
78// Create a reference to the serial port for the modem
79HardwareSerial& modemSerial = Serial1; // Use hardware serial if possible
80const int32_t modemBaud = 9600; // All XBee's use 9600 by default
81
82// Modem Pins - Describe the physical pin connection of your modem to your board
83// NOTE: Use -1 for pins that do not apply
84const int8_t modemVccPin = -2; // MCU pin controlling modem power
85const int8_t modemStatusPin = 19; // MCU pin used to read modem status
86const bool useCTSforStatus = false; // Flag to use the XBee CTS pin for status
87const int8_t modemResetPin = 20; // MCU pin connected to modem reset pin
88const int8_t modemSleepRqPin = 23; // MCU pin for modem sleep/wake request
89const int8_t modemLEDPin = redLED; // MCU pin connected an LED to show modem
90 // status (-1 if unconnected)
91
92// Network connection information
93const char* apn = "xxxxx"; // The APN for the gprs connection
94
95// NOTE: If possible, use the `STATUS/SLEEP_not` (XBee pin 13) for status, but
96// the `CTS` pin can also be used if necessary
97DigiXBeeCellularTransparent modemXBCT(&modemSerial, modemVccPin, modemStatusPin,
98 useCTSforStatus, modemResetPin,
99 modemSleepRqPin, apn);
100// Create an extra reference to the modem by a generic name
101DigiXBeeCellularTransparent modem = modemXBCT;
102/** End [digi_xbee_cellular_transparent] */
103
104
105// ==========================================================================
106// Using the Processor as a Sensor
107// ==========================================================================
108/** Start [processor_sensor] */
109#include <sensors/ProcessorStats.h>
110
111// Create the main processor chip "sensor" - for general metadata
112const char* mcuBoardVersion = "v1.1";
113ProcessorStats mcuBoard(mcuBoardVersion);
114/** End [processor_sensor] */
115
116
117// ==========================================================================
118// Maxim DS3231 RTC (Real Time Clock)
119// ==========================================================================
120/** Start [ds3231] */
121#include <sensors/MaximDS3231.h>
122
123// Create a DS3231 sensor object
124MaximDS3231 ds3231(1);
125/** End [ds3231] */
126
127
128// ==========================================================================
129// Bosch BME280 Environmental Sensor
130// ==========================================================================
131/** Start [bme280] */
132#include <sensors/BoschBME280.h>
133
134const int8_t I2CPower = sensorPowerPin; // Power pin (-1 if unconnected)
135uint8_t BMEi2c_addr = 0x76;
136// The BME280 can be addressed either as 0x77 (Adafruit default) or 0x76 (Grove
137// default) Either can be physically mofidied for the other address
138
139// Create a Bosch BME280 sensor object
140BoschBME280 bme280(I2CPower, BMEi2c_addr);
141/** End [bme280] */
142
143
144// ==========================================================================
145// Maxim DS18 One Wire Temperature Sensor
146// ==========================================================================
147/** Start [ds18] */
148#include <sensors/MaximDS18.h>
149
150// OneWire Address [array of 8 hex characters]
151// If only using a single sensor on the OneWire bus, you may omit the address
152// DeviceAddress OneWireAddress1 = {0x28, 0xFF, 0xBD, 0xBA, 0x81, 0x16, 0x03,
153// 0x0C};
154const int8_t OneWirePower = sensorPowerPin; // Power pin (-1 if unconnected)
155const int8_t OneWireBus = 6; // OneWire Bus Pin (-1 if unconnected)
156
157// Create a Maxim DS18 sensor objects (use this form for a known address)
158// MaximDS18 ds18(OneWireAddress1, OneWirePower, OneWireBus);
159
160// Create a Maxim DS18 sensor object (use this form for a single sensor on bus
161// with an unknown address)
162MaximDS18 ds18(OneWirePower, OneWireBus);
163/** End [ds18] */
164
165
166// ==========================================================================
167// Creating the Variable Array[s] and Filling with Variable Objects
168// ==========================================================================
169/** Start [variable_arrays] */
170Variable* variableList[] = {
171 new ProcessorStats_SampleNumber(&mcuBoard,
172 "12345678-abcd-1234-ef00-1234567890ab"),
173 new BoschBME280_Temp(&bme280, "12345678-abcd-1234-ef00-1234567890ab"),
174 new BoschBME280_Humidity(&bme280, "12345678-abcd-1234-ef00-1234567890ab"),
175 new BoschBME280_Pressure(&bme280, "12345678-abcd-1234-ef00-1234567890ab"),
176 new BoschBME280_Altitude(&bme280, "12345678-abcd-1234-ef00-1234567890ab"),
177 new MaximDS18_Temp(&ds18, "12345678-abcd-1234-ef00-1234567890ab"),
178 new ProcessorStats_Battery(&mcuBoard,
179 "12345678-abcd-1234-ef00-1234567890ab"),
180 new MaximDS3231_Temp(&ds3231, "12345678-abcd-1234-ef00-1234567890ab"),
181 new Modem_RSSI(&modem, "12345678-abcd-1234-ef00-1234567890ab"),
182 new Modem_SignalPercent(&modem, "12345678-abcd-1234-ef00-1234567890ab"),
183};
184
185
186// Count up the number of pointers in the array
187int variableCount = sizeof(variableList) / sizeof(variableList[0]);
188
189// Create the VariableArray object
190VariableArray varArray(variableCount, variableList);
191/** End [variable_arrays] */
192
193
194// ==========================================================================
195// The Logger Object[s]
196// ==========================================================================
197/** Start [loggers] */
198// Create a new logger instance
199Logger dataLogger(LoggerID, loggingInterval, &varArray);
200/** End [loggers] */
201
202
203// ==========================================================================
204// Creating Data Publisher[s]
205// ==========================================================================
206/** Start [publishers] */
207// A Publisher to Monitor My Watershed / EnviroDIY Data Sharing Portal
208// Device registration and sampling feature information can be obtained after
209// registration at https://monitormywatershed.org or https://data.envirodiy.org
210const char* registrationToken =
211 "12345678-abcd-1234-ef00-1234567890ab"; // Device registration token
212const char* samplingFeature =
213 "12345678-abcd-1234-ef00-1234567890ab"; // Sampling feature UUID
214
215// Create a data publisher for the Monitor My Watershed/EnviroDIY POST endpoint
216#include <publishers/EnviroDIYPublisher.h>
217EnviroDIYPublisher EnviroDIYPOST(dataLogger, &modem.gsmClient,
218 registrationToken, samplingFeature);
219/** End [publishers] */
220
221
222// ==========================================================================
223// Working Functions
224// ==========================================================================
225/** Start [working_functions] */
226// Flashes the LED's on the primary board
227void greenredflash(uint8_t numFlash = 4, uint8_t rate = 75) {
228 for (uint8_t i = 0; i < numFlash; i++) {
229 digitalWrite(greenLED, HIGH);
230 digitalWrite(redLED, LOW);
231 delay(rate);
232 digitalWrite(greenLED, LOW);
233 digitalWrite(redLED, HIGH);
234 delay(rate);
235 }
236 digitalWrite(redLED, LOW);
237}
238
239// Reads the battery voltage
240// NOTE: This will actually return the battery level from the previous update!
241float getBatteryVoltage() {
242 if (mcuBoard.sensorValues[0] == -9999) mcuBoard.update();
243 return mcuBoard.sensorValues[0];
244}
245/** End [working_functions] */
246
247
248// ==========================================================================
249// Arduino Setup Function
250// ==========================================================================
251/** Start [setup] */
252void setup() {
253// Wait for USB connection to be established by PC
254// NOTE: Only use this when debugging - if not connected to a PC, this
255// could prevent the script from starting
256#if defined(SERIAL_PORT_USBVIRTUAL)
257 while (!SERIAL_PORT_USBVIRTUAL && (millis() < 10000)) {
258 // wait
259 }
260#endif
261
262 // Start the primary serial connection
263 Serial.begin(serialBaud);
264
265 // Print a start-up note to the first serial port
266 Serial.print(F("Now running "));
267 Serial.print(sketchName);
268 Serial.print(F(" on Logger "));
269 Serial.println(LoggerID);
270 Serial.println();
271
272 Serial.print(F("Using ModularSensors Library version "));
273 Serial.println(MODULAR_SENSORS_VERSION);
274 Serial.print(F("TinyGSM Library version "));
275 Serial.println(TINYGSM_VERSION);
276 Serial.println();
277
278// Allow interrupts for software serial
279#if defined(SoftwareSerial_ExtInts_h)
280 enableInterrupt(softSerialRx, SoftwareSerial_ExtInts::handle_interrupt,
281 CHANGE);
282#endif
283#if defined(NeoSWSerial_h)
284 enableInterrupt(neoSSerial1Rx, neoSSerial1ISR, CHANGE);
285#endif
286
287 // Start the serial connection with the modem
288 modemSerial.begin(modemBaud);
289
290 // Set up pins for the LED's
291 pinMode(greenLED, OUTPUT);
292 digitalWrite(greenLED, LOW);
293 pinMode(redLED, OUTPUT);
294 digitalWrite(redLED, LOW);
295 // Blink the LEDs to show the board is on and starting up
296 greenredflash();
297
298 // Set the timezones for the logger/data and the RTC
299 // Logging in the given time zone
300 Logger::setLoggerTimeZone(timeZone);
301 // It is STRONGLY RECOMMENDED that you set the RTC to be in UTC (UTC+0)
302 Logger::setRTCTimeZone(0);
303
304 // Attach the modem and information pins to the logger
305 dataLogger.attachModem(modem);
306 modem.setModemLED(modemLEDPin);
307 dataLogger.setLoggerPins(wakePin, sdCardSSPin, sdCardPwrPin, buttonPin,
308 greenLED);
309
310 // Begin the logger
311 dataLogger.begin();
312
313 // Note: Please change these battery voltages to match your battery
314 // Set up the sensors, except at lowest battery level
315 if (getBatteryVoltage() > 3.4) {
316 Serial.println(F("Setting up sensors..."));
317 varArray.setupSensors();
318 }
319
320 // Sync the clock if it isn't valid or we have battery to spare
321 if (getBatteryVoltage() > 3.55 || !dataLogger.isRTCSane()) {
322 // Synchronize the RTC with NIST
323 // This will also set up the modem
324 dataLogger.syncRTC();
325 }
326
327 // Create the log file, adding the default header to it
328 // Do this last so we have the best chance of getting the time correct and
329 // all sensor names correct
330 // Writing to the SD card can be power intensive, so if we're skipping
331 // the sensor setup we'll skip this too.
332 if (getBatteryVoltage() > 3.4) {
333 Serial.println(F("Setting up file on SD card"));
334 dataLogger.turnOnSDcard(
335 true); // true = wait for card to settle after power up
336 dataLogger.createLogFile(true); // true = write a new header
337 dataLogger.turnOffSDcard(
338 true); // true = wait for internal housekeeping after write
339 }
340
341 // Call the processor sleep
342 Serial.println(F("Putting processor to sleep\n"));
343 dataLogger.systemSleep();
344}
345/** End [setup] */
346
347
348// ==========================================================================
349// Arduino Loop Function
350// ==========================================================================
351/** Start [loop] */
352// Use this short loop for simple data logging and sending
353void loop() {
354 // Note: Please change these battery voltages to match your battery
355 // At very low battery, just go back to sleep
356 if (getBatteryVoltage() < 3.4) {
357 dataLogger.systemSleep();
358 }
359 // At moderate voltage, log data but don't send it over the modem
360 else if (getBatteryVoltage() < 3.55) {
361 dataLogger.logData();
362 }
363 // If the battery is good, send the data to the world
364 else {
365 dataLogger.logDataAndPublish();
366 }
367}
368/** End [loop] */