Проект "умной" теплицы.
- Войдите на сайт для отправки комментариев
День добрый ! Прошу помощи с оптимизацией кода на ардуино.
Хотелось бы сделать что-то типа "умной" теплицы.
Начал с датчика температуры. Под руками был аналоговый 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 нанофарад. Так , что хоть какая-то защита от дребезга есть.
Просто не пойму, почему в одну сторону всё четко считается, а в обратную - бардак.
механика несимметричная
попробуйте такой вариант
// Мой вариант подключения платы энкодера KY-040 // к выходам энкодера подключены конденсаторы по 33н // на плате уже исходно стоят подтягивающие резисторы по 10к // число проверок изменяется условием для i, больше 16 не проверял #define CLK 2 //clk , подключен к пину с прерыванием INT0 #define DT 3 //dt, может быть на произвольном цифровом пине volatile byte enc = 0; // счетчик энкодера от 0 до 255 void encoder() // Обработка прерывания { char j=0; for(byte i=0; i<8; i++) // несколько раз читаем состояния пина dt {if(digitalRead(DT)) j++; else j--; } // для защиты от дребезга контактов if(j>0) enc--; else enc++; // и по результатам тестов принимаем решение } void setup() { pinMode(CLK, INPUT); //digitalWrite(CLK, HIGH); pinMode(DT, INPUT); //digitalWrite(DT, HIGH); attachInterrupt(0, encoder, FALLING); // clk вешаем на прерывание 0 по спаду импльса Serial.begin(9600); } void loop() { Serial.println(enc); }зы. здесь на форуме есть темы и по енкодеру, и по умным теплицам, где много чего реализовано .... пользуйтесь поиском
Искал, находил, читал) Кое что , возможно, к себе утащу. Сейчас , скорее, все -же вопрос о том , как оптимизировать код.
Ну или же , допишу как есть, проверю работу и оформлю тему в проектах. А там уже и код перепилим.
пост по энкодеру дополнил см. выше, это же и к вопросу оптимизации подходит
какие там емкости подключены к выходам энкодера 33, 68 или 100 нан - не принципиально.
Ух , появилось время, проверил код. Увы - тоже самое. В одну сторону считает нормально , во вторую -не пойми как. убрал макетку, подсоединил шлейф - тоже самое. Сменил плату - на нано. Тоже самое. Подозреваю что такая фигня происходит из-за дребезга. Ибо считать может 1,2 ,3..... 9 и при следующем "шажке" - 16, 17...
А назад прокручиваешь - 17, 16, -233, -232, -231....-225, -189....
В общем не могу понять - где косяк то всплыл... Ёмкостей мало , или 3 тий китайский энкодер - совсем уж китайский?
#include <DHT.h> #include <OneWire.h> #include <DallasTemperature.h> #include <Ethernet.h> //DHT temperature and humidity #define dhtPin 7 #define dhType DHT11 DHT dhtSensor(dhtPin,dhType); //dallas temperature #define bus A2 // OneWire oneWire(bus); // шина для чтения датчика DallasTemperature dallaSensor(&oneWire); //fork #define moisturePin A0 #define ledPin 13 #define sensorCount 4 float sensorData[sensorCount]; char *sensorName[]= {"dht_t", "dht_h", "ground_t", "ground_h", "brightness"}; #define dhtTemp 0 #define dhtHumidity 1 #define dallas 2 #define fork 3 #define brightness 4 //Block C ---------------------------------------------------------------------------- #include <ArduinoJson.h> #include <Servo.h> #define INPUT_BUFFER_LEN 128 #define servoPin 5 #define pumpRelayPin 14 Servo servo; bool isProtectionMode = false; bool servoState = false; bool pumpState = false; bool lightOn = true; bool handRefreshTime; bool flags[3]; char *flagName[] = {"servState","pumpState","isProtectionMode"}; char inputBuffer[INPUT_BUFFER_LEN] = ""; short int dayTime; short int nighTime; //------------------------------------------------------------------------------------ char* sensorDescription[] = {"Air temperature in *C", "Air humidity in %", "Soil temperature in *C", "Soil humidity in %", "Brightness in %"}; #define brightneSensor A1 //Ethernet byte mac[] = {0xBA, 0xDC, 0xC0, 0xFE, 0x00, 0x00}; IPAddress ip(192, 168, 1, 250); IPAddress dnsServerIP(192, 168, 1, 1); IPAddress gateway(192, 168, 1, 1); IPAddress subnet(255, 255, 255, 0); EthernetClient client; IPAddress iot_add(52,4,204,67); bool openWindow = false; short int reacTime = 3; //задержка системы в секундах void ifBadEthernetSetUp() { if (Ethernet.begin(mac) == 0) { Serial.println("Failed to configure Ethernet using DHCP"); Ethernet.begin(mac, ip, dnsServerIP, gateway, subnet); } } void connectToThigworx() { if (client.connect(iot_add, 80)) { Serial.println("iot connected"); if(client.connected()){ Serial.println("client connected"); client.print("POST /Thingworx/Things/"); client.print(thingName); client.print("/Services/"); client.print(serviceName); client.print("?appKey="); client.print(appKey); client.print("&method=post&x-thingworx-session=true"); } } } // CCCC void sendFlags(){ for(short i=0; i<3; ++i){ client.print("&"); client.print(flagName[i]); client.print("="); client.print(flags[i]); } } //CCCCC void sendData(){ for(short i=0; i<sensorCount; ++i){ client.print("&"); client.print(sensorName[i]); client.print("="); client.print(sensorData[i]); } sendFlags(); // client.print("&"); // client.print("windowOpen"); // client.print("="); // client.print(openWindow); } void closePack(){ client.println(" HTTP/1.1"); client.println("Accept: application/json"); client.print("Host: "); client.println(iot_server); client.println("Content-Type: application/json"); client.println(); } float readDHTemperature(){ return dhtSensor.readTemperature(); } float readDHTHumidity(){ return dhtSensor.readHumidity(); } float readDallas(){ dallaSensor.requestTemperatures(); return dallaSensor.getTempCByIndex(0); } float readMoisture(){ return analogRead(moisturePin)/10.23; } int readBrightness(){ float valueBrightness=abs((1000-analogRead(brightneSensor))/10); return valueBrightness; } void getInfo(){ // sensorData[dhtTemp] = readDHTemperature(); // sensorData[dhtHumidity]= readDHTHumidity(); // sensorData[dallas] = readDallas(); // sensorData[fork] = readMoisture(); // sensorData[brightness] = readBrightness(); sensorData[dhtTemp] = random(0,60); sensorData[dhtHumidity]= random(5,100); sensorData[dallas] = random(0,20); sensorData[fork] = random(0,100); sensorData[brightness] = 5; } void showInfo(){ for(short int i=0; i<sensorCount; ++i){ Serial.println(String(sensorDescription[i]) + ": "+String(sensorData[i])); } Serial.println("-------------------------------"); Serial.println("\t\n"); } void blinkledOn(){ digitalWrite(ledPin, HIGH); } void blinkledOFF(){ digitalWrite(ledPin, LOW); } void setReacTime(){ if(handRefreshTime){ if(readBrightness() < 20){ reacTime = nighTime; }else{ reacTime = dayTime; } } if(readBrightness() < 20){ reacTime =5 ; }else{ reacTime =1; } } //Block C ---------------------------------------------------------------------------- void getServerResponse(){ short int i; bool inputFlag=false; char inputSmb; while(client.connected()){ while(client.available()){ inputSmb = client.read(); if(inputSmb == '{'){ inputFlag= true; }else{ if(inputSmb == '}'){ inputFlag = false; } } if(inputFlag == true){ inputBuffer[i] = inputSmb; i++; } } } inputBuffer[i] = '}'; inputBuffer[i+1] = '\0'; } void responseParser(){ StaticJsonBuffer<INPUT_BUFFER_LEN> jsonBuffer; JsonObject& jsonArray = jsonBuffer.parseObject(inputBuffer); servoState = jsonArray["servState"]; pumpState = jsonArray["PumpState"]; lightOn = jsonArray["lightState"]; isProtectionMode = jsonArray["isProtectionMode"]; handRefreshTime = jsonArray[""]; dayTime = jsonArray[""]; nighTime= jsonArray[""]; ; } void workWithThingWorx(){ connectToThigworx(); sendData(); closePack(); getServerResponse(); responseParser(); client.stop(); } void tesTBlocC(){ Serial.println(servoState); Serial.println(pumpState); Serial.println(isProtectionMode); Serial.println(lightOn); } void getFlagState(){// {"servState","pumpState","isProtectionMode",reacTime"} flags[0]=servoState; flags[1] = pumpState; flags[2] = isProtectionMode; } void controlDevices() { if (servoState) { servo.write(120); } else { servo.write(90); } if(lightOn){ digitalWrite(ledPin,HIGH); } else{ analogWrite(ledPin,LOW); } // Помпа if(pumpState){ digitalWrite(pumpRelayPin, HIGH); } // //? } #define max_dht_t 15 #define max_ground_t 15 #define max_dht_hum 30 #define max_ground_hum 15 #define min_brightness 40 void protectedMode(){//{"dht_t", "dht_h", "ground_t", "ground_h", "brightness"}; if(sensorData[0]>= max_dht_t || sensorData[2]>=max_ground_t|| sensorData[1]>= max_dht_hum){ servoState= true; } if(sensorData[3]>=max_ground_hum){ pumpState = true; } if(sensorData[4]<=min_brightness){ lightOn = true; } } //------------------------------------------------------------------------------------- void setup() { // put your setup code here, to run once: Serial.begin(9600); dhtSensor.begin(); pinMode(ledPin, OUTPUT); servo.attach(servoPin); dallaSensor.begin(); ifBadEthernetSetUp(); } void loop() { blinkledOn(); getInfo(); getFlagState(); workWithThingWorx(); if(flags[2]){ protectedMode(); } controlDevices(); showInfo(); setReacTime(); tesTBlocC(); delay(reacTime*1000); blinkledOFF(); }Готовый код - не вариант. Даже с учетом того , что есть dht11.
Меня больше другое интересует - Если писать обращение к энкодеру через код Грея - все работатет. Ну почти все. То есть - крутим энкодер вправо , мониторим порт и видим стрелочку вправо ->, если влево , то <-.И всё работает чётко. Но стоит добавить счетчик ( +1 вправо и -1 влево) , как начинается черт знает что. Стрелочки всегда правильно выдаёт , а вот цифры выводит через раз. Иногда выводит просто стрелочку и всё...
Я думаю, что стоит посмотреть в сторону фильтра Калмана. Тут неплохая статья.