Эффект присутствия хозяев в доме или на даче

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

Чтобы повторить проект понадобятся часы реального времени, модуль на DS1307 или DS3231 (рекомендуется), LCD Keypad Shield, можно и текстовый LCD 16х2 отдельно. Можно использовать графический OLED дисплей SSD1306 0.96 128×64, для него отдельная версия исходного кода или вообще не подключать дисплей, он необходим только для контроля установленного времени. Еще понадобится релейный модуль и Arduino UNO или её дешевый аналог.

схема подключения модулей к LCD Keypad Shield, который одевается на Arduino UNO

Untitled Sketch_bb

установка текущего времени в проекте не предусмотрена, поэтому нужно заранее установить время в модуль часов.

сам код с комментариями для LCD Keypad Shield

// Эффект присутствия хозяев в доме или на даче
// для LCD Keypad Shield
// https://www.youtube.com/watch?v=imEvfyIiwtY

#include <Wire.h>
#include <LiquidCrystal.h>
#include <EEPROM.h>

#define OUT1 12 //выходы на релейный модуль
#define OUT2 13

LiquidCrystal lcd(8,9,4,5,6,7);

byte tm;
byte timerOut2 = 2;

///// часы ..
byte decToBcd(byte val){
  return ( (val/10*16) + (val%10) );
}

byte bcdToDec(byte val){
  return ( (val/16*10) + (val%16) );
}

void setDateDs1307(byte second,        // 0-59
                   byte minute,        // 0-59
                   byte hour,          // 1-23
                   byte dayOfWeek,     // 1-7
                   byte dayOfMonth,    // 1-28/29/30/31
                   byte month,         // 1-12
                   byte year)          // 0-99
{
   Wire.beginTransmission(0x68);
   Wire.write(0);
   Wire.write(decToBcd(second));    
   Wire.write(decToBcd(minute));
   Wire.write(decToBcd(hour));     
   Wire.write(decToBcd(dayOfWeek));
   Wire.write(decToBcd(dayOfMonth));
   Wire.write(decToBcd(month));
   Wire.write(decToBcd(year));
   Wire.endTransmission();
}

void getDateDs1307(byte *second,
          byte *minute,
          byte *hour,
          byte *dayOfWeek,
          byte *dayOfMonth,
          byte *month,
          byte *year)
{

  Wire.beginTransmission(0x68);
  Wire.write(0);
  Wire.endTransmission();

  Wire.requestFrom(0x68, 7);

  *second     = bcdToDec(Wire.read() & 0x7f);
  *minute     = bcdToDec(Wire.read());
  *hour       = bcdToDec(Wire.read() & 0x3f); 
  *dayOfWeek  = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month      = bcdToDec(Wire.read());
  *year       = bcdToDec(Wire.read());
}
  

void setup(){
//  Serial.begin(9600);
 
  Wire.begin();
  lcd.begin(16,2);
  
  pinMode(OUT1, OUTPUT);
  pinMode(OUT2, OUTPUT);
  digitalWrite(OUT1, LOW);
  digitalWrite(OUT2, LOW);

  randomSeed(analogRead(1));
//  timerReset(); // для отладки
  
 
}

void loop(){
    byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
    getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  
    lcdPrint(); // вывод на экран
  
  if (12 == hour &&   // в 12:00 генерируем новое расписание
       0 == minute && 
       0 == second) timerReset(); 
   
  // сверяем расписание с текущим временем и управляем реле
  if (EEPROM.read(0) == hour && EEPROM.read(1) == minute) digitalWrite(OUT1, HIGH);
  if (EEPROM.read(2) == hour && EEPROM.read(3) == minute) digitalWrite(OUT1, LOW);
  
  if (digitalRead(OUT1)){ // если первый выход выключен, то и со вторым нечего делать ненужно
      if (tm != minute){ // таймер для второго выхода
            tm = minute;     // 
            timerOut2--;
      }
      if (timerOut2 == 0){
        
        // в функции random(40, 60) диапазон для второго выхода в минутах
        if(digitalRead(OUT2)) timerOut2 = random(40, 60); // выключение на 40-59 минут
        else timerOut2 = random(3, 7); //включение на 3-6 мин
        
        digitalWrite(OUT2, !digitalRead(OUT2));
      }
  }
  else digitalWrite(OUT2, LOW);
   
 // 
}
 

 /// вывод инфы на экран 16x2// 
void lcdPrint(){
  char time[9], data[11], onOff[23], off[9];
  
    byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
    getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);

 /// время
  snprintf(time, sizeof(time), "%02d:%02d:%02d", hour, minute, second);           
  snprintf(data, sizeof(data), "%02d/%02d/%02d", dayOfMonth, month, hour);
  
  lcd.setCursor(0, 0);
  lcd.print(time);
   
 ///  on/off 
  snprintf(onOff, sizeof(onOff), " %02d:%02d/%02d:%02d|", 
                              EEPROM.read(0), EEPROM.read(1), EEPROM.read(2), EEPROM.read(3));             
  lcd.setCursor(0, 1);
  lcd.print(onOff);
  lcd.print(timerOut2);
  
  /// статус 
  lcd.setCursor(9, 0);
  if(digitalRead(OUT1)) lcd.print("ON ");
  else lcd.print("OFF");
  if(digitalRead(OUT2)) lcd.print(" ON ");
  else lcd.print(" OFF");  
}
/////////////////////

void timerReset(){  //  генерирует раcписание работы для первого выхода и для всего устройства тоже
  EEPROM.write(0, 20);              //часы вкл, постоянно в 8 вечера
  EEPROM.write(1, random(0, 60));   //минуты вкл 0-59
  EEPROM.write(2, random(22, 24));  //часы выкл 22-23
  EEPROM.write(3, random(10, 45));  //минуты выкл 10-44
  delay(1100); // нужно подождать, иначе функция вызавиться повторно
}

 

и для OLED 0.96 128×64

// Эффект присутствия хозяев в доме или на даче
// для OLED
// https://www.youtube.com/watch?v=imEvfyIiwtY

#include <OLED_I2C.h>
#include <Wire.h>
#include <EEPROM.h>

#define OUT1 3 //выходы на реле
#define OUT2 4

OLED  myOLED(SDA, SCL, 8);

extern uint8_t SmallFont[];
byte tm;
byte timerOut2 = 2;

///// часы ..
byte decToBcd(byte val){
  return ( (val/10*16) + (val%10) );
}

byte bcdToDec(byte val){
  return ( (val/16*10) + (val%16) );
}

void setDateDs1307(byte second,        // 0-59
                   byte minute,        // 0-59
                   byte hour,          // 1-23
                   byte dayOfWeek,     // 1-7
                   byte dayOfMonth,    // 1-28/29/30/31
                   byte month,         // 1-12
                   byte year)          // 0-99
{
   Wire.beginTransmission(0x68);
   Wire.write(0);
   Wire.write(decToBcd(second));    
   Wire.write(decToBcd(minute));
   Wire.write(decToBcd(hour));     
   Wire.write(decToBcd(dayOfWeek));
   Wire.write(decToBcd(dayOfMonth));
   Wire.write(decToBcd(month));
   Wire.write(decToBcd(year));
   Wire.endTransmission();
}

void getDateDs1307(byte *second,
          byte *minute,
          byte *hour,
          byte *dayOfWeek,
          byte *dayOfMonth,
          byte *month,
          byte *year)
{

  Wire.beginTransmission(0x68);
  Wire.write(0);
  Wire.endTransmission();

  Wire.requestFrom(0x68, 7);

  *second     = bcdToDec(Wire.read() & 0x7f);
  *minute     = bcdToDec(Wire.read());
  *hour       = bcdToDec(Wire.read() & 0x3f); 
  *dayOfWeek  = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month      = bcdToDec(Wire.read());
  *year       = bcdToDec(Wire.read());
}
  

void setup(){
//  Serial.begin(9600);
  myOLED.begin();
  myOLED.setBrightness(1);
  myOLED.setFont(SmallFont);
  
  
  pinMode(OUT1, OUTPUT);
  pinMode(OUT2, OUTPUT);
  digitalWrite(OUT1, HIGH);
  
  randomSeed(analogRead(1));
//  timerReset(); // для отладки
  
  myOLED.clrScr();

}

void loop(){
    byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
    getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  
    lcdPrint(); // вывод на экран
  
  if (12 == hour &&   // в 12 дня генерируем новое расписание
       0 == minute && 
       0 == second) timerReset(); 
   

  if (EEPROM.read(0) == hour && EEPROM.read(1) == minute) digitalWrite(OUT1, HIGH);
  if (EEPROM.read(2) == hour && EEPROM.read(3) == minute) digitalWrite(OUT1, LOW);
  
  if (digitalRead(OUT1)){ // если первый выход выключен, то и со вторым нечего делать ненужно
      if (tm != minute){ // таймер для второго выхода
            tm = minute;     // 
            timerOut2--;
      }
      if (timerOut2 == 0){
          
        if(digitalRead(OUT2)) timerOut2 = random(40, 60);    // если свет включен
        else timerOut2 = random(3, 7); //если выключен, включаем на 3-7 мин
        
        digitalWrite(OUT2, !digitalRead(OUT2));
      }
  }
  else digitalWrite(OUT2, LOW);
   
 // 
}

/// вывод инфы на экран oled// 

void lcdPrint(){
  char time[9], data[11], onOff[23], off[9];
  
    byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
    getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);

 /// время
  snprintf(time, sizeof(time), "%02d:%02d:%02d", hour, minute, second);           
  snprintf(data, sizeof(data), "%02d/%02d/%02d", dayOfMonth, month, hour);
  
  myOLED.setFont(SmallFont);
  myOLED.print(time, LEFT, 0);
  myOLED.print(data, LEFT, 9);
  
 ///  on/off 
  snprintf(onOff, sizeof(onOff), "1.on/off %02d:%02d/%02d:%02d", 
                              EEPROM.read(0), EEPROM.read(1), EEPROM.read(2), EEPROM.read(3));             
  myOLED.print(onOff, LEFT, 25);
  myOLED.print("2.", LEFT, 34);
  myOLED.printNumI(timerOut2, 12, 34);
  
  /// статус 
  if(digitalRead(OUT1)) myOLED.print("|ON ", 100, 0);
  else myOLED.print("|OFF", 100, 0);
  if(digitalRead(OUT2)) myOLED.print("|ON ", 100, 9);
  else myOLED.print("|OFF", 100, 9);
  
  myOLED.update();
}


void timerReset(){    
  EEPROM.write(0, 19); //часы вкл
  EEPROM.write(1, random(0, 59)); //минуты вкл
  EEPROM.write(2, random(0, 2)); //часы выкл
  EEPROM.write(3, random(30, 59)); //минуты выкл
  delay(1000); // нужно подождать, иначе функция вызавиться повторно
}