Seeed Studio XIAO ESP32S3 Sense 是一款集成度极高的开发板,在硬件配置上亮点十足。它搭载了 ESP32-S3R8 SoC 处理器,具备强大的无线通信能力,支持 2.4GHz WiFi 以及低功耗蓝牙® BLE 5.0 双模,能轻松应对各类无线应用场景。
音频处理上,开发板内置数字麦克风,可用于语音感应和音频识别,为语音交互类应用提供了硬件基础。同时,它具备锂电池充电管理功能,方便为设备供电,增强了使用的便捷性与灵活性。
开发板的布局图


基本思路:Seeed XIAO ESP32S3 Sense通过摄像头收集图像数据,当用户按下按键,就把当前摄像头获得的图像打包为json,通过http方式上送到阿里云的AI接口。AI负责解析图片内容,然后返回解析的内容,ESP32S3收到返回的内容,就用OLED现实出来,展示给用户解读。因为OLED比较小,采用多页展示的方式显示给用户。以此逻辑绘制流程图:

首先在阿里云大模型服务平台 申请API Key,后边图像的解析都需要用到阿里云的大模型服务平台。

然后系统先初始化硬件,给摄像头单独启用一个进程,用来不停地读取摄像头。并将每次读取到的图片转换为base64编码格式。
代码
uint8_t CAMER_FLAG = 0;
String imageStr;
uint8_t camera_init()
{
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sccb_sda = SIOD_GPIO_NUM;
config.pin_sccb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
// init with high specs to pre-allocate larger buffers
if (psramFound())
{
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
}
else
{
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK)
{
Serial.printf("Camera init failed with error 0x%x", err);
return err;
}
// drop down frame size for higher initial frame rate
sensor_t *s = esp_camera_sensor_get();
s->set_framesize(s, FRAMESIZE_QVGA);
return 0;
}
// 从摄像头不停读取图片,当有信号发生时,拍照
void TaskCamImage(void *pvParameters)
{
camera_fb_t *pic = NULL;
camera_init();
for (;;)
{
pic = esp_camera_fb_get();
if (!pic)
{
log_e("Camera capture failed");
}
log_d("img_buf_len=%d , width=%d , height=%d ", pic->len, pic->height, pic->width);
log_w("Total PSRAM: %d, %d", ESP.getPsramSize(), ESP.getFreePsram());
if (CAMER_FLAG==1)
{
// fb->buf转为base64字符串
imageStr = base64::encode(pic->buf, pic->len);
// Serial.print("image_base64 success!");
Serial.print(" len=");
Serial.print(pic->len);
Serial.println();
// Serial.print(" base64_str=");
// Serial.println(imageStr);
CAMER_FLAG = 9; //形成了完整的图片base64字符串,可以发送给AI了
}
esp_camera_fb_return(pic);
}
}在按键功能实现方面,引入了“OneButton”库来精准监控按键操作。该库能够实时捕捉按键按下的动作,一旦检测到按键被按下,系统便会迅速构建符合 AI 识别要求的 JSON 语句。此 JSON 语句作为数据载体,会与当前通过摄像头采集到的图片内容一同打包。随后,借助 HTTP 接口,将包含图片和 JSON 语句的数据包上传至指定服务器,以便后续 AI 模型对图片内容进行分析与处理。
// 构建待上传的json 数据
String buildPalyLoad()
{
String payload;
data_json.clear();
data_json["model"] = "qwen-vl-max-latest";
JsonArray msg = data_json.createNestedArray("messages"); // 添加数组.
StaticJsonDocument<120> subnode;
subnode["role"] = "user";
JsonArray content = subnode.createNestedArray("content");
StaticJsonDocument<120> connode;
connode["type"] = "image_url";
connode["image_url"] = "CAME_IMAGE_BASE64";
// connode["image_url"] = image_base64();
content.add(connode);
connode.clear();
connode["type"] = "text";
connode["text"] = "简单描述图片";
content.add(connode);
msg.add(subnode);
serializeJson(data_json, payload);
return payload;
}
void loop(void)
{
// String imageStr;
uint8_t butflag;
btn.tick();
if (WiFi.status() == WL_CONNECTED)
{
if (CAMER_FLAG == 9)
{
CAMER_FLAG = 10;
aiEchoStr = "网络请求中";
String payload = buildPalyLoad();
payload.replace("\"CAME_IMAGE_BASE64\"", "{\"url\":\"data:image/jeg;base64," + imageStr + "\"}");
// Serial.println(payload);
// Serial.println("请稍后...");
HTTPClient http_image_request;
http_image_request.setTimeout(60000);
http_image_request.begin(apiUrl);
http_image_request.addHeader("Content-Type", "application/json");
http_image_request.addHeader("Authorization", String("Bearer ") + String(apiKey));
int httpCode = http_image_request.POST(payload);
if (httpCode == 200)
{
String response = http_image_request.getString();
http_image_request.end();
DynamicJsonDocument jsonDoc(1024);
deserializeJson(jsonDoc, response);
const char* world = jsonDoc["choices"][0]["message"]["content"];
aiEchoStr = world; //获得到AI返回的文字
CAMER_FLAG = 11;
// Serial.println(response);
}
else
{
CAMER_FLAG = 0;
Serial.println("error request: " + String(httpCode));
http_image_request.end();
}
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
else
{
log_e("[WIFI] is not Connecting!");
vTaskDelay(10000 / portTICK_PERIOD_MS);
}
}通过 HTTP 协议将摄像头实时捕捉到的图片内容上传至阿里云后,阿里云服务器会迅速对图片进行解析,并返回一串解析后的字符串。由于解析内容采用中文表述,所以返回的字符串也是一连串中文字符。为了在 OLED 屏幕上清晰呈现这些中文内容,项目选用了 U8g2 库来驱动 OLED 显示中文。不过,板子上的 OLED 屏幕尺寸仅为 128X64 像素,显示空间有限,无法一次性完整展示所有返回的汉字。针对这一问题,采用了分屏显示的方式。每次在屏幕上显示 4 行内容,每行限定为 8 个汉字,这样既能保证汉字清晰可辨,又能充分利用屏幕空间。并且,每一页内容会在屏幕上停留 2 秒钟,方便用户阅读,之后系统会自动翻页,展示后续内容,以此实现解析内容的完整呈现。void oledDispRow(String dispstr, uint8_t rownum)
{
Serial.println(rownum);
u8g2.setFont(u8g2_font_unifont_t_chinese3); // use chinese2
u8g2.clear();
u8g2.firstPage();
do
{
for (uint16_t i = 0; i <= rownum; i++)
{
String subStr = dispstr.substring(i * OLED_ROW, i * OLED_ROW + OLED_ROW);
Serial.printf("i=%d ,rows= %d ", i, rownum);
Serial.println(subStr);
u8g2.setCursor(0, (i + 1) * 16);
u8g2.print(subStr); // Chinese "Hello World"
}
} while (u8g2.nextPage());
}
// 将AI获得的字符串解析出来,并显示
void TaskOledDisp(void *pvParameters)
{
uint16_t strlen = 0;
u8g2.begin();
u8g2.enableUTF8Print();
for (;;)
{
if (CAMER_FLAG == 10)
{
strlen = aiEchoStr.length();
Serial.print("10 strlen:");
Serial.println(strlen);
for (uint16_t i = 0; i < strlen; i += OLED_ROW * 4)
{
String subStr = aiEchoStr.substring(i, i + OLED_ROW * 4);
Serial.println(subStr);
oledDispRow(subStr, (subStr.length() / (OLED_ROW)));
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
if (CAMER_FLAG == 11)
{
strlen = aiEchoStr.length();
Serial.print("11 strlen:");
Serial.println(strlen);
for (uint16_t i = 0; i < strlen; i += OLED_ROW * 4)
{
String subStr = aiEchoStr.substring(i, i + OLED_ROW * 4);
Serial.println(subStr);
oledDispRow(subStr, (subStr.length() / (OLED_ROW)));
vTaskDelay(pdMS_TO_TICKS(5000));
}
CAMER_FLAG = 0;
}
if (CAMER_FLAG == 0) u8g2.clear();
vTaskDelay(pdMS_TO_TICKS(50));
}
}效果展示:
系统上电后,需要保障WIFI的畅通。

当遇到感兴趣的画面后,将摄像头对准感兴趣的场景,然后按下蓝色按键(红色按键未接入系统)。oled显示网络请求中,需要等待一会,等待AI的处理。
稍后阿里平台传回AI模型分析的结果,在OLED上展示。

如果开发板有连着电脑,可以通过电脑串口看见AI平台返回的结果内容。

我要赚赏金
