Android Things 智能家居网关实战:架构、外设与规则引擎
发布时间:2026/10/4 13:56:55 作者:尧图编辑部 阅读量:1,286

1. 为什么 Android Things 做智能家居是个看起来很美的选择2016 年前后智能家居赛道涌进来一大批开发者手里攥着树莓派、各种开发板脑子里想的都是我要做一个自己的中控。当时摆在面前的路无非几条要么用 ESP8266/ESP32 这类 Wi-Fi 模组配 Arduino 框架要么上 STM32 跑裸机或 RTOS要么干脆用一台旧手机或者树莓派跑 Linux。Android Things 是 Google 在 2016 年底端出来的另一条路——把 Android 框架裁剪后塞进嵌入式设备让写过 Android App 的人能直接上手做硬件。这个卖点当年确实打动了不少人。你不需要重新学一套工具链Android Studio 还是那个 Android StudioJava/Kotlin 还是那套语言Activity、Service、Handler这些概念原封不动搬过来只是把findViewById换成了PeripheralManager去拿 GPIO、I2C、UART 的句柄。对于移动端转过来的开发者学习曲线几乎是平的。但这里有个必须先说清楚的前提Android Things 的官方支持周期已经结束。Google 在 2019 年前后就停止了对新项目的推荐2020 年后官方镜像和云服务逐步下线。所以今天再谈基于 Android Things 构建智能家居系统更现实的意义是两类人一类是手里还有旧开发板比如 NXP i.MX7D、Raspberry Pi 3 Model B 的 AT 版本想物尽其用的另一类是想理解用高级操作系统做 IoT 网关这套架构思路的。架构思路本身不会过时STM32 方案和 Android Things 方案在系统分层上的思考是可以互相借鉴的。我个人的判断是如果你今天从零开始做一个量产级别的智能家居中控Android Things 不是首选ESP32 ESP-IDF 或者 STM32 FreeRTOS 在成本、功耗、供货稳定性上都更靠谱。但如果你是想学网关型设备怎么设计或者手上正好有块 AT 开发板那这套东西依然值得动手跑一遍。下面我按一个真实可落地的智能家居系统来拆从硬件选型一直讲到场景联动逻辑中间会穿插大量我在实际调试中踩过的坑。2. 系统分层把智能家居拆成能动手的四块2.1 网关层、设备层、通信层、应用层的职责边界很多人一上来就想我要做个 App 控制灯结果做到一半发现设备怎么发现、状态怎么同步、断网了怎么办全都没想清楚。智能家居系统本质上是一个分布式系统先分层再动手能省掉后面 80% 的重构。我习惯把它拆成四层设备层真正干活的末端节点比如灯、插座、温湿度传感器、窗帘电机。它们通常资源极其有限用 MCUSTM32、ESP8266 之类跑只负责采集和执行。通信层设备层和网关层之间的数据通道。短距离常用 Zigbee、BLE、433MHzWi-Fi 设备则直接走局域网。这一层决定了你的系统能挂多少设备、响应有多快。网关层也就是 Android Things 开发板所在的位置。它负责协议转换、设备管理、本地自动化规则执行、和云端同步。这是整个系统的大脑也是本文的主角。应用层手机 App、Web 面板、语音助手。用户看到和操作的部分。Android Things 的价值集中在网关层。它比 STM32 网关强的地方在于能跑完整的网络协议栈、能开本地数据库、能做复杂的规则引擎、能直接对接云 API。而 STM32 做网关往往受限于内存和算力规则引擎只能做很简单的 if-else。2.2 为什么网关要选能跑操作系统的方案这里要解释一个关键取舍。假设你有 30 个设备每个设备每秒上报一次状态网关要做的处理包括解析协议、更新本地状态表、判断是否触发联动规则、把变化推给 App、定期同步云端。这一套下来裸机 MCU 用状态机硬写也能实现但代码会迅速变成一团乱麻而且一旦要加一个新协议比如从 Zigbee 加一个 BLE 设备整个状态机都要动。跑操作系统的网关可以把这些任务拆成独立的线程或进程一个线程收数据一个线程跑规则引擎一个线程管云端同步彼此通过消息队列通信。Android Things 基于 Android天然有HandlerThread、Service、LiveData这些工具写起来比裸机舒服太多。代价是功耗和成本上去了——一块 i.MX7D 开发板功耗在瓦级而 STM32 网关可以做到毫瓦级。所以如果你的设备是电池供电、要求几个月续航网关这层就不该用 Android Things而应该让设备直连云端或者用低功耗 Mesh。2.3 一张表看清 Android Things 网关和 STM32 网关的取舍维度Android Things 网关STM32 网关典型主控i.MX7D、RPi3STM32F4/F7/H7内存512MB~1GB128KB~1MB功耗瓦级毫瓦级开发语言Java/KotlinC协议栈丰富度高可跑完整 TCP/IP、MQTT、HTTP中需自己裁剪规则引擎复杂度可做复杂逻辑一般只做简单联动成本高低适合场景常供电中控、协议转换网关电池设备、低成本节点这张表不是要分高下而是帮你判断自己的项目该站哪一边。我见过有人拿 STM32 硬做复杂规则引擎最后代码维护不动也见过有人拿 Android Things 做电池传感器两天就没电。选型错了后面全是坑。3. 硬件准备PeripheralManager 能碰到的和碰不到的3.1 开发板与外围器件的实际搭配Android Things 官方支持过的板子里最常用的是 NXP i.MX7D 和 Raspberry Pi 3 Model B。i.MX7D 的优点是自带 NXP 的引脚扩展GPIO、I2C、PWM 都规整RPi3 的优点是便宜好买但它的 GPIO 在 Android Things 下的映射和树莓派原生 Linux 不完全一样需要查官方文档的引脚表。外围器件我建议这样配一套最小系统一块 OLED 屏I2C 接口SSD1306 驱动用来显示网关状态和 IP。一个温湿度传感器DHT22 或 SHT30前者用 GPIO 单总线后者用 I2C。一个继电器模块控制一盏灯。一个按钮做本地手动开关。一个 RGB LED做状态指示。这套下来成本不高但能把 GPIO 输入输出、I2C 读写、PWM 调光这几类操作全跑一遍。等你把这几个跑通接真实设备只是换驱动的事。3.2 引脚映射的坑别照抄树莓派教程这是我最想提醒的一点。网上大量树莓派 GPIO 控制的教程是给 Raspbian 写的用的是 BCM 编号或者 WiringPi 编号。Android Things 用的是自己的命名体系比如PeripheralManagerService里通过GPIO_12这样的字符串去拿引脚这个编号对应的是板子的物理引脚还是 SoC 引脚不同板子不一样。我当年第一次接 DHT22照着某篇树莓派教程接了 BCM4结果 Android Things 里死活读不到数据。折腾了半天才发现AT 下 RPi3 的引脚命名和 BCM 编号不是一一对应的得查官方那张Raspberry Pi 3 pinout表。所以我的建议是接线前先把官方引脚表打印出来贴在桌上每接一根线就在表上标一下别凭记忆。3.3 供电和电平匹配3.3V 与 5V 混用的处理Android Things 开发板的 GPIO 基本都是 3.3V 逻辑。但很多继电器模块、传感器是 5V 供电和 5V 逻辑。直接把 5V 信号灌进 3.3V 的 GPIO轻则读不准重则烧引脚。处理办法有两个一是选 3.3V 兼容的模块现在很多继电器模块支持 3.3V 触发二是加电平转换电路比如用 TXS0108E 这类双向电平转换芯片。我一般优先选前者省事。如果非要用 5V 模块至少加个分压电阻或者光耦隔离别省这几毛钱。供电上还有个细节开发板和继电器最好分开供电。继电器吸合瞬间电流冲击比较大如果和开发板共用一路电源可能导致开发板复位。我吃过这个亏网关跑着跑着突然重启查了半天是继电器把电压拉下去了。4. 从点亮一个 LED 到跑通完整外设链路4.1 用 PeripheralManager 打开 GPIO 的标准姿势Android Things 里操作外设的入口是PeripheralManager。拿一个 GPIO 输出引脚的代码大概长这样PeripheralManagerManager manager PeripheralManager.getInstance(); Gpio ledGpio manager.openGpio(GPIO_12); ledGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW); ledGpio.setValue(true); // 点亮看起来简单但有几个点新手容易忽略。第一openGpio的字符串必须和板子文档一致写错了会抛IOException。第二setDirection要在setValue之前调用否则行为未定义。第三用完记得close()不然这个引脚会被一直占用下次打开就失败。我建议把每个外设的打开和关闭封装成一个AutoCloseable的包装类用 try-with-resources 管理避免忘记释放。4.2 I2C 读传感器寄存器地址和时序I2C 设备操作比 GPIO 复杂一点因为要理解寄存器这个概念。以 SHT30 温湿度传感器为例它的 I2C 地址是 0x44读温度要先发一个测量命令比如 0x2C06等一段时间再读 6 个字节前 3 个是温度后 3 个是湿度每个值 2 字节数据加 1 字节 CRC。I2cDevice device manager.openI2cDevice(I2C1, 0x44); byte[] command {(byte)0x2C, (byte)0x06}; device.write(command, 2); Thread.sleep(20); byte[] buffer new byte[6]; device.read(buffer, 6); // 解析 buffer...这里的坑在于不同传感器的命令字和时序完全不同必须对着数据手册写。而且Thread.sleep的时间不能太短SHT30 高重复性模式下测量要 15ms 左右你睡 5ms 读出来就是脏数据。我一般会留 20~30ms 余量。4.3 把外设封装成可复用的驱动层当你接了五六个外设之后如果每个都直接在主逻辑里openGpio、openI2cDevice代码会非常乱。我的做法是给每个外设写一个驱动类比如LedDriver、Sht30Driver、RelayDriver对外只暴露read()、write()这样的方法内部处理引脚、时序、CRC 校验。这样做的好处是主业务逻辑只关心读温度这个动作不关心它是 I2C 还是单总线。将来换传感器只改驱动类业务代码不动。这也是从 STM32 那套 HAL 库学来的思路——分层永远是对的。4.4 一个真实的调试案例DHT22 读出来全是 0DHT22 是单总线协议对时序要求很苛刻微秒级。Android Things 跑在 Linux 内核上GPIO 的读写延迟受调度影响抖动可能到毫秒级。我当时读 DHT22返回的数据全是 0 或者校验失败。排查过程是这样的先用逻辑分析仪抓了一下波形发现 Android Things 发出的起始信号宽度就不对本该 1ms 的低电平实际有 1.5ms 还带抖动。这说明在用户态做微秒级时序控制本身就不靠谱。最后的解决方案是换传感器改用 I2C 的 SHT30。I2C 有时钟线同步对时序抖动容忍度高得多。这个教训很深刻在跑操作系统的平台上别碰对时序要求到微秒级的单总线协议除非你能写内核驱动。这也是为什么很多 Android Things 项目最后都选 I2C 和 UART 设备。5. 通信协议选型设备怎么把数据送到网关5.1 Wi-Fi、Zigbee、BLE 在网关侧的接入差异设备层和网关层之间的通信直接决定了系统能挂多少设备、响应多快、功耗多低。Wi-Fi 设备接入最简单设备直接连路由器网关通过局域网 socket 或者 MQTT 和它通信。优点是协议通用缺点是 Wi-Fi 设备功耗高、路由器能挂的终端数有限家用路由器一般 20~30 个就吃力了。Zigbee 设备功耗低、能自组网一个网关能挂几十上百个。但 Android Things 开发板本身没有 Zigbee 射频你得外接一个 Zigbee 协调器模块比如 CC2530 或 CC2652通过 UART 和网关通信。这就多了一层协议转换。BLE 介于两者之间适合低功耗小数据量场景但连接数也有限。我的建议是小规模10 个设备以内用 Wi-Fi 直连中大规模用 Zigbee 外挂协调器。BLE 更适合做配网辅助比如用手机给设备配 Wi-Fi 密码。5.2 MQTT 主题设计别用一堆平铺的 topic网关和云端、App 之间我强烈建议用 MQTT。它轻量、支持发布订阅、断线重连机制成熟。但主题topic设计有讲究。新手常犯的错是设计成device1/temp、device2/temp这种平铺结构设备一多就乱。我习惯用层级结构home/{room}/{deviceType}/{deviceId}/state home/livingroom/light/001/state home/bedroom/sensor/003/telemetry这样订阅的时候可以用通配符比如home/livingroom/#订阅客厅所有设备home//sensor//telemetry订阅所有传感器数据。规则引擎处理起来也方便按房间、按类型批量操作。5.3 本地优先断网时系统还得能跑这是智能家居系统设计里最容易被忽视、但用户最在意的一点。用户按开关灯必须立刻亮不能等云端转一圈。所以联动规则必须在网关本地执行云端只做远程控制和数据备份。我的做法是网关本地维护一份设备状态表和规则表规则引擎在本地跑。云端下发的指令走 MQTT 到网关网关执行后再把结果同步回云端。断网时本地规则照常工作只是 App 远程控制失效。等网络恢复网关把离线期间的状态变化批量同步上去。这个本地优先的原则无论你用 Android Things 还是 STM32 网关都必须遵守。我见过太多系统把联动逻辑放云端结果一断网整个家就傻了。6. 本地规则引擎让设备自己会思考6.1 规则的数据结构设计规则引擎的核心是把如果……就……这种逻辑变成可存储、可执行的数据。我一般用这样的结构{ id: rule_001, enabled: true, trigger: { type: device_state, deviceId: sensor_003, field: temperature, operator: , value: 28 }, condition: { type: time_range, start: 22:00, end: 06:00 }, action: { type: device_command, deviceId: ac_001, command: on, params: {mode: cool, temp: 26} } }这样一条规则表示卧室温度超过 28 度且在夜间时段就打开空调制冷到 26 度。用 JSON 存好处是 App 端可以直接编辑网关端解析执行云端也能同步。6.2 触发、条件、动作三段式执行执行逻辑分三步先判断触发条件是否满足温度是否超阈值再判断附加条件是否在时间段内最后执行动作发指令给空调。任何一步不满足就跳过。这里有个性能细节如果每次设备上报都遍历所有规则设备一多就会卡。我的优化是给规则建索引按deviceId分组设备上报时只检查涉及这个设备的规则。这样即使有几百条规则单次检查也是毫秒级。6.3 防抖与去重避免规则被反复触发温度在 28 度上下浮动时规则会被反复触发空调开开关关体验极差。解决办法是加滞回hysteresis触发阈值和恢复阈值分开。比如超过 28 度开空调但要降到 26 度以下才认为事件结束中间这段不重复触发。另外还要加时间窗去重同一条规则在 N 秒内只执行一次。我用的是lastExecutedAt时间戳执行前检查距上次执行是否超过冷却时间。这两个机制加上规则引擎就稳了。7. 云端同步与 App 通信的工程细节7.1 状态同步全量还是增量网关和云端同步状态有两种策略。全量同步是定期把整个状态表推上去简单但流量大增量同步是只推变化的部分省流量但需要处理丢包和乱序。我的做法是混合状态变化时立即推增量同时每隔几分钟做一次全量校验。这样既保证实时性又能纠正长时间运行后的状态漂移。增量消息里带上版本号或者时间戳云端收到旧版本就丢弃避免乱序覆盖。7.2 设备发现与配网流程新设备加入系统需要一个配网流程。Wi-Fi 设备常见的是 SmartConfig 或者 AP 配网设备进入配网模式App 把 Wi-Fi 密码传过去设备连上后向网关注册。网关这边要维护一个待认领设备列表。设备注册时带上自己的类型和 ID网关先把它放进待认领区App 上弹出提示让用户确认这是客厅的灯确认后才正式纳入系统。这个确认步骤很重要否则设备一多你根本分不清哪个是哪个。7.3 安全别让智能家居变成裸奔安全这块我必须多说几句。很多 DIY 智能家居系统MQTT 不设密码、局域网内谁都能发指令这在真实家庭环境里是灾难。最低限度的安全措施包括MQTT 启用用户名密码和 TLS设备注册时用一次性 token 验证网关的本地 API 只监听局域网不暴露到公网App 和网关之间的通信加密。这些都不难做但省掉任何一条你的系统就是个敞开的门。8. 我在实际项目里踩过的几个典型坑8.1 内存泄漏Service 没停导致的 OOMAndroid Things 虽然裁剪过但毕竟还是 Android内存管理那套东西还在。我有个项目跑了两天就崩查 log 发现是 OOM。原因是我的设备监听 Service 在设备重连时反复创建旧的没销毁每个都持有PeripheralManager的引用。解决办法是给 Service 加单例控制重连时复用已有实例只重新注册回调。另外用LeakCanary在开发阶段监控内存能提前发现这类问题。嵌入式设备内存本来就紧张泄漏积累起来比手机崩得还快。8.2 看门狗与自动恢复网关死机了怎么办网关是常供电设备跑几个月不死机是基本要求。但现实是再稳的系统也可能因为某个驱动异常卡住。所以必须加看门狗机制。Android Things 层面我用了两个手段一是硬件看门狗如果开发板支持定时喂狗超时自动重启二是软件层面的心跳检测主线程定期检查各子线程是否存活发现卡死就主动重启对应 Service。双保险下来系统连续跑几个月基本没问题。8.3 固件升级OTA 在 Android Things 上的现实Android Things 官方提供过 OTA 更新机制但官方服务下线后这套东西就得自己搭。我的做法是网关启动时检查一个固定的更新服务器有新版就下载 APK 静默安装然后重启应用。这里要注意的是升级过程要能回滚。如果新版 APK 启动就崩设备会陷入升级-崩溃-再升级的死循环。所以我在升级前会备份当前版本新版本启动后如果 N 秒内没上报心跳就自动回滚到旧版。这个机制救过我好几次。9. 从 Android Things 迁移到其他平台的思路如果你看完上面这些觉得 Android Things 官方支持没了不放心想迁移到别的平台其实架构思路是通用的。网关层的职责——协议转换、设备管理、本地规则引擎、云端同步——在任何平台上都一样。迁移到 Linux比如树莓派跑 Raspberry Pi OS是最平滑的因为 Android Things 本身就是 Linux 内核你的外设驱动逻辑、MQTT 通信、规则引擎几乎可以照搬只是把 Java 换成 Python 或者 C把PeripheralManager换成gpiod、smbus这些库。迁移到 STM32 则要重新考虑资源约束规则引擎得简化协议栈要裁剪但本地优先、分层解耦这些原则不变。我甚至见过有人把网关拆成两级STM32 做实时性要求高的设备接入Android Things 或 Linux 做上层规则和云同步各取所长。说到底Android Things 只是实现智能家居网关的一种手段真正值钱的是你对这套系统分层的理解。工具会过时架构思路不会。把上面这些跑通一遍你换任何平台都能快速搭起来。