本帖最后由 名动神界 于 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点过。

(典型的延时拍摄截图)
- 第二步是测试远程喊话,这个费了点时间,因为我本人硬件调试经验薄弱,有些瞎子摸象的窘态,不过最终还是成功地使用百度语音合成生成了声音。
- // 配置参数
- #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);
- }
- }
复制代码
主要的难点是把百度语音合成的音频流格式,也就是aue,和板子的播放命令(playmp3,playwav,playaudio)相匹配。本人水平有限,使用aue=3,也就是mp3格式,aue=36,也就是wav格式,都调试失败。最后我把aue设置了无压缩的pcm格式,这是一种没有文件头、原汗原味的声音流,通过i2s.write命令播放成功。
i2s.write- 功能:直接向I2S总线发送原始PCM音频数据流。
- 数据要求:输入数据必须是未经压缩的PCM格式(如16位/32位量化、单/双声道)35。
- 处理流程:需手动完成音频数据的解码、格式转换(如调整字节序、声道排列)和缓冲区管理57。
- 典型应用:播放自定义音频流或需要低延迟控制的场景,如实时语音传输或音频合成
- 第三步:测试web远程喊话,这个可以通过特殊手段把服务端html模板写入固件来实现,不过我还没学会。我是通过arduino ide中定义了html网页变量来做成功的。
- 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);
- }
复制代码
第四步就是制作延时拍摄了,开始走了弯路,总想用板子向web发送更新命令,失败n次,换成web端向板子请求数据,最后终于成功了<h2>实时监控</h2>
<img id='stream' src='/capture' onload='setTimeout(function(){ document.getElementById(\"stream\").src = \"/capture?\" + new Date().getTime(); }, 100);'>
onload这个回调函数,可以让图片实时更新,更新的频是100毫秒,也就是每秒钟回传10张。- 最后就制作完成了,下面是视频演示
|