Добрый день. Меня зовут Никита Сергеевич, мне 14 лет и в этом посте я имею желание рассказать вам о разработанном мною пол года назад устройстве (и приложении), потому что готовое устройство спаял и собрал мой отец. Хотя возможно такое устройство вы уже видели не раз, но мне это не мешает.

Создавалось все это для автоматического включения и выключения света в курятнике, но применимо и для других целей.





Готовое и собранное устройство может подать на указанный цифровой пин arduino 1 или 0 в период от и до указанного времени в точности до минут (вне периода подается значение противоположное указанному в приложении при добавлении таймера ).

Все параметры указываются в приложении. А вот отсюда поподробнее…

Есть приложение для android в котором необходимо подключиться к блютус модулю и добавить таймер. Приложение интуитивно понятное и думаю проблем с ним не возникнет.





.apk файл будет в папке этого проекта.

Необходимые элементы:

  1. arduino;
  2. rtc module ds1307;
  3. bluetooth module HC-05;
  4. соединительные элементы(например провода);
  5. прошивка для arduino; скетч на мой взгляд сложный и лучше в подробности не вдаваться.
  6. приложение для android;

Разбираемся с arduino и подключаемыми модулями.

Ага, пункты 1-4:



arduino - hc-05
         5v - vcc
      gnd - gnd
      TX1 - RXD
      RX0 - TXD

arduino - ds1307
         5v - 5v
      gnd - gnd
        A5 - scl
        A4 - sda

Не забываем при загрузке скетча отключать пин RX0 на arduino. Вы сказали «скетч»?

5.Скетч в студию:

#include <Wire.h>                                            //библиотека для работы с шиной i2c
#include <iarduino_RTC.h>                                    //библиотека для rtc модуля(если cчто, лежит в папке проекта(Timer->firmware_for_timer->iarduino_RTC))     
#include <EEPROM.h>                                          //библиотека для работы с энергонезависимой памятью

iarduino_RTC time(RTC_DS1307);                               //создание объекта для работы с модулем реального времени

int values;                                                  //переменная для получения данных

unsigned int to_minutes(unsigned int hours, unsigned int minutes)//функция для перевода часов и минут в минуты
{
  return (60*hours)+minutes;
}

void setup()
{
  time.begin();                                             //начало работы часов
  Serial.begin(9600);                                       //открытие сериал порта
 
}

void loop()
{
  if(Serial.available() > 0)                                //если что-то пришло
  {
    values = Serial.read();
    time.settime(0,-1, values);                             //считывание и установка часов
    delay(50);
    values = Serial.read();
    time.settime(0, values);                                //считывание и установка минут
    delay(50);

    //очищаем энергонезависимую память
    for (int i = 0 ; i < EEPROM.length() ; i++)
    {
      EEPROM.write(i, 0);
    }
    //////////////////////////////////
    
    values = Serial.read();                                 
    delay(50);
    
    //запись данных в энергонезависимую память, полученных с телефона
    while (values > 0)                                        //пока есть что получать
    {
      for(int i = 0; i < 7; ++i)
      {
        EEPROM.write(EEPROM.read(EEPROM.length() - 1) * 7 + i, Serial.read());
        delay(50);
      }

      EEPROM.write(EEPROM.length() - 1, EEPROM.read(EEPROM.length() - 1) + 1);

      values = Serial.read();
      delay(50);
    }
    /////////////////////////////////////////
  }

  //проход по каждому таймеру
  for(int i = 0; i < EEPROM.read(EEPROM.length() - 1); ++i)
  {
    if(EEPROM.read(i * 7 + 0) == 1)                                                                               //если таймер нужно проверять
    {
      bool find = 0;                                                                                              //флаг
      time.gettime();
      if((to_minutes(EEPROM.read(i * 7 + 1), EEPROM.read(i * 7 + 2)) <= to_minutes(time.Hours, time.minutes)) && //если пришло время для работы
         (to_minutes(EEPROM.read(i * 7 + 3), EEPROM.read(i * 7 + 4)) > to_minutes(time.Hours, time.minutes)))
      {
        pinMode(EEPROM.read(i * 7 + 5), OUTPUT);                                                                  //настраиваем пин таймера как выход
        digitalWrite(EEPROM.read(i * 7 + 5), EEPROM.read(i * 7 + 6));                                             //посылаем на пин нужное значение
      }                           
      else                                                                                                        //иначе если время не пришло
      {
        //ищем таймер с таким же пином
        for (int j = 0; j < EEPROM.read(EEPROM.length() - 1); ++j)
        {
          if(EEPROM.read(i * 7 + 5) == EEPROM.read(j * 7 + 5) && (i != j) && (EEPROM.read(j * 7 + 0) == 1))
          {
            //и если нашли таймер с таким же пином и если для него время пришло
            if((to_minutes(EEPROM.read(j * 7 + 1), EEPROM.read(j * 7 + 2)) <= to_minutes(time.Hours, time.minutes)) &&
               (to_minutes(EEPROM.read(j * 7 + 3), EEPROM.read(j * 7 + 4)) > to_minutes(time.Hours, time.minutes))) //если пришло время для работы
            {
              pinMode(EEPROM.read(i * 7 + 5), OUTPUT);                                                             //настраиваем пин таймера как выход
              digitalWrite(EEPROM.read(i * 7 + 5), EEPROM.read(j * 7 + 6));                                        //посылаем на пин нужное значение
              find = 1;                                                                                            //поднимаем флаг
            }
          }
        }

        if(find == 0)                                                                                              //если таймер с таким же пином не найден
        {
          pinMode(EEPROM.read(i * 7 + 5), OUTPUT);                                                                 //настраиваем пин таймера как выход
          digitalWrite(EEPROM.read(i * 7 + 5), !EEPROM.read(i * 7 + 6));                                           //посылаем на пин значение противоположное нужному
        }
      }
    }
  }
}

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

6.
Приложение интуитивно понятное и думаю проблем с ним не возникнет.
Но лучше разобраться сразу. При первом входе в приложение необходимо будет подключиться к блютус модулю, для этого нажимаем «синхронизировать устройство»
и выбираем наш модуль. При галочке «подключаться автоматически» телефон сам будет пытаться подключиться к блютус модулю, при открытии приложения(эту фишку взял у alex gyver).«таймеры» здесь список всех таймеров и их поиск по имени. При нажатии на имя таймера открывается экран изменения этого таймера. Здесь же(где список таймеров) в правом нижнем углу расположен "+" нажав на который открывается экран добавления таймера, где указывается:

  • его имя,
  • от какого времени(часы минуты),
  • до какого времени(часы минуты),
  • управляемый пин,
  • значение для него
  • и состояние(будет arduino включать его или не будет в нужное время).


Архив проекта;
Там же приложение.

На этом хочу поблагодарить за внимание и сказать что, если нашли какие-то косяки или баги — смело пишите об этом, будем исправлять.

Комментарии (80)


  1. FForth
    27.07.2019 18:14
    +1

    Как куры отнеслись к данному нововведению в их жизни? :)

    P.S. Никита Сергеевич, для вашего развития, могу порекомендовать к начальному прочтению пару книг по некоторому срезу понимания программирования.
    ЛЕО БРОУДИ «СПОСОБ МЫШЛЕНИЯ — Ф О Р Т ЯЗЫК И ФИЛОСОФИЯ ДЛЯ РЕШЕНИЯ ЗАДАЧ»
    Л.Броуди «Начальный курс программирования на языке Форт»

    Сам же начинал понимние идей заложенных в Форт дизайн языка с С.Н.БАРАНОВ Н.Р. НОЗДРУНОВ «ЯЗЫК ФОРТ И ЕГО РЕАЛИЗАЦИИ»

    Одна из популярных Форт-систем для AVR amForth, но для этого и других контроллеров есть и другие Форт системы.

    P.S. Здесь подборка другой Форт литературы
    а здесь Рускоязычный Форт-форум (активен с 2006г)

    Forth Haiku — «Развлекалочка» по мотивам Форт языка


    1. DmitrySpb79
      27.07.2019 19:45
      +4

      И зачем тут Форт? Для «автоматизации курятника» Питон имхо куда больше пригодится ;)


      1. FForth
        27.07.2019 20:19
        +1

        Сказано же, для развития «бренной» души.
        А, Вы его пробовали понять?

        P.S. Здесь В Online книги Броуди по основам языка Форт (сейчас, правда, актуален стандарт 94г) приводится пример с сортировкой куринных яиц по размеру. :)


        1. DmitrySpb79
          27.07.2019 20:33
          +8

          О развитии души можно на пенсии думать, а в 14 лет учить стоит то, что реально пригодится ;)

          Top 10 languages to learn 2018:
          image

          Хотя список и немного спорный имхо, но Форта там по-любому нет.


          1. FForth
            27.07.2019 21:23
            -4

            На пенсии уже поздно думать о душе (лучше заранее) :)

            Изучая «попсу», можно не пoлучить необходимые результаты по времени.
            Есть люди, которые смотрят на тренды, а есть кто выбирает свой путь и следует ему. :)
            P.S. Здесь Форт по версии EEE на 43-м текущем месте (был на 39), но об нём ещё не забыли
            Хотя деньги, конечно, платит энтерпрайз и менеджеры от него.

            Что из этого списка Вам действительно было полезно и Вы использовали в своей работе?


          1. DeVeLoPer-HiKiTa Автор
            27.07.2019 21:36
            -5

            Посмотрите это. Может узнаете что-то новое.


          1. vsergoog
            27.07.2019 22:14
            +8

            Это хорошо, мне с моим С++ в ближайшие годы можно не ожидать молодых, мотивированных конкурентов со слабыми амбициями по зарплате!


          1. AntonSazonov
            28.07.2019 12:20
            +1

            А их тут точно десять?


        1. iig
          27.07.2019 21:34

          В применении к бытовой автоматике более интересна поддержка железа. К ардуине подключить термометр по i2c — несколько строчек кода, например. Если использовать С++. Как это будет выглядеть на forth?


          1. FForth
            27.07.2019 22:26
            +1

            В применении к I2C в amForth
            библиотека поддержки выглядит так

            P.S. А, здесь, один из примеров её применения в репозитории кода
            Ничего сложного, если понимать синтаксис и семантику Форт языка.


            1. iig
              27.07.2019 23:17
              +2

              Понятно, что раз программа может запускаться на микроконтроллере, и есть доступ к его регистрам — все можно написать. Но в случае С++ и ардуины всё уже написано, и оно под рукой. А ардуино + forth это закат солнца вручную. Искать решения на давно забытых форумах (последний пост в 2008 году) и на покойной rapidshare? Да и в чем профит от натягивания виртуальной стековой машины на процессор с регистрами тоже не совсем понятно.


              1. Rigidus
                29.07.2019 15:32
                +1

                Есть много профита в изучении немейнстримновых концепций и языков — это сильно расширяет кругозор и весьма полезно практически. Иногда весьма с неожиданной стороны — например зная форт и немножко линала типичный джаваскриптер сможет наваять фортоподобный domain specific language на wasm для того чтобы крутить трехмерные модели на вебсайте заказчика без привлечения тяжеловесных emscripten и плюсовых библиотек.

                Работать это будет быстрее. И будет иметь преимущество в переносимости и скорости написания по сравнению с чистым wasm. Еще одним из преимуществ является то, что форт реально красив с точки зрения элегантности технических решений — в процесс изучения можно случайно получить море удовольствия

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


                1. DeVeLoPer-HiKiTa Автор
                  29.07.2019 17:41

                  Согласен


                1. FForth
                  29.07.2019 19:25
                  +1

                  Но, Форт некоторым разработчикам, почему то, как «кость в горле»
                  и пытаются заминусовать мои сообщения в данном треде и «поправить» карму. :)


                  1. Rigidus
                    29.07.2019 23:00

                    Любое сообщество защищает себя от т.н. еретиков, это часто проще чем попытататься извлечь из еретических идей полезное для себя.


        1. 0xd34df00d
          27.07.2019 23:05
          +4

          Надо переименоваться в HHaskell и начать пиарить понятно что.


          1. Cerberuser
            28.07.2019 08:14

            А IIdris — это будет уже следующий шаг?


  1. vsergoog
    27.07.2019 18:48

    Таймеры для управления светом, это хорошо… Но не хватает привязки к восходу/заходу солнца.
    Пару лет назад решал эту задачу, по этому позволю себе попиариться. Может и вам пригодится: github


  1. FForth
    27.07.2019 19:16

    1. iig
      27.07.2019 19:46

      Forth и arduino — странная комбинация.


      1. FForth
        27.07.2019 23:29
        +1

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


        1. iig
          28.07.2019 07:59

          Программирование на любом языке высокого уровня не требует знания архитектурных особенностей железа. Это норма. Даже на С можно писать, не задумываясь о том, RISC или CISC платформа будет выполнять этот код.
          А по поводу странного — можно просто сравнить количество проектов под Atmega на С, С++ и forth ;) Например, на Хабре. Или на примере объяснить, почему скетч из этой статьи на forth будет выглядеть лучше чем на С++.


          1. AVKinc
            28.07.2019 12:33
            +1

            Сделать что то боле менее серьезное на микроконтроллере (тем более слабеньком) без знания архитектуры ИМХО практически невозможно.


            1. iig
              28.07.2019 14:16

              Таймер для курятника — достаточно серьезный проект? ;)


              1. AVKinc
                28.07.2019 15:38

                Я как то делал таймер для курятника с режимом плавный рассвет закат, ШИМ, дисплей на динамической индикации, термостат, охранка, вентиляция, куча настроек и т.д. Это конечно не очень серьезно но тем не менее вполне себе в районе 1200 строк. Восьмая мега под завязку.


      1. assembled
        29.07.2019 21:17
        +1

        Что же странного? Forth как раз изначально и разработан для контроллеров.


        1. iig
          29.07.2019 21:22

          А потом что-то пошло не так и С оказалось удобнее.


          1. Rigidus
            29.07.2019 23:07

            Ничего не шло «не так», просто на С написали UNIX, который стал killer application потому что был переносим на разные архитектуры с меньшими усилиями. Это привело к скачку популярности — не потому что форт хуже чем си, а потому что рабочий метод популяризовать что-то — это облегчить людям жизнь. Популярность вовлекает контрибьюторов и язык развивается безотносительно того насколько он был хорош или плох на старте. Мое личное мнение — форт очень интересен, во время его изучения мне было куда интереснее, чем когда я изучал Си.


  1. DmitrySpb79
    27.07.2019 19:53
    +4

    Начинание хорошее, устройство полезное. Никита Сергеевич, так держать :)

    Но на схеме видна только Ардуино, RTC и Bluetooth. А как собственно свет-то управляется — реле, транзистор, тиристор? И непонятен вопрос питания и электробезопасности — для устройства, работающего от сети, еще и в потенциально сырой среде, это важно.

    Если уж управлять светом, то нужен датчик освещенности, чтобы не просто по таймеру его включать, а тогда когда он реально нужен. Ну и термодатчик тогда уж добавить — если в курятнике жарко/холодно, можно посылать уведомление (реальный случай, недавно в одном большом хозяйстве 2000 цыплят сдохли от жары +40).


    1. DeVeLoPer-HiKiTa Автор
      27.07.2019 20:31
      +2

      Отец попросил сделать «мозги» устройства. Все остальное он сделал сам. А насчет температуры и освещенности то такие задачи передо мною не стаяли. А так я думаю это не очень сложно организовать. Для полного комфорта можно и кондиционер запилить)))))))).


      1. DmitrySpb79
        27.07.2019 20:48
        +6

        Понятно, но раз уж выложены фото устройства и код, лучше иметь полную схему, для тех кто захочет повторить.

        Пожелания по улучшению кода:

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

        — продумать работу с EEPROM, например, возможно, не нужно очищать всю память при записи лишь нескольких значений (for (int i = 0; i < EEPROM.length(); i++)). Eeprom кстати имеет ограниченное число циклов записей.

        — Не очень понятны строки типа EEPROM.write(EEPROM.read(EEPROM.length() — 1) * 7 + i, Serial.read()); — это по замыслу, запись в конец блока памяти? Зачем делается EEPROM.read, если только что eeprom очищалась нулями? (или это хитрый сдвиг такой?). Кстати, признак хорошего тона в программировании это как раз такие вещи писать в комментариях, это облегчает понимание кода. Также если EEPROM.length() это константа, равная объему eeprom в Arduino, то ее как раз стоило бы объявить глобальной переменной типа int eeprom_len = EEPROM.length(), это облегчило бы читаемость кода и немного его ускорило (быстрее прочитать готовую переменную, чем обращаться каждый раз заново к функции).

        Это не придирки, а пожелания на будущее, как сделать еще лучше.


        1. lamerok
          27.07.2019 23:01

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

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

          С другой стороны для 14 летнего подростка похвально… да и настраивать не так часто надо. Так что EEPROM на всю жизнь хватит. Молодец!


          1. DmitrySpb79
            28.07.2019 11:20

            > Насколько, я понял… EEPROM.length() это количество таймеров

            EEPROM это встроенный класс в Arduino www.arduino.cc/en/Reference/EEPROM, и думаю, length() возвращает объем флеш-памяти на контроллере, т.е. это всегда константа. Проверять лень :)


            1. lamerok
              28.07.2019 12:13

              Да, так и есть, я скобки пропустил, получается тогда, количество таймеров лежит по адресу EEPROM.length() — 1. Вначале оно обнуляется а потом увеличивается пока данные из последовательно го порта поступают.


      1. IRFC
        27.07.2019 22:08
        +2

        Стоять пишется через о :)


  1. igrushkin
    27.07.2019 22:58

    Прекрасный тег и ник


  1. Cadil_TM
    28.07.2019 10:31

    Мне симпатичен ваш подход: осваивать новое что-то делая. Успехов!
    На схеме не заметил проводков, по которым, собственно, управляется лампочка в курятнике.
    Трансформатор в блоке питания — это, думаю, устаревший опыт отца. И еще: не злоупотребляйте перезаписями в флеш-память.


    1. VT100
      28.07.2019 11:07
      +1

      Трансформатор в блоке питания — это, думаю, устаревший опыт отца.

      Зато — почти «железобетонный».


      1. Cadil_TM
        28.07.2019 13:07

        Зато — почти «железобетонный».

        Не спорю, но добавьте сюда мостик, электролиты, микросхему стабилизатора и сравните с модулем понижающего преобразователя AC-DC объемом около 3 см3 со всевозможными защитами. Совсем не очевидно, что «железобетоннее» в целом.


        1. VT100
          29.07.2019 07:34
          +1

          но добавьте… и сравните с ...

          И, весьма вероятно, фэйковой надёжностью. Так-что — варианты для выбора есть.


          1. Cadil_TM
            29.07.2019 14:08

            И, весьма вероятно, фэйковой надёжностью.

            Делать вывод о фейковой надежности модуля на том основании, что в его схеме есть «ненадежные» электролиты — сомнительная идея.
            Ресурс электролитического конденсатора зависит, в частности, от соотношения между постоянной, переменной (пульсацией) составляющими напряжения на конденсаторе и его номинальным напряжением.
            И если разработчик в официальной спецификации приводит величину надежности
            (MTBF 100000h), то — эта цифра обоснована.
            Другое дело — китайская реализация модуля, впрочем, как и всего остального…


  1. pin2t
    28.07.2019 10:59
    +8

    Крутизна, в 14 лет уже программы для микроконтроллеров пишете.
    Я ничего не соображаю в электронике, но есть у меня пара советов по организации кода. Организация кода даже в таком маленьком проекте очень важна, программу будет проще понять и даже вы сами когда захотите добавить тот же датчик температуры через полгода, быстрее поймете что же там как работает.
    Итак, во-первых и самое главное, в программе мало названий, относящихся к предметной области (управлению устройствами по таймерам), все названия очень асбтрактные и относятся к платформе (values, dititalWrite, EEPROM и т.п.). Нет даже ни одного названия timer. Из-за этого логика работы плохо понятна, даже в такой небольшой программе.

    iarduino_RTC time(RTC_DS1307);  

    Лучше назвать эту переменную clock. На это кстати даже намекает название класса RTC (Real Time Clock)
    int values; 

    Эта переменная используется только внутри функции loop, там её лучше и объявить. Глобальные переменные плохи тем, что их значение может меняться где угодно в программе, из-за этого разные функции оказываются слишком сильно связанны между собой, через глобальную переменную.
    Кроме того, название во множественном числе values предполагает массив значений, а тут оно только одно, лучше value или data
    Но в целом эта переменная вообще не нужна, ниже будет видно почему
    unsigned int to_minutes(unsigned int hours, unsigned int minutes)//функция для перевода часов и минут в минуты
    {
      return (60*hours)+minutes;
    }

    Вот например хорошая функция, все понятно что она делает благодаря названиям переменных и самой функции. Ну а коментарий тут вообще ненужен, он ничего нового не объясняет, все итак понятно из кода
    if(Serial.available() > 0)                                //если что-то пришло

    Опятьже, такие коментарии объясняющие очевидное только увеличивают размер текста необходимый для прочтения и не несут полезной информации
    Функция loop и делает слишком много работы, и устанавливает настройки с телефона в энергонезависимую память, и включает/выключает устройства по таймерам. Лучше разбить её на две, а лучше три или четыре
    Например
    
    void storeTimers()
    {
      for (int i = 0 ; i < EEPROM.length() ; i++)
        EEPROM.write(i, 0);
      unsigned int timers = 0;
      while (Serial.read() > 0) {                                       //пока есть что получать
        delay(50);
        for(int i = 0; i < 7; ++i) {
          EEPROM.write(timers * 7 + i, Serial.read());
          delay(50);
        }
        ++timers;
        EEPROM.write(EEPROM.length() - 1, timers);
      }
    }
    

    Как уже правильно написали, логика записи настроек таймеров не очень понятна, скорее всего работает запись настроект только одного таймера.
    Далее, комментарий типа
    if(EEPROM.read(i * 7 + 0) == 1)                                                                               //если таймер нужно проверять
    

    он как раз и написан потому что без него вообще непонятно чего к чему. Но, если добавить функцию с подходящим названием, коментарий становится ненужен. Вот например так
    bool enabled(int timer)
    {
      return EEPROM.read(i * 7) == 1;
    }
    

    Соответственно проверка превращается в
    if (enabled(i))

    Гораздо понятнее и не требуется коментарий
    Далее, повторяющиеся конструкции вроде
    if((to_minutes(EEPROM.read(i * 7 + 1), EEPROM.read(i * 7 + 2)) <= to_minutes(time.Hours, time.minutes)) && //если пришло время для работы
             (to_minutes(EEPROM.read(i * 7 + 3), EEPROM.read(i * 7 + 4)) > to_minutes(time.Hours, time.minutes)))
    и
    pinMode(EEPROM.read(i * 7 + 5), OUTPUT);                                                                  //настраиваем пин таймера как выход
            digitalWrite(EEPROM.read(i * 7 + 5), EEPROM.read(i * 7 + 6));                                             //посылаем на пин нужное значение

    тоже вынесем в функции
    
    bool ready(int timer)
    {
      return (to_minutes(EEPROM.read(timer * 7 + 1), EEPROM.read(timer * 7 + 2)) <= to_minutes(clock.Hours, clock.minutes)) &&
             (to_minutes(EEPROM.read(timer * 7 + 3), EEPROM.read(timer * 7 + 4)) > to_minutes(clock.Hours, clock.minutes));
    }
    
    void set(int timer, bool invert)
    {
      pinMode(EEPROM.read(timer * 7 + 5), OUTPUT);
      unsigned int val = EEPROM.read(timer * 7 + 6);
      digitalWrite(EEPROM.read(timer * 7 + 5), invert ? !val : val);
    }
    

    Итого получается следующий код
    #include <Wire.h>                                            
    #include <iarduino_RTC.h>                                    
    #include <EEPROM.h>                                          
    
    iarduino_RTC clock(RTC_DS1307);
    
    unsigned int to_minutes(unsigned int hours, unsigned int minutes)
    {
      return (60*hours)+minutes;
    }
    
    void setup()
    {
      time.begin();
      Serial.begin(9600);
    }
    
    // читает текущее время из последовательного порта
    void storeTime()
    {
      clock.settime(0, -1, Serial.read());
      delay(50);
      clock.settime(0, Serial.read());
    }
    
    // читает настройки таймеров из последовательного порта и сохраняет в энергонезависимой памяти
    void storeTimers()
    {
      for (int i = 0 ; i < EEPROM.length() ; i++)
        EEPROM.write(i, 0);
      unsigned int timers = 0;
      while (Serial.read() > 0) {
        delay(50);
        for(int i = 0; i < 7; ++i) {
          EEPROM.write(timers * 7 + i, Serial.read());
          delay(50);
        }
        ++timers;
        EEPROM.write(EEPROM.length() - 1, timers);
      }
    }
    
    bool enabled(int timer)
    {
      return EEPROM.read(timer * 7) == 1;
    }
    
    bool ready(int timer)
    {
      return (to_minutes(EEPROM.read(timer * 7 + 1), EEPROM.read(timer * 7 + 2)) <= to_minutes(clock.Hours, clock.minutes)) &&
             (to_minutes(EEPROM.read(timer * 7 + 3), EEPROM.read(timer * 7 + 4)) > to_minutes(clock.Hours, clock.minutes));
    }
    
    void set(int timer, bool invert)
    {
      pinMode(EEPROM.read(timer * 7 + 5), OUTPUT);
      unsigned int val = EEPROM.read(timer * 7 + 6);
      digitalWrite(EEPROM.read(timer * 7 + 5), invert ? !val : val);
    }
    
    void loop()
    {
      if(Serial.available() > 0) {
        storeTime();
        storeTimers();
      }
    
      //проход по каждому таймеру
      int timers = EEPROM.read(EEPROM.length() - 1);
      for(int timer = 0; timer < timers; ++timer) {
        if(enabled(timer)) {
          clock.gettime();
          if(ready(timer))
            set(timer, false);
          else {
            bool found = false;
            //ищем таймер с таким же пином
            for (int t = 0; t < timers && !found; ++t) {
              if ((t != timer) && enabled(t) && ready(t) &&
                 EEPROM.read(timer * 7 + 5) == EEPROM.read(t * 7 + 5)) {
                set(t, false)
                found = true;
              }
            }
            if(!found)
              set(timer, true);
          }
        }
      }
    }

    Примерно так. Такой код намного легче понять и в нем не требуются коментарии, потому что все функции маленькие и имеют названия из предметной области. Надеюсь будет полезно, удачи.
    PS: Имя, ник и теги огонь


    1. vladkorotnev
      29.07.2019 08:59
      +1

      Для полного кайфа лучше таймер как сущность вынести в struct. Сходу, правда, не скажу, можно ли в ардуине читать и писать на флеш сразу пачку байт, но если можно, то код станет ещё куда читабельнее и удобнее.


      Автору же хотелось бы ещё посоветовать, если есть желание заниматься электроникой и дальше, отходить от fritzing и потихоньку осваивать полноценные принципиальные схемы — они тоже куда читабельнее и понятнее в роли документации к проекту :-)


      1. DeVeLoPer-HiKiTa Автор
        29.07.2019 09:32

        В архиве проекта есть. Можете скачать и посмотреть.


        1. vladkorotnev
          29.07.2019 09:48

          Если есть, тогда вообще отлично :-)


          Говоря про архив проекта — попробуйте использовать Github или Bitbucket. Так большему числу людей будет удобнее видеть код (например, я сейчас на работе и скачивание с яндекса заблокировано). Плюсом также — вся история изменений в проекте (если что-то сломалось или стало работать не так, можно найти, из-за чего), возможность вести список задач и проблем, собирать багрепорты, получать улучшения и доработки от других в виде пулл-реквестов.


          1. DeVeLoPer-HiKiTa Автор
            29.07.2019 17:43

            Возьму на вооружение.


      1. pin2t
        29.07.2019 10:56

        Как вариант. Особенно если мы бы хотели сэкономить количество чтений EEPROM. Но с точки зрения упаковки всех настроек таймера в struct вообщем неважно есть там чтения пачки байт или нет, можно также int-ами читать, это все равно будет скрыто в функции чтения.


        1. vladkorotnev
          29.07.2019 11:06
          +1

          К счастью чтения-то экономить не надо :-)


  1. dndzph
    28.07.2019 12:11

    Главное что поставленную задачу прибор выполняет, но не проще ли было делать на
    ESP32? Там уже блютус встроен, плюс можно было поставить модуль камеры, датчик температуры, а в приложении настроит получение фотоотчета.


  1. marsdenden
    28.07.2019 15:59
    +3

    Предлагаю дальнейшее развитие. Курам для увеличения яйценоскости увеличивают световой день и таймер для этого — хорошая штука. Но при этом хорошо бы и поставить себя на место если и не курицы, то хотя бы петуха — резкое отключение света — стресс. У меня в курятник заходят по мере захода солнца и не все сразу, а сильно по очереди. При этом садятся на насест пока видят. С учетом этой физиологической особенности (ака куриная слепота) хорошо бы сделать плавное гашение света в течении длительного времени, от 15 до 30 минут. Включение тоже было бы неплохо плавным, но не обязательно таким длительным, а, к примеру, в течении трех минут


    1. vladkorotnev
      29.07.2019 09:00

      Тут уже вопрос к силовой части, которая в проект не вошла. Если там просто реле, то особо яркостью не порулить.


      1. Yuriy_krd
        29.07.2019 12:30
        +1

        С помощью реле вполне себе можно рулить яркостью. Через ШИМ(Широтно-импульсная модуляция ). Только, чтобы это не напоминало трещотку, нужно твердотельное реле или диммирование через мосфет-транзистор. Вполне себе получается. Я себе сделал подобие часов с функцией «рассвет» ко времени будильника. Работает как с лампами накаливания, так и со светодиодами. Именно таким способом регулируется яркость всех светодиодных устройств.


        1. vsergoog
          29.07.2019 12:39

          А если за реле стоит светодиодная лапма на 220В?


          1. Yuriy_krd
            29.07.2019 12:53

            Ответ все тот же. Твердотельное реле способно коммутировать до 40А при 220В (это чуть меньше 10КВт в максимуме). Мосфет-транзисторы чуть поменьше, но ватт 300 берут без проблем (с прикрученным радиатором на транзисторе).
            Ради эксперимента делал диммер для светодиодной лампы на цоколе Е27. Конструкция работает безупречно, разве что дороговата для простого диммера и размерами великовата).


            1. vsergoog
              29.07.2019 13:10

              Да, я забыл, с диммируемыми лампами должно работать.


              1. Yuriy_krd
                29.07.2019 13:17

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


                1. vsergoog
                  29.07.2019 13:22

                  Спасибо, не знал подобных подробностей.


                1. vladkorotnev
                  30.07.2019 03:09

                  Не совсем переменник там. Переменник там бы очень дико грелся и не очень долго работал :-)
                  Там стоит тиристор и компаратор, который при нарастании напряжения по модулю выше какой-то точки отключает выход. То есть получаем на выходе не синусоиду, а как бы её огрызок.
                  Так как средней светодиодной лампе с её импульсником на входе до чисто активной нагрузки как пешком до Китая, то получаем полный набор спецэффектов от нерегулируемости и до мигания как накуренный стробоскоп на дискотеке.
                  Зато таким диммером, оставшимся после ремонта, вполне себе можно регулировать старый советский паяльник ватт на 40-60 — только термометр прикрутить, и колхозная паяльная станция готова :-)


                  1. Yuriy_krd
                    30.07.2019 07:15

                    Таки не во всех !) у меня дома в диммере стоит обычный переменный резистор с выключением (мощность самого резистора не помню, давно разбирал). Ограничение нагрузки (согласно надписи) — 150Вт. Вот и вся конструкция.


                    1. VT100
                      30.07.2019 07:32

                      И оба заблуждаются (ну, или опечатываются).

                      Там стоит тиристор и компаратор, который при нарастании напряжения по модулю выше какой-то точки отключает выход.

                      Включает выход. Т.к. тиристор (симистор) — проще включить, чем выключить. Выключение делается автоматически, за счёт перехода сети через ноль.

                      … у меня дома в диммере стоит обычный переменный резистор… Ограничение нагрузки (согласно надписи) — 150Вт.

                      Не может быть потому, что для ограничения мощности на нагрузке до 75 Вт — на резисторе придётся сжечь сравнимую мощность.


    1. Cadil_TM
      29.07.2019 14:19

      Курам для увеличения яйценоскости увеличивают световой день ...

      Уточню — для увеличения производительности кур сутки в помещениях курятников птицефабрик без естественного освещения делят на два «световых дня». В результате на выходе — два яйца вместо одного.
      Продолжительность жизни курицы при таком режиме (0,5 года) — не в счет.
      Извините за повтор — не там разместил пост.


    1. Cadil_TM
      29.07.2019 14:26

      Курам для увеличения яйценоскости увеличивают световой день ...

      Уточню — для увеличения производительности кур сутки в помещениях курятников птицефабрик без естественного освещения делят на два «световых дня». В результате на выходе — два яйца вместо одного.
      Продолжительность жизни курицы при таком режиме (0,5 года) — не в счет.
      Извините за повтор — не там разместил пост.


  1. Imbashket
    29.07.2019 09:27

    Если вдаваться в подробности выращивания птиц, то лучше всего сделать плавное включение и отключение света (минут за 10). Тогда они меньше пугаются и лучше ведут себя по другим показателям. На птицефермах еще специально уменьшают «сутки» путем увеличения включений/отключений света.


  1. zoldaten
    29.07.2019 15:50

    1. Судя по кишочкам на фото, arduino nano питается по Vin пину. Сколько на нее подается? 9 V? С учетом того, что с ног она еще питает HC-05 и DC1307 и вкл/выкл реле.
    2. Нет ли ложных срабатываний при переключении реле? У меня с этим были проблемы одно время в проекте wi-fi розетки www.youtube.com/watch?v=QAulFZaALm4
    3. Влияет ли использование в коде функции delay(50), которая подвешивает код, на производительность и таймер?


    1. DeVeLoPer-HiKiTa Автор
      29.07.2019 21:47

      1. Везде 5 вольт.
      2. Обнаружены не были.
      3. Все окей.


    1. Yuriy_krd
      30.07.2019 07:18

      Кстати, тоже заинтересовало, почему delay вместо delayMicroseconds или millis…


      1. DeVeLoPer-HiKiTa Автор
        30.07.2019 09:28

        Так разница между delay и delayMicroseconds только в передаваемых величинах (миллисекунды и микросекунды соответственно). А в millis'е нужды не увидел. И к тому же мне кажется вариант с delay наиболее компактен.


        1. Yuriy_krd
          30.07.2019 09:34

          Да, верно, маху дал с delay и delayMicroseconds. Я вариант с delay использовал только для моргания светодиодами в самом начале. Ведь delay вешает намертво систему на паузу. Такое в более-менее серьезных проектах, как мне кажется, неприемлемо. Особенно, если используются таймеры. Но это мое мнение.


          1. vsergoog
            30.07.2019 10:41

            Я делал простейшую систему с атомарными «задачами». Задаче гарантируется, что она будет запускаться с определенным интервалом. Со своей стороны задача гарантирует, что она не будет использовать функций с задержками. Если задаче нужно подождать, то она просто пропускает необходимое число циклов. Просто, эффективно, и позволяет между опросами задач отправлять в сон контроллер для снижения энергопотребления.


            1. DeVeLoPer-HiKiTa Автор
              30.07.2019 13:48

              Снижение энергопотребления это круто.


  1. lduhl
    29.07.2019 21:45

    жесть. я в 14 лет гонял в дюка3д и игрули на Спектруме. Хоть и умел программировать. молодец! )


  1. PR200SD
    30.07.2019 09:52

    1 плата wemos, и вы получаете вменяемое устройство управляемое с телефона без лишних плат, правда желателен wi-fi ;)


    1. DeVeLoPer-HiKiTa Автор
      30.07.2019 10:02
      +1

      И пропустить все танцы с бубном?


      1. iig
        30.07.2019 12:02

        Там тоже без танцев не обойтись ;)
        Но удобно же. Благодаря встроенному WIFI — -1 модуль.
        Очень много памяти (по сравнению с ардуиной), можно, к примеру, рассчитать заранее по дням время восхода-заката солнца, и включать свет когда это действительно нужно.


        1. vsergoog
          30.07.2019 14:12

          А в чем проблема расчитывать его раз в сутки?


          1. iig
            30.07.2019 14:53

            Не проблема ;). Любую задачу можно решить более чем 1 способом.
            Нужно немного упороться в астрономию. Или взять готовую, уже рассчитанную таблицу. Или спросить у openweathermap…


            1. vsergoog
              30.07.2019 15:49

              Уже расчитанная таблица будет зависеть от координат спрашивающего. Да еще и от года. Проще один раз написать функцию, тут я уже давал ссылку на реализацию.
              Но лично я сейчас больше предпочитаю esp8266(nodemcu). Там тоже wifi на борту.


              1. iig
                30.07.2019 16:03

                Координаты — константа ;)
                Год… На интервале в 20 лет вряд ли набежит значительная погрешность. А через 20 лет, возможно, на месте курятника будет бассейн или небоскреб.
                Формула тоже хорошо, если она есть, и если она правильная.


  1. zoldaten
    30.07.2019 12:25

    Так понимаю, проект уже публиковался community.alexgyver.ru/threads/tajmer-na-arduino.1039 Но сила Хабра — бесконечна!


    1. DeVeLoPer-HiKiTa Автор
      30.07.2019 13:50

      Да, Хабр мощная штука.