前言 Y5 L# M9 e5 C# s3 Z
本文为手把手教学ESP8266著名开源项目——太空人WiFi天气时钟,不同的是本次项目采用的是STM32作为MCU。
( C0 W: T% H5 g; s; |) g
两者开发过程中有因为各自芯片的特点(时钟频率,内存大小等),导致开发程序大不相同,很多地方需要特殊设计一下。而作者使用STM32开发的原因很简单,ESP8266虽然计算能力等方面优于STM32F1xx,但是弊端也很明显。
) L: Z8 a3 K' r" B: D
其所具备的引脚和外设太少,扩展性一般(ESP32算是二者优点兼备)。加之网上ESP8266的太空人WiFi天气时钟已经开源的很完善了,所以尝试用STM32实现一下,也方便后续利用STM32拓展开发。(文末有代码开源!) * D% L9 h1 s& @/ t
实验硬件
) m6 z# I2 Z y# n4 a5 k
STM32F103ZET6;7针1.3寸TFT-LCD(240×240);ESP8266
- D$ d/ K* H- b+ ]8 x7 w4 f
硬件实物图:
$ H$ I- p1 b0 R* ]. M4 m
5 Q! t3 i! P( L" r
/ S% o8 n6 t \1 F' \/ b2 x3 M
效果图 : p, m, T! o5 \0 a" H% m7 A) y
# N. N+ Q( D1 w& \; _5 c9 ]) H! t9 q6 f+ ~2 m& L
引脚连接: - $ M, B2 `, \& s
- LCD显示引脚:5 N. ?$ [, p" ]
* M, J8 d) z2 w7 z6 V, L: e. G- VCC --> 3.3V
+ } G2 b$ q- n8 f S8 b
; V( w; P+ H1 l" p0 y: @2 P+ P- GND --> GND
6 M1 a- N' p, e - 8 F8 n# C0 y1 u, v' A' P4 n( O
- CLK --> PA54 ?5 t3 `3 | ^
- ) X* t7 ?& C/ ? i- `3 |. q
- DIN --> PA7
7 p1 ~8 X$ k0 u7 h4 v
# O; R. j, i. V8 M1 H1 c/ E- RES --> PB0
; Y; X0 ^+ ]! Q3 p; M0 s - # L; i3 y) E& r* O8 J
- DC --> PB1
0 a; B1 S8 Y# J ?% w$ F
2 G# f' e ~, a- CS --> PA4
复制代码- ESP8266模块引脚:6 P M2 U2 e: j$ u% c0 ]+ S
- ) C; k/ }: Z6 k$ A1 V" Z4 `
- VCC --> 3.3V5 W1 J+ ]$ a! C6 y0 t0 U1 |
- 6 k3 l# x/ T9 p+ l
- GND --> GND" w( h* {) L' G* ? T4 N
5 ?7 p7 l: ?; {. P& W& n4 C, W- RX--> PB10
' g/ A4 b0 m# f8 A- f7 g" Q" { - ! G/ |3 S3 _3 t
- TX --> PB11
( V! O' S# l4 h4 V) b - " ^" B/ |" n9 L& G
- RST --> PB9
* _& q T+ G" b0 u2 H* L" j/ v; v
4 `5 R* @/ J9 C3 ^# c; g- EN --> PB7
复制代码 9 @( n* E5 [1 g _" d* h
6 w- F2 F3 |, E' r+ E1 G/ |" u
一、ESP8266简介与使用 9 j- M; y; M' M
1.1 ESP8266简介
4 o2 j5 H# N) h, [2 @
SP8266是一款超低功耗的UART-WiFi透传模块,拥有业内极富竞争力的封装尺寸和超低能耗技术,专为移动设备和物联网应用设计,可将用户的物理设备连接到Wi-Fi无线网络上,进行互联网或局域网通信,实现联网功能。
8 ?, l+ s" h# H, j
ESP8266是上海乐鑫信息科技(国产)设计的低功耗WiFi芯片,集成完整的TCP/IP协议栈和MCU(网上ESP8266型号很多,基本都具备联网功能,部分型号可以直接作为MCU使用)。 5 h8 Q" e7 e. j
而ESP8266模块是深圳安信可公司基于ESP8266芯片研发(增加必要外围电路、串口flash、板载天线等)的串口WiFi模块,成本低、使用简便、功能强大。 3 i, [+ x) o ?2 c
, Z3 {% F5 a3 O4 m
, P) T! n3 e$ t) r
8 H& [4 D' R' h, ? R
5 s. ^/ d; ~- s1 K. l1.2 硬件与网络的桥梁—ESP8266 ( T W/ Q: X+ ?" e, A0 C3 ~/ v
ESP8266模块和串口蓝牙JDY-31模块一样,串口WiFi模块也是扩展单片机功能的又一神器。 5 Z; h& Z/ C* i$ J
小巧的 ESP8266 WiFi模块通过串口AT指令与单片机通讯,实现串口透传,非常好上手(部分型号ESP8266可以直接当MCU,无需再通过串口与其他MCU通讯)。
( r! ^/ ~+ {6 u( h( T
透传,又称透明传输,具体来说就是“输入即输出(如从WiFi模块串口输入的字符会透传到服务器端)”,数据不改变,不同协议之间的转换(如串口到WiFi、蓝牙等)由模块完成。
: Y' y& }9 F1 c: h' I0 |7 |5 _ ]0 d6 e6 m
使用者无需关心内部具体实现,因此模块对于使用者是“透明的”、似乎不存在的(因为可无视中间的实现原理)。一个高度封装的模块,应该隐藏内部实现细节,仅对外提供使用接口。
/ E4 z& i# k- N1 L7 t8 d, G
把硬件联网之后,就再也不是“玩单机”了。配合服务器端的Socket网络编程,可以玩许多东西。所以我觉得WiFi模块是连接软件(网络编程)与硬件(单片机)的桥梁,把所学的单片机(MCU)和Web知识联系起来了。 1 {3 v9 }; X* |8 k
如今大火的物联网等概念都属于“智能硬件",ESP8266等模块的出现大大减少了网络开发的难度系数,也进一步促进了技术下放。而且,通过学习ESP8266/ESP32等模块,可以熟悉大量TCP/IP等网络协议,对后续Linux系统板网络开发也是极具意义的。 $ z5 Q4 G% Z9 W
$ `$ t: i9 X' F. R
1.3 ESP8266使用——AT指令
, T" U) X0 W0 h5 {
AT指令最早在蓝牙模块上接触过,所谓AT指令实质上就是一些起控制作用的特殊字符串。模块可以通过AT指令控制搭配使用源代码API函数开发,总体开发速度快,难度较低。
a. x. k' Y' K1 |/ K2 n
说明:下面仅列举一些最常用的AT指令及用法,指令的详细参数及使用说明请参考官方文档:ESP8266 AT指令集。
* \. T( X, D; x3 c/ o5 k
基础AT指令 / T4 a! x6 T0 x2 ]- g: v" b: [$ @+ I
% q3 \- t: K+ t4 S2 t
) s& K. p1 U7 ~
- l+ `( D4 `2 P' R) [8 j+ K
关于WiFi模式这里要说明一下,sta模式下模块相当于客户端,像我们手机平板一样是要去连接路由器的,而AP模式下模块相当于路由器,是发射WiFi被别人连的。
7 F& J% y4 X+ |- K1 V8 K7 }2 T
ESP8266支持两种模式并存(模块出厂默认的是AP模式) 。另外,扫描WiFi指令 AT+CWLAP 只能在sta模式下使用,否则会报ERRO错误, AT+CWJAP 和 AT+CWQAP 指令也同理。 0 F" g: m* M/ u( m4 c
sta模式连接WiFi演示
+ Q5 b: a9 D$ ^, ^
1. 发送 AT+CWMODE=1 指令配置模块为sta模式(参数1,2,3分别对应模式sta,AP和sta/AP)。 ) t6 e) i; _9 l
2. 发送 AT+CWLAP 指令扫描当前附近WiFi,模块会返回可用AP列表。
- i/ s) `- A+ J3 a; ?- J8 l, S m% G
3. 使用 AT+CWJAP="WiFi名称","WiFi密码" 连接到指定的路由器,比如我在图书馆的WiFi是 “Wang”,密码是“123456”,实际连接WiFi发送的指令就是 AT+CWJAP="Wang","123456" 。
. V7 e- ]9 h7 Z1 v/ p
4. 返回的“WIFI CONNECTED”说明连接成功,“WIFI GOT IP”代表模块分配到了IP。 ) z/ X5 ~+ m" [4 w, k% N! ~
5. 最后可使用 AT+CWQAP 断开当前连接的WiFi。 ) W3 H2 W! V9 S% v
- J* g4 c" {$ N% I$ l$ V% W0 y
TCP/IP相关AT指令 - ^6 D% L7 h4 ?. J
传输控制协议(英语:Transmission Control Protocol,缩写为 TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。 / H/ A, k5 Y; u2 \2 x% g9 c
在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,用户数据报协议(UDP)是同一层内另一个重要的传输协议。 # w3 k9 |4 P* U' a6 h
在因特网协议族(Internet protocol suite)中,TCP层是位于IP层之上,应用层之下的中间层。不同主机的应用层之间经常需要可靠的、像管道一样的连接,但是IP层不提供这样的流机制,而是提供不可靠的包交换。 + v4 Q4 I/ I" w0 r; ~: `
我们常说互联网互联网,那两个连接到互联网的设备该如何相互“交流”呢?TCP连接就是其中一种最常用的方式。 3 h( X+ I' ~ _; U1 z
TCP是面向连接的传输层协议,通信双方都要实现TCP协议,其中一方只需目标ip地址和端口号就能发起连接,连接一旦建立,就像在双方之间拉了一条管子,管子两端可进行全双工(双向同时收发)通信。 ! `1 R) b; G! M2 r
TCP是传输层协议,是在网络层IP协议的基础上封装而来。而这些封装的实现细节也是与我们无关,我们只需使用系统所提供的相关接口“拿来即用”,比如网络编程中的Socket。
) F! x/ D# ~# N4 T
ESP8266模块中也实现了TCP/IP协议栈,模块作为客户端可轻松使用AT指令向服务端发起TCP连接。连接TCP服务器并开启透传模式后,模块串口收到的数据就会通过TCP连接透传到服务端,这样就完成了数据从硬件串口通过网络到程序进程的传输,实现软硬结合。 5 F3 Y5 G) j9 Z! i" t* ]2 r
" m/ @+ B. k: m0 H" R- K+ Q2 V1 W
9 g3 V: r0 X. Z4 GAT 是最常用的指令,用于测试模块能否正常接受指令。在sscom中向串口发送指令 AT ,若收到模块返回的 OK 则说明模块的AT指令可正常工作。发送 AT+GMR 可查看AT指令及SDK的版本号,一般最新版指令会增加一些新功能,可随时关注官方的更新。
3 s2 M8 A7 S' p
WiFi功能AT指令
p9 M) T1 D/ b2 h/ [
WiFi是让硬件联网的基础,和其他功能一样,这里仅列举所需的常用指令,更详细指令说明还得查阅文档。
+ v+ @6 O2 U3 ?* J" f" E
# e: n% f* F3 O& @5 P( c
0 B3 _. A2 l8 [8 [把WiFi模块和电脑连接,在sscom确定AT指令能正常使用后,就可以开始配置TCP连接了,具体步骤如下: ) u& r0 A ?7 O. c2 ?
1. 根据上面“sta模式连接WiFi演示”一节把模块连上WiFi
- q K" k6 p) l( y( [' k8 v8 X3 a
2. 输入指令 AT+CIPMUX=0 设置单连接
% @$ `% p5 ~, D, {. A# s9 U( \ % r2 _; t7 q* h: K3 I. |
3. 从“网络调试助手”得知本机IP和端口,输入指令 AT+CIPSTART="TCP","192.168.43.140",1234 (指令参数分别为连接类型、目标IP地址和端口号)向服务器发起TCP连接请求,握手成功并建立连接后,服务器端的“网络调试助手”就会显示客户端IP和端口信息,此时双方已做好收发数据的准备(根据实际需要连接的IP地址来)
% P0 R* U5 i8 ~9 {) o% z; C6 u5 Z
4. 输入指令 AT+CIPMODE=1 开启透传模式
0 I5 A+ A6 W# x' b; w! a8 }2 k4 V
5. 输入命令 AT+CIPSEND 进入透传模式,此时模块会把所有串口收到的数据都从TCP端口发送至服务器,同样的,从服务器收到的数据也会从模块串口发送出去打印到sscom上。这样WiFi模块就真正成为了连接硬件与网络的桥梁,实现了串口到TCP的协议转换 / Q$ z" o. e3 i- X) {0 u
以上其实就是大概本次项目需要使用到的指令,ESP8266配置代码如下: - void esp8266_config(void)& I4 r) Q5 |; h% A
- {& r. j: p5 K/ l% r& l
- char str[200];
1 ], x8 H6 T+ Z# `" B# D8 Q - sprintf(str, "AT+CWJAP="%s","%s"\r\n", WIFI_NAME, WIFI_PSW);
; m, A% w& j, _+ Q4 p1 O, V - // SendATCmd("+++", 500); // 退出透传模式+ u3 U! T2 O& A4 [. w6 M3 F
- SendATCmd("AT\r\n", 2000); // 测试ESP01模块是否存在- `& g+ M! x3 G1 y
- // SendATCmd("AT+GMR\r\n",3000); // 查看模块版本信息
! K% a! Z5 ^0 h+ G3 K - SendATCmd("AT+CWMODE=1\r\n", 2000); // 开启STA+AP模式 ================== + }- e* n- ~& ~/ n( P/ A( x
- SendATCmd("AT+RST\r\n", 3000); c3 {* c% P f: `
- SendATCmd(str, 10000); // 连接无线路由器或者手机热点,等待10秒 ============( S6 ^' d5 E4 C: h
- SendATCmd("AT+CIPMUX=0\r\n", 2000); // 关闭多连接- g1 o9 Y. w8 R% J$ Y" ^4 k+ W* P
- SendATCmd("AT+CIPSTART="TCP","api.seniverse.com",80\r\n", 2000); // 连接心知 天气TCP服务器
. |- I# E& `5 s8 F) l* \ - SendATCmd("AT+CIPMODE=1\r\n", 500); // 开启透传模式" Z0 c/ j/ |2 a8 n/ ^$ `
- SendATCmd("AT+CIPSEND\r\n", 500); // 开始透传
- y: ~7 w; }$ J3 v& G) Z4 P6 Z - }
复制代码
' T) k0 ~ ]# ^$ v8 S- @二、知心天气API使用 - s+ e* Z! V# W+ ~) B( a- i( O s
本项目为WiFi天气时钟,自然离不开需要从网页上读取天气信息。这里我们使用业内比较著名的知心天气。
: Z; e2 m/ F7 |' f/ X4 U. K; v0 g
2.1 登陆心知天气官网,注册 8 b( ^2 E# t" t9 F0 Z6 N% F
没有账号的朋友可以自己去注册一下,流程很简单。不商用的话,知心天气是免费的,还是比较良心的(网站响应率也很高)。
' |$ K2 l# }$ Q1 D
- U6 j" N$ S( W8 X' j& I0 M, C
$ i$ n6 v, H4 f
点击“立即免费试用” % K9 u' Y; i+ k; K: x3 Z
+ `8 }/ k4 o' z" H
7 T8 I U) q. o! T- I5 G4 @
点击免费版的“免费申请” ( n1 _: D$ j" ^ a, C0 P
9 a/ u$ W, e+ ^
' h% k4 F x- R8 f+ c
申请后可查看到自己的私钥(自行保存后面需要用到)
* ?/ T/ t- z$ d4 O$ y4 |: | Z
/ Z0 f4 @* c) Q" K) b+ B
7 L! \7 E1 l5 I& t% h5 t
2.1 API函数的使用
0 s& W0 P2 a3 c1 h+ \* X$ P
目前,大部分网络数据调用都是习惯性的调用数据提供商的API接口函数。 # c! @. W8 u0 I2 d8 s! m
重新点击“产品”—>“天气数据”,点击“查看API文档”
4 K% Q8 p) q T7 |5 X
3 L- D$ E1 v( q4 \& c
4 s, S! U# m2 ]" K9 |& @
点击"天气实况",打开对应的API接口文档
( c, d3 u7 o& l7 Y4 I" A# e( H
( U( g- B. A% a1 L( [
& |8 K: i4 j5 ]: p
查看天气实况的接口地址,以及返回的数据结果示例(自行保存后面需要用到)
# } r* S! E9 G" k( s, b' S) r
* y# a" p, i7 i
: `3 P+ a5 |3 z' P4 x% ~# r; d9 U/ H- b
1)上述知心天气API接口函数的寻找和使用通用性很高,大部分网络数据读取的流程与之类似。
" d+ W n h# q; \# H
2)嵌入式开发大部分情况下一般都是C语言进行开发的,由于C语言的局限性,没有直接的字典类型处理(python) - f2 z7 ~0 ^: [
所以,对于服务器返回给ESP8266的JSON数据一般是无法直接解码读取的。目前有2种方法处理:①、移植CJSON去解码;②取巧去比对字符串(本次使用的方法), }2 }* I! p+ F; k1 H9 Q! I
; q5 P7 y4 z7 o. z# p
项目使用过程中直接使用知心天气自带的API函数,项目大致流程:开启STA模式后,成功连上WiFi后,通过TCP协议去访问执行天气网站的服务器,在发送特定的API接口函数,服务器响应后返回需要的结果信息。
7 H q& I: g1 {4 }0 G1 z) E9 [4 i
, l' R% u0 e# B! Z* E6 U0 g
三、UART串口通讯
. L9 h& B! B9 Y) K. u: a
STM32作为MCU与ESP8266直接的通讯就是简单的UART(串口)通信,这一点依旧与蓝牙模块很类似。 ( N: n! t4 A" I- ?
使用方法:通过串口UARTx_TX连接ESP8266的UART_RX,然后单片机通过串口发送AT指令集。 6 u- L! j+ _, F; T$ `
ESP8266后续从服务器接受的数据信息也从ESP8266的UART_TX传输给单片机UARTx_RX。后续只需要使用自己的方法去解析串口接收到的数据,即可得到自己想要的数据信息。 & Q0 m% c3 [: o
$ j. V6 ]1 |5 W' N
2 u0 x2 d2 t; C/ W4 h
h7 p! T7 ?( J" k) h$ [" H
四、CubeMX配置
# j/ a7 i0 P" U7 k( f6 d" X
RCC配置外部高速晶振(精度更高)—HSE
9 U9 o9 _8 v3 q( X" N8 e9 G I1 ?
6 [% _$ I4 w" e% O( P" A3 W
7 P+ B( P5 j5 T+ g. m. e
SYS配置:Debug设置成Serial Wire(否则可能导致芯片自锁) 4 ?7 ]0 z, P' H3 P4 z$ N8 i
8 z {3 K4 S# x& J K' y& S `& | `/ m/ ?. H
GPIO配置:此处模拟使用SPI通信,并且设置ESP8266的EN和RST : X( f# W$ t8 t" `6 ]* }4 N
. q5 ~5 Y( V6 i/ D, M' s1 G( I! ~& O) X% T" x9 z
RTC配置:年月日,时分秒 : J# J; N* X$ v; c" a% t( s
4 ?, i& O, l: m7 y
6 s+ M+ k; _. S3 c& p
UART1和UART3配置:MCU分别与电脑和ESP8266通讯(记得开启串口通信中断)
1 ~9 T. j. j& J' T
% T' j# t2 ?9 h6 s$ i# [) z5 ] b5 l! _+ Q D$ r% i8 x
! v5 Y3 c. y' E4 p, |
- I0 K4 }- {" }( h2 y
V, c) {9 Z- h( F
1 y5 G$ ]0 B0 c; K. b0 q
" c8 U9 b8 ^( f
# I; |8 T9 T# `$ |
时钟树配置
, `7 s# n5 Y: c {/ g
; M! t! {/ f. e! U$ R+ x3 ?8 L c7 P: C- B& h ~% C
工程配置
, Q- J3 r: e; R
: m: D3 x6 y7 B
/ G% t5 N8 O- @6 h
7 W0 ~ _. x ~# w2 Q: b* x* ?
五、代码与解析
# F. I6 a7 F' M+ C8 w j% d* ^
5.1 TFT-LCD显示代码
# ~. H5 S k* m# N8 h* u
LCD显示部分其实都是非常基础的操作,不熟悉的可以去看看笔者另一篇文章了解一下。作者这里主要把工程中不一样的地方指出来一下。 , ~. l6 d! k! Y. B5 d7 D0 M
5.1.1 UI设计 8 ~: Y8 y$ G6 X' J) f
WiFi天气时钟中最要的点——UI设计,需要去设计很多界面图标,作者这里耗费了超级多的时间,翻遍了GitHub和视觉中国。
: g& [. a' | ?4 i6 Z E+ V% P
最后找到了差不多符合作者要求的UI库(有需要的可以评论区留下邮箱),如下: 9 @" e& J% u) B0 C8 E+ e* Y4 b! s* o
0 C9 T) L( R& z% d, J R7 G
+ P" d) B* e4 F2 N- ]) C, x; b
5.1.2 GIF动图实现
, Q, T8 k. L, a% S# |
目前,由于STM32自身内存的缘故,其实STM32是不太适合实现GIF动图的。所以,网上这方面的资料和代码都很少。目前,较为主流的方法:(1)enWin或者Lvgl库实现GIF动图;(2)从SD卡读取数据去显示。
6 O# i5 H8 x. t7 H9 G5 h8 q6 q9 `6 [
作者这里用了一直笨方法去实现了GIF显示,就是去循环遍历GIF动图的每一帧。
" h9 F- X9 F- X7 ^* f
使用GIF分离器去分离GIF动图的每一帧;
9 Q9 X+ o4 I7 _) s( _3 P% s! [
9 x& o1 j3 s" @: ?2 K
/ f+ W8 V. `; L* ], J' C- h. ~
再利用Image2Lcd 2.9(破解版)去提前图模; 1 t7 }' ]" k3 ]5 `! q) G
/ n& g6 Z T# F. [% T$ W1 m
1 i7 @6 O/ R9 ~) |: _
将取模代码变为2维数组,第一维度为帧数,第二维度为每帧图片的取模。 3 m+ B. U& A9 {3 X+ N1 X m2 J
% s! p: Q& d! D3 V4 I, d
- A+ }5 c! z4 l. b. v8 a1 ~) }
之后循环显示该GIF数组的每一帧,即可实现GIF动图显示。 5 t7 j. M8 w% q2 o
代码: - void showimage4(const unsigned char *p)
' w4 ]% e0 Y2 F1 { - {
2 V- m5 Z; O( _% C8 k: C+ X - int i; * s! S; l* f8 M- W' g8 ]' _
- unsigned char picH,picL;7 E; s5 [5 H( e2 a
- 1 h, f0 z* P1 K% t4 m8 a
- Address_set(180,146,228,195);
9 y" V: [/ q, Q4 M - for(i=0;i<49*50;i++); i) H K4 |, e$ ^- R9 a
- { ; H- C9 H) A. R' Z1 S! n
- picL=*(p+i*2); . k9 t% g6 ?0 r9 W' h9 n% A" O
- picH=*(p+i*2+1);
7 O7 d" t) ~5 L# P3 u - LCD_WR_DATA(picH<<8|picL); ' T& F) c; J3 e+ z. m
- } , P4 H: y9 u/ \: W8 O
- }
3 t! Z+ M. d! p, w - 4 ?1 | X! r( B, {
- # t5 S9 `6 c( q8 f; Q+ p6 X1 f
- for(int a=0;a<11;a++)" s2 R$ P9 m( Y: o" T5 s
- {
# U+ \8 h' X, c: A, n - showimage4(gImage_1[a]);
& l* k6 L- Z5 R, @7 K }9 w - }
复制代码9 r' B8 d% I8 c1 p9 _
ESP8266.h(AT控制): - #ifndef __ESP8266_H
* h' X. H. m5 c% _ - #define __ESP8266_H$ n% P& L( H% P4 ]: h9 t
- ( l2 M3 V) U( Y- W7 k3 q& h
- //#include "stdint.h", m% C) Q! Y% s5 P# q# `- G S
- ; ~7 _8 O% ]0 H* r% X! C+ c
- //uint8_t aRxBuffer_rx1; //接收中断缓冲0 j1 r; N y5 F3 X/ i6 Z) F" _
- //uint8_t aRxBuffer_rx3; //接收中断缓冲- Z7 m* W- ?7 |$ M. D. s0 Y
-
, A8 W" M. d2 R( a# e( i& f - //typedef struct {
a& ~7 {3 K* W, C- D h U$ m - // uint16_t size;
* L \/ l7 w1 p* s/ U# _1 ~8 P - // uint8_t buf[1022]; // 接收缓冲数组, w" c# F+ V! ~: d
- //} UART_RXDATA;
& C" D' O* V0 m" k Y! ^ - [5 ?' a( L# a0 `
- //UART_RXDATA g_uart1_rx;
1 p- X E D3 s' s - //UART_RXDATA g_uart3_rx;$ a1 d' \7 M2 i; x% a
- l5 q* ^/ e$ {% o; z
- //char Data_buff[1022];: ?% O6 G6 [% d- m8 X( n
- e- ~) M l8 }. z- v: E
- //char weather[10]; //存储天气 W% C2 B: B M3 k$ S3 j- t7 Q k
-
* m, ^, d3 p+ @& r - //uint8_t temperature[2]={0,0}; //储存最高气温和最低气温
# O1 J6 J$ d" x' d& C - //uint8_t temp = 0;7 p% t6 i0 l1 U
- 9 F/ W$ k! K3 `
- //需要连接的wifi账号和密码,需要修改,且WiFi频段不支持5GHz
+ G/ w: ^3 H9 l% \( F2 j/ ~ - #define WIFI_NAME "Wang"
* y9 E7 Y, Z5 E2 b; f7 r! x - #define WIFI_PSW "123456"
7 E2 `* t8 Y. a8 p$ `' u6 U! O% a -
% ^2 e- \+ O* D1 N$ W - 心知天气api,注意key=后面需要替换成自己账号的密钥) C% D1 A$ g* [9 j+ X3 }. N
- //char *get="GET https://api.seniverse.com/v3/weather/daily.json?key=SkV9zIBpwJAOixrJZ&location=chongqing&language=en&unit=c\r\n";# T" R! y1 O( N5 @
- 3 k w1 t O/ x2 m& P8 o
- //void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);! f/ r" V7 u8 i' J: [4 c: M
- void SendATCmd(char *cmd, int waitms);
5 o# W1 g0 \$ A" E4 b - void esp8266_config(void);; h+ N2 `0 v3 v8 D' p
- % ^0 Q2 N- K2 }( Q. O7 A6 E0 P
-
# N$ M. g# E' r; M* Z - #endif
复制代码
, b( ~/ X* l, `
ESP8266.h(AT控制): - #ifndef __ESP8266_H
% d5 |. n, t& C* ^+ ?/ }$ E - #define __ESP8266_H
" u0 K) g6 I* H( V - ; k! t' g6 d" o! I7 N4 t/ D
- //#include "stdint.h"
4 Z r! g- ~/ ]' C2 b - % v6 g: Y& D! ^0 \; H. R
- //uint8_t aRxBuffer_rx1; //接收中断缓冲- S$ M' S# p9 p! M3 ~# a
- //uint8_t aRxBuffer_rx3; //接收中断缓冲0 M, V- N; s: A$ i
-
5 B0 E3 F9 U1 `% Q7 D - //typedef struct {
. O" W# A" u% v, E - // uint16_t size;
. h/ R0 g `4 u: g" v% P* Y8 k) U* } - // uint8_t buf[1022]; // 接收缓冲数组
* T M) ?% r+ v6 c* q - //} UART_RXDATA;
u* _2 m6 |! c9 ]6 D3 n6 m - 8 h# A' q+ X, @- o1 l
- //UART_RXDATA g_uart1_rx;
! r: Q5 y- \/ y( j! t - //UART_RXDATA g_uart3_rx;
& L$ [. l2 R4 _" A8 H& ^# E -
4 ~( h, I. {; W* M8 s - //char Data_buff[1022];
9 ]) Q' l9 k& L - % ?+ x% O! o* z$ }) u- c0 e7 V7 T
- //char weather[10]; //存储天气. `( R" V; R0 \
-
1 w. u3 y0 p; s8 h, D: @7 C - //uint8_t temperature[2]={0,0}; //储存最高气温和最低气温. u6 C$ x/ ?* B0 _" l
- //uint8_t temp = 0;$ F2 O$ D. P8 p- e
- ; ]! H* G. k$ i0 L7 `
- //需要连接的wifi账号和密码,需要修改,且WiFi频段不支持5GHz( U5 \" i$ c3 Z; P i2 z/ }
- #define WIFI_NAME "Wang"
j* z! p* p: {' q - #define WIFI_PSW "123456"' W2 ^ T/ [! b1 j% y" s9 u+ @8 f
-
- X+ J0 I4 q" S N" C/ v - 心知天气api,注意key=后面需要替换成自己账号的密钥/ U; {/ r6 l* i$ d7 X
- //char *get="GET https://api.seniverse.com/v3/weather/daily.json?key=SkV9zIBpwJAOixrJZ&location=chongqing&language=en&unit=c\r\n";2 W9 K2 j% A, Z f2 ^& S; d
- 2 x! {, Y% Z2 [/ c- V
- //void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);; g+ |2 A# k: E& D& |4 \* U) T
- void SendATCmd(char *cmd, int waitms);
, f+ l) t7 F4 V" E) ] - void esp8266_config(void);! b1 M/ }* G' q. t( L9 n/ d5 m {5 S8 e
-
; V0 \6 d* M" x, M, r. I! p3 S6 ^ - - Y+ {# j3 _ v
- #endif
复制代码
/ p0 P+ E/ {: ?. N$ K. S
ESP8266.c: - #include "esp8266.h"
7 O' K5 j# W6 h, z, M- t7 V% G - #include "usart.h"
( g' D4 x% {' H5 w2 {6 V% F/ o - #include <stdio.h>" V2 o/ F( z1 p# G2 e' G; W
- #include <string.h>
h! x% i* R' Q5 V7 `0 f - #include <stdlib.h>( ^- a$ ]2 o: L
- #include "lcd.h"
$ n5 v; j' a$ q' d5 k( _! g; D - + p5 ?" @+ W9 o
- B+ n: M' C- I
- void SendATCmd(char *cmd, int waitms) 4 `! q$ m/ y+ d) m H9 j! Y
- { // 发送AT指令给串口38 H! T7 B' a- O6 ^
- if (NULL != cmd)
) r6 r# k" E6 ?0 w0 r! a6 ` - {& q1 a' G1 t3 ]# Y! f# F# \/ p
- HAL_UART_Transmit(&huart3, (uint8_t *)cmd, strlen(cmd), 0xFFFF); 7 T" S2 @: Q" w0 ~8 _
- if (waitms > 0)# l# }- {' }* U! f- ^
- HAL_Delay(waitms); // 延时等待ESP01模块应答时间9 l8 t, x; e, K! z8 x6 J' c- P1 G
- }5 u3 s( F3 \) o
- }! p5 V2 A! ]0 ?4 e9 M
-
1 W5 A8 s& d2 g7 n8 y; _/ Y - void esp8266_config(void)
, T/ n% B: `5 \; P/ O - {4 z9 K* m" L/ O0 ^! v
- char str[200];% Q1 c8 Z! b; Q( u1 K* P
- sprintf(str, "AT+CWJAP="%s","%s"\r\n", WIFI_NAME, WIFI_PSW);
; Y2 g. p( e$ Z: F% o - // SendATCmd("+++", 500); // 退出透传模式% E: v8 ]7 ]; S. u
- SendATCmd("AT\r\n", 2000); // 测试ESP01模块是否存在
6 J# N4 V/ v4 C& K, d" I - // SendATCmd("AT+GMR\r\n",3000); // 查看模块版本信息
9 f3 P% _ x" k3 i) U - SendATCmd("AT+CWMODE=1\r\n", 2000); // 开启STA+AP模式 ==================
$ \7 R; K7 {- V+ m0 j9 j2 r - SendATCmd("AT+RST\r\n", 3000); $ P4 V) x8 Q* F8 U
- SendATCmd(str, 10000); // 连接无线路由器或者手机热点,等待10秒 ============
. o- m- {- F& @+ j9 l. a4 o- W - SendATCmd("AT+CIPMUX=0\r\n", 2000); // 关闭多连接
6 ]4 o- s( a x0 q% B: ?7 A% C1 E - SendATCmd("AT+CIPSTART="TCP","api.seniverse.com",80\r\n", 2000); // 连接心知天气TCP服务器
1 X" ]$ p1 v$ W& d5 x3 A0 l - SendATCmd("AT+CIPMODE=1\r\n", 500); // 开启透传模式3 @2 p- g" u: A' v: P! a5 A) r
- SendATCmd("AT+CIPSEND\r\n", 500); // 开始透传0 W8 N" w; S; j: K z
- C* P) x X* V9 I& ^% B; J
- SendATCmd("GET https://api.seniverse.com/v3/weather/daily.json?key=SkV9zIBpwJAOixrJZ&location=zhenjiang&language=en&unit=c\r\n", 2000);
2 o) p" ]! p! s+ ` - }% }: r' |0 G0 \# I
-
复制代码0 R3 |, i1 K8 P& d8 {4 T
注意,key=后面尽量换成自己的密钥,location=后面也可以换成自己所在城市的字母。
, D _; \1 q9 O# \+ A8 A A
5.2.2 ESP8266信息解码
0 C, K+ ^2 b" l: ]
这部分作者取巧,使用了字符串对比和指针取值的操作。 strstr()函数: r4 i* B8 F& p$ B; ?, X' _$ F
: |8 g, G5 m w" i Q# ?; Z
{: {: I0 R1 J, C8 _
代码 - char *p;% [+ d% R8 X( G6 `8 _- b
- p = strstr(Data_buff,"text_day"); //查找天气
# T& V! h6 t8 `& O0 L - sscanf(p+11,"%[^"]",weather);
& @# z- c0 C4 T& I - // LCD_ShowString(40,80,(uint8_t*)weather); ( k y0 i) Z: z% ]) i) P" Q0 l
- p = strstr(Data_buff,"high"); //查找气温0 o5 X* G$ o/ R' N
- temperature[0]=atoi(p+7);
( c E R0 w, ^, E8 t% @ - p = strstr(Data_buff,"low");( z- W# J" o+ s
- temperature[1]=atoi(p+6);; u) ]5 q* Y% s" [% I
- // LCD_ShowxNum2(45,40,temperature[1],2,24,0);9 t5 L9 p2 C" v. R+ ?" a+ }3 e
- LCD_ShowxNum2(160,207,temperature[0],2,24,0);
1 }7 b! }/ f) [" D1 P, k( ^4 m' | - 3 \) e1 G) e1 W. z6 ~
- //温度
% T( K7 E% Q2 J2 E2 `& p - value = (temperature[1]+temperature[0])/2;$ B& d$ W; g" p0 M+ n* Q1 A
- LCD_ShowxNum2(52,160,value,2,24,0);
, n" G' n5 \5 B0 x ? - # E0 G6 U' E9 _7 ], ~
- //湿度
4 v$ r* u1 g, M - p = strstr(Data_buff,"humidity");; M. \. L0 i7 M+ @
- humidity=atoi(p+11);- |. D9 e7 G$ R/ ~$ R; q2 |% y
- LCD_ShowxNum2(132,160,humidity,2,24,0);
3 u$ z0 N ]1 Z1 Q0 G - LCD_ShowNew(161,160,'%',24,0);
& S- D# Z7 E4 I/ B) q/ W - 5 M/ H% k9 e4 b3 G4 r
- if((strstr(weather,"Overcast")) || (strstr(weather,"Mostly Cloudy")) || (strstr(weather,"Partly Cloudy")) || strstr(weather,"Cloudy"))
, K; J& h% V* ?2 U3 \ - {
7 P5 g& \ z. g P; H+ b - Overcast();: ^9 x2 ^1 k# J: Y& `6 [
- }6 P4 M' f. b/ \
- if((strstr(weather,"Sunny")) || (strstr(weather,"Clear")) || (strstr(weather,"Fair"))) //ÇçÌì
& z7 N( p8 e3 U& P' G5 B - {& d- q {- A' g
- Sunny();! [5 e& g+ a% _1 B0 Z9 E2 b. f
- }
: v) k! F* z# u% y h# m - if((strstr(weather,"Shower"))) ; }% S0 D% \* q: }
- {
) C, N5 X3 V+ b+ F- M7 G0 p+ d4 _% w5 @ - Shower();
% V, {+ y& p' X- ^ D1 \' T - }
6 c/ ~3 f$ ?8 O9 \& A+ b7 ^ c, \$ e5 [ - if((strstr(weather,"Thundershower")) || (strstr(weather,"Thundershower with Hail")))
& `$ I1 h/ F8 } V7 q( a - {
c8 i+ R' u: }" X9 z - Thundershower();
4 b0 V( M& e: `7 Q - }
5 ^4 q) u7 w# w" o! M/ v% B - if((strstr(weather,"Light rain")) || (strstr(weather,"Moderate Rain")))
/ y0 [& W5 t3 m - {
8 k0 q! F# a9 A5 Q( s) w+ N0 i - smallrain();
& I( }, A, Y0 ?3 {' ?- f2 G - }, `5 N( p* G: ~% }7 }% l) V9 D/ T
- if((strstr(weather,"Heavy Rain")) || (strstr(weather,"Storm")) || (strstr(weather,"Heavy Storm")) || (strstr(weather,"Severe Storm"))) ! ~- O# s% B0 A" p5 p3 t: g
- {% Q/ b. R' C. U. W# c8 `
- Bigrain();
$ I' U0 J' v8 r: p% R' D7 P - }
# z/ \# ]! h6 Y; u6 n - if((strstr(weather,"Ice Rain")) || (strstr(weather,"Sleet")) || (strstr(weather,"Snow Flurry")) || (strstr(weather,"Light Snow")) || (strstr(weather,"Moderate Snow")) || (strstr(weather,"Heavy Snow")) || (strstr(weather,"Snowstorm"))) : q5 W* B* Z* {
- {+ m; A# a* j. Z* b' E
- snow();4 `4 M" o) o; r* t/ f7 h2 w! Z% o
- }
复制代码
% i/ T4 Q1 e* p# W
5.3 RTC代码 8 l5 Q; z" E6 S( b% }4 J3 M6 v+ X
rtcdisplay.h: - #ifndef __RTCDISPLAY_H
5 W9 i$ J- y5 y - #define __RTCDISPLAY_H* q' _5 ]6 Y0 U4 O6 F9 L
-
3 c% C8 m+ x* D( R# v0 D - void RTC_display();
- y9 J& [5 G7 p7 q; D -
- l+ T/ E$ r7 u, ^. O - #endif
复制代码# O. @; ]$ \& P- [; O' a$ W( a
rtcdisplay.c: - #include "rtcdisplay.h": N: ~" V4 Y, K8 w! h* L# z
- #include "rtc.h"( q9 V; ^# m+ ~7 j0 V
- #include "lcd.h"
* E1 \1 B1 E/ i8 l+ A7 I9 W/ p - ( g7 D0 l9 M, C% Z" C
- RTC_DateTypeDef GetData; //获取日期结构体% U) w$ b6 i- K, k8 @
- RTC_TimeTypeDef GetTime; //获取时间结构体
( C9 |5 \7 Z2 I2 w6 A& n - : Q% }0 f m8 _& g6 z
- void RTC_display() //RTC DISPLAY
$ h1 \# j; W( ]0 s - {& P2 F( V+ v7 V9 ^
-
- g. X2 Z; H. H+ G) N- M - /* Get the RTC current Time *// H% ]5 b7 b$ ?* V& z& z6 g
- HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);* A( s y7 i6 a5 T& f4 n
- /* Get the RTC current Date */
k. Q+ d$ w& U o+ q; B) A3 K - HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
; \3 B8 j/ S/ J" B9 D. @4 W/ i# m - * s$ E2 H9 n* N6 ^7 P" W
- /* Display date Format : yy/mm/dd */
1 [ J$ m7 e* n k - // OLED_ShowNum(0,0,2000+GetData.Year,4,16); //year
4 v2 b# S1 j) O3 J* C - // OLED_ShowStr(35,30,".",2);( g, P2 C5 Z7 ~- K1 e; }' Z
- // OLED_ShowNum(45,0,GetData.Month,2,16); //month
% W Q+ j; b# |8 e2 J - // OLED_ShowStr(60,30,".",2);
; r% F4 H% V* q, p# K/ W* x - // OLED_ShowNum(70,0,GetData.Date,2,16); //date% ~5 Q- R# k# p0 n4 [' O
- 7 o1 M+ B. u; s( k5 w% A$ E( e
- /* Display time Format : hh:mm:ss */
5 o% X" r* }9 S' t1 [% ? - LCD_ShowxNum2(15,75,GetTime.Hours,2,60,0); //hour% R8 X! Z1 @2 v- N% |1 {6 S E
- // LCD_ShowNew(75,65,':',60,0);
; U8 ^; |% H( y+ h, O( |% G, G( y - LCD_ShowxNum2(105,75,GetTime.Minutes,2,60,0); //min6 F& D' \8 r% t! o, g6 X) Y9 `
- LCD_ShowxNum2(180,105,GetTime.Seconds,2,32,0); //seconds$ s1 `% l; U" F# J6 ~. I
-
5 N1 W* d- x6 w: O) q0 v7 \5 S - }
复制代码& N. J8 F% `" {& S9 S
这里RTC的时钟显示
' @5 _% @( A% R3 c, K0 l1 [+ t
转载自: 古月居 如有侵权请联系删除
, B8 a) D! \ u- m. A# i6 y / Z, w$ ~0 l3 o3 D2 d# g. ~
9 p7 ?, Z; C$ N* y; A7 H$ k" G
|