- Ở 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.