2025交互标牌 - 用Seeed XIAO ESP32S3 Sense看世界
该项目使用了Seeed XIAO ESP32S3 Sense,实现了摄像头看世界,用AI来理解看到的画面,并使用OLED展现出来的设计,它的主要功能为:使用阿里云AI,协助理解看到的事物。。
标签
嵌入式系统
Funpack活动
显示
AI
阿里云
aramy
更新2025-08-18
95

一、硬件介绍:

Seeed Studio XIAO ESP32S3 Sense 集成了摄像头传感器、数字麦克风和支持 SD 卡的功能。处理器 ESP32-S3R8 SoC,支持 2.4GHz WiFi 和低功耗蓝牙® BLE 5.0 双模,适用于多种无线应用。它具有锂电池充电管理功能。该电路板配备了一个插入式 OV2640 摄像头传感器,可显示 1600*1200 的全分辨率。它的底座甚至兼容 OV5640,支持高达 2592*1944 的分辨率。电路板还配有数字麦克风,用于语音感应和音频识别。SenseCraft AI 可为XIAO ESP32S3 Sense 提供各种预训练的人工智能(AI)模型和无代码部署。

为了方便使用,给Seeed Studio XIAO ESP32S3 Sense 配了个扩展板。该扩展板专为 Seeed Studio XIAO 设计,仅有半个 Raspberry Pi 4 的大小。它使得原型开发和项目构建变得简单快捷。通过其丰富的外设,包括 OLED、RTC、可扩展内存、无源蜂鸣器、RESET/用户按钮、5V伺服连接器、多种数据接口……您可以探索 Seeed Studio XIAO 的无限可能性。

pinpinpin4.jpg

二、任务选择:

这次活动任务只有一个:互动标牌。这里我把Seeed XIAO ESP32S3 Sense做为一个眼睛,用它来看世界,用AI来理解看到的东西,通过文字的方式,用OLED展示出来。

三、任务实现

实现本项目,我使用Vscode+platformIO,使用Arduino编程。系统框图如下,摄像头用来采集看到的数据,按键和OLED屏幕负责和用户交互。

image.png

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

image.png

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

image.png

3、然后系统先初始化硬件,给摄像头单独启用一个进程,用来不停地读取摄像头。并将每次读取到的图片转换为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);
    }
}

4、按键部分使用了“OneButton”库,监控着按键按下的动作,一旦有按键按下的动作,就构建AI能够设别的json语句,通过http接口上送当前采集到的图片内容。

// 构建待上传的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);
  }
}

5、通过http将摄像头当前看见的图片内容上送后,阿里云那边会返回对图片的解析内容。解析的内容也是一串字符串,这里使用了中文,所以返回的字符串也是一串中文字符串。这里使用了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的畅通。

image.png

当遇到感兴趣的画面后,将摄像头对准感兴趣的场景,然后按下蓝色按键(红色按键未接入系统)。oled显示网络请求中,需要等待一会,等待AI的处理。image.png

稍后阿里平台传回AI模型分析的结果,在OLED上展示。

image.png

image.png

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

14174660c0c8b88ef9c43a6188566a90.png

五、心得体会

这次项目没有使用ESP32S3自身的机器学习能力,改为调用阿里云大模型服务平台的AI服务,能够不再受限于单片机的算力,可以实现更为复杂的AI分析。但是在使用OLED屏幕做展示时,遇到了问题。问题1,屏幕太小,显示内容有限,不太适合这种场合的内容展示。问题2,使用U8g2显示中文时,很多中文都无法显示,U8g2自带的中文字库局限性太大了。最后感谢电子森林举办的这次活动,通过这次活动实现了将AI工具随身携带的功能,收获满满!

附件下载
xiaoesp32s3_cam_homework.zip
团队介绍
单片机业余爱好者,瞎捣鼓小能手。
评论
0 / 100
查看更多
猜你喜欢
2025交互标牌 - 基于Seeed XIAO ESP32S3 Sense实现手机铃声放大器该项目使用了Seeed XIAO ESP32S3 Sense,实现了来电声光提醒的设计,它的主要功能为:通过识别 DTMF 音频中的数据,当指定用户来电时通过报警器提醒用户接听。
Zoologsit
49
2025交互标牌 - 基于Seed XIAO ESP32S3 Sense实现Midi 音乐智能创编该项目使用了Seed XIAO ESP32S3 Sense 开发板,实现了实现 MIDI 音乐智能创编的设计,它的主要功能为:通过哈希表建立 “音符 - 频率” 映射,将抽象音符转化为蜂鸣器可识别的振动频率,结合字符串解析模块拆解旋律指令。引入大语言模型,用户通过自然语言指令即可让 AI 生成符合规则的音符序列,经处理后由硬件播放。硬件上搭配定制音频解码方案与扩展底板,软件实现频率映射、歌曲解析、发声控制等功能,还集成在线音乐播放等附加功能,为嵌入式音乐场景提供实用解决方案。。
genvex
47
2025交互标牌 - 用Seeed XIAO ESP32S3 Sense实现的吓人姬该项目使用了Seeed XIAO ESP32S3 Sense,实现了具备环境感知、AI分析与动态交互的恐怖主题机械姬装置的设计,它的主要功能为:系统通过10G雷达检测人体接近,触发多模态响应链:包括摄像头拍摄、LED眼睛发光、语音播放及舵机驱动的机械面罩动作,形成完整的恐怖体验闭环。技术亮点在于低功耗传感与高性能计算的协同架构设计,以及机械动作、声光反馈与AI分析的实时交互逻辑。装置可扩展应用于密室逃脱、主题展馆等场景,具有显著的商业转化潜力。
搜主意
64
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号