<环境配置>——树莓派libmodbus编译使用实例

1.安装编译

主要有最新版本和稳定版本3.0.6。最新版本编译较为简单,但是可能出现错误,实测树莓派4B出现部分错误仍然可以使用。

github地址:

https://github.com/stephane/libmodbus

 1.直接命令行下载:

git clone git://github.com/stephane/libmodbus

2.安装编译工具

sudo apt-get install automake autoconf libtool

3.进入解压后的文件,运行./autogen.sh,自动配置./configure环境

./autogen.sh

4.新建安装环境和进行交叉编译,修改install相应目录

mkdir install

 ./configure --host=arm-none-linux-gnueabi --prefix=/pi/git-proj/libmodbus/install/

make install

5.install安装完成后,将其目录下libmodbus.so、libmodbus.so.5、libmodbus.so.5.1.0 复制到系统/usr/lib/目录下,解决动态库调用的问题。

检验库是否配置成功:

分别在2个命令行下运行modbus/tests/unit-test-server 和unit-test-client tcp,用系统私有地址进行模拟通信,如果能收到报文,说明库配置的没有问题。

./unit-test-server tcp

./unit-test-client tcp

2.SDK相应的说明

https://blog.csdn.net/qq_23670601/article/details/82155378#%E6%BA%90%E6%96%87%E6%A1%A3libmodbus

感谢以上博主,将libmodbus官方手册翻译成中文,可对应参照查看。

3.仿真软件

很多同学可能只有一台电脑,或者没有现场的PLC,可以下载Modbus Poll和Modbus Slave模拟主站和从站的通讯。

https://www.cnblogs.com/hieroly/p/9063710.html

 楼主进行的是modbus tcp通讯:

树莓派---Server---从站slave(等待PLC报文后,进行数据采集,返回报文)

西门子PLC---Client---主站Poll(发送报文获取数据)

4.Modbus TCP功能码

ModbusTCP的数据帧可分为两部分:MBAP+PDU。

 

MBAP为报文头

长度为7字节,组成如下:

帧结构PDU——功能码+数据

Modbus的操作对象有四种:

1.线圈---可读可写

2.离散输入---可读

3.输入寄存器--可读

4.保持寄存器---可读可写

功能码:16进制

0x01: 读线圈寄存器            1

0x02: 读离散输入寄存器     2

0x03: 读保持寄存器            3

0x04: 读输入寄存器            4

0x05: 写单个线圈寄存器     5

0x06: 写单个保持寄存器     6

0x0f: 写多个线圈寄存器     15

0x10: 写多个保持寄存器    16

0x17:读写多个保持寄存器  23

功能码实例:

功能码0x17 

 

 

 

 

 

 

 

 

5.实际软件程序

unit-test.h

#ifndef _UNIT_TEST_H_
#define _UNIT_TEST_H_

/* Constants defined by configure.ac */
#define HAVE_INTTYPES_H 1
#define HAVE_STDINT_H 1

#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#ifdef HAVE_STDINT_H
# ifndef _MSC_VER
# include <stdint.h>
# else
# include "stdint.h"
# endif
#endif

#define SERVER_ID         17
#define INVALID_SERVER_ID 18


//开辟本地Server的线圈和寄存器

//线圈(一个线圈,2个字节0xFF)
const uint16_t UT_BITS_ADDRESS = 0x00;             //线圈首地址
const uint16_t UT_BITS_NB = 0x04;                  //线圈数量
const uint8_t UT_BITS_TAB[] = { 0x01, 0x02, 0x03, 0x04 }; //线圈初始值
//input线圈
const uint16_t UT_INPUT_BITS_ADDRESS = 0x04;
const uint16_t UT_INPUT_BITS_NB = 0x04;
const uint8_t UT_INPUT_BITS_TAB[] = { 0x01, 0x02, 0x03, 0x04};

//寄存器(一个寄存器,4个字节0xFFFF,高位+低位)
const uint16_t UT_REGISTERS_ADDRESS = 0x00;
/* Raise a manual exception when this adress is used for the first byte */
const uint16_t UT_REGISTERS_ADDRESS_SPECIAL = 0x0F;
const uint16_t UT_REGISTERS_NB = 0x04;
const uint16_t UT_REGISTERS_TAB[] = { 0x1111,0x0002, 0x0003, 0x0004 };
/* If the following value is used, a bad response is sent.
   It's better to test with a lower value than
   UT_REGISTERS_NB_POINTS to try to raise a segfault. */
const uint16_t UT_REGISTERS_NB_SPECIAL = 0x2;



//input寄存器
const uint16_t UT_INPUT_REGISTERS_ADDRESS = 0x00;
const uint16_t UT_INPUT_REGISTERS_NB = 0x01;
const uint16_t UT_INPUT_REGISTERS_TAB[] = { 0x000A };

const float UT_REAL = 916.540649;
const uint32_t UT_IREAL = 0x4465229a;

#endif /* _UNIT_TEST_H_ */
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <modbus.h>
#include <iostream>

#include "unit-test.h"
using namespace  std;


int main()
{
    int socket;
    modbus_t *ctx;
    modbus_mapping_t *mb_mapping;
    int rc = -1;
    uint8_t *query;
    int header_length;

    //创建Modbus Tcp环境
    ctx = modbus_new_tcp("192.168.178.100", 8888);
    //创建报文
    query = (uint8_t*)malloc(MODBUS_TCP_MAX_ADU_LENGTH);

    //分配线圈,寄存器的数组
    mb_mapping = modbus_mapping_new(
        UT_BITS_ADDRESS + UT_BITS_NB,
        UT_INPUT_BITS_ADDRESS + UT_INPUT_BITS_NB,
        UT_REGISTERS_ADDRESS + UT_REGISTERS_NB,
        UT_INPUT_REGISTERS_ADDRESS + UT_INPUT_REGISTERS_NB);
    if (mb_mapping == NULL) {
        fprintf(stderr, "Failed to allocate the mapping: %s\n",
                modbus_strerror(errno));
        modbus_free(ctx);
        return -1;
    }

    //初始化赋值
    //线圈
    for(int i = 0;i<UT_BITS_NB;i++){
        mb_mapping->tab_bits[i] = UT_BITS_TAB[i];
    }
    //input线圈
    for(int i = 0;i<UT_INPUT_BITS_NB;i++){
        mb_mapping->tab_input_bits[i] = UT_INPUT_BITS_TAB[i];
    }
    //寄存器
    for(int i = 0;i<UT_REGISTERS_NB;i++){
        mb_mapping->tab_registers[i] = UT_REGISTERS_TAB[i];
    }
    //寄存器
    for(int i = 0;i<UT_INPUT_REGISTERS_NB;i++){
        mb_mapping->tab_input_registers[i] = UT_INPUT_REGISTERS_TAB[i];
    }


    //从后端,检索当前表头长度
    header_length = modbus_get_header_length(ctx);

    //设置从站,从站序列号
    modbus_set_slave(ctx, SERVER_ID);
    //启动调试模式
    modbus_set_debug(ctx, TRUE);

    //设置超时相应时间
//    struct timeval response_timeout;
//    response_timeout.tv_sec = 1;
//    response_timeout.tv_usec = 0;
//    modbus_set_response_timeout(ctx,&response_timeout);


    //开始监听
    socket = modbus_tcp_listen(ctx, 5);
    //开始阻塞
    modbus_tcp_accept(ctx, &socket);
    cout<<"accept !!"<<endl;

    for (;;) {
        //收到指示请求,并返回请求长度
        //规定发送功能码0x17报文:格式
        rc = modbus_receive(ctx, query);
        cout<<"以上为接收报文,报文长:"<<rc<<endl;

        if (rc == -1) {
            /* Connection closed by the client or error */
            modbus_tcp_accept(ctx, &socket);
            continue;
        }

        if (query[header_length] == 0x17) {
            cout<<"功能码:"<<"0x17"<<endl;
            if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 3)
                == UT_REGISTERS_NB_SPECIAL) {
                cout<<"0x171"<<endl;
                printf("Set an incorrect number of values\n");
                MODBUS_SET_INT16_TO_INT8(query, header_length + 3,
                                         UT_REGISTERS_NB_SPECIAL - 1);
            } else if (MODBUS_GET_INT16_FROM_INT8(query, header_length + 1)
                == UT_REGISTERS_ADDRESS_SPECIAL) {
                cout<<"0x172"<<endl;
                printf("Reply to this special register address by an exception\n");
                modbus_reply_exception(ctx, query,
                                       MODBUS_EXCEPTION_SLAVE_OR_SERVER_BUSY);
                continue;
            }
            int slope = 0;
            double high = 0;
            int high1 = 0;
            int high2 = 0;
            //liquid high
            cam.grab();
            cam.retrieve(frame);
            temp =frame(Rect(roiX,roiY,roiW,roiH));
            input = otsu(temp);

            edgeDetect::AboveProcess(input,slope,begin,end);
            edgeDetect::belowProcess(input,templateImg,tubeWidth,high);

            high1 = (static_cast<int>(high*10)) /10;
            high2 = (static_cast<int>(high*10)) %10;

            //低位表示
            query[18]= slope;
            //前为液面高度的小数点前数值,后为小数点后数值
            query[20]= high1;
            query[22]= high2;

        }
        cout<<"回复报文:"<<endl;
        rc = modbus_reply(ctx, query, rc, mb_mapping);
        cout<<"回复报文长度:"<<rc<<endl;
        if (rc == -1) {
            break;
        }
    }

    printf("Quit the loop: %s\n", modbus_strerror(errno));

    close(socket);
    modbus_mapping_free(mb_mapping);
    free(query);
    //释放Modbus Tcp环境
    modbus_free(ctx);

    return 0;

}

 

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值