Розумне квітникарство, або Пусти ИТшника в город... Частина 2

Друзі, наші технологічні розкопки на ниві домашньо-офісного озеленення викликали явний інтерес з вашої сторони (попередня стаття Розумне квітникарство, або Пусти ІТ-шника в город… Частина 1). Тому, як і обіцяли, відповідаємо на ваші запитання.

Але спочатку – фото компонентів. Сам мікроконтролер-МОЗОК:


Годинник реального часу з автономним живленням:



Датчик вологості ґрунту (вбитий):


LCD-модуль, 4 шурупа :)


1. А де самі подробиці? Код, милиці, проблеми виникли?
Власне, сам код перероблений під схему з одним реле. У заголовній частині подано посилання на використовувані бібліотеки.

/* 
Pich Irrigation Box © Mikhail Pichugin 
2016 
*/

#include <avr/sleep.h> 
#include <Wire.h>
#if defined(ARDUINO) && ARDUINO > 18 // Arduino 0019 or later
#include <SPI.h>
#endif
#include <Sodaq_DS3231.h> // rep/ https://github.com/SodaqMoja/Sodaq_DS3231
#include <LiquidCrystal_I2C.h> // https://github.com/marcmerlin/NewLiquidCrystal
#include <LcdBarGraph.h> // rep/ https://github.com/prampec/LcdBarGraph 
#include <Streaming.h> // http://arduiniana.org/libraries/streaming/

// -------------------------------------------------------------------------
// -- character with one bar
byte ch_level1[8] = {
B10000,
B10000,
B10000,
B10000,
B10000,
B10000,
B10000,
B10000
};
// -- character with two bars
byte ch_level2[8] = {
B11000,
B11000,
B11000,
B11000,
B11000,
B11000,
B11000,
B11000
};
// -- character with three bars
byte ch_level3[8] = {
B11100,
B11100,
B11100,
B11100,
B11100,
B11100,
B11100,
B11100
};
// -- character with four bars
byte ch_level4[8] = {
B11110,
B11110,
B11110,
B11110,
B11110,
B11110,
B11110,
B11110
};

#define I2C_ADDR 0x27 
#define BACKLIGHT_PIN 3
#define En_pin 2
#define Rw_pin 1
#define Rs_pin 0
#define D4_pin 4
#define D5_pin 5
#define D6_pin 6
#define D7_pin 7
//#define SD_pin 4

boolean last_hour_irrig = false;
boolean errorFlag = false;
boolean errorPulse = false;
boolean dispPulse = false;

byte int0Button = 0;
byte int0ButtonPin = 7; //pin кнопки ресет
byte irrig_hours[] = {9,20}; //годинники поливу від xx до yy
byte lcdNumCols = 20; //кількість символів на дисплеї по осі Х

int sensorPin = A0; //pin сенсора вологості грунту
int sensorValue = 0; //поточне значення вологості
int last_hour_sensor = 0; //значення вологості заміряну в попередній раз
int moisturemin = 850; //мінімальне значення вологості при якому включається полив
int irrig_delay = 10; //тривалість поливу в сек.
int pinINT0 = 2; //pin переривання INT0 з годинника реального часу
int Relay1 = 4;

unsigned long prevMillis =0;
const long dispInterval =1000;

DateTime last_date_irrig;
LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);
LcdBarGraph lbg(&lcd,lcdNumCols, 0, 3);

volatile boolean alarmFlag; 
void alarm() { 
alarmFlag = true; 
} 
// -------------------------------------------------------------------------
void setup()
{
// Common Initialize
Serial.begin(57600);
//while (!Serial) {
// ; // wait for serial port to connect. Needed for native USB port only
//}
Wire.begin();

// Initialize Real Time Clock
rtc.begin();
pinMode(pinINT0, INPUT);
last_date_irrig = rtc.now();
//if(last_date_irrig.year() == 2000){ 
//DateTime t(2016,2,28,11,0,0,6); 
//rtc.setDateTime(t); 
//Serial.println(F("-Setting date&time")); } 

// Initialize INT0 for accepting interrupts
PORTD |= 0x04; 
DDRD &=~ 0x04;
attachInterrupt(0, alarm, FALLING);
rtc.enableInterrupts(EveryHour); //interrupt at EverySecond, EveryMinute, EveryHour or rtc.enableInterrupts(18,4,0); // interrupt at (h,m,s)

// Initialize LCD
lcd.begin (lcdNumCols,4); 
lcd.createChar(1, ch_level1);
lcd.createChar(2, ch_level2);
lcd.createChar(3, ch_level3);
lcd.createChar(4, ch_level4);

lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE);
lcd.setBacklight(HIGH);
lcd.home (); // go home
//lcd.print("Start");

// Initialize Relay & Button
pinMode(Relay1, OUTPUT); // relay init
digitalWrite(Relay1, HIGH); // relay off
pinMode(int0ButtonPin, INPUT_PULLUP); // int0button

//Serial.println(F("-Init OK")); 
}

// -------------------------------------------------------------------------
void loop()
{
unsigned long currentMillis = millis();

// -- Input circut -------------------------------------------------------
int0Button = digitalRead(int0ButtonPin); //int0 button
if (int0Button == LOW) {
alarmFlag = true;
errorFlag = false;
}

rtc.convertTemperature();
DateTime now = rtc.now(); //get the current date-time
sensorValue = analogRead(sensorPin);


// -- Output circut ------------------------------------------------------
if (currentMillis-prevMillis>= dispInterval) {
prevMillis = currentMillis;
lcd.setCursor (0,0); lcd.print(int(rtc.getTemperature())); lcd.write((byte)223); 
lcd.setCursor (4,0); lcd.print(now.year(), DEC); lcd.print('/'); lcd.print(now.month(), DEC); lcd.print('/'); lcd.print(now.date(), DEC); lcd.print(' '); lcd.print(now.hour(), DEC); 
if (dispPulse) {
dispPulse = false; lcd.print(F(":"));
}
else { dispPulse = true; lcd.print(F(" ")); 
}
if (now.minute()<=9) {lcd.print("0");}
lcd.print(now.minute(), DEC);

lcd.setCursor (0,1); lcd.print(F("Sensor:")); lcd.print(sensorValue); lcd.print(F(" ")); lcd.print(F("min:")); lcd.print(moisturemin);

lcd.setCursor (0,2);
if (errorFlag) {
if (errorPulse) {errorPulse = false; lcd.print(F("Relay :OFF, ERROR ")); }
else {errorPulse = true; lcd.print(F("Relay :OFF, ERROR")); }
Serial.println(F("ERROR"));
}
else {
lcd.print(F("irH:")); lcd.print(irrig_hours[0], DEC); lcd.print(F(" to ")); lcd.print(irrig_hours[1], DEC); lcd.print(" "); lcd.print(irrig_delay); lcd.print(F("Sec "));

}

lcd.setCursor (0,3); lcd.print(F("Lst:")); lcd.print(last_date_irrig.year(), DEC); lcd.print('/'); lcd.print(last_date_irrig.month(), DEC); lcd.print('/');
lcd.print(last_date_irrig.date(), DEC); lcd.print(' '); lcd.print(last_date_irrig.hour(), DEC); lcd.print(':'); 
if (last_date_irrig.minute()<=9) {lcd.print("0");}
lcd.print(last_date_irrig.minute(), DEC);
} 
// -- Action circut -------------------------------------------------------
if (alarmFlag && !errorFlag && now.hour()>=irrig_hours[0] && now.hour()<=irrig_hours[1]) { 
alarmFlag = false; //reset alarmFlag for next interrupt
Serial.println(F("Moisture check Interrupt"));
lcd.clear();
lcd.setCursor (0,0); lcd.print(F("Moisture check Int."));

if (sensorValue>=moisturemin) {
lcd.setCursor (0,1); lcd.print(F("Sensor data :")); lcd.print(sensorValue); 
if (last_hour_irrig && abs(last_hour_sensor-sensorValue)<=5) { 
errorFlag = true;
}
else {
Serial.println(F("-Irrigation"));
lcd.setCursor (0,2); lcd.print(F("Relay :ON"));
lcd.setCursor (0,3); lcd.print(F("Irrigation ")); lcd.print(irrig_delay); lcd.print(F(" Sec "));
digitalWrite(Relay1, LOW); // Relay ON
for (int x=1; x<irrig_delay*5; x++) {
lbg.drawValue( x, irrig_delay*5);
delay(200);
} 
digitalWrite(Relay1, HIGH); // Relay OFF
last_date_irrig = rtc.now();
last_hour_irrig = true;
last_hour_sensor = sensorValue;

}
}
else {
last_hour_irrig = false;
last_hour_sensor = 0;

}
lcd.clear(); 
}
rtc.clearINTStatus();
Serial.println(sensorValue);


}

Милиці і проблеми торкнулися тільки апаратної частини. Наприклад, конкретно з нашим LCD-модулем коректно заробила тільки ця бібліотека.

2. А де WiFi і додаток для android/ios? Де статистика споживання води з даними з хмари? Скільки проживе квітка при відключенні 220В?
Підключення по Wi-Fi, так само як і по Bluetooth, на початковому етапі не планувалося – ресурсів даного контролера замало. Проживе квітка достатньо, щоб всі про нього знають, могли припустити, що з поливом щось не так, і дати бідоласі напитися

Можу тільки сказати, що в домашніх умовах я підключав Ethernet-модуль і система звітувала в Твіттері про своїх діях. На роботі цей модуль довелося відключити, бо не зрозуміло, що писати в заявці для відділу інформаційного забезпечення: «Прошу підключити до ЛВС фікус виду Каучуконосний, MAC-адреса такий-то...»? До речі, а який це реально вид, хто скаже?

3. Як впоралися з надлишковою потужністю моторчика омивача? Не було думки використовувати акваріумну помпу? Автомобільний це занадто все-таки.
Були побоювання, що потужна струмінь води буде розмивати грунт – ми навіть запаслися форсунки омивачів фар. Але звичайного поділу одного потоку на два через Т-конектор виявилося достатньо.

4. Як будете вирішувати питання корозії датчика вологості? Датчик вологості де розташований.
Взагалі-то датчик не стільки кородує, стільки руйнується від електролізу. Є розуміння, що, швидше за все, квітки некорисно мати в грунті сліди цього процесу, але нічого іншого поки запропонувати не можемо. Були ідеї відключення датчика на час між вимірами, зважування, індукційного датчика, різницевих температурних датчиків і ще щось- не пригадаю вже ;)

Можу сказати, що стандартного датчика вистачає приблизно на 3-4 тижні, але тут треба враховувати мінералізацію конкретної грунту. Зараз буде нова саморобна версія з нержавійки. Подивимося, на скільки її вистачить.

5. Як вираховували норму поливу?
Споживання води розраховували експериментально. Шанували відповідну літературу по фікусів, досвідченим шляхом вирахували, скільки йому потрібно води. Потім аналогічно з'ясували, який витрата води біля насоса. В нашому випадку, щоб насос накачав необхідну кількість води, потрібно 10 секунд. Ця тривалість і закладена в прошивку. У поточній версії ніякі настройки інтерактивно змінити не можна.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.