2015-08-28 3927 views
0

我们使用libmodbus库从支持Modbus over RTU的电能表EM6400读取寄存器值。我们正面临以下两个问题。modbus_read_register - 错误连接超时

1)我们所面临的问题与modbus_read_registers API,这个API返回-1,该错误信息是:

错误连接超时:请选择。

在调试库之后,我们发现这个问题是由于响应消息中请求字节的回显。 read()_modbus_rtu_recv中的API调用首先返回请求字节,然后返回响应字节。因此,length_to_read基于请求字节而不是响应字节(其中包含读取的字节数)在compute_data_length_after_meta()中计算,并且发生连接超时问题。 我们尝试使用3.0.6和3.1.2 libmodbus版本,但在两个版本中都会出现相同的问题。

2)modbus_rtu_set_serial_mode(ctx,MODBUS_RTU_RS485)返回“BAD文件描述符”。

请确认是否有任何API调用缺失或任何参数设置不正确。

我们用于读取寄存器值的示例代码如下。这个样本的

int main() 
{ 

    modbus_t *ctx; 
    uint16_t tab_reg[2] = {0,0}; 
    float avgVLL = -1;; 
    int res = 0; 
    int rc; 
    int i; 
    struct timeval response_timeout; 
    uint32_t tv_sec = 0; 
    uint32_t tv_usec = 0; 
    response_timeout.tv_sec = 5; 
    response_timeout.tv_usec = 0; 

    ctx = modbus_new_rtu("/dev/ttyUSB0", 19200, 'E', 8, 1); 
    if (NULL == ctx) 
    { 
        printf("Unable to create libmodbus context\n"); 
        res = 1; 
    } 
    else 
    { 
     printf("created libmodbus context\n"); 
     modbus_set_debug(ctx, TRUE); 
     //modbus_set_error_recovery(ctx, MODBUS_ERROR_RECOVERY_LINK |MODBUS_ERROR_RECOVERY_PROTOCOL); 
     rc = modbus_set_slave(ctx, 1); 
     printf("modbus_set_slave return: %d\n",rc); 
     if (rc != 0) 
     { 
         printf("modbus_set_slave: %s \n",modbus_strerror(errno)); 
     } 

     /* Commented - Giving 'Bad File Descriptor' issue 
     rc = modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485); 
     printf("modbus_rtu_set_serial_mode: %d \n",rc); 

     if (rc != 0) 
     { 
         printf("modbus_rtu_set_serial_mode: %s \n",modbus_strerror(errno)); 
     } 
     */ 

     // This code is for version 3.0.6 
     modbus_get_response_timeout(ctx, &response_timeout); 
     printf("Default response timeout:%ld sec %ld usec \n", response_timeout.tv_sec, response_timeout.tv_usec); 

     response_timeout.tv_sec = 60; 
     response_timeout.tv_usec = 0; 

     modbus_set_response_timeout(ctx, &response_timeout); 
     modbus_get_response_timeout(ctx, &response_timeout); 
     printf("Set response timeout:%ld sec %ld usec \n", response_timeout.tv_sec, response_timeout.tv_usec); 


     /* This code is for version 3.1.2 
     modbus_get_response_timeout(ctx, &tv_sec, &tv_usec); 
     printf("Default response timeout:%d sec %d usec \n",tv_sec,tv_usec); 

     tv_sec = 60; 
     tv_usec = 0; 

     modbus_set_response_timeout(ctx, tv_sec,tv_usec); 
     modbus_get_response_timeout(ctx, &tv_sec, &tv_usec); 
     printf("Set response timeout:%d sec %d usec \n",tv_sec,tv_usec); 
     */ 

     rc = modbus_connect(ctx); 
     printf("modbus_connect: %d \n",rc); 

     if (rc == -1) { 
         printf("Connection failed: %s\n", modbus_strerror(errno)); 
      res = 1; 
     } 

     rc = modbus_read_registers(ctx, 3908, 2, tab_reg); 
     printf("modbus_read_registers: %d \n",rc); 

     if (rc == -1) { 
         printf("Read registers failed: %s\n", modbus_strerror(errno)); 
      res = 1; 
     } 

     for (i=0; i < 2; i++) { 
      printf("reg[%d]=%d (0x%X)\n", i, tab_reg[i], tab_reg[i]); 
     } 

     avgVLL = modbus_get_float(tab_reg); 

     printf("Average Line to Line Voltage = %f\n", avgVLL); 

     modbus_close(ctx); 
     modbus_free(ctx); 
    } 
} 

输出如下:

created libmodbus context 
modbus_set_slave return: 0 
modbus_rtu_set_serial_mode: -1 
modbus_rtu_set_serial_mode: Bad file descriptor 
Default response timeout:0 sec 500000 usec 
Set response timeout:60 sec 0 usec 
Opening /dev/ttyUSB0 at 19200 bauds (E, 8, 1) 
modbus_connect: 0 
[01][03][0F][44][00][02][87][0A] 
Waiting for a confirmation... 
ERROR Connection timed out: select 
<01><03><0F><44><00><02><87><0A><01><03><04><C4><5F><43><D4><C6><7E>modbus_read_registers: -1 
Read registers failed: Connection timed out 
reg[0]=0 (0x0) 
reg[1]=0 (0x0) 
Average Line to Line Voltage = 0.000000 

回答

0

第1期)可能是一个硬件问题,在您的RS-485适配器启用 “本地回显”。本地回声有时用于确认总线上的数据字节的发送。您需要禁用它,或者找到另一个RS-485适配器。

我有我的MinimalModbus Python库的文件中写的:Local Echo

它列出了一些常见的方式RS-485适配器禁用本地回音。

+0

我查看了libmodbus文档,没有读/忽略回显响应的标志。所以,如果OP有一个强制本地回声的RS-485适配器,可能没有太多可以做的事情...... –