The documentation below is an outline of the class, and provides code snippets that can easily be coppied/pasted into your code as we work through the exercises. These are being provided to allow you to keep up with the material and hopefully get the most out of the class!
The following tasks should be completed prior to class.
- Install Arduino IDE: Link
- Install VCP drivers for CP2102 USB to serial chip: Link
- Create an account on Adafruit IO: Link
- Create an account on IFTTT: Link
We need to tell the Arduino software where to search for board support packages so we can add support for the ESP8266. Launch the Arduino software, open the Preferences window, and add the following link to the Additional Boards Manager URLs field shown in the red box of the image below:
http://arduino.esp8266.com/stable/package_esp8266com_index.json
Launch the "Boards Manager" from the Tools -> Board: Arduino/Genuino Uno -> Boards Manager
menu item.
In the board manager, search for ESP8266
. Install the latest version of the package from the ESP8266 Community.
Alternatively, you can download the board package files from here. You will need to unzip and store this file where your Arduino installation keeps board packages.
https://geappliances-my.sharepoint.com/:u:/r/personal/paul_goodjohn_geappliances_com/Documents/packages.zip?csf=1&e=QtSS7U
Plug your ESP8266 into your computer's USB port. Then configure the device as shown here under the "Tools" menu of the Arduino IDE.
Install the following libraries using the Arduino Library Manager: Sketch -> Include Library -> Manage Libraries...
- Search "ssd1306": Install -
ESP8266 and ESP32 Oled Driver ... by Daniel Eichhorn, Fabrice Weinberg
- Documentation: Link
- Search "dht": Install -
DHT sensor library by Adafruit
- Documentation: Link
- Search "adafruit unified sensor": Install -
Adafruit Unified Sensor by Adafruit
- Documentation: Link
- Search "ntp": Install -
NTPClient by Fabrice Weinberg
- Documentation: Link
- Search "mqtt": Install -
Adafruit MQTT Library by Adafruit
- Documentation: Link
The following code provides a basic architecture for running periodic "tasks". In this example, the built-in ESP8266 LED toggles from a callback function (task) every 500 ms. We will build on this architecture for the future exercises.
// Includes
// Variables
// Functions
void setup()
{
// Exercise 1: Blink the on-board LED.
pinMode(LED_BUILTIN, OUTPUT);
}
void LedTask()
{
int state = digitalRead(LED_BUILTIN);
digitalWrite(LED_BUILTIN, !state);
}
typedef struct
{
unsigned long previousMillis;
unsigned long elapsedMillis;
unsigned long timeoutMillis;
void (*callback)();
} Timer_t;
static Timer_t schedulerTable[] =
{
{0, 0, 500, &LedTask},
// Add tasks here.
};
void runScheduler()
{
// Run each timer in the scheduler table, and call
for (int i = 0; i < sizeof(schedulerTable)/sizeof(Timer_t); i++)
{
// Note: millis() will overflow after ~50 days.
unsigned long currentMillis = millis();
Timer_t *t = &schedulerTable[i];
t->elapsedMillis += currentMillis - t->previousMillis;
t->previousMillis = currentMillis;
if (t->elapsedMillis >= t->timeoutMillis)
{
t->elapsedMillis = 0;
t->callback();
}
}
}
void loop()
{
runScheduler();
}
In this exercise, we will be connecting your ESP8266 to the iot_class
network with the password password
. Once connected, we will be able to do some internet-y things in the upcoming exercises ✨.
#include <ESP8266WiFi.h>
const char* ssid = "SSID";
const char* password = "PASSWORD";
Add the following code to your setup()
function. It will connect to the network and print the status/IP address to the serial monitor window.
Serial.begin(115200);
delay(10);
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
For this exercise, we need to wire the display to the ESP8266. Once wired, we can add the code to read the time from the NTP server and write it to the display.
Wire the ESP8266 pins to the display pins as defined in this table.
ESP8266 Pin | Display Pin |
---|---|
GPIO5 | SDA |
GPIO4 | SCK |
3.3V | VDD |
GND | GND |
These images show the pins used for this exercise on the ESP8266, and also on the display.
Now let's add the code to make the display work ...
#include <Wire.h>
#include "SSD1306Wire.h"
#include <NTPClient.h>
#include <WiFiUdp.h>
This variable creates a display object with an I2C address of 0x3C (default), SDA signal on pin GPIO5, and SCK signal on pin GPIO4.
SSD1306Wire display(0x3c, 5, 4);
These variables will connect to the time.nist.gov
server and offset the time for Eastern Daylight Time (Louisville, KY).
int utc = -4; // Eastern daylight time
WiFiUDP udp;
NTPClient timeClient(udp, "time.nist.gov", utc * 3600, 60000);
Next we need to initialize the display and the NTP client in setup()
. This snippet initializes the display object we created, and will print "Hello world" to the display in 24 point Arial font.
// Exercise 3 - Setup the display
display.init();
display.clear();
display.flipScreenVertically();
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.setFont(ArialMT_Plain_24);
display.drawString(0, 0, "Hello world");
display.display();
// Exercise 3 - Start the NTP client to read the current time from the internet.
timeClient.begin();
timeClient.update();
Create a Task that runs every 1 second (1000ms) to refresh the contents of the display.
void DisplayTask()
{
display.clear(); // Clear screen.
// Exercise 3 - Read the current time
timeClient.update();
String timeOfDay = timeClient.getFormattedTime();
Serial.println(timeOfDay);
// Exercise 3 - Write the current time to the display
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.setFont(ArialMT_Plain_24);
display.drawString(63, 0, timeOfDay);
display.display(); // Render screen.
}
Update the schedulerTable to look like this:
static Timer_t schedulerTable[] =
{
{0, 0, 500, &LedTask},
{0, 0, 1000, &DisplayTask},
// Add tasks here.
};
Congratulations, you now have a desktop clock that syncs with a government time server every minute!
Our clock would be so much better if it also displayed the current temperature and humidity. We can use the DHT22 sensor to do this! First we need to wire up the sensor, then we will add the code to read it and display the temperature and humidity.
Wire the ESP8266 pins to the display pins as defined in this table.
ESP8266 Pin | DHT22 Pin |
---|---|
GPIO0 | DAT |
3.3V | VCC |
GND | GND |
These images show the pins used for this exercise on the ESP8266, and also on the DHT22 sensor.
Now let's add the code to make the sensor work ...
#include <Adafruit_Sensor.h>
#include <DHT.h>
The following initializes a DHT22 sensor connected to pin GPIO0. It also creates two variables to hold the temperature and humidity values when we read them.
DHT dht(0, DHT22);
float temperature;
float humidity;
Initialize the sensor object in setup()
.
// Exercise 4 - Initialize the temperature sensor.
dht.begin();
Create a periodic task that runs every 2 seconds. This code should read the temp/humidity from the sensor, and if they are valid store them to the variables defined above. Don't forget to add this periodic task to your scheduler table with a 2000ms timeout.
void SensorTask()
{
// Read the temp/humidity.
float temp = dht.readTemperature(true);
float hum = dht.readHumidity();
// Store them if they are both valid.
if (!isnan(temp) && !isnan(hum))
{
temperature = temp;
humidity = hum;
}
}
Finally, update the DisplayTask()
to write the latest temperature and humidity.
// Exercise 4 - Format the temperature and humidity and write to the display
String tempString = String(temperature, 1);
String humidityString = String(humidity, 1);
String sensorString = String(tempString + "F " + humidityString + "%");
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.setFont(ArialMT_Plain_16);
display.drawString(63, 31, sensorString);
Now we are going to create a cloud dashboard that we can log into to remotely monitor the temperature and humidity from our device. Log into your account at https://io.adafruit.com and do the following.
- Create a feed called
temperature
. - Create a feed called
humidity
. - Create a dashboard called
IoT Class
and add widgets for the feeds (guage for humidity, time plot for temperature).
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
These allow your device to access your specific Adafruit account (you created one, right?)
#define AIO_SERVER "io.adafruit.com"
#define AIO_SERVERPORT 1883
#define AIO_USERNAME "YOUR AIO USERNAME"
#define AIO_KEY "YOUR AIO KEY"
These variables tell your device how to pass temperature and humidity into your Adafruit IO account using feeds.
WiFiClient client;
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);
Adafruit_MQTT_Publish temperatureFeed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/temperature");
Adafruit_MQTT_Publish humidityFeed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/humidity");
This new function should be called from Setup()
. It connects your device to the Adafruit IO system. Any errors that it encounters will be displayed on the Arduino serial monitor.
void connectToAdafruit() {
Serial.print(F("Connecting to Adafruit IO... "));
int8_t ret;
while ((ret = mqtt.connect()) != 0)
{
switch (ret)
{
case 1: Serial.println(F("Wrong protocol")); break;
case 2: Serial.println(F("ID rejected")); break;
case 3: Serial.println(F("Server unavail")); break;
case 4: Serial.println(F("Bad user/pass")); break;
case 5: Serial.println(F("Not authed")); break;
case 6: Serial.println(F("Failed to subscribe")); break;
default: Serial.println(F("Connection failed")); break;
}
if (ret >= 0)
{
mqtt.disconnect();
}
Serial.println(F("Retrying connection..."));
delay(5000);
}
Serial.println(F("Adafruit IO Connected!"));
}
Create a new task called CloudTask()
that sends your temperature and humidity to the your Adafruit IO feeds every 5 seconds. The free Adafruit IO account has a limit of 30 messages per minute (or one every 2 seconds), so a 5 second periodic task should keep us within the limit.
void CloudTask()
{
if (! mqtt.ping(3))
{
Serial.println(F("Failed to reach the server"));
if (! mqtt.connected())
{
connectToAdafruit();
}
}
if (! temperatureFeed.publish(temperature))
{
Serial.println(F("Failed to publish temperature"));
}
else
{
Serial.print(temperature);
Serial.println(F(" F published!"));
}
if (! humidityFeed.publish(humidity))
{
Serial.println(F("Failed to publish humidity"));
}
else
{
Serial.print(humidity);
Serial.println(F("% published!"));
}
}
For this exercise, you will need to do the following.
- Log into https://ifttt.com/
- Create a new applet.
- Configure
This
to use Adafruit (you'll need to provide your Adafruit login credentials). - Select "Monitor a feed on Adafruit IO".
- Select Feed = humidity, Relationship = greater than, Value = 98.
- Press "Create".
- Configure
That
to use SMS. - Select "Send me an SMS" (you'll need to provide your number and validate it with a pin).
- Accept the defaults and press "Create Action".
Now breathe some hot air on your sensor and watch the texts roll in! You may want do come up with a way to debounce this, so you aren't spammed with texts, but you get the idea of what's possible! You probably want to go ahead and disable this applet in the meantime.