add some code

This commit is contained in:
2025-09-05 13:25:11 +08:00
parent 9ff0a99e7a
commit 3cf1229a85
8911 changed files with 2535396 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
#include "ec801e_at_modem.h"
#include <esp_log.h>
#include <esp_err.h>
#include <cassert>
#include <sstream>
#include <iomanip>
#include <cstring>
#include "ec801e_ssl.h"
#include "ec801e_tcp.h"
#include "ec801e_udp.h"
#include "ec801e_mqtt.h"
#include "http_client.h"
#include "web_socket.h"
#define TAG "Ec801EAtModem"
Ec801EAtModem::Ec801EAtModem(std::shared_ptr<AtUart> at_uart) : AtModem(at_uart) {
// 子类特定的初始化在这里
// ATE0 关闭 echo
at_uart_->SendCommand("ATE0");
// 设置 URC 端口为 UART1
at_uart_->SendCommand("AT+QURCCFG=\"urcport\",\"uart1\"");
}
void Ec801EAtModem::HandleUrc(const std::string& command, const std::vector<AtArgumentValue>& arguments) {
// Handle Common URC
AtModem::HandleUrc(command, arguments);
}
bool Ec801EAtModem::SetSleepMode(bool enable, int delay_seconds) {
if (enable) {
if (delay_seconds > 0) {
at_uart_->SendCommand("AT+QSCLKEX=1," + std::to_string(delay_seconds) + ",30");
}
return at_uart_->SendCommand("AT+QSCLK=1");
} else {
return at_uart_->SendCommand("AT+QSCLK=0");
}
}
std::unique_ptr<Http> Ec801EAtModem::CreateHttp(int connect_id) {
assert(connect_id >= 0);
return std::make_unique<HttpClient>(this, connect_id);
}
std::unique_ptr<Tcp> Ec801EAtModem::CreateTcp(int connect_id) {
assert(connect_id >= 0);
return std::make_unique<Ec801ETcp>(at_uart_, connect_id);
}
std::unique_ptr<Tcp> Ec801EAtModem::CreateSsl(int connect_id) {
assert(connect_id >= 0);
return std::make_unique<Ec801ESsl>(at_uart_, connect_id);
}
std::unique_ptr<Udp> Ec801EAtModem::CreateUdp(int connect_id) {
assert(connect_id >= 0);
return std::make_unique<Ec801EUdp>(at_uart_, connect_id);
}
std::unique_ptr<Mqtt> Ec801EAtModem::CreateMqtt(int connect_id) {
assert(connect_id >= 0);
return std::make_unique<Ec801EMqtt>(at_uart_, connect_id);
}
std::unique_ptr<WebSocket> Ec801EAtModem::CreateWebSocket(int connect_id) {
assert(connect_id >= 0);
return std::make_unique<WebSocket>(this, connect_id);
}

View File

@@ -0,0 +1,26 @@
#ifndef _EC801E_AT_MODEM_H_
#define _EC801E_AT_MODEM_H_
#include "at_modem.h"
class Ec801EAtModem : public AtModem {
public:
Ec801EAtModem(std::shared_ptr<AtUart> at_uart);
~Ec801EAtModem() override = default;
bool SetSleepMode(bool enable, int delay_seconds=0) override;
// 实现基类的纯虚函数
std::unique_ptr<Http> CreateHttp(int connect_id) override;
std::unique_ptr<Tcp> CreateTcp(int connect_id) override;
std::unique_ptr<Tcp> CreateSsl(int connect_id) override;
std::unique_ptr<Udp> CreateUdp(int connect_id) override;
std::unique_ptr<Mqtt> CreateMqtt(int connect_id) override;
std::unique_ptr<WebSocket> CreateWebSocket(int connect_id) override;
protected:
void HandleUrc(const std::string& command, const std::vector<AtArgumentValue>& arguments) override;
};
#endif // _EC801E_AT_MODEM_H_

View File

@@ -0,0 +1,244 @@
#include "ec801e_mqtt.h"
#include <esp_log.h>
#define TAG "Ec801EMqtt"
Ec801EMqtt::Ec801EMqtt(std::shared_ptr<AtUart> at_uart, int mqtt_id) : at_uart_(at_uart), mqtt_id_(mqtt_id) {
event_group_handle_ = xEventGroupCreate();
urc_callback_it_ = at_uart_->RegisterUrcCallback([this](const std::string& command, const std::vector<AtArgumentValue>& arguments) {
if (command == "QMTRECV" && arguments.size() >= 4) {
if (arguments[0].int_value == mqtt_id_) {
auto topic = arguments[2].string_value;
if (on_message_callback_) {
on_message_callback_(topic, at_uart_->DecodeHex(arguments[3].string_value));
}
}
} else if (command == "QMTSTAT" && arguments.size() == 2) {
if (arguments[0].int_value == mqtt_id_) {
auto error_code = arguments[1].int_value;
if (error_code != 0) {
auto error_message = ErrorToString(error_code);
ESP_LOGE(TAG, "MQTT error occurred: %s", error_message.c_str());
if (on_error_callback_) {
on_error_callback_(error_message);
}
if (connected_) {
connected_ = false;
if (on_disconnected_callback_) {
on_disconnected_callback_();
}
}
xEventGroupSetBits(event_group_handle_, EC801E_MQTT_DISCONNECTED_EVENT);
}
}
} else if (command == "QMTCONN" && arguments.size() == 3) {
if (arguments[0].int_value == mqtt_id_) {
error_code_ = arguments[2].int_value;
if (error_code_ == 0) {
if (!connected_) {
connected_ = true;
if (on_connected_callback_) {
on_connected_callback_();
}
}
xEventGroupSetBits(event_group_handle_, EC801E_MQTT_CONNECTED_EVENT);
} else {
if (connected_) {
connected_ = false;
if (on_disconnected_callback_) {
on_disconnected_callback_();
}
}
xEventGroupSetBits(event_group_handle_, EC801E_MQTT_DISCONNECTED_EVENT);
}
}
} else if (command == "QMTOPEN" && arguments.size() == 2) {
if (arguments[0].int_value == mqtt_id_) {
error_code_ = arguments[1].int_value;
if (error_code_ == 0) {
xEventGroupSetBits(event_group_handle_, EC801E_MQTT_OPEN_COMPLETE);
} else {
xEventGroupSetBits(event_group_handle_, EC801E_MQTT_OPEN_FAILED);
}
}
} else if (command == "QMTDISC" && arguments.size() == 2) {
if (arguments[0].int_value == mqtt_id_) {
if (arguments[1].int_value == 0) {
xEventGroupSetBits(event_group_handle_, EC801E_MQTT_DISCONNECTED_EVENT);
} else {
ESP_LOGE(TAG, "Failed to disconnect from MQTT broker");
}
}
}
});
}
Ec801EMqtt::~Ec801EMqtt() {
at_uart_->UnregisterUrcCallback(urc_callback_it_);
vEventGroupDelete(event_group_handle_);
}
bool Ec801EMqtt::Connect(const std::string broker_address, int broker_port, const std::string client_id, const std::string username, const std::string password) {
EventBits_t bits;
if (broker_port == 8883) {
// Config SSL Context
at_uart_->SendCommand("AT+QSSLCFG=\"sslversion\",2,4;+QSSLCFG=\"ciphersuite\",2,0xFFFF;+QSSLCFG=\"seclevel\",2,0");
if (!at_uart_->SendCommand(std::string("AT+QMTCFG=\"ssl\",") + std::to_string(mqtt_id_) + ",1,2")) {
ESP_LOGE(TAG, "Failed to set MQTT to use SSL");
return false;
}
}
// Set version
if (!at_uart_->SendCommand(std::string("AT+QMTCFG=\"version\",") + std::to_string(mqtt_id_) + ",4")) {
ESP_LOGE(TAG, "Failed to set MQTT version to 3.1.1");
return false;
}
// Set clean session
if (!at_uart_->SendCommand(std::string("AT+QMTCFG=\"session\",") + std::to_string(mqtt_id_) + ",1")) {
ESP_LOGE(TAG, "Failed to set MQTT clean session");
return false;
}
// Set keep alive
if (!at_uart_->SendCommand(std::string("AT+QMTCFG=\"keepalive\",") + std::to_string(mqtt_id_) + "," + std::to_string(keep_alive_seconds_))) {
ESP_LOGE(TAG, "Failed to set MQTT keep alive");
return false;
}
// Set HEX encoding (ASCII for sending, HEX for receiving)
if (!at_uart_->SendCommand("AT+QMTCFG=\"dataformat\"," + std::to_string(mqtt_id_) + ",0,1")) {
ESP_LOGE(TAG, "Failed to set MQTT to use HEX encoding");
return false;
}
xEventGroupClearBits(event_group_handle_, EC801E_MQTT_OPEN_COMPLETE | EC801E_MQTT_OPEN_FAILED);
std::string command = "AT+QMTOPEN=" + std::to_string(mqtt_id_) + ",\"" + broker_address + "\"," + std::to_string(broker_port);
if (!at_uart_->SendCommand(command)) {
ESP_LOGE(TAG, "Failed to open MQTT connection");
return false;
}
bits = xEventGroupWaitBits(event_group_handle_, EC801E_MQTT_OPEN_COMPLETE | EC801E_MQTT_OPEN_FAILED, pdTRUE, pdFALSE, pdMS_TO_TICKS(EC801E_MQTT_CONNECT_TIMEOUT_MS));
if (bits & EC801E_MQTT_OPEN_FAILED) {
const char* error_code_str[] = {
"Connected",
"Parameter error",
"MQTT identifier occupied",
"PDP activation failed",
"Domain name resolution failed",
"Server disconnected"
};
const char* message = error_code_ < 6 ? error_code_str[error_code_] : "Unknown error";
ESP_LOGE(TAG, "Failed to open MQTT connection: %s", message);
if (error_code_ == 2) { // MQTT 标识符被占用
at_uart_->SendCommand(std::string("AT+QMTDISC=") + std::to_string(mqtt_id_));
bits = xEventGroupWaitBits(event_group_handle_, EC801E_MQTT_DISCONNECTED_EVENT, pdTRUE, pdFALSE, pdMS_TO_TICKS(EC801E_MQTT_CONNECT_TIMEOUT_MS));
if (!(bits & EC801E_MQTT_DISCONNECTED_EVENT)) {
ESP_LOGE(TAG, "Failed to disconnect from previous connection");
return false;
}
return Connect(broker_address, broker_port, client_id, username, password);
}
return false;
} else if (!(bits & EC801E_MQTT_OPEN_COMPLETE)) {
ESP_LOGE(TAG, "MQTT connection timeout");
return false;
}
xEventGroupClearBits(event_group_handle_, EC801E_MQTT_CONNECTED_EVENT | EC801E_MQTT_DISCONNECTED_EVENT);
command = "AT+QMTCONN=" + std::to_string(mqtt_id_) + ",\"" + client_id + "\",\"" + username + "\",\"" + password + "\"";
if (!at_uart_->SendCommand(command)) {
ESP_LOGE(TAG, "Failed to connect to MQTT broker");
return false;
}
// 等待连接完成
bits = xEventGroupWaitBits(event_group_handle_, EC801E_MQTT_CONNECTED_EVENT | EC801E_MQTT_DISCONNECTED_EVENT, pdTRUE, pdFALSE, pdMS_TO_TICKS(EC801E_MQTT_CONNECT_TIMEOUT_MS));
if (bits & EC801E_MQTT_DISCONNECTED_EVENT) {
const char* error_code_str[] = {
"Accepted",
"Rejected: Unacceptable protocol version",
"Rejected: Identifier rejected",
"Rejected: Server unavailable",
"Rejected: Wrong username or password",
"Rejected: Unauthorized"
};
const char* message = error_code_ < 6 ? error_code_str[error_code_] : "Unknown error";
ESP_LOGE(TAG, "Failed to connect to MQTT broker: %s", message);
return false;
} else if (!(bits & EC801E_MQTT_CONNECTED_EVENT)) {
ESP_LOGE(TAG, "MQTT connection timeout");
return false;
}
return true;
}
bool Ec801EMqtt::IsConnected() {
return connected_;
}
void Ec801EMqtt::Disconnect() {
if (!connected_) {
return;
}
at_uart_->SendCommand(std::string("AT+QMTDISC=") + std::to_string(mqtt_id_));
}
bool Ec801EMqtt::Publish(const std::string topic, const std::string payload, int qos) {
if (!connected_) {
return false;
}
// If payload size is larger than 64KB, a CME ERROR 601 will be returned.
std::string command = "AT+QMTPUBEX=" + std::to_string(mqtt_id_) + ",0,0,0,\"" + topic + "\",";
command += std::to_string(payload.size());
if (!at_uart_->SendCommandWithData(command, 1000, true, payload.data(), payload.size())) {
return false;
}
return true;
}
bool Ec801EMqtt::Subscribe(const std::string topic, int qos) {
if (!connected_) {
return false;
}
std::string command = "AT+QMTSUB=" + std::to_string(mqtt_id_) + ",0,\"" + topic + "\"," + std::to_string(qos);
return at_uart_->SendCommand(command);
}
bool Ec801EMqtt::Unsubscribe(const std::string topic) {
if (!connected_) {
return false;
}
std::string command = "AT+QMTUNS=" + std::to_string(mqtt_id_) + ",0,\"" + topic + "\"";
return at_uart_->SendCommand(command);
}
std::string Ec801EMqtt::ErrorToString(int error_code) {
switch (error_code) {
case 0:
return "Connected";
case 1:
return "Server disconnected or reset";
case 2:
return "Ping timeout or failed";
case 3:
return "Connect timeout or failed";
case 4:
return "Receive CONNACK timeout or failed";
case 5:
return "Client sends DISCONNECT packet, but server actively disconnects MQTT connection";
case 6:
return "Client actively disconnects MQTT connection because sending data packets always fails";
case 7:
return "Link does not work or server is unavailable";
case 8:
return "Client actively disconnects MQTT connection";
default:
return "Unknown error";
}
}

View File

@@ -0,0 +1,45 @@
#ifndef EC801E_MQTT_H
#define EC801E_MQTT_H
#include "mqtt.h"
#include "at_uart.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/event_groups.h>
#include <string>
#include <functional>
#define EC801E_MQTT_CONNECT_TIMEOUT_MS 10000
#define EC801E_MQTT_CONNECTED_EVENT BIT1
#define EC801E_MQTT_DISCONNECTED_EVENT BIT2
#define EC801E_MQTT_OPEN_COMPLETE BIT5
#define EC801E_MQTT_OPEN_FAILED BIT6
class Ec801EMqtt : public Mqtt {
public:
Ec801EMqtt(std::shared_ptr<AtUart> at_uart, int mqtt_id);
~Ec801EMqtt();
bool Connect(const std::string broker_address, int broker_port, const std::string client_id, const std::string username, const std::string password);
void Disconnect();
bool Publish(const std::string topic, const std::string payload, int qos = 0);
bool Subscribe(const std::string topic, int qos = 0);
bool Unsubscribe(const std::string topic);
bool IsConnected();
private:
std::shared_ptr<AtUart> at_uart_;
int mqtt_id_;
bool connected_ = false;
int error_code_ = 0;
EventGroupHandle_t event_group_handle_;
std::string message_payload_;
std::list<UrcCallback>::iterator urc_callback_it_;
std::string ErrorToString(int error_code);
};
#endif

View File

@@ -0,0 +1,160 @@
#include "ec801e_ssl.h"
#include <esp_log.h>
#define TAG "Ec801ESsl"
Ec801ESsl::Ec801ESsl(std::shared_ptr<AtUart> at_uart, int ssl_id) : at_uart_(at_uart), ssl_id_(ssl_id) {
event_group_handle_ = xEventGroupCreate();
urc_callback_it_ = at_uart_->RegisterUrcCallback([this](const std::string& command, const std::vector<AtArgumentValue>& arguments) {
if (command == "QSSLOPEN" && arguments.size() == 2) {
if (arguments[0].int_value == ssl_id_ && !instance_active_) {
if (arguments[1].int_value == 0) {
connected_ = true;
instance_active_ = true;
xEventGroupClearBits(event_group_handle_, EC801E_SSL_DISCONNECTED | EC801E_SSL_ERROR);
xEventGroupSetBits(event_group_handle_, EC801E_SSL_CONNECTED);
} else {
connected_ = false;
xEventGroupSetBits(event_group_handle_, EC801E_SSL_ERROR);
}
}
} else if (command == "QSSLCLOSE" && arguments.size() == 1) {
if (arguments[0].int_value == ssl_id_) {
instance_active_ = false;
}
} else if (command == "QISEND" && arguments.size() == 3) {
if (arguments[0].int_value == ssl_id_) {
if (arguments[1].int_value == 0) {
xEventGroupSetBits(event_group_handle_, EC801E_SSL_SEND_COMPLETE);
} else {
xEventGroupSetBits(event_group_handle_, EC801E_SSL_ERROR);
}
}
} else if (command == "QSSLURC" && arguments.size() >= 2) {
if (arguments[1].int_value == ssl_id_) {
if (arguments[0].string_value == "recv" && arguments.size() >= 4) {
if (stream_callback_) {
stream_callback_(at_uart_->DecodeHex(arguments[3].string_value));
}
} else if (arguments[0].string_value == "closed") {
if (connected_) {
connected_ = false;
// instance_active_ 保持 true需要发送 QICLOSE 清理
if (disconnect_callback_) {
disconnect_callback_();
}
}
xEventGroupSetBits(event_group_handle_, EC801E_SSL_DISCONNECTED);
} else {
ESP_LOGE(TAG, "Unknown QIURC command: %s", arguments[0].string_value.c_str());
}
}
} else if (command == "QSSLSTATE" && arguments.size() > 5) {
if (arguments[0].int_value == ssl_id_) {
connected_ = arguments[5].int_value == 2;
instance_active_ = true;
xEventGroupSetBits(event_group_handle_, EC801E_SSL_INITIALIZED);
}
} else if (command == "FIFO_OVERFLOW") {
xEventGroupSetBits(event_group_handle_, EC801E_SSL_ERROR);
Disconnect();
}
});
}
Ec801ESsl::~Ec801ESsl() {
Disconnect();
at_uart_->UnregisterUrcCallback(urc_callback_it_);
}
bool Ec801ESsl::Connect(const std::string& host, int port) {
// Clear bits
xEventGroupClearBits(event_group_handle_, EC801E_SSL_CONNECTED | EC801E_SSL_DISCONNECTED | EC801E_SSL_ERROR);
// Keep data in one line; Use HEX encoding in response
at_uart_->SendCommand("AT+QICFG=\"close/mode\",1;+QICFG=\"viewmode\",1;+QICFG=\"sendinfo\",1;+QICFG=\"dataformat\",0,1");
// Config SSL Context
at_uart_->SendCommand("AT+QSSLCFG=\"sslversion\",1,4;+QSSLCFG=\"ciphersuite\",1,0xFFFF;+QSSLCFG=\"seclevel\",1,0");
// at_uart_->SendCommand("AT+QSSLCFG=\"cacert\",1,\"UFS:cacert.pem\"");
// 检查这个 id 是否已经连接
std::string command = "AT+QSSLSTATE=1," + std::to_string(ssl_id_);
at_uart_->SendCommand(command);
// 断开之前的连接(不触发回调事件)
if (instance_active_) {
at_uart_->SendCommand("AT+QSSLCLOSE=" + std::to_string(ssl_id_));
xEventGroupWaitBits(event_group_handle_, EC801E_SSL_DISCONNECTED, pdTRUE, pdFALSE, SSL_CONNECT_TIMEOUT_MS / portTICK_PERIOD_MS);
instance_active_ = false;
}
// 打开 TCP 连接
command = "AT+QSSLOPEN=1,1," + std::to_string(ssl_id_) + ",\"" + host + "\"," + std::to_string(port) + ",1";
if (!at_uart_->SendCommand(command)) {
ESP_LOGE(TAG, "Failed to open TCP connection");
return false;
}
// 等待连接完成
auto bits = xEventGroupWaitBits(event_group_handle_, EC801E_SSL_CONNECTED | EC801E_SSL_ERROR, pdTRUE, pdFALSE, SSL_CONNECT_TIMEOUT_MS / portTICK_PERIOD_MS);
if (bits & EC801E_SSL_ERROR) {
ESP_LOGE(TAG, "Failed to connect to %s:%d", host.c_str(), port);
return false;
}
return true;
}
void Ec801ESsl::Disconnect() {
if (!instance_active_) {
return;
}
at_uart_->SendCommand("AT+QSSLCLOSE=" + std::to_string(ssl_id_));
if (connected_) {
connected_ = false;
if (disconnect_callback_) {
disconnect_callback_();
}
}
}
int Ec801ESsl::Send(const std::string& data) {
const size_t MAX_PACKET_SIZE = 1460;
size_t total_sent = 0;
if (!connected_) {
ESP_LOGE(TAG, "Not connected");
return -1;
}
while (total_sent < data.size()) {
size_t chunk_size = std::min(data.size() - total_sent, MAX_PACKET_SIZE);
std::string command = "AT+QSSLSEND=" + std::to_string(ssl_id_) + "," + std::to_string(chunk_size);
if (!at_uart_->SendCommandWithData(command, 1000, true, data.data() + total_sent, chunk_size)) {
ESP_LOGE(TAG, "Send command failed");
Disconnect();
return -1;
}
auto bits = xEventGroupWaitBits(event_group_handle_, EC801E_SSL_SEND_COMPLETE | EC801E_SSL_SEND_FAILED, pdTRUE, pdFALSE, pdMS_TO_TICKS(SSL_CONNECT_TIMEOUT_MS));
if (bits & EC801E_SSL_SEND_FAILED) {
ESP_LOGE(TAG, "Send failed, retry later");
vTaskDelay(pdMS_TO_TICKS(100));
continue;
} else if (!(bits & EC801E_SSL_SEND_COMPLETE)) {
ESP_LOGE(TAG, "Send timeout");
return -1;
}
total_sent += chunk_size;
}
return data.size();
}

View File

@@ -0,0 +1,36 @@
#ifndef EC801E_SSL_H
#define EC801E_SSL_H
#include "tcp.h"
#include "at_uart.h"
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
#define EC801E_SSL_CONNECTED BIT0
#define EC801E_SSL_DISCONNECTED BIT1
#define EC801E_SSL_ERROR BIT2
#define EC801E_SSL_SEND_COMPLETE BIT3
#define EC801E_SSL_SEND_FAILED BIT4
#define EC801E_SSL_INITIALIZED BIT5
#define SSL_CONNECT_TIMEOUT_MS 10000
class Ec801ESsl : public Tcp {
public:
Ec801ESsl(std::shared_ptr<AtUart> at_uart, int ssl_id);
~Ec801ESsl();
bool Connect(const std::string& host, int port) override;
void Disconnect() override;
int Send(const std::string& data) override;
private:
std::shared_ptr<AtUart> at_uart_;
int ssl_id_;
bool instance_active_ = false;
EventGroupHandle_t event_group_handle_;
std::list<UrcCallback>::iterator urc_callback_it_;
};
#endif // EC801E_SSL_H

View File

@@ -0,0 +1,159 @@
#include "ec801e_tcp.h"
#include <esp_log.h>
#define TAG "Ec801ETcp"
Ec801ETcp::Ec801ETcp(std::shared_ptr<AtUart> at_uart, int tcp_id) : at_uart_(at_uart), tcp_id_(tcp_id) {
event_group_handle_ = xEventGroupCreate();
urc_callback_it_ = at_uart_->RegisterUrcCallback([this](const std::string& command, const std::vector<AtArgumentValue>& arguments) {
if (command == "QIOPEN" && arguments.size() == 2) {
if (arguments[0].int_value == tcp_id_) {
if (arguments[1].int_value == 0) {
connected_ = true;
instance_active_ = true;
xEventGroupClearBits(event_group_handle_, EC801E_TCP_DISCONNECTED | EC801E_TCP_ERROR);
xEventGroupSetBits(event_group_handle_, EC801E_TCP_CONNECTED);
} else {
connected_ = false;
xEventGroupSetBits(event_group_handle_, EC801E_TCP_ERROR);
if (disconnect_callback_) {
disconnect_callback_();
}
}
}
} else if (command == "QISEND" && arguments.size() == 3) {
if (arguments[0].int_value == tcp_id_) {
if (arguments[1].int_value == 0) {
xEventGroupSetBits(event_group_handle_, EC801E_TCP_SEND_COMPLETE);
} else {
xEventGroupSetBits(event_group_handle_, EC801E_TCP_SEND_FAILED);
}
}
} else if (command == "QIURC" && arguments.size() >= 2) {
if (arguments[1].int_value == tcp_id_) {
if (arguments[0].string_value == "recv" && arguments.size() >= 4) {
if (connected_ && stream_callback_) {
stream_callback_(at_uart_->DecodeHex(arguments[3].string_value));
}
} else if (arguments[0].string_value == "closed") {
if (connected_) {
connected_ = false;
// instance_active_ 保持 true需要发送 QICLOSE 清理
if (disconnect_callback_) {
disconnect_callback_();
}
}
xEventGroupSetBits(event_group_handle_, EC801E_TCP_DISCONNECTED);
} else {
ESP_LOGE(TAG, "Unknown QIURC command: %s", arguments[0].string_value.c_str());
}
}
} else if (command == "QISTATE" && arguments.size() > 5) {
if (arguments[0].int_value == tcp_id_) {
connected_ = arguments[5].int_value == 2;
instance_active_ = true;
xEventGroupSetBits(event_group_handle_, EC801E_TCP_INITIALIZED);
}
} else if (command == "FIFO_OVERFLOW") {
xEventGroupSetBits(event_group_handle_, EC801E_TCP_ERROR);
Disconnect();
}
});
}
Ec801ETcp::~Ec801ETcp() {
Disconnect();
at_uart_->UnregisterUrcCallback(urc_callback_it_);
if (event_group_handle_) {
vEventGroupDelete(event_group_handle_);
}
}
bool Ec801ETcp::Connect(const std::string& host, int port) {
// Clear bits
xEventGroupClearBits(event_group_handle_, EC801E_TCP_CONNECTED | EC801E_TCP_DISCONNECTED | EC801E_TCP_ERROR);
// Keep data in one line; Use HEX encoding in response
at_uart_->SendCommand("AT+QICFG=\"close/mode\",1;+QICFG=\"viewmode\",1;+QICFG=\"sendinfo\",1;+QICFG=\"dataformat\",0,1");
// 检查这个 id 是否已经连接
std::string command = "AT+QISTATE=1," + std::to_string(tcp_id_);
at_uart_->SendCommand(command);
// 断开之前的连接(不触发回调事件)
if (instance_active_) {
at_uart_->SendCommand("AT+QICLOSE=" + std::to_string(tcp_id_));
xEventGroupWaitBits(event_group_handle_, EC801E_TCP_DISCONNECTED, pdTRUE, pdFALSE, TCP_CONNECT_TIMEOUT_MS / portTICK_PERIOD_MS);
instance_active_ = false;
}
// 打开 TCP 连接
command = "AT+QIOPEN=1," + std::to_string(tcp_id_) + ",\"TCP\",\"" + host + "\"," + std::to_string(port) + ",0,1";
if (!at_uart_->SendCommand(command)) {
ESP_LOGE(TAG, "Failed to open TCP connection");
return false;
}
// 等待连接完成
auto bits = xEventGroupWaitBits(event_group_handle_, EC801E_TCP_CONNECTED | EC801E_TCP_ERROR, pdTRUE, pdFALSE, TCP_CONNECT_TIMEOUT_MS / portTICK_PERIOD_MS);
if (bits & EC801E_TCP_ERROR) {
ESP_LOGE(TAG, "Failed to connect to %s:%d", host.c_str(), port);
return false;
}
return true;
}
void Ec801ETcp::Disconnect() {
if (!instance_active_) {
return;
}
if (at_uart_->SendCommand("AT+QICLOSE=" + std::to_string(tcp_id_))) {
instance_active_ = false;
}
if (connected_) {
connected_ = false;
if (disconnect_callback_) {
disconnect_callback_();
}
}
}
int Ec801ETcp::Send(const std::string& data) {
const size_t MAX_PACKET_SIZE = 1460;
size_t total_sent = 0;
if (!connected_) {
ESP_LOGE(TAG, "Not connected");
return -1;
}
while (total_sent < data.size()) {
size_t chunk_size = std::min(data.size() - total_sent, MAX_PACKET_SIZE);
std::string command = "AT+QISEND=" + std::to_string(tcp_id_) + "," + std::to_string(chunk_size);
if (!at_uart_->SendCommandWithData(command, 1000, true, data.data() + total_sent, chunk_size)) {
ESP_LOGE(TAG, "Send command failed");
Disconnect();
return -1;
}
auto bits = xEventGroupWaitBits(event_group_handle_, EC801E_TCP_SEND_COMPLETE | EC801E_TCP_SEND_FAILED, pdTRUE, pdFALSE, pdMS_TO_TICKS(TCP_CONNECT_TIMEOUT_MS));
if (bits & EC801E_TCP_SEND_FAILED) {
ESP_LOGE(TAG, "Send failed, retry later");
vTaskDelay(pdMS_TO_TICKS(100));
continue;
} else if (!(bits & EC801E_TCP_SEND_COMPLETE)) {
ESP_LOGE(TAG, "Send timeout");
return -1;
}
total_sent += chunk_size;
}
return data.size();
}

View File

@@ -0,0 +1,37 @@
#ifndef EC801E_TCP_H
#define EC801E_TCP_H
#include "tcp.h"
#include "at_uart.h"
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
#include <string>
#define EC801E_TCP_CONNECTED BIT0
#define EC801E_TCP_DISCONNECTED BIT1
#define EC801E_TCP_ERROR BIT2
#define EC801E_TCP_SEND_COMPLETE BIT3
#define EC801E_TCP_SEND_FAILED BIT4
#define EC801E_TCP_INITIALIZED BIT5
#define TCP_CONNECT_TIMEOUT_MS 10000
class Ec801ETcp : public Tcp {
public:
Ec801ETcp(std::shared_ptr<AtUart> at_uart, int tcp_id);
~Ec801ETcp();
bool Connect(const std::string& host, int port) override;
void Disconnect() override;
int Send(const std::string& data) override;
private:
std::shared_ptr<AtUart> at_uart_;
int tcp_id_;
bool instance_active_ = false;
EventGroupHandle_t event_group_handle_;
std::list<UrcCallback>::iterator urc_callback_it_;
};
#endif // EC801E_TCP_H

View File

@@ -0,0 +1,141 @@
#include "ec801e_udp.h"
#include <esp_log.h>
#define TAG "Ec801EUdp"
Ec801EUdp::Ec801EUdp(std::shared_ptr<AtUart> at_uart, int udp_id) : at_uart_(at_uart), udp_id_(udp_id) {
event_group_handle_ = xEventGroupCreate();
urc_callback_it_ = at_uart_->RegisterUrcCallback([this](const std::string& command, const std::vector<AtArgumentValue>& arguments) {
if (command == "QIOPEN" && arguments.size() == 2) {
if (arguments[0].int_value == udp_id_) {
connected_ = arguments[1].int_value == 0;
if (connected_) {
instance_active_ = true;
xEventGroupClearBits(event_group_handle_, EC801E_UDP_DISCONNECTED | EC801E_UDP_ERROR);
xEventGroupSetBits(event_group_handle_, EC801E_UDP_CONNECTED);
} else {
xEventGroupSetBits(event_group_handle_, EC801E_UDP_ERROR);
}
}
} else if (command == "QISEND" && arguments.size() == 3) {
if (arguments[0].int_value == udp_id_) {
if (arguments[1].int_value == 0) {
xEventGroupSetBits(event_group_handle_, EC801E_UDP_SEND_COMPLETE);
} else {
xEventGroupSetBits(event_group_handle_, EC801E_UDP_SEND_FAILED);
}
}
} else if (command == "QIURC" && arguments.size() >= 2) {
if (arguments[1].int_value == udp_id_) {
if (arguments[0].string_value == "recv" && arguments.size() >= 4) {
if (connected_ && message_callback_) {
message_callback_(at_uart_->DecodeHex(arguments[3].string_value));
}
} else if (arguments[0].string_value == "closed") {
connected_ = false;
instance_active_ = false;
xEventGroupSetBits(event_group_handle_, EC801E_UDP_DISCONNECTED);
} else {
ESP_LOGE(TAG, "Unknown QIURC command: %s", arguments[0].string_value.c_str());
}
}
} else if (command == "QISTATE" && arguments.size() > 5) {
if (arguments[0].int_value == udp_id_) {
connected_ = arguments[5].int_value == 2;
instance_active_ = true;
xEventGroupSetBits(event_group_handle_, EC801E_UDP_INITIALIZED);
}
} else if (command == "FIFO_OVERFLOW") {
xEventGroupSetBits(event_group_handle_, EC801E_UDP_ERROR);
Disconnect();
}
});
}
Ec801EUdp::~Ec801EUdp() {
Disconnect();
at_uart_->UnregisterUrcCallback(urc_callback_it_);
if (event_group_handle_) {
vEventGroupDelete(event_group_handle_);
}
}
bool Ec801EUdp::Connect(const std::string& host, int port) {
// Clear bits
xEventGroupClearBits(event_group_handle_, EC801E_UDP_CONNECTED | EC801E_UDP_DISCONNECTED | EC801E_UDP_ERROR);
// Keep data in one line; Use HEX encoding in response
at_uart_->SendCommand("AT+QICFG=\"close/mode\",1;+QICFG=\"viewmode\",1;+QICFG=\"sendinfo\",1;+QICFG=\"dataformat\",0,1");
// 检查这个 id 是否已经连接
std::string command = "AT+QISTATE=1," + std::to_string(udp_id_);
at_uart_->SendCommand(command);
// 断开之前的连接(不触发回调事件)
if (instance_active_) {
at_uart_->SendCommand("AT+QICLOSE=" + std::to_string(udp_id_));
xEventGroupWaitBits(event_group_handle_, EC801E_UDP_DISCONNECTED, pdTRUE, pdFALSE, UDP_CONNECT_TIMEOUT_MS / portTICK_PERIOD_MS);
instance_active_ = false;
}
// 打开 UDP 连接
command = "AT+QIOPEN=1," + std::to_string(udp_id_) + ",\"UDP\",\"" + host + "\"," + std::to_string(port) + ",0,1";
if (!at_uart_->SendCommand(command)) {
ESP_LOGE(TAG, "Failed to open UDP connection");
return false;
}
// 等待连接完成
auto bits = xEventGroupWaitBits(event_group_handle_, EC801E_UDP_CONNECTED | EC801E_UDP_ERROR, pdTRUE, pdFALSE, UDP_CONNECT_TIMEOUT_MS / portTICK_PERIOD_MS);
if (bits & EC801E_UDP_ERROR) {
ESP_LOGE(TAG, "Failed to connect to %s:%d", host.c_str(), port);
return false;
}
return true;
}
void Ec801EUdp::Disconnect() {
if (!instance_active_) {
return;
}
if (at_uart_->SendCommand("AT+QICLOSE=" + std::to_string(udp_id_))) {
instance_active_ = false;
}
}
int Ec801EUdp::Send(const std::string& data) {
const size_t MAX_PACKET_SIZE = 1460;
if (!connected_) {
ESP_LOGE(TAG, "Not connected");
return -1;
}
if (data.size() > MAX_PACKET_SIZE) {
ESP_LOGE(TAG, "Data block exceeds maximum limit");
return -1;
}
// 在循环外预先分配command
std::string command = "AT+QISEND=" + std::to_string(udp_id_) + "," + std::to_string(data.size());
if (!at_uart_->SendCommandWithData(command, 1000, true, data.data(), data.size())) {
ESP_LOGE(TAG, "Failed to send command");
return -1;
}
auto bits = xEventGroupWaitBits(event_group_handle_, EC801E_UDP_SEND_COMPLETE | EC801E_UDP_SEND_FAILED, pdTRUE, pdFALSE, pdMS_TO_TICKS(UDP_CONNECT_TIMEOUT_MS));
if (bits & EC801E_UDP_SEND_FAILED) {
ESP_LOGE(TAG, "Failed to send data");
return -1;
} else if (!(bits & EC801E_UDP_SEND_COMPLETE)) {
ESP_LOGE(TAG, "Send timeout");
return -1;
}
return data.size();
}

View File

@@ -0,0 +1,36 @@
#ifndef EC801E_UDP_H
#define EC801E_UDP_H
#include "udp.h"
#include "at_uart.h"
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
#define EC801E_UDP_CONNECTED BIT0
#define EC801E_UDP_DISCONNECTED BIT1
#define EC801E_UDP_ERROR BIT2
#define EC801E_UDP_SEND_COMPLETE BIT3
#define EC801E_UDP_SEND_FAILED BIT4
#define EC801E_UDP_INITIALIZED BIT5
#define UDP_CONNECT_TIMEOUT_MS 10000
class Ec801EUdp : public Udp {
public:
Ec801EUdp(std::shared_ptr<AtUart> at_uart, int udp_id);
~Ec801EUdp();
bool Connect(const std::string& host, int port) override;
void Disconnect() override;
int Send(const std::string& data) override;
private:
std::shared_ptr<AtUart> at_uart_;
int udp_id_;
bool instance_active_ = false;
EventGroupHandle_t event_group_handle_;
std::list<UrcCallback>::iterator urc_callback_it_;
};
#endif // EC801E_UDP_H