我有一个奇怪的问题。一段时间以来,我一直在尝试更换一个小型协议(protocol)转换器(基本上是以太网的双向串行...主站和从站),以获得具有更多功能的东西。
背景故事
经过大量的逆向工程,我发现了该设备的工作原理,并且我一直在尝试复制它,并且我已经成功地将我的板连接到设备...我尝试将原始设备连接为主控板和我的板作为从属板,反之亦然,一切都工作得很好,实际上更好,因为在更高的速度下,不再有数据包丢失(连接 2 个原始板会导致数据包丢失)。
但是,当我尝试将我的设备连接为主设备,将另一台设备连接为从设备时..运行完全相同的代码,它可以进行 2 或 3 次交换,然后停止...最终有时几分钟后,它会再尝试 2 到 3 次。
如何进行测试
- 我连接了 modbus 主站和从站(modbustools,两个不同的实例)。主站为串行RTU modbus,从站为串行RTU modbus;
- 我将其中一台设备配置为主设备并将其连接到串行端口,以便它接收串行 modbus 并将协议(protocol)发送到与其连接的设备;
- 我配置我的从站,使其通过串行端口连接到从站 modbus。基本上它的工作原理是创建一个套接字并连接到主站的IP,然后通过以太网等待主站传输,通过串行将其发送到从站modbus(modbustools),接收响应,将其发送给主站,然后将其发送到modbus主站(modbustools);
我有点困惑,但这就是它的工作原理......我的主人等待套接字连接,然后它们之间的通信开始,因为这就是旧的工作方式。
我现在已经编写了一个 echo 客户端来测试连接。基本上现在,我的代码连接到服务器(我的主服务器),它接收一个数据包,然后回复收到的相同数据包。当我尝试将其连接到我的两 block 板时,它们不起作用。情况基本相同,进行 2 或 3 次交换后就停止了,但是当我将其连接到原始设备时,它会继续运行,不会出现任何故障。
来源
这是我的 TCP 主控(实际上是服务器)初始化:
void initClient() {
if(tcp_modbus == NULL) {
tcp_modbus = tcp_new();
previousPort = port;
tcp_bind(tcp_modbus, IP_ADDR_ANY, port);
tcp_sent(tcp_modbus, sent);
tcp_poll(tcp_modbus, poll, 2);
tcp_setprio(tcp_modbus, 128);
tcp_err(tcp_modbus, error);
tcp_modbus = tcp_listen(tcp_modbus);
tcp_modbus->so_options |= SOF_KEEPALIVE; // enable keep-alive
tcp_modbus->keep_intvl = 1000; // sends keep-alive every second
tcp_accept(tcp_modbus, acceptmodbus);
isListening = true;
}
}
static err_t acceptmodbus(void *arg, struct tcp_pcb *pcb, err_t err) {
tcp_arg(pcb, pcb);
/* Set up the various callback functions */
tcp_recv(pcb, modbusrcv);
tcp_err(pcb, error);
tcp_accepted(pcb);
gb_ClientHasConnected = true;
}
//receives the packet, puts it in an array "ptransparentmessage->data"
//states which PCB to use in order to reply and the length that was received
static err_t modbusrcv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
if(p == NULL) {
return ERR_OK;
} else if(err != ERR_OK) {
return err;
}
tcp_recved(pcb, p->len);
memcpy(ptransparent.data, p->payload,p->len);
ptransparent->pcb = pcb;
ptransparent->len = p->len;
}
串行接收基本上是这样的: 检测收到的一个字节,启动超时,当超时结束时,发送通过已连接到服务器的 TCP 套接字接收到的任何内容。然后它通过acceptmodbus 函数接收数据包并通过串行端口发送。
这是我的客户(从属)代码:
void init_slave() {
if(tcp_client == NULL) {
tcp_client = tcp_new();
tcp_bind(tcp_client, IP_ADDR_ANY, 0);
tcp_arg(tcp_client, NULL);
tcp_recv(tcp_client, modbusrcv);
tcp_sent(tcp_client, sent);
tcp_client->so_options |= SOF_KEEPALIVE; // enable keep-alive
tcp_client->keep_intvl = 100; // sends keep-alive every 100 mili seconds
tcp_err(tcp_client, error);
err_t ret = tcp_connect(tcp_client, &addr, portCnt, connected);
}
}
其余代码是相同的。唯一改变的是操作流程。
- 连接到服务器
- 等待数据包
- 通过串口发送
- 等待响应超时(与服务器超时相同,只是以不同的方式开始计数...服务器在收到一个字节后启动,客户端在通过串行端口发送一些内容后启动)
- 获取响应并将其发送到服务器
观察:
通信中未检测到错误。经过一些测试后,似乎并不是交换次数导致了挂起。它会在一段时间后发生。在我看来,这听起来像是断开连接问题或超时错误,但没有发生断开连接,也没有收到更多数据包。当我停止调试并检查套接字时,没有检测到任何异常情况。
最佳答案
如果我正确理解你的问题,你有一台带有两个串行端口的计算机,每个端口运行一个 Modbus 客户端和服务器实例。然后,您可以从每一端访问 STM32 板,这些板在其串行端口上接收数据,并转发到将它们相互连接的以太网上的 TCP。
不容易说,但根据您描述的症状,您肯定遇到一个或多个超时问题,很可能是在串行方面。我认为在不测试代码的情况下帮助您查明代码到底出了什么问题并不容易,如果您无法展示完整的功能部分,当然也不容易。
但是您可以改进很多的是在末端进行调试的方式。 您可以尝试用可以提供更多详细信息的内容替换 modbustools。
获取额外调试信息的最简单解决方案是使用 pymodbus ,您只需使用 pip
安装库并使用 examples 提供的客户端和服务器即可。您需要的唯一修改是将它们更改为串行接口(interface)注释并取消注释几行。这将为您提供非常有用的调试详细信息。
如果您的计算机上有 C 开发环境,最好选择 libmodbus 。这个库有一组很棒的 unit tests 。同样,您只需编辑代码来设置串行端口的名称并运行服务器和客户端。
最后,我不知道这对您有多大用处,但您可能想看看 SerialPCAP 。使用此工具,您可以点击 RS-485 总线并查看其上运行的所有查询和响应。我想您有 RS-232,它是点对点的,不能与总线上的三个设备一起使用。如果是这样,您可以尝试port forwarding .
编辑:仔细阅读你的问题我发现这句话特别麻烦:
...detect one byte received, start timeout, when timeout ends send whatever was received via a TCP socket that was already connected to the server...
为什么需要引入这种人为延迟?在 Modbus 中,您有非常明确的包定义,您可以通过最小 3.5 帧间距来识别它们,这就是您所说的超时吗?
不相关,但我还记得有一个 serial forwarder example包含在pymodbus中,可能会以某种方式帮助你(也许你可以用它来模拟你的一个板?)。
关于c - lwip 板无法维持与另一个 lwip 板的连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56762288/