查看: 328|回复: 0

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

[复制链接]
  • TA的每日心情
    开心
    2019-11-15 10:19
  • 签到天数: 36 天

    [LV.5]常住居民I

    发表于 2019-11-8 18:10 | 显示全部楼层 |阅读模式
    本帖最后由 零知实验室-roc 于 2019-11-8 18:17 编辑


    引述:
    我们一起学了这么久的零知ESP8266的教程,是不是该检验一下自己了呢?在前面的分享中,有小伙伴留言:太简单!那我就顺水推舟,拔高一下,实践搞个小项目——天气时钟。

    到现在为止,作为一个基本的开发者,咱们已经对零知ESP8266有了最基本的了解了,可能你早就按耐不住要做项目了吧!那咱今儿个就做个小项目瞧瞧。继续给我们电子世界的轮廓加一个点。

    一、硬件准备
    电脑,windows系统
    零知ESP8266开发板
    OLED SSD1306模块
    micro-usb线

    二、连线
    1.jpg

    2.jpg

    三、软件库
    ①打开零知开发工具最新版,选中开发板,如图所示:
    3.png

    ②点击库,然后安装以下两个库,如图:
    4.png

    安装完成后,就可以啦。

    四、打开零知开发软件,新建工程,命名weather—station。然后烧写如下代码(已经做好了中文注释,复制粘贴即可):

    kittenblock中小学创客名师推荐的图形化编程软件

    /*
    2019年6月13日13:47:26
    by 零知实验室
    */
    
    #include <ESPWiFi.h>
    #include <ESPHTTPClient.h>
    #include <JsonListener.h>
    
    // time
    #include <time.h>                       
    #include <sys/time.h>                   
    #include <coredecls.h>                 
    
    #include "SSD1306Wire.h"
    #include "OLEDDisplayUi.h"
    #include "Wire.h"
    #include "OpenWeatherMapCurrent.h"
    #include "OpenWeatherMapForecast.h"
    #include "WeatherStationFonts.h"
    #include "WeatherStationImages.h"
    
    
    /***************************
     * 开始设置
     **************************/
    
    // 这里填写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密钥
    // https://docs.thingpulse.com/how-tos/openweathermap-key/
    //也可以在零知实验室查看原帖获取,或者留言给我呦,免费分享给你
    String OPEN_WEATHER_MAP_APP_ID = "3213ac05f30cc2f7d8d8da6d2b03f2e8";  //得到密匙  下面会有教程
    /*
    转到https://openweathermap.org/find?q=并搜索位置。 
    通过结果设置并选择最接近要显示的实际位置的条目数据 
    它将是一个类似于https://openweathermap.org/city/2657896.的链接最后的数字是你分配给下面常量的数字。
     */
    String OPEN_WEATHER_MAP_LOCATION_ID = "1795565"; //city:深圳   数字指的是openweathermap的分配的ID   同样也有教程
    
    //从此列表中选择语言代码:
    //阿拉伯文-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.flipScreenVertically();
      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, "pool.ntp.org");
    
      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(¤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 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;
    }
    

    代码中获取信息的步骤在
    这里
    验证完毕,然后再点击“上传”即可。

    完整工程:weather_station1.7z(若有任何问题,欢迎评论留言)

    五、结果
    5.jpg

    获取返回结果代码:200表示成功了

    如果错误码,可以把地址粘贴到浏览器里看看是什么原因。
    6.jpg

    看看视频效果:拂尘~走


    您需要登录后才可以回帖 登录 | 立即注册  

    本版积分规则

    热门推荐

    【Arduino】108种传感器模块系列实验(55)---DHT11温湿度传感器
    【Arduino】108种传感器模
    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是
    【Arduino】168种传感器系列实验(149)--AS608光学指纹识别模块
    【Arduino】168种传感器系
    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是
    黑乌鸦的Arduino兵器库-----DFPlayer MiNi mp3音乐模块
    黑乌鸦的Arduino兵器库---
    嗯 这是一个系列后期会罗列几乎所有的常见基础模块的demo所以 大家的回复将是我最大
    【原创】 drawbot平面关节scara机械臂写字机 画画机器人直播...
    【原创】 drawbot平面关节
    这个项目上个月就在做了,结构和代码反反复复改了多次,加上自己又太忙,一直没来得及
    【干货分享】mega2560原理图PCB图纸altium designer18
    【干货分享】mega2560原理
    分享一下mega2560的板子 AD版本 **** 本内容被作者隐藏 **** ergo
    Copyright   ©2015-2016  Arduino中文社区  Powered by©Discuz!   
    快速回复 返回顶部 返回列表