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();