みなさん、今晩は、トドお父さんです。
本日は、今回 電子工作してDIYで作ったこたつタイマーのソフトについて説明します。
こないだまで、ArduinoのLEDをチカチカさせて喜んでいるレベルでしたので
期待しないで下さいねー。 (半分は自分備忘録です、スミマせんね)
// 【こたつタイマー 6分単位 Arduinoプログラム】
const int cathode_pins[] = {1,2,3,4,5,0,6}; // カソードに接続
const int buttonPin = 7; // the number of the pushbutton pin
const int LED1 = 13 ; // LED blink 1sec duration
const int speker = 8 ; //speker on pin 8
const int digit1 = 9; // Display digit 1
const int digit2 = 10; // Display digit 2
const int RELAY_ON = 12; // AC Relay Control
⇒ ここまでは、Arduinoの入出力ピンの設定です。ArduinoのCPUは28ピンのIC なので、入出力ピンはそんなにたくさん持っていません。 デジタル入出力用 0-13P までの14ピン、アナログ入力専用 A0-A5 6ピン
ピンは3、5、6、9-11Pまでの6ピンと、色々と制限があります。 (ここでいうピン数は、実際の28Pのピンとは違うのでちゃんと確認してくださいね)
今回は、LEDのカソード(-側)に接くピンが、[0,1,2,3,4,5,6] の7個です。
これは標準のArduino基板です
終了時のブザーにつなぐのが8P、こたつON/OFFのリレー制御につなぐのが12P 1秒ごとに点滅するのは、現在と同じ13Pです。 スイッチ入力の7Pだけが入力につかっているピンになります。 これ以降は、変数の設定。
これ以降の動作に必要な定数・変数を宣言します。
//int ledState = HIGH; // the current state of the output pin int k=0 ; // Buzzer counter int Count_up =0 ; // Count up state int buttonState; // the current reading from the input pin int lastButtonState = LOW; // the previous reading from the input pin const int number_of_cathode_pins = sizeof(cathode_pins) / sizeof(cathode_pins[0]);// 配列の数 int start_num=2 ; // Number to countdown from 20min=20*600sec initial 2HOURS int start_num1 = start_num; // the following variables are long's because the time, measured in miliseconds, // will quickly become a bigger number than can be stored in an int. long lastDebounceTime = 0; // the last time the output pin was toggled long debounceDelay = 50; // the debounce time; increase if the output flickers unsigned long time;次のSetupはコメントにあるように、電源ONでリセットがかかって起動時に1回だけ通るプログラムです。
入力ピンをINPUTに、出力ピンをINPUTに設定し、リレーをONします。
// setup() は,最初に一度だけ実行される
void setup() { for (int i = 0; i < number_of_cathode_pins; i++) { pinMode(cathode_pins[i], OUTPUT); // cathode_pinsを出力モードに設定する } pinMode(digit1, OUTPUT); pinMode(digit2, OUTPUT); pinMode(speker, OUTPUT); // speker on pin 8 pinMode(RELAY_ON, OUTPUT); pinMode(LED1, OUTPUT); pinMode(buttonPin,INPUT); // Button Input // set initial AC Relay State digitalWrite(RELAY_ON,HIGH); }
つぎに説明する loop() { } からが プログラム本体です。
カッコの中が順番に実行され、終わりまで行くと最初に戻って何度も実行します。だから loop() なんですね。
まず、最初の部分はSWの入力をチェックする部分です。
押しボタンSWが押されたら、カウンターを +5します。(x6分= +30分)
ハード的に、ONすると、HIGHレベルに、OFF(通常)はLOWレベルになるように、
SWの上接点を+5Vに、下接点をCPUの7ピンにつなぎます。7ピンは10kΩでプルダウンします。
SWを押した瞬間、チャッタリングといって多重にONを検出しないように、ある間隔
で2重読みして、同じ レベルなら、押された(あるいはボタンが押されなくなった)て検出します。
これを英語の世界では Button Debounce って呼んでいるようです。
Arduino本家から、このデバウンスのプログラムを移植します。
色々と先人の力作を持ってこれるところが、オープンソースのだいご味ですね。
http://arduino.cc/en/Tutorial/Debounce
時間待ちには、Millis() という関数を使います。 前に参照のHPではDelay()を使ってました。
Delay()は、その待ち時間の間、プログラムが止まってしまいますが、Millis()はCPU
の内部タイマーをmS単位で持ってくるだけです。
Delay()を使うと、LEDの表示が止まってしまいます。
Millis()では、時間を渡した後にループが高速に続きますから、全く問題ありません。
void loop() { // read the state of the switch into a local variable: int reading = digitalRead(buttonPin); // If the switch changed, due to noise or pressing: if (reading != lastButtonState) { // reset the debouncing timer lastDebounceTime = millis(); } if ((millis() - lastDebounceTime) > debounceDelay) { // whatever the reading is at, it's been there for longer // than the debounce delay, so take it as the actual current state: // if the button state has changed: if (reading != buttonState) { buttonState = reading; // only increment ciunrter if the new button state is HIGH if (buttonState == HIGH) {// ledState = !ledState;// If button is pressed, increase timer number if (Count_up == 0 ) { start_num = start_num + 5 ;// If button is pushed add 30min // if ((start_num - (millis()/1000))> 50) { // start_num = 50 + (millis()/1000) ;// 360sec =6min, Upper Limit = 50 * 6min = 300min(5 Hourrs) if (start_num - (millis()/360000) > 50 ) { start_num = 50 + (millis()/360000) ; } }// If state is count up, restart from initial. else { Count_up = 0; k = 0; // Buzzer is Initialized digitalWrite(RELAY_ON,HIGH); // Power_Relay is ON // start_num = 10 + (millis()/1000); start_num = 10 + (millis()/360000); } } } } // save the reading. Next time through the loop, // it'll be the lastButtonState: lastButtonState = reading;
2行目まではLoop文ですね。
それから先は、まったくの本家サイトよりのチャタリング防止の移植です。
4行目、readingって変数に、ボタンの状況を読ませます。押されたら、”HIGH"
通常は”LOW"ですね。(7P入力はプルダウンしているので)
reading はBottonState とLastBottonStateって2つの変数にバックアップされます。
LastBottonStateは、現在、ボタンが押されているとシステムが認識していれば”1(=HIGH)"
そうでなでれば、”0(=LOW)"になっています。(後ででてきます)
ここで、ボタンが押されると、readingが0”⇒”1”になるので、6行目以降のif文が起動します。
lastDebounceTime = millis() で現在の時間をlastDebounceTimeに保存します。
if ((millis() – lastDebounceTime) > debounceDelay) {
ここで、ボタンが押された状態が、debounceDelay時間以上になったら初めて押されたと
判断するわけです。これで、ノイズや、ONしたのに何度もON/OFFしたとご検出されて
OFFになってしまう(チャタリング)現象が除去できますよね。
(最初の変数設定のところで、debounceDelay=50(mS) になっています)
ここで、ボタンが押されたので、カウンタを+5します。
if (Count_up == 0 ) {
start_num = start_num + 5 ;
start_num って変数を使ってカウントダウンしてます。
初期値は20 (秒または2時間(20x6分))です。
+5して、50(秒または5時間(50x6分)を超えたら、50に制限します。
//でコメントしている行は、デバッグ時 1秒単位で動作させる部分です。
実際のプログラムは、6分=1秒 x 360倍しています。
else Count_up = 0;
時間切れカウントアップした時にボタンが押されると、カウンターを10に戻して、
リレーをまたONします。後に説明するブザーのカウンタを0に戻します。
// it'll be the lastButtonState: lastButtonState = reading;
スイッチの処理が終わったら、忘れずLastButtonStateに この値を保存します。
こんどはボタンを押し終わって”HIGH”から、”LOW”になる時も、同様のチャッタ防止の
動作をするためです。(説明は割愛します)
//start_num limitation 50 //long startTime = millis(); // if((millis()/1000) < start_num){ // displayNumber(start_num -(millis()/1000)); // 360sec =6 min if((int(millis()/1000)/360) < start_num){ displayNumber((start_num - (millis()/360000)); if ((millis()/500)% 2 == 1 ) { digitalWrite(LED1, HIGH); } else { digitalWrite(LED1, LOW); } } else { // reached zero, flash the display Count_up =1; // count up state k=k+1; // count_up buzzer count time=millis(); while(millis() < time+200) { displayNumber(0); // display 0 for 0.2 second digitalWrite(RELAY_ON,LOW); //TIME UP Relay OFF if(k<6) { tone (8, 1000, 100); } } time=millis(); while(millis() < time+200) { lightNumber(10); // Turn display off for 0.2 second } }
2行目、現在の時間とstart_num表示値から、残り時間を計算して7セグメント
のLEDに表示します。 (残り時間が0でカウントアップ以外 の場合)
4行目以降は、mSカウント値の500mSの剰余(mod)により、LEDをONしたり
OFFしたりする処理です。(これ考えた人、頭いいですね)
else {
// reached zero, flash the display
Count_up =1; // count up state
ここで、タイマー終了の検出をするんでしたね。
残り時間が0でカウントアップしたら、ブザーを6回鳴らして、リレーを”L”にして
こたつの電源を切ります。
また、LEDには00表示を0.8秒ON、0.2秒OFFにして点滅し、
タイマーが終了したことを知らせます。
また、ここでボタンを押すと10(=1時間)から、再度 リレーONして動作を始めるのは
先に説明したとおりです。
ここからは、2桁の7セグメントLED表示器への表示プログラムですね。
前に紹介したHPに詳しく説明していますから、ここでは省略します。
// 2ケタの表示を計算する
void displayNumber(int toDisplay) {
long beginTime = millis();
for(int digit = 2 ; digit > 0 ; digit–) {
//Turn on a digit for a short amount of time
switch(digit) {
case 1:
digitalWrite(digit1, HIGH);
break;
case 2:
digitalWrite(digit2, HIGH);
break;
}
//Turn on the right segments for this digit
// lightNumber(toDisplay % 6);
// toDisplay /= 6;
lightNumber(toDisplay % 10);
toDisplay /= 10;
// delayMicroseconds(DISPLAY_BRIGHTNESS);
//Display digit for fraction of a second (1us to 5000us, 500 is pretty good)
//Turn off all segments
delay(2) ;
lightNumber(0);
//Turn off all digits
digitalWrite(digit1, LOW);
digitalWrite(digit2, LOW);
}
//
// while( (millis() – beginTime) < 10) ;
//Wait for 20ms to pass before we paint the display again
}
// 7SEG表示パターン
const int digits[] = {
0b00111111, // 0
0b00000110, // 1
0b01011011, // 2
0b01001111, // 3
0b01100110, // 4
0b01101101, // 5
0b01111101, // 6
0b00100111, // 7
0b01111111, // 8
0b01101111, // 9
0b00000000, // 10
};
// 1けたの数字(n)を表示する
void lightNumber (int n) {
for (int i = 0; i < number_of_cathode_pins; i++) {
digitalWrite(cathode_pins[i], digits[n] & (1 << i) ? LOW : HIGH);
}
}
この説明で、わかりましたかね?
後で、この説明を読んでプログラム思い出せないようでしたら、また書き加えますね。
(2015年12月28日に加筆修正しました(^v^))
digitalWrite(cathode_pins[i], digits[n] & (1 << i) ? LOW : HIGH)
この式の意味を忘れていました、ググって回答をゲト。
この式は三項演算子っていって、<条件式> ? <真式> : <偽式>
digit[n]で定義したdigit[5] = 0b01101101 を1ビットずつシフト比較してcathode_pins[i]
の[1]~[7] までのON/OFFを入れていく式ですね、年取って頭固くなってますね。
またまた、長文・駄文で失礼しました。
それでは、まったねー (^u^)
おやすみなさい。