viliamk
Published © GPL3+

Arduino opens a window when the air in a room is exhaled

Automatic window opening by Arduino

IntermediateFull instructions provided836
Arduino opens a window when the air in a room is exhaled

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
Stepper motor 28BYJ-48 12V
×1
ULN2003 driver
×1
OLED display 128x32
×1

Story

Read more

Code

ShiftRegister

Arduino
Serves shift registers for the clock
#define dataPin  A2   //PIN 14 of 1 shift register  
#define latchPin A3   //PIN 12 of both shift registers  
#define clockPin A1   //PIN 11 of both shift registers
#define resetPin 10    //PIN 10 of both shift registers

/*
 * 2 Shift registers 74HC595 for displaying the clock on the 7segment4digit display
 * 
 * Q0    - PIN 15 of shift register I
 * Q1-Q7 - PINS 1-7 of shift register I
 * +5V - PIN 16 of shift register
 * GND - PIN 8 of shift register
 * PIN 9 of shift register I to PIN 14 of shift register II
 * Both shift registers share latchPin, clockPin, resetPin
 * 
 * MOSFET IRF520N:
 * Gates to pin 1-4 of shift register II
 * Sources to ground
 * Drains do Digit1-Digit4 of 7segmet4digit display
 */

void InitializeShiftRegister() {
  DDRC |= B00001110;      //pinMode(dataPin, OUTPUT); 
                          //pinMode(clockPin, OUTPUT); 
                          //pinMode(latchPin, OUTPUT);
  DDRB |= B00000100;      //pinMode(resetPin, OUTPUT);
  ClearShiftRegister();   //digitalWrite(resetPin, HIGH);
                          //digitalWrite(latchPin, HIGH);
}

void SetShiftRegister(byte digitID, byte digitValue) {
  PORTC &= B11110101;             //digitalWrite(latchPin, LOW);
                                  //digitalWrite(clockPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, digitID);   //shift out high byte, alternative: LSBFIRST / MSBFIRST
  PORTC &= B11111101;             //digitalWrite(clockPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, digitValue);  //shift out low byte, alternative: LSBFIRST / MSBFIRST
  PORTC |= B00001000;             //digitalWrite(latchPin, HIGH);
}

void ClearShiftRegister() {
  PORTB &= B11111011;   //digitalWrite(resetPin, LOW);
  PORTB |= B00000100;   //digitalWrite(resetPin, HIGH);
  PORTC &= B11110111;   //digitalWrite(latchPin, LOW);
  PORTC |= B00001000;   //digitalWrite(latchPin, HIGH);
}

CO2CubeWindow

Arduino
Main loop
/*
 * Receives the signal from MyCO2Cube and opens / closes a window
 */

#define releaseButton 3

bool openWindow = false;
bool released = false;
volatile bool releaseButtonPushed = false;
bool set = false;
bool locked = false;
unsigned long lockTime, infoTime;
int lockedInfo;

void setup() {
  InitializeOledDisplay();
  InitializeMyClock();  //contains InitializeShiftRegister();
  InitializeReceiver();
  InitializeStepMotor();
  InitializeFan();
  InitializeCO2CubeWindow();
  DisplayText("SETTING", false, true);
}

void InitializeCO2CubeWindow() {
  pinMode(releaseButton, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(releaseButton), ReleaseButtonPushed, LOW);
  lockTime = millis();
}

void loop() {
  if (set) {
    GetSignal();    
    
    if (releaseButtonPushed) {      //switches between Normal, Release and Locked mode
      detachInterrupt(digitalPinToInterrupt(releaseButton));      
      releaseButtonPushed = false;
      if (locked) {
        locked = false;
        TurnMyClockOFF();
        DisplayOpenClose();
      }
      else {
        released = !released;
        if ((millis() - lockTime) < 800) locked = true;
        lockTime = millis();
        if (locked) {
          DisplayText("LOCKED", false, true);
          released = false;
          infoTime = millis();
          lockedInfo = 0;
        } else {
          if (released) DisplayText("RELEASE", false, true);
          else {
            DisplayOpenClose();
          }
          TurnMyClockOFF();
        }
      }
      delay(700);
      attachInterrupt(digitalPinToInterrupt(releaseButton), ReleaseButtonPushed, LOW);
    }

    if (locked) {
      CloseWindow();
      if (millis() - infoTime > 7000) {
        DisplayLockedInfo();              //shows some statistics
        infoTime = millis();
      }
    }
    else {
      if (released) ReleaseWindow();
      else {
        if (openWindow) OpenWindow();
        else CloseWindow();
      }      
    }
    
    DisplayMyClock();       //shows the clock
  } 
  else CloseWindow();       //sets device after turning on
}

void ReleaseButtonPushed() {
  releaseButtonPushed = true;
}

Fan

Arduino
Serves the fan
 
#define fanONOFFPin A0

bool fanON = true;

void InitializeFan() {
  pinMode(fanONOFFPin, OUTPUT);
  TurnFanOFF();  
}

void TurnFanON() {
  if (!fanON) {
    digitalWrite(fanONOFFPin, HIGH);
    fanON = true;
  }
}

void TurnFanOFF() {
  if (fanON) {
    digitalWrite(fanONOFFPin, LOW);
    fanON = false;
  }
}

MyClock

Arduino
Displays the clock on 7segment4digit display
/*
 * MyClock for 7segment4digit display:
 * 1st up row: D4, A, F, D3, D2, B
 * 2nd bottom row: E, D, DP, C, G, D1
 * 
 * DS1307 rtc module
 * 
 * !!! VBAT PIN must be connected to +3V of the lithium battery
 */

#include <Wire.h>
#include "RTClib.h"

RTC_DS1307 rtc;
DateTime myTime, openTime, lastTime;
bool openTimeNotSet = true;
int openCount = 0;

byte digitID[4] = {B00000010, B00000100, B00001000, B00010000};  //pins 1-4 of shift register II to Mosfet IRF520N gates, Byte = {0, 0, 0, D4, D3, D2, D1, 0}
byte digitValue[4];
byte number[11] = {B11010111, B00010001, B11001101, B01011101, B00011011, B01011110, B11011110, B00010101, B11011111, B01011111, B00001000};  //pins 15 & 1-8 of shift register I, Byte = {E, D, DP, C, G, A, F, B}
byte dotValue = B00100000;
int k = 0;
int minuteHigh, minuteLow, hourHigh, hourLow, lastMinute = 1;
bool myClockON = true;
unsigned long startTime, actualTime;

void InitializeMyClock() {
  InitializeShiftRegister();
  
  if (!rtc.begin()) {
    DisplayText("ClkERR", false, true);
    while(1);
  }
  //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));   //sets the RTC to the date & time from the PC

  digitValue[0] = number[10];
  digitValue[1] = number[10];
  digitValue[2] = number[10] | dotValue;
  digitValue[3] = number[10];
  TurnMyClockOFF();
  startTime = millis();
}

void DisplayMyClock() {
  if (myClockON) {
    actualTime = millis();
    if (((actualTime - startTime) > 10000) || (actualTime < startTime)) {  
      GetTime();
      startTime = millis();
    }
    SetShiftRegister(digitID[k], digitValue[k]);
    k++; if (k > 3) k = 0;
  }
}

void GetTime() {
  myTime = rtc.now();
  if (myTime.minute() != lastMinute) {
    lastMinute = myTime.minute();
    minuteHigh = lastMinute / 10;
    minuteLow = lastMinute - (minuteHigh * 10);
    hourHigh = myTime.hour() / 10;
    hourLow = myTime.hour() - (hourHigh * 10);
    digitValue[0] = number[minuteLow];
    digitValue[1] = number[minuteHigh];
    digitValue[2] = number[hourLow] | dotValue;
    digitValue[3] = number[hourHigh];
  }
}

void RecordOpen() {
  if (openTimeNotSet) {
    openTime = rtc.now();
    openCount = 0;
    openTimeNotSet = false;
  }
  
  lastTime = rtc.now();
  int actualHour = lastTime.hour();
  int actualDay = lastTime.day();
  int lastHour = openTime.hour();
  int lastDay = openTime.day();
    
  if ((actualHour >= 22) && (actualHour <= 6)) {
    if  (   ((actualHour >= 22) && (actualHour < 24) && ((lastHour < 22) || (lastDay < actualDay))) 
        || ((actualHour >= 0) && (actualHour <= 6) && ( ((lastHour < 22) && (lastDay < actualDay)) || ((lastHour >= 22) && (lastDay < (actualDay -1))) ) )   ) {
      openTime = rtc.now();
      openCount = 0;
    }
    openCount++;
  }
}

void TurnMyClockOFF() {
  if (myClockON) {
    ClearShiftRegister();
    myClockON = false;
  }
}

void TurnMyClockON() {
  if (!myClockON) myClockON = true;
}

OledDisplay

Arduino
Serves OledDisplay
/*
 * OLED display 128x32
 * SDA - pin A4
 * SCK - pin A5
 * VCC - 3V-5V
 */

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

bool displayON;

void InitializeOledDisplay() {
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { 
    while(1);
  }
  //display.ssd1306_command(SSD1306_SETCONTRAST);
  //display.ssd1306_command(255);
  //display.dim(true);  // set contrast to 31 (reduces consumption to half)
  //display.display();
  display.setTextColor(WHITE);
  DisplayText("WELCOME", false, false);
}

#define vertical 12
bool holdDisplay = false;
char holdString[32];

void DisplayText(char text[32], bool showReceived, bool setHoldDisplay) {
  display.clearDisplay();
  TurnDisplayON();

  if (setHoldDisplay) {
    strcpy(holdString,text);
    holdDisplay = true;
  } else {
    if (showReceived) {
      display.setCursor(0,0);  
      display.setTextSize(1);
      display.print("Received:");  
    }
    display.setCursor(0,vertical);
    display.setTextSize(3); 
    display.print(text);   
    display.display();
    delay(2000);
  }
  
  if (holdDisplay) {
    display.clearDisplay();
    display.setCursor(0,vertical);
    display.setTextSize(3);
    display.print(holdString);   
    display.display();
  } 
  else TurnDisplayOFF();
}

void DisplayLockedInfo() {
  if (lockedInfo == 0) {
    display.clearDisplay();
    display.setCursor(0,0);
    display.setTextSize(1);
    display.print("Open at 22:00-6:00");
    display.setCursor(0,vertical);
    display.setTextSize(3); 
    display.print(openCount);
    display.print("x");   
    display.display();
  } 

  if (lockedInfo == 1) {
    display.clearDisplay();
    display.setCursor(0,0);
    display.setTextSize(1);
    display.print("Last open:");
    if (!openTimeNotSet) {
      display.setCursor(0,vertical);
      display.setTextSize(2); 
      display.print(lastTime.day());
      display.print(".");
      display.print(lastTime.month());
      display.print(".");
      display.print(lastTime.hour());
      display.print(":");
      display.print(lastTime.minute());   
    } 
    display.display();
  } 
  
  if (lockedInfo == 2) DisplayText("LOCKED", false, true);

  lockedInfo++; if (lockedInfo > 2) lockedInfo = 0;
}

void DisplayOpenClose() {
  if (openWindow) DisplayText("OPEN", false, true);
  else DisplayText("CLOSE", false, true);
}

void TurnDisplayOFF() {
  if (displayON) {
    displayON = false;
    display.ssd1306_command(SSD1306_DISPLAYOFF);
  }
}

void TurnDisplayON() {
  if (!displayON) {
    displayON = true;
    display.ssd1306_command(SSD1306_DISPLAYON);
  }
}

PIN_scheme

Arduino
/* Movement unit
   PIN 0 - (RX0) 
   PIN 1 - (TX1) DO NOT USE!
   PIN 2 - closeButton
   PIN 3 - releaseButton (interrupt)

   PIN 4 - INT1 stepper motor module ULN2003
   PIN 5 - INT2 stepper motor module ULN2003
   PIN 6 - INT3 stepper motor module ULN2003
   PIN 7 - INT4 stepper motor module ULN2003

   PIN 8 - CSN nRF24L01 
   PIN 9 - CE nRF24L01
   PIN 10 - RESET PIN shiftregister I & II
   PIN 11 - MOSI nRF24L01
   PIN 12 - MISO nRF24L01
   PIN 13 - SCK nRF24L01

   PIN A0 - Fan MOSFET ON/OFF pin
   PIN A1 - CLOCK PIN shiftregister I & II
   PIN A2 - DATA PIN shiftregister I
   PIN A3 - LATCH PIN shiftregister I & II

   PIN A4 - (SDA) OLED display, DS1307
   PIN A5 - (SCL) OLED display, DS1307

   PIN A6 - (INPUT only) 
   PIN A7 - (INPUT only) 

   VCC 12V - Fan, StepMotor
   VCC 5V - OLED display, DS1307, shift register I & II, RESET PIN of shift register I & II
   VCC 3.3V - nRF24L01
   GND - nRF24L01, StepMotor, Fan, OLED display, DS1307, closeButton, releaseButton
*/

Receiver

Arduino
Serves nRFL01 receiver
/*
 * nRF24L01 Receiver
 */
 
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 receiver(9, 8);                // CE, CSN
const byte address[6] = "openw";    //address through which two modules communicate

void InitializeReceiver() {
  if (!receiver.begin()) {
    DisplayText("ComERR", false, true);
    while(1);
  }

  receiver.setPALevel(RF24_PA_MIN);     //RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, and RF24_PA_max
  receiver.setDataRate(RF24_250KBPS);   //RF24_250KBPS, RF24_1MBPS, RF24_2MBPS
  
  receiver.openReadingPipe(0, address);
  receiver.startListening();            //Set module as receiver
}

void GetSignal() {
  receiver.powerUp();
  if (receiver.available()) {
    TurnFanOFF();
    TurnStepMotorOFF();
    TurnMyClockOFF();
    char text[32] = {0};
    receiver.read(&text, sizeof(text));
    if (strcmp(text,"open") == 0) { openWindow = true; RecordOpen(); if (!released && !locked) DisplayOpenClose(); }
    if (strcmp(text,"close") == 0) { openWindow = false; if (!released && !locked) DisplayOpenClose(); }
    DisplayText(text, true, false);
  }
}

StepMotor

Arduino
Serves stepper motor
/*
 * 12V Stepper motor 28BYJ-48 with ULN2003 driver
 */

#include <Stepper.h>

#define fullOpenWindowSteps 100000
#define releaseSteps 15000
#define closeButton 2
#define isClosed ((PIND & B100) == 0)

const int stepsPerRevolution = 2048; 
const int rpm = 14;
long steps = fullOpenWindowSteps;   
bool wasOpen = false;

Stepper stepMotor = Stepper(stepsPerRevolution, 4, 6, 5, 7);
  
void InitializeStepMotor() {
  pinMode(closeButton, INPUT_PULLUP);
  stepMotor.setSpeed(rpm);
  TurnStepMotorOFF();
}

void CloseWindow() {
  if (isClosed) {
    if (wasOpen) {        
      for (int k = 0; k < 50; k++) stepMotor.step(-1);    //noise reduction of the closeButton
      wasOpen = false;
    }
    TurnStepMotorOFF();
    if (!locked) TurnDisplayOFF();
    TurnMyClockON();
    steps = 0;
    if (!set) {
      set = true;
      DisplayText("READY", false, false);
    }
  }
  else {
    TurnMyClockOFF();
    if (steps > 0) steps--;
    stepMotor.step(-1);
    wasOpen = true;
  }
}

void OpenWindow() {
  if (steps < fullOpenWindowSteps) {
    stepMotor.step(1);
    steps++;
  }
  else {
    TurnStepMotorOFF();
    TurnDisplayOFF();
    TurnMyClockON();
    TurnFanON();     
  }
}

void ReleaseWindow() {
  if (steps < releaseSteps) {
    stepMotor.step(1);
    steps++;
  }
  else {
    TurnStepMotorOFF();
    TurnMyClockON();
  }
}

void TurnStepMotorOFF() {
  PORTD &= B00001111;
}

Credits

viliamk
14 projects • 5 followers

Comments