Skip to content

Commit b93c106

Browse files
MengzhangLIYour Name
authored andcommitted
Initial commit: AI Blind Glasses project with ESP32 firmware and eval benchmarks
Made-with: Cursor
0 parents  commit b93c106

173 files changed

Lines changed: 10011 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
/*
2+
* CameraWebServer_PDM_Audio — Phase A: Concurrent Mode
3+
*
4+
* Camera + PDM microphone run simultaneously (no time-division multiplexing).
5+
*
6+
* Core design: concurrent operation
7+
* - Camera uses DVP interface, PDM uses I2S_NUM_0; both are hardware-independent
8+
* - Both camera and PDM are initialized in setup()
9+
* - /ws_audio: streaming starts on connect, stops on disconnect; no audio_begin/audio_end needed
10+
* - /audio_begin /audio_end kept as no-ops (backward compatibility)
11+
*
12+
* Hardware: Seeed Studio XIAO ESP32S3 + OV5640
13+
* PDM microphone pins (per schematic XIAO_ESP32S3_V1.3_SCH_260115):
14+
* - IO42 = PDM_CLK (clock)
15+
* - IO41 = PDM_DATA (data)
16+
*
17+
* WS audio format: 16kHz, mono, PCM16 LE, 20ms/frame (640 bytes)
18+
* Audio processing: DC offset removal + software gain (done on ESP32)
19+
*
20+
* Endpoints:
21+
* GET / - Web UI (Camera)
22+
* GET /capture - JPEG capture (always available)
23+
* GET :81/stream - MJPEG video stream (always available)
24+
* WS /ws_audio - PCM16 audio stream (stream on connect)
25+
* GET /status - System status JSON
26+
* GET /audio_begin, /audio_end - Backward compatibility (no-op)
27+
*
28+
* Arduino IDE settings:
29+
* Board: XIAO_ESP32S3 | PSRAM: OPI PSRAM
30+
* Partition: Huge APP (3MB No OTA)
31+
*/
32+
33+
#include "esp_camera.h"
34+
#include <WiFi.h>
35+
36+
// ===================
37+
// Select camera model
38+
// ===================
39+
#define CAMERA_MODEL_XIAO_ESP32S3 // Has PSRAM
40+
#include "camera_pins.h"
41+
42+
// ===========================
43+
// Enter your WiFi credentials
44+
// ===========================
45+
const char *ssid = "YOUR_WIFI_NAME";
46+
const char *password = "YOUR_WIFI_PASSWORD";
47+
48+
49+
// Forward declarations
50+
void startCameraServer();
51+
void setupLedFlash(int pin);
52+
extern bool pdm_mic_init(); // defined in app_httpd.cpp
53+
54+
// ========================================
55+
// Camera config (global, for reinit)
56+
// ========================================
57+
static camera_config_t cam_config;
58+
59+
static void setup_cam_config() {
60+
cam_config.ledc_channel = LEDC_CHANNEL_0;
61+
cam_config.ledc_timer = LEDC_TIMER_0;
62+
cam_config.pin_d0 = Y2_GPIO_NUM;
63+
cam_config.pin_d1 = Y3_GPIO_NUM;
64+
cam_config.pin_d2 = Y4_GPIO_NUM;
65+
cam_config.pin_d3 = Y5_GPIO_NUM;
66+
cam_config.pin_d4 = Y6_GPIO_NUM;
67+
cam_config.pin_d5 = Y7_GPIO_NUM;
68+
cam_config.pin_d6 = Y8_GPIO_NUM;
69+
cam_config.pin_d7 = Y9_GPIO_NUM;
70+
cam_config.pin_xclk = XCLK_GPIO_NUM;
71+
cam_config.pin_pclk = PCLK_GPIO_NUM;
72+
cam_config.pin_vsync = VSYNC_GPIO_NUM;
73+
cam_config.pin_href = HREF_GPIO_NUM;
74+
cam_config.pin_sccb_sda = SIOD_GPIO_NUM;
75+
cam_config.pin_sccb_scl = SIOC_GPIO_NUM;
76+
cam_config.pin_pwdn = PWDN_GPIO_NUM;
77+
cam_config.pin_reset = RESET_GPIO_NUM;
78+
cam_config.xclk_freq_hz = 10000000; // 10MHz, headroom for WiFi stack
79+
cam_config.frame_size = FRAMESIZE_HD; // 1280x720
80+
cam_config.pixel_format = PIXFORMAT_JPEG;
81+
cam_config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
82+
cam_config.fb_location = CAMERA_FB_IN_PSRAM;
83+
cam_config.jpeg_quality = 12;
84+
cam_config.fb_count = 2;
85+
86+
if (cam_config.pixel_format == PIXFORMAT_JPEG) {
87+
if (psramFound()) {
88+
cam_config.jpeg_quality = 10;
89+
cam_config.fb_count = 2;
90+
cam_config.grab_mode = CAMERA_GRAB_LATEST;
91+
} else {
92+
cam_config.frame_size = FRAMESIZE_SVGA;
93+
cam_config.fb_location = CAMERA_FB_IN_DRAM;
94+
}
95+
} else {
96+
cam_config.frame_size = FRAMESIZE_240X240;
97+
#if CONFIG_IDF_TARGET_ESP32S3
98+
cam_config.fb_count = 2;
99+
#endif
100+
}
101+
}
102+
103+
static void apply_sensor_settings() {
104+
sensor_t *s = esp_camera_sensor_get();
105+
if (!s) {
106+
Serial.println("[CAM] WARNING: sensor_get returned NULL");
107+
return;
108+
}
109+
110+
// Basic orientation
111+
s->set_vflip(s, 1);
112+
s->set_hmirror(s, 0);
113+
114+
// Exposure
115+
s->set_aec2(s, 1);
116+
s->set_ae_level(s, -2);
117+
s->set_aec_value(s, 168);
118+
119+
// Gain
120+
s->set_agc_gain(s, 0);
121+
s->set_gainceiling(s, (gainceiling_t)0);
122+
123+
// White balance
124+
s->set_awb_gain(s, 1);
125+
s->set_wb_mode(s, 0);
126+
127+
// Contrast/saturation
128+
s->set_contrast(s, 2);
129+
s->set_brightness(s, 0);
130+
s->set_saturation(s, 2);
131+
132+
// Lens correction + sharpness
133+
s->set_lenc(s, 1);
134+
s->set_sharpness(s, 1);
135+
136+
// Resolution
137+
s->set_framesize(s, FRAMESIZE_HD);
138+
}
139+
140+
// ========================================
141+
// Camera functions callable from app_httpd.cpp
142+
// ========================================
143+
bool init_camera() {
144+
setup_cam_config();
145+
esp_err_t err = esp_camera_init(&cam_config);
146+
if (err != ESP_OK) {
147+
Serial.printf("[CAM] init FAILED: 0x%x\n", err);
148+
return false;
149+
}
150+
apply_sensor_settings();
151+
Serial.printf("[CAM] init OK, free heap: %u, PSRAM free: %u\n",
152+
(unsigned)ESP.getFreeHeap(),
153+
(unsigned)heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
154+
return true;
155+
}
156+
157+
void deinit_camera() {
158+
esp_err_t err = esp_camera_deinit();
159+
if (err != ESP_OK) {
160+
Serial.printf("[CAM] deinit failed: 0x%x, retrying...\n", err);
161+
delay(200);
162+
err = esp_camera_deinit();
163+
}
164+
Serial.printf("[CAM] deinit: %s, free heap: %u\n",
165+
err == ESP_OK ? "OK" : "FAIL",
166+
(unsigned)ESP.getFreeHeap());
167+
}
168+
169+
// ========================================
170+
// setup & loop
171+
// ========================================
172+
void setup() {
173+
Serial.begin(115200);
174+
Serial.setDebugOutput(true);
175+
Serial.println();
176+
Serial.println("========================================");
177+
Serial.println(" CameraWebServer + PDM Audio (XIAO S3)");
178+
Serial.println("========================================");
179+
180+
// Camera init (DVP interface)
181+
if (!init_camera()) {
182+
Serial.println("[FATAL] Camera init failed!");
183+
return;
184+
}
185+
186+
// PDM mic init (I2S_NUM_0, runs concurrently with camera)
187+
if (!pdm_mic_init()) {
188+
Serial.println("[WARNING] PDM mic init failed! Audio will not work.");
189+
// Continue — camera still works
190+
}
191+
192+
#if defined(CAMERA_MODEL_ESP_EYE)
193+
pinMode(13, INPUT_PULLUP);
194+
pinMode(14, INPUT_PULLUP);
195+
#endif
196+
197+
#if defined(LED_GPIO_NUM)
198+
setupLedFlash(LED_GPIO_NUM);
199+
#endif
200+
201+
// WiFi
202+
WiFi.begin(ssid, password);
203+
WiFi.setSleep(false);
204+
205+
Serial.print("[WiFi] Connecting");
206+
int wifi_retry = 0;
207+
while (WiFi.status() != WL_CONNECTED) {
208+
delay(500);
209+
Serial.print(".");
210+
wifi_retry++;
211+
if (wifi_retry > 40) { // 20 seconds timeout
212+
Serial.println("\n[WiFi] FAILED to connect! Restarting...");
213+
ESP.restart();
214+
}
215+
}
216+
Serial.println();
217+
Serial.printf("[WiFi] Connected! IP: %s\n", WiFi.localIP().toString().c_str());
218+
219+
// Start web server
220+
startCameraServer();
221+
222+
Serial.println("========================================");
223+
Serial.printf("[READY] Web UI: http://%s/\n", WiFi.localIP().toString().c_str());
224+
Serial.printf("[READY] Capture: http://%s/capture\n", WiFi.localIP().toString().c_str());
225+
Serial.printf("[READY] Stream: http://%s:81/stream\n", WiFi.localIP().toString().c_str());
226+
Serial.printf("[READY] Audio WS: ws://%s/ws_audio\n", WiFi.localIP().toString().c_str());
227+
Serial.printf("[READY] Free heap: %u bytes\n", (unsigned)ESP.getFreeHeap());
228+
Serial.println("========================================");
229+
}
230+
231+
void loop() {
232+
// Health monitoring
233+
Serial.printf("[HEALTH] heap=%u PSRAM=%u RSSI=%d uptime=%lus\n",
234+
(unsigned)ESP.getFreeHeap(),
235+
(unsigned)heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
236+
WiFi.RSSI(),
237+
millis() / 1000);
238+
delay(10000);
239+
}

0 commit comments

Comments
 (0)