| 本帖最后由 名动神界 于 2025-5-27 11:15 编辑 
 测试时比较仓促,加之能力有限、经验不足,测试结果不具备普遍意义,这是一次简易的试用报告。相信dfrobot的产品质量都是过硬的,我没有把它的性能发挥出来。本案例中用到的所有头文件在此,下面不再重复
 
 所有引脚的配置如下图:复制代码#include <WiFi.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include "ESP_I2S.h"
#include "esp_camera.h"
#include "FS.h"
#include "SD.h"
#include "SPI.h"
 
 复制代码// 摄像头引脚配置(ESP32-S3默认)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 5
#define Y9_GPIO_NUM 4
#define Y8_GPIO_NUM 6
#define Y7_GPIO_NUM 7
#define Y6_GPIO_NUM 14
#define Y5_GPIO_NUM 17
#define Y4_GPIO_NUM 21
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 16
#define VSYNC_GPIO_NUM 1
#define HREF_GPIO_NUM 2
#define PCLK_GPIO_NUM 15
#define SIOD_GPIO_NUM 8
#define SIOC_GPIO_NUM 9
// TF卡SPI引脚配置
#define SCK_PIN 12
#define MISO_PIN 13
#define MOSI_PIN 11
#define CS_PIN 10
// 麦克风引脚
#define MIC_DATA 39
#define MIC_CLK 38
 
 第一步,是测试延时拍摄,因为板子不具备h264等编码器,录制mp4需占用80%以上的cpu且工作非常不稳定。
 复制代码void setup() {
  Serial.begin(115200);
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_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_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;
  config.frame_size = FRAMESIZE_UXGA;
  config.jpeg_quality = 10;
  config.fb_count = 2;
esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed: 0x%x", err);
    return;}
  SPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, CS_PIN);  //初始化sd卡
  if(!SD.begin(CS_PIN)){
    Serial.println("SD Card Mount Failed");
    return;}}
void loop() {
  // 捕获帧并保存
  camera_fb_t *fb = esp_camera_fb_get();  
  if(!fb) {
    Serial.println("Camera capture failed");
    return;}
String path = "/video_" + String(millis()) + ".jpg"; // 创建唯一文件名
  File file = SD.open(path.c_str(), FILE_WRITE);// 写入SD卡
  if(!file){
    Serial.println("Failed to open file");
  } else {
    file.write(fb->buf, fb->len);
    Serial.println("Saved: " + path);}
  file.close();
  esp_camera_fb_return(fb);
delay(100);} // 控制帧率
  录了6000多张,不过由于昨晚角度没放好,一直录的是距离桌面2米高的屋顶,红外夜视效果就出不来了,官方公布的红外夜视距离是2米以内。后面的图片是选取的典型状态图,第一张是晚上10点,最后一张是早上7点过。
 
  (典型的延时拍摄截图)
 
 
 第二步是测试远程喊话,这个费了点时间,因为我本人硬件调试经验薄弱,有些瞎子摸象的窘态,不过最终还是成功地使用百度语音合成生成了声音。
 
 主要的难点是把百度语音合成的音频流格式,也就是aue,和板子的播放命令(playmp3,playwav,playaudio)相匹配。本人水平有限,使用aue=3,也就是mp3格式,aue=36,也就是wav格式,都调试失败。最后我把aue设置了无压缩的pcm格式,这是一种没有文件头、原汗原味的声音流,通过i2s.write命令播放成功。复制代码// 配置参数
#define BAIDU_TTS_URL "http://tsn.baidu.com/text2audio"
#define API_KEY "***"
#define SECRET_KEY "***"
#define SAMPLE_RATE 16000
I2SClass i2s;
WiFiClient client;
String getToken() {
  HTTPClient http;
  String url = "https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials";
  url += "&client_id=" + String(API_KEY);
  url += "&client_secret=" + String(SECRET_KEY);
  
  http.begin(url);
  int httpCode = http.GET();
  
  if(httpCode == HTTP_CODE_OK) {
    String payload = http.getString();
    int tokenStart = payload.indexOf("access_token") + 15;
    int tokenEnd = payload.indexOf(""", tokenStart);
    String accessToken = payload.substring(tokenStart, tokenEnd);
    return accessToken;
  }
  return "";
}
void playAudio(uint8_t* data, size_t len) {
  i2s.write(data, len);
}
void textToSpeech(String text) {
  String token = getToken();
  if(token == "") {
    Serial.println("Failed to get token");
    return;
  }
  HTTPClient http;
  String url = BAIDU_TTS_URL "?tex=" + text;
  url += "&tok=" + token;
  url += "&cuid=esp32_device_zzq";
  url += "&ctp=1";
  url += "&lan=zh";
  url += "&spd=5";
  url += "&pit=5";
  url += "&vol=5";
  url += "&per=0";
  url += "&aue=4"; // mp3格式
  http.begin(client, url);
  int httpCode = http.GET();
  
  if(httpCode == HTTP_CODE_OK) {
    WiFiClient* stream = http.getStreamPtr();
    uint8_t buffer[512];
    while(stream->available()) {
      size_t len = stream->readBytes(buffer, sizeof(buffer));
      playAudio(buffer, len);
    }
  }
  http.end();
}
void setup() {
  Serial.begin(115200);
  WiFi.begin("qzezsteam", "qzez@1953");
  while(WiFi.status() != WL_CONNECTED) delay(500);
  Serial.println(WiFi.localIP());
  i2s.setPins(45, 46, 42);
  if(!i2s.begin(I2S_MODE_STD, SAMPLE_RATE, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO)) {
    Serial.println("I2S init failed");
  }
}
void loop() {
  if(Serial.available()) {
    String input = Serial.readStringUntil('\n');
    Serial.println(input);
    textToSpeech(input);
  }
}
 i2s.write
 功能:直接向I2S总线发送原始PCM音频数据流。数据要求:输入数据必须是未经压缩的PCM格式(如16位/32位量化、单/双声道)35。处理流程:需手动完成音频数据的解码、格式转换(如调整字节序、声道排列)和缓冲区管理57。典型应用:播放自定义音频流或需要低延迟控制的场景,如实时语音传输或音频合成第三步:测试web远程喊话,这个可以通过特殊手段把服务端html模板写入固件来实现,不过我还没学会。我是通过arduino ide中定义了html网页变量来做成功的。第四步就是制作延时拍摄了,开始走了弯路,总想用板子向web发送更新命令,失败n次,换成web端向板子请求数据,最后终于成功了复制代码void handleRoot() {
  String html = R"rawliteral(
  <!DOCTYPE html>
  <html>
  <head>
    <meta charset="UTF-8">
    <title>远程监控系统</title>
  </head>
  <body>
    <div>
      <div>
        <h2>远程喊话</h2>
        <form action="/speak" method="POST">
          <input type="text" name="text" placeholder="输入文字"><br>
          <select name="preset">
            <option value="">--选择预设短语--</option>
            <option value="坏人,请赶快离开!">警告短语</option>
            <option value="你有什么事么?">询问短语</option>
            <option value="你好我是实验室主人">欢迎短语</option>
          </select><br>
          <input type="submit" value="播放">
        </form>
      </div>
      <div>
  </body>
  </html>
  )rawliteral";
  server.send(200, "text/html; charset=utf-8", html);
}
<h2>实时监控</h2><img id='stream' src='/capture' onload='setTimeout(function(){ document.getElementById(\"stream\").src = \"/capture?\" + new Date().getTime(); }, 100);'>
 
 onload这个回调函数,可以让图片实时更新,更新的频是100毫秒,也就是每秒钟回传10张。
最后就制作完成了,下面是视频演示
 
 
 
 
 |