Retrieving the SSID & Passphrase from files on the SDCard, this also configures the Thingspeak Write Key from a file on the SDCard.
This is using an ESP8266-12E, with a 0.96" I2C OLED Display(SSD1306), SHT21 and A Micro SDCard Breakout.
This will retrieve live Weather Data from Thingspeak provided by a Fine Offset WH1080 Weather Station located in Renfrew Scotland with MQ-2 & MQ-7 data also displayed.
The Local Data is provided by an I2C SHT21 Temperature & Humidity Sensor, this is written to a Thingspeak Channel via a simple "GET".
MQ-2 + MQ-7 Feed....
https://thingspeak.com/channels/209159
WH1080 Feed....
https://thingspeak.com/channels/168700
The Output of the Sketch is here.....
https://thingspeak.com/channels/209164
This is the sketch the Icon files should be included also......
/*
Udp NTP Client
Get the time from a Network Time Protocol (NTP) time server
Demonstrates use of UDP sendPacket and ReceivePacket
For more on NTP time servers and the messages needed to communicate with them,
see http://en.wikipedia.org/wiki/Network_Time_Protocol
created 4 Sep 2010
by Michael Margolis
modified 9 Apr 2012
by Tom Igoe
updated for the ESP8266 12 Apr 2015
by Ivan Grokhotkov
Used as the Basis for this Sketch Jan 2017
Connect the SDCard to the following pins on the esp8266:
| GPIO NodeMCU Name |
===========================
| 15 D8 SS |
| 13 D7 MOSI |
| 12 D6 MISO |
| 14 D5 SCK |
===========================
Files Required on SDCard :
ssid.txt Maximum length 24 Chars, increase if more Chars needed !!! ssid[24]
wifipsk.txt Maximum length 32 Chars, increase if more Chars needed !!! wifipsk[32]
thingkey.txt Maximum length 20 Chars, increase if more Chars needed !!! thingspeak_key[20]
Connect the I2C to the following pins on the esp8266:
| GPIO NodeMCU Name |
===========================
| 4 D3 SCL |
| 5 D4 SDA |
===========================
This code is in the public domain.
*/
// Include the correct display library
// For a connection via I2C using Wire include
#include <Wire.h> // Only needed for Arduino 1.6.5 and earlier
#include "SSD1306.h" // alias for `#include "SSD1306Wire.h"`
#include "HTU21D.h"
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include "ThingSpeak.h"
#include <TimeLib.h>
#include <SPI.h>
#include <SD.h>
// Include the UI lib
#include "OLEDDisplayUi.h"
char ssid[16]; // Storage for your network SSID (name) from SDCard
char pass[24]; // Strorage for your network password from SDCard
char thingspeak_key[20]; // Strorage for your Thingspeak Write Key from SDCard
// Include custom images
#include "images.h"
unsigned long ulNextMeas_ms; // Counter for loop selection, no delay......
unsigned long epoch;
unsigned int localPort = 2390; // local port to listen for UDP packets
/* Don't hardwire the IP address or we won't get the benefits of the pool.
* Lookup the IP address for the host name instead */
//IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server
IPAddress timeServerIP; // time.nist.gov NTP server address
const char* ntpServerName = "time.nist.gov";
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
// A UDP instance to let us send and receive packets over UDP
WiFiUDP udp;
WiFiClient client;
unsigned long i2ctimer=20000;
float newval,newval1;
HTU21D myHTU21D;
// Thinspeak Channels....
unsigned long weatherStationChannelNumber = 168700;
unsigned long AirQuailityChannelNumber = 209159;
// Initialize the OLED display using Wire library
SSD1306 display(0x3c, 4, 5);
OLEDDisplayUi ui ( &display );
int screenW = 128;
int screenH = 64;
int clockCenterX = screenW/2;
int clockCenterY = (screenH/2)+5;
int clockRadius = 25;
int LcNt=0;
int cLckset=0;
float humidity,temperature,Inttemperature,Inthumidity,pressure,rainfall,windSpeed,CloudBase,Ds1,Ds2,CH4Smoke,CMonoxide;
// utility function for digital clock display: prints leading 0
String twoDigits(int digits){
if(digits < 10) {
String i = '0'+String(digits);
return i;
}
else {
return String(digits);
}
}
void clockOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {
}
void analogClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
// ui.disableIndicator();
// Draw the clock face
// display->drawCircle(clockCenterX + x, clockCenterY + y, clockRadius);
display->drawCircle(clockCenterX + x, clockCenterY + y, 2);
//
//hour ticks
for( int z=0; z < 360;z= z + 30 ){
//Begin at 0° and stop at 360°
float angle = z ;
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
int x2 = ( clockCenterX + ( sin(angle) * clockRadius ) );
int y2 = ( clockCenterY - ( cos(angle) * clockRadius ) );
int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) );
int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) );
display->drawLine( x2 + x , y2 + y , x3 + x , y3 + y);
}
// display second hand
float angle = second() * 6 ;
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) );
int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) );
display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
//
// display minute hand
angle = minute() * 6 ;
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) );
y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) );
display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
//
// display hour hand
angle = hour() * 30 + int( ( minute() / 12 ) * 6 ) ;
angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) );
y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) );
display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
}
void digitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
String timenow = String(hour())+":"+twoDigits(minute())+":"+twoDigits(second());
if(CloudBase<=1500){
display->drawString(clockCenterX + x , 10, "Cloudy" );
}
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_24);
if(CloudBase<=1500){
display->drawString(clockCenterX + x , 10, "Cloudy" );
}
display->drawString(clockCenterX + x , clockCenterY + y, timenow );
}
void digitalLTempHumFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_16);
display->drawString(clockCenterX + x , 10, "Local Sensor" );
display->drawString(clockCenterX + x , 25, "Temp " + String(newval1) );
display->drawString(clockCenterX + x , 40, "Humid " + String(newval) );
}
void digitalTempHumFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
String timenow = String(hour())+":"+twoDigits(minute())+":"+twoDigits(second());
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_16);
display->drawString(clockCenterX + x , 10, "E-Monitor INT" );
display->drawString(clockCenterX + x , 25, "Temp " + String(Inttemperature) );
display->drawString(clockCenterX + x , 40, "Humid " + String(Inthumidity) );
}
void digitalETempHumFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
String timenow = String(hour())+":"+twoDigits(minute())+":"+twoDigits(second());
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_16);
display->drawString(clockCenterX + x , 10, "E-Monitor EXT" );
display->drawString(clockCenterX + x , 25, "Temp " + String(Ds1) );
display->drawString(clockCenterX + x , 40, "Humid " + String(Ds2) );
}
void digitalFTempHumFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
String timenow = String(hour())+":"+twoDigits(minute())+":"+twoDigits(second());
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_16);
display->drawString(clockCenterX + x , 10, "E-Monitor AIR" );
display->setFont(ArialMT_Plain_10);
display->drawString(clockCenterX + x , 25, "Barometer " + String(pressure) + " MSL" );
display->drawString(clockCenterX + x , 40, "Wind " + String(windSpeed) + " MPH" );
}
void digitalGTempHumFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
String timenow = String(hour())+":"+twoDigits(minute())+":"+twoDigits(second());
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_16);
display->drawString(clockCenterX + x , 10, "E-Monitor Rain" );
if(rainfall>0){
display->drawString(clockCenterX + x , 25, "Rainfall Today" );
display->drawString(clockCenterX + x , 40, String(rainfall) + "MM" );
}else{
display->drawString(clockCenterX + x , 25, "Dry Today..." );
display->drawString(clockCenterX + x , 40, "So Far.... " );
}
}
void digitalAQTempHumFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(ArialMT_Plain_16);
display->drawString(clockCenterX + x , 10, "E-Monitor AIR-Q" );
display->setFont(ArialMT_Plain_10);
display->drawString(clockCenterX + x , 25, "CH4/Smoke " + String(CH4Smoke) + " PPM" );
display->drawString(clockCenterX + x , 40, "CO " + String(CMonoxide) + " PPM" );
}
// This array keeps function pointers to all frames
// frames are the single views that slide in
FrameCallback frames[] = { analogClockFrame, digitalClockFrame, digitalLTempHumFrame, digitalTempHumFrame, digitalETempHumFrame, digitalFTempHumFrame, digitalAQTempHumFrame, digitalGTempHumFrame };
// how many frames are there?
int frameCount = 8;
// Overlays are statically drawn on top of a frame eg. a clock
OverlayCallback overlays[] = { clockOverlay };
int overlaysCount = 1;
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println();
Serial.print(F("Initializing SD card..."));
if (!SD.begin(15)) {
Serial.println(F("initialization failed!"));
return;
}
Serial.println(F("initialization done."));
File myFile0 = SD.open("ssid.txt");
if (myFile0) {
String tmsg="";
Serial.println("ssid.txt:");
// read from the file until there's nothing else in it:
while (myFile0.available()) {
tmsg=myFile0.readString();
}
tmsg.trim();
int len = tmsg.length()+1;
ssid[len];
tmsg.toCharArray(ssid, len);
// close the file:
myFile0.close();
Serial.println(ssid);
}
File myFile1 = SD.open("wifipsk.txt");
if (myFile1) {
String tmsg1="";
Serial.println("wifipsk.txt:");
// read from the file until there's nothing else in it:
while (myFile1.available()) {
tmsg1=myFile1.readString();
}
tmsg1.trim();
int len1 = tmsg1.length()+1;
pass[len1];
tmsg1.toCharArray(pass, len1);
// close the file:
myFile1.close();
Serial.println(pass);
}
File myFile2 = SD.open("thingkey.txt");
if (myFile2) {
String tmsg2="";
Serial.println("thingkey.txt:");
// read from the file until there's nothing else in it:
while (myFile2.available()) {
tmsg2=myFile2.readString();
}
tmsg2.trim();
int len2 = tmsg2.length()+1;
thingspeak_key[len2];
tmsg2.toCharArray(thingspeak_key, len2);
// close the file:
myFile2.close();
Serial.println(thingspeak_key);
}
delay(250);
Wire.begin(4,5);
delay(250);
while (myHTU21D.begin() != true)
{
Serial.println(F("HTU21D sensor is not present"));
delay(5000);
}
Serial.println(F("HTU21D sensor is present"));
newval = myHTU21D.readCompensatedHumidity();
newval1 = myHTU21D.readTemperature();
Serial.println(F(""));
Serial.print(F("Compensated Humidity: "));
Serial.print(newval);
Serial.println(F(" +-2%RH in range 0%RH - 100%RH at tmp. range 0deg.C - 80deg.C"));
Serial.println(F(""));
Serial.print(F("Temperature: "));
Serial.print(newval1);
Serial.println(F(" +-0.5 deg.C"));
Serial.println(F(""));
Serial.println(F(""));
// The ESP is capable of rendering 60fps in 80Mhz mode
// but that won't give you much time for anything else
// run it in 160Mhz mode or just set it to 30 fps
ui.setTargetFPS(60);
// Customize the active and inactive symbol
ui.setActiveSymbol(activeSymbol);
ui.setInactiveSymbol(inactiveSymbol);
// You can change this to
// TOP, LEFT, BOTTOM, RIGHT
ui.setIndicatorPosition(TOP);
// Defines where the first frame is located in the bar.
ui.setIndicatorDirection(LEFT_RIGHT);
// You can change the transition that is used
// SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN
ui.setFrameAnimation(SLIDE_LEFT);
// Add frames
ui.setFrames(frames, frameCount);
// Add overlays
ui.setOverlays(overlays, overlaysCount);
// Initialising the UI will init the display too.
ui.init();
display.flipScreenVertically();
// We start by connecting to a WiFi network
Serial.print(F("Connecting to "));
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.setFont(ArialMT_Plain_10);
display.drawString(0, 0, "Connecting to .....");
display.drawString(0, 10, ssid);
display.display();
Serial.println(ssid);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(F("."));
}
Serial.println(F(""));
String ipADD = "";
for (byte thisByte = 0; thisByte < 4; thisByte++) {
// print the value of each byte of the IP address:
ipADD+=WiFi.localIP()[thisByte], DEC;
ipADD+=".";
}
Serial.println(F("WiFi connected"));
Serial.println(F("IP address: "));
Serial.println(WiFi.localIP());
// clear the display
display.clear();
display.drawString(0, 0, "WiFi Connected to....");
display.drawString(0, 10, ssid);
display.drawString(0, 30, ipADD);
display.drawString(0, 40, "Please Wait....");
display.drawString(0, 50, "Setting Time.......");
display.display();
Serial.println(F("Starting UDP"));
udp.begin(localPort);
Serial.print(F("Local port: "));
Serial.println(udp.localPort());
delay(1000);
ThingSpeak.begin(client);
delay(10000);
humidity = ThingSpeak.readFloatField(weatherStationChannelNumber,1);
if(humidity!=0){Ds2=humidity;}
temperature = ThingSpeak.readFloatField(weatherStationChannelNumber,2);
if(temperature!=0){Ds1=temperature;}
Inttemperature = ThingSpeak.readFloatField(weatherStationChannelNumber,3);
Inthumidity = ThingSpeak.readFloatField(weatherStationChannelNumber,4);
pressure = ThingSpeak.readFloatField(weatherStationChannelNumber,5);
rainfall = ThingSpeak.readFloatField(weatherStationChannelNumber,6);
windSpeed = ThingSpeak.readFloatField(weatherStationChannelNumber,7);
CloudBase = ThingSpeak.readFloatField(weatherStationChannelNumber,8);
CH4Smoke = ThingSpeak.readFloatField(AirQuailityChannelNumber,1);
CMonoxide = ThingSpeak.readFloatField(AirQuailityChannelNumber,2);
while(cLckset==0){
// wait ten seconds before asking for the time again
delay(10000);
sEtNTP();}
setTime(epoch+1);// Allow for the time to get this displayed.....
ulNextMeas_ms = millis()+300000;
}
void sEtNTP(){
//get a random server from the pool
WiFi.hostByName(ntpServerName, timeServerIP);
sendNTPpacket(timeServerIP); // send an NTP packet to a time server
// wait to see if a reply is available
delay(1000);
int cb = udp.parsePacket();
if (!cb) {
Serial.println("no packet yet");
cLckset=0;
}
else {
cLckset=1;
Serial.print(F("packet received, length="));
Serial.println(cb);
// We've received a packet, read the data from it
udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
//the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, esxtract the two words:
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
Serial.print(F("Seconds since Jan 1 1900 = " ));
Serial.println(secsSince1900);
// now convert NTP time into everyday time:
Serial.print(F("Unix time = "));
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
epoch = secsSince1900 - seventyYears;
// print Unix time:
Serial.println(epoch);
// print the hour, minute and second:
Serial.print(F("The UTC time is ")); // UTC is the time at Greenwich Meridian (GMT)
Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day)
Serial.print(F(":"));
if ( ((epoch % 3600) / 60) < 10 ) {
// In the first 10 minutes of each hour, we'll want a leading '0'
Serial.print(F("0"));
}
Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute)
Serial.print(F(":"));
if ( (epoch % 60) < 10 ) {
// In the first 10 seconds of each minute, we'll want a leading '0'
Serial.print(F("0"));
}
Serial.println(epoch % 60); // print the second
}
}
void loop()
{
if (millis()>=ulNextMeas_ms)
{
ulNextMeas_ms = millis()+300000;
// Use WiFiClient class to create Thingspeak Post
WiFiClient client;
if (!client.connect("api.thingspeak.com", 80)) {
return;
}
String url = "/update?key=";
url += thingspeak_key;
url += "&field1=";
url += newval1;
url += "&field2=";
url += newval;
// This will send the request to Thingspeak
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: api.thingspeak.com\r\n" +
"Connection: close\r\n\r\n");
delay(50);
client.stop();
}
int remainingTimeBudget = ui.update();
if (remainingTimeBudget > 0) {
// You can do some work here
// Don't do stuff if you are below your
// time budget.
//delay(remainingTimeBudget);
if(millis()>=i2ctimer){ // 1 second repeat......
newval = myHTU21D.readCompensatedHumidity();
newval1 = myHTU21D.readTemperature();
i2ctimer=millis()+1000;
LcNt++;
if(LcNt==300){ // 5 minute repeat......
LcNt=0;
humidity = ThingSpeak.readFloatField(weatherStationChannelNumber,1);
if(humidity!=0){Ds2=humidity;}
temperature = ThingSpeak.readFloatField(weatherStationChannelNumber,2);
if(temperature!=0){Ds1=temperature;}
Inttemperature = ThingSpeak.readFloatField(weatherStationChannelNumber,3);
Inthumidity = ThingSpeak.readFloatField(weatherStationChannelNumber,4);
pressure = ThingSpeak.readFloatField(weatherStationChannelNumber,5);
rainfall = ThingSpeak.readFloatField(weatherStationChannelNumber,6);
windSpeed = ThingSpeak.readFloatField(weatherStationChannelNumber,7);
CloudBase = ThingSpeak.readFloatField(weatherStationChannelNumber,8);
CH4Smoke = ThingSpeak.readFloatField(AirQuailityChannelNumber,1);
CMonoxide = ThingSpeak.readFloatField(AirQuailityChannelNumber,2);
sEtNTP();
if(cLckset!=0){
cLckset=0;
setTime(epoch+1);// Allow for the time to get this displayed.......
}
}
}
}
}
// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{
Serial.println(F("sending NTP packet..."));
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
udp.beginPacket(address, 123); //NTP requests are to port 123
udp.write(packetBuffer, NTP_PACKET_SIZE);
udp.endPacket();
}
This is provided separately to maintain consistency with the Library Example.....
const char activeSymbol[] PROGMEM = {
B00000000,
B00000000,
B00011000,
B00100100,
B01000010,
B01000010,
B00100100,
B00011000
};
const char inactiveSymbol[] PROGMEM = {
B00000000,
B00000000,
B00000000,
B00000000,
B00011000,
B00011000,
B00000000,
B00000000
};