Запутался с кодом включения света по движению в темноте.

Тема в разделе "Arduino & Shields", создана пользователем Gomez, 20 ноя 2023.

  1. Gomez

    Gomez Гик

    Вот поскипал лишнее, оставил, что должно относиться к проблеме.
    Как-то странно - то работает, то нет. То ли нужно включить в темноте, то ли без движения, то ли в темноте без движения...
    Всё ли оптимально в моём гуанокоде? :)

    Код (C++):
    void loop() {

      if (millis() - timerSensors > 500) {
        timerSensors = millis();
        lightness = analogRead(photoPin);
        motionDetected = digitalRead(pirPin);
      }

        // Засек движение
        if (motionDetected) {
          timerLightOn = millis();
          if (!lightOn && lightness < data.trigVal) {
            lightOn = true;
          }
        }
     
        // если свет зажжен и прошло достаточно времени
        if (lightOn && millis() - timerLightOn >= data.durVal * 1000) {
          lightOn = false;
        }

        if (lightOn != lastLightState) {
          if (lightOn) {
            letThereBeLight(true);
          } else {
            letThereBeLight(false);
          }
        }
      lastLightState = lightOn;
    }

    void letThereBeLight(bool onOff) {

      if (onOff) {
          gCurrentPatternNumber = 1; // управляю FastLED-ом. Паттерн 1 - вкл. 0 - выкл
      } else {
          cur_bright = 0;
          gCurrentPatternNumber = 0;    
      }
    }

     
    data.trigVal - минимальное значение яркости, с которого начинает срабатывать.
     
  2. parovoZZ

    parovoZZ Гуру

    любите вы усложнять...
    Код (C++):

    lightOn = motionDetected || lightOn && !timerSensors.Q;
     
    ну и каждое обнаруженное движение обнуляет счётчик таймера timerSensors
     
    Gomez нравится это.
  3. Airbus

    Airbus Радиохулиган Модератор

    Gomez нравится это.
  4. Asper Daffy

    Asper Daffy Иксперд

    Тут неудивительно запутаться, Вы выбрали самый сложный путь реализации. Такие задачи делаются не в лоб, а через состояния автомата, тогда там очень трудно ошибиться и запутаться.

    По коду трудно что-то сказать - это же не цельный код, а огрызок.

    Одна ошибка бросается в глаза сразу - Вы зачем-то всегда удлиняете длительность включения света на полсекунды за счёт использования старого значения motionDetected.

    Ну и стилистика, конечно ... например, зачем писать
    Код (C++):

          if (lightOn) {
            letThereBeLight(true);
          } else {
            letThereBeLight(false);
          }
     
    вместо простого
    Код (C++):
    letThereBeLight(lightOn);
    Хотя, на вкус и цвет.
     
    Gomez, DetSimen и issaom нравится это.
  5. Gomez

    Gomez Гик

    Я понимаю, что всё криво. Поскольку не смог представить в уме чёткий алгоритм, получается, как обычно, по кирпичикам и костыликам "давайте решать проблемы по мере их поступления" :D
    Например, lastLightState ввёл потому, что иначе pir, при движении "залипающий" в HIGH только секунду, рапортует "выключен, ой нет, снова движение, включился" и тогда по сто раз дергается функция letThereBeLight, что мешает в ней, например, плавно включить и плавно погасить подсветку. Поэтому начинаются флаги, флаги, флаги, в которых путаешься.

    А как это "через состояния автомата"? :rolleyes:
     
  6. parovoZZ

    parovoZZ Гуру

    Gomez нравится это.
  7. ИгорьК

    ИгорьК Гуру

    У тебя "есть два путя".
    1. Ты мучаешься сам, осознанно пишешь как сумеешь говнокод, ни на кого не обращая внимание, получаешь работающее устройство, опыт и удовольствие.
    2. Ты изучаешь конечный автомат, но взамен сбиваешь коленки, ибо это главная икона ардуинщегов, и сам им становишься, ибо перестанешь отвечать на вопросы адекватно уровню вопрошающего.
     
    b707, Gomez и DetSimen нравится это.
  8. Asper Daffy

    Asper Daffy Иксперд

    Вот для этого и нужны автоматы.

    Для начала можно почитать вот это. Для понимания основных идей вполне достаточно первой главы. Остальное - это уже как эти идеи реализовывать.

    Вот здесь я описываю сто такое автомат и разбираю просто пример

    Вот здесь другой пример, тоже простой

    А вот здесь я строю универсальную программу для любого автомата (примерно, как старших главах той книги).

    Наконец, реальный проект:

    не так давно я делал таблетницу для пожилого человека, который часто не может вспомнить пил он утром таблетки или нет. Нужно было, чтобы он мог (при появлении сомнений) открыть коробку и увидеть когда он в последний раз её открывал. Т.е. в ней должны быть часы (их нужно уметь устанавливать), При открытии она должна показывать время, когда её открывали в последний раз, а если её держат открытой более пяти секунд, то запоминала новое время последнего открывания. Ну, и при этом беречь батарейку, чуть что всё выключать и погружаться в сон.

    Так вот, прежде, чем вообще хоть одну букву программы писать, я составил список состояний, список возможных сигналов и таблицу переходов, внимательно всё это проверил на полноту и непротиворечивость, а также сделал пояснения что именно должно делаться на выходах. Полюбуйтесь на всё это в эсель файле. И только после этого, имея перед глазами эту таблицу, я начал писать программу. Никакой отладки (кроме исправления опечаток) не потребовалось. В программах, которые пишутся с такой подготовкой ошибиться трудно.

    Т.е., в лоб, как Вы я писать не стал - это почти бесполезно, запутаешься обязательно. Хотя, опыт программирования у меня более 40 лет.
     
    Gomez, DetSimen и ИгорьК нравится это.
  9. ИгорьК

    ИгорьК Гуру

    Я б себе сделал. Как это выглядит в итоге? Имею в виду не код, а именно само устройство.
     
    Gomez нравится это.
  10. ИгорьК

    ИгорьК Гуру

    Все нормально - мозг думает, значит существует :)

    Вот и давай отважно по первому пути. Можно обойтись и без конечного автомата.

    1. Начнем с конца. Тыж наверно хочешь, чтобы у тебя свет включался/выключался плавно? Попробуй написать неблокирующую функцию, которая проверяет значение некого флага и включает и выключает свет в зависимости от него. Внутри себя функция хранит текущее значение "света" и плюсует/минусует его в зависимости от флага. Доходит до минимума/максимума - ничего не делает.

    Что такое неблокирующая функция? Она не имеет внутри себя циклов. Функция постоянно вызывается в теле программы и обрабатывает флаг и свою "световую" переменную. Возможно внутри себя функция будет проверять время для поддержания "шага" изменения яркости. Это как тебе нравится. Можно даже менять время этого шага, если хочется пижонства. (Если хочется пижонства с форсом - вызывай эту функцию не в главном цикле, а в прерывании по таймеру. Тогда установленным временем таймера можно определять плавность изменения света.)

    Если у тебя свет имеет состояние только вкл/выкл - все равно пусть эта функция остается. Тогда она будет совсем простой: проверила флаг и шваркнула свет в зависимости от ситуации.

    2. Продолжим проверкой датчика движения. Тут все просто:
    - есть движение - флаг для световой функции поднят и установлен таймер сколько надо. Можно каждое обнаруженное движение прибавлять к нему, а лучше устанавливать заново (ибо если кто там мельтешит - таймер наплюсуется на сутки). Двигаемся? Значит от "сейчас" плюс столько. Дабы упростить ситуацию - сделай отдельную функцию "установить таймер". Вызывай ее по обнаружению движения.

    - нет движения - проверяем истечение времени таймера и сбрасываем флаг "света".

    ==========================================

    В указанной логике можно творить веселые вещи. На кухне у меня сделано так.
    Заходы на кухню имеют обычно две цели: или что-то украсть в холодильнике/попить водички, или что-то делать.

    Функция таймера может не просто устанавливать время, а измерять время нахождения в зоне датчика. Если человек мельтешит долго - он что-то делает. Можно установить полную яркость и надолго - соответствующий флаг для функции света. Если примчалисть за водой или мимо прошли - свет сначала включается на минимальную яркость и так продолжается, например, 45 секунд.

    Естественно, функция включения света становится немного сложнее - ее флаг, например, может быть не бинарным, а цифровым - целевое значение уровня освещенности.

    В общем, ИМХО, конечный автомат это икона, но можно и по-другому.
     
    Последнее редактирование: 23 ноя 2023
    Gomez нравится это.
  11. ИгорьК

    ИгорьК Гуру

    Идея реально важная и интересная. Буду делать. Вижу e-ink дисплей сверху таблетницы, ее даже открывать не нужно. МК вижу ESP32 - он более экономный чем 8266, будет отправлять данные об открытии куда-нибудь в агрегатор. Лучше бы, конечно ZigBee, но я его не умею. Даже если делать от батареек и устройство будет работать месяц - это все равно здорово. В общем, спасибо за идею.
     
    Gomez нравится это.
  12. Asper Daffy

    Asper Daffy Иксперд

    Вечером сфотографирую и выложу.

    От батареек работает больше года. Внутри DS3231 и ATtiny85. Дисплей от Nokia. Работает только 10 сек при открывании. Всё остальное время спит в режиме Power Down.
     
    Gomez, DetSimen и ИгорьК нравится это.
  13. Asper Daffy

    Asper Daffy Иксперд

    В качестве коробки была куплена вот такая.

    [​IMG]

    Главными критериями были размер и "глубина" крышки, т.к. всё мое добро изнутри в крышку укладывается.

    Вот как выглядит внутренность крышки

    [​IMG]

    Из важного, надо сказать про датчик закрытия. Это нормально-замкнутая кнопка. Т.е. при закрытой крышке кнопка разомкнута (нажата), а при открывании кнопка отпускается и замыкается. Это позволяет не тратить батарею при закрытой крышке. Если бы была обычная нормально-разомкнутая кнопка, то при закрытой крышке она была бы замкнута и через резистор подтяжки постоянно тёк бы небольшой ток.
     
    Последнее редактирование: 23 ноя 2023
    DetSimen, Gomez, KindMan и ещё 1-му нравится это.
  14. ИгорьК

    ИгорьК Гуру

    Класс! Спасибо!
     
    Gomez нравится это.
  15. Gomez

    Gomez Гик

    Спасибо за обогащение мыслями! Буду думать. :)
    А вот это да! Не додумался, спасибо!
    Зато додумался до простой вещи - сначала не понимал, почему в готовых устройствах pir+фоторезистор свет не отключается сразу, когда включаешь на кухне обычный свет. Потом через несколько секунд понял. Потом понял, что можно замерять не только порог темноты, но и порог яркости - если стало ярко ощутимее больше, чем даёт твой девайс, подсветку можно таки гасить. :)

    А про таблетки - хорошая тема, хоть я забываю только одни вечером. Но думаю обойтись просто кнопкой и сбросом таймера через часов 20. Уж если таблетку взял, сражузе нажать кнопку "пока..." ой, то есть "выпил" забыть не должен.
     
    ИгорьК нравится это.
  16. parovoZZ

    parovoZZ Гуру

    На телефоне поставить напоминалку и все.
     
  17. Airbus

    Airbus Радиохулиган Модератор

    После этого не забыть выпить таблетку а не просто сбросить
     
    ИгорьК нравится это.
  18. Asper Daffy

    Asper Daffy Иксперд

    Она сработает, когда сидишь в сортире. Как только выйдешь раздастся звонок от начальства, после звонка припрётся соседка кота своего искать на твоём участке, а там ... выпил-не выпил ... ХЗ.
     
    DetSimen и ИгорьК нравится это.
  19. ИгорьК

    ИгорьК Гуру

    Да простят меня за флуд, но самая частая причина не выпить таблетки - "щас, через минуточку".
     
    Asper Daffy, DetSimen и Airbus нравится это.
  20. Airbus

    Airbus Радиохулиган Модератор

    Лень—двигатель прогресса! Как бы не пародоксально это звучало