0

I'm trying to create a motion/fall sensing device. I have a sensor that continuously checks if motion and fall is detected in void loop(). If both motion and fall are detected, then I'd like to send an image to my computer. My image capture function is camCapture(). The problem is that once the motion and fall detected, it continuously calls camCapture() and repeatedly sends images to the computer given that camCapture() is called in void loop(). I'd like to only send a single image when it is triggered, and a single image every time it is triggered thereafter. For example, if the motion and fall is triggered, then send an image once. If it detects motion and fall again after it has already gone back to LOW, then send an image again once.

I can't use setup() since I can only run a function once, and it defeats the purpose of what I'm trying to do. My void loop() code is below.

void loop()
{
  // Read data from accelerometer
  mma.read();  
  sensors_event_t event;
  mma.getEvent(&event);


  // Store magnitude of all axis
  magnitude = calculateMagnitude(event.acceleration.x, event.acceleration.y, event.acceleration.z);

  // Motion is detected! 20 seconds will be given to detect a fall from the accelerometer
  if(digitalRead(pirSensor) == HIGH)
  {
    num_timeAtPIR = millis(); // Time that PIR sensor was activated with respect to the system time
    bool_waitForAccel = 1; // Set flag to true to wait for accelerometer
    Serial.println("Motion detected!");
    digitalWrite(buzzer, LOW);
    //delay(1000);

  }
  else if(magnitude > sensitivity)
  {
    num_timeAtAccel = millis();
    Serial.println("Fall detected!");
    Serial.print("Magnitude is: ");
    Serial.println(magnitude);
    bool_waitForPIR = 1; // Waiting for PIR
    Serial.println();

    digitalWrite(buzzer, HIGH);
    delay(4000);

  }
  else // No motion is detected
  {
    Serial.println("No motion detected! :(");
    bool_waitForPIR = 0;
    bool_waitForAccel = 0;
    Firebase.setInt(firebaseData, pirPath, 0);
    Firebase.setInt(firebaseData, accelPath, 0);
    digitalWrite(buzzer, LOW);
  }

  // Motion then Fall are detected. Alarm is activated and picture is captured and save onto server.
  if(bool_waitForAccel == 1 && (magnitude > sensitivity))
  {

    Serial.println("Motion and Fall detected!");
    Serial.print("Magnitude is: ");
    Serial.println(magnitude);
    bool_waitForAccel = 0; // No longer waiting for accelerometer
    Serial.println();


    digitalWrite(buzzer, HIGH);
    delay(4000);

    camCapture(); // Take picture!

  }

  // Fall then motion are detected. Alarm is activated and picture/video is captured to save onto website.
  else if(bool_waitForPIR == 1 && (magnitude > sensitivity))
  {
    Serial.println("Fall detected");
    Serial.print("Magnitude is: ");
    Serial.println(magnitude);
    Serial.println();

    bool_waitForPIR = 0; // No longer waiting for PIR

    digitalWrite(buzzer, HIGH);

    delay(4000);


    camCapture(); // Take picture!

  }

  // 20 second timer to check if a Fall is detected after Motion is detected.
  if(millis() > (num_timeAtPIR + period))
  {
    bool_waitForAccel = 0; // Set flag to false
  }
  else if(millis() > (num_timeAtAccel + period)) // 20 second timer to check if Motion is detected after a fall is detected
  {
    bool_waitForPIR = 0;
  }


}

UPDATE 1: Posting the rest of the code.

// Program header files
#include <Wire.h>
#include <WiFi.h>
#include <WebServer.h>
#include <ArduCAM.h>
#include <SPI.h>
#include "FS.h"
#include "FirebaseESP32.h"
#include "memorysaver.h"
#include <Adafruit_MMA8451.h>
#include <Adafruit_Sensor.h>

//Time and Date NTP Server Header
#include <NTPClient.h>
#include <WiFiUdp.h>

//Define NTP Client to get time 
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

//Variables to save date and time 
String formattedDate = "";
String dayStamp = "";
String timeStamp = "";

String logPath = "/Log";
String tempPath = "/Log/Temperature";
String pirPath = "/Log/PIR_Sensor";
String accelPath = "/Log/Accelerometer";
String pirDatePath = "/Log/pir_Date";
String pirTimePath = "/Log/pir_Time";
String accelDatePath = "/Log/accel_Date";
String accelTimePath = "/Log/accel_Time";

// Database/Wi-Fi configuration
#define FIREBASE_HOST "host"
#define FIREBASE_AUTH "auth"
#define AP_SSID "apssid"
#define AP_PASSWORD "password"

#define WIFI_SSID "ssid"
#define WIFI_PASSWORD "passsword"

// Variables to keep track of previous and current time to create a delay
unsigned long currentTime = 0;
unsigned long prevTime = 0;

unsigned long bool_waitForAccel = 0; // Used to flag the accelerometer
unsigned long bool_waitForPIR = 0; // Used to flag the PIR
unsigned long num_timeAtPIR = 0; // Used to keep track of the time the PIR sensor detected motion
unsigned long num_timeAtAccel = 0; // Used to keep track of the time the accelerometer detected a fall
unsigned long period = 20000; // Amount of time given to check after motion is detected


int statusLED = 26; // Choose the pin for status LED
int buzzer = 12; // Choose the pin for Piezzo Buzzer. Connected from MOSFET gate.
int tempSensor = 2; // Choose pin for Temperature Sensor
int pirSensor = 27; // Choose pin for controller PIR Sensor

int PIR_Power = 25; // Controls Power gate for PIR Sensor
int ArduCAM_Power = 32; // Controls Power gate for ArduCAM

const int CS = 5; // GPIO5 as Slave Select for ArduCAM


int wifiType = 0; // 0: Station    1: AP (Access Point)


// Set up requests to send captured image to external server
bool onlineMode = true;
String start_request = "";
String boundary = "_cam_";
String end_request = "\n--" + boundary + "--\n";

// Set buffer size for camera data
static const size_t bufferSize = 2048;
static uint8_t buffer[bufferSize] = {0xFF};
byte buf[256];
static int iPic = 0;
static int kPic = 0;
uint8_t temp = 0, temp_last = 0;
uint32_t len = 0;
bool is_header = false;


// Threshhold for declaring a fall on the accelerometer
float sensitivity = 11.0;

float magnitude = 0.0; // Stores the calculated magnitude


// Accelerometer, Semaphore, and Firebase instances
Adafruit_MMA8451 mma = Adafruit_MMA8451();
FirebaseData firebaseData;

#if defined (OV2640_MINI_2MP) || defined (OV2640_CAM)
ArduCAM myCAM(OV2640, CS);
#endif

WiFiClient client;

void start_capture() 
{
  myCAM.clear_fifo_flag();
  myCAM.start_capture();
}

void camCapture()
{
  digitalWrite(ArduCAM_Power, HIGH);

  // Flush the FIFO
  myCAM.flush_fifo();
  // Clear capture done flag
  myCAM.clear_fifo_flag();
  // Start capture
  myCAM.start_capture();


  Serial.println("Start capture!");
  while(!myCAM.get_bit(ARDUCHIP_TRIG, CAP_DONE_MASK));

  len = myCAM.read_fifo_length();
  Serial.print("The FIFO length is: ");
  Serial.println(len);

  if(len >= MAX_FIFO_SIZE) // 8M
  {
    Serial.println("Over size!");
  }
  if(len == 0) // 0KB
  {
    Serial.println("Size is 0KB"); 
    return; 
  }

  long full_length;
  if(client.connect("xx.xx.xx.xx", 80) || onlineMode)
  {
    if(onlineMode)
    {
      while(client.available())
      {
        String line = client.readStringUntil('\r');
      }
    }


  start_request = start_request + 
  "\n--" + boundary + "\n" + 
  "Content-Disposition: form-data; name=\"data\"; filename=\"CAM.jpg\"\n" + 
  "Content-Transfer-Encoding: binary\n\n";


  full_length = start_request.length() + len + end_request.length();
  client.println("POST /uploads.php HTTP/1.1");
  client.println("Host: xx.xx.xx.x");
  client.println("Content-Type: multipart/form-data; boundary =" + boundary);
  client.print("Content-Length: "); client.println(full_length);
  client.println();
  client.print(start_request);

  iPic = 0;

  static uint8_t buffer[bufferSize] = {0xFF};

  while (len--)
  {
    temp_last = temp;
    temp =  SPI.transfer(0x00);
    //Read JPEG data from FIFO
    if ( (temp == 0xD9) && (temp_last == 0xFF) ) //If find the end ,break while,
    {
      buf[iPic++] = temp;  //save the last  0XD9
      //Write the remain bytes in the buffer
      myCAM.CS_HIGH();
      client.write(buf, iPic);
      //Close the file
      //file.close();
      Serial.println(F("Image save OK."));
      is_header = false;
      iPic = 0;
    }
    if (is_header == true)
    {
      //Write image data to buffer if not full
      if (iPic < 256)
        buf[iPic++] = temp;
      else
      {
        //Write 256 bytes image data to file
        myCAM.CS_HIGH();
        client.write(buf, 256);
        iPic = 0;
        buf[iPic++] = temp;
        myCAM.CS_LOW();
        myCAM.set_fifo_burst();
      }
    }
    else if ((temp == 0xD8) & (temp_last == 0xFF))
    {
      is_header = true;
      buf[iPic++] = temp_last;
      buf[iPic++] = temp;
    }
  }

  // End the POST request to the server
  client.println(end_request);

  myCAM.CS_HIGH();

  // Stop the client
  client.stop();
  digitalWrite(ArduCAM_Power, LOW);
  }
  else // No connection to the server. 
  {
    Serial.println("Could not connect to external server!");
    Serial.println("Please check the IP address or Port");
    return;
  }
}

// Calculate the magnitude with the x, y, and z axis of the accelerometer.
// Gives an overall value that is better than just using the z-axis
float calculateMagnitude(float x, float y, float z)
{
  float magnitude = sqrt(sq(x) + sq(y) + sq(z));

  return magnitude;
}

void setup() 
{
  Wire.begin();
  Serial.begin(115200);
  SPI.begin();
  SPI.setFrequency(4000000);

  pinMode(CS, OUTPUT);
  pinMode(PIR_Power, OUTPUT); // Set mosfet gate to output for PIR Sensor
  pinMode(ArduCAM_Power, OUTPUT); // Set mosfet gate to output for ArduCAM

  pinMode(statusLED, OUTPUT);      // Declare LED as output
  pinMode(buzzer, OUTPUT);         // Declare Piezo Buzzer as output
  pinMode(pirSensor, INPUT);        // Declare PIR Sensor as input

  Serial.println();

  // Check if accelerometer is properly connected/found
  if(!mma.begin())
  {
    Serial.println("Couldn't start MMA8451 Accelerometer.");
    Serial.println("Check your connections.");
    while(1);
  }
  Serial.println("MMA8451 Accelerometer found!");

  // Set the g-range for the accelerometer. Can be adjusted to 2g, 4g, or 8g.
  mma.setRange(MMA8451_RANGE_2_G);

  // Check if camera is found
  digitalWrite(ArduCAM_Power, HIGH);
  uint8_t vid, pid;
  uint8_t temp;
  while(1)
  {
    //Check if the ArduCAM SPI bus is OK
    myCAM.write_reg(ARDUCHIP_TEST1, 0x55);
    temp = myCAM.read_reg(ARDUCHIP_TEST1);
    if (temp != 0x55) {
      Serial.println(F("SPI interface Error!"));
      delay(2);
      continue;
    }
    else
      break;
  }

  #if defined (OV2640_MINI_2MP) || defined (OV2640_CAM)
  //Check if the camera module type is OV2640
  myCAM.wrSensorReg8_8(0xff, 0x01);
  myCAM.rdSensorReg8_8(OV2640_CHIPID_HIGH, &vid);
  myCAM.rdSensorReg8_8(OV2640_CHIPID_LOW, &pid);
  if ((vid != 0x26 ) && (( pid != 0x41 ) || ( pid != 0x42 )))
    Serial.println(F("Can't find OV2640 module!"));
  else
    Serial.println(F("OV2640 detected."));
  #endif

  //Change to JPEG capture mode and initialize the OV2640 module
  myCAM.set_format(JPEG);
  myCAM.InitCAM();

  myCAM.OV2640_set_JPEG_size(OV2640_640x480);
  myCAM.clear_fifo_flag();
  // Turn off Camera
  digitalWrite(ArduCAM_Power, LOW);


  // Connect to WiFi
  if (wifiType == 0)
  {
    if(!strcmp(WIFI_SSID,"SSID"))
    {
      Serial.println(F("Please set your SSID"));
      while(1);
    }
    if(!strcmp(WIFI_PASSWORD,"PASSWORD"))
    {
      Serial.println(F("Please set your PASSWORD"));
      while(1);
    }
    // Connect to WiFi network
    Serial.println();
    Serial.print(F("Connecting to: "));
    Serial.println(WIFI_SSID);

    WiFi.mode(WIFI_STA);

    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

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

    Serial.println();
    Serial.println(F("WiFi connected"));
    Serial.println();
    Serial.println(WiFi.localIP());
  }
  else if (wifiType == 1)
  {
    Serial.println();
    Serial.println();
    Serial.print(F("Shared AP: "));
    Serial.println(AP_SSID);
    Serial.print(F("The password is: "));
    Serial.println(AP_PASSWORD);

    WiFi.mode(WIFI_AP);

    WiFi.softAP(AP_SSID, AP_PASSWORD);

    Serial.println();
    Serial.print("AP IP Address is: ");
    Serial.println(WiFi.softAPIP());
  }

  // Initialize a NTPClient to get time
  timeClient.begin();
  timeClient.setTimeOffset(-14400);

  //If Firebase is connected, turn on the LED
  Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);
  Firebase.reconnectWiFi(true);
  digitalWrite(statusLED,HIGH);

  Serial.println("------------------------------------");
  Serial.println("Path exist test...");
  if (Firebase.pathExist(firebaseData, logPath))
  {
    Serial.println("Path " + logPath + " exists");
  }
  else
  {
    Serial.println("Path " + logPath + " does not exist");
    Serial.println("Adding all necessary paths to /Log");
    Serial.println("Will have to reinitialize later once everything is set up.");

     // Initialize All  variables to 0;
    Firebase.setFloat(firebaseData, tempPath, 0.0);
    Firebase.setInt(firebaseData, pirPath, 0);
    Firebase.setInt(firebaseData, accelPath, 0);
    //Firebase.setString(firebaseData, pirDatePath, ???);
    //Firebase.setString(firebaseData, pirTimePath, ???);
    //Firebase.setString(firebaseData, accelDatePath, ???);
    //Firebase.setString(firebaseData, accelTimePath, ???);

  }
  Serial.println("------------------------------------");
  Serial.println(); 

  digitalWrite(PIR_Power, HIGH);

  digitalWrite(pirSensor, LOW);
  digitalWrite(statusLED, LOW);

  Serial.println("Sensors booting up...");
  delay(10000); // Let sensors boot up, 10s
  Serial.println("Sensors ready!");
}
  • Thank you for the really well done formatting. So many people post questions with totally crap formatting that make the question very difficult to read and understand. – John Romkey Apr 12 at 4:51
  • @JohnRomkey Haha! Thanks, I gotta make everyone’s life a little easier somehow. – fern132 Apr 12 at 5:05
1

You need to add a state variable to your loop() function.

At the start of loop() add a variable declaration like:

void loop()
{
  static bool capture_sent = false;

Then change your line

  if(bool_waitForAccel == 1 && (magnitude > sensitivity))
  {

to

  if(!capture_sent && bool_waitForAccel == 1 && (magnitude > sensitivity))
  {
    capture_sent = true;

or, if you want to perform some of the logic in that stanza and just not call camCapture() then leave the if line alone and make the call to camCapture() conditional upon !capture_sent.

Then in any place in your code where you see the condition which tells you you'd want to call camCapture() again when triggered, reset capture_sent to `false. I'm leaving it up to you to decide where that goes because you understand what conditions determine that.

  • Awesome! Thanks for the reply! I’ll implement those changes in the morning and let you know how it goes. – fern132 Apr 12 at 5:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.