【飞凌嵌入式FCU1501试用】Modbus-TCP通信与传感器数据采集

原创2026-07-08 14:46:33浏览2
2
Star

## 引言

在工业物联网(IIoT)领域,边缘计算设备扮演着至关重要的角色。RK3506作为一款高性能、低功耗的边缘计算芯片,非常适合用于工业数据采集场景。本文将介绍如何基于RK3506构建一个完整的Modbus-TCP传感器采集系统,包括传感器模拟器、采集器开发和测试验证。

## 项目架构设计

### 整体架构

整个系统采用**分布式架构**,由三个核心组件组成:

- **传感器模拟器**(PC端):模拟Modbus从机,生成传感器数据,支持故障注入

- **RK3506采集器**(RK3506开发板):定时轮询传感器,数据解析,异常检测

- **LLM服务**(云端/本地):接收异常事件,生成专业诊断报告

### 组件分工

| 组件 | 运行平台 | 职责 |

|------|----------|------|

| **传感器模拟器** | PC | 模拟Modbus从机,生成传感器数据,支持故障注入 |

| **RK3506采集器** | RK3506开发板 | 定时轮询传感器,数据解析,异常检测 |

| **LLM服务** | 云端/本地 | 接收异常事件,生成专业诊断报告 |

## Modbus协议原理

### RTU vs TCP

Modbus协议有两种主要传输模式:

**RTU模式**:基于串行通信(RS-485),使用CRC16校验,适合长距离传输。

**TCP模式**:基于以太网,使用MBAP(Modbus Application Protocol)头,传输速度更快,适合网络环境。

由于项目初期没有USB转485硬件,本文重点介绍**Modbus-TCP模式**。

### MBAP头结构

Modbus-TCP报文由6字节MBAP头和PDU(Protocol Data Unit)组成:

```

┌──────┬──────┬──────┬──────┬──────┬──────┐

│ 事务ID │ 协议ID │ 长度 │ 从站地址 │ 功能码 │ ...数据... │

│ (2字节) │ (2字节) │ (2字节) │ (1字节) │ (1字节) │ │

└──────┴──────┴──────┴──────┴──────┴──────┘

0-1 2-3 4-5 6 7

```

- **事务ID**:用于匹配请求和响应

- **协议ID**:固定为0(Modbus协议)

- **长度**:后续字节数(包括从站地址)

- **从站地址**:目标设备地址(0-247)

- **功能码**:操作类型(0x03=读保持寄存器)

### 常用功能码

| 功能码 | 名称 | 用途 |

|--------|------|------|

| 0x03 | 读保持寄存器 | 读取传感器数据 |

| 0x06 | 写单个寄存器 | 控制设备参数 |

| 0x10 | 写多个寄存器 | 批量控制 |

## 传感器模拟器实现

### 设计思路

模拟器使用Python实现,支持异步TCP服务器,模拟4个Modbus从机:

- **Slave 1**:环境传感器(温度、湿度)

- **Slave 2**:振动传感器(X/Y/Z轴、RMS)

- **Slave 3**:电力传感器(电压、电流、功率)

- **Slave 4**:管道传感器(压力、流量)

### 核心代码

#### 传感器数据更新逻辑

```python

def update(self, t):

if self.slave_id == 1:

# 环境传感器:模拟正弦波动

temp = 25.0 + math.sin(t / 30) * 1.5

humi = 50.0 + math.sin(t / 45) * 4.0

# 故障注入:高温故障

if self.fault == 'high_temp':

elapsed = time.time() - self.fault_start

ramp = min(elapsed / 300.0, 1.0)

temp = 55.0 + ramp * 30.0 # 从55°C逐渐升至85°C

self.registers[0] = int(temp * 10) # 温度×10

self.registers[1] = int(humi * 10) # 湿度×10

```

#### TCP请求处理

```python

def handle_tcp_request(self, request):

if len(request) < 12:

return None

trans_id = (request[0] << 8) | request[1]

proto_id = (request[2] << 8) | request[3]

length = (request[4] << 8) | request[5]

slave = request[6]

func = request[7]

if slave != self.slave_id or proto_id != 0:

return None

addr = (request[8] << 8) | request[9]

count = (request[10] << 8) | request[11]

if func == 0x03: # 读保持寄存器

pdu_len = 3 + count * 2

response = bytearray()

response.append(trans_id >> 8)

response.append(trans_id & 0xFF)

response.append(0x00) # 协议ID高字节

response.append(0x00) # 协议ID低字节

response.append(pdu_len >> 8)

response.append(pdu_len & 0xFF)

response.append(self.slave_id)

response.append(0x03)

response.append(count * 2)

for i in range(count):

val = self.registers[addr + i]

response.append((val >> 8) & 0xFF)

response.append(val & 0xFF)

return bytes(response)

```

### 运行模拟器

```bash

# 启动TCP模拟器,注入高温故障到Slave 1

python3 simulator/modbus_slave_sim.py \

--mode tcp \

--tcp-port 7502 \

--slaves 1,2,3,4 \

--fault high_temp \

--fault-slave 1

```

## RK3506采集器开发

### 交叉编译环境

RK3506使用ARM架构,需要配置交叉编译工具链:

```bash

# 工具链配置文件

source ~/3506-toolchain/host/environment-setup

# 关键环境变量

# CC=arm-buildroot-linux-gnueabihf-gcc

# CFLAGS=-march=armv8-a+crc -mtune=cortex-a55

# ARCH=arm

# CROSS_COMPILE=arm-buildroot-linux-gnueabihf-

```

### 项目结构

```

src/

├── main.c # 主程序入口

├── modbus_transport.h # 传输层抽象接口

├── modbus_tcp.c # TCP传输实现

├── modbus_rtu.c # RTU传输实现

├── sensor_map.h # 传感器映射定义

├── sensor_map.c # 传感器数据处理

├── anomaly.h # 异常检测接口

├── anomaly.c # 异常检测算法

├── event_json.h # 事件序列化接口

├── event_json.c # JSON序列化实现

├── llm_client.h # LLM客户端接口

├── llm_client.c # LLM客户端实现

├── serial_port.h # 串口接口

└── serial_port.c # 串口实现

```

### 传输层抽象设计

采用**策略模式**设计传输层,RTU和TCP共用一套接口:

```c

typedef struct modbus_transport {

int (*connect)(modbus_transport_t *t);

int (*read)(modbus_transport_t *t, uint8_t *buf, int len, int timeout_ms);

int (*write)(modbus_transport_t *t, const uint8_t *buf, int len);

void (*close)(modbus_transport_t *t);

} modbus_transport_t;

typedef struct {

modbus_transport_t base;

const char *host;

int port;

int sockfd;

} modbus_tcp_t;

```

### 传感器映射配置

定义传感器与Modbus寄存器的映射关系:

```c

sensor_map_t sensor_maps[MAX_SENSORS] = {

{

"env", 1, 0, 2,

{0.1, 0.1},

{"temperature", "humidity"},

{"C", "%RH"}

},

{

"vibration", 2, 0, 4,

{0.01, 0.01, 0.01, 0.01},

{"x", "y", "z", "rms"},

{"mm/s", "mm/s", "mm/s", "mm/s"}

},

{

"power", 3, 0, 3,

{0.1, 0.1, 0.1},

{"voltage", "current", "power"},

{"V", "A", "W"}

},

{

"pipe", 4, 0, 2,

{0.01, 0.01},

{"pressure", "flow"},

{"MPa", "m3/h"}

}

};

```

### 主循环设计

```c

while (1) {

// 创建TCP连接(每次轮询新建连接)

fd = socket(AF_INET, SOCK_STREAM, 0);

// ... 设置超时、连接服务器 ...

for (int i = 0; i < sensor_map_count; i++) {

sensor_data_t *data = &sensors[i];

uint16_t raw[MAX_FIELDS];

// 读取保持寄存器

ret = modbus_read_holding_registers_tcp(fd,

data->map->slave_id,

data->map->start_addr,

data->map->count, raw);

if (ret == MODBUS_ERR_OK) {

data->comm_error_count = 0;

sensor_data_update(data, raw);

print_sensor_data(data);

// 异常检测(第二篇介绍)

anomaly_event_t event;

if (anomaly_detect(data, &event)) {

handle_anomaly(&event);

}

} else {

data->comm_error_count++;

printf("[%s] Slave %d: COMM ERROR (%d)\n",

data->map->name, data->map->slave_id, ret);

}

usleep(100000); // 100ms间隔

}

close(fd);

sleep(POLL_INTERVAL_SEC); // 1秒轮询间隔

}

```

### 编译脚本

```bash

#!/bin/bash

cd "$(dirname "$0")"/..

echo "Building for RK3506..."

source ~/3506-toolchain/host/environment-setup

mkdir -p build

cd build

cmake ..

make -j4

echo "Build completed. Output: build/modbus_collector"

```

## 测试验证

### 编译部署

```bash

# 编译

bash scripts/build.sh

# 部署到RK3506

scp build/modbus_collector root@<RK3506_IP>:/root/

# 赋予执行权限

ssh root@<RK3506_IP> "chmod +x /root/modbus_collector"

```

### 运行采集器

```bash

# 在RK3506上运行

./modbus_collector --mode tcp --host <SIMULATOR_IP> --port <PORT>

```

### 测试结果

```

Starting RK3506 Modbus-TCP Collector

TCP host: <SIMULATOR_IP>:<PORT>

Polling sensors...

--- 13:19:30 ---

[env] Slave 1: temperature=85.00°C, humidity=51.00%RH

[vibration] Slave 2: x=0.71mm/s, y=0.15mm/s, z=0.15mm/s, rms=0.74mm/s

[power] Slave 3: voltage=24.40V, current=1.40A, power=34.50W

[pipe] Slave 4: pressure=0.64MPa, flow=54.72m3/h

```

所有4个传感器数据均成功采集,数据格式正确。

## 总结

本文介绍了基于RK3506的工业边缘采集系统的第一部分——Modbus-TCP通信与传感器数据采集。主要内容包括:

1. **系统架构设计**:分布式架构,PC端模拟器 + RK3506采集器

2. **Modbus协议原理**:MBAP头结构、功能码解析

3. **传感器模拟器**:Python实现,支持4类传感器和故障注入

4. **RK3506采集器**:C语言开发,传输层抽象设计

5. **测试验证**:编译部署成功,所有传感器数据正常采集

**下一篇预告**:将介绍异常检测算法、LLM智能诊断集成和异步处理优化。

---

**项目源码**:[rk3506-modbus-llm](https://github.com/xxx/rk3506-modbus-llm)

**技术交流**:欢迎在评论区留言讨论!

---

> **提示**:本文是系列文章的第一篇,第二篇将深入讲解异常检测与LLM智能诊断。如果你觉得本文对你有帮助,欢迎点赞、收藏、关注!