联网数据与人工智能

分析weixue天气/热点/赛事/云端摘要的真实来源、串行编排、HTTP分片与两层JSON,指出降级和部分成功语义。

版本与验证范围

静态源码/电路核对;用户历史反馈单独注明;本次未编译、烧录或测量

返回 00-项目导航 · 配套阅读 06-蓝牙网页与本地存储

本篇分析 weixue 完整终端应用。moqian 是另一个驱动演示工程,不能把这里的联网和人工智能能力算到那个演示工程上。行号以核对当天源码为准;没有修改原工程,没有实际请求收费接口、重新编译、烧录或验证硬件。

一、先把“取数据”和“生成内容”分开

这个项目最值得学习的地方,是让微控制器做联网、解析和显示,让远端服务提供数据或推理。ESP32-S3 上没有在这条调用链中加载大语言模型、训练参数或进行本地大模型推理。deepseek_fetch_all() 通过 HTTPS 请求云端 DeepSeek;它还调用天气和赛事服务,因此函数名容易造成误解。

内容 当前真实来源 ESP32-S3 做什么
原始热点标题 baidu_fetch_hot()、weibo_fetch_hot() 接收响应,提取标题,整理分类
天气数值 weather_fetch_openmeteo() 解析 Open-Meteo 的天气代码与温度数组
NBA 比赛 nba_cdn_fetch() 解析 CDN 返回的结构化赛况
天气摘要、热点筛选 do_deepseek_request() 发提示词,解析模型返回内容

因此,提示词中的“传感器数据”只是文字描述;实际天气数据来自网络接口。weather_7d_t 虽然带有 7d,当前 URL 请求的是今日加未来三日的四天数据,不能据名字宣称七日天气已经实现。对应证据是 deepseek_fetch.c:162、:168 和 :276。

二、沿着真实调用链读一次

启动时,main.c:1033 调用 data_fetch_orchestrate(..., true, false):允许模型请求,跳过本次 NBA 请求。data_fetch.c:129 内部的顺序是:

流程示意 按原 Mermaid 线性链重绘
  1. 清空本轮新闻结构
  2. 百度热点
  3. 微博热点
  4. 过滤标点、去重、生成显示列表
  5. 收集最多十条原始标题
  6. 按条件获取赛事
  7. 获取天气
  8. 请求云端摘要与筛选
  9. 把人工智能热点放到列表前部
  10. 保存原始新闻缓存
查看原始 Mermaid 源码
mermaid
flowchart TD
    A[清空本轮新闻结构] --> B[百度热点]
    B --> C[微博热点]
    C --> D[过滤标点、去重、生成显示列表]
    D --> E[收集最多十条原始标题]
    E --> F[按条件获取赛事]
    F --> G[获取天气]
    G --> H[请求云端摘要与筛选]
    H --> I[把人工智能热点放到列表前部]
    I --> J[保存原始新闻缓存]

build_flat_news_inline() 在 data_fetch.c:56 把分类新闻变成供界面轮播的一维数组;data_fetch_collect_raw_hot() 在 :83 提取给模型的候选标题;:97 用 memmove() 为置顶热点腾位置。这里是一条串行流水线,并没有同时请求多个网站。

要特别注意:include_deepseek=false 会跳过整个 deepseek_fetch_all(),所以连其中的天气与赛事也跳过。它并不是“保留天气,只关闭模型”的独立开关。另一个 data_fetch_hot_only() 也只处理百度和微博,不能代替天气更新。

三、哪些操作真正会触发云端请求

触发条件 代码入口 当前行为
开机且联网 main.c:1033 获取热点、天气和模型结果;跳过 NBA
无线网络恢复 main.c:1253 停止蓝牙,更新全部数据,再恢复蓝牙
确认键刷新或蓝牙刷新命令 main.c:1281、ble_cmd.c:256 设置标志后,由主循环执行全部更新
周期更新 main.c:1314 调用相同编排;该分支没有包裹停止蓝牙逻辑
进入主界面后延迟获取赛事 main.c:1115 只调用 nba_cdn_fetch(),不请求模型

TICK_RSS=24000 与主循环每次延时 50 ms 对应名义 20 分钟,见 main.c:1092。但这是循环计数,遇到同步 HTTP 或屏幕刷新阻塞就会延后,不能当作准确的墙上时钟定时器。注释“后台拉 NBA,不阻塞”也不能按字面理解::1120 是主循环直接调用同步函数,没有创建独立任务。

四、HTTP 分片为什么不能直接交给 JSON 解析

网络传来的是字节流,一次 esp_http_client_read() 可能只读到 JSON 的前半截,甚至停在一个汉字的 UTF-8 编码中间。应先按实际字节数拼接,最后补 \0,再进行完整解析。

do_deepseek_request() 在 deepseek_fetch.c:555 使用 init → open → write → fetch_headers → read → close → cleanup。:603 为响应分配缓冲区,已知且较小的 Content-Length 使用长度加一,否则最多分配 16384 字节;:608 循环累积读取。微博上限是 65536 字节,天气缓冲区是 4096 字节,均须为字符串结尾预留一字节。

HTTP 的 Transfer-Encoding: chunked 与“应用多次读取”不是同一层概念。ESP-IDF 负责处理 HTTP 分块编码;应用仍需反复取出有效正文。fetch_headers() 返回零可能表示没有长度头或使用分块编码,不能直接判作空正文。ESP-IDF 5.1.2 HTTP 客户端文档

当前读循环遇到 r<=0 就退出,没有区分正常结束、读取失败和等待超时,也没有用完整性检查确认正文收齐;达到容量上限时会截断。:589 的发送逻辑只检查负返回值,也没有处理写入长度小于请求长度的情况。这些是现有实现的边界,不代表已经在设备上复现故障。

下面是便于理解的读取骨架,属于教学改进示意,没有写入固件;err 必须传回调用方,且不能把整个缓冲区写满后再补结束符。

c
size_t used = 0;
esp_err_t err = ESP_OK;
while (used < capacity - 1) {
    int n = esp_http_client_read(client, buf + used, capacity - 1 - used);
    if (n < 0) { err = ESP_FAIL; break; }
    if (n == 0) break;
    used += (size_t)n;
}
buf[used] = '\0';
if (!esp_http_client_is_complete_data_received(client)) err = ESP_FAIL;
/* 只有 err == ESP_OK 时,才继续解析。 */

五、JSON 有两层,内存也有两种所有权

请求由 build_request_json()(deepseek_fetch.c:90)构造,加入 model 和 messages 后序列化。返回值是堆上的字符串,发送完成后释放;不能返回函数内局部数组的地址。

响应第一层是服务端协议对象,deepseek_fetch.c:674 先解析它;:683 定位 choices[0].message.content。其中 content 是字符串,字符串内部才是项目约定的第二层 JSON,例如:

json
{"weather":{"today_summary":"出门留意天气"},"hot":[1,3,5]}

:693 再次 cJSON_Parse() 才能提取天气与热点。对象成员指针依附于 JSON 树;需要长期保存的文字用 snprintf() 复制到结构体,最后分别释放内层与外层树。接收缓冲区、JSON 树和最终业务结构体不是同一份内存,释放顺序必须明确。

百度模块值得单独比较:baidu_fetch.c:95 起用 strstr() 搜索 JSON 文本中的固定片段,而微博模块用 cJSON 查找数组和字段。前者对空格、转义和接口格式变化更敏感,不能把两者概括成“都已通过标准 JSON 解析”。

六、约束模型输出,比提示词许愿更重要

build_prompt()(deepseek_fetch.c:36)提供真实天气与原始标题,要求模型返回热点序号。parse_hot()(:386)把从一开始的序号映射回原始标题,可以减少模型改写新闻带来的失真。不过 :401 仍兼容字符串标题,所以“绝不接收模型新写标题”还不是强制约束。

parse_weather()(:335)在 Open-Meteo 有效时保留其温度,只更新摘要;网络天气失败时,代码也允许有限范围的模型温度作为后备。这个后备温度只有范围检查,没有新的气象事实来源,答辩时应明确这一局限。

deepseek_fetch_all() 在模型请求失败而天气或赛事仍有效时也可能返回 ESP_OK(:661)。因此 g_ds_valid=true 表示编排接受了部分可用数据,不能单独证明模型调用成功。验证应分别观察天气有效位、热点数量、赛事数量及请求错误,而非只看一个“成功”标志。

七、数据生命周期、并发与验收练习

本轮新闻先被清空,再被各来源填入,然后转换成显示数组,最后保存 NVS;天气、模型热点和赛事主要保存在各自全局结构体。storage_save_news_cache() 保存的是 news_data_t,不包含后来置顶到显示数组中的模型热点,也不等于保存了完整天气、赛事和模型状态。联网失败时保留旧内容的策略尚不完整:新一轮编排先清空 RAM,不能承诺总能继续显示上一轮数据。

超时值也需要按调用链理解:热点和天气配置 15000 ms,赛事配置 8000 ms,模型配置 15000 ms;部分来源还有重试。各阶段串行叠加,整轮更新不能承诺在 15 秒内完成。主循环阻塞期间,蓝牙队列不能及时被消费,网页任务却可能仍读写共享状态,见 06-蓝牙网页与本地存储。

可按下列顺序验收,全部使用虚构数据,避免调用收费接口或展示真实配置:

  1. 画调用链:找出四个编排调用点,写出两个布尔参数;验收标准是能解释开机为什么没有赛事请求。
  2. 手工拆包:把上述内层 JSON 按任意三个字节分段拼接;验收标准是完整拼接后能解析,任取中间片段不能被当成一条业务消息。
  3. 检查降级:推演“天气成功、模型失败”“热点成功、天气和赛事都失败”两种路径;验收标准是能指出总返回值与各字段有效性不是同一概念。
  4. 拟定设备测量表:记录每个来源耗时、HTTP 状态、实际接收长度、剩余堆和屏幕响应延迟;验收标准是用测量确认超时与内存边界。这一步尚需真实设备执行。

复习时应能回答:云端模型究竟生成了什么?一次成功的 HTTP 状态为什么还不能证明 JSON 完整、业务正确或缓存已保存?

来源与关联索引
来源
嵌入式比赛
原笔记
项目/墨嵌墨水屏/知识讲解/05-联网数据与人工智能.md
资料性质
实践指南。原文的版本、来源与验证边界继续保留。
关联笔记 4
返回文章目录

想去哪里?

22 个入口

首页作品作品展厅全部项目文章总览文件夹标签资料归档关于认识 Lan探索路线工具与工作台最近在做InkFeed · 4.2 英寸电子纸信息终端VIBRATION · 柔性振动检测与采集链路TRACE · 航向保持与灰度循迹小车STRIDE · 八自由度四足机器人SCOPE · 固件日志浏览器温度为什么被 AI 改了?FIFO 有半帧,就一定是故障吗?直线守航向,弧线再看黑线。

Tab 选择入口 · Enter 打开 · Esc 关闭