# SmartHomeNodeGO **Repository Path**: yjygit/SmartHomeNodeGO ## Basic Information - **Project Name**: SmartHomeNodeGO - **Description**: 这是一个go编写的用于智能网关、路由器的后台小型管理系统。 特点: 1、包含了前后端、设备连接端。 2、尽量少的使用第三方包,已保能运行在大多数MIPS架构CPU的路由器上。 3、路由器需要使用到扩展容量卡,硬盘或U盘,至少需要50M存储空间。 - **Primary Language**: Go - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 4 - **Forks**: 1 - **Created**: 2021-01-28 - **Last Updated**: 2026-08-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # SmartHomeNodeGO #### 视频介绍 https://www.bilibili.com/video/BV1dh411C7QX/ #### 介绍 用go编写的可用于智能路由器Web端管理系统。\ 集成了 HTTP服务 和 TCP 连接服务。\ 可给智能家居设备提供服务。\ 可理解为边缘计算。 #### 特点 1. 无需安装,直接运行,所有服务会自动关联。 2. 包含了前端,后端,连接端,定时任务,文件存储。 3. 使用拓展硬盘或U盘,告别容量拮据且读写缓慢的的flash。 4. 尽量少的使用第三方包,确保能运行在大多数MIPS架构CPU的路由器上。 #### 软件架构 1. 后端使用GO开发,前端使用VueJs开发。 2. 后端框架使用gin,前端框架使用 vue-admin + element-ui。 3. 没有使用数据库,以json文件存储设备数据,简单使用了go-cache缓存。 #### 安装教程 * 如要修改本代码,请安装 GoLang 1.14+ 环境。 * 项目使用 go mod 管理包,使用以下命令: ``` bash # 初始化 go mod 文件 go mod init smartHomeNode/v1 # 安装依赖 go mod tidy # 基础运行(默认端口 8080 ): go run main.go # 指定端口: go run main.go -p 8081 # 系统初始化: go run main.go -c cc ``` #### 交叉编译 ``` bash # 路由器 cpu RT-5350: GOOS=linux GOARCH=mipsle GOMIPS=softfloat CGO_ENABLED=0 go build main.go # 树莓派 B+: GOOS=linux GOARCH=arm GOARM=6 CGO_ENABLED=0 go build main.go # 树莓派 3B: GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=0 go build main.go # Linux 64位: Linux: GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build main.go Windows: set GOARCH=amd64 set GOOS=linux set CGO_ENABLED=0 go build main.go *说明: GOOS:目标平台的操作系统(darwin、freebsd、linux、windows) GOARCH:目标平台的体系架构(386、amd64、arm、mipsle) 交叉编译不支持 CGO 所以要禁用它(CGO_ENABLED=0) ``` #### 使用教程 ``` bash 1. 基础运行(默认端口 8080 ): ./main 2. 指定端口: ./main -p 8081 3. 初始化运行: ./main -c cc * 可能需要添加权限,linux 系统下请使用: chmod +x main ``` #### 使用说明 1. 请确保路由本机时间准确。 2. 确保防火墙允许连接服务开启的端口。 3. Web服务默认 8080 端口,TCP 连接使用 50001 端口。 4. 如需使用 80端口 请确保路由器原本后台服务已经改到别的端口。 5. *ESP8266 MINI D1 控制代码在项目 arduino/ESP8266-MINI-D1.ino ## 物联网设备接入协议 本协议用于局域网内的执行器(灯、开关、继电器等)和传感器接入 SmartHomeNodeGO。生产环境默认地址如下: | 用途 | 传输层 | 默认端口 | 环境变量 | | --- | --- | ---: | --- | | 执行器注册、保活、状态上报和控制 | TCP | `50001` | `SMARTHOME_TCP_SERVER` | | 温湿度传感器数据上报 | UDP | `50005` | `SMARTHOME_UDP_SERVER` | 服务端地址是运行 SmartHomeNodeGO 的主机 IP,例如当前局域网服务器为 `10.1.8.2`。协议没有设备鉴权和传输加密,只应在可信局域网内使用,不能直接暴露到公网。 ### 1. 通用报文格式 设备上行报文由三个字段组成,字段之间使用 `/:` 分隔: ```text <命令>/:<设备名或固定字段>/:<值> ``` - 命令区分大小写,必须使用大写。 - 报文不需要 JSON,也不要求在末尾添加换行符。 - 设备名应在同一套系统中保持唯一,建议只使用字母、数字、`-` 和 `_`。 - 单条 TCP 报文应控制在 128 字节以内,单条 UDP 报文应控制在 64 字节以内。 - 服务端处理完一条合法命令后返回 ASCII 文本 `SUCCESS`;命令不支持或控制失败时返回 `FAIL`。 ### 2. TCP 执行器接入 #### 快速接入流程 1. 建立到 `<服务器 IP>:50001` 的长 TCP 连接。 2. 发送 `LOGIN/:NAME/:<设备名>`,读取服务端返回的 `SUCCESS`。 3. 立即使用 `STATUS/:<设备名>/:<状态>` 上报当前真实状态。 4. 保持连接并接收平台下发的控制数据。 5. 每 25 至 30 秒重新发送一次 `LOGIN` 作为心跳;断线后自动重连并重新执行以上步骤。 6. 本地状态变化或执行平台命令后,使用 `STATUS` 上报最新真实状态。 #### 设备上行报文 | 场景 | 报文 | 服务端响应 | 说明 | | --- | --- | --- | --- | | 注册/心跳 | `LOGIN/:NAME/:TEST001` | `SUCCESS` | `NAME` 是固定字段,`TEST001` 是唯一设备名 | | 上报开启 | `STATUS/:TEST001/:ON` | `SUCCESS` | 前端显示设备已开启 | | 上报关闭 | `STATUS/:TEST001/:OFF` | `SUCCESS` | 前端显示设备已关闭 | | 上报功率值 | `STATUS/:TEST001/:128` | `SUCCESS` | 数值范围建议为 `0` 到 `255` | `LOGIN` 只负责注册连接和刷新在线时间,不代表设备的开关状态。设备连接成功后必须再发送一次 `STATUS`,否则控制台可能只能看到设备在线,无法显示准确状态。 #### 平台下发报文 设备需要持续读取同一条 TCP 连接。平台可能下发以下两类 ASCII 数据: ```text TEST001:ON TEST001:OFF 128 ``` - 开关命令带设备名前缀,格式为 `<设备名>:ON` 或 `<设备名>:OFF`。 - 功率/亮度命令直接下发十进制数值,当前范围为 `0` 到 `255`,没有设备名前缀。 - 设备应忽略设备名与自身不一致的开关命令。 - 执行完成后应主动发送对应的 `STATUS` 报文确认真实状态。平台对下发报文本身不等待设备 ACK,状态确认以设备后续的 `STATUS` 为准。 > TCP 是字节流。固件不能假设一次 `read` 必然只得到一条完整命令;应保留接收缓冲区,能够处理拆包和连续到达的数据。当前协议没有显式结束符,建议每次等待并处理一个完整的已知命令,并避免在同一次写操作中拼接多条上行报文。 #### TCP 交互示例 ```text 设备 -> 平台: LOGIN/:NAME/:TEST001 平台 -> 设备: SUCCESS 设备 -> 平台: STATUS/:TEST001/:OFF 平台 -> 设备: SUCCESS 平台 -> 设备: TEST001:ON 设备执行开灯 设备 -> 平台: STATUS/:TEST001/:ON 平台 -> 设备: SUCCESS 设备 -> 平台: LOGIN/:NAME/:TEST001 # 25 至 30 秒心跳 平台 -> 设备: SUCCESS ``` #### 固件主循环伪代码 ```text connect(server, 50001) send("LOGIN/:NAME/:" + deviceName) expect("SUCCESS") send("STATUS/:" + deviceName + "/:" + currentState) while connected: if heartbeatElapsed(25 seconds): send("LOGIN/:NAME/:" + deviceName) command = receiveIfAvailable() if command == deviceName + ":ON": turnOn() send("STATUS/:" + deviceName + "/:ON") else if command == deviceName + ":OFF": turnOff() send("STATUS/:" + deviceName + "/:OFF") else if command is integer from 0 to 255: setPower(command) send("STATUS/:" + deviceName + "/:" + command) on disconnect: wait with backoff, reconnect, login and report current state ``` ### 3. UDP 温湿度传感器接入 传感器不需要建立长连接。向 `<服务器 IP>:50005` 发送一个 UDP 数据报,然后等待同一地址返回 `SUCCESS`。若超时,可间隔数秒重试,避免高频无限重发。 | 数据类型 | 上报示例 | 值格式 | | --- | --- | --- | | 温度 | `TEMP/:ROOM01/:23.6` | 摄氏温度数值,不带单位 | | 湿度 | `HUM/:ROOM01/:58.2` | 相对湿度数值,不带 `%` | 示例: ```text 传感器 -> 平台: TEMP/:ROOM01/:23.6 平台 -> 传感器: SUCCESS 传感器 -> 平台: HUM/:ROOM01/:58.2 平台 -> 传感器: SUCCESS ``` 建议每 30 至 60 秒上报一次。服务端按设备名分别保存温度和湿度,因此同一个物理传感器可以用同一设备名上报两种数据。 ### 4. 在线和数据有效期 - TCP 设备的在线注册信息在最后一次 `LOGIN` 或 `STATUS` 后约 3 分钟过期。 - 设备开关/功率状态在最后一次 `STATUS` 后约 90 秒过期。 - 温湿度最新值在最后一次 UDP 上报后保留约 60 小时。 - TCP 连接关闭时,服务端会立即清除该连接关联的在线信息和设备状态。 因此执行器应保持 TCP 长连接,并按 25 至 30 秒发送心跳;即使状态未变化,也建议至少每 60 秒重新上报一次 `STATUS`,以保证控制台持续显示准确状态。 ### 5. 联调工具与参考实现 - ESP8266 开关参考:`arduino/ESP8266_MINI_D1_LED.ino` - ESP8266 温湿度参考:`arduino/ESP8266_MINI_D1_DHT11.ino` - Windows 图形化开关模拟器:`script/py/switch_device_tester.py` - 模拟器使用说明:`script/py/switch_device_tester.md` 联调时不要让测试设备连接生产端口。项目隔离预览默认使用 TCP `15001`、UDP `15005`,仅在对应预览服务已启动时使用这些端口。 ## 路由器部署与程序包制作(iStoreOS / OpenWrt) 本章记录在生产路由器(iStoreOS 24.10.x)上的部署方式、自启 watchdog 脚本, 以及一次真实的踩坑。**制作部署程序包或手工部署前务必先读本章。** ### 1. 当前生产环境 | 项目 | 值 | | --- | --- | | 路由器 | iStoreOS 24.10.5,`192.168.100.1`(x86_64) | | 部署目录 | `/data/SmartHome`(U 盘/外置存储,勿放 flash) | | Web/API | TCP `8080`(前端 `dist/` 与 `/api/v2`、`/api` 同端口) | | 设备协议 | TCP `50001` | | 温湿度上报 | UDP `50005` | | 健康检查 | `GET /healthz` | | 自启入口 | `/etc/rc.local` 调用 `/data/SmartHome/run.sh` | 部署目录 `/data/SmartHome` 下结构: ```text /data/SmartHome/ ├── main # 交叉编译的 Linux amd64 二进制(无需安装) ├── dist/ # 前端构建产物(index.html + assets/) ├── data/ # 运行时数据(Alias/、Sensor/、Week/、switch.json、time.json) ├── run.sh # 自启 watchdog(见下) ├── start.sh # 手动启动辅助脚本(注意:OpenWrt 无 nohup,不可直接跑) ├── stop.sh # 手动停止 ├── backend.pid # 记录当前 main 进程 pid └── deploy_main.log # 本次手工启动时附加的首进程日志(可选) ``` ### 2. 自启 watchdog:run.sh 系统重启后由 `/etc/rc.local` 拉起。它只**负责拉起** main,不做 kill;每 100 秒 探测一次,main 不在就重启。**注意:main 一旦丢进程,是靠这个脚本自愈的,所以 脚本里启动命令必须与要启用的功能一致。** ```sh #!/bin/sh # SmartHomeNode Start... # Date: 2024-02-27 # 2026-08-30: 与 start.sh 保持一致,watchdog 拉起 main 时启用 legacy 接口及 TCP/UDP 端口 # (修复 /api/button/control/:name/:ON|OFF 等老控制接口返回 NOT_FOUND 的问题)。 sleep 15 while true do if ps |grep main|grep -v grep;then echo "SmartHomeNode Server OK..." else echo "service starting..." cd /data/SmartHome export SMARTHOME_ENABLE_LEGACY_API=1 export SMARTHOME_TCP_SERVER=":50001" export SMARTHOME_UDP_SERVER=":50005" ./main >/dev/null 2>/dev/null & fi sleep 100 done ``` `/etc/rc.local` 中的相关自启行(OpenWrt 以 `/bin/sh ... &` 方式拉起): ```sh /bin/sh /data/SmartHome/run.sh >/dev/null 2>&1 & ``` > 说明:`main` 的后台启动用 `&` 即可(OpenWrt 无 `nohup`,`start.sh` 里的 `nohup` > 在该平台跑不起来,所以自启必须以 `run.sh` 为准)。 ### 3. 踩坑说明:legacy 控制接口返回 NOT_FOUND **现象**:部署后 `/api/v2/devices`、`/api/v2/scenes` 等新接口正常,但老的 `GET /api/button/control/{设备名}/ON|OFF`、`/api/button/delay/...` 返回 `{"code":"NOT_FOUND","msg":"not found"}`。 **根因**:legacy 路由由环境变量 `SMARTHOME_ENABLE_LEGACY_API` 控制(见 `routes/api.go` 中 `config.LegacyAPIEnabled()`)。自启 watchdog `run.sh` 用裸 `./main` 启动,**没有设置该环境变量**,因此 legacy 路由未注册。对比 `start.sh` 里写了 `SMARTHOME_ENABLE_LEGACY_API=1`,但它不是自启入口(且依赖 OpenWrt 不存在的 `nohup`),所以真正在跑的是 `run.sh` 拉起的无 legacy 进程。 **修复**:在 `run.sh` 拉起 `main` 前 `export SMARTHOME_ENABLE_LEGACY_API=1`(并 同时显式 `SMARTHOME_TCP_SERVER=:50001`、`SMARTHOME_UDP_SERVER=:50005`,与 `start.sh` 保持一致)。改脚本后需**主动杀掉旧的 main 进程**,因为 watchdog 只负责 拉起、不负责热更;必要时同样重启 watchdog 进程使其加载新版脚本。 **验证方法**(在路由器本机): ```sh curl -s http://127.0.0.1:8080/healthz curl -s -o /dev/null -w "%{http_code}\n" "http://127.0.0.1:8080/api/button/control/CHIP06/ON" # 期望 200 # 修复前该请求返回 404 / NOT_FOUND ``` > 注意:legacy 接口无鉴权,只能用于可信家庭局域网调试,生产不应暴露到 WAN。 ### 4. 制作部署程序包 checklist - **先在本仓库交叉编译**生成 `main`(Linux amd64:`GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build main.go`;路由为 MIPS 时见上文“交叉编译”节)。 - **附带 `dist/` 前端产物**(Vue3 构建后复制到同目录),`main` 会在同端口提供页面与 API。 - **附上 `run.sh`(用上面第 2 节的版本)**,并确保里面 export 了 `SMARTHOME_ENABLE_LEGACY_API=1` 与 TCP/UDP 端口,避免重蹈 NOT_FOUND 覆辙。 - **`data/` 目录放空或自带初始数据**(首次运行 `./main -c cc` 初始化;`data/` 必须是 可写存储,勿放只读 flash)。 - 提供 `start.sh`(手动启动,含环境变量)与 `stop.sh`;仅作辅助,自启以 `run.sh` 为准。 - 部署到路由器后: 1. 写入 `/etc/rc.local` 自启行并 `chmod +x`; 2. 首次手工跑 `./main`(或 `./start.sh`)并 `curl /healthz` 确认; 3. 用 `/api/v2/devices` 确认设备可发现、用 legacy `/api/button/control/..` 确认能控制; 4. 重启一轮路由器,确认 watchdog 自愈有效(kill 掉 main 后再等约 100 秒会自拉起)。 #### 项目地址 SERVER: \ github: https://github.com/crazyjy/SmartHomeNodeGO \ gitee: https://gitee.com/yjygit/SmartHomeNodeGO H5: \ github: https://github.com/crazyjy/SmartHomeNodeH5 \ gitee: https://gitee.com/yjygit/SmartHomeNodeH5 #### 参与贡献 1. 【飞鱼智能】- 淘宝店,欢迎选购。\ https://shop223999585.taobao.com/?spm=2013.1.1000126.d21.25751acaxy0uZy