Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

Интеллектуальный контроллер полива

Компоненты и расходные материалы

Arduino Nano R3
× 1
расширитель ввода-вывода
× 1
Пакет расширителя ввода-вывода
× 1

Приложения и онлайн-сервисы

IDE Arduino

Об этом проекте

Создайте интеллектуальный контроллер орошения с помощью Arduino

Интеллектуально орошайте свой двор с помощью динамических циклов воды. Прекратите поливать свой двор, если идет дождь или идет дождь с момента последнего полива. Используйте датчик освещенности, чтобы определить время восхода солнца и соответственно автоматически настроить время начала полива. Прекратите поливать двор, если слишком холодно.

Список возможностей

  • Датчик наружной температуры
  • Датчик наружного дождя
  • Датчик внешнего освещения
  • Часы реального времени с батарейным питанием для еженедельного планирования.
  • Энергонезависимое хранилище - никогда не теряйте полив из-за потери мощности
  • Обнаружение восхода солнца
  • Умный полив экономит ваши счета за воду.
  • Поливайте перед восходом солнца, чтобы дать время замачиваться.
  • Прекратите полив, когда на улице слишком холодно
  • Уменьшить рост грибка.
  • Простое управление расписанием

Детали, необходимые для создания интеллектуального контроллера полива

  • Расширитель ввода-вывода
  • x2 1-Wire с I2C.
  • Соединение 1-Wire
  • Оптический разъем
  • Разделитель
  • Arduino Nano.
  • 4-канальное реле постоянного тока 5 В
  • Датчик освещенности TSL2561.
  • Водонепроницаемый датчик температуры DS18B20.
  • Оптический инфракрасный датчик уровня воды.
  • DS3231 AT24C32 IIC Прецизионные часы реального времени.
  • Дисплей I2C SSD1306 OLED 128x64.
  • Прозрачный водонепроницаемый пластиковый корпус размером 200x120x75 мм.
  • Прозрачный водонепроницаемый пластиковый корпус 100x68x50 мм.
  • Водонепроницаемый нейлоновый кабельный ввод ip68 pg7.
  • IP68 pg11 Водонепроницаемый нейлоновый кабельный ввод.
  • Разъем RJ11 Keystone с винтовыми клеммами.
  • 50-футовый провод 4C4P RJ11.
  • 6-футовый провод 4C4P RJ11.
  • Заголовок 2,54 мм.
  • 2-контактный 2-контактный микропереключатель SPST с мгновенной кнопкой
  • Блок питания от настенного блока питания 12 В постоянного тока, 1 А.

Схема подключения

OLED-дисплей

Нажмите кнопку «Меню», чтобы отобразить меню, и продолжайте нажимать кнопку, чтобы просмотреть все пункты меню. Меню будет автоматически удалено через 30 секунд бездействия. Нажмите кнопку выбора, чтобы выполнить желаемую функцию меню.

Так зачем использовать расширитель ввода-вывода?

  • Упрощение проектирования.
  • Готовые запчасти
  • Нет драйвера 1-Wire для написания
  • Нет драйвера DS3231 RTC для записи
  • Нет драйвера EEPROM для записи
  • Нет драйвера дисплея OLED для написания
  • Нет отображаемых шрифтов, занимающих пространство кода Arduino.
  • Не нужно писать драйвер датчика температуры
  • Нет оптического датчика дождя для записи
  • Экономит место для кода на Arduino; всего 12710 байт (39%)
  • Всего три дня на написание кода.
  • Простота подключения с помощью стандартного телефонного кабеля RJ11.
  • Нет проблем с длиной кабеля датчика.
  • Дешевле в сборке, чем аналогичные коммерческие системы.
  • Легко вносить изменения для адаптации к индивидуальным требованиям.
  • Один источник питания

Постройте систему

Подключите Arduino Nano к расширителю ввода-вывода и запрограммируйте его с помощью следующего кода. Шестиконтактный разъем - это последовательный порт отладки программного обеспечения, который не нужен при окончательной установке.

Убедитесь, что вы изменили определенный адрес ONEWIRE_TO_I2C_ROM1 и ONEWIRE-TO_I2C_ROM2, чтобы он соответствовал адресу 1-Wire на I2C.

  / * Оптимизирован эскиз модуля расширения ввода-вывода 
*
* Система полива v1.1
*
* /

#include
#include // Файл, расположенный \ Program Files (x86) \ Arduino \ hardware \ tools \ avr \ avr \ include \ time.h
#include
#include
#include
#include "IOExpander.h"

#define FAHRENHEIT
# define INIT_BOARD "g5w1; g11w1; g11d0,75; g12w1; g12d0,75; rsf"
#define ONEWIRE_TO_I2C_ROM1 "i4scc"
#define ONEWIRE_TO_I2C_ROM2 "i6 / "t6s0300"
#define RTC_SENSOR "s4te"
#define I2C_EEPROM "s4tf"
#define I2C_OLED "s4t10"
#define I2C_LIGHT "s3t9; sc0"
# define OPTICAL_SENSOR "g5a"
#define BUTTON1 "g11d"
#define BUTTON2 "g12d"

#define WATER_TIME_BEFORE_SUNRISE 60
#define SUNRISE_LUX 100
# define RAIN_DETECT_LEVEL 4.0
#define DO_NOT_WATER_TEMP 4.4444 // 40F

#define MAX_ZONES 4

#define HOUR_IN_DA Y 24L
#define MIN_IN_HOUR 60L
#define SEC_IN_MIN 60L
#define SEC_IN_HOUR (MIN_IN_HOUR * SEC_IN_MIN)
#define SEC_IN_DAY (HOUR_IN_DAY * SEC_IN_HOUR)



#define SEC_IN_WEEK (SEC_IN_DAY * DAYS_IN_WEEK)

#define SUN 0x01
#define MON 0x02
#define TUE 0x04
#define WED 0x08
# define THR 0x10
#define FRI 0x20
#define SAT 0x40
#define EVERYDAY (SUN | ПН | ВТ | СР | THR | ПЯТ | SAT)

#define SUNRISE 0x80

#define MENU_OPTIONS 9
#define MENU_TIME 30

#define OFF 0
#define ON 1

#define STATE_ON_OFF 0x01

// # define SERIAL_DEBUG

#ifdef SERIAL_DEBUG
SoftwareSerial swSerial (8,7 );
#endif

char будний день [] [4] ={"ВС", "ПН", "ВТ", "СР", "ЧТ", "ПТ", " SAT "};

char menu [] [13] ={" Далее ",
" Вода ",
" Сброс ",
" Мин. Часы + " ,
«Минуты часов -»,
«Часовые часы +»,
«Часовые часы -»,
«Восход»,
«ВКЛ / ВЫКЛ»};

перечисление {
MENU_NEXT,
MENU_WATER,
MENU_RESET,
MENU_CLOCK_MIN_PLUS,
MENU_CLOCK_MIN_MINUS,
MENU_CLOCK_HOUR_PLUS,
MENU_CLOCK_HOUR_MINUS,
MENU_SUNRISE,
MENU_ON_OFF
};

typedef struct {
char description [16];
uint8_t relay;
} ZONE;

typedef struct {
uint8_t zone;
uint8_t days;
int8_t hour;
int8_t min;
uint8_t duration;
} РАСПИСАНИЕ;

typedef struct {
time_t sunrise_time;
time_t last_water_time;
uint8_t water_schedule;
uint8_t water_duration;
uint8_t rain [MAX_ZONES];
uint8_t state;
uint8_t crc;
} NVRAM;

перечисление {
ZONE1,
ZONE2,
ZONE3,
ZONE4
};

перечисление {
RELAY1 =1,
RELAY2,
RELAY3,
RELAY4
};

ZONE zone [] ={
{"Передний правый", RELAY1},
{"Передний левый", RELAY2},
{"Кусты", RELAY3},
{"Left Side", RELAY4},
};

РАСПИСАНИЕ [] ={
{ZONE1, SUNRISE | EVERYDAY, -1, 0, 4},
{ZONE2, EVERYDAY, 6, 15, 5},
{ZONE3, EVERYDAY, 6, 0, 10},
{ZONE4, EVERYDAY , 6, 10, 6},
};

NVRAM nvram;
bool update_nvram =false;

uint8_t crc8 (uint8_t * data, uint16_t length)
{
uint8_t crc =0;

while (length--) {
crc =_crc8_ccitt_update (crc, * data ++);
}
return crc;
}

int led =13;
bool init_oled =true;
bool update_oled =true;
bool init_board =true;

#ifdef FAHRENHEIT
#define C2F (temp) CelsiusToFahrenheit (temp)
float CelsiusToFahrenheit (float celsius)
{
return ((celsius * 9) / 5) + 32;
}
#else
#define C2F (temp) (temp)
#endif

void SerialPrint ( const char * str, float decimal, char error)
{
Serial.print (str);
if (error) Serial.print (F ("NA"));
else Serial.print (decimal, 1);
}

time_t NextScheduleTime (time_t last_time, uint8_t * next_schedule)
{
time_t next_time =-1;
time_t clk_time;
uint8_t i;
tm clk;
uint8_t wday;

for (i =0; я if (расписание [i]. days &SUNRISE) {
clk_time =nvram.sunrise_time;
clk_time + =schedule [i] .hour * SEC_IN_HOUR;
clk_time + =расписание [i] .min * SEC_IN_MIN;
localtime_r (&clk_time, &clk);
}
else {
localtime_r (&last_time, &clk);
clk. tm_hour =расписание [i] .hour;
clk.tm_min =расписание [i] .min;
clk.tm_sec =0;
clk_time =mktime (&clk);
}
wday =clk.tm_wday;
while (clk_time <=last_time ||! (расписание [i] .days &(1 < {
clk_time + =SEC_IN_DAY;
if (++ wday> SATURDAY) wday =SUNDAY;
if (wday ==clk.tm_wday) break; // Проверяем только одну неделю
}
if (clk_time next_time =clk_time;
* next_schedule =i;
}
}
return next_time;
}

void StartScheduleTime (time_t start_time, uint8_t start_schedule)
{
uint8_t i;

nvram. last_water_time =start_time;
nvram.water_schedule =start_schedule;
nvram.water_duration =schedule [start_schedule] .duration + 1;
update_nvram =true;
// Проверяем, шел ли дождь
i =расписание [start_schedule] .zone;
if (i 0) {
if (nvram.rain [i]> nvram.water_duration) nvram .water_duration =0;
else nvram.water_duration - =nvram.rain [i];
nvram.rain [i] =0;
}
}

void WaterScheduleTime (void)
{
uint8_t i;

nvram.water_duration--;
update_nvram =true;
i =расписание [ nvram.water_schedule] .zone;
if (i Serial.print ("r");
Serial.print (zone [i] .relay);
if (nvram.water_duration> 0) Serial.println ("o");
else Serial.println ("f");
SerialReadUntilDone ();
}
}

void setup () {
Serial.begin (115200);
#ifdef SERIAL_DEBUG
swSerial.begin (115200);
#endif
pinMode (led, OUTPUT);
// задержка (1000);
wdt_enable (WDTO_8S);
}

void loop () {
static tm rtc;
tm clk, sunrise_clk;
time_t rtc_time;
time_t clk_time;
static time_t next_time;
static uint8_t last_sec;
static uint8_t last_min;
bool error_rtc;
bool error_light;
bool error_temp;
static long lux =0;
static float temp, rain;
static uint8_t sunrise_counter =MIN_IN_HOUR;
static bool check_sunrise =false;
uint8_t i;
static bool read_nvram =true;
static time_t water_time;
static uint8_t water_schedule;
uint8_t sz;
uint8_t wday;
long n;
bool button1, button2;
static int8_t menu_select =-1;
static time_t menu_time =0;

Serial.println ();
if (SerialReadUntilDone ()) {
if (init_board) {
SerialCmdDone (INIT_BOARD);
init_board =false;
}

if (init_oled) {
if (SerialCmdNoError (ONEWIRE_TO_I2C_ROM1)) {
SerialCmdDone (I2C_OLED "; si; sc; sd");
init_oled =false;
}
}

if (SerialCmdDone (RTC_SENSOR)) {
error_rtc =! SerialReadTime (&rtc);
если (! error_rtc) {
clk =rtc; // mktime () может изменить структуру tm
rtc_time =mktime (&clk);
localtime_r (&rtc_time, &rtc); // Получить wday.
}

if (read_nvram) {
if (SerialCmdNoError (I2C_EEPROM)) {
SerialReadEEPROM ((uint8_t *) &nvram, 0, sizeof (nvram));
if (nvram.crc! =crc8 ((uint8_t *) &nvram, sizeof (nvram) -sizeof (uint8_t))) {
//swSerial.println("CRC8 Failure! ");
// Инициализируем nvram
memset (&nvram, 0, sizeof (nvram));
clk =rtc;
clk.tm_hour =6;
clk .tm_min =0;
clk.tm_sec =0;
nvram.sunrise_time =mktime (&clk);
if (nvram.sunrise_time update_nvram =true;
}
// Проверяем время последней поливки не менее одной недели
if (rtc_time - nvram.last_water_time> SEC_IN_WEEK) nvram.last_water_time =rtc_time - SEC_IN_WEEK;
// Проверяем время восхода
if (rtc_time> nvram.sunrise_time) {
localtime_r (&nvram.sunrise_time, &sunrise_clk);
clk =rtc;
clk.tm_hour =sunrise_clk.tm_hour;
clk.tm_min =sunrise_clk.tm_min;
clk.tm_sec =sunrise_clk.tm_sec;
nvram.sunrise_time =mktime (&clk);
if (nvram.sunrise_time }
if (nvram.water_duration) {
nvram. water_duration ++;
water_time =nvram.last_water_time;
}
иначе {
clk_time =(nvram.last_water_time)? nvram.last_water_time:rtc_time;
water_time =NextScheduleTime (clk_time, &water_schedule);
}
read_nvram =false;
}
}
}

// Обрабатывать только раз в минуту
if (rtc.tm_min! =last_min)
{
// Запросить измерение температуры 1-Wire. Прочтите позже.
error_temp =! SerialCmdNoError (ONEWIRE_TEMPERATURE);
if (! Error_temp) SerialCmdDone ("tt");

error_light =! SerialCmdNoError (ONEWIRE_TO_I2C_ROM2 "oROM2" );
если (! error_light) {
SerialCmdDone (I2C_LIGHT); // Не использовать овердрайв
SerialCmd ("sr");
SerialReadInt (&lux);
SerialReadUntilDone ();
}

if (SerialCmd (OPTICAL_SENSOR)) {
SerialReadFloat (&rain);
SerialReadUntilDone ();
}

error_temp =! SerialCmdNoError (ONEWIRE_TEMPERATURE);
if (! error_temp) {
SerialCmd ("tr");
SerialReadFloat (&temp);
SerialReadUntilDone ();
}

// Сейчас восход солнца?
if (lux if (sunrise_counter> 0) sunrise_counter--;
else check_sunrise =true;
}
else {
if (счетчик_восхода sunrise_counter ++;
if (check_sunrise &&sunrise_counter ==MIN_IN_HOUR) {
nvram.sunrise_time =rtc_time + (SEC_IN_DAY - SEC_IN_HOUR);
false check_sunrise;

update_nvram =true;
}
}
}

// Идёт дождь?
if (rain <=RAIN_DETECT_LEVEL) {
for (i =0; i if (nvram.rain [i] <-1) nvram.rain [i] ++; }
update_nvram =true;
}

// Проверить расписание
if (menu_select ==-1 &&! nvram.water_duration) {
while (water_time + (расписание [water_schedule] .duration * SEC_IN_MIN) water_time =NextScheduleTime (water_time, &water_schedule);
}
if (water_time <=rtc_time) {
StartScheduleTime (water_time, water_schedule);
if (temp <=DO_NOT_WATER_TEMP || nvram.state &STATE_ON_OFF ==OFF)
nvram.water_duration =0;
}
}

// Нам нужно поливать?
если (nvram.water_duration) {
WaterScheduleTime ();
if (! nvram.water_duration)
water_time =NextScheduleTime (water_time, &water_schedule);
}

last_min =rtc.tm_min;
update_oled =true;
}

// Проверить кнопки
button1 =SerialReadButton (BUTTON1);
if (button1 ) {
if (menu_select ==-1) menu_select =0;
else {
if (++ menu_select> =MENU_OPTIONS)
menu_select =0;
}
menu_time =rtc_time;
update_oled =true;
}
if (menu_select> =0) {
button2 =SerialReadButton (BUTTON2);
if ( button2) {
clk_time =rtc_time;
переключатель (menu_select) {
case MENU_NEXT:
case MENU_RESET:
if (nvram.water_duration) {
nvram .water_duration =1;
WaterScheduleTime ();
}
water_time =NextScheduleTime ((menu_select ==MENU_NEXT) ? water_time:rtc_time, &water_schedule);
break;
case MENU_WATER:
StartScheduleTime (water_time, water_schedule);
WaterScheduleTime ();
break;
case MENU_CLOCK_MIN_PLUS:
clk_time + =SEC_IN_MIN;
break;
case MENU_CLOCK_MIN_MINUS:
clk_time - =SEC_IN_MIN;
break;
case MENU_CLOCK_HOUR_PLUS:
clk_time + =SEC_IN_HOUR;
break;
case MENU_CLOCK_HOUR_MINUS:
clk_time - =SEC_IN_HOUR;
break;
case MENU_ON_OFF:
nvram.state ^ =STATE_ON_OFF;
update_nvram =true;
break;
}
if (clk_time! =rtc_time) {
if (SerialCmdDone (RTC_SENSOR)) {
localtime_r ( &clk_time, &clk);
SerialWriteTime (&clk);
rtc_time =clk_time;
}
}
menu_time =rtc_time;
update_oled =true;
}
}
if (menu_select> =0 &&rtc_time - menu_time> MENU_TIME) {
menu_select =-1;
update_oled =true;
}

if (update_oled) {
if (S erialCmdNoError (ONEWIRE_TO_I2C_ROM1)) {
Serial.print ("st10; so1; sc; sf0; sa0; sd0,0, \" ");
if (nvram.water_duration) Serial.print (nvram. water_duration);
else {
if ((nvram.state &STATE_ON_OFF) ==OFF) Serial.print ("OFF");
else if (rain <=RAIN_DETECT_LEVEL) Serial.print ("Дождь");
иначе if (temp <=DO_NOT_WATER_TEMP) Serial.print ("Холодный");
else Serial.print ("v1.1");
}
Serial.print ("\"; sf2; sa1; sd75,0, \ "");
if (menu_select ==7) {// Восход солнца
clk_time =nvram.sunrise_time;
localtime_r (&clk_time, &clk);
}
else clk =rtc;
Serial.print (clk.tm_hour - ((clk.tm_hour> 12)? 12:0));
Serial.print (":");
if (clk.tm_min <10) Serial.print ("0");
Serial.print (clk.tm_min);
Serial.println ("\" ");
SerialReadUntilDone ();

Serial.print (" sf1; sa0; sd79,8, \ "");
Последовательный .print ((clk.tm_hour> 12)? "PM":"AM");
Serial.print ("\"; sf0; sa1; sd127,1, \ "");
Последовательный .print (день недели [clk.tm_wday]);
Serial.print ("\"; sd127,13, \ "");
Serial.print (clk.tm_mon + 1);
Serial.print ("/");
Serial.print (clk.tm_mday);
Serial.println ("\" ");
SerialReadUntilDone ();

Serial.print (" sf0; sa0; sd1 , 36, \ "");
i =расписание [water_schedule] .zone;
if (i localtime_r (&water_time , &clk);
if (water_time - rtc_time> SEC_IN_DAY) {
Serial.print ("\"; sa1; sd126,36, \ "");
Serial.print (clk. tm_mon + 1);
Serial.print ("/");
Serial.print (clk.tm_mday);
Serial.print ("");
Serial.print (clk.tm_hour - ((clk.tm_hour> 12)? 12:0));
Serial.print (":");
if (clk.tm_min <10) Serial.print (" 0 ");
Serial.print (clk.tm_min);
Serial.print (" ");
}
else {
Serial.print (" \ "; sf1; sa1; sd111,30, \" ");
Serial.print (clk.tm_hour - ((clk.tm_hour> 12)? 12:0));
Serial.print ( ":");
if (clk.tm_min <10) Serial.print ("0");
Serial.print (clk.tm_min);
Seria l.print ("\"; sf0; sd126,36, \ "");
}
Serial.print ((clk.tm_hour> 12)? "PM":"AM");
if (nvram.water_duration) Serial.print ("\"; so2; sc0,29,128,19 ");
Serial.println ();
SerialReadUntilDone ();

if (menu_select ==-1) {
//Serial.print ("\"; sa0; sd0,52, \ "");
//Serial.print(rain);
SerialPrint ("\"; so1; sa2; sd63,52, \ "", C2F (temp), error_temp);
if (! error_temp) Serial.print ("\", 248, \ ""
#ifdef FAHRENHEIT
"F"
#else
"C"
#endif
);
Serial.print (" / ");
Serial.print (lux);
}
else {
Serial.print (" \ "; so0; sc0,51,128,14; sf0; sa2; sd63,52, \ "");
if (menu_select ==MENU_ON_OFF) {
Serial.print ((nvram.state &STATE_ON_OFF)? "ВЫКЛ":"ВКЛ");
}
иначе Serial.print (меню [menu_select]);
}
Serial.println ("\"; sd ");
SerialReadUntilDone ();
update_oled =false;
}
else init_oled =true;
}

if (update_nvram) {
if (SerialCmdNoError (I2C_EEPROM)) {
nvram.crc =crc8 ((uint8_t *) &nvram, sizeof (nvram) -sizeof (uint8_t));
//swSerial.println(nvram.crc , HEX);
SerialWriteEEPROM ((uint8_t *) &nvram, 0, sizeof (nvram));
update_nvram =false;
}
}

delay (50);
}
else {
digitalWrite (led, HIGH);
delay (500);
digitalWrite (led, LOW);
delay (500);
init_board =true;
init_oled =true;
}
wdt_reset ();
}

Примечание. Если вы используете порт USB для программирования Arduino Nano, вы должны отключить его от расширителя ввода-вывода, поскольку он также использует тот же единственный последовательный порт, вместо этого, если вы хотите отладить, используйте порт ICSP для программирования ATmega328P. Чтобы включить порт отладки программного обеспечения, раскомментируйте определение SERIAL_DEBUG.

Сначала необходимо настроить разветвитель на изоляцию линии передачи данных оптического инфракрасного датчика от линии удаленного датчика 1-Wire. Припаяйте резистор 0603 с нулевым сопротивлением к резистору R2.

Просверлите отверстие 7/16 дюйма в маленьком корпусе и отверстие 11/16 дюйма в большом корпусе с правой стороны для PG7 и PG11. Воспользуйтесь инструментом dremel, чтобы немного увеличить отверстия, пока сальник не войдет в плотное соединение. PG7 будет питать удаленные датчики и PG11 для проводов 12 В постоянного тока, 24 В переменного тока, коллектора и провода удаленных датчиков RJ11.

Подключите микропереключатель кнопки мгновенного действия SPST и подключите его к винтовой клемме RJ11. Для изоляции контактов используйте термоусадочную трубку.

Подключите все провода и соберите / подайте все детали в большой корпус. Ваш 50-футовый провод RJ11 для удаленных датчиков должен просто проходить через сальник PG11, не разрезая его.

Просверлите отверстие 9/16 дюйма в верхней части небольшого корпуса для оптического инфракрасного датчика воды. Используйте инструмент dremel, чтобы немного увеличить отверстие, пока не поместится датчик. Корпус небольшого удаленного датчика плотно прилегает, но если содержимое укладываются в рекомендуемой ориентации, она должна точно подходить. Если сделать провода RJ11 как можно короче, это поможет втиснуть все это в меньший корпус. После сборки рекомендуется добавить немного морского клея в шайбу гайки сальника, прежде чем навинчивать гайку, чтобы создать лучшую печать.

Установите корпус дистанционного датчика снаружи и установите его на возвышении с восточной стороны вашего дома так, чтобы оптический инфракрасный датчик воды и датчик освещенности были направлены в небо без препятствий.

Просверлите отверстия диаметром 1/4 дюйма в верхней средней части большого корпуса и установите кнопки. Используйте инструмент dremel, чтобы немного увеличить отверстие до тех пор, пока кнопки не встанут на место.

Протестируйте систему и убедитесь, что все работает правильно. Чтобы проверить реле и датчики, отключите Arduino от IO Expander и подключите его напрямую к компьютеру, чтобы управлять им вручную. Убедившись, что все работает, соберите все части в корпус, используя двусторонний скотч и упаковочную пену, чтобы закрепить доски, и наслаждайтесь преимуществами и экономией вашего интеллектуального контроллера полива.

Видео в действии

Обновление от 12.09.2019

Выпущена версия 1.1, в которой исправлена ​​проблема с запуском, если система теряла питание на несколько дней.

Обновление 02.10.2019

При подключении 1-Wire к I2C к DS3231, а затем к OLED-экрану SSD1306 у вас будет в общей сложности три разных подтяжки на линиях SDA и SCL, как показано на изображении ниже в кружке. Это фактически приведет к подтягиванию 4,7k / 3 =1,56k, которое может быть слишком сильным и привести к случайным искажениям экрана.

Поскольку DS3231 использует блок резисторов, который используется другими линиями, удалите другие подтягивающие резисторы:

  • 1-Wire с I2C R3 и R4.
  • SSD1306 OLED R6 и R7.

Код

  • Интеллектуальный контроллер полива
Умный контроллер орошения C / C ++
Используйте Arduino для разумного полива вашего двора или сада.
 / * Оптимизирован эскиз IO Expander * * Система полива v1.1 * * / # include  #include  // Файл расположен \ Program Файлы (x86) \ Arduino \ hardware \ tools \ avr \ avr \ include \ time.h # include  #include  #include  #include "IOExpander. h "#define FAHRENHEIT # define INIT_BOARD" g5w1; g11w1; g11d0,75; g12w1; g12d0,75; rsf "#define ONEWIRE_TO_I2C_ROM1" i4scc "#define ONEWIRE_TO_I2C_ROM1" i4scc "#define ONEWIRE_TO_I2C_ROM1" i4scc "#define ONEWIRE_TO_I2C_ROM" #################################### define I2C_EEPROM "s4tf" #define I2C_OLED "s4t10" #define I2C_LIGHT "s3t9; sc0" #define OPTICAL_SENSOR "g5a" #define BUTTON1 "g11d" #define BUTTON2 "g12d_SENSOR" #define BUTTON2 "g12d" define DO_NOT_WATER_TEMP 4.4444 // 40F # define MAX_ZONES 4 # define HOUR_IN_DAY 24L #define MIN_IN_HOUR 60L # define SEC_IN_MIN 60L # define SEC_IN_HOUR (MIN_IN_HOUR * SEC_IN_MIN) #define SEC_IN_IN_DAY (HOUR_IN_IN_DAY_ OUR) #define DAYS_IN_WEEK 7 # define SEC_IN_WEEK (SEC_IN_DAY * DAYS_IN_WEEK) #define SUN 0x01 # define MON 0x02 #define TUE 0x04 #define WED 0x08 # define THR 0x10 #define FRI 0x20 # define | ПН | ВТ | СР | THR | ПЯТ | SAT) #define SUNRISE 0x80 # define MENU_OPTIONS 9 #define MENU_TIME 30 #define OFF 0 # define ON 1 # define STATE_ON_OFF 0x01 // # define SERIAL_DEBUG # ifdef SERIAL_DEBUGSoftwareSerial swSerial (8,7); # endifcharday [] {«ВС», «ПН», «ВТ», «СР», «ЧТ», «ПТ», «СБ»}; char menu [] [13] ={«Далее», «Вода», «Сброс» , «Мин. Часов +», «Мин. Часов -», «Час часов +», «Часы -», «Восход», «ВКЛ / ВЫКЛ»}; enum {MENU_NEXT, MENU_WATER, MENU_RESET, MENU_CLOCK_MIN_PLUS, MENU_CLOCK_MIN_MINUS, MENU_CLOCK_HOUR_HOUR_HOUR_MIN_MINUS, MENU_CLOCK_HOUR_HOUR_ MENU_CLOCK_HOUR_MINUS, MENU_SUNRISE, MENU_ON_OFF}; typedef struct {описание символа [16]; uint8_t relay;} ZONE; typedef struct {uint8_t zone; uint8_t дней; int8_t час; int8_t min; uint8_t duration;} РАСПИСАНИЕ; typedef struct {time_t sunrise_time; time_t last_water_time; uint8_t water_schedule; uint8_t water_duration; uint8_t дождь [MAX_ZONES]; состояние uint8_t; uint8_t crc;} NVRAM; enum {ZONE1, ZONE2, ZONE3, ZONE4}; enum {RELAY1 =1, RELAY2, RELAY3, RELAY4}; ZONE zone [] ={{"Front Right", RELAY1}, {"Front Left" , RELAY2}, {"Втулки", RELAY3}, {"Left Side", RELAY4},}; РАСПИСАНИЕ расписание [] ={{ZONE1, SUNRISE | EVERYDAY, -1, 0, 4}, {ZONE2, EVERYDAY, 6, 15, 5}, {ZONE3, EVERYDAY, 6, 0, 10}, {ZONE4, EVERYDAY, 6, 10, 6},}; NVRAM nvram; bool update_nvram =false; uint8_t crc8 (uint8_t * данные, длина uint16_t) {uint8_t crc =0; while (длина--) {crc =_crc8_ccitt_update (crc, * data ++); } return crc;} int led =13; bool init_oled =true; bool update_oled =true; bool init_board =true; #ifdef FAHRENHEIT # определить C2F (temp) CelsiusToFahrenheit (temp) float CelsiusToFahrenheit (float celsius) {return ((celsiusToFahrenheit (float celsius) {return ((celsius 9) / 5) + 32;} # else # define C2F (temp) (temp) #endifvoid SerialPrint (const char * str, float decimal, char error) {Serial.print (str); если (ошибка) Serial.print (F ("NA")); иначе Serial.print (десятичный, 1);} time_t NextScheduleTime (time_t last_time, uint8_t * next_schedule) {time_t next_time =-1; time_t clk_time; uint8_t i; tm clk; uint8_t wday; for (i =0; i  СУББОТА) wday =ВОСКРЕСЕНЬЕ; если (wday ==clk.tm_wday) перерыв; // Проверяем только одну неделю} if (clk_time  0) {if (nvram.rain [i]> nvram.water_duration) nvram.water_duration =0; иначе nvram.water_duration - =nvram.rain [i]; nvram.rain [i] =0; }} void WaterScheduleTime (void) {uint8_t i; nvram.water_duration--; update_nvram =правда; i =расписание [nvram.water_schedule] .zone; если (я  0) Serial.println ("o"); иначе Serial.println ("е"); SerialReadUntilDone (); }} void setup () {Serial.begin (115200); # ifdef SERIAL_DEBUG swSerial.begin (115200); # endif pinMode (led, OUTPUT); // задержка (1000); wdt_enable (WDTO_8S);} void loop () {static tm rtc; tm clk, sunrise_clk; time_t rtc_time; time_t clk_time; статический time_t next_time; статический uint8_t last_sec; статический uint8_t last_min; bool error_rtc; bool error_light; bool error_temp; статический длинный люкс =0; статическая температура поплавка, дождь; статический uint8_t sunrise_counter =MIN_IN_HOUR; static bool check_sunrise =false; uint8_t i; static bool read_nvram =true; статическое time_t water_time; статический uint8_t water_schedule; uint8_t sz; uint8_t wday; длинный п; bool button1, button2; статический int8_t menu_select =-1; статическое time_t menu_time =0; Serial.println (); если (SerialReadUntilDone ()) {если (init_board) {SerialCmdDone (INIT_BOARD); init_board =false; } if (init_oled) { if (SerialCmdNoError(ONEWIRE_TO_I2C_ROM1)) { SerialCmdDone(I2C_OLED ";si;sc;sd"); init_oled =false; } } if (SerialCmdDone(RTC_SENSOR)) { error_rtc =!SerialReadTime(&rtc); if (!error_rtc) { clk =rtc; // mktime() can change struct tm rtc_time =mktime(&clk); localtime_r(&rtc_time, &rtc); // Get wday. } if (read_nvram) { if (SerialCmdNoError(I2C_EEPROM)) { SerialReadEEPROM((uint8_t*)&nvram, 0, sizeof(nvram)); if (nvram.crc !=crc8((uint8_t*)&nvram, sizeof(nvram)-sizeof(uint8_t))) { //swSerial.println("CRC8 Failure!"); // Initialize nvram memset(&nvram, 0, sizeof(nvram)); clk =rtc; clk.tm_hour =6; clk.tm_min =0; clk.tm_sec =0; nvram.sunrise_time =mktime(&clk); if (nvram.sunrise_time  SEC_IN_WEEK) nvram.last_water_time =rtc_time - SEC_IN_WEEK; // Check sunrise time if (rtc_time> nvram.sunrise_time) { localtime_r(&nvram.sunrise_time, &sunrise_clk); clk =rtc; clk.tm_hour =sunrise_clk.tm_hour; clk.tm_min =sunrise_clk.tm_min; clk.tm_sec =sunrise_clk.tm_sec; nvram.sunrise_time =mktime(&clk); if (nvram.sunrise_time  0) sunrise_counter--; else check_sunrise =true; } else { if (sunrise_counter =MENU_OPTIONS) menu_select =0; } menu_time =rtc_time; update_oled =true; } if (menu_select>=0) { button2 =SerialReadButton(BUTTON2); if (button2) { clk_time =rtc_time; switch(menu_select) { case MENU_NEXT:case MENU_RESET:if (nvram.water_duration) { nvram.water_duration =1; WaterScheduleTime(); } water_time =NextScheduleTime((menu_select ==MENU_NEXT) ? water_time :rtc_time, &water_schedule); ломать; case MENU_WATER:StartScheduleTime(water_time, water_schedule); WaterScheduleTime(); ломать; case MENU_CLOCK_MIN_PLUS:clk_time +=SEC_IN_MIN; ломать; case MENU_CLOCK_MIN_MINUS:clk_time -=SEC_IN_MIN; ломать; case MENU_CLOCK_HOUR_PLUS:clk_time +=SEC_IN_HOUR; ломать; case MENU_CLOCK_HOUR_MINUS:clk_time -=SEC_IN_HOUR; ломать; case MENU_ON_OFF:nvram.state ^=STATE_ON_OFF; update_nvram =true; ломать; } if (clk_time !=rtc_time) { if (SerialCmdDone(RTC_SENSOR)) { localtime_r(&clk_time, &clk); SerialWriteTime(&clk); rtc_time =clk_time; } } menu_time =rtc_time; update_oled =true; } } if (menu_select>=0 &&rtc_time - menu_time> MENU_TIME) { menu_select =-1; update_oled =true; } if (update_oled) { if (SerialCmdNoError(ONEWIRE_TO_I2C_ROM1)) { Serial.print("st10;so1;sc;sf0;sa0;sd0,0,\""); if (nvram.water_duration) Serial.print(nvram.water_duration); else { if ((nvram.state &STATE_ON_OFF) ==OFF) Serial.print("OFF"); else if (rain <=RAIN_DETECT_LEVEL) Serial.print("Rain"); else if (temp <=DO_NOT_WATER_TEMP) Serial.print("Cold"); else Serial.print("v1.1"); } Serial.print("\";sf2;sa1;sd75,0,\""); if (menu_select ==7) { // Sunrise clk_time =nvram.sunrise_time; localtime_r(&clk_time, &clk); } else clk =rtc; Serial.print(clk.tm_hour-((clk.tm_hour>12)?12:0)); Serial.print(":"); if (clk.tm_min <10) Serial.print("0"); Serial.print(clk.tm_min); Serial.println("\""); SerialReadUntilDone(); Serial.print("sf1;sa0;sd79,8,\""); Serial.print((clk.tm_hour>12)?"PM":"AM"); Serial.print("\";sf0;sa1;sd127,1,\""); Serial.print(weekday[clk.tm_wday]); Serial.print("\";sd127,13,\""); Serial.print(clk.tm_mon+1); Serial.print("/"); Serial.print(clk.tm_mday); Serial.println("\""); SerialReadUntilDone(); Serial.print("sf0;sa0;sd1,36,\""); i =schedule[water_schedule].zone; if (i  SEC_IN_DAY) { Serial.print("\";sa1;sd126,36,\""); Serial.print(clk.tm_mon+1); Serial.print("/"); Serial.print(clk.tm_mday); Serial.print(" "); Serial.print(clk.tm_hour-((clk.tm_hour>12)?12:0)); Serial.print(":"); if (clk.tm_min <10) Serial.print("0"); Serial.print(clk.tm_min); Serial.print(" "); } else { Serial.print("\";sf1;sa1;sd111,30,\""); Serial.print(clk.tm_hour-((clk.tm_hour>12)?12:0)); Serial.print(":"); if (clk.tm_min <10) Serial.print("0"); Serial.print(clk.tm_min); Serial.print("\";sf0;sd126,36,\""); } Serial.print((clk.tm_hour>12)?"PM":"AM"); if (nvram.water_duration) Serial.print("\";so2;sc0,29,128,19"); Serial.println (); SerialReadUntilDone(); if (menu_select ==-1) { //Serial.print("\";sa0;sd0,52,\""); //Serial.print(rain); SerialPrint("\";so1;sa2;sd63,52,\"", C2F(temp), error_temp); if (!error_temp) Serial.print("\",248,\"" #ifdef FAHRENHEIT "F" #else "C" #endif ); Serial.print(" / "); Serial.print(lux); } else { Serial.print("\";so0;sc0,51,128,14;sf0;sa2;sd63,52,\""); if (menu_select ==MENU_ON_OFF) { Serial.print((nvram.state &STATE_ON_OFF) ? "OFF" :"ON"); } else Serial.print(menu[menu_select]); } Serial.println("\";sd"); SerialReadUntilDone(); update_oled =false; } else init_oled =true; } if (update_nvram) { if (SerialCmdNoError(I2C_EEPROM)) { nvram.crc =crc8((uint8_t*)&nvram, sizeof(nvram)-sizeof(uint8_t)); //swSerial.println(nvram.crc, HEX); SerialWriteEEPROM((uint8_t*)&nvram, 0, sizeof(nvram)); update_nvram =false; } } delay(50); } else { digitalWrite(led, HIGH); delay(500); digitalWrite(led, LOW); delay(500); init_board =true; init_oled =true; } wdt_reset();}

Схема

Intelligently water your yard or garden

Производственный процесс

  1. Разработка систем интеллектуального земледелия с открытым исходным кодом
  2. Интеллектуальная сенсорная плата ускоряет разработку периферийного ИИ
  3. Контроллер умного дома 433 МГц с Sensorflare и RaspberryPi
  4. Датчик температуры Raspberry Pi
  5. Сотовый Интернет вещей:умный мусорный бак
  6. Умный бармен
  7. Датчик эмоций / ЭЭГ
  8. Контроллер полива Win10 IOT с датчиками влажности
  9. Сверхчувствительный и надежный датчик для смарт-текстиля
  10. Ультратонкий сенсор для умных контактных линз