个人网站的作用,百度的广告怎么免费发布,如何运用网站做推广,html链接网站模板伏羲天气预报模型C语言基础#xff1a;嵌入式系统数据接口开发 最近在做一个工业物联网的项目#xff0c;需要让现场的STM32设备能定时获取天气数据来做一些智能控制。比如#xff0c;根据降雨预测自动关闭户外阀门#xff0c;或者依据温度调整设备运行模式。一开始觉得 struct sockaddr_in server_addr; struct hostent *server; // 1. 创建socket sockfd lwip_socket(AF_INET, SOCK_STREAM, 0); if (sockfd 0) { printf(“创建socket失败\n”); return -1; } // 2. 通过域名获取服务器IP地址DNS解析 server lwip_gethostbyname(server_host); if (server NULL) { printf(“无法解析主机名%s\n”, server_host); lwip_close(sockfd); return -1; } // 3. 设置服务器地址结构 memset(server_addr, 0, sizeof(server_addr)); server_addr.sin_family AF_INET; server_addr.sin_port htons(server_port); // 转换端口号为网络字节序 memcpy(server_addr.sin_addr.s_addr, server-h_addr, server-h_length); // 4. 发起连接 if (lwip_connect(sockfd, (struct sockaddr *)server_addr, sizeof(server_addr)) 0) { printf(“连接服务器失败\n”); lwip_close(sockfd); return -1; } printf(“成功连接到服务器 %s:%d\n”, server_host, server_port); return sockfd; // 返回socket描述符后续收发数据都用它 }这个函数就像给你的设备拨了个电话sockfd就是电话听筒。2.2 组装与发送HTTP GET请求连接建立后我们需要按照HTTP协议的格式向服务器“说”出我们的请求。对于获取天气数据通常是一个GET请求。int weather_send_request(int sockfd, const char *api_path, const char *api_key) { char request_buffer[512]; // 根据你的请求头大小调整 int bytes_sent; // 组装HTTP请求报文 // 注意每行结尾必须是 \r\n最后有一个空行 snprintf(request_buffer, sizeof(request_buffer), “GET %s HTTP/1.1\r\n” “Host: api.weather.service.com\r\n” // 替换为你的主机名 “User-Agent: STM32-Client/1.0\r\n” “Authorization: Bearer %s\r\n” // 假设使用Bearer Token认证 “Connection: close\r\n” “\r\n”, // 这个空行很重要标识请求头结束 api_path, api_key); // 发送请求 bytes_sent lwip_write(sockfd, request_buffer, strlen(request_buffer)); if (bytes_sent 0) { printf(“发送请求失败\n”); return -1; } printf(“已发送 %d 字节的请求。\n”, bytes_sent); return 0; }这里有几个关键点Host头必须正确如果API需要认证通常通过Authorization头传递密钥Connection: close告诉服务器发完数据就断开连接这样我们处理起来简单。2.3 接收与初步处理HTTP响应服务器“回话”的数据是混合体先是HTTP响应头接着是一个空行然后才是我们想要的JSON数据体。我们需要把它们分开。int weather_receive_response(int sockfd, char *json_buffer, int buf_size) { char recv_buffer[256]; int total_received 0; int in_body 0; // 标志位是否已进入响应体JSON部分 char *body_start NULL; // 清空目标JSON缓冲区 json_buffer[0] ‘\0’; while (1) { int len lwip_read(sockfd, recv_buffer, sizeof(recv_buffer) - 1); if (len 0) { break; // 连接已关闭或出错 } recv_buffer[len] ‘\0’; // 确保字符串结束 // 如果还没找到响应体就在收到的数据里找 “\r\n\r\n” if (!in_body) { char *header_end strstr(recv_buffer, “\r\n\r\n”); if (header_end) { // 找到了响应体从空行后开始 in_body 1; body_start header_end 4; // 跳过 “\r\n\r\n” 这4个字符 int body_len len - (body_start - recv_buffer); // 将响应体的第一部分拷贝到json_buffer if (body_len 0 (total_received body_len) buf_size) { strncpy(json_buffer total_received, body_start, body_len); total_received body_len; } } } else { // 已经进入响应体直接追加数据 if ((total_received len) buf_size) { strncpy(json_buffer total_received, recv_buffer, len); total_received len; } else { printf(“JSON缓冲区溢出\n”); break; } } } json_buffer[total_received] ‘\0’; // 确保字符串结束 printf(“收到响应体长度%d\n”, total_received); return total_received; }这个函数的作用就像从一封来信中把前面的信封HTTP头撕掉只留下信纸JSON内容。3. 使用cJSON解析气象数据现在我们拿到了原始的JSON字符串比如{“city”:”Beijing”, “temp”:25.5, “humidity”:60, “forecast”:”sunny”}。需要用cJSON把它变成在C语言里可以方便访问的数据。3.1 解析JSON对象假设我们获取的天气数据JSON结构如下{ “location”: “某工业区”, “current”: { “temperature”: 22.3, “humidity”: 65, “weather_code”: 100 }, “forecast”: [ { “time”: “2023-10-27 14:00”, “precipitation_prob”: 80 } ] }解析代码如下#include “cJSON.h” void parse_weather_json(const char *json_string) { cJSON *root cJSON_Parse(json_string); if (root NULL) { const char *error_ptr cJSON_GetErrorPtr(); if (error_ptr ! NULL) { printf(“JSON解析错误%s\n”, error_ptr); } return; } // 1. 解析顶层字段 cJSON *location cJSON_GetObjectItem(root, “location”); if (cJSON_IsString(location)) { printf(“地点%s\n”, location-valuestring); } // 2. 解析嵌套对象 cJSON *current cJSON_GetObjectItem(root, “current”); if (current) { cJSON *temp cJSON_GetObjectItem(current, “temperature”); cJSON *humi cJSON_GetObjectItem(current, “humidity”); if (cJSON_IsNumber(temp) cJSON_IsNumber(humi)) { printf(“当前温度%.1f°C 湿度%.0f%%\n”, temp-valuedouble, humi-valuedouble); } } // 3. 解析数组 cJSON *forecast cJSON_GetObjectItem(root, “forecast”); if (cJSON_IsArray(forecast)) { int array_size cJSON_GetArraySize(forecast); printf(“未来有 %d 个预报时段\n”, array_size); cJSON *first_item cJSON_GetArrayItem(forecast, 0); if (first_item) { cJSON *precip cJSON_GetObjectItem(first_item, “precipitation_prob”); if (cJSON_IsNumber(precip)) { printf(“下一时段降水概率%.0f%%\n”, precip-valuedouble); // 这里就可以根据概率值做逻辑判断了 } } } // 务必释放内存 cJSON_Delete(root); }cJSON的使用逻辑很直观Parse生成树GetObjectItem或GetArrayItem获取节点通过IsNumber/IsString判断类型然后从valuedouble或valuestring中取值。3.2 关键的内存管理技巧在嵌入式环境内存是黄金。cJSON在解析时会在堆heap上动态分配内存来构建树状结构。确保堆空间足够在STM32CubeIDE的工程中打开startup_stm32f4xx.s或其他对应型号的文件找到Heap_Size并适当调大。对于解析中等复杂度的JSON将Heap_Size设置为0x8002KB或更多是一个安全的起点。如果解析失败并伴随硬故障首先怀疑堆溢出。及时删除避免泄漏每一个通过cJSON_Parse或cJSON_CreateXXX创建的对象最终都必须用cJSON_Delete来释放。否则每次请求都会泄露内存设备很快会死机。复用缓冲区如果可能复用接收JSON的字符缓冲区而不是每次都malloc新的。4. 工业物联网场景实战案例理论讲完了我们来点实际的。假设我们有一个户外的大型储罐需要根据天气预报来智能控制其加热系统和泄压阀。业务逻辑每30分钟获取一次未来2小时的精细化天气预报温度、降水概率。如果预测温度低于5°C则启动罐体加热器防止介质凝固。如果预测降水概率大于70%则提前部分关闭泄压阀入口防止雨水倒灌。4.1 主循环与业务逻辑整合我们把前面学的所有模块组合起来放到一个独立的任务或主循环中。void weather_client_task(void const *argument) { const char *host “api.fuxi-weather.com”; const char *api_key “your_actual_api_key_here”; const char *path “/v1/forecast/hourly?locationplant_a”; int port 80; char json_buffer[1024]; // 根据API返回数据量调整大小 while (1) { printf(“\n 开始新一轮天气数据获取 \n”); // 1. 建立连接 int sock weather_tcp_connect(host, port); if (sock 0) { goto cleanup_and_sleep; // 连接失败跳转到清理和休眠 } // 2. 发送请求 if (weather_send_request(sock, path, api_key) 0) { lwip_close(sock); goto cleanup_and_sleep; } // 3. 接收响应 if (weather_receive_response(sock, json_buffer, sizeof(json_buffer)) 0) { // 4. 解析并执行业务逻辑 process_weather_for_control(json_buffer); } // 5. 关闭连接 lwip_close(sock); cleanup_and_sleep: // 6. 等待30分钟后再执行 osDelay(30 * 60 * 1000); // 使用RTOS的延时如果是裸机程序则用定时器 } } // 具体的业务处理函数 void process_weather_for_control(const char *json) { cJSON *root cJSON_Parse(json); if (!root) return; float temp 0.0, precip_prob 0.0; // ... 解析温度(temp)和降水概率(precip_prob)的代码参考第3节 ... // 控制逻辑 if (temp 5.0) { printf(“[控制] 温度过低启动加热器。\n”); // HAL_GPIO_WritePin(HEATER_GPIO_Port, HEATER_Pin, GPIO_PIN_SET); } else { printf(“[控制] 温度正常关闭加热器。\n”); // HAL_GPIO_WritePin(HEATER_GPIO_Port, HEATER_Pin, GPIO_PIN_RESET); } if (precip_prob 70.0) { printf(“[控制] 高降水预警调整泄压阀。\n”); // set_valve_position(70); // 假设一个关阀函数 } cJSON_Delete(root); }4.2 错误处理与健壮性增强实际工业环境网络不稳定代码必须足够健壮。添加超时机制给socket的connect、read、write操作设置超时。lwIP可以通过setsockopt设置SO_RCVTIMEO和SO_SNDTIMEO。增加重试逻辑当一次请求失败连接失败、接收超时、解析错误不要立刻放弃。可以加入一个有限次数的重试循环比如3次每次重试前稍作延时。心跳与状态上报设备可以定期向服务器或本地监控中心发送心跳包报告自己的“健康状态”和最后一次成功获取天气数据的时间便于远程诊断。5. 总结走完这一趟你会发现在嵌入式系统里搞网络通信和数据处理核心思路就是“精细”和“稳健”。和PC编程大手大脚地申请内存、依赖丰富的库不同在这里你需要清楚地知道每一字节内存的来龙去脉每一个网络包该如何妥善处理。这套从连接、请求、接收、解析到应用的流程不仅仅适用于天气API对于接入其他任何提供HTTP/JSON接口的云服务如设备管理平台、数据中台、第三方服务都是相通的。关键在于理解协议、选对工具lwIP, cJSON、并做好资源管理。下次如果你的STM32设备需要从云端获取点什么东西不妨试试今天的方法。先从简单的API调起慢慢增加复杂性。过程中遇到的坑都是宝贵的经验。祝你开发顺利获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。