`` M5Stack支持蓝牙,本节就利用BLE功能来做一个蓝牙MIDI外设,通过M5Stack的按键触发MIDI事件,这里仅抛砖引玉,当然你还可以外接更多不同类型的传感器,实现有创意的交互体验。 一、认识MIDI
MIDI(Musical Instrument Digital Interface)乐器数字接口 ,是20 世纪80 年代初为解决电声乐器之间的 通信问题而提出的。MIDI是编曲界最广泛的音乐标准格式,可称为“计算机能理解的乐谱”。它用音符的数字控制信号来记录音乐,目前的 电子类乐器基本都支持MIDI接口,MIDI接口有常用的标准MIDI接口,也有USB-MIDI接口,还有虚拟MIDI接口WIFI-MIDI和BLE-MIDI,今天使用的就是BLE-MIDI,BLE-MIDI好处就是距离远,连接方便,可以做到小型化,甚至可以作为可穿戴的控制系统,任何数据都有可能转变为音乐数据。
二、MIDI数据格式简要说明 MIDI文件由头文件和数据组成,有兴趣的网友可以自行了解,这里只讲重点,也就是接下来程序里需要用到的控制内容。程序中用到的重要参数有三个,
1. MIDI通道: MIDI文件支持16轨道,发送消息需要指定发送的通道 2. 消息类型: 指定要发送的MIDI操作,见下表 3. MIDI参数: MIDI的详细数据,如力度大小、效果器参数
我们发送的MIDI数据一共5个字节,包括头文件两字节 0x80,0x80,数据3字节(通道, 音调/控制器号,力度/控制器参数)
三、BLE BLE服务的建立依赖于GATT,那什么是GATT呢? 所有蓝牙低功耗设备使用“通用属性规范”(GATT)。蓝牙低功耗提供该应用程序接口了解到操作系统通常基于GATT概念。GATT具有以下术语: 客户端 一个发出GATT命令和请求的设备,然后接受响应。例如一个计算机或智能手机。 服务器 一个接受GATT命令和请求的设备,然后返回响应。例如我们的M5Stack。 特征 在客户端与服务器间传递的数据,例如MIDI事件。 服务 有关特征的收集,具有一系列操作来执行特定功能。例如,MIDI事件的提供的服务。 描述符 描述符提供有关特征的其他信息。 简单来说要使用BLE需要进行以下几部:
建立BLE服务器,开启指定服务,声明BLE特征,查找指定服务UUID,查找指定UUID特征,发现指定UUID服务广播,建立连接。
代码实现
基本的LBE框架都在代码里,按照建立BLE服务器的步骤来执行,有两点需要注意,代码中的两处UUID是MIDI设备唯一的识别特征,不可更改
ABC按键分别对应0x3C,0x40和0x43(C3,G3,C4)
- #include <BLEDevice.h>
- #include <BLEServer.h>
- #include <BLEUtils.h>
- #include <BLE2902.h>
-
- #include <M5Stack.h>
-
- ///// Button
- const uint8_t buttonPins[3] = {39, 38, 37, }; //A, B, C
- boolean buttonFlags[3] = {false, false, false};
- uint8_t buttonNote[3] = {60, 64, 67};
-
- BLECharacteristic *pCharacteristic;
- bool deviceConnected = false;
-
- #define MIDI_SERVICE_UUID "03b80e5a-ede8-4b33-a751-6ce34ec4c700"
- #define MIDI_CHARACTERISTIC_UUID "7772e5db-3868-4112-a1a9-f2669d106bf3"
-
- void printConnectionStatus(const char* message){
- M5.Lcd.fillRect(0, 210, 320, 20, BLACK);
- M5.Lcd.setTextSize(2);
- M5.Lcd.setCursor(160,210);
- M5.Lcd.printf("%13s", message);
- }
-
- BLEServer *pServer; // ■
- BLESecurity *pSecurity; //■
-
- class MyServerCallbacks: public BLEServerCallbacks {
- void onConnect(BLEServer* pServer) {
- deviceConnected = true;
- printConnectionStatus("connected");
- Serial.println("connected");
- }
- void onDisconnect(BLEServer* pServer) {
- deviceConnected = false;
- printConnectionStatus("disconnected");
- Serial.println("disconnected");
- }
- };
-
- void noteOn(uint8_t channel, uint8_t pitch, uint8_t velocity) {
- uint8_t midiPacket[] = {0x80, 0x80, (uint8_t)(0x90 | channel), pitch, velocity };
-
- pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes
- pCharacteristic->notify();
- }
- void noteOff(uint8_t channel, uint8_t pitch, uint8_t velocity) {
- uint8_t midiPacket[] = {0x80, 0x80, (uint8_t)(0x80 | channel), pitch, velocity };
-
- pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
- pCharacteristic->notify();
- }
- void controlChange(uint8_t channel, uint8_t control, uint8_t value) {
- uint8_t midiPacket[] = {0x80, 0x80, (uint8_t)(0xB0 | channel), control, value };
- pCharacteristic->setValue(midiPacket, 5); // packet, length in bytes)
- pCharacteristic->notify();
- }
-
- void setup() {
- M5.begin(); //LCD
-
- BLEDevice::init("M5EasyMIDI"); //Device Name ■
-
- // Create the BLE Server
- pServer = BLEDevice::createServer(); // ■
- pServer->setCallbacks(new MyServerCallbacks());
- BLEDevice::setEncryptionLevel((esp_ble_sec_act_t)ESP_LE_AUTH_REQ_SC_BOND);
-
- // Create the BLE Service
- BLEService *pService = pServer->createService(BLEUUID(MIDI_SERVICE_UUID));
-
- // Create a BLE Characteristic
- pCharacteristic = pService->createCharacteristic(
- BLEUUID(MIDI_CHARACTERISTIC_UUID),
- BLECharacteristic::ROPERTY_READ |
- BLECharacteristic::ROPERTY_NOTIFY |
- BLECharacteristic::ROPERTY_WRITE_NR
- );
- pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED);
-
- // Create a BLE Descriptor
- pCharacteristic->addDescriptor(new BLE2902());
-
- // Start the service
- pService->start();
-
- // Start advertising
- pSecurity = new BLESecurity();
- pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND);
- pSecurity->setCapability(ESP_IO_CAP_NONE);
- pSecurity->setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK);
-
- pServer->getAdvertising()->addServiceUUID(MIDI_SERVICE_UUID);
- pServer->getAdvertising()->start();
-
-
- for (int i = 0; i < 3; i++) {
- pinMode(buttonPins, INPUT);
- M5.Lcd.setTextDatum(MC_DATUM);
- M5.Lcd.drawString("M5 Easy MIDI", 160, 0, 4);
- }
- } //setup
-
- void loop() {
- for (int i = 0; i < 3; i++) {
- int buttonValue = digitalRead(buttonPins);
-
- if (buttonValue == LOW && buttonFlags == false) {
- if (deviceConnected) {
- // noteOn
- noteOn(0, buttonNote, 127);
- }
-
- M5.Lcd.setTextSize(3);
- M5.Lcd.fillRect(0, i * 32 + 32, 100, 24, RED);
- //delay(500);
- M5.Lcd.setCursor(0, i * 32 + 32);
- M5.Lcd.printf("%2d On", buttonPins);
-
- delay(100); //
- buttonFlags = true;
- } //if
-
- if (buttonValue == HIGH && buttonFlags == true) {
- if (deviceConnected) {
- // noteOff
- noteOff(0, buttonNote, 127);
- }
-
- //M5.Lcd.setTextSize(3);
- M5.Lcd.fillRect(48, i * 32 + 32, 64, 24, BLUE);
- M5.Lcd.setCursor(48, i * 32 + 32);
- M5.Lcd.printf("Off");
-
- delay(100);
- M5.Lcd.fillRect(0, i * 32 + 32, 320, 24, BLACK);
-
- buttonFlags = false;
- } // if
- } //for
- }
复制代码
``
0
|
|
|
|