你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32作为MCU的太空人WiFi天气时钟

[复制链接]
攻城狮Melo 发布时间:2023-3-26 13:30
前言
  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
微信图片_20230326132657.png 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
微信图片_20230326132701.png
# N. N+ Q( D1 w& \
; _5 c9 ]) H! t9 q6 f+ ~2 m& L
引脚连接:
  1. $ M, B2 `, \& s
  2. LCD显示引脚:5 N. ?$ [, p" ]

  3. * M, J8 d) z2 w7 z6 V, L: e. G
  4. VCC --> 3.3V
    + }  G2 b$ q- n8 f  S8 b

  5. ; V( w; P+ H1 l" p0 y: @2 P+ P
  6. GND --> GND
    6 M1 a- N' p, e
  7. 8 F8 n# C0 y1 u, v' A' P4 n( O
  8. CLK --> PA54 ?5 t3 `3 |  ^
  9. ) X* t7 ?& C/ ?  i- `3 |. q
  10. DIN --> PA7
    7 p1 ~8 X$ k0 u7 h4 v

  11. # O; R. j, i. V8 M1 H1 c/ E
  12. RES --> PB0
    ; Y; X0 ^+ ]! Q3 p; M0 s
  13. # L; i3 y) E& r* O8 J
  14. DC --> PB1
    0 a; B1 S8 Y# J  ?% w$ F

  15. 2 G# f' e  ~, a
  16. CS --> PA4
复制代码
  1. ESP8266模块引脚:6 P  M2 U2 e: j$ u% c0 ]+ S
  2. ) C; k/ }: Z6 k$ A1 V" Z4 `
  3. VCC --> 3.3V5 W1 J+ ]$ a! C6 y0 t0 U1 |
  4. 6 k3 l# x/ T9 p+ l
  5. GND --> GND" w( h* {) L' G* ?  T4 N

  6. 5 ?7 p7 l: ?; {. P& W& n4 C, W
  7. RX--> PB10
    ' g/ A4 b0 m# f8 A- f7 g" Q" {
  8. ! G/ |3 S3 _3 t
  9. TX --> PB11
    ( V! O' S# l4 h4 V) b
  10. " ^" B/ |" n9 L& G
  11. RST --> PB9
    * _& q  T+ G" b0 u2 H* L" j/ v; v

  12. 4 `5 R* @/ J9 C3 ^# c; g
  13. 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
微信图片_20230326132705.png , P) T! n3 e$ t) r
8 H& [4 D' R' h, ?  R

5 s. ^/ d; ~- s1 K. l
1.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
微信图片_20230326132719.png
% q3 \- t: K+ t4 S2 t 微信图片_20230326132724.png ) 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 G
AT 是最常用的指令,用于测试模块能否正常接受指令。在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
微信图片_20230326132729.png
# 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配置代码如下:
  1. void esp8266_config(void)& I4 r) Q5 |; h% A
  2. {& r. j: p5 K/ l% r& l
  3.     char str[200];
    1 ], x8 H6 T+ Z# `" B# D8 Q
  4.     sprintf(str, "AT+CWJAP="%s","%s"\r\n", WIFI_NAME, WIFI_PSW);
    ; m, A% w& j, _+ Q4 p1 O, V
  5. //    SendATCmd("+++", 500);        // 退出透传模式+ u3 U! T2 O& A4 [. w6 M3 F
  6.     SendATCmd("AT\r\n", 2000);      // 测试ESP01模块是否存在- `& g+ M! x3 G1 y
  7. //    SendATCmd("AT+GMR\r\n",3000);  // 查看模块版本信息
    ! K% a! Z5 ^0 h+ G3 K
  8.     SendATCmd("AT+CWMODE=1\r\n", 2000);  // 开启STA+AP模式 ==================    + }- e* n- ~& ~/ n( P/ A( x
  9.     SendATCmd("AT+RST\r\n", 3000);    c3 {* c% P  f: `
  10.     SendATCmd(str, 10000);  // 连接无线路由器或者手机热点,等待10秒 ============( S6 ^' d5 E4 C: h
  11.     SendATCmd("AT+CIPMUX=0\r\n", 2000);  // 关闭多连接- g1 o9 Y. w8 R% J$ Y" ^4 k+ W* P
  12.     SendATCmd("AT+CIPSTART="TCP","api.seniverse.com",80\r\n", 2000);  // 连接心知  天气TCP服务器
    . |- I# E& `5 s8 F) l* \
  13.     SendATCmd("AT+CIPMODE=1\r\n", 500);  // 开启透传模式" Z0 c/ j/ |2 a8 n/ ^$ `
  14.     SendATCmd("AT+CIPSEND\r\n", 500);    // 开始透传
    - y: ~7 w; }$ J3 v& G) Z4 P6 Z
  15. }
复制代码

' 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
微信图片_20230326132734.png
- U6 j" N$ S( W8 X' j& I0 M, C

$ i$ n6 v, H4 f
点击“立即免费试用”
% K9 u' Y; i+ k; K: x3 Z
微信图片_20230326132740.png
+ `8 }/ k4 o' z" H

7 T8 I  U) q. o! T- I5 G4 @
点击免费版的“免费申请”
( n1 _: D$ j" ^  a, C0 P
微信图片_20230326132745.png
9 a/ u$ W, e+ ^

' h% k4 F  x- R8 f+ c
申请后可查看到自己的私钥(自行保存后面需要用到)

* ?/ T/ t- z$ d4 O$ y4 |: |  Z
微信图片_20230326132748.png / 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
微信图片_20230326132751.png
3 L- D$ E1 v( q4 \& c

4 s, S! U# m2 ]" K9 |& @
点击"天气实况",打开对应的API接口文档

( c, d3 u7 o& l7 Y4 I" A# e( H
微信图片_20230326132755.png
( U( g- B. A% a1 L( [

& |8 K: i4 j5 ]: p
查看天气实况的接口地址,以及返回的数据结果示例(自行保存后面需要用到)

# }  r* S! E9 G" k( s, b' S) r
微信图片_20230326132808.png * 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
微信图片_20230326132812.png
$ 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 ?
微信图片_20230326132822.png
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
微信图片_20230326132827.png
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
微信图片_20230326132833.png
. 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
微信图片_20230326132838.png
4 ?, i& O, l: m7 y

6 s+ M+ k; _. S3 c& p
UART1和UART3配置:MCU分别与电脑和ESP8266通讯(记得开启串口通信中断)

1 ~9 T. j. j& J' T
微信图片_20230326132843.png
% T' j# t2 ?9 h6 s$ i# [) z
5 ]  b5 l! _+ Q  D$ r% i8 x
微信图片_20230326132848.png ! v5 Y3 c. y' E4 p, |
- I0 K4 }- {" }( h2 y
微信图片_20230326132851.png
  V, c) {9 Z- h( F

1 y5 G$ ]0 B0 c; K. b0 q
微信图片_20230326132854.png " c8 U9 b8 ^( f

# I; |8 T9 T# `$ |
时钟树配置

, `7 s# n5 Y: c  {/ g
微信图片_20230326132858.png
; M! t! {/ f. e! U$ R+ x3 ?
8 L  c7 P: C- B& h  ~% C
工程配置

, Q- J3 r: e; R
微信图片_20230326132902.png : 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
微信图片_20230326132907.png 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! [
微信图片_20230326132911.png
9 x& o1 j3 s" @: ?2 K

/ f+ W8 V. `; L* ], J' C- h. ~
再利用Image2Lcd 2.9(破解版)去提前图模;
1 t7 }' ]" k3 ]5 `! q) G
微信图片_20230326132915.png / 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
微信图片_20230326132918.png % 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
代码:
  1. void showimage4(const unsigned char *p)
    ' w4 ]% e0 Y2 F1 {
  2. {
    2 V- m5 Z; O( _% C8 k: C+ X
  3.   int i; * s! S; l* f8 M- W' g8 ]' _
  4.   unsigned char picH,picL;7 E; s5 [5 H( e2 a
  5. 1 h, f0 z* P1 K% t4 m8 a
  6.   Address_set(180,146,228,195);
    9 y" V: [/ q, Q4 M
  7.   for(i=0;i<49*50;i++); i) H  K4 |, e$ ^- R9 a
  8.     {  ; H- C9 H) A. R' Z1 S! n
  9.       picL=*(p+i*2);  . k9 t% g6 ?0 r9 W' h9 n% A" O
  10.       picH=*(p+i*2+1);        
    7 O7 d" t) ~5 L# P3 u
  11.       LCD_WR_DATA(picH<<8|picL);         ' T& F) c; J3 e+ z. m
  12.     }  , P4 H: y9 u/ \: W8 O
  13. }
    3 t! Z+ M. d! p, w
  14. 4 ?1 |  X! r( B, {
  15. # t5 S9 `6 c( q8 f; Q+ p6 X1 f
  16. for(int a=0;a<11;a++)" s2 R$ P9 m( Y: o" T5 s
  17. {
    # U+ \8 h' X, c: A, n
  18.     showimage4(gImage_1[a]);
    & l* k6 L- Z5 R, @7 K  }9 w
  19. }
复制代码
9 r' B8 d% I8 c1 p9 _
ESP8266.h(AT控制):
  1. #ifndef __ESP8266_H
    * h' X. H. m5 c% _
  2. #define __ESP8266_H$ n% P& L( H% P4 ]: h9 t
  3. ( l2 M3 V) U( Y- W7 k3 q& h
  4. //#include "stdint.h", m% C) Q! Y% s5 P# q# `- G  S
  5. ; ~7 _8 O% ]0 H* r% X! C+ c
  6. //uint8_t aRxBuffer_rx1;      //接收中断缓冲0 j1 r; N  y5 F3 X/ i6 Z) F" _
  7. //uint8_t aRxBuffer_rx3;      //接收中断缓冲- Z7 m* W- ?7 |$ M. D. s0 Y

  8. , A8 W" M. d2 R( a# e( i& f
  9. //typedef struct {
      a& ~7 {3 K* W, C- D  h  U$ m
  10. //  uint16_t size;
    * L  \/ l7 w1 p* s/ U# _1 ~8 P
  11. //  uint8_t buf[1022]; // 接收缓冲数组, w" c# F+ V! ~: d
  12. //} UART_RXDATA;
    & C" D' O* V0 m" k  Y! ^
  13.   [5 ?' a( L# a0 `
  14. //UART_RXDATA g_uart1_rx;
    1 p- X  E  D3 s' s
  15. //UART_RXDATA g_uart3_rx;$ a1 d' \7 M2 i; x% a
  16.   l5 q* ^/ e$ {% o; z
  17. //char Data_buff[1022];: ?% O6 G6 [% d- m8 X( n
  18.   e- ~) M  l8 }. z- v: E
  19. //char weather[10];        //存储天气  W% C2 B: B  M3 k$ S3 j- t7 Q  k

  20. * m, ^, d3 p+ @& r
  21. //uint8_t temperature[2]={0,0};     //储存最高气温和最低气温
    # O1 J6 J$ d" x' d& C
  22. //uint8_t temp = 0;7 p% t6 i0 l1 U
  23. 9 F/ W$ k! K3 `
  24. //需要连接的wifi账号和密码,需要修改,且WiFi频段不支持5GHz
    + G/ w: ^3 H9 l% \( F2 j/ ~
  25. #define WIFI_NAME "Wang"
    * y9 E7 Y, Z5 E2 b; f7 r! x
  26. #define WIFI_PSW    "123456"
    7 E2 `* t8 Y. a8 p$ `' u6 U! O% a

  27. % ^2 e- \+ O* D1 N$ W
  28. 心知天气api,注意key=后面需要替换成自己账号的密钥) C% D1 A$ g* [9 j+ X3 }. N
  29. //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 @
  30. 3 k  w1 t  O/ x2 m& P8 o
  31. //void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);! f/ r" V7 u8 i' J: [4 c: M
  32. void SendATCmd(char *cmd, int waitms);
    5 o# W1 g0 \$ A" E4 b
  33. void esp8266_config(void);; h+ N2 `0 v3 v8 D' p
  34. % ^0 Q2 N- K2 }( Q. O7 A6 E0 P

  35. # N$ M. g# E' r; M* Z
  36. #endif
复制代码

, b( ~/ X* l, `
ESP8266.h(AT控制):
  1. #ifndef __ESP8266_H
    % d5 |. n, t& C* ^+ ?/ }$ E
  2. #define __ESP8266_H
    " u0 K) g6 I* H( V
  3. ; k! t' g6 d" o! I7 N4 t/ D
  4. //#include "stdint.h"
    4 Z  r! g- ~/ ]' C2 b
  5. % v6 g: Y& D! ^0 \; H. R
  6. //uint8_t aRxBuffer_rx1;      //接收中断缓冲- S$ M' S# p9 p! M3 ~# a
  7. //uint8_t aRxBuffer_rx3;      //接收中断缓冲0 M, V- N; s: A$ i

  8. 5 B0 E3 F9 U1 `% Q7 D
  9. //typedef struct {
    . O" W# A" u% v, E
  10. //  uint16_t size;
    . h/ R0 g  `4 u: g" v% P* Y8 k) U* }
  11. //  uint8_t buf[1022]; // 接收缓冲数组
    * T  M) ?% r+ v6 c* q
  12. //} UART_RXDATA;
      u* _2 m6 |! c9 ]6 D3 n6 m
  13. 8 h# A' q+ X, @- o1 l
  14. //UART_RXDATA g_uart1_rx;
    ! r: Q5 y- \/ y( j! t
  15. //UART_RXDATA g_uart3_rx;
    & L$ [. l2 R4 _" A8 H& ^# E

  16. 4 ~( h, I. {; W* M8 s
  17. //char Data_buff[1022];
    9 ]) Q' l9 k& L
  18. % ?+ x% O! o* z$ }) u- c0 e7 V7 T
  19. //char weather[10];        //存储天气. `( R" V; R0 \

  20. 1 w. u3 y0 p; s8 h, D: @7 C
  21. //uint8_t temperature[2]={0,0};     //储存最高气温和最低气温. u6 C$ x/ ?* B0 _" l
  22. //uint8_t temp = 0;$ F2 O$ D. P8 p- e
  23. ; ]! H* G. k$ i0 L7 `
  24. //需要连接的wifi账号和密码,需要修改,且WiFi频段不支持5GHz( U5 \" i$ c3 Z; P  i2 z/ }
  25. #define WIFI_NAME "Wang"
      j* z! p* p: {' q
  26. #define WIFI_PSW    "123456"' W2 ^  T/ [! b1 j% y" s9 u+ @8 f

  27. - X+ J0 I4 q" S  N" C/ v
  28. 心知天气api,注意key=后面需要替换成自己账号的密钥/ U; {/ r6 l* i$ d7 X
  29. //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
  30. 2 x! {, Y% Z2 [/ c- V
  31. //void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);; g+ |2 A# k: E& D& |4 \* U) T
  32. void SendATCmd(char *cmd, int waitms);
    , f+ l) t7 F4 V" E) ]
  33. void esp8266_config(void);! b1 M/ }* G' q. t( L9 n/ d5 m  {5 S8 e

  34. ; V0 \6 d* M" x, M, r. I! p3 S6 ^
  35. - Y+ {# j3 _  v
  36. #endif
复制代码

/ p0 P+ E/ {: ?. N$ K. S
ESP8266.c:
  1. #include "esp8266.h"
    7 O' K5 j# W6 h, z, M- t7 V% G
  2. #include "usart.h"
    ( g' D4 x% {' H5 w2 {6 V% F/ o
  3. #include <stdio.h>" V2 o/ F( z1 p# G2 e' G; W
  4. #include <string.h>
      h! x% i* R' Q5 V7 `0 f
  5. #include <stdlib.h>( ^- a$ ]2 o: L
  6. #include "lcd.h"
    $ n5 v; j' a$ q' d5 k( _! g; D
  7. + p5 ?" @+ W9 o
  8.   B+ n: M' C- I
  9. void SendATCmd(char *cmd, int waitms) 4 `! q$ m/ y+ d) m  H9 j! Y
  10. { // 发送AT指令给串口38 H! T7 B' a- O6 ^
  11.   if (NULL != cmd)
    ) r6 r# k" E6 ?0 w0 r! a6 `
  12.   {& q1 a' G1 t3 ]# Y! f# F# \/ p
  13.     HAL_UART_Transmit(&huart3, (uint8_t *)cmd, strlen(cmd), 0xFFFF);  7 T" S2 @: Q" w0 ~8 _
  14.     if (waitms > 0)# l# }- {' }* U! f- ^
  15.       HAL_Delay(waitms);  // 延时等待ESP01模块应答时间9 l8 t, x; e, K! z8 x6 J' c- P1 G
  16.   }5 u3 s( F3 \) o
  17. }! p5 V2 A! ]0 ?4 e9 M

  18. 1 W5 A8 s& d2 g7 n8 y; _/ Y
  19. void esp8266_config(void)
    , T/ n% B: `5 \; P/ O
  20. {4 z9 K* m" L/ O0 ^! v
  21.     char str[200];% Q1 c8 Z! b; Q( u1 K* P
  22.     sprintf(str, "AT+CWJAP="%s","%s"\r\n", WIFI_NAME, WIFI_PSW);
    ; Y2 g. p( e$ Z: F% o
  23. //    SendATCmd("+++", 500);        // 退出透传模式% E: v8 ]7 ]; S. u
  24.     SendATCmd("AT\r\n", 2000);      // 测试ESP01模块是否存在
    6 J# N4 V/ v4 C& K, d" I
  25. //    SendATCmd("AT+GMR\r\n",3000);  // 查看模块版本信息
    9 f3 P% _  x" k3 i) U
  26.     SendATCmd("AT+CWMODE=1\r\n", 2000);  // 开启STA+AP模式 ==================   
    $ \7 R; K7 {- V+ m0 j9 j2 r
  27.     SendATCmd("AT+RST\r\n", 3000);  $ P4 V) x8 Q* F8 U
  28.     SendATCmd(str, 10000);  // 连接无线路由器或者手机热点,等待10秒 ============
    . o- m- {- F& @+ j9 l. a4 o- W
  29.     SendATCmd("AT+CIPMUX=0\r\n", 2000);  // 关闭多连接
    6 ]4 o- s( a  x0 q% B: ?7 A% C1 E
  30.     SendATCmd("AT+CIPSTART="TCP","api.seniverse.com",80\r\n", 2000);  // 连接心知天气TCP服务器
    1 X" ]$ p1 v$ W& d5 x3 A0 l
  31.     SendATCmd("AT+CIPMODE=1\r\n", 500);  // 开启透传模式3 @2 p- g" u: A' v: P! a5 A) r
  32.     SendATCmd("AT+CIPSEND\r\n", 500);    // 开始透传0 W8 N" w; S; j: K  z
  33.     C* P) x  X* V9 I& ^% B; J
  34.     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+ `
  35. }% }: 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
微信图片_20230326132934.png
: |8 g, G5 m  w" i  Q# ?; Z

  {: {: I0 R1 J, C8 _
代码
  1.       char *p;% [+ d% R8 X( G6 `8 _- b
  2.       p =  strstr(Data_buff,"text_day");        //查找天气   
    # T& V! h6 t8 `& O0 L
  3.       sscanf(p+11,"%[^"]",weather);  
    & @# z- c0 C4 T& I
  4. //      LCD_ShowString(40,80,(uint8_t*)weather);      ( k  y0 i) Z: z% ]) i) P" Q0 l
  5.       p = strstr(Data_buff,"high");                //查找气温0 o5 X* G$ o/ R' N
  6.       temperature[0]=atoi(p+7);
    ( c  E  R0 w, ^, E8 t% @
  7.       p = strstr(Data_buff,"low");( z- W# J" o+ s
  8.       temperature[1]=atoi(p+6);; u) ]5 q* Y% s" [% I
  9. //      LCD_ShowxNum2(45,40,temperature[1],2,24,0);9 t5 L9 p2 C" v. R+ ?" a+ }3 e
  10.       LCD_ShowxNum2(160,207,temperature[0],2,24,0);
    1 }7 b! }/ f) [" D1 P, k( ^4 m' |
  11. 3 \) e1 G) e1 W. z6 ~
  12.       //温度
    % T( K7 E% Q2 J2 E2 `& p
  13.       value = (temperature[1]+temperature[0])/2;$ B& d$ W; g" p0 M+ n* Q1 A
  14.       LCD_ShowxNum2(52,160,value,2,24,0);      
    , n" G' n5 \5 B0 x  ?
  15. # E0 G6 U' E9 _7 ], ~
  16.       //湿度
    4 v$ r* u1 g, M
  17.       p = strstr(Data_buff,"humidity");; M. \. L0 i7 M+ @
  18.       humidity=atoi(p+11);- |. D9 e7 G$ R/ ~$ R; q2 |% y
  19.       LCD_ShowxNum2(132,160,humidity,2,24,0);
    3 u$ z0 N  ]1 Z1 Q0 G
  20.       LCD_ShowNew(161,160,'%',24,0);
    & S- D# Z7 E4 I/ B) q/ W
  21.       5 M/ H% k9 e4 b3 G4 r
  22.       if((strstr(weather,"Overcast")) || (strstr(weather,"Mostly Cloudy")) || (strstr(weather,"Partly Cloudy")) || strstr(weather,"Cloudy"))  
    , K; J& h% V* ?2 U3 \
  23.       {
    7 P5 g& \  z. g  P; H+ b
  24.         Overcast();: ^9 x2 ^1 k# J: Y& `6 [
  25.       }6 P4 M' f. b/ \
  26.       if((strstr(weather,"Sunny")) || (strstr(weather,"Clear")) || (strstr(weather,"Fair")))      //ÇçÌì
    & z7 N( p8 e3 U& P' G5 B
  27.       {& d- q  {- A' g
  28.         Sunny();! [5 e& g+ a% _1 B0 Z9 E2 b. f
  29.       }
    : v) k! F* z# u% y  h# m
  30.       if((strstr(weather,"Shower")))        ; }% S0 D% \* q: }
  31.       {
    ) C, N5 X3 V+ b+ F- M7 G0 p+ d4 _% w5 @
  32.         Shower();
    % V, {+ y& p' X- ^  D1 \' T
  33.       }
    6 c/ ~3 f$ ?8 O9 \& A+ b7 ^  c, \$ e5 [
  34.       if((strstr(weather,"Thundershower")) || (strstr(weather,"Thundershower with Hail")))   
    & `$ I1 h/ F8 }  V7 q( a
  35.       {
      c8 i+ R' u: }" X9 z
  36.         Thundershower();
    4 b0 V( M& e: `7 Q
  37.       }
    5 ^4 q) u7 w# w" o! M/ v% B
  38.       if((strstr(weather,"Light rain")) || (strstr(weather,"Moderate Rain")))      
    / y0 [& W5 t3 m
  39.       {
    8 k0 q! F# a9 A5 Q( s) w+ N0 i
  40.         smallrain();
    & I( }, A, Y0 ?3 {' ?- f2 G
  41.       }, `5 N( p* G: ~% }7 }% l) V9 D/ T
  42.       if((strstr(weather,"Heavy Rain")) || (strstr(weather,"Storm")) || (strstr(weather,"Heavy Storm")) || (strstr(weather,"Severe Storm")))    ! ~- O# s% B0 A" p5 p3 t: g
  43.       {% Q/ b. R' C. U. W# c8 `
  44.         Bigrain();
    $ I' U0 J' v8 r: p% R' D7 P
  45.       }
    # z/ \# ]! h6 Y; u6 n
  46.       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* {
  47.       {+ m; A# a* j. Z* b' E
  48.         snow();4 `4 M" o) o; r* t/ f7 h2 w! Z% o
  49.       }
复制代码

% i/ T4 Q1 e* p# W
5.3 RTC代码
8 l5 Q; z" E6 S( b% }4 J3 M6 v+ X
rtcdisplay.h:
  1. #ifndef __RTCDISPLAY_H
    5 W9 i$ J- y5 y
  2. #define __RTCDISPLAY_H* q' _5 ]6 Y0 U4 O6 F9 L

  3. 3 c% C8 m+ x* D( R# v0 D
  4. void RTC_display();
    - y9 J& [5 G7 p7 q; D

  5. - l+ T/ E$ r7 u, ^. O
  6. #endif
复制代码
# O. @; ]$ \& P- [; O' a$ W( a
rtcdisplay.c:
  1. #include "rtcdisplay.h": N: ~" V4 Y, K8 w! h* L# z
  2. #include "rtc.h"( q9 V; ^# m+ ~7 j0 V
  3. #include "lcd.h"
    * E1 \1 B1 E/ i8 l+ A7 I9 W/ p
  4. ( g7 D0 l9 M, C% Z" C
  5. RTC_DateTypeDef GetData;  //获取日期结构体% U) w$ b6 i- K, k8 @
  6. RTC_TimeTypeDef GetTime;   //获取时间结构体
    ( C9 |5 \7 Z2 I2 w6 A& n
  7. : Q% }0 f  m8 _& g6 z
  8. void RTC_display()        //RTC DISPLAY
    $ h1 \# j; W( ]0 s
  9. {& P2 F( V+ v7 V9 ^
  10.   
    - g. X2 Z; H. H+ G) N- M
  11.     /* Get the RTC current Time *// H% ]5 b7 b$ ?* V& z& z6 g
  12.     HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN);* A( s  y7 i6 a5 T& f4 n
  13.     /* Get the RTC current Date */
      k. Q+ d$ w& U  o+ q; B) A3 K
  14.     HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN);
    ; \3 B8 j/ S/ J" B9 D. @4 W/ i# m
  15.   * s$ E2 H9 n* N6 ^7 P" W
  16.     /* Display date Format : yy/mm/dd */
    1 [  J$ m7 e* n  k
  17. //    OLED_ShowNum(0,0,2000+GetData.Year,4,16);      //year
    4 v2 b# S1 j) O3 J* C
  18. //    OLED_ShowStr(35,30,".",2);( g, P2 C5 Z7 ~- K1 e; }' Z
  19. //    OLED_ShowNum(45,0,GetData.Month,2,16);        //month
    % W  Q+ j; b# |8 e2 J
  20. //    OLED_ShowStr(60,30,".",2);
    ; r% F4 H% V* q, p# K/ W* x
  21. //    OLED_ShowNum(70,0,GetData.Date,2,16);          //date% ~5 Q- R# k# p0 n4 [' O
  22.   7 o1 M+ B. u; s( k5 w% A$ E( e
  23.     /* Display time Format : hh:mm:ss */
    5 o% X" r* }9 S' t1 [% ?
  24.     LCD_ShowxNum2(15,75,GetTime.Hours,2,60,0);        //hour% R8 X! Z1 @2 v- N% |1 {6 S  E
  25. //    LCD_ShowNew(75,65,':',60,0);
    ; U8 ^; |% H( y+ h, O( |% G, G( y
  26.     LCD_ShowxNum2(105,75,GetTime.Minutes,2,60,0);      //min6 F& D' \8 r% t! o, g6 X) Y9 `
  27.     LCD_ShowxNum2(180,105,GetTime.Seconds,2,32,0);      //seconds$ s1 `% l; U" F# J6 ~. I

  28. 5 N1 W* d- x6 w: O) q0 v7 \5 S
  29. }
复制代码
& 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
收藏 评论0 发布时间:2023-3-26 13:30

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版