STM32与心知天气结合实现天气预报功能(HAL库详解)

一、简介

最近刚学完ESP-01S模块,想做点小东西,用STM32+心知天气+ESP-01s实现在OLED屏幕上显示最近天气。效果如图所示:

通过ESP-01S模块获取心知天气传回来的JSON数据,再通过解析数据显示在OLED屏幕上。

二、心知天气

首先,在心知天气获取API:

心知天气官网【控制台】【添加产品】【免费版】然后复制API密钥中的私钥

在心知天气操作手册中获取接口:

 

我们需要记录和了解以下信息:

 

经过修改接口连接,链接如下:我们可以获取到成都 明天  只获取一天的天气预报内容

https://api.seniverse.com/v3/weather/daily.json?key=S3sPxT5_RyH0If&location=chengdu&language=zh-Hans&unit=c&start=1&days=1

(需要用自己的API,上述接口连接不能直接使用) 

将上段连接复制到网页,我们可以得到一串JSON数据包:

 

如果有错误会返回错误代码,错误代码的详细解释如下:

 

三、STM32实战代码(HAL库)

其中ESP-01S的驱动代码在:

(首先获取上面连接的代码,然后按下面的修改就可以了)

(1)初始化代码

首先,我们需要设置ESP8266的模式,代码如下:

/**
  * @brief   ESP8266初始化
  * @param   None
  * @retval  None
 **/
void ESP8266_Init(void)
{
	__HAL_UART_ENABLE_IT(&ESP01_UART_HANDLE, UART_IT_IDLE);
	HAL_UART_Receive_DMA(&ESP01_UART_HANDLE, g_uart2_rx_buffer, ESP01_BUFFER_SIZE);
	//ESP测试AT指令
	while (!ESP8266_TEST());
	HAL_Delay(500);
	//配置为STA模式
	while (!ESP8266_Net_Mode_Choose(STA));
	HAL_Delay(500);
	//设置单链路模式
	while (!ESP8266_Set_MultiplesMode(0));
	HAL_Delay(500);
	//连接WIFI
	while (! ESP8266_JoinAP());
	HAL_Delay(2000);
	//连接TCP服务器
	while (!ESP8266_Connect_TCPServer(0, IP, PORT));
	HAL_Delay(500);
	while(!ESP8266_Send_Cmd("AT+CIPMODE=1\r\n", "OK")) HAL_Delay(500);
	while(!ESP8266_Send_Cmd("AT+CIPSEND\r\n", ">")) HAL_Delay(500);
}

初始化流程为:1.配置STA 2.设置为单链路模式 3.连接WIFI(需改为自己的密码和ID) 4.连接TCP服务器(服务器为心知天气地址 域名:api.seniverse.com 端口:80)5.设置为透传模式

(2)写获取天气JSON的函数

 

/**
  * @brief   获取天气
  * @param   None
  * @retval  None
 **/
void ESP8266_GetWeather(uint8_t day) {
    // 构造符合HTTP标准的请求
    char request[256];
    snprintf(request, sizeof(request),"GET https://api.seniverse.com/v3/weather/daily.json?key=S3sPxT5_Exr0If&location=chengdu&language=zh-Hans&unit=c&start=%d&days=1\r\n",day);
    ESP8266_Send_Cmd(request, "results");
}

(接口连接为:GET + 自己的接口连接)

day表示获取那一天的天气 0为今天 1为明天 …

(3)写获取收到的JSON天气预报的函数

/**
  * @brief  复制处理缓冲区数据到data
  * @param  data: 要发送的JSON数据
  * @retval 数据长度
  */
uint16_t esp8266_copy_rxdata(char *data, uint16_t max_len) {
    if (!data || max_len == 0) return 0;

    __disable_irq();  // 防止中断修改数据
    uint16_t valid_len = (ESP01_RX_LEN <= max_len) ? ESP01_RX_LEN : max_len;
    memcpy(data, g_esp8266_rx_buffer, valid_len);
    __enable_irq();

    return valid_len;
}

我们通过ESP8266发送给TCP一段 GET + 自己的接口连接的函数,如果代码正确,此时会在串口助手上返回给我们天气的JSON包。(这段代码的作用就是复制JSON数据到指针data中,然后我们通过解析JSON数据显示在OLED上)

JSON具体含义如下:

我只显示了 date日期、code_day天气状况、high最高温度、low最低温度显示在OLED屏上 

接着,我们编写Weather函数(解析获取到的JSON)

weather.c如下:

#include "weather.h"
#include "esp01_uart.h"
#include "stdlib.h"
#include "cJSON.h"
#include "stdio.h"
#include "OLED.h"
#include <string.h>
#include "UART3_PRINTF.h"

#define ESP01_BUFFER_SIZE 1024
#define MAX_STR_LEN 32

char weather_data[ESP01_BUFFER_SIZE] = {0};
daily_weather_t today_weather = {0};

// 天气代码转换函数
const char* code_to_text(const char* code)
{
    if(!code) return "Unknown";

    // 根据心知天气API代码表编写(常用代码示例)
    if(strcmp(code, "0") == 0)  return "Sunny";       // 晴
    if(strcmp(code, "1") == 0)  return "Clear";      // 晴
    if(strcmp(code, "4") == 0)  return "Cloudy";     // 多云
    if(strcmp(code, "9") == 0)  return "Shower";     // 阵雨
    if(strcmp(code, "10") == 0) return "Storm";      // 雷阵雨
    if(strcmp(code, "11") == 0) return "Thundersht"; // 雷阵雨伴有冰雹
    if(strcmp(code, "13") == 0) return "Foggy";      // 雾
    if(strcmp(code, "14") == 0) return "Haze";       // 霾
    if(strcmp(code, "15") == 0) return "Dust";       // 浮尘
    if(strcmp(code, "19") == 0) return "Blizzard";   // 暴雪
    if(strcmp(code, "20") == 0) return "Fog";        // 浓雾
    return "Unknown";
}
void ESP8266_GetWeatherData(uint8_t day)
{
    memset(weather_data, 0, sizeof(weather_data));
    ESP8266_GetWeather(day);
    uint16_t len = esp8266_copy_rxdata(weather_data, sizeof(weather_data));

    if (len == 0) {
        printf("获取天气数据失败\n");
        return;
    }

    cJSON *root = cJSON_Parse(weather_data);
    if (!root) {
        printf("JSON解析失败\n");
        return;
    }

    cJSON *results = cJSON_GetObjectItem(root, "results");
    if (!results || !cJSON_IsArray(results)) {
        printf("找不到results字段\n");
        cJSON_Delete(root);
        return;
    }

    cJSON *first_result = cJSON_GetArrayItem(results, 0);
    if (!first_result) {
        printf("results数组为空\n");
        cJSON_Delete(root);
        return;
    }

    cJSON *daily = cJSON_GetObjectItem(first_result, "daily");
    if (!daily || !cJSON_IsArray(daily)) {
        printf("找不到daily字段\n");
        cJSON_Delete(root);
        return;
    }

    cJSON *today = cJSON_GetArrayItem(daily, 0);
    if (!today) {
        printf("daily数组为空\n");
        cJSON_Delete(root);
        return;
    }

    // 解析具体字段(添加所有必要的安全检查)
    cJSON *date = cJSON_GetObjectItem(today, "date");
    cJSON *code_day = cJSON_GetObjectItem(today, "code_day");
    cJSON *high = cJSON_GetObjectItem(today, "high");
    cJSON *low = cJSON_GetObjectItem(today, "low");

    if (date && date->valuestring)
        strncpy(today_weather.date, date->valuestring, MAX_STR_LEN);
    if (code_day && code_day->valuestring)
        strncpy(today_weather.code_day, code_day->valuestring, MAX_STR_LEN);
    if (high && high->valuestring)
        strncpy(today_weather.high, high->valuestring, MAX_STR_LEN);
    if (low && low->valuestring)
        strncpy(today_weather.low, low->valuestring, MAX_STR_LEN);

    cJSON_Delete(root);
}

void ESP8266_ShowWeather(void)
{
	// 获取天气描述(限制最大显示长度)
	char weather_desc[16] = {0};
	const char* raw_desc = code_to_text(today_weather.code_day);
	strncpy(weather_desc, raw_desc, 15);

    // 显示布局调整(假设OLED分辨率128x64,8x16字体)
    OLED_ShowString(0, 0,  "Date: ", OLED_8X16);
    OLED_ShowString(40, 0, today_weather.date, OLED_8X16);

    OLED_ShowString(64, 16, weather_desc, OLED_8X16);

    OLED_ShowString(72, 32, today_weather.high, OLED_8X16);

    OLED_ShowString(72, 48, today_weather.low, OLED_8X16);

    OLED_Update();
}

weather.h如下:

#ifndef __WEATHER_H
#define __WEATHER_H

#include "main.h"

typedef struct
{
    char date[20];
    char code_day[10];
    char high[10];
    char low[10];
} daily_weather_t;

// 添加函数声明
const char* code_to_text(const char* code);
void ESP8266_GetWeatherData(uint8_t day);
void ESP8266_ShowWeather(void);

#endif

Main.c中代码如下:

  OLED_Init();
  OLED_ShowString(0, 16, "成都", OLED_8X16);
  OLED_ShowString(0, 32, "最高气温   ℃", OLED_8X16);
  OLED_ShowString(0, 48, "最低气温   ℃", OLED_8X16);
  OLED_Update();
  ESP8266_Init();
  ESP8266_GetWeatherData(2);
  ESP8266_ShowWeather();

解析JSON数据的库是开源代码:

连接如下:DaveGamble/cJSON: Ultralightweight JSON parser in ANSI C 

 

 

 

 

 

 

 

 

 

作者:m0_63127436

物联沃分享整理
物联沃-IOTWORD物联网 » STM32与心知天气结合实现天气预报功能(HAL库详解)

发表回复