libmodbus es una biblioteca de controladores modbus basada en lenguaje C, que admite sistemas operativos como
Linux
, ,Mac OS X
,Win32
etc.Se admiten las siguientes funciones:
- apoyo
Modbus-RTU
_- apoyo
Modbus-TCP
_- Admite códigos de función comunes (01/02/03/04/05/06/07/0F/10/11/16/17).
- Admite lectura y escritura de tipo de bobina, lectura y escritura de registro, lectura discreta, etc.
- Admite dirección de transmisión
0
, dirección esclava1-247
.- Admite conversión de datos enteros y de punto flotante, endian grande y pequeño y otros modos.
1 Obtener el código fuente
La última versión del sitio web oficial: libmodbus-3.1.7.tar.gz
Dirección de almacén de código abierto: https://github.com/stephane/libmodbus/
2 Genere el archivo de configuración config.h
Ejecute el siguiente comando en la terminal de Linux para completar la descompresión:
sudo tar zxvf libmodbus-3.1.7.tar.gz
La estructura del directorio libmodbus-3.1.7 descomprimido es la siguiente:
.
├── acinclude.m4
├── aclocal.m4
├── AUTHORS
├── build-aux/
├── config.h.in
├── configure*
├── configure.ac
├── COPYING.LESSER
├── doc/
├── libmodbus.pc.in
├── m4/
├── Makefile.am
├── Makefile.in
├── MIGRATION
├── NEWS
├── README.md
├── src/
└── tests/
Ejecute el siguiente comando en la terminal de Linux para generar el archivo de configuración config.h:
./configure
Después de la generación, el indicador es el siguiente:
libmodbus 3.1.7
===============
prefix: /usr/local
sysconfdir: ${prefix}/etc
libdir: ${exec_prefix}/lib
includedir: ${prefix}/include
compiler: gcc
cflags: -g -O2
ldflags:
documentation: no
tests: yes
3 uso
3.1 Maestro Modbus
3.1.1 Sondeo-Modbus-TCP
Use Modbus Slave para simular el dispositivo esclavo, libmodbus como maestro para leer y escribir el registro de retención esclavo.
(1) Configuración de conexión Modbus Esclavo
Haga clic en Conexión, seleccione Conectar, aparecerá el cuadro de diálogo Configuración de la conexión, seleccione Conexión Modbus TCP/IP
, complete la Dirección IP como 192.168.3.100
(dirección local de la computadora) y complete el Puerto como 502
.
(2) Configuración de esclavo esclavo Modbus
Haga clic en Configuración, seleccione Definición de esclavo, complete ID de esclavo como 1
, Función como 03 Holding Register(4x)
, Dirección como 0
y Cantidad como 10
.
(3) rutinas de lectura y escritura poll_wr_regs_demo
/**
* @file poll_wr_regs_demo.c
* @author 李云亮 ([email protected])
* @brief modbus主站读写多个保持寄存器
* @version 1.0.0
* @date 2022-08-19
*
* @copyright Copyright (c) 2022
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "modbus.h"
#define MODBUS_TCP_SERVER_IP "192.168.3.100" // TCP IP地址
#define MODBUS_TCP_SERVER_PORT 502 // TCP 端口号
#define REG_ADDR_START 0 // 寄存器起始地址
#define REG_ADDR_END 9 // 寄存器结束地址
#define REG_NUM ((REG_ADDR_END) - (REG_ADDR_START) + 1) // 寄存器个数
int main(int argc, char const *argv[])
{
/* code */
int i = 0;
int ret = 0;
modbus_t* ctx = NULL;
uint16_t* sendBuf = NULL;
uint16_t* recvBuf = NULL;
// 1.创建TCP
ctx = modbus_new_tcp(MODBUS_TCP_SERVER_IP, MODBUS_TCP_SERVER_PORT);
if (NULL == ctx)
{
printf("Set modbus TCP failed!\n");
return -1;
}
// 2.设置调试模式
ret = modbus_set_debug(ctx, TRUE);
if (-1 == ret)
{
printf("Set modbus debug mode failed!\n");
}
// 3.连接Server
ret = modbus_connect(ctx);
if (-1 == ret)
{
printf("Connect modbus server failed!\n");
modbus_free(ctx);
return -1;
}
// 4.设置从机地址
ret = modbus_set_slave(ctx, 1);
// 5.申请内存
sendBuf = (uint16_t*)malloc(REG_NUM * (sizeof(uint16_t)));
if (NULL == sendBuf)
{
printf("modbus sendBuf malloc failed!\n");
free(sendBuf);
return -1;
}
recvBuf = (uint16_t*)malloc(REG_NUM * (sizeof(uint16_t)));
if (NULL == recvBuf)
{
printf("modbus recvBuf malloc failed!\n");
free(recvBuf);
return -1;
}
memset(sendBuf, 0, REG_NUM);
memset(recvBuf, 0, REG_NUM);
// 6.写入多个保持寄存器
for (i = 0; i < REG_NUM; i++)
{
sendBuf[i] = i;
}
ret = modbus_write_registers(ctx, REG_ADDR_START, REG_NUM, sendBuf);
if (REG_NUM != ret)
{
printf("modbus write regs failed!\n");
return -1;
}
else
{
// 7.读多个保持寄存器
ret = modbus_read_registers(ctx, REG_ADDR_START, REG_NUM, recvBuf);
if (REG_NUM != ret)
{
printf("modbus read regs failed!\n");
return -1;
}
else
{
printf("result data:\n");
for (i = 0; i < REG_NUM; i++)
{
printf("%d ", recvBuf[i]);
}
printf("\n");
}
}
// 8.释放内存
free(sendBuf);
free(recvBuf);
// 9.断开连接
modbus_close(ctx);
modbus_free(ctx);
return 0;
}
(4) Compilar y ejecutar
lyl@ubuntu18:~/Desktop/gitee/libmodbus-demo/libmodbusUse$ make
+ Linking output/demo/poll_wr_regs_demo ...
lyl@ubuntu18:~/Desktop/gitee/libmodbus-demo/libmodbusUse$ ./output/demo/poll_wr_regs_demo
Connecting to 192.168.3.100:502
[00][01][00][00][00][1B][01][10][00][00][00][0A][14][00][00][00][01][00][02][00][03][00][04][00][05][00][06][00][07][00][08][00][09]
Waiting for a confirmation...
<00><01><00><00><00><06><01><10><00><00><00><0A>
[00][02][00][00][00][06][01][03][00][00][00][0A]
Waiting for a confirmation...
<00><02><00><00><00><17><01><03><14><00><00><00><01><00><02><00><03><00><04><00><05><00><06><00><07><00><08><00><09>
result data:
0 1 2 3 4 5 6 7 8 9
(5) Análisis del marco de datos
-
Análisis de registro de escritura maestra
# 主站写多个寄存器 [00][01][00][00][00][1B][01][10][00][00][00][0A][14][00][00][00][01][00][02][00][03][00][04][00][05][00][06][00][07][00][08][00][09]
identificador de comunicación identificador de protocolo Longitud de datos dirección esclava Código de función dirección inicial Número de registros Longitud de datos datos 00 01 00 00 00 1B 01 0x10(16) 00 00 00 0A(10) 0x14(20) 0-9 # 从站返回写结果 <00><01><00><00><00><06><01><10><00><00><00><0A>
identificador de comunicación identificador de protocolo Longitud de datos dirección esclava Código de función dirección inicial Número de registros 00 01 00 00 00 06 01 0x10(16) 00 00 00 0A(10) -
Análisis de registro de lectura maestro
# 主站读多个寄存器 [00][02][00][00][00][06][01][03][00][00][00][0A]
identificador de comunicación identificador de protocolo Longitud de datos dirección esclava Código de función dirección inicial Número de registros 00 02 00 00 00 06 01 03 00 00 00 0A(10) # 从站返回读结果 <00><02><00><00><00><17><01><03><14><00><00><00><01><00><02><00><03><00><04><00><05><00><06><00><07><00><08><00><09>
identificador de comunicación identificador de protocolo Longitud de datos dirección esclava Código de función Longitud de datos datos 00 02 00 00 00 17(23) 01 03 0x14(20) 0-9
3.2 Esclavo Modbus
3.2.1 Esclavo-Modbus-TCP
Utilice Modbus Poll para simular un dispositivo maestro y libmodbus como esclavo.
(1) Configuración de conexión de sondeo Modbus
Haga clic en Conexión, seleccione Conectar, aparecerá el cuadro de diálogo Configuración de la conexión, seleccione Conexión Modbus TCP/IP
, complete la Dirección IP como 192.168.3.102
(dirección esclava de Ubuntu) y complete el Puerto como 1502
.
(2) Configuración de host esclavo Modbus
Haga clic en Configuración, seleccione Definición de lectura/escritura, complete ID de esclavo como 1
, Función como 03 Holding Register(4x)
, Dirección como 0
y Cantidad como 10
.
(3) Slave_wr_regs_demo rutinas de lectura y escritura
/**
* @file slave_wr_regs_demo.c
* @author 李云亮 ([email protected])
* @brief modbus从站读写例程
* @version 1.0.0
* @date 2022-08-25
*
* @copyright Copyright (c) 2022
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "modbus.h"
#define MODBUS_SLAVE_TCP_SERVER_IP "192.168.3.102"
#define MODBUS_SLAVE_TCP_SERVER_PORT 1502
#define MAPPING_SIZE 10
#define MODBUS_POLL_CONNECT_MAX 1
int main(int argc, char const *argv[])
{
/* code */
int socket = -1;
modbus_t *ctx;
modbus_mapping_t *mb_mapping;
uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH] = {
0};
int rc = 0;
int i = 0;
// 1.创建从机TCP
ctx = modbus_new_tcp(MODBUS_SLAVE_TCP_SERVER_IP, MODBUS_SLAVE_TCP_SERVER_PORT); //开发板ip自行修改
if (NULL == ctx)
{
printf("Set modbus TCP failed!\n");
return -1;
}
// 2.设置调试模式
modbus_set_debug(ctx, TRUE);
// 3.初始化寄存器
mb_mapping = modbus_mapping_new(MAPPING_SIZE, MAPPING_SIZE, MAPPING_SIZE, MAPPING_SIZE);
if (mb_mapping == NULL) {
printf("Failed to allocate the mapping!\n");
modbus_free(ctx);
return -1;
}
for (i = 0; i < MAPPING_SIZE; i++)
{
mb_mapping->tab_registers[i] = i;
}
// 4.侦听主站连接
socket = modbus_tcp_listen(ctx, MODBUS_POLL_CONNECT_MAX);
if (-1 == socket)
{
printf("Unable to listen TCP!\n");
modbus_free(ctx);
return -1;
}
// 5.创建连接
modbus_tcp_accept(ctx, &socket);
while (1)
{
// 6.接收请求
rc = modbus_receive(ctx, query);
if (rc > 1) {
// 7.发送响应
modbus_reply(ctx, query, rc, mb_mapping);
printf("In the loop \n");
}
else
{
// Connection closed by the client or error
modbus_mapping_free(mb_mapping);
modbus_close(ctx);
modbus_free(ctx);
modbus_tcp_accept(ctx, &socket);
break;
}
}
return 0;
}