├── README.md ├── camera.ino ├── esp32_camera_web_server.ino ├── esp32_camera_web_server.ino.esp32.bin ├── html.ino ├── pin_mapping_ai_thinker.h ├── pin_mapping_t-camera.h ├── pin_mapping_t-camera_plus.h ├── pin_mapping_t-journal.h ├── web_server.ino └── wifi.ino /README.md: -------------------------------------------------------------------------------- 1 | # ESP32 Camera web server 2 | 3 | 4 | This is a firmware for AI thinker ESP32-CAM board which provides the following functions: 5 | 6 | * Stream from the camera 7 | * Single frame from the camera 8 | * Ability to upload new firmware OTA 9 | -------------------------------------------------------------------------------- /camera.ino: -------------------------------------------------------------------------------- 1 | void camera_init(){ 2 | camera_config_t config; 3 | 4 | config.ledc_channel = LEDC_CHANNEL_0; 5 | config.ledc_timer = LEDC_TIMER_0; 6 | 7 | config.pin_d0 = D0_GPIO_NUM; 8 | config.pin_d1 = D1_GPIO_NUM; 9 | config.pin_d2 = D2_GPIO_NUM; 10 | config.pin_d3 = D3_GPIO_NUM; 11 | config.pin_d4 = D4_GPIO_NUM; 12 | config.pin_d5 = D5_GPIO_NUM; 13 | config.pin_d6 = D6_GPIO_NUM; 14 | config.pin_d7 = D7_GPIO_NUM; 15 | 16 | config.pin_xclk = XCLK_GPIO_NUM; 17 | config.pin_pclk = PCLK_GPIO_NUM; 18 | 19 | config.pin_vsync = VSYNC_GPIO_NUM; 20 | config.pin_href = HREF_GPIO_NUM; 21 | 22 | config.pin_sscb_sda = SIOD_GPIO_NUM; 23 | config.pin_sscb_scl = SIOC_GPIO_NUM; 24 | 25 | config.pin_pwdn = PWDN_GPIO_NUM; 26 | config.pin_reset = RESET_GPIO_NUM; 27 | 28 | config.xclk_freq_hz = 20000000; 29 | config.pixel_format = PIXFORMAT_JPEG; 30 | 31 | // Frame parameters 32 | //config.frame_size = FRAMESIZE_UXGA; 33 | //config.frame_size = FRAMESIZE_SXGA; 34 | //config.frame_size = FRAMESIZE_XGA; 35 | config.frame_size = FRAMESIZE_VGA; 36 | config.jpeg_quality = 10; 37 | config.fb_count = 2; 38 | 39 | // camera init 40 | esp_err_t err = esp_camera_init(&config); 41 | if (err != ESP_OK) { 42 | Serial.printf("[CAMERA] init failed with error 0x%x", err); 43 | return; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /esp32_camera_web_server.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * BOARD SETTINGS 3 | * Board type: ESP32 Wrover Module 4 | */ 5 | 6 | // Libraries 7 | #include "esp_camera.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | // Getting WIFI_SSID and WIFI_PASSWORD from external credentials file 15 | #include "credentials.h"; 16 | 17 | //#include "pin_mapping_ai_thinker.h"; 18 | #include "pin_mapping_t-journal.h"; 19 | //#include "pin_mapping_t-camera.h"; 20 | //#include "pin_mapping_t-camera_plus.h"; 21 | 22 | 23 | WebServer web_server(80); 24 | 25 | 26 | void setup() { 27 | Serial.begin(115200); 28 | Serial.setDebugOutput(true); 29 | Serial.println(); 30 | 31 | camera_init(); 32 | wifi_setup(); 33 | web_server_setup(); 34 | } 35 | 36 | void loop() { 37 | wifi_connection_manager(); 38 | web_server.handleClient(); 39 | } 40 | -------------------------------------------------------------------------------- /esp32_camera_web_server.ino.esp32.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximemoreillon/esp32_camera_web_server/8c4e9032f8656db7f18d0e6a2a405e49d118197a/esp32_camera_web_server.ino.esp32.bin -------------------------------------------------------------------------------- /html.ino: -------------------------------------------------------------------------------- 1 | // Head partials 2 | // Style 3 | const String styles = ""; 8 | 9 | // Body partials 10 | const String header = "

ESP32 CAMERA

"; 11 | 12 | const String nav = ""; 18 | 19 | const String footer= ""; 23 | 24 | // Update form 25 | String update_form = "

Firmware update

" 26 | "
" 27 | "" 28 | "" 29 | "
"; 30 | 31 | // Combinations 32 | String head ="" 33 | "ESP32 camera" 34 | + styles + 35 | "" 36 | ""; 37 | 38 | String pre_main = "" 39 | + head + 40 | "" 41 | + header + nav + 42 | "
"; 43 | 44 | String post_main = "
" 45 | + footer + 46 | "" 47 | "" 48 | ""; 49 | 50 | // Not found 51 | String not_found = "

Not found

"; 52 | -------------------------------------------------------------------------------- /pin_mapping_ai_thinker.h: -------------------------------------------------------------------------------- 1 | #define PWDN_GPIO_NUM 32 2 | #define RESET_GPIO_NUM -1 3 | 4 | #define XCLK_GPIO_NUM 0 5 | #define SIOD_GPIO_NUM 26 6 | #define SIOC_GPIO_NUM 27 7 | 8 | #define D0_GPIO_NUM 5 9 | #define D1_GPIO_NUM 18 10 | #define D2_GPIO_NUM 19 11 | #define D3_GPIO_NUM 21 12 | #define D4_GPIO_NUM 36 13 | #define D5_GPIO_NUM 39 14 | #define D6_GPIO_NUM 34 15 | #define D7_GPIO_NUM 35 16 | 17 | #define VSYNC_GPIO_NUM 25 18 | #define HREF_GPIO_NUM 23 19 | #define PCLK_GPIO_NUM 22 20 | -------------------------------------------------------------------------------- /pin_mapping_t-camera.h: -------------------------------------------------------------------------------- 1 | #define PWDN_GPIO_NUM -1 2 | #define RESET_GPIO_NUM -1 3 | 4 | #define XCLK_GPIO_NUM 32 5 | #define SIOD_GPIO_NUM 13 6 | #define SIOC_GPIO_NUM 12 7 | 8 | #define D0_GPIO_NUM 5 9 | #define D1_GPIO_NUM 14 10 | #define D2_GPIO_NUM 4 11 | #define D3_GPIO_NUM 15 12 | #define D4_GPIO_NUM 18 13 | #define D5_GPIO_NUM 23 14 | #define D6_GPIO_NUM 36 15 | #define D7_GPIO_NUM 39 16 | 17 | #define VSYNC_GPIO_NUM 27 18 | #define HREF_GPIO_NUM 25 19 | #define PCLK_GPIO_NUM 19 20 | -------------------------------------------------------------------------------- /pin_mapping_t-camera_plus.h: -------------------------------------------------------------------------------- 1 | #define PWDN_GPIO_NUM -1 2 | #define RESET_GPIO_NUM -1 3 | 4 | #define XCLK_GPIO_NUM 4 5 | #define SIOD_GPIO_NUM 18 6 | #define SIOC_GPIO_NUM 23 7 | 8 | #define D0_GPIO_NUM 34 9 | #define D1_GPIO_NUM 13 10 | #define D2_GPIO_NUM 26 11 | #define D3_GPIO_NUM 35 12 | #define D4_GPIO_NUM 39 13 | #define D5_GPIO_NUM 38 14 | #define D6_GPIO_NUM 37 15 | #define D7_GPIO_NUM 36 16 | 17 | #define VSYNC_GPIO_NUM 5 18 | #define HREF_GPIO_NUM 27 19 | #define PCLK_GPIO_NUM 25 20 | -------------------------------------------------------------------------------- /pin_mapping_t-journal.h: -------------------------------------------------------------------------------- 1 | #define PWDN_GPIO_NUM 0 2 | #define RESET_GPIO_NUM 15 3 | 4 | #define PCLK_GPIO_NUM 21 5 | #define XCLK_GPIO_NUM 27 6 | #define SIOD_GPIO_NUM 25 7 | #define SIOC_GPIO_NUM 23 8 | 9 | #define D0_GPIO_NUM 17 10 | #define D1_GPIO_NUM 35 11 | #define D2_GPIO_NUM 34 12 | #define D3_GPIO_NUM 5 13 | #define D4_GPIO_NUM 39 14 | #define D5_GPIO_NUM 18 15 | #define D6_GPIO_NUM 36 16 | #define D7_GPIO_NUM 19 17 | 18 | #define VSYNC_GPIO_NUM 22 19 | #define HREF_GPIO_NUM 26 20 | -------------------------------------------------------------------------------- /web_server.ino: -------------------------------------------------------------------------------- 1 | 2 | void web_server_setup(){ 3 | Serial.println(F("[HTTP] Web server initialization")); 4 | 5 | web_server.on("/", handle_root); 6 | web_server.on("/stream", HTTP_GET, handle_stream); 7 | web_server.on("/frame", HTTP_GET, handle_frame); 8 | web_server.on("/update_form", handle_update_form); 9 | web_server.on("/update",HTTP_POST, handle_update, handle_update_upload); 10 | 11 | web_server.onNotFound(handle_not_found); 12 | web_server.begin(); 13 | } 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | void handle_stream(void) { 22 | 23 | Serial.println(F("[HTTP] '/stream' requested")); 24 | 25 | // Initialize an empty frame buffer 26 | camera_fb_t * fb = NULL; 27 | 28 | // Sending response header 29 | String response = "HTTP/1.1 200 OK\r\n"; 30 | response += "Access-Control-Allow-Origin: *\r\n"; 31 | response += "Content-Type: multipart/x-mixed-replace; boundary=frame\r\n\r\n;"; 32 | web_server.sendContent(response); 33 | 34 | // Sending actual content 35 | WiFiClient client = web_server.client(); 36 | 37 | long stream_start_time = millis(); 38 | 39 | while (client.connected() && millis() - stream_start_time < 60000) { 40 | 41 | // Frame capture 42 | fb = esp_camera_fb_get(); 43 | 44 | response = "--frame\r\n"; 45 | response += "Content-Type: image/jpeg\r\n\r\n"; 46 | web_server.sendContent(response); 47 | 48 | client.write((char *)fb->buf, fb->len); 49 | 50 | web_server.sendContent("\r\n"); 51 | 52 | // Empty the buffer 53 | if(fb) { 54 | esp_camera_fb_return(fb); 55 | fb = NULL; 56 | } 57 | } 58 | 59 | Serial.println("Stream terminated"); 60 | } 61 | 62 | 63 | void handle_frame(void) { 64 | 65 | Serial.println(F("[HTTP] '/frame' requested")); 66 | 67 | 68 | // Frame capture 69 | Serial.print(F("[Camera] Capturing frame ")); 70 | camera_fb_t * fb = NULL; 71 | fb = esp_camera_fb_get(); 72 | Serial.println(F("OK")); 73 | 74 | 75 | // Send response header 76 | Serial.println(F("[HTTP] Sending frameBuffer...")); 77 | String response = "HTTP/1.1 200 OK\r\n"; 78 | response += "Access-Control-Allow-Origin: *\r\n"; // CORS 79 | response += "Content-disposition: inline; filename=capture.jpg\r\n"; 80 | response += "Content-type: image/jpeg\r\n\r\n"; 81 | web_server.sendContent(response); 82 | 83 | // Send frame 84 | web_server.client().write((char *)fb->buf, fb->len); 85 | Serial.println(F("[HTTP] FrameBuffer sent")); 86 | 87 | // Empty the buffer 88 | if(fb) { 89 | esp_camera_fb_return(fb); 90 | fb = NULL; 91 | } 92 | } 93 | 94 | 95 | void handle_update_form(){ 96 | Serial.println(F("[HTTP] '/update_form' requested")); 97 | 98 | String html = pre_main + update_form + post_main; 99 | web_server.sendHeader("Connection", "close"); 100 | 101 | web_server.send(200, "text/html", html); 102 | } 103 | 104 | 105 | void handle_update(){ 106 | String upload_status; 107 | 108 | if(Update.hasError()) upload_status = "Upload failed"; 109 | else upload_status = "Upload success"; 110 | 111 | String html = pre_main + upload_status + post_main; 112 | web_server.sendHeader("Connection", "close"); 113 | web_server.send(200, "text/html", html); 114 | ESP.restart(); 115 | 116 | } 117 | 118 | void handle_update_upload() { 119 | HTTPUpload& upload = web_server.upload(); 120 | if (upload.status == UPLOAD_FILE_START) { 121 | Serial.setDebugOutput(true); 122 | Serial.printf("Update: %s\n", upload.filename.c_str()); 123 | if (!Update.begin()) { //start with max available size 124 | Update.printError(Serial); 125 | } 126 | } 127 | else if (upload.status == UPLOAD_FILE_WRITE) { 128 | if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { 129 | Update.printError(Serial); 130 | } 131 | } 132 | else if (upload.status == UPLOAD_FILE_END) { 133 | if (Update.end(true)) { //true to set the size to the current progress 134 | Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); 135 | } 136 | else { 137 | Update.printError(Serial); 138 | } 139 | Serial.setDebugOutput(false); 140 | } 141 | } 142 | 143 | void handle_not_found() { 144 | String html = pre_main + not_found + post_main; 145 | web_server.sendHeader("Connection", "close"); 146 | web_server.send(200, "text/html", html); 147 | } 148 | 149 | void handle_root() { 150 | Serial.println(F("[HTTP] '/' requested")); 151 | 152 | String root_main = "

Home

" 153 | "
IP address: "+ WiFi.localIP().toString() +"
"; 154 | 155 | String html = pre_main + root_main + post_main; 156 | web_server.sendHeader("Connection", "close"); 157 | web_server.send(200, "text/html", html); 158 | } 159 | -------------------------------------------------------------------------------- /wifi.ino: -------------------------------------------------------------------------------- 1 | void wifi_setup() { 2 | // Settings 3 | WiFi.mode(WIFI_STA); 4 | WiFi.begin(WIFI_SSID,WIFI_PASSWORD); 5 | Serial.println(F("[WIFI] Connecting...")); 6 | } 7 | 8 | void wifi_connection_manager(){ 9 | 10 | static int wifi_connected = -1; // 1: connected, 0: disconnected, -1: unknown 11 | 12 | if(WiFi.status() != WL_CONNECTED) { 13 | if(wifi_connected != 0){ 14 | // Wifi connection status changed to "disconnected" 15 | wifi_connected = 0; 16 | Serial.println(F("[WiFi] Disconnected")); 17 | } 18 | } else { 19 | if(wifi_connected != 1){ 20 | // Wifi connection status changed to "connected" 21 | wifi_connected = 1; 22 | Serial.print(F("[WiFi] Connected, IP: ")); 23 | Serial.println(WiFi.localIP()); 24 | } 25 | } 26 | 27 | } 28 | --------------------------------------------------------------------------------