ロータリーエンコーダーで操作する赤外線リモコンをM5ATOM Matrixで作る。

M5Stack

 家電の音量調整はボタンよりもノブやダイヤルを回すほうが絶対に直感的だ。という電子工作の作品。

 コンテスト「M5Stack Creativity Contest」応募用。応募作品は以下。

 音量はボタンじゃなくノブで調整したい

 ウチで使用しているAVアンプとテレビの音量をノブで調整したくて作ったもの。特にAVアンプは可変幅が大きく、ボタン押しっぱなしとかだとうまく調整ができなかったので。

 基板には余っていたD1 mini用のプロとボードを使用(2個頼んだつもりが20個届いた)。ソケット幅はATOMとは互換性がなかったものの、1列ぶんはそのまま使用。ソケットとなりの列のピン穴はつながってるので、はんだブリッジが最小で済むかな、というのもあり。

 コードは以下。まずはメインプログラム。使用するライブラリも記載済み。ロータリーエンコーダー用とIR(赤外線)リモート用。

#include "M5Atom.h"
#include "dispChar.h"
#include <RotaryEncoder.h>  //https://github.com/mathertel/RotaryEncoder
#include <IRremote.h>    //https://github.com/SensorsIot/Arduino-IRremote

#define PIN_SEND 33   // 内蔵は12、IR UNITは26、シールドは33
#define PIN_ENC_SWITCH 22 // エンコーダーのスイッチ

IRsend irsend(PIN_SEND);

bool switchStatus    = HIGH;
bool switchStatusOld = HIGH;

// Setup a RoraryEncoder
RotaryEncoder encoder(19, 23);

// LED用
extern const unsigned char AtomImageData[375 + 2];
uint8_t DisBuff[2 + 5 * 5 * 3];
void setBuff(uint8_t Rdata, uint8_t Gdata, uint8_t Bdata)
{
    DisBuff[0] = 0x05;
    DisBuff[1] = 0x05;
    for (int i = 0; i < 25; i++)
    {
        DisBuff[2 + i * 3 + 0] = Rdata;
        DisBuff[2 + i * 3 + 1] = Gdata;
        DisBuff[2 + i * 3 + 2] = Bdata;
    }
}

uint8_t mode = 0;


void setup()
{
  M5.begin(true, false, true);
  delay(10);

  pinMode(PIN_ENC_SWITCH, INPUT_PULLUP);   // エンコーダーのスイッチ

  // LED用
  for(int i=0; i< 3; i++){
    setBuff(0x20, 0x20, 0x20);  // 白
    M5.dis.displaybuff(DisBuff);
    delay(150);  
    setBuff(0x0, 0x0, 0x0);  // 黒
    M5.dis.displaybuff(DisBuff);
    delay(150);  
  }
  setBuff(0x30, 0x00, 0x00);  // 赤
  M5.dis.displaybuff(DisBuff);
  delay(50);  
  dispChar('T',  0x30, 0x0, 0x0 ,0,0,0);  // T 赤
 
} // setup()


// Read the current position of the encoder and print out when changed.
void loop()
{
  static int pos = 0;
  encoder.tick();

  int newPos = encoder.getPosition();
  if (pos != newPos) {
    //Serial.print(newPos);
    //Serial.println();

    RotaryEncoder::Direction dir = encoder.getDirection();
    //Serial.print("Direction "); // 方向
    if (dir == RotaryEncoder::Direction::CLOCKWISE) {
         //Serial.println(1);   // 方向 時計回り 1
      switch(mode){
        case 0:
          irsend.sendNEC(0x02fd58a7, 32); //TOSHIBA REGZA VOLUME UP 02fd58a7 
          break;
        case 1:
          irsend.sendNEC(0x5ea158a7, 32); // YAMAHA VOLUME UP
          break;
        case 2:
          irsend.sendNEC(0x60C5D827, 32); // FAN UP
          break;
      }
    } else {
         //Serial.println(-1);  // 方向 反時計回り -1
      switch(mode){
        case 0:
          irsend.sendNEC(0x02fd7887, 32); //TOSHIBA REGZA VOL DOWN 02fd7887
          break;
        case 1:
          irsend.sendNEC(0x5ea1d827, 32); // YAMAHA VOLUME DOWN
          break;
        case 2:
          irsend.sendNEC(0x60C548B7, 32); // YAMAHA VOLUME DOWN
          break;
      }
    }

    pos = newPos;
  } // if

    if (M5.Btn.wasPressed())
    {
      Serial.print("button pressed");
      Serial.println();

      mode++;
      if (mode >= 3)
      {
          mode = 0;
      }

      switch (mode)
      {
      case 0:
          setBuff(0x30, 0x00, 0x00);  // 赤
          M5.dis.displaybuff(DisBuff);
          dispChar('T',  0x30, 0x0, 0x0 ,0,0,0);  // T赤
          break;
      case 1:
          setBuff(0x00, 0x30, 0x00);  // 緑
          M5.dis.displaybuff(DisBuff);
          dispChar('A',  0x0, 0x30, 0x0 ,0,0,0);  // A緑
          break;
      case 2:
          setBuff(0x00, 0x00, 0x30);  // 青
          M5.dis.displaybuff(DisBuff);
          dispChar('F',  0x0, 0x0, 0x30 ,0,0,0);  // F青
          break;
      case 3:
          setBuff(0x10, 0x10, 0x10);  // 白
          M5.dis.displaybuff(DisBuff);
          break;
      default:
          break;
      }

    }

    switchStatus = digitalRead(PIN_ENC_SWITCH);
    if(switchStatus != switchStatusOld){

      if(switchStatus == LOW){  // ONになった時
        Serial.println("SW ON! ");
        //irsend.sendNEC(0x02fd7887, 32); //TOSHIBA REGZA VOL DOWN 02fd7887

        switch (mode)
        {
          case 0:
              irsend.sendNEC(0x02FD48B7, 32); // TV ON/OFF 
              delay(500);
              break;
          case 1:
              irsend.sendNEC(0x5EA1B847, 32); // AMP ON 
              delay(500);
              break;
          case 2:
              irsend.sendNEC(0x60C5F807, 32); // FAN ON/OFF
              delay(500);
              break;
          case 3:
              break;
          default:
              break;
        }

      } else {                  // OFFになった時
        Serial.println("SW OFF...");
      }
      switchStatusOld = switchStatus;
    }

    
    M5.update();
    //delay(50);
  
} // loop ()

 キャラクター表示用のライブラリ的なもの。「dispChar.h」として保存。新規タブの追加で。micro:bitと同じフォントを表示するやつ。


uint8_t FONTDATA[475] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x8, 0x0, 0x8, 0xa, 0x4a, 0x40, 0x0, 0x0, 0xa, 0x5f, 0xea, 0x5f, 0xea, 0xe, 0xd9, 0x2e, 0xd3, 0x6e, 0x19, 0x32, 0x44, 0x89, 0x33, 0xc, 0x92, 0x4c, 0x92, 0x4d, 0x8, 0x8, 0x0, 0x0, 0x0, 0x4, 0x88, 0x8, 0x8, 0x4, 0x8, 0x4, 0x84, 0x84, 0x88, 0x0, 0xa, 0x44, 0x8a, 0x40, 0x0, 0x4, 0x8e, 0xc4, 0x80, 0x0, 0x0, 0x0, 0x4, 0x88, 0x0, 0x0, 0xe, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x0, 0x1, 0x22, 0x44, 0x88, 0x10, 0xc, 0x92, 0x52, 0x52, 0x4c, 0x4, 0x8c, 0x84, 0x84, 0x8e, 0x1c, 0x82, 0x4c, 0x90, 0x1e, 0x1e, 0xc2, 0x44, 0x92, 0x4c, 0x6, 0xca, 0x52, 0x5f, 0xe2, 0x1f, 0xf0, 0x1e, 0xc1, 0x3e, 0x2, 0x44, 0x8e, 0xd1, 0x2e, 0x1f, 0xe2, 0x44, 0x88, 0x10, 0xe, 0xd1, 0x2e, 0xd1, 0x2e, 0xe, 0xd1, 0x2e, 0xc4, 0x88, 0x0, 0x8, 0x0, 0x8, 0x0, 0x0, 0x4, 0x80, 0x4, 0x88, 0x2, 0x44, 0x88, 0x4, 0x82, 0x0, 0xe, 0xc0, 0xe, 0xc0, 0x8, 0x4, 0x82, 0x44, 0x88, 0xe, 0xd1, 0x26, 0xc0, 0x4, 0xe, 0xd1, 0x35, 0xb3, 0x6c, 0xc, 0x92, 0x5e, 0xd2, 0x52, 0x1c, 0x92, 0x5c, 0x92, 0x5c, 0xe, 0xd0, 0x10, 0x10, 0xe, 0x1c, 0x92, 0x52, 0x52, 0x5c, 0x1e, 0xd0, 0x1c, 0x90, 0x1e, 0x1e, 0xd0, 0x1c, 0x90, 0x10, 0xe, 0xd0, 0x13, 0x71, 0x2e, 0x12, 0x52, 0x5e, 0xd2, 0x52, 0x1c, 0x88, 0x8, 0x8, 0x1c, 0x1f, 0xe2, 0x42, 0x52, 0x4c, 0x12, 0x54, 0x98, 0x14, 0x92, 0x10, 0x10, 0x10, 0x10, 0x1e, 0x11, 0x3b, 0x75, 0xb1, 0x31, 0x11, 0x39, 0x35, 0xb3, 0x71, 0xc, 0x92, 0x52, 0x52, 0x4c, 0x1c, 0x92, 0x5c, 0x90, 0x10, 0xc, 0x92, 0x52, 0x4c, 0x86, 0x1c, 0x92, 0x5c, 0x92, 0x51, 0xe, 0xd0, 0xc, 0x82, 0x5c, 0x1f, 0xe4, 0x84, 0x84, 0x84, 0x12, 0x52, 0x52, 0x52, 0x4c, 0x11, 0x31, 0x31, 0x2a, 0x44, 0x11, 0x31, 0x35, 0xbb, 0x71, 0x12, 0x52, 0x4c, 0x92, 0x52, 0x11, 0x2a, 0x44, 0x84, 0x84, 0x1e, 0xc4, 0x88, 0x10, 0x1e, 0xe, 0xc8, 0x8, 0x8, 0xe, 0x10, 0x8, 0x4, 0x82, 0x41, 0xe, 0xc2, 0x42, 0x42, 0x4e, 0x4, 0x8a, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x8, 0x4, 0x80, 0x0, 0x0, 0x0, 0xe, 0xd2, 0x52, 0x4f, 0x10, 0x10, 0x1c, 0x92, 0x5c, 0x0, 0xe, 0xd0, 0x10, 0xe, 0x2, 0x42, 0x4e, 0xd2, 0x4e, 0xc, 0x92, 0x5c, 0x90, 0xe, 0x6, 0xc8, 0x1c, 0x88, 0x8, 0xe, 0xd2, 0x4e, 0xc2, 0x4c, 0x10, 0x10, 0x1c, 0x92, 0x52, 0x8, 0x0, 0x8, 0x8, 0x8, 0x2, 0x40, 0x2, 0x42, 0x4c, 0x10, 0x14, 0x98, 0x14, 0x92, 0x8, 0x8, 0x8, 0x8, 0x6, 0x0, 0x1b, 0x75, 0xb1, 0x31, 0x0, 0x1c, 0x92, 0x52, 0x52, 0x0, 0xc, 0x92, 0x52, 0x4c, 0x0, 0x1c, 0x92, 0x5c, 0x90, 0x0, 0xe, 0xd2, 0x4e, 0xc2, 0x0, 0xe, 0xd0, 0x10, 0x10, 0x0, 0x6, 0xc8, 0x4, 0x98, 0x8, 0x8, 0xe, 0xc8, 0x7, 0x0, 0x12, 0x52, 0x52, 0x4f, 0x0, 0x11, 0x31, 0x2a, 0x44, 0x0, 0x11, 0x31, 0x35, 0xbb, 0x0, 0x12, 0x4c, 0x8c, 0x92, 0x0, 0x11, 0x2a, 0x44, 0x98, 0x0, 0x1e, 0xc4, 0x88, 0x1e, 0x6, 0xc4, 0x8c, 0x84, 0x86, 0x8, 0x8, 0x8, 0x8, 0x8, 0x18, 0x8, 0xc, 0x88, 0x18, 0x0, 0x0, 0xc, 0x83, 0x60};

void dispChar(char str, uint8_t R, uint8_t G, uint8_t B ,uint8_t Rb, uint8_t Gb, uint8_t Bb){

    if(str < 32 || str > 127){
      Serial.println("dispChar Error...");
      return;
    }
    
    uint8_t DisBuffString[2 + 5 * 5 * 3];
    DisBuffString[0] = 0x05;
    DisBuffString[1] = 0x05;
    int start = ((int)str -32) * 5;
    
    for (int row = 0 ; row < 5 ; row++){
      for (int i = 0; i < 5; i++){ 
        if(( FONTDATA[ start + row ] & (1 << abs(i-4))) != 0){
          DisBuffString[2 + ((row) * 5 + i) * 3 + 0] = R;
          DisBuffString[2 + ((row) * 5 + i) * 3 + 1] = G;
          DisBuffString[2 + ((row) * 5 + i) * 3 + 2] = B;
        }
        else {
          DisBuffString[2 + ((row) * 5 + i) * 3 + 0] = Rb;
          DisBuffString[2 + ((row) * 5 + i) * 3 + 1] = Gb;
          DisBuffString[2 + ((row) * 5 + i) * 3 + 2] = Bb;
        }
      }
    }
    M5.dis.displaybuff(DisBuffString);
}

 気になった点はあとで追加していきます。

 なお、赤外線LEDと増幅用のトランジスタ、抵抗の配線については、以前の記事「ESP8266ボードで赤外線リモコンを作る」とほぼ同じ。ピン番号が違う程度。いや、LEDに直列の抵抗も省きました(計算上は問題なさそうなので)。ウチのリビング(8畳)内ならどこからでもリモコンの信号が問題なく届く感じです。

コメント