Skip to content

Commit

Permalink
Merge branch 'feature/support_to_execute_preset_cmds' into 'master'
Browse files Browse the repository at this point in the history
feat(FCS-1431): Supported to execute preset at commands

See merge request application/esp-at!1550
  • Loading branch information
xcguang committed May 17, 2024
2 parents 2d0d28b + 521c6d1 commit aad4144
Show file tree
Hide file tree
Showing 9 changed files with 281 additions and 3 deletions.
3 changes: 3 additions & 0 deletions components/at/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ endif()
if (CONFIG_AT_BASE_ON_UART)
list(APPEND srcs "src/at_uart_cmd.c")
endif()
if (CONFIG_AT_SELF_COMMAND_SUPPORT)
list(APPEND srcs "src/at_self_cmd.c")
endif()

if (CONFIG_AT_WEB_SERVER_SUPPORT)
if(NOT CONFIG_AT_WEB_USE_FATFS)
Expand Down
31 changes: 31 additions & 0 deletions components/at/include/esp_at.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,34 @@ void esp_at_main_preprocess(void);
* @return at_mfg_params_storage_mode_t
*/
at_mfg_params_storage_mode_t at_get_mfg_params_storage_mode(void);

/**
* @brief Do some things before esp-at is ready.
*
* @note This function can be overridden with custom implementation. For example, you can override this function to:
* a) execute some preset AT commands by calling at_exe_cmd() API.
* b) do some initializations by calling APIs from esp-idf or esp-at.
*/
void esp_at_ready_before(void);

#ifdef CONFIG_AT_SELF_COMMAND_SUPPORT
/**
* @brief Execute AT command from self and wait for expected response.
*
* AT typically communicates with the host MCU via physical interface (UART/SPI/SDIO) or virtual interface (Socket),
* here, we define a new API that allows users to send AT commands via esp-at self instead of a physical/virtual interface,
* to check the response of the AT commands. This enables users to execute certain preset AT commands before AT ready,
* thereby modifying the default initial configuration or status of the AT firmware.
*
* @param[in] cmd: AT command string
* @param[in] expected_response: expected response string
* @param[in] timeout_ms: timeout in milliseconds
*
* @note Once exprected response is received, the function will return immediately.
*
* @return
* - ESP_OK: the expected response is received within the timeout
* - others: see esp_err.h
*/
esp_err_t at_exe_cmd(const char *cmd, const char *expected_response, uint32_t timeout_ms);
#endif
2 changes: 2 additions & 0 deletions components/at/include/esp_at_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ typedef struct {
bool (*wait_write_complete)(int32_t timeout_msec); /*!< wait write finish */
} esp_at_device_ops_struct;

typedef int32_t (*at_read_data_fn_t)(uint8_t *data, int32_t len);
typedef int32_t (*at_write_data_fn_t)(uint8_t *data, int32_t len);
typedef int32_t (*at_get_data_len_fn_t)(void);

/**
* @brief esp_at_custom_net_ops_struct
Expand Down
59 changes: 59 additions & 0 deletions components/at/include/esp_at_self_cmd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once

#include <stdint.h>
#include "sdkconfig.h"

/**
* This header file defines an AT self-interface feature.
*
* AT typically communicates with the host MCU via physical interface (UART/SPI/SDIO) or virtual interface (Socket),
* here, we define a new self-interface that allows users to send AT commands via esp-at self instead of a physical/virtual interface,
* to check the response of the AT commands. This enables users to execute certain preset AT commands before AT ready,
* thereby modifying the default initial configuration or status of the AT firmware.
*/
#ifdef CONFIG_AT_SELF_COMMAND_SUPPORT
/**
* @brief Get the current self-interface mode
*
* @return
* - true: the self-interface is enabled
* - false: otherwise
*/
bool at_self_cmd_get_mode(void);

/**
* @brief Read the data from the self-interface
*
* @param[inout] buffer: the buffer to store the returned data
* @param[in] buffer_len: the length of the buffer
*
* @return
* - the actual length of the data read
*/
int32_t at_self_cmd_read_data(uint8_t *buffer, int32_t buffer_len);

/**
* @brief Write the data to the self-interface
*
* @param[in] data: the data to write
* @param[in] len: the length of the data
*
* @return
* - the actual length of the data written
*/
int32_t at_self_cmd_write_data(uint8_t *data, int32_t len);

/**
* @brief Get the length of the buffered data in the self-interface
*
* @return
* - the buffered length of the data in the self-interface
*/
int32_t at_self_cmd_get_data_len(void);

#endif
11 changes: 11 additions & 0 deletions components/at/src/at_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ static void at_bt_controller_mem_release(void)
}
#endif

__attribute__((weak)) void esp_at_ready_before(void)
{
#ifdef CONFIG_AT_SELF_COMMAND_SUPPORT
at_exe_cmd("AT+GMR\r\n", "OK", 1000);
at_exe_cmd("AT+SYSRAM?\r\n", "OK", 1000);
#endif
}

static esp_err_t at_module_config_init(void)
{
char buffer[AT_BUFFER_ON_STACK_SIZE] = {0};
Expand Down Expand Up @@ -333,6 +341,9 @@ void esp_at_init(void)
at_cmd_set_terminator(CONFIG_AT_COMMAND_TERMINATOR);
#endif

// do some special things before AT is ready
esp_at_ready_before();

esp_at_ready();
ESP_LOGD(TAG, "esp_at_init done");
}
126 changes: 126 additions & 0 deletions components/at/src/at_self_cmd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "sdkconfig.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "esp_at_core.h"
#include "esp_at.h"
#include "esp_at_interface.h"

#ifdef CONFIG_AT_SELF_COMMAND_SUPPORT
#define AT_CMD_RESP_BIT BIT(0)

typedef struct {
EventGroupHandle_t status_bits; /*!< status bits for self command event */
char *cmd; /*!< command string from self command */
char *resp; /*!< expected response from self command */
bool mode; /*!< self command mode */
} at_self_cmd_t;

at_self_cmd_t *s_self_cmd = NULL;
static const char *TAG = "at-self-cmd";

bool at_self_cmd_get_mode(void)
{
if (!s_self_cmd) {
return false;
}
return s_self_cmd->mode;
}

static void at_self_cmd_set_mode(bool mode)
{
s_self_cmd->mode = mode;
}

int32_t at_self_cmd_read_data(uint8_t *buffer, int32_t buffer_len)
{
int32_t len = strlen(s_self_cmd->cmd) < buffer_len ? strlen(s_self_cmd->cmd) : buffer_len;
memcpy(buffer, s_self_cmd->cmd, len);
return len;
}

int32_t at_self_cmd_write_data(uint8_t *data, int32_t len)
{
at_write_data_fn_t write_fn = at_interface_get_write_fn();
int32_t ret = write_fn(data, len);

// check the response
if (strstr((char *)data, s_self_cmd->resp)) {
xEventGroupSetBits(s_self_cmd->status_bits, AT_CMD_RESP_BIT);
}

return ret;
}

int32_t at_self_cmd_get_data_len(void)
{
return strlen(s_self_cmd->cmd);
}

static void at_self_cmd_cleanup(void)
{
if (s_self_cmd) {
if (s_self_cmd->cmd) {
free(s_self_cmd->cmd);
s_self_cmd->cmd = NULL;
}
if (s_self_cmd->resp) {
free(s_self_cmd->resp);
s_self_cmd->resp = NULL;
}
if (s_self_cmd->status_bits) {
vEventGroupDelete(s_self_cmd->status_bits);
s_self_cmd->status_bits = NULL;
}
free(s_self_cmd);
s_self_cmd = NULL;
}
}

esp_err_t at_exe_cmd(const char *cmd, const char *expected_response, uint32_t timeout_ms)
{
esp_err_t ret = ESP_OK;

// init
s_self_cmd = (at_self_cmd_t *)malloc(sizeof(at_self_cmd_t));
if (!s_self_cmd) {
return ESP_ERR_NO_MEM;
}

s_self_cmd->status_bits = xEventGroupCreate();
s_self_cmd->cmd = strdup(cmd);
s_self_cmd->resp = strdup(expected_response);
if (!s_self_cmd->status_bits || !s_self_cmd->cmd || !s_self_cmd->resp) {
at_self_cmd_cleanup();
return ESP_ERR_NO_MEM;
}
at_self_cmd_set_mode(true);

// command notify
esp_at_port_recv_data_notify(strlen(cmd), portMAX_DELAY);

// wait for response
EventBits_t uxBits = xEventGroupWaitBits(s_self_cmd->status_bits, AT_CMD_RESP_BIT, pdFALSE, pdFALSE, timeout_ms / portTICK_PERIOD_MS);
if (!(uxBits & AT_CMD_RESP_BIT)) {
ESP_LOGE(TAG, "<%.*s> cannot get expected response <%s> within %ums", strlen(cmd) - 2, cmd, expected_response, timeout_ms);
ret = ESP_ERR_TIMEOUT;
}

// deinit
at_self_cmd_set_mode(false);
at_self_cmd_cleanup();
return ret;
}
#endif
5 changes: 5 additions & 0 deletions main/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ config AT_COMMAND_TERMINATOR
help
Now just support one character, the range is from 0x01 to 0xFF.

config AT_SELF_COMMAND_SUPPORT
bool "Support for executing AT commands from esp-at itself instead of external MCU."
default n
depends on AT_ENABLE

config AT_BASE_COMMAND_SUPPORT
bool "AT base command support."
default "y"
Expand Down
39 changes: 36 additions & 3 deletions main/interface/at_interface_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
#include "esp_log.h"
#include "esp_at.h"
#include "esp_at_core.h"
#ifdef CONFIG_AT_SELF_COMMAND_SUPPORT
#include "esp_at_self_cmd.h"
#endif

// static variables
static esp_at_device_ops_struct s_interface_ops;
Expand All @@ -21,7 +24,17 @@ static int32_t at_port_read_data(uint8_t *buffer, int32_t len)
if (!s_interface_ops.read_data) {
return -1;
}
int32_t ret = s_interface_ops.read_data(buffer, len);

int32_t ret = 0;

at_read_data_fn_t read_fn = s_interface_ops.read_data;
#ifdef CONFIG_AT_SELF_COMMAND_SUPPORT
if (unlikely(at_self_cmd_get_mode())) {
read_fn = at_self_cmd_read_data;
}
#endif

ret = read_fn(buffer, len);

#if CONFIG_AT_RX_DATA_DEBUG
if (ret > 0) {
Expand All @@ -42,15 +55,30 @@ static int32_t at_port_write_data(uint8_t *data, int32_t len)
ESP_LOG_BUFFER_HEXDUMP("intf-tx", data, at_min(len, CONFIG_AT_TX_DATA_MAX_LEN), ESP_LOG_INFO);
#endif

return s_interface_ops.write_data(data, len);
at_write_data_fn_t write_fn = s_interface_ops.write_data;
#ifdef CONFIG_AT_SELF_COMMAND_SUPPORT
if (unlikely(at_self_cmd_get_mode())) {
write_fn = at_self_cmd_write_data;
}
#endif

return write_fn(data, len);
}

static int32_t at_port_get_data_len(void)
{
if (!s_interface_ops.get_data_length) {
return -1;
}
return s_interface_ops.get_data_length();

at_get_data_len_fn_t get_data_len_fn = s_interface_ops.get_data_length;
#ifdef CONFIG_AT_SELF_COMMAND_SUPPORT
if (unlikely(at_self_cmd_get_mode())) {
get_data_len_fn = at_self_cmd_get_data_len;
}
#endif

return get_data_len_fn();
}

static bool at_port_wait_tx_done(int32_t ms)
Expand All @@ -62,6 +90,11 @@ static bool at_port_wait_tx_done(int32_t ms)
return s_interface_ops.wait_write_complete(ms);
}

at_write_data_fn_t at_interface_get_write_fn(void)
{
return s_interface_ops.write_data;
}

void at_interface_ops_init(esp_at_device_ops_struct *ops)
{
s_interface_ops.read_data = ops->read_data;
Expand Down
8 changes: 8 additions & 0 deletions main/interface/include/esp_at_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,11 @@ void at_interface_ops_init(esp_at_device_ops_struct *ops);
* - not NULL: use the system-hooks and the interface-hooks.
*/
void at_interface_hooks(esp_at_custom_ops_struct *if_hooks);

/**
* @brief This function is used to get the raw write pointer from interface.
*
* @return
* at_write_data_fn_t: The pointer of the raw write function.
*/
at_write_data_fn_t at_interface_get_write_fn(void);

0 comments on commit aad4144

Please sign in to comment.