119 lines
3.5 KiB
C
119 lines
3.5 KiB
C
#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);
|
||
}
|
||
|