AWS_IoT_Core.ino example

Example logging data and publishing to AWS IoT Core.

Example logging data and publishing to AWS IoT Core.

Before using this example, you MUST obtain the certificates from AWS IoT and upload them to your modem. This library does NOT support SSL connections natively - it offload the SSL connection process onto the modem. This means that the modem must be able to handle the SSL connection and must have the certificates loaded onto it. Make sure you know the certificate file name as you loaded it onto the module. For supported modems, you can use the AWS_IoT_SetCertificates program in the extras folder to load your certificates. Once you have confirmed your certificates are loaded and working, there is no reason to rerun that program unless you have a new modem, reset your modem, or your certificates change. Most modules store the certificates in flash, which has a limited number of read/write cycles. To avoid wearing out the flash unnecessarily, you should only run load/write the certificates when necessarily, instead of every time you re-start your board.

See the walkthrough page for detailed instructions.

1/** =========================================================================
2 * @example{lineno} AWS_IoT_Core.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 logging data and publishing to AWS IoT Core.
8 *
9 * Before using this example, you **MUST** obtain the certificates from AWS IoT
10 * and upload them to your modem. This library does NOT support SSL connections
11 * natively - it offload the SSL connection process onto the modem. This means
12 * that the modem must be able to handle the SSL connection and must have the
13 * certificates loaded onto it. Make sure you know the certificate file name as
14 * you loaded it onto the module. For supported modems, you can use the
15 * AWS_IoT_SetCertificates program in the extras folder to load your
16 * certificates. Once you have confirmed your certificates are loaded and
17 * working, there is no reason to rerun that program unless you have a new
18 * modem, reset your modem, or your certificates change. Most modules store the
19 * certificates in flash, which has a limited number of read/write cycles. To
20 * avoid wearing out the flash unnecessarily, you should only run load/write the
21 * certificates when necessarily, instead of every time you re-start your board.
22 *
23 * See [the walkthrough page](@ref example_aws_iot_core) for detailed
24 * instructions.
25 *
26 * @m_examplenavigation{example_aws_iot_core,}
27 * ======================================================================= */
28
29// ==========================================================================
30// Defines for TinyGSM
31// ==========================================================================
32/** Start [defines] */
33#ifndef TINY_GSM_RX_BUFFER
34#define TINY_GSM_RX_BUFFER 64
35#endif
36#ifndef TINY_GSM_YIELD_MS
37#define TINY_GSM_YIELD_MS 2
38#endif
39#ifndef MQTT_MAX_PACKET_SIZE
40#define MQTT_MAX_PACKET_SIZE 1024
41#endif
42/** End [defines] */
43
44// ==========================================================================
45// Include the libraries required for any data logger
46// ==========================================================================
47/** Start [includes] */
48// The Arduino library is needed for every Arduino program.
49#include <Arduino.h>
50
51// Include the main header for ModularSensors
52#include <ModularSensors.h>
53/** End [includes] */
54
55
56// ==========================================================================
57// Data Logging Options
58// ==========================================================================
59/** Start [logging_options] */
60static const char AWS_IOT_ENDPOINT[] TINY_GSM_PROGMEM =
61 "YOUR_ENDPOINT-ats.iot.YOUR_REGION.amazonaws.com";
62#define THING_NAME "YOUR_THING_NAME"
63// The name of this program file - this is used only for console printouts at
64// start-up
65const char* sketchName = "AWS_IoT_Core.ino";
66// Logger ID, also becomes the prefix for the name of the data file on SD card
67// NOTE: Your LoggerID will be used as your Thing Name, primary topic, and
68// client name when connecting to AWS IoT Core. _**Make sure it is unique!**_
69const char* LoggerID = THING_NAME;
70// Sampling feature UUID
71// This is used as the sub-topic for AWS IOT Core
72const char* samplingFeature = "YOUR_SAMPLING_FEATURE_ID";
73// How frequently (in minutes) to log data
74const int8_t loggingInterval = 5;
75// Your logger's timezone.
76const int8_t timeZone = -5; // Eastern Standard Time
77// NOTE: Daylight savings time will not be applied! Please use standard time!
78
79// Set the input and output pins for the logger
80// NOTE: Use -1 for pins that do not apply
81const int32_t serialBaud = 115200; // Baud rate for debugging
82const int8_t greenLED = 8; // Pin for the green LED
83const int8_t redLED = 9; // Pin for the red LED
84const int8_t buttonPin = 21; // Pin for debugging mode (ie, button pin)
85const int8_t wakePin = 31; // MCU interrupt/alarm pin to wake from sleep
86// Mayfly 0.x D31 = A7
87// Set the wake pin to -1 if you do not want the main processor to sleep.
88// In a SAMD system where you are using the built-in rtc, set wakePin to 1
89const int8_t sdCardPwrPin = -1; // MCU SD card power pin
90const int8_t sdCardSSPin = 12; // SD card chip select/slave select pin
91const int8_t sensorPowerPin = 22; // MCU pin controlling main sensor power
92/** End [logging_options] */
93
94
95// ==========================================================================
96// Wifi/Cellular Modem Options
97// ==========================================================================
98/** Start [espressif_esp32] */
99// For almost anything based on the Espressif ESP32 using the AT command
100// firmware
101#include <modems/EspressifESP32.h>
102// Create a reference to the serial port for the modem
103HardwareSerial& modemSerial = Serial1; // Use hardware serial if possible
104const int32_t modemBaud = 115200; // Communication speed of the modem
105// NOTE: This baud rate too fast for an 8MHz board, like the Mayfly! The
106// module should be programmed to a slower baud rate or set to auto-baud using
107// the AT+UART_CUR or AT+UART_DEF command.
108
109// Modem Pins - Describe the physical pin connection of your modem to your board
110// NOTE: Use -1 for pins that do not apply
111const int8_t modemVccPin = -2; // MCU pin controlling modem power
112const int8_t modemResetPin = 20; // MCU pin connected to modem reset pin
113const int8_t modemLEDPin =
114 redLED; // MCU pin connected an LED to show modem status
115
116// Network connection information
117const char* wifiId = "xxxxx"; // The WiFi access point
118const char* wifiPwd = "xxxxx"; // The password for connecting to WiFi
119
120// Create the loggerModem object
121EspressifESP32 modemESP(&modemSerial, modemVccPin, modemResetPin, wifiId,
122 wifiPwd);
123// Create an extra reference to the modem by a generic name
124EspressifESP32 modem = modemESP;
125/** End [espressif_esp32] */
126
127/** Start [modem_variables] */
128// Create RSSI and signal strength variable pointers for the modem
129/** End [modem_variables] */
130
131
132// ==========================================================================
133// Using the Processor as a Sensor
134// ==========================================================================
135/** Start [processor_stats] */
136#include <sensors/ProcessorStats.h>
137
138// Create the main processor chip "sensor" - for general metadata
139#if defined(ENVIRODIY_STONEFLY_M4)
140const char* mcuBoardVersion = "v0.1";
141#elif defined(ARDUINO_AVR_ENVIRODIY_MAYFLY)
142const char* mcuBoardVersion = "v1.1";
143#else
144const char* mcuBoardVersion = "unknown";
145#endif
146ProcessorStats mcuBoard(mcuBoardVersion, 5);
147/** End [processor_stats] */
148
149
150// ==========================================================================
151// Everlight ALS-PT19 Ambient Light Sensor
152// ==========================================================================
153/** Start [everlight_alspt19] */
154#include <sensors/EverlightALSPT19.h>
155
156// NOTE: Use -1 for any pins that don't apply or aren't being used.
157const int8_t alsPower = sensorPowerPin; // Power pin
158#if defined(ENVIRODIY_STONEFLY_M4)
159const int8_t alsData = A8; // The ALS PT-19 data pin
160#else
161const int8_t alsData = A4; // The ALS PT-19 data pin
162#endif
163const int8_t alsSupply = 3.3; // The ALS PT-19 supply power voltage
164const int8_t alsResistance = 10; // The ALS PT-19 loading resistance (in kΩ)
165const uint8_t alsNumberReadings = 10;
166
167// Create a Everlight ALS-PT19 sensor object
168EverlightALSPT19 alsPt19(alsPower, alsData, alsSupply, alsResistance,
169 alsNumberReadings);
170/** End [everlight_alspt19] */
171
172// ==========================================================================
173// Sensirion SHT4X Digital Humidity and Temperature Sensor
174// ==========================================================================
175/** Start [sensirion_sht4x] */
176#include <sensors/SensirionSHT4x.h>
177
178// NOTE: Use -1 for any pins that don't apply or aren't being used.
179const int8_t SHT4xPower = sensorPowerPin; // Power pin
180const bool SHT4xUseHeater = true;
181
182// Create an Sensirion SHT4X sensor object
183SensirionSHT4x sht4x(SHT4xPower, SHT4xUseHeater);
184/** End [sensirion_sht4x] */
185
186
187// ==========================================================================
188// Creating the Variable Array[s] and Filling with Variable Objects
189// ==========================================================================
190/** Start [variable_arrays] */
191Variable* variableList[] = {
192 new SensirionSHT4x_Humidity(&sht4x, "12345678-abcd-1234-ef00-1234567890ab"),
193 new SensirionSHT4x_Temp(&sht4x, "12345678-abcd-1234-ef00-1234567890ab"),
194 new EverlightALSPT19_Voltage(&alsPt19,
195 "12345678-abcd-1234-ef00-1234567890ab"),
196 new EverlightALSPT19_Current(&alsPt19,
197 "12345678-abcd-1234-ef00-1234567890ab"),
198 new EverlightALSPT19_Illuminance(&alsPt19,
199 "12345678-abcd-1234-ef00-1234567890ab"),
200
201 new Modem_RSSI(&modem, "12345678-abcd-1234-ef00-1234567890ab", "RSSI"),
202 new Modem_SignalPercent(&modem, "12345678-abcd-1234-ef00-1234567890ab",
203 "signalPercent"),
204#if defined(TINY_GSM_MODEM_HAS_BATTERY)
205 new Modem_BatteryState(&modem, "12345678-abcd-1234-ef00-1234567890ab",
206 "modemBatteryCS"),
207 new Modem_BatteryPercent(&modem, "12345678-abcd-1234-ef00-1234567890ab",
208 "modemBatteryPct"),
209 new Modem_BatteryVoltage(&modem, "12345678-abcd-1234-ef00-1234567890ab",
210 "modemBatterymV"),
211#endif
212#if defined(TINY_GSM_MODEM_HAS_TEMPERATURE)
213
214 new Modem_Temp(&modem, "12345678-abcd-1234-ef00-1234567890ab", "modemTemp"),
215#endif
216 new ProcessorStats_Battery(&mcuBoard,
217 "12345678-abcd-1234-ef00-1234567890ab"),
218 new ProcessorStats_FreeRam(&mcuBoard,
219 "12345678-abcd-1234-ef00-1234567890ab"),
220 new ProcessorStats_SampleNumber(&mcuBoard,
221 "12345678-abcd-1234-ef00-1234567890ab"),
222 new ProcessorStats_ResetCode(&mcuBoard,
223 "12345678-abcd-1234-ef00-1234567890ab"),
224};
225// Count up the number of pointers in the array
226int variableCount = sizeof(variableList) / sizeof(variableList[0]);
227
228// Create the VariableArray object
229VariableArray varArray;
230/** End [variable_arrays] */
231
232
233// ==========================================================================
234// The Logger Object[s]
235// ==========================================================================
236/** Start [loggers] */
237// Create a logger instance
238Logger dataLogger;
239/** End [loggers] */
240
241
242// ==========================================================================
243// AWS IoT Core MQTT Publisher
244// ==========================================================================
245/** Start [aws_io_t_publisher] */
246// The endpoint for your AWS IoT instance
247const char* awsIoTEndpoint = AWS_IOT_ENDPOINT;
248// The name of your certificate authority certificate file
249const char* caCertName = "AmazonRootCA1.pem";
250// The name of your client certificate file
251const char* clientCertName = THING_NAME "-certificate.pem.crt";
252// The name of your client private key file
253const char* clientKeyName = THING_NAME "-private-key.pem.key";
254
255// Create a data publisher for AWS IoT Core
256#include <publishers/AWS_IoT_Publisher.h>
257AWS_IoT_Publisher awsIoTPub(dataLogger, awsIoTEndpoint, caCertName,
258 clientCertName, clientKeyName, samplingFeature);
259/** End [aws_io_t_publisher] */
260
261
262// ==========================================================================
263// Working Functions
264// ==========================================================================
265/** Start [working_functions] */
266// Flashes the LED's on the primary board
267void greenRedFlash(uint8_t numFlash = 4, uint8_t rate = 75) {
268 for (uint8_t i = 0; i < numFlash; i++) {
269 digitalWrite(greenLED, HIGH);
270 digitalWrite(redLED, LOW);
271 delay(rate);
272 digitalWrite(greenLED, LOW);
273 digitalWrite(redLED, HIGH);
274 delay(rate);
275 }
276 digitalWrite(redLED, LOW);
277}
278
279// Reads the battery voltage
280// NOTE: This will actually return the battery level from the previous update!
281float getBatteryVoltage() {
282 if (mcuBoard.sensorValues[0] == -9999) mcuBoard.update();
283 return mcuBoard.sensorValues[0];
284}
285/** End [working_functions] */
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 // Set the timezones for the logger/data and the RTC
321 // Logging in the given time zone
322 Logger::setLoggerTimeZone(timeZone);
323 // It is STRONGLY RECOMMENDED that you set the RTC to be in UTC (UTC+0)
324 loggerClock::setRTCOffset(0);
325
326 // Attach the modem and information pins to the logger
327 dataLogger.attachModem(modem);
328 modem.setModemLED(modemLEDPin);
329 dataLogger.setLoggerPins(wakePin, sdCardSSPin, sdCardPwrPin, buttonPin,
330 greenLED);
331
332 // Begin the variable array[s], logger[s], and publisher[s]
333 varArray.begin(variableCount, variableList);
334 dataLogger.begin(LoggerID, loggingInterval, &varArray);
335
336 // Note: Please change these battery voltages to match your battery
337 // Set up the sensors, except at lowest battery level
338 if (getBatteryVoltage() > 3.4) {
339 Serial.println(F("Setting up sensors..."));
340 varArray.setupSensors();
341 }
342
343 // Sync the clock if it isn't valid or we have battery to spare
344 if (getBatteryVoltage() > 3.55 || !loggerClock::isRTCSane()) {
345 // Synchronize the RTC with NIST
346 // This will also set up the modem
347 dataLogger.syncRTC();
348 }
349
350 // Create the log file, adding the default header to it
351 // Do this last so we have the best chance of getting the time correct and
352 // all sensor names correct
353 // Writing to the SD card can be power intensive, so if we're skipping
354 // the sensor setup we'll skip this too.
355 if (getBatteryVoltage() > 3.4) {
356 Serial.println(F("Setting up file on SD card"));
357 dataLogger.turnOnSDcard(
358 true); // true = wait for card to settle after power up
359 dataLogger.createLogFile(true); // true = write a new header
360 dataLogger.turnOffSDcard(
361 true); // true = wait for internal housekeeping after write
362 }
363
364 // Call the processor sleep
365 Serial.println(F("Putting processor to sleep"));
366 dataLogger.systemSleep();
367}
368/** End [setup] */
369
370
371// ==========================================================================
372// Arduino Loop Function
373// ==========================================================================
374/** Start [loop] */
375// Use this short loop for simple data logging and sending
376void loop() {
377 // Note: Please change these battery voltages to match your battery
378 // At very low battery, just go back to sleep
379 if (getBatteryVoltage() < 3.4) {
380 dataLogger.systemSleep();
381 }
382 // At moderate voltage, log data but don't send it over the modem
383 else if (getBatteryVoltage() < 3.55) {
384 dataLogger.logData();
385 }
386 // If the battery is good, send the data to the world
387 else {
388 dataLogger.logDataAndPublish();
389 }
390}
391/** End [loop] */
392
393// cSpell:ignore modemBatterymV