Example for DRWI CitSci 2G sites.
Example for DRWI CitSci 2G sites.=========================================================================
See the walkthrough page for detailed instructions.
1/** =========================================================================
2 * @example{lineno} DRWI_2G.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>
7 * @brief Example for DRWI CitSci 2G sites.
9 * See [the walkthrough page](@ref example_drwi_2g) for detailed instructions.
11 * @m_examplenavigation{example_drwi_2g,}
12 * ======================================================================= */
14// ==========================================================================
16// ==========================================================================
18#ifndef TINY_GSM_RX_BUFFER
19#define TINY_GSM_RX_BUFFER 64
21#ifndef TINY_GSM_YIELD_MS
22#define TINY_GSM_YIELD_MS 2
26// ==========================================================================
27// Include the libraries required for any data logger
28// ==========================================================================
29/** Start [includes] */
30// The Arduino library is needed for every Arduino program.
33// Include the main header for ModularSensors
34#include <ModularSensors.h>
38// ==========================================================================
39// Data Logging Options
40// ==========================================================================
41/** Start [logging_options] */
42// The name of this program file
43const char* sketchName = "DRWI_CitSci.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!
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
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] */
68// ==========================================================================
69// Wifi/Cellular Modem Options
70// ==========================================================================
71/** Start [sodaq_2g_bee_r6] */
72// For the Sodaq 2GBee R6 and R7 based on the SIMCom SIM800
73// NOTE: The Sodaq GPRSBee doesn't expose the SIM800's reset pin
74#include <modems/Sodaq2GBeeR6.h>
76// Create a reference to the serial port for the modem
77HardwareSerial& modemSerial = Serial1; // Use hardware serial if possible
78const int32_t modemBaud = 9600; // SIM800 does auto-bauding by default
80// Modem Pins - Describe the physical pin connection of your modem to your board
81// NOTE: Use -1 for pins that do not apply
82const int8_t modemVccPin = 23; // MCU pin controlling modem power
83const int8_t modemStatusPin = 19; // MCU pin used to read modem status
84const int8_t modemLEDPin = redLED; // MCU pin connected an LED to show modem
85 // status (-1 if unconnected)
87// Network connection information
88const char* apn = "hologram"; // The APN for the gprs connection
90Sodaq2GBeeR6 modem2GB(&modemSerial, modemVccPin, modemStatusPin, apn);
91// Create an extra reference to the modem by a generic name
92Sodaq2GBeeR6 modem = modem2GB;
93/** End [sodaq_2g_bee_r6] */
96// ==========================================================================
97// Using the Processor as a Sensor
98// ==========================================================================
99/** Start [processor_sensor] */
100#include <sensors/ProcessorStats.h>
102// Create the main processor chip "sensor" - for general metadata
103const char* mcuBoardVersion = "v0.5b";
104ProcessorStats mcuBoard(mcuBoardVersion);
105/** End [processor_sensor] */
108// ==========================================================================
109// Maxim DS3231 RTC (Real Time Clock)
110// ==========================================================================
112#include <sensors/MaximDS3231.h>
114// Create a DS3231 sensor object
115MaximDS3231 ds3231(1);
119// ==========================================================================
120// Campbell OBS 3 / OBS 3+ Analog Turbidity Sensor
121// ==========================================================================
123#include <sensors/CampbellOBS3.h>
125const int8_t OBS3Power = sensorPowerPin; // Power pin (-1 if unconnected)
126const uint8_t OBS3NumberReadings = 10;
127const uint8_t ADSi2c_addr = 0x48; // The I2C address of the ADS1115 ADC
128// Campbell OBS 3+ *Low* Range Calibration in Volts
129const int8_t OBSLowADSChannel = 0; // ADS channel for *low* range output
130const float OBSLow_A = 0.000E+00; // "A" value (X^2) [*low* range]
131const float OBSLow_B = 1.000E+00; // "B" value (X) [*low* range]
132const float OBSLow_C = 0.000E+00; // "C" value [*low* range]
134// Create a Campbell OBS3+ *low* range sensor object
135CampbellOBS3 osb3low(OBS3Power, OBSLowADSChannel, OBSLow_A, OBSLow_B, OBSLow_C,
136 ADSi2c_addr, OBS3NumberReadings);
139// Campbell OBS 3+ *High* Range Calibration in Volts
140const int8_t OBSHighADSChannel = 1; // ADS channel for *high* range output
141const float OBSHigh_A = 0.000E+00; // "A" value (X^2) [*high* range]
142const float OBSHigh_B = 1.000E+00; // "B" value (X) [*high* range]
143const float OBSHigh_C = 0.000E+00; // "C" value [*high* range]
145// Create a Campbell OBS3+ *high* range sensor object
146CampbellOBS3 osb3high(OBS3Power, OBSHighADSChannel, OBSHigh_A, OBSHigh_B,
147 OBSHigh_C, ADSi2c_addr, OBS3NumberReadings);
151// ==========================================================================
152// Meter Hydros 21 Conductivity, Temperature, and Depth Sensor
153// ==========================================================================
154/** Start [hydros21] */
155#include <sensors/MeterHydros21.h>
157const char* hydrosSDI12address = "1"; // The SDI-12 Address of the Hydros 21
158const uint8_t hydrosNumberReadings = 6; // The number of readings to average
159const int8_t SDI12Power = sensorPowerPin; // Power pin (-1 if unconnected)
160const int8_t SDI12Data = 7; // The SDI12 data pin
162// Create a Meter Hydros 21 sensor object
163MeterHydros21 hydros(*hydrosSDI12address, SDI12Power, SDI12Data,
164 hydrosNumberReadings);
168// ==========================================================================
169// Creating the Variable Array[s] and Filling with Variable Objects
170// ==========================================================================
171/** Start [variable_arrays] */
172Variable* variableList[] = {
173 new MeterHydros21_Cond(&hydros),
174 new MeterHydros21_Temp(&hydros),
175 new MeterHydros21_Depth(&hydros),
176 new CampbellOBS3_Turbidity(&osb3low, "", "TurbLow"),
177 new CampbellOBS3_Turbidity(&osb3high, "", "TurbHigh"),
178 new ProcessorStats_Battery(&mcuBoard),
179 new MaximDS3231_Temp(&ds3231),
180 new Modem_RSSI(&modem),
181 new Modem_SignalPercent(&modem)};
183// All UUID's, device registration, and sampling feature information can be
184// pasted directly from Monitor My Watershed. To get the list, click the "View
185// token UUID list" button on the upper right of the site page.
187// *** CAUTION --- CAUTION --- CAUTION --- CAUTION --- CAUTION ***
188// Check the order of your variables in the variable list!!!
189// Be VERY certain that they match the order of your UUID's!
190// Rearrange the variables in the variable list if necessary to match!
191// *** CAUTION --- CAUTION --- CAUTION --- CAUTION --- CAUTION ***
192/* clang-format off */
193const char* UUIDs[] = {
194 "12345678-abcd-1234-ef00-1234567890ab", // Electrical conductivity (Decagon_CTD-10_Cond)
195 "12345678-abcd-1234-ef00-1234567890ab", // Temperature (Decagon_CTD-10_Temp)
196 "12345678-abcd-1234-ef00-1234567890ab", // Water depth (Decagon_CTD-10_Depth)
197 "12345678-abcd-1234-ef00-1234567890ab", // Turbidity (Campbell_OBS3_Turb)
198 "12345678-abcd-1234-ef00-1234567890ab", // Turbidity (Campbell_OBS3_Turb)
199 "12345678-abcd-1234-ef00-1234567890ab", // Battery voltage (EnviroDIY_Mayfly_Batt)
200 "12345678-abcd-1234-ef00-1234567890ab", // Temperature (EnviroDIY_Mayfly_Temp)
201 "12345678-abcd-1234-ef00-1234567890ab", // Received signal strength indication (Sodaq_2GBee_RSSI)
202 "12345678-abcd-1234-ef00-1234567890ab" // Percent full scale (Sodaq_2GBee_SignalPercent)
204const char* registrationToken = "12345678-abcd-1234-ef00-1234567890ab"; // Device registration token
205const char* samplingFeature = "12345678-abcd-1234-ef00-1234567890ab"; // Sampling feature UUID
208// Count up the number of pointers in the array
209int variableCount = sizeof(variableList) / sizeof(variableList[0]);
211// Create the VariableArray object
212VariableArray varArray(variableCount, variableList, UUIDs);
213/** End [variable_arrays] */
216// ==========================================================================
217// The Logger Object[s]
218// ==========================================================================
219/** Start [loggers] */
220// Create a new logger instance
221Logger dataLogger(LoggerID, loggingInterval, &varArray);
225// ==========================================================================
226// Creating Data Publisher[s]
227// ==========================================================================
228/** Start [publishers] */
229// Create a data publisher for the Monitor My Watershed/EnviroDIY POST endpoint
230#include <publishers/EnviroDIYPublisher.h>
231EnviroDIYPublisher EnviroDIYPOST(dataLogger, &modem.gsmClient,
232 registrationToken, samplingFeature);
233/** End [publishers] */
236// ==========================================================================
238// ==========================================================================
239/** Start [working_functions] */
240// Flashes the LED's on the primary board
241void greenredflash(uint8_t numFlash = 4, uint8_t rate = 75) {
242 for (uint8_t i = 0; i < numFlash; i++) {
243 digitalWrite(greenLED, HIGH);
244 digitalWrite(redLED, LOW);
246 digitalWrite(greenLED, LOW);
247 digitalWrite(redLED, HIGH);
250 digitalWrite(redLED, LOW);
253// Reads the battery voltage
254// NOTE: This will actually return the battery level from the previous update!
255float getBatteryVoltage() {
256 if (mcuBoard.sensorValues[0] == -9999) mcuBoard.update();
257 return mcuBoard.sensorValues[0];
259/** End [working_functions] */
262// ==========================================================================
263// Arduino Setup Function
264// ==========================================================================
267 // Start the primary serial connection
268 Serial.begin(serialBaud);
270 // Print a start-up note to the first serial port
271 Serial.print(F("Now running "));
272 Serial.print(sketchName);
273 Serial.print(F(" on Logger "));
274 Serial.println(LoggerID);
277 Serial.print(F("Using ModularSensors Library version "));
278 Serial.println(MODULAR_SENSORS_VERSION);
279 Serial.print(F("TinyGSM Library version "));
280 Serial.println(TINYGSM_VERSION);
283 // Start the serial connection with the modem
284 modemSerial.begin(modemBaud);
286 // Set up pins for the LED's
287 pinMode(greenLED, OUTPUT);
288 digitalWrite(greenLED, LOW);
289 pinMode(redLED, OUTPUT);
290 digitalWrite(redLED, LOW);
291 // Blink the LEDs to show the board is on and starting up
294 // Set the timezones for the logger/data and the RTC
295 // Logging in the given time zone
296 Logger::setLoggerTimeZone(timeZone);
297 // It is STRONGLY RECOMMENDED that you set the RTC to be in UTC (UTC+0)
298 Logger::setRTCTimeZone(0);
300 // Attach the modem and information pins to the logger
301 dataLogger.attachModem(modem);
302 modem.setModemLED(modemLEDPin);
303 dataLogger.setLoggerPins(wakePin, sdCardSSPin, sdCardPwrPin, buttonPin,
309 // Note: Please change these battery voltages to match your battery
310 // Set up the sensors, except at lowest battery level
311 if (getBatteryVoltage() > 3.4) {
312 Serial.println(F("Setting up sensors..."));
313 varArray.setupSensors();
316 // Sync the clock if it isn't valid or we have battery to spare
317 if (getBatteryVoltage() > 3.55 || !dataLogger.isRTCSane()) {
318 // Synchronize the RTC with NIST
319 // This will also set up the modem
320 dataLogger.syncRTC();
323 // Create the log file, adding the default header to it
324 // Do this last so we have the best chance of getting the time correct and
325 // all sensor names correct
326 // Writing to the SD card can be power intensive, so if we're skipping
327 // the sensor setup we'll skip this too.
328 if (getBatteryVoltage() > 3.4) {
329 Serial.println(F("Setting up file on SD card"));
330 dataLogger.turnOnSDcard(
331 true); // true = wait for card to settle after power up
332 dataLogger.createLogFile(true); // true = write a new header
333 dataLogger.turnOffSDcard(
334 true); // true = wait for internal housekeeping after write
337 // Call the processor sleep
338 Serial.println(F("Putting processor to sleep\n"));
339 dataLogger.systemSleep();
344// ==========================================================================
345// Arduino Loop Function
346// ==========================================================================
348// Use this short loop for simple data logging and sending
350 // Note: Please change these battery voltages to match your battery
351 // At very low battery, just go back to sleep
352 if (getBatteryVoltage() < 3.4) {
353 dataLogger.systemSleep();
355 // At moderate voltage, log data but don't send it over the modem
356 else if (getBatteryVoltage() < 3.55) {
357 dataLogger.logData();
359 // If the battery is good, send the data to the world
361 dataLogger.logDataAndPublish();