ESP8266官方AT指令的实现方法

描述

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");
测试时发现有些变量的数值被改变了
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分