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);
|
|||
|
}
|
|||
|
|