#include "modbus.h" #include "lwip/tcp.h" #include "esp_log.h" static const char *TAG = "modbus tcp"; /* 修正:应该将整个包的解析都放到Modbus的处理中,这里有一点纠缠了 */ static err_t ModBusSlave_recv(void *arg, struct tcp_pcb *newpcb, struct pbuf *p, err_t err) { // ESP_LOGI(TAG, "ModBusSlave_recv"); /* 处理接收到的包信息(存储在pbuf中),并发送包回应 */ if (p != NULL) { uint8_t *txbuf; uint8_t *rxbuf; int txlen, rxlen; /* 接收数据 */ tcp_recved(newpcb, p->tot_len); rxbuf = (uint8_t *)p->payload; /* 修正:pbuf的数据是用链表存储的,p->payload仅是该节点的数据,并不是所有的,但是目前只取了第一个节点 */ rxlen = p->len; /* MBAP报文头7Byte:事务处理标识2Byte + 协议标识2Byte + 长度2Byte(此字节往后的字节个数) + 单元标识符1Byte*/ /* 这里忽略了前面的6Byte未处理 */ if (rxlen < 6) { ESP_LOGI(TAG, "err: rxlen less 6"); pbuf_free(p); return tcp_close(newpcb); /* 修正:tcp_close的处理是否合理? */ } txbuf = (uint8_t *)mem_malloc(1024 + 16); if (txbuf == NULL) { ESP_LOGI(TAG, "err:tx malloc fault"); return tcp_close(newpcb); } /* 使用Modbus协议解析接收数据,同时准备应答包txbuf数据,发送应答包 */ /* 包含: */ /* MBAP:单元标识符1Byte */ /* PUD(帧结构):功能码1Byte + 地址2Byte(H+L) + ... */ txlen = ModbusSlaveProcess(&txbuf[6], &rxbuf[6], (rxlen - 6), 0); if (txlen > 0) { int i; for (i = 0; i < 4; i++) { txbuf[i] = rxbuf[i]; } txbuf[4] = (uint8_t)(txlen >> 8); txbuf[5] = (uint8_t)(txlen & 0xff); tcp_write(newpcb, txbuf, txlen + 6, 1); } mem_free(txbuf); pbuf_free(p); } else if (err == ERR_OK) { /* 有错误,关闭tcp */ tcp_err(newpcb, NULL); tcp_recv(newpcb, NULL); tcp_poll(newpcb, NULL, 120); err_t err_p = tcp_close(newpcb); if (ERR_OK != err_p) { // log_printf(LERROR, LOG_TAG "close error. err:%d\n", err_p); } return err_p; } return ERR_OK; } static void ModBusSlave_conn_err(void *arg, err_t err) { ESP_LOGI(TAG, "connection error, err:%d\n", err); } /* 修正:这里的实现时关闭tcp,为什么要60s定时关闭一次tcp呢?会不会影响程序? */ static err_t ModBusSlave_poll(void *arg, struct tcp_pcb *newpcb) { ESP_LOGI(TAG, "ModBusSlave_poll"); if (newpcb) { ESP_LOGI(TAG, "close pcb"); tcp_close(newpcb); newpcb = NULL; } return ERR_OK; } /* * 有tcp连接请求时触发该回调函数 * pcb:新分配的用于和请求者通信的控制块 */ static err_t ModBusSlave_accept(void *arg, struct tcp_pcb *pcb, err_t err) { ESP_LOGI(TAG, "accept assess"); /* 设置各种回调函数 */ tcp_err(pcb, ModBusSlave_conn_err); tcp_recv(pcb, ModBusSlave_recv); tcp_poll(pcb, ModBusSlave_poll, 120); /* 轮询函数,定时会触发 120表示60s触发一次 */ return ERR_OK; } void ModBusTCPSlave_init(void) { ESP_LOGI(TAG, "ModBusTCPSlave_init"); struct tcp_pcb *pcb; pcb = tcp_new(); /* IP_ADDR_ANY表示绑定到主机的所有IP上 502端口是专门分配给Modbus的 */ tcp_bind(pcb, IP_ADDR_ANY, 502); /* 开启被动监听 */ pcb = tcp_listen(pcb); /* 设置ModBusSlave_accept回调函数,在有tcp连接请求时触发 */ tcp_accept(pcb, ModBusSlave_accept); }