
简介RBI_Thingsboard是一份基于C开发的物联网平台完整源码面向嵌入式开发者和IoT后端工程师覆盖设备接入、数据采集、实时监控、历史数据分析等典型应用场景。压缩包共包含83个文件体积约1016KB以cpp源文件、h头文件和o编译中间文件为主同时含有Qt工程配置、makefile构建脚本、ui界面定义及txt/pdf说明文档能整体还原一套可编译、可扩展的工程结构。目前已有189人学习下载适合希望通过真实项目强化C技能的开发者。深入阅读源码可以理解如何依托C的高性能与跨平台能力处理高并发数据流、直接操作硬件资源以及实现串口和MQTT通信还能借鉴其模块化设计思路掌握数据库管理、数据可视化如QCustomPlot等关键模块的实际代码为后续开发、移植或二次定制提供扎实参考。1. 项目定位为什么选 ThingsBoard 做设备集成底座1.1 项目背景与核心需求我最近在推进 RBI_Thingsboard 这个项目核心任务是把一批工业现场设备接入统一的物联网平台完成数据采集、可视化监控、远程控制和数据分流。折腾一圈之后最终选定了 ThingsBoard 作为整个系统的基础底座。选择它的原因很直接ThingsBoard 开源、社区活跃、设备接入协议覆盖全面而且它对 MQTT、HTTP、CoAP 这些主流协议的支持非常成熟设备端适配成本低。这个项目面向的场景比较典型现场有大量不同型号的采集终端设备名称带有明显的业务标识比如设备名包含 tx 的属于传输链路节点需要单独过滤出来做链路健康度分析平台侧需要向上层展示实时数据看板要求图表展示能力强运维人员要能远程给设备下发控制命令调整采样频率或者复位某些异常模块。这几条需求叠加在一起恰好就是 ThingsBoard 最擅长的领域规则引擎处理数据流仪表盘满足可视化RPC 机制解决双向通信。另外还有一个隐形需求值得留意团队里不少成员是第一次接触 ThingsBoard所以整个项目在设计时就要尽量降低上手成本。ThingsBoard 的自定义 Widget 开发、规则链配置、设备配置文件管理等功能都能通过配置完成大部分工作少写代码这对我这个团队来说非常友好。简单说RBI_Thingsboard 的定位就是以 ThingsBoard 为核心快速搭建一套能落地、可扩展、方便运维的设备接入与管理平台。1.2 平台选型的关键考量聊到选型我不太推荐一上来就自研物联网平台除非团队规模很大且业务极其特殊。ThingsBoard 这类成熟开源平台的价值在于设备接入层、数据持久化层、可视化层都已经帮你趟过坑了你要做的事情是站在它肩膀上做业务适配。RBI_Thingsboard 项目里我们重点从三个维度评估了 ThingsBoard第一是数据链路完整性。设备端 MQTT 上报数据后ThingsBoard 通过规则链将数据转存到 Cassandra 或 PostgreSQL同时推送到仪表盘进行实时渲染。这一条链路完全是闭环的不需要额外写数据管道。第二是扩展性。ThingsBoard 支持自定义 Widget 和自定义规则节点Echarts 这类第三方库可以很好地嵌入这正好解决了我们仪表盘图表样式不够丰富的问题。第三是社区与文档。ThingsBoard 官方文档很详细而且遇到问题基本都能在社区找到相似案例这对项目排期紧张的情况来说是一种隐形的保障。当然 ThingsBoard 不是没有槽点。默认规则链日志不够直观刚开始调试规则时容易一头雾水高并发场景下对部署环境要求不低内存吃紧是常事。但这些都属于“能用配置和运维手段解决的问题”不影响它作为基础设施的价值。2. 从零启动 ThingsBoard环境准备与避坑要点2.1 环境依赖与安装部署ThingsBoard 的启动部署分两种主流方式Docker 容器部署和本地安装包部署。RBI_Thingsboard 项目在开发阶段我用的 Docker 方式原因很简单——省心不用手动装 Java 环境、不用单独初始化数据库一个 docker-compose 就能把 ThingsBoard 和 PostgreSQL 全部拉起来。# 使用 tb-postgres 镜像单机开发调试足够了 docker run -it -p 9090:9090 -p 1883:1883 -p 5683:5683/udp \ -v ~/thingsboard-data:/data \ --name thingsboard thingsboard/tb-postgres:latest等容器日志出现 ThingsBoard started successfully 就说明服务已经起来了。这里我踩过一个小坑首次启动时容器日志一直在刷数据库初始化信息表面看像卡住了其实是在建表、导入系统配置这个过程在低配机器上可能持续 3~5 分钟千万别急着 CtrlC。等日志稳定后访问http://localhost:9090默认账号是系统管理员sysadminthingsboard.org / sysadmin租户管理员是tenantthingsboard.org / tenant。如果你需要在本地环境直接跑源码那就要装 Java 11、PostgreSQL 和 ThingsBoard 安装包配置相对繁琐一些但好处是可以直接修改前端代码调试 Widget。本地部署时一定要确认 JAVA_HOME 指向的是 JDK 11JDK 8 会在启动时报 UnsupportedClassVersionError这个问题在团队新成员电脑上反复出现过好几次。2.2 启动过程关键参数调整启动 ThingsBoard 不是起来就完事了有几个参数直接影响运行稳定性尤其是内存配置。ThingsBoard 默认的 JVM 堆内存上限是 512MB这个数值在仪表盘加载较多 Widget、规则链并发处理时很容易触发 OutOfMemoryError。我一般在conf/thingsboard.conf里调整 JAVA_OPTSJAVA_OPTS-Xms1024M -Xmx2048M如果机器内存充裕推荐-Xms2048M -Xmx4096M毕竟 ThingsBoard 的规则引擎和 WebSocket 推送都是吃内存的大户。另外要注意 1883 端口是 MQTT 默认端口设备接入全靠它。如果本机部署了其他 MQTT Broker比如 Mosquitto一定要提前把端口错开否则 ThingsBoard 启动时端口绑定失败日志里会报Address already in use。提示修改 conf 文件后需要重启 ThingsBoard 服务才会生效。Docker 部署则通过-e JAVA_OPTS-Xmx2048M传入环境变量。3. 仪表盘开发实战Echarts 集成与多页面导航切换3.1 如何在 ThingsBoard 中集成 EchartsThingsBoard 自带的图表组件够用但离“好看”和“直观”还有差距。RBI_Thingsboard 项目里设备状态实时监控、链路延迟分析这些场景我用的是 Echarts 来做自定义 Widget 集成。ThingsBoard 支持两种集成方式一种是直接开发一个自定义 Widget在 HTML 中引入 Echarts 库另一种是通过 Resources 模块统一管理外部 JS 资源再在 Widget 中引用。推荐第二种更利于多个 Widget 复用同一个 Echarts 库。具体做法是进入 Widgets Library创建一个新的 Widget类型选Latest Values或Timeseries然后在 Resources 标签页添加 Echarts 的 CDN 地址或者上传 echarts.min.js 文件。HTML 标签里放一个 div 作为图表容器JavaScript 部分通过self.ctx.$scope.data获取设备上报的数据再调用 Echarts 的 setOption 完成渲染。下面是一个简化的核心逻辑var chartDom document.getElementById(chart); var myChart echarts.init(chartDom); self.ctx.$scope.$watch(function() { var data self.ctx.$scope.data; if (data data.length 0) { var values data.map(function(d) { return d.data[0][1]; }); myChart.setOption({ xAxis: { type: category, data: values.map(function(_, i) { return 点 i; }) }, yAxis: { type: value }, series: [{ type: line, data: values }] }); } });这里要提醒一个坑ThingsBoard 的 Widget 运行在一个受限的 AngularJS 上下文中直接裸写setInterval轮询数据是不行的必须用self.ctx.$scope.$watch来监听数据变化否则图表不会自动刷新。另外如果页面加载时图表不显示先检查 Echarts 资源是否正确加载可以在浏览器开发者工具里看 Network 面板确认 echarts.min.js 是 200 状态码。3.2 仪表盘导航切换的两种实现方式仪表盘导航切换是我在 RBI_Thingsboard 项目里花时间最多的一块。设备列表、实时数据、历史曲线、告警信息每个业务模块是独立仪表盘但用户希望在一个页面里通过 Tab 或按钮自由切换而不是来回跳转 URL。实现思路有两条路第一种是使用 ThingsBoard 自带的State Controller功能。在仪表盘设计模式中添加一个 State Controller Widget然后在 Dashboard States 里配置多个状态每个状态对应一个不同的仪表盘布局。这种方式对代码零依赖纯配置搞定。适合仪表盘数量少、切换逻辑简单的场景。缺点是状态多了之后管理起来有点乱如果某个仪表盘要从导航中移除需要逐个排查关联关系。第二种是写一个自定义导航 Widget核心是通过widgetContext.stateController.openState(stateId)来跳转到目标状态。这种方式灵活可以在导航按钮上自定义图标、样式甚至加权限判断。我在项目里是两种方案混用的顶部一级导航用 State Controller 配置侧边栏的细分页面跳转则用自定义 Widget 控制。需要注意openState方法只在当前仪表盘存在多个 State 时才有效如果目标状态在其他仪表盘得先通过 API 跳转到对应仪表盘再打开状态。// 自定义导航按钮跳转到指定 state widgetContext.stateController.openState(device_detail, { deviceId: xxx });注意State 切换时携带的参数可以通过widgetContext.stateController.getStateParams()获取这个机制在处理“从列表页点击某台设备跳到详情页”的场景时非常实用。4. RPC 控制命令下发从规则链到设备端4.1 RPC 机制的通信模型ThingsBoard 的 RPC 功能是远程控制设备的关键通道RBI_Thingsboard 项目里我要实现“平台下发指令让设备调整采样周期”就是靠它。ThingsBoard RPC 分两种模式持久化 RPC 和即时 RPC。持久化 RPC 会存在数据库里设备离线恢复后还能收到即时 RPC 则要求设备在线才能送达超时就丢弃。通信流程大致这样平台端通过 REST API 或规则链节点发起 RPC 请求ThingsBoard 将请求推送到设备端设备端在 MQTT 主题v1/rpc/request/上收到请求后处理完把结果发布到v1/rpc/response/{requestId}平台收到响应后返回给调用方。整个过程是异步的调用方需要处理超时逻辑。因为整个链路依赖 MQTT 持久会话所以设备接入时建议设置 MQTT Client ID 固定cleanSessionfalse否则设备一断线重连未收到的 RPC 请求就丢了。这一点在低功耗设备上尤其重要设备可能频繁休眠唤醒如果每个 session 都是新的平台侧根本无法判断设备是否在线。4.2 服务端下发控制命令的实现路径实际项目中下发控制命令我常用两种方式。一种是通过 REST API 直接调用适合外部系统集成比如监控平台里的某个按钮触发指令下发。另一种是通过规则链节点rpc call request适合在设备上报特定数据后自动触发控制逻辑比如设备报温度过高自动下发风扇开启指令。REST API 方式最简单构造一个 POST 请求即可curl -X POST http://localhost:9090/api/rpc/twoway/{deviceId} \ -H Content-Type: application/json \ -d {method: setSamplingInterval, params: {interval: 30}}注意要用X-Authorization: Bearer {JWT_TOKEN}请求头带上认证信息。{deviceId}是设备在 ThingsBoard 中的唯一标识可以在设备详情页或通过 REST API 获取。规则链方式稍微复杂一点在“发起 RPC 请求”节点中需要指定目标设备、请求方法名和参数还可以设置超时时间和是否持久化。例如我在项目中配置了一个规则链当设备上报temperature 80时自动调用setFanSpeed方法将风扇档位调到最高。这个自动联动能力在无人值守的机房场景中非常实用。{ deviceId: 目标设备ID, timeout: 2000, oneway: false, persistent: false, method: setFanSpeed, params: { level: 5 } }提示如果设备端没有正确订阅 RPC 主题平台侧会一直等到超时。建议设备端代码里对v1/rpc/request/的主题订阅逻辑做日志输出排查问题会快很多。5. 规则链过滤器精准筛选设备名称包含 tx 的数据5.1 规则链节点的选择RBI_Thingsboard 项目里有个明确需求过滤出设备名称中包含 tx 的数据单独走一条链路做传输节点状态监控。ThingsBoard 规则链提供了多种过滤手段比如Message Type Filter按消息类型过滤Originator Type Filter按设备类型过滤Script Filter写 JavaScript 逻辑过滤。针对“设备名包含某字符串”这种需求最合适的是 Script Filter因为它的判定逻辑最灵活且可以访问消息元数据中的设备信息。这里要理解一个概念进入规则链的消息除了负载数据外还带有元数据Metadata其中包含deviceName、deviceType、nodeId等字段。所以我们过滤设备名的本质就是在脚本里读取metadata.deviceName并做字符串匹配。5.2 脚本过滤器的核心代码与配置思路在规则链中添加 Filter 节点类型选 Script Filter然后编写过滤脚本。实现如下// 过滤设备名称中包含 tx 的数据 if (metadata.deviceName metadata.deviceName.indexOf(tx) ! -1) { return true; // 满足条件进入后续节点 } return false; // 不满足条件链路终止或者用更现代的写法return metadata.deviceName ? metadata.deviceName.includes(tx) : false;这段脚本返回 true 的消息会继续走 true 分支进入下一个处理节点返回 false 的则走 false 分支可以接到一个忽略节点或日志节点。实际配置时我建议先在 false 分支接一个script节点打日志确认过滤结果符合预期后再把 false 分支接终点避免误过滤导致数据异常。这里有个容易忽略的点metadata.deviceName字段名的大小写一定要准确。在部分版本中设备名称也存在deviceName和device_name两种写法取决于消息来源。我因为这个问题排查过好几次后来统一在规则链入口加了一个日志节点把完整 metadata 先打出来确认字段名之后再写过滤脚本就再也没出过问题。考虑到“规则链库”这个词被频繁搜索我再多说一句如果多个租户共用一套过滤逻辑可以将这个 Script Filter 节点保存到规则链库Rule Chain Library这样其他规则链可以直接复用。规则链库本质上是共享的规则链模板适合沉淀通用逻辑减少重复配置。提示过滤逻辑如果复杂不要在一个 Script 节点里堆大量代码。可以拆成多个过滤节点串联例如先按设备类型过滤再按设备名称关键词过滤每个节点职责单一后续维护会轻松很多。6. 常见问题排查与经验总结6.1 启动与运行阶段的典型问题启动阶段最常见的五类问题和处理方式整理成一张速查表问题现象可能原因排查与解决方法启动日志报 OutOfMemoryErrorJVM 堆内存配置过小调大-Xmx建议不低于 2GMQTT 设备连接不上1883 端口无响应端口被占用或防火墙拦截netstat -anp | grep 1883查看占用情况首次启动长时间停留在“DB initialization”低配置机器初始化较慢耐心等待不要中断容器进程登录页面无法访问前端资源未编译或 Nginx 配置错误检查 9090 端口监听情况及日志设备数据显示为空白设备接入后没有配置对应的资产/设备配置文件确认设备已分配到某个设备配置文件运行阶段还有一个高频坑设备上报数据频率过高导致规则链处理不过来。ThingsBoard 的规则引擎是单线程消费队列的如果消息堆积严重可以适当调大队列并发度。具体在conf/thingsboard.conf中修改QUEUE_RULE_ENGINE_POLL_SIZE和QUEUE_RULE_ENGINE_THREADS不过要量力而行并发上去了 CPU 开销也会增加。6.2 仪表盘与 RPC 的坑仪表盘这块我遇到最多的坑是 Echarts 图表不刷新。前面提到过用$watch监听数据变化但还有一个隐藏问题页面切换 State 后某些 Widget 不会重新初始化导致图表还停留在旧数据。我的解决方式是在 Widget 的onDestroy回调中销毁 echarts 实例然后在每次数据更新时判断实例是否存在不存在就重新初始化。RPC 下发过程中我们也遇到过一个比较隐蔽的问题设备在线状态显示正常但即时 RPC 总是超时。后来排查发现是设备端 MQTT 会话使用了cleanSessiontrue导致平台下发的请求在设备短暂掉线后直接丢失。把设备端的 cleanSession 改为 false同时要求设备重连后主动 re-subscribe RPC 主题问题就消失了。注意RPC 请求的 method 和 params 结构必须和设备端解析逻辑保持一致。强烈建议先在平台 REST API 调试工具里发一次测试请求确认设备端能正常应答再配置规则链的自动触发逻辑。我个人在实际操作中最大的体会是ThingsBoard 这种平台型软件最怕的不是功能不会用而是链路太长导致问题定位困难。所以 RBI_Thingsboard 项目一路做下来我在每个关键环节都增加了日志节点和调试输出设备上报、规则过滤、RPC 下发、仪表盘渲染每一个环节出问题都能快速定位到具体位置。后面大家在自己项目里也建议养成这个习惯比什么都管用。本文还有配套的精品资源点击获取