Друзья, наши технологические раскопки на ниве домашне-офисного озеленения вызвали явный интерес с вашей стороны (предыдущая статья Умное цветоводство, или Пусти ИТ-шника в огород… Часть 1). Посему, как и обещали, отвечаем на ваши вопросы.
Но сначала – фото компонентов. Сам микроконтроллер-МОЗГ:
Часы реального времени с автономным питанием:
Датчик влажности почвы (убитый):
LCD-модуль, 4 шурупа :)
1. А где сами подробности-то? Код, костыли, проблемы возникшие?
Собственно, сам код переделан под схему с одним реле. В заголовочной части даны ссылки на используемые библиотеки.
/*
Pich Irrigation Box (c) 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()
{
// Initialize common
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 секунд. Эта длительность и заложена в прошивку. В текущей версии никакие настройки поменять интерактивно нельзя.
Автор: Инфосистемы Джет