数码之家

 找回密码
 立即注册
搜索
查看: 4707|回复: 5

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

[复制链接]
发表于 2019-11-8 18:32:09 | 显示全部楼层 |阅读模式
本帖最后由 roc2 于 2019-11-8 18:37 编辑

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

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

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

二、连线




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


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


安装完成后,就可以啦。

四、打开零知开发软件,新建工程,命名weather—station。然后烧写如下代码(已经做好了中文注释,复制粘贴即可):
  1. /*
  2. 2019年6月13日13:47:26
  3. by 零知实验室
  4. */

  5. #include <ESPWiFi.h>
  6. #include <ESPHTTPClient.h>
  7. #include <JsonListener.h>

  8. // time
  9. #include <time.h>                       
  10. #include <sys/time.h>                  
  11. #include <coredecls.h>                 

  12. #include "SSD1306Wire.h"
  13. #include "OLEDDisplayUi.h"
  14. #include "Wire.h"
  15. #include "OpenWeatherMapCurrent.h"
  16. #include "OpenWeatherMapForecast.h"
  17. #include "WeatherStationFonts.h"
  18. #include "WeatherStationImages.h"


  19. /***************************
  20. * 开始设置
  21. **************************/

  22. // 这里填写WiFi凭证信息
  23. const char* WIFI_SSID = "WiFi名";
  24. const char* WIFI_PWD = "WiFi密码";

  25. #define TZ              8       // 通用协调时  东八区  北京时间为准
  26. #define DST_MN          60      // 在一些国家依然用夏令时

  27. // Setup
  28. const int UPDATE_INTERVAL_SECS = 20 * 60; // 更新20分钟

  29. // 展示设置
  30. const int I2C_DISPLAY_ADDRESS = 0x3c;

  31. const int SDA_PIN = D3;
  32. const int SDC_PIN = D4;

  33. // OpenWeatherMap设置
  34. // 在此处注册以获取API密钥
  35. // https://docs.thingpulse.com/how-tos/openweathermap-key/
  36. //也可以在零知实验室查看原帖获取,或者留言给我呦,免费分享给你
  37. String OPEN_WEATHER_MAP_APP_ID = "3213ac05f30cc2f7d8d8da6d2b03f2e8";  //得到密匙  下面会有教程
  38. /*
  39. 转到https://openweathermap.org/find?q=并搜索位置。
  40. 通过结果设置并选择最接近要显示的实际位置的条目数据
  41. 它将是一个类似于https://openweathermap.org/city/2657896.的链接最后的数字是你分配给下面常量的数字。
  42. */
  43. String OPEN_WEATHER_MAP_LOCATION_ID = "1795565"; //city:深圳   数字指的是openweathermap的分配的ID   同样也有教程

  44. //从此列表中选择语言代码:
  45. //阿拉伯文-ar,保加利亚语-bg,加泰罗尼亚语-ca,捷克语-cz,德语-de,希腊语-el,
  46. //英语-en,波斯语(波斯语)-fa,芬兰语-fi,法语-fr,加利西亚语-gl,
  47. //克罗地亚语-hr,匈牙利语-hu,意大利语-it,日语-ja,韩语-kr,
  48. //拉脱维亚-la,立陶宛语-lt,马其顿语-mk,荷兰语-nl,波兰语-pl,
  49. //葡萄牙语-pt,罗马尼亚语-ro,俄语-ru,瑞典语-se,斯洛伐克语-sk,
  50. //斯洛文尼亚文-sl,西班牙文-es,土耳其文-tr,乌克兰文-ua,越南文-vi,
  51. //简体中文-zh_cn,繁体中文-zh_tw。
  52. String OPEN_WEATHER_MAP_LANGUAGE = "zh_cn";    //这里选择中文简体。
  53. const uint8_t MAX_FORECASTS = 4;

  54. const boolean IS_METRIC = true;

  55. // 根据你的需要调整语言
  56. const String WDAY_NAMES[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"}; //每周七天
  57. const String MONTH_NAMES[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"}; //12个月

  58. /***************************
  59. * 结束设置
  60. **************************/
  61. // 初始化OLED地址
  62. //  I2C接口:SDA引脚14 SCL引脚12
  63. SSD1306Wire     display(I2C_DISPLAY_ADDRESS, SDA_PIN, SDC_PIN);  //为OLED创建一个实例display
  64. OLEDDisplayUi   ui( &display );   //创建一个ui实例

  65. OpenWeatherMapCurrentData currentWeather;     //创建一个当前天气数据
  66. OpenWeatherMapCurrent currentWeatherClient;   //创建一个当前天气客户端

  67. OpenWeatherMapForecastData forecasts[MAX_FORECASTS];
  68. OpenWeatherMapForecast forecastClient;

  69. #define TZ_MN           ((TZ)*60)
  70. #define TZ_SEC          ((TZ)*3600)
  71. #define DST_SEC         ((DST_MN)*60)
  72. time_t now;

  73. // 标记每10分钟更改一次。
  74. bool readyForWeatherUpdate = false;

  75. String lastUpdate = "--";

  76. long timeSinceLastWUpdate = 0;

  77. //申明原型
  78. void drawProgress(OLEDDisplay *display, int percentage, String label);
  79. void updateData(OLEDDisplay *display);
  80. void drawDateTime(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
  81. void drawCurrentWeather(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
  82. void drawForecast(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y);
  83. void drawForecastDetails(OLEDDisplay *display, int x, int y, int dayIndex);
  84. void drawHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state);
  85. void setReadyForWeatherUpdate();

  86. //添加框架
  87. //此数组保留指向所有帧的函数指针
  88. //框架是从右向左滑动的单个视图
  89. FrameCallback frames[] = { drawDateTime, drawCurrentWeather, drawForecast };
  90. int numberOfFrames = 3;

  91. OverlayCallback overlays[] = { drawHeaderOverlay };
  92. int numberOfOverlays = 1;

  93. void setup() {
  94.   Serial.begin(115200);
  95.   Serial.println();
  96.   Serial.println();

  97.   // 初始化显示
  98.   display.init();
  99.   display.clear();
  100.   display.display();

  101.   //display.flipScreenVertically();
  102.   display.setFont(ArialMT_Plain_10);
  103.   display.setTextAlignment(TEXT_ALIGN_CENTER);
  104.   display.setContrast(255);

  105.   WiFi.begin(WIFI_SSID, WIFI_PWD);

  106.   int counter = 0;
  107.   while (WiFi.status() != WL_CONNECTED) {
  108.     delay(500);
  109.     Serial.print(".");
  110.     display.clear();
  111.     display.drawString(64, 10, "Connecting to WiFi");
  112.     display.drawXbm(46, 30, 8, 8, counter % 3 == 0 ? activeSymbole : inactiveSymbole);
  113.     display.drawXbm(60, 30, 8, 8, counter % 3 == 1 ? activeSymbole : inactiveSymbole);
  114.     display.drawXbm(74, 30, 8, 8, counter % 3 == 2 ? activeSymbole : inactiveSymbole);
  115.     display.display();

  116.     counter++;
  117.   }
  118.   // 从网络时间服务得到时间
  119.   configTime(TZ_SEC, DST_SEC, "pool.ntp.org");

  120.   ui.setTargetFPS(30);

  121.   ui.setActiveSymbol(activeSymbole);
  122.   ui.setInactiveSymbol(inactiveSymbole);

  123.   // 你可以改变它,
  124.   // 向上(TOP), 向左(LEFT), 向下(BOTTOM), 向右(RIGHT)
  125.   ui.setIndicatorPosition(BOTTOM);

  126.   // 定义第一个帧位于中间的位置
  127.   ui.setIndicatorDirection(LEFT_RIGHT);

  128.   // 你可以更改幻灯片通过
  129.   // 向左滑动(SLIDE_LEFT),向右滑动( SLIDE_RIGHT)向上滑动( SLIDE_TOP), 向下滑动(SLIDE_DOWN)
  130.   ui.setFrameAnimation(SLIDE_LEFT);   //这里填写设置向左滑动,根据上面提供的注释、个人喜好选择

  131.   ui.setFrames(frames, numberOfFrames);

  132.   ui.setOverlays(overlays, numberOfOverlays);

  133.   // Inital UI takes care of initalising the display too.
  134.   ui.init();

  135.   Serial.println("");

  136.   updateData(&display);

  137. }

  138. void loop() {

  139.   if (millis() - timeSinceLastWUpdate > (1000L*UPDATE_INTERVAL_SECS)) {
  140.     setReadyForWeatherUpdate();
  141.     timeSinceLastWUpdate = millis();
  142.   }

  143.   if (readyForWeatherUpdate && ui.getUiState()->frameState == FIXED) {
  144.     updateData(&display);
  145.   }

  146.   int remainingTimeBudget = ui.update();

  147.   if (remainingTimeBudget > 0) {
  148.     //你可以在这里添加一些代码,当然要在下面的remainingTimeBudget(停留时间预算内)
  149.         //否则会出现闪频状态
  150.     delay(remainingTimeBudget);
  151.   }


  152. }

  153. void drawProgress(OLEDDisplay *display, int percentage, String label)   //绘制进度
  154. {
  155.   display->clear();
  156.   display->setTextAlignment(TEXT_ALIGN_CENTER);
  157.   display->setFont(ArialMT_Plain_10);
  158.   display->drawString(64, 10, label);
  159.   display->drawProgressBar(2, 28, 124, 10, percentage);
  160.   display->display();
  161. }

  162. void updateData(OLEDDisplay *display)   //更新数据
  163. {
  164.   drawProgress(display, 10, "Updating time...");
  165.   drawProgress(display, 30, "Updating weather...");
  166.   currentWeatherClient.setMetric(IS_METRIC);
  167.   currentWeatherClient.setLanguage(OPEN_WEATHER_MAP_LANGUAGE);
  168.   currentWeatherClient.updateCurrentById(&currentWeather, OPEN_WEATHER_MAP_APP_ID, OPEN_WEATHER_MAP_LOCATION_ID);
  169.   drawProgress(display, 50, "Updating forecasts...");
  170.   forecastClient.setMetric(IS_METRIC);
  171.   forecastClient.setLanguage(OPEN_WEATHER_MAP_LANGUAGE);
  172.   uint8_t allowedHours[] = {12};
  173.   forecastClient.setAllowedHours(allowedHours, sizeof(allowedHours));
  174.   forecastClient.updateForecastsById(forecasts, OPEN_WEATHER_MAP_APP_ID, OPEN_WEATHER_MAP_LOCATION_ID, MAX_FORECASTS);

  175.   readyForWeatherUpdate = false;
  176.   drawProgress(display, 100, "Done...");
  177.   delay(1000);
  178. }



  179. void drawDateTime(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) //绘制数据时间

  180. {
  181.   now = time(nullptr);
  182.   struct tm* timeInfo;
  183.   timeInfo = localtime(&now);
  184.   char buff[16];


  185.   display->setTextAlignment(TEXT_ALIGN_CENTER);
  186.   display->setFont(ArialMT_Plain_10);
  187.   String date = WDAY_NAMES[timeInfo->tm_wday];

  188.   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);
  189.   display->drawString(64 + x, 5 + y, String(buff));
  190.   display->setFont(ArialMT_Plain_24);

  191.   sprintf_P(buff, PSTR("%02d:%02d:%02d"), timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec);
  192.   display->drawString(64 + x, 15 + y, String(buff));
  193.   display->setTextAlignment(TEXT_ALIGN_LEFT);
  194. }

  195. void drawCurrentWeather(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y)   //绘制当前天气信息
  196. {
  197.   display->setFont(ArialMT_Plain_10);
  198.   display->setTextAlignment(TEXT_ALIGN_CENTER);
  199.   display->drawString(64 + x, 38 + y, currentWeather.description);

  200.   display->setFont(ArialMT_Plain_24);
  201.   display->setTextAlignment(TEXT_ALIGN_LEFT);
  202.   String temp = String(currentWeather.temp, 1) + (IS_METRIC ? "°C" : "°F");
  203.   display->drawString(60 + x, 5 + y, temp);

  204.   display->setFont(Meteocons_Plain_36);
  205.   display->setTextAlignment(TEXT_ALIGN_CENTER);
  206.   display->drawString(32 + x, 0 + y, currentWeather.iconMeteoCon);
  207. }


  208. void drawForecast(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y)  //绘制预测信息
  209. {
  210.   drawForecastDetails(display, x, y, 0);
  211.   drawForecastDetails(display, x + 44, y, 1);
  212.   drawForecastDetails(display, x + 88, y, 2);
  213. }

  214. void drawForecastDetails(OLEDDisplay *display, int x, int y, int dayIndex)   //绘制预测明细
  215. {
  216.   time_t observationTimestamp = forecasts[dayIndex].observationTime;
  217.   struct tm* timeInfo;
  218.   timeInfo = localtime(&observationTimestamp);
  219.   display->setTextAlignment(TEXT_ALIGN_CENTER);
  220.   display->setFont(ArialMT_Plain_10);
  221.   display->drawString(x + 20, y, WDAY_NAMES[timeInfo->tm_wday]);

  222.   display->setFont(Meteocons_Plain_21);
  223.   display->drawString(x + 20, y + 12, forecasts[dayIndex].iconMeteoCon);
  224.   String temp = String(forecasts[dayIndex].temp, 0) + (IS_METRIC ? "°C" : "°F");
  225.   display->setFont(ArialMT_Plain_10);
  226.   display->drawString(x + 20, y + 34, temp);
  227.   display->setTextAlignment(TEXT_ALIGN_LEFT);
  228. }

  229. void drawHeaderOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) //绘制页眉

  230. {
  231.   now = time(nullptr);
  232.   struct tm* timeInfo;
  233.   timeInfo = localtime(&now);
  234.   char buff[14];
  235.   sprintf_P(buff, PSTR("%02d:%02d"), timeInfo->tm_hour, timeInfo->tm_min);
  236.   display->setColor(WHITE);
  237.   display->setFont(ArialMT_Plain_10);
  238.   display->setTextAlignment(TEXT_ALIGN_LEFT);
  239.   display->drawString(0, 54, String(buff));
  240.   display->setTextAlignment(TEXT_ALIGN_RIGHT);
  241.   String temp = String(currentWeather.temp, 1) + (IS_METRIC ? "°C" : "°F");
  242.   display->drawString(128, 54, temp);
  243.   display->drawHorizontalLine(0, 52, 128);
  244. }

  245. void setReadyForWeatherUpdate()     //设置为天气更新准备
  246. {
  247.   Serial.println("Setting readyForUpdate to true");
  248.   readyForWeatherUpdate = true;
  249. }
复制代码

获取数据的方法点击
这里
验证完毕,然后再点击“上传”即可。

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

五、结果


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

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


看看视频效果:拂尘~走

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
发表于 2020-11-30 19:15:54 | 显示全部楼层
我复制过去一直验证不通过,是什么问题啊
回复 支持 反对

使用道具 举报

发表于 2020-11-30 20:46:39 | 显示全部楼层
用零知示例里的可以通过但是上传后时间不对,日期正确,请大佬明示,谢谢
回复 支持 反对

使用道具 举报

发表于 2022-3-2 06:44:04 | 显示全部楼层
longjunling 发表于 2020-11-30 20:46
用零知示例里的可以通过但是上传后时间不对,日期正确,请大佬明示,谢谢
...

UTC时区+8,程序里可能 不是
回复 支持 反对

使用道具 举报

发表于 2022-3-3 09:56:47 | 显示全部楼层
可以通过不同的i2c地址码控制两个或者更多的OLED屏幕吗?求个类似的代码
回复 支持 反对

使用道具 举报

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

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2024-4-26 06:00 , Processed in 0.156000 second(s), 12 queries , Redis On.

Powered by Discuz!

© 2006-2023 smzj.net

快速回复 返回顶部 返回列表