DRWI Sites with EnviroDIY LTE Bees

The DRWI EnviroDIY LTEbee example uses the sensors and equipment common to older stations (2016-2020) deployed by groups participating in the DRWI Citizen Science project with the Stroud Water Research Center. It includes a Meter Hydros 21 (CTD), a Campbell OBS3+, (Turbidity) and a SIM7080G-based EnviroDIY LTEbee for communication.

The exact hardware configuration used in this example:

  • Mayfly v1.x board
  • EnviroDIY SIM7080 LTE module (with Hologram SIM card)
  • Hydros21 CTD sensor
  • Campbell Scientific OBS3+ Turbidity sensor

An EnviroDIY LTE SIM7080 module can be used with the older Mayfly v0.5b boards if you change line 101 (for modemVccPin) from 18 to -1. This is because the Mayfly v1.0 board has a separate 3.3v regulator to power the Bee socket and is controlled by turning pin 18 on or off. Mayfly v0.5b has the Bee socket constantly powered, therefore using "-1" is the proper setting for that line of code.

The EnviroDIY LTE SIM7080 module includes 2 antennas in the package. The small thin one is the cellular antenna, and should be connected to the socket labeled "CELL". The thicker block is the GPS antenna, and should be connected to the "GPS" socket, but only if you intend to use the GPS functionality of the module. ModularSensors does not currently suport GPS functionality, but other libraries such as TinyGPS can work with the SIM7080 module.

The included cell antenna works best in high-signal-strength areas. For most remote areas and logger deployments, we suggest a larger LTE antenna, like the W3907B0100 from PulseLarsen (Digikey 1837-1003-ND or Mouser 673-W3907B0100)



Unique Features of the DRWI EnviroDIY LTE Example

  • Specifically for sites within the Delaware River Watershed Initiative.
  • Uses a EnviroDIY LTE Bee based on the SIMCom SIM7080G

PlatformIO Configuration

1; PlatformIO Project Configuration File
2;
3; Build options: build flags, source filter
4; Upload options: custom upload port, speed and extra flags
5; Library options: dependencies, extra library storages
6; Advanced options: extra scripting
7;
8; Please visit documentation for the other options and examples
9; http://docs.platformio.org/page/projectconf.html
10
11[platformio]
12description = ModularSensors example intended for DRWI users with CTD, turbidity, and a EnviroDIY SIM7080G LTE modem
13
14[env:mayfly]
15monitor_speed = 57600
16board = mayfly
17platform = atmelavr
18framework = arduino
19lib_ldf_mode = deep+
20lib_ignore =
21 RTCZero
22 Adafruit NeoPixel
23 Adafruit GFX Library
24 Adafruit SSD1306
25 Adafruit ADXL343
26 Adafruit STMPE610
27 Adafruit TouchScreen
28 Adafruit ILI9341
29build_flags =
30 -DSDI12_EXTERNAL_PCINT
31lib_deps =
32 envirodiy/EnviroDIY_ModularSensors
33; ^^ Use this when working from an official release of the library
34; https://github.com/EnviroDIY/ModularSensors.git#develop
35; ^^ Use this when if you want to pull from the develop branch

The Complete Code

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>
6 *
7 * @brief Example for DRWI CitSci LTE sites.
8 *
9 * This example shows proper settings for the following configuration:
10 *
11 * Mayfly v1.0 board
12 * EnviroDIY SIM7080 LTE module (with Hologram SIM card)
13 * Hydros21 CTD sensor
14 * Campbell Scientific OBS3+ Turbidity sensor
15 *
16 * See [the walkthrough page](@ref example_drwi_ediylte) for detailed
17 * instructions.
18 *
19 * @m_examplenavigation{example_drwi_ediylte,}
20 * ======================================================================= */
21
22// ==========================================================================
23// Defines for TinyGSM
24// ==========================================================================
25/** Start [defines] */
26#ifndef TINY_GSM_RX_BUFFER
27#define TINY_GSM_RX_BUFFER 64
28#endif
29#ifndef TINY_GSM_YIELD_MS
30#define TINY_GSM_YIELD_MS 2
31#endif
32/** End [defines] */
33
34// ==========================================================================
35// Include the libraries required for any data logger
36// ==========================================================================
37/** Start [includes] */
38// The Arduino library is needed for every Arduino program.
39#include <Arduino.h>
40
41// Include the main header for ModularSensors
42#include <ModularSensors.h>
43/** End [includes] */
44
45
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 uint8_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!
59
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
67// Mayfly 0.x D31 = A7
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] */
72
73
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>
80
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
85
86// Modem Pins - Describe the physical pin connection of your modem to your board
87// NOTE: Use -1 for pins that do not apply
88
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
96 // status
97
98// Network connection information
99const char* apn =
100 "hologram"; // APN connection name, typically Hologram unless you have a
101 // different provider's SIM card. Change as needed
102
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] */
109
110
111// ==========================================================================
112// Using the Processor as a Sensor
113// ==========================================================================
114/** Start [processor_sensor] */
115#include <sensors/ProcessorStats.h>
116
117// Create the main processor chip "sensor" - for general metadata
118const char* mcuBoardVersion = "v1.1";
119ProcessorStats mcuBoard(mcuBoardVersion);
120/** End [processor_sensor] */
121
122
123// ==========================================================================
124// Maxim DS3231 RTC (Real Time Clock)
125// ==========================================================================
126/** Start [ds3231] */
127#include <sensors/MaximDS3231.h>
128
129// Create a DS3231 sensor object
130MaximDS3231 ds3231(1);
131/** End [ds3231] */
132
133
134// ==========================================================================
135// Meter Hydros 21 Conductivity, Temperature, and Depth Sensor
136// ==========================================================================
137/** Start [hydros21] */
138#include <sensors/MeterHydros21.h>
139
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
144
145// Create a Meter Hydros 21 sensor object
146MeterHydros21 hydros(*hydrosSDI12address, SDI12Power, SDI12Data,
147 hydrosNumberReadings);
148/** End [hydros21] */
149
150
151// ==========================================================================
152// Campbell OBS 3 / OBS 3+ Analog Turbidity Sensor
153// ==========================================================================
154/** Start [obs3] */
155#include <sensors/CampbellOBS3.h>
156
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]
165
166// Create a Campbell OBS3+ *low* range sensor object
167CampbellOBS3 osb3low(OBS3Power, OBSLowADSChannel, OBSLow_A, OBSLow_B, OBSLow_C,
168 ADSi2c_addr, OBS3NumberReadings);
169
170
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]
176
177// Create a Campbell OBS3+ *high* range sensor object
178CampbellOBS3 osb3high(OBS3Power, OBSHighADSChannel, OBSHigh_A, OBSHigh_B,
179 OBSHigh_C, ADSi2c_addr, OBS3NumberReadings);
180/** End [obs3] */
181
182
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),
196};
197
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
201// of the site page.
202
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 ***
209
210// Replace all of the text in the following section with the UUID array from
211// MonitorMyWatershed
212
213/* clang-format off */
214// --------------------- Beginning of Token UUID List ---------------------
215
216
217const char* UUIDs[] = // UUID array for device sensors
218 {
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)
227};
228const char* registrationToken = "12345678-abcd-1234-ef00-1234567890ab"; // Device registration token
229const char* samplingFeature = "12345678-abcd-1234-ef00-1234567890ab"; // Sampling feature UUID
230
231
232// ----------------------- End of Token UUID List -----------------------
233/* clang-format on */
234
235// Count up the number of pointers in the array
236int variableCount = sizeof(variableList) / sizeof(variableList[0]);
237
238// Create the VariableArray object
239VariableArray varArray(variableCount, variableList, UUIDs);
240/** End [variable_arrays] */
241
242
243// ==========================================================================
244// The Logger Object[s]
245// ==========================================================================
246/** Start [loggers] */
247// Create a new logger instance
248Logger dataLogger(LoggerID, loggingInterval, &varArray);
249/** End [loggers] */
250
251
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, &modem.gsmClient,
259 registrationToken, samplingFeature);
260/** End [publishers] */
261
262
263// ==========================================================================
264// Working Functions
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);
272 delay(rate);
273 digitalWrite(greenLED, LOW);
274 digitalWrite(redLED, HIGH);
275 delay(rate);
276 }
277 digitalWrite(redLED, LOW);
278}
279
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];
285}
286
287
288// ==========================================================================
289// Arduino Setup Function
290// ==========================================================================
291/** Start [setup] */
292void setup() {
293 // Start the primary serial connection
294 Serial.begin(serialBaud);
295
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);
301 Serial.println();
302
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);
307 Serial.println();
308
309 // Start the serial connection with the modem
310 modemSerial.begin(modemBaud);
311
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
318 greenredflash();
319
320 pinMode(20, OUTPUT); // for proper operation of the onboard flash memory
321 // chip's ChipSelect (Mayfly v1.0 and later)
322
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 Logger::setRTCTimeZone(0);
328
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,
333 greenLED);
334
335 // Begin the logger
336 dataLogger.begin();
337
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.setupSensors();
343 }
344
345 /** Start [setup_sim7080] */
346 modem.setModemWakeLevel(HIGH); // ModuleFun Bee inverts the signal
347 modem.setModemResetLevel(HIGH); // ModuleFun Bee inverts the signal
348 Serial.println(F("Waking modem and setting Cellular Carrier Options..."));
349 modem.modemWake(); // NOTE: This will also set up the modem
350 modem.gsmModem.setBaud(modemBaud); // Make sure we're *NOT* auto-bauding!
351 modem.gsmModem.setNetworkMode(38); // set to LTE only
352 // 2 Automatic
353 // 13 GSM only
354 // 38 LTE only
355 // 51 GSM and LTE only
356 modem.gsmModem.setPreferredMode(1); // set to CAT-M
357 // 1 CAT-M
358 // 2 NB-IoT
359 // 3 CAT-M and NB-IoT
360 /** End [setup_sim7080] */
361
362
363 // Sync the clock if it isn't valid or we have battery to spare
364 if (getBatteryVoltage() > 3.55 || !dataLogger.isRTCSane()) {
365 // Synchronize the RTC with NIST
366 // This will also set up the modem
367 dataLogger.syncRTC();
368 }
369
370 // Create the log file, adding the default header to it
371 // Do this last so we have the best chance of getting the time correct and
372 // all sensor names correct
373 // Writing to the SD card can be power intensive, so if we're skipping
374 // the sensor setup we'll skip this too.
375 if (getBatteryVoltage() > 3.4) {
376 Serial.println(F("Setting up file on SD card"));
377 dataLogger.turnOnSDcard(
378 true); // true = wait for card to settle after power up
379 dataLogger.createLogFile(true); // true = write a new header
380 dataLogger.turnOffSDcard(
381 true); // true = wait for internal housekeeping after write
382 }
383
384 // Call the processor sleep
385 Serial.println(F("Putting processor to sleep\n"));
386 dataLogger.systemSleep();
387}
388/** End [setup] */
389
390
391// ==========================================================================
392// Arduino Loop Function
393// ==========================================================================
394/** Start [loop] */
395// Use this short loop for simple data logging and sending
396void loop() {
397 // Note: Please change these battery voltages to match your battery
398 // At very low battery, just go back to sleep
399 if (getBatteryVoltage() < 3.4) {
400 dataLogger.systemSleep();
401 }
402 // At moderate voltage, log data but don't send it over the modem
403 else if (getBatteryVoltage() < 3.55) {
404 dataLogger.logData();
405 }
406 // If the battery is good, send the data to the world
407 else {
408 dataLogger.logDataAndPublish();
409 }
410}
411/** End [loop] */