大体分为硬件端和软件端,硬件端包括:esp32在面包板上的连接,画pcb电路图,焊接调试pcb电路,最后用三维设计外壳。软件端包括:用python编写的后端服务器,页面展示的客户端。(本项目要用到有关乐理知识)
1.使esp32发出不同的声音
2.esp32发声(奏乐)
2.录制esp32发音,判断摁键摁下的长短
3.利用pygame发声
4.用udp连接使服务器和硬件建立联系,硬件摁下琴键服务端收到udp信息,发出对应的音调
5.esp32的WiFi连接
1.在页面绘制UI琴键
2.构建电子琴结构
3.服务端和前端建立websocket连接
4.硬件发送udp向服务端,服务端收到信息通过websocket连接发送到前端
1.开启作弊模式
2.切换上一首下一首,清除页面前一个音符,清空页面全部音符
3.播放音乐
1.esp32在面包板上的连接
2.画pcb电路图
3.pcb电路板的焊接
4.三维模型的设计打印
1.前端优化
2.三维设计,pcb焊接,锂电池的供电
3.硬件的调试(按定义好的音调奏乐)
------------------------------------------------------------------------------
在动手做之前我们应该了解基本乐理知识:音乐基础乐理知识大全 | 乐理知识 - 知乎 (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循环一次
窗口显示按键样式和音符
# -*- 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())
{{key.note}}
服务端:
from flask import Flask #Flask服务器库
from flask_sockets import Sockets #WS连接库
import pygame.midi #弹奏音乐的库
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) #弹出声音
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中初始化
this.ws.onmessage = ((event) => {
// 将接收到的数据序列化为JSON结构(Object对象)
let socketMessage = event.data
console.log(socketMessage)
});
this.ws.send('要发送的内容')
注意注意:
开始前记得,安装和在main.js中引用!
本项目需要三个icon,第一个:开始播放,第二个:删除前一个音符,第三个删除全部音符
代码如下:
需要三端联调
硬件端代码如下(只有作弊的代码,不全):
from machine import Pin
import time
import socket
import json
p0 = Pin(0, Pin.IN) # create input pin on GPIO0
print(p0.value()) # get value, 0 or 1
musickeydict = {
"status":"1",
"note":"C3"
}
#client 发送端
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ("9.7.0.65", 9302) # 接收方 服务器的ip地址和端口号
def sendtone(note,status):
musickeydict["note"] = note
musickeydict["status"] = status
client_socket.sendto(bytes(json.dumps(musickeydict).encode('utf-8')), server_address) #将msg内容发送给指定接收方
key_time = 0
while True :
time.sleep(0.01)
if (p0.value()==0):
key_time +=1
if key_time ==3:
print("cheat")
sendtone('X1','1')
else:
key_time = 0
运用两块esp32,
假设 a块和b块,
a的esp32的boot键摁下是发送'X1','1' ,
b的esp32键摁下发送'X2','1' 。
创建全局变量(下)
# 0:正常模式;1:Cheat模式
cheatMode = 0
# 开启Cheat模式时,对应到乐谱的第几个音
cheatIndex = 0
创建乐曲对象 & 导入乐谱(下)
# 创建乐曲对象,以note数组的形式导入
song = ["C4","B3","C4","G4","A4","A4","G4","D4","D4","F4","E4","C4","B3","C4","E4","F4","F4","G4","D4","C4","C4","C4","B3","C4","G4","A4","A4","G4","D4","D4","F4","E4","D4","E4","C4","B3","C4","E4","F4","F4","G4","D4","C4","C4"]
开启/关闭Cheat模式
当接收到X1或X2时,开启/关闭Cheat模式
if str(noteinfo['note']) == 'X1':
cheatMode = 1
cheatIndex = 0
print("开启cheat模式")
elif str(noteinfo['note']) == 'X2':
cheatMode = 0
cheatIndex = 0
print("关闭cheat模式")
当检测到cheat开启:
if cheatMode == 1 and str(noteinfo["status"])=='1' and str(noteinfo["note"])!='X1' and str(noteinfo["note"])!='X2':
noteinfo["note"] = currentGroup[cheatIndex]
print(currentGroup[cheatIndex])
cheatIndex = cheatIndex + 1
if len(currentGroup) == cheatIndex:
cheatIndex = 0
css:
简谱显示CSS
.voice {
margin-top: 30px;
border: 1px solid #eee;
box-shadow: 5px 5px 5px #666;
width: 70vw;
height: 520px;
overflow-y: auto;
padding: 20px;
background-color: wheat;
}
.voice .stave {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: flex-start;
flex-wrap: wrap;
}
.voice .note {
margin-right: 10px;
margin-bottom: 10px;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
height: 80px;
font-size: 40px;
min-width: 40px;
}
.voice .note-val {
position: relative;
}
.voice .half-key {
position: absolute;
font-size: 16px;
top: 5px;
left: 25px
}
.note-type-half {
width: 15px;
height: 1px;
background-color: #333;
margin-bottom: 2px;
margin-left: 2px;
}
.voice .key {
height: 12px;
}
.voice .dot {
width: 5px;
height: 5px;
margin: 2px 0;
border-radius: 50%;
background-color: #333;
margin-left: 7px;
}
.op-area {
z-index: 99;
position: absolute;
right: 20vw;
top: 5px;
display: flex;
align-items: center;
justify-content: center;
}
硬件和服务器的联调:
服务器:
def udp_conn():
global instruments
global instrumentIndex
udp_socket = socket.socket(socket.AF_INET , socket.SOCK_DGRAM)
address=('9.7.5.3',9301)
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"))
if str(noteinfo["note"]) == "S1":
instrumentIndex = instrumentIndex + 1
if instrumentIndex == 4:
instrumentIndex = 1
print(instruments[instrumentIndex])
player.set_instrument(instruments[instrumentIndex])
elif str(noteinfo["note"]) == "S2":
instrumentIndex = instrumentIndex - 1
if instrumentIndex == -1:
instrumentIndex = 3
print(instruments[instrumentIndex])
player.set_instrument(instruments[instrumentIndex])
else:
play(noteinfo)
if wsSev:
wsSev.send(json.dumps(noteinfo))
except:
wsTarget.close()
硬件:
因为每块esp32上只有一个boot键
假设有a和b
a是发送S1的也就是上一个乐器 b是发送S2的也就是上一个乐器
# 先要连上wifi wifi连接代码此处省略……………………
import socket
import time
import WiFi
import json
WiFi.wifi_connect()
from machine import TouchPad, Pin,PWM
from time import sleep
print("系统充电")
time.sleep(1)
LED = Pin(2,Pin.OUT)#面包板
# LED = Pin(22,Pin.OUT)#PCB
p0 = Pin(0, Pin.IN, Pin.PULL_UP) # create input pin on GPIO0
pwm0 = PWM(Pin(23))
freq_val=5
duty_val=0
pwm0.freq(freq_val)
pwm0.duty(duty_val)
# PINn = (32,33,27,13,12,15,14,4)#PCB元组
PINn = (12,13,14,27,33,32,15,4)#面包板元组
TPx = [TouchPad(Pin(i)) for i in PINn]#TPx 所有的触摸管脚定义
musickeydict = {
"status":"1",
"note":"G3"
}
# musickeydict["note"] = 'G6'
# musickeydict["status"] = '0'
# print(musickeydict)
def sendtone(note,status):
musickeydict["note"] = note
musickeydict["status"] = status
client_socket.sendto(bytes(json.dumps(musickeydict).encode('utf-8')), server_address) #将msg内容发送给指定接收方
msg = ('D3',#需要发送的信息
'C3',
'E3',
'F3',
'G3',
'A3',
'B3',
'C4',
)
# do_connect()# wifi连接
LED.on()# LED点亮说明WIFI连接正常
time.sleep(1)
#client 发送端
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# server_address = ("192.168.43.234", 8008) # 接收方 服务器的ip地址和端口号
server_address = ("9.7.5.3", 9301) # 接收方 服务器的ip地址和端口号
key_time = 0 #按键时间
key_time1 = 0 #按键时间
keychar = ''
def dzq():
'电子琴触摸按键子程序'
global key_time,keychar,key_time1
for ton,tp in zip(msg,TPx):
'遍历所有的触摸按钮'
tp_val = 10000
try:
tp_val = tp.read()
except:
print("触摸读取错误")
break
if tp_val<200:#有触摸按键按下
key_time += 1#按键时间+1
if key_time == 3:
try:
sendtone(ton,'1')
# musickeydict["note"] = ton
# musickeydict["status"] = '1'
# client_socket.sendto(bytes(json.dumps(musickeydict).encode('utf-8')), server_address) #将msg内容发送给指定接收方
except:
print("发送失败")
keychar = ton
LED.on()
break
else:
'如果运行完所有的循环,没有break,就执行这个else'
if key_time != 0:
sendtone(keychar,'0')
# musickeydict["note"] = keychar
# musickeydict["status"] = '0'
# client_socket.sendto(bytes(json.dumps(musickeydict).encode('utf-8')), server_address) #将msg内容发送给指定接收方
key_time = 0#按键时间清零
LED.off()
if (p0.value()==0):
key_time1 +=1
if key_time1 ==3:
print("切换下一个乐器")
sendtone("S1",1)
html:
>
>
定义一个方法如下:
playSong(info) {
console.log(info)
if (info == 'top') {
let a ={
note:"S1"
}
this.ws.send(JSON.stringify(a))
}
else {
let b ={
note:"S2"
}
this.ws.send(JSON.stringify(b))
}
增加退格、清空按钮
通过ElementUI,新增歌单选择功能
export function getSong() {
// 歌曲内容
let song = [{
notenum: 5,
lh: 0,
half: false,
notetype: 0,
}]
return song
}
import {
getSong as getSong1
} from './mysong1'
import {
getSong as getSong2
} from './mysong2'
//当前歌单index
songListIndex: 0,
//歌单
songList: [],
创建歌单对象列表,对象中包含3个属性,分别是
speed:n 播放速度(以0.5一拍,n倍速)
song: 歌曲内容
name: 歌曲名称
mounted() {
this.songList = [{
speed: 1,
song: getSong1(
name: 'Way Back Home'
}, {
speed: 2,
song: getSong2(
name: '青石巷'
}, {
speed: 2,
song: getSong3(
name: '菊次郎的夏天'
}, {
speed: 1,
song: getSong4(
name: '起风了'
}, {
speed: 1.5,
song: getSong5(
name: '白月光与朱砂痣'
}]
changeSong() {
// 指向当前对应的歌曲
this.songs = this.songList[this.songListIndex].song
}
let duration = this.durationList[key.notetype]
if (element.speed) {
duration = duration * element.speed
}
我用的是立创eda
@change="input_words()">
style="font-size:20px">
export default {
name: 'index',
components: {},
data() {
return {
options_I: [{
name: '乐器1',
value: '1',
}, {
name: '乐器2',
value: '2',
}, {
name: '乐器3',
value: '3',
}, {
name: '乐器4',
value: '4',
}, {
name: '乐器5',
value: '5',
}],
value_I: [],
options_v: [{
value: {
1: '0'
},
}, {
value: {
2: '0'
},
}, {
value: {
3: '0'
},
}, {
value: {
4: '0'
},
}, {
value: {
5: '0'
},
}],
value_I: [],
value1: 0,
instrumentVolumes: [{
value: 85,
isMute: false,
}, {
value: 96,
isMute: false,
}, {
value: 118,
isMute: false,
}, {
value: 102,
isMute: false,
}, {
value: 74,
isMute: false,
}, {
value: 66,
isMute: false,
}],
// 输入的文本内容
input1: "85",
input2: "96",
input3: "118",
input4: "102",
input5: "74",
methods: {
formatTooltip(val) {
let dict = {
volume: this.input
}
this.ws.send(JSON.stringify(dict))
return val / 99;
},
input_words() {
let dict = {
intr1: this.instrumentVolumes[0].value,
intr2: this.instrumentVolumes[1].value,
intr3: this.instrumentVolumes[2].value,
intr4: this.instrumentVolumes[3].value,
intr5: this.instrumentVolumes[4].value,
intr5: this.instrumentVolumes[5].value,
send: '1'
// volume: this.input
}
console.log(dict)
this.ws.send(JSON.stringify(dict))
},
简谱显示 需要import 比如说song1 song2......
import pygame.midi #弹奏音乐的库
import json
import socket
from flask import Flask #Flask服务器库
from flask_sockets import Sockets #WS连接库
from concurrent.futures import ThreadPoolExecutor #引用线程池
from gevent import pywsgi
from geventwebsocket.handler import WebSocketHandler
# 创建线程池执行器
executor = ThreadPoolExecutor(3)
# 初始化pygame设置
device = 0 # device number in win10 laptop
# initize Pygame MIDI ----------------------------------------------------------
pygame.midi.init() # PYGAMEMIDI库的初始化
# set the output device --------------------------------------------------------
player = pygame.midi.Output(device)
# 定义了一个输出音轨
player.set_instrument(1) # 设置乐器音色 乐器 http://www.ccarh.org/courses/253/handout/gminstruments/
# set the instrument ------------------------------------0000000000000000000000000-----------------------
# 初始化设置结束
cheats = 0
index = 0
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
music = ['G3','G3','G3','G3','G3','G3','G3','G3','G3','G3','G3','G3',
'E4','E4','E4','E4','E4','E4','E4','E4','E4','E4','E4','E4',
'D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4',
'E4','E4','E4','E4','E4','E4','E4','E4','E4','E4','E4','E4',
'E3','E3','E3','E3','E3','E3','E3','E3','E3','E3','E3','E3',
'C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4',
'B3','B3','B3','B3','B3','B3','B3','B3','B3','B3','B3','B3',
'A3','A3','A3','A3','A3','A3','A3','A3','A3','A3','A3','A3',
'A3','A3','A3','A3','A3','A3','A3','A3','A3','A3','A3','A3',
'D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4',
'C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4',
'D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4',
'A3','A3','A3','A3','A3','A3','A3','A3','A3','A3','A3','A3',
'C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4',
'D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4',
'D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4',
'D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4',
'D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4',
'C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4',
'D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4',
'E4','E4','E4','E4','E4','E4','E4','E4','E4','E4','E4','E4',
'D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4',
'E3','E3','E3','E3','E3','E3','E3','E3','E3','E3','E3','E3',
'E4','E4','E4','E4','E4','E4','E4','E4','E4','E4','E4','E4',
'D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4',
'E4','E4','E4','E4','E4','E4','E4','E4','E4','E4','E4','E4',
'E3','E3','E3','E3','E3','E3','E3','E3','E3','E3','E3','E3',
'C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4',
'B3','B3','B3','B3','B3','B3','B3','B3','B3','B3','B3','B3',
'A3','A3','A3','A3','A3','A3','A3','A3','A3','A3','A3','A3',
'G3','G3','G3','G3','G3','G3','G3','G3','G3','G3','G3','G3',
'A3','A3','A3','A3','A3','A3','A3','A3','A3','A3','A3','A3',
'C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4',
'C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4',
'D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4',
'E4','E4','E4','E4','E4','E4','E4','E4','E4','E4','E4','E4',
'D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4',
'D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4','D4',
'B3','B3','B3','B3','B3','B3','B3','B3','B3','B3','B3','B3',
'C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4','C4',
'C4','C4','C4','C4','C4','C4',]
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,
'C8':108}
times = 0
def send(msg,server_address):
global times
try:
times += 1
print(times)
#将msg内容发送给指定接收方,要bytes(zmsg.encode('utf-8'))转一下字符格式
client_socket.sendto(bytes(msg.encode('utf-8')),server_address)
except:
print("数据发送失败!")
def Play(noteinfo): #使用Pygame调用声卡发声
if noteinfo['status'] == '1':
#print(noteinfo['instrument'])
player.set_instrument(noteinfo['instrument'])
player.note_on(Tone[noteinfo['note']],noteinfo['volume']) #弹出声音
else:
player.set_instrument(noteinfo['instrument'])
print(noteinfo)
player.note_off(Tone[noteinfo['note']],noteinfo['volume']) #关闭声音
wsSev = {} #建立一个新的服务器变量
app = Flask(__name__)
sockets = Sockets(app)
# 服务器启动时,开启ws服务器
def main_app():
# 设定服务器网址和端口
server = pywsgi.WSGIServer(('192.168.43.82',9300),app,handler_class=WebSocketHandler)
#开启udp监听
executor.submit(udp_conn)
print("websocket服务启动")
server.serve_forever() #服务器始终处于监听状态
# udp监听硬件端发送的数据
def udp_conn():
global wsSev,instrument,noteinfo,cheats,index
server_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
address=('',9302) #服务器地址,但是要另外开一个端口
server_socket.bind(address)
print('UDP监听开启')
try:
while True:
revc_data,client = server_socket.recvfrom(1024)
if str(revc_data,encoding="utf-8") == "on":
print(bytes.decode(revc_data),client[0])
noteinfo = {'volume':127}
send(json.dumps({'computer':'1'}),(client[0],9301))
print([0])
else:
noteinfo = json.loads(str(revc_data,encoding = "utf-8"))
print(str(revc_data,encoding = "utf-8"))
if noteinfo['note'] == 'X1':
cheats += 1
elif cheats > 0:
noteinfo["note"] = music[index]
print('000000000000000000000')
print(music[index])
Play(noteinfo)
index += 1
if index == 40 * 6 - 1:
index = 0
if wsSev:
wsSev.send(json.dumps(noteinfo))
except:
wsTarget.close()
# 持续监听客户端发送的数据
def ws_listener():
global wsSev
try:
while True:
message = wsSev.receive() #收到WS数据
print(message)
if message is not None: #如果内容不为空
noteinfo = json.loads(message)
if noteinfo['send'] == '1':
print(str(json.dumps(noteinfo)))
send(json.dumps(noteinfo),("192.168.1.220",9301))
else:
Play(noteinfo) #弹奏音乐
#wsSev.send("我接收到了!")
except:
wsSev.close()
# 客户端与服务器建立连接的接口
#当客户端调用接口时,自动进入connect_server(socket)函数,然后就默认连接上了
@sockets.route('/connectServer')
def connect_server(socket):
global wsSev
print('connected')
wsSev = socket
print('接收到客户端的连接')
#wsSev.send("你已成功连接至服务器") #反馈客户端连接的信息
ws_listener()
#主程序
if __name__ == "__main__":
main_app()
# -*- coding: utf-8 -*-
import socket
import time
import sys
import json
import network
import _thread
from machine import Pin, PWM, TouchPad
LED = Pin(2, Pin.OUT) #定义GPIO2的口
p18 = Pin(18, Pin.IN)
PINNum = (12,13,14,27,33,32,15,4) # 定义触摸功能的引脚号,元组
TP8 = [TouchPad(Pin(i)) for i in PINNum] # 启用引脚的触摸功能
pwm0 = PWM(Pin(23)) # 定义Pin26脚产生一个PWM脉冲驱动蜂鸣器
key = ('B2','C3','D3','E3','F3','G3','A3','B3') # 定义不同音调 不加窗口
BuzzerKey = (523, 586, 658, 697, 783, 879, 987, 1045) # 定义不同音调的频率
freq_val = 0 # 频率的临时量
duty_val = 0 # 占空比的临时量
msg = ''
key_time = 0 #记录键盘按下的次数
connect = 6
mode = 'buzzer'
message = {'intr1':'127','intr2':'127','intr3':'127','intr4':'127','intr5':'127'}
ii = 0
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ("192.168.43.82", 9302) # 接收方 服务器的ip地址和端口号
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
address = ("", 9301)
server_socket.bind(address) # 为服务器绑定一个固定的地址,ip和端口
server_socket.settimeout(1314/520) #设置一个时间提示,如果1314/520秒钟没接到数据进行提示
#WIFI连接流程
wifi = network.WLAN(network.STA_IF)
wifi.active(True)
wifi.connect('LEBRON', 'woaini7890')
def tone():
while True:
if False:
key = ('C3','D3','E3','F3','G3','A3','B3','switch')
BuzzerKey = ()
elif False:
key = ('C4','D4','E4','F4','G4','A4','B4','switch')
BuzzerKey = ()
elif False:
key = ('C5','D5','E5','F5','G5','A5','B5','switch')
BuzzerKey = ()
elif False:
key = ('C6','D6','E6','F6','G6','A6','B6','switch')
BuzzerKey = ()
def send(msg,server_address):
try:
print(msg)
#将msg内容发送给指定接收方,要bytes(msg.encode('utf-8'))转一下字符格式
client_socket.sendto(bytes(msg.encode('utf-8')), server_address)
except:
print("数据发送失败!")
def computer_Play():
global key_time,mode,pwm0,ii
msg = {'note':'X1','status':'1','volume':int(message['intr1']),'instrument':0,'piano':'1'}
pwm0.duty(0)
LED.off()
while True:
if mode == 'buzzer':
break
for i, tp in enumerate(TP8): # 遍历所有的触摸按键,前面加序号,从0开始
try:
read = tp.read()
except:
read = 200
if read < 200:
LED.on()
key_time += 1 #按键次数加1
if key_time == 2:
msg = {'note':key[i],'status':'1','volume':int(message['intr1']),'instrument':0,'piano':'1'}
send(json.dumps(msg),server_address)
msg = {'note':key[i],'status':'1','volume':int(message['intr2']),'instrument':1,'piano':'0'}
send(json.dumps(msg),server_address)
msg = {'note':key[i],'status':'1','volume':int(message['intr3']),'instrument':8,'piano':'0'}
send(json.dumps(msg),server_address)
msg = {'note':key[i],'status':'1','volume':int(message['intr4']),'instrument':9,'piano':'0'}
send(json.dumps(msg),server_address)
msg = {'note':key[i],'status':'1','volume':int(message['intr5']),'instrument':10,'piano':'0'}
send(json.dumps(msg),server_address)
ii = i
break
else:
if key_time != 0:
LED.off()
msg = {'note':key[ii],'status':'0','volume':int(message['intr1']),'instrument':0,'piano':'1'}
send(json.dumps(msg),server_address)
msg = {'note':key[ii],'status':'0','volume':int(message['intr2']),'instrument':1,'piano':'0'}
send(json.dumps(msg),server_address)
msg = {'note':key[ii],'status':'0','volume':int(message['intr3']),'instrument':8,'piano':'0'}
send(json.dumps(msg),server_address)
msg = {'note':key[ii],'status':'0','volume':int(message['intr4']),'instrument':9,'piano':'0'}
send(json.dumps(msg),server_address)
msg = {'note':key[ii],'status':'0 ','volume':int(message['intr5']),'instrument':10,'piano':'0'}
send(json.dumps(msg),server_address)
time.sleep(0.01)
key_time = 0
def buzzer_Play(): # 电子琴的主程序
global mode,pwm0
pwm0.duty(0)
LED.off()
while True:
if mode == 'computer':
break
for i,tp in enumerate(TP8): # 遍历所有的触摸按键,前面加序号,从0开始
try:
read = tp.read()
except:
read = 200
if read < 200:
freq_val = BuzzerKey[i] # 频率设为按下的音调
duty_val = 512 # 占空比打开
LED.on() # 开灯
break
else:
freq_val = 0
duty_val = 0 # 关闭占空比
LED.off()
# 上面的程序是根据按钮设置频率和占空比
if pwm0.freq() != freq_val: # 如果读到的频率不是现在临时量中的频率,则设置频率和占空比
pwm0.freq(freq_val)
time.sleep(0.01) # 频率和占空比不能同时设置,否则会导致占空比设置失败
pwm0.duty(duty_val)
def sound(threadName,delay):
global mode
while True:
if mode == 'computer':
computer_Play()
elif mode == 'buzzer':
buzzer_Play()
_thread.start_new_thread(sound,("newThread",1))
for i in range(100):
print('第{}次尝试连接WIFI热点'.format(i))
if wifi.isconnected():
break
time.sleep(0.1)
if not wifi.isconnected():
wifi.active(False) #关掉连接,免得repl死循环输出
print('wifi connection error, please reconnect')
buzzer_Play()
else:
print('network config:', wifi.ifconfig())
while True:
#正常情况下接收数据并且显示,如果1秒钟没有接收数据进行提示(打印 "time out")
#当然可以不要这个提示,那样的话把"try:" 以及 "except"后的语句删掉就可以了
try:
# 接收客户端传来的数据 recvfrom接收客户端的数据,默认是阻塞的,直到有客户端传来数据
# recvfrom 参数的意义,表示最大能接收多少数据,单位是字节
# recvfrom返回值说明
# receive_data表示接受到的传来的数据,是bytes类型
# client 表示传来数据的客户端的身份信息,客户端的ip和端口,元组
receive_data,client = server_socket.recvfrom(1024)
initial = json.loads(receive_data)
if initial == {'computer': '1'}:
pass
else:
message = initial
print(client[0],message)
connect = 0
mode = 'computer'
except: #如果没有接收数据进行提示(打印 "time out")
if connect % 3 == 1:
send('on',server_address)
elif connect == 12:
print("time out")
mode = 'buzzer'
elif connect == 13:
connect = 0
connect += 1