/** ========================================================================= * @file DRWI_SIM7080LTE.ino * @brief Example for DRWI CitSci LTE sites. * * @author Sara Geleskie Damiano ; Modified by: Matt Barney (mbarney@tu.org) * @copyright (c) 2017-2020 Stroud Water Research Center (SWRC) * and the EnviroDIY Development Team * This example is published under the BSD-3 license. * * Build Environment: Visual Studios Code with PlatformIO * Hardware Platform: EnviroDIY Mayfly Arduino Datalogger * * DISCLAIMER: * THIS CODE IS PROVIDED "AS IS" - NO WARRANTY IS GIVEN. * ======================================================================= */ // For code editing purposes, this is a regexp that can be used to search for UUIDs: // ([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}){1} // ========================================================================== // Defines for the Arduino IDE // NOTE: These are ONLY needed to compile with the Arduino IDE. // If you use PlatformIO, you should set these build flags in your // platformio.ini // ========================================================================== /** Start [defines] */ #ifndef TINY_GSM_RX_BUFFER #define TINY_GSM_RX_BUFFER 64 #endif #ifndef TINY_GSM_YIELD_MS #define TINY_GSM_YIELD_MS 2 #endif /** End [defines] */ // ========================================================================== // Include the libraries required for any data logger // ========================================================================== /** Start [includes] */ // The Arduino library is needed for every Arduino program. #include // EnableInterrupt is used by ModularSensors for external and pin change // interrupts and must be explicitly included in the main program. #include // Include the main header for ModularSensors #include /** End [includes] */ // ========================================================================== // Data Logging Options // ========================================================================== /** Start [logging_options] */ // The name of this program file const char* sketchName = "DRWI_SIM7080LTE.ino"; // Logger ID, also becomes the prefix for the name of the data file on SD card const char* LoggerID = "SpokaneR-SpokVly"; // How frequently (in minutes) to log data const uint8_t loggingInterval = 15; // Your logger's timezone. const int8_t timeZone = -8; // -7 is Mountain Standard Time // NOTE: Daylight savings time will not be applied! Please use standard time! // Set the input and output pins for the logger // NOTE: Use -1 for pins that do not apply const int32_t serialBaud = 115200; // Baud rate for debugging const int8_t greenLED = 8; // Pin for the green LED const int8_t redLED = 9; // Pin for the red LED const int8_t buttonPin = 21; // Pin for debugging mode (ie, button pin) const int8_t wakePin = 31; // MCU interrupt/alarm pin to wake from sleep // Mayfly 0.x D31 = A7 // Set the wake pin to -1 if you do not want the main processor to sleep. // In a SAMD system where you are using the built-in rtc, set wakePin to 1 const int8_t sdCardPwrPin = -1; // MCU SD card power pin const int8_t sdCardSSPin = 12; // SD card chip select/slave select pin const int8_t sensorPowerPin = 22; // MCU pin controlling main sensor power /** End [logging_options] */ // ========================================================================== // Wifi/Cellular Modem Options // ========================================================================== // Create a reference to the serial port for the modem HardwareSerial &modemSerial = Serial1; // Use hardware serial if possible // Modem Pins - Describe the physical pin connection of your modem to your board const int8_t modemVccPin = 18; // MCU pin controlling modem power --- Pin 18 is the power enable pin for the bee socket on Mayfly v1.0, // use -1 if using Mayfly 0.5b or if the bee socket is constantly powered (ie you changed SJ18 on Mayfly1.0 to 3.3v) const int8_t modemStatusPin = 19; // MCU pin used to read modem status (-1 if not applicable) const int8_t modemSleepRqPin = 23; // MCU pin used for modem sleep/wake request (-1 if not applicable) const int8_t modemLEDPin = redLED; // MCU pin connected an LED to show modem status (-1 if unconnected) // Network connection information const char *apn = "hologram"; // The APN for the gprs connection: Use "hologram" for hologram SIMs. // For Verizon SIM cards, use "" (empty string) for SIMCom modems, and either 'vzwinternet' or '-' for Digi modems. /** Start [sim7080] */ // For almost anything based on the SIMCom SIM7080G #include const int32_t modemBaud = 9600; // SIM7080 does auto-bauding by default, but for simplicity we set to 9600 // Create the modem object SIMComSIM7080 modem7080(&modemSerial, modemVccPin, modemStatusPin, modemSleepRqPin, apn); // Create an extra reference to the modem by a generic name SIMComSIM7080 modem = modem7080; /** End [sim7080] */ // ========================================================================== // Using the Processor as a Sensor // ========================================================================== /** Start [processor_sensor] */ #include // Create the main processor chip "sensor" - for general metadata const char* mcuBoardVersion = "v1.1"; ProcessorStats mcuBoard(mcuBoardVersion); /** End [processor_sensor] */ // ========================================================================== // Maxim DS3231 RTC (Real Time Clock) // ========================================================================== /** Start [ds3231] */ #include // Create a DS3231 sensor object MaximDS3231 ds3231(1); /** End [ds3231] */ // ========================================================================== // Meter Hydros 21 Conductivity, Temperature, and Depth Sensor // ========================================================================== /** Start [hydros21] */ #include const char* hydros21SDI12address = "1"; // The SDI-12 Address of the Hydros21 const uint8_t hydros21NumberReadings = 6; // The number of readings to average const int8_t hydros21Power = sensorPowerPin; // Power pin (-1 if unconnected) const int8_t hydros21Data = 7; // The SDI12 data pin // Create a Meter Hydros 21 sensor object MeterHydros21 hydros21(*hydros21SDI12address, hydros21Power, hydros21Data, hydros21NumberReadings); /** End [hydros21] */ // ========================================================================== // Creating Variable objects for those values for which we're reporting in converted units, via calculated variables // ========================================================================== // Create a temperature variable pointer for the Meter Hydros 21 Variable *hydros21Temp = new MeterHydros21_Temp (&hydros21, "e43c537a-a246-40a7-b6be-21369d90e151"); // Create a depth variable pointer for the Meter Hydros 21. Use a dummy // UUID if this variable is not being displayed on the data portal. Variable *hydros21Depth = new MeterHydros21_Depth(&hydros21, "12345678-abcd-1234-ef00-1234567890ab"); // Create a temperature variable pointer for the DS3231. Use a dummy // UUID if this variable is not being displayed on the data portal. Variable *ds3231Temp = new MaximDS3231_Temp(&ds3231, "12345678-abcd-1234-ef00-1234567890ab"); // ========================================================================== // Units conversion functions // ========================================================================== float convertDegCtoF(float tempInput) { // Simple deg C to deg F conversion return tempInput * 1.8 + 32; } float convertMmtoIn(float mmInput) { // Simple millimeters to inches conversion return mmInput / 25.4; } // ========================================================================== // Calculation functions for calculated variables // ========================================================================== float getHydros21TempinF(void) { // Convert temp for the hydros21 return convertDegCtoF(hydros21Temp->getValue()); } float getHydros21DepthInIn(void) { // Convert depth for the hydros21 return convertMmtoIn(hydros21Depth->getValue()); } float getDS3231TempinF(void) { // Convert temp for the DS3231 return convertDegCtoF(ds3231Temp->getValue()); } // ========================================================================== // Constructing calculated variables. // (Based on baro_rho_correction.ino and VariableBase.h from enviroDIY.) // ========================================================================== // Create the calculated water temperature Variable object and return a pointer to it Variable *calcHydros21TempF = new Variable( getHydros21TempinF, // function that does the calculation 1, // resolution "temperatureSensor", // var name. This must be a value from http://vocabulary.odm2.org/variablename/ "degreeFahrenheit", // var unit. This must be a value from This must be a value from http://vocabulary.odm2.org/units/ "TempInF", // var code "f342ca3e-ce65-4b73-8db2-382e4144b113"); // Create the calculated depth Variable object and return a pointer to it Variable *calcHydros21DepthIn = new Variable( getHydros21DepthInIn, // function that does the calculation 1, // resolution "CTDdepth", // var name. This must be a value from http://vocabulary.odm2.org/variablename/ "Inch", // var unit. This must be a value from This must be a value from http://vocabulary.odm2.org/units/ "waterDepth", // var code "862fea8b-9098-4e3f-81a3-1e659a08b87b"); // Create the calculated Mayfly temperature Variable object and return a pointer to it Variable *calcDS3231TempF = new Variable( getDS3231TempinF, // function that does the calculation 1, // resolution "temperatureDatalogger", // var name. This must be a value from http://vocabulary.odm2.org/variablename/ "degreeFahrenheit", // var unit. This must be a value from http://vocabulary.odm2.org/units/ "TempInF", // var code "ac26bc1f-7baa-4972-a9d0-4fb5edebef5f"); // ========================================================================== // Creating the Variable Array[s] and Filling with Variable Objects // ========================================================================== Variable *variableList[] = { calcHydros21TempF, calcHydros21DepthIn, new MeterHydros21_Cond(&hydros21, "0ca14898-cc0e-4fcb-9e5f-55804570feed"), hydros21Temp, new ProcessorStats_Battery(&mcuBoard, "582734d7-0676-406e-b7e6-0e97126dd276"), calcDS3231TempF, new Modem_RSSI(&modem, "a128cf91-c0cb-4828-b33f-031b4f99c9d5"), new Modem_SignalPercent(&modem, "f45b78f4-1c90-481f-bfbf-c9fc4f1057dc"), // Adding the following 2 to the array so that the related sensors will get queried // for values, even if we're only displaying _calculated_ versions of those values // on the data portal. (See https://www.envirodiy.org/topic/plotting-calculated-values/) hydros21Depth, ds3231Temp }; const char* registrationToken = "becf4f49-bdd0-4c7f-bdf4-a6aaead9cc85"; // Device registration token const char* samplingFeature = "7e5954a9-97ac-4a0c-ab4e-e2ee74d3befb"; // Sampling feature UUID // Count up the number of pointers in the array int variableCount = sizeof(variableList) / sizeof(variableList[0]); // Create the VariableArray object VariableArray varArray(variableCount, variableList); // ========================================================================== // The Logger Object[s] // ========================================================================== // Create a new logger instance Logger dataLogger(LoggerID, loggingInterval, &varArray); // ========================================================================== // Creating Data Publisher[s] // ========================================================================== /** Start [publishers] */ // Create a data publisher for the Monitor My Watershed/EnviroDIY POST endpoint #include EnviroDIYPublisher EnviroDIYPOST(dataLogger, &modem.gsmClient, registrationToken, samplingFeature); /** End [publishers] */ // ========================================================================== // Working Functions // ========================================================================== /** Start [working_functions] */ // Flashes the LED's on the primary board void greenredflash(uint8_t numFlash = 4, uint8_t rate = 75) { for (uint8_t i = 0; i < numFlash; i++) { digitalWrite(greenLED, HIGH); digitalWrite(redLED, LOW); delay(rate); digitalWrite(greenLED, LOW); digitalWrite(redLED, HIGH); delay(rate); } digitalWrite(redLED, LOW); } // Reads the battery voltage // NOTE: This will actually return the battery level from the previous update! float getBatteryVoltage() { if (mcuBoard.sensorValues[0] == -9999) mcuBoard.update(); return mcuBoard.sensorValues[0]; } // ========================================================================== // Arduino Setup Function // ========================================================================== /** Start [setup] */ void setup() { // Start the primary serial connection Serial.begin(serialBaud); // Print a start-up note to the first serial port Serial.print(F("Now running ")); Serial.print(sketchName); Serial.print(F(" on Logger ")); Serial.println(LoggerID); Serial.println(); Serial.print(F("Using ModularSensors Library version ")); Serial.println(MODULAR_SENSORS_VERSION); Serial.print(F("TinyGSM Library version ")); Serial.println(TINYGSM_VERSION); Serial.println(); // Start the serial connection with the modem modemSerial.begin(modemBaud); // Set up pins for the LED's pinMode(greenLED, OUTPUT); digitalWrite(greenLED, LOW); pinMode(redLED, OUTPUT); digitalWrite(redLED, LOW); // Blink the LEDs to show the board is on and starting up greenredflash(); pinMode(20, OUTPUT); //for proper operation of the onboard flash memory chip's ChipSelect (Mayfly v1.0 and later) // Set the timezones for the logger/data and the RTC // Logging in the given time zone Logger::setLoggerTimeZone(timeZone); // It is STRONGLY RECOMMENDED that you set the RTC to be in UTC (UTC+0) Logger::setRTCTimeZone(0); // Attach the modem and information pins to the logger dataLogger.attachModem(modem); modem.setModemLED(modemLEDPin); dataLogger.setLoggerPins(wakePin, sdCardSSPin, sdCardPwrPin, buttonPin, greenLED); // Begin the logger dataLogger.begin(); // Note: Please change these battery voltages to match your battery // Set up the sensors, except at lowest battery level if (getBatteryVoltage() > 3.4) { Serial.println(F("Setting up sensors...")); varArray.setupSensors(); } /** Start [setup_sim7080] */ modem.setModemWakeLevel(HIGH); // ModuleFun Bee inverts the signal modem.setModemResetLevel(HIGH); // ModuleFun Bee inverts the signal Serial.println(F("Waking modem and setting Cellular Carrier Options...")); modem.modemWake(); // NOTE: This will also set up the modem modem.gsmModem.setBaud(modemBaud); // Make sure we're *NOT* auto-bauding! modem.gsmModem.setNetworkMode(38); // set to LTE only // 2 Automatic // 13 GSM only // 38 LTE only // 51 GSM and LTE only modem.gsmModem.setPreferredMode(1); // set to CAT-M // 1 CAT-M // 2 NB-IoT // 3 CAT-M and NB-IoT /** End [setup_sim7080] */ // Sync the clock if it isn't valid or we have battery to spare if (getBatteryVoltage() > 3.55 || !dataLogger.isRTCSane()) { // Synchronize the RTC with NIST // This will also set up the modem dataLogger.syncRTC(); } // Create the log file, adding the default header to it // Do this last so we have the best chance of getting the time correct and // all sensor names correct // Writing to the SD card can be power intensive, so if we're skipping // the sensor setup we'll skip this too. if (getBatteryVoltage() > 3.4) { Serial.println(F("Setting up file on SD card")); dataLogger.turnOnSDcard(true); // true = wait for card to settle after power up dataLogger.createLogFile(true); // true = write a new header dataLogger.turnOffSDcard(true); // true = wait for internal housekeeping after write } // Call the processor sleep Serial.println(F("Putting processor to sleep\n")); dataLogger.systemSleep(); } /** End [setup] */ // ========================================================================== // Arduino Loop Function // ========================================================================== /** Start [loop] */ // Use this short loop for simple data logging and sending void loop() { // Note: Please change these battery voltages to match your battery // At very low battery, just go back to sleep if (getBatteryVoltage() < 3.4) { dataLogger.systemSleep(); } // At moderate voltage, log data but don't send it over the modem else if (getBatteryVoltage() < 3.55) { dataLogger.logData(); } // If the battery is good, send the data to the world else { dataLogger.logDataAndPublish(); } } /** End [loop] */