# ESP8266开发Oled天气时钟 **Repository Path**: lisxi/ESP8266 ## Basic Information - **Project Name**: ESP8266开发Oled天气时钟 - **Description**: No description available - **Primary Language**: C/C++ - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 0 - **Created**: 2021-01-26 - **Last Updated**: 2024-07-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ![输入图片说明](https://images.gitee.com/uploads/images/2021/0207/210820_395f57d4_8005245.jpeg "成品.jpg") ![输入图片说明](https://images.gitee.com/uploads/images/2021/0207/211050_3235f5ea_8005245.png "图片11.png") 【材料】 ●ESP8266主板一块 15.00 ●Oled屏(0.96或1.3英寸)一块 IIC或SPI接口均可 12.00或22.00 【程序】 一、ESP8266联网方式 1.用ESP8266实现自动配网 用ESP8266实现连接WiFi网络,可以通过固定路由器登录方式(方法一),为使程序更加通用,可以利用ESP8266的自动配网功能(方法二,实现在任意路由器登录。 方法一:固定登录方式 ``` #include // ESP8266WiFi库 const char *ssid = "xxxxx"; // 要连接的WiFi名称 const char *password = "xxxxxxxx"; // WiFi密码 void setup() { Serial.begin(9600); // 启动串口通讯 WiFi.softAP(ssid, password); // 启动NodeMCU的AP模式。 Serial.print("Access Point: "); // 通过串口监视器输出信息 Serial.println(ssid); // 告知用户NodeMCU所建立的WiFi名 Serial.print("IP address: "); // 输出获得的IP地址 Serial.println(WiFi.softAPIP()); } void loop() {} ``` 方法二:自动配网功能 ``` #include #include #include #include void setup() { Serial.begin(9600); WiFiManager wifiManager; wifiManager.autoConnect("AutoConnectAP"); //配网时连接到AP的名称,可修改 // wifiManager.resetSettings(); //用于测试时清除联网信息 Serial.println("ESP8266 WiFi Settings Cleared"); } void loop() {} ``` 2.用手机配网 配网时,打开手机WiFi页面,选择搜到的“AutoConnectAP”并连接,然后在搜到的WiFi中选择要连接的设备(如XXXXXX),输入WiFi密码,完成配网。 ![手机配网](https://images.gitee.com/uploads/images/2021/0205/210042_4e7bf020_8005245.png "图片2.png") 二、Oled屏 3.规格与参数 Oled屏有多种规格,通常使用的、价格比较便宜的有0.96寸、0.91寸(细长形)、1.3寸,其中0.96和1.3寸显示分辨率通常为128x64,显示屏的接口类型有I2S、SPI两种,显示驱动芯片种类也比较多,最常见的有SSD1306、ST7920、SH1106等,要根据不同类型的芯片选择不同的驱动方式。 4.驱动程序 Arduino中Oled驱动库是U8gLib,它是一种嵌入式设备的单色图形库,U8g2是它的增强版,包括所有图形和字体,但需要占用内存来显示。U8x8是它的简化版,只能纯文本输出字符,直接写屏,不需要内存缓冲。本例程使用的是U8x8驱动程序。 针对不同接口和驱动芯片的显示屏,需要用不同的驱动程序。本例程使用的SSD1306芯片IIC和SPI接口驱动如下: ●IIC接口:U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(SCL, SDA, U8X8_PIN_NONE); //SCL接D1,SDA接D2 ●SPI接口:U8X8_SSD1306_128X64_NONAME_4W_SW_SPI u8x8(/* clock=*/ 14, /* data=*/ 13, /* cs=*/ 4, /* dc=*/5, /* reset=*/ 32); //Oled-ESP8266对应接口:D0-D5 ,D1-D7,RES-RES,DC-D1,CS-D2 (参考资料:https://github.com/olikraus/u8g2/wiki/u8x8reference) 2.显示控制 ``` u8x8.begin(); //打开显示控制程序 u8x8.clear(); //清除屏幕信息 u8x8.setFont(u8x8_font_7x14B_1x2_f); //设置字体 //参考资料 https://github.com/olikraus/u8g2/wiki/u8x8reference#setfont // https://github.com/olikraus/u8g2/wiki/fntlist8x8 u8x8.drawString(0, 0, "Connecting"); //在指定位置显示文本信息 ``` 三、关键程序 5.获取时间信息 (1)定义变量,发送请求: ``` const long timezoneOffset = 8 * 60 * 60; // ? hours * 60 * 60 中国位于东8区 String now_address = "", now_weather = "", now_temperature = ""; //用来存储报文得到的字符串 const char *ntpServer = "pool.ntp.org"; //时间服务器地址 WiFiUDP ntpUDP; //定义类,基于WiFiUDP.h timeClient.setTimeOffset(timezoneOffset); NTPClient timeClient(ntpUDP, ntpServer); timeClient.begin(); //发送服务请求 ``` (2)返回参数: ``` unsigned long epochTime = timeClient.getEpochTime(); struct tm *ptm = gmtime ((time_t *)&epochTime); //定义时间参数结构体 String year = String(ptm->tm_year + 1900); // 提取年月日时分秒星期 unsigned int month = ptm->tm_mon + 1; unsigned int day = ptm->tm_mday; unsigned int hour = timeClient.getHours() + 8; //东8区时差,这是有问题的表达 unsigned int minute = timeClient.getMinutes(); unsigned int second = timeClient.getSeconds(); String weekDay = weekDays[timeClient.getDay()]; ``` 6.获取天气信息 天气信息提供网站常用的是“心知天气(https://www.seniverse.com/)”和“和风天气(https://dev.qweather.com/)”,需要注册免费用户、添加产品,获取网站提供的密钥(KEY),用http.begin()命令获得天气参数,命令格式为: "http://api.seniverse.com/v3/weather/now.json?key=xxxxxxxx&location=xxxxxx&language=zh-Hans&unit=c"); 其中:key=用户密钥,location=地区或城市。 ●心知天气城市列表查询:https://docs.seniverse.com/api/start/start.html ●和风天气城市列表下载:https://github.com/qwd/LocationList 本示例是用心知天气获取天气数据,和风天气请查询相关资料。 ``` int httpget_now = http.GET(); //赋值,如果正确获取信息,httpget_now 返回值“HTTP_CODE_OK” String respone = http.getString(); //获取返回字符串 JsonObject& root = doc.parseObject(respone); JsonObject& results_0 = root["results"][0]; JsonObject& root = doc.parseObject(respone); JsonObject& results_0 = root["results"][0]; JsonObject& results_0_location = results_0["location"]; //提取位置或城市信息 const char* results_0_location_id = results_0_location["id"]; ``` 各种天气数据都被保存在Results_0的JSON字符串中,如天气状况、气温、风力、最低/最高温度待,使用需要提取。 需要注意的是:ESP8266主要获取日期信息时,只需要“读取”请求,所以可以多次反复获取服务器信息来不断获得时间,而天气信息需要向服务器发送GET请求,因而请求次数不能太频繁,否则服务器会拒绝请求。程序中设计了一个updateDelay变量,用于控制向服务器发出请求的时间间隔。 四、技术难点 7.天气状况表达 Oled属于较简单的显示设备,显示缓存比较小,一般不带汉字库,所以天气状况不方便用汉字表达(U8x8只能支持字符方式),用英语表达会比较长,占屏幕空间,而且不太符合中国人的习惯,所以我想到用特殊字符来表达。查找大量资料后,最后找到u8x8_font_open_iconic_weather_2x2库中有天气符号,尽管只有简单的6个符号,也基本够用了。 ![天气图标](https://images.gitee.com/uploads/images/2021/0205/210233_0dc1f6dc_8005245.png "图片3.png") 8.字库中字符的显示 每一个字符在字库中都有一个地址,如64/40(前一个是十进制地址,后一个是十六进制地址),如何才能让字符在Oled屏上显示出来?通过查找资料结果,可用地址访问方式(“\x80”)显示十六进制字符,如要显示太阳字符命令形式为:u8x8.drawString(8, 0,”\x45”); (由此我想到这种方法可以应用在其他很多地方,如可以用“实心矩形+空心矩形”图案来表示电池电量多少,还可以用来表示喇叭音量大小、音乐的播放状态、无线或蓝牙是否连接或信号强弱等信息。) 9.天气代码转变为字符 向心知天气发送GET请求后,返回的天气状况是用代码表示的(参考地址:https://docs.seniverse.com/api/start/code.html),须将代码转换成天气字符显示在Oled屏上,本示例用了一个swich…case语句,将天气代码与对应的天气图标关联起来。 ``` switch (tq) { case 0: ... case 3: tq_code = "\x45"; break; //晴 case 4: ... case 8: tq_code = "\x41"; break; //多云 case 9: tq_code = "\x40"; break; //阴 case 10: ... case 20: tq_code = "\x43"; break; //雨 default: tq_code = "\x44"; //其他情况显示★表示“特殊天气” } u8x8.setFont(u8x8_font_open_iconic_weather_2x2); u8x8.drawString(8, 0, strcpy(new char[tq_code.length() + 1], tq_code.c_str())); ```