ESP32 墨水屏金价牌(下):仅靠 ESP32 自更新撞上的坑
墨抒颖注: 这里就遇到了内存问题,实际上不应该有这种问题的,手写代码做这部分应该是直接写一个超低内存消耗针对接口数据的结构体,然后直接解析,而不是把整个JSON读进来再解析。AI直接许愿出来的代码考虑的是通用PC或高性能机器的方案,没想到这个常见的小性能设备就一下子顾及不到了。后续多次提示也不行,最后还是自己折腾才好一点。
上篇把牌子做成了串口显示器。但我一直不甘心:能不能让它脱开 PC,自己联网抓数、自己刷?能,就是过程很疼。ESP32 那 320KB RAM,在 JSON 和字符串面前脆得跟纸一样。
这篇就是那条自更新路上撞的坑。
一、内存结构与拆分
金价数据有两种:分时(一天内的点)和日线(历史走势)。一开始我没分开,内存分配浪费得厉害,日线其实只用最近 90 天,我却按完整历史来分。
解法是把结构拆开:IntraSeries(1500) 装分时,1500 个点够一天;DailySeries(100) 装日线,留 90 天。日线用小的,别跟分时挤大数组。
二、OOM 与流式解析
内存墙最狠的两下,都出在「把整个 JSON 读进来再解析」这个动作上。
| # | 坑 | 根因 | 解法 |
|---|---|---|---|
| 1 | 新浪日线 OOM | getString() 分配 500KB + ArduinoJson 再 ~1MB → 超出 ESP32 内存 |
流式读只保留末尾 16KB + 原地重建合法 JSON 数组再解析 |
| 2 | 分时点超上限截错 | 原解析从头读、超限丢的是「最新」 | 两遍扫描,保留最新 MAX_INTRADAY 个 |
#1 是压垮骆驼的稻草。新浪日线返回的是 2008 年至今的完整历史(上篇提过,AU0 有 512KB),ESP32 上 getString() 一下 500KB,再交给 ArduinoJson 又吃 1MB,直接 OOM。解法是流式读:不全读完,只留末尾 16KB,最新的数据都在尾巴上,再原地拼个合法 JSON 数组去解析。
#2 更隐蔽。分时点数超上限时,原来的解析从头读,超了就把最新的丢掉,屏幕上永远是旧行情。改成两遍扫描:先数总数,再只留最后 MAX_INTRADAY 个。
三、两个小坑
| # | 坑 | 根因 | 解法 |
|---|---|---|---|
| 3 | PI 重定义报错 |
ESP32 Arduino.h 已经 #define PI |
直接用它,别自己定义 |
| 4 | 时间不显示 / 不同步 | NTP 等 5s 不够、服务器可能不通 | 等 15s + 加 fallback(time.cloudflare.com) |
#3 是提醒习惯自己 define 常量的人:Arduino.h 早把 PI define 好了,你再 define 一遍直接报重定义。NTP 同步别省那几秒,默认服务器不通的时候,加个 time.cloudflare.com 兜底。
三条教训
两篇串起来,我记住的就三条。
PC 抓数据渲染,ESP32 只当显示器。硬让 320KB 的 MCU 去解析 512KB 的 JSON,纯属自己找罪受。
抓数据绕风控,命门在 TLS 指纹。requests 被拒、curl 能通,那就全程 curl,别在 Python 里发 HTTP。
硬件时序等握手信号,别猜延时。开串口会触发复位,等 READY 再发;setup 里别做清屏这种慢活。
最后,这块屏只能全刷,秒级跳动别指望。不过金价本来就分钟级,全刷刚好。
最终效果
实时数据推送成功
