1. 导论
先来看看别人是怎么实现的:利用ESP8266+OLED(I2C)打造智能时钟(网络校时+实时天气+天气预报)
我们要分析这篇文章中的代码并加以改造。可以点击上面的链接去原帖膜拜一下。
同时,本文也会探讨一些方法和原理(通式通法),包括:
- ArduinoJson的使用
- 字体的使用
- U8g2的使用
等一些其他内容,希望能够有所帮助。
(没想到能写这么多)
2. 连接WiFi
WiFi的连接与前面的示例是基本一致的,ssid和password分别是WiFi的名称和密码。
可以像示例中一样写,也可以像这篇文章的作者一样:
这一块是定义常量,应该写在前面,注意还要导入相关库:
3. 获取天气
获取实时天气的函数为:
其中的reqRes是要请求的地址,作者使用了心知天气,这里先简单介绍一下心知天气。
3.1 心知天气
心知天气的官网地址为:心知天气
点击上面的链接跳转官网后,点击右上角注册/登录。
根据说明注册并登录后,点击控制台,然后点击产品管理->添加产品,选择免费版。
免费版可以使用V3版3项数据,具体配置可以都可以在官网中找到。
V3版的使用手册为:心知天气使用手册(V3版)
3.2 实时天气
实时天气在手册的这一页:天气实况
接口地址为:
https://api.seniverse.com/v3/weather/now.json?key=your_api_key&location=beijing&language=zh-Hans&unit=c
参数列表见上,其中API密钥在产品管理->免费版->API密钥->私钥处获取。
原作者设置语言为英语en,其他三个参数是预先定义好的:
这里填写私钥和城市的拼音。城市拼音参见:接口中的通用参数-Location
拼接好了一个字符串参数reqRes,传入函数httpRequest,这个函数的作用是发请求,在下面定义。
3.3 发送请求
可以看到,在这个函数里又定义了一个字符串变量httpRequest,这是模拟HTTP请求报文。具体原理涉及《计算机网络》,不在这里赘述了。
拼接字符串中的host是目标主机(也即接口地址的主机域名部分),在程序开始已经定义了:
接下来发送请求(大括号未闭合):
先试图连接主机,如果连接成功,client.connect(host, 80)应当为true,这个函数或者说表达式能够检验连接是否建立。
接下来,通过client.print(httpRequest)发送请求。
发送请求成功后,服务器会给我们响应,具体以响应报文的形式实现(这也是《计算机网络》中的内容)。
简单来说,响应报文由响应行、响应头和响应体组成。响应行是响应报文的第一行,而readStringUntil(' ')表示读取到第一个换行符(即' '),可见这个status_response就是响应行。响应行包括协议、状态码和描述,可以在串口监视器中看看它长什么样。
然后,使用find跳过HTTP响应头,这里不再细讲了。
之后判断stat是0还是1,这个参数为0时解析实时信息,为1时解析未来信息。
可以看到,在用于获取实时天气的函数 TandW中,为httpRequest传的stat参数就是0,作者还特意在这个函数那里标记了0。
3.4 信息解析
信息解析需要使用ArduinoJson这个库(返回的响应是json格式的),在库管理器中安装它,就像前一篇中一样。
原作者的代码如下:
解析的主要原理在于
- 定义了一个对象,其容量根据JSON结构的需求计算而来。这是为了确保有足够的空间存储解析后的JSON数据。
- 使用函数从读取JSON数据,并将其解析到中。
- 从中提取天气信息,包括当前天气状况、天气代码、温度和最后更新时间。
特别需要指出的是,capacity就是创建doc时指定的容量,它是根据响应而定的。
接下来介绍一下设置方法:
借助ApiPost(或者其他的接口测试工具),对接口进行HTTP测试,得到响应为
观察响应的结构,可以发现:
- 顶层对象:仅包含一个键。
- 数组:包含一个对象。
- 中的对象:包含三个键(, , )。
- 对象:包含六个键(, , , , , )。
- 对象:包含三个键(, , )。
由此可知容量是上面五个的和:
和原作者的比对一下,会发现还差了230,这是哪里来的呢?
其实,因为这个json里面有大量的字符串,所以需要额外的空间来保证字符串值都被存储。
字符串值的内存开销通常比简单的数字或布尔值要大,因为它们需要存储每个字符。此外,还需要为每个字符串分配结束字符'0'(这是《C++程序设计》中的,可以去阅读相关书籍,这里不再赘述)。
但是这个值到底该怎么取呢?
实际上,这个值通常是一个估计值,因为除了上述开销,还应当留有一定的裕量。
但是,可以借助ArduinoJson库的Assistant来方便地确定这个值,链接:https://arduinojson.org/v6/assistant/。
在步骤2中输入Json即可,这里就不放图了,接下来是步骤3:
可以看到,在Strings这一项,显示的数字是204,也就是说,对于我们这个capacity,需要加上的字符串存储空间为204,并且至少为204(建议发请求时设定language为en,就像原作者一样,因为中文占3字节,为了节省空间,还是英文更好一点)。
再考虑到裕量,所以设定为230,这是比较合理的选择。
以上就是通式通法。
特别说明:上述求和的方法目前已被弃用,推荐设为定值,即采用ArduinoJson库的Assistant的建议,也许还有其他的设置,但我还没有研究,仍采用的这种旧方法。如果有大佬有方案,可以在评论区指点一手或者贴个链接让我去膜一下。O(∩_∩)O
此外再补充两点:
- 如果出现错误DeserializationError::NoMemory,这意味着容量选小了,应当扩大容量。
- DynamicJsonDocument,内存分配在heap区,无固定大小,可以自动增长所需空间,方法调用完自动回收,建议内存大小大于1KB使用。
上面的第2条我是在简书上看到的,原文链接为玩转 ESP32 + Arduino (十五) ArduinoJSON库(V6版本),欢迎前去膜拜。
此外,这个assistant还有一个非常强大的功能,它可以根据json生成解析代码:
原作者在这里进行了类型转换:
本来提取到的数据都是C风格的字符串,在这里将其转换成了String和int类型。
在最开始,定义了两个全局变量,用于保存断网前的最新数据:
可以看到,在类型转换后,就将获得的数据展示并储存了下来:
其中display_0函数就是用于将数据显示到屏幕上,而下面两行的作用就是将数据储存在全局变量里,这样就可以在其他函数中使用了。
值得注意的是,在这个解析函数parseInfo_now中,除了client这个需要解析的对象外,还存在一个参数i,不难看出,如果i==1,就解析数据、更新数据、显示数据,而其他情况下就直接显示原有数据,即results_0_now_temperature_int_old和results_0_now_text_str_old。
3.5 未来天气
与实时天气大同小异,这里不再赘述了。
4. 数据展示
上面用到的display_0函数如下:
这一部分使用了u8g2这个库,它是一个非常强大的LCD、OLED、elink的扩展库,需要在库管理器中安装。如果使用的显示屏属于上面三种,就可以使用这个库。
注意导入方式为:
4.1 U8g2
接下来先简单介绍一下这个库的常用函数:
这个函数的作用是清除图形缓冲区。这是准备绘制新图形之前的常见步骤,以确保从干净的状态开始,简言之——清屏。
这个函数的作用是设置字体,可以看到我们已经传入了一个参数u8g2_font_wqy16_t_gb2312,它代表着龙泉驿点阵宋体16x16 点阵字库。
GitHub上有个中文字库仓库,得到了较为广泛地使用:https://github.com/larryli/u8g2_wqy。
同样,也有英文字库。具体可以搜索相关资料,这里不再赘述。
这两行的作用是设置光标和输出。需要注意的是,它的意思是将画图位置移动到x=15,y=14处,然后以这个点的右上区域进行字符串的显示,纵坐标必须留有裕量,否则输出会到屏幕外面,这样就看不到了。
因为刚才设置了中文字体,所以能够输出中文(想修改输出的内容就在此修改)。
此外,显示屏的分辨率值得注意,更高分辨率的显示屏可以显示更多的字符。例如,128x64像素的OLED屏幕在使用16x16字体时,理论上每行可以显示8个字符。所以这个情况是合适的(我的显示屏就是128x64的),而且这六个字得以基本居中,但如果长度大于8,建议滚动显示以保证效果。
这个函数用于将在内存中准备好的图形缓冲区内容发送到显示屏上。在U8g2库中,绘制操作(如设置像素、绘制线条、显示文本等)首先在内存中的一个图形缓冲区(frame buffer)进行,而不是直接在显示屏上。这种方法允许复杂的图形处理和更新,而不会直接影响到屏幕显示,从而避免了绘图过程中可能出现的闪烁现象。
此外,还有draw系列函数没有用到,具体包括画线、画框等,使用方法比较简单,大家可以自行了解。
特别注意,使用以上函数前,应当先初始化显示屏,这意味着将显示屏所需的硬件接口设置为正确的工作状态,这里就是指配置I2C(这是一种基础通信协议,具体可参看《通信原理》)。
初始化方法如下:
特别地,对于我们的项目,还需要调用下面这个方法来显示各种语言的文本(主要是中文):
它的作用是允许UTF8形式输出。
4.2 温度显示
第一行的文字部分已经在4.1中简要介绍过了,接下来讨论温度显示的实现方法。
可以观察到,上面三行代码实现了将温度显示到屏幕上。
在输出前先设置了字体为Logisoso24,它的特点是具有较大的尺寸(大约24个像素高),适合显示重要的或需要突出的信息。字体名称中的后缀表示该字体支持透明背景,即在文本周围不会有一个不透明的矩形框。
但是有一个问题,原作者只设计了数字的显示,没有单位(即摄氏度°C),这样比较影响观感,如何优化呢?
很简单,在温度后面加上一行代码即可:
但是这产生了又一个奇怪的问题,我们可以看到显示屏只显示了C,而没有°。
这是因为:在U8g2库中,不是所有的字体都包含全范围的Unicode字符,特别是对于一些专用或较大的字体来说,它们可能仅支持有限的字符集。
而我们所使用的Logisoso24恰好属于这种情况,解决方案有两种(对于其他的特殊字符也使用):
- 采用Unicode字符集,例如u8g2_font_unifont_t_symbols,它支持的字符更多。
- 手动绘制符号,对于这种情况就是说在合适的位置画一个圆。
优化后的代码如下(摄氏度的符号会居于数字的右下侧):
最后是显示气象名称:
包括Sunny、Cloudy、Overcast等等,具体可以参看心知天气的手册,不再赘述了。
不过谨记,必须发送缓冲区才能使上述生效:
5. 时间获取
5.1 NTP
时间的获取是根据NTP实现的,先简要介绍一下NTP。
NTP(Network Time Protocol,网络时间协议),
是用于同步网络中计算机的时间的协议。它可以使计算机时钟与世界标准时间(如UTC)对齐。NTP是一种分层的、自适应的同步系统,包括一组分布式的服务器和客户端。这些服务器和客户端通过网络交换时间信息,以此来调整客户端的本地时钟。
正因为分布式,所以我们使用阿里云的:
它主要为国内网络设备提供服务。
NTP基于UDP报文进行传输,使用的UDP端口号为123,如下面这个函数所示:
UDP是一种运输层协议,具体原理、应用等可参看《计算机网络》。
NTP的具体实现原理可以参看《通信原理》,这里不再细讲原理。
特别需要指出的是,尽管NTP的精度很高,但误差仍然存在,因此如果发现存在几百毫秒的误差,也是正常的。当然,我们的项目也并不要求那么精确。
5.2 代码实现
获取数据的部分已经在5.1中展示了,数据显示的部分如下:
大家可以按需修改,如果本篇文章有疏漏,参考原作者的文章即可:利用ESP8266+OLED(I2C)打造智能时钟(网络校时+实时天气+天气预报)
此外还有断网处理,与3.4中相近,这里不再赘述了。
没想到写了这么多,,下一篇放我自己的代码(也许说是补丁或者其他的更加合适...)。
至少下一篇会介绍ESP8266的中断机制,其他的emm看情况看看能写多少吧。
到此这篇esp8266oled制作天气时钟(esp8266天气时钟教程)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/do-yfwjc/74511.html