modbus协议使用小记
下载了libmodbus库,交叉编译后运行,总是接收回复时不正确。原因不明。
由于使用到modbus的需求比较简单,所以选择直接拼出modbus的请求报文,然后用串口直接发送和接收的方式,
拼modbus的请求报文关键在于理解modbus协议,
比如请求报文:
[01][03][00][00][00][02][c4][0b]
第一个字节(0x01)表示设备地址(从机地址),就是设备编号,可以同时接多个设备,设备之间用设备号区分。
第二个字节(0x03)表示功能号,常见的如下:
01 (0x01) 读线圈
02 (0x02) 读离散量输入
03 (0x03) 读保持寄存器
04 (0x04) 读输入寄存器
05 (0x05) 写单个线圈
06 (0x06) 写单个寄存器
15 (0x0F) 写多个线圈
16 (0x10) 写多个寄存器
这里03表示读保持寄存器,具体寄存器的意义需要根据厂家提供的说明。
第三、四字节 (0x00 0x00)表示寄存器起始地址,这里是0,表示从第一个寄存器开始。
第五、六字节 (0x00 0x02) 表示地址偏移量,这里是2,表示从起始地址开始偏移2个字节。
第七、八字节 (0xc4 0x0b) 是CRC校验码。0xc4 是低位,0x0b是高位。就是反序的。
以上一起连起来,就是读取0x01号从机的保持寄存器中从0x00地址开始,到0x00+0x02地址为止的值。
一开始,在485接口上试的时候总是发送什么接收到什么,原因不明,后来再发送和接收之间加上了sleep,
终于能接收到回复报文了,但是前面还是会接收到自己发送的请求报文,尝试过清楚串口缓存,还是不行,
没办法,只能在接收到的报文中手动去掉前面8个字节的请求报文,这样处理后是可以用的,接收后使用查表法
校验CRC。
一开始接收的时候使用了select, 然后在接收时会有20%左右概率出现illegal seek,原因不明。后来去掉了select,
直接使用read读,这样效果好了很多,出现illegal seek的概率大概只有1%。已经够用了,出现illegal seek的时候
检查CRC报错后直接放弃,依然使用上一次的数据。
checkcrc.h
/*
* checkcrc.h
*
* Created on: May 6, 2015
* Author: root
*/ #ifndef CHECKCRC_H_
#define CHECKCRC_H_
#include <stdint.h> uint16_t CRC16(uint8_t *Pushdata, uint8_t length); #endif /* CHECKCRC_H_ */
checkcrc.cpp
/*
* checkcrc.c
*
* Created on: May 6, 2015
* Author: root
*/ #include "checkcrc.h" /* CRC 高位字节值表
CRC high byte
*/
const uint8_t auchCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
};
/* CRC低位字节值表
CRC low byte
*/
const uint8_t auchCRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
0x43, 0x83, 0x41, 0x81, 0x80, 0x40
} ; uint16_t CRC16(uint8_t *Pushdata, uint8_t length)
{
uint8_t uchCRCHi=0xFF;
uint8_t uchCRCLo=0xFF;
uint8_t uIndex;
while( length-- )
{
uIndex=uchCRCHi^*Pushdata++;
uchCRCHi=uchCRCLo^auchCRCHi[uIndex];
uchCRCLo=auchCRCLo[uIndex];
}
//printf("crchi:%.2x crclo:%.2x \n", uchCRCHi, uchCRCLo);
return (uchCRCHi<< | uchCRCLo);
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> //文件控制定义
#include <termios.h>//终端控制定义
#include <errno.h>
//#include <stdint.h>
#include <string.h>
#include "checkcrc.h" const char *comport = "/dev/ttySAC1";
int bRate = ; int serial_fd = ; //打开串口并初始化设置
int init_serial(void)
{
serial_fd = open(comport, O_RDWR | O_NOCTTY | O_NDELAY);
if (serial_fd < ) {
perror("open");
return -;
}
else
printf("open %s success! \n", comport); //串口主要设置结构体termios <termios.h>
struct termios options; /**1. tcgetattr函数用于获取与终端相关的参数。
*参数fd为终端的文件描述符,返回的结果保存在termios结构体中
*/
tcgetattr(serial_fd, &options);
/**2. 修改所获得的参数*/
options.c_cflag |= (CLOCAL | CREAD);//设置控制模式状态,本地连接,接收使能
options.c_cflag &= ~CSIZE;//字符长度,设置数据位之前一定要屏掉这个位
options.c_cflag &= ~CRTSCTS;//无硬件流控
options.c_cflag |= CS8;//8位数据长度
options.c_cflag &= ~CSTOPB;//1位停止位
options.c_iflag |= IGNPAR;//无奇偶检验位
options.c_oflag = ; //输出模式
options.c_lflag = ; //不激活终端模式 cfsetospeed(&options, B38400);//设置波特率 /**3. 设置新属性,TCSANOW:所有改变立即生效*/
tcflush(serial_fd, TCIFLUSH);//溢出数据可以接收,但不读
tcsetattr(serial_fd, TCSANOW, &options); return ;
} void uart_send(int fd)
{
const unsigned char buf[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0xc4, 0x0b};
int len = write(fd, buf, sizeof(buf));
//tcflush(fd, TCIOFLUSH);
printf("send length = %d \n", len);
int i;
for (i = ; i < sizeof(buf); i++)
printf("[%.2x]", buf[i]);
printf("\n");
} void uart_recv(int fd, unsigned char *modbusdata, int *length)
{
unsigned char data[];
int len=, ret = ;
fd_set fs_read;
struct timeval tv_timeout; //FD_ZERO(&fs_read);
//FD_SET(fd, &fs_read);
tv_timeout.tv_sec = ;
tv_timeout.tv_usec = ;
/*
ret = select(fd+1, &fs_read, NULL, NULL, &tv_timeout);
if (ret == -1)
{
printf("select time out\n");
}
if (FD_ISSET(fd, &fs_read)) {
len = read(fd, data, sizeof(data));
printf("receive len: %d (bytes)\n", len);
} else {
perror("select");
}
*/
len = read(fd, data, sizeof(data));
printf("receive len: %d (bytes)\n", len);
unsigned char * p = NULL;
p = data;
p += ;
len -= ;
printf("modbus data length: %d (bytes)\n", len);
memcpy(modbusdata, p, len);
*length = len;
} void modbusCompute(unsigned char * modbusdata, int len)
{
unsigned char * p;
p = modbusdata;
int i;
for (i = ; i < len; i++)
{
printf("<%.2x>",modbusdata[i]);
}
printf("\n");
p += ;
uint16_t hiValue = *p * + *(p+);
uint16_t loValue = *(p+) * + *(p+);
uint16_t sp = hiValue * * + loValue;
printf("[ sp = %d ]\n", sp);
uint16_t sl = ;
uint16_t sh = ;
double x;
x = * (sp - sl)*1.0/(sh - sl);
printf("[x = %.2lf]\n", x); double y;
if (x > && x <= )
y = 0.7 * x;
else if (x > && x <= )
y = 0.6 * x + 0.1;
else if (x > && x <= )
y = 0.25 * x + 1.5;
else if (x > && x <= )
y = 0.36 * x + 0.4;
else if (x > && x <= )
y = 0.29 * x + 1.8;
else if (x > )
y = 0.25 * x + ;
printf("[y = %.2lf]\n", y);
} int main(int argc, char **argv)
{
unsigned char modbusdata[];
int length;
init_serial();
uart_send(serial_fd);
usleep();
uart_recv(serial_fd, modbusdata, &length); uint16_t calCrc = modbusdata[length-] * + modbusdata[length-]; if (calCrc == CRC16(modbusdata, length-))
{
printf("crc ok\n");
modbusCompute(modbusdata, length);
}else{
printf("crc error\n");
} close(serial_fd);
return ;
}
(完)
modbus协议使用小记的更多相关文章
- modbus协议讲义
Modbus 一个工业上常用的通讯协议.一种通讯约定.Modbus协议包括RTU.ASCII.TCP.其中MODBUS-RTU最常用,比较简单,在单片机上很容易实现.虽然RTU比较简单,但是看 ...
- 模拟Modbus协议问题
问题: 在嵌入式系统开发中,Modbus协议是工业控制系统中广泛应用的一种协议.本题用来简单模拟Modbus协议,只需根据条件生成符合该协议的数据帧,并解析所获取的数据.假设设备使用的协议发送数据格式 ...
- 各种非标232,485协议,自定义协议转modbus协议模块定制开发,各种流量计协议转modbus,
工业现场经常会碰到通过485或者232采集各类仪表数据,但是很多早期的仪表和设备不支持标准modbus协议,而是采用自定义的协议,这些协议数据由plc或者dcs系统来实现采集,不仅费时麻烦,而且不方便 ...
- Socket编程之聊天程序 - 模拟Fins/ModBus协议通信过程
设备控制软件编程涉及到的基本通信方式主要有TCP/IP与串口,用到的数据通信协议有Fins与ModBus. 更高级别的通信如.net中的Remoting与WCF在进行C/S架构软件开发时会采用. 本篇 ...
- 基于AVR128单纯Modbus协议实施
Modbus通信协议Modicon公司1979在发展中,适用于工业现场总线协议控制.Modbus通信系统包含芯片的节点,并与组合物可编程控制的公共传输线,它的目的是收集和监视多个节点的数据.Modbu ...
- MODBUS协议详解
MODBUS是一个工业上通信常用的通讯协议,一般在PLC上面用的比较多,主要是定义了一种数据传输的规范,比如数据发给谁,数据是干嘛的,数据错没错,接收到数据的从机告诉我数据有没有接受到等. 传输的方式 ...
- C# MODBUS协议 上位机(转)
源:C# MODBUS协议 上位机 C#写了一款上位机监控软件,基于MODBUS_RTU协议. 软件的基本结构: 采用定时器(Timer控件)为时间片. 串口采用serialPort1_DataRec ...
- 串口屏Modbus协议,串口屏的modbus协议资料,串口屏modbus通讯协议开发,串口屏之modbus协议使用技巧
串口屏Modbus协议,串口屏的modbus协议资料,串口屏modbus通讯协议开发,串口屏之modbus协议使用技巧 本例程中用51单片机作为Modbus从机,从机的设备地址为2,从机有4个寄存器, ...
- 认识Modbus协议
1.什么是Modbus? Modbus协议是应用于电子控制器上的一种通用语言.通过此协议,控制器相互之间,控制器经由网络(例如以太网)和其它设备之间可以通信.Modbus协议定义了一个控制器能认识使用 ...
随机推荐
- load average 定义(网易面试)
1. load average 定义 linux系统中的Load对当前CPU工作量的度量.简单的说是进程队列的长度. Load Average 就是一段时间 (1 分钟.5分钟.15分钟) 内平均 L ...
- 【技术博客】MySQL和Django常用操作
MySQL和Django是搭建网站常用的配置之一,在此记录一下在Windows系统搭建网站时MySQL以及Django常用的操作. MySQL MySQL的SQL语句不区分大小写,推荐将保留字大写,数 ...
- 利用Windows内置工具winsat测试硬盘速度(SSD&机械盘对比)
利用Windows内置工具winsat测试硬盘速度(SSD&机械盘对比) 以下是红色内容是在命令行运行: C:\Users\Administrator>winsat diskWindow ...
- JAVA字符编码一:Unicode,GBK,GB2312,UTF-8概念基础
第一篇:JAVA字符编码系列一:Unicode,GBK,GB2312,UTF-8概念基础 来源:holen'blog 对字符编码与Unicode,ISO 10646,UCS,UTF8,UTF16, ...
- Nginx warn:an upstream response is buffered to a temporary file
我通过nginx下载文件,error.log中出现如下警告日志:warn:an upstream response is buffered to a temporary file . 虽然网上各种例 ...
- 使用bugly热更新时自定义升级弹窗的UI样式
项目的热更新用的bugly,不过一直都只是使用他自带的升级弹窗. 不过UI小姐姐说弹窗太丑了,要自定义. bugly有提供自定义UI的官方文档:https://bugly.qq.com/docs/us ...
- git 本地删除修改文件后从远程拉取
单个文件 git checkout a.php 当前目录 git checkout .
- postman 测试Excel文件导入导出功能
1.导入的测试方法 选择form-data,key值填写方法对应的参数,选择File,Value处上传文件即可. 2. 导出的测试方法 在导出文件的时候,响应结果是乱码,然后在测试的时候选择下载,下载 ...
- Operation之结合操作符
startWith 该方法会在Observable序列开始之前插入一些事件元素. 即发生事件消息之前, 会发出这些预先插入的事件消息 Observable.of("2", &quo ...
- Ubuntu16.04安装Supervisor
安装 sudo apt-get install supervisor 启动,否则会报 unix:///tmp/supervisor.sock no such file service supervis ...