使用libmodbus库实现一个modbus-rtu的主机测试,并将获取的设备信息写入到redis实时数据库中。
在libmodbus官网下载libmodbus库,网址为:https//libmodbus.org/download/
解压刚才下载的库压缩包,
unzip libmodbus-master.zip
生成Makefile:
./configure --host=loongarch64-linux- --prefix=~/2k500/libmodbus/install
在执行配置的配过成中,会出现如下图所示的错误提示。
解决的方法:
由于龙芯的处理器配置描述在系统中不存在,所以会出现这样的提示。需要更换包含龙芯信息的如下两个文件config.guess和config.sub。将下载的两个文件替换掉/usr/share/misc中的原有两个文件即可。
执行make & make install 后,可编译出所需的库文件。
在执行了make install后,在我们执行的目录中会建立一个install目录,其中包含三个文件夹,尤其是lib下的文件,放置到2k500的相应目录下,我们就完成了部署。
为了在程序中直接操作redis数据,我们需要一个接口库。通过网络查询,发现hiredis这个C语言的操作库对于我们来说还是非常的方便的。
Hiredis是Redis数据库的一个极简C客户端库。
它是极简主义的,因为它只增加了对协议的最小支持,但同时它使用了一个高级的类似printf的API。
除了支持发送命令和接收应答之外,它还附带了一个与I/O层分离的应答解析器。它是一个流解析器,设计为易于重用,例如,它可以用于更高级别的语言绑定,以实现高效的应答解析。
Hiredis只支持二进制安全的Redis协议,所以你可以使用它与任何版本的Redis >= 1.2.0。
这个库附带了多个api。有同步API、异步API和应答解析API。
源码下载:
https://github.com/redis/hiredis(官方提供)
也可以在gitee上找到源码,下载速度也快,推荐。
将下载源码解压到ubuntu的开发环境各种,执行如下命令完成编译:
make CC=loongarch64-linux-gnu-gcc
将库文件和头文件分别提取出来备用。
我们需要将编译生成的动态库复制到ls2k500系统中的 /usr/lib目录下,并且需要修改为:
cd /usr/lib
mv libhiredis.so libhiredis.so.1.1.1-dev
ln –s libhiredis.so.1.1.1-dev libhiredis.so
建立主站程序目t3(目录随便),建立目录结果如下图所示:
其中包括inc(头文件目录),lib(库文件目录),src(源文件目录),Makefile文件。
程序代码包含两个部分,分别为Modbus主站数据获取功能代码,和redis数据库操作代码。
1)modbus主站代码
利用libmodbus库实现主站功能,主要步骤。
Modbus对象创建:
// 创建modbus对象
ctx = modbus_new_rtu(ip_or_device, 115200, 'N', 8, 1);
从站站号设置:
// 设置从站号
if (use_backend ==RTU) {
modbus_set_slave(ctx,SERVER_ID);
}
响应时间设置(100ms):
// 设置超时时间 100ms
modbus_set_response_timeout(ctx, 0, 100 * 1000);
连接:
// 连接
if (modbus_connect(ctx) == -1) {
fprintf(stderr, "Connectionfailed: %s\n", modbus_strerror(errno));
modbus_free(ctx);
return -1;
}
根据设备信息,建立数据存储内存空间:这里主要读取输入状态,保持寄存器和输入寄存器。
// 创建设备数据存储空间
/* Allocate and initialize the memory to store the bits */
nb_points = (UT_BITS_NB > UT_INPUT_BITS_NB) ? UT_BITS_NB : UT_INPUT_BITS_NB;
tab_rp_bits = (uint8_t *) calloc(nb_points, sizeof(uint8_t));
/*Allocate and initialize the memory to store the registers */
nb_points = (UT_REGISTERS_NB > UT_INPUT_REGISTERS_NB) ? UT_REGISTERS_NB: UT_INPUT_REGISTERS_NB;
tab_rp_registers= (uint16_t *) calloc(nb_points, sizeof(uint16_t));
获取数据的循环主体(简略):
// 完成设备数据获取
while(1)
{
/** DISCRETE INPUTS **/
rc = modbus_read_input_bits(ctx,UT_INPUT_BITS_ADDRESS, UT_INPUT_BITS_NB, tab_rp_bits);
/** HOLDING REGISTERS **/
rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, UT_REGISTERS_NB, tab_rp_registers);
/** INPUT REGISTERS **/
rc = modbus_read_input_registers(ctx, UT_INPUT_REGISTERS_ADDRESS, UT_INPUT_REGISTERS_NB, tab_rp_registers);
usleep(1000 * 1000);
}
清理现场,关闭连接:
/*Close the connection */
modbus_close(ctx);
modbus_free(ctx);
2)redis数据操作代码
利用hiredis库完成redis数据的操作,hiredis的各个函数的具体使用网上挺多的,这里就不详细描述了。主要实现步骤:
连接redis数据库:
//连接redis,若出错redisContext.err会设置为1,redisContext.errstr会包含描述错误信息
redisContext *redis_handle = redisConnect("172.17.100.102", 6379);
if (redis_handle == NULL || redis_handle->err)
{
if (redis_handle)
{
printf("Error:%s\n", redis_handle->errstr);
}
else
{
printf("can't allocate redis context\n");
}
return -1;
}
输入数据库密码:
reply = (redisReply *)redisCommand(redis_handle, "AUTH %s", passwd);
if( reply == NULL)
{
printf("Error: AUTH error!\n");
redisFree(redis_handle);
printf("redisFree\n");
return -1;
}
if( !(reply->type ==
REDIS_REPLY_STATUS && memcmp(reply->str, "OK", 2) == 0) )
{
printf("Error: AUTH error!\n");
freeReplyObject(reply);
redisFree(redis_handle);
printf("redisFree\n");
return -1;
}
读取的离散输入数据写入(数据库中键值为BIT+数字):
for (i = 0; i <UT_INPUT_BITS_NB; i++) {
//set
reply = (redisReply *)redisCommand(redis_handle , "SET BIT%d%02X", i, tab_rp_bits[i]);
if (reply!=NULL && reply->type==REDIS_REPLY_STATUS)
{
printf("%s\n", reply->str);
}
freeReplyObject(reply);
}
读取的保持寄存器写入(数据库中键值为HOLD_REG+数字):
for (i = 0; i <UT_REGISTERS_NB; i++) {
//set
reply = (redisReply *)redisCommand(redis_handle , "SET HOLD_REG%d%04X", i, tab_rp_registers[i]);
if (reply!=NULL && reply->type==REDIS_REPLY_STATUS)
{
printf("%s\n", reply->str);
}
freeReplyObject(reply);
}
读取的输入寄存器写入(数据库中键值为INPUT_REG+数字):
for (i = 0; i <UT_INPUT_REGISTERS_NB; i++) {
//set
reply = (redisReply *)redisCommand(redis_handle , "SET INPUT_REG%d%04X", i, tab_rp_registers[i]);
if (reply!=NULL && reply->type==REDIS_REPLY_STATUS)
{
printf("%s\n", reply->str);
}
freeReplyObject(reply);
}
网上搜索可以找到modbus的子站仿真软件,用来配合调试比较方便。
Modbus slave模拟保持寄存器的存储状态。
Modbus主机获取modbus辅机保持寄存器数据内容。
通过treenms观察redis数据库中的保持寄存器内容。
更多回帖