天天看點

【MIDI_Boy】使用ESP32做一個炫酷的【卡林巴】【電子琴】, 可通過藍牙MIDI連接配接手機庫樂隊摘要設計思路硬體設計軟體設計實物效果圖

摘要

本項目主要使用ESP32子產品作為主要,通過藍牙MIDI協定連接配接手機,配合手機APP(庫樂隊等),實作了一個電子琴。

渲染圖如下:

【MIDI_Boy】使用ESP32做一個炫酷的【卡林巴】【電子琴】, 可通過藍牙MIDI連接配接手機庫樂隊摘要設計思路硬體設計軟體設計實物效果圖

示範視訊

自制卡林巴電子琴,可通過藍牙連接配接手機庫樂隊 MIDI Boy【工科生的第一件樂器】

設計思路

【MIDI_Boy】使用ESP32做一個炫酷的【卡林巴】【電子琴】, 可通過藍牙MIDI連接配接手機庫樂隊摘要設計思路硬體設計軟體設計實物效果圖

本設計以真實的卡林巴琴為參考,采用下圖所示的

滑鼠按鍵

代替卡林巴琴的金屬彈片,使用

蜂鳴器

作為發生元器件。

【MIDI_Boy】使用ESP32做一個炫酷的【卡林巴】【電子琴】, 可通過藍牙MIDI連接配接手機庫樂隊摘要設計思路硬體設計軟體設計實物效果圖
【MIDI_Boy】使用ESP32做一個炫酷的【卡林巴】【電子琴】, 可通過藍牙MIDI連接配接手機庫樂隊摘要設計思路硬體設計軟體設計實物效果圖

僅僅采用蜂鳴器作為發聲元器件,其表現力可能不會太好。是以本項目采用ESP32子產品作為主要,利用其藍牙功能連接配接手機,通過與手機的互動增強其表現力。

【MIDI_Boy】使用ESP32做一個炫酷的【卡林巴】【電子琴】, 可通過藍牙MIDI連接配接手機庫樂隊摘要設計思路硬體設計軟體設計實物效果圖

ESP32是一顆功能強大的物聯網晶片,可同時支援WiFi和藍牙功能,20+可用GPIO,運作頻率最高可達240MHz,可以采用

C語言

Ardiuno

MicroPython

等方式進行開發。

硬體設計

為了模拟真實的卡林巴琴的手感,設計硬體之前首先測量了上圖所示的17鍵的卡林巴琴的

尺寸

鍵距

, 最終确定尺寸141mm*88mm,鍵距 7.6mm。

除了按鍵和蜂鳴器外,還使用了

CH340

序列槽晶片用于燒錄程式,若幹

WS2812

彩燈烘托音樂氛圍。

完整原理圖如下:

【MIDI_Boy】使用ESP32做一個炫酷的【卡林巴】【電子琴】, 可通過藍牙MIDI連接配接手機庫樂隊摘要設計思路硬體設計軟體設計實物效果圖
【MIDI_Boy】使用ESP32做一個炫酷的【卡林巴】【電子琴】, 可通過藍牙MIDI連接配接手機庫樂隊摘要設計思路硬體設計軟體設計實物效果圖

PCB布局參考:

【MIDI_Boy】使用ESP32做一個炫酷的【卡林巴】【電子琴】, 可通過藍牙MIDI連接配接手機庫樂隊摘要設計思路硬體設計軟體設計實物效果圖

為了追求美觀,将所有的線路都安排在了PCB闆的背面。

另外,還是為了美觀,将USB接口放置到了天線的下方,這種設計會影響無線信号,大家不要模仿哦!

硬體開源位址: https://oshwhub.com/Dr.Zhang/midi_boy

軟體設計

前文提到,ESP32有多種開發方式,我這裡采用了MicroPython的開發方式,其優點是開發環境搭建起來比較簡單,代碼量也不較少,目前程式并不完善,核心代碼如下:

from machine import Pin, Timer
from time import sleep_ms
import ubluetooth
from esp32 import raw_temperature

class BLE():
  
    def __init__(self, name):
        
        self.name = name
        self.ble = ubluetooth.BLE()
        self.ble.active(True)

        self.led = Pin(14, Pin.OUT)
        self.timer1 = Timer(0)
        self.timer2 = Timer(1)
        
        self.disconnected()
        self.ble.irq(self.ble_irq)
        self.register()
        self.advertiser()
        self.isConnected = False


    def connected(self):
        
        self.timer1.deinit()
        self.timer2.deinit()


    def disconnected(self):
        
        self.timer1.init(period=1000, mode=Timer.PERIODIC, callback=lambda t: self.led(1))
        sleep_ms(200)
        self.timer2.init(period=1000, mode=Timer.PERIODIC, callback=lambda t: self.led(0))
    

    def ble_irq(self, event, data): # 藍牙事件處理

        if event == 1: # Central disconnected
          self.isConnected = True
          self.connected()
          self.led(1)
        
        elif event == 2: # Central disconnected
            self.isConnected = False
            self.advertiser()
            self.disconnected()
        
        elif event == 4: # New message received
            
            buffer = self.ble.gatts_read(self.midi)
            message = buffer.decode('UTF-8')[:-1]
            print(message)
            
            if received == 'blue_led':
                blue_led.value(not blue_led.value())
                
    def register(self): # 注冊MIDI藍牙服務
      
        MIDI_SERVER_UUID = ubluetooth.UUID('03B80E5A-EDE8-4B33-A751-6CE34EC4C700')
        MIDI_CHAR_UUID   = (ubluetooth.UUID('7772E5DB-3868-4112-A1A9-F2669D106BF3'), 
          ubluetooth.FLAG_READ | ubluetooth.FLAG_WRITE | ubluetooth.FLAG_NOTIFY , )
            
        BLE_MIDI_SERVER = (MIDI_SERVER_UUID, (MIDI_CHAR_UUID , ) , )
        SERVICES = (BLE_MIDI_SERVER, )
        
        ((self.midi,), ) = self.ble.gatts_register_services(SERVICES)


    def send(self, data):
      if self.isConnected :
        self.ble.gatts_notify(0, self.midi, data)


    def advertiser(self): # 設定廣播及掃描響應資料
        name = bytes(self.name, 'UTF-8')
        self.ble.gap_advertise(100, adv_data = b'\x02\x01\x05' + bytearray((len(name) + 1, 0x09)) + name ,  
          resp_data = b'\x11\x07\x00\xC7\xC4\x4E\xE3\x6C\x51\xA7\x33\x4B\xE8\xEd\x5A\x0E\xB8\x03')
        
ble = BLE("ESP32")

k_d6 = Pin(32, Pin.IN, Pin.PULL_UP)
k_b5 = Pin(33, Pin.IN, Pin.PULL_UP)
k_g5 = Pin(25, Pin.IN, Pin.PULL_UP)
k_e5 = Pin(26, Pin.IN, Pin.PULL_UP)
k_c5 = Pin(27, Pin.IN, Pin.PULL_UP)
k_a4 = Pin(12, Pin.IN, Pin.PULL_UP)
k_f4 = Pin(13, Pin.IN, Pin.PULL_UP)
k_d4 = Pin(15, Pin.IN, Pin.PULL_UP)

k_c4 = Pin(4,  Pin.IN, Pin.PULL_UP)

k_e4 = Pin(16, Pin.IN, Pin.PULL_UP)
k_g4 = Pin(17, Pin.IN, Pin.PULL_UP)
k_b4 = Pin(5,  Pin.IN, Pin.PULL_UP)
k_d5 = Pin(18, Pin.IN, Pin.PULL_UP)
k_f5 = Pin(19, Pin.IN, Pin.PULL_UP)
k_a5 = Pin(21, Pin.IN, Pin.PULL_UP)
k_c6 = Pin(22, Pin.IN, Pin.PULL_UP)
k_e6 = Pin(23, Pin.IN, Pin.PULL_UP)

key_pin_list   = [k_c4,k_d4,k_e4,k_f4,k_g4,k_a4,k_b4,k_c5,k_d5,k_e5,k_f5,k_g5,k_a5,k_b5,k_c6,k_d6,k_e6]
key_name_list  = ['k_c4','k_d4','k_e4','k_f4','k_g4','k_a4','k_b4','k_c5','k_d5','k_e5','k_f5','k_g5','k_a5','k_b5','k_c6','k_d6','k_e6']
key_value_last = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
key_value_now  = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]

midi_start = 0x48 #C4鍵的音高

# 與C4相比的音程查
midi_inve  = [0,2,4,5,7,9,11,12,14,16,17,19,21,23,24,26,28]

while True :
  
  for i in range(17):
    key_value_now[i] = key_pin_list[i].value()

    if not key_value_last[i] == key_value_now[i] :
      if key_value_now[i] == 0:
        print("on_" + key_name_list[i])
        ble.send(bytearray([0x80, 0x80, 0x90, midi_start + midi_inve[i] , 0x63]))
      else :
        print("off_" + key_name_list[i])
        ble.send(bytearray([0x80, 0x80, 0x80, midi_start + midi_inve[i] , 0x00]))
        
      key_value_last[i] = key_value_now[i]
      sleep_ms(10)
 
           

實物效果圖

【MIDI_Boy】使用ESP32做一個炫酷的【卡林巴】【電子琴】, 可通過藍牙MIDI連接配接手機庫樂隊摘要設計思路硬體設計軟體設計實物效果圖

如果你喜歡改文章,歡迎

點贊

評論

收藏

轉發

!

我是鵬老師!

繼續閱讀