1 2 3 4 5 6 7 8 9 10 9/10 9,18оценок: 11

Контроллер для теплицы на Arduino

Тема в разделе "Теплицы и парники", создана пользователем Cofessor, 20.10.15.

Статус темы:
Закрыта.
  1. Cofessor
    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467

    Cofessor

    Виталий

    Cofessor

    Виталий

    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467
    Адрес:
    Брянск
    Согласен, так понятней. Переделал. 1.jpg 1.jpg
     
  2. Cofessor
    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467

    Cofessor

    Виталий

    Cofessor

    Виталий

    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467
    Адрес:
    Брянск
    Несколько раз брался за процедуру обработки нажатия, а чистый единичный импульс всё равно получить не мог. В прежнем варианте у меня программа просто не выходила из процедуры обработки нажатия кнопки, пока не закончится установка всех параметров, поэтому вопрос дребезга и казался мне малосущественным. Но после того сделал динамический опрос, когда программа за время нажатия кнопки мухой пролетает по всему телу программы много раз, оказалось что избавиться от проскоков или, наоборот, от отсутствия реакции на нажатие не просто.
    В конце концов, оказалось что в программу надо было добавить всего одну строку:
    while (analogRead (A0)<1000); /Ждать, пока кнопка отпустится
    То есть, функция возвращает результат не по нажатию, а по отпусканию кнопки.
    Теперь вроде всё работает чётко, тьфу, тьфу (вообще, я не суеверный, это так, эмоции), в общем, будущее покажет, может ещё какая бяка проявится.
    И вот весь скетч на данный момент:
    Код:
    #include "DHT.h"            //библ. для датчика DHT22
    #include <LiquidCrystal.h>  //библ. для дисплея LCD1602
    #include <OneWire.h>        //библ. для датчика DS18B20
    #include <DS1307.h>         //библ. для RTC
    DS1307 rtc(A4,A5);
    OneWire ds(A2);             //DS18B20 подключен к A2
    LiquidCrystal lcd(8,9,10,11,12,13);
    #define DHTPIN A1           //DHT22 подключен к А1
    #define DHTTYPE DHT22
    DHT dht(DHTPIN,DHTTYPE);    //Создаём объект dht
    byte l_kr[8]={B00011,B00101,B00101,B01001,B01001,B01001,B10001,B00000};   //Русские символы
    byte p_kr[8]={B11111,B10001,B10001,B10001,B10001,B10001,B10001,B00000};
    byte grad_kr[8]={B00110,B01001,B01001,B00110,B00000,B00000,B00000,B00000};
    byte u_kr[8]={B10001,B10001,B10001,B01111,B00001,B10001,B01110,B00000};
    byte d_kr[8]={B00010,B00110,B01010,B01010,B01010,B01010,B11111,B10001};
    byte i_kr[8]={B10001,B10001,B10001,B10011,B10101,B11001,B10001,B10001};
    byte g_kr[8]={B11111,B10001,B10000,B10000,B10000,B10000,B10000,B10000}; 
    float t,h,TReading,TempLSB,TempMSB2;           //Переменные для датчиков
    long prevMillis=0,prevMillis1=0,prevMillis2=0,prevMillis3=0; //Начала отсчёта времён
    byte t_heat=15,t_close=25,t_open=27,t_cool=30; //пороговые значения температур
    byte hhour_w1,lhour_w1,hmin_w1,lmin_w1;        //часы и минуты включения полива
    byte hhour_w2,lhour_w2,hmin_w2,lmin_w2;        //часы и минуты выключения полива
    byte hhour_l1,lhour_l1,hmin_l1,lmin_l1;        //часы и минуты включения освещения
    byte hhour_l2,lhour_l2,hmin_l2,lmin_l2;        //часы и минуты выключения освещения
    byte present=0,i,j,w_interv=0,days_left,flag,param=0;
    int TempMSB,key;
    
    void setup(){//Здесь добавить включение/выключение подсветки
      rtc.halt(false);       //Инициализация и установка часов
      rtc.setDOW(SUNDAY);          //Установлено воскресенье
      rtc.setTime(17,8,0);         //Время в 24ч формате
      rtc.setDate(8,12,2015);      //Дата
      rtc.setSQWRate(SQW_RATE_1);  //Установить SQW, выход на 1 Гц,
      rtc.enableSQW(true);         //включить SQW
    
      lcd.createChar(1,l_kr);      //Знак Л
      lcd.createChar(2,p_kr);      //Знак П
      lcd.createChar(3,grad_kr);   //Знак градуса
      lcd.createChar(4,u_kr);      //Знак У
      lcd.createChar(5,d_kr);      //Знак Д
      lcd.createChar(6,i_kr);      //Знак И
      lcd.createChar(7,g_kr);      //Знак Г
      lcd.begin(16,2);             //Инициализация LCD1602
      dht.begin();                 //Инициализация DHT22
    }
    void loop() {
      if(millis()-prevMillis>2000){   //Опрос датчика температуры и влажности DHT21 1 раз за 2 сек
        prevMillis=millis();
        _DHT21();                 }
      if(millis()-prevMillis1>1000) { //Опрос датчика температуры DS18B20 1 раз в сек
        prevMillis1=millis();
        DS18B20();                  }
      if(millis()-prevMillis2>1000) { //Управление исполнительными механизмами
        prevMillis2=millis();
        actuators();                }
      if(millis()-prevMillis3>200){   //Выбор меню
        prevMillis3=millis();
        menu();                   } 
      if(analogRead(A0)<1000) {      //Нажатие кнопки
        int knopka();
        key=knopka();                //Сохранили № кнопки
        if(key==1)param++;           //Нажата кнопка меню и выбора параметров
        if(key==2)flag=1;     }      //Нажата кнопка установки параметра
      if(flag==1)      {
        flag=0;
        setParameters();
                       }
    } //End LOOP
    //-------Опрос датчика DHT21 1 раз за 2 сек---------------------------
    void _DHT21(){
        h = dht.readHumidity();               //и считать текущее значение
        t = dht.readTemperature();            //температуры и влажности
        if (isnan(t)||isnan(h)){              //Если не удалось,
        lcd.setCursor(0, 0);
        lcd.print("Failed read DHT");}       //пишем на LCD "Ошибка чтения"
    }
    //-------Опрос датчика DS18B20 1 раз в сек----------------------------
    void DS18B20(){
       byte data[12];          // массив для хранения информации с датчиков
       byte addr[8];           // массив для хранения адреса датчиков
       if (!ds.search(addr)) { //Если адрес DS18B20 не найден - возврат
          ds.reset_search();
          return;    }
        ds.reset();
        ds.select(addr);       //Выбираем датчик
        ds.write(0x44);        //запускаем конвертацию, 1 - оставляем питание на линии
        present=ds.reset();
        ds.select(addr);       //выбираем датчик
        ds.write(0xBE);        //считываем ОЗУ датчика
        for (i=0;i<9;i++) {    //записываем 9 байт полученных с ОЗУ датчика
          data[i]=ds.read();
          }
        TempLSB=data[0];       //высчитываем температуру
        TempMSB=data[1];
        TempMSB2=TempMSB<<4 ;
        if(TempMSB2>1000){
          TempMSB2=TempMSB2-3968;
          TReading=TempMSB2+TempLSB/16-128;
        }
        else {TReading = (TempMSB << 4) + TempLSB/16;}
    }
    //-------Управление исполнительными механизмами------------------------
    void actuators(){
           DDRD=DDRD|B00111100;           //Настроить разряды 2-5 на вывод
           if(t<t_heat){                  //Если t < t_heat -
             PORTD=B00000100;}            //включить нагрев
           if(t>t_heat+1){                //Если t > t_heat+1 -
             PORTD=B00000000;}            //выключить нагрев
       }
    void menu(){ //---Меню на 3 экрана---
        if(param==0){                       //Меню №1(главное)
          lcd.home();
          lcd.print("TE\2\1=");
          lcd.print(t, 0);                  //Температура в теплице
          lcd.print("\3C B\1=");
          lcd.print(h, 0);                  //Влажность в теплице
          lcd.print("%");
          lcd.print(" ");
          lcd.setCursor(0, 1);
          lcd.print("HA\4\1=");
          lcd.print(TReading, 0);           //Температура на улице
          lcd.print("\3C");
          lcd.print(" ");
          lcd.setCursor(10, 1);
          lcd.print(" ");   
          lcd.print(rtc.getTimeStr());      //Текущее время
          lcd.setCursor(15, 1);
        }   
    //Меню №2 - Установка порога включения ТЭНа и времени вкл/выкл досветки
        if(param>0&&param<10){
          lcd.home();                      //Меню №2
          lcd.print("BK\1 HA\7PEB ");      //ВКЛ ТЭН 00
          lcd.print(t_heat);                //t° вкл. ТЭНа
          lcd.print(" \3C");     
          lcd.setCursor(0, 1);
          lcd.print("CBET ");               //СВЕТ   
          lcd.print(hhour_l1);              //Время вкл дес. час
          lcd.print(lhour_l1);              //ед. час
          lcd.print(":");
          lcd.print(hmin_l1);               //дес. мин
          lcd.print(lmin_l1);               //ед. мин
          lcd.print(" ");
          lcd.print(hhour_l2);              //Время выкл дес. час
          lcd.print(lhour_l2);              //ед. час
          lcd.print(":"); 
          lcd.print(hmin_l2);               //дес. мин
          lcd.print(lmin_l2);               //ед. мин
        }
        else if(param>9&&param<19){
    //Меню №3 - Установка времени вкл/выкл полива и периодичности полива
          lcd.home();                       //Меню №3
          lcd.print("BO\5A ");              //ВОДА
          lcd.print(hhour_w1);              //Время вкл дес. час
          lcd.print(lhour_w1);              //ед. час
          lcd.print(":");
          lcd.print(hmin_w1);               //дес. мин
          lcd.print(lmin_w1);               //ед. мин
          lcd.print(" ");
          lcd.print(hhour_w2);              //Время выкл дес. час
          lcd.print(lhour_w2);              //ед. час
          lcd.print(":"); 
          lcd.print(hmin_w2);               //дес. мин
          lcd.print(lmin_w2);               //ед. мин     
          lcd.setCursor(0, 1);
          lcd.print("\6HT ");               //Интервал полива
          lcd.print(w_interv);
          lcd.print(" C\4T OCT ");          //в сутках
          lcd.print(days_left);             //и сколько осталось до полива
          lcd.print(" ");   
        }
        else if(param>18 ){param=0;}
    }
    //------------Опрос кнопок------------------------------------
    int knopka(){
    uint16_t pitch, key_values[4]={100,200,400,600};
    uint32_t presstime;
      if(millis()-presstime>100){        //Дождаться окончания дребезга
        presstime=millis(); 
        pitch=analogRead(A0);            //и считать снова
        for(int j=0;j<4;j++){
          if(pitch<key_values[j]){
            while(analogRead(A0)<1000);  //Ждать, пока кнопка отпустится
            return j+1;//Возвращает № нажатой кнопки
          }
        }
      }
    }
    //Установка параметров
    void setParameters(){
          if(param==1)             { //Уст. температуры вкл. нагрева
            t_heat++;
            if(t_heat>20)t_heat=12;}
          //Установка времени включения досветки   
          else if(param==2)           {
            hhour_l1++;
            if(hhour_l1>2)hhour_l1=0; }
          else if(param==3)  {
            lhour_l1++;
            if((lhour_l1>3&&hhour_l1>1)||lhour_l1>9)lhour_l1=0;}
          else if(param==4)        {
            hmin_l1++;
            if(hmin_l1>5)hmin_l1=0;}
          else if(param==5)        {
            lmin_l1++;
            if(lmin_l1>9)lmin_l1=0;}
           //Установка времени выключения досветки   
           else if(param==6)          {
            hhour_l2++;
            if(hhour_l2>2)hhour_l2=0; }
          else if(param==7)  {
            lhour_l2++;
            if((lhour_l2>3&&hhour_l2>1)||lhour_l2>9)lhour_l2=0;}
          else if(param==8)        {
            hmin_l2++;
            if(hmin_l2>5)hmin_l2=0;}
          else if(param==9)         {
            lmin_l2++;
            if(lmin_l2>9)lmin_l2=0; }
          //Установки времени включения полива     
          else if(param==10)          {
            hhour_w1++;
            if(hhour_w1>2)hhour_w1=0;}
          else if(param==11)  {
            lhour_w1++;
            if((lhour_w1>3&&hhour_w1>1)||lhour_w1>9)lhour_w1=0;}
          else if(param==12)        {
            hmin_w1++;
            if(hmin_w1>5)hmin_w1=0; }
          else if(param==13)        {
            lmin_w1++;
            if(lmin_w1>9)lmin_w1=0; }
           //Установка времени выключения полива     
          else if(param==14)          {
            hhour_w2++;
            if(hhour_w2>2)hhour_w2=0;}
          else if(param==15)  {
            lhour_w2++;
            if((lhour_w2>3&&hhour_w2>1)||lhour_w2>9)lhour_w2=0;}
          else if(param==16)        {
            hmin_w2++;
            if(hmin_w2>5)hmin_w2=0;}
          else if(param==17)        {
            lmin_w2++;
            if(lmin_w2>9)lmin_w2=0; }
          else if(param==18)          {
            w_interv++;
            days_left=w_interv;
            if(w_interv>7)w_interv=0; }
          else if(param>18)param=1;
    }  //Конец setParametrs()
    /
    Хотя в данном варианте установки параметров ещё не все вопросы решены, как было в версии для одной кнопки:
    - изменяемый разряд не выделяется среди других, что раздражает,
    - не сделан возврат в основное меню по тайм-ауту.
    Надеюсь, на этом я не сяду надолго. тогда останется только сделать включение ИМ по совпадению установленного и текущего времени.
     
    Последнее редактирование: 16.12.16
  3. timon2006
    Регистрация:
    09.03.15
    Сообщения:
    979
    Благодарности:
    1.356

    timon2006

    Живу здесь

    timon2006

    Живу здесь

    Регистрация:
    09.03.15
    Сообщения:
    979
    Благодарности:
    1.356
    1. Дребезг при отпускании кнопки?
    2. Изменения в настройках (и на дисплее) при отпускании, а не при нажатии, пмсм - не комильфо.
    3. Длинные нажатия не определяются (например, при установке времени были бы полезны)
     
  4. Cofessor
    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467

    Cofessor

    Виталий

    Cofessor

    Виталий

    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467
    Адрес:
    Брянск
    Скетч посмотрите, как у меня время устанавливается. Я ранее об этом говорил - если устанавливать по отдельности десятки часов, единицы часов и т. д., то длинные нажатия становятся не нужными и установка происходит намного быстрее, потому что максимальная цифра - 9.
     
  5. timon2006
    Регистрация:
    09.03.15
    Сообщения:
    979
    Благодарности:
    1.356

    timon2006

    Живу здесь

    timon2006

    Живу здесь

    Регистрация:
    09.03.15
    Сообщения:
    979
    Благодарности:
    1.356
    ОК. Хотите, как радист морзянку - Ваше право:).
    А другие пункты?
     
  6. Cofessor
    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467

    Cofessor

    Виталий

    Cofessor

    Виталий

    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467
    Адрес:
    Брянск
    Это более естественно. не знаю, откуда возникла идея перебирать минуты и секунды от 1 до 60. А если бы цифра была до 1000? Всё равно бы упрямо перебирали 1000 цифр, вместо того чтобы установить 40 цифр?
    Паузу после отпускания поставлю, если лишние срабатывания ещё будут. Пока проверяю на симуляторе и ловлю чисто логические ошибки. Формирует ли симулятор искусственно дребезг? - Не знаю, когда спаяю реальное устройство, если будут проскоки, подправлю. Поскольку на симуляторе проскоки больше не проявляются, какой смысл воевать с тем что не видишь?
    2-й пункт не понимаю какое имеет значение? Нажал... и палец прилип?
     
  7. timon2006
    Регистрация:
    09.03.15
    Сообщения:
    979
    Благодарности:
    1.356

    timon2006

    Живу здесь

    timon2006

    Живу здесь

    Регистрация:
    09.03.15
    Сообщения:
    979
    Благодарности:
    1.356
    Когда такое будет необходимо, тогда буду думать, как это сделать:).
    Когда увидите, может быть поздно:(. Дребезг иногда и на кнопках не сразу проявляется. Пмсм, лучше сразу сделать правильно.
    В критической ситуации и кнопки продавливают, не то, что "палец прилип":).
    Простая логика: ничего не делаешь - ничего не меняется, нажал кнопку - чего-то поменялось.
    Пмсм, Вы делаете ошибку: надо сначала определиться с действиями пользователя (нажал то-то, увидел на экране это и т. д.), а потом, исходя из этого, писать программу.
     
  8. Cofessor
    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467

    Cofessor

    Виталий

    Cofessor

    Виталий

    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467
    Адрес:
    Брянск
    Так это же обычные программные ошибки. Заметил - исправил, может быть затратив всего несколько минут. У меня правда нет ноутбука для дачи, но ведь гроу-бокс делаю, сам сижу рядом с лоджией - можно исправлять и тут же проверять в реале.
    А что в данном случае неясного? Понятно что требуется: установить нужное время срабатывания, установить нужную температуру срабатывания, влажность и т. п. Если к контроллеру подойдёт кто-то в первый раз, то он увидит одну или две кнопки. Если ему никто не объяснил как этим пользоваться, то как он будет действовать? Методом научного тыка, так? Типа: ах, здесь есть кнопка, видимо она тут не случайно, значит её нужно нажимать. ну нажал, смотрит... ничаво... како-ое разочарование... - чешет затылок... а кнопку то бросил. Далее, думаю, легко смякитит, что кнопка явно не для того, чтобы за неё держаться до скончания века. Только и всего. Полагаю, это не предмет, чтобы на этом задерживаться. Тем более что есть золотое правило: всегда можно сделать новую версию, если что-то не нравится.
     
  9. Cofessor
    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467

    Cofessor

    Виталий

    Cofessor

    Виталий

    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467
    Адрес:
    Брянск
    Для возврата в основное меню по тайм-ауту понадобилось добавить всего 2 строчки:
    Код:
    if (analogRead (A0)<1000) { /Нажатие кнопки
    prevMillis4=millis(); /Сброс сч-ка тайм аута
    int knopka();
    key=knopka(); /Сохранили № кнопки
    if (key=1) param+; /Нажата кнопка меню и выбора параметров
    if (key=2) flag=1; } /Нажата кнопка установки параметра
    if (flag=1) {
    flag=0;
    setParameters();}
    if (millis()-prevMillis4>10000) param=0;/Если >10сек, возврат в основное меню
     
    Последнее редактирование: 17.12.16
  10. timon2006
    Регистрация:
    09.03.15
    Сообщения:
    979
    Благодарности:
    1.356

    timon2006

    Живу здесь

    timon2006

    Живу здесь

    Регистрация:
    09.03.15
    Сообщения:
    979
    Благодарности:
    1.356
    Есть законы Мерфи. Какая-нибудь вроде бы "обычная программная ошибка" запросто может повлечь глобальные (от переделки платы до изменения алгоритма программы) последствия. Не все и не любая, но может.

    В критической ситуации пользователь будет не затылок чесать, и не "мякитить", а кнопку на заднюю стенку корпуса выдавливать:).

    Ну, грабли, так грабли:)]. Есть еще бриллиантовое правило: делай хорошо, плохо оно само получится.
     
  11. Cofessor
    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467

    Cofessor

    Виталий

    Cofessor

    Виталий

    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467
    Адрес:
    Брянск
    Так что делать если сразу просто не выходит? Попробую позже уходить на опрос кнопок не по факту падения напряжения на analogRead (A0), а каждые 2 мсек. Далее анализирую предыдущий и последующий уровни и отсекаю фронты, если они неравнозначны. Так и тут оба сигнала могут попасть как раз в нижние гребни дребезга (те же грабли!), т. е., в принципе, возможна ситуация когда процедура дребезга не заметит - надёжнее задержку делать. В общем, довольно ясно сейчас вижу как должно быть, но просто решил подождать до проверки на реальном устройстве.
    Короче, думаю дурного влияния от процедуры нажатия кнопок на программу не будет, она лишь выдаёт номер параметра и флаг, которым пользуется только setParameters. Если флаг установлен, setParameters тут же его сбрасывает, чтобы не запускаться повторно, остальным процедурам вообще по фигу.
    Если что и беспокоит, так это то, что устанавливаемый разряд никак не выделяется среди других и возможно из-за этого снова вернусь к автоматической установке цифр, т. е. к установке одной кнопкой.
     
    Последнее редактирование: 17.12.16
  12. timon2006
    Регистрация:
    09.03.15
    Сообщения:
    979
    Благодарности:
    1.356

    timon2006

    Живу здесь

    timon2006

    Живу здесь

    Регистрация:
    09.03.15
    Сообщения:
    979
    Благодарности:
    1.356
    Не надо "предыдущий и последующий уровни" анализировать, надо статистику набирать:), тогда никаких "нижних гребней дребезга" не будет.
    Пмсм, лучше подумать, как его выделить (да хотя бы и миганием), чем делать управление одной кнопкой. Ничего сложного в этом не вижу.
    А писать программу по принципу "лишь бы программисту было удобнее и проще", не комильфо.
     
  13. Cofessor
    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467

    Cofessor

    Виталий

    Cofessor

    Виталий

    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467
    Адрес:
    Брянск
    Тем не менее, реализовал установку от одной кнопки, выкинув пару команд и добавив пару других. Просто хотел сравнить старый и новый вариант от одной кнопки. В новом всё летает без задержки и объём намного меньше, так что прогресс, какой-никакой, идёт.
    Над миганием ещё подумаю.
     
    Последнее редактирование: 17.12.16
  14. Cofessor
    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467

    Cofessor

    Виталий

    Cofessor

    Виталий

    Регистрация:
    23.06.13
    Сообщения:
    9.290
    Благодарности:
    8.467
    Адрес:
    Брянск
    Надоело раздумывать над миганием. Почти уверен что хорошего решения нет - параметров слишком много и каждый привязан к своему знакоместу на LCD. Не код получится, а хрен знает что.
    Пожалуй, остановлюсь на одной кнопке, как оптимальном варианте. Хотя, добавлю вторую - сделаю сдвиг в обе стороны, а перебор цифр - автоматически. Если себя не покажет в практике, тогда уже нужно будет подумать ещё, хотя это уже проверено.
    Завтра, будет время, займусь сравнением установок времени с текущим и включением оборудования по времени.
     
  15. timon2006
    Регистрация:
    09.03.15
    Сообщения:
    979
    Благодарности:
    1.356

    timon2006

    Живу здесь

    timon2006

    Живу здесь

    Регистрация:
    09.03.15
    Сообщения:
    979
    Благодарности:
    1.356
    Решений на самом деле много, и среди них есть совсем даже неплохие:).
    Одно из несложных: требуется 1 счетчик, 1 флаг, предполагается, что есть программа, рисующая дисплей, когда мы в настройках.
    Счетчик считает чего угодно, например, число циклов loop. Досчитав до числа N, обнуляется, инвертирует флаг и вызывает программу рисования. Программа рисует, перед рисованием собственно редактируемого параметра проверяет флаг. Если флаг = 0, то вместо параметра выводит пустые знакоместа, если флаг = 1, выводит параметр. Все. Число N определяет частоту мигания.
     
Статус темы:
Закрыта.