Skip to content

4.14 C99 Modbus

概述

C99 Modbus 分两层: ArmHandle* 负责 SDK 会话, ArmModbusSlaveHandle* 负责某个从站上下文。

推荐调用顺序

步骤接口说明
1Arm_Connect建立机器人会话。
2Arm_Modbus_GetParam按需读取目标通道的串口参数。
3Arm_Modbus_GetSlave创建从站句柄;该步骤只创建本地上下文,不验证远端从站是否存在。
4Arm_ModbusSlave_Read* / Arm_ModbusSlave_Write*对线圈、保持寄存器、离散输入、输入寄存器执行读写。
5Arm_ModbusSlave_SerialSend / Arm_ModbusSlave_SerialReceive按需使用串口透传。
6Arm_ModbusSlave_Destroy销毁从站句柄。

接口签名

Arm_Modbus_GetSlave

c
int Arm_Modbus_GetSlave(ArmHandle* h, int channel, int32_t slaveId, int32_t masterId, ArmModbusSlaveHandle** outSlave);
说明
描述创建 Modbus 从站句柄。
请求参数h : ArmHandle* ,C99 会话句柄,通常来自 Arm_Create() ,业务接口需要先连接成功
channel : int ,Modbus 通道枚举值
slaveId : int32_t ,Modbus 从站 ID
masterId : int32_t ,主站参数 ID 或通道上下文 ID
outSlave : ArmModbusSlaveHandle** ,输出 Modbus 从站句柄,成功后调用方负责销毁
返回值STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理

Arm_Modbus_GetParam

c
int Arm_Modbus_GetParam(ArmHandle* h, int channel, int32_t masterId, ArmSerialParams* outParams);
说明
描述读取 Modbus 通道串口参数。
请求参数h : ArmHandle* ,C99 会话句柄,通常来自 Arm_Create() ,业务接口需要先连接成功
channel : int ,Modbus 通道枚举值
masterId : int32_t ,主站参数 ID 或通道上下文 ID
outParams : ArmSerialParams* ,串口参数输出结构体指针
返回值STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理

Arm_Modbus_SetParam

c
int Arm_Modbus_SetParam(ArmHandle* h, const ArmSerialParams* params, int32_t* outId);
说明
描述设置 Modbus 通道串口参数。
请求参数h : ArmHandle* ,C99 会话句柄,通常来自 Arm_Create() ,业务接口需要先连接成功
params : const ArmSerialParams* ,串口参数结构体指针
outId : int32_t* ,参数记录 ID 输出指针
返回值STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理

Arm_ModbusSlave_Destroy

c
void Arm_ModbusSlave_Destroy(ArmModbusSlaveHandle* slave);
说明
描述销毁 Modbus 从站句柄。
请求参数slave : ArmModbusSlaveHandle* ,Modbus 从站句柄,来自 Arm_Modbus_GetSlave()
返回值无返回值

Arm_ModbusSlave_IsValid

c
int Arm_ModbusSlave_IsValid(ArmModbusSlaveHandle* slave);
说明
描述检查 Modbus 从站句柄是否有效。
请求参数slave : ArmModbusSlaveHandle* ,Modbus 从站句柄,来自 Arm_Modbus_GetSlave()
返回值1 表示句柄有效, 0 表示句柄已失效; slave == NULL 或异常时返回错误码

Arm_ModbusSlave_GetParam

c
int Arm_ModbusSlave_GetParam(ArmModbusSlaveHandle* slave, ArmSerialParams* outParams);
说明
描述按从站上下文读取串口参数。
请求参数slave : ArmModbusSlaveHandle* ,Modbus 从站句柄,来自 Arm_Modbus_GetSlave()
outParams : ArmSerialParams* ,串口参数输出结构体指针
返回值STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理

Arm_ModbusSlave_SetParam

c
int Arm_ModbusSlave_SetParam(ArmModbusSlaveHandle* slave, const ArmSerialParams* params, int32_t* outId);
说明
描述按从站上下文设置串口参数。
请求参数slave : ArmModbusSlaveHandle* ,Modbus 从站句柄,来自 Arm_Modbus_GetSlave()
params : const ArmSerialParams* ,串口参数结构体指针
outId : int32_t* ,参数记录 ID 输出指针
返回值STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理

Arm_ModbusSlave_ReadCoils

c
int Arm_ModbusSlave_ReadCoils(ArmModbusSlaveHandle* slave, int32_t addr, int32_t num, int32_t* outArray, size_t maxCount, size_t* outCount);
说明
描述读取线圈。
请求参数slave : ArmModbusSlaveHandle* ,Modbus 从站句柄,来自 Arm_Modbus_GetSlave()
addr : int32_t ,起始地址
num : int32_t ,读取数量
outArray : int32_t* ,输出数组,由调用方分配
maxCount : size_t ,输出数组容量,表示调用方最多可接收多少个元素
outCount : size_t* ,输出数量指针,成功时写入实际数量
返回值STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理
备注数组输出由调用方分配, maxCount 表示容量, outCount 返回实际数量。

Arm_ModbusSlave_WriteCoils

c
int Arm_ModbusSlave_WriteCoils(ArmModbusSlaveHandle* slave, int32_t addr, const int32_t* values, size_t count);
说明
描述写入线圈。
请求参数slave : ArmModbusSlaveHandle* ,Modbus 从站句柄,来自 Arm_Modbus_GetSlave()
addr : int32_t ,起始地址
values : const int32_t* ,写入值数组
count : size_t ,数组元素数量
返回值STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理

Arm_ModbusSlave_ReadHoldingRegs

c
int Arm_ModbusSlave_ReadHoldingRegs(ArmModbusSlaveHandle* slave, int32_t addr, int32_t num, int32_t* outArray, size_t maxCount, size_t* outCount);
说明
描述读取保持寄存器。
请求参数slave : ArmModbusSlaveHandle* ,Modbus 从站句柄,来自 Arm_Modbus_GetSlave()
addr : int32_t ,起始地址
num : int32_t ,读取数量
outArray : int32_t* ,输出数组,由调用方分配
maxCount : size_t ,输出数组容量,表示调用方最多可接收多少个元素
outCount : size_t* ,输出数量指针,成功时写入实际数量
返回值STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理
备注数组输出由调用方分配, maxCount 表示容量, outCount 返回实际数量。

Arm_ModbusSlave_WriteHoldingRegs

c
int Arm_ModbusSlave_WriteHoldingRegs(ArmModbusSlaveHandle* slave, int32_t addr, const int32_t* values, size_t count);
说明
描述写入保持寄存器。
请求参数slave : ArmModbusSlaveHandle* ,Modbus 从站句柄,来自 Arm_Modbus_GetSlave()
addr : int32_t ,起始地址
values : const int32_t* ,写入值数组
count : size_t ,数组元素数量
返回值STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理

Arm_ModbusSlave_ReadDiscreteInputs

c
int Arm_ModbusSlave_ReadDiscreteInputs(ArmModbusSlaveHandle* slave, int32_t addr, int32_t num, int32_t* outArray, size_t maxCount, size_t* outCount);
说明
描述读取离散输入。
请求参数slave : ArmModbusSlaveHandle* ,Modbus 从站句柄,来自 Arm_Modbus_GetSlave()
addr : int32_t ,起始地址
num : int32_t ,读取数量
outArray : int32_t* ,输出数组,由调用方分配
maxCount : size_t ,输出数组容量,表示调用方最多可接收多少个元素
outCount : size_t* ,输出数量指针,成功时写入实际数量
返回值STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理
备注数组输出由调用方分配, maxCount 表示容量, outCount 返回实际数量。

Arm_ModbusSlave_ReadInputRegs

c
int Arm_ModbusSlave_ReadInputRegs(ArmModbusSlaveHandle* slave, int32_t addr, int32_t num, int32_t* outArray, size_t maxCount, size_t* outCount);
说明
描述读取输入寄存器。
请求参数slave : ArmModbusSlaveHandle* ,Modbus 从站句柄,来自 Arm_Modbus_GetSlave()
addr : int32_t ,起始地址
num : int32_t ,读取数量
outArray : int32_t* ,输出数组,由调用方分配
maxCount : size_t ,输出数组容量,表示调用方最多可接收多少个元素
outCount : size_t* ,输出数量指针,成功时写入实际数量
返回值STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理
备注数组输出由调用方分配, maxCount 表示容量, outCount 返回实际数量。

Arm_ModbusSlave_WriteInputRegs

c
int Arm_ModbusSlave_WriteInputRegs(ArmModbusSlaveHandle* slave, int32_t addr, const int32_t* values, size_t count);
说明
描述写入输入寄存器。
请求参数slave : ArmModbusSlaveHandle* ,Modbus 从站句柄,来自 Arm_Modbus_GetSlave()
addr : int32_t ,起始地址
values : const int32_t* ,写入值数组
count : size_t ,数组元素数量
返回值STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理

Arm_ModbusSlave_SerialSend

c
int Arm_ModbusSlave_SerialSend(ArmModbusSlaveHandle* slave, const char* msg);
说明
描述发送串口透传数据。
请求参数slave : ArmModbusSlaveHandle* ,Modbus 从站句柄,来自 Arm_Modbus_GetSlave()
msg : const char* ,串口透传发送文本
返回值STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理

Arm_ModbusSlave_SerialReceive

c
int Arm_ModbusSlave_SerialReceive(ArmModbusSlaveHandle* slave, char* outBuf, size_t bufSize);
说明
描述接收串口透传数据。
请求参数slave : ArmModbusSlaveHandle* ,Modbus 从站句柄,来自 Arm_Modbus_GetSlave()
outBuf : char* ,输出字符串缓冲区,由调用方分配
bufSize : size_t ,输出缓冲区大小,包含结尾 \0 的空间
返回值STATUS_CODE 整数值; 0 表示成功,其他值按状态码处理
备注字符串输出使用调用方提供的缓冲区,缓冲区不足时按状态码返回。

常用类型

ArmModbusChannel

枚举值说明
ARM_MODBUS_CHANNEL_CONTROLLER_TCP_TO_4852控制器 TCP 转 485 通道
ARM_MODBUS_CHANNEL_WRIST_485_03手腕 485_0 通道
ARM_MODBUS_CHANNEL_WRIST_485_14手腕 485_1 通道
ARM_MODBUS_CHANNEL_CONTROLLER_4855控制器 485 通道

ArmModbusParity

枚举值说明
ARM_MODBUS_PARITY_NONE78无校验,ASCII N
ARM_MODBUS_PARITY_ODD79奇校验,ASCII O
ARM_MODBUS_PARITY_EVEN69偶校验,ASCII E

ArmSerialParams

字段类型说明
idint32_t参数记录 ID
channelint32_tArmModbusChannel 枚举值
ipchar[128]目标 IP
portint32_t端口
baudint32_t波特率
dataBitint32_t数据位
stopBitint32_t停止位
parityint32_tArmModbusParity 枚举值
timeoutint32_t超时,毫秒

指针与缓冲区规则

场景返回
h == NULLslave == NULL返回错误码
必填 out 指针为 NULL返回 INVALID_PARAMETER
读数组时 outArray == NULLoutCount != NULL只回写数量
读数组缓冲区不足返回 BUFFER_TOO_SMALL ,并把 outCount0
Arm_ModbusSlave_SerialReceivebufSize == 0返回 BUFFER_TOO_SMALL
SerialReceive 失败且 outBuf 有效写空字符串

参数与生命周期

规则
连接前提需要先 Arm_Connect() 成功
addr起始地址要求 addr >= 0
读数量num 要求 1..120
写数量count 要求 1..1024
地址上界addr + 数量 - 1 不能超过 65535
valuescount > 0 时不能为空
从站句柄依赖创建它的 ArmHandle* ,不能跨 Arm_Disconnect() 复用
outIdSetParam 成功时写入控制器确认的参数记录 ID

行为约定:

  • Arm_Modbus_GetSlave() 创建本地句柄;远端从站状态由后续读写结果体现。
  • Arm_ModbusSlave_IsValid() 检查本地句柄是否仍绑定有效会话;远端在线状态由读写结果体现。
  • Arm_Modbus_GetParam()ARM_MODBUS_CHANNEL_CONTROLLER_TCP_TO_485 通道上会把 masterId 发给控制器;其他通道通常忽略 masterId
  • 串口透传按通道工作,不带 slaveId / masterId
  • 读接口返回控制器返回的整型数组;线圈值按控制器结果呈现。
  • 写接口校验地址范围和数量;每个 values 元素的业务含义交由控制器处理。

失败口径

场景返回
从站句柄无效INVALID_SESSION
通道枚举或校验位枚举非法INVALID_PARAMETER
addr < 0INVALID_PARAMETER
num < 1num > 120INVALID_PARAMETER
count < 1count > 1024INVALID_PARAMETER
addr + 数量 - 1 > 65535INVALID_PARAMETER
控制器返回非预期数据结构OTHER_ERR

最小调用示例

c
#include <stdio.h>  // 引入 printf,用于打印 Modbus 查询结果
#include "c_arm_api.h"  // 引入 C99 SDK 总头文件
int main(void)  // 示例程序入口
{  // 进入示例主函数
    ArmHandle* h = Arm_Create();  // 创建 C99 会话句柄
    ArmModbusSlaveHandle* slave = NULL;  // 准备接收 Modbus 从站句柄
    int32_t coils[8] = {0};  // 准备用于接收线圈值的缓冲区
    size_t outCount = 0U;  // 准备用于接收实际读取数量的变量
    if (h == NULL) {  // 判断会话句柄是否创建失败
        return 1;  // 创建失败时退出
    }  // 结束句柄创建判断
    if (Arm_Connect(h, "10.27.1.254", NULL) != 0) {  // 连接控制器或路由地址
        Arm_Destroy(h);  // 连接失败时销毁会话句柄
        return 1;  // 返回错误码
    }  // 结束连接判断
    if (Arm_Modbus_GetSlave(h, ARM_MODBUS_CHANNEL_CONTROLLER_485, 1, 0, &slave) != 0) {  // 创建控制器 485 通道的从站句柄
        Arm_Disconnect(h);  // 创建失败时断开机器人连接
        Arm_Destroy(h);  // 创建失败时销毁会话句柄
        return 1;  // 返回错误码
    }  // 结束从站句柄创建判断
    int ret = Arm_ModbusSlave_ReadCoils(slave, 0, 2, coils, 8, &outCount);  // 从地址 0 开始读取 2 个线圈值
    if (ret == 0) {  // 判断线圈读取是否成功
        printf("coil_count=%zu\n", outCount);  // 打印实际读取数量
    }  // 结束线圈读取判断
    Arm_ModbusSlave_Destroy(slave);  // 销毁从站句柄
    Arm_Disconnect(h);  // 断开机器人连接
    Arm_Destroy(h);  // 销毁会话句柄
    return ret == 0 ? 0 : 1;  // 根据读取结果返回示例状态
}  // 结束示例主函数

场景化示例

下面片段默认已经有连接成功的 ArmHandle* h 。设置参数、写寄存器和串口透传会影响现场设备,确认后再执行。

查询和设置串口参数

c
ArmSerialParams params = {0};  // 准备串口参数输出结构体
int32_t savedId = 0;  // 准备接收控制器确认的参数记录 ID
int getRet = Arm_Modbus_GetParam(h, ARM_MODBUS_CHANNEL_CONTROLLER_485, 0, &params);  // 查询控制器 485 通道参数
params.channel = ARM_MODBUS_CHANNEL_CONTROLLER_485;  // 指定要设置的通道
/* int setRet = Arm_Modbus_SetParam(h, &params, &savedId); */  // 设置串口参数会改变控制器配置,确认后再执行
(void)getRet;  // 示例中保留查询状态码
(void)savedId;  // 示例中保留设置输出变量

从站读写

c
int32_t values[4] = {0};  // 准备读取输出缓冲区
int32_t writeValues[2] = {1, 0};  // 准备写入线圈的值
size_t outCount = 0U;  // 准备接收读取数量
int coilsRet = Arm_ModbusSlave_ReadCoils(slave, 0, 2, values, 4, &outCount);  // 读取线圈
int holdingRet = Arm_ModbusSlave_ReadHoldingRegs(slave, 0, 2, values, 4, &outCount);  // 读取保持寄存器
int discreteRet = Arm_ModbusSlave_ReadDiscreteInputs(slave, 0, 2, values, 4, &outCount);  // 读取离散输入
int inputRet = Arm_ModbusSlave_ReadInputRegs(slave, 0, 2, values, 4, &outCount);  // 读取输入寄存器
/* int writeCoilsRet = Arm_ModbusSlave_WriteCoils(slave, 0, writeValues, 2); */  // 写线圈会改变从站状态,确认后再执行
(void)coilsRet;  // 示例中保留线圈读取状态码
(void)holdingRet;  // 示例中保留保持寄存器读取状态码
(void)discreteRet;  // 示例中保留离散输入读取状态码
(void)inputRet;  // 示例中保留输入寄存器读取状态码

按从站上下文取参和串口透传

c
ArmSerialParams params = {0};  // 准备串口参数输出结构体
char recvBuf[1024] = {0};  // 准备串口透传接收缓冲区
int valid = Arm_ModbusSlave_IsValid(slave);  // 检查从站句柄是否仍有效
int getRet = Arm_ModbusSlave_GetParam(slave, &params);  // 按从站上下文查询串口参数
/* int sendRet = Arm_ModbusSlave_SerialSend(slave, "010300000002"); */  // 串口透传发送会访问现场总线,确认后再执行
/* int recvRet = Arm_ModbusSlave_SerialReceive(slave, recvBuf, sizeof(recvBuf)); */  // 串口透传接收会等待现场数据,确认后再执行
(void)valid;  // 示例中保留有效性检查结果
(void)getRet;  // 示例中保留查询状态码

示例代码

c99/modbus_basic/src/main.cpp
cpp
#include <stdio.h>
#include <string.h>

extern "C" {
#include "c_arm_api.h"
}

int main(void)
{
    // [ZH] 本示例直接在源码中写死连接地址,不解析命令行参数。
    // [EN] This example hard-codes the connection addresses in the source code and does not parse command-line arguments.
    // [ZH] 创建并连接 SDK 句柄。
    // [EN] Create the SDK handle and connect to the robot.
    ArmHandle* handle = Arm_Create();
    if (handle == NULL) {
        printf("[c99_modbus] 创建句柄失败 / Failed to create the handle\n");
        return 1;
    }
    int ret = Arm_Connect(handle, "10.27.1.2", "10.27.1.102");
    if (ret != 0) {
        printf("[c99_modbus] 连接失败 / Connect failed, 状态码 / Status code: %d\n", ret);
        Arm_Destroy(handle);
        return 1;
    }
    printf("[c99_modbus] 机器人连接成功 / Robot connected successfully\n");

    // [ZH] 准备主站串口参数并读取当前值。
    // [EN] Prepare master serial params and read the current values.
    ArmSerialParams masterParams = {0};
    int32_t paramId = 0;
    snprintf(masterParams.ip, sizeof(masterParams.ip), "127.0.0.1");
    masterParams.channel = ARM_MODBUS_CHANNEL_CONTROLLER_485;
    masterParams.id = 1;
    masterParams.port = 502;
    masterParams.baud = 9600;
    masterParams.dataBit = 8;
    masterParams.stopBit = 1;
    masterParams.parity = ARM_MODBUS_PARITY_NONE;
    masterParams.timeout = 1000;
    ret = Arm_Modbus_GetParam(handle, ARM_MODBUS_CHANNEL_CONTROLLER_485, 0, &masterParams);
    printf("[c99_modbus] GetParam 状态码 / GetParam status code: %d, channel=%d, baud=%d\n", ret, masterParams.channel, masterParams.baud);
    ret = Arm_Modbus_SetParam(handle, &masterParams, &paramId);
    printf("[c99_modbus] SetParam 状态码 / SetParam status code: %d, id=%d\n", ret, paramId);

    // [ZH] 获取从站句柄并顺序执行全部从站接口。
    // [EN] Acquire the slave handle and execute all slave APIs in sequence.
    ArmModbusSlaveHandle* slave = NULL;
    ret = Arm_Modbus_GetSlave(handle, ARM_MODBUS_CHANNEL_CONTROLLER_485, 1, 0, &slave);
    printf("[c99_modbus] GetSlave 状态码 / GetSlave status code: %d\n", ret);
    if (slave != NULL) {
        int valid = Arm_ModbusSlave_IsValid(slave);
        printf("[c99_modbus] Slave_IsValid / Slave_IsValid: %d\n", valid);
        int32_t values[8] = {0};
        size_t valueCount = 0U;
        ret = Arm_ModbusSlave_ReadCoils(slave, 0, 2, values, 8U, &valueCount);
        printf("[c99_modbus] ReadCoils 状态码 / ReadCoils status code: %d, count=%zu\n", ret, valueCount);
        int32_t writeCoils[2] = {1, 0};
        ret = Arm_ModbusSlave_WriteCoils(slave, 0, writeCoils, 2U);
        printf("[c99_modbus] WriteCoils 状态码 / WriteCoils status code: %d\n", ret);
        ret = Arm_ModbusSlave_ReadHoldingRegs(slave, 0, 2, values, 8U, &valueCount);
        printf("[c99_modbus] ReadHoldingRegs 状态码 / ReadHoldingRegs status code: %d, count=%zu\n", ret, valueCount);
        int32_t holdingRegs[2] = {1, 2};
        ret = Arm_ModbusSlave_WriteHoldingRegs(slave, 0, holdingRegs, 2U);
        printf("[c99_modbus] WriteHoldingRegs 状态码 / WriteHoldingRegs status code: %d\n", ret);
        ret = Arm_ModbusSlave_ReadDiscreteInputs(slave, 0, 2, values, 8U, &valueCount);
        printf("[c99_modbus] ReadDiscreteInputs 状态码 / ReadDiscreteInputs status code: %d, count=%zu\n", ret, valueCount);
        ret = Arm_ModbusSlave_ReadInputRegs(slave, 0, 2, values, 8U, &valueCount);
        printf("[c99_modbus] ReadInputRegs 状态码 / ReadInputRegs status code: %d, count=%zu\n", ret, valueCount);
        int32_t inputRegs[2] = {3, 4};
        ret = Arm_ModbusSlave_WriteInputRegs(slave, 0, inputRegs, 2U);
        printf("[c99_modbus] WriteInputRegs 状态码 / WriteInputRegs status code: %d\n", ret);
        ret = Arm_ModbusSlave_SetParam(slave, &masterParams, &paramId);
        printf("[c99_modbus] Slave_SetParam 状态码 / Slave_SetParam status code: %d, id=%d\n", ret, paramId);
        ret = Arm_ModbusSlave_GetParam(slave, &masterParams);
        printf("[c99_modbus] Slave_GetParam 状态码 / Slave_GetParam status code: %d, baud=%d\n", ret, masterParams.baud);
        ret = Arm_ModbusSlave_SerialSend(slave, "hello from c99 example");
        printf("[c99_modbus] SerialSend 状态码 / SerialSend status code: %d\n", ret);
        char recvBuf[256] = {0};
        ret = Arm_ModbusSlave_SerialReceive(slave, recvBuf, sizeof(recvBuf));
        printf("[c99_modbus] SerialReceive 状态码 / SerialReceive status code: %d, 内容 / Text: %s\n", ret, recvBuf);
        Arm_ModbusSlave_Destroy(slave);
        printf("[c99_modbus] 从站句柄已释放 / Slave handle destroyed\n");
    }

    // [ZH] 断开连接并销毁句柄。
    // [EN] Disconnect and destroy the handle.
    Arm_Disconnect(handle);
    Arm_Destroy(handle);
    printf("[c99_modbus] 示例结束 / Example finished\n");
    return 0;
}