Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

Controlador de Irrigação Inteligente com Arduino – Rega Automática com Sensores de Clima e Luz

Componentes e Materiais

Aplicativos e Serviços Online

Sobre o Projeto

Crie um Controlador de Irrigação Inteligente com Arduino

Irrigue seu jardim de forma inteligente com ciclos de rega dinâmicos. Pare a rega se estiver chovendo ou se choveu desde a última rega. Use o sensor de luz para detectar o nascer do sol e ajustar automaticamente os horários de início. Pare a rega se estiver muito frio.

Lista de Recursos

Controlador de Irrigação Inteligente com Arduino – Rega Automática com Sensores de Clima e Luz

Peças Necessárias para Construir o Controlador

Diagrama de Fiação

Controlador de Irrigação Inteligente com Arduino – Rega Automática com Sensores de Clima e Luz

Display OLED

Controlador de Irrigação Inteligente com Arduino – Rega Automática com Sensores de Clima e Luz

Pressione o botão Menu para exibir o menu e continue pressionando para percorrer as opções. O menu some automaticamente após 30 segundos de inatividade. Pressione o botão Select para executar a função desejada.

Por Que Usar o Expansor de I/O?

Construa o Sistema

Conecte o Arduino Nano ao Expansor de I/O e programe com o código abaixo. O header de 6 pinos é a porta serial de depuração e não é necessário na instalação final.

Controlador de Irrigação Inteligente com Arduino – Rega Automática com Sensores de Clima e Luz

Certifique-se de alterar os endereços ONEWIRE_TO_I2C_ROM1 e ONEWIRE_TO_I2C_ROM2 para corresponder aos endereços dos seus conversores 1-Wire para I2C.

Código Principal

/* Sketch do Expansor de I/O otimizado
 * Sistema de Irrigação v1.1
 */
#include <math.h>
#include <time.h>
#include <util/crc16.h>
#include <avr/wdt.h>
#include <SoftwareSerial.h>
#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 "i6s8f"
#define ONEWIRE_TEMPERATURE "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 // 40°F

#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_DAY (HOUR_IN_DAY * SEC_IN_HOUR)
#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 SAT 0x40
#define EVERYDAY (SUN | MON | TUE | WED | THR | FRI | 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 weekday[][4] = {"SUN","MON","TUE","WED","THU","FRI","SAT"};
char menu[][13] = {"Next","Water","Reset","Clock Min +","Clock Min -","Clock Hour +","Clock Hour -","Sunrise","ON/OFF"};

enum { 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; } SCHEDULE;
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;

enum { ZONE1, ZONE2, ZONE3, ZONE4 };
enum { RELAY1 = 1, RELAY2, RELAY3, RELAY4 };

ZONE zone[] = {
  {"Front Right", RELAY1},
  {"Front Left", RELAY2},
  {"Bushes", RELAY3},
  {"Left Side", RELAY4},
};

SCHEDULE schedule[] = {
  {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, clk_time;
  uint8_t i; tm clk; uint8_t wday;
  for (i = 0; i < sizeof(schedule)/sizeof(SCHEDULE); i++) {
    if (schedule[i].days & SUNRISE) {
      clk_time = nvram.sunrise_time + schedule[i].hour * SEC_IN_HOUR + schedule[i].min * SEC_IN_MIN;
      localtime_r(&clk_time, &clk);
    } else {
      localtime_r(&last_time, &clk);
      clk.tm_hour = schedule[i].hour; clk.tm_min = schedule[i].min; clk.tm_sec = 0;
      clk_time = mktime(&clk);
    }
    wday = clk.tm_wday;
    while (clk_time <= last_time || !(schedule[i].days & (1 << wday))) {
      clk_time += SEC_IN_DAY;
      if (++wday > 6) wday = 0;
      if (wday == clk.tm_wday) break;
    }
    if (clk_time < next_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 = schedule[start_schedule].zone;
  if (i < MAX_ZONES && nvram.rain[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() {
  uint8_t i;
  nvram.water_duration--;
  update_nvram = true;
  i = schedule[nvram.water_schedule].zone;
  if (i < MAX_ZONES) {
    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);
  wdt_enable(WDTO_8S);
}

void loop() {
  static tm rtc; tm clk, sunrise_clk; time_t rtc_time, clk_time;
  static time_t next_time; static uint8_t last_sec, last_min;
  bool error_rtc, error_light, 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, 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);
      if (!error_rtc) { clk = rtc; rtc_time = mktime(&clk); localtime_r(&rtc_time, &rtc); }
      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))) {
            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 < rtc_time) nvram.sunrise_time += SEC_IN_DAY;
            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 < rtc_time) nvram.sunrise_time += SEC_IN_DAY;
          }
          if (nvram.water_duration) { nvram.water_duration++; water_time = nvram.last_water_time; }
          else { 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) {
      error_temp = !SerialCmdNoError(ONEWIRE_TEMPERATURE); if (!error_temp) SerialCmdDone("tt");
      error_light = !SerialCmdNoError(ONEWIRE_TO_I2C_ROM2 ";oo0");
      if (!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 < SUNRISE_LUX) { if (sunrise_counter > 0) sunrise_counter--; else check_sunrise = true; }
      else { if (sunrise_counter < MIN_IN_HOUR) { sunrise_counter++; if (check_sunrise && sunrise_counter == MIN_IN_HOUR) { nvram.sunrise_time = rtc_time + (SEC_IN_DAY - SEC_IN_HOUR); check_sunrise = false; update_nvram = true; } } }

      if (rain <= RAIN_DETECT_LEVEL) { for (i=0;i= 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); 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 (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) { 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 < MAX_ZONES) Serial.print(zone[i].description); 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); 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) { 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)); 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();
}

Nota: Se usar a porta USB para programar o Arduino Nano, desconecte-o do Expansor de I/O, pois ambos usam a mesma porta serial. Para depuração, use a porta ICSP para programar o ATmega328P. Para habilitar a porta de depuração de software, descomente SERIAL_DEBUG.

O divisor deve ser configurado para isolar a linha de dados do sensor infravermelho óptico da linha do sensor remoto 1-Wire. Solde um resistor zero ohm 0603 em R2.

Controlador de Irrigação Inteligente com Arduino – Rega Automática com Sensores de Clima e Luz

Fure um orifício de 7/16" na caixa pequena e 11/16" na maior no lado direito para os prensa-cabos PG7 e PG11. Use uma ferramenta rotativa para alargar até ajustar. O PG7 alimenta os sensores remotos e o PG11 os fios de 12VDC, 24VAC, manifold e cabo RJ11.

Controlador de Irrigação Inteligente com Arduino – Rega Automática com Sensores de Clima e Luz

Conecte as chaves micro push button SPST ao terminal de parafuso RJ11. Use tubo termorretrátil para isolar os contatos.

Controlador de Irrigação Inteligente com Arduino – Rega Automática com Sensores de Clima e Luz

Conecte todos os fios e monte tudo na caixa grande. O cabo RJ11 de 50ft para sensores remotos deve passar pelo prensa-cabo PG11 sem necessidade de corte.

Controlador de Irrigação Inteligente com Arduino – Rega Automática com Sensores de Clima e Luz

Fure um orifício de 9/16" no topo da caixa pequena para o sensor de água infravermelho óptico. A caixa pequena é apertada, mas se os componentes forem dispostos na orientação recomendada, cabem. Encurtar os fios RJ11 ajuda. Após montar, adicione cola marítima na arruela do prensa-cabo antes de apertar a porca para melhor vedação.

Controlador de Irrigação Inteligente com Arduino – Rega Automática com Sensores de Clima e Luz

Instale a caixa de sensores remotos do lado externo, elevada no lado leste da casa, com o sensor óptico de água e sensor de luz apontados para o céu sem obstruções.

Controlador de Irrigação Inteligente com Arduino – Rega Automática com Sensores de Clima e Luz

Fure orifícios de 1/4" no topo, meio e base da caixa grande para os botões. Use ferramenta rotativa para ajustar.

Controlador de Irrigação Inteligente com Arduino – Rega Automática com Sensores de Clima e Luz

Teste o sistema e verifique se tudo funciona. Para testar relés e sensores, desconecte o Arduino do Expansor e conecte diretamente ao computador para controle manual. Após verificar, monte tudo na caixa usando fita dupla face e espuma de embalagem para fixar as placas, e aproveite os benefícios e economia do seu Controlador de Irrigação Inteligente.

Vídeo em Operação

Atualização 12/09/2019

Lançada v1.1 que corrigiu problema de inicialização se o sistema ficasse sem energia por vários dias.

Atualização 02/10/2019

Ao conectar o 1-Wire para I2C ao DS3231 e depois ao OLED SSD1306, haverá três pull-ups diferentes nas linhas SDA e SCL (conforme imagem abaixo circulado). Isso resulta em pull-up efetivo de 4.7k / 3 = 1.56k, muito forte, causando corrupções aleatórias na tela.

Controlador de Irrigação Inteligente com Arduino – Rega Automática com Sensores de Clima e Luz

Como o DS3231 usa um resistor pack compartilhado, remova os outros pull-ups:

Código

Controlador de Irrigação Inteligente C/C++

Use seu Arduino para irrigar seu jardim ou quintal de forma inteligente.

/* IO Expander sketch optimized * Irrigation System v1.1 * */
#include <math.h> #include <time.h> #include <util/crc16.h> #include <avr/wdt.h> #include <SoftwareSerial.h> #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 "i6s8f"
#define ONEWIRE_TEMPERATURE "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_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_DAY (HOUR_IN_DAY * SEC_IN_HOUR)
#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 SAT 0x40
#define EVERYDAY (SUN | MON | TUE | WED | THR | FRI | 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 weekday[][4] = {"SUN","MON","TUE","WED","THU","FRI","SAT"};
char menu[][13] = {"Next","Water","Reset","Clock Min +","Clock Min -","Clock Hour +","Clock Hour -","Sunrise","ON/OFF"};
enum { 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; } SCHEDULE;
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;
enum { ZONE1, ZONE2, ZONE3, ZONE4 };
enum { RELAY1 = 1, RELAY2, RELAY3, RELAY4 };
ZONE zone[] = { {"Front Right", RELAY1}, {"Front Left", RELAY2}, {"Bushes", RELAY3}, {"Left Side", RELAY4} };
SCHEDULE schedule[] = { {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, clk_time; uint8_t i; tm clk; uint8_t wday; for (i = 0; i < sizeof(schedule)/sizeof(SCHEDULE); i++) { if (schedule[i].days & SUNRISE) { clk_time = nvram.sunrise_time + schedule[i].hour * SEC_IN_HOUR + schedule[i].min * SEC_IN_MIN; localtime_r(&clk_time, &clk); } else { localtime_r(&last_time, &clk); clk.tm_hour = schedule[i].hour; clk.tm_min = schedule[i].min; clk.tm_sec = 0; clk_time = mktime(&clk); } wday = clk.tm_wday; while (clk_time <= last_time || !(schedule[i].days & (1 << wday))) { clk_time += SEC_IN_DAY; if (++wday > 6) wday = 0; if (wday == clk.tm_wday) break; } if (clk_time < next_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 = schedule[start_schedule].zone; if (i < MAX_ZONES && nvram.rain[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() { uint8_t i; nvram.water_duration--; update_nvram = true; i = schedule[nvram.water_schedule].zone; if (i < MAX_ZONES) { 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); wdt_enable(WDTO_8S); }
void loop() { static tm rtc; tm clk, sunrise_clk; time_t rtc_time, clk_time; static time_t next_time; static uint8_t last_sec, last_min; bool error_rtc, error_light, 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, 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); if (!error_rtc) { clk = rtc; rtc_time = mktime(&clk); localtime_r(&rtc_time, &rtc); } 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))) { 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 < rtc_time) nvram.sunrise_time += SEC_IN_DAY; 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 < rtc_time) nvram.sunrise_time += SEC_IN_DAY; } if (nvram.water_duration) { nvram.water_duration++; water_time = nvram.last_water_time; } else { 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) { error_temp = !SerialCmdNoError(ONEWIRE_TEMPERATURE); if (!error_temp) SerialCmdDone("tt"); error_light = !SerialCmdNoError(ONEWIRE_TO_I2C_ROM2 ";oo0"); if (!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 < SUNRISE_LUX) { if (sunrise_counter > 0) sunrise_counter--; else check_sunrise = true; } else { if (sunrise_counter < MIN_IN_HOUR) { sunrise_counter++; if (check_sunrise && sunrise_counter == MIN_IN_HOUR) { nvram.sunrise_time = rtc_time + (SEC_IN_DAY - SEC_IN_HOUR); check_sunrise = false; update_nvram = true; } } } if (rain <= RAIN_DETECT_LEVEL) { for (i = 0; i < MAX_ZONES; i++) if (nvram.rain[i] < 255) nvram.rain[i]++; update_nvram = true; } if (menu_select == -1 && !nvram.water_duration) { while (water_time + (schedule[water_schedule].duration * SEC_IN_MIN) < rtc_time) 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; } } if (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; 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); 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 (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) { 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 < MAX_ZONES) Serial.print(zone[i].description); 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); 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) { 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)); 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(); }

Esquemático

Irrigue seu quintal ou jardim de forma inteligente.

Controlador de Irrigação Inteligente com Arduino – Rega Automática com Sensores de Clima e Luz

Processo de manufatura

  1. Construindo Sistemas de Smart Farming Open-Source: Capacitando Agricultores com IoT Acessível e Interoperável
  2. ECM3532: Primeira Placa de Sensor IA Integrada Ultra-Baixo Consumo da Eta Compute Acelera Desenvolvimento Edge
  3. Construa um Controlador Doméstico Inteligente 433 MHz com Sensorflare e Raspberry Pi
  4. Construa Monitor de Temperatura Doméstico com Raspberry Pi, MCP9808, InfluxDB e Grafana
  5. Lixeira Inteligente: Solução IoT Celular para Gestão Automatizada de Resíduos
  6. Bartender Inteligente: Construa sua Própria Estação de Coquetéis com Raspberry Pi
  7. Sensor de Emoção & EEG: Hardware de Baixo Custo para Detecção de Emoções em Tempo Real
  8. Controlador de Irrigação Inteligente com Windows 10 IoT, Raspberry Pi e Sensores de Umidade XBee
  9. Sensor de Deformação Ultra-Sensível e Durável Revoluciona Têxteis Inteligentes
  10. Sensor Ultra-Fino Revolucionário Melhora Funcionalidade de Lentes de Contato Inteligentes