From e8a1298cc6509e7912bd1f78f9ef9c81fdf04dc2 Mon Sep 17 00:00:00 2001 From: Alan <60433566+alanesq@users.noreply.github.com> Date: Sun, 21 Nov 2021 21:21:37 +0000 Subject: [PATCH] now also works with PlatformIO --- esp32cam-demo.ino | 429 ++++++++++++++++++++++++---------------------- platformio.ini | 14 ++ 2 files changed, 239 insertions(+), 204 deletions(-) create mode 100644 platformio.ini diff --git a/esp32cam-demo.ino b/esp32cam-demo.ino index a0fa201..6a575e1 100644 --- a/esp32cam-demo.ino +++ b/esp32cam-demo.ino @@ -1,55 +1,47 @@ /******************************************************************************************************************* - * - * ESP32Cam development board demo sketch using Arduino IDE + * + * ESP32Cam development board demo sketch using Arduino IDE or PlatformIO * Github: https://github.com/alanesq/ESP32Cam-demo - * - * Tested with ESP32 board manager version 1.0.6 - * + * + * Tested with ESP32 board manager version 1.0.6 + * * Starting point sketch for projects using the esp32cam development board with the following features * web server with live video streaming and RGB data from camera demonstrated. * sd card support using 1-bit mode (data pins are usually 2,4,12&13 but using 1bit mode only uses pin 2) * flash led is still available for use (pin 4) and does not flash when accessing sd card * Stores image in Spiffs if no sd card present * PWM control of the illumination/flash LED - * + * * GPIO: * You can use io pins 13 and 12 for input or output (but 12 must not be high at boot) * pin 16 is used for psram but you may get away with using it as input for a button etc. * You could also use pins 1 & 3 if you do not use Serial (disable serialDebug in the settings below) * Pins 14, 2 & 15 should be ok to use if you are not using an SD Card * More info: https://randomnerdtutorials.com/esp32-cam-ai-thinker-pinout/ - * + * * You can use a MCP23017 io expander chip to give 16 gpio lines by enabling 'useMCP23017' in the setup section and connecting * the i2c pins to 12 and 13 on the esp32cam module. Note: this requires the adafruit MCP23017 library to be installed. - * + * * Created using the Arduino IDE with ESP32 module installed, no additional libraries required * ESP32 support for Arduino IDE: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json - * + * * Info on the esp32cam board: https://randomnerdtutorials.com/esp32-cam-video-streaming-face-recognition-arduino-ide/ - * + * * To see a more advanced sketch along the same format as this one have a look at https://github.com/alanesq/CameraWifiMotion * which includes email support, FTP, OTA updates and motion detection - * + * * Sending image to TFT display see: https://www.survivingwithandroid.com/esp32-cam-tft-display-picture-st7735/ - * - * esp32cam-demo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the + * + * esp32cam-demo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - * + * + * *******************************************************************************************************************/ #if !defined ESP32 #error This sketch is only for an ESP32Cam module #endif -#include "esp_camera.h" // https://github.com/espressif/esp32-camera -// #include "camera_pins.h" - - -// ****************************************************************************************************************** - - - // --------------------------------------------------------------------------------------------------------- @@ -65,6 +57,37 @@ // --------------------------------------------------------------------------------------------------------- +#include // required by PlatformIO +#include "esp_camera.h" // https://github.com/espressif/esp32-camera +// #include "camera_pins.h" +#include +#include +#include // used by requestWebPage() +#include "driver/ledc.h" // used to configure pwm on illumination led + +// spiffs used to store images without an sd card + #include + #include // gives file access on spiffs + + +// forward declarations (required by PlatformIO) + bool initialiseCamera(); + bool cameraImageSettings(); + String localTime(); + void flashLED(int reps); + void showError(int errorNo); + byte storeImage(); + void handleRoot(); + void handlePhoto(); + bool handleImg(); + void handleNotFound(); + void readRGBImage(); + bool getNTPtime(int sec); + void handleStream(); + String requestWebPage(String ip, String page, int port, int maxChars, String cuttoffText); + void handleTest(); + void brightLed(byte ledBrightness); + void MessageRGB(WiFiClient &client, String theText); // --------------------------------------------------------------- @@ -72,7 +95,7 @@ // --------------------------------------------------------------- const char* stitle = "ESP32Cam-demo"; // title of this sketch - const char* sversion = "08Oct21"; // Sketch version + const char* sversion = "21Nov21"; // Sketch version bool sendRGBfile = 0; // if set '/rgb' will send the rgb data as a file rather than display some on a HTML page @@ -82,12 +105,12 @@ // Camera related const bool flashRequired = 1; // If flash to be used when capturing image (1 = yes) - const framesize_t FRAME_SIZE_IMAGE = FRAMESIZE_VGA; // Image resolution: + const framesize_t FRAME_SIZE_IMAGE = FRAMESIZE_VGA; // Image resolution: // default = "const framesize_t FRAME_SIZE_IMAGE = FRAMESIZE_VGA" - // 160x120 (QQVGA), 128x160 (QQVGA2), 176x144 (QCIF), 240x176 (HQVGA), - // 320x240 (QVGA), 400x296 (CIF), 640x480 (VGA, default), 800x600 (SVGA), + // 160x120 (QQVGA), 128x160 (QQVGA2), 176x144 (QCIF), 240x176 (HQVGA), + // 320x240 (QVGA), 400x296 (CIF), 640x480 (VGA, default), 800x600 (SVGA), // 1024x768 (XGA), 1280x1024 (SXGA), 1600x1200 (UXGA) - #define PIXFORMAT PIXFORMAT_JPEG; // image format, Options = YUV422, GRAYSCALE, RGB565, JPEG, RGB888 + #define PIXFORMAT PIXFORMAT_JPEG; // image format, Options = YUV422, GRAYSCALE, RGB565, JPEG, RGB888 int cameraImageExposure = 0; // Camera exposure (0 - 1200) If gain and exposure both set to zero then auto adjust is enabled int cameraImageGain = 0; // Image gain (0 - 30) @@ -100,12 +123,12 @@ int brightLEDbrightness = 0; // initial brightness (0 - 255) const int ledFreq = 5000; // PWM settings const int ledChannel = 15; // needs to be 14 or 15 with esp32cam (timer3?) - const int ledRresolution = 8; + const int ledRresolution = 8; const int iopinA = 13; // general io pin 13 const int iopinB = 12; // general io pin 12 (must not be high at boot) const int iopinC = 16; // input only pin 16 (used by PSRam but you may get away with using it for a button) - + const int serialSpeed = 115200; // Serial data speed to use // NTP - Internet time @@ -114,7 +137,7 @@ long unsigned lastNTPtime; tm timeinfo; time_t now; - + // camera settings (for the standard - OV2640 - CAMERA_MODEL_AI_THINKER) // see: https://randomnerdtutorials.com/esp32-cam-camera-pin-gpios/ // set camera resolution etc. in 'initialiseCamera()' and 'cameraImageSettings()' @@ -137,29 +160,20 @@ #define PCLK_GPIO_NUM 22 // pixel_clock_pin - + // ****************************************************************************************************************** -#include -#include -#include // used by requestWebPage() -#include "driver/ledc.h" // used to configure pwm on illumination led - -// spiffs used to store images without an sd card - #include - #include // gives file access on spiffs - WebServer server(80); // serve web pages on port 80 -// Used to disable brownout detection - #include "soc/soc.h" - #include "soc/rtc_cntl_reg.h" +// Used to disable brownout detection + #include "soc/soc.h" + #include "soc/rtc_cntl_reg.h" // sd-card #include "SD_MMC.h" // sd card - see https://randomnerdtutorials.com/esp32-cam-take-photo-save-microsd-card/ - #include - #include // gives file access + #include + #include // gives file access #define SD_CS 5 // sd chip select pin = 5 // MCP23017 IO expander on pins 12 and 13 (optional) @@ -169,7 +183,7 @@ WebServer server(80); // serve web pages on port 80 Adafruit_MCP23017 mcp; // Wire.setClock(1700000); // set frequency to 1.7mhz #endif - + // Define some global variables: uint32_t lastStatus = millis(); // last time status light changed status (to flash all ok led) uint32_t lastCamera = millis(); // timer for periodic image capture @@ -177,9 +191,9 @@ WebServer server(80); // serve web pages on port 80 int imageCounter; // image file name on sd card counter uint32_t illuminationLEDstatus; // current brightness setting of the illumination led String spiffsFilename = "/image.jpg"; // image name to use when storing in spiffs - - - + + + // ****************************************************************************************************************** @@ -188,14 +202,14 @@ WebServer server(80); // serve web pages on port 80 // --------------------------------------------------------------- void setup() { - + if (serialDebug) { - Serial.begin(serialSpeed); // Start serial communication + Serial.begin(serialSpeed); // Start serial communication // Serial.setDebugOutput(true); - + Serial.println("\n\n\n"); // line feeds Serial.println("-----------------------------------"); - Serial.printf("Starting - %s - %s \n", stitle, sversion); + Serial.printf("Starting - %s - %s \n", stitle, sversion); Serial.println("-----------------------------------"); // Serial.print("Reset reason: " + ESP.getResetReason()); } @@ -271,19 +285,19 @@ void setup() { } // SD Card - if one is detected set 'sdcardPresent' High - if (!SD_MMC.begin("/sdcard", true)) { // if loading sd card fails + if (!SD_MMC.begin("/sdcard", true)) { // if loading sd card fails // note: ('/sdcard", true)' = 1bit mode - see: https://www.reddit.com/r/esp32/comments/d71es9/a_breakdown_of_my_experience_trying_to_talk_to_an/ - if (serialDebug) Serial.println("No SD Card detected"); + if (serialDebug) Serial.println("No SD Card detected"); sdcardPresent = 0; // flag no sd card available } else { uint8_t cardType = SD_MMC.cardType(); if (cardType == CARD_NONE) { // if invalid card found - if (serialDebug) Serial.println("SD Card type detect failed"); + if (serialDebug) Serial.println("SD Card type detect failed"); sdcardPresent = 0; // flag no sd card available } else { // valid sd card detected uint16_t SDfreeSpace = (uint64_t)(SD_MMC.totalBytes() - SD_MMC.usedBytes()) / (1024 * 1024); - if (serialDebug) Serial.printf("SD Card found, free space = %dMB \n", SDfreeSpace); + if (serialDebug) Serial.printf("SD Card found, free space = %dMB \n", SDfreeSpace); sdcardPresent = 1; // flag sd card available } } @@ -298,7 +312,7 @@ void setup() { } // open the image folder and step through all files in it - File root = fs.open("/img"); + File root = fs.open("/img"); while (true) { File entry = root.openNextFile(); // open next file in the folder @@ -309,8 +323,8 @@ void setup() { root.close(); if (serialDebug) Serial.printf("Image file count = %d \n",imageCounter); } - - // define i/o pins + + // define i/o pins pinMode(indicatorLED, OUTPUT); // defined again as sd card config can reset it digitalWrite(indicatorLED,HIGH); // led off = High pinMode(iopinA, OUTPUT); // pin 13 - free io pin, can be used for input or output @@ -365,7 +379,7 @@ void loop() { - + // <<< YOUR CODE HERE >>> @@ -374,17 +388,17 @@ void loop() { // // demo to Capture an image and save to sd card every 5 seconds (i.e. time lapse) -// if ( ((unsigned long)(millis() - lastCamera) >= 5000) && sdcardPresent ) { +// if ( ((unsigned long)(millis() - lastCamera) >= 5000) && sdcardPresent ) { // lastCamera = millis(); // reset timer // storeImage(); // save an image to sd card // } - + // flash status LED to show sketch is running ok - if ((unsigned long)(millis() - lastStatus) >= TimeBetweenStatus) { + if ((unsigned long)(millis() - lastStatus) >= TimeBetweenStatus) { lastStatus = millis(); // reset timer digitalWrite(indicatorLED,!digitalRead(indicatorLED)); // flip indicator led status } - + } // loop @@ -398,9 +412,9 @@ void loop() { // returns TRUE if successful bool initialiseCamera() { - + camera_config_t config; - + config.ledc_channel = LEDC_CHANNEL_0; config.ledc_timer = LEDC_TIMER_0; config.pin_d0 = Y2_GPIO_NUM; @@ -421,8 +435,8 @@ bool initialiseCamera() { config.pin_reset = RESET_GPIO_NUM; config.xclk_freq_hz = 20000000; // XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental) config.pixel_format = PIXFORMAT; // Options = YUV422, GRAYSCALE, RGB565, JPEG, RGB888 - config.frame_size = FRAME_SIZE_IMAGE; // Image sizes: 160x120 (QQVGA), 128x160 (QQVGA2), 176x144 (QCIF), 240x176 (HQVGA), 320x240 (QVGA), - // 400x296 (CIF), 640x480 (VGA, default), 800x600 (SVGA), 1024x768 (XGA), 1280x1024 (SXGA), + config.frame_size = FRAME_SIZE_IMAGE; // Image sizes: 160x120 (QQVGA), 128x160 (QQVGA2), 176x144 (QCIF), 240x176 (HQVGA), 320x240 (QVGA), + // 400x296 (CIF), 640x480 (VGA, default), 800x600 (SVGA), 1024x768 (XGA), 1280x1024 (SXGA), // 1600x1200 (UXGA) config.jpeg_quality = 10; // 0-63 lower number means higher quality config.fb_count = 1; // if more than one, i2s runs in continuous mode. Use only with JPEG @@ -433,19 +447,19 @@ bool initialiseCamera() { if (serialDebug) Serial.println("Warning: No PSRam found so defaulting to image size 'CIF'"); config.frame_size = FRAMESIZE_CIF; } - + //#if defined(CAMERA_MODEL_ESP_EYE) // pinMode(13, INPUT_PULLUP); // pinMode(14, INPUT_PULLUP); - //#endif - + //#endif + esp_err_t camerr = esp_camera_init(&config); // initialise the camera if (camerr != ESP_OK) { if (serialDebug) Serial.printf("ERROR: Camera init failed with error 0x%x", camerr); } - cameraImageSettings(); // apply custom camera settings - + cameraImageSettings(); // apply custom camera settings + return (camerr == ESP_OK); // return boolean result of camera initialisation } @@ -461,26 +475,26 @@ bool initialiseCamera() { // - Returns TRUE if successful // BTW - some interesting info on exposure times here: https://github.com/raduprv/esp32-cam_ov2640-timelapse -bool cameraImageSettings() { - - sensor_t *s = esp_camera_sensor_get(); - // something to try?: if (s->id.PID == OV3660_PID) +bool cameraImageSettings() { + + sensor_t *s = esp_camera_sensor_get(); + // something to try?: if (s->id.PID == OV3660_PID) if (s == NULL) { if (serialDebug) Serial.println("Error: problem reading camera sensor settings"); return 0; - } + } // if both set to zero enable auto adjust - if (cameraImageExposure == 0 && cameraImageGain == 0) { + if (cameraImageExposure == 0 && cameraImageGain == 0) { // enable auto adjust - s->set_gain_ctrl(s, 1); // auto gain on - s->set_exposure_ctrl(s, 1); // auto exposure on + s->set_gain_ctrl(s, 1); // auto gain on + s->set_exposure_ctrl(s, 1); // auto exposure on s->set_awb_gain(s, 1); // Auto White Balance enable (0 or 1) } else { // Apply manual settings - s->set_gain_ctrl(s, 0); // auto gain off + s->set_gain_ctrl(s, 0); // auto gain off s->set_awb_gain(s, 1); // Auto White Balance enable (0 or 1) - s->set_exposure_ctrl(s, 0); // auto exposure off + s->set_exposure_ctrl(s, 0); // auto exposure off s->set_agc_gain(s, cameraImageGain); // set gain manually (0 - 30) s->set_aec_value(s, cameraImageExposure); // set exposure manually (0-1200) } @@ -496,19 +510,19 @@ bool cameraImageSettings() { // s->set_exposure_ctrl(s, 0); // auto exposure off (1 or 0) // s->set_agc_gain(s, cameraImageGain); // set gain manually (0 - 30) // s->set_aec_value(s, cameraImageExposure); // set exposure manually (0-1200) -// s->set_vflip(s, cameraImageInvert); // Invert image (0 or 1) +// s->set_vflip(s, cameraImageInvert); // Invert image (0 or 1) // s->set_quality(s, 10); // (0 - 63) -// s->set_gainceiling(s, GAINCEILING_32X); // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128) +// s->set_gainceiling(s, GAINCEILING_32X); // Image gain (GAINCEILING_x2, x4, x8, x16, x32, x64 or x128) // s->set_brightness(s, cameraImageBrightness); // (-2 to 2) - set brightness // s->set_lenc(s, 1); // lens correction? (1 or 0) // s->set_saturation(s, 0); // (-2 to 2) // s->set_contrast(s, cameraImageContrast); // (-2 to 2) -// s->set_sharpness(s, 0); // (-2 to 2) +// s->set_sharpness(s, 0); // (-2 to 2) // s->set_hmirror(s, 0); // (0 or 1) flip horizontally // s->set_colorbar(s, 0); // (0 or 1) - show a testcard // s->set_special_effect(s, 0); // (0 to 6?) apply special effect // s->set_whitebal(s, 0); // white balance enable (0 or 1) -// s->set_awb_gain(s, 1); // Auto White Balance enable (0 or 1) +// s->set_awb_gain(s, 1); // Auto White Balance enable (0 or 1) // s->set_wb_mode(s, 0); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home) // s->set_dcw(s, 0); // downsize enable? (1 or 0)? // s->set_raw_gma(s, 1); // (1 or 0) @@ -590,7 +604,7 @@ byte storeImage() { return 0; } else { - if (file.write(fb->buf, fb->len)) { + if (file.write(fb->buf, fb->len)) { if (serialDebug) { Serial.print("The picture has been saved as " + spiffsFilename); Serial.print(" - Size: "); @@ -600,7 +614,7 @@ byte storeImage() { } else { if (serialDebug) Serial.println("Error: writing image to spiffs...will format and try again"); if (!SPIFFS.format()) { - if (serialDebug) Serial.println("Error: Unable to format Spiffs"); + if (serialDebug) Serial.println("Error: Unable to format Spiffs"); return 0; } file = SPIFFS.open(spiffsFilename, FILE_WRITE); @@ -609,10 +623,10 @@ byte storeImage() { return 0; } } - file.close(); + file.close(); } } - + // save the image to sd card if (sdcardPresent) { if (serialDebug) Serial.printf("Storing image #%d to sd card \n", imageCounter); @@ -624,7 +638,7 @@ byte storeImage() { // return 0 } else { if (file.write(fb->buf, fb->len)) { // File created ok so save image to it - if (serialDebug) Serial.println("Image saved to sd card"); + if (serialDebug) Serial.println("Image saved to sd card"); imageCounter ++; // increment image counter } else { if (serialDebug) Serial.println("Error: failed to save image to sd card"); @@ -634,10 +648,10 @@ byte storeImage() { file.close(); // close image file on sd card } } - + esp_camera_fb_return(fb); // return frame so memory can be released - - if (sdcardPresent) return 2; // saved to sd card + + if (sdcardPresent) return 2; // saved to sd card else return 1; // saved in spiffs } // storeImage @@ -668,18 +682,18 @@ void handleRoot() { // Action any button presses or settings entered on web page // if button1 was pressed (toggle io pin A) - // Note: if using an input box etc. you would read the value with the command: String Bvalue = server.arg("demobutton1"); + // Note: if using an input box etc. you would read the value with the command: String Bvalue = server.arg("demobutton1"); if (server.hasArg("button1")) { digitalWrite(iopinA,!digitalRead(iopinA)); // toggle output pin on/off - if (serialDebug) Serial.println("Button 1 pressed"); + if (serialDebug) Serial.println("Button 1 pressed"); } - + // if button2 was pressed (toggle io pin B) if (server.hasArg("button2")) { digitalWrite(iopinB,!digitalRead(iopinB)); // toggle output pin on/off if (serialDebug) Serial.println("Button 2 pressed"); } - + // if button3 was pressed (toggle flash LED) if (server.hasArg("button3")) { if (brightLEDbrightness == 0) brightLed(10); // turn led on dim @@ -694,7 +708,7 @@ void handleRoot() { String Tvalue = server.arg("exp"); // read value if (Tvalue != NULL) { int val = Tvalue.toInt(); - if (val >= 0 && val <= 1200 && val != cameraImageExposure) { + if (val >= 0 && val <= 1200 && val != cameraImageExposure) { if (serialDebug) Serial.printf("Exposure changed to %d\n", val); cameraImageExposure = val; cameraImageSettings(); // Apply camera image settings @@ -707,7 +721,7 @@ void handleRoot() { String Tvalue = server.arg("gain"); // read value if (Tvalue != NULL) { int val = Tvalue.toInt(); - if (val >= 0 && val <= 31 && val != cameraImageGain) { + if (val >= 0 && val <= 31 && val != cameraImageGain) { if (serialDebug) Serial.printf("Gain changed to %d\n", val); cameraImageGain = val; cameraImageSettings(); // Apply camera image settings @@ -725,25 +739,25 @@ void handleRoot() { // html main body - // Info on the arduino ethernet library: https://www.arduino.cc/en/Reference/Ethernet + // Info on the arduino ethernet library: https://www.arduino.cc/en/Reference/Ethernet // Info in HTML: https://www.w3schools.com/html/ // Info on Javascript (can be inserted in to the HTML): https://www.w3schools.com/js/default.asp // Verify your HTML is valid: https://validator.w3.org/ - - + + client.write("

Hello from ESP32Cam

\n"); // sd card details - if (sdcardPresent) client.printf("

SD Card detected - %d images stored

\n", imageCounter); + if (sdcardPresent) client.printf("

SD Card detected - %d images stored

\n", imageCounter); else client.write("

No SD Card detected

\n"); // io pin details if (digitalRead(iopinA) == LOW) client.write("

Output Pin 13 is Low

\n"); else client.write("

Output Pin 13 is High

\n"); - + if (digitalRead(iopinB) == LOW) client.write("

Output Pin 12 is Low

\n"); else client.write("

Output Pin 12 is High

\n"); - + if (digitalRead(iopinC) == LOW) client.write("

Input Pin 16 is Low

\n"); else client.write("

Input Pin 16 is High

\n"); @@ -753,11 +767,11 @@ void handleRoot() { // Current real time client.print("

Current time: " + localTime() + "

\n"); -// // touch input on the two gpio pins +// // touch input on the two gpio pins // client.printf("

Touch on pin 12: %d

\n", touchRead(T5) ); // client.printf("

Touch on pin 13: %d

\n", touchRead(T4) ); - // Control bottons + // Control bottons client.write(" \n"); client.write(" \n"); client.write("
\n"); @@ -765,21 +779,21 @@ void handleRoot() { // Image setting controls client.write("
CAMERA SETTINGS: \n"); client.printf("Exposure: \n", cameraImageExposure); - client.printf("Gain: \n", cameraImageGain); - client.write(" - Set both to zero for auto adjust
\n"); + client.printf("Gain: \n", cameraImageGain); + client.write(" - Set both to zero for auto adjust
\n"); // links to the other pages available client.write("
LINKS: \n"); - client.write("Capture an image - \n"); - client.write("View stored images - \n"); - client.write("Capture Image as raw RGB data - \n"); - client.write("Live stream
\n"); - - - + client.write("Capture an image - \n"); + client.write("View stored images - \n"); + client.write("Capture Image as raw RGB data - \n"); + client.write("Live stream
\n"); + + + // -------------------------------------------------------------------- - + // end html client.write("\n"); delay(3); @@ -808,21 +822,21 @@ void handlePhoto() { // save an image to sd card or spiffs byte sRes = storeImage(); // save an image to sd card or spiffs (store sucess or failed flag - 0=fail, 1=spiffs only, 2=spiffs and sd card) - + // html header client.write(" photo \n"); // basic html header // html body if (sRes == 2) { client.printf("

Image saved to sd card as image number %d

\n", imageCounter); - client.write("View Image\n"); // link to the image + client.write("View Image\n"); // link to the image } else if (sRes == 1) { client.write("

Image saved in Spiffs

\n"); - client.write("View Image\n"); // link to the image + client.write("View Image\n"); // link to the image } else { - client.write("

Error: Failed to save image to sd card

\n"); + client.write("

Error: Failed to save image to sd card

\n"); } - + // end html client.write("\n"); delay(3); @@ -841,7 +855,7 @@ void handlePhoto() { bool handleImg() { WiFiClient client = server.client(); // open link with client - bool pRes = 0; + bool pRes = 0; // log page request including clients IP address if (serialDebug) { @@ -849,10 +863,10 @@ bool handleImg() { Serial.printf("Image display requested from: %d.%d.%d.%d \n", cip[0], cip[1], cip[2], cip[3]); if (imageCounter == 0) Serial.println("Error: no images to display"); } - + int imgToShow = imageCounter; // default to showing most recent file - // get image number from url parameter + // get image number from url parameter if (server.hasArg("img")) { String Tvalue = server.arg("img"); // read value imgToShow = Tvalue.toInt(); // convert string to int @@ -861,8 +875,8 @@ bool handleImg() { // if stored on sd card if (sdcardPresent) { - if (serialDebug) Serial.printf("Displaying image #%d from sd card", imgToShow); - + if (serialDebug) Serial.printf("Displaying image #%d from sd card", imgToShow); + String tFileName = "/img/" + String(imgToShow) + ".jpg"; fs::FS &fs = SD_MMC; // sd card file system File timg = fs.open(tFileName, "r"); @@ -882,7 +896,7 @@ bool handleImg() { // if stored in SPIFFS if (!sdcardPresent) { - if (serialDebug) Serial.println("Displaying image from spiffs"); + if (serialDebug) Serial.println("Displaying image from spiffs"); File f = SPIFFS.open(spiffsFilename, "r"); // read file from spiffs if (!f) { if (serialDebug) Serial.println("Error reading " + spiffsFilename); @@ -894,8 +908,8 @@ bool handleImg() { } else { pRes = 1; // flag sucess } - f.close(); - } + f.close(); + } } return pRes; } // handleImg @@ -915,7 +929,7 @@ void handleNotFound() { // log page request if (serialDebug) Serial.print("Invalid page requested"); - + tReply = "File Not Found\n\n"; tReply += "URI: "; tReply += server.uri(); @@ -931,7 +945,7 @@ void handleNotFound() { server.send ( 404, "text/plain", tReply ); tReply = ""; // clear variable - + } // handleNotFound @@ -943,10 +957,10 @@ void handleNotFound() { // ---------------------------------------------------------------- //Demonstration on how to access raw RGB data from the camera // Notes: -// Set sendRGBfile to 1 in the settings at top of sketch to just send the rgb data as a file which can then be used with +// Set sendRGBfile to 1 in the settings at top of sketch to just send the rgb data as a file which can then be used with // the Processing sketch: https://github.com/alanesq/esp32cam-demo/blob/master/Misc/displayRGB.pde // otherwise a web page is displayed showing some sample rgb data usage. -// You may want to disable auto white balance when experimenting with RGB otherwise the camera is always trying to adjust the +// You may want to disable auto white balance when experimenting with RGB otherwise the camera is always trying to adjust the // image colours to mainly white. (disable in the 'cameraImageSettings' procedure). // It will fail on the highest resolution (1600x1200) as it requires more than the 4mb of available psram to store the data (1600x1200x3 bytes) // I learned how to read the RGB data from: https://github.com/Makerfabs/Project_Touch-Screen-Camera/blob/master/Camera_v2/Camera_v2.ino @@ -964,43 +978,43 @@ void readRGBImage() { // html header if (!sendRGBfile) client.write(" photo \n"); // basic html header - + MessageRGB(client,"LIVE IMAGE AS RGB DATA"); // 'MessageRGB' sends the String to both serial port and web page - + // ****** the main code for converting an image to RGB data ***** - + // capture a live image from camera (as a jpg) camera_fb_t * fb = NULL; tTimer = millis(); // store time that image capture started - fb = esp_camera_fb_get(); + fb = esp_camera_fb_get(); MessageRGB(client, "Image capture took " + String(millis() - tTimer) + " milliseconds"); // report time it took to capture an image - if (!fb) MessageRGB(client," -error capturing image from camera- "); - MessageRGB(client,"Image resolution=" + String(fb->width) + "x" + String(fb->height)); // display image resolution + if (!fb) MessageRGB(client," -error capturing image from camera- "); + MessageRGB(client,"Image resolution=" + String(fb->width) + "x" + String(fb->height)); // display image resolution + - // allocate memory to store the rgb data (in psram, 3 bytes per pixel) if (!psramFound()) MessageRGB(client," -error no psram found- "); MessageRGB(client,"Free psram before rgb data stored = " + String(heap_caps_get_free_size(MALLOC_CAP_SPIRAM))); void *ptrVal = NULL; // create a pointer for memory location to store the data uint32_t ARRAY_LENGTH = fb->width * fb->height * 3; // calculate memory required to store the RGB data (i.e. number of pixels in the jpg image x 3) if (heap_caps_get_free_size( MALLOC_CAP_SPIRAM) < ARRAY_LENGTH) MessageRGB(client," -error: not enough free psram to store the rgb data- "); - ptrVal = heap_caps_malloc(ARRAY_LENGTH, MALLOC_CAP_SPIRAM); // allocate memory space for the rgb data + ptrVal = heap_caps_malloc(ARRAY_LENGTH, MALLOC_CAP_SPIRAM); // allocate memory space for the rgb data uint8_t *rgb = (uint8_t *)ptrVal; // create the 'rgb' array pointer to the allocated memory space MessageRGB(client,"Free psram after rgb data stored = " + String(heap_caps_get_free_size(MALLOC_CAP_SPIRAM))); - - + + // convert the captured jpg image (fb) to rgb data (store in 'rgb' array) tTimer = millis(); // store time that image conversion process started - bool jpeg_converted = fmt2rgb888(fb->buf, fb->len, PIXFORMAT_JPEG, rgb); - if (!jpeg_converted) MessageRGB(client," -error converting image to RGB- "); + bool jpeg_converted = fmt2rgb888(fb->buf, fb->len, PIXFORMAT_JPEG, rgb); + if (!jpeg_converted) MessageRGB(client," -error converting image to RGB- "); MessageRGB(client, "Conversion from jpg to RGB took " + String(millis() - tTimer) + " milliseconds");// report how long the conversion took - if (sendRGBfile) client.write(rgb, ARRAY_LENGTH); // send the rgb data as a file - + if (sendRGBfile) client.write(rgb, ARRAY_LENGTH); // send the rgb data as a file + // ****** examples of reading the resulting RGB data ***** - + // display some of the resulting data uint32_t resultsToShow = 60; // how much data to display MessageRGB(client,"R , G , B"); @@ -1010,7 +1024,7 @@ void readRGBImage() { // uint16_t x = (i / 3) % fb->width; // uint16_t y = floor( (i / 3) / fb->width); } - + // find the average values for each colour over entire image uint32_t aRed = 0; uint32_t aGreen = 0; @@ -1019,14 +1033,14 @@ void readRGBImage() { aBlue+=rgb[i]; aGreen+=rgb[i+1]; aRed+=rgb[i+2]; - } + } aRed = aRed / (fb->width * fb->height); // divide total by number of pixels to give the average value aGreen = aGreen / (fb->width * fb->height); aBlue = aBlue / (fb->width * fb->height); MessageRGB(client,"Average Blue = " + String(aBlue)); MessageRGB(client,"Average Green = " + String(aGreen)); - MessageRGB(client,"Average Red = " + String(aRed)); - + MessageRGB(client,"Average Red = " + String(aRed)); + // ******************************************************* @@ -1034,11 +1048,11 @@ void readRGBImage() { // end html if (!sendRGBfile) client.write("\n"); delay(3); - client.stop(); + client.stop(); - // finished with the data so free up the space used in psram - esp_camera_fb_return(fb); - heap_caps_free(ptrVal); + // finished with the data so free up the memory space used in psram + esp_camera_fb_return(fb); // camera frame buffer + heap_caps_free(ptrVal); // rgb data } // readRGBImage @@ -1046,15 +1060,15 @@ void readRGBImage() { // send line of text to both serial port and web page void MessageRGB(WiFiClient &client, String theText) { - if (!sendRGBfile) client.print(theText + "
\n"); - if (serialDebug || theText.indexOf('error') > 0) Serial.println(theText); + if (!sendRGBfile) client.print(theText + "
\n"); + if (serialDebug || theText.indexOf("error") > 0) Serial.println(theText); } // ****************************************************************************************************************** - + // ---------------------------------------------------------------- // -get time from ntp server // ---------------------------------------------------------------- @@ -1109,11 +1123,11 @@ void handleStream(){ const int cntLen = strlen(CTNTTYPE); // temp stores - char buf[32]; + char buf[32]; int s; camera_fb_t * fb = NULL; - // send html header + // send html header client.write(HEADER, hdrLen); client.write(BOUNDARY, bdrLen); @@ -1121,7 +1135,7 @@ void handleStream(){ while (true) { if (!client.connected()) break; - fb = esp_camera_fb_get(); // capture live image + fb = esp_camera_fb_get(); // capture live image s = fb->len; // store size of image (i.e. buffer length) client.write(CTNTTYPE, cntLen); // send content type html (i.e. jpg image) sprintf( buf, "%d\r\n\r\n", s ); // format the image's size as html and put in to 'buf' @@ -1130,12 +1144,12 @@ void handleStream(){ client.write(BOUNDARY, bdrLen); // send html boundary see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type esp_camera_fb_return(fb); // return image so memory can be released } - + if (serialDebug) Serial.println("Video stream stopped"); delay(3); client.stop(); - + } // handleStream @@ -1145,67 +1159,73 @@ void handleStream(){ // ---------------------------------------------------------------- // request a web page // ---------------------------------------------------------------- -// parameters = ip address, page to request, port to use (usually 80), maximum chars to receive, ignore all in reply before this text -// e.g. String q = requestWebPage("192.168.1.166","/log",80,600,""); +// @param ip ip address +// @param page web page to request +// @param port ip port to use (usually 80) +// @param maxChars maximum number of chars to receive +// @param cuttoffText ignore all in reply before this text +// @return the reply as a string +// Example usage: requestWebPage("192.168.1.166", "/log", 80, 600, ""); String requestWebPage(String ip, String page, int port, int maxChars, String cuttoffText = ""){ - int maxWaitTime = 3000; // max time to wait for reply (ms) + uint32_t maxWaitTime = 3000; // max time to wait for reply (ms) char received[maxChars + 1]; // temp store for incoming character data - int received_counter = 0; // number of characters which have been received + int received_counter = 0; // counter of number of characters which have been received - if (!page.startsWith("/")) page = "/" + page; // make sure page begins with "/" + if (!page.startsWith("/")) page = "/" + page; // make sure page begins with "/" if (serialDebug) { Serial.print("requesting web page: "); Serial.print(ip); Serial.println(page); } - + WiFiClient client; - // Connect to the site - if (!client.connect(ip.c_str() , port)) { - if (serialDebug) Serial.println("Web client connection failed"); + // Connect to the site + if (!client.connect(ip.c_str() , port)) { + if (serialDebug) Serial.println("Web client connection failed"); return "web client connection failed"; - } + } if (serialDebug) Serial.println("Connected to host - sending request..."); - + // send request - A basic request looks something like: "GET /index.html HTTP/1.1\r\nHost: 192.168.0.4:8085\r\n\r\n" - client.print("GET " + page + " HTTP/1.1\r\n" + - "Host: " + ip + "\r\n" + - "Connection: close\r\n\r\n"); - + client.println("GET " + page + " HTTP/1.1 "); + client.println("Host: " + ip ); + client.println("User-Agent: arduino-ethernet"); + client.println("Connection: close"); + client.println(); // needed to end HTTP header + if (serialDebug) Serial.println("Request sent - waiting for reply..."); - + // Wait for a response uint32_t ttimer = millis(); - while ( !client.available() && (uint32_t)(millis() - ttimer) < maxWaitTime ) { + while ( client.connected() && !client.available() && (uint32_t)(millis() - ttimer) < maxWaitTime ) { delay(10); } + if ( ((uint32_t)(millis() - ttimer) > maxWaitTime ) && serialDebug) Serial.println("-Timed out"); // read the response - while ( client.available() && received_counter < maxChars ) { - #if defined ESP8266 - delay(2); // it just reads 255s on esp8266 if this delay is not included - #endif + while ( client.connected() && client.available() && received_counter < maxChars ) { + delay(4); received[received_counter] = char(client.read()); // read one character received_counter+=1; } received[received_counter] = '\0'; // end of string marker - + if (serialDebug) { Serial.println("--------received web page-----------"); Serial.println(received); Serial.println("------------------------------------"); Serial.flush(); // wait for serial data to finish sending } - + client.stop(); // close connection if (serialDebug) Serial.println("Connection closed"); - // if cuttoffText was supplied then only return the text following this + // if cuttoffText was supplied then only return the text following this if (cuttoffText != "") { char* locus = strstr(received,cuttoffText.c_str()); // locus = pointer to the found text if (locus) { // if text was found @@ -1213,9 +1233,10 @@ String requestWebPage(String ip, String page, int port, int maxChars, String cut return locus; // return the reply text following 'cuttoffText' } else if (serialDebug) Serial.println("The text '" + cuttoffText + "' WAS NOT found in reply"); } - + return received; // return the full reply text -} + +} // requestWebPage @@ -1248,19 +1269,19 @@ void handleTest() { - - - - // < YOUR TEST CODE GOES HERE> + + + + // < YOUR TEST CODE GOES HERE> - - -// // demo useage of the mcp23017 io chip + + +// // demo useage of the mcp23017 io chip // #if useMCP23017 == 1 // while(1) { // mcp.digitalWrite(0, HIGH); @@ -1276,8 +1297,8 @@ void handleTest() { // ------------------------------------------------------------------- - - + + // end html client.write("\n"); delay(3); diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..53dfa0e --- /dev/null +++ b/platformio.ini @@ -0,0 +1,14 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32cam] +platform = espressif32 +board = esp32cam +framework = arduino