作为一个理工男,对音乐总有着迷之兴趣。但是在这方面一直缺乏指导,只在小学的时候上过几节音乐,记得少许简谱的概念。后来中学忙于文化课,大学选择了电子专业,与音乐就越来越远。现在已经参加工作了,小时候埋在心里的种子终究会发芽,工作闲暇利用自己专业相关的知识,设计了几款电子乐器,与诸君分享。
卡林巴琴简介
真实的卡林巴琴
卡林巴琴本是非洲的一种民族乐器,外观如上图所示,通过弹拨发出声音。
电子卡林巴琴
演示视频观看地址:https://www.bilibili.com/video/BV135411w73B/
本设计模仿卡林巴琴的外观,采用下图所示的鼠标按键代替卡林巴琴的金属弹片,蓝牙连接手机,在手机上发出声音。
鼠标按键
电子卡琳巴琴
该设计采用ESP32作为主控单元,ESP32是一颗功能强大的物联网芯片,可同时支持WiFi和蓝牙功能,20+可用GPIO,运行频率最高可达240MHz,可以采用C语言,Ardiuno,MicroPython等方式进行开发。
整体的设计思路是先使用ESP32的蓝牙功能实现MIDI协议,连接手机后,与手机上的APP配合,手机在手机上发出声音。编程语言使用的是MicroPython,上手相对比较简单,非电子专业的同学也可以尝试做一下。
电子卡琳巴琴
工作原理也非常简单,当程序检测到有按键按下时,ESP32通过蓝牙发送相应的MIDI指令到手机,手机收到相关的指令后,在手机上播放对应的音符。
关于蓝牙MIDI协议,可以参考这篇文章《【蓝牙乐器DIY】蓝牙MIDI协议》。
讲完了工作原理,接下来我们看下硬件设计。如果你没有PCB板设计经验,可以尝试制作如下图所示的简易版本,使用现成的ESP32_Dev_Kit开发板配合鼠标按键,在一款较大的洞洞板上简单焊接就完成了。
注意,使用鼠标按键的上面;上面两个引脚分别连接GND和ESP的引脚。参考下图:
如果你有一定的PCB设计能力,建议制作下图所示的版本,集成了锂电池充电电路和WS2812彩灯,同时外观也更为美观。
原理图如下图:
元器件布局参考:
为了追求美观,所有的走线都放置在了PCB板的背面。
所有的设计资料也都已开源,地址如下:https://oshwhub.com/Dr.Zhang/midi_boy
软件设计
编程语言使用的是MicroPython,开发环境使用的是uPyCraft,程序的逻辑是先通过蓝牙实现MIDI服务,等待手机连接,手机连接成功后当检测到有按键按下,就发送相关的MIDI指令到手机上。
uPyCraft下载地址:https://shyboy.oss-cn-shenzhen.aliyuncs.com/readonly/uPyCraft_V1.1.exe
核心代码如下:
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(2, 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() 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)
如果你不知道如何在ESP32上运行MicroPython程序代码,可参考这个视频:https://www.bilibili.com/video/BV18r4y1Q7uP/
代码运行后,根据手机平台的不同,使用如下方式与设备建立连接:
iOS平台
Android平台
连接成功就可以在手机上演奏了。
如果您觉得该制作有趣,也可以自己制作一个哦~
如果您有别的创意,欢迎在回帖留言~
附录1:元器件列表
名称 | 型号 | 封装 | 数量 | 备注 |
USB接口 | MicroUSB | 贴片 | 1 | |
串口芯片 | CH340N | SOP-8 | 1 | 修改电路可用CH340C替换 |
电容 | 0.1uF(104) | 0805 | 5 | |
电阻 | 1K | 0603 | 2 | 用作LED限流 |
电阻 | 2K | 0603 | 1 | 用作锂电池充电限流 |
电阻 | 10K | 0603 | 1 | 用作EN引脚上拉 |
按键 | K4-6×6_TH | 直插 | 2 | 用作复位及Boot选择 |
开关 | MSS22D18G2 | 直插 | 1 | |
ESP32模块 | ESP32-S | SMD | 1 | |
鼠标按键 | * | 直插 | 17 | 注意选择 三脚带弯柄 |
红色LED | * | 0603 | 1 | 用作充电指示 |
蓝色LED | * | 0603 | 1 | 用作连接状态指示 |
5V-3.3V芯片 | AMS1117 3.3 | SOT-223 | 1 | |
锂电池充电 | TP4055 | SOT-23-5 | 1 | |
彩色灯珠 | WS2812 | 贴片 | 17 | 可选 |
锂电池 | 3.7V 1000mAh | * | 1 | 500mAh – 2000mAh均可 |
附录2:相关资料链接汇总
1、卡林巴琴(ESP32版)电路原理图文件:https://oshwhub.com/Dr.Zhang/midi_boy
2、开发环境-uPyCraft下载地址:https://shyboy.oss-cn-shenzhen.aliyuncs.com/readonly/uPyCraft_V1.1.exe
3、卡林巴琴制作教程与成品演示视频:https://www.bilibili.com/video/BV135411w73B/
4、在ESP32上运行MicroPython的教程:https://www.bilibili.com/video/BV18r4y1Q7uP/
5、蓝牙MIDI协议讲解:http://forum.eepw.com.cn/thread/360113/1