eloquentarduino/EloquentArduino

Detect motion and save to SD card

paulohlcosta opened this issue · 9 comments

First, thank you very much for this amazing funciton eloquentarduino!

I tryed your code with my ESP32-CAM, and it worked well via serial. I am a beginner in coding and it helped me a lot!
Now I am trying to make a version that will capture motion in the best quality possible saving frames to the SD Card with a 1000 ms interval. I started by mixing 2 codes, but no success yet.

I followed alan's conversation in the topic https://eloquentarduino.github.io/2020/01/motion-detection-with-esp32-cam-only-arduino-version/ and would like to suggest a code where the board detects motion by itself and save good quality pictures in the SD card.

Refer:
https://github.com/RuiSantosdotme/ESP32-CAM-Arduino-IDE/tree/master/ESP32-CAM-Take-Photo-Save-MicroSD-Card

Again, thank you very much!

Edit2: here's my code:
https://github.com/paulohlcosta/esp32-cam_motion_detection_sdcard/blob/master/esp32-cam_motion_detection_sdcard.ino

I'll probably do this in a follow-up post, since it is the most natural continuation. Need to get an SD card to test.

Hello,
I have been playing with some of your code this morning. I would find an SD card saving option extremely useful aswell.

Additionally I was trying to follow the code on here: Eloquent Easy ESP32 camera HTTP video streaming link. The combination of http and motion detection no longer works. I think that the motion detection code has been updated and some of the variables no longer apply,

@tennisparty You're totally right on the HTTP Server + Motion detection, I forgot to update the code on the blog. I will fix this during the weekend, thank you for pointing it out.

About the SD card, yes, many of you readers ask this feature: I will sure look into this too.

Thankyou, that's great :) much appreciated. I look forward to trying it out.

I also have a ov7725 camera for my esp32 - one of these; ov7725. I bought it for low light use (recording bats 🦇) but am struggling to get it to work, I'm wondering whether this is because it does not work with jpeg (I get this error when I try it). Entirely out of interest do you think it would be possible to for me to adapt your code to use it? All of the code online uses jpeg streams so I get the impression I won't be able to!

Hello, did you get a chance to update the code at the weekend, im really keen to try it

I didn't looked at this. I'm trying now, but it is not as easy as I thought because motion detection needs the image to be in PIXFORMAT_GRAYSCALE, but the web server needs the image to be in PIXFORMAT_JPEG. If I try to simply call camera.begin(FRAMESIZE_QVGA, PIXFORMAT_JPEG) before server.start(), my board crashes. I need to investigate further this issue. I'm putting the updated sketch on my blog, nonetheless, so you can try it.

Regarding the SD card, I don't have one so I cannot test, but it should work out of the box: replace File imageFile = SPIFFS.open("your-filename.jpg") with File imageFile = SD.open("your-filename.jpg"). Can you please report if you succed? (of course you have to properly initialize the SD)

Thanks, I have got your example code working with the SD card and the ov2640 and the ov7725 on an AI_THINKER board with the code below. Currently it just continually writes over your-filename2.jpg on the SD card.

// The models available are
//   - CAMERA_MODEL_WROVER_KIT
//   - CAMERA_MODEL_ESP_EYE
//   - CAMERA_MODEL_M5STACK_PSRAM
//   - CAMERA_MODEL_M5STACK_WIDE
//   - CAMERA_MODEL_AI_THINKER
#define CAMERA_MODEL_AI_THINKER

#include <Arduino.h>
#include <FS.h>
#include <SPIFFS.h>
#include "EloquentVision.h"
#include "SD_MMC.h"            // SD Card ESP32

// Pin definition for CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27
#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
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22

#define FRAME_SIZE FRAMESIZE_QVGA
#define SOURCE_WIDTH 320
#define SOURCE_HEIGHT 240
#define CHANNELS 1
#define DEST_WIDTH 32
#define DEST_HEIGHT 24
#define BLOCK_VARIATION_THRESHOLD 0.3
#define MOTION_THRESHOLD 0.2

using namespace Eloquent::Vision;
using namespace Eloquent::Vision::IO;
using namespace Eloquent::Vision::ImageProcessing;
using namespace Eloquent::Vision::ImageProcessing::Downscale;
using namespace Eloquent::Vision::ImageProcessing::DownscaleStrategies;

// an easy interface to capture images from the camera
ESP32Camera camera;
// the buffer to store the downscaled version of the image
uint8_t resized[DEST_HEIGHT][DEST_WIDTH];
// the downscaler algorithm
// for more details see https://eloquentarduino.github.io/2020/05/easier-faster-pure-video-esp32-cam-motion-detection
Cross<SOURCE_WIDTH, SOURCE_HEIGHT, DEST_WIDTH, DEST_HEIGHT> crossStrategy;
// the downscaler container
Downscaler<SOURCE_WIDTH, SOURCE_HEIGHT, CHANNELS, DEST_WIDTH, DEST_HEIGHT> downscaler(&crossStrategy);
// the motion detection algorithm
MotionDetection<DEST_WIDTH, DEST_HEIGHT> motion;
JpegWriter<SOURCE_WIDTH, SOURCE_HEIGHT> jpegWriter;

void startSDtasks();
bool debounceMotion(bool touch = false);
void printFilesize(const char *filename);


/**
 *
 */
void setup() {
    Serial.begin(115200);
    SPIFFS.begin(true);
    camera.begin(FRAME_SIZE, PIXFORMAT_GRAYSCALE);
    motion.setBlockVariationThreshold(BLOCK_VARIATION_THRESHOLD);

  //Serial.println("Starting SD Card");
  if(!SD_MMC.begin()){
    Serial.println("SD Card Mount Failed");
    return;
  }
 
  uint8_t cardType = SD_MMC.cardType();
  if(cardType == CARD_NONE){
    Serial.println("No SD Card attached");
    return;
  }
}

/**
 *
 */
void loop() {
    uint32_t start = millis();
    camera_fb_t *frame = camera.capture();

    // resize image and detect motion
    downscaler.downscale(frame->buf, resized);
    motion.update(resized);
    motion.detect();

    // compute FPS
    Serial.print(1000.0f / (millis() - start));
    Serial.println(" fps");

    // on motion detected, save image to filesystem
    if (motion.ratio() > MOTION_THRESHOLD) {
        Serial.println("Motion detected");

        // save image
        if (debounceMotion()) {
            // take a new pic in the hope it is less affected by the motion noise
            // (you may comment this out if you want)
            delay(500);
            frame = camera.capture();

            // write as jpeg
            File imageFile = SD_MMC.open("/your-filename2.jpg", FILE_WRITE);
            // you can tweak this value as per your needs
            uint8_t quality = 30;

            if(!imageFile){
                Serial.println("Opening file failed");
                return;
            }
            Serial.println("The image will be saved as /capture.jpg");
            jpegWriter.writeGrayscale(imageFile, frame->buf, quality);
            imageFile.close();
            printFilesize("/capture.jpg");

            debounceMotion(true);
        }
    }
}

/**
 * Debounce repeated motion detections
 * @return
 */
bool debounceMotion(bool touch) {
    static uint32_t lastMotion = 0;

    // update last tick
    if (lastMotion == 0 || touch)
        lastMotion = millis();

    // debounce
    if (millis() - lastMotion > 5000) {
        lastMotion = millis();

        return true;
    }

    return false;
}


/**
 * Print file size (for debug)
 * @param filename
 */
void printFilesize(const char *filename) {
    File file = SD_MMC.open(filename, "r");
    Serial.print(filename);
    Serial.print(" size is ");
    Serial.print(file.size() / 1000);
    Serial.println(" kb");

    file.close();
}

I'd love a version which buffers video the psram and then can save the buffered frames and the next few frames when motion is detected. Currently this code has a bit of lag between the motion event and the flash going off (picture being taken).

Look forward the webserver streaming. There is some jpeg conversion code in this expressif repository which might be helpful for a stream? https://github.com/espressif/esp32-camera/tree/master/conversions