// OLED INIT
#include <GyverOLED.h>
GyverOLED<SSD1306_128x64, OLED_NO_BUFFER> oled;
// IR REMOTE
boolean nec_ok = 0;
byte i, nec_state = 0, command, inv_command;
unsigned int address;
unsigned long nec_code;
// LCD 16x2 I2C
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
// SERVo
#include <Servo.h>
#define SERVOPIN 10
Servo servo;
// DHT22
#include <DHT.h>
#define DHTPIN 4
DHT dht(DHTPIN, DHT22);
// RTC
#include <RTClib.h>
RTC_DS1307 rtc;
// PIR Sensor
#define PIRPIN 3
bool pirState = false;
// Buzzer
#define SPEAKERPIN 9
// Relay
#define RELAYPIN 8
// RGB
const int rgbPins[] = { 13, 12, 11};
// LED PIN
#define LEDPIN 7
byte light = 125;
// LIGHT Sensor
#define LIGHTPIN A0
int lightSensor() {
int analogValue = analogRead(LIGHTPIN);
int mappedLight = map(analogValue, 0, 1023, 100, 0);
return mappedLight;
}
// Millis
unsigned long interval = 1000;
unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
// PWM
bool state = LOW;
const int pwmPeriod = 10000;
unsigned int onTime = 0;
unsigned int offTime = 0;
unsigned long currentMicros;
unsigned long previousMicros = 0;
// MENU CONTROL IR REMOTE
byte BTN_UP = 64;
byte BTN_DOWN = 25;
byte BTN_ACCEPT = 21;
byte BTN_CANCEL = 67;
// OLED MENu
const char* menuItems[] = {"Темп. i Волог.", "Змiна кольору RGB LED", "Закрити дверi", "Яскравiсть LED" };
const int menuLength = sizeof(menuItems) / sizeof(menuItems[0]);
byte currentMenu = 0;
// MENU
enum pageType {ROOT_MENU, SUB_MENU1, SUB_MENU2, SUB_MENU3};
enum pageType currPage = ROOT_MENU;
#define ROOT_MENU_CNT 3
void setup() {
// SERIAL PORT
Serial.begin(9600);
Serial.println("Program Started...");
// DHT22
dht.begin();
// RTC
rtc.begin();
// OLED
oled.init();
// LCD 16x2
lcd.begin(20, 4);
// SERVO
servo.attach(SERVOPIN);
servo.write(90);
// RELAY
pinMode(RELAYPIN, OUTPUT);
// LED
pinMode(LEDPIN, OUTPUT);
// IR REMOTE
TCCR1A = 0;
TCCR1B = 0; // Disable Timer1 module
TCNT1 = 0; // Set Timer1 preload value to 0 (reset)
TIMSK1 = 1; // enable Timer1 overflow interrupt
attachInterrupt(0, remote_read, CHANGE); // Enable external interrupt (INT0)
// RGB INIT
for( int i=0; i<3; i++)
pinMode( rgbPins[i], OUTPUT);
}
void loop() {
currentMillis = millis();
// autoTurnOnLight();
// if (currentMillis - previousMillis >= interval) {
// previousMillis = currentMillis;
// humidityControl();
// drawDateAndTimeLCD();
// }
switch (currPage) {
case ROOT_MENU: page_RootMenu(); break;
case SUB_MENU1: page_subMenu1(); break;
case SUB_MENU2: page_subMenu2(); break;
case SUB_MENU3: page_subMenu3(); break;
}
}
void page_RootMenu(void){
uint32_t loopStartMs;
uint8_t sub_Pos = 0;
boolean updateDisplay = true;
// boolean btn_Up_WasDown = false;
// boolean btn_Down_WasDown = false;
// boolean btn_Accept_WasDown = false;
while (true) {
if(nec_ok){
nec_ok = 0;
nec_state = 0;
TCCR1B = 0;
command = nec_code >> 8;
Serial.println(command);
if (BTN_DOWN == command) {
if (sub_Pos == ROOT_MENU_CNT) {sub_Pos =0;} else {sub_Pos++;}
updateDisplay = true;
}
if (BTN_UP == command) {
if (sub_Pos == 0) {sub_Pos = ROOT_MENU_CNT;} else {sub_Pos--;}
updateDisplay = true;
}
attachInterrupt(0, remote_read, CHANGE);
if (BTN_ACCEPT == command) {
switch(sub_Pos) {
case 0: currPage = SUB_MENU1; return;
case 2: currPage = SUB_MENU2; return;
case 3: currPage = SUB_MENU3; return;
}
}
}
loopStartMs = millis();
if (updateDisplay) {
updateDisplay = false;
oled.setCursor(0,0);
printSelected(0, sub_Pos); oled.println("Temp. & Humidity");
printSelected(1, sub_Pos); oled.println("Change Light Level");
printSelected(2, sub_Pos); oled.println("Change RGB Color");
printSelected(3, sub_Pos); oled.println("Close Door");
oled.update();
}
while (millis() - loopStartMs < 25) {delay(2);}
}
}
void page_subMenu1(void) {
uint32_t loopStartMs;
uint32_t lastMs;
while (true) {
currentMicros = micros();
if(nec_ok){
nec_ok = 0;
nec_state = 0;
TCCR1B = 0;
command = nec_code >> 8;
Serial.println(command);
attachInterrupt(0, remote_read, CHANGE);
if (BTN_CANCEL == command) {currPage = ROOT_MENU; return;}
}
loopStartMs = millis();
autoTurnOnLight();
if (loopStartMs - lastMs >= 1000) {
lastMs = loopStartMs;
float temp = dht.readTemperature();
int humidity = dht.readHumidity();
humidityControl();
drawDateAndTimeLCD();
drawTemperatureAndHumidityLCD();
oled.clear();
oled.setCursor(0, 0);
oled.print("Temperature: ");
oled.print(temp, 1);
oled.setCursor(0, 1);
oled.print("Humidity: ");
oled.print(humidity);
oled.update();
}
}
}
void page_subMenu2(void){}
void page_subMenu3(void){}
void printSelected(uint8_t p1, uint8_t p2) {
if (p1 == p2) {
oled.invertText(true);
} else {
oled.invertText(false);
}
}
// TURN ON RELAY WHEN HUMIDITY IS LOW (DHT22)
void humidityControl() {
byte humidity = dht.readHumidity();
if (humidity <= 50) {
digitalWrite(RELAYPIN, HIGH);
} else {
digitalWrite(RELAYPIN, LOW);
}
}
// SHOW TEMPERATURE AND HUMIDITY IN HOUSE
void drawTemperatureAndHumidity() {
float temp = dht.readTemperature();
int humidity = dht.readHumidity();
oled.clear();
oled.setCursor(0, 0);
oled.print("Temperature: ");
oled.print(temp, 1);
oled.setCursor(0, 1);
oled.print("Humidity: ");
oled.print(humidity);
oled.update();
}
// SHOW TEMPERATURE AND HUMIDITY IN HOUSE
void drawTemperatureAndHumidityLCD() {
float temp = dht.readTemperature();
int humidity = dht.readHumidity();
//lcd.clear();
lcd.setCursor(0, 2);
lcd.print("Temperature: ");
lcd.print(temp, 1);
lcd.setCursor(0, 3);
lcd.print("Humidity: ");
lcd.print(humidity);
}
// SHOW DATE AND TIME
void drawDateAndTimeLCD() {
DateTime now = rtc.now();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Date: ");
lcd.print(now.year(), DEC);
lcd.print(":");
lcd.print(now.month(), DEC);
lcd.print(":");
lcd.print(now.day(), DEC);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("Time: ");
lcd.print(now.hour(), DEC);
lcd.print(":");
lcd.print(now.minute(), DEC);
lcd.print(":");
lcd.print(now.second(), DEC);
}
// SHOW DATE AND TIME
void drawDateAndTime() {
DateTime now = rtc.now();
oled.clear();
oled.setCursor(0, 0);
oled.print("Date: ");
oled.print(now.year(), DEC);
oled.print(":");
oled.print(now.month(), DEC);
oled.print(":");
oled.print(now.day(), DEC);
oled.print(" ");
oled.setCursor(0, 1);
oled.print("Time: ");
oled.print(now.hour(), DEC);
oled.print(":");
oled.print(now.minute(), DEC);
oled.print(":");
oled.print(now.second(), DEC);
oled.update();
}
// AUTO LIGHT WORKING
void autoTurnOnLight() {
byte lightLevel = lightSensor();
if (lightLevel <= 50) {
generatePWM(LEDPIN, light);
} else {
generatePWM(LEDPIN, 0);
}
}
// PWM
void generatePWM(int pin, int level) {
if (level == 0) {
digitalWrite(pin, LOW);
return;
}
if (level == 255) {
digitalWrite(pin, HIGH);
return;
}
onTime = map(level, 0, 255, 0, pwmPeriod);
offTime = pwmPeriod - onTime;
if (state == HIGH && (currentMicros - previousMicros >= onTime)) {
state = LOW;
previousMicros = currentMicros;
digitalWrite(pin, state);
} else if (state == LOW && (currentMicros - previousMicros >= offTime)) {
state = HIGH;
previousMicros = currentMicros;
digitalWrite(pin, state);
}
}
// IR RECEIVER
void remote_read() {
unsigned int timer_value;
if(nec_state != 0){
timer_value = TCNT1; // Store Timer1 value
TCNT1 = 0; // Reset Timer1
}
switch(nec_state){
case 0 : // Start receiving IR data (we're at the beginning of 9ms pulse)
TCNT1 = 0; // Reset Timer1
TCCR1B = 2; // Enable Timer1 module with 1/8 prescaler ( 2 ticks every 1 us)
nec_state = 1; // Next state: end of 9ms pulse (start of 4.5ms space)
i = 0;
return;
case 1 : // End of 9ms pulse
if((timer_value > 19000) || (timer_value < 17000)){ // Invalid interval ==> stop decoding and reset
nec_state = 0; // Reset decoding process
TCCR1B = 0; // Disable Timer1 module
}
else
nec_state = 2; // Next state: end of 4.5ms space (start of 562µs pulse)
return;
case 2 : // End of 4.5ms space
if((timer_value > 10000) || (timer_value < 8000)){
nec_state = 0; // Reset decoding process
TCCR1B = 0; // Disable Timer1 module
}
else
nec_state = 3; // Next state: end of 562µs pulse (start of 562µs or 1687µs space)
return;
case 3 : // End of 562µs pulse
if((timer_value > 1400) || (timer_value < 800)){ // Invalid interval ==> stop decoding and reset
TCCR1B = 0; // Disable Timer1 module
nec_state = 0; // Reset decoding process
}
else
nec_state = 4; // Next state: end of 562µs or 1687µs space
return;
case 4 : // End of 562µs or 1687µs space
if((timer_value > 3600) || (timer_value < 800)){ // Time interval invalid ==> stop decoding
TCCR1B = 0; // Disable Timer1 module
nec_state = 0; // Reset decoding process
return;
}
if( timer_value > 2000) // If space width > 1ms (short space)
bitSet(nec_code, (31 - i)); // Write 1 to bit (31 - i)
else // If space width < 1ms (long space)
bitClear(nec_code, (31 - i)); // Write 0 to bit (31 - i)
i++;
if(i > 31){ // If all bits are received
nec_ok = 1; // Decoding process OK
detachInterrupt(0); // Disable external interrupt (INT0)
return;
}
nec_state = 3; // Next state: end of 562µs pulse (start of 562µs or 1687µs space)
}
}
ISR(TIMER1_OVF_vect) { // Timer1 interrupt service routine (ISR)
nec_state = 0; // Reset decoding process
TCCR1B = 0; // Disable Timer1 module
}