オーディオタイマーで換気
 
最近寒いですよね。
寒いときはあまり考えないんですが、去年
服にカビが生えてしまって着れなくなる事件が発生しまして。
それ以来カビに関して調べていたのですが、
湿気と温度がポイントで、
湿気がたまらなくすることがポイントの様です。
 
それで、なんですが、
自分が普段部屋にいない平日日中の時間に自動的に喚起してくれるシステムを考えたわけです。
毎日セットするのも面倒なので、
月から金まで選択可能で、ONとOFFにする時間も選択可能なやつ。
 
それの作成記事です。
 
昔々音楽を聴くのがカセットだった時代に、
FMラジオとか録音するのにオーディオタイマーっていうのがあって、
ラジオが始まる時間になると自動的に電源が入って録音を開始してくれて、
終わる時間になると電源を切って録音が終了するっていうやつ。
あれみたいなものです。
 
で、完成の写真とその動画です。
 

 

 
■機能
 
機能的は以下の5個にしました。
 
・週間繰り返しモードで指定した曜日の指定した時間だけONにする
・ずっとONにする
・ずっとOFFにする。
・一定期間経過後にONにしてその後はずっとONにする
・一定時間経過後にOFFにしてその後はずっとOFFにする
 
■操作
 黄ボタンがSET
 緑ボタンがSELECT
 
 最初電入れた直後は現在時刻を入力
  緑で選択して、黄で決定していきます。
  年→月→日→時→分 をそれぞれ決定すると
  通常の時刻表示に切り替わります。
 
 時刻表示から黄ボタン押すとモード切替に入って
  緑ボタンを押すと
  WEEKLY→AlWayON→AlWayOFF→AfterON→AfterOFFと切り替わります。
  表示の意味は
  WEEKLY  :
   週間繰り返しモードで指定した曜日の指定した時間だけONにする
  AlWayON
   ずっとONにする
  AlWayOFF
   ずっとOFFにする。
  AfterON
   一定期間経過後にONにしてその後はずっとONにする
  AfterOFF
   一定時間経過後にOFFにしてその後はずっとOFFにする
  モードを決めて黄ボタンを押し、画面の表示に従って追加の操作をしていきます。
 
 時計表示の時に緑ボタンを押すと液晶のバックライトを消します。
 もう一度何かを押すとバックライトが点灯します。
 
■回路図
 

■プログラム
 
バグっていたので
こちらの記事を参考にしてね。→https://ameblo.jp/fc2miha/entry-12833772963.html

 
以下はバグっています。

#include <stdio.h>
#include <time.h>

#include <LiquidCrystal.h>
//LCD        arduino
// 4(rs)     D12
// 5(rw)     D11
// 6(enable) D10 
// 11(db4)    D5
// 12(db5)    D4
// 13(db6)    D3
// 14(db7)    D2
LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);

//
//D8をSSRへ接続
//

struct BUTTON
{
  int value;
  int flag;
  int input_pin;
};
struct BUTTON bt_select;
struct BUTTON bt_enter;
int lcd_back_light = 1;

void button_initialize()
{
  memset(&bt_select, NULL, sizeof(bt_select));
  bt_select.input_pin = 6;
  memset(&bt_enter, NULL, sizeof(bt_enter));
  bt_enter.input_pin = 7;
}

void tick_button(struct BUTTON *bt)
{
  int value = digitalRead(bt->input_pin);
  if(value!=bt->value){
    bt->value = value;
    bt->flag = 1;
//    if(value==HIGH){
//      digitalWrite(13, HIGH);
//      lcd_back_light = 1;
//    }
    delay(10);
  }
}

int get_button(int *status, struct BUTTON *bt)
{
  if(bt->flag == 1){
    *status = bt->value;
    bt->flag = 0;
    return 1;
  }
  else{
    return 0;
  }
}

void setup()
{
  Serial.begin(9600); 
  
  delay(620);

  Serial.print("AcPowerControler version0.1 ¥n");
  
  //
  button_initialize();
  alarm_initialise();

  //lcd back light
  digitalWrite(13, HIGH);

  //SSR
  pinMode(8, OUTPUT);
  digitalWrite(8, LOW);
  //Input
  pinMode(6, INPUT);  //enter button
  pinMode(7, INPUT);  //select button
  //back light
  pinMode(13, OUTPUT);  //LCD back light

  //LCD
  lcd.begin(2,16);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("AcPowerControler");
  delay(500);
  //
  setup_time_input();
}

void setup_time_input()
{
  int YYYY = input("input year?", 4, 2020, 2020, 2100);
  int MM = input("input month?", 2, 1, 1, 12);
  int DD = input("input day?", 2, 1, 1, 31);
  int hh = input("input hour?", 2, 0, 0, 23);
  int mm = input("input minits?", 2, 0, 0, 59);

  initDateTime(YYYY, MM, DD, hh, mm);

  lcd.clear();

}

int input(char *str, int keta, int def, int mMin, int nMax)
{
  lcd.clear();
  lcd.print(str);

  int  i = 0;
  char buf[17];
  char ch[2];
  //
  lcd.blink();
  //
  int value = def;

  int flag = 1;
  int status;
  while(1){
    tick_button(&bt_select);
    tick_button(&bt_enter);

    if(flag==1){
      //
      if(keta==4){
        sprintf(buf, "%04d", value);
      }
      if(keta==2){
        sprintf(buf, "%02d", value);
      }
      if(keta==1){
        sprintf(buf, "%01d", value);
      }
      lcd.setCursor(0, 1);
      lcd.print(buf);
      lcd.setCursor(keta-1, 1);
      flag = 0;
    }
    if(get_button(&status, &bt_enter)){
      if(status==HIGH){
        return value;
      }
    }
    if(get_button(&status, &bt_select)){
      if(status==HIGH){
        flag = 1;
        value++;
        if(value>nMax){
          value = mMin;
        }
      }
    }
  }
}

void lcd_print(int x, int y, char *str)
{
  lcd.setCursor(x, y);
  lcd.print(str);
}

struct DateTime {
  time_t base_time;
  unsigned long BaseMillis;
  unsigned long base_startup_keika_sec;
};
struct DateTime date_time;


void initDateTime(int YYYY, int MM, int DD, int hh, int mm)
{
    struct tm now;
    now.tm_year = YYYY-1900;     /* 2001年 */
    now.tm_mon  = MM-1;       /* 5月    */
    now.tm_mday = DD;      /* 20日   */
    now.tm_wday = 0;       /* 日曜日 */
    now.tm_hour = hh;      /* 14時   */
    now.tm_min  = mm;      /* 20分   */
    now.tm_sec  = 0;       /* 00秒   */
    now.tm_isdst= -1;      /* 夏時間無効 */
    
    date_time.base_time = mktime(&now);
    if (date_time.base_time == (time_t) - 1) { //error
      date_time.base_time = 0;
    }
    date_time.BaseMillis = millis();
    date_time.base_startup_keika_sec = date_time.BaseMillis / 1000;
}

struct HHMM{
  int HH;
  int MM;
  int YOUBI;
};
struct HHMM hhmm;

unsigned long getSec()
{
  //現在時刻累積秒を求める
  unsigned long now_startup_keika_sec = millis() / 1000;
  return date_time.base_time + ((time_t)now_startup_keika_sec - (time_t)date_time.base_startup_keika_sec);
}

void getDateTime(struct HHMM *hhmm)
{
  //現在時刻累積秒を求める
  unsigned long now_startup_keika_sec = millis() / 1000;
  time_t now_time = date_time.base_time + ((time_t)now_startup_keika_sec - (time_t)date_time.base_startup_keika_sec);
  
  //localtimeにする
  //now_time = now_time + 60 * 60 * 9;

  struct tm   *time_inf;
  time_inf = localtime(&now_time);

  hhmm->HH = time_inf->tm_hour;
  hhmm->MM = time_inf->tm_min;
  hhmm->YOUBI = time_inf->tm_wday;
}

void getDateTimeStr(char *buf1, char *buf2)
{
  //現在時刻累積秒を求める
  unsigned long now_startup_keika_sec = millis() / 1000;
  time_t now_time = date_time.base_time + ((time_t)now_startup_keika_sec - (time_t)date_time.base_startup_keika_sec);
  
  //localtimeにする
  //now_time = now_time + 60 * 60 * 9;

  struct tm   *time_inf;
  time_inf = localtime(&now_time);

  int year = time_inf->tm_year + 1900;
  int month = time_inf->tm_mon + 1;
  int day = time_inf->tm_mday;
  int hour = time_inf->tm_hour;
  int minute = time_inf->tm_min;
  int second = time_inf->tm_sec;
  char *wday[] = {"SUN","MON","TUE","WED","TUE","FRI","SAT"};
  sprintf(buf1, "%04d/%02d/%02d %s", year, month, day, wday[time_inf->tm_wday]);
  sprintf(buf2, "%02d:%02d:%02d", hour, minute, second);
}

void tick_time()
{
  unsigned long now = millis();
  if(now < date_time.BaseMillis){
    date_time.base_startup_keika_sec  = ((4294967295 - date_time.BaseMillis) + now)/1000;
    date_time.BaseMillis = now;
  }
}

struct ALARM
{
  int mode;   //  0=week, 1=alway on, 2=alway off, 3=after on, 4=after off
  int SUN;
  int MON;
  int TUE;
  int WED;
  int THU;
  int FRI;
  int SAT;
  int start_hh;
  int start_mm;
  int end_hh;
  int end_mm;
  int after_on_hh;
  int after_off_hh;
  unsigned long setSec;
};
struct ALARM alarm;

int flag = 0;
unsigned long nCount = 0;
void loop()
{
  nCount++;

  //lcd.noCursor();

  tick_time();
  tick_button(&bt_select);
  tick_button(&bt_enter);

  //LCD back light
  if(lcd_back_light==1){
    digitalWrite(13, HIGH);
  }
  else{
    digitalWrite(13, LOW);
  }
  
  //DEBUG
  //int kakeru = 60;    //DEBUG
  int kakeru = 3600;  //本番向け
  //
  unsigned long nowSec = getSec();
  //表示
  if(alarm.mode==0 || alarm.mode==1 || alarm.mode==2){  //week alway
    char date[20];
    char time[20];
    getDateTimeStr(date, time);
    lcd_print(0, 0, date);
    lcd_print(0, 1, time);
    if(alarm.mode==0){
      lcd_print(8, 1, " weekly  ");
    }
    if(alarm.mode==1){
      lcd_print(8, 1, " all ON  ");
    }
    if(alarm.mode==2){
      lcd_print(8, 1, " all OFF ");
    }
  }else
  if(alarm.mode==3 || alarm.mode==4){
    int sec;
    if(alarm.mode==3){
      sec = alarm.setSec+(alarm.after_on_hh * kakeru) - nowSec;
    }
    else{
      sec = alarm.setSec+(alarm.after_off_hh * kakeru) - nowSec;
    }
//    Serial.print("sec=");
//    Serial.println(sec);
    if(sec<0){
      sec=0;
    }
    int hh = sec / 3600;
    int mm = (sec - (hh * 3600))/60;
    int ss = sec % 60;
    char buf[17];
    sprintf(buf, "%02d:%02d:%02d", hh, mm, ss);
    lcd_print(0, 0, buf);
    if(alarm.mode==3){
      lcd_print(8, 1, " afterON ");
    }
    if(alarm.mode==4){
      lcd_print(8, 1, "afterOFF");
    }
  }
  

  lcd.setCursor(1, 0);

  //現在時刻取得
  struct HHMM hhmm;
  getDateTime(&hhmm);
  //日付跨ぎの補正
  int alarmStartHHMM = (alarm.start_hh * 100 + alarm.start_mm)  ;
  int alarmEndHHMM   = (alarm.end_hh * 100 + alarm.end_mm);
  int nowHHMM = (hhmm.HH * 100 + hhmm.MM);
  if(alarmStartHHMM>alarmEndHHMM){
    alarmEndHHMM = alarmEndHHMM + 2400;
  }
  if(alarmStartHHMM>nowHHMM){
    nowHHMM = nowHHMM + 2400;
  }
  //
  if(alarm.mode==0){  //week
    //
    if((hhmm.YOUBI==0 && alarm.SUN==1)
     ||(hhmm.YOUBI==1 && alarm.MON==1)
     ||(hhmm.YOUBI==2 && alarm.TUE==1)
     ||(hhmm.YOUBI==3 && alarm.WED==1)
     ||(hhmm.YOUBI==4 && alarm.THU==1)
     ||(hhmm.YOUBI==5 && alarm.FRI==1)
     ||(hhmm.YOUBI==6 && alarm.SAT==1)){
  /*    
   *   if( (alarm.start_hh * 100 + alarm.start_mm) <= (hhmm.HH * 100 + hhmm.MM) 
                                                  && (hhmm.HH * 100 + hhmm.MM) < (alarm.end_hh * 100 + alarm.end_mm)){
  */
      if( alarmStartHHMM <= nowHHMM && nowHHMM < alarmEndHHMM){
        digitalWrite(8, HIGH);
      }
      else{
        digitalWrite(8, LOW);
      }
    }
    else{
      digitalWrite(8, LOW);
    }
  }else
  if(alarm.mode==1){    //alway on
    digitalWrite(8, HIGH);
  }else
  if(alarm.mode==2){    //alway off
    digitalWrite(8, LOW);
  }else
  if(alarm.mode==3){    //after on
    if(alarm.setSec+(alarm.after_on_hh * kakeru) >= nowSec){
      digitalWrite(8, LOW);
    }
    else{
      digitalWrite(8, HIGH);
    }
  }else
  if(alarm.mode==4){    //after off
    if(alarm.setSec+(alarm.after_off_hh * kakeru) <= nowSec){
      digitalWrite(8, LOW);
    }
    else{
      digitalWrite(8, HIGH);
    }
  }
  
  
  //
  int status;
  if(get_button(&status, &bt_select)){
    if(status==HIGH){
      //alarm_input();
      if(lcd_back_light==0){
        lcd_back_light = 1;
      }
      else{
        lcd_back_light = 0;
      }
    }
  }
  if(get_button(&status, &bt_enter)){
    if(status==HIGH){
      alarm_input();
    }
  }
  delay(10);
}

void alarm_initialise()
{
  memset(&alarm, NULL, sizeof(struct ALARM));
  alarm.SUN = 1;
  alarm.MON = 1;
  alarm.TUE = 1;
  alarm.WED = 1;
  alarm.THU = 1;
  alarm.FRI = 1;
  alarm.SAT = 1;
  alarm.after_on_hh = 1;
  alarm.after_off_hh = 1;
}

void alarm_input()
{
  //lcd back light
  digitalWrite(13, HIGH);
  //
  alarm.mode = select_input5("mode :", "weekly    ", "always on ", "always off", "after on  ", "after off ", alarm.mode);
  if(alarm.mode==0){
    //weekly
    alarm.SUN = select_input("[SUN] : ", "  ", "ON", alarm.SUN);
    alarm.MON = select_input("[MON] : ", "  ", "ON", alarm.MON);
    alarm.TUE = select_input("[TUE] : ", "  ", "ON", alarm.TUE);
    alarm.WED = select_input("[WED] : ", "  ", "ON", alarm.WED);
    alarm.THU = select_input("[THU] : ", "  ", "ON", alarm.THU);
    alarm.FRI = select_input("[FRI] : ", "  ", "ON", alarm.FRI);
    alarm.SAT = select_input("[SAT] : ", "  ", "ON", alarm.SAT);
  
    alarm.start_hh = input("start hour",   2, alarm.start_hh, 0, 23);
    alarm.start_mm = input("start minits", 2, alarm.start_mm, 0, 59);
    alarm.end_hh   = input("end hour",     2, alarm.end_hh,   0, 23);
    alarm.end_mm   = input("end minits",   2, alarm.end_mm,   0, 59);
  }
  if(alarm.mode==3){
    //after on
    alarm.after_on_hh = input("after hour",   1, alarm.after_on_hh, 1, 9);
    alarm.setSec = getSec();
    Serial.print("alarm.setSec=");
    Serial.println(alarm.setSec);
  }
  if(alarm.mode==4){
    //after off
    alarm.after_off_hh = input("after hour",   1, alarm.after_off_hh, 1, 9);
    alarm.setSec = getSec();
  }

  lcd.clear();

  lcd_back_light = 1;
}

int select_input(char *str, char *select1, char *select2, int def)
{
  lcd.clear();
  lcd.print(str);

  int  value = def;
  char buf[17];
  char select[17];
  //
  lcd.blink();
  //
  strcpy(buf, "");
  strcpy(select, select1);

  int flag = 1;
  int status;
  while(1){
    tick_button(&bt_select);
    tick_button(&bt_enter);

    if(flag==1){
      if(value==0){
        sprintf(buf, "%s", select1);
      }
      else{
        sprintf(buf, "%s", select2);
      }
      lcd.setCursor(0, 1);
      lcd.print(buf);
      lcd.setCursor(strlen(buf)-1, 1);
      flag = 0;
    }
    if(get_button(&status, &bt_enter)){
      if(status==HIGH){
        return value;
      }
    }
    if(get_button(&status, &bt_select)){
      if(status==HIGH){
        flag = 1;
        if(value==0){
          value = 1;
        }
        else{
          value = 0;
        }
      }
    }
  }
  return 0;
}

int select_input5(char *str, char *select1, char *select2, char *select3, char *select4, char *select5, int def)
{
  lcd.clear();
  lcd.print(str);

  int  value = def;
  char buf[17];
  char select[17];
  //
  lcd.blink();
  //
  int flag = 1;
  int status;
  while(1){
    tick_button(&bt_select);
    tick_button(&bt_enter);

    if(flag==1){
      if(value==0){
        sprintf(buf, "%s", select1);
      }else
      if(value==1){
        sprintf(buf, "%s", select2);
      }else
      if(value==2){
        sprintf(buf, "%s", select3);
      }else
      if(value==3){
        sprintf(buf, "%s", select4);
      }
      else{
        sprintf(buf, "%s", select5);
      }
      lcd.setCursor(0, 1);
      lcd.print(buf);
      lcd.setCursor(strlen(buf)-1, 1);
      flag = 0;
    }
    if(get_button(&status, &bt_enter)){
      if(status==HIGH){
        return value;
      }
    }
    if(get_button(&status, &bt_select)){
      if(status==HIGH){
        flag = 1;
        if(value==0){
          value = 1;
        }else
        if(value==1){
          value = 2;
        }else
        if(value==2){
          value = 3;
        }else
        if(value==3){
          value = 4;
        }
        else{
          value = 0;
        }
      }
    }
  }
  return 0;
}