【零知ESP8266项目篇】1 OLED天气时钟

时间:2025-03-10 10:20:38
/* 2019年6月13日13:47:26 by 零知实验室 */ #include <> #include <> #include <> // time #include <> #include <sys/> #include <> #include "" #include "" #include "" #include "" #include "" #include "" #include "" /*************************** * 开始设置 **************************/ // 这里填写WiFi凭证信息 const char* WIFI_SSID = "WiFi名"; const char* WIFI_PWD = "WiFi密码"; #define TZ 8 // 通用协调时 东八区 北京时间为准 #define DST_MN 60 // 在一些国家依然用夏令时 // Setup const int UPDATE_INTERVAL_SECS = 20 * 60; // 更新20分钟 // 展示设置 const int I2C_DISPLAY_ADDRESS = 0x3c; const int SDA_PIN = D3; const int SDC_PIN = D4; // OpenWeatherMap设置 // 在此处注册以获取API密钥 ///how-tos/openweathermap-key/ //也可以在零知实验室官网查看“无线”项目篇中提供账户,获得API密匙 String OPEN_WEATHER_MAP_APP_ID = "3213ac05f30cc2f7d8d8da6d2b03f2e8"; //得到密匙 下面会有教程 /* 转到/find?q=并搜索位置。 通过结果设置并选择最接近要显示的实际位置的条目数据 它将是一个类似于/city/2657896.的链接最后的数字是你分配给下面常量的数字。 */ String OPEN_WEATHER_MAP_LOCATION_ID = "1795565"; //city:深圳 //从此列表中选择语言代码: //阿拉伯文-ar,保加利亚语-bg,加泰罗尼亚语-ca,捷克语-cz,德语-de,希腊语-el, //英语-en,波斯语(波斯语)-fa,芬兰语-fi,法语-fr,加利西亚语-gl, //克罗地亚语-hr,匈牙利语-hu,意大利语-it,日语-ja,韩语-kr, //拉脱维亚-la,立陶宛语-lt,马其顿语-mk,荷兰语-nl,波兰语-pl, //葡萄牙语-pt,罗马尼亚语-ro,俄语-ru,瑞典语-se,斯洛伐克语-sk, //斯洛文尼亚文-sl,西班牙文-es,土耳其文-tr,乌克兰文-ua,越南文-vi, //简体中文-zh_cn,繁体中文-zh_tw。 String OPEN_WEATHER_MAP_LANGUAGE = "zh_cn"; //这里选择中文简体。 const uint8_t MAX_FORECASTS = 4; const boolean IS_METRIC = true; // 根据你的需要调整语言 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"}; //12个月 /*************************** * 结束设置 **************************/ // 初始化OLED地址 // I2C接口:SDA引脚14 SCL引脚12 SSD1306Wire display(I2C_DISPLAY_ADDRESS, SDA_PIN, SDC_PIN); //为OLED创建一个实例display OLEDDisplayUi ui( &display ); //创建一个ui实例 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; // 标记每10分钟更改一次。 bool readyForWeatherUpdate = false; String lastUpdate = "--"; long timeSinceLastWUpdate = 0; //申明原型 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 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 }; int numberOfFrames = 3; OverlayCallback overlays[] = { drawHeaderOverlay }; int numberOfOverlays = 1; void setup() { Serial.begin(115200); Serial.println(); Serial.println(); // 初始化显示 display.init(); display.clear(); display.display(); //(); display.setFont(ArialMT_Plain_10); display.setTextAlignment(TEXT_ALIGN_CENTER); display.setContrast(255); 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++; } // 从网络时间服务得到时间 configTime(TZ_SEC, DST_SEC, ""); ui.setTargetFPS(30); ui.setActiveSymbol(activeSymbole); ui.setInactiveSymbol(inactiveSymbole); // 你可以改变它, // 向上(TOP), 向左(LEFT), 向下(BOTTOM), 向右(RIGHT) ui.setIndicatorPosition(BOTTOM); // 定义第一个帧位于中间的位置 ui.setIndicatorDirection(LEFT_RIGHT); // 你可以更改幻灯片通过 // 向左滑动(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); } 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) { //你可以在这里添加一些代码,当然要在下面的remainingTimeBudget(停留时间预算内) //否则会出现闪频状态 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(&currentWeather, 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 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(60 + x, 5 + y, temp); display->setFont(Meteocons_Plain_36); display->setTextAlignment(TEXT_ALIGN_CENTER); display->drawString(32 + 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; }