news 2026/9/19 10:11:19

BrewUI:从传感器到PID的智能酿造控制仪表盘实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
BrewUI:从传感器到PID的智能酿造控制仪表盘实战

从把第一锅麦汁煮糊,到能稳定复现同一款社交型IPA,中间隔着一条长长的仪表盘之路。BrewUI这个项目,就是我在这个过程中攒出来的一个面向自酿爱好者和家庭精酿玩家的Web端酿造管理界面。它不只是在手机上显示个温度数字那么简单,而是一个集配方管理、酿造流程编排、传感器数据采集、加热设备控制、发酵跟踪于一体的自托管系统。如果你正在捣鼓自己的酿造设备,或者想给自己的酿造间加一套能看曲线、能控温度、能存配方的交互界面,这篇内容应该能帮你省掉不少弯路。

我最早用的是某商业温控器的配套App,功能封闭,传感器数据拉不出来,想改个分步温度曲线得蹲在设备前按半天。后来干脆用ESP32加PT100传感器,把数据通过MQTT推到家里的小服务器上,再自己写了BrewUI这套界面来管理整个酿造过程。整套系统跑下来已经有半年多,从糖化阶段的温度跟踪,到煮沸阶段的功率控制,再到发酵间的温度记录,都用这一套界面在管。下文把整个项目的设计思路、核心模块、实现细节和踩坑记录都摊开来讲。

1. 项目整体设计与思路拆解

1.1 为什么选择Web端的仪表盘架构

BrewUI的核心定位,是给酿造过程提供一个实时、可交互、可回看的控制面板。之所以选Web架构而不是桌面应用或手机原生App,有几个非常现实的原因:第一,酿造间里往往不止一台设备,温度传感器、加热棒、循环泵、电子秤,可能分布在不同的位置,Web界面在任何一台手机、平板、电脑上打开浏览器就能看到,不需要为每台设备单独装客户端;第二,传感器数据和酿造记录需要长期保存,放在一台常开的小主机上跑一套Web服务,比让手机App在后台偷偷挂着靠谱得多;第三,开发调试方便,前端改了个样式、后端加了条API,刷新页面就能生效,迭代速度比原生应用快出好几个量级。

这套架构选型在技术圈里叫“前后端分离加消息中间件”,听起来唬人,拆开其实就是三条线:前端页面负责任务看板、曲线展示和操作入口;后端服务负责配方存储、会话状态管理和数据聚合;消息中间件负责在传感器与后端之间搬运实时温度数据。BrewUI用的是MQTT协议来承载设备数据。为什么是MQTT而不是HTTP轮询或者WebSocket直连?因为MQTT的发布订阅模型适合设备端频繁上报小体积数据,也不会因为连接数量多了把后端拖垮。

1.2 功能边界与模块划分

在动手写代码之前,我把BrewUI的功能边界划得特别清楚,避免做着做着变成一个什么都要管的怪物项目。核心功能只锁定四块:

  • 配方管理:麦芽、酒花、酵母、水的用量记录,步骤化的糖化温度曲线和煮沸时间参数
  • 酿造会话控制:创建一次酿造任务,从配方中载入流程,按步骤推进,自动记录每个阶段的时间与温度
  • 实时数据可视化:温度传感器数据秒级刷新,展示糖化和发酵阶段的实时曲线
  • 设备控制输出:通过继电器或智能插座控制加热棒和泵,支持手动开关和按曲线自动调节

这四块之外的功能,比如库存管理、成本核算、社区分享,我全部砍掉了。后来实际使用中发现,这个克制非常关键。项目能做到快速上线并且稳定运行,很大程度上就是因为每一块功能背后都有明确的使用场景,没有多余的东西分散精力。界面设计上我也坚持了信息密度优先的原则,一屏内能看到当前步骤、剩余时间、实时温度和下一阶段动作,不需要来回切换页面。

2. 核心细节解析与实操要点

2.1 传感器选型与数据采集链路

温度传感器是整个系统的眼睛。我对比过DS18B20、PT100和NTC热敏电阻,最终选的是PT100加MAX31865转换模块的组合。原因很直接:DS18B20在0到100摄氏度范围内勉强够用,但到了煮沸阶段,探头离加热管近一点读数就开始飘;NTC需要自己做标定,不同批次的传感器一致性差,换成新的就得重新校准。PT100虽然贵一些,但线性度和长期稳定性都更好,配合MAX31865模块,温度分辨率能到0.01摄氏度,对付酿造场景绰绰有余。

传感器数据采集链路我用了比较保守的架构:ESP32开发板通过SPI接口读取MAX31865的转换结果,然后通过WiFi把温度值打包成MQTT消息,发到局域网里的MQTT Broker上。MQTT消息主题设计成扁平结构:

  • brewui/sensor/temp/mash_tun- 糖化桶温度
  • brewui/sensor/temp/boil_kettle- 煮沸锅温度
  • brewui/device/status- 设备在线状态

2.2 温度控制策略:为什么不能只靠开关

加热功率控制是酿造自动化里最容易翻车的环节。很多DIY方案干脆让加热棒全功率运行,温度到了设定值就断电,低于设定值就重新通电。这个逻辑在煮热水的时候没什么问题,但是用在糖化上就很灾难。麦汁是黏稠液体,热传导不均匀,加热棒周围的温度先到,探头的温度还没跟上,断电之后余热继续传导,最后整锅温度能冲过设定值两三度。对糖化来说,温度区间差两三度,出糖效率和口感就完全不一样了。

BrewUI在功率控制上走了PID调节的路子,配合固态继电器实现可控硅斩波调功。简单解释就是:系统根据当前温度和目标温度的偏差,加上温度变化趋势,计算出每秒应该给加热棒多少百分比的功率,而不是简单通断。加热阶段全功率推进,接近目标温度时自动降功率,让温度平滑落在设定区间内。实际的功率输出频率控制在一个比较低的频率,比如每秒调节一次,避免对电网造成太大冲击,也减轻继电器触点的磨损。

PID参数我一开始是手动调的,后来发现直接用经典Ziegler-Nichols方法整定,先把系统打成临界振荡,记下临界周期和临界增益,再套公式算参数,就能得到一个不错的起点。

2.3 配方的数据化表达

酿造配方在系统里不只是几张图片或者一段文字,而是一份结构化的数据。BrewUI把配方拆成三层:基础信息层、原料清单层、工序步骤层。基础信息层记录配方名称、风格类型、目标产量、目标原麦汁浓度;原料清单层按麦芽、酒花、酵母、其他添加物分类,每种原料记录名称、用量和使用时间节点;工序步骤层是整个配方的灵魂,定义了从糖化到煮沸再到冷却的完整时间线。

每个工序步骤是一个独立的对象,包含步骤类型、目标温度、持续时间、升温速率、搅拌或循环动作等字段。系统执行酿造会话时,就是按顺序加载这些步骤,配合实时温度数据推进流程。举个例子,分段出糖的配方可以这样定义:

  • 蛋白质休止:52°C保持10分钟
  • 糖化休止:63°C保持30分钟
  • 糖化强化:68°C保持30分钟
  • 灭酶:78°C保持5分钟

这套数据模型是整个BrewUI的基石,前端、后端、设备控制都围绕它来协作。

3. 实操过程与核心环节实现

3.1 前端界面搭建与技术栈选择

前端我选的是React加Vite的组合,状态管理用Zustand,图表用ECharts,UI组件库用的是Ant Design。这套组合在BrewUI这个场景下有几个实际的考量:React生态成熟,网上资料多,遇到问题好查;Vite的开发时热更新非常快,改完代码一两秒就能在浏览器里看到效果;Zustand比Redux轻量得多,对于酿造会话这种不算特别复杂的状态同步完全够用;ECharts画温度曲线不需要额外封装,折线图、面积图开箱即用。

界面布局上,我把仪表盘设计成了三栏结构。左侧是会话信息栏,显示当前配方的名称、步骤序号、酿造的进度条和关键指标;中间是主操作区,包含实时温度曲线和当前步骤的操作按钮;右侧是设备状态栏,显示加热棒功率、泵的启停状态、传感器在线情况。移动端通过媒体查询做了响应式适配,实际使用中大部分时间还是用平板看曲线,手机端主要看个大概。

3.2 后端API与实时通信

后端服务用Node.js的Express框架,搭配SQLite存数据。为什么选SQLite而不是MySQL或者PostgreSQL?因为这个项目的使用场景是家庭自托管,数据量不大,QPS也很低,SQLite单文件部署、不需要单独维护数据库服务,备份的时候直接把文件拷走就行。后期如果真要多设备并发访问,再迁移到PostgreSQL也不迟,数据访问层我做了隔离,切换成本不高。

API设计遵循RESTful风格,重要的接口有几个。

  • POST /api/sessions创建新的酿造会话
  • GET /api/sessions/:id获取会话详情
  • POST /api/sessions/:id/start启动酿造流程
  • POST /api/sessions/:id/step/:stepId/complete手动标记当前步骤完成
  • GET /api/sessions/:id/temperature获取历史温度数据

实时数据推送走WebSocket。MQTT收到传感器数据之后,后端一方面写入时序数据库,另一方面通过WebSocket广播给所有在线前端页面。实现的时候遇到一个问题:MQTT消息频率太高的时候,WebSocket广播会拖垮主进程。后来在广播逻辑里加了节流,每两秒批量推送一次最新温度值,前端拿到数据之后用ECharts的appendData方法增量更新曲线,性能一下子就上来了。

3.3 数据存储与时序数据处理

酿造过程会产生大量的时序数据。糖化阶段温度每两秒一条,发酵阶段每三十秒一条,一锅酿造下来大概产生几千条记录,一年几十锅,数据量也就是几十万条级别。这个量级在SQLite里完全扛得住,关键是要建好索引。我在温度记录表里对session_idtimestamp建了联合索引,查询某一次会话的完整曲线时,很快就能返回结果。

温度记录表的结构是这样设计的:

CREATE TABLE temperature_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, session_id INTEGER NOT NULL, sensor_type TEXT NOT NULL, temperature REAL NOT NULL, recorded_at INTEGER NOT NULL ); CREATE INDEX idx_temp_logs_session_time ON temperature_logs(session_id, recorded_at);

配方表、步骤表、原料表也都遵循了外键关联的规范写法,保证数据完整性。会话结束后,系统会自动生成一份HTML格式的酿造报告,包含完整的温度曲线、步骤执行时间和原料清单,导出之后可以作为自己的酿造笔记存档。

3.4 设备控制模块的实现细节

设备控制是BrewUI里和物理世界交互的部分。ESP32除了上报传感器数据,还订阅了控制主题,后端通过MQTT向设备下发控制指令。加热棒接到固态继电器上,控制频率是一秒,数据库里记录每一秒的功率百分比,方便事后分析。

控制指令的消息格式用JSON:

{ "device": "heater_mash_tun", "action": "set_power", "power": 45, "timestamp": 1699999999 }

ESP32端解析这条消息之后,通过PWM或者定时通断来调节输出功率。PID计算放后端做,而不是放在设备端,好处是调参不用重新刷固件,改完参数界面上一保存,下一次控制周期就生效。

4. 常见问题与排查技巧实录

4.1 温度读数漂移和探头故障

PT100本身很稳定,但整个链条上还是有不少地方会出问题。最典型的故障是接线松动。MAX31865模块和ESP32之间的连接线如果用了杜邦线,时间久了或者震动之后会接触不良,读出来温度一会是20度一会是120度。排查方法很简单,看前端曲线如果出现脉冲式的毛刺,多半就是接线问题。后来我把所有传感器接线都换成了航空插座,固定牢靠,这种问题就再也没出现过。

还有一种情况是探头位置放得不对。探头如果太靠近加热棒,测出来的温度明显偏高,导致PID系统以为温度到了,实际麦汁整体温度还没到位。我现在的做法是把探头放在糖化桶的循环回路中间,让麦汁从底部抽出、经过泵和探头再回到顶部,实时测的是循环之后的混合液温度,这个位置更能代表整体糖化温度。如果你用的是静态糖化桶没有循环,建议把探头放在桶壁中下部,并且加一个轻柔的搅拌,避免局部温度分层。

4.2 WebSocket连接不稳定

用了一段时间之后,发现前端页面偶尔会卡在启动页面,日志里报WebSocket连接失败。排查发现是家里路由器在长时间没有数据交互的情况下,会断开TCP连接,前后端之间没有心跳机制,连接断了之后前端不知道,一直等消息,看起来就像卡死了。

解决办法是让前端每隔30秒发送一次Ping消息,后端收到后立即回Pong,如果连续三次Ping没有收到回复,前端就主动重连。同时在后端WebSocket的配置里也加了自动检测,连接空闲超过60秒会主动发送探测包。加了这套保活机制之后,页面挂一天都不会掉线。

4.3 加热功率失控的风险保护

设备控制一定要考虑一件事:软件或者网络出问题的时候,加热器能不能停下来。我踩过一次坑,PID服务进程崩溃了,ESP32没有收到新的控制指令,然而后端的控制逻辑默认是保持上一次的输出状态,于是加热棒就按着上一次的功率一直烧,差点把麦汁煮过头。这套逻辑放在酿造里虽然不至于出大事故,但是把糖化温度推到80度以上,一锅麦汁就废了。

现在我把安全逻辑做了双保险:第一层,ESP32端设置一个看门狗,如果超过5秒没有收到后端的心跳消息,就自动进入待机状态,切断加热输出;第二层,后端每次发送控制指令时带上过期时间,设备收到后只在这个时间窗内执行,超时自动归零。酿造过程中系统再出崩溃,最多损失几秒钟的加热,不会出现持续加热的失控场景。

4.4 温度曲线波动过大

如果你看到的温度曲线像锯齿一样上下乱跳,先别看PID参数,先检查采样数据本身。我遇到过传感器数据本身就带噪声的情况,原因是ESP32的电源用的是劣质开关电源,纹波太大,影响了模拟信号转换的稳定性。换了一个质量好的线性电源之后,数据干净多了。另外,在软件层面我加了简单的移动平均滤波,取最近五次的平均值作为展示值和控制值,这个处理基本看不出数据延迟,但波动明显减小。

如果不是数据噪声的问题,那就是PID参数太激进了,表现为温度在目标值附近振荡。这时候把比例增益调小一点,同时适当加大积分时间,让系统慢下来,稳定优先。

4.5 数据库会话状态异常

中途断电或者强制重启服务器,可能会让酿造会话状态卡在中间,前端一直显示某个步骤未完成。BrewUI的处理方式是在启动时扫描所有状态为“进行中”的会话,提示用户选择恢复或者终止。恢复的逻辑是回到上次完成步骤的末尾,重新加载后续步骤;终止的逻辑是保留已记录的数据,将会话标记为已中断,存档备查。

5. 功能增强与扩展空间

5.1 多用户权限与分享能力

目前的BrewUI本质上是一个单用户系统,登录鉴权只是简单的一层保护。不过在实际使用中,酿造圈的朋友互相交流配方是个很常见的需求。我后续计划加入一个轻量的用户系统,让每个用户可以创建公开配方或者私有配方,公开配方可以通过链接分享给其他人直接导入。这样不同酿造者之间交流配方会方便很多,不需要再发Excel表格或者手打的配方截图。

5.2 与自动化酿造设备深度集成

现在BrewUI的控制输出主要面向加热棒和循环泵,后续想接入更多设备。比如电控阀门的开关、水流计的流量记录、pH计的数据采集。这些设备的接入路径和温度传感器相似,都是走MQTT协议上报,后端增加对应的数据类型解析即可。架构上不需要大改,扩展性已经留好了。

5.3 发酵阶段的长期监控

与糖化和煮沸阶段的实时控制不同,发酵是一个持续数天甚至数周的过程。BrewUI目前对发酵的支持还比较基础,主要是温度和时间的记录。后面计划加入发酵度估算功能,通过记录起始和结束比重,自动计算表观发酵度,并在界面上画出发酵曲线。这样一瓶酒什么时候能倒桶、什么时候能装瓶,看一眼界面心里就有数了。

对于已经把设备凑齐的自酿爱好者来说,BrewUI这套东西最大的价值不是帮你把酿造变得全自动,而是让你每一次酿造的数据都有迹可循。我自己在用了半年之后,最大的变化是同一款配方的复现率明显提高——不是靠运气,而是靠每一锅记录下来的温度曲线和步骤日志。好的酿造不是玄学,是一组可量化的过程参数,配合一个能把参数记录下来的工具,仅此而已。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 10:11:18

ZYNQ7010开发板串口通信实战:PS/PL协同调试全链路解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 10:10:54

VSCode AI编程助手Continue配置与实战指南

1. 为什么要在编辑器里塞一个AI助手用VSCode写代码的人多少都动过这个念头:能不能让编辑器自己把那些重复的、模板化的、查文档才能写出来的代码直接补全?不是那种基于语法树的简单提示,而是真的理解上下文、能根据注释生成实现、能解释一段看…

作者头像 李华
网站建设 2026/9/19 10:06:16

系统镜像怎么装?U盘启动盘、ISO挂载与虚拟机三种方法详解

系统镜像文件的安装,说难不难,说简单也确实有不少坑。我这些年装过的系统少说也有上百台,从老旧的Windows XP到现在的Ubuntu、Debian,折腾来折腾去,发现很多人其实卡住的不是镜像下载,而是卡在“镜像拿到手…

作者头像 李华
网站建设 2026/9/19 10:05:50

大型园区网设计:分层架构、冗余配置与设备选型实战

简介:这是一份以西南交通大学大型园区网络设计为背景的组网方案PDF,围绕校园网建设从需求分析、总体设计原则到设备选型与层次化网络规划展开,适合网络工程、系统集成方向的在校学生与初级工程师参考。压缩包为单个PDF文件,包体大…

作者头像 李华