- Ở bài trước mình đã hướng dẫn các bạn sử dụng kit ESP8266 LCD 12864B5 để lập trình đồng hồ thời gian thực.
Trong bài này mình hướng dẫn thêm dự báo thời tiết. Như vậy bộ kit của chúng ta sẽ có những chức năng sau
- Hiển thị ngày giờ được cập nhật từ internet
- Lấy dữ liệu nhiệt độ, độ ẩm tại thành phố của bạn từ internet thông qua website Weatherbit.io
Dưới đây là hình ảnh sản phẩm
Code
/*
* Author: RedFox97
* Đấu nối
* |---------|---------|---------------|
* | LCD | ESP8266 | Chân đặc biệt |
* |---------|---------|---------------|
* | RST | D0 | GPIO16 |
* | SCLK | D6 | GPIO12 |
* | A0 | D5 | GPIO14 |
* | SID | D7 | GPIO13 |
* |---------|---------|---------------|
*/
#include <ESP8266WiFi.h>
#include <time.h>
#include <Ticker.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h> // ver 5.1.3
#include "ST7565_homephone_esp8266.h"
#include "Img.h"
const unsigned char logo[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
0x18, 0x1C, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0xFE, 0xFE, 0xFE, 0xFE,
0xFE, 0xFE, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1C, 0x18, 0x10, 0x00, 0x00, 0x00,
0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF0, 0xE0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFC,
0xFC, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x98, 0x90, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x01, 0x03,
0x07, 0x01, 0x00, 0x00, 0x7F, 0x7F, 0x7F, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x7F, 0x7F,
0x7F, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x7F, 0x73, 0x73, 0x73, 0x73, 0x73, 0x31, 0x10,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const static unsigned char tdo[]= {
0x00,0x07, 0x05, 0x07, 0x00,
};
const unsigned char temperature[] = {
0x00, 0x00, 0x00, 0x80, 0x7e, 0x01, 0x7e, 0x80, 0x2a, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04,
0x08, 0x08, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00
};
const unsigned char humidity[] = {
0x00, 0x80, 0x60, 0x18, 0x06, 0x01, 0x06, 0x18, 0x60, 0x80, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04,
0x08, 0x08, 0x08, 0x04, 0x02, 0x01, 0x00, 0x00
};
const unsigned char rain [] = {
0xC0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xB8, 0xDE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0xFA,
0xFE, 0xBC, 0xD8, 0xF0, 0xC0, 0xC0, 0xC0, 0x80, 0x03, 0x07, 0x0D, 0x0F, 0x0F, 0x8F, 0x0F, 0x1F,
0x1F, 0xBF, 0x37, 0x37, 0x37, 0xB7, 0x37, 0x3B, 0x1F, 0x8F, 0x0F, 0x1F, 0x1F, 0x1F, 0x0F, 0x07,
0x00, 0x00, 0x00, 0x00, 0x03, 0x31, 0x18, 0x00, 0x03, 0x01, 0x30, 0x18, 0x03, 0x01, 0x30, 0x18,
0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const unsigned char cloudy [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x90, 0x08, 0x08, 0x08,
0x18, 0x24, 0x02, 0x02, 0x02, 0x02, 0x24, 0x18, 0x10, 0x10, 0x10, 0x10, 0x20, 0xC0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
};
const unsigned char lighting [] = {
0x00, 0x00, 0x00, 0x80, 0x80, 0xC0, 0x60, 0x60, 0x60, 0x60, 0x70, 0x30, 0x30, 0x30, 0xE0, 0xC0,
0xC0, 0xC0, 0xC0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0x0D, 0x0C, 0x0C, 0x0C,
0x8C, 0xCC, 0xEC, 0x6C, 0x2C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0F, 0x07, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x23, 0x1B, 0x0F, 0x07, 0x03, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const unsigned char lighting_rain [] = {
0x00, 0x00, 0x00, 0x80, 0x80, 0xC0, 0x60, 0x60, 0x60, 0x60, 0x70, 0x30, 0x30, 0x30, 0xE0, 0xC0,
0xC0, 0xC0, 0xC0, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4F, 0x0D, 0x4C, 0x0C, 0x0C,
0x8C, 0xCC, 0xEC, 0x6C, 0x2C, 0x0C, 0x0C, 0x0C, 0x0C, 0x4C, 0x0C, 0x4C, 0x0F, 0x07, 0x00, 0x00,
0x00, 0x10, 0x02, 0x10, 0x02, 0x00, 0x00, 0x02, 0x03, 0x23, 0x1B, 0x0F, 0x07, 0x03, 0x01, 0x10,
0x02, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const unsigned char night [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x40, 0x20, 0x20, 0x20, 0xA0, 0xA0, 0x40,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x83, 0x00,
0x00, 0x7C, 0x82, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x04, 0x08, 0x09, 0x09, 0x0A, 0x0A, 0x04,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const unsigned char clear_sky [] = {
0x00, 0x00, 0x40, 0xE0, 0xC0, 0x00, 0x08, 0x9C, 0xD8, 0xC0, 0xE2, 0xEC, 0xEC, 0xE2, 0xC0, 0xD8,
0x9C, 0x08, 0x00, 0xC0, 0xE0, 0x40, 0x00, 0x00, 0x24, 0x18, 0x18, 0x00, 0x00, 0x3C, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3C, 0x00, 0x00, 0x18, 0x18, 0x24,
0x00, 0x00, 0x02, 0x07, 0x03, 0x00, 0x10, 0x39, 0x1B, 0x03, 0x47, 0x37, 0x37, 0x47, 0x03, 0x1B,
0x39, 0x10, 0x00, 0x03, 0x07, 0x02, 0x00, 0x00,
};
const unsigned char clear_cloudy [] = {
0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x20, 0x80, 0xC0, 0xC0, 0x5C, 0x40, 0x40, 0xC0, 0x80, 0x20,
0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0xC1, 0x41, 0x50, 0x57, 0xFD,
0xF8, 0x38, 0x18, 0x18, 0x18, 0x18, 0x3D, 0xF7, 0xD0, 0x81, 0x81, 0x81, 0x00, 0x00, 0x00, 0x00,
0x00, 0x07, 0x0F, 0x18, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x31, 0x33, 0x1E, 0x00, 0x00,
};
const unsigned char night_cloud [] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x20, 0x20, 0x10, 0x90, 0x90, 0x50,
0x50, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x40, 0x7E,
0xC1, 0x20, 0x10, 0x1E, 0x11, 0x10, 0x20, 0xC0, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, 0x04, 0x08, 0x08, 0x08, 0x08, 0x09, 0x08, 0x08, 0x08, 0x08, 0x09, 0x08,
0x08, 0x08, 0x08, 0x08, 0x09, 0x06, 0x00, 0x00,
};
const char* ssid = "MTE"; // SSID của WiFi
const char* password = "LamGiCoMatKhau@999"; // Mật khẩu của WiFi
// Tọa độ có thể tùy vào vị trí bạn ở
const String latitude = "21.0651854"; // Kinh độ
const String longitude = "105.7185722"; // Vĩ độ
const String key = "ca66835ddbaa496c9d11aee5f48fd28e"; // auth key
// Tọa độ tâm đồng hồ
const int clockCenterX = 32;
const int clockCenterY = 32;
String dayOfWeek, thang, ngay, gio, phut, giay, nam;
int hour, minute, sec;
int nhietDoHienTai, doAmHienTai, thoiTietHienTai;
ST7565 lcd(16, 12, 14, 13);
Ticker flip;
HTTPClient http;
void setup() {
Serial.begin(9600); // Khởi tạo Serial với baudrate 9600
WiFi.begin(ssid,password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
lcd.ON();
lcd.SET(35,0,0,0,4);
configTime(7 * 3600, 0, "pool.ntp.org", "time.nist.gov");
capNhatThoiGian();
flip.attach(1,ngat); // Khởi tạo ngắt 1s
duBaoHienTai(latitude, longitude, key); // Cập nhật thời tiết
}
void loop() {
if(nam.toInt() < 2000) { // Nếu năm chưa cập nhật đúng năm hiện tại sẽ tiếp tục chạy hàm cập nhật
capNhatThoiGian();
}
if(hour != 23 && minute==0 && sec==0 || hour == 23 && minute == 7 && sec == 0) { // Một tiếng cập nhật thời tiết 1 lần
Serial.println("Cập nhật");
duBaoHienTai(latitude, longitude, key);
}
lcd.clear();
lcd.Circle(clockCenterX, clockCenterY, 28, 1); // Vẽ vòng ngoài của đồng hồ
lcd.Circle(clockCenterX, clockCenterY, 29, 1);
for (int i=0; i<3; i++) { // Vẽ Tâm đồng hồ
lcd.Circle(clockCenterX, clockCenterY, i, 1);
}
for (int i=0; i<12; i++) { // Vẽ 12 vị trí giờ tương ứng từ 1 đến 12
drawMark(i);
}
hienThiThoiTiet(62,36,String(nhietDoHienTai),String(doAmHienTai));
iconThoiTiet(103,36,thoiTietHienTai);
// lcd.Bitmap(73,28,40,40,logo,BLACK); // Hiển thị Logo MTE
canGiua(94,8,dayOfWeek); // Hiển thị ngày trong tuần
hienThi(67,23,ngay+"/"+thang+"/"+nam); // Hiển thị ngày, tháng, năm
drawHour(hour,minute); // Vẽ kim giờ
drawMin(minute); // Vẽ kim phút
drawSec(sec); // Vẽ kim giây
lcd.display();
}
void ngat() {
// Đếm thời gian để không phải cập nhật thời gian liên tục
sec++;
if(sec == 60) {
minute++;
sec = 0;
}
if(minute == 60) {
hour++;
minute = 0;
}
if(hour == 24) { // Sau 1 ngày cập nhật thời gian 1 lần
hour = 0;
capNhatThoiGian();
}
// Serial.print(String(hour)+":"+String(minute)+":"+String(sec));
// Serial.print("\t");
// Serial.println(ngay+"/"+thang+"/"+nam);
}
void capNhatThoiGian() {
time_t now = time(nullptr);
String data = ctime(&now);
// Serial.println(data);
// Tách dữ liệu
String ngayTrongTuan = data.substring(0,3);
String month = data.substring(4,7);
ngay = data.substring(8,10);
gio = data.substring(11,13);
phut = data.substring(14,16);
giay = data.substring(17,19);
nam = data.substring(20,24);
//Biến đổi
ngayTrongTuan.toUpperCase();
ngay.trim();
if(ngayTrongTuan == "MON") dayOfWeek = "MONDAY";
else if(ngayTrongTuan == "TUE") dayOfWeek = "TUESDAY";
else if(ngayTrongTuan == "WED") dayOfWeek = "WEDNESDAY";
else if(ngayTrongTuan == "THU") dayOfWeek = "THURSDAY";
else if(ngayTrongTuan == "FRI") dayOfWeek = "FRIDAY";
else if(ngayTrongTuan == "SAT") dayOfWeek = "SATURDAY";
else if(ngayTrongTuan == "SUN") dayOfWeek = "SUNDAY";
if(ngay.toInt() < 10) {
ngay = "0" + ngay;
}
if(month=="Jan") thang="01";
else if(month=="Feb") thang="02";
else if(month=="Mar") thang="03";
else if(month=="Apr") thang="04";
else if(month=="May") thang="05";
else if(month=="Jun") thang="06";
else if(month=="Jul") thang="07";
else if(month=="Aug") thang="08";
else if(month=="Sep") thang="09";
else if(month=="Oct") thang="10";
else if(month=="Nov") thang="11";
else if(month=="Dec") thang="12";
hour = gio.toInt();
minute = phut.toInt();
sec = giay.toInt();
}
void hienThi(int x, int y,String chuoi) { // Hiển thị 1 chuỗi kí tự
lcd.Asc_String(x,y,(char*) chuoi.c_str(),BLACK);
}
void canGiua(int x, int y, String a) { // Căn giữa 1 chuỗi kí tự
lcd.Asc_String(x+1-(a.length()*6)/2,y+1,(char*) a.c_str(),BLACK);
}
void drawMark(int h) {
float x1, y1, x2, y2;
h=h*30;
h=h+270;
x1=27*cos(h*0.0175);
y1=27*sin(h*0.0175);
x2=24*cos(h*0.0175);
y2=24*sin(h*0.0175);
lcd.DrawLine(x1+32, y1+32, x2+32, y2+32, 1);
}
void drawSec(int s) {
float x1, y1, x2, y2;
s=s*6;
s=s+270;
x1=27*cos(s*0.0175);
y1=27*sin(s*0.0175);
x2=24*cos(s*0.0175);
y2=24*sin(s*0.0175);
lcd.DrawLine(clockCenterX, clockCenterY, x2+clockCenterX, y2+clockCenterY, 1);
}
void drawMin(int m) {
float x1, y1, x2, y2, x3, y3, x4, y4;
m=m*6;
m=m+270;
x1=25*cos(m*0.0175);
y1=25*sin(m*0.0175);
x2=3*cos(m*0.0175);
y2=3*sin(m*0.0175);
x3=10*cos((m+8)*0.0175);
y3=10*sin((m+8)*0.0175);
x4=10*cos((m-8)*0.0175);
y4=10*sin((m-8)*0.0175);
lcd.DrawLine(x1+clockCenterX, y1+clockCenterY, x3+clockCenterX, y3+clockCenterY, 1);
lcd.DrawLine(x3+clockCenterX, y3+clockCenterY, x2+clockCenterX, y2+clockCenterY, 1);
lcd.DrawLine(x2+clockCenterX, y2+clockCenterY, x4+clockCenterX, y4+clockCenterY, 1);
lcd.DrawLine(x4+clockCenterX, y4+clockCenterY, x1+clockCenterX, y1+clockCenterY, 1);
}
void drawHour(int h, int m) {
float x1, y1, x2, y2, x3, y3, x4, y4;
h=(h*30)+(m/2);
h=h+270;
x1=20*cos(h*0.0175);
y1=20*sin(h*0.0175);
x2=3*cos(h*0.0175);
y2=3*sin(h*0.0175);
x3=8*cos((h+12)*0.0175);
y3=8*sin((h+12)*0.0175);
x4=8*cos((h-12)*0.0175);
y4=8*sin((h-12)*0.0175);
lcd.DrawLine(x1+clockCenterX, y1+clockCenterY, x3+clockCenterX, y3+clockCenterY, 1);
lcd.DrawLine(x3+clockCenterX, y3+clockCenterY, x2+clockCenterX, y2+clockCenterY, 1);
lcd.DrawLine(x2+clockCenterX, y2+clockCenterY, x4+clockCenterX, y4+clockCenterY, 1);
lcd.DrawLine(x4+clockCenterX, y4+clockCenterY, x1+clockCenterX, y1+clockCenterY, 1);
}
void duBaoHienTai(String latitude, String longitude,String key) {
http.begin("http://api.weatherbit.io/v2.0/current?&lat="+latitude+"&lon="+longitude+"&key="+key);
if(http.GET() == HTTP_CODE_OK) {
String data = http.getString();
// Serial.println(data);
DynamicJsonBuffer jsonBuffer(1200);
JsonObject& root = jsonBuffer.parseObject((char*) data.c_str());
nhietDoHienTai = root["data"][0]["temp"].as<int>();
doAmHienTai = root["data"][0]["rh"].as<int>();
thoiTietHienTai = root["data"][0]["weather"]["code"].as<int>();
// Serial.println(String(nhietDoHienTai)+"\t"+String(doAmHienTai)+"\t"+String(thoiTietHienTai));
}
http.end();
}
void hienThiThoiTiet(int x, int y,String nhietdo, String doam) { // Hiển thị form nhiệt độ, độ ẩm
lcd.Bitmap(x,y,12,12,temperature,BLACK);
lcd.Bitmap(x,y+14,12,12,humidity,BLACK);
if(nhietdo.toInt() < 10) { // Nhiệt độ
hienThi(x+14,y+2,nhietdo);
lcd.Bitmap(x+20,y+2,5,7,tdo,BLACK);
lcd.Asc_Char(x+26,y+2,'C',BLACK);
}
else {
hienThi(x+14,y+2,nhietdo);
lcd.Bitmap(x+26,y+2,5,7,tdo,BLACK);
lcd.Asc_Char(x+32,y+2,'C',BLACK);
}
hienThi(x+14,y+16,doam+"%"); // Độ ẩm
}
void iconThoiTiet(int x, int y,int code) { //https://www.weatherbit.io/api/codes Hiển thị Icon thời tiết
switch(code) {
// Mua kem sam set
case 200:
case 201:
case 202:
lcd.Bitmap(x,y,24,24,lighting_rain,BLACK);
break;
// Sam set
case 230:
case 231:
case 232:
case 233:
lcd.Bitmap(x,y,24,24,lighting,BLACK);
break;
// May + mua
case 300:
case 301:
case 302:
case 500:
case 501:
case 502:
case 511:
case 520:
case 521:
case 522:
case 623:
lcd.Bitmap(x,y,24,24,rain,BLACK);
break;
// Clear Sky
case 800:
if(hour <18 && hour>5)
lcd.Bitmap(x,y,24,24,clear_sky,BLACK);
else
lcd.Bitmap(x,y,24,24,night,BLACK);
break;
case 700:
case 711:
case 721:
case 731:
case 741:
case 751:
case 801:
case 802:
case 803:
if(hour <18 && hour>5)
lcd.Bitmap(x,y,24,24,clear_cloudy,BLACK);
else
lcd.Bitmap(x,y,24,24,night_cloud,BLACK);
break;
// Trời nhiều mây
case 611:
case 612:
case 804:
lcd.Bitmap(x,y,24,24,cloudy,BLACK);
break;
}
}
Sơ đồ đấu nối mạch USB-TTL với kit

| KIT ESP8266 | USB to TTL |
| RX | RX |
| VCC | VCC |
| TX | TX |
| GND | GND |
Để nạp code cho mạch, sau khi nhấn Upload trên IDE, bạn nhấn giữ nút GPIO0 -> nhấn RESET, nhả RESET, cuối cùng nhả nút GPIO0 ra.