2025-06-26 17:09:21 +02:00
|
|
|
/*
|
|
|
|
|
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
#include "esp_at.h"
|
|
|
|
|
#include "driver/gpio.h"
|
|
|
|
|
#include "driver/uart.h"
|
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
|
|
|
#include "freertos/event_groups.h"
|
|
|
|
|
#include "freertos/semphr.h"
|
|
|
|
|
#include "freertos/queue.h"
|
|
|
|
|
#include "esp_netif.h"
|
|
|
|
|
#include "esp_netif_ppp.h"
|
|
|
|
|
#include "esp_check.h"
|
|
|
|
|
#include "esp_http_server.h"
|
|
|
|
|
#include "esp_timer.h"
|
|
|
|
|
|
|
|
|
|
static uint8_t at_test_cmd_test(uint8_t *cmd_name)
|
|
|
|
|
{
|
|
|
|
|
uint8_t buffer[64] = {0};
|
|
|
|
|
snprintf((char *)buffer, 64, "test command: <AT%s=?> is executed\r\n", cmd_name);
|
|
|
|
|
esp_at_port_write_data(buffer, strlen((char *)buffer));
|
|
|
|
|
|
|
|
|
|
return ESP_AT_RESULT_CODE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t at_query_cmd_test(uint8_t *cmd_name)
|
|
|
|
|
{
|
|
|
|
|
uint8_t buffer[64] = {0};
|
|
|
|
|
snprintf((char *)buffer, 64, "query command: <AT%s?> is executed\r\n", cmd_name);
|
|
|
|
|
esp_at_port_write_data(buffer, strlen((char *)buffer));
|
|
|
|
|
|
|
|
|
|
return ESP_AT_RESULT_CODE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t at_setup_cmd_test(uint8_t para_num)
|
|
|
|
|
{
|
|
|
|
|
uint8_t index = 0;
|
|
|
|
|
printf("setup command: <AT%s=%d> is executed\r\n", esp_at_get_current_cmd_name(), para_num);
|
|
|
|
|
|
|
|
|
|
// get first parameter, and parse it into a digit
|
|
|
|
|
int32_t digit = 0;
|
|
|
|
|
if (esp_at_get_para_as_digit(index++, &digit) != ESP_AT_PARA_PARSE_RESULT_OK) {
|
|
|
|
|
return ESP_AT_RESULT_CODE_ERROR;
|
|
|
|
|
}
|
|
|
|
|
printf("digit: %d\r\n", digit);
|
|
|
|
|
|
|
|
|
|
// get second parameter, and parse it into a string
|
|
|
|
|
uint8_t *str = NULL;
|
|
|
|
|
if (esp_at_get_para_as_str(index++, &str) != ESP_AT_PARA_PARSE_RESULT_OK) {
|
|
|
|
|
return ESP_AT_RESULT_CODE_ERROR;
|
|
|
|
|
}
|
|
|
|
|
printf("string: %s\r\n", str);
|
|
|
|
|
|
|
|
|
|
// allocate a buffer and construct the data, then send the data to mcu via interface (uart/spi/sdio/socket)
|
|
|
|
|
uint8_t *buffer = (uint8_t *)malloc(512);
|
|
|
|
|
if (!buffer) {
|
|
|
|
|
return ESP_AT_RESULT_CODE_ERROR;
|
|
|
|
|
}
|
|
|
|
|
int len = snprintf((char *)buffer, 512, "setup command: <AT%s=%d,\"%s\"> is executed\r\n",
|
|
|
|
|
esp_at_get_current_cmd_name(), digit, str);
|
|
|
|
|
esp_at_port_write_data(buffer, len);
|
|
|
|
|
|
|
|
|
|
// remember to free the buffer
|
|
|
|
|
free(buffer);
|
|
|
|
|
|
|
|
|
|
return ESP_AT_RESULT_CODE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define TAG "at_custom_cmd"
|
|
|
|
|
static esp_netif_t *s_netif = NULL;
|
|
|
|
|
static httpd_handle_t http_server = NULL;
|
|
|
|
|
|
|
|
|
|
static void on_ppp_event(void *arg, esp_event_base_t base, int32_t event_id, void *data)
|
|
|
|
|
{
|
|
|
|
|
esp_netif_t **netif = data;
|
|
|
|
|
if (base == NETIF_PPP_STATUS && event_id == NETIF_PPP_ERRORUSER) {
|
|
|
|
|
printf("Disconnected!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void on_ip_event(void *arg, esp_event_base_t base, int32_t event_id, void *data)
|
|
|
|
|
{
|
|
|
|
|
ip_event_got_ip_t *event = (ip_event_got_ip_t *)data;
|
|
|
|
|
esp_netif_t *netif = event->esp_netif;
|
|
|
|
|
if (event_id == IP_EVENT_PPP_GOT_IP) {
|
|
|
|
|
printf("Got IPv4 event: Interface \"%s(%s)\" address: " IPSTR, esp_netif_get_desc(netif),
|
|
|
|
|
esp_netif_get_ifkey(netif), IP2STR(&event->ip_info.ip));
|
|
|
|
|
ESP_ERROR_CHECK(esp_netif_napt_enable(s_netif));
|
|
|
|
|
|
|
|
|
|
} else if (event_id == IP_EVENT_PPP_LOST_IP) {
|
|
|
|
|
ESP_LOGI(TAG, "Disconnected");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static SemaphoreHandle_t at_sync_sema = NULL;
|
|
|
|
|
static void wait_data_callback(void)
|
|
|
|
|
{
|
2025-07-11 11:00:56 +02:00
|
|
|
static uint8_t buffer[1500] = {0};
|
2025-06-26 17:09:21 +02:00
|
|
|
int len = esp_at_port_read_data(buffer, sizeof(buffer) - 1);
|
2025-07-11 11:00:56 +02:00
|
|
|
|
|
|
|
|
// Check for the escape sequence "+++" in the received data
|
|
|
|
|
const uint8_t escape_seq[] = "+++";
|
|
|
|
|
uint8_t *escape_ptr = memmem(buffer, len, escape_seq, 3);
|
|
|
|
|
|
|
|
|
|
if (escape_ptr != NULL) {
|
|
|
|
|
printf("Found +++ sequence, signal to the command processing thread\n");
|
|
|
|
|
|
|
|
|
|
int data_before_escape = escape_ptr - buffer;
|
|
|
|
|
if (data_before_escape > 0) {
|
|
|
|
|
esp_netif_receive(s_netif, buffer, data_before_escape, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (at_sync_sema) {
|
|
|
|
|
xSemaphoreGive(at_sync_sema);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-06-26 17:09:21 +02:00
|
|
|
esp_netif_receive(s_netif, buffer, len, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static esp_err_t transmit(void *h, void *buffer, size_t len)
|
|
|
|
|
{
|
|
|
|
|
printf("transmit: %d bytes\n", len);
|
|
|
|
|
esp_at_port_write_data(buffer, len);
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t at_exe_cmd_test(uint8_t *cmd_name)
|
|
|
|
|
{
|
|
|
|
|
uint8_t buffer[64] = {0};
|
|
|
|
|
snprintf((char *)buffer, 64, "execute command: <AT%s> is executed\r\n", cmd_name);
|
|
|
|
|
esp_at_port_write_data(buffer, strlen((char *)buffer));
|
2025-07-11 11:00:56 +02:00
|
|
|
printf("Command <AT%s> executed successfully\r\n", cmd_name);
|
2025-06-26 17:09:21 +02:00
|
|
|
if (!at_sync_sema) {
|
|
|
|
|
at_sync_sema = xSemaphoreCreateBinary();
|
|
|
|
|
assert(at_sync_sema != NULL);
|
|
|
|
|
esp_netif_driver_ifconfig_t driver_cfg = {
|
|
|
|
|
.handle = (void *)1,
|
|
|
|
|
.transmit = transmit,
|
|
|
|
|
};
|
|
|
|
|
const esp_netif_driver_ifconfig_t *ppp_driver_cfg = &driver_cfg;
|
|
|
|
|
|
|
|
|
|
esp_netif_inherent_config_t base_netif_cfg = ESP_NETIF_INHERENT_DEFAULT_PPP();
|
|
|
|
|
esp_netif_config_t netif_ppp_config = { .base = &base_netif_cfg,
|
|
|
|
|
.driver = ppp_driver_cfg,
|
|
|
|
|
.stack = ESP_NETIF_NETSTACK_DEFAULT_PPP
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
s_netif = esp_netif_new(&netif_ppp_config);
|
|
|
|
|
esp_netif_ppp_config_t netif_params;
|
|
|
|
|
ESP_ERROR_CHECK(esp_netif_ppp_get_params(s_netif, &netif_params));
|
|
|
|
|
netif_params.ppp_our_ip4_addr.addr = ESP_IP4TOADDR(192, 168, 11, 1);
|
|
|
|
|
netif_params.ppp_their_ip4_addr.addr = ESP_IP4TOADDR(192, 168, 11, 2);
|
|
|
|
|
netif_params.ppp_error_event_enabled = true;
|
|
|
|
|
ESP_ERROR_CHECK(esp_netif_ppp_set_params(s_netif, &netif_params));
|
|
|
|
|
if (esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, on_ip_event, NULL) != ESP_OK) {
|
|
|
|
|
printf("Failed to register IP event handler");
|
|
|
|
|
}
|
|
|
|
|
if (esp_event_handler_register(NETIF_PPP_STATUS, ESP_EVENT_ANY_ID, on_ppp_event, NULL) != ESP_OK) {
|
|
|
|
|
printf("Failed to register NETIF_PPP_STATUS event handler");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
esp_at_port_write_data((uint8_t *)"CONNECT\r\n", strlen("CONNECT\r\n"));
|
|
|
|
|
|
|
|
|
|
// set the callback function which will be called by AT port after receiving the input data
|
|
|
|
|
esp_at_port_enter_specific(wait_data_callback);
|
|
|
|
|
esp_netif_action_start(s_netif, 0, 0, 0);
|
|
|
|
|
esp_netif_action_connected(s_netif, 0, 0, 0);
|
|
|
|
|
|
2025-07-11 11:00:56 +02:00
|
|
|
while (xSemaphoreTake(at_sync_sema, pdMS_TO_TICKS(1000)) == pdFALSE) {
|
|
|
|
|
printf(".");
|
2025-06-26 17:09:21 +02:00
|
|
|
}
|
|
|
|
|
return ESP_AT_RESULT_CODE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t at_test_cereg(uint8_t *cmd_name)
|
|
|
|
|
{
|
|
|
|
|
printf("%s: AT command <AT%s> is executed\r\n", __func__, cmd_name);
|
|
|
|
|
return ESP_AT_RESULT_CODE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t at_query_cereg(uint8_t *cmd_name)
|
|
|
|
|
{
|
|
|
|
|
printf("%s: AT command <AT%s> is executed\r\n", __func__, cmd_name);
|
|
|
|
|
static uint8_t buffer[] = "+CEREG: 7,8\r\n";
|
|
|
|
|
esp_at_port_write_data(buffer, sizeof(buffer));
|
|
|
|
|
return ESP_AT_RESULT_CODE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t at_setup_cereg(uint8_t num)
|
|
|
|
|
{
|
|
|
|
|
printf("%s: AT command <AT%d> is executed\r\n", __func__, num);
|
|
|
|
|
return ESP_AT_RESULT_CODE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t at_exe_cereg(uint8_t *cmd_name)
|
|
|
|
|
{
|
|
|
|
|
printf("%s: AT command <AT%s> is executed\r\n", __func__, cmd_name);
|
|
|
|
|
return ESP_AT_RESULT_CODE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static esp_err_t hello_get_handler(httpd_req_t *req)
|
|
|
|
|
{
|
|
|
|
|
const char* resp_str = "Hello from ESP-AT HTTP Server!";
|
|
|
|
|
httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static esp_err_t root_get_handler(httpd_req_t *req)
|
|
|
|
|
{
|
|
|
|
|
const char* resp_str = "ESP-AT HTTP Server is running";
|
|
|
|
|
httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static esp_err_t test_get_handler(httpd_req_t *req)
|
|
|
|
|
{
|
|
|
|
|
const char* resp_str = "{\"status\":\"success\",\"message\":\"Test endpoint working\",\"timestamp\":12345}";
|
|
|
|
|
httpd_resp_set_type(req, "application/json");
|
|
|
|
|
httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static esp_err_t async_get_handler(httpd_req_t *req)
|
|
|
|
|
{
|
|
|
|
|
printf("Starting async chunked response handler\r\n");
|
|
|
|
|
|
|
|
|
|
// Set content type for plain text response
|
|
|
|
|
httpd_resp_set_type(req, "text/plain");
|
|
|
|
|
|
|
|
|
|
// Static counter to track requests
|
|
|
|
|
static uint8_t req_count = 0;
|
|
|
|
|
req_count++;
|
|
|
|
|
|
|
|
|
|
// Send initial response with request count
|
|
|
|
|
char buffer[256];
|
|
|
|
|
snprintf(buffer, sizeof(buffer), "=== Async Response #%d ===\r\n", req_count);
|
|
|
|
|
httpd_resp_sendstr_chunk(req, buffer);
|
|
|
|
|
|
|
|
|
|
// Long message broken into chunks
|
|
|
|
|
const char* chunks[] = {
|
|
|
|
|
"This is a simulated slow server response.\r\n",
|
|
|
|
|
"Chunk 1: The ESP-AT HTTP server is demonstrating...\r\n",
|
|
|
|
|
"Chunk 2: ...asynchronous chunked transfer encoding...\r\n",
|
|
|
|
|
"Chunk 3: ...with artificial delays between chunks...\r\n",
|
|
|
|
|
"Chunk 4: ...to simulate real-world network conditions.\r\n",
|
|
|
|
|
"Chunk 5: Processing data... please wait...\r\n",
|
|
|
|
|
"Chunk 6: Still processing... almost done...\r\n",
|
|
|
|
|
"Chunk 7: Final chunk - transfer complete!\r\n",
|
|
|
|
|
"=== END OF RESPONSE ===\r\n"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int num_chunks = sizeof(chunks) / sizeof(chunks[0]);
|
|
|
|
|
|
|
|
|
|
// Send each chunk with delays
|
|
|
|
|
for (int i = 0; i < num_chunks; i++) {
|
|
|
|
|
// Add a delay to simulate slow processing
|
|
|
|
|
vTaskDelay(pdMS_TO_TICKS(1500)); // 1.5 second delay between chunks
|
|
|
|
|
|
|
|
|
|
// Add chunk number and timestamp
|
|
|
|
|
snprintf(buffer, sizeof(buffer), "[%d/%d] [%d ms] %s",
|
|
|
|
|
i + 1, num_chunks, (int)(esp_timer_get_time() / 1000), chunks[i]);
|
|
|
|
|
|
|
|
|
|
printf("Sending chunk %d: %s", i + 1, chunks[i]);
|
|
|
|
|
httpd_resp_sendstr_chunk(req, buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add final summary
|
|
|
|
|
vTaskDelay(pdMS_TO_TICKS(500));
|
|
|
|
|
snprintf(buffer, sizeof(buffer), "\r\nTransfer completed in %d chunks with delays.\r\n", num_chunks);
|
|
|
|
|
httpd_resp_sendstr_chunk(req, buffer);
|
|
|
|
|
|
|
|
|
|
// Send NULL to signal end of chunked transfer
|
|
|
|
|
httpd_resp_sendstr_chunk(req, NULL);
|
|
|
|
|
|
|
|
|
|
printf("Async chunked response completed\r\n");
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const httpd_uri_t hello = {
|
|
|
|
|
.uri = "/hello",
|
|
|
|
|
.method = HTTP_GET,
|
|
|
|
|
.handler = hello_get_handler,
|
|
|
|
|
.user_ctx = NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const httpd_uri_t root = {
|
|
|
|
|
.uri = "/",
|
|
|
|
|
.method = HTTP_GET,
|
|
|
|
|
.handler = root_get_handler,
|
|
|
|
|
.user_ctx = NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const httpd_uri_t test = {
|
|
|
|
|
.uri = "/test",
|
|
|
|
|
.method = HTTP_GET,
|
|
|
|
|
.handler = test_get_handler,
|
|
|
|
|
.user_ctx = NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const httpd_uri_t async_uri = {
|
|
|
|
|
.uri = "/async",
|
|
|
|
|
.method = HTTP_GET,
|
|
|
|
|
.handler = async_get_handler,
|
|
|
|
|
.user_ctx = NULL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static esp_err_t start_http_server(void)
|
|
|
|
|
{
|
|
|
|
|
if (http_server != NULL) {
|
|
|
|
|
printf("HTTP server already running\r\n");
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
|
|
|
|
config.server_port = 8080;
|
|
|
|
|
config.lru_purge_enable = true;
|
|
|
|
|
|
|
|
|
|
printf("Starting HTTP server on port: %d\r\n", config.server_port);
|
|
|
|
|
if (httpd_start(&http_server, &config) == ESP_OK) {
|
|
|
|
|
printf("Registering URI handlers\r\n");
|
|
|
|
|
httpd_register_uri_handler(http_server, &hello);
|
|
|
|
|
httpd_register_uri_handler(http_server, &root);
|
|
|
|
|
httpd_register_uri_handler(http_server, &test);
|
|
|
|
|
httpd_register_uri_handler(http_server, &async_uri);
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("Error starting HTTP server!\r\n");
|
|
|
|
|
return ESP_FAIL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static esp_err_t stop_http_server(void)
|
|
|
|
|
{
|
|
|
|
|
if (http_server != NULL) {
|
|
|
|
|
httpd_stop(http_server);
|
|
|
|
|
http_server = NULL;
|
|
|
|
|
printf("HTTP server stopped\r\n");
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
}
|
|
|
|
|
return ESP_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* HTTP Server AT Commands */
|
|
|
|
|
static uint8_t at_test_httpd(uint8_t *cmd_name)
|
|
|
|
|
{
|
|
|
|
|
uint8_t buffer[64] = {0};
|
|
|
|
|
snprintf((char *)buffer, 64, "AT%s=<0/1> - Start/Stop HTTP server\r\n", cmd_name);
|
|
|
|
|
esp_at_port_write_data(buffer, strlen((char *)buffer));
|
|
|
|
|
return ESP_AT_RESULT_CODE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t at_query_httpd(uint8_t *cmd_name)
|
|
|
|
|
{
|
|
|
|
|
uint8_t buffer[64] = {0};
|
|
|
|
|
snprintf((char *)buffer, 64, "+HTTPD:%d\r\n", http_server != NULL ? 1 : 0);
|
|
|
|
|
esp_at_port_write_data(buffer, strlen((char *)buffer));
|
|
|
|
|
return ESP_AT_RESULT_CODE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t at_setup_httpd(uint8_t para_num)
|
|
|
|
|
{
|
|
|
|
|
int32_t action = 0;
|
|
|
|
|
if (esp_at_get_para_as_digit(0, &action) != ESP_AT_PARA_PARSE_RESULT_OK) {
|
|
|
|
|
return ESP_AT_RESULT_CODE_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (action == 1) {
|
|
|
|
|
if (start_http_server() == ESP_OK) {
|
|
|
|
|
printf("HTTP server started successfully\r\n");
|
|
|
|
|
return ESP_AT_RESULT_CODE_OK;
|
|
|
|
|
}
|
|
|
|
|
} else if (action == 0) {
|
|
|
|
|
if (stop_http_server() == ESP_OK) {
|
|
|
|
|
return ESP_AT_RESULT_CODE_OK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ESP_AT_RESULT_CODE_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint8_t at_exe_httpd(uint8_t *cmd_name)
|
|
|
|
|
{
|
|
|
|
|
// Default action: start server
|
|
|
|
|
if (start_http_server() == ESP_OK) {
|
|
|
|
|
printf("HTTP server started via execute command\r\n");
|
|
|
|
|
return ESP_AT_RESULT_CODE_OK;
|
|
|
|
|
}
|
|
|
|
|
return ESP_AT_RESULT_CODE_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const esp_at_cmd_struct at_custom_cmd[] = {
|
|
|
|
|
{"+PPPD", at_test_cmd_test, at_query_cmd_test, at_setup_cmd_test, at_exe_cmd_test},
|
|
|
|
|
{"+CEREG", at_test_cereg, at_query_cereg, at_setup_cereg, at_exe_cereg},
|
|
|
|
|
{"+HTTPD", at_test_httpd, at_query_httpd, at_setup_httpd, at_exe_httpd},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool esp_at_custom_cmd_register(void)
|
|
|
|
|
{
|
|
|
|
|
return esp_at_custom_cmd_array_regist(at_custom_cmd, sizeof(at_custom_cmd) / sizeof(esp_at_cmd_struct));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ESP_AT_CMD_SET_INIT_FN(esp_at_custom_cmd_register, 1);
|