0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

ESP8266官方AT指令的實現(xiàn)方法

CHANBAEK ? 來源:小陳學(xué)不停 ? 作者:小陳學(xué)不停 ? 2023-05-17 16:13 ? 次閱讀

1 AT指令
官方的AT固件是不開源的,指令解析和功能實現(xiàn)被封裝成靜態(tài)庫了,這套AT指令可以很方便的控制芯片,滿足一些基本的功能需求,比如AT+MQTT,AT+WEB服務(wù)器等,今天記錄一下如何實現(xiàn)這樣一套AT指令,這套指令完全可以復(fù)用到其他的主控上,復(fù)用到未來的項目上。

2 串口部分
2.1 參數(shù)配置

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 串口任務(wù)

#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指令解析和數(shù)據(jù)透傳

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] == '+')
        { /* 執(zhí)行指令解析 */


            index = at_cmd_search(&p[2], len); 
/* 查找匹配的執(zhí)行指令,0-已匹配,!0-未匹配 */
            if (index >= 0)
            {
                /*查找到相應(yīng)的指令后獲取指令+TEST的長度,根據(jù)長度找到是?還是=*/


                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指令功能實現(xiàn)
    以填空的形式,實現(xiàn)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指令測試
上電后默認(rèn)會返回AT ready
3.1 發(fā)送GMR指令

AT+GMR

返回

SDK version: fe6604a-dirty
AT VERSION: V0.1
Compile time: April 16, 2023 20:28:00
AT OK

3.2 發(fā)送AP指令

AT+AP="ssid=TEST_WIFI&psk=01234567&dhcp=1&"

連接路由器WIFI,名稱為TEST_WIFI,密碼為01234567,使能動態(tài)獲取IP
返回

AT OK

4 字符串處理相關(guān)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: 是否以指定子字符串結(jié)尾
 * @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指令來測試基本的外設(shè)功能,比如指定PIN來操作GPIO、指定ADC來獲取傳感器數(shù)據(jù)、指定SPI來讀取FLASH數(shù)據(jù)等,無論是什么MCU,無論是什么項目都可以通過這樣的AT指令來幫助我們更快地測試硬件。
BUG記錄
esp8266中在使用sprintf時,如果超出了給定數(shù)組的長度,并不會引起崩潰,而是會改變某個變量的數(shù)值。

char test_buf[8];
sprintf(test_buf,"(set=%s)","hello world");
測試時發(fā)現(xiàn)有些變量的數(shù)值被改變了
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • AT
    AT
    +關(guān)注

    關(guān)注

    2

    文章

    191

    瀏覽量

    65124
  • 服務(wù)器
    +關(guān)注

    關(guān)注

    12

    文章

    8957

    瀏覽量

    85080
  • 指令
    +關(guān)注

    關(guān)注

    1

    文章

    606

    瀏覽量

    35617
  • ESP8266
    +關(guān)注

    關(guān)注

    50

    文章

    962

    瀏覽量

    44753
  • MQTT
    +關(guān)注

    關(guān)注

    5

    文章

    647

    瀏覽量

    22392
收藏 人收藏

    評論

    相關(guān)推薦

    ESP8266 WIFIAT指令集_v0.1

    ESP8266 WIFIAT指令集_v0.1,Espressif AT 指令
    發(fā)表于 12-29 14:16 ?36次下載

    ESP8266_AT指令使用示例_Version 1.3

    ESP8266__AT Command Examples,AT 指令使用示例
    發(fā)表于 12-30 14:17 ?41次下載

    0B-ESP8266官方詳細(xì)介紹手冊

    ESP8266官方詳盡系統(tǒng)描述,中文版?。。。?!
    發(fā)表于 02-22 15:37 ?39次下載

    ESP8266模塊官方使用指導(dǎo)

    無線WiFi ESP8266模塊主導(dǎo)文件,內(nèi)容包括命令指導(dǎo),使用測試、實例和使用方法
    發(fā)表于 06-08 17:52 ?233次下載

    使用esp8266實現(xiàn)STM32聯(lián)網(wǎng)(最簡單USART方法

    到電腦上的java程序 這一篇 esp8266與STM32連接,電腦通過STM32配置esp8266實現(xiàn)聯(lián)網(wǎng)發(fā)送數(shù)據(jù)具體流程如下圖 2= esp8266怎么和STM32連接(引腳連
    發(fā)表于 11-22 11:51 ?1.2w次閱讀

    esp8266 at指令集詳解

    ESP8266可廣泛應(yīng)用于智能電網(wǎng)、智能交通、智能家具、手持設(shè)備、工業(yè)控制等領(lǐng)域。本文介紹了esp8266 at指令集,以及ESP8266使用AT
    發(fā)表于 12-08 13:52 ?14.3w次閱讀
    <b class='flag-5'>esp8266</b> at<b class='flag-5'>指令</b>集詳解

    ESP8266-WIFI-SmartPlug官方例程使用教程

    ESP8266-WIFI-SmartPlug官方例程使用教程
    發(fā)表于 01-25 10:19 ?12次下載

    ESP8266的AT指令使用示例資料免費下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是ESP8266的AT指令使用示例資料免費下載。
    發(fā)表于 03-18 08:00 ?10次下載
    <b class='flag-5'>ESP8266</b>的AT<b class='flag-5'>指令</b>使用示例資料免費下載

    ESP8266 AT指令文檔的詳細(xì)資料說明

    本文檔主要內(nèi)容詳細(xì)介紹的是ESP8266 AT 指令文檔的詳細(xì)資料說明。
    發(fā)表于 04-19 16:39 ?46次下載
    <b class='flag-5'>ESP8266</b> AT<b class='flag-5'>指令</b>文檔的詳細(xì)資料說明

    ESP8266的AT指令集詳細(xì)說明

    本文檔的主要內(nèi)容詳細(xì)介紹的是ESP8266的AT指令集詳細(xì)說明。
    發(fā)表于 12-26 16:00 ?27次下載
    <b class='flag-5'>ESP8266</b>的AT<b class='flag-5'>指令</b>集詳細(xì)說明

    esp8266AT指令封裝

    esp8266AT指令封裝
    發(fā)表于 11-23 18:21 ?8次下載
    <b class='flag-5'>esp8266</b>AT<b class='flag-5'>指令</b>封裝

    ESP8266學(xué)習(xí)之路——環(huán)境部署

    文章目錄前言ESP8266SDK前言ESP8266SDK官方鏈接
    發(fā)表于 12-22 18:47 ?28次下載
    <b class='flag-5'>ESP8266</b>學(xué)習(xí)之路——環(huán)境部署

    ESP8266 wifi模塊指令集pdf

    ESP8266 wifi模塊指令集pdf
    發(fā)表于 01-06 13:51 ?28次下載

    ESP8266 AT指令設(shè)置中文手冊

    ESP8266 AT指令設(shè)置中文手冊免費下載。
    發(fā)表于 04-24 09:30 ?72次下載

    ESP8266初次如何實現(xiàn)無線通信(基于電腦與ESP8266)

    一.需要的軟件及硬件 1.軟件 2.硬件 二.ESP8266的使用 .ESP8266的介紹 2.ESP8266進行初始AT指令的配置步驟 三.所用軟件安裝分享
    發(fā)表于 05-30 09:53 ?28次下載
    <b class='flag-5'>ESP8266</b>初次如何<b class='flag-5'>實現(xiàn)</b>無線通信(基于電腦與<b class='flag-5'>ESP8266</b>)