Проект "умной" теплицы.
- Войдите на сайт для отправки комментариев
День добрый ! Прошу помощи с оптимизацией кода на ардуино.
Хотелось бы сделать что-то типа "умной" теплицы.
Начал с датчика температуры. Под руками был аналоговый lm335. Раньшн подключал его к ардуино , проблем этот датчик не вызывает.
Далее по замыслам , нужно было добавить пороговую температуру и гистерезис.
Почитал форумы - выбор пал на энкодер. с кнопкой. То есть управление датчиком - на энкодере, переключение кнопкой.
Управление кнопочкой честно содрал отсюда) Так же посмотрел проект регулировки яркости светодиода.
И код даже работает. Правда , энкодер иногда гонит чушь - то есть кручу вправо - счетчик работает четко - один щелчок и плюс одно значение, а вот влево - может на щелчок прибавить, может отнять... Но это может быть неисправен и энкодер. Так же не обращайте внимание на analog_write -просто мне по яркости свечения светододов удобнее отслеживать что происходит. В планах - подключитьдисплей для вывода информации.
По коду- оптимизация заключается в добавлении процедуры.( Хотя бы, знаю что написано криво, но это криво работает и я даже понимаю как) Но не совсем понимаю как.
Просто по подобной схеме нужно будет настроить датчик влажности почвы на полив и датчик влажности воздуха на проветривание помещения. И получается, что код настройки значений на энкодере будет повторяться 4 раза... Мы же должны объявить процедуру , а в последующем уже обращаться к ней, так?
И не нужно ли записывать отдельной командой записывать в память ардуино значения пороговой температуры и гистерезиса?
Код писал с комментариями, так что если где совсем мысль не верна - ткните носом пожалуйста.
int raw = 0; //значение аналогового выхода. int regim = 1;// переменные отвечающие за режим кнопки int flag = 0;// float temp = 0;//температура в Кельвинах float tempc = 0;// температура в Цельсиях int tempz = 1; // температура срабатывания датчика( изначально задано 1 градус цельсия, чтобы температура не опустилась ниже нуля). int gist = 0; // температурный гистерезис int fadeAmount = 1; // шаг изменения гистерезиса unsigned long currentTime; unsigned long loopTime; const int pin_A = 2; // pin 2 выходы энкодера const int pin_B = 3; // pin 3 unsigned char encoder_A; //значения выхода энкодера unsigned char encoder_B; unsigned char encoder_A_prev = 0; void setup() { Serial.begin(9600); pinMode( A0, INPUT );//установка а0,1 как аналоговый вход pinMode( A1, INPUT ); pinMode(9, OUTPUT); // устанавливаем pin 9 как выход pinMode(10, OUTPUT); pinMode(pin_A, INPUT); pinMode(pin_B, INPUT); currentTime = millis(); loopTime = currentTime; } void loop() { if (digitalRead(A1) == HIGH && flag == 0) //если кнопка нажата // и перемення flag равна 0 , то ... { regim++; flag = 1; //это нужно для того что бы с каждым нажатием кнопки происходило только одно действие if (regim > 2) //ограничим количество режимов { regim = 1; //так как мы используем только одну кнопку,то переключать режимы будем циклично } } if (digitalRead(A1) == LOW && flag == 1) //если кнопка НЕ нажата и переменная flag равна - 1 ,то ... { flag = 0; //обнуляем переменную "knopka" } if (regim == 1) //первый режим { currentTime = millis(); //блок обработки энкодера, задаём пороговую температуру tempz if (currentTime >= (loopTime + 5)) { // проверяем каждые 5мс (200 Гц) encoder_A = digitalRead(pin_A); // считываем состояние выхода А энкодера encoder_B = digitalRead(pin_B); // считываем состояние выхода B энкодера if ((!encoder_A) && (encoder_A_prev)) { // если состояние изменилось с положительного к нулю if (encoder_B) { // выход В в полож. сост., значит вращение по часовой стрелке, увеличиваем пороговую температуру, не более чем до50, шаг 1 градус. if (tempz + fadeAmount <= 50) tempz += fadeAmount; } else { // выход В в 0 сост., значит вращение против часовой стрелки, уменьшаем гистерезис, но не ниже 0 if (tempz - fadeAmount >= 0) tempz -= fadeAmount; } } encoder_A_prev = encoder_A; // сохраняем значение А для следующего цикла loopTime = currentTime; analogWrite(9, tempz); // устанавливаем вывод значения на 9 ножку, смотрим. } } if (regim == 2) //второй режим { currentTime = millis(); //блок обработки энкодера, задаём пороговую температуру гистерезиса if (currentTime >= (loopTime + 5)) { // проверяем каждые 5мс (200 Гц) encoder_A = digitalRead(pin_A); // считываем состояние выхода А энкодера encoder_B = digitalRead(pin_B); // считываем состояние выхода B энкодера if ((!encoder_A) && (encoder_A_prev)) { // если состояние изменилось с положительного к нулю if (encoder_B) { // выход В в полож. сост., значит вращение по часовой стрелке // увеличиваем гистерезис, не более чем до50, шаг 1 градус. if (gist + fadeAmount <= 50) gist += fadeAmount; } else { // выход В в 0 сост., значит вращение против часовой стрелки // уменьшаем гистерезис, но не ниже 0 if (gist - fadeAmount >= 0) gist -= fadeAmount; } } encoder_A_prev = encoder_A; // сохраняем значение А для следующего цикла loopTime = currentTime; analogWrite(10, gist); // устанавливаем вывод значения на 9 ножку, смотрим. } } //блок считывания температуры с лм335 { raw = analogRead(A0); temp = ( raw / 1023.0 ) * 5.0 * 1000 / 10;//перевод аналоговго сигнала в Кельвины tempc = temp - 273.15; //получаем температуру в градусах Цельсия if (tempz < tempc + gist) // Задаётся температура ( пороговая) которая сравнивается с текущей температурой плюс температура гистерезиса: если заданная температура(пороговая) меньше температуры с датчика + гистерезис, то выход ставим в "высокое состояние" ) digitalWrite(13, HIGH); else digitalWrite(13, LOW); if (tempz > tempc + gist) // если заданная температура(пороговая) больше температуры с датчика + гистерезис, то выход ставим в "низкое состояние" digitalWrite(13, LOW); else digitalWrite(13, HIGH); delay(20); Serial.println(temp); //опрос порта на вывод температуры в кельвинах и цельсиях Serial.println(tempc); Serial.println(tempz); Serial.println(gist); } }
В принципе, все задаваемые пороговые значения лучше хранить в EEPROM, при обесточивании устройства не понадобится вводить заново.
За eeprom- спасибо) Но пока что , это слишком " круто" для меня.Я пока дисплей то " знаю" как прикрутить только через библиотеку...
Есть энное кол- во литийона - пока на нем сделаю резервное питание.
Небольшое замечание по поводу энкодера - на землю стоят кондёры по 68 нанофарад. Так , что хоть какая-то защита от дребезга есть.
Просто не пойму, почему в одну сторону всё четко считается, а в обратную - бардак.
механика несимметричная
попробуйте такой вариант
зы. здесь на форуме есть темы и по енкодеру, и по умным теплицам, где много чего реализовано .... пользуйтесь поиском
Искал, находил, читал) Кое что , возможно, к себе утащу. Сейчас , скорее, все -же вопрос о том , как оптимизировать код.
Ну или же , допишу как есть, проверю работу и оформлю тему в проектах. А там уже и код перепилим.
пост по энкодеру дополнил см. выше, это же и к вопросу оптимизации подходит
какие там емкости подключены к выходам энкодера 33, 68 или 100 нан - не принципиально.
Ух , появилось время, проверил код. Увы - тоже самое. В одну сторону считает нормально , во вторую -не пойми как. убрал макетку, подсоединил шлейф - тоже самое. Сменил плату - на нано. Тоже самое. Подозреваю что такая фигня происходит из-за дребезга. Ибо считать может 1,2 ,3..... 9 и при следующем "шажке" - 16, 17...
А назад прокручиваешь - 17, 16, -233, -232, -231....-225, -189....
В общем не могу понять - где косяк то всплыл... Ёмкостей мало , или 3 тий китайский энкодер - совсем уж китайский?
Готовый код - не вариант. Даже с учетом того , что есть dht11.
Меня больше другое интересует - Если писать обращение к энкодеру через код Грея - все работатет. Ну почти все. То есть - крутим энкодер вправо , мониторим порт и видим стрелочку вправо ->, если влево , то <-.И всё работает чётко. Но стоит добавить счетчик ( +1 вправо и -1 влево) , как начинается черт знает что. Стрелочки всегда правильно выдаёт , а вот цифры выводит через раз. Иногда выводит просто стрелочку и всё...
Я думаю, что стоит посмотреть в сторону фильтра Калмана. Тут неплохая статья.