Функция запуска при срабатывании

Я пытаюсь создать устройство, реагирующее на движение/падение. У меня есть датчик, который постоянно проверяет, обнаружено ли движение и падение в voidloop(). Если обнаружено и движение, и падение, я хотел бы отправить изображение на свой компьютер. Моя функция захвата изображений — camCapture(). Проблема в том, что после обнаружения движения и падения он постоянно вызывает camCapture() и неоднократно отправляет изображения на компьютер, учитывая, что camCapture() вызывается в void цикл(). Я хотел бы отправлять только одно изображение при его запуске и одно изображение каждый раз, когда оно срабатывает в дальнейшем. Например, если срабатывает движение и падение, то отправьте изображение один раз. Если он обнаружит движение и снова упадет после того, как уже вернулся в НИЗКИЙ режим, отправьте изображение еще раз.

Я не могу использовать setup(), поскольку я могу запустить функцию только один раз, и это противоречит цели того, что я пытаюсь сделать. Мой код voidloop () приведен ниже.

void loop()
{
  // Читаем данные с акселерометра
  mma.read();  
  sensors_event_t event;
  mma.getEvent(&event);


  // Сохраняем величину всех осей
  magnitude = calculateMagnitude(event.acceleration.x, event.acceleration.y, event.acceleration.z);

  // Обнаружено движение! На обнаружение падения с акселерометра будет дано 20 секунд.
  if(digitalRead(pirSensor) == HIGH)
  {
    num_timeAtPIR = millis(); // Время активации PIR-датчика относительно системного времени
    bool_waitForAccel = 1; // Установите флаг в значение true, чтобы дождаться сигнала акселерометра
    Serial.println("Motion detected!");
    digitalWrite(buzzer, LOW);
    //задержка(1000);

  }
  else if(magnitude > sensitivity)
  {
    num_timeAtAccel = millis();
    Serial.println("Fall detected!");
    Serial.print("Magnitude is: ");
    Serial.println(magnitude);
    bool_waitForPIR = 1; // Ожидание PIR
    Serial.println();

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

  }
  else // Движение не обнаружено
  {
    Serial.println("No motion detected! :(");
    bool_waitForPIR = 0;
    bool_waitForAccel = 0;
    Firebase.setInt(firebaseData, pirPath, 0);
    Firebase.setInt(firebaseData, accelPath, 0);
    digitalWrite(buzzer, LOW);
  }

  // Обнаружено движение, затем падение. Сигнализация активируется, изображение захватывается и сохраняется на сервере.
  if(bool_waitForAccel == 1 && (magnitude > sensitivity))
  {

    Serial.println("Motion and Fall detected!");
    Serial.print("Magnitude is: ");
    Serial.println(magnitude);
    bool_waitForAccel = 0; // Больше не ждем акселерометра
    Serial.println();


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

    camCapture(); // Сделать фотографию!

  }

  // Падение, затем обнаружено движение. Сигнализация активируется, и изображение/видео захватывается для сохранения на веб-сайте.
  else if(bool_waitForPIR == 1 && (magnitude > sensitivity))
  {
    Serial.println("Fall detected");
    Serial.print("Magnitude is: ");
    Serial.println(magnitude);
    Serial.println();

    bool_waitForPIR = 0; // Больше не ждем PIR

    digitalWrite(buzzer, HIGH);

    delay(4000);


    camCapture(); // Сделать фотографию!

  }

  // 20-секундный таймер для проверки обнаружения падения после обнаружения движения.
  if(millis() > (num_timeAtPIR + period))
  {
    bool_waitForAccel = 0; // Устанавливаем флаг в значение false
  }
  else if(millis() > (num_timeAtAccel + period)) // 20-секундный таймер для проверки обнаружения движения после обнаружения падения
  {
    bool_waitForPIR = 0;
  }


}

ОБНОВЛЕНИЕ 1: Публикация остальной части кода.

// Заголовочные файлы программы
#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>

//Заголовок NTP-сервера времени и даты
#include <NTPClient.h>
#include <WiFiUdp.h>

//Определяем NTP-клиент для получения времени
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

//Переменные для сохранения даты и времени
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";

// Конфигурация базы данных/Wi-Fi
#define FIREBASE_HOST "host"
#define FIREBASE_AUTH "auth"
#define AP_SSID "apssid"
#define AP_PASSWORD "password"

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

// Переменные для отслеживания предыдущего и текущего времени для создания задержки
unsigned long currentTime = 0;
unsigned long prevTime = 0;

unsigned long bool_waitForAccel = 0; // Используется для обозначения акселерометра
unsigned long bool_waitForPIR = 0; // Используется для обозначения PIR
unsigned long num_timeAtPIR = 0; // Используется для отслеживания времени, когда PIR-датчик обнаружил движение
unsigned long num_timeAtAccel = 0; // Используется для отслеживания времени, когда акселерометр обнаружил падение
unsigned long period = 20000; // Количество времени, отведенное на проверку после обнаружения движения


int statusLED = 26; // Выбираем контакт для светодиода состояния
int buzzer = 12; // Выберите пин для Пьеццо Зуммера. Подключен от затвора MOSFET.
int tempSensor = 2; // Выбор контакта для датчика температуры
int pirSensor = 27; // Выбор контакта для PIR-датчика контроллера

int PIR_Power = 25; // Управляет силовым входом для PIR-датчика
int ArduCAM_Power = 32; // Управляет силовым вентилем для ArduCAM

const int CS = 5; // GPIO5 как выбор ведомого устройства для ArduCAM


int wifiType = 0; // 0: Станция 1: AP (точка доступа)


// Настраиваем запросы на отправку захваченного изображения на внешний сервер
bool onlineMode = true;
String start_request = "";
String boundary = "_cam_";
String end_request = "\n--" + boundary + "--\n";

// Устанавливаем размер буфера для данных камеры
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;


// Порог объявления падения по акселерометру
float sensitivity = 11.0;

float magnitude = 0.0; // Сохраняет рассчитанную величину


// Экземпляры акселерометра, семафора и Firebase
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);

  // Очищаем FIFO
  myCAM.flush_fifo();
  // Очистить флаг завершения захвата
  myCAM.clear_fifo_flag();
  // Начинаем захват
  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) // 0 КБ
  {
    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);
    //Читаем данные JPEG из FIFO
    if ( (temp == 0xD9) && (temp_last == 0xFF) ) //Если найден конец, прервать while,
    {
      buf[iPic++] = temp;  //сохраняем последний 0XD9
      //Записываем оставшиеся байты в буфер
      myCAM.CS_HIGH();
      client.write(buf, iPic);
      //Закрываем файл
      //файл.закрыть();
      Serial.println(F("Image save OK."));
      is_header = false;
      iPic = 0;
    }
    if (is_header == true)
    {
      //Записываем данные изображения в буфер, если он не заполнен
      if (iPic < 256)
        buf[iPic++] = temp;
      else
      {
        //Запись 256-байтовых данных изображения в файл
        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;
    }
  }

  // Завершаем POST-запрос к серверу
  client.println(end_request);

  myCAM.CS_HIGH();

  // Остановим клиент
  client.stop();
  digitalWrite(ArduCAM_Power, LOW);
  }
  else // Нет соединения с сервером.
  {
    Serial.println("Could not connect to external server!");
    Serial.println("Please check the IP address or Port");
    return;
  }
}

// Вычисляем величину по осям x, y и z акселерометра.
// Дает общее значение, которое лучше, чем просто использование оси Z
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); // Устанавливаем затвор МОП-транзистора на выход для PIR-датчика
  pinMode(ArduCAM_Power, OUTPUT); // Устанавливаем затвор МОП-транзистора на выход для ArduCAM

  pinMode(statusLED, OUTPUT);      // Объявляем светодиод как выход
  pinMode(buzzer, OUTPUT);         // Объявляем пьезо-зуммер как выход
  pinMode(pirSensor, INPUT);        // Объявляем PIR-датчик как входные данные

  Serial.println();

  // Проверяем, правильно ли подключен/найден акселерометр
  if(!mma.begin())
  {
    Serial.println("Couldn't start MMA8451 Accelerometer.");
    Serial.println("Check your connections.");
    while(1);
  }
  Serial.println("MMA8451 Accelerometer found!");

  // Установите диапазон g для акселерометра. Можно настроить на 2 г, 4 г или 8 г.
  mma.setRange(MMA8451_RANGE_2_G);

  // Проверяем, найдена ли камера
  digitalWrite(ArduCAM_Power, HIGH);
  uint8_t vid, pid;
  uint8_t temp;
  while(1)
  {
    //Проверяем, в порядке ли шина ArduCAM SPI
    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)
  //Проверяем, является ли тип модуля камеры 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

  //Переходим в режим захвата JPEG и инициализируем модуль OV2640
  myCAM.set_format(JPEG);
  myCAM.InitCAM();

  myCAM.OV2640_set_JPEG_size(OV2640_640x480);
  myCAM.clear_fifo_flag();
  // Выключаем камеру
  digitalWrite(ArduCAM_Power, LOW);


  // Подключаемся к Wi-Fi
  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);
    }
    // Подключаемся к сети Wi-Fi
    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());
  }

  // Инициализируем NTPClient, чтобы получить время
  timeClient.begin();
  timeClient.setTimeOffset(-14400);

  //Если Firebase подключен, включаем светодиод
  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.");

     // Инициализируем все переменные значением 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); // Даем сенсорам загрузиться, 10 с
  Serial.println("Sensors ready!");
}

, 👍0

Обсуждение

Спасибо за очень хорошее форматирование. Так много людей публикуют вопросы в совершенно дерьмовом формате, из-за чего вопрос становится очень трудным для чтения и понимания., @romkey

@JohnRomkey Ха-ха! Спасибо, мне нужно как-то облегчить всем жизнь., @fern132


1 ответ


Лучший ответ:

1

Вам необходимо добавить переменную состояния в функцию loop().

В начале loop() добавьте объявление переменной, например:

void loop()
{
  static bool capture_sent = false;

Тогда измените свою линию

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

в

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

или, если вы хотите выполнить часть логики в этом разделе и просто не вызывать camCapture(), оставьте строку if в покое и выполните вызов

code>camCapture() при условии !capture_sent.

Затем в любом месте вашего кода, где вы видите условие, которое говорит о том, что вы хотите снова вызвать camCapture() при срабатывании, сбросьте capture_sent на `false . Я оставляю вам право решать, куда это пойдет, потому что вы понимаете, какие условия это определяют.

,

Потрясающий! Спасибо за ответ! Утром я внесу эти изменения и сообщу вам, как все пройдет., @fern132