Сигнализация для велика

evgesh

✩✩✩✩✩✩✩
1 Апр 2022
15
0
Привет! Четвертый месяц пошел как я втянулся в тему ардуинки, и решил замутить сигналку для элвелика + еще плюшки. В общем я столкнулся с некоторыми проблемами:

Всё работает прекрасно до того момента, как вибраций больше 2 и прошло менее 5 сек.

Нужно что бы после условия запускалась функция Blink, значения с датчика больше не обрабатывались, секундомер был не активен, а флаг "radioFlag" оставался "true".

Помогите пожалуйста грамотно организовать код.

Функция:
void alarm() {

  if (radioFlag == true) {

    digitalWrite(GREEN_LED, HIGH);

    vibrationDetect = digitalRead(VIB_PIN); // Ззначение с вибродатчика

    if (vibrationDetect) {

      if (millis() - timer >= 1000) { // Антидребезг вибрации 1 сек
        timer = millis();
        vibrationDetectCount++; // Счетчик вибраций
        Serial.print("Количество вибраций: ");
        Serial.println(vibrationDetectCount);
      }
    }
    if (vibrationDetectCount >= 1) { // Если количество вибраций 1, запуск таймера
      if (millis() - globaltimer > 1000) {
        globaltimer = millis();
        SEC = SEC + 1;
        if (SEC > 59) {
          SEC = 0;
        }
        digitalWrite(RED_LED, HIGH);
        Serial.println(SEC);
      }
    }
    if (SEC >= 10 && vibrationDetectCount <= 1) {
      vibrationDetectCount = 0;
      SEC = 0;
      digitalWrite(RED_LED, LOW);
    }
    if (vibrationDetectCount >= 2 && SEC <= 5) { // Если вибраций 2 или больше и прошло меньше или равно 5 сек
      digitalWrite(RED_LED, HIGH); // Аларм!!!
      alarmEnableFlag = true;
      gabaritn();
    }
  } else if (radioFlag == false) {

    digitalWrite(RED_LED, LOW);
    digitalWrite(GREEN_LED, LOW);
    SEC = 0;
    vibrationDetect = 0;
    vibrationDetectCount = 0;
    alarmEnableFlag = false;
  }
}
Blink:
void gabaritn() {
  static uint32_t tmr;
  if (millis() - tmr >= 100) { //СКОРОСТЬ
    tmr = millis();
    digitalWrite(MOSFET, !digitalRead(MOSFET));
  }
}
Gyver'у большущее спасибо за огромный вклад😊
 

BOT_Zilla

✩✩✩✩✩✩✩
1 Апр 2022
11
8
Вы в функции gabaritn объявляете переменную tmr как static, и тут же пытаетесь ее изменить.
 

PiratFox

★★★★★✩✩
13 Фев 2020
1,706
474
@BOT_Zilla, это не возбраняется. Тип static означает, что значение переменной не будет уничтожено при выходе из функции, а менять её значение можно как угодно в пределах этой же функции. Очевидно, Вы перепутали с типом const.
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,302
Москва
Условия срабатывания написаны я бы сказал несколько сумбурно.
1)
C++:
if (radioFlag == true) {
    ..
    }
else if (radioFlag == false) {
..
  }
второй if просто лишний. У radioFlag только 2 состояния , либо ИСТИНА либо ЛОЖЬ (не глагол!). Но это будет работать.

2)
пары условий:
if (vibrationDetectCount >= 1)
if (SEC >= 10 && vibrationDetectCount <= 1) {
и
if (vibrationDetectCount >= 1)
if (vibrationDetectCount >= 2 && SEC <= 5)

могут сработать одновременно. Это так и задумывалось ?
 
  • Лойс +1
Реакции: evgesh

BOT_Zilla

✩✩✩✩✩✩✩
1 Апр 2022
11
8
@PiratFox, да, полностью согласен с замечанием.

@evgesh,в те условия, где обрабатываются срабатывание датчика и запуск таймера, можно добавить проверку флага alarmEnableFlag. Тогда, пока флаг поднят, условие не будет выполнятся
пример:
if (vibrationDetect & !alarmEnableFlag){}
C++:
if (vibrationDetectCount & !alarmEnableFlag) { // Если количество вибраций 1, запуск таймера
Потом флаг не забыть сбросить, когда тревога снимется.
 
  • Лойс +1
Реакции: evgesh и PiratFox

evgesh

✩✩✩✩✩✩✩
1 Апр 2022
15
0
Избыточное цитирование. Отредактируй или сообщение будет удалено
2)
пары условий:
if (vibrationDetectCount >= 1)
if (SEC >= 10 && vibrationDetectCount <= 1) {
и
if (vibrationDetectCount >= 1)
if (vibrationDetectCount >= 2 && SEC <= 5)

могут сработать одновременно. Это так и задумывалось ?
У меня путаница с if и else if. Понимаю как они должны работать, но по факту они так не работают т.к. я что-то не правильно пишу..

Должно быть так:

Если вибраций больше 1 -> Включается таймер. Счетчик вибраций активен;

Если в течение 10 сек вибраций было меньше или равно 1 -> Таймер становится не активен и счетчик вибраций сбрасывается;

НО Если вибраций 2 или больше и прошло меньше 5 сек после предыдущего срабатывания -> Запускается функция gabaritn() до того момента, как alarmEnableFlag не станет false, секундомер останавливается датчик вибраций более не активен
 

evgesh

✩✩✩✩✩✩✩
1 Апр 2022
15
0
Избыточное цитирование. Отредактируй или сообщение будет удалено
@evgesh,в те условия, где обрабатываются срабатывание датчика и запуск таймера, можно добавить проверку флага alarmEnableFlag. Тогда, пока флаг поднят, условие не будет выполнятся
пример:
if (vibrationDetect & !alarmEnableFlag){}
C++:
if (vibrationDetectCount & !alarmEnableFlag) { // Если количество вибраций 1, запуск таймера
Потом флаг не забыть сбросить, когда тревога снимется.

Это работает! Спасибо! Странно, что перед тем как я написал на форум, датчик все равно продолжал считать вибрации.

У меня проблемы с условиями, т.к. после значения(SEC <= 5) функция перестает выполняться :

C++:
if (vibrationDetectCount >= 2 && SEC <= 5)
 

BOT_Zilla

✩✩✩✩✩✩✩
1 Апр 2022
11
8
Запускается функция gabaritn() до того момента, как alarmEnableFlag не станет false
А в каком месте опускается флаг alarmEnableFlag и при каком условии? Судя по части кода в топе, если хоть раз попадаешь в условие if (vibrationDetectCount >= 2 && SEC <= 5), то оно выполняется уже всегда.
 
  • Лойс +1
Реакции: evgesh

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,302
Москва
@evgesh,
Ну это со временем придет.
1) Берем переменную таймера и делаем ее раным нулю. Это означает , что таймер не запущен.
2) Если была вибрация, то делаем
а) если аларм_флаг включены, то выходим.
б) если таймер!=0 то таймер=миллис
в) кол-во вибраций++

с) проверяем таймер таким образом:
если таймер больше 10 сек и кол-во вибраций меньше 2, то таймер=0
если таймер меньше 5 сек и кол-во вибраций больше 2, то включаем аларм_флаг
Это если брать строго по условиям автора. А что делать если было 2 (3) срабатывания за время более 5 секунд но менее 10 ? как бы выходит из всех условий. У автора есть больше равно, но они опять же дублируются. А по времени и более 1 условия гораздо сложнее коммутировать

Мне не очень понятно желание сделать эти 5 секунд. Я бы оставил 10 на сброс и было бы проще:
далее 2 варианта
с) если время больше 10
{
обнулить таймер таймер и сбросить счетчик вибраций
// потому что если бы было критическое число вибраций до 10 сек, то уже был бы включен аларм в другой части. А тут
// просто надо выключить счетчик, что бы при сл. вибрации начать заново.
}
иначе (время не больше 10 сек)
{
если вибраций больше 2 (3.. сколько надо для включения) , то включить аларм_флаг, сбросить счетчик, обнулить таймер
}
 
  • Лойс +1
Реакции: evgesh

evgesh

✩✩✩✩✩✩✩
1 Апр 2022
15
0
А в каком месте опускается флаг alarmEnableFlag и при каком условии? Судя по части кода в топе, если хоть раз попадаешь в условие if (vibrationDetectCount >= 2 && SEC <= 5), то оно выполняется уже всегда.
Только при radioFlag == false:

C++:
  } else if (radioFlag == false) {

    digitalWrite(RED_LED, LOW);
    digitalWrite(GREEN_LED, LOW);
    SEC = 0;
    vibrationDetect = 0;
    vibrationDetectCount = 0;
    alarmEnableFlag = false;
  }
Да, из условия vibrationDetectCount >= 2 && SEC <= 5 выйти можно только после radioFlag == false. Это для того, что бы функция gabaritn() не переставала выполняться.


а) если аларм_флаг включены, то выходим.
Не понял...а как выйти из условия? break работает же только в циклах.

int SEC = 0;
bool vibrationDetect;
byte vibrationDetectCount;
...
Эти все переменные объявлены глобально
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,302
Москва
Предполагается, что есть какая то функция , которая проверяет состояние датчиков и устанавливает флаги в нужное состояние.
Выход из функции это return.
 
  • Лойс +1
Реакции: evgesh

evgesh

✩✩✩✩✩✩✩
1 Апр 2022
15
0
Получилось!

alarm():
void alarm() {

  if (radioFlag == true) {

    digitalWrite(GREEN_LED, HIGH);

    vibrationDetect = digitalRead(VIB_PIN); // Ззначение с вибродатчика

    if (vibrationDetect & !alarmEnableFlag) {

      if (millis() - timer >= 1000) { // Антидребезг вибрации 1 сек
        timer = millis();
        vibrationDetectCount++; // Счетчик вибраций
        Serial.print("Количество вибраций: ");
        Serial.println(vibrationDetectCount);
      }
    }
    if (vibrationDetectCount >= 1 && !alarmEnableFlag) { // Если количество вибраций 1, запуск таймера
      if (millis() - globaltimer > 1000) {
        globaltimer = millis();
        SEC = SEC + 1;
        if (SEC > 59) {
          SEC = 0;
        }
        digitalWrite(RED_LED, HIGH);
        Serial.println(SEC);
      }
    }
    if (SEC >= 10 && vibrationDetectCount <= 1) {
      vibrationDetectCount = 0;
      SEC = 0;
      digitalWrite(RED_LED, LOW);
    }
    if (vibrationDetectCount >= 2 && SEC <= 5) { // Если вибраций 2 или больше и прошло меньше или равно 5 сек
      digitalWrite(RED_LED, HIGH); // Аларм!!!
      SEC = 0;
      alarmEnableFlag = true;
      gabaritn();
    }
  } else if (radioFlag == false) {

    digitalWrite(RED_LED, LOW);
    digitalWrite(GREEN_LED, LOW);
    SEC = 0;
    vibrationDetect = 0;
    vibrationDetectCount = 0;
    alarmEnableFlag = false;
  }
}
Я хотел эти условия SEC >= 10 && vibrationDetectCount <= 1 и vibrationDetectCount >= 2 && SEC <= 5 поместить в условие vibrationDetectCount >= 1, но тогда все перестаёт работать. Хотя на мой взгляд это было бы правильнее, чем использовать флаги. Но я только новичок в этом, поэтому могу ошибаться.

Хотел так:
if (vibrationDetect & !alarmEnableFlag) {

    if (vibrationDetectCount >= 1) {
    }
    
    if (SEC >= 10 && vibrationDetectCount <= 1) {   
    }
    
    if (vibrationDetectCount >= 2 && SEC <= 5) {
    }
}

Так же я еще ни разу не пользовался switch. Есть ли смысл его здесь использовать?
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,302
Москва
Ну про switch надо почитать литературу. Тут оно не совсем подходит.
И еще, вот например такое условие:
vibrationDetectCount >= 1
легко заменяется на такое:
vibrationDetectCount >0

Ну раз получилось что то, то рабочий вариант сохранятся как резервный, а далее можно экспериментировать как угодно

И про флаги, иногда они очень удобны, что бы не запутаться в условиях.
 
  • Лойс +1
Реакции: evgesh

evgesh

✩✩✩✩✩✩✩
1 Апр 2022
15
0
И еще, вот например такое условие:
vibrationDetectCount >= 1
легко заменяется на такое:
vibrationDetectCount >0
Спасибо!

А как можно организовать проверку состояния с помощью digitalRead?
Например если digitalWrite зависнет в состоянии true, после чего все мигалки будут работать зеркально

Мои нагромождения:
#define GREEN_LED 5
#define RED_LED 6
#define MOSFET 8
#define BTN_PIN 12
#define SIGNAL 9
#define VIB_PIN 7

#include "GyverButton.h"
GButton butt1(BTN_PIN);

#include <RCSwitch.h> //
RCSwitch mySwitch = RCSwitch(); //

uint32_t timer;                 // Таймер для функции alarm()
uint32_t vdcTmr;                // Таймер для функции vibrationDetectCount // alarm()
int SEC = 0;                    // Счетчик секунд после срабатывания обнаружение вибрации // alarm()
bool vibrationDetect;           // Обнаружение вибрации // alarm()
byte vibrationDetectCount;      // Счетчик обнаружений вибрации // alarm()
uint32_t radioTmr;              // Таймер для функции radio()
bool radioFlag;                 // Флаг включение сигнализации
bool alarmEnableFlag = false;   // Флаг активации звуковой сигнализации
bool lightsFlag;                // Флаг включения фар
bool stopSensor;                // Датчик тормоза

bool mosfetFlag; // Проверка МОСФЕТА

void setup() {
  Serial.begin(9600);

  mySwitch.enableReceive(0);    // Прерывание на D2 // Радиомодуль

  pinMode(GREEN_LED, OUTPUT);
  pinMode(RED_LED, OUTPUT);
  pinMode(SIGNAL, OUTPUT);
  pinMode(VIB_PIN, INPUT);

  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);

  pinMode(MOSFET, OUTPUT);

  butt1.setTimeout(500);       // Тайм-аут удержания кнопки

}

void loop() {

  butt1.tick();

  if (butt1.isHolded());
  if (butt1.isSingle());
  if (butt1.isHold());
  if (butt1.isPress());


  int value1 = analogRead(A1); // включение фар
  lightsFlag = (value1 > 1000) ? lightsFlag = true : lightsFlag = false;
  if (lightsFlag) {
    gabaritn(500);
    digitalWrite(GREEN_LED, 1); // проверка
    int value0 = analogRead(A0); // датчик тормоза
    stopSensor = (value0 > 900) ? stopSensor = false : stopSensor = true;
  } else if (!lightsFlag) {
    stopSensor = false;
    digitalWrite(RED_LED, 0); // проверка
    digitalWrite(GREEN_LED, 0); // проверка
  }
  if (stopSensor) {
    gabaritn(100);
    digitalWrite(RED_LED, 1); // проверка
  }

  radio();
  alarm();


}



void stopSignal(byte repeats, int period) {

  static uint32_t tmr;
  byte counter = 0;

  while (counter < repeats) {
    if (millis() - tmr >= period) {
      tmr = millis();
      digitalWrite(MOSFET, !digitalRead(MOSFET));
      counter++;
    }
  }
}


bool digitalCheck() { //Проверка на состояние
  mosfetFlag = digitalRead(MOSFET);
}




void gabaritn(int period) {

  static uint32_t tmr;
  if (millis() - tmr >= period) { //СКОРОСТЬ
    tmr = millis();
    digitalWrite(MOSFET, !digitalRead(MOSFET));
  }
}

void radio() {
  if (mySwitch.available()) {
    byte value = mySwitch.getReceivedValue();
    if (mySwitch.getReceivedValue() == 4) {
      if (millis() - radioTmr >= 1000) {
        radioTmr = millis();
        Serial.println("С пульта пришло 4");
        radioFlag = true;
        stopSignal(6, 200);
      }
    }
    else if (mySwitch.getReceivedValue() == 8) {
      if (millis() - radioTmr >= 1000) {
        radioTmr = millis();
        Serial.println("С пульта пришло 8");
        radioFlag = false;
        stopSignal(4, 500);
      }
    }
    mySwitch.resetAvailable();
  }
}



void alarm() {

  if (radioFlag == true) {

    digitalWrite(GREEN_LED, HIGH);

    vibrationDetect = digitalRead(VIB_PIN); // Ззначение с вибродатчика

    if (vibrationDetect & !alarmEnableFlag) {

      if (millis() - timer >= 1000) { // Антидребезг вибрации 1 сек
        timer = millis();
        vibrationDetectCount++; // Счетчик вибраций
        Serial.print("Количество вибраций: ");
        Serial.println(vibrationDetectCount);
      }
    }
    if (vibrationDetectCount >0 && !alarmEnableFlag) { // Если количество вибраций больше 0, запуск таймера
      if (millis() - vdcTmr > 1000) {
        vdcTmr = millis();
        SEC = SEC + 1;
        if (SEC > 59) {
          SEC = 0;
        }
        digitalWrite(RED_LED, HIGH);
        Serial.println(SEC);
      }
    }
    if (SEC >= 10 && vibrationDetectCount <= 1) {
      vibrationDetectCount = 0;
      SEC = 0;
      digitalWrite(RED_LED, LOW);
    }
    if (vibrationDetectCount >= 2 && SEC <= 5) { // Если вибраций 2 или больше и прошло меньше или равно 5 сек
      digitalWrite(RED_LED, HIGH); // Аларм!!!
      SEC = 0;
      alarmEnableFlag = true;
      gabaritn(100);
    }
  } else if (radioFlag == false) {

    digitalWrite(RED_LED, LOW);
    digitalWrite(GREEN_LED, LOW);
    SEC = 0;
    vibrationDetect = 0;
    vibrationDetectCount = 0;
    alarmEnableFlag = false;
  }
}


void flash(byte repats) { // функция работает только после сброса флага или вечный цикл

  static byte counter = 0;

  while (true) {

    static uint32_t tmr;
    static bool flag;
    uint32_t period;

    if (flag) period = 500;
    else period = 500;


    if (counter < repats && millis() - tmr >= period) {
      tmr = millis();
      digitalWrite(MOSFET, !digitalRead(MOSFET));
      counter++;
      flag = !flag;
    }
    if (counter == repats) {
    }
  }
}

void beep(byte repeats) {

  while (true) {

    static uint32_t tmr;
    static bool flag;
    uint32_t period;

    if (flag) period = 10;
    else period = 500;

    static byte counter = 0;

    if (counter < repeats && millis() - tmr >= period) {
      tmr = millis();
      digitalWrite(SIGNAL, !digitalRead(SIGNAL));
      counter++;
      flag = !flag;
    }
    if (counter == repeats) {
      counter = 0;
      break;
    }
  }
}
 

evgesh

✩✩✩✩✩✩✩
1 Апр 2022
15
0
Почему-то зависает цикл while:

while (flag) { // При нажатии на кнопку флаг true
gabaritn(500); // Тут запускается функция с бесконечным циклом
if (!flag) break; // После отпускании кнопки флаг false, но функция продолжает выполняться, а монитор порта зависает
}
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,302
Москва
А флаг внутри цикла меняется ?

Про
Например если digitalWrite зависнет в состоянии true,
Не понял. Что будет записано то и будет. Как он может зависнуть? Но в любом случае можно использовать digitalRead
 

evgesh

✩✩✩✩✩✩✩
1 Апр 2022
15
0
На скриншоте функция "gabaritn" продолжает выполняться даже после флага "!test", при этом в мониторе порта последнее значение - "1"
 

Вложения

Старик Похабыч

★★★★★★★
14 Авг 2019
4,263
1,302
Москва
Вот функция. Для того, что бы из нее выйти значение flag должно быть false
и строка if (!flag) break вообще лишняя.

C++:
while (flag) { // При нажатии на кнопку флаг true
gabaritn(500); // Тут запускается функция с бесконечным циклом
if (!flag) break; // После отпускании кнопки флаг false, но функция продолжает выполняться, а монитор порта зависает
}
А теперь смотри функцию которая внутри цикла:
C++:
void gabaritn() {
  static uint32_t tmr;
  if (millis() - tmr >= 100) { //СКОРОСТЬ
    tmr = millis();
    digitalWrite(MOSFET, !digitalRead(MOSFET));
  }
}
И видим, что flag нигде не меняется. Ну и выхода естественно не будет. 1 - это и есть значение true , по которому программа попадает в цикл . навсегда. Ну до ресета.