MIDI_Boy

1个月前

简介:本项目主要使用ESP32模块作为主控,通过蓝牙MIDI协议连接手机,配合手机APP(库乐队等),实现了一个电子琴。

开源协议: MIT

  • 548
  • 0
  • 2

描述

摘要

 

本项目主要使用ESP32模块作为主控,通过蓝牙MIDI协议连接手机,配合手机APP(库乐队等),实现了一个电子琴。

 

渲染图如下:

MIDI Boy 渲染图

 

演示视频

 

暂无



设计思路

 

卡林巴琴

 

本设计以真实的卡林巴琴为参考,采用下图所示的鼠标按键代替卡林巴琴的金属弹片,使用蜂鸣器作为发生元器件。

 

鼠标按键

 

蜂鸣器

 

仅仅采用蜂鸣器作为发声元器件,其表现力可能不会太好。所以本项目采用ESP32模块作为主控,利用其蓝牙功能连接手机,通过与手机的互动增强其表现力。

 

ESP32模块

 

ESP32是一颗功能强大的物联网芯片,可同时支持WiFi和蓝牙功能,20+可用GPIO,运行频率最高可达240MHz,可以采用C语言ArdiunoMicroPython等方式进行开发。



硬件设计

 

为了模拟真实的卡林巴琴的手感,设计硬件之前首先测量了上图所示的17键的卡林巴琴的尺寸键距 , 最终确定尺寸141mm*88mm,键距 7.6mm。

除了按键和蜂鸣器外,还使用了CH340串口芯片用于烧录程序,若干WS2812彩灯烘托音乐氛围。

 

完整原理图如下:

MIDI Boy原理图
MIDI原理图

PCB布局参考:

在这里插入图片描述

 

为了追求美观,将所有的线路都安排在了PCB板的背面。

另外,还是为了美观,将USB接口放置到了天线的下方,这种设计会影响无线信号,大家不要模仿哦!

 

硬件开源地址:



软件设计

 

前文提到,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 原理图

在编辑器中打开

BOM

ID Name Designator Footprint Quantity
1 鼠标按键 D6,E4,F4,B4,D4,C5,D5,C4,E5,F5,E6,A4,B5,A5,G5,G4,C6 鼠标按键 17
2 蜂鸣器 BUZZER1,BUZZER2 B-12*7.5 2
3 TOUCH T9 TOUCH_JX 1
4 LED-TH-5mm_B LED2,LED1 LED-TH_BD5.0_BLUE 2
5 0.1u C13,C14,C15,C10,C12,C11 C0603 6
6 USB-Micro_1 USB2,USB1 MICRO-USB-SMD_5P-P0.65-H-F_C10418 2
7 CH340N U4,U3 SOP-8_L5.0-W4.0-P1.27-LS6.0-BL 2
8 WS2812 D2,D1,D3,D7 WS2812 4
9 K4-6×6_TH RESET_KEY,FLASH_KEY1,FLASH_KEY,RESET_KEY1 KEY-TH_4P-L6.0-W6.0-P4.50-LS6.5 4
10 0.1u C8,C3,C1,C2,C7,C9 C0805 6
11 1k R1,R5,R2,R4,R3 R0603 5
12 ESP32-S U1,U6 WIRELM-SMD_ESP32-S 2
13 ASM1117 U5,U2 SOT-223 2
14 TTP229-BSF-16 U7 SSOP-28_L9.9-W3.9-P0.64-LS6.0-BL 1

展开

工程成员

工程附件

服务时间

周一至周五 9:00~18:00
  • 153 6159 2675

服务时间

周一至周五 9:00~18:00
  • 立创EDA微信号

    easyeda

  • QQ交流群

    664186054

  • 立创EDA公众号

    lceda-cn