Here is the code for my display. Its much more complex than you need, so don't worry too much about that. It runs on a Wemos D1 mini. The display is a 128x64 graphic LCD display with i2c backpack, and a rotary encoder for input.
#include <ESP8266WiFiMulti.h>
#include <WiFiUdp.h>
#include <TimeLib.h>
#include <Wire.h>
#include <I2C_graphical_LCD_display.h>
I2C_graphical_LCD_display lcd;
WiFiClient client;
const byte clkPin = D7;
const byte dirPin = D6;
const byte btnPin = D5;
extern "C" {
#include "user_interface.h"
}
const unsigned long dataUpdatePeriod = 15 * 60 * 1000;
const unsigned long displayUpdatePeriod = 10 * 1000;
const char* ssid = "ssid";
const char* password = "password";
char myhostname[] = "DataDisplay";
static const char ntpServerName[] = "uk.pool.ntp.org";
int timeZone = 0; // Central European Time
ESP8266WiFiMulti wifiMulti;
WiFiUDP Udp;
unsigned int localPort = 8888; // local port to listen for UDP packets
int prevDay, prevHour, prevMin;
time_t getNtpTime();
void sendNTPpacket(IPAddress &address);
time_t prevDisplay = 0; // when the digital clock was displayed
String sensorData[20][6];
String displayPageName[20];
int displayPages;
void setup() {
pinMode(clkPin, INPUT_PULLUP);
pinMode(dirPin, INPUT_PULLUP);
pinMode(btnPin, INPUT_PULLUP);
Serial.begin(115200);
Wire.setClock(400000L);
lcd.begin();
//Connect to WiFi
wifi_station_set_hostname(myhostname);
wifiMulti.addAP("ssid1", "password1");
wifiMulti.addAP("ssid2", "password2");
Serial.println("Connecting");
lcd.gotoxy (0, 0);
lcd.print("Connecting");
int i = 0;
while (wifiMulti.run() != WL_CONNECTED) {
delay(500);
Serial.print(".");
lcd.print(".");
}
Serial.printf("\nConnected to '%s' IP address: %s RSSI: %d\n", WiFi.SSID().c_str(), WiFi.localIP().toString().c_str(), WiFi.RSSI());
lcd.clear();
lcd.gotoxy (0, 0);
lcd.printf("SSID: %s", WiFi.SSID().c_str());
lcd.gotoxy (0, 8);
lcd.printf("IP: %s", WiFi.localIP().toString().c_str());
lcd.gotoxy (0, 16);
lcd.printf("RSSI: %d", WiFi.RSSI());
delay(3000);
lcd.clear();
wifi_fpm_set_sleep_type(LIGHT_SLEEP_T); // Enable light sleep mode
Serial.println("Starting UDP");
Udp.begin(localPort);
Serial.print("Local port: ");
Serial.println(Udp.localPort());
Serial.println("waiting for sync");
setSyncProvider(getNtpTime);
setSyncInterval(24UL * 60 * 60);
}
/*-------- NTP code ----------*/
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
time_t getNtpTime()
{
IPAddress ntpServerIP; // NTP server's ip address
while (Udp.parsePacket() > 0) ; // discard any previously received packets
Serial.println("Transmit NTP Request");
// get a random server from the pool
WiFi.hostByName(ntpServerName, ntpServerIP);
Serial.print(ntpServerName);
Serial.print(": ");
Serial.println(ntpServerIP);
sendNTPpacket(ntpServerIP);
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
Serial.println("Receive NTP Response");
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serial.println("No NTP Response :-(");
return 0; // return 0 if unable to get the time
}
// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
// 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();
}
bool isBST(int year, int month, int day, int hour) {
// bst begins at 01:00 gmt on the last sunday of march
// and ends at 01:00 gmt (02:00 bst) on the last sunday of october
// january, february, and november are out
if (month < 3 || month > 10) return false;
// april to september are in
if (month > 3 && month < 10) return true;
// in march we are bst if its past 1am gmt on the last sunday in the month
if (month == 3) {
// last sunday of march
int lastMarSunday = (31 - (5 * year / 4 + 4) % 7);
if (day > lastMarSunday) return true;
if (day < lastMarSunday) return false;
if (hour < 1) return false;
return true;
}
// in october we must be before 1am gmt (2am bst) on the last sunday to be bst
if (month == 10) {
// last sunday of october
int lastOctSunday = (31 - (5 * year / 4 + 1) % 7);
if (day < lastOctSunday) return true;
if (day > lastOctSunday) return false;
if (hour >= 1) return false;
return true;
}
}
void updateClockDisplay() {
char weekdayStr[4];
strcpy(weekdayStr, dayShortStr(weekday()));
lcd.gotoxy (0, 56);
lcd.setInv(true);
lcd.printf("%02d:%02d:%02d %s %02d %s", hour(), minute(), second(), weekdayStr, day(), monthShortStr(month()));
lcd.setInv(false);
}
void updateSensorReadings() {
if (!client.connect("www.myserver.co.uk", 80)) {
Serial.println("Connection to host failed");
}
else {
Serial.println("Reading page");
client.println("GET /sensors_simple.php HTTP/1.1");
client.println("Host: www.myserver.co.uk");
client.println("Connection: close");
client.println();
displayPages = 0;
int pageLine = 0;
int lineCount = 0;
String line;
String lastPageName;
const int linesPerPage = 6;
while (!client.available()); //Wait for response
while (client.readStringUntil('\n') != "\r"); //Discard header
line = client.readStringUntil('\n'); //Discard unwanted line
while (client.available() && client.peek() != '\r') {
String newPageName = client.readStringUntil(',');
String sensorName = client.readStringUntil(',');
String sensorValue = client.readStringUntil('\n');
if (newPageName != lastPageName || pageLine >= linesPerPage) {
for (int pl = 0; pl < linesPerPage; pl++) sensorData[displayPages][pl] = " ";
pageLine = 0;
lastPageName = newPageName;
Serial.printf("New page: %s\n", newPageName.c_str());
displayPageName[displayPages++] = (newPageName + " ").substring(0, 21);
}
String spaces = String("______________________").substring(0, 21 - sensorName.length() - sensorValue.length());
sensorData[displayPages - 1][pageLine] = sensorName + spaces + sensorValue;
Serial.printf("Stored: %s\n", sensorData[displayPages - 1][pageLine++].c_str());
}
while (client.connected() && client.available()) {
line = client.readStringUntil('\n'); //Discard unwanted line
}
Serial.println("Connection closed");
client.stop();
}
}
int currentPage;
void updateDisplay(int page) {
lcd.gotoxy (0, 0);
lcd.setInv(true);
lcd.print(displayPageName[page]);
lcd.setInv(false);
for (int l = 0; l < 6; l++) {
lcd.gotoxy (0, l * 8 + 8);
lcd.print(sensorData[page][l]);
}
}
void loop() {
unsigned long timeNow = millis();
static unsigned long lastSensorTime;
static unsigned long lastDisplayTime;
if (timeStatus() != timeNotSet) {
if (now() != prevDisplay) { //update the display only if time has changed
prevDisplay = now();
updateClockDisplay();
if (minute() != prevMin) {
Serial.printf("Time is %02d:%02d:%02d\n", hour(), minute(), second());
prevMin = minute();
if (hour() != prevHour) {
prevHour = hour();
if (timeZone != (isBST(year(), month(), day(), hour()) ? 1 : 0)) {
timeZone = 1 - timeZone;
adjustTime(timeZone * 60 * 60);
Serial.print("Time Zone is now ");
Serial.println(timeZone);
}
}
}
}
}
if (lastSensorTime == 0 || (timeNow - lastSensorTime) > dataUpdatePeriod) {
lastSensorTime = timeNow;
updateSensorReadings();
}
if (lastDisplayTime == 0 || (timeNow - lastDisplayTime) > displayUpdatePeriod) {
lastDisplayTime = timeNow;
if (++currentPage >= displayPages) currentPage = 0;
updateDisplay(currentPage);
}
static byte lastClkPin = HIGH;
byte newClkPin = digitalRead(clkPin);
byte newDirPin = digitalRead(dirPin);
static byte lastBtnPin = HIGH;
byte newBtnPin = digitalRead(btnPin);
if (newClkPin != lastClkPin) {
if (newClkPin == LOW) {
if (newDirPin == HIGH) {
lastDisplayTime = timeNow;
if (++currentPage >= displayPages) currentPage = 0;
updateDisplay(currentPage);
}
else {
lastDisplayTime = timeNow;
if (--currentPage < 0) currentPage = displayPages - 1;
updateDisplay(currentPage);
}
}
delay(20);
lastClkPin = newClkPin;
}
}
Here is my PHP script:
<?php
$servername = "www.myserver.co.uk";
$username = "username";
$password = "password";
$dbname = "databasename";
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
echo "Sorry, the website is experiencing problems: " . $conn->connect_error();
exit;
}
$sql = "SELECT s.sensor_time, TIMESTAMPDIFF(MINUTE, s.sensor_time, NOW()) as sensor_age,
s.sensor_name, s.sensor_value, i.sensor_desc, i.sensor_units
FROM granary_sensors s
JOIN ( SELECT sensor_name, MAX(sensor_time) AS max_time
FROM granary_sensors
GROUP BY sensor_name) AS m
ON m.sensor_name = s.sensor_name
AND m.max_time = s.sensor_time
JOIN sensor_info i
ON i.sensor_name = s.sensor_name
WHERE TIMESTAMPDIFF(MINUTE, s.sensor_time, NOW()) < 60*24*90
ORDER BY s.sensor_name;";
if (!$result = $conn->query($sql)) {
echo "Sorry, the website is experiencing problems.";
exit;
}
// List sensors, values and last updates with links to graphs
while ($reading = $result->fetch_assoc()) {
echo $reading['sensor_name'] . ":" . $reading['sensor_value'] . $reading['sensor_units'];
if ($reading['sensor_age'] > 20) echo "*";
echo "\n";
}
$result->free();
$conn->close();
?>
Here is the output of the php script as seen on browser:
Conservatory,Abs Humid,7.5g/m3
Conservatory,Battery,3.76V
Conservatory,Rel Humid,77.33%
Conservatory,Temp,10.51C
Greenhouse,Abs Humid,4.58g/m3
Greenhouse,Battery,3.85V
Greenhouse,Rel Humid,96.31%
Greenhouse,Soil,0
Greenhouse,Temp,-0.28C
Ground Floor,Abs Humid,5.5g/m3
Ground Floor,Battery,3.23V
Ground Floor,Rel Humid,27.51%
Ground Floor,Temp,22.51C
Indoor,Battery,3.83V
Indoor,Pressure,953.7mb
Indoor,Rel Humid,34.24%
Indoor,Temp,22.14C
Middle Floor,Abs Humid,7.08g/m3
Middle Floor,Battery,3.61V
Middle Floor,Rel Humid,35.47%
Middle Floor,Temp,22.49C
Outdoor,Abs Humid,4.04g/m3
Outdoor,Battery,4.08V
Outdoor,Light,0Lux
Outdoor,Rel Humid,108.62%
Outdoor,Temp,-3.79C
Top Floor,Abs Humid,6.02g/m3
Top Floor,Battery,3.92V
Top Floor,Pressure,952.34mb
Top Floor,Rel Humid,34.64%
Top Floor,Temp,20.09C
Water Tank Room,Battery,3.92V
Water Tank Room,Temp,21C
Weather Station,Abs Humid,0.04g/m3
Weather Station,Battery,3.44V
Weather Station,Rain,0mm/hr
Weather Station,Rel Humid,1%
Weather Station,Temp,-2.1C
Weather Station,Wind Dir,45Degrees
Weather Station,Wind Gust,7Km/hr
Weather Station,Wind Speed,1.42Km/hr
As you can see, each line has 3 fields: location of sensor, name of sensor, value of sensor.