#M5Piano
AliExpressで購入しました。
とてもかわいいM5Stack公式のピアノボードです。公式サイトでの名前は「Acrylic Piano Board with RGB LED」と書かれています。M5Pianoが正しい名前なのかはわかりませんが、本体裏面には「M5.Piano」と大きく印字されています。
M5Stack(私はM5Stack Basicを使用)の側面にピンを差し込んで使用します。
2018年に発売されたもののようですが、日本では未発売のお品のようです。
すべての鍵盤(白鍵も黒鍵も)にタッチセンサーが付いており、またすべてのキーの上部にLEDが付いています。
LEDは均等に配置されているので黒鍵が無い場所(ミとファの間、シとドの間)にも余分にLEDが付いています。
#プログラム
サンプルコードをネット検索で下記1件だけ見つけました。
https://github.com/m5stack/Applications-M5PIANO
起動時にLEDをすべて順に点灯させたあと消灯。鍵盤をタッチすると音が鳴り、タッチした鍵盤の上のLEDランプが光るプログラムです。
ただしかしこれ爆音で音が割れるうえ、音が止まらなくなって暴走することしばしば。
ということで上記ソースをベースに修正しました。
【修正/改造点】
- 音をボリューム指定できるように修正(プログラムでは3に設定しています)
下記サイト参考にさせていただきました。ありがとうございます。
http://nuneno.cocolog-nifty.com/blog/2020/01/post-b9cd2c.html - 黒鍵の音(半音)が白鍵の音と同じ音に設定されていたので修正
- キーを押したときに光るLEDの位置が最初のほうがおかしい点の修正
- 一部関数化
#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)) )
byte key[4];
// #define pRed pixels.Color(5,0,0)
// #define pBlack pixels.Color(0,0,0)
uint16_t CurrentVolume = 3; // 音量
void setup() {
M5.begin();
Wire.begin(26, 5); // join i2c bus (address optional for master)
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();
}
// Speaker初期化
ledcSetup(TONE_PIN_CHANNEL, 0, 10);
ledcAttachPin(SPEAKER_PIN, TONE_PIN_CHANNEL);
}
// toneEx
// 引数
// frequency (Hz)
// duration 鳴動時間
void toneEx(uint16_t frequency, uint16_t duration){
toneOn(frequency, CurrentVolume);
delay(duration);
toneOff();
}
// toneOn
// 引数
// frequency (Hz)
// vol (0 ~ 9、0:無音 9:最大)
void toneOn(uint16_t frequency, uint16_t vol) {
ledcSetup(TONE_PIN_CHANNEL, frequency, 10);
ledcWrite(TONE_PIN_CHANNEL,0x1FF>>(9-vol));
}
// toneOff
void toneOff() {
ledcWriteTone(TONE_PIN_CHANNEL, 0);
digitalWrite(SPEAKER_PIN, 0);
}
#define NOTE_D0 -1
#define NOTE_DL1 261 // frequency (Hz) ド
#define NOTE_DL1_ 277 // ド#
#define NOTE_DL2 293 // レ
#define NOTE_DL2_ 311 // レ#
#define NOTE_DL3 329 // ミ
#define NOTE_DL4 349 // ファ
#define NOTE_DL4_ 370 // ファ#
#define NOTE_DL5 392 // ソ
#define NOTE_DL5_ 415 // ソ#
#define NOTE_DL6 440 // ラ
#define NOTE_DL6_ 466 // ラ#
#define NOTE_DL7 494 // シ
#define NOTE_D1 523 // ド
#define NOTE_D1_ 554 // ド#
#define NOTE_D2 587 // レ
#define NOTE_D2_ 622 // レ#
#define NOTE_D3 659 // ミ
#define NOTE_D4 698 // ファ
#define NOTE_D4_ 740 // ファ#
#define NOTE_D5 784 // ソ
#define NOTE_D5_ 830 // ソ#
#define NOTE_D6 880 // ラ
#define NOTE_D6_ 932 // ラ#
#define NOTE_D7 988 // シ
#define NOTE_DH1 1046 // ド
#define NOTE_DH1_ 1108 // ド#
#define NOTE_DH2 1174 // レ
#define NOTE_DH2_ 1244 // レ#
#define NOTE_DH3 1318 // ミ
#define NOTE_DH4 1397 // ファ
#define NOTE_DH4_ 1480 // ファ#
#define NOTE_DH5 1568 // ソ
#define NOTE_DH5_ 1661 // ソ#
#define NOTE_DH6 1760 // ラ
#define NOTE_DH6_ 1864 // ラ#
#define NOTE_DH7 1975 // シ
// キーのタッチ判定処理:LED・音
void setKeySound(int ledNo, int data, byte b, int keyCnt, byte setValue, uint16_t frequency){
if ((data & b) == b) {
Set_Bit(key[keyCnt], setValue);
Leds[ledNo] = CRGB::Red;
// 音の出し方を変更
//M5.Speaker.tone(frequency, 20);
toneEx(frequency, 20); // 20ミリ秒ならす
// 音ON
//toneOn(frequency, CurrentVolume);
} else {
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);
}
// // 最初にOFFる
// toneOff();
// キータッチ判定、LED/音
setKeySound(0, data0, 0x10, 0, 0, NOTE_DL1);
setKeySound(1, data0, 0x20, 0, 1, NOTE_DL1_);
setKeySound(2, data0, 0x40, 0, 2, NOTE_DL2);
setKeySound(3, data1, 0x01, 0, 3, NOTE_DL2_);
setKeySound(4, data1, 0x02, 0, 4, NOTE_DL3);
setKeySound(6, data2, 0x04, 0, 5, NOTE_DL4);
setKeySound(7, data2, 0x08, 0, 6, NOTE_DL4_);
setKeySound(8, data2, 0x10, 0, 7, NOTE_DL5);
setKeySound(9, data0, 0x01, 1, 0, NOTE_DL5_);
setKeySound(10, data0, 0x02, 1, 1, NOTE_DL6);
setKeySound(11, data0, 0x04, 1, 2, NOTE_DL6_);
setKeySound(12, data0, 0x08, 1, 3, NOTE_DL7);
setKeySound(14, data3, 0x10, 1, 4, NOTE_D1);
setKeySound(15, data3, 0x20, 1, 5, NOTE_D1_);
setKeySound(16, data3, 0x40, 1, 6, NOTE_D2);
setKeySound(17, data4, 0x01, 1, 7, NOTE_D2_);
setKeySound(18, data4, 0x02, 2, 0, NOTE_D3);
setKeySound(20, data5, 0x02, 2, 1, NOTE_D4);
setKeySound(21, data5, 0x04, 2, 2, NOTE_D4_);
setKeySound(22, data5, 0x08, 2, 3, NOTE_D5);
setKeySound(23, data5, 0x10, 2, 4, NOTE_D5_);
setKeySound(24, data3, 0x01, 2, 5, NOTE_D6);
setKeySound(25, data3, 0x02, 2, 6, NOTE_D6_);
setKeySound(26, data3, 0x04, 2, 7, NOTE_D7);
setKeySound(28, data3, 0x08, 3, 0, NOTE_DH1);
// 音をStartStopで鳴らすときはここをコメントに
//delay(20);
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
}
サンプルプログラムからそうだったのですが、音がピピピピと少し途切れ途切れになって連続した音になりません。
これはloop()のときに200ミリ秒の音を再生して、次またループに入ったときに200ミリ秒の音を再生して…ってやってるので、ループごとに途切れてしまうためです。
本当はドのキーを押したときにドを出し続けて、手を離したときにドの音を止めたいですが、内部では「音を全部止める」しかできない感じになっているので、例えば和音ドミソ出しててドだけ止めようと思ったら、全部止めてからミソを出す、という風にしか書けないのでどうしても一瞬切れてしまう瞬間があるんです。
それでもその切れ目をなるべくなくそうと思い、音を止めるタイミングをループをまたがないようできないかとか色々工夫してみたんですが、そうすると和音が出せなくなってしまったりと色々問題が発生してしまったので、音が連続しない件はそのままとしました。