Hardware components | ||||||
| × | 1 | ||||
| × | 7 | ||||
Software apps and online services | ||||||
| ||||||
Hand tools and fabrication machines | ||||||
|
This is my first project with ESP8266 so I wanted to try to make some simple device. From one of my previous projects (Bluetooth-controlled scrolling text), I have a finished 8x56 LEDs matrix consisting of 7pcs MAX7219 LED matrix modules 8x8, and located in the appropriate box.
Let me mention that these modules are older, and as you see in the given picture, contain DIL IC on the front. The new modules are made in smd technology and usually composed of 4 coupled matrices, and they are turned 90 degrees clockwise.
I decided to make a beautiful animated (flip) clock with big digits, which is synchronized over the Internet. The basis for my project was the code of Pawel A. Hernik from which I removed the part that shows the weather information and currency rate. I did this to make the code as simple as possible and more understandable.I also made the following changes to adjust to my project:
- Display of 7 instead of 6 matrices
- Clock and seconds blinking dots are moved for 4 LEDs to the right
- Texts "connecting" and "getting data" are displayed in the middle of the screen
- An increased period of time between two data collections from a server
- UTC offset changed to "1" for my country
We must first install the ESP8266 board on Arduino IDE, and then upload the code on the appropriate board and port. The code cannot be compiled on the latest version of the ESP board (2.5.0), so we must install an older version (2.4.2).
On the same hardware with minimal changes, it can be installed more codes.For example, in the video below, you can see a YouTube channel Subscriptions and views counter.as can be seen from the picture, the circuit is very simple, and the two codes are:
/*
ESP-01 pinout from top:
GND GP2 GP0 RX/GP3
TX/GP1 CH RST VCC
MAX7219
ESP-1 from rear
Re Br Or Ye
Gr -- -- --
USB to Serial programming
ESP-1 from rear, FF to GND, RR to GND before upload
Gr FF -- Bl
Wh -- RR Vi
GPIO 2 - DataIn
GPIO 1 - LOAD/CS
GPIO 0 - CLK
------------------------
NodeMCU 1.0 pinout:
D8 - DataIn
D7 - LOAD/CS
D6 - CLK
*/
#include "Arduino.h"
#include <ESP8266WiFi.h>
WiFiClient client;
String date;
#define NUM_MAX 7
// for ESP-01 module
//#define DIN_PIN 2 // D4
//#define CS_PIN 3 // D9/RX
//#define CLK_PIN 0 // D3
// for NodeMCU 1.0
#define DIN_PIN 15 // D8
#define CS_PIN 13 // D7
#define CLK_PIN 12 // D6
#include "max7219.h"
#include "fonts.h"
// =======================================================================
// CHANGE YOUR CONFIG HERE:
// =======================================================================
const char* ssid = "SSID"; // SSID of local network
const char* password = "password"; // Password on network
// =======================================================================
void setup()
{
Serial.begin(115200);
initMAX7219();
sendCmdAll(CMD_SHUTDOWN,1);
sendCmdAll(CMD_INTENSITY,0);
Serial.print("Connecting WiFi ");
WiFi.begin(ssid, password);
printStringWithShift("Connecting ",16);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected: "); Serial.println(WiFi.localIP());
}
// =======================================================================
#define MAX_DIGITS 16
byte dig[MAX_DIGITS]={0};
byte digold[MAX_DIGITS]={0};
byte digtrans[MAX_DIGITS]={0};
int updCnt = 0;
int dots = 0;
long dotTime = 0;
long clkTime = 0;
int dx=0;
int dy=0;
byte del=0;
int h,m,s;
// =======================================================================
void loop()
{
if(updCnt<=0) { // every 10 scrolls, ~450s=7.5m
updCnt = 10;
Serial.println("Getting data ...");
printStringWithShift(" Getting data",15);
getTime();
Serial.println("Data loaded");
clkTime = millis();
}
if(millis()-clkTime > 40000 && !del && dots) { // clock for 15s, then scrolls for about 30s
printStringWithShift(date.c_str(),40);
delay(5000);
updCnt--;
clkTime = millis();
}
if(millis()-dotTime > 500) {
dotTime = millis();
dots = !dots;
}
updateTime();
showAnimClock();
}
// =======================================================================
void showSimpleClock()
{
dx=dy=0;
clr();
showDigit(h/10, 4, dig6x8);
showDigit(h%10, 12, dig6x8);
showDigit(m/10, 21, dig6x8);
showDigit(m%10, 29, dig6x8);
showDigit(s/10, 38, dig6x8);
showDigit(s%10, 46, dig6x8);
setCol(19,dots ? B00100100 : 0);
setCol(36,dots ? B00100100 : 0);
refreshAll();
}
// =======================================================================
void showAnimClock()
{
byte digPos[6]={4,12,21,29,38,46};
int digHt = 12;
int num = 6;
int i;
if(del==0) {
del = digHt;
for(i=0; i<num; i++) digold[i] = dig[i];
dig[0] = h/10 ? h/10 : 10;
dig[1] = h%10;
dig[2] = m/10;
dig[3] = m%10;
dig[4] = s/10;
dig[5] = s%10;
for(i=0; i<num; i++) digtrans[i] = (dig[i]==digold[i]) ? 0 : digHt;
} else
del--;
clr();
for(i=0; i<num; i++) {
if(digtrans[i]==0) {
dy=0;
showDigit(dig[i], digPos[i], dig6x8);
} else {
dy = digHt-digtrans[i];
showDigit(digold[i], digPos[i], dig6x8);
dy = -digtrans[i];
showDigit(dig[i], digPos[i], dig6x8);
digtrans[i]--;
}
}
dy=0;
setCol(19,dots ? B00100100 : 0);
setCol(36,dots ? B00100100 : 0);
refreshAll();
delay(30);
}
// =======================================================================
void showDigit(char ch, int col, const uint8_t *data)
{
if(dy<-8 | dy>8) return;
int len = pgm_read_byte(data);
int w = pgm_read_byte(data + 1 + ch * len);
col += dx;
for (int i = 0; i < w; i++)
if(col+i>=0 && col+i<8*NUM_MAX) {
byte v = pgm_read_byte(data + 1 + ch * len + 1 + i);
if(!dy) scr[col + i] = v; else scr[col + i] |= dy>0 ? v>>dy : v<<-dy;
}
}
// =======================================================================
void setCol(int col, byte v)
{
if(dy<-8 | dy>8) return;
col += dx;
if(col>=0 && col<8*NUM_MAX)
if(!dy) scr[col] = v; else scr[col] |= dy>0 ? v>>dy : v<<-dy;
}
// =======================================================================
int showChar(char ch, const uint8_t *data)
{
int len = pgm_read_byte(data);
int i,w = pgm_read_byte(data + 1 + ch * len);
for (i = 0; i < w; i++)
scr[NUM_MAX*8 + i] = pgm_read_byte(data + 1 + ch * len + 1 + i);
scr[NUM_MAX*8 + i] = 0;
return w;
}
// =======================================================================
void printCharWithShift(unsigned char c, int shiftDelay) {
if (c < ' ' || c > '~'+25) return;
c -= 32;
int w = showChar(c, font);
for (int i=0; i<w+1; i++) {
delay(shiftDelay);
scrollLeft();
refreshAll();
}
}
// =======================================================================
void printStringWithShift(const char* s, int shiftDelay){
while (*s) {
printCharWithShift(*s, shiftDelay);
s++;
}
}
// =======================================================================
float utcOffset = 1;
long localEpoc = 0;
long localMillisAtUpdate = 0;
void getTime()
{
WiFiClient client;
if (!client.connect("www.google.com", 80)) {
Serial.println("connection to google failed");
return;
}
client.print(String("GET / HTTP/1.1\r\n") +
String("Host: www.google.com\r\n") +
String("Connection: close\r\n\r\n"));
int repeatCounter = 0;
while (!client.available() && repeatCounter < 10) {
delay(500);
//Serial.println(".");
repeatCounter++;
}
String line;
client.setNoDelay(false);
while(client.connected() && client.available()) {
line = client.readStringUntil('\n');
line.toUpperCase();
if (line.startsWith("DATE: ")) {
date = " "+line.substring(6, 22);
h = line.substring(23, 25).toInt();
m = line.substring(26, 28).toInt();
s = line.substring(29, 31).toInt();
localMillisAtUpdate = millis();
localEpoc = (h * 60 * 60 + m * 60 + s);
}
}
client.stop();
}
// =======================================================================
void updateTime()
{
long curEpoch = localEpoc + ((millis() - localMillisAtUpdate) / 1000);
long epoch = round(curEpoch + 3600 * utcOffset + 86400L) % 86400L;
h = ((epoch % 86400L) / 3600) % 24;
m = (epoch % 3600) / 60;
s = epoch % 60;
}
// =======================================================================
/*
Code for the video:
https://youtu.be/mn9L85bhyjI
(c)2016 Pawel A. Hernik
ESP-01 pinout:
GPIO 2 - DataIn
GPIO 1 - LOAD/CS
GPIO 0 - CLK
------------------------
NodeMCU 1.0 pinout:
D8 - DataIn
D7 - LOAD/CS
D6 - CLK
*/
#include "Arduino.h"
#include <ESP8266WiFi.h>
WiFiClient client;
#define NUM_MAX 7
#define ROTATE 0
// for ESP-01 module
//#define DIN_PIN 2 // D4
//#define CS_PIN 3 // D9/RX
//#define CLK_PIN 0 // D3
// for NodeMCU 1.0
#define DIN_PIN 15 // D8
#define CS_PIN 13 // D7
#define CLK_PIN 12 // D6
#include "max7219.h"
#include "fonts.h"
// =======================================================================
// Your config below!
// =======================================================================
const char* ssid = "SSID"; // SSID of local network
const char* password = "password"; // Password on network
const char* YTchannel = "Youtube user ID"; // YT user id
// =======================================================================
void setup()
{
Serial.begin(115200);
initMAX7219();
sendCmdAll(CMD_SHUTDOWN,1);
sendCmdAll(CMD_INTENSITY,0);
Serial.print("Connecting WiFi ");
WiFi.begin(ssid, password);
printStringWithShift("... WiFi ... ",20);
while (WiFi.status() != WL_CONNECTED) {
Serial.print("."); delay(500);
}
Serial.println("");
Serial.print("Connected: "); Serial.println(WiFi.localIP());
}
// =======================================================================
void loop()
{
Serial.println("Getting data ...");
printStringWithShift(" ... YT ... ",20);
int subs, views, cnt = 0;
String yt1,yt2;
while(1) {
if(!cnt--) {
cnt = 50; // data is refreshed every 50 loops
if(getYTSubs(YTchannel,&subs,&views)==0) {
yt1 = " SUBSCRIBERS: "+String(subs)+" ";
yt2 = " VIEWS: "+String(views);
} else {
yt1 = " YouTube";
yt2 = " Error!";
}
}
printStringWithShift(yt1.c_str(),20);
delay(3000);
printStringWithShift(yt2.c_str(),20);
delay(3000);
}
}
// =======================================================================
int showChar(char ch, const uint8_t *data)
{
int len = pgm_read_byte(data);
int i,w = pgm_read_byte(data + 1 + ch * len);
for (i = 0; i < w; i++)
scr[NUM_MAX*8 + i] = pgm_read_byte(data + 1 + ch * len + 1 + i);
scr[NUM_MAX*8 + i] = 0;
return w;
}
// =======================================================================
void printCharWithShift(unsigned char c, int shiftDelay) {
if (c < ' ' || c > MAX_CHAR) return;
c -= 32;
int w = showChar(c, font);
for (int i=0; i<w+1; i++) {
delay(shiftDelay);
scrollLeft();
refreshAll();
}
}
// =======================================================================
void printStringWithShift(const char* s, int shiftDelay){
while (*s) {
printCharWithShift(*s++, shiftDelay);
}
}
// =======================================================================
unsigned int convToInt(const char *txt)
{
unsigned int val = 0;
for(int i=0; i<strlen(txt); i++)
if(isdigit(txt[i])) val=val*10+(txt[i]&0xf);
return val;
}
// =======================================================================
const char* ytHost = "www.youtube.com";
int getYTSubs(const char *channelId, int *pSubs, int *pViews)
{
if(!pSubs || !pViews) return -2;
WiFiClientSecure client;
Serial.print("connecting to "); Serial.println(ytHost);
if (!client.connect(ytHost, 443)) {
Serial.println("connection failed");
return -1;
}
client.print(String("GET /channel/") + String(channelId) +"/about HTTP/1.1\r\n" + "Host: " + ytHost + "\r\nConnection: close\r\n\r\n");
int repeatCounter = 10;
while (!client.available() && repeatCounter--) {
Serial.println("y."); delay(500);
}
int idxS, idxE, statsFound = 0;
*pSubs = *pViews = 0;
while (client.connected() && client.available()) {
String line = client.readStringUntil('\n');
if(statsFound == 0) {
statsFound = (line.indexOf("about-stats")>0);
} else {
idxS = line.indexOf("<b>");
idxE = line.indexOf("</b>");
String val = line.substring(idxS + 3, idxE);
if(!*pSubs)
*pSubs = convToInt(val.c_str());
else {
*pViews = convToInt(val.c_str());
break;
}
}
}
client.stop();
return 0;
}
Comments