本文介绍了如何使用ESP8266和STM32微控制器,结合OLED显示屏,通过心知天气API获取数据,并利用MQTT协议和CJSON库解析天气信息,实现实时天气显示的功能。项目涵盖了硬件准备、数据解析和通信过程。
当前文章介绍如何使用ESP8266和STM32微控制器,搭配OLED显示屏,制作一个能够实时显示天气预报的智能设备。将使用心知天气API来获取天气数据,并使用MQTT协议将数据传递给STM32控制器,最终在OLED显示屏上显示。
心知天气是一家专业的气象数据服务提供商,致力于为全球用户提供高质量、定制化的气象数据服务。其主要产品包括天气API、空气质量API、灾害预警API等。用户可以通过心知天气的API接口,获取准确、实时的天气数据,从而为各种应用场景提供支持,例如智能家居、出行、电商等。心知天气的数据覆盖全球200多个国家和地区,每日处理超过10亿次API请求,是业内领先的气象数据服务提供商之一。
https://www.seniverse.com/
ESP8266是一款WiFi模块,它具有强大的网络连接功能,可以轻松地连接到互联网。将使用ESP8266模块来获取天气数据,并将其发送给STM32控制器。具体来说,我们将使用正点原子ATK-ESP8266模块,这是一款集成ESP8266芯片的小板子。
STM32是一款强大的32位微控制器,具有多种接口和功能。将使用STM32F103C8T6控制器,这是一款非常流行的型号,易于获得且价格较为合理。
OLED是一种非常流行的显示技术,具有高对比度、低功耗、快速响应等优点。将使用0.96英寸128x64像素的OLED显示屏。
- {
- "results": [
- {
- "location": {
- "id": "WTEMH46Z5N09",
- "name": "合肥",
- "country": "CN",
- "path": "合肥,合肥,安徽,中国",
- "timezone": "Asia/Shanghai",
- "timezone_offset": "+08:00"
- },
- "now": {
- "text": "阴",
- "code": "9",
- "temperature": "12",
- "feels_like": "18",
- "pressure": "1000",
- "humidity": "89",
- "visibility": "12.0",
- "wind_direction": "西南",
- "wind_direction_degree": "245",
- "wind_speed": "19.0",
- "wind_scale": "3",
- "clouds": "85",
- "dew_point": ""
- },
- "last_update": "2023-04-04T14:20:13+08:00"
- }
- ]
- }
-
CJSON是一款轻量级的C语言JSON解析器,其全称是“cJSON”,由Dave Gamble编写。它简单易用,可嵌入到C应用程序中,既支持JSON字符串的解析,也支持JSON对象的创建及操作。CJSON不依赖于任何其他的库或组件,使用它只需要引入其头文件即可。
CJSON的使用方式相对来说比较简单,需要进行以下几个步骤:
- 1. 在应用程序中包含cJSON的头文件:#include "cJSON.h"。
- 2. 调用cJSON_Parse函数,将JSON字符串转换为CJSON对象。
- 3. 使用cJSON提供的API函数对CJSON对象进行操作,包括读取、修改、删除、添加等。
- 4. 在程序结束时,记得释放cJSON对象的内存空间,避免内存泄漏。
-
CJSON的解析速度相对较快,占用的内存开销也比较小,因此非常适用于资源有限的嵌入式系统中使用。
使用CJSON解析上述JSON数据非常简单,只需要按照以下步骤操作:
- #include <cJSON.h>
-
- char* json_data = "{\"results\":[{\"location\":{\"id\":\"WTEMH46Z5N09\",\"name\":\"合肥\",\"country\":\"CN\",\"path\":\"合肥,合肥,安徽,中国\",\"timezone\":\"Asia/Shanghai\",\"timezone_offset\":\"+08:00\"},\"now\":{\"text\":\"阴\",\"code\":\"9\",\"temperature\":\"12\",\"feels_like\":\"18\",\"pressure\":\"1000\",\"humidity\":\"89\",\"visibility\":\"12.0\",\"wind_direction\":\"西南\",\"wind_direction_degree\":\"245\",\"wind_speed\":\"19.0\",\"wind_scale\":\"3\",\"clouds\":\"85\",\"dew_point\":\"\"},\"last_update\":\"2023-04-04T14:20:13+08:00\"}]}";
- cJSON* root = cJSON_Parse(json_data);
-
在这个代码片段中,我们首先定义了一个字符串类型的变量json_data,用于存储上述JSON数据。然后,我们调用cJSON_Parse()函数来解析JSON数据,并将解析结果保存在root指针所指向的cJSON对象中。
- cJSON* location = cJSON_GetObjectItem(root, "location");
- char* city = cJSON_GetObjectItem(location, "name")->valuestring;
- cJSON* now = cJSON_GetObjectItem(root, "now");
- int temperature = cJSON_GetObjectItem(now, "temperature")->valueint;
- char* text = cJSON_GetObjectItem(now, "text")->valuestring;
-
在这个代码片段中,我们使用cJSON_GetObjectItem()函数从root指针所指向的cJSON对象中提取一个名为location的JSON对象,并从该JSON对象中获取名为name的字符串类型变量。类似地,我们也可以从root指针所指向的cJSON对象中提取名为now的JSON对象,并从该JSON对象中获取名为temperature和text的整型和字符串类型变量。
- cJSON_Delete(root);
-
最后,我们需要释放之前创建的cJSON对象,以释放内存空间。
完整的代码示例如下:
- #include <cJSON.h>
- #include <stdio.h>
-
- int main() {
- char* json_data = "{\"results\":[{\"location\":{\"id\":\"WTEMH46Z5N09\",\"name\":\"合肥\",\"country\":\"CN\",\"path\":\"合肥,合肥,安徽,中国\",\"timezone\":\"Asia/Shanghai\",\"timezone_offset\":\"+08:00\"},\"now\":{\"text\":\"阴\",\"code\":\"9\",\"temperature\":\"12\",\"feels_like\":\"18\",\"pressure\":\"1000\",\"humidity\":\"89\",\"visibility\":\"12.0\",\"wind_direction\":\"西南\",\"wind_direction_degree\":\"245\",\"wind_speed\":\"19.0\",\"wind_scale\":\"3\",\"clouds\":\"85\",\"dew_point\":\"\"},\"last_update\":\"2023-04-04T14:20:13+08:00\"}]}";
- cJSON* root = cJSON_Parse(json_data);
-
- cJSON* location = cJSON_GetObjectItem(root, "location");
- char* city = cJSON_GetObjectItem(location, "name")->valuestring;
- cJSON* now = cJSON_GetObjectItem(root, "now");
- int temperature = cJSON_GetObjectItem(now, "temperature")->valueint;
- char* text = cJSON_GetObjectItem(now, "text")->valuestring;
-
- printf("City: %s\n", city);
- printf("Temperature: %d\n", temperature);
- printf("Weather: %s\n", text);
-
- cJSON_Delete(root);
-
- return 0;
- }
-
在这个代码示例中,使用了cJSON_Parse()、cJSON_GetObjectItem()、cJSON_Delete()等函数来解析和处理JSON数据。
下面是ESP8266访问HTTP接口请求的代码:
- #include <SoftwareSerial.h>
-
- // 定义ESP8266串口对象
- SoftwareSerial esp8266(PA10, PA9); // RX, TX
-
- void setup() {
- Serial.begin(9600);
-
- // 初始化ESP8266串口通信波特率为9600
- esp8266.begin(9600);
-
- // 发送AT指令测试ESP8266是否正常工作
- esp8266.println("AT");
- delay(500);
- if (esp8266.find("OK")) {
- Serial.println("ESP8266 is working properly.");
- } else {
- Serial.println("ESP8266 is not working properly.");
- }
- }
-
- void loop() {
- // 向ESP8266发送HTTP请求
- esp8266.println("AT+CIPSTART=\"TCP\",\"api.seniverse.com\",80");
- if (esp8266.find("OK")) {
- Serial.println("TCP connection established.");
- } else {
- Serial.println("TCP connection failed.");
- }
-
- String url = "/v3/weather/now.json?key=your_API_KEY&location=your_LOCATION";
- String request = "GET " + url + " HTTP/1.1\r\n" +
- "Host: api.seniverse.com\r\n" +
- "User-Agent: STM32/1.0\r\n" +
- "Connection: close\r\n\r\n";
- int length = request.length();
- String cmd = "AT+CIPSEND=" + String(length);
- esp8266.println(cmd);
- if (esp8266.find(">")) {
- Serial.println("Sending HTTP request...");
- esp8266.print(request);
- } else {
- Serial.println("Failed to send HTTP request.");
- }
-
- // 接收HTTP响应
- while (esp8266.available()) {
- String response = esp8266.readStringUntil('\n');
- Serial.println(response);
- }
-
- // 关闭TCP连接
- esp8266.println("AT+CIPCLOSE");
- delay(1000);
- }
-
在这个示例代码中,初始化了ESP8266串口对象,并通过发送AT指令测试ESP8266是否正常工作。然后,在loop()函数中,向ESP8266发送一个HTTP请求,包括请求头和请求体。发送完毕后,等待ESP8266返回HTTP响应并将其打印出来。最后,关闭TCP连接并等待一秒钟,然后重复上述步骤。