This project is a simple micropython pwm buzzer driver, which supports the application of uasycio to achieve multi-channel music playback, or multi-channels merged into one to achieve chord sound playback.
The method of composing music is more in line with the editing method of numbered musical notation.
Composers can compose music in python code in this way.
Currently only tested ESP32S2 chip.
After confirming the relevant documents, it has been determined that it should be compatible with all chips launched by Espressif that already support the micropython firmware , such as ESP8266,ESP32,ESP32S2,ESP32S3,ESP32C3.
A musical score is a list, and each element in the list contains two values,first value is beat,second value is tone.
Example:
Musical_Score=[(1/2,"L1"),(1/4,"L2"),(1/2,"L3"),(1,"L4"),(2,"L5"),
(1/2,"1#"),(1/4,"2#"),(1/2,"3"),(1,"4#"),(2,"5#"),
(1/2,"H1#"),(1/4,"H2#"),(1/2,"H3"),(1,"H4#"),(2,"H5#"),(1,"0")]
Numbers 1 to 7 represent the musical notes (more accurately the scale degrees).
They always correspond to the diatonic major scale.
For example, in the key of C, their relationship with the notes and the solfège is as follows:
Note | Solfège | Notation |
---|---|---|
C | do | 1 |
D | re | 2 |
E | mi | 3 |
F | fa | 4 |
G | so | 5 |
A | la | 6 |
B | ti | 7 |
In the numbered notation described,dots above or below a musical note raise or lower it to other octaves.
The number of dots equals the number of octaves.
Here,the dots above a musical note change to "L",such as "L1".
The dots below a musical note change to "H",such as "H1".
If it is sharp (higher in pitch by one semitone (half step)), its notation is "#", such as "1#".
The number "0" represents the musical rest.
With these rules, you can edit the musical score, it is recommended to try the simple music that you are most familiar with and love first, and search for its numbered musical notation on the Internet and edit it into code accordingly.
More knowledge about numbered musical notation please refer to Wikipedia or other more professional music theory articles or videos.
A board with micropython firmware already. (Currently only tested ESP32S2)
Upload the buzzer.py
file to the board.
A buzzer and its matching circuit,or just Some connecting lines with a loudspeaker,need to pay attention to the use of voltage and power to avoid failure to work or accidental damage.
Use one wire to share the ground, namely GND to GND.
Use another wire to connect the pin that will output the PWM signal from the board to the signal receiving end of the buzzer or loudspeaker .
Currently only tested ESP32S2.
from machine import Pin,PWM
import time
import buzzer
Musical_Score=[(1/2,"L1"),(1/4,"L2"),(1/2,"L3"),(1,"L4"),(2,"L5"),
(1/2,"1#"),(1/4,"2#"),(1/2,"3"),(1,"4#"),(2,"5#"),
(1/2,"H1#"),(1/4,"H2#"),(1/2,"H3"),(1,"H4#"),(2,"H5#"),(1,"0")]
buzzer=buzzer.Buzzer(Pin(1))
buzzer.play(score=Musical_Score,tempo=60,freq_multiple=1,output=0)
-
buzzer.Buzzer(pwm)
pwm
: A necessary PWM channel, such asmachine.PWM(Pin(1))
.
-
play(score,tempo,freq_multiple,output)
score
: A list include musical score , it is necessary.tempo
: The speed at which a passage of music is or should be played, unit is bpm(Beat Per Minute), Default value istempo=60
, means 60 beats per minute.freq_multiple
: Each tone has its corresponding PWM frequency, setting this value will make the frequency multiply by this value. Default value isfreq_multiple=1
.output
: Default value isoutput=0
.If the value isoutput=1
, detailed information will be printed when each tone is played.
from machine import Pin,PWM
import uasyncio as asyncio
import buzzer
Musical_Score1=[(1/2,"L1"),(1/4,"L2"),(1/2,"L3"),(1,"L4"),(2,"L5")]
Musical_Score2=[(1/2,"1#"),(1/4,"2#"),(1/2,"3"),(1,"4#"),(2,"5#")]
async def main():
buzzer1=buzzer.Buzzer(1)
buzzer2=buzzer.Buzzer(2)
tasks=[asyncio.create_task(
buzzer1.async_play(Musical_Score1,tempo=60,freq_multiple=1,output=1,channel=0))]
tasks.append(asyncio.create_task(
buzzer2.async_play(Musical_Score2,tempo=60,freq_multiple=1,output=1,channel=1)))
await asyncio.gather(*tasks)
asyncio.run(main())
-
buzzer.Buzzer(pwm)
same as ordinary method -
async_play(score,tempo,freq_multiple,output,channel))
mostly same as ordinary methodchannel
: Only valid whenoutput=1
.This channel value will be output when printing detailed information, in order to distinguish the channel corresponding to the specific information.
The application method of uasyncio
is detailed inpeterhinch's Github project :micropython-async
For general applications, please refer to the code in the example.
-
Create an async function ,
async def main():
-
Create a tasks list ,
tasks=[]
-
Create async tasks to the list , such as
tasks=[asyncio.create_task()]
ortasks.append(asyncio.create_task())
-
Use the
gather
method to gather all tasks,await asyncio.gather(*tasks)
-
Use the
run
method to run the async function created in step 1. When the execution reachesawait asyncio.gather(*tasks)
, the corresponding music score will be played on each channel.
If you have any questions or good ideas, please submit your issue, and specify your hardware and software conditions and specific questions in detail.
The code of this program is relatively simple, I think it will not be difficult to transplant and adapt, but there is no other platform available in my hand, and those who are interested are welcome to pull requests.