├── README.md ├── code.py ├── keys.py ├── new.wav ├── playi2s.py ├── tdeck_repl.py └── virtcode.py /README.md: -------------------------------------------------------------------------------- 1 | # tdeck_repl 2 | LILYGO T-Deck Virtual REPL 3 | 4 | Placing code.py and tdeck_repl.py on the LilyGO [T-DECK](https://www.lilygo.cc/products/t-deck) will present a virtual REPL on the device keyboard when it boots. The trackball will function as an up/down/left/right arrow for accessing the command line history and edit functions. 5 | 6 | To generate an equal sign (=) enter an underscore followed by a dash (_-) and to generate square brackets enter a parenthesis followed or proceeded by a plus sign ( [ = (+, ] = +) ). 7 | Pressing the speaker key (just left of the enter key) will generate the dollar sign ($). 8 | 9 | Multi-key translation table: 10 | _- -> = 11 | (+ -> [ 12 | +) -> ] 13 | (- -> < 14 | -) -> > 15 | _# -> ^ 16 | _/ -> \ 17 | -/ -> % 18 | *To display the key translation table on the T-Deck, type "import keys" from the REPL prompt.* 19 | 20 | In order to enable T-Deck keyboard input in python scripts add the following block of code to the import section of your code: 21 | ```py 22 | try: 23 | from tdeck_repl import input 24 | except: 25 | pass 26 | ``` 27 | The Virtual REPL will treat a file named virtcode.py in the root directory ('/') the way the native REPL treats a code.py file, that is it will be executed within the Virtual REPL when the Microcontroller is powered up or reset. When the virtcode.py program exits the Virtual REPL will take over control. 28 | -------------------------------------------------------------------------------- /code.py: -------------------------------------------------------------------------------- 1 | import os 2 | try: 3 | from tdeck_repl import input 4 | except: 5 | pass 6 | try: 7 | from codeop import compile_command 8 | except: 9 | pass 10 | 11 | if 'virtcode.py' in os.listdir('/'): 12 | import virtcode 13 | 14 | print("Repl in (Circuit-)Python") 15 | 16 | __cmd = "" 17 | while True: 18 | if 'compile_command' in dir(): 19 | # 9.0.0 alpha 7 or later supports multiple line statements 20 | __line = input(",,, " if __cmd else "=>> " ) 21 | 22 | if __cmd: 23 | __cmd += ("\n" + (" " if __line != "" else "") + __line) 24 | else: 25 | if __line.lower() == 'exit': 26 | break 27 | __cmd = __line 28 | try: 29 | if compile_command(__cmd): 30 | exec(compile_command(__cmd)) 31 | __cmd = "" 32 | except Exception as __err: 33 | print("*ERROR* Exception:",str(__err)) 34 | __cmd = "" 35 | else: 36 | # Pre 9.0.0 alpha 7 code (single line statments only) 37 | __line = input("=>> ") 38 | if __line.lower() == 'exit': 39 | break 40 | __result = None 41 | try: 42 | exec('__result='+__line) 43 | except: 44 | try: 45 | exec(__line) 46 | except Exception as err: 47 | print("*ERROR* Exception:",str(err)) 48 | 49 | if __result != None and __line.find('=') == -1: 50 | print(__result) 51 | -------------------------------------------------------------------------------- /keys.py: -------------------------------------------------------------------------------- 1 | def show(): 2 | print(" Entered Lilygo Keys Results") 3 | print(" ------------------- -------") 4 | print(" _- =") 5 | print(" (+ [") 6 | print(" +) ]") 7 | print(" (- <") 8 | print(" -) >") 9 | print(" _# ^") 10 | print(" _/ \\") 11 | print(" -/ %") 12 | print(" _! &") 13 | print(" Speaker Key (left of enter) $") 14 | print(" Track ball up up arrow") 15 | print(" Track ball down down arrow") 16 | print(" Track ball left left arrow") 17 | print(" Track ball right right arrow") 18 | 19 | 20 | show() 21 | print('To re-display keys from repl type "keys.show()"') -------------------------------------------------------------------------------- /new.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RetiredWizard/tdeck_repl/89e699197a0c9934d38727a1477a3b90ea59e4d0/new.wav -------------------------------------------------------------------------------- /playi2s.py: -------------------------------------------------------------------------------- 1 | # 2 | # Files that have been converted as follows have been tested 3 | # Other formats may work..... 4 | # 5 | 6 | """Audio Format : PCM 7 | Format settings : Little / Signed 8 | Codec ID : 1 9 | Duration : 4 min 5 s 10 | Bit rate mode : Constant 11 | Bit rate : 128 kb/s 12 | Channel(s) : 1 channel 13 | Sampling rate : 8 000 Hz 14 | Bit depth : 16 bits 15 | Stream size : 3.75 MiB (100%) 16 | """ 17 | 18 | 19 | import board 20 | try: 21 | import bitbangio 22 | except: 23 | import busio as bitbangio 24 | try: 25 | import adafruit_sdcard 26 | except: 27 | try: 28 | import sdcardio as adafruit_sdcard 29 | except: 30 | pass 31 | try: 32 | from pydos_ui import Pydos_ui 33 | try: 34 | from pydos_ui import input 35 | except: 36 | pass 37 | except: 38 | try: 39 | from tdeck_repl import Pydos_ui 40 | from tdeck_repl import input 41 | except: 42 | from sys import stdin as Pydos_ui 43 | 44 | import digitalio 45 | import storage 46 | import audiocore 47 | import audiobusio 48 | 49 | def Playi2s(): 50 | fname = input("Filename:") 51 | try: 52 | if "SD_SPI" in dir(board): 53 | spi = board.SD_SPI() 54 | elif "SD_SCK" in dir(board): 55 | spi = bitbangio.SPI(board.SD_SCK,board.SD_MOSI,board.SD_MISO) 56 | elif "SPI" in dir(board): 57 | spi = board.SPI() 58 | else: 59 | spi = bitbangio.SPI(board.SCK,board.MOSI,board.MISO) 60 | 61 | if "SD_CS" in dir(board): 62 | cs = digitalio.DigitalInOut(board.SD_CS) 63 | elif "SDCARD_CS" in dir(board): 64 | cs = digitalio.DigitalInOut(board.SDCARD_CS) 65 | else: 66 | cs = digitalio.DigitalInOut(board.CS) 67 | 68 | try: 69 | sd = adafruit_sdcard.SDCard(spi,cs) 70 | except: 71 | cs.deinit() 72 | if "SD_CS" in dir(board): 73 | sd = adafruit_sdcard.SDCard(spi,board.SD_CS) 74 | elif "SDCARD_CS" in dir(board): 75 | sd = adafruit_sdcard.SDCard(spi,board.SDCARD_CS) 76 | else: 77 | sd = adafruit_sdcard.SDCard(spi,board.CS) 78 | 79 | vfs = storage.VfsFat(sd) 80 | storage.mount(vfs,'/sd') 81 | print('SD card mounted on /sd') 82 | except: 83 | pass 84 | f = open(fname, "rb") 85 | wav = audiocore.WaveFile(f) 86 | a = None 87 | if 'I2S_BIT_CLOCK' in dir(board): 88 | a = audiobusio.I2SOut(board.I2S_BIT_CLOCK, board.I2S_WORD_SELECT, board.I2S_DATA) 89 | elif 'SPEAKER_SCK' in dir(board): 90 | a = audiobusio.I2SOut(board.SPEAKER_SCK, board.SPEAKER_WS, board.SPEAKER_DOUT) 91 | else: 92 | print('No I2S pins defined on the board') 93 | 94 | if a is not None: 95 | print("Press Q to quit") 96 | try: 97 | a.play(wav) 98 | while True: 99 | if 'read_keyboard' in dir(Pydos_ui): 100 | cmnd = Pydos_ui.read_keyboard(1) 101 | else: 102 | cmnd = Pydos_ui.read(1) 103 | if cmnd in "qQ": 104 | a.stop() 105 | break 106 | except: 107 | pass 108 | 109 | a.deinit() 110 | f.close() 111 | 112 | Playi2s() 113 | if __name__ != "PyDOS": 114 | print("Enter 'playi2s.Playi2s()' in the REPL ro re-run.") -------------------------------------------------------------------------------- /tdeck_repl.py: -------------------------------------------------------------------------------- 1 | from sys import stdin 2 | 3 | import board 4 | from supervisor import runtime 5 | from adafruit_bus_device.i2c_device import I2CDevice 6 | import keypad 7 | import time 8 | 9 | class PyDOS_UI: 10 | 11 | def __init__(self): 12 | self.trackevent = None 13 | self.scrollable = False 14 | self.arrow = "" 15 | self._cmdhistlock = False 16 | self._seqCnt = 0 17 | self._key = bytearray(1) 18 | self._touched = False 19 | _ADDRESS_KBD = 0x55 20 | 21 | self.commandHistory = [""] 22 | 23 | self._i2c = I2CDevice(board.I2C(), _ADDRESS_KBD) 24 | self.trackball = keypad.Keys( 25 | [ 26 | board.TRACKBALL_CLICK, 27 | board.TRACKBALL_UP, 28 | board.TRACKBALL_DOWN, 29 | board.TRACKBALL_LEFT, 30 | board.TRACKBALL_RIGHT 31 | ], 32 | value_when_pressed=False 33 | ) 34 | 35 | def serial_bytes_available(self): 36 | if not self._touched: 37 | retval = 0 38 | 39 | # Find the last direction the trackball was moved 40 | # and wait for trackball to stop moving 41 | self.arrow = '' 42 | self.trackevent = Pydos_ui.trackball.events.get() 43 | if self.trackevent and (self.trackevent.key_number not in [1,2] or not self._cmdhistlock): 44 | self.arrow = ' ABDC'[self.trackevent.key_number] 45 | if self.arrow == " ": 46 | self.arrow = "" 47 | else: 48 | retval = 1 49 | self._touched = True 50 | 51 | if not self._touched: 52 | with self._i2c as i2c: 53 | try: 54 | i2c.readinto(self._key) 55 | except Exception as err: 56 | self._key=bytearray(1) 57 | if self._key[0] != 0: 58 | retval = 1 59 | self._touched = True 60 | else: 61 | retval = self.uart_bytes_available() 62 | else: 63 | retval = 1 64 | 65 | return retval 66 | 67 | def uart_bytes_available(self): 68 | # Does the same function as supervisor.runtime.serial_bytes_available 69 | retval = runtime.serial_bytes_available 70 | 71 | return retval 72 | 73 | def read_keyboard(self,num): 74 | retval = "" 75 | while num > 0: 76 | self.serial_bytes_available() 77 | if self._touched: 78 | if self.arrow != "": 79 | if self._seqCnt == 0: 80 | retval += chr(27) 81 | self._seqCnt = 1 82 | num -= 1 83 | elif self._seqCnt == 1: 84 | retval += chr(91) 85 | self._seqCnt = 2 86 | num -= 1 87 | elif self._seqCnt == 2: 88 | retval += self.arrow 89 | self._seqCnt = 0 90 | self._touched = False 91 | self.arrow = "" 92 | num -= 1 93 | else: 94 | retval += chr(self._key[0]) 95 | self._touched = False 96 | num -= 1 97 | else: 98 | if self.uart_bytes_available(): 99 | retval = stdin.read(num) 100 | num -= len(retval) 101 | 102 | return retval 103 | 104 | Pydos_ui = PyDOS_UI() 105 | 106 | def input(disp_text=None): 107 | 108 | if disp_text != None: 109 | print(disp_text,end="") 110 | 111 | bld_chr1 = '_(+(-__-_' 112 | bld_chr2 = '-+)-)/#/!' 113 | bld_chr = '=[]<>\^%&' 114 | bld_started = False 115 | 116 | histPntr = len(Pydos_ui.commandHistory) 117 | Pydos_ui._cmdhistlock = False 118 | 119 | keys = '' 120 | editCol = 0 121 | loop = True 122 | ctrlkeys = '' 123 | arrow = '' 124 | onLast = True 125 | onFirst = True 126 | blink = True 127 | timer = time.time() 128 | 129 | while loop: 130 | #print(editCol,keys) 131 | if ctrlkeys == '': 132 | arrow = "" 133 | else: 134 | ctrlkeys = '' 135 | 136 | if arrow == 'A' or arrow == 'B': 137 | if len(Pydos_ui.commandHistory) > 0: 138 | print(('\x08'*(editCol))+(" "*(len(keys)+1))+('\x08'*(len(keys)+1)),end="") 139 | 140 | if arrow == 'A': 141 | histPntr -= 1 142 | else: 143 | histPntr += 1 144 | 145 | histPntr = histPntr % len(Pydos_ui.commandHistory) 146 | print(Pydos_ui.commandHistory[histPntr],end="") 147 | keys = Pydos_ui.commandHistory[histPntr] 148 | editCol = len(keys) 149 | if editCol == 0: 150 | onFirst = True 151 | else: 152 | onFirst = False 153 | elif arrow == 'D': 154 | Pydos_ui._cmdhistlock = True 155 | if len(keys) > editCol: 156 | print(keys[editCol:editCol+1]+"\x08",end="") 157 | elif editCol == len(keys): 158 | print(" \x08",end="") 159 | 160 | editCol = max(0,editCol-1) 161 | if editCol > 0: 162 | print('\x08',end="") 163 | onLast = False 164 | elif editCol == 0: 165 | if not onFirst: 166 | print('\x08',end="") 167 | onFirst = True 168 | elif arrow == 'C': 169 | if len(keys) > editCol: 170 | print(keys[editCol:editCol+1]+"\x08",end="") 171 | 172 | editCol += 1 173 | editCol = min(len(keys),editCol) 174 | if editCol < len(keys): 175 | print(keys[editCol-1:editCol],end="") 176 | onFirst = False 177 | elif editCol == len(keys): 178 | if not onLast: 179 | print(keys[editCol-1:],end="") 180 | onLast = True 181 | Pydos_ui._cmdhistlock = False 182 | 183 | if Pydos_ui.serial_bytes_available(): 184 | if Pydos_ui.uart_bytes_available(): 185 | keys = keys[:editCol]+stdin.read(1)+keys[editCol:] 186 | else: 187 | keys = keys[:editCol]+Pydos_ui.read_keyboard(1)+keys[editCol:] 188 | 189 | editCol += 1 190 | if keys[editCol-1] == '\x1b': 191 | keys = keys[:editCol-1]+keys[editCol:] 192 | if Pydos_ui.uart_bytes_available(): 193 | ctrlkeys = stdin.read(2) 194 | else: 195 | ctrlkeys = Pydos_ui.read_keyboard(2) 196 | # ctrlkeys = up:[A down:[B right:[C left:[D 197 | arrow = ctrlkeys[1] 198 | 199 | # Convert two character sequences into missing keyboard keys 200 | # '_-' -> '=' '(+' -> '[' '+)' -> ']' 201 | bld_done = False 202 | if bld_started: 203 | bcindx = bld_chr2.find(keys[editCol-1:editCol]) 204 | nextbc = 0 205 | while nextbc != -1 and bld_started: 206 | if keys[editCol-2:editCol] == bld_chr1[bcindx]+bld_chr2[bcindx]: 207 | bld_started = False 208 | bld_done = True 209 | keys = keys[:editCol-2]+bld_chr[bcindx]+keys[editCol:] 210 | print('\x08'+keys[editCol-2:]+' '+('\x08'*(len(keys[editCol:])+(1 if onLast else 2))),end="") 211 | editCol -= 1 212 | else: 213 | nextbc = bld_chr2[bcindx+1:].find(keys[editCol-1:editCol]) 214 | bcindx = bcindx + nextbc + 1 215 | 216 | if bld_chr1.find(keys[editCol-1:editCol]) != -1 and arrow == "" and not bld_done: 217 | bld_started = True 218 | else: 219 | bld_started = False 220 | 221 | if arrow != "" and ctrlkeys != "": 222 | editCol -= 1 223 | elif arrow !='': 224 | pass 225 | elif keys[editCol-1:editCol] in ['\x08','\x7f']: 226 | keys = keys[:max(0,editCol-2)]+keys[editCol:] 227 | if editCol > 1: 228 | print(('\x08'*(editCol-1))+keys+' \x08\x08',end="") 229 | editCol = max(0,editCol-2) 230 | if editCol < len(keys): 231 | print("\x08"*(len(keys)-editCol),end="") 232 | else: 233 | editCol -= 1 234 | onFirst = True 235 | elif len(keys[editCol-1:editCol]) > 0 and keys[editCol-1:editCol] in '\n\r': 236 | if len(keys) > editCol: 237 | print(keys[editCol:editCol+1]+"\x08",end="") 238 | elif editCol == len(keys): 239 | print(" \x08",end="") 240 | keys = keys[:editCol-1]+keys[editCol:] 241 | if keys.strip() != "": 242 | Pydos_ui.commandHistory.append(keys) 243 | if len(Pydos_ui.commandHistory) > 10: 244 | Pydos_ui.commandHistory.pop(1) 245 | histPntr = len(Pydos_ui.commandHistory) 246 | print() 247 | loop = False 248 | elif not bld_done: 249 | onFirst = False 250 | print(keys[editCol-1:],end="") 251 | if len(keys[editCol-1:]) > 1: 252 | print(" \x08",end="") 253 | if editCol < len(keys): 254 | print("\x08"*(len(keys)-editCol),end="") 255 | 256 | if loop: 257 | if time.time() != timer: 258 | blink = not blink 259 | timer = time.time() 260 | 261 | if blink: 262 | print("_\x08",end="") 263 | else: 264 | if len(keys) > editCol: 265 | print(keys[editCol:editCol+1]+"\x08",end="") 266 | else: 267 | print(" \x08",end="") 268 | 269 | return keys 270 | -------------------------------------------------------------------------------- /virtcode.py: -------------------------------------------------------------------------------- 1 | print('Hello World!') --------------------------------------------------------------------------------