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协议定义了一个控制器能认识使用 ...
随机推荐
- Spring系列:下载
Spring 官网:http://projects.spring.io/spring-framework/ Spring下载地址:https://repo.spring.io/simple/libs- ...
- MySQL 是如何利用索引的
阅读本文大概需要 4 分钟. 一.前言 在 MySQL 中进行 SQL 优化的时候,经常会在一些情况下,对 MySQL 能否利用索引有一些迷惑.例如: MySQL 在遇到范围查询条件的时候就停止匹配了 ...
- 【译文】走出Java ClassLoader迷宫 Find a way out of the ClassLoader maze
本文是一篇译文.原文:Find a way out of the ClassLoader maze 对于类加载器,普通Java应用开发人员不需要了解太多.但对于系统开发人员,正确理解Java的类加载器 ...
- GoodNotes 模板分享
画了一个A4纸模板,分享出来: 模板下载 原始PSD下载
- Django实现自动发布(1数据模型)
公司成立之初,业务量较小,一个程序包揽了所有的业务逻辑,此时服务器数量少,上线简单,基本开发-测试-上线都是由开发人员完成. 随着业务量逐渐上升,功能增多,代码量增大,而单一功能上线需要重新编译整个程 ...
- jenkins安装启动(docker)
mkdir /opt/jenkins -pvim /opt/jenkins/Dockerfile FROM jenkins/jenkins:lts EXPOSE 8080 50000 vim /opt ...
- Spring Boot-JPA、Hibernate、Spring data jpa之间的关系
什么么是JPA? 全称Java Persistence API,可以通过注解或者XML描述[对象-关系表]之间的映射关系,并将实体对象持久化到数据库中. 为我们提供了: 1)ORM映射元数据:JPA支 ...
- js判断字符串是否以指定字符串开头或是否包含指定字符串
1. 用js判断一个字符串是否是以某个子字符串开头如:ssss001是否以ssss开头, 可以这样做: 1 2 3 4 5 6 var fdStart = strCode.indexOf(" ...
- DVT JetBrains License Server(JetBrains授权服务器)2018 v1.1 最新版 含32位/64位
DVT JetBrains License Server是JetBrains系列软件授权服务器,支持2017版本得jetbrains pycharm,JetBrainswebstorm,JetBrai ...
- springboot装配OkHttp组件
在SpringBoot应用中,发送Http通常我们使用RestTemplate,但有部分组件底层是使用OkHttp进行Http的操作,而且OKHttp也是一个很优秀的HTTP组件. RestTempa ...