Example logging data and publishing to ThingSpeak.
        Example logging data and publishing to ThingSpeak.
See the walkthrough page for detailed instructions.
  1/** ========================================================================= 
  2 * @example{lineno} logging_to_ThingSpeak.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 logging data and publishing to ThingSpeak. 
  9 * See [the walkthrough page](@ref example_thingspeak) for detailed 
 12 * @m_examplenavigation{example_thingspeak,} 
 13 * ======================================================================= */ 
 15// ========================================================================== 
 17// ========================================================================== 
 19#ifndef TINY_GSM_RX_BUFFER 
 20#define TINY_GSM_RX_BUFFER 64 
 22#ifndef TINY_GSM_YIELD_MS 
 23#define TINY_GSM_YIELD_MS 2 
 27// ========================================================================== 
 28//  Include the libraries required for any data logger 
 29// ========================================================================== 
 30/** Start [includes] */ 
 31// The Arduino library is needed for every Arduino program. 
 34// Include the main header for ModularSensors 
 35#include <ModularSensors.h> 
 39// ========================================================================== 
 40//  Data Logging Options 
 41// ========================================================================== 
 42/** Start [logging_options] */ 
 43// The name of this program file 
 44const char* sketchName = "logging_to_ThingSpeak.ino"; 
 45// Logger ID, also becomes the prefix for the name of the data file on SD card 
 46const char* LoggerID = "XXXXX"; 
 47// How frequently (in minutes) to log data 
 48const int8_t loggingInterval = 15; 
 49// Your logger's timezone. 
 50const int8_t timeZone = -5;  // Eastern Standard Time 
 51// NOTE:  Daylight savings time will not be applied!  Please use standard time! 
 53// Set the input and output pins for the logger 
 54// NOTE:  Use -1 for pins that do not apply 
 55const int32_t serialBaud = 115200;  // Baud rate for debugging 
 56const int8_t  greenLED   = 8;       // Pin for the green LED 
 57const int8_t  redLED     = 9;       // Pin for the red LED 
 58const int8_t  buttonPin  = 21;      // Pin for debugging mode (ie, button pin) 
 59const int8_t  wakePin    = 31;  // MCU interrupt/alarm pin to wake from sleep 
 61// Set the wake pin to -1 if you do not want the main processor to sleep. 
 62// In a SAMD system where you are using the built-in rtc, set wakePin to 1 
 63const int8_t sdCardPwrPin   = -1;  // MCU SD card power pin 
 64const int8_t sdCardSSPin    = 12;  // SD card chip select/slave select pin 
 65const int8_t sensorPowerPin = 22;  // MCU pin controlling main sensor power 
 66/** End [logging_options] */ 
 69// ========================================================================== 
 70//  Wifi/Cellular Modem Options 
 71// ========================================================================== 
 72/** Start [espressif_esp8266] */ 
 73// For almost anything based on the Espressif ESP8266 using the AT command 
 75#include <modems/EspressifESP8266.h> 
 76// Create a reference to the serial port for the modem 
 77HardwareSerial& modemSerial = Serial1;  // Use hardware serial if possible 
 78const int32_t   modemBaud   = 115200;   // Communication speed of the modem 
 79// NOTE:  This baud rate too fast for an 8MHz board, like the Mayfly!  The 
 80// module should be programmed to a slower baud rate or set to auto-baud using 
 81// the AT+UART_CUR or AT+UART_DEF command. 
 83// Modem Pins - Describe the physical pin connection of your modem to your board 
 84// NOTE:  Use -1 for pins that do not apply 
 85const int8_t modemVccPin   = -2;  // MCU pin controlling modem power 
 86const int8_t modemResetPin = 20;  // MCU pin connected to modem reset pin 
 87const int8_t modemLEDPin = 
 88    redLED;  // MCU pin connected an LED to show modem status 
 90// Network connection information 
 91const char* wifiId  = "xxxxx";  // The WiFi access point 
 92const char* wifiPwd = "xxxxx";  // The password for connecting to WiFi 
 94// Create the loggerModem object 
 95EspressifESP8266 modemESP(&modemSerial, modemVccPin, modemResetPin, wifiId, 
 97// Create an extra reference to the modem by a generic name 
 98EspressifESP8266 modem = modemESP; 
 99/** End [espressif_esp8266] */ 
102// ========================================================================== 
103//  Using the Processor as a Sensor 
104// ========================================================================== 
105/** Start [processor_sensor] */ 
106#include <sensors/ProcessorStats.h> 
108// Create the main processor chip "sensor" - for general metadata 
109const char*    mcuBoardVersion = "v1.1"; 
110ProcessorStats mcuBoard(mcuBoardVersion); 
111/** End [processor_sensor] */ 
114// ========================================================================== 
115//  Maxim DS3231 RTC (Real Time Clock) 
116// ========================================================================== 
118#include <sensors/MaximDS3231.h> 
120// Create a DS3231 sensor object 
121MaximDS3231 ds3231(1); 
125// ========================================================================== 
126//  Campbell OBS 3 / OBS 3+ Analog Turbidity Sensor 
127// ========================================================================== 
129#include <sensors/CampbellOBS3.h> 
131const int8_t  OBS3Power = sensorPowerPin;  // Power pin (-1 if unconnected) 
132const uint8_t OBS3NumberReadings = 10; 
133const uint8_t ADSi2c_addr        = 0x48;  // The I2C address of the ADS1115 ADC 
134// Campbell OBS 3+ *Low* Range Calibration in Volts 
135const int8_t OBSLowADSChannel = 0;  // ADS channel for *low* range output 
136const float  OBSLow_A         = 0.000E+00;  // "A" value (X^2) [*low* range] 
137const float  OBSLow_B         = 1.000E+00;  // "B" value (X) [*low* range] 
138const float  OBSLow_C         = 0.000E+00;  // "C" value [*low* range] 
140// Create a Campbell OBS3+ *low* range sensor object 
141CampbellOBS3 osb3low(OBS3Power, OBSLowADSChannel, OBSLow_A, OBSLow_B, OBSLow_C, 
142                     ADSi2c_addr, OBS3NumberReadings); 
145// Campbell OBS 3+ *High* Range Calibration in Volts 
146const int8_t OBSHighADSChannel = 1;  // ADS channel for *high* range output 
147const float  OBSHigh_A         = 0.000E+00;  // "A" value (X^2) [*high* range] 
148const float  OBSHigh_B         = 1.000E+00;  // "B" value (X) [*high* range] 
149const float  OBSHigh_C         = 0.000E+00;  // "C" value [*high* range] 
151// Create a Campbell OBS3+ *high* range sensor object 
152CampbellOBS3 osb3high(OBS3Power, OBSHighADSChannel, OBSHigh_A, OBSHigh_B, 
153                      OBSHigh_C, ADSi2c_addr, OBS3NumberReadings); 
157// ========================================================================== 
158//  Meter Hydros 21 Conductivity, Temperature, and Depth Sensor 
159// ========================================================================== 
160/** Start [hydros21] */ 
161#include <sensors/MeterHydros21.h> 
163const char*   hydrosSDI12address = "1";  // The SDI-12 Address of the Hydros 21 
164const uint8_t hydrosNumberReadings = 6;  // The number of readings to average 
165const int8_t  SDI12Power = sensorPowerPin;  // Power pin (-1 if unconnected) 
166const int8_t  SDI12Data  = 7;               // The SDI12 data pin 
168// Create a Meter Hydros 21 sensor object 
169MeterHydros21 hydros21(*hydrosSDI12address, SDI12Power, SDI12Data, 
170                       hydrosNumberReadings); 
174// ========================================================================== 
175//  Creating the Variable Array[s] and Filling with Variable Objects 
176// ========================================================================== 
177/** Start [variable_arrays] */ 
178Variable* variableList[] = { 
179    new MeterHydros21_Cond(&hydros21, "12345678-abcd-1234-ef00-1234567890ab"), 
180    new MeterHydros21_Temp(&hydros21, "12345678-abcd-1234-ef00-1234567890ab"), 
181    new MeterHydros21_Depth(&hydros21, "12345678-abcd-1234-ef00-1234567890ab"), 
182    new CampbellOBS3_Turbidity(&osb3low, "12345678-abcd-1234-ef00-1234567890ab", 
184    new CampbellOBS3_Turbidity( 
185        &osb3high, "12345678-abcd-1234-ef00-1234567890ab", "TurbHigh"), 
186    new ProcessorStats_Battery(&mcuBoard, 
187                               "12345678-abcd-1234-ef00-1234567890ab"), 
188    new MaximDS3231_Temp(&ds3231, "12345678-abcd-1234-ef00-1234567890ab"), 
189    new Modem_RSSI(&modem, "12345678-abcd-1234-ef00-1234567890ab")}; 
190// Count up the number of pointers in the array 
191int variableCount = sizeof(variableList) / sizeof(variableList[0]); 
193// Create the VariableArray object 
194VariableArray varArray; 
195/** End [variable_arrays] */ 
198// ========================================================================== 
199//  The Logger Object[s] 
200// ========================================================================== 
201/** Start [loggers] */ 
202// Create a logger instance 
207// ========================================================================== 
208//  Creating Data Publisher[s] 
209// ========================================================================== 
210// Create a channel with fields on ThingSpeak in advance 
211// The fields will be sent in exactly the order they are in the variable array. 
212// Any custom name or identifier given to the field on ThingSpeak is irrelevant. 
213// No more than 8 fields of data can go to any one channel.  Any fields beyond 
214// the eighth in the array will be ignored. 
215const char* thingSpeakClientName = 
216    "XXXXXXXXXXXXXXXX";  // The client name for your MQTT device 
217const char* thingSpeakMQTTUser = 
218    "XXXXXXXXXXXXXXXX";  // The user name for your MQTT device. 
219const char* thingSpeakMQTTPassword = 
220    "XXXXXXXXXXXXXXXX";  // The password for your MQTT device 
221const char* thingSpeakChannelID = 
222    "######";  // The numeric channel id for your channel 
223const char* thingSpeakAPIKey = 
224    "XXXXXXXXXXXXXXXX";  // The ThingSpeak user REST API key 
226// Create a data publisher for ThingSpeak 
227#include <publishers/ThingSpeakPublisher.h> 
228ThingSpeakPublisher TsMqtt; 
232// ========================================================================== 
234// ========================================================================== 
235/** Start [working_functions] */ 
236// Flashes the LED's on the primary board 
237void greenRedFlash(uint8_t numFlash = 4, uint8_t rate = 75) { 
238    for (uint8_t i = 0; i < numFlash; i++) { 
239        digitalWrite(greenLED, HIGH); 
240        digitalWrite(redLED, LOW); 
242        digitalWrite(greenLED, LOW); 
243        digitalWrite(redLED, HIGH); 
246    digitalWrite(redLED, LOW); 
249// Reads the battery voltage 
250// NOTE: This will actually return the battery level from the previous update! 
251float getBatteryVoltage() { 
252    if (mcuBoard.sensorValues[0] == -9999) mcuBoard.update(); 
253    return mcuBoard.sensorValues[0]; 
255/** End [working_functions] */ 
258// ========================================================================== 
259//  Arduino Setup Function 
260// ========================================================================== 
263    // Start the primary serial connection 
264    Serial.begin(serialBaud); 
266    // Print a start-up note to the first serial port 
267    Serial.print(F("Now running ")); 
268    Serial.print(sketchName); 
269    Serial.print(F(" on Logger ")); 
270    Serial.println(LoggerID); 
273    Serial.print(F("Using ModularSensors Library version ")); 
274    Serial.println(MODULAR_SENSORS_VERSION); 
275    Serial.print(F("TinyGSM Library version ")); 
276    Serial.println(TINYGSM_VERSION); 
279    // Start the serial connection with the modem 
280    modemSerial.begin(modemBaud); 
282    // Set up pins for the LED's 
283    pinMode(greenLED, OUTPUT); 
284    digitalWrite(greenLED, LOW); 
285    pinMode(redLED, OUTPUT); 
286    digitalWrite(redLED, LOW); 
287    // Blink the LEDs to show the board is on and starting up 
290    // Set the timezones for the logger/data and the RTC 
291    // Logging in the given time zone 
292    Logger::setLoggerTimeZone(timeZone); 
293    // It is STRONGLY RECOMMENDED that you set the RTC to be in UTC (UTC+0) 
294    loggerClock::setRTCOffset(0); 
296    // Attach the modem and information pins to the logger 
297    dataLogger.attachModem(modem); 
298    modem.setModemLED(modemLEDPin); 
299    dataLogger.setLoggerPins(wakePin, sdCardSSPin, sdCardPwrPin, buttonPin, 
302    // Begin the variable array[s], logger[s], and publisher[s] 
303    varArray.begin(variableCount, variableList); 
304    dataLogger.begin(LoggerID, loggingInterval, &varArray); 
305    TsMqtt.begin(dataLogger, thingSpeakClientName, thingSpeakMQTTUser, 
306                 thingSpeakMQTTPassword, thingSpeakChannelID); 
307    TsMqtt.setRESTAPIKey(thingSpeakAPIKey); 
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 || !loggerClock::isRTCSane()) { 
318        // Set up the modem, synchronize the RTC with NIST, and publish 
319        // configuration information to rename the channel and fields. 
320        dataLogger.makeInitialConnections(); 
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 the 
327    // 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(true); 
331        // true = wait for card to settle after power up 
332        dataLogger.createLogFile(true);  // true = write a new header 
333        dataLogger.turnOffSDcard(true); 
334        // true = wait for internal housekeeping after write 
337    // Call the processor sleep 
338    Serial.println(F("Putting processor to sleep")); 
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();