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!");
}