Skip to content

Commit 0caf3e1

Browse files
committed
Lilygo T-Deck keyboard improvements + I2S audio support
1 parent 992b70b commit 0caf3e1

File tree

14 files changed

+258
-110
lines changed

14 files changed

+258
-110
lines changed

PyDOS.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def PyDOS():
6767
global envVars
6868
if "envVars" not in globals().keys():
6969
envVars = {}
70-
_VER = "1.29"
70+
_VER = "1.30"
7171
prmpVals = ['>','(',')','&','|','\x1b','\b','<','=',' ',_VER,'\n','$','']
7272

7373
print("Starting Py-DOS...")
@@ -250,7 +250,7 @@ def dirLoop(tmpDir,lastDir,isFile,swPause,swWide,swRecur,prSum, \
250250
break
251251
dirHeadPrntd = True
252252

253-
if not ForD:
253+
if not ForD:
254254
fSize = 0
255255
nDirs += 1
256256
else:
@@ -459,6 +459,7 @@ def readBATFile(BATfile):
459459
gc.collect()
460460

461461
while True:
462+
scrWdth = int(envVars["_scrWidth"])
462463
if condCmd != "":
463464
cmdLine = condCmd
464465
condCmd = ""
@@ -836,7 +837,7 @@ def readBATFile(BATfile):
836837
envVars[envCmdVar] = tHeight
837838
elif envVars.get(envCmdVar) != None:
838839
envVars.pop(envCmdVar)
839-
840+
840841
scrWdth = int(envVars["_scrWidth"])
841842
if envCmdVar == 'LIB':
842843
path.clear()
@@ -869,7 +870,7 @@ def readBATFile(BATfile):
869870
# Second argument has valid path
870871
if validPath and args[2][-1] != sep and '?' not in newdir2 and \
871872
'*' not in newdir2 and newdir2 !="." and newdir2 != "..":
872-
873+
873874
# second argument doesn't specify an existing target
874875
if newdir2 not in os.listdir(tmpDir2):
875876
currDRen = False

cpython/boardconfigs/pydos_bcfg_lilygo_tdeck.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
import board
44

55
Pydos_pins = {
6+
'i2s_BitClock' : (board.SPEAKER_SCK,"board.SPEAKER_SCK IO7"),
7+
'i2s_WordSelect' : (board.SPEAKER_WS,"board.SPEAKER_WS IO5"),
8+
'i2s_Data' : (board.SPEAKER_DOUT,"board.SPEAKER_DOUT IO6"),
69
'SCL' : (board.SCL,"SCL IO8"),
710
'SDA' : (board.SDA,"SDA IO18"),
811
'SCK' : [(board.SCK,"SCK IO40")],

cpython/boardconfigs/pydos_bcfg_pimoroni_pico_dv_base.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
import board
44

55
Pydos_pins = {
6+
'i2s_BitClock' : (board.I2S_BIT_CLOCK,"board.I2S_BIT_CLOCK, GP27"),
7+
'i2s_WordSelect' : (board.I2S_WORD_SELECT,"board.I2S_WORD_SELECT, GP28"),
8+
'i2s_Data' : (board.I2S_DATA,"board.IS2_DATA, GP26"),
69
'SCL' : (board.GP1,"GP1"),
710
'SDA' : (board.GP0,"GP0"),
811
'SCK' : [(board.SD_SCK,"SD_SCK")],

cpython/lib/optional/pydos_ui_lilygokbd.py

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
from sys import stdin,stdout,implementation
22

33
import board
4+
import keypad
5+
import time
46
from supervisor import runtime
57
from pydos_hw import Pydos_hw
68
from adafruit_bus_device.i2c_device import I2CDevice
7-
import countio
8-
import keypad
9-
import time
109

1110
class PyDOS_UI:
1211

1312
def __init__(self):
13+
self.trackevent = None
1414
self.scrollable = False
1515
self.arrow = ""
1616
self._cmdhistlock = False
@@ -22,13 +22,16 @@ def __init__(self):
2222
self.commandHistory = [""]
2323

2424
self._i2c = I2CDevice(Pydos_hw.I2C(), _ADDRESS_KBD)
25-
self.trackball = {
26-
"A": countio.Counter(board.TRACKBALL_UP),
27-
"B": countio.Counter(board.TRACKBALL_DOWN),
28-
"C": countio.Counter(board.TRACKBALL_RIGHT),
29-
"D": countio.Counter(board.TRACKBALL_LEFT)
30-
}
31-
self.click = keypad.Keys([board.TRACKBALL_CLICK], value_when_pressed=False)
25+
self.trackball = keypad.Keys(
26+
[
27+
board.TRACKBALL_CLICK,
28+
board.TRACKBALL_UP,
29+
board.TRACKBALL_DOWN,
30+
board.TRACKBALL_LEFT,
31+
board.TRACKBALL_RIGHT
32+
],
33+
value_when_pressed=False
34+
)
3235

3336
def get_screensize(self):
3437
return (19,51)
@@ -39,26 +42,13 @@ def serial_bytes_available(self):
3942

4043
# Find the last direction the trackball was moved
4144
# and wait for trackball to stop moving
42-
largestDir = 0
4345
self.arrow = ''
44-
trackloop = True
45-
while trackloop:
46-
trackloop = False
47-
for p,c in Pydos_ui.trackball.items():
48-
if c.count > largestDir:
49-
trackloop = True
50-
if p not in "AB" or not self._cmdhistlock:
51-
self.arrow = p
52-
largestDir = c.count
53-
c.reset()
54-
else:
55-
c.reset()
56-
if self.arrow != "" and not trackloop:
57-
time.sleep(.05)
58-
# clear all counters
59-
for p,c in Pydos_ui.trackball.items():
60-
if c.count > 0:
61-
c.reset()
46+
self.trackevent = Pydos_ui.trackball.events.get()
47+
if self.trackevent and (self.trackevent.key_number not in [1,2] or not self._cmdhistlock):
48+
self.arrow = ' ABDC'[self.trackevent.key_number]
49+
if self.arrow == " ":
50+
self.arrow = ""
51+
else:
6252
retval = 1
6353
self._touched = True
6454

@@ -231,7 +221,7 @@ def input(disp_text=None):
231221
editCol -= 1
232222
elif arrow !='':
233223
pass
234-
elif keys[editCol-1:editCol] == '\x08':
224+
elif keys[editCol-1:editCol] in ['\x08','\x7f']:
235225
keys = keys[:max(0,editCol-2)]+keys[editCol:]
236226
if editCol > 1:
237227
print(('\x08'*(editCol-1))+keys+' \x08\x08',end="")

lib/pydos_bcfg.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
77
led
88
sndPin
9+
i2s_BitClock
10+
i2s_WordSelect
11+
i2s_Data
912
neoPixel
1013
neoPixel_Pow
1114
dotStar_Clock
@@ -68,6 +71,14 @@
6871
Pydos_pins['sndPin'] = (board.BUZZER,"board.BUZZER")
6972
elif 'D12' in dir(board):
7073
Pydos_pins['sndPin'] = (board.D12,"D12 GPIO12")
74+
if 'I2S_BIT_CLOCK' in dir(board):
75+
Pydos_pins['i2s_BitClock'] = (board.I2S_BIT_CLOCK,"board.I2S_BIT_CLOCK")
76+
Pydos_pins['i2s_WordSelect'] = (board.I2S_WORD_SELECT,"board.I2S_WORD_SELECT")
77+
Pydos_pins['i2s_Data'] = (board.I2S_DATA,"board.I2S_DATA")
78+
elif 'SPEAKER_SCK' in dir(board):
79+
Pydos_pins['i2s_BitClock'] = (board.SPEAKER_SCK,"board.SPEAKER_SCK")
80+
Pydos_pins['i2s_WordSelect'] = (board.SPEAKER_WS,"board.SPEAKER_WS")
81+
Pydos_pins['i2s_Data'] = (board.SPEAKER_DOUT,"board.SPEAKER_DOUT")
7182
if 'NEOPIXEL' in dir(board):
7283
Pydos_pins['neoPixel'] = (board.NEOPIXEL,"NEOPIXEL")
7384
if 'NEOPIXEL_POWER' in dir(board):

lib/pydos_hw.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ def __init__(self):
7676
pass
7777

7878
self.sndPin = Pydos_pins.get('sndPin',(None,None))[0]
79+
self.i2sSCK = Pydos_pins.get('i2s_BitClock',(None,None))[0]
80+
self.i2sWS = Pydos_pins.get('i2s_WordSelect',(None,None))[0]
81+
self.i2sDATA = Pydos_pins.get('i2s_Data',(None,None))[0]
7982
self.neoPixel = Pydos_pins.get('neoPixel',(None,None))[0]
8083
self.neoPixel_Pow = Pydos_pins.get('neoPixel_Pow',(None,None))[0]
8184
self.dotStar_Clock = Pydos_pins.get('dotStar_Clock',(None,None))[0]
@@ -125,6 +128,21 @@ def __init__(self):
125128
pass
126129

127130
if implementation.name.upper() == 'CIRCUITPYTHON':
131+
if not self.i2sSCK:
132+
if 'I2S_BIT_CLOCK' in dir(board):
133+
try:
134+
self.i2sWS = board.I2S_WORD_SELECT
135+
self.i2sDATA = board.I2S_DATA
136+
self.i2sSCK = board.I2S_BIT_CLOCK
137+
except:
138+
self.i2sWS = None
139+
elif 'SPEAKER_SCK' in dir(board):
140+
try:
141+
self.i2sWS = board.SPEAKER_WS
142+
self.i2sDATA = board.SPEAKER_DOUT
143+
self.i2sSCK = board.SPEAKER_SCK
144+
except:
145+
self.i2sWS = None
128146
if not self.led:
129147
if 'LED1' in dir(board):
130148
self.led = board.LED1

piano.py

Lines changed: 67 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,15 @@
1111
from supervisor import ticks_ms
1212
from pwmio import PWMOut
1313
from board import board_id
14+
try:
15+
import synthio
16+
import audiobusio
17+
except:
18+
pass
1419

1520
def piano():
1621

22+
noteMS = 500
1723
if sys.implementation.name.upper() == 'MICROPYTHON':
1824
oldPWM = False
1925
try:
@@ -23,66 +29,78 @@ def piano():
2329
except:
2430
pwm=machine.PWM(Pydos_hw.sndPin)
2531
elif sys.implementation.name.upper() == 'CIRCUITPYTHON':
26-
Pydos_hw.sndGPIO.deinit() # Workaround for ESP32-S2 GPIO issue
27-
pwm=PWMOut(Pydos_hw.sndPin, duty_cycle=0, frequency=440, variable_frequency=True)
32+
if Pydos_hw.i2sSCK:
33+
noteMS = 375
34+
i2s = audiobusio.I2SOut(Pydos_hw.i2sSCK,Pydos_hw.i2sWS,Pydos_hw.i2sDATA)
35+
synth = synthio.Synthesizer(sample_rate=22050)
36+
e = synthio.Envelope(attack_time=.0001,decay_time=.0001,release_time=0,attack_level=.5,sustain_level=.5)
37+
i2s.play(synth)
38+
else:
39+
Pydos_hw.sndGPIO.deinit() # Workaround for ESP32-S2 GPIO issue
40+
pwm=PWMOut(Pydos_hw.sndPin, duty_cycle=0, frequency=440, variable_frequency=True)
2841

2942
print ("\nPress +/- to change volume, 'q' to quit...")
3043

3144
volume = 400
45+
lastVol = 400
3246
cmnd = None
3347
press = False
3448
noneAt = ticks_ms()
3549
firstNone = True
3650
note = 0
51+
rnote = 0.0
52+
lastNote = -1
3753
while cmnd != "q":
3854
if Pydos_ui.serial_bytes_available() != 0:
3955
cmnd = Pydos_ui.read_keyboard(1)
4056
#print("->"+cmnd+"<- : ",ord(cmnd[0]))
4157
firstNone = True
4258
if cmnd=="h": # middle C
43-
note=int(261.6256+.5)
59+
rnote=261.6256
4460
press = True
4561
elif cmnd == "a": # E
46-
note=int(164.8138+.5)
62+
rnote=164.8138
4763
press = True
4864
elif cmnd == "s": # F
49-
note=int(174.6141+.5)
65+
rnote=174.6141
5066
press = True
5167
elif cmnd == "d": # G
52-
note=int(195.9977+.5)
68+
rnote=195.9977
5369
press = True
5470
elif cmnd == "r": # G sharp/A flat
55-
note=int(207.6523+.5)
71+
rnote=207.6523
5672
press = True
5773
elif cmnd == "f": # A
58-
note=int(220)
74+
rnote=220.0
5975
press = True
6076
elif cmnd == "t": # A sharp/B flat
61-
note=int(233.0819+.5)
77+
rnote=233.0819
6278
press = True
6379
elif cmnd == "g": # B
64-
note=int(246.9417+.5)
80+
rnote=246.9417
6581
press = True
6682
elif cmnd == "u": # C sharp/D flat
67-
note=int(277.1826+.5)
83+
rnote=277.1826
6884
press = True
6985
elif cmnd == "j": # D
70-
note=int(293.6648+.5)
86+
rnote=293.6648
7187
press = True
7288
elif cmnd == "k": # E
73-
note=int(329.6276+.5)
89+
rnote=329.6276
7490
press = True
7591
elif cmnd == "l": # F
76-
note=int(349.2262+.5)
92+
rnote=349.2262
7793
press = True
7894
elif cmnd == "o": # F sharp/G flat
79-
note=int(369.9944+.5)
95+
rnote=369.9944
8096
press = True
8197
elif cmnd == ";": # G
82-
note=int(391.9954+.5)
98+
rnote=391.9954
8399
press = True
84100
elif cmnd == "q":
85101
break
102+
if press:
103+
note = int(rnote+.5)
86104

87105
if cmnd == "+":
88106
volume += 100
@@ -93,7 +111,7 @@ def piano():
93111
noneAt = ticks_ms()
94112
firstNone = False
95113
else:
96-
if ticks_ms() > noneAt+500:
114+
if ticks_ms() > noneAt+noteMS:
97115
firstNone = True
98116
press = False
99117

@@ -111,18 +129,25 @@ def piano():
111129
if "ESP32" in sys.implementation._machine or "S2" in sys.implementation._machine:
112130
sleep(.1)
113131
elif sys.implementation.name.upper() == 'CIRCUITPYTHON':
114-
if pwm.frequency != note:
115-
pwm.frequency = note
116-
if pwm.duty_cycle != volume:
117-
pwm.duty_cycle = volume
118-
if "s2" in board_id or "s3" in board_id:
119-
sleep(.1)
132+
if Pydos_hw.i2sSCK:
133+
if lastNote != rnote:
134+
if lastNote != -1:
135+
synth.release(snote)
136+
volume = max(0,min(1000,volume))
137+
if lastVol != volume:
138+
lastVol = volume
139+
e = synthio.Envelope(attack_time=.0001,decay_time=.0001,release_time=0,attack_level=volume/1000,sustain_level=volume/1000)
140+
snote = synthio.Note(frequency=rnote,envelope=e)
141+
synth.press(snote)
142+
lastNote = rnote
143+
else:
144+
if pwm.frequency != note:
145+
pwm.frequency = note
146+
if pwm.duty_cycle != volume:
147+
pwm.duty_cycle = volume
148+
if "s2" in board_id or "s3" in board_id:
149+
sleep(.1)
120150

121-
#time.sleep(.1)
122-
#cmnd = kbdInterrupt()
123-
#while cmnd != None:
124-
#cmnd = kbdInterrupt()
125-
#pressedat = time.time()
126151
else:
127152
if sys.implementation.name.upper() == 'MICROPYTHON':
128153
if oldPWM:
@@ -132,15 +157,23 @@ def piano():
132157
else:
133158
pwm.duty(0)
134159
elif sys.implementation.name.upper() == 'CIRCUITPYTHON':
135-
if pwm.duty_cycle != 0:
136-
pwm.duty_cycle = 0
137-
#print("Release")
160+
if Pydos_hw.i2sSCK:
161+
if lastNote != -1:
162+
synth.release(snote)
163+
lastNote = -1
164+
else:
165+
if pwm.duty_cycle != 0:
166+
pwm.duty_cycle = 0
138167

139168
if sys.implementation.name.upper() == 'CIRCUITPYTHON':
140-
pwm.deinit()
141-
quietSnd() # Workaround for ESP32-S2 GPIO issue
169+
if Pydos_hw.i2sSCK:
170+
synth.deinit()
171+
i2s.deinit()
172+
else:
173+
pwm.deinit()
174+
quietSnd() # Workaround for ESP32-S2 GPIO issue
142175

143-
if Pydos_hw.sndPin:
176+
if Pydos_hw.sndPin or Pydos_hw.i2sSCK:
144177
piano()
145178
else:
146179
print("Sound Pin not found")

0 commit comments

Comments
 (0)