Skip to content

Arduino Sketch to turn an ESP32 dev board into a configurable MQTT motion detector

Notifications You must be signed in to change notification settings

BeardedTek-com/esp32-cam-motion-detector

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 

Repository files navigation

esp32-cam-motion-detector

Arduino Sketch to turn an ESP32 dev board into a configurable MQTT motion detector 20220509_010204

A bit more robust and a few more features:

  • Enable/Disable Motion Detection
    • configured by publishing "enable" or "disable" to motion/office/settings/enable
  • Configurable "Heartbeat"
    • MQTT Topic: motion/office/online
    • reports onliine every 60 seconds
    • configured by publishing a new interval in ms to motion/office/settings/heartbeat
      • By default between 60000 - 300000 (1 minute - 5 minutes)
  • Configurable Motion "Lockout"
    • Stops motion detection for X miliseconds after motion detected
    • configured by publishing a new interval in ms to motion/office/settings/lockout
      • By default between 1000 - 60000 (1-60 seconds)
  • LED Indication
    • By default runs on pin 12 (works on FreeNove wrover dev board)
/**
 * ESP32 Camera as Motion Detector
 * Based on https://eloquentarduino.com/projects/esp32-arduino-motion-detection
 * Tested using ESP32 wrover dev board w/ qqvga camera only
 * 
 * In Arduino IDE you need the following extra libraries installed:
 *   - EloquantArduino
 *   - PubSubClient
 */

// REQUIRED SETTINGS:

// Include WiFi and MQTT PubSub
#include <WiFi.h>
#include <PubSubClient.h>

// WiFi Setup *REQUIRED*
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

// MQTT Broker Setup *REQUIRED*
const char* mqtt_server = "YOUR_MQTT_BROKER";           // MQTT Broker IP/URL
const int mqtt_port = 1883;                         // MQTT Broker Port


// MQTT Pub Topics **REQUIRED**
const char* onlinetopic = "motion/office/online";                 // Tells us it's online. (heartbeat)
unsigned long heartbeatInterval = 60000;                    // Heartbeat interval in ms (60000 = 60 seconds)
const char* motiontopic = "motion/office/status";                 // Tells us if there's motion or not
const char* lockoutTimePub = "motion/office/newLockout";          // Tells us new Motion Lockout time when set
const char* heartbeatIntervalPub = "motion/office/newHeartbeat";  // Tells us new Heartbeat Interval when set

//MQTT Topics to change settings
const char* MQTTheartbeat = "motion/office/settings/heartbeat";   // set heartbeat interval
const int MQTTheartbeatMIN = 59999;                               // 1 minute
const int MQTTheartbeatMAX = 300001;                              // 5 minutes
const char* MQTTmotionenable = "motion/office/settings/enable";   // "enable" or "disable"
const char* MQTTmotionlockout = "motion/office/settings/lockout"; // time in ms to keep motion triggered
const int MQTTmotionlockoutMIN = 999;
const int MQTTmotionlockoutMAX = 60001;
// MQTT Sub Topics
const char* subtopic = "motion/office/settings/+";

// LED Pin
#define LEDPIN 12



// NO NEED TO EDIT BELOW THIS LINE UNLESS YOU WANT TO CHANGE THINGS


#include "eloquent.h"
#include "eloquent/vision/motion/naive.h"


// uncomment based on your camera and resolution

//#include "eloquent/vision/camera/ov767x/gray/vga.h"
//#include "eloquent/vision/camera/ov767x/gray/qvga.h"
//#include "eloquent/vision/camera/ov767x/gray/qqvga.h"
//#include "eloquent/vision/camera/esp32/aithinker/gray/vga.h"
//#include "eloquent/vision/camera/esp32/aithinker/gray/qvga.h"
//#include "eloquent/vision/camera/esp32/aithinker/gray/qqvga.h"
//#include "eloquent/vision/camera/esp32/wrover/gray/vga.h"
//#include "eloquent/vision/camera/esp32/wrover/gray/qvga.h"
#include "eloquent/vision/camera/esp32/wrover/gray/qqvga.h"
//#include "eloquent/vision/camera/esp32/eye/gray/vga.h"
//#include "eloquent/vision/camera/esp32/eye/gray/qvga.h"
//#include "eloquent/vision/camera/esp32/eye/gray/qqvga.h"
//#include "eloquent/vision/camera/esp32/m5/gray/vga.h"
//#include "eloquent/vision/camera/esp32/m5/gray/qvga.h"
//#include "eloquent/vision/camera/esp32/m5/gray/qqvga.h"
//#include "eloquent/vision/camera/esp32/m5wide/gray/vga.h"
//#include "eloquent/vision/camera/esp32/m5wide/gray/qvga.h"
//#include "eloquent/vision/camera/esp32/m5wide/gray/qqvga.h"



// Setup Detection Variables
bool motionEnabled = true;
bool detection = false;
bool motionEvent = false;
unsigned long motionLockoutTime = 5000;
unsigned long detectionStartTime = millis();
unsigned long lastHeartbeat = millis();
bool initialHeartbeat = false;


WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

#define THUMB_WIDTH 32
#define THUMB_HEIGHT 24


Eloquent::Vision::Motion::Naive<THUMB_WIDTH, THUMB_HEIGHT> detector;


void setup() {
  
    pinMode(LEDPIN, OUTPUT);
    digitalWrite(LEDPIN, LOW);
    delay(4000);
    Serial.begin(115200);

    // turn on high freq for fast streaming speed
    camera.setHighFreq();

    if (!camera.begin())
        eloquent::abort(Serial, "Camera init error");

    Serial.println("Camera init OK");

    // wait for at least 10 frames to be processed before starting to detect
    // motion (false triggers at start)
    // then, when motion is detected, don't trigger for the next 10 frames
    detector.startSinceFrameNumber(10);
    detector.debounceMotionTriggerEvery(10);

    // or, in one call
    detector.throttle(10);

    // trigger motion when at least 10% of pixels change intensity by
    // at least 15 out of 255
    detector.setPixelChangesThreshold(0.1);
    detector.setIntensityChangeThreshold(15);
    setup_wifi();
    client.setServer(mqtt_server, 1883);
    client.setCallback(callback);
}

void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;
  
  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();

  // MQTT Actions
  
  // Heartbeat Interval
  if (String(topic) == MQTTheartbeat){
    unsigned long newHeartbeatInterval = strtoul(messageTemp.c_str(),NULL,0);
    Serial.print("Received new Heartbeat Interval: ");
    Serial.println(newHeartbeatInterval);
    if ((newHeartbeatInterval-MQTTheartbeatMIN)*(newHeartbeatInterval-MQTTheartbeatMAX)){
      heartbeatInterval = newHeartbeatInterval;
      Serial.print("Heartbeat Interval Set To :");
      Serial.print(newHeartbeatInterval);
      Serial.println(" via MQTT");
      client.publish(heartbeatIntervalPub,messageTemp.c_str());
    }
    else{
      Serial.print("New Heartbeat Interval Must be > ");
      Serial.println(MQTTheartbeatMIN);
      Serial.print("Heartbeat Interval Remains: ");
      Serial.println(heartbeatInterval);
    }    
  }

  
  // Motion Lockout Time
  if (String(topic) == MQTTmotionlockout){
    unsigned long newLockoutTime = strtoul(messageTemp.c_str(),NULL,0);
    Serial.print("Received new lockout: ");
    Serial.println(newLockoutTime);
    if ((newLockoutTime-MQTTmotionlockoutMIN)*(newLockoutTime-MQTTmotionlockoutMAX)){
      motionLockoutTime = newLockoutTime;
      Serial.print("Motion Lockout Time Set To :");
      Serial.print(newLockoutTime);
      Serial.println(" via MQTT");
      client.publish(lockoutTimePub,messageTemp.c_str());
    }
    else{
      Serial.print("New Lockout Time Must be > ");
      Serial.println(MQTTmotionlockoutMIN);
      Serial.print("Lockout Time Remains: ");
      Serial.println(motionLockoutTime);
    }    
  }

  // Motion Detection Enable/Disable
  if (String(topic) == MQTTmotionenable){
    if (messageTemp == "enable"){
      if (motionEnabled == false){
        motionEnabled = true;
        Serial.println("Motion Sensor Enabled");
        client.publish(MQTTmotionenable, "Enabled");
      }
    }
    if (messageTemp == "disable"){
      if (motionEnabled == true){
        motionEnabled = false;
        Serial.println("MotionSensor Disabled");
        client.publish(MQTTmotionenable, "Disabled");
      }
    }
  }

  
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266Client")) {
      Serial.println("connected");
      // Subscribe
      client.subscribe(subtopic);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void detect_motion(){
    if (camera.capture()){
        // perform motion detection on resized image for fast detection
        camera.image.resize<THUMB_WIDTH, THUMB_HEIGHT>();
        //camera.image.printAsJsonTo(Serial);
        detector.update(camera.image);
    
        // if motion is detected, print coordinates to serial in JSON format
        if (detector.isMotionDetected()) {
            detection = true;
        }
        else{
            detection = false;
        }
        // release memory
        camera.free();
    }
    else {
      delay(1000);
      detect_motion();
    }
}
void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();                                // Listen to MQTT Broker

  // Get current execution time
  unsigned long currentTime = millis();         // Used for HEARTBEAT and MOTION DETECTION

  // BEGIN HEARTBEAT
  if (initialHeartbeat){
    unsigned long timeoutHeartbeat = lastHeartbeat + heartbeatInterval;
    if (currentTime > timeoutHeartbeat){
      Serial.print("Heartbeat: ");
      Serial.println(currentTime);
      client.publish(onlinetopic,"online");
      lastHeartbeat = millis();
    }
  }
  if (!initialHeartbeat){
    Serial.print("Heartbeat: ");
    Serial.println(currentTime);
    client.publish(onlinetopic,"online");
    lastHeartbeat = millis();
    initialHeartbeat = true;
  }
  // END HEARTBEAT
  
  
  // BEGIN MOTION DETECTION
  if (motionEnabled){
    if (motionEvent){
      unsigned long timeout = detectionStartTime + motionLockoutTime;
      if (currentTime > timeout){
        detect_motion();
        if (!detection){
          Serial.println("Motion Detection Event End");
          client.publish(motiontopic, "off");   // Publish motiontopic "off"
          digitalWrite(LEDPIN, LOW);            // Turn off LED
          motionEvent = false;
        }
      }
    }
    else{
      detect_motion();
      if (detection){
        Serial.println("Motion Detection Event Begin");
        client.publish(motiontopic, "on");   // Publish motiontopic "on"
        digitalWrite(LEDPIN, HIGH);               // Turn on LED
        detectionStartTime = millis();
        motionEvent = true;
      }
    }
  }
  // END MOTION DETECTION
}

About

Arduino Sketch to turn an ESP32 dev board into a configurable MQTT motion detector

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages