ジェネリック定刻起床装置2号機

1号機はこれ
pyn-pyn.hatenablog.jp
途中で正気に戻ってしまって諦めた


こっちは正気に戻ったあとに作ったやつ


注意書き
AC100Vを直接触る回路なのでマネして感電したり燃えたりブレーカーと保存してないデータをまとめてふっとばしたりしても知らないよ.



素材(1号機の記事からほぼコピペ)

・送風機
www.amazon.co.jp
12V(ACアダプタorシガソケ)の送風機も買ったけど,12Vだとぱわみがなくて体を押し上げられなかったから100Vのやつじゃないとだめらしい

・散水ホース
https://www.amazon.co.jp/gp/product/B00CTGTN92/

・厚手のゴミ袋かメロブのレジ袋
 今使ってるのは45L,厚さ70umって書いてあった

・Aruduino nano互換機
 8MHz品が余ってたのでこれを使った.動作クロックが16MHzだとプログラム的にはUNOと動作コンパチだと思う.

miniUSBケーブル
http://www.yodobashi.com/ec/product/100000001001230585/index.html
Arduino nano用
電気食うもんでもないし細いのでよし

・ACアダプタ
https://www.yodobashi.com/product/100000001003173268/
Arduino nano用
電気食うもんでもないし(ry

・目覚まし時計
Amazon.co.jp: CASIO(カシオ) 目覚まし時計 大音量 ブラック アナログ スリープバスター ベル音アラーム TQ-647-1JF: ホーム&キッチン
1号機でドツボったので既製品つっこんだ
はじめ百均の目覚まし時計使ってたらえげつなく時間ずれたのでおとなしくちゃんとしたやつ買ったほうがいい
ベル式で単三電池2本で動くやつがおすすめ(理由はこのあとで)
ところでこの目覚まし時計,ベルが2つあるようで実際に動いてるのは片方だけだった

・3Vリレー
3V小型リレー 接点容量:2A 946H−1C−3D: パーツ一般 秋月電子通商-電子部品・ネット通販
さっきベル式で単三電池2本って書いた理由はこれ
ベル殴るモーターの配線をそのままリレーに繋げば,リレーの接点を使って深く考えずに使えるのでおすすめ
目覚まし時計内部の回路を観察してえいやっとつなげなくもないけど過電流過電圧逆接その他etcで目覚まし時計壊すかもしれないので
(目覚まし時計だけでえいやっと組み込んだので実はまだ使ってない)

・いい感じのリレーモジュール (リレードライバ内蔵してるからつないでTTLやらCMOSレベルで叩けば動くので便利)
https://www.amazon.co.jp/gp/product/B088FLWF81/

・百均のすべりどめマット
f:id:PYN_pyn:20200922193351j:plain
こんなかんじにごみ袋に添えて使う
ないと膨らんだときに滑ってゴミ袋がお布団から逃げる

あとヒューズとかヒューズホルダとかスイッチとかボリュームとか細かいの
https://www.yodobashi.com/product/100000001003562421/
https://www.yodobashi.com/product/100000001000618980/
プラスチックケース SY−110G: 電子工作便利商品 秋月電子通商-電子部品・ネット通販
ガラス管ヒューズ MF61NR 250V2A 32mm: パーツ一般 秋月電子通商-電子部品・ネット通販

ヒューズホルダとかヒューズはヨドバシとかで買ったほうが早いし安全だと思う
ガバガバ製品買って燃えてもどうかと思うので


中身
f:id:PYN_pyn:20200922193439j:plain
100Vラインに接するリレーはタッパーに閉じ込めてarduinoとか目覚まし時計の信号線に混触しないようにしたつもり.


ざっくりとした構成
f:id:PYN_pyn:20200922203539j:plain




f:id:PYN_pyn:20200922193310j:plain
リセットピンにつけたプルダウン抵抗を外すシャンパ(普段はショートさせてる)
構成図でも書いたとおり,10kΩで内部プルアップされているarduinoを1kΩでプルダウンしてLOにすることで,常時リセットボタンを押しているのに近い状態にしている.
これを目覚まし時計の接点でHIに持ち上げることで,リセットが解除されて送風機が動くというしくみ.
ところでarduinoはスケッチ書き込み時にリセットが必要というかリセットピンを使うんですが,それを失念してなんか書き換えられんなぁとやってたら.
常時リセット押しっぱなしじゃ書き込めないわとジャンパを追加した次第.


スケッチ
 送風機でふくらませるだけなら目覚まし時計とリレーだけで良かったんですが,送風機には「15分以上運転するな」という刻印が入ってる.
ということで,過熱対策として指定時刻になったら1分送風4分停止(冷却)を繰り返し,指定時刻から30分経ったら停止をやるやつ.
2号機は時限機能を目覚まし時計に丸投げしたので,setupにべちゃっと書いておしまい.
目覚まし時計でリセットピンを操作されてリセットが解除されるとsetupが一周だけ回るので30分経ったらとまる.
パイロットランプつけなかったので冷却時間か指定時刻じゃないのかわかりにくいので冷却中のちょっとみょんみょんさせてる.

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);

  for (int i = 0; i < 2; i = i + 1) {
    digitalWrite(13, HIGH);
    delay(100);
    digitalWrite(13, LOW);
    delay(200);
  }

  for (int i = 0; i < 5; i = i + 1) {
    for (int i = 0; i < 7; i = i + 1) {
      digitalWrite(13, HIGH);
      delay(9000);
      digitalWrite(13, LOW);
      delay(1000);
    }
    for (int i = 0; i < 24; i = i + 1) {
      digitalWrite(13, HIGH);
      delay(1000);
      digitalWrite(13, LOW);
      delay(9000);
    }
  }

  digitalWrite(13, HIGH);
  delay(100);
  digitalWrite(13, LOW);

  digitalWrite(13, LOW);
}

void loop() {

}

ジャンル限らず自作って正気を失ったオタクしかしないらしいし正気を取り戻した版って言い張っていいんかこれ

ジェネリック定刻起床装置(供養)

JR東日本が使ってる定刻起床装置というものがある
www.jreastmall.com
ジェネリック定刻起床装置をつくって,実際動かせるところまで行ったものの結局放棄した.

以下,それの備忘録あるいは墓標もしくは産廃置場



注意その1
AC100Vを直接触る回路なのでマネして感電したり燃えたりブレーカーと保存してないデータをまとめてふっとばしたりしても知らないよ.
(一応AC100Vラインを直接触らない方法も紹介だけします)
途中で匙投げたやつだから本当にまともに動くかもよくわかんないよ.



素材

・送風機
www.amazon.co.jp
12V(ACアダプタorシガソケ)の送風機も買ったけど,12Vだとぱわみがなくて体を押し上げられなかったから100Vのやつじゃないとだめらしい

・散水ホース
www.amazon.co.jp

・厚手のゴミ袋かメロブのレジ袋

・AruduinoUNOまたは互換機(UNO以外は未検証なだけ)
 自作キーボード界隈の住人だとUNOよりもPro Microとかを持ってる人が多いのだろうか

・DS3231 (RTCをやってくれる子)
https://www.amazon.co.jp/gp/product/B081GXLB4H/

・HD44780+PCF8574A (I2Cでつながる液晶)
https://www.amazon.co.jp/gp/product/B07WNQPZHV/

・いい感じのリレーモジュール (リレードライバ内蔵してるからつないでTTLやらCMOSレベルで叩けば動くので便利)
https://www.amazon.co.jp/gp/product/B088FLWF81/

あとヒューズとかヒューズホルダとかスイッチとかボリュームとか細かいの
ヒューズホルダとかヒューズはヨドバシとかで買ったほうが早いし安全だと思う
ガバガバ製品買って燃えてもどうかと思うので

100Vライン直接触るのは怖いって場合はこういうのもある.
自分でリレーを調理するより2桁か3桁値が張るけど.
www.amazon.co.jp
生えてるUSB端子に5Vかければ,タップに刺さってる機器に通電してくれるという仕掛け.
非連動コンセントもついてるので,タイマー用とか制御機器用の電源はそっちにつないでおけばOK.



以前作成した光目覚まし時計はそんな感じだった.
気が向けば個別で記事を書くかもしれない.
f:id:PYN_pyn:20200814055738j:plain
動画



注意その2
液晶は裏面のコントラスト調整用ボリュームをぐりぐりやって調整しないと何も映らなかったりするよ
DS3231に1次電池を使う場合はパターンカットすること
I2Cライブラリは"LiquidCrystal I2C by Frank de Brabander"を使用すること
液晶バックライトLEDのジャンパピンは5V側についているので,analogWrite等で調光したい場合は注意



やる気のない概略図
f:id:PYN_pyn:20200814054543j:plain
送風機をタイマーでONしてお布団ごと体を持ち上げて起こすだけ.
......なのだが,送風機には「15分以上運転するな」という刻印が入ってる.
ということで,過熱対策として
指定時刻になったら1分送風4分停止(冷却)を繰り返し,指定時刻から30分経ったら停止,
としようとこれを作り始めた.
が,おそらく電気系の処置が雑魚だったのか数日でRTCの電池が切れることが発覚.
「目覚まし時計を改造して過熱対策だけarduinoでえいやしたほうがはやいのでは?」
とだいぶ正気に戻ってきてたので放棄した.(今作ってるどころだが実際はやい)



遺影
f:id:PYN_pyn:20200814051449j:plain
f:id:PYN_pyn:20200814051524j:plain
f:id:PYN_pyn:20200814053945j:plain
これをオフトゥンの下に敷いてふくらませる



最後に使ったスケッチ

/*
  ardunioIDE ver1.8.14
*/

//RTC
#include <DS3232RTC.h>
tmElements_t tm;

//LCD
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2); // I2C: 0x3F, 16x2 LCD

void LCDsetup()  {
  lcd.init();
  lcd.backlight();// Switch on the backlight
}

String DdS(int in) { //ゼロ埋めして1~9を01~09にする
  String out;
  if (in < 10) {
    out = "0" + String(in);
  } else {
    out = String(in);
  }
  return out;
}

int CrTime[] = {1970, 1, 1, 0, 0, 0}; //年,月,日,時,分,秒

void tm2Cr() {
  CrTime[0] = tm.Year + 1970;
  CrTime[1] = tm.Month;
  CrTime[2] = tm.Day;
  CrTime[3] = tm.Hour;
  CrTime[4] = tm.Minute;
  CrTime[5] = tm.Second;
}
String CrTimeText;
String CrDataText;
void Cr2tx() {
  CrDataText = DdS(CrTime[0]) + "-" + DdS(CrTime[1]) + "-" + DdS(CrTime[2]);
  CrTimeText = DdS(CrTime[3]) + DdS(CrTime[4]) + " " + DdS(CrTime[5]);
}

void Cr2Write() {
  //setTime(1, 2, 3, 4, 5, 2018);//時、分、秒、日、月、年の順で入力
  //RTC.set(now());//書き込み
  setTime(CrTime[3], CrTime[4], CrTime[5], CrTime[2], CrTime[1], CrTime[0]);
  RTC.set(now());
}

void Cr2Serial()
{
  Serial.print(CrDataText + " " + CrTimeText);
}

void Cr2LCD() {
  //lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(CrDataText);
  lcd.setCursor(0, 1);
  lcd.print(CrTimeText);
}

int Sts = 0;
void Sts2LCD() {
  String s;
  switch (Sts) {
    case 0: s = "ALoff" ;
      break;
    case 1: s = "ALon " ;
      break;
    case 2: s = "ALAM!" ;
      break;
    case 3: s = "AL&Co" ;
      break;
    case 99: s = "TmOut" ;
      break;
    case 10: s = "A-SET" ;
      break;
    case 20: s = "T-SET" ;
      break;
  }
  lcd.setCursor(11, 0);
  lcd.print(s);
}


//ピンの割当
//入力
int TsetPIN = 10;
int AsetPIN = 11;
int AonPIN = 12;
int UbtnPIN = 5;
int DbtnPIN = 6;
int LbtnPIN = 7;
int RbtnPIN = 8;
int ARAMsetVolPIN = A0;
int backLEDsetPIN = A1;
//出力
int RelayOut1PIN = 4;
int backLEDPIN = 9;
//入出力状態の初期化
//スイッチはアクティブLO
int Tset = HIGH;
int Aset = HIGH;
int Aon = HIGH;
int Ubtn = HIGH;
int Dbtn = HIGH;
int Lbtn = HIGH;
int Rbtn = HIGH;
//リレーはアクティブHI
int RelayOut1 = LOW;
//ボリュームでアラーム時刻設定 5V(1013)で23時59分,0V(0)で00時00分
int ARAMsetVol = 0;
//バックライトは5V(255)で点灯,0V(0)で消灯
int backLED = 10;
//pinModeを設定(setup()内から呼ぶ)
void pinSetup() {
  pinMode(TsetPIN, INPUT_PULLUP);
  pinMode(AsetPIN, INPUT_PULLUP);
  pinMode(AonPIN, INPUT_PULLUP);
  pinMode(UbtnPIN, INPUT_PULLUP);
  pinMode(DbtnPIN, INPUT_PULLUP);
  pinMode(LbtnPIN, INPUT_PULLUP);
  pinMode(RbtnPIN, INPUT_PULLUP);
  pinMode(ARAMsetVolPIN, INPUT);
  pinMode(backLEDsetPIN, INPUT_PULLUP);

  pinMode(RelayOut1PIN, OUTPUT);
  pinMode(backLEDPIN, OUTPUT);
}
//ピンをまとめて読み書き
void pinRW() {
  Tset = digitalRead(TsetPIN);
  Aset = digitalRead(AsetPIN);
  Aon = digitalRead(AonPIN);
  Ubtn = digitalRead(UbtnPIN);
  Dbtn = digitalRead(DbtnPIN);
  Lbtn = digitalRead(LbtnPIN);
  Rbtn = digitalRead(RbtnPIN);
  ARAMsetVol = analogRead(ARAMsetVolPIN);
  backLED = analogRead(backLEDsetPIN);

  digitalWrite(RelayOut1PIN, RelayOut1);
  analogWrite(backLEDPIN, backLED/4);//ADとDAのフルスケールをあわせるために4で割るっっっっg
}

int Pos = 0; //カーソル位置的ななにか
//ボタンで値を操作するやつ
int key2num(int val, int MIN, int MAX, int PIT, int CU, int CD) { //操作対象の元の値,最小値,最大値,ピッチ,上げるボタン,下げるボタン
  if (CU == LOW) {
    val = val + PIT;
    if (val > MAX) {
      val = MIN;
    }
  } else if (CD == LOW) {
    val = val - PIT;
    if (val < MIN) {
      val = MAX;
    }
  }
  return val;
}

void btnSerial() {
  Serial.print(String(Tset) + String(Aset) + String(Aon) + String(Ubtn) + String(Dbtn) + String(Lbtn) + String(Rbtn));
  Serial.print(" ");
}

int ArTime[] = {0, 0}; //時,分
void Vol2Aram() {
  //ADした値の前処理
  int ASVivr = 5; //設定する時刻の最小単位[分]
  int ASVmax = 24 * 60 / ASVivr; //24時間をASVintで刻んだときのFS
  long ASV = ARAMsetVol;
  //ボリューム両端に不感帯を設置
  int ASVcut = 10;
  if (ASV > 1013 - ASVcut) {
    ASV = 1013 - ASVcut;
  }
  if (ASV < ASVcut) {
    ASV = ASVcut;
  }
  ASV = (ASV - ASVcut) * (ASVmax - 1) / (1013 - ASVcut * 2); //フルスケール=ASVmax-1に変換 24時は設定する意味がないので-1

  long MMMM = ASV * 24 * 60 / ASVmax; //分単位に変換
  ArTime[0] = MMMM / 60; //時を取り出し
  ArTime[1] = MMMM - ArTime[0] * 60; //分を取り出し
}
String ArTimeText;
void Ar2tx() {
  ArTimeText = DdS(ArTime[0]) + DdS(ArTime[1]);
}

void Ar2LCD() {
  lcd.setCursor(12, 1);
  lcd.print(ArTimeText);
}

void Aram2Serial() {
  Serial.print(" " + ArTimeText + " ");
}

int AramTimeOut = 1; //一定上アラームONで放置されたとき用 0:通常(アラーム許可) 1:タイムアウト(アラーム禁止)10:アラーム発動中(アラーム許可)

void ARAM() {
  if (AramTimeOut == 1) { //AramTimeOut == 1のときは何もしない
  } else {
    RelayOut1 = HIGH;
    Sts = 2;//表示をアラーム発動中に
  }
}


void ARAMOFF() {
  RelayOut1 = LOW;
}

void setup() {
  RTC.read(tm);
  tm2Cr();
  pinSetup();
  pinRW();
  Serial.begin(9600);
  LCDsetup();
}

void loop() {
  //アラーム
  if (AramTimeOut == 1) { //タイムアウト状態だったらアラームをOFFに
    ARAMOFF();
  }
  if (Aon == HIGH) { //アラームスイッチをOFF(=HIGH)のときにOFFになるように保険(ここはなくても動くはず,下で書き忘れたとき対策)
    ARAMOFF();
  }

  Vol2Aram();//ボリュームから読み取った設定値を取得
  if (Aon == LOW) {
    int CrMMMM = CrTime[3] * 60 + CrTime[4]; //時分を分に変換
    int ArMMMM = ArTime[0] * 60 + ArTime[1];
    Sts = 1;//表示をアラームONに(下で表示の指定があったらそっちが優先)
    if (AramTimeOut == 0) {//タイムアウト状態が解除されてるかチェック
      if (CrMMMM == ArMMMM) { //設定時刻になったらリレ-をオン(SET命令のごとくLOWの命令が来るまでリレーがOFFにならないことに注意)
        ARAM();
        AramTimeOut = 10;//アラーム発動中に切り替え
      }
    }
    if (AramTimeOut == 10) {
      int AET = CrMMMM - ArMMMM; //アラーム発動からの経過時間[分]
      if (AET < 0) { //日付超え時の処理
        AET = AET + 24 * 60;
      }
      int AET5 = AET - AET / 5 * 5; //5分で1周するように整形
      if (AET5 < 0) { //なんかマイナスになるタイミングがあるけどめんどくさいのでこう
        AET5 = AET5 + 10;
      }
      if (1 <= AET5) {//1分経ったら
        ARAMOFF();
        Sts = 3;//表示を冷却中に
      } else if (AET5 == 0) {//5分経ったら
        ARAM();//再開
      }
      if (AET > 29) { //アラーム開始後29分を超えたら
        AramTimeOut = 1; //タイムアウト(アラーム禁止)
      }
    }
    if (AramTimeOut == 1) {
      Sts = 99; //表示をタイムアウトに
    }
  }

  if (Aon == HIGH) {
    ARAMOFF();
    Sts = 0; //表示をアラームOFFに
    AramTimeOut = 0; //タイムアウト状態を解除
  }


  //設定用
  if (Tset == LOW) {//時刻合わせモード
    Sts = 20; //液晶に時刻設定モードと表示(最後に出てくるStsなので上でアラーム関係をいじっててもここで上書きされる)

    //左右キーで操作対象を変更
    Pos = key2num(Pos, 0, 5, 1, Rbtn, Lbtn);
    //操作と操作対象の点滅(再描写を前提に一時的に文字を消す)
    switch (Pos) {
      case 0: //時
        CrTime[3] = key2num(CrTime[3], 0, 23, 1, Ubtn, Dbtn);
        lcd.setCursor(0, 1);
        lcd.print("  ");
        break;
      case 1: //分
        CrTime[4] = key2num(CrTime[4], 0, 59, 1, Ubtn, Dbtn);
        lcd.setCursor(2, 1);
        lcd.print("  ");
        break;
      case 2: //秒
        CrTime[5] = key2num(CrTime[5], 0, 59, 1, Ubtn, Dbtn);
        lcd.setCursor(5, 1);
        lcd.print("  ");
        break;
      case 3: //年
        CrTime[0] = key2num(CrTime[0], 2000, 2100, 1, Ubtn, Dbtn);
        lcd.setCursor(0, 0);
        lcd.print("    ");
        break;
      case 4: //月
        CrTime[1] = key2num(CrTime[1], 0, 12, 1, Ubtn, Dbtn);
        lcd.setCursor(5, 0);
        lcd.print("  ");
        break;
      case 5: //日
        CrTime[2] = key2num(CrTime[2], 0, 31, 1, Ubtn, Dbtn);
        lcd.setCursor(8, 0);
        lcd.print("  ");
        break;
    }

    Cr2Write();
    delay(50);
  } else if (Aset == LOW) {//アラーム設定モード(使ってないよ!)
    Sts = 10;

  } else {
    //どの設定モードでないとき
    Pos = 0; //どちらかというと設定モードに入ったときにカーソルが先頭に来るように
    RTC.read(tm);
    tm2Cr();
  }
  pinRW();//ピンの読み書き
  Cr2tx();//Crの配列をYYYY-MM-DDとHHMM SSに変換(液晶表示用)
  Ar2tx();//

  //シリアルでPCに送信(デバック用)
  Cr2Serial();
  Aram2Serial();
  btnSerial();
  Serial.println();

  //LCDに表示
  Cr2LCD();
  Ar2LCD();
  Sts2LCD();

  delay(100);
}

参考
https://moyoi-memo.hatenablog.com/entry/2018/10/15/205546
ArduinoArduinoでRTC(DS3231)を使ってみた - 高専ガチ勢のメモ書き
https://ht-deko.com/arduino/lcd_i2c.html
[I2C] 1602 LCD ディスプレイ (HD44780 + PCF8574A)