MENU

5x7ドットマトリクスLEDで文字を出力してみる

なんかのときに秋月で買った5x7 ドットマトリクスLEDが家にあったんで光らせてみました。

今回はArduino (ProMicro)で5x7ドットマトリクスLEDを光らせて文字を出力するまでの手順を紹介しています。
原理確認のために、5x7ドットマトリクスLEDをゆっくりと光らせた動画も載せてますので、興味があればそちらだけでも見ていってください。→動画位置までジャンプ

ドットマトリクスLEDについて

ドットマトリクスLEDのデータシートを確認すると、中身の回路はこんな感じです。

f:id:moutakusan:20201103210417p:plain
5x7ドットマトリックス回路図

光らせ方の原理は前回作ったキーマトリックススイッチと同じですね。

geekyfab.com

キーマトリックススイッチのスイッチ部をLEDに置き換えたものと言えます。

COL①~⑤を順番にHighにしていって、タイミングを合わせて光らせたい場所のROW①~⑦をLowにして電流を流すことで、特定のLEDだけを光らせることができます。 例えば左上端のLEDだけを光らせたければ、COL①がHighのタイミングでROW①をLowにすればいいですね。
(ROW②~⑦は電流が流れないようにHighか開放状態にします)

これを人の目にもとまらぬ速さで全LEDに対して行うと、残像効果でまるで全LEDが同時に光っているように見えるという算段です。

この記事の最後に、ドットマトリクスLEDをゆっくり光らせたときの動画も載せてます。
そちらを見れば、わかりやすいかも。→動画位置までジャンプ

回路を描いてみる

ドットマトリクスLEDを光らせるための回路はこんな感じにしました。

f:id:moutakusan:20201103220236p:plain
5x7ドットマトリクスLED点灯回路
抵抗はLEDに流れる電流を制限して、LEDの明るさを調整するためのものです。
抵抗値は、LEDに流したい電流値とデータシートに書いてあるLEDの順方向電圧から計算します。
今回は、電流を適当に10mAとして、順方向電圧が3.2Vくらいになるようなので、
  (5-3.2)V/0.01A = 180ohm
の抵抗を使用しました。

コードを書いてみる

COL①~⑤をHighに走査して、タイミングを合わせてROW①~⑦をLOWにします。
とりあえず、"A"と出力してみましょうか。

const int columnPinNumber[] = {9,10,14,15,16};
const int rowPinNumber[] = {2,3,4,5,6,7,8};

// "A"
bool LEDMatrix[7][5] ={
  {0,1,1,1,0},
  {1,0,0,0,1},
  {1,0,0,0,1},
  {1,0,0,0,1},
  {1,1,1,1,1},
  {1,0,0,0,1},
  {1,0,0,0,1}
};

void init5x7DotMatrixLED(int columnPinNumber[5], int rowPinNumber[7]);
void bright5x7DotMatrixLED(int columnPinNumber[5], int rowPinNumber[7], bool LEDMatrix[7][5]);
void openDrain(int pin, bool state);

void setup() {
  init5x7DotMatrixLED(columnPinNumber,rowPinNumber);
}

void loop() {
  bright5x7DotMatrixLED(columnPinNumber,rowPinNumber,LEDMatrix);
}

//5x7 Dot Matrix LED初期化関数
void init5x7DotMatrixLED(int columnPinNumber[5], int rowPinNumber[7]){
  for(int i=0; i<=4; i++){
    pinMode(columnPinNumber[i],OUTPUT);
    digitalWrite(columnPinNumber[i],LOW);
  }

  for(int i=0; i<=6; i++){
    openDrain(columnPinNumber[i],HIGH);
  }
}

//5x7 Dot Matrix LED 点灯関数
void bright5x7DotMatrixLED(int columnPinNumber[5], int rowPinNumber[7], bool LEDMatrix[7][5]){
  for(int i=0; i<=4; i++){
    digitalWrite(columnPinNumber[i],HIGH);
    for(int j=0; j<=6; j++){
      openDrain(rowPinNumber[j],1-LEDMatrix[j][i]);
      delayMicroseconds(476);     //=60Hz
      openDrain(rowPinNumber[j],HIGH);
    }
    digitalWrite(columnPinNumber[i],LOW);
  }
}

//オープンドレイン動作関数
void openDrain(int pin, bool state){
  if(state == HIGH){
    pinMode(pin,INPUT);
  }
  else if (state == LOW){
    pinMode(pin,OUTPUT);
    digitalWrite(pin,LOW);    
  }
}

オープンドレイン動作関数について少し補足です。
Arduinoの出力ピンはハイインピーダンス(開放)出力ができないため、ピンを入力に切り替えて無理やりハイインピーダンスにする必要があります。
そんなことをやっているのがオープンドレイン動作関数さんです。

動かしてみる

では、実際に回路を組んで動かしてみましょう。

かなり簡単な回路なんですが、一点だけ、ドットマトリクスLEDのピン配置がなぜかデータシートに書かかれていませんでした。
いや、書かれてはいるんですがデータシートに書かれてるような識別マークは手元のものには書いてないし、どちらにしろ1ピンの位置しかわからない内容だし…

ともかく、裏面から見ると透明な樹脂のしたにうっすらと”1”と”12”の表記があり、下図のように時計回りに割り当てられていると推測できます。

f:id:moutakusan:20201107153429p:plain
5x7ドットマトリックスLED ピン配置

で、実際に回路を組んでコードを書き込むと…

f:id:moutakusan:20201107154735p:plain
”A”が表示された!!

”A”が表示されました!やったー!

何が起こってるか見てみる

5x7=35個のLEDを12本のピンで制御してるなんて不思議ですね。
原理は初めに説明した通りなんですが、本当に?って思いはありますよね。

ということで、コード中の一つ当たりの点灯時間を476usから100msくらいに伸ばしてみましょう。
すると、こうなります。

f:id:moutakusan:20201107161520g:plain
スロー点灯
一つずつ順番に光らせている様子が見ることができましたね。
これを目にもとまらぬ速さで繰り返すことで”A”を表示させていたようです。


今回はこれで終わります。

おしまい。


[追記]
64x32ドットマトリクスLEDの光らせ方の紹介記事も公開しました。
こっちではカラーのドット絵とかも出力してます。 geekyfab.com

4x4キーマトリックススイッチを作ってみる その2

 前回、4x4キーマトリックスの回路を描いて、ユニバーサル基板上に実装してみました。

geekyfab.com

 今回はプログラムを書いてキーマトリックスが動作することを確認していきたいと思います。

…の前に、前回作った回路の導通チェックをしているとおかしいところがあったんで、半田付けを修正しました。修正後が↓になります。

赤丸のとこが修正箇所
…まぁ、接続が間違ってたんでつなぎ変えたんですけど、Afterの写真だけ見てもわかりませんよね。

Arduinoにつないでみる。

 Arduino(ProMicro)に接続したキーマトリックスの回路図はこんなんです。

ProMicroにキーマトリックスを接続

単純にGPIOポートにキーマトリックスのKEYIN、KEYOUTを接続しただけです。

部品表はこちら。

緒言 部品 購入先リンク
U1 ProMicro Amazon
SW1~16 6x6x5mm タクトスイッチ Amazon
D1~4 1N4148 秋月電子
R1~4 15kohm 1/4W 1% 金属被膜抵抗 Aliexpress
- ユニバーサル基板 秋月電子
- ピンヘッダ 秋月電子

コードを書いてみる

 KEYOUT1~4を順番にHIGHにしていって、どのタイミングでKEYIN1~4のどれがHGIHになるかを見ればいいわけです。ですので、動作確認用のコードはこんな感じにしました。

const int KEYIN[] = {18,19,20,21};
const int KEYOUT[] = {10,14,15,16};

unsigned long previousMillis = 0;  
const long interval = 50;          
int columnNum=0;
int sw[4][4]={0};

void setup() {
  for(int i=0; i<=3; i++){
    pinMode(KEYIN[i],INPUT);
    pinMode(KEYOUT[i],OUTPUT);
    digitalWrite(KEYOUT[i], LOW);
  }
  Serial.begin(9600);
}

void loop() {
  unsigned long currentMillis = millis();
  
  if(currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    digitalWrite(KEYOUT[columnNum], HIGH);
    for(int rowNum=0; rowNum<=3; rowNum++){
      sw[columnNum][rowNum]=digitalRead(KEYIN[rowNum]);
      delay(10);
    }
    digitalWrite(KEYOUT[columnNum], LOW);
    columnNum++;
    columnNum &=3;
  }

  if(columnNum==3){
    for(int i=0; i<=3; i++){
      for(int j=0; j<=3; j++){
        Serial.print(sw[i][j]);
      }
      Serial.println();
    }
    Serial.println();
    Serial.println();
    delay(500);
  }
  
}

 一列ずつ順番にスイッチの状態を見ていって、4x4の配列sw[4][4]にスイッチの状態を書き込んでるって感じですね。で、結果をシリアル出力で見てます。

動かしてみた

 回路図に沿ってキーマトリックスとProMicroを接続し、書いたコードをProMicroに書き込みます。

ProMicroと接続してコードを書き込む

 走らせてみると、こんな感じで反応してくれました。

0100
0000
0000
0000


0000
0000
0010
0000


0000
0000
0000
0000

.
.
.

 ちゃんと動いてくれました。よしよし。

おわりに

 キーマトリックスは昔からあるものなので、技術情報も多く、あまり苦労せずに回路やコードを書くことができました。ユニバーサル基板のサイズを攻めすぎたせいか、配線が悩ましく、半田付けには少し苦労しました。
 キーマトリックスの製作はこれにて一旦完了です。お疲れ様でした。


おしまい。


GEEKY Fab.では思い出のゲームで遊ぼうをテーマにハードウェアを作ってます。
よかったら↓から見ていってください。


4x4キーマトリックススイッチを作ってみる

前回はマルチリモコンを作りました。
geekyfab.com

でも、作ったマルチリモコンにはスイッチは二つだけ…
これでは用途が限られすぎています。
そこで今回はマルチリモコンにスイッチをいっぱい追加したいので、キーマトリックスを作ってみようと思います。



キーマトリックスとは

キーマトリックスとはたくさんのスイッチのON/OFF判定を少ない入出力ポート数で達成する方法の一つです。キー (スイッチ)がマトリックス上に並んでる回路なので、キーマトリックスです。

例えば、16個のスイッチのON/OFF判定をしようと思ったら、単純に各スイッチを一つずつマイコンのポートにつなげばできます。
ただ、それだと当然マイコンのポートを16ポート使ってしまうことになります。
Arduino UNO rev.3だとDigital pinとして使えるポートは全部で22ポートなので、仮に16ポートをボタンだけで埋めると残りはたった6ポートです。
これはさすがにもったいない。

Arduino UNO Rev.3だとこういうことになる

そこでキーマトリックスの出番です。キーマトリックスを使用すると、元の半分の8ポートで16個のスイッチのON/OFF判定ができるようになります。

キーマトリックスを使えば8ポートでOK

では、そんな便利なキーマトリックスをどうやって実現するのでしょうか?
次からは、実際に回路を描いて説明してみます。

キーマトリックス回路

4x4キーマトリックスの回路図は下図の感じになります。

4x4 キーマトリックス回路図
KEYOUT1~4がマイコン(今回はArduino)のデジタル出力端子、KEYIN1~4がデジタル入力端子につながります。
各スイッチ(SW1~16)の一方のピンが、電源ではなくデジタル出力端子につながっているところがポイントです。
もしKEYOUT1~4が、1ポートにつきボタン1個を使う単純な回路のように、5V(High)で固定されていたらどうなるでしょうか?

仮にSW1を押したときはKEYIN1が反応することになります。

SW1を押すとKYEIN1が反応する
ではSW5を押したときは?
…同じくKEYIN1が反応することになります。
SW5を押したときもKYEIN1が反応する
つまり、マイコンからはSW1を押したときもSW5を押したときも同じように見えてしまい、実際にはどのスイッチが押されているかわかりません。

では、KEYOUT1だけがHigh(5V)でKYEOUT2~4がLow(0V)とした場合、SW1とSW5を押したときの反応はどうなるでしょうか?

SW1を押したときの反応はさっきと同じですね。
KEYIN1が反応します。

SW5を押したときは?
ここがさっきと変わってきますね。
KEYOUT2は0Vなので、SW5は押しても押さなくても0Vのままとなり、KEYIN1は反応しません。

キーマトリックスではこのKEYOUT側を一つだけHighにするってやつを、順番に行い続けます。
例えば、初めの10ms間はKYEOUT1だけをHighにし、次の10ms間ではKYEOUT2だけをHighにし…という具合に。
このように、キーマトリックスでは同時に見るスイッチの数を絞ることで、少ないピン数で多くのスイッチを見ることができてるんですね。

スイッチ以外の部品について

回路図で使われてるスイッチ以外の部品の役割も説明しておきます。D1~4は逆流防止用のダイオードです。
例えば、SW1とSW5が同時に押された場合にダイオードがなければKEYOUT1からKEYOUT2に電流が流れてしまい、最悪故障してしまいます。

ダイオードがなければ逆流する
こういうことがないようにするためのダイオードです。
ちなみに、SWごとにダイオードをつけることでボタンを同時押ししても誤判定されなくなりますが、今回はそこまでしてません。

R1~R4はプルダウン抵抗です。
これがないとスイッチが押されてないときのKEYIN1~4の状態は不定となります。
値は適当です。

ユニバーサル基板で作ってみた

キーマトリックスは使う機会が多いと思うので、ユニバーサル基板上に実装してモジュール化してみました。

作ったキーマトリックス(表面)
作ったキーマトリックス(裏面)
ギリギリサイズすぎて抵抗が無理のある付け方になってしまいました。
抵抗が付いている方がKEYIN,ダイオードが付いている方がKEYOUT。
抵抗側のピンが5ピンなのは、pin1(一番手前)でArduino側のGNDと接続するためです。

使った部品リストは下記です。

緒言 部品 購入先リンク
SW1~16 6x6x5mm タクトスイッチ Amazon
D1~4 1N4148 秋月電子
R1~4 15kohm 1/4W 1% 金属被膜抵抗 Aliexpress
- ユニバーサル基板 秋月電子
- ピンヘッダ 秋月電子

タクトスイッチは100個入りで¥615という安さ。
抵抗もAliexpressで買ったので、3120個入の抵抗キットで$15という安さ。
ただし、余りまくり。


次回、Arduinoとつないでプログラムを組んで実際に動かしてみたいと思います。




つづく。

geekyfab.com

*1:例えばしなぷすさんのHPでとてもわかりやすく解説されている。

Arduino (ProMicro)で家用のマルチリモコンを作る  その4

 前回の続きで、IRremoteライブラリを使ってエアコンのリモコンを実装してみたいと思います。

 まずは受信機でエアコンの”冷房ボタン”を押したときの受信データを確認すると…

f:id:moutakusan:20201001152722g:plain
止まらない!

あららららら。エアコンの信号の長さに対して受信データを格納する配列が足りてないため、暴走してしまうみたいですね。
 受信データを格納している配列はIRremoteInt.h内で宣言されているrawbufなので、このサイズを大きくすれば対応できそうです。↓はIRremoteInt.hの一部抜粋。

struct irparams_struct {
    // The fields are ordered to reduce memory over caused by struct-padding
    volatile uint8_t rcvstate;      ///< State Machine state
    uint8_t recvpin;                ///< Pin connected to IR data from detector
    uint8_t blinkpin;
    uint8_t blinkflag;              ///< true -> enable blinking of pin on IR processing
    unsigned int rawlen;            ///< counter of entries in rawbuf
    unsigned int timer;             ///< State timer, counts 50uS ticks.
    unsigned int rawbuf[RAW_BUFFER_LENGTH];  ///< raw data
    uint8_t overflow;               ///< Raw buffer overflow occurred
};

見たまんまRAW_BUFFER_LENGTHの値を変更すればrawbufを拡張できそうです。デフォルトでRAW_BUFFER_LENGTHは101となっていたので、これを801とかにして再度エアコンの冷房信号を受信してみると…

f:id:moutakusan:20201001154157p:plain
エアコンの冷房信号の受信結果

ちゃんと受信できたっぽいです。配列長が531だったみたいですね。かなり長いなあ。
 では、早速受信できたrawDataを前回作ったマルチリモコン用のプログラムのirSignal[]にべた書きで代入してみてエアコンを制御できるか確認してみます。
 あ、その前にRAW_BUFFER_LENGTHの値が801のままだとメモリをいっぱい使ってしまうので、値は戻しておきましょう。私が使っているPro Microでは、801のままだとメモリ不足でコンパイル通りませんでした。
 スケッチを書き込んでLEDをエアコンでタクトスイッチを押してみると…

f:id:moutakusan:20201001234159g:plain
エアコンをON!
ちゃんと点きました~。
 そしてもう一個の方のタクトスイッチを押すとテレビのオンオフもできてるので、もうこれはマルチリモコンといってもいいでしょう。完成だ完成。

とりあえずできた

 ということでArduinoで作るマルチリモコンいったん完成!!シーリングライトでつまずきましたが、そのほかの部分はけっこうすんなり実装できました。事前に調べていたときはエアコンの制御が苦労しそうかなと思っていましたが、そこはすんなりできましたね。IRremoteライブラリは偉大です。
 今後はできたリモコンをベースに機能拡張をしていけたらいいなと思います。下記は次にやりたいことリストです。

  • ボタンの数を増やす
  • 受信信号をボタンに直接割り当てる学習機能の追加
  • ユニバーサル基板上へ実装する
  • シーリングライトを制御する

 ここまでで、Arduinoで作るマルチリモコンはひと段落とします。上記リストの内容はおいおいクリアして、普段使いできるレベルには仕上げたいとは思っています。




 次は何をやろうかな。

Arduino (ProMicro)で家用のマルチリモコンを作る  その3

 前回の続きです。

moutakusan.hatenablog.com

 テレビへのリモコン操作はできたので、プラスで何か別のものもリモコン操作できたらマルチリモコンって言ってもいいですよね。エアコンのリモコンはハードルが高そうなので、単純であろうシーリングライトのリモコンを実装してみます。

 前々回作った受信器でシーリングライトのON/OFF信号を受信してみると…

f:id:moutakusan:20200920233735p:plain
シーリングライトのリモコン受信データ
 0x46BBC5EEというデータが送られているという結果になりましたが、rawDataを見る限り32bitsもないですよね。どう読めばいいんでしょうか。

 おさらいですが、赤外線リモコンリーダー→データ→リピート→データ→リピート→… の流れで信号が来ます。IRremoteライブラリは読み取ったリーダーの長さからライブラリ内のどの規格に合致するかを確かめ、合致した規格でリーダー後の信号をデコードしていくという流れになっています。そして、合致する規格がない場合はハッシュ関数を通して32 bitの値にして出力しているようです。そのため、ライブラリ内のどの規格にも合致しなかったシーリングライトの信号の復調結果は32bitで出力されているみたいです。
 ただ、残念ながらこのデコード値だけを記録したところで実際の信号のHigh, Low期間が分からないので使えません。ライブラリ内に用意されていない規格はsendRaw関数を使ってHigh, Low期間を直接指定して信号を送信してあげる必要がありそうです。いったん受信したrawDataをコード内にべた書きしてsendRaw関数と前回作った赤外送信部回路で送信してみましょう。

シーリングライトへの信号送信実験

 サンプルスケッチのIRSendRawDemo.inoの送信データirSignalに先ほど受信したrawDataを放り込んで、

#include <IRremote.h>

IRsend IrSender;

// On the Zero and others we switch explicitly to SerialUSB
#if defined(ARDUINO_ARCH_SAMD)
#define Serial SerialUSB
#endif

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

    Serial.begin(115200);
#if defined(__AVR_ATmega32U4__) || defined(SERIAL_USB) || defined(SERIAL_PORT_USBVIRTUAL)
    delay(2000); // To be able to connect Serial monitor after reset and before first printout
#endif
    // Just to know which program is running on my Arduino
    Serial.println(F("START " __FILE__ " from " __DATE__));
    Serial.print(F("Ready to send IR signals at pin "));
    Serial.println(IR_SEND_PIN);  //IR_SEND_PIN=5
}

void loop() {
    int khz = 38; // 38kHz carrier frequency for the NEC protocol
    /*
     * Send data from RAM
     */
    unsigned int irSignal[] = {1250,450, 1200,500, 350,1300, 1250,450, 400,1300, 400,1250, 450,1250, 1200,500, 400,1250, 1250,450, 400,1300, 350};
    Serial.print("send data:");
    for(int i=0; i<sizeof(irSignal); i++){
        Serial.print(sizeof(irSignal));
        Serial.print(",");
    }
    Serial.println("");
    for(int i=0; i <3; i++){
        IrSender.sendRaw(irSignal, sizeof(irSignal) / sizeof(irSignal[0]), khz); // Note the approach used to automatically calculate the size of the array.
    }
    delay(2000);

}

として、実行すると…ライトがオンオフしてくれません…
 何が悪いんでしょうか?試しにテレビのリモコンの信号をirSignalに入れてkhzを37に変更して実行してみると…こちらは動きました。上記のコードで動くのは動くみたいです。オシロとかあれば切り分けも楽だなぁと思ったら、どうやらArduinoで作ってる人もいる模様。今度やってみよう。
 ともかく、シーリングライトが動かない問題はなかなか解決しそうにありませんのでいったん置いておいて、先にマルチリモコンの回路とコードを書いていくことにします。

回路

 テレビのON/OFF用とシーリングライトのON/OFF用にタクトスイッチを一つずつつけます。 タクトスイッチの接続ピンは割り込みに対応したピンにしておきます。  テレビとシーリングライトの二方向に同時に信号を送信できるように、LEDを1つ直列に追加します。 前回と同程度の信号強度にするためには電流を100mAのままにしないといけないので、
    RIR = ( 5-1.35 x 2 ) V / 0.1 A = 23 ohm
なので、電流制限抵抗は22ohmに変更します。
 これらを反映させると、回路図は下図のようになりました。

f:id:moutakusan:20200928230959p:plain
マルチリモコン回路図

コード

 テレビとシーリングライトの送信用コードを関数にして、それぞれのタクトスイッチが押されたときに割り込みで実行されるようにしました。

#define SERIAL_PRT_ON 1

#include <IRremote.h>

IRsend irsend;

const int TV_SW_PIN = 3;
const int LIGHT_SW_PIN = 7;

struct sendTV{
  static bool TVState;
  void TVCtrl(unsigned long tAddress, unsigned long tData);
  static void TVStateChage();
};

struct sendLight{
  static bool LightState;
  void LightOnOff();
  static void LightStateChage();
};

sendTV sTV;
sendLight sLight;
bool sendTV::TVState=0;
bool sendLight::LightState=0;

void setup() {
    pinMode(LED_BUILTIN, OUTPUT);
    pinMode(TV_SW_PIN, INPUT);
    pinMode(LIGHT_SW_PIN, INPUT);

#if SERIAL_PRT_ON
    Serial.begin(115200);
#if defined(__AVR_ATmega32U4__)
    while (!Serial); //delay for Leonardo, but this loops forever for Maple Serial
#endif
    // Just to know which program is running on my Arduino
    Serial.println(F("START " __FILE__ " from " __DATE__));
    Serial.print(F("Ready to send IR signals at pin "));
    Serial.println(IR_SEND_PIN);  //IR_SEND_PIN=5
#endif    

    attachInterrupt(digitalPinToInterrupt(TV_SW_PIN),sendTV::TVStateChage, RISING);
    attachInterrupt(digitalPinToInterrupt(LIGHT_SW_PIN),sendLight::LightStateChage, RISING);

}

void loop() {
  if(sendTV::TVState){
    sTV.TVCtrl(0x555a, 0xf148688B); //0x555a:TV address, 0xf148688B:Power ON/OFF
    sendTV::TVState=0;
  }

  if(sendLight::LightState){
    sLight.LightOnOff();
    sendLight::LightState=0;
  }  
}

void sendTV::TVCtrl(unsigned long tAddress, unsigned long tData){    
  for (int i = 0; i < 3; i++) {
    irsend.sendPanasonic(tAddress, tData);
#if SERIAL_PRT_ON
    Serial.print(F("sendPanasonic(0x"));
    Serial.print(tAddress,HEX);
    Serial.print(F(", 0x"));
    Serial.print(tData,HEX);
    Serial.println(F(")"));
#endif
    delay(40);
  }
}

void sendTV::TVStateChage(){
  sendTV::TVState = 1;
}

void sendLight::LightOnOff(){
  int khz = 38; // 38kHz carrier frequency for the NEC protocol
  unsigned int irSignal[] = {1300,400, 1250,450, 450,1200, 1300,400, 450,1200, 500,1200, 450,1250, 1300,400, 450,1200, 1300,400, 450,1200, 500};

#if SERIAL_PRT_ON
  Serial.print("send data:");
  for(int i=0; i<sizeof(irSignal)/ sizeof(irSignal[0]); i++){      
    Serial.print(i);
    Serial.print(":");
    Serial.print(irSignal[i]);
    Serial.print(",");
    
  }
  Serial.println("");
#endif

  for(int i=0; i <3; i++){
      irsend.sendRaw(irSignal, sizeof(irSignal) / sizeof(irSignal[0]), khz); // Note the approach used to automatically calculate the size of the array.
      delay(10);
  }
}

void sendLight::LightStateChage(){
  sendLight::LightState = 1;
}

コードが汚いのは許して欲しい。

結果

 タクトスイッチを押すとテレビが付きました!これはかなりリモコンっぽい!シーリングライトの方は相変わらずつきませんが…
 シーリングライトについてはその後いろいろ頑張りましたが、点かず。エアコンのリモコンの実装のほうが簡単なのではなかろうか。次回、エアコンのリモコンの実装をやってみます。




まだつづく。

Arduino (ProMicro)で家用のマルチリモコンを作る  その2

 前回の続きです。

moutakusan.hatenablog.com

 ↓こういうデータがダンプできたわけですが、どういう規格になってるんでしょうか。

f:id:moutakusan:20200908223306p:plain
ダンプされたデータ

 規格についてはこちらのサイトやこちらのサイトが参考にさせていただきました。 あと、Arduinoを使用した解析についてはdok kozoさんの記事jumbleatさんのブログも参考にさせていただきました。
 ポイントは、

  • 赤外線規格には数種類ある
    • 出力結果のEncodingってとこですね。
  • いずれの規格も全体の流れはリーダー→データ→リピート→データ→リピート→… の流れ
    • 出力結果のTimingの"+3450, -1600"がリーダー、その後の"500, -300"と"+550, -1150"がデータ。数字には少しばらつきがある。
  • データはHi期間とLow期間の比であらわされる
    • 出力結果のTimingの"+"がHigh期間、"-"がLow機関に対応している。

の三点ですかね。
 加えて、EncodingのフォーマットがPanasonicの場合は、アドレスが信号に含まれるみたいです。
 ここでちゃんとEncodingフォーマットが表示されていて、上手くデコードできている場合は、IRRemoteライブラリを使えば簡単に送信側もできそうですね。先にそっちをちょっとやってみることにします。

テレビの赤外線送信部を作ってみる

 テレビの赤外線信号はIRRemoteライブラリをそのまま使えば上手くデコードできてたみたいなので、デコードしたデータを参考に赤外線送信部を作ってみます。
 とりあえず、下記にデコードした結果の一部を書いてみました。

リモコンキー デコード結果
電源 0x555A F148688B
1ch 0x555A F148724C
2ch 0x555A F148F244
録画 0x555A F148C9F7

 デコード結果の前半の0x555Aがアドレス、後半がデータです。では試しにこれらのデータをテレビに投げてみましょう。

回路

回路はこう。

f:id:moutakusan:20200917165515p:plain
送信部回路図、 ピン5の先は前回作った受光部の回路につながってる
 赤外LEDはコレを使いました。赤外LEDの順方向電圧はTyp. 1.35V、最大定格電流はPulse駆動の場合は1000mAなんで、
    RIR = (5-1.35)V/1A = 3.65 ohm
ということで、3.65ohm以上ならOK。実際にはUSBの供給電流は最大500mAなので、余裕をもって100mA (RIR=36.5 ohm)くらいで使うことにします。1000 mA流したければ、別電源いりますね。これに合わせてトランジスタ2SC1815を使うことにしました。150mAまで流せるやつです。
 注意点として、定電流のときのLEDの最大定格は100mAなので、前のスケッチが残っていたりしてD5ピンがHighになっている状態で回路の電源入れるとLEDが壊れます。スケッチを書き込んでからLEDを接続するようにするなどして対策してください。

コード

 コードはスケッチ例のIRSendDemo.inoを編集して、テレビのON/OFF信号を5秒に1回投げるようにしました。

#include <IRremote.h>

IRsend irsend;

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

    Serial.begin(115200);
    // Just to know which program is running on my Arduino
    Serial.println(F("START " __FILE__ " from " __DATE__));
    Serial.print(F("Ready to send IR signals at pin "));
    Serial.println(IR_SEND_PIN);  //IR_SEND_PIN=5
}

void loop() {
    unsigned long tAddress = 0x555a;  //TV address
    unsigned long tData = 0xf148688B;  //Power ON/OFF
    
    for (int i = 0; i < 3; i++) {
        irsend.sendPanasonic(tAddress, tData);
        Serial.print(F("sendPanasonic(0x"));
        Serial.print(tAddress,HEX);
        Serial.print(F(", 0x"));
        Serial.print(tData,HEX);
        Serial.println(F(")"));
        delay(40);
    }

    delay(5000); 
}

結果

 スケッチを回路に書き込むと赤外LEDがチカチカ(目視ではもちろん見えませんが、スマホのカメラ越しだと赤外LEDの光が見えます。)

f:id:moutakusan:20200917164434g:plain
赤外LEDチカチカ
 赤外LEDをテレビの方に向けると…ちゃんとテレビがON/OFFしてくれました!!ちなみに、テレビからどれくらい離れても届くか試したところ、だいたい4.5mくらいまでは反応してくれました。家の中で使う分には十分なので、回路はこのまま進めようと思います。

 次回はもう少しIRremoteライブラリをいじってみようかなぁ。


つづく

Arduino (ProMicro)で家用のマルチリモコンを作る  その1

こういうやつですね。

作ってみましょう。

仕様

 家にあるリモコンから赤外線を読み込んでデータを解析し、保存するモードと、保存したデータを送信するモードがあるといいですね。

手順

 下記順番で進めていこうと思います。

  1. 赤外線読み込み部を作る。
  2. データ保存部を作る。
  3. 赤外線送信部を作る。
  4. 全部組み合わせて動かす。

まずは、”1. 赤外線読み込み部を作る”からやっていきます。

1. 赤外線読み込み部を作る

 こちらの記事が参考になりそうですね。コードは記事中に載っているIRremote Arduino Libraryを使えば簡単にできそうです。こちらのライブラリはIDEからもインストールできるみたいですね。

f:id:moutakusan:20200907221021p:plain
IRremote LibraryはIDEからインストールできる
readmeのHintsの中に

Use the IRrecvDumpV2 example to dump out the IR timing. You can then reproduce/send this timing with the IRsendRawDemo example. For long codes like from air conditioners, you can change the length of the input buffer in IRremoteInt.h.

なんていう記述もあり、やりたいことほぼそのまんまです。あと、エアコンはテレビなどに比べて長めの赤外線信号でやりとりしてるみたいなんで、それにも対応が必要らしいってことはわかってたんですが、そちらの問題もケアできそうです。これを参考に作っていきましょう。

 赤外線受光モジュールは秋月電子様のこちらを使います。データシートを見ると、回路例が載ってるのでこの通りに作っていきましょう。(For Noisy Power Supplyとあるので、Vsが安定してるならR2とC1は必要ないかも。)

f:id:moutakusan:20200906161859p:plain
赤外線受光モジュール回路例

材料

コーディング

 さっそく、IRremoteをインストールしたときについてきたスケッチ例の中の”IRrecvDumpV2.ino”を動かしてみましょう(こちらです)。ざっと見た感じ、受信用のピンだけ回路に合わせて変えれば動くのかな。まぁやってみましょう。

実行結果

f:id:moutakusan:20200908223215p:plain
ピッ

f:id:moutakusan:20200908223306p:plain
パッ

 動きましたー。よくわからない形式のデータがダンプされてますね。次回はここらへんの調査をしていきます。


つづく。

ProMicroで熱中症アラート付き温度計を作ってみた

 ProMicroを手に入れました。正確にはProMicroの互換品ですが。

 ProMicro自体もArduino Leonardoの互換品ということで、互換品の互換品になりますね。今回はProMicroと温度センサモジュールとキャラクLCDモジュールを使って温度計を作ってみましょう。
 どうせなら、熱中症の危険度がLEDの色で一目でわかるようにしてみましょう。

仕様

 温度計なので、LCDに温度を表示させるのは決まりです。LEDの色と閾値を決めなければいけません。
 こちらの環境省の資料によれば部屋の温度が28℃が最もよく、24℃以下では外気温との温度差が大きく体に負担とのことです。
 また、こちらの資料では、暑さ指数(WGBT)が

暑さ指数 危険度
28℃~31℃ 厳重警戒
31℃以上 危険

となっていますね。
 暑さ指数は実際には湿度によっても変わるんですが、今回は手元に湿度計がないので温度だけ見てアラートを出すことにします。湿度測定への拡張は作ってしまってからでも簡単にできますしね。
 ということで、LEDの色と閾値は下記の感じでいきましょう。

室温 危険度
24℃以下 黄色
24~28.5℃ 青色
28.5~31℃ 黄色
31℃以上 赤色

材料

回路

 こんな感じです。 f:id:moutakusan:20200905170158p:plain

 LEDの電流制限抵抗は赤が順方向電圧2.1V minで、最大定格50mAなので、
    RRED = (5-1.8)V/0.05A = 64 ohm
青と緑は順方向電圧2.9V minで、最大定格30mAなので、
    RBG = (5-2.9)V/0.03A = 70 ohm
って感じで、抵抗のMIN値だけ計算して、適当に決めました。

配線

こんなんになりました。配線多くて見た目はいまいちだなぁ。 f:id:moutakusan:20200905174246p:plain

こちらの記事とかだと、ユニバーサル基板にLCDモジュールを取り付けてきれいに配線してるなぁ。今後の課題です。

コード

基本的にはArduino IDEに収録されてるコードのコピペで作れてしまいます。今回は、

  • ファイル → スケッチ例 → Wire → master_reader
  • ファイル → スケッチ例 → LiquidCrystal → HelloWorld

の二つをベースに作りました。

#include <Wire.h>
#include <LiquidCrystal.h>

const int rs = 9, en = 10, d4 = 8, d5 = 7, d6 = 6, d7 = 5;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

const int rled=15, gled=14, bled = 16;

void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
  Wire.beginTransmission(0x48);
  Wire.write(0x03);
  Wire.write(0x80); 
  Wire.endTransmission();
  delay(240);  

  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.print("temperature");  

  pinMode(rled,OUTPUT);
  pinMode(bled,OUTPUT);
  pinMode(gled,OUTPUT);
  digitalWrite(rled,LOW);
  digitalWrite(gled,LOW);
  digitalWrite(bled,LOW);  
  
}

void loop() {
  Wire.requestFrom(0x48, 2);    // request 6 bytes from slave device #8

  int byteData;
  int data=0;
  float temp;
  while (Wire.available()) { // slave may send less than requested
    byteData = (Wire.read()<<8) | Wire.read(); // receive a byte as character
    if(byteData & 0x8000){
      data = -((~byteData & 0x7fff) +1 );
    }
    else{
      data = byteData&0x7fff;
    }
  }
  temp = data*0.0078;

  // set the cursor to column 0, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  lcd.setCursor(0, 1);
  // print the number of seconds since reset:
  lcd.print(temp);

  lcd.setCursor(6,1);
  lcd.print("degC");

  if(temp>31){
    digitalWrite(rled,HIGH);
    digitalWrite(gled,LOW);
    digitalWrite(bled,LOW);
  }
  else if (temp>28.5| temp<24){
    digitalWrite(rled,HIGH);
    digitalWrite(gled,HIGH);
    digitalWrite(bled,LOW);
    
  }
  else{
    digitalWrite(rled,LOW);
    digitalWrite(gled,LOW);
    digitalWrite(bled,HIGH);        
  }

  delay(500);
}

完成品

 室温が28℃以下の部屋で、温度センサー部を指で温めて動作を確認します。

f:id:moutakusan:20200905231410g:plain
28.5℃以下から28.5℃以上
f:id:moutakusan:20200905231518g:plain
31℃以下から31℃以上

 動きました!温度によってLEDの色も変化してくれていますね。ただ、黄色がただの赤と緑になってますね。フルカラーLEDを初めて使ったんですが、上手く色が混ざらないものなんでしょうか?拡散板とか使えばいいのかなぁ。もしくは輝度調整?また次の機会にそこらへんは見ていくことにしましょう。