Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

modbus

  • ModbusASCLL
  • ModbusRTU
    - 二进制编码
    - CRC错误校验
  • ModbusTCP/IP

RTU

Modbus-RTU模式是指当控制器设为在Modbus网络上以RTU(远程终端模式)模式通信,在消息中的每个8Bit包含两个4Bit的十六进制字符。

1、信息帧发送至少要以3.5个字符时间的停顿间隔开始。在最后一个字符传输字符之后,一个至少3.5个字符时间的停顿标定了信息帧的结束。一个新的信息帧可在此停顿之后开始。如果一个新消息在小于3.5个字符时间内接着前个消息开始,接受的设备将认为它是前一信息帧的延续,这将导致CRC码的值出错。

2、整个信息帧必须作为一连续的流传输。如果在帧完成之前有超过1.5字符时间的停顿时间,接受设备将刷新不完整的信息帧并认为存在丢包现象。

功能码

  • 01 读线圈状态 位操作 读单个或多个
  • 02 读离散输入状态 位操作 读单个或多个
  • 03 读保持寄存器 字操作 读单个或多个
  • 04 读输入寄存器 字操作 读单个或多个
  • 05 写单个线圈 位操作 单个
  • 06 写单个保持寄存器 字操作 单个
  • 15 写多个线圈 位操作 多个
  • 16 写多个保持寄存器 字操作 多个

example code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//GPIO9 is RXD1
//GPIO10 is TXD1
//帧结构 = 地址 + 功能码+ 数据 + 校验 = slaveID + 0X03 + 0X0
#include "DHT.h"
#include <HardwareSerial.h>

#define DHTPIN 4
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

HardwareSerial SerialPort(1); // use UART1
#define bufferSize 255 //一帧数据的最大字节数量 报文最大数量
#define baudrate 9600 //定义通讯波特率
#define slaveID 0x07 //定义modbus RTU从站站号
#define modbusDataSize 100 //定义modbus数据库空间大小
#define N 5

void setup()
{
Serial.begin(115200);
Serial1.begin(9600, SERIAL_8N1, 35, 33);
pinMode(32,OUTPUT);
digitalWrite(32, LOW);
dht.begin();
}

void loop() {
//延时
unsigned int characterTime;
//数据缓冲区定义
unsigned char frame[bufferSize];
//接收到的数据原始CRC
unsigned int receivedCrc;
//接收到的数据长度
unsigned char address=0;
unsigned char temp_h;//温度整数部分
unsigned char temp_l;//温度小数部分
//读取DHT11温度
float h = dht.readHumidity();
// Read temperature as Celsius (the default)
float t = dht.readTemperature();
// Read temperature as Fahrenheit (isFahrenheit = true)
float f = dht.readTemperature(true);
// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(t) || isnan(f)) {
Serial.println(F("Failed to read from DHT sensor!"));
return;
}
temp_h=(unsigned char ) t;
temp_l=0x00;
delay(100);

//延时1.5个字符宽度
characterTime = 15000000/baudrate;

//如果串口缓冲区数据量大于0进入条件
while(Serial1.available()>0)
{
//接收的数据量应小于一帧数据的最大字节数量
if(address<bufferSize)
{
frame[address]=Serial1.read();
Serial.write(frame[address]);
address++;
}
else
{
//清空缓冲区
Serial1.read();
}
//延迟
delayMicroseconds(characterTime);
//数据读取完成
if(Serial1.available()==0)
{
//校验CRC
unsigned short internalCrc = ModRTU_CRC((char*)frame, address-2);
internalCrc >> 1;
unsigned char high = internalCrc&0xFF;
unsigned char low = internalCrc>>8;
Serial.write(&frame[0], N+2);

//校验通过
if(low==frame[address - 1]&&high==frame[address - 2])
{
unsigned char slaveCode = frame[0];
//设备号匹配,兼容广播模式
if(slaveCode==slaveID||slaveCode==0)
{
//检查功能码
unsigned char funcCode = frame[1];
if(funcCode==3)
{
//组装返回的数据
frame[2] = 0x02;
frame[3] = temp_h;
frame[4] = temp_l;
//frame[5] = 0x00;
//frame[6] = val;
}
}
}
internalCrc = ModRTU_CRC((char*)frame, N);
internalCrc >> 1;
frame[N] = internalCrc&0xFF;
frame[N+1] = internalCrc>>8;
digitalWrite(32, HIGH);
Serial1.write(&frame[0], N+2);
Serial.write(&frame[0], N+2);
delay(10);
digitalWrite(32, LOW);
}
}
}

unsigned int ModRTU_CRC(char * buf, int len)
{
unsigned int crc = 0xFFFF;
for (int pos = 0; pos < len; pos++) {
crc ^= (unsigned int)buf[pos];
for (int i = 8; i != 0; i--) {
if ((crc & 0x0001) != 0) {
crc >>= 1;
crc ^= 0xA001;
}
else
crc >>= 1;
}
}
return crc;
}

评论