コンビニ弁当は500W、家は600W
私は普段、コンビニで買ったお弁当を家で温めるとき、600Wの電子レンジの500Wモードを使用して温めています。そうすると、明らかに動作を止めているような動作音がします。もったいない!
ということで、温め時間を500Wから600Wに換算する装置を作成します。
主要部品
部品 | 型番 | 秋月の販売コード | 数量 |
---|---|---|---|
AVR DD シリーズマイコン | AVR64DD28-I/SP | 118314 | 1 |
7セグLED 時計用 | E40364-IFOW | 114426 | 2 |
ボタン電池基板取付用ホルダー CR2032用 | CH74-2032LF | 101443 | 1 |
タクトスイッチ | 秋月の安いやつ | 103651(緑) | 5 |
マイコンは、新しいAVR DD シリーズを使用しました。Atmega328pより安く、DIP型もあるため使いやすいです。電源は、ボタン電池を使用しました。基盤サイズを秋月C基盤と同じにしたかったので、フットプリントが小さい立てるタイプの電池ソケットにしました。
回路図
適当に回路図を描きます。7セグLEDのシンボルは12ピンコネクタのシンボルで代用しています。
LEDの制御は、普通のダイナミック制御ですが、トランジスタ等を使用しませんでした。この場合、LEDに逆電圧がかかることがあります。LEDは5Vの逆電圧に耐えられるので、今回の電源電圧である3Vは十分耐えられるはずです。電流制限抵抗には、適当に1kΩにしました。暗くて見づらかったので、ちゃんと計算すればよかったです。7セグLEDのセグメントに当てるピンは、PDにまとめました。こうすることで、プログラムでのピン操作が一回のレジスタ書き換えですみます。7セグの桁に当てるピンも同様にPAにまとめました。
電源端子は「VDD」「AVDD」「VDDIO2」の3種類あります。そのそれぞれのピンにパスコンを付ける必要があります。回路図では、上部にまとめて書いてあります。
操作スイッチは3つ用意しました。「10秒」「1分」「10分」の三種類です。押すごとに弁当側の時間が増えます。電源スイッチも用意しました。押すことでマイコンをスリープから復帰させます。リセットスイッチもつけましたが、おまけ程度で普段は使いません。
電源部分には、電池を逆に入れても壊れないようにダイオードを入れてみました。また、リセッタブルヒューズも付けてみました。
回路を書いてみると、マイコンのピンをほとんど使い切ることになりました。使っていないピンは一つのみでした。
基盤作成
回路図ができたら、基盤を設計して業者に発注します。設計にはKiCadを使用しました。
基板サイズは、秋月のC基盤と同じサイズにしました。基盤の発注は5枚からなので、いつも4枚余ります。私はいつも、同じ基盤をスペーサーで裏面に付けて、部品の足などが手や服に引っかからないようにしています。そうすることで、余りは3枚になりますが、3枚も余ります。基盤サイズを秋月のユニバーサル基板と同じサイズにすることで、ユニバーサル基板で作成した別プロジェクトの基盤の裏につけることができ、無駄なく使用できます。
基板サイズが決まったら、部品を並べます。基板サイズが小さめなので、抵抗は立てて実装します。また、電池も立てて付けられるソケットを選びました。適当に部品を並べたら、適当に配線します。もちろん自動配線です。基板サイズが小さく部品同士の間隔も狭いので、配線幅を細くして足の間に3本の配線が通るようにしました。
プログラム
プログラム作成には、Arduino IDEを使用しました。ボードパッケージにはDxCoreを使用しました。pinModeFast
等、DxCoreで追加された関数を使用しています。動作周波数を1MHzに設定し省電力化を図りました。また、最後の操作から30秒後にスリープモードに入り、更に消費電力を抑えるようにしました。パワーボタンを押すことでスリープから復帰するようにしています。
すべての処理をloop関数で行っています。LEDの制御やボタンの押下判定もloop関数で行っている関係で、delay
関数を使用した処理の停止は使えません。millis
関数を使用した経過時間の計算を多用しています。ちょっと分かりづらいコードになってしまいました。特に、ボタンのチャタリング除去には、Onになった時に最後にOffになった時間からの経過時間を見る、というあまりない実装になりました。未だにチャタリング除去をソフトで実装する方法が分かりません。
電子レンジのワット数は600Wで固定にしました。ボタンで変えられたら便利ですけど、プログラムが複雑になりそうですし、どのようなプログラムにすれば良いかわからないので、可変にしませんでした。
/* 電子レンジワット換算
* AVR64DD28
* 1MHz Internal
* BOD 2.45V
* BOD enable/sample 32Hz
* reset/updi
* MultiVoltage Disable
*/
// 記載ワット/自宅ワット* 時間
#include <avr/io.h>
#include <avr/sleep.h>
#define LED_SEG_A PIN_PD1
#define LED_SEG_B PIN_PD2
#define LED_SEG_C PIN_PD3
#define LED_SEG_D PIN_PD4
#define LED_SEG_E PIN_PD5
#define LED_SEG_F PIN_PD6
#define LED_SEG_G PIN_PD7
#define LED_SEG_COL PIN_PF0
#define SEG_PORT PORTD
// 7segビット情報。回路の都合で左に1桁シフトしている。wiki参照。
#define SEG_BIT_0 0x7E
#define SEG_BIT_1 0x0C
#define SEG_BIT_2 0xB6
#define SEG_BIT_3 0x9E
#define SEG_BIT_4 0xCC
#define SEG_BIT_5 0xDA
#define SEG_BIT_6 0xFA
#define SEG_BIT_7 0x0E
#define SEG_BIT_8 0xFE
#define SEG_BIT_9 0xDE
// LED 弁当 1秒の位
#define LED_1DIG4 PIN_PA0
// LED 弁当 秒 10の位
#define LED_1DIG3 PIN_PA1
// LED 弁当 分 1の位
#define LED_1DIG2 PIN_PA2
// LED 弁当 分 10の位
#define LED_1DIG1 PIN_PA3
// LED レンジ 秒 1の位
#define LED_2DIG4 PIN_PA4
// LED レンジ 秒 10の位
#define LED_2DIG3 PIN_PA5
// LED レンジ 分 1の位
#define LED_2DIG2 PIN_PA6
// LED レンジ 分 10の位
#define LED_2DIG1 PIN_PA7
// LED 弁当 1秒の位
#define LED_1DIG4_bm (1 << 0)
// LED 弁当 秒 10の位
#define LED_1DIG3_bm (1 << 1)
// LED 弁当 分 1の位
#define LED_1DIG2_bm (1 << 2)
// LED 弁当 分 10の位
#define LED_1DIG1_bm (1 << 3)
// LED レンジ 秒 1の位
#define LED_2DIG4_bm (1 << 4)
// LED レンジ 秒 10の位
#define LED_2DIG3_bm (1 << 5)
// LED レンジ 分 1の位
#define LED_2DIG2_bm (1 << 6)
// LED レンジ 分 10の位
#define LED_2DIG1_bm (1 << 7)
#define DIG_PORT PORTA
#define SW_10S PIN_PC0
#define SW_1M PIN_PC1
#define SW_10M PIN_PC2
#define SW_POW PIN_PC3
const int32_t dispW = 500;
const int32_t myW = 600;
int32_t inSec = 0;
int32_t outSec = 0;
#define SLEEP_TIME_MILLIS 30000
unsigned long lastOperationMillis = 0;
#define BUTTON_THRESHOLD_MILLIS 100
unsigned long last10sSwOffMillis = 0;
unsigned long last1mSwOffMillis = 0;
unsigned long last10mSwOffMillis = 0;
bool Sw10sIsOn = false;
bool Sw1mIsOn = false;
bool Sw10mIsOn = false;
int8_t dispDegit = 0;
void setup() {
pinModeFast(LED_SEG_A, OUTPUT);
pinModeFast(LED_SEG_B, OUTPUT);
pinModeFast(LED_SEG_C, OUTPUT);
pinModeFast(LED_SEG_D, OUTPUT);
pinModeFast(LED_SEG_E, OUTPUT);
pinModeFast(LED_SEG_F, OUTPUT);
pinModeFast(LED_SEG_G, OUTPUT);
pinModeFast(LED_SEG_COL, OUTPUT);
pinModeFast(LED_1DIG4, OUTPUT);
pinModeFast(LED_1DIG3, OUTPUT);
pinModeFast(LED_1DIG2, OUTPUT);
pinModeFast(LED_1DIG1, OUTPUT);
pinModeFast(LED_2DIG4, OUTPUT);
pinModeFast(LED_2DIG3, OUTPUT);
pinModeFast(LED_2DIG2, OUTPUT);
pinModeFast(LED_2DIG1, OUTPUT);
pinModeFast(SW_10S, INPUT_PULLUP);
pinModeFast(SW_1M , INPUT_PULLUP);
pinModeFast(SW_10M, INPUT_PULLUP);
pinModeFast(SW_POW, INPUT_PULLUP);
// スリープ設定
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
//スリープ復帰する割り込み
attachInterrupt(SW_POW, wakeupFunction, FALLING);
// 未使用ピンの処理
pinModeFast(PIN_PF1, OUTPUT);
}
void loop() {
// ワット計算
outSec = dispW * inSec / myW;
// スリープ 30秒でスリープさせる
if ((millis() - lastOperationMillis) > SLEEP_TIME_MILLIS){
// 消灯
SEG_PORT.OUT = 0;
DIG_PORT.OUT = 0;
// スリープ
sleep_cpu();
inSec = 0;
lastOperationMillis = millis();
}
// ボタン入力
if (digitalRead(SW_10S) == LOW){
// 最後にオフになった時間からの経過時間で判定
if ((millis() - last10sSwOffMillis) > BUTTON_THRESHOLD_MILLIS && !Sw10sIsOn){
inSec += 10;
lastOperationMillis = millis();
Sw10sIsOn = true;
}
}
else {
last10sSwOffMillis == millis();
Sw10sIsOn = false;
}
if (digitalRead(SW_1M) == LOW){
// 最後にオフになった時間からの経過時間で判定
if ((millis() - last1mSwOffMillis) > BUTTON_THRESHOLD_MILLIS && !Sw1mIsOn){
inSec += 60;
lastOperationMillis = millis();
Sw1mIsOn = true;
}
}
else {
last1mSwOffMillis == millis();
Sw1mIsOn = false;
}
if (digitalRead(SW_10M) == LOW){
// 最後にオフになった時間からの経過時間で判定
if ((millis() - last10mSwOffMillis) > BUTTON_THRESHOLD_MILLIS && !Sw10mIsOn){
inSec += 600;
lastOperationMillis = millis();
Sw10mIsOn = true;
}
}
else {
last10mSwOffMillis == millis();
Sw10mIsOn = false;
}
// 時間表示
if (dispDegit < 4){
dispTime(LED_1DIG4_bm, inSec);
}
else {
dispTime(LED_2DIG4_bm, outSec);
}
}
// 指定のLEDに時間を表示します
// oneSecDig_bm:一秒の桁を表すビットマスク
// sec:表示する時間。秒単位
void dispTime(uint8_t oneSecDig_bm, int32_t sec){
// 分抽出
int minutes = sec / 60;
// 10分の位
int8_t minutesTen = minutes / 10;
// 1分の位
int8_t minutesOne = minutes % 10;
// 10秒の位
int seconds = sec % 60;
int8_t secondsTen = seconds / 10;
// 1秒の位
int8_t secondsOne = seconds % 10;
int tempDegit;
if (dispDegit < 4){
tempDegit = dispDegit;
} else {
tempDegit = dispDegit - 4;
}
switch(tempDegit){
case 0:
if (minutesTen == 0) {
// 10を指定して、消灯させる。
disp7SegSingle(oneSecDig_bm << 3, -1);
} else {
disp7SegSingle(oneSecDig_bm << 3, minutesTen);
}
break;
case 1:
disp7SegSingle(oneSecDig_bm << 2, minutesOne);
break;
case 2:
disp7SegSingle(oneSecDig_bm << 1, secondsTen);
break;
case 3:
disp7SegSingle(oneSecDig_bm, secondsOne);
break;
}
if (dispDegit < 7){
dispDegit++;
} else {
dispDegit = 0;
}
}
// 一桁分の7セグを光らせます。指定の桁以外は消灯します。
// 引数
// dig_bm : 桁を示すビットマスク
// num : 表示する数字 1~9以外は消灯
void disp7SegSingle(uint8_t idg_bm, int8_t num){
// 表示消す
SEG_PORT.OUT = 0;
DIG_PORT.OUT = 0;
// アノードコモンなので、セグメント側は電流引き込みになる。そのためビット反転している。
switch(num){
case 0:
SEG_PORT.OUT = ~SEG_BIT_0;
break;
case 1:
SEG_PORT.OUT = ~SEG_BIT_1;
break;
case 2:
SEG_PORT.OUT = ~SEG_BIT_2;
break;
case 3:
SEG_PORT.OUT = ~SEG_BIT_3;
break;
case 4:
SEG_PORT.OUT = ~SEG_BIT_4;
break;
case 5:
SEG_PORT.OUT = ~SEG_BIT_5;
break;
case 6:
SEG_PORT.OUT = ~SEG_BIT_6;
break;
case 7:
SEG_PORT.OUT = ~SEG_BIT_7;
break;
case 8:
SEG_PORT.OUT = ~SEG_BIT_8;
break;
case 9:
SEG_PORT.OUT = ~SEG_BIT_9;
break;
default:
SEG_PORT.OUT = 0xff;
break;
}
DIG_PORT.OUT = idg_bm;
}
void wakeupFunction(){
// なにもしない
}
完成!!
ということで、完成です。
これで、コンビニ弁当ライフが快適に!!
投稿者の人気記事
-
shigobu
さんが
2025/02/04
に
編集
をしました。
(メッセージ: 初版)
-
shigobu
さんが
2025/02/07
に
編集
をしました。
-
shigobu
さんが
2025/02/08
に
編集
をしました。
ログインしてコメントを投稿する