大体分为硬件端和软件端,硬件端包括:esp32在面包板上的连接,画pcb电路图,焊接调试pcb电路,最后用三维设计外壳。软件端包括:用python编写的后端服务器,页面展示的客户端。(本项目要用到有关乐理知识)
我们的项目总的流程图 文字描述:esp32硬件先连接WiFi,蓝灯亮了表示连接成功,不亮就用蜂鸣器发声,亮的话,esp32的琴键摁下将对应摁键通过udp通讯发送到服务端,服务端收到udp信息 可以利用pygame发声,也可以通过服务端和前端的websocket连接,将信息发送到前端,前端收到信息,去实现对应的琴键变颜色,记录简谱。 还可以通过前端的琴键摁下,让服务端的pygame发声,并变颜色和记录简谱。 拓展部分:1.作弊模式:硬件摁下一个摁键,不管硬件摁下哪个按键,都发出前端已经规定好的音谱。 软件端要达到的效果如下: 一 后端(thonny)1.使esp32发出不同的声音
2.esp32发声(奏乐)
2.录制esp32发音,判断摁键摁下的长短
3.利用pygame发声
4.用udp连接使服务器和硬件建立联系,硬件摁下琴键服务端收到udp信息,发出对应的音调
5.esp32的WiFi连接
二 前端(vscode)1.在页面绘制UI琴键
2.构建电子琴结构
3.服务端和前端建立websocket连接
4.硬件发送udp向服务端,服务端收到信息通过websocket连接发送到前端
以下是拓展部分:1.开启作弊模式
2.切换上一首下一首,清除页面前一个音符,清空页面全部音符
3.播放音乐
硬件端要达到的效果如下:1.esp32在面包板上的连接
2.画pcb电路图
3.pcb电路板的焊接
4.三维模型的设计打印
------------------------------------------------------------------------------
在动手做之前我们应该了解基本乐理知识:音乐基础乐理知识大全 | 乐理知识 - 知乎 (zhihu.com)
在发音之前我们一个了解蜂鸣器有关知识:蜂鸣器原理 - 单片机教程 - C语言网 (dotcpp.com)
ESP32 MicroPython上手指南 — MicroPython 1.14 文档 (01studio.org)esp32官网:ESP32 MicroPython上手指南 — MicroPython 1.14 文档 (01studio.org)
了解面包板:(4条消息) 面包板使用简介_countofdane的博客-CSDN博客_面包板的详细使用方法
用到的硬件有esp32 剥线钳 导线 蜂鸣器(下图是esp32)
"万事俱备只欠东风 --------------------------------"
首先我们需要先将esp32在面包板上的连线完成
完成连线过后我们可以去实现功能了
①当面包板上的琴键摁出发出不同音调并且亮蓝灯代码如下:
from machine import TouchPad, Pin,PWM
from time import sleep
LED = Pin(2,Pin.OUT)
pwm0 = PWM(Pin(23))
freq_val=5
duty_val=0
pwm0.freq(freq_val)
pwm0.duty(duty_val)
PINn = (32,33,27,13,12,14,15,4)#元组
TPx = [TouchPad(Pin(i)) for i in PINn]#TPx 所有的触摸管脚定义
Tone2 = (#对应管脚要演奏声音的频率
523,
586,
658,
697,
783,
879,
987,
1045,
)
def dzq():
global freq_val,duty_val
'电子琴子程序'
for ton,tp in zip(Tone2,TPx):
'遍历所有的触摸按钮'
if tp.read()<200:
freq_val=ton
duty_val = 1024//2
LED.on()
break
else:
#'如果运行完所有的循环,没有break,就执行这个else'
freq_val = 10
duty_val = 0
LED.off()
freq1 = pwm0.freq()#读取当前频率
# pwm0.freq(freq_val)
# sleep(0.01)
# pwm0.duty(duty_val)
if freq1 != freq_val:
'只有频率被修改的时候,才去重新配置频率和占空比'
pwm0.freq(freq_val)
sleep(0.01)#需要一定时间的延时,否则的话,同时修改评论和占空比会导致占空比修改失败
pwm0.duty(duty_val)
# while pwm0.duty() != duty_val:
# pwm0.duty(duty_val)
# print(pwm0.freq(),pwm0.duty())
while True:
dzq()
sleep(0.01)
------------------------------------------------------------------------
②实现录制播放并且记录摁下时间长短:import time #引入时间类
from machine import TouchPad, Pin, PWM #引入触摸管脚
import threading
switch = TouchPad(Pin(32))
switch_mode = 0
PINn = (33,27,13,12,14,15,4)#元组
TPx = [TouchPad(Pin(i)) for i in PINn]#TPx 所有的触摸管脚定义
LED = Pin(2, Pin.OUT)
pwm0 = PWM(Pin(26))
time.sleep(0.5)
pwm0.duty(0)
time.sleep(0.01)
pwm0.freq(1)
Tone = (#对应管脚要演奏声音的频率
523,
586,
658,
697,
783,
879,
987,
)
my_rhythm = [] #创建节拍空数组
my_tones = [] #创建音调空数组
release=0 #松开状态
press=1 #按下状态
key_state = [release]*8 #按键状态缓存变量
ALLkey_state = release
start = time.ticks_ms() #记录开始的时间戳
end = 0 #记录结束的时间戳
time_diff = 0 #开始和结束的时间差
def Record_time():
global start,end,time_diff
end = time.ticks_ms() #记录这一次按键变化结束的时间戳
time_diff = time.ticks_diff(end, start) #根据这一次按键变化的开始和结束时间戳,计算出按键变化的时间差
my_rhythm.append(time_diff/1000) #将时间差存入数组末尾
# print(my_rhythm) #打印数组
#-----------------------------------------------------
start = time.ticks_ms() #记录下一次按键变化开始的时间戳
def playtone(frequency):
pwm0.duty(512)
time.sleep(0.01)
pwm0.freq(frequency)
def bequiet():
pwm0.duty(0)
time.sleep(0.01)
pwm0.freq(1)
def playsong():
global switch_mode
for i in range(len(my_rhythm)):
if (my_tones[i] == 0 ):
bequiet()
else:
playtone(my_tones[i])
time.sleep(my_rhythm[i])
bequiet()
switch_mode=0
print('music_OK')
my_music = threading.Thread(target=playsong)
def switchkey():
global switch_mode,start
while True:
if switch.read()<200:
if switch_mode==0:
my_rhythm.clear()
my_tones.clear()
switch_mode=1
start = time.ticks_ms() #记录开始的时间戳
my_tones.append(0) #第一个项为空拍,与节拍数组格式对应
LED.on()
elif switch_mode==1:
Record_time()
print(my_tones)
print(my_rhythm)
switch_mode=2
LED.off()
my_music.start()
while switch.read()<200:
time.sleep(0.01)
sw_key = threading.Thread(target=switchkey)
sw_key.start()
while True:
for i in range(7): #循环7次,读取7个按键状态
if TPx[i].read()<200: #读取按键电容值,判断按键是否按下
if key_state[i] != press: #如果按键状态缓存变量 非 此按键键值,表示按键发生了改变
key_state[i] = press #将按键状态缓存变量 置为 此按键键值
if switch_mode==1:
Record_time() #记录按键改变的时间差
my_tones.append(Tone[i]) #记录当前按键的音调
playtone(Tone[i]) #发出对应频率的音调
ALLkey_state = press #表示有按键按下
else:
key_state[i] = release #将当前按键状态置为松开
if press not in key_state:
if ALLkey_state != release : #全部松开状态下 判断之前是否有按键按下
ALLkey_state = release #将按键状态置为全部松开状态
if switch_mode==1:
Record_time() #记录按键改变的时间差
my_tones.append(0) #记录当前空拍音调
bequiet() #不发声
time.sleep(0.1) #延时
import pygame.midi
import pygame
import time
# 初始化设置
volume = 127 # 音量 0-127
pygame.init() # 初始化PYgame
windowSurface = pygame.display.set_mode((800, 600)) # 建立窗口
device = 0 # device number in win10 laptop
instrument = 0 # 乐器 http://www.ccarh.org/courses/253/handout/gminstruments/
# initize Pygame MIDI ----------------------------------------------------------
pygame.midi.init() # PYGAMEMIDI库的初始化
# 初始化设置结束
screen = pygame.display.set_mode((400,400))
# 设置窗口的标题,即游戏名称
pygame.display.set_caption('pygame 钢琴')
# 引入字体类型
f = pygame.font.Font('C:/Windows/Fonts/simhei.ttf',135)
# 生成文本信息,第一个参数文本内容;第二个参数,字体是平滑;
# 第三个参数,RGB模式的字体颜色;第四个参数,RGB模式字体背景颜色;
text = f.render("Zhang",True,(255,244,255),(31,56,99))
#获得显示对象的rect区域坐标
textRect =text.get_rect()
# 设置显示对象居中
textRect.center = (200,200)
# 将准备好的文本信息,绘制到主屏幕 Screen 上。
screen.blit(text,textRect)
# 固定代码段,实现点击"X"号退出界面的功能,几乎所有的pygame都会使用该段代码
Tone = { # 音调字典,不全,需要大家完善。从C1-C5都完善起来
'A0':21,'A#0':22,'B0':23,
'C1':24,'C#1':25,'D1':26,'D#1':27,'E1':28,'F1':29,'F#1':30,'G1':31,'G#1':32,'A1':33,'A#1':34,'B1':35,
'C2':36,'C#2':37,'D2':38,'D#2':39,'E2':40,'F2':41,'F#2':42,'G2':43,'G#2':44,'A2':45,'A#2':46,'B2':47,
'C3':48,'C#3':49,'D3':50,'D#3':51,'E3':52,'F3':53,'F#3':54,'G3':55,'G#3':56,'A3':57,'A#3':58,'B3':59,
'C4':60,'C#4':61,'D4':62,'D#4':63,'E4':64,'F4':65,'F#4':66,'G4':67,'G#4':68,'A4':69,'A#4':70,'B4':71,
'C5':72,'C#5':73,'D5':74,'D#5':75,'E5':76,'F5':77,'F#5':78,'G5':79,'G#5':80,'A5':81,'A#5':82,'B5':83,
'C6':84,'C#6':85,'D6':86,'D#6':87,'E6':88,'F6':89,'F#6':90,'G6':91,'G#6':92,'A6':93,'A#6':94,'B6':95,
'C7':96,'C#7':97,'D7':98,'D#7':99,'E7':100,'F7':101,'F#7':102,'G7':103,'G#7':104,'A7':105,'A#7':106,'B7':107,
'C8':108,
}
# set the output device --------------------------------------------------------
player = pygame.midi.Output(device) # 定义了一个输出音轨
# set the instrument -----------------------------------------------------------
player.set_instrument(instrument) # 设置乐器音色
key_value = ('a','s','d','f','g','h','j','q','w','e','r','t','y','u','z','x','c','v','b','n','m','1','2','3','4','5','6','7','i','o','p','k','l',)
key_tone = {
'a':"C2",'s':"D2",'d':"E2",'f':"F2",'g':"G2",'h':"A2",'j':"B2",
'q':"C3",'w':"D3",'e':"E3",'r':"F3",'t':"G3",'y':"A3",'u':"B3",
'z':"C1",'x':"D1",'c':"E1",'v':"F1",'b':"G1",'n':"A1",'m':"B1",
'1':"C4",'2':"D4",'3':"E4",'4':"F4",'5':"G4",'6':"A4",'7':"B4",
'i':'C5','o':'D5','p':'E5','k':'F5','l':'G5',
}
while True:
for event in pygame.event.get(): # 检测事件
if event.type == pygame.QUIT:
exit()
if event.type == pygame.KEYDOWN:
for i in range(33):
if event.key == pygame.__dict__[ 'K_'+key_value[i] ]:
print('正在发第'+str(i)+'个的音')
t1 = time.time()
player.note_on(Tone[ key_tone[ key_value[i] ] ], volume)
elif event.type == pygame.KEYUP:# 按键=松开的话,关闭对应的音调
for i in range(33):
if event.key == pygame.__dict__[ 'K_'+key_value[i] ]:
print('停止发第'+str(i)+'个')
t2 = time.time()
t3 = t2 - t1
print(str(t3)+'s')
player.note_off(Tone[ key_tone[ key_value[i] ] ], volume)
# 循环获取事件,监听事件状态
for event in pygame.event.get():
# 判断用户是否点了"X"关闭按钮,并执行if代码段
if event.type == pygame.QUIT:
#卸载所有模块
print("退出")
pygame.quit()
#终止程序,确保退出程序
sys.exit()
pygame.display.flip() #更新屏幕内容
pygame发声
④窗口显示按键样式和音符:import pygame.midi
import pygame
import time
import sys
# 初始化设置
volume = 127 # 音量 0-127
pygame.init() # 初始化PYgame
windowSurface=pygame.display.set_mode((800,600)) #建立窗口
# screen = pygame.display.set_mode((400,400))
# 设置窗口标题,即游戏名称
pygame.display.set_caption('键盘钢琴')
#引入字体
f = pygame.font.Font('C:/Windows/Fonts/simhei.ttf',75)
#生成文本信息,第一个参数文本内容;第二个参数,字体是否平滑;
#第三个参数,RGB模式的字体颜色;第四个参数,RGB模式字体背景颜色;
text = f.render("Lebron",True,'deeppink','purple')
# 获得显示对象的rect区域坐标
textRect = text.get_rect()
# 设置显示对象居中
textRect.center = (400,40)
# 将准备好的文本信息,绘制到主屏幕 Screen 上。
windowSurface.blit(text,textRect)
device = 0 # device number in win10 laptop
instrument = 0 #乐器 http://www.ccarh.org/courses/253/handout/gminstruments/
# initize Pygame MIDI ----------------------------------------------------------
pygame.midi.init()# PYGAMEMIDI库的初始化
Tone = { # 音调字典
'A0':21,'A#0':22,'B0':23,
'C1':24,'C#1':25,'D1':26,'D#1':27,'E1':28,'F1':29,'F#1':30,'G1':31,'G#1':32,'A1':33,'A#1':34,'B1':35,
'C2':36,'C#2':37,'D2':38,'D#2':39,'E2':40,'F2':41,'F#2':42,'G2':43,'G#2':44,'A2':45,'A#2':46,'B2':47,
'C3':48,'C#3':49,'D3':50,'D#3':51,'E3':52,'F3':53,'F#3':54,'G3':55,'G#3':56,'A3':57,'A#3':58,'B3':59,
'C4':60,'C#4':61,'D4':62,'D#4':63,'E4':64,'F4':65,'F#4':66,'G4':67,'G#4':68,'A4':69,'A#4':70,'B4':71,
'C5':72,'C#5':73,'D5':74,'D#5':75,'E5':76,'F5':77,'F#5':78,'G5':79,'G#5':80,'A5':81,'A#5':82,'B5':83,
'C6':84,'C#6':85,'D6':86,'D#6':87,'E6':88,'F6':89,'F#6':90,'G6':91,'G#6':92,'A6':93,'A#6':94,'B6':95,
'C7':96,'C#7':97,'D7':98,'D#7':99,'E7':100,'F7':101,'F#7':102,'G7':103,'G#7':104,'A7':105,'A#7':106,'B7':107,
'C8':108,
}
# set the output device --------------------------------------------------------
player = pygame.midi.Output(device)#定义了一个输出音轨
# set the instrument -----------------------------------------------------------
player.set_instrument(instrument)#设置乐器音色
key_tone = {
'1':"C5", '2':"D5", '3':"E5", '4':"F5", '5':"G5", '6':"A5", '7':"B5",
'q':"C3", 'w':"D3", 'e':"E3", 'r':"F3", 't':"G3", 'y':"A3", 'u':"B3",
'a':"C4", 's':"D4", 'd':"E4", 'f':"F4", 'g':"G4", 'h':"A4", 'j':"B4",
'z':"C2", 'x':"D2", 'c':"E2", 'v':"F2", 'b':"G2", 'n':"A2", 'm':"B2",
}
text_col = 'black' # 文本颜色
bd_col1 = 'white' # 背景颜色(初始值)
bd_col2 = 'red' # 背景颜色(按下反显值)
def key_color (text,x,y,color): #按键改变颜色(文本内容,坐标x,坐标y,颜色R)
key = f.render(text,True,text_col,color)
key_rect = key.get_rect()
key_rect.center = (x,y)
windowSurface.blit(key,key_rect)
for key in key_tone.keys(): #在窗口中循环打印字典中的字符key内容
n=list(key_tone.keys()).index(key) #检索字符 是否在字符串列表中,返回字符所在位置
key_color(key+' ',50+(n//7)*60+(n%7)*90,150+(n//7)*100,bd_col1)
# print( list(key_tone.keys()) )
# print( list(key_tone.keys())[0] )
# print(list(key_tone.keys()).index('r'))
while True:
for event in pygame.event.get(): # 检测事件
if event.type == pygame.QUIT:
#卸载所有模块
print("退出")
pygame.quit()
#终止程序,确保退出程序
sys.exit()
if event.type == pygame.KEYDOWN:
if chr(event.key) in key_tone.keys():#所按下的按键,在音符键盘的字典中
n=list(key_tone.keys()).index(chr(event.key))#检索字符 是否在字符串列表中,返回字符所在位置
key_color(chr(event.key)+' ',50+(n//7)*60+(n%7)*90,150+(n//7)*100,bd_col2)
print('正在发'+key_tone[chr(event.key)]+'音')
t1 = time.time() #记录t1的时间戳
player.note_on(Tone[key_tone[chr(event.key)]], volume)
elif event.type == pygame.KEYUP:# 按键=松开的话,关闭对应的音调
if chr(event.key) in key_tone.keys():#所按下的按键,在音符键盘的字典中
n=list(key_tone.keys()).index(chr(event.key))#检索字符 是否在字符串列表中,返回字符所在位置
key_color(chr(event.key)+' ',50+(n//7)*60+(n%7)*90,150+(n//7)*100,bd_col1)
print('停止发'+key_tone[chr(event.key)]+'音')
t2 = time.time() #记录t2的时间戳
t3 = t2 - t1 #根据t1和t2的时间戳,计算t3时间差
print(str(t3)+'s') #打印出时间差
player.note_off(Tone[key_tone[chr(event.key)]], volume)
pygame.display.flip() #更新
time.sleep(0.005) #每0.005s循环一次
窗口显示按键样式和音符
⑤udp通讯:# -*- coding: utf-8 -*-
import socket
import time
#client 发送端
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
PORT = 8008
while True:
start = time.time() #获取当前时间
print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(start))) #以指定格式显示当前时间
msg=input("本客户端192.168.43.131,请输入要发送的内容:")
server_address = ("192.168.43.82", PORT) # 接收方 服务器的ip地址和端口号
client_socket.sendto(bytes(msg.encode("utf-8")), server_address) #将msg内容发送给指定接收方
now = time.time() #获取当前时间
run_time = now-start #计算时间差,即运行时间
print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(now)))
print("run_time: %d seconds\n" %run_time)
time.sleep(1)
###
# -*- coding: utf-8 -*-
import pygame.midi
import socket #导入socket模块
import time #导入time模块
#server 接收端
# 设置服务器默认端口号
PORT = 8008
# 创建一个套接字socket对象,用于进行通讯
# socket.AF_INET 指明使用INET地址集,进行网间通讯
# socket.SOCK_DGRAM 指明使用数据协议,即使用传输层的udp协议
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
address = ("", PORT)
server_socket.bind(address) #为服务器绑定一个固定的地址,ip和端口
server_socket.settimeout(10) #设置一个时间提示,如果10秒钟没接到数据进行提示
device = 0 # device number in win10 laptop
instrument = 0 #乐器 http://www.ccarh.org/courses/253/handout/gminstruments/
# initize Pygame MIDI ----------------------------------------------------------
pygame.midi.init()# PYGAMEMIDI库的初始化
# set the output device --------------------------------------------------------
player = pygame.midi.Output(device)#定义了一个输出音轨
volume = 127 # 音量 0-127
# set the instrument -----------------------------------------------------------
player.set_instrument(instrument)#设置乐器音色
Tone = { # 音调字典
'A0':21,'AS0':22,'B0':23,
'C1':24,'CS1':25,'D1':26,'DS1':27,'E1':28,'F1':29,'FS1':30,'G1':31,'GS1':32,'A1':33,'AS1':34,'B1':35,
'C2':36,'CS2':37,'D2':38,'DS2':39,'E2':40,'F2':41,'FS2':42,'G2':43,'GS2':44,'A2':45,'AS2':46,'B2':47,
'C3':48,'CS3':49,'D3':50,'DS3':51,'E3':52,'F3':53,'FS3':54,'G3':55,'GS3':56,'A3':57,'AS3':58,'B3':59,
'C4':60,'CS4':61,'D4':62,'DS4':63,'E4':64,'F4':65,'FS4':66,'G4':67,'GS4':68,'A4':69,'AS4':70,'B4':71,
'C5':72,'CS5':73,'D5':74,'DS5':75,'E5':76,'F5':77,'FS5':78,'G5':79,'GS5':80,'A5':81,'AS5':82,'B5':83,
'C6':84,'CS6':85,'D6':86,'DS6':87,'E6':88,'F6':89,'FS6':90,'G6':91,'GS6':92,'A6':93,'AS6':94,'B6':95,
'C7':96,'CS7':97,'D7':98,'DS7':99,'E7':100,'F7':101,'FS7':102,'G7':103,'GS7':104,'A7':105,'AS7':106,'B7':107,
'C8':108,
}
player.note_on(Tone['C3'], volume)
time.sleep(1)
player.note_off(Tone['C3'], volume)
while True:
#正常情况下接收数据并且显示,如果10秒钟没有接收数据进行提示(打印 "time out")
#当然可以不要这个提示,那样的话把"try:" 以及 "except"后的语句删掉就可以了
try:
now = time.time() #获取当前时间
# 接收客户端传来的数据 recvfrom接收客户端的数据,默认是阻塞的,直到有客户端传来数据
# recvfrom 参数的意义,表示最大能接收多少数据,单位是字节
# recvfrom返回值说明
# receive_data表示接受到的传来的数据,是bytes类型
# client 表示传来数据的客户端的身份信息,客户端的ip和端口,元组
receive_data, client = server_socket.recvfrom(1024)
tone_temp = str(receive_data,'utf-8')#bytes转换为str字符串
if tone_temp in Tone.keys():
player.note_on(Tone[tone_temp], volume)
print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(now))) #以指定格式显示时间
print("来自客户端%s,发送的%s\n" % (client, receive_data)) #打印接收的内容
except socket.timeout: #如果10秒钟没有接收数据进行提示(打印 "time out")
print("time out")
服务器用pycharm 发送端用thonny
#用thonny 做客户端 pycharm 做服务端
#用thonny 做客户端 pycharm 做服务端
def is_legal_wifi(essid, password):
'''
判断WIFI密码是否合法
'''
if len(essid) == 0 or len(password) == 0:
return False
return True
def do_connect():
import json
import network
# 尝试读取配置文件wifi_confi.json,这里我们以json的方式来存储WIFI配置
# wifi_config.json在根目录下
# 若不是初次运行,则将文件中的内容读取并加载到字典变量 config
try:
with open('wifi_config.json','r') as f:
config = json.loads(f.read())
# 若初次运行,则将进入excpet,执行配置文件的创建
except:
essid = ''
password = ''
while True:
essid = input('wifi name:') # 输入essid
password = input('wifi passwrod:') # 输入password
if is_legal_wifi(essid, password):
config = dict(essid=essid, password=password) # 创建字典
with open('wifi_config.json','w') as f:
f.write(json.dumps(config)) # 将字典序列化为json字符串,存入wifi_config.json
break
else:
print('ERROR, Please Input Right WIFI')
#以下为正常的WIFI连接流程
wifi = network.WLAN(network.STA_IF)
if not wifi.isconnected():
print('connecting to network...')
wifi.active(True)
wifi.connect(config['essid'], config['password'])
import utime
for i in range(200):
print('第{}次尝试连接WIFI热点'.format(i))
if wifi.isconnected():
break
utime.sleep_ms(100) #一般睡个5-10秒,应该绰绰有余
if not wifi.isconnected():
wifi.active(False) #关掉连接,免得repl死循环输出
print('wifi connection error, please reconnect')
import os
# 连续输错essid和password会导致wifi_config.json不存在
try:
os.remove('wifi_config.json') # 删除配置文件
except:
pass
do_connect() # 重新连接
else:
print('network config:', wifi.ifconfig())
import socket
import time
from machine import Pin
from time import sleep
LED=Pin(2,Pin.OUT)
do_connect()
LED.on()
#多键触摸发声
#-----------------------------------
from machine import TouchPad, Pin, #引用touch库,GPIO库,PWM库
from time import sleep #引用time库
touch_do=TouchPad(Pin(13)) #创建 DO音 TouchPad对象
touch_re=TouchPad(Pin(12)) #创建 RE音 TouchPad对象
touch_mi=TouchPad(Pin(14)) #创建 MI音 TouchPad对象
touch_fa=TouchPad(Pin(27)) #创建 FA音 TouchPad对象
touch_so=TouchPad(Pin(33)) #创建 SO音 TouchPad对象
touch_la=TouchPad(Pin(32)) #创建 LA音 TouchPad对象
touch_si=TouchPad(Pin(15)) #创建 SI音 TouchPad对象
touch_si=TouchPad(Pin(4)) #创建 SI音 TouchPad对象
# LED点亮说明WIFI连接正常
#client 发送端
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ("192.168.43.82", 8008) # 接收方 服务器的ip地址和端口号
while True:
#循环体
# A=touch_do.read()
# B=touch_re.read()
# C=touch_mi.read()
# D=touch_fa.read()
# E=touch_so.read()
# F=touch_la.read()
# G=touch_si.read()
# H=touch_si.read()
PINn = (13,12,14,27,33,32,15,4)#元组
TPx = [TouchPad(Pin(i)) for i in PINn]
Tone2 = (
"A3",
"A7",
"A5",
"B3",
"D3",
"C7",
"B5",
"A1",
)
for ton,tp in zip(Tone2,TPx):
if tp.read()<200:
print("已经发送啦!")
msg = ton
client_socket.sendto(bytes(msg.encode('utf-8')), server_address) #将msg内容发送给指定接收方
time.sleep(0.01)
后期这个直接烧录进esp32
import network
import time
#WIFI连接流程
def wifi_connect():
wifi = network.WLAN(network.STA_IF)
wifi.active(True)
wifi.connect('', '')
for i in range(200):
print('第{}次尝试连接WIFI热点'.format(i))
if wifi.isconnected():
break
time.sleep(0.1) #一般睡个5-10秒,应该绰绰有余
if not wifi.isconnected():
wifi.active(False) #关掉连接,免得repl死循环输出
print('wifi connection error, please reconnect')
else:
print('network config:', wifi.ifconfig())
@mousedown="mouseDown(key)" @mouseup="mouseup(key)">
{{key.note}}
服务端:
from flask import Flask #Flask服务器库
from flask_sockets import Sockets #WS连接库
import pygame.midi #d奏音乐的库
import pygame #pygame的库
import json
from concurrent.futures import ThreadPoolExecutor
import socket
instruments = [1,46,25,56]
# 创建线程池执行器
executor = ThreadPoolExecutor(2)
# 初始化pygame设置
volume = 127 # 音量 0-127
device = 0 # device number in win10 laptop
instrument = 1 # 乐器 http://www.ccarh.org/courses/253/handout/gminstruments/
# initize Pygame MIDI ----------------------------------------------------------
pygame.midi.init() # PYGAMEMIDI库的初始化
# set the output device --------------------------------------------------------
player = pygame.midi.Output(device) # 定义了一个输出音轨
# set the instrument -----------------------------------------------------------
player.set_instrument(instrument) # 设置乐器音色
# 初始化设置结束
Tone = {#音调字典
'C0':12,'CS0':13,'D0':14,'DS0':15,'E0':16,'F0':17,'FS0':18,'G0':19,'GS0':20,'A0':21,'AS0':22,'B0':23,
'C1':24,'CS1':25,'D1':26,'DS1':27,'E1':28,'F1':29,'FS1':30,'G1':31,'GS1':32,'A1':33,'AS1':34,'B1':35,
'C2':36,'CS2':37,'D2':38,'DS2':39,'E2':40,'F2':41,'FS2':42,'G2':43,'GS2':44,'A2':45,'AS2':46,'B2':47,
'C3':48,'CS3':49,'D3':50,'DS3':51,'E3':52,'F3':53,'FS3':54,'G3':55,'GS3':56,'A3':57,'AS3':58,'B3':59,
'C4':60,'CS4':61,'D4':62,'DS4':63,'E4':64,'F4':65,'FS4':66,'G4':67,'GS4':68,'A4':69,'AS4':70,'B4':71,
'C5':72,'CS5':72,'D5':74,'DS5':75,'E5':76,'F5':77,'FS5':78,'G5':79,'GS5':80,'A5':81,'AS5':82,'B5':83,
'C6':84,'CS6':85,'D6':86,'DS6':87,'E6':88,'F6':89,'FS6':90,'G6':91,'GS6':92,'A6':93,'AS6':94,'B6':95,
'C7':96,'CS7':97,'D7':98,'DS7':99,'E7':100,'F7':101,'FS7':102,'G7':103,'GS7':104,'A7':105,'AS7':106,'B7':107,
}
wsSev = {}
app = Flask(__name__)
sockets = Sockets(app)
cheatMode = 0
cheatIndex = 0
currentGroup = ["C4","D4","E4","F4","G4","A4","B4"]
# 服务器启动时,开启ws服务器
def main_app():
from gevent import pywsgi
from geventwebsocket.handler import WebSocketHandler
server = pywsgi.WSGIServer(('192.168.10.106', 9302), app, handler_class=WebSocketHandler)
# 开启udp监听
executor.submit(udp_conn)
print("websocket服务启动")
server.serve_forever()
def udp_conn():
global wsSev
global cheatMode
global cheatIndex
global currentGroup
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
address=('192.168.10.106', 9302)
udp_socket.bind(address)
print('UDP监听开启')
try:
while True:
revc_data = udp_socket.recvfrom(1024)
print(str(revc_data[0], encoding = "utf-8"))
noteinfo = json.loads(str(revc_data[0], encoding = "utf-8"))
print(cheatMode)
if cheatMode == 1 and str(noteinfo["status"])=='1':
noteinfo["note"] = currentGroup[cheatIndex]
print(currentGroup[cheatIndex])
cheatIndex = cheatIndex + 1
if len(currentGroup) == cheatIndex:
cheatIndex = 0
play(noteinfo)
if wsSev:
wsSev.send(json.dumps(noteinfo))
except:
wsTarget.close()
# 持续监听客户端发送的数据
def ws_listener():
global wsSev
try:
while True:
message = wsSev.receive()
print(message)
if message is not None:
noteinfo = json.loads(message)
play(noteinfo)
#wsSev.send("我接收到了!")
except:
wsSev.close()
# 客户端与服务器建立连接的接口
@sockets.route('/connectServer')
def connect_server(socket):
global wsSev
print('connected')
wsSev = socket
print('接收到客户端的连接')
#wsSev.send("你已成功连接至服务器")
ws_listener()
def play(noteinfo): #使用Pygame调用声卡发声
global cheatMode
global cheatIndex
if str(noteinfo['status']) == "1":
print("发声")
print(noteinfo['note'])
if str(noteinfo['note']) == 'X1':
cheatMode = 1
cheatIndex = 0
print("开启cheat模式")
elif str(noteinfo['note']) == 'X2':
cheatMode = 0
cheatIndex = 0
print("关闭cheat模式")
else:
player.note_on(Tone[noteinfo['note']], volume) #d出声音
else:
print("停止")
player.note_off(Tone[noteinfo['note']], volume) #关闭声音
if __name__ == "__main__":
main_app()
this.ws = new WebSocket("ws://9.7.0.65:9303/connectServer");
ws需要作为一个全局对象,在data中初始化
2、持续监听(接收数据)this.ws.onmessage = ((event) => {
// 将接收到的数据序列化为JSON结构(Object对象)
let socketMessage = event.data
console.log(socketMessage)
});
3、发送数据
this.ws.send('要发送的内容')
icon图标(前端):
用到UI控件 Element:Element - The world's most popular Vue UI framework
注意注意:
开始前记得,安装和在main.js中引用!
本项目需要三个icon,第一个:开始播放,第二个:删除前一个音符,第三个删除全部音符
代码如下:
接下来 准备写下拉选择播放音乐:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)