ESP32调用大模型API对话
·
前言
大家好,博主也是第一次发文章。记录一下学习过程以及思考,希望帮到有需要的人,也希望各位能一起讨论学习。
实现思路
这里我是用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 options
附言
如果对有些地方不解和建议或者讨论欢迎一起讨论,看到了会尽量回复的,目前也是打工人一枚,偶尔看看这些比较感兴趣的东西,休息时间还是打游戏多哈哈哈。这里就是一些胡言乱语啦,或许你和我一样,对生活对事业或者学业迷惘,我想说做你觉得正确的事情就行,朝着感兴趣的方向坚持学习即使每天一小步也是非常棒的!毕竟谁的青春不迷惘呢!
更多推荐



所有评论(0)