mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-29 10:17:30 +02:00
feat(modem_sim): Modem simulator based on esp-at
This commit is contained in:
@ -45,7 +45,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["release-v5.0", "release-v5.1", "release-v5.2", "release-v5.3", "release-v5.4", "latest"]
|
||||
test: ["target", "target_ota", "target_iperf"]
|
||||
test: ["target", "target_ota", "target_iperf", "target_urc"]
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
|
28
.github/workflows/modem_sim__build.yml
vendored
Normal file
28
.github/workflows/modem_sim__build.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
name: "modem_sim: build-tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, labeled]
|
||||
|
||||
jobs:
|
||||
build_modem_sim:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'modem_sim') || github.event_name == 'push'
|
||||
name: Build
|
||||
strategy:
|
||||
matrix:
|
||||
idf_ver: ["release-v5.4"]
|
||||
runs-on: ubuntu-22.04
|
||||
container: espressif/idf:${{ matrix.idf_ver }}
|
||||
steps:
|
||||
- name: Checkout esp-protocols
|
||||
uses: actions/checkout@v3
|
||||
- name: Build ESP-AT with IDF-${{ matrix.idf_ver }}
|
||||
shell: bash
|
||||
run: |
|
||||
cd common_components/modem_sim
|
||||
./install.sh
|
||||
source export.sh
|
||||
idf.py build
|
@ -61,8 +61,8 @@ repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: commit message scopes
|
||||
name: "commit message must be scoped with: mdns, dns, modem, websocket, asio, mqtt_cxx, console, common, eppp, tls_cxx, mosq, sockutls, lws"
|
||||
entry: '\A(?!(feat|fix|ci|bump|test|docs|chore)\((mdns|dns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp|tls_cxx|mosq|sockutls|lws)\)\:)'
|
||||
name: "commit message must be scoped with: mdns, dns, modem, websocket, asio, mqtt_cxx, console, common, eppp, tls_cxx, mosq, sockutls, lws, modem_sim"
|
||||
entry: '\A(?!(feat|fix|ci|bump|test|docs|chore)\((mdns|dns|modem|common|console|websocket|asio|mqtt_cxx|examples|eppp|tls_cxx|mosq|sockutls|lws|modem_sim)\)\:)'
|
||||
language: pygrep
|
||||
args: [--multiline]
|
||||
stages: [commit-msg]
|
||||
|
11
common_components/modem_sim/export.sh
Executable file
11
common_components/modem_sim/export.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
source $IDF_PATH/export.sh
|
||||
|
||||
export AT_CUSTOM_COMPONENTS="`pwd`/pppd_cmd"
|
||||
|
||||
cd modem_sim_esp32/esp-at
|
||||
|
||||
python -m pip install -r requirements.txt
|
||||
|
||||
python build.py reconfigure
|
59
common_components/modem_sim/install.sh
Executable file
59
common_components/modem_sim/install.sh
Executable file
@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Create directory "modem_sim_esp32", go inside it
|
||||
# Usage: ./install.sh [platform] [module]
|
||||
|
||||
SCRIPT_DIR=$(pwd)
|
||||
mkdir -p modem_sim_esp32
|
||||
cd modem_sim_esp32
|
||||
|
||||
# Shallow clone https://github.com/espressif/esp-at.git
|
||||
if [ ! -d "esp-at" ]; then
|
||||
git clone --depth 1 https://github.com/espressif/esp-at.git
|
||||
else
|
||||
echo "esp-at directory already exists, skipping clone."
|
||||
fi
|
||||
|
||||
cd esp-at
|
||||
|
||||
# Add esp-idf directory which is a symlink to the $IDF_PATH
|
||||
if [ -z "$IDF_PATH" ]; then
|
||||
echo "Error: IDF_PATH environment variable is not set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -L "esp-idf" ]; then
|
||||
ln -sf "$IDF_PATH" esp-idf
|
||||
else
|
||||
echo "esp-idf symlink already exists, skipping."
|
||||
fi
|
||||
|
||||
# Create "build" directory
|
||||
mkdir -p build
|
||||
|
||||
# Default values for platform and module
|
||||
platform="PLATFORM_ESP32"
|
||||
module="WROOM-32"
|
||||
|
||||
# Override defaults if parameters are provided
|
||||
if [ ! -z "$1" ]; then
|
||||
platform="$1"
|
||||
fi
|
||||
if [ ! -z "$2" ]; then
|
||||
module="$2"
|
||||
fi
|
||||
|
||||
# Create file "build/module_info.json" with content
|
||||
cat > build/module_info.json << EOF
|
||||
{
|
||||
"platform": "$platform",
|
||||
"module": "$module",
|
||||
"description": "4MB, Wi-Fi + BLE, OTA, TX:17 RX:16",
|
||||
"silence": 0
|
||||
}
|
||||
EOF
|
||||
|
||||
cp "$SCRIPT_DIR/sdkconfig.defaults" "module_config/module_esp32_default/sdkconfig.defaults"
|
||||
|
||||
echo "Installation completed successfully!"
|
||||
echo "Created modem_sim_esp32 directory with esp-at repository and configuration"
|
14
common_components/modem_sim/pppd_cmd/CMakeLists.txt
Normal file
14
common_components/modem_sim/pppd_cmd/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
file(GLOB_RECURSE srcs *.c)
|
||||
|
||||
set(includes "include")
|
||||
|
||||
# Add more required components you need here, separated by spaces
|
||||
set(require_components at freertos nvs_flash)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${srcs}
|
||||
INCLUDE_DIRS ${includes}
|
||||
REQUIRES ${require_components})
|
||||
|
||||
idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE TRUE)
|
418
common_components/modem_sim/pppd_cmd/custom/at_custom_cmd.c
Normal file
418
common_components/modem_sim/pppd_cmd/custom/at_custom_cmd.c
Normal file
@ -0,0 +1,418 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
extern uint8_t g_at_cmd_port;
|
||||
|
||||
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)
|
||||
{
|
||||
static uint8_t buffer[1024] = {0};
|
||||
// xSemaphoreGive(at_sync_sema);
|
||||
int len = esp_at_port_read_data(buffer, sizeof(buffer) - 1);
|
||||
ESP_LOG_BUFFER_HEXDUMP("ppp_uart_recv", buffer, len, ESP_LOG_VERBOSE);
|
||||
esp_netif_receive(s_netif, buffer, len, NULL);
|
||||
}
|
||||
|
||||
static esp_err_t transmit(void *h, void *buffer, size_t len)
|
||||
{
|
||||
// struct eppp_handle *handle = h;
|
||||
printf("transmit: %d bytes\n", len);
|
||||
// ESP_LOG_BUFFER_HEXDUMP("ppp_uart_send", buffer, len, ESP_LOG_INFO);
|
||||
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));
|
||||
printf("YYYEEES Command <AT%s> executed successfully\r\n", cmd_name);
|
||||
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);
|
||||
|
||||
// receive input data
|
||||
// while(xSemaphoreTake(at_sync_sema, portMAX_DELAY)) {
|
||||
// int len = esp_at_port_read_data(buffer, sizeof(buffer) - 1);
|
||||
// if (len > 0) {
|
||||
// buffer[len] = '\0'; // null-terminate the string
|
||||
// printf("Received data: %s\n", buffer);
|
||||
// } else {
|
||||
// printf("No data received or error occurred.\n");
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
|
||||
// uart_write_bytes(g_at_cmd_port, "CONNECT\r\n", strlen("CONNECT\r\n"));
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
printf("-");
|
||||
}
|
||||
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: 0,1,2,3,4,5\r\n";
|
||||
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;
|
||||
}
|
||||
|
||||
/* HTTP Server handlers */
|
||||
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},
|
||||
/**
|
||||
* @brief You can define your own AT commands here.
|
||||
*/
|
||||
};
|
||||
|
||||
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);
|
12
common_components/modem_sim/pppd_cmd/include/at_custom_cmd.h
Normal file
12
common_components/modem_sim/pppd_cmd/include/at_custom_cmd.h
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
#include "esp_at_core.h"
|
||||
#include "esp_at.h"
|
||||
|
||||
/**
|
||||
* @brief You can include more header files here for your own AT commands.
|
||||
*/
|
77
common_components/modem_sim/sdkconfig.defaults
Normal file
77
common_components/modem_sim/sdkconfig.defaults
Normal file
@ -0,0 +1,77 @@
|
||||
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||
# Espressif IoT Development Framework (ESP-IDF) 5.4.1 Project Minimal Configuration
|
||||
#
|
||||
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
|
||||
CONFIG_APP_PROJECT_VER_FROM_CONFIG=y
|
||||
CONFIG_APP_PROJECT_VER="v4.1.0.0-dev"
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="module_config/module_esp32_default/partitions_at.csv"
|
||||
CONFIG_PARTITION_TABLE_MD5=n
|
||||
CONFIG_AT_CUSTOMIZED_PARTITION_TABLE_FILE="module_config/module_esp32_default/at_customize.csv"
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BT_BTU_TASK_STACK_SIZE=5120
|
||||
CONFIG_BT_BLE_BLUFI_ENABLE=y
|
||||
CONFIG_BT_STACK_NO_LOG=y
|
||||
CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY=y
|
||||
CONFIG_BTDM_CTRL_MODE_BTDM=y
|
||||
CONFIG_BTDM_CTRL_LPCLK_SEL_EXT_32K_XTAL=y
|
||||
CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE=200
|
||||
CONFIG_ESP_TLS_PSK_VERIFICATION=y
|
||||
CONFIG_ESP_TLS_INSECURE=y
|
||||
CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y
|
||||
CONFIG_ESP_ERR_TO_NAME_LOOKUP=n
|
||||
CONFIG_GPIO_ESP32_SUPPORT_SWITCH_SLP_PULL=y
|
||||
CONFIG_ETH_DMA_RX_BUFFER_NUM=3
|
||||
CONFIG_ETH_DMA_TX_BUFFER_NUM=3
|
||||
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
|
||||
CONFIG_HTTPD_MAX_URI_LEN=1024
|
||||
CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP=y
|
||||
CONFIG_RTC_CLK_SRC_EXT_CRYS=y
|
||||
CONFIG_RTC_EXT_CRYST_ADDIT_CURRENT=y
|
||||
CONFIG_RTC_CLK_CAL_CYCLES=1024
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_PM_SLP_DISABLE_GPIO=y
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80=y
|
||||
CONFIG_ESP_TASK_WDT_PANIC=y
|
||||
CONFIG_ESP_TASK_WDT_TIMEOUT_S=60
|
||||
CONFIG_ESP_DEBUG_OCDAWARE=n
|
||||
CONFIG_ESP_WIFI_IRAM_OPT=n
|
||||
CONFIG_ESP_WIFI_RX_IRAM_OPT=n
|
||||
CONFIG_ESP_WIFI_SLP_IRAM_OPT=y
|
||||
CONFIG_ESP_WIFI_SLP_BEACON_LOST_OPT=y
|
||||
CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=0
|
||||
CONFIG_FATFS_LFN_HEAP=y
|
||||
CONFIG_FREERTOS_UNICORE=y
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=n
|
||||
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
|
||||
CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL_ERROR=y
|
||||
CONFIG_LWIP_MAX_SOCKETS=16
|
||||
CONFIG_LWIP_SO_LINGER=y
|
||||
CONFIG_LWIP_SO_RCVBUF=y
|
||||
CONFIG_LWIP_IP4_REASSEMBLY=y
|
||||
CONFIG_LWIP_IP6_REASSEMBLY=y
|
||||
CONFIG_LWIP_IPV6_AUTOCONFIG=y
|
||||
CONFIG_LWIP_TCP_MAXRTX=6
|
||||
CONFIG_LWIP_TCP_SYNMAXRTX=3
|
||||
CONFIG_LWIP_PPP_SUPPORT=y
|
||||
CONFIG_LWIP_PPP_SERVER_SUPPORT=y
|
||||
CONFIG_LWIP_SNTP_MAX_SERVERS=3
|
||||
CONFIG_LWIP_SNTP_STARTUP_DELAY=n
|
||||
CONFIG_MBEDTLS_DYNAMIC_BUFFER=y
|
||||
CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y
|
||||
CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=n
|
||||
CONFIG_MBEDTLS_HAVE_TIME_DATE=y
|
||||
CONFIG_MBEDTLS_DHM_C=y
|
||||
CONFIG_NEWLIB_NANO_FORMAT=y
|
||||
CONFIG_VFS_SUPPORT_TERMIOS=n
|
||||
CONFIG_WL_SECTOR_SIZE_512=y
|
||||
CONFIG_AT_PROCESS_TASK_STACK_SIZE=6144
|
||||
CONFIG_AT_MQTT_COMMAND_SUPPORT=y
|
||||
CONFIG_AT_HTTP_COMMAND_SUPPORT=y
|
||||
CONFIG_AT_BLE_COMMAND_SUPPORT=n
|
||||
CONFIG_AT_BLE_HID_COMMAND_SUPPORT=n
|
||||
CONFIG_AT_BLUFI_COMMAND_SUPPORT=n
|
7
components/esp_modem/test/target_urc/CMakeLists.txt
Normal file
7
components/esp_modem/test/target_urc/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
set(COMPONENTS main)
|
||||
project(urc_test)
|
2
components/esp_modem/test/target_urc/main/CMakeLists.txt
Normal file
2
components/esp_modem/test/target_urc/main/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "urc_test.cpp"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,7 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
## Required IDF version
|
||||
idf: ">=4.1.0"
|
||||
espressif/esp_modem:
|
||||
version: "^1.0.0"
|
||||
override_path: "../../../"
|
158
components/esp_modem/test/target_urc/main/urc_test.cpp
Normal file
158
components/esp_modem/test/target_urc/main/urc_test.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <cstring>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_netif.h"
|
||||
#include "cxx_include/esp_modem_dte.hpp"
|
||||
#include "esp_modem_config.h"
|
||||
#include "cxx_include/esp_modem_api.hpp"
|
||||
#include "cxx_include/esp_modem_dce_factory.hpp"
|
||||
#include "cxx_include/esp_modem_command_library_utils.hpp"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char *TAG = "urc_test";
|
||||
static EventGroupHandle_t s_event_group = nullptr;
|
||||
|
||||
class ESP_AT_Module: public ::esp_modem::ModuleIf {
|
||||
public:
|
||||
explicit ESP_AT_Module(std::shared_ptr<::esp_modem::DTE> dte, const esp_modem_dce_config *config):
|
||||
dte(std::move(dte)) {}
|
||||
|
||||
bool setup_data_mode() override
|
||||
{
|
||||
// not using network here
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_mode(::esp_modem::modem_mode mode) override
|
||||
{
|
||||
// we never allow mode change
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<::esp_modem::DTE> dte;
|
||||
};
|
||||
|
||||
class DCE : public esp_modem::DCE_T<ESP_AT_Module> {
|
||||
using DCE_T<ESP_AT_Module>::DCE_T;
|
||||
public:
|
||||
|
||||
|
||||
bool init()
|
||||
{
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
if (sync() == esp_modem::command_result::OK) {
|
||||
ESP_LOGI(TAG, "Modem in sync");
|
||||
return true;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(500 * (i + 1)));
|
||||
}
|
||||
ESP_LOGE(TAG, "Failed to sync with esp-at");
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_modem::command_result sync()
|
||||
{
|
||||
auto ret = esp_modem::dce_commands::generic_command_common(dte.get(), "AT\r\n");
|
||||
ESP_LOGI(TAG, "Syncing with esp-at...(%d)", static_cast<int>(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool http_get(const std::string &url)
|
||||
{
|
||||
std::string command = "AT+HTTPCGET=\"" + url + "\"\r\n";
|
||||
set_urc(handle_urc);
|
||||
auto ret = dte->write(esp_modem::DTE_Command(command));
|
||||
ESP_LOGI(TAG, "HTTP GET...(%d)", static_cast<int>(ret));
|
||||
return ret > 0;
|
||||
}
|
||||
|
||||
bool start_http_server() const
|
||||
{
|
||||
auto ret = esp_modem::dce_commands::generic_command_common(dte.get(), "AT+HTTPD\r\n");
|
||||
ESP_LOGI(TAG, "Start HTTP server...(%d)", static_cast<int>(ret));
|
||||
return ret == esp_modem::command_result::OK;
|
||||
}
|
||||
|
||||
static constexpr int transfer_completed = 1;
|
||||
private:
|
||||
static esp_modem::command_result handle_urc(uint8_t *data, size_t len)
|
||||
{
|
||||
static int start_chunk = 0;
|
||||
static int end_chunk = 0;
|
||||
std::string_view chunk((const char*)data + start_chunk, len - start_chunk);
|
||||
int newline = chunk.find('\n');
|
||||
if (newline == std::string_view::npos) {
|
||||
end_chunk = len; // careful, this grows buffer usage
|
||||
printf(".");
|
||||
return esp_modem::command_result::TIMEOUT;
|
||||
}
|
||||
printf("%.*s\n", newline, (char*)data + start_chunk);
|
||||
start_chunk = end_chunk;
|
||||
// check for the last one
|
||||
constexpr char last_chunk[] = "Transfer completed";
|
||||
if (memmem(data, len, last_chunk, sizeof(last_chunk) - 1) != nullptr) {
|
||||
xEventGroupSetBits(s_event_group, transfer_completed);
|
||||
}
|
||||
return esp_modem::command_result::OK;
|
||||
}
|
||||
};
|
||||
|
||||
class Factory: public ::esp_modem::dce_factory::Factory {
|
||||
public:
|
||||
static std::unique_ptr<DCE> create(const esp_modem::dce_config *config, std::shared_ptr<esp_modem::DTE> dte, esp_netif_t *netif)
|
||||
{
|
||||
return build_generic_DCE<ESP_AT_Module, DCE, std::unique_ptr<DCE>>(config, std::move(dte), netif);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<DCE> create(std::shared_ptr<esp_modem::DTE> dte)
|
||||
{
|
||||
esp_netif_config_t netif_ppp_config = ESP_NETIF_DEFAULT_PPP();
|
||||
static esp_netif_t *netif = esp_netif_new(&netif_ppp_config);
|
||||
assert(netif);
|
||||
|
||||
esp_modem_dce_config_t dce_config = ESP_MODEM_DCE_DEFAULT_CONFIG("APN"); // dummy config (not used with esp-at)
|
||||
return Factory::create(&dce_config, std::move(dte), netif);
|
||||
}
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
/* Init and register system/core components */
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
s_event_group = xEventGroupCreate();
|
||||
|
||||
esp_modem_dte_config_t dte_config = ESP_MODEM_DTE_DEFAULT_CONFIG();
|
||||
dte_config.dte_buffer_size = 1024;
|
||||
dte_config.uart_config.tx_io_num = 18;
|
||||
dte_config.uart_config.rx_io_num = 17;
|
||||
auto uart_dte = esp_modem::create_uart_dte(&dte_config);
|
||||
if (uart_dte == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to create UART DTE");
|
||||
return;
|
||||
}
|
||||
auto dce = create(std::move(uart_dte));
|
||||
if (!dce->init()) {
|
||||
ESP_LOGE(TAG, "Failed to setup network");
|
||||
return;
|
||||
}
|
||||
|
||||
dce->start_http_server();
|
||||
|
||||
dce->http_get("http://127.0.0.1:8080/async");
|
||||
|
||||
EventBits_t bits = xEventGroupWaitBits(s_event_group, 1, pdTRUE, pdFALSE, pdMS_TO_TICKS(15000));
|
||||
if (bits & DCE::transfer_completed) {
|
||||
ESP_LOGI(TAG, "Request finished!");
|
||||
}
|
||||
dce->sync();
|
||||
vEventGroupDelete(s_event_group);
|
||||
ESP_LOGI(TAG, "Done");
|
||||
}
|
3
components/esp_modem/test/target_urc/sdkconfig.defaults
Normal file
3
components/esp_modem/test/target_urc/sdkconfig.defaults
Normal file
@ -0,0 +1,3 @@
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
CONFIG_LWIP_PPP_SUPPORT=y
|
||||
CONFIG_ESP_MODEM_URC_HANDLER=y
|
Reference in New Issue
Block a user