From b547310699271188ac8588b6102d9418206a3bff Mon Sep 17 00:00:00 2001 From: Alan <60433566+alanesq@users.noreply.github.com> Date: Tue, 20 Oct 2020 07:29:09 +0100 Subject: [PATCH] Add files via upload --- Misc/esp32cam-demo-with NTP support.ino | 1000 +++++++++++++++++++++++ 1 file changed, 1000 insertions(+) create mode 100644 Misc/esp32cam-demo-with NTP support.ino diff --git a/Misc/esp32cam-demo-with NTP support.ino b/Misc/esp32cam-demo-with NTP support.ino new file mode 100644 index 0000000..07c0dd7 --- /dev/null +++ b/Misc/esp32cam-demo-with NTP support.ino @@ -0,0 +1,1000 @@ + /******************************************************************************************************************* + * + * ESP32Cam development board demo sketch using Arduino IDE + * Github: https://github.com/alanesq/ESP32Cam-demo + * + * Starting point sketch for projects using the esp32cam development board with the following features + * web server with live video streaming + * sd card support (using 1bit mode to free some io pins) + * io pins available for use are 13 and 12 (12 must be low at boot) + * flash led is still available for use on pin 4 and does not flash when accessing sd card + * + * + * - created using the Arduino IDE with ESP32 module installed (https://dl.espressif.com/dl/package_esp32_index.json) + * 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, time from NTP servers and motion detection + * + * Example of how to use the image data: https://forum.arduino.cc/?topic=708266#msg4760255 + * + * + * + * 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. + * + * + *******************************************************************************************************************/ + + +#include "esp_camera.h" // https://github.com/espressif/esp32-camera +#include +#include + + +// --------------------------------------------------------------- +// -SETTINGS +// --------------------------------------------------------------- + + // Wifi settings (enter your wifi network details) + + const char* ssid = ""; + const char* password = ""; + + const char* stitle = "ESP32Cam-demo-NTP"; // title of this sketch + const char* sversion = "20Oct20"; // Sketch version + + const bool debugInfo = 1; // show additional debug info. on serial port (1=enabled) + + // 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: + // default = "const framesize_t FRAME_SIZE_IMAGE = FRAMESIZE_XGA" + // 160x120 (QQVGA), 128x160 (QQVGA2), 176x144 (QCIF), 240x176 (HQVGA), + // 320x240 (QVGA), 400x296 (CIF), 640x480 (VGA, default), 800x600 (SVGA), + // 1024x768 (XGA), 1280x1024 (SXGA), 1600x1200 (UXGA) + int cameraImageExposure = 0; // Camera exposure (0 - 1200), if gain and exposure set to zero then auto adjust is enabled + int cameraImageGain = 0; // Image gain (0 - 30) + + const int TimeBetweenStatus = 600; // speed of flashing system running ok status light (milliseconds) + + const int indicatorLED = 33; // onboard status LED pin (33) + + const int brightLED = 4; // onboard flash LED pin (4) + + const int iopinA = 13; // general io pin + const int iopinB = 12; // general io pin (must be low at boot) + + const int serialSpeed = 115200; // Serial data speed to use + + + +// ****************************************************************************************************************** + + +WebServer server(80); // serve web pages on port 80 + +#include "soc/soc.h" // Used to disable brownout detection +#include "soc/rtc_cntl_reg.h" + +#include "SD_MMC.h" // sd card - see https://randomnerdtutorials.com/esp32-cam-take-photo-save-microsd-card/ +#include +#include // gives file access +#define SD_CS 5 // sd chip select pin = 5 + +// Define 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 + bool sdcardPresent; // flag if an sd card is detected + int imageCounter; // image file name on sd card counter + +// camera settings (for the standard - OV2640 - CAMERA_MODEL_AI_THINKER) +// see: https://randomnerdtutorials.com/esp32-cam-camera-pin-gpios/ + #define CAMERA_MODEL_AI_THINKER + #define PWDN_GPIO_NUM 32 // power to camera on/off + #define RESET_GPIO_NUM -1 // -1 = not used + #define XCLK_GPIO_NUM 0 + #define SIOD_GPIO_NUM 26 // i2c sda + #define SIOC_GPIO_NUM 27 // i2c scl + #define Y9_GPIO_NUM 35 + #define Y8_GPIO_NUM 34 + #define Y7_GPIO_NUM 39 + #define Y6_GPIO_NUM 36 + #define Y5_GPIO_NUM 21 + #define Y4_GPIO_NUM 19 + #define Y3_GPIO_NUM 18 + #define Y2_GPIO_NUM 5 + #define VSYNC_GPIO_NUM 25 // vsync_pin + #define HREF_GPIO_NUM 23 // href_pin + #define PCLK_GPIO_NUM 22 // pixel_clock_pin + + camera_config_t config; + + +// Time from NTP server +// from https://raw.githubusercontent.com/RalphBacon/No-Real-Time-Clock-RTC-required---use-an-NTP/master + #include + #include // UDP library which is how we communicate with Time Server + const uint16_t localPort = 8888; // Just an open port we can use for the UDP packets coming back in + const char timeServer[] = "uk.pool.ntp.org"; + const uint16_t NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message + byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets + WiFiUDP NTPUdp; // A UDP instance to let us send and receive packets over UDP + const uint16_t timeZone = 0; // timezone (0=GMT) + const String DoW[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; + // How often to resync the time (under normal and error conditions) + const uint16_t _resyncSeconds = 7200; // 7200 = 2 hours + const uint16_t _resyncErrorSeconds = 60; // 60 = 1 min + bool NTPok = 0; // Flag if NTP is curently connecting ok + + +// ****************************************************************************************************************** + + +// --------------------------------------------------------------- +// -SETUP SETUP SETUP SETUP SETUP SETUP +// --------------------------------------------------------------- + +void setup() { + + Serial.begin(serialSpeed); // Start serial communication + + Serial.println("\n\n\n"); // line feeds + Serial.println("-----------------------------------"); + Serial.printf("Starting - %s - %s \n", stitle, sversion); + Serial.println("-----------------------------------"); + + WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Turn-off the 'brownout detector' + + // Define small indicator led + pinMode(indicatorLED, OUTPUT); + digitalWrite(indicatorLED,HIGH); + + // Connect to wifi + digitalWrite(indicatorLED,LOW); // small indicator led on + Serial.print("\nConnecting to "); + Serial.print(ssid); + Serial.print("\n "); + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.print("\nWiFi connected, "); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + server.begin(); + digitalWrite(indicatorLED,HIGH); // small indicator led off + + // define the web pages (i.e. call these procedures when url is requested) + server.on("/", handleRoot); // root page + server.on("/stream", handleStream); // stream live video + server.on("/photo", handlePhoto); // save image to sd card + server.on("/img", handleImg); // show image from sd card + server.onNotFound(handleNotFound); // invalid url requested + + // set up camera + Serial.print(("\nInitialising camera: ")); + if (setupCameraHardware()) Serial.println("OK"); + else { + Serial.println("Error!"); + showError(2); // critical error so stop and flash led + } + + // SD Card - if one is detected set 'sdcardPresent' High + 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/ + 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 + 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); + Serial.printf("SD Card found, free space = %dMB \n", SDfreeSpace); + sdcardPresent = 1; // flag sd card available + } + } + fs::FS &fs = SD_MMC; // sd card file system + + // discover the number of image files stored in '/img' folder of the sd card and set image file counter accordingly + imageCounter = 0; + if (sdcardPresent) { + int tq=fs.mkdir("/img"); // create the '/img' folder on sd card (in case it is not already there) + if (!tq) Serial.println("Unable to create IMG folder on sd card"); + + // open the image folder and step through all files in it + File root = fs.open("/img"); + while (true) + { + File entry = root.openNextFile(); // open next file in the folder + if (!entry) break; // if no more files in the folder + imageCounter ++; // increment image counter + entry.close(); + } + root.close(); + Serial.printf("Image file count = %d",imageCounter); + } + + // define io pins + pinMode(indicatorLED, OUTPUT); // defined again as sd card config can reset it + digitalWrite(indicatorLED,HIGH); // led off = High + pinMode(brightLED, OUTPUT); // flash LED + digitalWrite(brightLED,LOW); // led off = Low + pinMode(iopinA, OUTPUT); // pin 13 - free io pin, can be input or output + pinMode(iopinB, OUTPUT); // pin 12 - free io pin, can be input or output (must be low at boot) + + if (!psramFound()) { + Serial.println("Warning: No PSRam found so defaulting to image size 'CIF'"); + framesize_t FRAME_SIZE_IMAGE = FRAMESIZE_CIF; + } + + // start NTP + NTPUdp.begin(localPort); // What port will the UDP/NTP packet respond on? + setSyncProvider(getNTPTime); // What is the function that gets the time (in ms since 01/01/1900)? + setSyncInterval(_resyncErrorSeconds); // How often should we synchronise the time on this machine (in seconds) + + Serial.println("\n\nStarted..."); + + cameraImageSettings(); // Apply camera image settings + +} // setup + + + +// ****************************************************************************************************************** + + +// ---------------------------------------------------------------- +// -LOOP LOOP LOOP LOOP LOOP LOOP LOOP +// ---------------------------------------------------------------- + + +void loop() { + + server.handleClient(); // handle any incoming web page requests + + + + + + + // <<< your code here >>> + + + + + +// // Capture an image and save to sd card every 5 seconds +// if ((unsigned long)(millis() - lastCamera) >= 5000) { +// lastCamera = millis(); // reset timer +// storeImage(); // save an image to sd card +// } + + + // flash status LED to show sketch is running + if ((unsigned long)(millis() - lastStatus) >= TimeBetweenStatus) { + lastStatus = millis(); // reset timer + digitalWrite(indicatorLED,!digitalRead(indicatorLED)); // flip indicator led status + time_t t=now(); // read current time to ensure NTP auto refresh keeps triggering (otherwise only triggers when time is required causing a delay in response) + } + +} // loop + + + +// ****************************************************************************************************************** + + +// ---------------------------------------------------------------- +// Configure the camera +// ---------------------------------------------------------------- +// returns TRUE if sucessful + +bool setupCameraHardware() { + + 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; // XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental) + config.pixel_format = PIXFORMAT_JPEG; // 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), 1600x1200 (UXGA) + config.jpeg_quality = 5; // 0-63 lower number means higher quality + config.fb_count = 1; // if more than one, i2s runs in continuous mode. Use only with JPEG + + esp_err_t camerr = esp_camera_init(&config); // initialise the camera + if (camerr != ESP_OK) Serial.printf("ERROR: Camera init failed with error 0x%x", camerr); + + + return (camerr == ESP_OK); // return boolean result of camera initialisation +} + + + +// ****************************************************************************************************************** + + +// ---------------------------------------------------------------- +// Misc small procedures +// ---------------------------------------------------------------- + + + +// flash the indicator led 'reps' number of times +void flashLED(int reps) { + for(int x=0; x < reps; x++) { + digitalWrite(indicatorLED,LOW); + delay(1000); + digitalWrite(indicatorLED,HIGH); + delay(500); + } +} + + + +// critical error - stop sketch and continually flash error code on indicator led +void showError(int errorNo) { + while(1) { + flashLED(errorNo); + delay(4000); + } +} + + + +// ****************************************************************************************************************** + + +// ---------------------------------------------------------------- +// Capture image from camera and save to sd card +// ---------------------------------------------------------------- +// returns TRUE if sucessful + +bool storeImage() { + + if (sdcardPresent) { + if (debugInfo) Serial.printf("Storing image #%d to sd card \n", imageCounter); + } else { + if (debugInfo) Serial.println("Storing image requested but there is no sd card"); + return 0; // no sd card available so exit procedure + } + + fs::FS &fs = SD_MMC; // sd card file system + bool tResult = 0; // result flag + + // capture live image from camera + if (flashRequired) digitalWrite(brightLED,HIGH); // turn flash on + camera_fb_t *fb = esp_camera_fb_get(); // capture image frame from camera + digitalWrite(brightLED,LOW); // turn flash off + if (!fb) { + Serial.println("Error: Camera capture failed"); + flashLED(3); + } + + // save the image to sd card + String SDfilename = "/img/" + String(imageCounter + 1) + ".jpg"; // build the image file name + File file = fs.open(SDfilename, FILE_WRITE); // create file on sd card + if (!file) { + Serial.println("Error: Failed to create file on sd-card: " + SDfilename); + flashLED(4); + } else { + if (file.write(fb->buf, fb->len)) { // File created ok so save image to it + if (debugInfo) Serial.println("Image saved to sd card"); + tResult = 1; // set sucess flag + imageCounter ++; // increment image counter + } else { + Serial.println("Error: failed to save image to sd card"); + flashLED(4); + } + file.close(); // close image file on sd card + } + esp_camera_fb_return(fb); // return frame so memory can be released + + return tResult; // return image save sucess flag + +} // storeImage + + + +// ****************************************************************************************************************** + + +// ---------------------------------------------------------------- +// -root web page requested i.e. http://x.x.x.x/ +// ---------------------------------------------------------------- + +void handleRoot() { + + WiFiClient client = server.client(); // open link with client + + // log the page request including clients IP address + if (debugInfo) { + IPAddress cip = client.remoteIP(); + Serial.printf("Root page requested from: %d.%d.%d.%d \n", cip[0], cip[1], cip[2], cip[3]); + } + + + // 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"); + if (server.hasArg("button1")) { + digitalWrite(iopinA,!digitalRead(iopinA)); // toggle output pin on/off + if (debugInfo) 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 (debugInfo) Serial.println("Button 2 pressed"); + } + + // if button3 was pressed (toggle flash LED) + if (server.hasArg("button3")) { + digitalWrite(brightLED,!digitalRead(brightLED)); // toggle flash LED on/off + if (debugInfo) Serial.println("Button 3 pressed"); + } + + // if exposure was adjusted - cameraImageExposure + if (server.hasArg("exp")) { + String Tvalue = server.arg("exp"); // read value + if (Tvalue != NULL) { + int val = Tvalue.toInt(); + if (val >= 0 && val <= 1200 && val != cameraImageExposure) { + if (debugInfo) Serial.printf("Exposure changed to %d\n", val); + cameraImageExposure = val; + cameraImageSettings(); // Apply camera image settings + } + } + } + + // if image gain was adjusted - cameraImageGain + if (server.hasArg("gain")) { + String Tvalue = server.arg("gain"); // read value + if (Tvalue != NULL) { + int val = Tvalue.toInt(); + if (val >= 0 && val <= 31 && val != cameraImageGain) { + if (debugInfo) Serial.printf("Gain changed to %d\n", val); + cameraImageGain = val; + cameraImageSettings(); // Apply camera image settings + } + } + } + + + // html header + client.write(" root \n"); // basic html header + client.write("
\n"); // used by the buttons in the html (action = the web page to send it to + + + // -------------------------------------------------------------------- + + + // html main body + // 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); + else client.write("

No SD Card detected

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

Pin 13 is Low

\n"); + else client.write("

Pin 13 is High

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

Pin 12 is Low

\n"); + else client.write("

Pin 12 is High

\n"); + + // Read time from NTP + client.print("

Time: " + currentTime() + "

"); + + // Control bottons + client.write(" \n"); + client.write(" \n"); + client.write("
\n"); + + // 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"); + + // links to the other pages available + client.write("
LINKS: \n"); + client.write("Capture an image - \n"); + client.write("View stored images - \n"); + client.write("Live stream
\n"); + + + + // -------------------------------------------------------------------- + + + // end html + client.write("
\n"); + delay(3); + client.stop(); + +} // handleRoot + + +// ****************************************************************************************************************** + + +// ---------------------------------------------------------------- +// -photo save to sd card i.e. http://x.x.x.x/photo +// ---------------------------------------------------------------- + +void handlePhoto() { + + WiFiClient client = server.client(); // open link with client + + // log page request including clients IP address + if (debugInfo) { + IPAddress cip = client.remoteIP(); + Serial.printf("Photo requested from: %d.%d.%d.%d \n", cip[0], cip[1], cip[2], cip[3]); + } + + // save an image to sd card + bool sRes = storeImage(); // save an image to sd card (store sucess or failed flag) + + // html header + client.write(" photo \n"); // basic html header + + // html body + if (sRes == 1) { + client.printf("

Image saved to sd card as image number %d

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

Error: Failed to save image to sd card

\n"); + } + + // end html + client.write("\n"); + delay(3); + client.stop(); + +} // handlePhoto + + + + + + +// ---------------------------------------------------------------- +// -show image from sd card i.e. http://x.x.x.x/img?img=x +// ---------------------------------------------------------------- +// default image = most recent +// returns 1 if image displayed ok + +bool handleImg() { + + WiFiClient client = server.client(); // open link with client + + // log page request including clients IP address + if (debugInfo) { + IPAddress cip = client.remoteIP(); + 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 + if (server.hasArg("img")) { + String Tvalue = server.arg("img"); // read value + imgToShow = Tvalue.toInt(); // convert string to int + if (imgToShow < 1 || imgToShow > imageCounter) imgToShow = imageCounter; // validate image number + } + + if (debugInfo) 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"); + if (timg) { + size_t sent = server.streamFile(timg, "image/jpeg"); // send the image + timg.close(); + } else { + if (debugInfo) Serial.println("Error: image file not found"); + WiFiClient client = server.client(); // open link with client + client.write(" \n"); + client.write("

Error: Image not foundlen; // 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' + client.write(buf, strlen(buf)); // send result (image size) + client.write((char *)fb->buf, s); // send the image data + 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 (debugInfo) Serial.println("Video stream stopped"); + delay(3); + client.stop(); + + +} // handleStream + + +// ****************************************************************************************************************** + +// ---------------------------------------------------------------- +// -Change camera settings +// ---------------------------------------------------------------- +// Returns TRUE is successful + +bool cameraImageSettings() { + + sensor_t *s = esp_camera_sensor_get(); + if (s == NULL) { + Serial.println("Error: problem getting camera sensor settings"); + return 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 + } else { + // Apply manual settings + s->set_gain_ctrl(s, 0); // auto gain 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) + } + + return 1; +} // cameraImageSettings + + +// // More camera settings available: +// // If you enable gain_ctrl or exposure_ctrl it will prevent a lot of the other settings having any effect +// // more info on settings here: https://randomnerdtutorials.com/esp32-cam-ov2640-camera-settings/ +// s->set_gain_ctrl(s, 0); // auto gain off (1 or 0) +// 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_quality(s, 10); // (0 - 63) +// 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_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_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) +// s->set_aec2(s, 0); // automatic exposure sensor? (0 or 1) +// s->set_ae_level(s, 0); // auto exposure levels (-2 to 2) +// s->set_bpc(s, 0); // black pixel correction +// s->set_wpc(s, 0); // white pixel correction + + + + + + + +// -------------------------------------------------------------------------------------------------------------- +// NTP Real Time +// -------------------------------------------------------------------------------------------------------------- + + +// ---------------------------------------------------------------- +// -Return current time and date as string +// ---------------------------------------------------------------- +// supplies time in the format: '23-04-2020_09-23-10_Mon' + +String currentTime() { + + time_t t=now(); // get current time + t+=timeZone; // adjust for timezone + + if (IsBST()) t+=3600; // add one hour if it is Summer Time + + String ttime = formatDateNumber(day(t)); + ttime += "-"; + ttime += formatDateNumber(month(t)); + ttime += "-"; + ttime += formatDateNumber(year(t)); + ttime += "_"; + ttime += formatDateNumber(hour(t)); + ttime += "-"; + ttime += formatDateNumber(minute(t)); + ttime += "-"; + ttime += formatDateNumber(second(t)); + ttime += "_"; + ttime += DoW[weekday(t)-1]; + + return ttime; +} + + +// convert number to String and add leading zero if required +String formatDateNumber(int input) { + String tval = ""; + if (input < 10) tval = "0"; // add leading zero if required + tval += String(input); + return tval; +} + + + +//----------------------------------------------------------------------------- +// -British Summer Time check +//----------------------------------------------------------------------------- + +// returns true if it is British Summer time +// code from https://my-small-projects.blogspot.com/2015/05/arduino-checking-for-british-summer-time.html + +boolean IsBST() +{ + int imonth = month(); + int iday = day(); + int hr = hour(); + + //January, february, and november are out. + if (imonth < 3 || imonth > 10) { return false; } + //April to September are in + if (imonth > 3 && imonth < 10) { return true; } + + // find last sun in mar and oct - quickest way I've found to do it + // last sunday of march + int lastMarSunday = (31 - (5* year() /4 + 4) % 7); + //last sunday of october + int lastOctSunday = (31 - (5 * year() /4 + 1) % 7); + + //In march, we are BST if is the last sunday in the month + if (imonth == 3) { + + if( iday > lastMarSunday) + return true; + if( iday < lastMarSunday) + return false; + + if (hr < 1) + return false; + + return true; + + } + //In October we must be before the last sunday to be bst. + //That means the previous sunday must be before the 1st. + if (imonth == 10) { + + if( iday < lastOctSunday) + return true; + if( iday > lastOctSunday) + return false; + + if (hr >= 1) + return false; + + return true; + } + +} + + + +//----------------------------------------------------------------------------- +// send an NTP request to the time server at the given address +//----------------------------------------------------------------------------- + +void sendNTPpacket(const char* address) { + + // set all bytes in the buffer to 0 + memset(packetBuffer, 0, NTP_PACKET_SIZE); + // Initialize values needed to form NTP request + // (see URL above for details on the packets) + packetBuffer[0] = 0b11100011; // LI, Version, Mode + packetBuffer[1] = 0; // Stratum, or type of clock + packetBuffer[2] = 6; // Polling Interval + packetBuffer[3] = 0xEC; // Peer Clock Precision + // 8 bytes of zero for Root Delay & Root Dispersion + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; + + // all NTP fields have been given values, now you can send a packet requesting a timestamp: + // Note that Udp.begin will request automatic translation (via a DNS server) from a + // name (eg pool.ntp.org) to an IP address. Never use a specific IP address yourself, + // let the DNS give back a random server IP address + NTPUdp.beginPacket(address, 123); //NTP requests are to port 123 + + // Get the data back + NTPUdp.write(packetBuffer, NTP_PACKET_SIZE); + + // All done, the underlying buffer is now updated + NTPUdp.endPacket(); + +} + + + +//----------------------------------------------------------------------------- +// contact the NTP pool and retrieve the time +//----------------------------------------------------------------------------- +// +// code from https://github.com/RalphBacon/No-Real-Time-Clock-RTC-required---use-an-NTP + +time_t getNTPTime() { + + // Send a UDP packet to the NTP pool address + Serial.print("\nSending NTP packet to "); + Serial.println(timeServer); + sendNTPpacket(timeServer); + + // Wait to see if a reply is available - timeout after X seconds. At least + // this way we exit the 'delay' as soon as we have a UDP packet to process + #define UDPtimeoutSecs 3 + int timeOutCnt = 0; + while (NTPUdp.parsePacket() == 0 && ++timeOutCnt < (UDPtimeoutSecs * 10)){ + delay(100); + // yield(); + } + + // Is there UDP data present to be processed? Sneak a peek! + if (NTPUdp.peek() != -1) { + // We've received a packet, read the data from it + NTPUdp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer + + // The time-stamp starts at byte 40 of the received packet and is four bytes, + // or two words, long. First, extract the two words: + unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); + unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); + + // combine the four bytes (two words) into a long integer + // this is NTP time (seconds since Jan 1 1900) + unsigned long secsSince1900 = highWord << 16 | lowWord; // shift highword 16 binary places to the left then combine with lowword + Serial.print("Seconds since Jan 1 1900 = "); + Serial.println(secsSince1900); + + // now convert NTP time into everyday time: + //Serial.print("Unix time = "); + + // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: + const unsigned long seventyYears = 2208988800UL; // UL denotes it is 'unsigned long' + + // subtract seventy years: + unsigned long epoch = secsSince1900 - seventyYears; + + // Reset the interval to get the time from NTP server in case we previously changed it + setSyncInterval(_resyncSeconds); + NTPok = 1; // flag NTP is currently connecting ok + + return epoch; + } + + // Failed to get an NTP/UDP response + Serial.println("No response received from NTP"); + setSyncInterval(_resyncErrorSeconds); // try more frequently until a response is received + NTPok = 0; // flag NTP not currently connecting + + return 0; + +} + + + + +// ****************************************************************************************************************** +// end