Example for DRWI CitSci LTE sites.
        Example for DRWI CitSci LTE sites.
This example shows proper settings for the following configuration:
Mayfly v1.0 board EnviroDIY SIM7080 LTE module (with Hologram SIM card) Hydros21 CTD sensor Campbell Scientific OBS3+ Turbidity sensor
See the walkthrough page for detailed instructions.
  1/** ========================================================================= 
  2 * @example{lineno} DRWI_SIM7080LTE.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 LTE sites. 
  9 * This example shows proper settings for the following configuration: 
 12 * EnviroDIY SIM7080 LTE module (with Hologram SIM card) 
 14 * Campbell Scientific OBS3+ Turbidity sensor 
 16 * See [the walkthrough page](@ref example_drwi_ediylte) for detailed 
 19 * @m_examplenavigation{example_drwi_ediylte,} 
 20 * ======================================================================= */ 
 22// ========================================================================== 
 24// ========================================================================== 
 26#ifndef TINY_GSM_RX_BUFFER 
 27#define TINY_GSM_RX_BUFFER 64 
 29#ifndef TINY_GSM_YIELD_MS 
 30#define TINY_GSM_YIELD_MS 2 
 34// ========================================================================== 
 35//  Include the libraries required for any data logger 
 36// ========================================================================== 
 37/** Start [includes] */ 
 38// The Arduino library is needed for every Arduino program. 
 41// Include the main header for ModularSensors 
 42#include <ModularSensors.h> 
 46// ========================================================================== 
 47//  Data Logging Options 
 48// ========================================================================== 
 49/** Start [logging_options] */ 
 50// The name of this program file 
 51const char* sketchName = "DRWI_SIM7080LTE.ino"; 
 52// Logger ID, also becomes the prefix for the name of the data file on SD card 
 53const char* LoggerID = "XXXXX"; 
 54// How frequently (in minutes) to log data 
 55const int8_t loggingInterval = 15; 
 56// Your logger's timezone. 
 57const int8_t timeZone = -5;  // Eastern Standard Time 
 58// NOTE:  Daylight savings time will not be applied!  Please use standard time! 
 60// Set the input and output pins for the logger 
 61// NOTE:  Use -1 for pins that do not apply 
 62const int32_t serialBaud = 57600;  // Baud rate for debugging 
 63const int8_t  greenLED   = 8;      // Pin for the green LED 
 64const int8_t  redLED     = 9;      // Pin for the red LED 
 65const int8_t  buttonPin  = 21;     // Pin for debugging mode (ie, button pin) 
 66const int8_t  wakePin    = 31;     // MCU interrupt/alarm pin to wake from sleep 
 68const int8_t sdCardPwrPin   = -1;  // MCU SD card power pin 
 69const int8_t sdCardSSPin    = 12;  // SD card chip select/slave select pin 
 70const int8_t sensorPowerPin = 22;  // MCU pin controlling main sensor power 
 71/** End [logging_options] */ 
 74// ========================================================================== 
 75//  Wifi/Cellular Modem Options 
 76// ========================================================================== 
 77/** Start [sim_com_sim7080] */ 
 78// For almost anything based on the SIMCom SIM7080G 
 79#include <modems/SIMComSIM7080.h> 
 81// Create a reference to the serial port for the modem 
 82HardwareSerial& modemSerial = Serial1;  // Use hardware serial if possible 
 83const int32_t   modemBaud = 9600;  //  SIM7080 does auto-bauding by default, but 
 84                                   //  for simplicity we set to 9600 
 86// Modem Pins - Describe the physical pin connection of your modem to your board 
 87// NOTE:  Use -1 for pins that do not apply 
 89const int8_t modemVccPin = 18; 
 90// MCU pin controlling modem power --- Pin 18 is the power enable pin for the 
 91// bee socket on Mayfly v1.0, use -1 if using Mayfly 0.5b or if the bee socket 
 92// is constantly powered (ie you changed SJ18 on Mayfly 1.x to 3.3v) 
 93const int8_t modemStatusPin  = 19;  // MCU pin used to read modem status 
 94const int8_t modemSleepRqPin = 23;  // MCU pin for modem sleep/wake request 
 95const int8_t modemLEDPin = redLED;  // MCU pin connected an LED to show modem 
 98// Network connection information 
100    "hologram";  // APN connection name, typically Hologram unless you have a 
101                 // different provider's SIM card. Change as needed 
103// Create the modem object 
104SIMComSIM7080 modem7080(&modemSerial, modemVccPin, modemStatusPin, 
105                        modemSleepRqPin, apn); 
106// Create an extra reference to the modem by a generic name 
107SIMComSIM7080 modem = modem7080; 
108/** End [sim_com_sim7080] */ 
111// ========================================================================== 
112//  Using the Processor as a Sensor 
113// ========================================================================== 
114/** Start [processor_sensor] */ 
115#include <sensors/ProcessorStats.h> 
117// Create the main processor chip "sensor" - for general metadata 
118const char*    mcuBoardVersion = "v1.1"; 
119ProcessorStats mcuBoard(mcuBoardVersion); 
120/** End [processor_sensor] */ 
123// ========================================================================== 
124//  Maxim DS3231 RTC (Real Time Clock) 
125// ========================================================================== 
127#include <sensors/MaximDS3231.h> 
129// Create a DS3231 sensor object 
130MaximDS3231 ds3231(1); 
134// ========================================================================== 
135//  Meter Hydros 21 Conductivity, Temperature, and Depth Sensor 
136// ========================================================================== 
137/** Start [hydros21] */ 
138#include <sensors/MeterHydros21.h> 
140const char*   hydrosSDI12address = "1";  // The SDI-12 Address of the Hydros 21 
141const uint8_t hydrosNumberReadings = 6;  // The number of readings to average 
142const int8_t  SDI12Power = sensorPowerPin;  // Power pin (-1 if unconnected) 
143const int8_t  SDI12Data  = 7;               // The SDI12 data pin 
145// Create a Meter Hydros 21 sensor object 
146MeterHydros21 hydros(*hydrosSDI12address, SDI12Power, SDI12Data, 
147                     hydrosNumberReadings); 
151// ========================================================================== 
152//  Campbell OBS 3 / OBS 3+ Analog Turbidity Sensor 
153// ========================================================================== 
155#include <sensors/CampbellOBS3.h> 
157const int8_t  OBS3Power = sensorPowerPin;  // Power pin (-1 if unconnected) 
158const uint8_t OBS3NumberReadings = 10; 
159const uint8_t ADSi2c_addr        = 0x48;  // The I2C address of the ADS1115 ADC 
160// Campbell OBS 3+ *Low* Range Calibration in Volts 
161const int8_t OBSLowADSChannel = 0;  // ADS channel for *low* range output 
162const float  OBSLow_A         = 0.000E+00;  // "A" value (X^2) [*low* range] 
163const float  OBSLow_B         = 1.000E+00;  // "B" value (X) [*low* range] 
164const float  OBSLow_C         = 0.000E+00;  // "C" value [*low* range] 
166// Create a Campbell OBS3+ *low* range sensor object 
167CampbellOBS3 osb3low(OBS3Power, OBSLowADSChannel, OBSLow_A, OBSLow_B, OBSLow_C, 
168                     ADSi2c_addr, OBS3NumberReadings); 
171// Campbell OBS 3+ *High* Range Calibration in Volts 
172const int8_t OBSHighADSChannel = 1;  // ADS channel for *high* range output 
173const float  OBSHigh_A         = 0.000E+00;  // "A" value (X^2) [*high* range] 
174const float  OBSHigh_B         = 1.000E+00;  // "B" value (X) [*high* range] 
175const float  OBSHigh_C         = 0.000E+00;  // "C" value [*high* range] 
177// Create a Campbell OBS3+ *high* range sensor object 
178CampbellOBS3 osb3high(OBS3Power, OBSHighADSChannel, OBSHigh_A, OBSHigh_B, 
179                      OBSHigh_C, ADSi2c_addr, OBS3NumberReadings); 
183// ========================================================================== 
184//  Creating the Variable Array[s] and Filling with Variable Objects 
185// ========================================================================== 
186/** Start [variable_arrays] */ 
187Variable* variableList[] = { 
188    new MeterHydros21_Cond(&hydros), 
189    new MeterHydros21_Depth(&hydros), 
190    new MeterHydros21_Temp(&hydros), 
191    new CampbellOBS3_Turbidity(&osb3low, "", "TurbLow"), 
192    new CampbellOBS3_Turbidity(&osb3high, "", "TurbHigh"), 
193    new ProcessorStats_Battery(&mcuBoard), 
194    new MaximDS3231_Temp(&ds3231), 
195    new Modem_SignalPercent(&modem), 
198// All UUID's, device registration, and sampling feature information can be 
199// pasted directly from Monitor My Watershed. 
200// To get the list, click the "View  token UUID list" button on the upper right 
203// *** CAUTION --- CAUTION --- CAUTION --- CAUTION --- CAUTION *** 
204// Check the order of your variables in the variable list!!! 
205// Be VERY certain that they match the order of your UUID's! 
206// Rearrange the variables in the variable list ABOVE if necessary to match! 
207// Do not change the order of the variables in the section below. 
208// *** CAUTION --- CAUTION --- CAUTION --- CAUTION --- CAUTION *** 
210// Replace all of the text in the following section with the UUID array from 
213/* clang-format off */ 
214// ---------------------   Beginning of Token UUID List   --------------------- 
217const char* UUIDs[] =  // UUID array for device sensors 
219        "12345678-abcd-1234-ef00-1234567890ab",  // Specific conductance (Meter_Hydros21_Cond) 
220        "12345678-abcd-1234-ef00-1234567890ab",  // Water depth (Meter_Hydros21_Depth) 
221        "12345678-abcd-1234-ef00-1234567890ab",  // Temperature (Meter_Hydros21_Temp) 
222        "12345678-abcd-1234-ef00-1234567890ab",  // Turbidity (Campbell_OBS3_Turb) (Low) 
223        "12345678-abcd-1234-ef00-1234567890ab",  // Turbidity (Campbell_OBS3_Turb) (High) 
224        "12345678-abcd-1234-ef00-1234567890ab",  // Battery voltage (EnviroDIY_Mayfly_Batt) 
225        "12345678-abcd-1234-ef00-1234567890ab",  // Battery voltage (EnviroDIY_Mayfly_Batt) 
226        "12345678-abcd-1234-ef00-1234567890ab",  // Percent full scale (EnviroDIY_LTEB_SignalPercent) 
228const char* registrationToken = "12345678-abcd-1234-ef00-1234567890ab";  // Device registration token 
229const char* samplingFeature = "12345678-abcd-1234-ef00-1234567890ab";  // Sampling feature UUID 
232// -----------------------   End of Token UUID List  ----------------------- 
235// Count up the number of pointers in the array 
236int variableCount = sizeof(variableList) / sizeof(variableList[0]); 
238// Create the VariableArray object 
239VariableArray varArray(variableCount, variableList, UUIDs); 
240/** End [variable_arrays] */ 
243// ========================================================================== 
244//  The Logger Object[s] 
245// ========================================================================== 
246/** Start [loggers] */ 
247// Create a new logger instance 
248Logger dataLogger(LoggerID, loggingInterval, &varArray); 
252// ========================================================================== 
253//  Creating Data Publisher[s] 
254// ========================================================================== 
255/** Start [publishers] */ 
256// Create a data publisher for the Monitor My Watershed/EnviroDIY POST endpoint 
257#include <publishers/EnviroDIYPublisher.h> 
258EnviroDIYPublisher EnviroDIYPost(dataLogger, registrationToken, 
260/** End [publishers] */ 
263// ========================================================================== 
265// ========================================================================== 
266/** Start [working_functions] */ 
267// Flashes the LED's on the primary board 
268void greenRedFlash(uint8_t numFlash = 4, uint8_t rate = 75) { 
269    for (uint8_t i = 0; i < numFlash; i++) { 
270        digitalWrite(greenLED, HIGH); 
271        digitalWrite(redLED, LOW); 
273        digitalWrite(greenLED, LOW); 
274        digitalWrite(redLED, HIGH); 
277    digitalWrite(redLED, LOW); 
280// Reads the battery voltage 
281// NOTE: This will actually return the battery level from the previous update! 
282float getBatteryVoltage() { 
283    if (mcuBoard.sensorValues[0] == -9999) mcuBoard.update(); 
284    return mcuBoard.sensorValues[0]; 
288// ========================================================================== 
289// Arduino Setup Function 
290// ========================================================================== 
293    // Start the primary serial connection 
294    Serial.begin(serialBaud); 
296    // Print a start-up note to the first serial port 
297    Serial.print(F("Now running ")); 
298    Serial.print(sketchName); 
299    Serial.print(F(" on Logger ")); 
300    Serial.println(LoggerID); 
303    Serial.print(F("Using ModularSensors Library version ")); 
304    Serial.println(MODULAR_SENSORS_VERSION); 
305    Serial.print(F("TinyGSM Library version ")); 
306    Serial.println(TINYGSM_VERSION); 
309    // Start the serial connection with the modem 
310    modemSerial.begin(modemBaud); 
312    // Set up pins for the LED's 
313    pinMode(greenLED, OUTPUT); 
314    digitalWrite(greenLED, LOW); 
315    pinMode(redLED, OUTPUT); 
316    digitalWrite(redLED, LOW); 
317    // Blink the LEDs to show the board is on and starting up 
320    pinMode(20, OUTPUT);  // for proper operation of the onboard flash memory 
321                          // chip's ChipSelect (Mayfly v1.0 and later) 
323    // Set the timezones for the logger/data and the RTC 
324    // Logging in the given time zone 
325    Logger::setLoggerTimeZone(timeZone); 
326    // It is STRONGLY RECOMMENDED that you set the RTC to be in UTC (UTC+0) 
327    loggerClock::setRTCOffset(0); 
329    // Attach the modem and information pins to the logger 
330    dataLogger.attachModem(modem); 
331    modem.setModemLED(modemLEDPin); 
332    dataLogger.setLoggerPins(wakePin, sdCardSSPin, sdCardPwrPin, buttonPin, 
338    // Note:  Please change these battery voltages to match your battery 
339    // Set up the sensors, except at lowest battery level 
340    if (getBatteryVoltage() > 3.4) { 
341        Serial.println(F("Setting up sensors...")); 
342        varArray.sensorsPowerUp(); 
343        varArray.setupSensors(); 
344        varArray.sensorsPowerDown(); 
347    /** Start [setup_sim7080] */ 
348    modem.setModemWakeLevel(HIGH);   // ModuleFun Bee inverts the signal 
349    modem.setModemResetLevel(HIGH);  // ModuleFun Bee inverts the signal 
350    Serial.println(F("Waking modem and setting Cellular Carrier Options...")); 
351    modem.modemWake();  // NOTE:  This will also set up the modem 
352    modem.gsmModem.setBaud(modemBaud);   // Make sure we're *NOT* auto-bauding! 
353    modem.gsmModem.setNetworkMode(38);   // set to LTE only 
357                                         // 51 GSM and LTE only 
358    modem.gsmModem.setPreferredMode(1);  // set to CAT-M 
361                                         // 3 CAT-M and NB-IoT 
362    /** End [setup_sim7080] */ 
365    // Sync the clock if it isn't valid or we have battery to spare 
366    if (getBatteryVoltage() > 3.55 || !loggerClock::isRTCSane()) { 
367        // Synchronize the RTC with NIST 
368        // This will also set up the modem 
369        dataLogger.syncRTC(); 
372    // Create the log file, adding the default header to it 
373    // Do this last so we have the best chance of getting the time correct and 
374    // all sensor names correct 
375    // Writing to the SD card can be power intensive, so if we're skipping 
376    // the sensor setup we'll skip this too. 
377    if (getBatteryVoltage() > 3.4) { 
378        Serial.println(F("Setting up file on SD card")); 
379        dataLogger.turnOnSDcard( 
380            true);  // true = wait for card to settle after power up 
381        dataLogger.createLogFile(true);  // true = write a new header 
382        dataLogger.turnOffSDcard( 
383            true);  // true = wait for internal housekeeping after write 
386    // Call the processor sleep 
387    Serial.println(F("Putting processor to sleep\n")); 
388    dataLogger.systemSleep(); 
393// ========================================================================== 
394//  Arduino Loop Function 
395// ========================================================================== 
397// Use this short loop for simple data logging and sending 
399    // Note:  Please change these battery voltages to match your battery 
400    // At very low battery, just go back to sleep 
401    if (getBatteryVoltage() < 3.4) { 
402        dataLogger.systemSleep(); 
404    // At moderate voltage, log data but don't send it over the modem 
405    else if (getBatteryVoltage() < 3.55) { 
406        dataLogger.logData(); 
408    // If the battery is good, send the data to the world 
410        dataLogger.logDataAndPublish(); 
415// cspell: ignore ediylte Turb