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;
}

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

結果

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




まだつづく。