libModbus的安装和测试
libModbus 安装PS D:\soft\vcpkg\installed\x86-windows\binvcpkginstalllibmodbus:x64-windows PS D:\soft\vcpkg\installed\x86-windows\binvcpkglist|Select-String modbus libmodbus:x86-windows3.1.10 libmodbus is afreesoftware library to send/repython 测试保持寄存器 registers[1] 每秒加1importctypesfromctypesimport*importtime# # 加载 libmodbus 动态链接库# # 使用 ctypes 加载 Windows 平台下的 libmodbus DLL 文件# 该库提供了 Modbus TCP 协议的底层通信功能libctypes.CDLL(rD:\soft\vcpkg\installed\x86-windows\bin\modbus.dll)# # 声明 libmodbus 中需要用到的 C 函数签名# 必须指定参数类型(argtypes)和返回值类型(restype)# 否则 ctypes 会使用默认的 int 类型可能导致指针截断等问题# # modbus_new_tcp(ip, port) - modbus_t*# 创建一个新的 Modbus TCP 上下文返回上下文指针lib.modbus_new_tcp.argtypes[c_char_p,c_int]# 参数IP地址(字节串), 端口号lib.modbus_new_tcp.restypec_void_p# 返回值不透明指针(上下文句柄)# modbus_tcp_listen(ctx, nb_connection) - int# 创建监听套接字等待客户端连接lib.modbus_tcp_listen.argtypes[c_void_p,c_int]# 参数上下文指针, 最大连接数lib.modbus_tcp_listen.restypec_int# 返回值服务器套接字描述符失败返回 -1# modbus_tcp_accept(ctx, socket*) - int# 接受一个客户端连接请求lib.modbus_tcp_accept.argtypes[c_void_p,POINTER(c_int)]# 参数上下文指针, 指向服务器套接字的指针lib.modbus_tcp_accept.restypec_int# 返回值客户端套接字描述符失败返回 -1# modbus_mapping_new(nb_bits, nb_input_bits, nb_registers, nb_input_registers) - modbus_mapping_t*# 创建寄存器映射表用于存储 Modbus 数据lib.modbus_mapping_new.argtypes[c_int,c_int,c_int,c_int]# 参数线圈数, 离散输入数, 保持寄存器数, 输入寄存器数lib.modbus_mapping_new.restypec_void_p# 返回值映射表指针# modbus_receive(ctx, req_buffer) - int# 接收来自客户端的 Modbus 请求报文lib.modbus_receive.argtypes[c_void_p,POINTER(c_uint8)]# 参数上下文指针, 接收缓冲区(字节数组)lib.modbus_receive.restypec_int# 返回值接收到的字节数失败或断开返回 0# modbus_reply(ctx, req_buffer, req_length, mapping) - int# 根据收到的请求自动查找映射表并生成响应报文回复给客户端lib.modbus_reply.argtypes[c_void_p,POINTER(c_uint8),c_int,c_void_p]# 参数上下文, 请求缓冲区, 请求长度, 映射表lib.modbus_reply.restypec_int# 返回值发送的字节数失败返回 -1# # 创建 Modbus TCP 服务端# # 创建 Modbus TCP 上下文监听所有网卡(0.0.0.0)的 1502 端口# 1502 是 Modbus TCP 的默认端口号ctxlib.modbus_new_tcp(b0.0.0.0,1502)# 检查上下文是否创建成功返回 NULL(0) 表示失败ifnotctx:print(modbus_new_tcp failed)raiseSystemExit# 退出程序# 开始监听参数 1 表示最多同时接受 1 个连接server_socketlib.modbus_tcp_listen(ctx,1)print(listen:,server_socket)# 将服务器套接字包装为 ctypes 的 c_int 类型# 因为 modbus_tcp_accept 需要 POINTER(c_int) 类型的参数sockc_int(server_socket)# 阻塞等待客户端连接连接成功后返回客户端套接字描述符retlib.modbus_tcp_accept(ctx,byref(sock))print(accept:,ret)# 返回值小于 0 表示接受连接失败ifret0:print(accept failed)raiseSystemExit# # 创建寄存器映射表 (Holding Register 地址 0~32共 33 个)# # 参数说明# 第1个参数 0 - 线圈(Coil)数量这里不需要# 第2个参数 0 - 离散输入(Input Bit)数量这里不需要# 第3个参数 33 - 保持寄存器(Holding Register)数量分配 33 个(地址 0~32)# 第4个参数 0 - 输入寄存器(Input Register)数量这里不需要mappinglib.modbus_mapping_new(0,0,33,0)# 检查映射表是否创建成功ifnotmapping:print(mapping failed)raiseSystemExit# 打印映射表的内存地址调试用print(mapping:,hex(mapping))# # 获取 holding registers 数组指针用于直接读写寄存器值# # libmodbus 内部定义了 modbus_mapping_t 结构体其中包含了# 各种寄存器的数量、起始地址以及数据数组指针。# 为了在 Python 中直接操作寄存器数据需要用 ctypes.Structure# 定义一个与 C 语言完全一致的结构体然后将 mapping 指针强转为该结构体指针。## modbus_mapping_t 结构体字段与 libmodbus 源码保持一致# int nb_bits; - 线圈数量# int start_bits; - 线圈起始地址# int nb_input_bits; - 离散输入数量# int start_input_bits; - 离散输入起始地址# int nb_input_registers; - 输入寄存器数量# int start_input_registers; - 输入寄存器起始地址# int nb_registers; - 保持寄存器数量# int start_registers; - 保持寄存器起始地址# uint8_t *tab_bits; - 线圈数据数组指针# uint8_t *tab_input_bits; - 离散输入数据数组指针# uint16_t *tab_input_registers;- 输入寄存器数据数组指针# uint16_t *tab_registers; - 保持寄存器数据数组指针我们需要的classModbusMapping(Structure):与 libmodbus 中 modbus_mapping_t 结构体对应的 Python 定义_fields_[(nb_bits,c_int),# 线圈数量(start_bits,c_int),# 线圈起始地址(nb_input_bits,c_int),# 离散输入数量(start_input_bits,c_int),# 离散输入起始地址(nb_input_registers,c_int),# 输入寄存器数量(start_input_registers,c_int),# 输入寄存器起始地址(nb_registers,c_int),# 保持寄存器数量(start_registers,c_int),# 保持寄存器起始地址(tab_bits,POINTER(c_uint8)),# 线圈数据数组(tab_input_bits,POINTER(c_uint8)),# 离散输入数据数组(tab_input_registers,POINTER(c_uint16)),# 输入寄存器数据数组(tab_registers,POINTER(c_uint16)),# 保持寄存器数据数组核心]# 将 modbus_mapping_new 返回的 void* 强转为 ModbusMapping 结构体指针mapping_ptrcast(mapping,POINTER(ModbusMapping))# 取出保持寄存器数组的指针之后可以通过 registers[地址] 直接读写寄存器值registersmapping_ptr.contents.tab_registers# # 主循环每秒自增寄存器 处理 Modbus 请求# # 创建接收缓冲区260 字节足够容纳一个 Modbus TCP 报文query(c_uint8*260)()print(Modbus TCP server running...)# 记录上一次自增的时间戳用于控制每秒执行一次last_increment_timetime.time()whileTrue:# 每秒对地址 1 的保持寄存器自增 1 current_timetime.time()ifcurrent_time-last_increment_time1.0:registers[1]1# 地址 1 的寄存器值 1print(fregister[1] {registers[1]})# 打印当前值调试用last_increment_timecurrent_time# 更新上次自增时间# 接收并处理 Modbus 请求 # modbus_receive 会阻塞等待客户端发来的请求rclib.modbus_receive(ctx,query)ifrc0:# rc 0 表示成功接收到请求rc 为接收到的字节数print(recv len:,rc)# 将接收到的原始字节以十六进制格式打印出来方便调试print(bytes(query[:rc]).hex( ))# 根据请求自动查表并回复响应# modbus_reply 会读取 mapping 中对应地址的寄存器值# 组装成 Modbus 响应报文发送给客户端retlib.modbus_reply(ctx,query,rc,mapping)print(reply:,ret)else:# rc 0 表示客户端断开连接或发生错误print(client disconnected)breakc 测试#includeiostream#includecstring#includecstdio#includepthread.h#includeunistd.h#includemodbus.h// // 线程函数每秒对寄存器地址1自增1// 参数 param 传入的是 uint16_t* 类型的寄存器数组指针// void*incrementThread(void*param){uint16_t*registers(uint16_t*)param;while(true){sleep(1);// 休眠1秒POSIX标准函数单位秒registers[1]1;// 地址1的寄存器值 1std::coutregister[1] registers[1]std::endl;}returnNULL;}intmain(){// // 创建 Modbus TCP 服务端上下文// 监听所有网卡(0.0.0.0)的 1502 端口// modbus_t*ctxmodbus_new_tcp(0.0.0.0,1502);if(!ctx){std::cerrmodbus_new_tcp failedstd::endl;return1;}// // 开始监听参数 1 表示最多同时接受 1 个连接// intserver_socketmodbus_tcp_listen(ctx,1);std::coutlisten: server_socketstd::endl;// // 阻塞等待客户端连接// intretmodbus_tcp_accept(ctx,server_socket);std::coutaccept: retstd::endl;if(ret0){std::cerraccept failedstd::endl;modbus_free(ctx);return1;}// // 创建寄存器映射表// 参数线圈数, 离散输入数, 保持寄存器数, 输入寄存器数// 这里只分配 33 个保持寄存器地址 0~32// modbus_mapping_t*mappingmodbus_mapping_new(0,0,33,0);if(!mapping){std::cerrmapping failedstd::endl;modbus_free(ctx);return1;}std::coutmapping: mappingstd::endl;// // 获取保持寄存器数组指针// mapping-tab_registers 就是 uint16_t* 类型// 可以直接通过下标读写寄存器值// uint16_t*registersmapping-tab_registers;// // 创建 pthread 线程每秒对寄存器自增// pthread_create 参数// 1. 线程句柄指针// 2. 线程属性NULL 表示默认// 3. 线程函数// 4. 传给线程函数的参数// pthread_ttid;intthread_retpthread_create(tid,NULL,incrementThread,registers);if(thread_ret!0){std::cerrpthread_create failed, ret thread_retstd::endl;modbus_mapping_free(mapping);modbus_free(ctx);return1;}// 分离线程线程结束后自动回收资源无需 pthread_joinpthread_detach(tid);// // 主循环接收并处理 Modbus 请求// uint8_tquery[260];// 接收缓冲区260 字节足够容纳一个 Modbus TCP 报文std::coutModbus TCP server running...std::endl;while(true){// modbus_receive 会阻塞等待客户端发来的请求intrcmodbus_receive(ctx,query);if(rc0){// rc 0 表示成功接收到请求rc 为接收到的字节数std::coutrecv len: rcstd::endl;// 将接收到的原始字节以十六进制格式打印出来方便调试for(inti0;irc;i){printf(%02x ,query[i]);}std::coutstd::endl;// 根据请求自动查表并回复响应retmodbus_reply(ctx,query,rc,mapping);std::coutreply: retstd::endl;}else{// rc 0 表示客户端断开连接或发生错误std::coutclient disconnectedstd::endl;break;}}// // 清理资源// modbus_mapping_free(mapping);modbus_close(ctx);modbus_free(ctx);return0;}CMakeLists.txtcmake_minimum_required(VERSION3.26)project(untitled29)set(CMAKE_CXX_STANDARD11)add_executable(untitled29 main.cpp)# 手动指定 libmodbus 的头文件目录编译时查找 #include modbus/modbus.htarget_include_directories(untitled29 PRIVATED:/soft/vcpkg/installed/x86-windows/include)# 手动指定 libmodbus 的库文件目录链接时查找 modbus.libtarget_link_directories(untitled29 PRIVATED:/soft/vcpkg/installed/x86-windows/lib)# 链接 modbus 库自动找到 modbus.libtarget_link_libraries(untitled29 PRIVATE modbus pthread)测试

相关新闻

从舞蹈Cover到技术项目:拆解创意重制的底层逻辑与实践方法

从舞蹈Cover到技术项目:拆解创意重制的底层逻辑与实践方法

那天下午,我正为一个项目寻找合适的背景音乐,在茫茫曲库中偶然点开了一首名为《BAD》的歌曲。前奏响起,一种强烈的节奏感和戏剧张力瞬间抓住了我的注意力。这不仅仅是音乐,更像是一个精心编排的、充满故事感的声景。出于好奇&…

2026/8/7 23:35:48 阅读更多 →
HsMod终极解决方案:55项功能全面优化炉石传说游戏体验完整指南

HsMod终极解决方案:55项功能全面优化炉石传说游戏体验完整指南

HsMod终极解决方案:55项功能全面优化炉石传说游戏体验完整指南 【免费下载链接】HsMod Hearthstone Modification Based on BepInEx 项目地址: https://gitcode.com/GitHub_Trending/hs/HsMod HsMod是一款基于BepInEx框架开发的炉石传说多功能插件&#xff0…

2026/8/7 23:35:48 阅读更多 →
Unity游戏模组加载器MelonLoader:从原理到实战部署指南

Unity游戏模组加载器MelonLoader:从原理到实战部署指南

1. 项目概述:为什么我们需要一个专门的模组加载器?如果你是一个Unity游戏的深度玩家,或者是一个对游戏内部机制充满好奇的开发者,那么“模组”这个词对你来说一定不陌生。从《我的世界》到《星露谷物语》,再到《赛博朋…

2026/8/7 23:35:48 阅读更多 →

最新新闻

3分钟掌握GHelper:华硕笔记本轻量级控制终极指南

3分钟掌握GHelper:华硕笔记本轻量级控制终极指南

3分钟掌握GHelper:华硕笔记本轻量级控制终极指南 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook, Expertb…

2026/8/8 0:45:47 阅读更多 →
终极风扇控制指南:如何在Windows上实现完美静音与散热平衡

终极风扇控制指南:如何在Windows上实现完美静音与散热平衡

终极风扇控制指南:如何在Windows上实现完美静音与散热平衡 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitHub_Trendin…

2026/8/8 0:43:46 阅读更多 →
Jupyter内核切换:数据科学家的多环境管理核心技能

Jupyter内核切换:数据科学家的多环境管理核心技能

1. 项目概述:为什么Jupyter内核切换是数据科学家的必修课 如果你用过Jupyter Notebook,大概率遇到过这个场景:打开一个几个月前写的分析脚本,满怀期待地点击“运行全部”,结果第一个代码单元格就报错了—— ModuleNot…

2026/8/8 0:42:46 阅读更多 →
2026商城网站搭建平台哪个好?凡科商城、立亭云、Shopify对比

2026商城网站搭建平台哪个好?凡科商城、立亭云、Shopify对比

今天给大家带来2026商城网站搭建平台哪个好?凡科商城、立亭云、Shopify对比。商务部电子商务司2026年7月介绍,2026年1—6月,全国网上商品零售额增长4.8%,拉动社会消费品零售总额增长1.2个百分点;网上服务零售额增长6.0…

2026/8/8 0:42:46 阅读更多 →
计算机毕业设计之非物质文化遗产博物馆系统

计算机毕业设计之非物质文化遗产博物馆系统

当前,由于人们生活水平的提高和思想观念的改变,然后随着经济全球化的背景之下,互联网技术将进一步提高社会综合发展的效率和速度,互联网技术也会涉及到各个领域,于是传统的管理方式对时间、地点的限制太多,…

2026/8/8 0:41:45 阅读更多 →
《Go/Rust 系统编程并发原语深度 线上高并发排障实战》

《Go/Rust 系统编程并发原语深度 线上高并发排障实战》

《Go/Rust 系统编程并发原语深度 线上高并发排障实战》 作者: 钱钧安 (Qin Jūn Ān) (雷万钧)技术方向: AI 推理性能调优、大模型推理优化、AI 系统性能工程 💡 导语与现场排障背景 在生产环境重构 Go/Rust 系统编程与并发原语深度剖析 时,高并发场景…

2026/8/8 0:41:45 阅读更多 →

日新闻

AI多智能体时代来临,读懂MCP与A2A架构,抢占企业数字化新风口

AI多智能体时代来临,读懂MCP与A2A架构,抢占企业数字化新风口

当下AI应用飞速普及,无数企业下场搭建智能体系统,可落地阶段难题接踵而至:上下文无限堆积频繁爆栈、AI工具调用准确率低下、Token成本居高不下、企业数据权限混乱暗藏安全隐患……很多团队卡在架构搭建环节,空有前沿技术概念&…

2026/8/8 0:00:07 阅读更多 →
PHP二维码生成终极指南:用chillerlan/php-qrcode打造专业级二维码

PHP二维码生成终极指南:用chillerlan/php-qrcode打造专业级二维码

PHP二维码生成终极指南:用chillerlan/php-qrcode打造专业级二维码 【免费下载链接】php-qrcode A PHP QR Code generator and reader with a user-friendly API. 项目地址: https://gitcode.com/gh_mirrors/ph/php-qrcode 在当今数字时代,二维码已…

2026/8/8 0:00:08 阅读更多 →
UniApp微信小程序隐私保护组件开发:从原理到实战

UniApp微信小程序隐私保护组件开发:从原理到实战

1. 项目缘起:为什么我们需要一个隐私保护通用组件?最近在维护一个基于uniapp开发的微信小程序矩阵时,我遇到了一个非常棘手的问题。随着平台对用户隐私保护的要求越来越严格,几乎每一个新版本发布,或者在某些特定机型&…

2026/8/8 0:00:08 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/6 22:02:27 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/6 22:02:27 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/7 23:24:08 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/7 17:02:37 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/7 23:54:54 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/7 17:02:36 阅读更多 →