# ubibot-open-simulator **Repository Path**: ubibot/ubibot-open-simulator ## Basic Information - **Project Name**: ubibot-open-simulator - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-14 - **Last Updated**: 2026-09-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # UbiBot Open Simulator 一个可在主机上构建的 [UbiBot Open](https://gitee.com/ubibot-open) 设备模拟器,按嵌入式 C 的风格编写: 它能在 Windows/Linux 上编译运行,并且与真实 UbiBot 传感设备 ([ubibot-open-ws1b](https://gitee.com/ubibot-open/ubibot-open-ws1b))对接 [ubibot-open-server](https://gitee.com/ubibot-open/ubibot-open-server) 时使用的 HTTP 协议完全一致—— 以明文 pid+sn 作为身份(没有密钥、没有签名、没有激活步骤),定期采样三个内置传感器 (`field1` 温度 / `field2` 湿度 / `field3` 光照)并批量上报。服务端第一次见到某个 `sn` 时会自动创建 对应的设备记录。 **这份 C 代码不只是仅供模拟用的示例代码——它的设计目标是同时充当真实 FreeRTOS 固件的源码。** 分层方式见下面的"目录结构",移植方法见 [`freertos_port/README.md`](freertos_port/README.md)。 ## 快速开始 ```bash # 方式 1:CMake(推荐,Windows/Linux 上用法相同) cmake -S . -B build cmake --build build ctest --test-dir build # 纯逻辑单元测试,不需要运行中的服务端 # 方式 2:Makefile(gcc / mingw-w64) make # 构建 ub_device_sim 并运行单元测试 ``` 在 Windows 上使用 MinGW: ```bash cmake -S . -B build -G "MinGW Makefiles" cmake --build build ``` 或者直接用 mingw-w64 交叉编译(在 Linux 上生成 Windows 的 .exe): ```bash x86_64-w64-mingw32-gcc -std=c11 -Icore/include -Itransport/include -Iapp/include \ -o ub_device_sim.exe app/src/main.c core/src/ub_json.c core/src/ub_protocol.c \ transport/src/ub_transport_sockets.c app/src/ub_platform_host.c \ app/src/ub_sensors_sim.c app/src/ub_device.c -lws2_32 ``` 运行模拟设备(默认参数与 [ubibot-open-server](https://gitee.com/ubibot-open/ubibot-open-server) 的 `cmd/server/main.go` 预置的演示设备一致,因此在本地用 `go run ./cmd/server` 启动服务端后,不带任何参数 运行它就能直接用;由于协议已经不再要求预先注册/激活设备,换一个新的 `--sn` 也同样可行——服务端会在 首次上报时自动创建记录): ```bash ./build/ub_device_sim --host 127.0.0.1 --port 8080 ``` 可选参数:`--pid`、`--sn`、`--tick-ms`(主循环心跳间隔,默认 1000ms)。按 `Ctrl+C` 停止。 没有 `--secret` 参数(协议里根本没有密钥/签名的概念),也没有 `--data-dir` 参数(见下面的"目录结构" ——在这套协议下,设备没有任何值得跨重启保存的状态)。 ## 目录结构 ``` ubibot-open-simulator/ ├── core/ 零操作系统依赖:协议编解码,在任何平台上都无需改动 │ ├── include/ ub_json.h ub_protocol.h │ └── src/ 对应的实现——全程使用固定大小的缓冲区,不用 malloc ├── transport/ HTTP 传输接口 + 一个仅用于主机本地测试的 socket 实现 │ ├── include/ ub_transport.h(只有一个函数指针:post) │ └── src/ ub_transport_sockets.c——这里唯一"仅限主机"的文件;在 FreeRTOS 上 │ 替换为基于 lwIP/厂商 TLS 协议栈的实现 ├── app/ 设备状态机 + HAL 头文件的主机端实现 │ ├── include/ ub_platform.h ub_sensors.h ub_device.h │ └── src/ ub_device.c(状态机,与平台无关)+ ub_platform_host.c / │ ub_sensors_sim.c(主机端 HAL 实现,在 FreeRTOS 上被替换) ├── test/ 单元测试 + 一个集成测试(需要运行中的服务端)+ 用于手动联调的 │ mock_server.py ├── freertos_port/ FreeRTOS 移植说明和参考骨架代码(不参与编译,仅作映射参考) ├── CMakeLists.txt └── Makefile ``` 换句话说,**只有三个文件是仅限主机的**:`transport/src/ub_transport_sockets.c`、 `app/src/ub_platform_host.c` 和 `app/src/ub_sensors_sim.c`。整个 `core/`、状态机本身 (`app/src/ub_device.c`),以及 `app/include/*.h` 下定义的接口,都可以原封不动地放进 FreeRTOS 项目。 所谓移植,就是分别为 `ub_platform.h` / `ub_sensors.h` / `ub_transport.h` 各写一个 FreeRTOS 端的实现 文件,替换掉那三个 `_host`/`_sim`/`_sockets` 文件。 这套协议没有会话令牌,也没有服务端推送的配置/指令通道,所以设备端**没有任何需要持久化的状态** (早先的 `ub_storage.h` 持久化层——用于保存令牌、探头表和待确认队列——已随这些功能一并彻底移除): `ub_device_init()` 总是从已知的 pid/sn 和固定的采样/上报间隔重新开始,因此一个全新的进程和"重启后 恢复运行"是完全无法区分的。 ## 已验证的行为(在本仓库中实际跑过,而不只是声称) - 在 Linux 上用 gcc 原生构建:`ub_device_sim` + `test_protocol`(47/47)全部通过,零警告。 - 针对一个最小化的模拟 HTTP 服务端(`test/mock_server.py`,仅用于手动联调,不属于构建的一部分)测试了 完整的设备生命周期:无需激活——设备按定时器采样 → 通过 `POST /api/v1/data/report` 上报(服务端自动 创建设备记录)→ 收到 `{"c":0,"t":...}` 后清空已上报的缓冲区,并根据 `t` 重新校准本地时钟。 - 验证了服务端/网络不可用时,设备会保留已采样但尚未上报的数据,并在下一次心跳时重试——不丢数据、 不崩溃(受本地采样缓冲区容量限制;见下面的"已知简化")。 - 验证了时间戳新鲜度(错误码 1002)的处理路径:收到该错误后,设备主动调用 `/auth/time` 重新同步时钟, 然后在下一个周期用修正后的时间戳重试上报。 ## 与真实协议的对应关系 见 [ubibot-open-doc](https://gitee.com/ubibot-open/ubibot-open-doc) 中的 `protocol/hardware-communication-protocol.md`:§3 时间同步(`ub_device.c` 中的 `do_time_sync()`,仅在 出现 1002 错误后被动调用,而不是启动时的必经步骤);§4 数据上报(`do_report()`,由 `ub_device_tick()` 中的采样/上报定时器驱动);§5 数据字段(`sample()` 只使用 `field1`/`field2`/`field3`,底层由 `ub_sensors.h` 的 `ub_sensor_read_temperature`/`_humidity`/`_light` 提供);§7 错误处理(`do_report()` 针对非零 `c` 的分支:1002 触发时间重新同步,1103 记录一条持续性告警,其余情况保留缓冲区,下个周期重试)。 ## 已知简化(如实记录,而不是把缺陷藏起来) - `ub_sensors_sim.c` 生成的是随机游走数据,而不是真实的传感器读数;`field3`(光照)的取值范围是 0–100,000 lux(覆盖从夜晚到阳光直射),起始基准值为 20,000 lux,每一步最多变化 800 lux。 - 本地采样缓冲区固定为 16 条(`UB_SAMPLE_BUF_CAP`);一旦写满,新的采样会被丢弃,直到下一次上报成功 清空缓冲区——真实固件可能需要更大的缓冲区,或者溢写到 flash 的队列,才能承受更长时间的离线;本模拟器 有意不实现无界队列。 - 采样/上报间隔(默认 30s / 300s)是写死在 `ub_device.c` 中的常量,而不是命令行参数,而且也没有服务端 推送的配置通道可以修改它们——协议本身就不提供这种能力。 - `ub_transport_sockets.c` 不支持 TLS(`https://` 地址会被当作普通的 `http://` 处理)——只适用于本地/ 局域网测试;真实固件通常把传输层交给厂商现成的 TLS 协议栈。 - 单个设备进程内的所有 HTTP 调用都是同步/阻塞的,这与大多数单任务/单线程嵌入式设备的实际行为一致。 ## 与 UbiBot Open 其他部分的关系 本模拟器与 [ubibot-open-server](https://gitee.com/ubibot-open/ubibot-open-server) 之间没有任何代码依赖 ——它只通过 HTTP 与服务端通信,和真实固件完全一样。它以前位于那个仓库的 `simulation/` 目录下;后来被 拆分成独立仓库,这样就可以单独复用(用于协议/后端测试、作为整个设备端协议约 7 个文件的精简范例,或者 作为移植的起点),而不必连带引入 Go 后端。 ## 许可证 [Apache License 2.0](LICENSE)。 ## 参与贡献 见[组织级 CONTRIBUTING.md](https://gitee.com/ubibot-open/.github/blob/main/CONTRIBUTING.md)。