前言

大家好,博主也是第一次发文章。记录一下学习过程以及思考,希望帮到有需要的人,也希望各位能一起讨论学习。

实现思路

这里我是用vscode的espidf插件来开发,通过smartconfig配网(通过ESPTouch广播配网),https发送接收内容,收到的数据为json格式通过cjson来解析出大模型的回复。发送数据是通过post方法,数据体也是json格式也用到cjson来构建。发送数据的内容的话,这里我用的是串口取接收数据。

代码

这里我都是参考官网的实例程序改过来的,所以大部分你可以在espidf例程中找到对应的。
主程序:


#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_eap_client.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_smartconfig.h"
#include "esp_mac.h"
#include "esp_http_client.h"
#include "cJSON.h"
#include "../components/myuart/include/myuart.h"

#define AI_API_URL "your_api_url" //模型POST请求URL
#define AI_API_KEY "your_api_key" //模型秘钥
#define MODEL_NAME "model_name"//模型名字
#define max_size 4096
#define HTTPS_AI_DEBUG 0
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t s_wifi_event_group;

/* The event group allows multiple bits for each event,
   but we only care about one event - are we connected
   to the AP with an IP? */
static const int CONNECTED_BIT = BIT0;
static const int ESPTOUCH_DONE_BIT = BIT1;
static const char *TAG = "attch_ai";
static const char *TAG_http = "http_fun";
static void smartconfig_task(void * parm);

static void event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        xTaskCreate(smartconfig_task, "smartconfig_task", 4096, NULL, 3, NULL);
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        esp_wifi_connect();
        xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {
        ESP_LOGI(TAG, "Scan done");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {
        ESP_LOGI(TAG, "Found channel");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) {
        ESP_LOGI(TAG, "Got SSID and password");

        smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
        wifi_config_t wifi_config;
        uint8_t ssid[33] = { 0 };
        uint8_t password[65] = { 0 };
        uint8_t rvd_data[33] = { 0 };

        bzero(&wifi_config, sizeof(wifi_config_t));
        memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
        memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));

#ifdef CONFIG_SET_MAC_ADDRESS_OF_TARGET_AP
        wifi_config.sta.bssid_set = evt->bssid_set;
        if (wifi_config.sta.bssid_set == true) {
            ESP_LOGI(TAG, "Set MAC address of target AP: "MACSTR" ", MAC2STR(evt->bssid));
            memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
        }
#endif

        memcpy(ssid, evt->ssid, sizeof(evt->ssid));
        memcpy(password, evt->password, sizeof(evt->password));
        ESP_LOGI(TAG, "SSID:%s", ssid);
        ESP_LOGI(TAG, "PASSWORD:%s", password);
        if (evt->type == SC_TYPE_ESPTOUCH_V2) {
            ESP_ERROR_CHECK( esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)) );
            ESP_LOGI(TAG, "RVD_DATA:");
            for (int i=0; i<33; i++) {
                printf("%02x ", rvd_data[i]);
            }
            printf("\n");
        }

        ESP_ERROR_CHECK( esp_wifi_disconnect() );
        ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
        esp_wifi_connect();
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
        xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
    }
}

esp_err_t http_event_handler(esp_http_client_event_t *evt)
{   

    esp_err_t err=ESP_OK;
    static uint32_t output_len=0;     
    static char* output_buffer=NULL; 

    switch (evt->event_id)
    {
    case HTTP_EVENT_ERROR :
        ESP_LOGI(TAG_http,"HTTP_EVENT_ERROR!");
        break;
    case HTTP_EVENT_ON_CONNECTED:
        ESP_LOGI(TAG_http,"HTTP_EVENT_ON_CONNECTED");
        break;
    case HTTP_EVENT_HEADER_SENT:
        ESP_LOGI(TAG_http,"HTTP_EVENT_HEADER_SENT");
        break;
    case HTTP_EVENT_ON_HEADER ://获取报文头事件,通过这个获取后面数据的大小
        if(!strcmp(evt->header_key,"content-length")){
            u32_t total_num = atoi(evt->header_value);
            ESP_LOGI(TAG_http,"fond content-length :%s",evt->header_value);
            output_buffer = (char*)malloc(sizeof(char)*total_num+1);
            if(output_buffer==NULL)
            {
                ESP_LOGI(TAG_http,"fail to create buff!");
            }
             ESP_LOGI(TAG_http,"total num is %ld",total_num);
        }
        ESP_LOGI(TAG_http,"HTTP_EVENT_ON_HEADER key:%s value:%s",evt->header_key,evt->header_value);
        break;   
    case HTTP_EVENT_ON_DATA://获取报文数据事件,我选的非流式的但是运行的时候还是要多次进入这个事件,我也不太明白了。所以这里有事件就要复制一次更新末尾的位置
        ESP_LOGI(TAG_http,"HTTP_EVENT_ON_DATA len=%d",evt->data_len);
        if(!esp_http_client_is_chunked_response(evt->client) && output_buffer)
        {
            memcpy(output_buffer+output_len,(char*)evt->data,evt->data_len);
            output_len+=evt->data_len;
    #if HTTPS_AI_DEBUG
            ESP_LOGI(TAG_http,"revcie:%s len:%d",(char*)evt->data,evt->data_len);
    #endif        
            
        }
        break;
    case HTTP_EVENT_ON_FINISH://完成报文接收事件,这里处理数据
        ESP_LOGI(TAG_http,"HTTP_EVENT_ON_FINISH");
             if(!esp_http_client_is_chunked_response(evt->client) && output_buffer)
        {
            output_buffer[output_len]='\0';
    #if HTTPS_AI_DEBUG
            printf("buff :%s\r\n",output_buffer);
    #endif     
             cJSON*root = cJSON_Parse(output_buffer);
             free(output_buffer);
             output_len = 0;
            if(root!=NULL)
            {
                cJSON*choices = cJSON_GetObjectItem(root,"choices");
                if(cJSON_IsArray(choices) && (cJSON_GetArraySize(choices)>0))
                {
                    cJSON*choices_1 = cJSON_GetArrayItem(choices,0);
                    cJSON*message = cJSON_GetObjectItem(choices_1,"message");
                    cJSON*content  = cJSON_GetObjectItem(message,"content");
                    if(cJSON_IsString(content)&&content->valuestring!=NULL)
                    {
                        ESP_LOGI(TAG_http,"Ai say :%s",content->valuestring);
                    }
                    else
                    {
                        ESP_LOGI(TAG_http,"The reply content cannot be parsed.");
                    }
                }
                else{
                    ESP_LOGI(TAG_http,"can not find the choices");
                }
                cJSON_Delete(root);
            }
            else{
                    ESP_LOGI(TAG_http,"can not parse root");
            }
        }
        break;
    case HTTP_EVENT_DISCONNECTED:
        ESP_LOGI(TAG_http,"HTTP_EVENT_DISCONNECTED");
        break;
    default:
        break;
    }
    return err;
}

static void initialise_wifi(void)
{
    ESP_ERROR_CHECK(esp_netif_init());
    s_wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );

    ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
    ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
    ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );

    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK( esp_wifi_start() );
}

static void smartconfig_task(void * parm)
{
    EventBits_t uxBits;
    ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );
    smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) );
    while (1) {
        uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, false, false, portMAX_DELAY);
        if(uxBits & CONNECTED_BIT) {
            ESP_LOGI(TAG, "WiFi Connected to ap");
        }
        if(uxBits & ESPTOUCH_DONE_BIT) {
            ESP_LOGI(TAG, "smartconfig over");
            esp_smartconfig_stop();
            vTaskDelete(NULL);
        }
    }
}




 void http_send_task(void *parm)
{
    uart_data_t tmp_data;
    BaseType_t ret = pdPASS;
while (1)
{

    ESP_LOGI(TAG_http,"http_send_task run!");
    ret=xQueueReceive(queue_context,&tmp_data,portMAX_DELAY);
    if(ret==pdFAIL)
    {
        ESP_LOGI(TAG_http,"revice data fail");
    }
    else
    {
    ESP_LOGI(TAG_http,"cjson run!");
    cJSON*root = cJSON_CreateObject();
    cJSON_AddStringToObject(root,"model",MODEL_NAME);
    cJSON*message = cJSON_CreateArray();
    cJSON*message_0 = cJSON_CreateObject();
    cJSON_AddStringToObject(message_0,"role", "user");
    cJSON_AddStringToObject(message_0,"content",((char*)tmp_data.data));
    cJSON_AddItemToArray(message,message_0);
    cJSON_AddItemToObject(root,"messages",message);
    char *post_data = cJSON_PrintUnformatted(root);
    cJSON_Delete(root);

   EventBits_t tmp_bit = xEventGroupWaitBits(s_wifi_event_group,CONNECTED_BIT | ESPTOUCH_DONE_BIT,false,true,portMAX_DELAY);
    if(tmp_bit)
    {
        esp_http_client_config_t esp_client_config={
        .url = AI_API_URL,
        .event_handler = http_event_handler,
        .transport_type = HTTP_TRANSPORT_OVER_SSL,
        .timeout_ms = 30000,
    };
    esp_http_client_handle_t client = esp_http_client_init(&esp_client_config);
    if(client==NULL){
        ESP_LOGI(TAG_http,"can not init http_client!");
        free(post_data);
    }
    esp_http_client_set_method(client,HTTP_METHOD_POST);
    esp_http_client_set_header(client,"Content-Type","application/json");
    esp_http_client_set_header(client, "Authorization", "Bearer "AI_API_KEY);
        esp_http_client_set_post_field(client,post_data,strlen(post_data));

    err_t err = esp_http_client_perform(client);
    if (err == ESP_OK) {
        ESP_LOGI(TAG, "HTTP请求成功,stuas: %d", esp_http_client_get_status_code(client));
        
    } else {
        ESP_LOGE(TAG, "HTTP请求失败: %s", esp_err_to_name(err));
    }
        // 清理资源
    esp_http_client_cleanup(client);
    free(post_data);  
    }
    else
    {
        ESP_LOGI(TAG_http,"等待联网!");
    }
    }
    UBaseType_t uxHighWaterMark = uxTaskGetStackHighWaterMark(NULL); 
    ESP_LOGI(TAG, "HTTP任务栈高水位线: %u 字 (%u 字节)", 
                uxHighWaterMark, uxHighWaterMark * 4);

    vTaskDelay(5000/portTICK_PERIOD_MS);
}


}



void app_main(void)
{
    esp_err_t ret =  nvs_flash_init();
    if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        nvs_flash_erase();
        nvs_flash_init();
    }
    initialise_wifi();
    uart_contrl_task();//串口任务
    xTaskCreate(http_send_task,"http_send_task",4096,NULL,10,NULL);

}

myuart.h

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/uart.h"
#include "esp_log.h"

#include "freertos/queue.h"
#include "string.h"

typedef struct
{
    uint8_t data[1024];
    uint8_t len;
}uart_data_t;

#define BUF_SIZE (1024)
 

extern QueueHandle_t queue_context;


void uart_contrl_task(void);


myuart.c

#include <stdio.h>
#include "myuart.h"


#define ECHO_TEST_TXD (UART_PIN_NO_CHANGE)
#define ECHO_TEST_RXD (UART_PIN_NO_CHANGE)
#define ECHO_TEST_RTS (UART_PIN_NO_CHANGE)
#define ECHO_TEST_CTS (UART_PIN_NO_CHANGE)

#define ECHO_UART_PORT_NUM      (0)
#define ECHO_UART_BAUD_RATE     (115200)
#define ECHO_TASK_STACK_SIZE    (2048)

static const char *TAG = "UART Revice";
QueueHandle_t queue_context;


static void uart_recive_task(void *arg)
{
    /* Configure parameters of an UART driver,
     * communication pins and install the driver */
    uart_config_t uart_config = {
        .baud_rate = ECHO_UART_BAUD_RATE,
        .data_bits = UART_DATA_8_BITS,
        .parity    = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };
    int intr_alloc_flags = 0;

#if CONFIG_UART_ISR_IN_IRAM
    intr_alloc_flags = ESP_INTR_FLAG_IRAM;
#endif

    ESP_ERROR_CHECK(uart_driver_install(ECHO_UART_PORT_NUM, BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags));
    ESP_ERROR_CHECK(uart_param_config(ECHO_UART_PORT_NUM, &uart_config));
    ESP_ERROR_CHECK(uart_set_pin(ECHO_UART_PORT_NUM, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS));

    // Configure a temporary buffer for the incoming data
    uart_data_t *data = (uart_data_t *) malloc(sizeof(uart_data_t));
    if(data==NULL)
    {
        ESP_LOGI(TAG,"fail to malloc data");
        vTaskDelete(NULL);
    }
    
    while (1) {
        BaseType_t ret;
        // Read data from the UART
        data->len = uart_read_bytes(ECHO_UART_PORT_NUM, data->data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);
        // Write data back to the UART
        
        
        if (data->len) {
            data->data[data->len] = '\0';
            ret = xQueueSend(queue_context,data,portMAX_DELAY);
            if(ret==pdFAIL)
            {
               ESP_LOGI(TAG,"fail to send queue");
            }
            else
            {
                memset(data->data,0,1024);
                data->len=0;
            }
            
        }
    }
    free(data);
}



void uart_contrl_task(void)
{
    queue_context =xQueueCreate(3,sizeof(uart_data_t));
    xTaskCreate(uart_recive_task, "uart_recive_task", ECHO_TASK_STACK_SIZE, NULL, 10, NULL);
}

测试展示

测试展示

补充说明

因为使用了https,这里我并未使用ssl证书,所以需要在menuconfig中配置一个选项,ESP-TLS下的Allow potentially insecure optionsSSL证书验证跳过

附言

如果对有些地方不解和建议或者讨论欢迎一起讨论,看到了会尽量回复的,目前也是打工人一枚,偶尔看看这些比较感兴趣的东西,休息时间还是打游戏多哈哈哈。这里就是一些胡言乱语啦,或许你和我一样,对生活对事业或者学业迷惘,我想说做你觉得正确的事情就行,朝着感兴趣的方向坚持学习即使每天一小步也是非常棒的!毕竟谁的青春不迷惘呢!

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐