esp8266制作簡(jiǎn)易氣象站

ESP8266 CH340
ESP8266-NodeMCU搭建Arduino IDE開發(fā)環(huán)境
0.96 寸OLED 4針
從淘寶店鋪得到的資料:
0.91寸OLED 4針 鏈接:https://pan.baidu.com/s/1oGT7Pg00rmYFhS0QzzEJcw?pwd=8889 ?提取碼:8889
0.96寸OLED 鏈接:https://pan.baidu.com/s/1EHmikFHeFMRDAI_hr85tHQ?pwd=8xc1 ?提取碼:8xc1
1.3寸OLED 鏈接:https://pan.baidu.com/s/1SA0RHKdYF8p2XCBKIt-JYA?pwd=i2l9 ?提取碼:i2l9
1.54寸OLED 4針 鏈接:https://pan.baidu.com/s/1Da8EKFy0qM-NjZtNRNY41w?pwd=8889 ?提取碼:8889
1.54寸OLED 7針 鏈接:https://pan.baidu.com/s/1INwNfoje1pqrMyh-tWMZYw?pwd=8889 ?提取碼:8889
2.42寸OLED顯示液晶屏模塊 7針 鏈接:https://pan.baidu.com/s/1U0bzHRbcs30MQJjdikV-xQ?pwd=8889 ?提取碼:8889 ? ?
DHT11溫濕度傳感器
https://pan.baidu.com/s/1aROzKaKxbXm2DzMy2OtICA 提取碼: q6qb
ESP8266驅(qū)動(dòng)0.96寸OLED屏幕
NTP展示網(wǎng)絡(luò)時(shí)間
網(wǎng)絡(luò)時(shí)間協(xié)議NTP(Network Time Protocol)是TCP/IP協(xié)議族里面的一個(gè)應(yīng)用層協(xié)議,用來(lái)使客戶端和服務(wù)器之間進(jìn)行時(shí)鐘同步,提供高精準(zhǔn)度的時(shí)間校正。NTP服務(wù)器從權(quán)威時(shí)鐘源(例如原子鐘、GPS)接收精確的協(xié)調(diào)世界時(shí)UTC,客戶端再?gòu)姆?wù)器請(qǐng)求和接收時(shí)間。NTP基于UDP報(bào)文進(jìn)行傳輸。
1.接線方式


2.參考資料
https://randomnerdtutorials.com/esp8266-nodemcu-date-time-ntp-client-server-arduino/
https://blog.csdn.net/qq_45205470/article/details/126712272
3.修改后的代碼
#include <stdio.h>
#include <stdlib.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <NTPClient.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
// Setup Wifi
const char *ssid ? ? = "2023"; ? ? ?//wifiName
const char *password = "Internet"; ?//wifiPW
// Set date to day
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
WiFiUDP ntpUDP; // define ntp
NTPClient timeClient(ntpUDP, "ntp.aliyun.com");
Adafruit_SSD1306 display(128, 64, &Wire, -1);
void setup() {
?Serial.begin(115200); //打開串口通訊,設(shè)置傳輸速率為115200字節(jié)每秒
?WiFi.begin(ssid, password); // init wifi ssid and password
?while ( WiFi.status() != WL_CONNECTED ) {
? ?delay (500);
? ?Serial.print ( "." ); // while connecting will print this
?}
?
?timeClient.begin( );// wifi ready to use(這一步編譯可能會(huì)出錯(cuò),出錯(cuò)的話就下載最新的NTP庫(kù)版本)
?timeClient.setTimeOffset(28800);//設(shè)置時(shí)區(qū),同步當(dāng)?shù)貢r(shí)間
?// Set offset time in seconds to adjust for your timezone, for example:
?// GMT +1 = 3600
?// GMT +8 = 28800
?// GMT -1 = -3600
?// GMT 0 = 0
?if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
? ?Serial.println(F("SSD1306 allocation failed"));
? ?for (;;);
?}
?delay(2000);
?display.clearDisplay();
?display.setTextColor(WHITE);
}
void loop() {
?timeClient.update();
?// 3秒刷新顯示內(nèi)容
?delay(1000);
?// 清空顯示
?display.clearDisplay();
?// 使更改的顯示生效
?display.display();
?// 判斷應(yīng)該顯示的內(nèi)容
? display.setTextSize(1); ?// 設(shè)置字體大?。?~8)
? display.setCursor(0, 0); // 設(shè)置坐標(biāo)
? display.print("Time:"); // 顯示內(nèi)容
? display.setTextSize(2);
? display.setCursor(15, 15);
? display.print(timeClient.getHours());
? display.print(":");
? display.print(timeClient.getMinutes()); // separate by minutes
? display.print(":");
? display.println(timeClient.getSeconds()); // separate by second
? display.setTextSize(2);
? display.setCursor(25, 40);
? display.println(daysOfTheWeek[timeClient.getDay()]);
?Serial.print(daysOfTheWeek[timeClient.getDay()]);
?Serial.print(timeClient.getHours()); // separate by hour
?Serial.print(":");
?Serial.print(timeClient.getMinutes()); // separate by minutes
?Serial.print(":");
?Serial.println(timeClient.getSeconds()); // separate by
?display.display();
}
weather station(氣象站)
材料清單:
ESP8266 (ESP-12F)
0.96寸OLED屏幕
DHT11溫濕度檢測(cè)模塊
參考資料
效果展示



相關(guān)代碼
#include <Arduino.h>
#include <ESPWiFi.h>
#include <ESPHTTPClient.h>
#include <JsonListener.h>
#include <time.h> ? ? ? ? ? ? ? ? ? ? ? // time() ctime()
#include <sys/time.h> ? ? ? ? ? ? ? ? ? // struct timeval
#include <coredecls.h> ? ? ? ? ? ? ? ? ?// settimeofday_cb()
#include "SSD1306Wire.h"
#include "OLEDDisplayUi.h"
#include "Wire.h"
#include "OpenWeatherMapCurrent.h"
#include "OpenWeatherMapForecast.h"
#include "WeatherStationFonts.h"
#include "WeatherStationImages.h"
#include "DHT.h"
const char* WIFI_SSID = "2023"; ? ?//wifiName
const char* WIFI_PWD = "Internet"; //wifiPassword
#define TZ ? ? ? ? ? ? ?8 ? ? ? // (utc+) TZ in hours(這個(gè)時(shí)區(qū)可以自己谷歌查詢)
#define DST_MN ? ? ? ? ?0 ? ? ?// use 60mn for summer time in some countries
// Setup
const int UPDATE_INTERVAL_SECS = 10 * 60; // Update every 30 minutes
// Display Settings
const int I2C_DISPLAY_ADDRESS = 0x3C;
#if defined(ESP8266)
const int SDA_PIN = D2;
const int SDC_PIN = D1;
#else
const int SDA_PIN = 19; //D3;
const int SDC_PIN = 20; //D4;
#endif
String OPEN_WEATHER_MAP_APP_ID = "aa7f46b96a471b23c4e0c5dc069fcebb"; //keyApi獲取途徑(https://openweathermap.org/)
String OPEN_WEATHER_MAP_LOCATION_ID = "1814991"; //城市坐標(biāo)
// Pick a language code from this list:
// Arabic - ar, Bulgarian - bg, Catalan - ca, Czech - cz, German - de, Greek - el,
// English - en, Persian (Farsi) - fa, Finnish - fi, French - fr, Galician - gl,
// Croatian - hr, Hungarian - hu, Italian - it, Japanese - ja, Korean - kr,
// Latvian - la, Lithuanian - lt, Macedonian - mk, Dutch - nl, Polish - pl,
// Portuguese - pt, Romanian - ro, Russian - ru, Swedish - se, Slovak - sk,
// Slovenian - sl, Spanish - es, Turkish - tr, Ukrainian - ua, Vietnamese - vi,
// Chinese Simplified - zh_cn, Chinese Traditional - zh_tw.
String OPEN_WEATHER_MAP_LANGUAGE = "zh_cn";
const uint8_t MAX_FORECASTS = 4;
const boolean IS_METRIC = true;
// Adjust according to your language
const String WDAY_NAMES[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
const String MONTH_NAMES[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
/***************************
* End Settings
**************************/
SSD1306Wire ? ? display(I2C_DISPLAY_ADDRESS, SDA_PIN, SDC_PIN);
OLEDDisplayUi ? ui( &display );
OpenWeatherMapCurrentData currentWeather;
OpenWeatherMapCurrent currentWeatherClient;
OpenWeatherMapForecastData forecasts[MAX_FORECASTS];
OpenWeatherMapForecast forecastClient;
#define TZ_MN ? ? ? ? ? ((TZ)*60)
#define TZ_SEC ? ? ? ? ?((TZ)*3600)
#define DST_SEC ? ? ? ? ((DST_MN)*60)
time_t now;
// flag changed in the ticker function every 10 minutes
bool readyForWeatherUpdate = false;
String lastUpdate = "--";
long timeSinceLastWUpdate = 0;
//declaring prototypes
void drawProgress(OLEDDisplay *display, int percentage, String label);
void updateData(OLEDDisplay *display);
void drawDateTime(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void drawTemp(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void drawHum(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void drawCurrentWeather(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void drawForecast(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
void drawForecastDetails(OLEDDisplay *display, int x, int y, int dayIndex);
void drawHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state);
void setReadyForWeatherUpdate();
FrameCallback frames[] = { drawDateTime, drawCurrentWeather, drawForecast, drawTemp, drawHum };
int numberOfFrames = 5;
OverlayCallback overlays[] = { drawHeaderOverlay };
int numberOfOverlays = 1;
DHT dht = DHT(D3, DHT11, 6);
void setup() {
?Serial.begin(115200);
?Serial.println();
?Serial.println();
?// initialize dispaly
?display.init();
?display.clear();
?display.display();
?display.setFont(ArialMT_Plain_10);
?display.setTextAlignment(TEXT_ALIGN_CENTER);
?display.setContrast(255);
?dht.begin();
?WiFi.begin(WIFI_SSID, WIFI_PWD);
?int counter = 0;
?while (WiFi.status() != WL_CONNECTED) {
? ?delay(500);
? ?Serial.print(".");
? ?display.clear();
? ?display.drawString(64, 10, "Connecting to WiFi");
? ?display.drawXbm(46, 30, 8, 8, counter % 3 == 0 ? activeSymbole : inactiveSymbole);
? ?display.drawXbm(60, 30, 8, 8, counter % 3 == 1 ? activeSymbole : inactiveSymbole);
? ?display.drawXbm(74, 30, 8, 8, counter % 3 == 2 ? activeSymbole : inactiveSymbole);
? ?display.display();
? ?counter++;
?}
?// Get time from network time service
?configTime(TZ_SEC, DST_SEC, "pool.ntp.org");
?ui.setTargetFPS(30);
?ui.setActiveSymbol(activeSymbole);
?ui.setInactiveSymbol(inactiveSymbole);
?// You can change this to
?// TOP, LEFT, BOTTOM, RIGHT
?ui.setIndicatorPosition(BOTTOM);
?// Defines where the first frame is located in the bar.
?ui.setIndicatorDirection(LEFT_RIGHT);
?// You can change the transition that is used
?// SLIDE_LEFT, SLIDE_RIGHT, SLIDE_TOP, SLIDE_DOWN
?ui.setFrameAnimation(SLIDE_LEFT);
?ui.setFrames(frames, numberOfFrames);
?ui.setOverlays(overlays, numberOfOverlays);
?// Inital UI takes care of initalising the display too.
?ui.init();
?Serial.println("");
?updateData(&display);
?
?display.clear();
?display.drawXbm(26,0,logo1_width,logo1_height,logo1_bits);
?display.display();
?delay(5000);
?
?display.clear();
?display.setTextAlignment(TEXT_ALIGN_LEFT);
?display.setFont(ArialMT_Plain_10);
?display.drawString(0,0,"LOADING");
?display.display(); ?
?delay(4000);
?display.drawString(0,10,"TIME AND DATE");
?display.display(); ?
?delay(4000);
?display.drawString(0,20,"TEMPERATURE");
?display.display(); ?
?delay(4000);
?display.drawString(0,30,"HUMIDITY");
?display.display(); ?
?delay(4000);
?display.drawString(0,40,"FORECAST");
?display.display(); ?
?delay(4000);
?display.drawString(0,50,"INITIALIZING GUI");
?display.display(); ?
?delay(3000);
}
void loop() {
?
?
?if (millis() - timeSinceLastWUpdate > (1000L*UPDATE_INTERVAL_SECS)) {
? ?setReadyForWeatherUpdate();
? ?timeSinceLastWUpdate = millis();
?}
?if (readyForWeatherUpdate && ui.getUiState()->frameState == FIXED) {
? ?updateData(&display);
?}
?int remainingTimeBudget = ui.update();
?if (remainingTimeBudget > 0) {
? ?// You can do some work here
? ?// Don't do stuff if you are below your
? ?// time budget.
? ?delay(remainingTimeBudget);
?}
}
void drawProgress(OLEDDisplay *display, int percentage, String label) {
?display->clear();
?display->setTextAlignment(TEXT_ALIGN_CENTER);
?display->setFont(ArialMT_Plain_10);
?display->drawString(64, 10, label);
?display->drawProgressBar(2, 28, 124, 10, percentage);
?display->display();
}
void updateData(OLEDDisplay *display) {
?drawProgress(display, 10, "Updating time...");
?drawProgress(display, 30, "Updating weather...");
?currentWeatherClient.setMetric(IS_METRIC);
?currentWeatherClient.setLanguage(OPEN_WEATHER_MAP_LANGUAGE);
?currentWeatherClient.updateCurrentById(¤tWeather, OPEN_WEATHER_MAP_APP_ID, OPEN_WEATHER_MAP_LOCATION_ID);
?drawProgress(display, 50, "Updating forecasts...");
?forecastClient.setMetric(IS_METRIC);
?forecastClient.setLanguage(OPEN_WEATHER_MAP_LANGUAGE);
?uint8_t allowedHours[] = {12};
?forecastClient.setAllowedHours(allowedHours, sizeof(allowedHours));
?forecastClient.updateForecastsById(forecasts, OPEN_WEATHER_MAP_APP_ID, OPEN_WEATHER_MAP_LOCATION_ID, MAX_FORECASTS);
?readyForWeatherUpdate = false;
?drawProgress(display, 100, "Done...");
?delay(1000);
}
void drawDateTime(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
?now = time(nullptr);
?struct tm* timeInfo;
?timeInfo = localtime(&now);
?char buff[16];
?display->setTextAlignment(TEXT_ALIGN_CENTER);
?display->setFont(ArialMT_Plain_10);
?String date = WDAY_NAMES[timeInfo->tm_wday];
?sprintf_P(buff, PSTR("%s, %02d/%02d/%04d"), WDAY_NAMES[timeInfo->tm_wday].c_str(), timeInfo->tm_mday, timeInfo->tm_mon+1, timeInfo->tm_year + 1900);
?display->drawString(64 + x, 5 + y, String(buff));
?display->setFont(ArialMT_Plain_24);
?sprintf_P(buff, PSTR("%02d:%02d:%02d"), timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec);
?display->drawString(64 + x, 15 + y, String(buff));
?display->setTextAlignment(TEXT_ALIGN_LEFT);
}
void drawTemp(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
?float temperature = dht.readTemperature();
?
?display->setTextAlignment(TEXT_ALIGN_CENTER);
?display->setFont(ArialMT_Plain_10);
?
?display->drawString(64 + x, 5 + y, "Room Temperature");
?display->setFont(ArialMT_Plain_24);
?display->drawString(64 + x, 15 + y, String(temperature,1)+("°C"));
?display->setTextAlignment(TEXT_ALIGN_LEFT);
}
void drawHum(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
?int humidity = dht.readHumidity();
?
?display->setTextAlignment(TEXT_ALIGN_CENTER);
?display->setFont(ArialMT_Plain_10);
?
?display->drawString(64 + x, 5 + y, "Humidity");
?display->setFont(ArialMT_Plain_24);
?display->drawString(64 + x, 15 + y, String(humidity)+(" %"));
?display->setTextAlignment(TEXT_ALIGN_LEFT);
}
void drawCurrentWeather(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
?display->setFont(ArialMT_Plain_10);
?display->setTextAlignment(TEXT_ALIGN_CENTER);
?display->drawString(64 + x, 38 + y, currentWeather.description);
?display->setFont(ArialMT_Plain_24);
?display->setTextAlignment(TEXT_ALIGN_LEFT);
?String temp = String(currentWeather.temp, 1) + (IS_METRIC ? "°C" : "°F");
?display->drawString(51 + x, 5 + y, temp);
?display->setFont(Meteocons_Plain_36);
?display->setTextAlignment(TEXT_ALIGN_CENTER);
?display->drawString(23 ?+ x, 0 + y, currentWeather.iconMeteoCon);
}
void drawForecast(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
?drawForecastDetails(display, x, y, 0);
?drawForecastDetails(display, x + 44, y, 1);
?drawForecastDetails(display, x + 88, y, 2);
}
void drawForecastDetails(OLEDDisplay *display, int x, int y, int dayIndex) {
?time_t observationTimestamp = forecasts[dayIndex].observationTime;
?struct tm* timeInfo;
?timeInfo = localtime(&observationTimestamp);
?display->setTextAlignment(TEXT_ALIGN_CENTER);
?display->setFont(ArialMT_Plain_10);
?display->drawString(x + 20, y, WDAY_NAMES[timeInfo->tm_wday]);
?display->setFont(Meteocons_Plain_21);
?display->drawString(x + 20, y + 12, forecasts[dayIndex].iconMeteoCon);
?String temp = String(forecasts[dayIndex].temp, 0) + (IS_METRIC ? "°C" : "°F");
?display->setFont(ArialMT_Plain_10);
?display->drawString(x + 20, y + 34, temp);
?display->setTextAlignment(TEXT_ALIGN_LEFT);
}
void drawHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {
?now = time(nullptr);
?struct tm* timeInfo;
?timeInfo = localtime(&now);
?char buff[14];
?sprintf_P(buff, PSTR("%02d:%02d"), timeInfo->tm_hour, timeInfo->tm_min);
?display->setColor(WHITE);
?display->setFont(ArialMT_Plain_10);
?display->setTextAlignment(TEXT_ALIGN_LEFT);
?display->drawString(0, 54, String(buff));
?display->setTextAlignment(TEXT_ALIGN_RIGHT);
?String temp = String(currentWeather.temp, 1) + (IS_METRIC ? "°C" : "°F");
?display->drawString(128, 54, temp);
?display->drawHorizontalLine(0, 52, 128);
}
void setReadyForWeatherUpdate() {
?Serial.println("Setting readyForUpdate to true");
?readyForWeatherUpdate = true;
}
后續(xù)計(jì)劃
學(xué)習(xí)代碼中的知識(shí)點(diǎn),可以自己獨(dú)立進(jìn)行開發(fā)。
用solidworks設(shè)計(jì)一個(gè)外殼,3D打印出來(lái)。
開發(fā)個(gè)網(wǎng)頁(yè)端,方便修改。