##### Driver & Library Initilisation ##### [ING] #####
from machine import Pin, PWM, Timer, I2C
from toneLib import allNotes, mySong
import utime, array, sh1106
##### Configuration Options ##### [ING] #####
songSpeed = 120 #bpm for 4/4
usingWokwi = True #change to False when using Pi Pico
oledPresent = True
mainVolume = 32000 # max of 32768 (max duty cycle)
pwm1 = machine.PWM(machine.Pin(18)) # buzzer 1 on pin GP18
pwm2 = machine.PWM(machine.Pin(20)) # buzzer 2 on pin GP20
printNotes = 0 #default is 0. printNotes = 1 for debugging code
decayRate1 = 1.1
decayRate2 = 1.03
##### Blinking LED Initilisation ##### [ING] #####
myLed = Pin("LED",Pin.OUT) # Raspberry Pi Pico W: built-in LED
LEDTimer = Timer()
def blink(LEDTimer):
myLed.value(0)
##### Display Initilisation ##### [ING] #####
if oledPresent:
i2c = machine.I2C(0,sda=machine.Pin(4),scl=machine.Pin(5), freq=2000000) # Around 3MHz is limit.
oled = sh1106.SH1106_I2C(128, 64, i2c, Pin(16), 0x3c, rotate=180)
oled.rotate=180
oled.sleep(False)
startLine = 0
leftMargin = 0
newLine = False
pageOfText = []
displayTimer = Timer()
##### Routine to feed a line of text (keep to 16 long) ##### [Author: ING] #####
def updateDisplay(displayTimer):
global newLine, pageOfText
if oledPresent:
if newLine:
linePointer = 0
oled.fill(0)
for i in range(startLine,len(pageOfText)):
oled.text(pageOfText[i], leftMargin, linePointer)
linePointer += 10
oled.show()
newLine = False
##### Routine to process text lists so its ready for updateDisplay() ##### [Author: ING] #####
def oPrint(newText):
global pageOfText, newLine
numLines = len(pageOfText)
i=0
if numLines < 6:
pageOfText.append(newText)
else:
for i in range(0,numLines-1):
pageOfText[i] = pageOfText[i+1]
pageOfText[5]=newText
newLine = True
def tickNow(noteTimer):
global noteTicker
noteTicker = True
def play_poly_note(buzzNum, frequency, volume):
global pwm1, pwm2
frequencyHz = int (allNotes[frequency])
if buzzNum == 1:
if frequencyHz > 0:
pwm1.freq(frequencyHz)
pwm1.duty_u16(volume)
else:
pwm1.duty_u16(0) # rest
if buzzNum == 2:
if frequencyHz > 0:
pwm2.freq(frequencyHz)
pwm2.duty_u16(volume)
else:
pwm2.duty_u16(0) # rest
def stopProg():
while 1:
pwm1.deinit()
pwm2.deinit()
##### Music Initilisation ##### [Author: ING] #####
if usingWokwi:
songSpeed *= 4 # Wokwi plays a lot slower.
wholeNoteLength = (60/songSpeed)*4*1000 #for 4/4, in msecs.
playLength1 = 0
playLength2 = 0
stopNote1 = False
stopNote2 = False
playCounter1 = 100
playCounter2 = 100
getNextNote = True
noteNumber = 0 # start at the first note in array
note = mySong[noteNumber]
songLength = len (mySong)
noteTimer = Timer()
#################################
#### Main Start ##### [ING] #####
#################################
if printNotes:
print ('songLength =',songLength)
print('1 bar (1 whole note) takes', wholeNoteLength/1000,'seconds')
LEDTimer.init(freq=5, mode=Timer.PERIODIC, callback=blink)
if oledPresent:
displayTimer.init(freq=3, mode=Timer.PERIODIC, callback=updateDisplay)
noteTimer.init(freq=100,mode=Timer.PERIODIC, callback=tickNow) # updateBuzzStates serviced every 10msec (100Hz)
noteTicker = False
barCounter = 0
decayVolume1 = 0
decayVolume2 = 0
#### Main Loop ##### [ING] #####
while 1:
if getNextNote:
myLed.value(1)
getNextNote = False
if (noteNumber == songLength):
oPrint('Song finished!')
stopProg()
note = mySong[noteNumber]
if note[0] == 2: # buzzer 2
play_poly_note(2, note[1], mainVolume)
decayVolume2 = mainVolume
if printNotes:
print('playing Note:', noteNumber-1, note[1], 'on buzzer 2')
playLength2 = note[2]
noteNumber += 1 # advance and also play buzzer 1
note = mySong[noteNumber]
if note[0] == 1: # buzzer 1
play_poly_note(1, note[1], mainVolume)
decayVolume1 = mainVolume
if printNotes:
print('playing Note:', noteNumber-1, note[1], 'on buzzer 1')
playLength1 = note[2]
if note[0] == 0: # print the line of text
if note[1] == 'Bar':
barCounter += 1
songString = note[1]+" "+str(barCounter)
else:
songString = note[1]
oPrint(songString)
noteNumber += 1
####
if noteTicker == True:
noteTicker = False
if playLength1 >0:
playCounter1 = wholeNoteLength/(10*playLength1)
if printNotes:
print ('for',playCounter1, 'ticks')
playLength1 = 0
else:
if playCounter1 > 0:
playCounter1 -= 1
decayVolume1 = pwm1.duty_u16() # get the current duty cycle
if printNotes:
print('Vol 1 is',decayVolume1)
tempDecay = round(decayVolume1/decayRate1)
if decayVolume1 < tempDecay:
decayVolume1 = 0
else:
decayVolume1 = tempDecay
pwm1.duty_u16(decayVolume1)
else:
stopNote1 = True
if playLength2 >0:
playCounter2 = wholeNoteLength/(10*playLength2)
if printNotes:
print ('for',playCounter2, 'ticks')
playLength2 = 0
else:
if playCounter2 >0:
playCounter2 -= 1
decayVolume2 = pwm2.duty_u16() # get the current duty cycle
if printNotes:
print('Vol 2 is',decayVolume2)
tempDecay = round(decayVolume2/decayRate2)
# if decayVolume2 < tempDecay:
# decayVolume2 = 0
# else:
decayVolume2 = tempDecay
pwm2.duty_u16(decayVolume2)
else:
stopNote2 = True
if stopNote1 == True:
stopNote1 = False
pwm1.duty_u16(0)
getNextNote = True
if stopNote2 == True:
stopNote2 = False
pwm2.duty_u16(0)
#### END ##### [ING] #####