M5Stack+M5PianoをMIDIキーボード化

#M5Pianoについて
　先日記事を投稿しました。
　https://qiita.com/siroitori0413/items/9e0cb11495a8bbfeffd8

#MIDIキーボード化
　M5Stack+M5PianoをMIDIキーボード化することに成功したので記載しておきます。
　当方MIDIについては初心者ですのでおかしな表記あればご指摘ください。

◆使用したソフト類

* loopMIDI　：仮想MIDIポート<br>
https://www.tobias-erichsen.de/software/loopmidi.html
* The Hairless MIDI<->Serial Bridge　：仮想MIDIとシリアルポートをつなぐ役割<br>
https://projectgus.github.io/hairless-midiserial/
* Music Studio Producer（MSP）　：音楽制作ソフト<br>
http://forest.watch.impress.co.jp/library/software/msproducer/

　[★参考にさせていただいたサイトはこちら](https://computer-technology.hateblo.jp/entry/2016/12/03/MIDI%E3%82%AD%E3%83%BC%E3%83%9C%E3%83%BC%E3%83%89%E3%82%92PC%E3%81%AB%E3%81%A4%E3%81%AA%E3%81%84%E3%81%A7%E9%9F%B3%E3%82%92%E5%87%BA%E3%81%99%E3%81%9F%E3%82%81%E3%81%AE%E6%96%B9%E6%B3%95)

#プログラム（M5Stack）
キーボードがタッチされたときと手が離されたときそれぞれに、MIDIに信号を送信します。
MIDI.sendNoteOn(note, velocity, channel)
MIDI.sendNoteOff(note, velocity, channel)

```C++
#include <MIDI.h>

#include <M5Stack.h>
#include <Wire.h>

#include <FastLED.h>
// #include <Adafruit_NeoPixel.h>
#define LED_PIN            2

#define NUM_LEDS_PER_STRIP     29
CRGB Leds[NUM_LEDS_PER_STRIP];
// Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

#define LF        0x0A //New Line
#define CR        0x0D //Carriage  return
#define SP        0x20 //Spcae

#define Sensitivity_PWM1  0x00 //ch2,ch1
#define Sensitivity_PWM2  0x01 //ch4,ch3
#define Sensitivity_PWM3  0x02 //ch6,ch5
#define Sensitivity_PWM4  0x03 //ch21,ch7
#define Sensitivity_PWM5  0x04 //ch9,ch8
#define Sensitivity_PWM6  0x05 //ch11,ch10
#define Sensitivity_PWM7  0x06 //ch13,ch12
#define Sensitivity_PWM8  0x07 //ch15,ch14
#define Sensitivity_PWM9  0x08 //ch17,ch16
#define Sensitivity_PWM10 0x09 //ch19,ch18
#define Sensitivity_PWM11   0x0A //ch20

#define GTRL1     0x0B
#define GTRL2     0x0C

#define Cal_CTRL    0x0D

#define Port_CTRL1    0x0E
#define Port_CTRL2    0x0F
#define Port_CTRL3    0x10
#define Port_CTRL4    0x11
#define Port_CTRL5    0x12
#define Port_CTRL6    0x13

#define Cal_HOLD1   0x14
#define Cal_HOLD2   0x15
#define Cal_HOLD3   0x16

#define Err_CTRL    0x17

#define Output1     0x20
#define Output2     0x21
#define Output3     0x22

#define Ref_wr_H    0x23
#define Ref_wr_L    0x24
#define Ref_wr_CH1    0x25
#define Ref_wr_CH2    0x26
#define Ref_wr_CH3    0x27

#define Sen_RD_CTRL   0x28
#define Sensitivity_RD  0x29

#define Rd_CH1      0x30
#define Rd_CH2      0x31
#define Rd_CH3      0x32
#define Sen_H     0x33
#define Sen_L     0x34
#define Ref_H     0x35
#define Ref_L     0x36
#define Rd_CH1      0x37
#define Rd_CH2      0x38
#define Rd_CH3      0x39

#define TS20_SLAVE_GND  0x6A //0xD4<<1 //ADD Pin = GND
#define TS20_SLAVE_VDD  0x7A //0xF4<<1 //ADD Pin = VDD

void  Init_TS20(void); //Initialize TS20

#define RESET_PIN 7 //Reset pin
#define EN_PIN    6 //I2C Enable Pin
#define Set_Bit(val, bitn)    (val |=(1<<(bitn)))
#define Clr_Bit(val, bitn)     (val&=~(1<<(bitn)))
#define Get_Bit(val, bitn)    (val &(1<<(bitn)) )

MIDI_CREATE_DEFAULT_INSTANCE();

byte key[4];
// #define pRed pixels.Color(5,0,0)
// #define pBlack pixels.Color(0,0,0)

boolean notePressed[25];

void setup() {

  M5.begin();
  Wire.begin(26, 5);       // join i2c bus (address optional for master)
  MIDI.begin(1); // MIDI BEGIN!
  Serial.begin(115200);  // start serial for output (Spped)
  delay(100); //wait for 100[msec]
  Init_TS20(); //Initialize TS20
  delay(100); //wait for 100[msec]
  key[0] = 0;
  key[1] = 0;
  key[2] = 0;
  key[3] = 0;

  // 一番最初に光る緑の電源LED
  pinMode(2, OUTPUT);
  digitalWrite(2, HIGH);
  delay(500);
  digitalWrite(2, LOW);  // これが点いているとランプの位置がずれるのでOFFっておく

  // キーボードの上のLED
  FastLED.addLeds<NEOPIXEL, LED_PIN>(Leds, NUM_LEDS_PER_STRIP);
  FastLED.setBrightness(10);
  // pixels.begin();

  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setCursor(1, 10);
  M5.Lcd.setTextColor(TFT_YELLOW, TFT_BLACK);
  M5.Lcd.setTextSize(3);
  M5.Lcd.printf("M5PIANO");

  for (int i = 0; i < 29; i++)
  {
    Leds[i] = CRGB::Red;
    // pixels.setPixelColor(i, pRed);
    delay(20);
    FastLED.show();
  }
  delay(1000);
  for (int i = 0; i < 29; i++)
  {
    Leds[i] = CRGB::Black;
    // FastLED.setPixelColor(i, pBlack);
    delay(10);
    FastLED.show();
  }

  ledcSetup(TONE_PIN_CHANNEL, 0, 10);
  ledcAttachPin(SPEAKER_PIN, TONE_PIN_CHANNEL);
}

// キーのタッチ判定処理：LED・音
void refresh(int ledNo, int data, byte b, int keyCnt, int setValue) {
  if ((data & b) == b) {
    Leds[ledNo] = CRGB::Blue;
    if (notePressed[setValue-36] == false) {
      notePressed[setValue-36] = true;
      MIDI.sendNoteOn(setValue, 127, 1);
    }
  } else {
    if (notePressed[setValue-36] == true) {
      notePressed[setValue-36] = false;
      MIDI.sendNoteOff(setValue, 127, 1);
    }
    Leds[ledNo] = CRGB::Black;
    Clr_Bit(key[keyCnt], setValue);
  }
}

void loop() {

  int data0, data1, data2, data3, data4, data5;
  delay(5);

  Wire.beginTransmission(TS20_SLAVE_GND); // sned ic slave address
  Wire.write(byte(Output1)); // sends register address
  Wire.endTransmission(); // stop transmitting
  Wire.requestFrom(TS20_SLAVE_GND, 3); // key data read (3 byte)
  data0 = Wire.read();
  data1 = Wire.read();
  data2 = Wire.read();
  Wire.beginTransmission(TS20_SLAVE_VDD); // sned ic slave address
  Wire.write(byte(Output1)); // sends register address
  Wire.endTransmission(); // stop transmitting
  Wire.requestFrom(TS20_SLAVE_VDD, 3); // key data read (3 byte)
  data3 = Wire.read();
  data4 = Wire.read();
  data5 = Wire.read();


  M5.Lcd.setCursor(1, 50);
  M5.Lcd.printf("%d,%d,%d,%d,%d,%d   ", data0, data1, data2, data3, data4, data5);

  for (int i = 0; i < 29; i++)
  {
    Leds[i] = CRGB::Black;
    // pixels.setPixelColor(i, pBlack);
  }

  // キータッチ判定、LED/音
  refresh(0, data0, 0x10, 0, 36);
  refresh(1, data0, 0x20, 0, 37);
  refresh(2, data0, 0x40, 0, 38);
  refresh(3, data1, 0x01, 0, 39);
  refresh(4, data1, 0x02, 0, 40);
  refresh(6, data2, 0x04, 0, 41);
  refresh(7, data2, 0x08, 0, 42);
  refresh(8, data2, 0x10, 0, 43);
  refresh(9, data0, 0x01, 1, 44);
  refresh(10, data0, 0x02, 1, 45);
  refresh(11, data0, 0x04, 1, 46);
  refresh(12, data0, 0x08, 1, 47);

  refresh(14, data3, 0x10, 1, 48);
  refresh(15, data3, 0x20, 1, 49);  //
  refresh(16, data3, 0x40, 1, 50);
  refresh(17, data4, 0x01, 1, 51);  //
  refresh(18, data4, 0x02, 2, 52);
  refresh(20, data5, 0x02, 2, 53);
  refresh(21, data5, 0x04, 2, 54);  //
  refresh(22, data5, 0x08, 2, 55);
  refresh(23, data5, 0x10, 2, 56);  //
  refresh(24, data3, 0x01, 2, 57);
  refresh(25, data3, 0x02, 2, 58);  //
  refresh(26, data3, 0x04, 2, 59);

  refresh(28, data3, 0x08, 3, 60);

  FastLED.show();

  M5.update();

}

void  Init_TS20(void)
{
  //------------------ Software Reset Enable (Set)----------------------
  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(GTRL2)); // sends register address
  Wire.write(byte(0x1A)); // sends register data
  Wire.endTransmission(); // stop transmitting

  //----------------- Port Control ------------------------------------
  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Port_CTRL1)); // sends register address
  Wire.write(byte(0x00)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Port_CTRL2)); // sends register address
  Wire.write(byte(0x00)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Port_CTRL3)); // sends register address
  Wire.write(byte(0x00)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Port_CTRL4)); // sends register address
  Wire.write(byte(0x00)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Port_CTRL5)); // sends register address
  Wire.write(byte(0x00)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Port_CTRL6)); // sends register address
  Wire.write(byte(0x00)); // sends register data
  Wire.endTransmission(); // stop transmitting

  //--------------- Sensitivty control,(threshold level)-----------
  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Sensitivity_PWM1)); // sends register address
  Wire.write(byte(0x55)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Sensitivity_PWM2)); // sends register address
  Wire.write(byte(0x55)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Sensitivity_PWM3)); // sends register address
  Wire.write(byte(0x55)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Sensitivity_PWM4)); // sends register address
  Wire.write(byte(0xF5)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Sensitivity_PWM5)); // sends register address
  Wire.write(byte(0x55)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Sensitivity_PWM6)); // sends register address
  Wire.write(byte(0x55)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Sensitivity_PWM7)); // sends register address
  Wire.write(byte(0x55)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Sensitivity_PWM8)); // sends register address
  Wire.write(byte(0x55)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); /// send ic slave address
  Wire.write(byte(Sensitivity_PWM9)); // sends register address
  Wire.write(byte(0x55)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Sensitivity_PWM10)); // sends register address
  Wire.write(byte(0x55)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // sned ic slave address
  Wire.write(byte(Sensitivity_PWM11)); // sends register address
  Wire.write(byte(0x55)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(GTRL1)); // sends register address
  Wire.write(byte(0x4B)); // sends register data
  Wire.endTransmission(); // stop transmitting
  //------------------ Calibration On ---------------------------
  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Cal_HOLD1)); // sends register address
  Wire.write(byte(0x00)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Cal_HOLD2)); // sends register address
  Wire.write(byte(0x00)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Cal_HOLD3)); // sends register address
  Wire.write(byte(0x00)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Err_CTRL)); // sends register address
  Wire.write(byte(0x0D)); // sends register data
  Wire.endTransmission(); // stop transmitting

  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(Cal_CTRL)); // sends register address
  Wire.write(byte(0xFA)); // sends register data
  Wire.endTransmission(); // stop transmitting

  //------------------ Software Reset Disable (Clear) -----------------------
  Wire.beginTransmission(TS20_SLAVE_GND); // send ic slave address
  Wire.write(byte(GTRL2)); // sends register address
  Wire.write(byte(0x12)); // sends register data
  Wire.endTransmission(); // stop transmitting

}
```

#動かし方
① M5StackとM5Pianoを接続しプログラムを書き込んだ後、M5StackとPCをUSBケーブルでつなぐ
② loopMIDIを起動して適当な名前を付けて追加
![2020-12-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308578/a4dc25de-b72a-15bd-c6c1-4cace40c663e.png)
③ Hairless MIDIを開き、左上の「Serial<->MIDI Bridge On」の<b>チェックを外して</b>から、
・Serial Port にはM5StackがUSB接続されているCOMポートを選択
・MIDI Out には②でつけた名前を選択
![2020-12-11 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308578/7cb0f7c4-44ca-bb18-3cd8-eb9165c822b2.png)
④ M5Pianoの鍵盤を数回たたきながら③の「Serial<->MIDI Bridge On」のチェックをONにする
　※こちらの環境のみかもしれませんがなぜかこうすることでチェックが入れられました。すんなりチェックが入るならそれでOKです。

　これでMIDIキーボードとして接続が出来ました。
　M5Pianoの鍵盤をたたくことでPCで音を鳴らしたり編集したりすることができます。
![DSC_4611.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308578/3faf2bd6-80f5-0129-3f2a-02dcbbe3d242.jpeg)

#注意
MIDIキーボードとして接続した後M5tackのプログラムをArduino IDEから再度書き込むときは接続しているポートをMIDI用でつかんだままになっているのでいったん接続を解除（Hairless MIDIの「Serial<->MIDI Bridge On」のチェックOFF）しましょう。