Weather Station Clock Scrolling Message Matrix P4 Web Server ESP8266
Arduino tutorial:
RGB Matrix p4 64*32
DHT11
ESP8266
RGB Matrix p4 64*32
DHT11
ESP8266
Schema
ARDUINO 1.8.9
Weather Station Clock Scrolling Message Matrix P4 Web Server ESP8266
Library
DHT Sensor Library:
https://github.com/adafruit/DHT-sensor-library
esp8266 library (nodemcu) found at
https://github.com/esp8266/Arduino
RGB matrix Panel Library:
https://github.com/2dom/PxMatrix
Adafruit_GFX Library:
https://github.com/adafruit/Adafruit-GFX-Library
package_esp8266com_index.json:
http://arduino.esp8266.com/stable/package_esp8266com_index.json
https://github.com/adafruit/DHT-sensor-library
esp8266 library (nodemcu) found at
https://github.com/esp8266/Arduino
RGB matrix Panel Library:
https://github.com/2dom/PxMatrix
Adafruit_GFX Library:
https://github.com/adafruit/Adafruit-GFX-Library
package_esp8266com_index.json:
http://arduino.esp8266.com/stable/package_esp8266com_index.json
Code
// REQUIRES the following Arduino libraries:
// - DHT Sensor Library: https://github.com/adafruit/DHT-sensor-library
// - RGB matrix Panel Library: https://github.com/2dom/PxMatrix
// - Adafruit_GFX Library: https://github.com/adafruit/Adafruit-GFX-Library
// - esp8266 library (nodemcu) found at https://github.com/esp8266/Arduino
// - package_esp8266com_index.json found at http://arduino.esp8266.com/stable/package_esp8266com_index.json
// Find All "Great Projects" Videos : https://www.youtube.com/channel/UCCC8DuqicBtP3A_aC53HYDQ/videos
#include "DHT.h"
#define DHTPIN 0
#define DHTTYPE DHT11 // DHT 11
#include <ESP8266WiFi.h>
#include <time.h>
#include <Adafruit_GFX.h>
#include <FreeMonoBold12pt7b.h>
#include <kongtext4pt7b.h>
#include <PxMatrix.h>
#include <Ticker.h>
Ticker display_ticker;
#define P_LAT 16
#define P_A 5
#define P_B 4
#define P_C 15
#define P_D 12
#define P_OE 2
// Pins for LED MATRIX
#define matrix_width 64
#define matrix_height 32
DHT dht(DHTPIN, DHTTYPE);
float Humi;
float Temp;
static uint32_t lastTime = 0; // millis() memory
static bool flasher = false; // seconds passing flasher
uint8_t frameDelay = 10; // default frame delay value
int timezone = 1 * 3600;
int dst = 0;
int h,m,s,d,rh,t;
uint8_t dow;
int day;
uint8_t month;
String year;
String date;
String WeatherT;
String WeatherH;
String text;
#define MAX_MESG 9
#define MAX_MES 20
#define MAX_ME 50
#define BUF_SIZE 612
char curMessage[BUF_SIZE] = { "Hello! Enter new message?" };
char newMessage[BUF_SIZE];
bool newMessageAvailable = false;
static bool Mode = true;
uint8_t r=0, g=0, b=0;
unsigned int NewRTCh = 24;
unsigned int NewRTCm = 60;
unsigned int NewRTCs = 10;
char szTime[4]; // 00
char szMesg[10] = "";
char szBuf[10];
const char* ssid = "**********"; // SSID of local network
const char* password = "**********"; // Password on network
WiFiServer server(80);
// This defines the 'on' time of the display is us. The larger this number,
// the brighter the display. If too large the ESP will crash
uint8_t display_draw_time=10; //10-50 is usually fine
//PxMATRIX display(matrix_width,matrix_height,P_LAT, P_OE,P_A,P_B,P_C);
PxMATRIX display(64,32,P_LAT, P_OE,P_A,P_B,P_C,P_D);
//PxMATRIX display(64,64,P_LAT, P_OE,P_A,P_B,P_C,P_D,P_E);
// Some standard colors
uint16_t myRED = display.color565(255, 0, 0);
uint16_t myGREEN = display.color565(0, 255, 0);
uint16_t myBLUE = display.color565(0, 0, 255);
uint16_t myWHITE = display.color565(255, 255, 255);
uint16_t myYELLOW = display.color565(255, 255, 0);
uint16_t myCYAN = display.color565(0, 255, 255);
uint16_t myMAGENTA = display.color565(255, 0, 255);
uint16_t myBLACK = display.color565(255, 255, 255);
uint16_t myCOLORS[8]={myRED,myGREEN,myBLUE,myWHITE,myYELLOW,myCYAN,myMAGENTA,myBLACK};
#ifdef ESP8266
// ISR for display refresh
void display_updater()
{
display.display(display_draw_time);
}
#endif
void display_update_enable(bool is_enable)
{
#ifdef ESP8266
if (is_enable)
display_ticker.attach(0.002, display_updater);
else
display_ticker.detach();
#endif
}
void(* resetFunc) (void) = 0;//declare reset function at address 0
char *mon2str(uint8_t mon, char *psz, uint8_t len)
{
static const char str[][4] PROGMEM =
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
*psz = '\0';
mon--;
if (mon < 12)
{
strncpy_P(psz, str[mon], len);
psz[len] = '\0';
}
return(psz);
}
char *dow2str(uint8_t code, char *psz, uint8_t len)
{
static const char str[][10] PROGMEM =
{
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
};
*psz = '\0';
code--;
if (code < 7)
{
strncpy_P(psz, str[code], len);
psz[len] = '\0';
}
return(psz);
}
void getDateWeb()
{
date = dow2str(dow, szBuf, sizeof(szBuf)-1);
date += " ";
if (day < 10) { date += "0"; }
date += day;
date += mon2str(month, szBuf, sizeof(szBuf)-1);
date += year;
date += " ";
if (h < 10) { date += "0"; }
date += h;
date += ":";
if (m < 10) { date += "0"; }
date += m;
date += ":";
if (s < 10) { date += "0"; }
date += s;
}
void getWeather()
{
dht.begin();
Temp = dht.readTemperature();
Humi = dht.readHumidity();
if (isnan(Humi) || isnan(Temp)) {
delay(100);
return;
}
WeatherT = "Temperature : ";
WeatherT += Temp;
WeatherT += " $";
WeatherH = "Humidity : ";
WeatherH += Humi;
WeatherH += " %";
t = Temp;
rh = Humi;
}
void getDate()
// Code for reading date
{
text = mon2str(month, szBuf, sizeof(szBuf)-1);
display.setCursor(0,0);
display.fillRect(0, 0, 64, 8, display.color565(0, 0, 0));
display.setFont(&kongtext4pt7b);
display.setTextColor(myRED);
if (day < 10) { display.print("0"); }
display.print(day);
display.setTextColor(myGREEN);
display.print(text);
display.setTextColor(myRED);
display.print(year);
display.setFont();
}
void getDowe()
// Code for reading date
{
text = dow2str(dow, szBuf, sizeof(szBuf)-1);
uint16_t text_length = text.length();
int xpos = (matrix_width - text_length*7)/2;
display.setCursor(xpos,0);
display.fillRect(0, 0, 64, 8, display.color565(0, 0, 0));
display.setFont(&kongtext4pt7b);
uint8_t y = 0;
for (y=0; y<10; y++) {
display.setTextColor(Whel(y));
display.print(text[y]);
}
display.setFont();
}
void getRTCh(char *psz)
// Code for reading clock time
{
sprintf(psz, "%02d", h);
display.setCursor(0, 16);
display.setFont(&FreeMonoBold12pt7b);
display.setTextColor(myMAGENTA);
display.fillRect(0, 8, 24, 15, display.color565(0, 0, 0));
display.print(szTime);
display.setFont();
NewRTCh=h;
}
void getRTCm(char *psz)
// Code for reading clock time
{
sprintf(psz, "%02d", m);
display.setCursor(26, 16);
display.setFont(&FreeMonoBold12pt7b);
display.setTextColor(myMAGENTA);
display.fillRect(26, 8, 25, 15, display.color565(0, 0, 0));
display.print(szTime);
display.setFont();
NewRTCm=m;
}
void getTim(char *psz, bool f = true)
// Code for reading clock time
{
if (NewRTCs != s/10)
{
display.setCursor(20, 8);
display.setTextSize(2);
display.setTextColor(myCOLORS[g]);
display.fillRect(24, 12, 2, 6, display.color565(0, 0, 0));
display.print(f ? ':' : ' ');
display.setCursor(54, 10);
display.setTextSize(1);
display.fillRect(54, 10, 10, 6, display.color565(0, 0, 0));
display.setFont(&kongtext4pt7b);
display.setTextColor(myCOLORS[b]);
display.print(f ? ' ' : '*');
display.setFont();
display.setCursor(51, 16);
display.setTextSize(1);
display.setTextColor(myCOLORS[r]);
sprintf(psz, "%02d", s);
display.fillRect(51, 17, 13, 6, display.color565(0, 0, 0));
display.setFont(&kongtext4pt7b);
display.print(szTime);
display.setFont();
NewRTCs=s/10;
}
else
{
display.setCursor(20, 8);
display.setTextSize(2);
display.setTextColor(myCOLORS[g]);
display.fillRect(24, 12, 2, 6, display.color565(0, 0, 0));
display.print(f ? ':' : ' ');
display.setCursor(54, 10);
display.setTextSize(1);
display.fillRect(54, 10, 10, 6, display.color565(0, 0, 0));
display.setFont(&kongtext4pt7b);
display.setTextColor(myCOLORS[b]);
display.print(f ? ' ' : '*');
display.setFont();
display.setCursor(51, 16);
display.setTextColor(myCOLORS[r]);
sprintf(psz, "%02d", s);
display.fillRect(58, 17, 6, 6, display.color565(0, 0, 0));
display.setFont(&kongtext4pt7b);
display.print(szTime);
display.setFont();
}
}
void scroll_text(uint8_t ypos, unsigned long scroll_delay, String text)
{
uint16_t text_length = text.length();
// Asuming 5 pixel average character width
for (int xpos=matrix_width; xpos>-(matrix_width+text_length*5); xpos--)
{
handleWiFi();
display.setCursor(xpos,ypos);
display.fillRect(0, 24, 64, 8, display.color565(0, 0, 0));
display.setFont(&kongtext4pt7b);
display.print(text);
display.setFont();
delay(scroll_delay);
yield();
if (millis() - lastTime >= 1000)
{
lastTime = millis();
updateTime();
getTim(szTime, flasher);
flasher = !flasher;
if (NewRTCh != h)
{
getRTCh(szTime);
}
if (NewRTCm != m)
{
getRTCm(szTime);
getWeather();
Mode = true;
}
}
}
r++;
if(r == 8) {
r = 0;
g++;
if(g == 8) {
g = 0;
b++;
if(b == 8) {
b = 0;
}
}
}
}
const char WebResponse[] = "HTTP/1.1 200 OK\nContent-Type: text/html\n\n";
char WebPage[] =
"<!DOCTYPE html>" \
"<html>" \
"<head>" \
"<meta http-equiv=\"refresh\" content=\"60\"; url=\"http://192.168.1.24\" />" \
"<title>ESP8266 Weather Station</title>" \
"<script>" \
"strLine = \"\";" \
"function SendData()" \
"{" \
" nocache = \"/&nocache=\" + Math.random() * 1000000;" \
" var request = new XMLHttpRequest();" \
" strLine = \"&MSG=\" + document.getElementById(\"data_form\").Message.value;" \
" strLine = strLine + \"/&SP=\" + document.getElementById(\"data_form\").Speed.value;" \
" request.open(\"GET\", strLine + nocache, false);" \
" request.send(null);" \
"}" \
"function restart()" \
"{" \
" nocache = \"/&nocache=\" + Math.random() * 1000000;" \
" var request = new XMLHttpRequest();" \
" strLine = strLine + \"/&Rst=\" + document.getElementById(\"data_form\").rerset.value;" \
" request.open(\"GET\", strLine + nocache, false);" \
" request.send(null);" \
"}" \
"function resttime()" \
"{" \
" nocache = \"1/&nocache=\";" \
" var request = new XMLHttpRequest();" \
" strLine = strLine + \"/&St=\" + document.getElementById(\"data_form\").restmie.value;" \
" request.open(\"GET\", strLine + nocache, false);" \
" request.send(null);" \
"}" \
"</script>" \
"<style>" \
"body {" \
"text-align: center;" \
"margin: 5;" \
"padding: 5;" \
"background-color: rgba(72,72,72,0.4);" \
"}" \
"#wrapper { " \
"margin: 0 auto;" \
"width: 100%;" \
"}" \
"#form-div {" \
"background-color: rgba(72,72,72,0.4);" \
"padding-left: 10px;" \
"padding-right: 10px;" \
"padding-bottom: 80px;" \
"padding-top: 5px;" \
"-moz-border-radius: 7px;" \
"-webkit-border-radius: 7px;" \
"}" \
".data-input {" \
"padding: 10px 5px 5px 5px;" \
"background-color: #bbbbff;" \
"font-size:26px;" \
"color:red;" \
"padding-bottom:46px;" \
"border: 5px solid #444444;" \
"-moz-border-radius: 7px;" \
"-webkit-border-radius: 7px;" \
"}" \
".button-blue {" \
"padding: 5px 5px 5px 5px;" \
"float:left;" \
"width: 100%;" \
"border: #fbfb00 solid 3px;" \
"cursor:pointer;" \
"background-color: #4444ff;" \
"color:white;" \
"font-size:20px;" \
"padding-bottom:5px;" \
"font-weight:700;" \
"-moz-border-radius: 7px;" \
"-webkit-border-radius: 7px;" \
"}" \
".button-blue:hover {" \
"background-color: #2222aa;" \
"color: #ff93bd;" \
"}" \
".text {" \
"background-color: #ff0000;" \
"font-size:76px;" \
"color: #ffff99;" \
"}" \
"table {" \
"border: 2px solid #ff00ff;" \
"background-color: #ffffff;" \
"width:100%;" \
"color: #0000ff;" \
"-moz-border-radius: 7px;" \
"-webkit-border-radius: 7px;" \
"}" \
"h1 {" \
"color: #ff0000;" \
"background-color: #ffff00;" \
"-moz-border-radius: 7px;" \
"-webkit-border-radius: 7px;" \
"}" \
"tr {" \
"border: 2px solid #ff0000;" \
"background-color: #ffff00;" \
"color: #ff0000;" \
"-moz-border-radius: 7px;" \
"-webkit-border-radius: 7px;" \
"}" \
"td {" \
"border: 2px solid #ff0000;" \
"padding: 16px;" \
"-moz-border-radius: 7px;" \
"-webkit-border-radius: 7px;" \
"}" \
"</style>" \
"</head>" \
"<body>" \
"<div id=\"wrapper\">" \
"<div id=\"form-div\">" \
"<div class=\"submit\">" \
"<h1>ESP8266 Weather Station</h1>" \
"</div>" \
"<form id=\"data_form\" name=\"frmText\">" \
"<div class=\"data-input\">" \
"Message <input type=\"text\" name=\"Message\" maxlength=\"655\" class=\"button-white\" style=\"width:90%;height:35px;font-size:30px\">" \
"<input type=\"range\" name=\"Speed\"min=\"0\" max=\"50\" class=\"button-blue\" style=\"width:78%\">" \
"<input type=\"submit\" value=\"Send\" style=\"width:20%;float:right\" onclick=\"SendData()\" class=\"button-blue\">" \
"</div></br></br>" \
"<div class=\"data-input\">" \
"<input type=\"button\" name = \"restmie\" value=\"Update Time\" onClick=\"resttime()\" class=\"button-blue\">" \
"</div><div class=\"data-input\">" \
"<input type=\"button\" name = \"rerset\" value=\"Restart ESP8266\" onClick=\"restart()\" class=\"button-blue\">" \
"</div></form>" \
"<div class=\"data-input\">" \
"<table>" \
"<tr>" \
"<td style=\"width:48%\"><b>Temp. Celsius</b></td>" \
"<td style=\"width:48%\"><b>Humidity</b></td>" \
"</tr>" \
"<tr class=\"text\">";
uint8_t htoi(char c)
{
c = toupper(c);
if ((c >= '0') && (c <= '9')) return(c - '0');
if ((c >= 'A') && (c <= 'F')) return(c - 'A' + 0xa);
return(0);
}
void getData(char *szMesg, uint8_t len)
// Message may contain data for:
// New text (/&MSG=)
// Invert (/&I=)
// Speed (/&SP=)
{
char *pStart, *pEnd; // pointer to start and end of text
// check text message
pStart = strstr(szMesg, "/&MSG=");
if (pStart != NULL)
{
char *psz = newMessage;
pStart += 6; // skip to start of data
pEnd = strstr(pStart, "/&");
if (pEnd != NULL)
{
while (pStart != pEnd)
{
if ((*pStart == '%') && isdigit(*(pStart + 1)))
{
// replace %xx hex code with the ASCII character
char c = 0;
pStart++;
c += (htoi(*pStart++) << 4);
c += htoi(*pStart++);
*psz++ = c;
}
else
*psz++ = *pStart++;
}
*psz = '\0'; // terminate the string
newMessageAvailable = (strlen(newMessage) != 0);
}
}
// check reset time
pStart = strstr(szMesg, "/&St=");
if (pStart != NULL)
{
pStart += 5; // skip to start of data
if(*pStart != NULL)
{
getTime();
}
}
// check reset
pStart = strstr(szMesg, "/&Rst=");
if (pStart != NULL)
{
pStart += 6; // skip to start of data
if(*pStart != NULL)
{
resetFunc();
}
}
// check speed
pStart = strstr(szMesg, "/&SP=");
if (pStart != NULL)
{
pStart += 5; // skip to start of data
int16_t speed = atoi(pStart);
frameDelay = speed;
}
}
void handleWiFi(void)
{
static enum { S_IDLE, S_WAIT_CONN, S_READ, S_EXTRACT, S_RESPONSE, S_DISCONN } state = S_IDLE;
static char szBuf[1024];
static uint16_t idxBuf = 0;
static WiFiClient client;
static uint32_t timeStart;
switch (state)
{
case S_IDLE: // initialise
idxBuf = 0;
state = S_WAIT_CONN;
break;
case S_WAIT_CONN: // waiting for connection
{
client = server.available();
if (!client) break;
if (!client.connected()) break;
timeStart = millis();
state = S_READ;
}
break;
case S_READ: // get the first line of data
while (client.available())
{
char c = client.read();
if ((c == '\r') || (c == '\n'))
{
szBuf[idxBuf] = '\0';
client.flush();
state = S_EXTRACT;
}
else
szBuf[idxBuf++] = (char)c;
}
if (millis() - timeStart > 1000)
{
state = S_DISCONN;
}
break;
case S_EXTRACT: // extract data
// Extract the string from the message if there is one
getData(szBuf, BUF_SIZE);
state = S_RESPONSE;
break;
case S_RESPONSE: // send the response to the client
// Return the response to the client (web page)
getDateWeb();
client.print(WebResponse);
client.print(WebPage);
sendXMLFile(client);
state = S_DISCONN;
break;
case S_DISCONN: // disconnect client
client.flush();
client.stop();
state = S_IDLE;
break;
default: state = S_IDLE;
}
}
void setup() {
// Define your display layout here, e.g. 1/8 step
display.begin(16);
// Define your scan pattern here {LINE, ZIGZAG, ZAGGIZ, WZAGZIG, VZAG} (default is LINE)
//display.setScanPattern(LINE);
// Define multiplex implemention here {BINARY, STRAIGHT} (default is BINARY)
//display.setMuxPattern(BINARY);
display.setFastUpdate(true);
display.setRotation(0); // we don't wrap text so it scrolls nicely
display.clearDisplay();
display.setTextColor(myCYAN);
display.setCursor(2,0);
display.println("Connecting");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
display.print(".");
}
display.setTextWrap(false);
display.clearDisplay();
display.println("");
display.print("WiFi OK");
display_update_enable(true);
delay(3000);
getDate();
getWeather();
// Start the server
server.begin();
newMessage[0] = '\0';
}
void loop() {
if (newMessageAvailable)
{
strcpy(curMessage, newMessage);
newMessageAvailable = false;
}
if (Mode)
{
getDowe();
scroll_text(24,12,WeatherT);
scroll_text(24,12,WeatherH);
getDate();
Mode = false;
}
scroll_text(24,frameDelay,curMessage);
}
void getTime()
{
configTime(timezone, dst, "pool.ntp.org","time.nist.gov");
while(!time(nullptr)){
display.print(".");
}
}
void updateTime()
{
time_t now = time(nullptr);
struct tm* p_tm = localtime(&now);
dow = p_tm->tm_wday+1;
day = p_tm->tm_mday;
month = p_tm->tm_mon + 1;
year = p_tm->tm_year + 1900;
h = p_tm->tm_hour;
m = p_tm->tm_min;
s = p_tm->tm_sec;
}
void sendXMLFile(WiFiClient cl){
// send rest of HTTP header
cl.print("<td style=\"width:48%\">");
cl.print(t);
cl.print(".0 *C</td><td style=\"width:48%\">");
cl.print(rh);
cl.print(".0 %</td></tr></table></div><br>");
cl.print("<div class=\"submit\">");
cl.print("<input type=\"button\" value=\"Actualiser\"");
cl.print(" onClick=\"javascript:window.location.reload()\" class=\"button-blue\">");
cl.print("</div><div class=\"submit\">");
cl.print("<input type=\"button\" value=\"More Progets\" onClick=\"Javascript:window.open('https://www.youtube.com/channel/UCCC8DuqicBtP3A_aC53HYDQ/videos');\"");
cl.print(" class=\"button-blue\"></div>");
cl.print("<div class=\"submit\" style=\"width:100%\"><h1>");
cl.print(date);
cl.print("</h1></div>");
cl.print("</div></div></body></html>");
}
// Input a value 0 to 24 to get a color value.
uint16_t Whel(byte WheelPos) {
if(WheelPos < 1) {
return display.color565(255, 0, 0);
} else if(WheelPos < 2) {
WheelPos -= 1;
return display.color565(0, 255, 0);
} else if(WheelPos < 3) {
WheelPos -= 2;
return display.color565(255, 255, 0);
} else if(WheelPos < 4) {
WheelPos -= 3;
return display.color565(255, 0, 255);
} else if(WheelPos < 5) {
WheelPos -= 4;
return display.color565(0, 0, 255);
} else if(WheelPos < 6) {
WheelPos -= 5;
return display.color565(0, 255, 255);
} else if(WheelPos < 7) {
WheelPos -= 6;
return display.color565(255, 0, 255);
} else if(WheelPos < 8) {
WheelPos -= 7;
return display.color565(255, 255, 0);
} else if(WheelPos < 9) {
WheelPos -= 8;
return display.color565(255, 0, 0);
} else {
WheelPos -= 9;
return display.color565(255, 255, 255);
}
}
