1 AT指令
官方的AT固件是不开源的,指令解析和功能实现被封装成静态库了,这套AT指令可以很方便的控制芯片,满足一些基本的功能需求,比如AT+MQTT,AT+WEB服务器等,今天记录一下如何实现这样一套AT指令,这套指令完全可以复用到其他的主控上,复用到未来的项目上。
2 串口部分
2.1 参数配置
uart_config_t g_uart_config =
{
.baud_rate = CONFIG_BAUD_UART_DEFAULT,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
};
uart_param_config(EX_UART_NUM, &g_uart_config);
2.2 串口任务
#define RD_BUF_SIZE (2048)
static QueueHandle_t uart0_queue;
xTaskCreate(uart_event_task, "uart_event_task", 2048, &transport_config, 15, NULL);
2.3 接收中断处理
static void uart_rx_intr_handler_default(void *param)
{
uart_obj_t *p_uart = (uart_obj_t *) param;
uint8_t uart_num = p_uart->uart_num;
uart_dev_t *uart_reg = UART[uart_num];
int rx_fifo_len = uart_reg->status.rxfifo_cnt;
uint8_t buf_idx = 0;
uint32_t uart_intr_status = UART[uart_num]->int_st.val;
uart_event_t uart_event;
BaseType_t task_woken = 0;
while (uart_intr_status != 0x0) {
uart_select_notif_t notify = UART_SELECT_ERROR_NOTIF;
buf_idx = 0;
uart_event.type = UART_EVENT_MAX;
if (uart_intr_status & UART_TXFIFO_EMPTY_INT_ST_M) {
uart_clear_intr_status(uart_num, UART_TXFIFO_EMPTY_INT_CLR_M);
uart_disable_intr_mask(uart_num, UART_TXFIFO_EMPTY_INT_ENA_M);
// TX semaphore will only be used when tx_buf_size is zero.
if (p_uart->tx_waiting_fifo == true && p_uart->tx_buf_size == 0) {
p_uart->tx_waiting_fifo = false;
xSemaphoreGiveFromISR(p_uart->tx_fifo_sem, &task_woken);
if (task_woken == pdTRUE) {
portYIELD_FROM_ISR();
}
} else {
// We don't use TX ring buffer, because the size is zero.
if (p_uart->tx_buf_size == 0) {
continue;
}
int tx_fifo_rem = UART_FIFO_LEN - UART[uart_num]->status.txfifo_cnt;
bool en_tx_flg = false;
// We need to put a loop here, in case all the buffer items are very short.
// That would cause a watch_dog reset because empty interrupt happens so often.
// Although this is a loop in ISR, this loop will execute at most 128 turns.
while (tx_fifo_rem) {
if (p_uart->tx_len_tot == 0 || p_uart->tx_ptr == NULL || p_uart->tx_len_cur == 0) {
size_t size;
p_uart->tx_head = (uart_tx_data_t *) xRingbufferReceiveFromISR(p_uart->tx_ring_buf, &size);
if (p_uart->tx_head) {
// The first item is the data description
// Get the first item to get the data information
if (p_uart->tx_len_tot == 0) {
p_uart->tx_ptr = NULL;
p_uart->tx_len_tot = p_uart->tx_head->tx_data.size;
// We have saved the data description from the 1st item, return buffer.
vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &task_woken);
if (task_woken == pdTRUE) {
portYIELD_FROM_ISR();
}
} else if (p_uart->tx_ptr == NULL) {
// Update the TX item pointer, we will need this to return item to buffer.
p_uart->tx_ptr = (uint8_t *) p_uart->tx_head;
en_tx_flg = true;
p_uart->tx_len_cur = size;
}
} else {
// Can not get data from ring buffer, return;
break;
}
}
if (p_uart->tx_len_tot > 0 && p_uart->tx_ptr && p_uart->tx_len_cur > 0) {
// To fill the TX FIFO.
int send_len = p_uart->tx_len_cur > tx_fifo_rem ? tx_fifo_rem : p_uart->tx_len_cur;
for (buf_idx = 0; buf_idx < send_len; buf_idx++) {
UART[uart_num]->fifo.rw_byte = *(p_uart->tx_ptr++) & 0xff;
}
p_uart->tx_len_tot -= send_len;
p_uart->tx_len_cur -= send_len;
tx_fifo_rem -= send_len;
if (p_uart->tx_len_cur == 0) {
// Return item to ring buffer.
vRingbufferReturnItemFromISR(p_uart->tx_ring_buf, p_uart->tx_head, &task_woken);
if (task_woken == pdTRUE) {
portYIELD_FROM_ISR();
}
p_uart->tx_head = NULL;
p_uart->tx_ptr = NULL;
}
if (p_uart->tx_len_tot == 0) {
if (tx_fifo_rem == 0) {
en_tx_flg = true;
} else{
en_tx_flg = false;
}
xSemaphoreGiveFromISR(p_uart->tx_done_sem, &task_woken);
if (task_woken == pdTRUE) {
portYIELD_FROM_ISR();
}
} else {
en_tx_flg = true;
}
}
}
if (en_tx_flg) {
uart_clear_intr_status(uart_num, UART_TXFIFO_EMPTY_INT_CLR_M);
uart_enable_intr_mask(uart_num, UART_TXFIFO_EMPTY_INT_ENA_M);
}
}
} else if ((uart_intr_status & UART_RXFIFO_TOUT_INT_ST_M)
|| (uart_intr_status & UART_RXFIFO_FULL_INT_ST_M)
) {
rx_fifo_len = uart_reg->status.rxfifo_cnt;
if (p_uart->rx_buffer_full_flg == false) {
// We have to read out all data in RX FIFO to clear the interrupt signal
while (buf_idx < rx_fifo_len) {
p_uart->rx_data_buf[buf_idx++] = uart_reg->fifo.rw_byte;
}
// Get the buffer from the FIFO
// After Copying the Data From FIFO ,Clear intr_status
uart_clear_intr_status(uart_num, UART_RXFIFO_TOUT_INT_CLR_M | UART_RXFIFO_FULL_INT_CLR_M);
uart_event.type = UART_DATA;
uart_event.size = rx_fifo_len;
p_uart->rx_stash_len = rx_fifo_len;
// If we fail to push data to ring buffer, we will have to stash the data, and send next time.
// Mainly for applications that uses flow control or small ring buffer.
if (pdFALSE == xRingbufferSendFromISR(p_uart->rx_ring_buf, p_uart->rx_data_buf, p_uart->rx_stash_len, &task_woken)) {
uart_disable_intr_mask(uart_num, UART_RXFIFO_TOUT_INT_ENA_M | UART_RXFIFO_FULL_INT_ENA_M);
uart_event.type = UART_BUFFER_FULL;
p_uart->rx_buffer_full_flg = true;
} else {
p_uart->rx_buffered_len += p_uart->rx_stash_len;
}
notify = UART_SELECT_READ_NOTIF;
if (task_woken == pdTRUE) {
portYIELD_FROM_ISR();
}
} else {
uart_disable_intr_mask(uart_num, UART_RXFIFO_FULL_INT_ENA_M | UART_RXFIFO_TOUT_INT_ENA_M);
uart_clear_intr_status(uart_num, UART_RXFIFO_FULL_INT_CLR_M | UART_RXFIFO_TOUT_INT_CLR_M);
}
} else if (uart_intr_status & UART_RXFIFO_OVF_INT_ST_M) {
// When fifo overflows, we reset the fifo.
uart_reset_rx_fifo(uart_num);
uart_reg->int_clr.rxfifo_ovf = 1;
uart_event.type = UART_FIFO_OVF;
notify = UART_SELECT_ERROR_NOTIF;
} else if (uart_intr_status & UART_FRM_ERR_INT_ST_M) {
uart_reg->int_clr.frm_err = 1;
uart_event.type = UART_FRAME_ERR;
notify = UART_SELECT_ERROR_NOTIF;
} else if (uart_intr_status & UART_PARITY_ERR_INT_ST_M) {
uart_reg->int_clr.parity_err = 1;
uart_event.type = UART_PARITY_ERR;
notify = UART_SELECT_ERROR_NOTIF;
} else {
uart_reg->int_clr.val = uart_intr_status; // simply clear all other intr status
uart_event.type = UART_EVENT_MAX;
notify = UART_SELECT_ERROR_NOTIF;
}
#ifdef CONFIG_USING_ESP_VFS
if (uart_event.type != UART_EVENT_MAX && p_uart->uart_select_notif_callback) {
p_uart->uart_select_notif_callback(uart_num, notify, &task_woken);
if (task_woken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
#else
(void)notify;
#endif
if (uart_event.type != UART_EVENT_MAX && p_uart->xQueueUart) {
if (pdFALSE == xQueueSendFromISR(p_uart->xQueueUart, (void *)&uart_event, &task_woken)) {
ESP_EARLY_LOGV(UART_TAG, "UART event queue full");
}
if (task_woken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
uart_intr_status = uart_reg->int_st.val;
}
}
2.4 AT指令解析和数据透传
void uart_event_task(void *pvParameters)
{
uart_event_t event;
uart_driver_install(EX_UART_NUM, 2048, 2048, 100, &uart0_queue, 0);
if (pvParameters)
{
get_config = pvParameters;
}
/*at command say hello */
uart_write_bytes(EX_UART_NUM, (const char *) CONFIG_AT_HELLO, strlen(CONFIG_AT_HELLO));
uint8_t *dtmp = (uint8_t *) malloc(RD_BUF_SIZE);
for (;;)
{
// Waiting for UART event.
if (xQueueReceive(uart0_queue, (void *)&event, (portTickType)portMAX_DELAY))
{
bzero(dtmp, RD_BUF_SIZE);
switch (event.type)
{
// Event of UART receving data
// We'd better handler data event fast, there would be much more data events than
// other types of events. If we take too much time on data event, the queue might be full.
case UART_DATA:
uart_read_bytes(EX_UART_NUM, dtmp, event.size, portMAX_DELAY);
if (event.size > 0)
{
if((dtmp[0] == 'A') && (dtmp[1] == 'T') && (dtmp[event.size-2] == 0x0D) && (dtmp[event.size-1] == 0x0A))
{
uint8_t m = mstrlen((char *)dtmp);
uint8_t ret_parse = at_cmd_parse(dtmp, m);
if ((ESP_AT_RESULT_CODE_OK == ret_parse)
|| (ESP_AT_RESULT_CODE_SEND_OK == ret_parse))
{
esp_at_response_result(ESP_AT_RESULT_CODE_OK);
}
else if ((ESP_AT_RESULT_CODE_ERROR == ret_parse)
|| (ESP_AT_RESULT_CODE_SEND_FAIL == ret_parse)
|| (ESP_AT_RESULT_CODE_FAIL == ret_parse))
{
ESP_LOGI(TAG, "error parse =[%d]",ret_parse);
esp_at_response_result(ESP_AT_RESULT_CODE_ERROR);
}
}
else
{
//透传代码
...
}
// Note: Only one character was read even the buffer contains more. The other characters will
// be read one-by-one by subsequent calls to select() which will then return immediately
// without timeout.
}
break;
// Event of HW FIFO overflow detected
case UART_FIFO_OVF:
ESP_LOGI(TAG, "hw fifo overflow");
// If fifo overflow happened, you should consider adding flow control for your application.
// The ISR has already reset the rx FIFO,
// As an example, we directly flush the rx buffer here in order to read more data.
uart_flush_input(EX_UART_NUM);
xQueueReset(uart0_queue);
break;
// Event of UART ring buffer full
case UART_BUFFER_FULL:
ESP_LOGI(TAG, "ring buffer full");
// If buffer full happened, you should consider encreasing your buffer size
// As an example, we directly flush the rx buffer here in order to read more data.
uart_flush_input(EX_UART_NUM);
xQueueReset(uart0_queue);
break;
case UART_PARITY_ERR:
ESP_LOGI(TAG, "uart parity error");
break;
// Event of UART frame error
case UART_FRAME_ERR:
ESP_LOGI(TAG, "uart frame error");
break;
// Others
default:
ESP_LOGI(TAG, "uart event type: %d", event.type);
break;
}
}
}
free(dtmp);
dtmp = NULL;
vTaskDelete(NULL);
}
- AT指令解析
const esp_at_cmd_struct at_cmd_func[] =
{
{"+RST",NULL,NULL,at_cmd_reset},
{"+GMR",NULL,NULL,at_cmd_version},
{"+RESTORE",NULL,NULL,at_cmd_restore},
{"+EN",at_get_wifien,at_config_wifien},
{"+UART_DEF",at_queryCmdUartDef,at_setupCmdUartDef},
{"+MODE",at_get_workmode,at_config_workmode},
{"+AP",at_get_apinfo,at_config_ap},
};
int16_t at_cmd_search(unsigned char *p, unsigned char len)
{
int16_t ret = -1;
unsigned char *pstr;
unsigned char i, n;
for (i=0; i<LENGTH_OF_ARRAY(at_cmd_func); i++)
{
n = mstrlen(at_cmd_func[i].at_name);
uint8_t get_res = memcmp((const char *)p, (const char *)at_cmd_func[i].at_name, n);
if(!get_res)
{
ret = i;
break;
}
}
return ret;
}
uint8_t at_cmd_parse(uint8_t *p, uint8_t len)
{
uint8_t ret = ESP_AT_RESULT_CODE_ERROR;
int16_t index = -1;
if(len < 4)
{
return ESP_AT_RESULT_CODE_ERROR; /* 不符合指令最小长度 */
}
if ((p[0] == 'A') && (p[1] == 'T')
&& (p[len-2] == 0x0D) && (p[len-1] == 0x0A))
{
if (p[2] == '+')
{ /* 执行指令解析 */
index = at_cmd_search(&p[2], len);
/* 查找匹配的执行指令,0-已匹配,!0-未匹配 */
if (index >= 0)
{
/*查找到相应的指令后获取指令+TEST的长度,根据长度找到是?还是=*/
char *get_name = at_cmd_func[index].at_name;
if (str_is_notblank(get_name))
{
uint8_t n = mstrlen(get_name);
int8_t get_type = p[2+n];
if (get_type == '=')
{
if (at_cmd_func[index].at_set)
{
int8_t common_parameter_buffer[CONFIG_MAX_LEN_PARAMETER]={0};
sprintf((char *)common_parameter_buffer,"%s",&p[3+n]);
memset(revbuf,0,LENGTH_OF_ARRAY(revbuf));
uint8_t get_para_num = split((char *)common_parameter_buffer,",",revbuf);
if (get_para_num)
{
ret = at_cmd_func[index].at_set(get_para_num);
}
else
{
uint8_t get_para_num = split((char *)common_parameter_buffer,"&",revbuf);
ret = at_cmd_func[index].at_set(get_para_num);
}
}
}
else if (get_type == '?')
{
if (at_cmd_func[index].at_get)
{
ret = at_cmd_func[index].at_get(get_name);
}
}
else if (get_type == '\\r')
{
if (at_cmd_func[index].at_exe)
{
ret = at_cmd_func[index].at_exe(get_name);
}
}
else
{
ESP_LOGI(TAG, "undefine type=%02x\\r\\n",get_type);
}
}
}
else
{
ret = ESP_AT_RESULT_CODE_FAIL; /* 未找到匹配的指令 */
}
}
}
else
{/* 格式不匹配 */
return ESP_AT_RESULT_CODE_ERROR;
}
return ret;
}
- AT指令功能实现
以填空的形式,实现AT指令
typedef struct
{
char *at_name; /*!< at command name */
uint8_t (*at_get)(char *cmd_name); /*!< Query Command function pointer */
uint8_t (*at_set)(uint8_t para_num); /*!< Setup Command function pointer */
uint8_t (*at_exe)(char *cmd_name);
} esp_at_cmd_struct;
以AP和GMR指令为例,at_name为”+AP”,完整指令是AT+AP+回车字符,at_get为at_get_apinfo,at_set为at_config_ap
/* AT指令表 */
const esp_at_cmd_struct at_cmd_func[] =
{
{"+GMR",NULL,NULL,at_cmd_version},
{"+AP",at_get_apinfo,at_config_ap},
};
uint8_t at_cmd_version(char *cmd_name)
{
uint8_t buffer_tx[64] = {0};
snprintf((char *)buffer_tx, 64, "SDK version: %s\\r\\n", esp_get_idf_version());
esp_at_port_write_data(buffer_tx, mstrlen((char *)buffer_tx));
snprintf((char *)buffer_tx, 64, "AT VERSION: %s\\n", CONFIG_VERSION_AT);
esp_at_port_write_data(buffer_tx, mstrlen((char *)buffer_tx));
snprintf((char *)buffer_tx, 64, "Compile time: %s %s\\n", __DATE__, __TIME__);
esp_at_port_write_data(buffer_tx, mstrlen((char *)buffer_tx));
return ESP_AT_RESULT_CODE_OK;
}
static uint8_t at_get_apinfo(char *cmd_name)
{
uint8_t buffer[128];
snprintf((char*)buffer,sizeof(buffer) - 1,"%s:ssid=%s&psk=%s&enc=%d&hide=%d&ip=%s&gw=%s&masknet=%s&\\r\\n",
cmd_name,g_ap_config.ap.ssid,g_ap_config.ap.password,g_ap_config.ap.authmode,g_ap_config.ap.ssid_hidden,
set_ip_ap,set_gateway_ap,set_netmask_ap);
esp_at_port_write_data(buffer,mstrlen((char*)buffer));
return ESP_AT_RESULT_CODE_OK;
}
char param_info[128]={0};
static uint8_t at_config_ap(uint8_t para_num)
{
uint8_t buf_len = 0;
uint8_t find_parameters = 0;
uint8_t find_different = 0;
if (1 != para_num)
{
return ESP_AT_RESULT_CODE_ERROR;
}
if (!str_is_notblank(revbuf[0]))
{
return ESP_AT_RESULT_CODE_ERROR;
}
ESP_LOGI(TAG, "{%s}\\n",revbuf[0]);
buf_len = strlen(revbuf[0]);
if (buf_len < (8+2))
{
ESP_LOGI(TAG, "{%d}\\n",buf_len);
return ESP_AT_RESULT_CODE_ERROR;
}
char* new_buf = revbuf[0];
if (!new_buf)
{
return ESP_AT_RESULT_CODE_ERROR;
}
if ((new_buf[0] != '\"') || (new_buf[buf_len-1-2] != '\"'))
{
return ESP_AT_RESULT_CODE_ERROR;
}
if (new_buf[buf_len-1-2-1] != '&')
{
return ESP_AT_RESULT_CODE_ERROR;
}
new_buf += 1;
if (httpd_query_key_value(new_buf, "ssid", param_info, sizeof(param_info)) == ESP_OK)
{
//check ssid
if (is_valid_wifi_ssid(param_info))
{
uint8_t check_len = strlen(param_info);
if (memcmp(g_ap_config.ap.ssid,(uint8_t *)param_info,check_len)
|| (check_len != strlen((char *)g_ap_config.ap.ssid)))
{
sprintf((char *)g_ap_config.ap.ssid,"%s",param_info);
user_nvs_setkey("ap_name",param_info);
find_different++;
}
find_parameters++;
}
}
if (httpd_query_key_value(new_buf, "psk", param_info, sizeof(param_info)) == ESP_OK)
{
//check password
if (is_valid_wifi_password(param_info))
{
uint8_t check_len = strlen(param_info);
if (memcmp(g_ap_config.ap.password,(uint8_t *)param_info,check_len)
|| (check_len != strlen((char *)g_ap_config.ap.password)))
{
sprintf((char *)g_ap_config.ap.password,"%s",param_info);
user_nvs_setkey("ap_paswd",param_info);
find_different++;
}
find_parameters++;
}
}
if (httpd_query_key_value(new_buf, "enc", param_info, sizeof(param_info)) == ESP_OK)
{
//check enc
if (1 == strlen(param_info))
{
uint8_t get_val = (uint8_t)atoi(param_info);
if (is_valid_enc(get_val))
{
if (g_ap_config.ap.authmode != get_val)
{
g_ap_config.ap.authmode = get_val;
user_nvs_setkey("ap_enc_mode",param_info);
find_different++;
}
find_parameters++;
}
}
}
if (httpd_query_key_value(new_buf, "hide", param_info, sizeof(param_info)) == ESP_OK)
{
//check hide
if (1 == strlen(param_info))
{
uint8_t get_val = (uint8_t)atoi(param_info);
if (get_val<=1)
{
if (g_ap_config.ap.ssid_hidden != get_val)
{
g_ap_config.ap.ssid_hidden = get_val;
user_nvs_setkey("ap_hide",param_info);
find_different++;
}
find_parameters++;
}
}
}
if (httpd_query_key_value(new_buf, "ip", param_info, sizeof(param_info)) == ESP_OK)
{
//check ip
if (is_valid_ip(param_info))
{
uint8_t check_len = strlen(param_info);
if (memcmp((uint8_t *)set_ip_ap,(uint8_t *)param_info,LENGTH_OF_ARRAY(set_ip_ap))
|| (check_len != strlen(set_ip_ap)))
{
sprintf(set_ip_ap,"%s",param_info);
user_nvs_setkey("ip_ap",param_info);
find_different++;
}
find_parameters++;
}
}
if (httpd_query_key_value(new_buf, "gw", param_info, sizeof(param_info)) == ESP_OK)
{
//check gataway
if (is_valid_ip(param_info))
{
uint8_t check_len = strlen(param_info);
if (memcmp((uint8_t *)set_gateway_ap,(uint8_t *)param_info,LENGTH_OF_ARRAY(set_gateway_ap))
|| (check_len != strlen(set_gateway_ap)))
{
sprintf(set_gateway_ap,"%s",param_info);
user_nvs_setkey("gateway_ap",param_info);
find_different++;
}
find_parameters++;
}
}
if (httpd_query_key_value(new_buf, "masknet", param_info, sizeof(param_info)) == ESP_OK)
{
//check netmask
if (is_valid_ip(param_info))
{
uint8_t check_len = strlen(param_info);
if (memcmp((uint8_t *)set_netmask_ap,(uint8_t *)param_info,LENGTH_OF_ARRAY(set_netmask_ap))
|| (check_len != strlen(set_netmask_ap)))
{
sprintf(set_netmask_ap,"%s",param_info);
user_nvs_setkey("netmask_ap",param_info);
find_different++;
}
find_parameters++;
}
}
if (find_parameters)
{
user_nvs_close();
if (find_different)
{
//reconfig ap wifi info
notify_reset_task(0);
g_reconfig_ip(WT_AP);
at_wifi_reconnect(g_set_work_mode);
uint8_t send_work_mode = g_set_work_mode;
if (WIFI_MODE_STA != g_set_work_mode)
{
send_work_mode = 0;
}
update_json_str(send_work_mode);
}
return ESP_AT_RESULT_CODE_OK;
}
return ESP_AT_RESULT_CODE_ERROR;
}
/* Helper function to get a URL query tag from a query string of the type param1=val1¶m2=val2 */
esp_err_t httpd_query_key_value(const char *qry_str, const char *key, char *val, size_t val_size)
{
if (qry_str == NULL || key == NULL || val == NULL) {
return ESP_ERR_INVALID_ARG;
}
const char *qry_ptr = qry_str;
const size_t buf_len = val_size;
while (strlen(qry_ptr)) {
/* Search for the '=' character. Else, it would mean
* that the parameter is invalid */
const char *val_ptr = strchr(qry_ptr, '=');
if (!val_ptr) {
break;
}
size_t offset = val_ptr - qry_ptr;
/* If the key, does not match, continue searching.
* Compare lengths first as key from url is not
* null terminated (has '=' in the end) */
if ((offset != strlen(key)) ||
(strncasecmp(qry_ptr, key, offset))) {
/* Get the name=val string. Multiple name=value pairs
* are separated by '&' */
qry_ptr = strchr(val_ptr, '&');
if (!qry_ptr) {
break;
}
qry_ptr++;
continue;
}
/* Locate start of next query */
qry_ptr = strchr(++val_ptr, '&');
/* Or this could be the last query, in which
* case get to the end of query string */
if (!qry_ptr) {
qry_ptr = val_ptr + strlen(val_ptr);
}
/* Update value length, including one byte for null */
val_size = qry_ptr - val_ptr + 1;
/* Copy value to the caller's buffer. */
strlcpy(val, val_ptr, MIN(val_size, buf_len));
/* If buffer length is smaller than needed, return truncation error */
if (buf_len < val_size) {
return ESP_ERR_HTTPD_RESULT_TRUNC;
}
return ESP_OK;
}
ESP_LOGD(TAG, LOG_FMT("key %s not found"), key);
return ESP_ERR_NOT_FOUND;
}
3 AT指令测试
上电后默认会返回AT ready
3.1 发送GMR指令
AT+GMR
返回
SDK version: fe6604a-dirty
AT VERSION: V0.1
Compile time: April 16, 2023 20:28:00
AT OK
3.2 发送AP指令
AT+AP="ssid=TEST_WIFI&psk=01234567&dhcp=1&"
连接路由器WIFI,名称为TEST_WIFI,密码为01234567,使能动态获取IP
返回
AT OK
4 字符串处理相关API
uint8_t str_is_notblank(char *p_str)
{
if (!p_str)
{
return 0;
}
return (mstrlen(p_str)?1:0);
}
#include "string.h"
static uint8_t str_start_with(char* src, char* str)
{
if (strlen(src) < strlen(str)) {
return false;
}
for (int i = 0; i < strlen(str); i++) {
if (src[i] != str[i]) {
return false;
}
}
return true;
}
/**
* @description: 是否以指定子字符串结尾
* @param {src} 待比较的字符串
* @param {str} 指定的子字符串
* @return {*} true/false
*/
static uint8_t str_end_with(char* src, char* str)
{
if (strlen(src) < strlen(str)) {
return false;
}
char* ptr = src+(strlen(src)-strlen(str));
for (int i = 0; i < strlen(str); i++) {
if (ptr[i] != str[i]) {
return false;
}
}
return true;
}
uint8_t split(char *src,const char *separator,char **dest)
{
char *pNext;
uint8_t get_cnt = 0;
if (src == 0 || mstrlen(src) == 0)
return;
if (separator == 0 || mstrlen(separator) == 0)
return;
pNext = (char *)strtok(src,separator);
while(pNext != 0)
{
*dest++ = pNext;
pNext = (char *)strtok(0,separator);
get_cnt ++;
}
return get_cnt;
}
5 跨平台通用AT指令
在新的硬件回来之后,可以使用AT指令来测试基本的外设功能,比如指定PIN来操作GPIO、指定ADC来获取传感器数据、指定SPI来读取FLASH数据等,无论是什么MCU,无论是什么项目都可以通过这样的AT指令来帮助我们更快地测试硬件。
BUG记录
esp8266中在使用sprintf时,如果超出了给定数组的长度,并不会引起崩溃,而是会改变某个变量的数值。
char test_buf[8];
sprintf(test_buf,"(set=%s)","hello world");
测试时发现有些变量的数值被改变了
-
AT
+关注
关注
2文章
192浏览量
65205 -
服务器
+关注
关注
12文章
9138浏览量
85371 -
指令
+关注
关注
1文章
607浏览量
35698 -
ESP8266
+关注
关注
50文章
962浏览量
44977 -
MQTT
+关注
关注
5文章
650浏览量
22492
发布评论请先 登录
相关推荐
评论