
简介一个完整的物联网项目资源包聚焦设备控制、数据采集与产品溯源三大核心模块适合物联网开发者、嵌入式工程师及高校相关专业学生用于系统学习或二次开发。项目基于OneNET平台包含前端网关、后台管理与移动端APK覆盖从设备接入、数据上报到溯源查询的完整链路。压缩包共118个文件以Java源码为主105个辅以配置文件、Dockerfile、APK安装包及说明文档整体约9.14MB目录结构清晰便于按功能模块阅读和部署。当前已有115人学习下载可作为同类毕设、课设或企业项目的参考模板。通过阅读源码可快速理解MQTT通信、REST API封装、多角色权限控制及产品溯源数据模型等关键实现兼具教学与工程实用价值。1. 从“设备能上网”到“系统能溯源”OneNET 物联网项目的切入点一个做 IoT 的场景经常出现这种反差设备数据已经上传到平台折线图也能画出来但客户问“这批货是哪个工人做的、加工时温度多少、对应哪个订单”系统却答不上来。这里缺的不是传感器布点而是把设备控制、数据采集和产品溯源放在同一套技术框架里一起设计。这个 OneNET 物联网项目正好把三层凑齐了Android 网关负责设备接入和指令执行Java 后端处理客户、订单、工人和任务OneNET 云平台做设备数据沉淀顶部再用溯源 App 把产品码串成完整链路。对正在做物联网平台接入、又需要交付可追溯生产系统的技术人员来说它能提供的不是单一接口而是一套可以直接在大批量制造场景里复现的“端-云-业务”工程样例。2. 从文件名反推系统架构APK、Java Controller 与 OneNET 平台2.1 一份压缩包里的模块边界拿到onenet-iot-project.zip这类工程第一件事不是马上打开 IDE 逐个文件点开而是把文件名当成接口清单来读。包内同时存在origins.apk、gateway.apk、Dockerfile、OnenetMq.java以及一组订单、客户、任务、工人、管理员相关的 Controller这说明系统被拆成了四个层次设备端、网关侧、平台接入层、业务服务层。下表是我基于这类项目常见结构做的对应关系。文件判断依据在项目里承担的典型职责gateway.apkgateway 单词运行在 Android 网关设备上负责局域网设备数据采集、离线缓存、指令下发origins.apkorigins 单词面向操作员或质检人员的溯源查询扫码工具OnenetMq.javaMQTT 接入核心类封装与 OneNET 的 MQTT 连接、消息上下行、重连上报OrderController.javaController接收订单、维护订单状态为溯源提供原始单据CustomerController.javaController维护客户档案和客户与订单的关联关系WorkerController.javaController管理操作工人、设备负责人和对应权限TaskController.javaController生成生产任务、绑定产品批次和工位AdminController.javaController后台管理、账号权限、溯源策略配置Dockerfile部署描述把后端打包成容器镜像降低现场部署环境差异这里的重点不是文件数量而是 Controller 命名已经暴露了业务主链路客户下订单订单拆成任务任务关联工人和设备设备产生的数据最后汇总到溯源查询。换句话说这不是一个单纯做“设备监控”的项目而是一个把设备数据和经营数据放在同一套系统里的综合型物联网项目。2.2 端、云、业务三层的数据走向在这个系统里数据不是直接由设备存入后端数据库而是至少经过两条链。第一条是实时监控链路传感器通过串口、蓝牙或 4G 模块把数据送到网关 APK网关解析后上报到 OneNET 平台后端再通过 MQTT 或 HTTP 从平台侧读取。第二条是业务溯源链路网关上报的数据带产品批次号后端拿到数据后把批次号与 TaskController 里的生产任务绑定再通过任务关联订单和客户。用一句话串起来就是传感器/PLC - 网关 APK或 4G 模块- OneNET MQTT/HTTP 接口 -OnenetMq.java监听 - 后端按 Topic 和设备 ID 匹配任务 - 写入溯源库。一旦中间任何一环断掉表现出的症状就是“平台上有数据App 上查不到工单信息”。这种割裂状态在物联网项目里很常见根因往往不是代码逻辑而是设备上报数据时没有带业务主键。2.3 为什么选 OneNET 作为接入层自建 MQTT Broker 不是不可以但对这个项目来说OneNET 的价值在于把设备接入、数据流存储、APIKey 权限控制直接打包好团队不用自己维护 Broker 高可用也不用处理设备频繁掉线后的消息补偿。对溯源系统而言接入层最怕的是数据到了但不知道来自哪台设备、哪一批货。OneNET 的“产品-设备-数据流”模型天然保留这些维度后端拿到上报数据时可以直接把设备 ID 和批次字段当分组索引用。项目里放了Dockerfile说明作者从一开始就考虑过整体部署交付。我一般会把 Dockerfile 按后端和静态资源分离的模板改一下让两个 APK 作为资源文件和 Jar 包打进同一个镜像简化现场升级流程FROM openjdk:17-jre-slim WORKDIR /app COPY target/onenet-iot-project.jar /app/app.jar COPY origins.apk /app/resources/origins.apk COPY gateway.apk /app/resources/gateway.apk EXPOSE 8080 ENTRYPOINT [java, -jar, /app/app.jar]这段 Dockerfile 里Jar 包和 APK 放在同一目录便于溯源 App 下载安装时直接走静态资源路径。openjdk的具体版本要按项目实际编译时的 JDK 版本调整Jar 包用 Java 17 编译就不可能跑在 11 的运行时上。这里如果不想让容器承载太多文件也可以把 APK 放到对象存储Dockerfile 里只保留后端进程。3. 从传感器到 OneNET设备控制、数据采集的 MQTT 接入细节3.1 先定好 OneNET 产品模型再写 Java 代码接入 OneNET 的前置条件不是写代码而是在平台上建好产品、设备、数据流三层模型。产品对应某个设备类型设备对应具体硬件编号数据流是命名好的业务字段。在制造溯源场景里典型数据流至少需要temperature、device_status、product_batch其中product_batch是连接设备数据和业务系统的关键字段。把批次号放进设备数据后面才能根据溯源码直接定位到 TaskController 里的生产任务。平台上要记录四类信息产品 IDPID、接入协议MQTT、设备 ID、APIKey。产品创建后设备上报的数据会按数据流模板解析。常见的坑是把 APIKey 当成设备密码其实 APIKey 是服务端权限凭证设备端应使用平台为设备单独分配的接入鉴权信息。项目里OnenetMq.java通常就是负责封装这些参数并在启动时建立 MQTT 会话。3.2 数据上报 Topic 与 payload 格式OneNET 不同版本产品的 Topic 规则不一样。老版本比较常见的做法是把数据点发布到$dp这个 Topic新版产品模型更多使用$sys/{productId}/{deviceId}/thing/...这类带产品语义的路径。实际开发时先到 OneNET 控制台“设备详情 - Topic 列表”确认当前使用的 Topic再把 Topic 写进配置。下面是一份典型的数据上报 payload{ datastreams: [ { id: temperature, datapoints: [ { value: 26.5 } ] }, { id: product_batch, datapoints: [ { value: B20250601 } ] } ] }datastreams是 OneNET 数据流的标准结构id对应平台里创建的数据流名称datapoints里可以挂多个时间点适合补传网关离线期间缓存的数据。最容易翻车的地方是value的数据类型平台定义成数值型却传入字符串会直接返回格式错误反过来定义成字符串型却传数字同样会被拒绝。我一般在网关侧先把所有值序列化成字符串平台端字段类型也统一成字符串从源头避免类型不一致。3.3 指令下发服务端发布、网关订阅设备控制是比数据采集更容易出错的链路因为它是反向的业务层要通过后端往设备推指令。常见做法是后端从任务操作界面向 OneNET 发布指令消息网关 APK 订阅对应 Topic收到消息后再通过串口或 GPIO 控制设备。String cmdTopic OneNetConfig.getCmdTopic(deviceId); String cmdPayload {\cmd\:\motor_stop\,\source\:\task\,\taskId\:\T2025060101\}; MqttMessage msg new MqttMessage(cmdPayload.getBytes(StandardCharsets.UTF_8)); msg.setQos(1); client.publish(cmdTopic, msg);这段代码里携带的source和taskId很关键它让设备控制动作也能进入溯源链条。网关收到指令后先回执一个 ACK 消息再实际执行动作避免“按钮点了但设备没停”时无法定位问题。QoS建议设成 1至少保证消息不丢如果设成 0弱网下一次 PDU 丢失设备可能一整批都没有动作这类问题在 MQTT 调试里非常隐蔽。3.4 网关侧数据上报代码与弱网补传在gateway.apk这种 Android 网关场景里常用 Eclipse Paho 作为 MQTT 客户端。下面这段代码是网关定时上报的核心骨架放进 Android Service 就能满足开机自启、断线重连、定时上报三个基本要求。String broker tcp:// OneNetConfig.BROKER_HOST : OneNetConfig.BROKER_PORT; MqttClient client new MqttClient(broker, OneNetConfig.DEVICE_ID, new MemoryPersistence()); MqttConnectOptions options new MqttConnectOptions(); options.setAutomaticReconnect(true); options.setCleanSession(false); options.setConnectionTimeout(10); client.connect(options); String topic OneNetConfig.DATA_TOPIC; JSONObject point new JSONObject() .put(id, temperature) .put(value, temperature); JSONObject datastream new JSONObject() .put(datastreams, new JSONArray().put(point)); client.publish(topic, new MqttMessage(datastream.toString().getBytes(StandardCharsets.UTF_8)));automaticReconnect只对短时网络波动有效如果设备长时间休眠后重连必须把cleanSession设成 false否则平台会在设备离线期间清掉订阅关系重连后也无法收到指令。DEVICE_ID是设备唯一标识不能重复否则在线设备会被顶号。这里还需要注意网关离线时采集的数据应写入本地 SQLite恢复连接后按时间顺序补传补传 payload 里的datapoints数组就可以包含多个数值点。如果硬件不是 Android 而是 STM32 4G 模块逻辑要平移成 AT 指令先配置 4G 网络和 PDP 激活再设置 MQTT 参数最后连接平台。这类模块对 JSON 里的引号非常敏感最好用十六进制字符串格式上报避免 AT 指令转义把双引号吞掉这个细节会在最后一章具体展开。4. 产品溯源链路从订单、工单到设备数据的 Controller 设计4.1 溯源表不只有一个产品码很多溯源项目把数据库设计成一张“扫码表”存了产品码、时间、地点就算完成。真正可用的可追溯性至少包含三组关系产品与生产批次的关系、批次与任务和订单的关系、任务与设备采集数据的关系。项目里的OrderController、CustomerController、TaskController、WorkerController正好对应这些关系客户下订单订单拆成生产任务任务绑定产品批次、工位和工人设备上报的数据再按批次号挂到任务下面。设备和业务表之间的桥就是product_batch。只要网关上报数据时带了批次号OnenetMq.java接收到消息后就可以把数据写入“设备数据流水表”并在流水表里冗余一个批次字段。这样即使订单信息后续变更历史设备数据也不会因为关联表被修改而丢失。4.2 Controller 职责划分与接口设计下面把五个 Controller 的职责和溯源场景价值整理成表。这里的接口路径是按项目常见约定列出的语义一致实际路径要以源码里的注解为准。Controller核心字段典型接口溯源场景价值CustomerControllercustomerId, name, qualificationGET /customer/{id}溯源结果里展示客户资质OrderControllerorderId, customerId, productTypePOST /order、GET /order/{id}订单来源是溯源的头节点TaskControllertaskId, orderId, productBatch, stationIdPOST /task、PUT /task/{id}/state任务绑定产品批次和工位WorkerControllerworkerId, name, role, shiftGET /worker/{id}生产记录里携带操作人AdminControlleradminId, role, permissionPOST /admin/queryTrace后台溯源查询和权限控制这个设计把一次“扫码查产品”拆成两层调用前台传入产品码后端先通过 TaskController 查到批次和任务再用任务 ID 带出订单、客户和工人信息。多出来的表关联看上去比单表扫码复杂但它能保证质量投诉时同一个批次号能同时查出设备数据、操作人和订单状态这才是溯源系统真正的价值。4.3 一次溯源查询在 Controller 里怎么串联以扫码查产品为例在 Spring Boot 里如果是独立的溯源接口大致会写成下面的结构RestController RequestMapping(/trace) public class TraceController { GetMapping(/{productCode}) public MapString, Object trace(PathVariable String productCode) { // 1. 根据 productCode 查产品实例拿到 productBatch // 2. 通过 productBatch 查任务记录 // 3. 通过任务记录里的 orderId 和 workerId 分别查订单和工人 // 4. 将设备采集的温湿度数据和业务数据合并返回 MapString, Object result new HashMap(); result.put(batch, productBatch); result.put(order, orderService.getByTask(task)); result.put(worker, workerService.getById(task.getWorkerId())); result.put(deviceRecords, dataService.findByBatch(productBatch)); return result; } }这个接口说明一个容易被忽略的边界Controller 层只做参数接收和结果拼装真正的查询逻辑放到 Service 或数据访问组件里。如果项目里OnenetMq和各个业务表之间的关系越来越复杂建议单独抽一个TraceService否则一次扫码请求会串行调用四五个 Service接口延迟会明显升高。溯源数据写入时也要克制设备数据进入OnenetMq后不要直接改业务订单表先存到device_raw_log中间表再由后台任务解析成结构化溯源数据。这样原始报文可以回放也方便排查“某条温度为什么没匹配到工单”这类现场问题。5. 用 MQTTX 和 AT 指令完成设备链路验证5.1 先用 MQTTX 模拟设备接入MQTTX 是验证 OneNET 接入最快的外置工具适合在写网关代码前先把平台连接参数跑通。连接时要重点核对四个字段参数建议值说明HostOneNET 控制台提供的 MQTT 地址必须带端口 1883 或 8883Client ID设备 ID例如device_001重复时在线设备会被互踢Username产品 ID 或产品 key按平台接入文档填写PasswordAPIKey / 设备密钥不要填用户账号密码连接成功后再发布一条数据流消息到 OneNET 控制台“设备管理 - 数据流”里确认数据变化。常见现象是能连接但收不到数据这个时候问题基本出在订阅 Topic 上而不是连接参数。服务端测试时建议使用后端独立生成的 APIKey不要用管理员后台的全局 APIKey避免暴露整个产品权限。提示在服务端测试时使用由后端生成的 APIKey不要使用管理员后台 APIKey会暴露整个产品的操作权限。5.2 STM32 与 4G 模块联调时的 AT 指令自查用 STM32F103C8T6 A7670C 这类 4G 模块调 MQTT 时调试顺序要按“网络、PDP、MQTT、数据”四层逐级排查。先执行ATCGREG?看网络注册状态返回0,1表示已注册再执行ATCGACT?看 PDP 激活状态返回1,1表示激活成功完成后再配置 MQTT client、订阅 Topic。很多人一开始就盯着 MQTT 参数结果反复报连接失败实际是 APN 写错了PDP 根本没有激活。调试 AT 指令时末尾的\r\n不能省常见问题是在命令里带了空格或者误把ATCGACT?写成ATCGACT?。OneNET 上连接成功之后还需要把 MQTTX 收到的完整 payload 与模块发出的报文逐字节比对重点检查模块对 JSON 双引号的转义是否成了\22这种形式。数据链路验证不要只测一次正常上报还要模拟断网重连、弱网补传和设备重名互踢三种异常场景。当后台某条产品码查不出工单时第一件事也不是改数据库而是用 MQTTX 回放模拟同一台设备上报同一条product_batch这样基本能直接锁定问题出在网关解析侧还是业务关联侧。本文还有配套的精品资源点击获取