├── src ├── utils │ ├── setting.dat │ ├── settings.py │ ├── color.py │ ├── ui.py │ ├── drivers.py │ ├── fbconsole.py │ └── dos_font.py ├── logo.jpg ├── boot.py ├── app │ ├── pics.py │ ├── wifi.py │ └── chat.py ├── mpy_scr_test.py ├── kb_test.py ├── start.py ├── main.py ├── run.py ├── jojo.py └── buzzer_music.py ├── pic.jpg ├── firmware └── s3-psram-7789.bin └── README.md /src/utils/setting.dat: -------------------------------------------------------------------------------- 1 | {"OWNER": "TOM", "SSID": "er", "PWD": "re"} -------------------------------------------------------------------------------- /pic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jd3096-mpy/PICOCALC-micropython/HEAD/pic.jpg -------------------------------------------------------------------------------- /src/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jd3096-mpy/PICOCALC-micropython/HEAD/src/logo.jpg -------------------------------------------------------------------------------- /firmware/s3-psram-7789.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jd3096-mpy/PICOCALC-micropython/HEAD/firmware/s3-psram-7789.bin -------------------------------------------------------------------------------- /src/boot.py: -------------------------------------------------------------------------------- 1 | # This file is executed on every boot (including wake-boot from deepsleep) 2 | #import esp 3 | #esp.osdebug(None) 4 | #import webrepl 5 | #webrepl.start() 6 | -------------------------------------------------------------------------------- /src/app/pics.py: -------------------------------------------------------------------------------- 1 | import st7789 2 | from machine import SoftI2C,Pin,UART,Timer,SPI 3 | import time 4 | 5 | tft=st7789.ST7789( 6 | SPI(2, baudrate=20000000, sck=Pin(41), mosi=Pin(38)), 7 | 240, 8 | 320, 9 | cs=Pin(39, Pin.OUT), 10 | dc=Pin(40, Pin.OUT), 11 | backlight=Pin(36, Pin.OUT), 12 | reset=Pin(42, Pin.OUT), 13 | rotation=3, 14 | color_order=st7789.RGB, 15 | inversion=False) 16 | 17 | tft.init() 18 | tft.jpg('logo.jpg',0,0) -------------------------------------------------------------------------------- /src/mpy_scr_test.py: -------------------------------------------------------------------------------- 1 | from machine import SoftI2C,Pin,UART,Timer,SPI 2 | import time 3 | from utils.drivers import KEYBOARD 4 | from utils.drivers import SCREEN 5 | from utils.fbconsole import FBConsole 6 | import utils.dos_font as font 7 | import utils.settings as setting 8 | import utils.color as color 9 | import os 10 | import st7789 11 | 12 | set_dic=setting.load_setting() 13 | NAME=set_dic['OWNER'] 14 | 15 | screen=SCREEN(320,320) 16 | kb=KEYBOARD() 17 | screen.tft.jpg('logo.jpg',0,40) 18 | screen.tft.write(font,'screen test',0,0) -------------------------------------------------------------------------------- /src/kb_test.py: -------------------------------------------------------------------------------- 1 | from machine import I2C, Pin 2 | import time 3 | 4 | # I2C 配置 5 | I2C_SDA = 6 # 根据你的连接修改 6 | I2C_SCL = 7 # 根据你的连接修改 7 | I2C_ADDR = 31 8 | scp=Pin(34,Pin.IN,Pin.PULL_UP) 9 | sdp=Pin(21,Pin.IN,Pin.PULL_UP) 10 | 11 | # 创建 I2C 对象 12 | i2c = I2C(1, scl=scp, sda=sdp, freq=10000) # 10kHz 与 Arduino 代码匹配 13 | 14 | # 寄存器地址 15 | REG_ID_FIF = 0x01 # FIFO 读取寄存器(根据 Arduino 代码) 16 | REG_ID_KEY = 0x02 # 键盘状态寄存器(CapsLock, NumLock 状态) 17 | print(i2c.scan()) 18 | 19 | def get_key_event(): 20 | 21 | data = i2c.readfrom_mem(I2C_ADDR, 0x09,2) 22 | print(data) 23 | 24 | 25 | print("等待按键输入...") 26 | while True: 27 | get_key_event() 28 | 29 | time.sleep(0.1) # 避免轮询过快 -------------------------------------------------------------------------------- /src/utils/settings.py: -------------------------------------------------------------------------------- 1 | #system setting 2 | import json 3 | 4 | def load_setting(): 5 | setting={'OWNER':'jd3096','SSID':'dundundun','PWD':'30963096'} 6 | set_str=json.dumps(setting) 7 | try: 8 | with open('./utils/setting.dat', 'r') as f: 9 | setting=f.read() 10 | except: 11 | print('No setting file!') 12 | with open('./utils/setting.dat', 'w') as f: 13 | f.write(set_str) 14 | print(setting) 15 | set_dic=json.loads(setting) 16 | return set_dic 17 | 18 | def save_setting(set_dic): 19 | set_str=json.dumps(set_dic) 20 | with open('./utils/setting.dat', 'w') as f: 21 | f.write(set_str) 22 | print('saved!') 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PICOCALC-micropython 2 | 3 | 4 | 5 | 6 | #### V-0.0.2 2025-04-01 7 | 8 | - Added SD card detection and mounting functionality. If mounted successfully, it is recognized as the `sd` folder in the root directory. 9 | 10 | #### V-0.0.1 2025-03-27 11 | 12 | - Added the `mpy_scr_test.py` file to test whether the screen is working properly. 13 | 14 | ------ 15 | 16 | 17 | 18 | **Hardware:** WalnutPi-Pico 19 | 20 | **Usage:** 21 | 22 | 1. Flash the firmware from the `firmware` folder to the ESP32-S3. 23 | 2. Use Thonny to package and upload all files from the `src` folder to the development board. 24 | 25 | **Note:** This is only a test version. Feel free to share your ideas and suggestions in the **Issues** section! 26 | 27 | ![MICROPYTHON ON PICOLACL](pic.jpg) -------------------------------------------------------------------------------- /src/utils/color.py: -------------------------------------------------------------------------------- 1 | import st7789 2 | from utils.drivers import SCREEN 3 | import utils.dos_font as font 4 | 5 | COLOR_CANDY={ 6 | 'bg':st7789.color565(0, 0, 0), #BLACK 7 | 'font':st7789.color565(242, 242, 242), #WHITE 8 | 'warn':st7789.color565(229, 34, 34), #RED 9 | 'border':st7789.color565(166, 227, 45), #GREEN 10 | 'bg2':st7789.color565(196, 141, 255), #BLUE 11 | 12 | } 13 | COLOR_EVA={ 14 | 'bg':st7789.color565(56, 36, 87), 15 | 'font':st7789.color565(78, 244, 72), 16 | 'warn':st7789.color565(101, 34, 54), 17 | 'border':st7789.color565(251, 164, 5), 18 | 'bg2':st7789.color565(113, 78, 158), 19 | } 20 | 21 | #diaglo test 22 | # scr=SCREEN(320,240).tft 23 | # theme=COLOR_EVA 24 | # scr.init() 25 | # scr.rect(30, 30, 200, 100, theme['border']) 26 | # scr.fill_rect(32, 32, 196, 96, theme['bg']) 27 | # scr.fill_rect(32, 50, 196, 3, theme['border']) 28 | # scr.write(font,'TITLE',80,33,theme['font'],theme['bg']) 29 | 30 | -------------------------------------------------------------------------------- /src/start.py: -------------------------------------------------------------------------------- 1 | from machine import SoftI2C,Pin,UART,Timer,SPI 2 | import time 3 | from utils.drivers import KEYBOARD 4 | from utils.drivers import SCREEN 5 | from utils.fbconsole import FBConsole 6 | import utils.settings as setting 7 | import utils.color as color 8 | import os 9 | import st7789 10 | 11 | set_dic=setting.load_setting() 12 | NAME=set_dic['OWNER'] 13 | 14 | screen=SCREEN(320,320) 15 | kb=KEYBOARD() 16 | # screen.tft.jpg('logo.jpg',0,0) 17 | # time.sleep(1) 18 | theme=color.COLOR_CANDY 19 | scr = FBConsole(screen,bg_color=theme['bg'],fg_color=theme['font']) 20 | os.dupterm(scr) 21 | print('MPY CONSOLE 1.00 by jd3096') 22 | print('WELCOM! '+NAME) 23 | time.sleep(0.5) 24 | 25 | def check_key(t): 26 | re=kb.check_key() 27 | if re!=None: 28 | if len(re)==2: 29 | ip=re[1].to_bytes(1,'big') 30 | if ip!=b'\x00': 31 | scr._c=ip 32 | scr._press() 33 | 34 | 35 | tim=Timer(-1) 36 | tim.init(mode=Timer.PERIODIC, period=3, callback=check_key) 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/main.py: -------------------------------------------------------------------------------- 1 | from machine import SoftI2C,Pin,UART,Timer,SPI 2 | import time 3 | from utils.drivers import KEYBOARD 4 | from utils.drivers import SCREEN 5 | from utils.fbconsole import FBConsole 6 | import utils.settings as setting 7 | import utils.color as color 8 | import os 9 | import st7789 10 | 11 | set_dic=setting.load_setting() 12 | NAME=set_dic['OWNER'] 13 | 14 | screen=SCREEN(320,320) 15 | kb=KEYBOARD() 16 | screen.tft.jpg('logo.jpg',0,40) 17 | time.sleep(1) 18 | 19 | import machine, sdcard, os 20 | from machine import SPI,Pin 21 | spitf=SPI(baudrate=2000000, sck=Pin(4), mosi=Pin(5),miso=Pin(1)) 22 | 23 | theme=color.COLOR_CANDY 24 | scr = FBConsole(screen,bg_color=theme['bg'],fg_color=theme['font']) 25 | os.dupterm(scr) 26 | 27 | print('MPY CONSOLE ON PICOCALC by jd3096') 28 | print('V1.02') 29 | print('WELCOME! '+NAME) 30 | try: 31 | tf = sdcard.SDCard(spitf,Pin(2)) 32 | os.mount(tf, '/sd') 33 | os.listdir('/') 34 | print("LOAD SDCARD as '/sd'") 35 | except: 36 | print('NO SDARD') 37 | time.sleep(0.5) 38 | 39 | def check_key(t): 40 | re=kb.check_key() 41 | if re!=None: 42 | if re[0]==3: 43 | if re[1]==10: 44 | ip=b'\r\n' 45 | else: 46 | ip=re[1].to_bytes(1,'big') 47 | scr._c=ip 48 | scr._press() 49 | 50 | 51 | tim=Timer(-1) 52 | tim.init(mode=Timer.PERIODIC, period=10, callback=check_key) 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/app/wifi.py: -------------------------------------------------------------------------------- 1 | import network,time 2 | import utils.settings as setting 3 | 4 | sta_if = network.WLAN(network.STA_IF) 5 | connected=False 6 | set_dic=setting.load_setting() 7 | SSID=set_dic['SSID'] 8 | PWD=set_dic['PWD'] 9 | 10 | if sta_if.isconnected(): 11 | connected=True 12 | if connected: 13 | print('WIFI connected.') 14 | ip=sta_if.ifconfig()[0] 15 | netmask=sta_if.ifconfig()[1] 16 | DNS=sta_if.ifconfig()[3] 17 | print('IP:'+ip) 18 | print('Netmask:'+ip) 19 | print('DNS:'+ip) 20 | else: 21 | print('WIFI disconnected.') 22 | while 1: 23 | print('-'*20) 24 | print('CONNECT/SETTING/QUIT') 25 | print('Input the option:(c/s/q)') 26 | cmd=input() 27 | if cmd=='c': 28 | set_dic=setting.load_setting() 29 | SSID=set_dic['SSID'] 30 | PWD=set_dic['PWD'] 31 | start_time=time.time() 32 | if not sta_if.isconnected(): 33 | print('Connecting to network...') 34 | sta_if.active(True) 35 | sta_if.connect(SSID, PWD) 36 | print('network status:', sta_if.status()) 37 | while not sta_if.isconnected(): 38 | time.sleep_ms(200) 39 | if time.time()-start_time > 15: 40 | print('WIFI Timeout!') 41 | break 42 | if sta_if.isconnected(): 43 | print("WIFI CONNECTED") 44 | else: 45 | print("WIFI CONNECTED FAILED") 46 | elif cmd=='s': 47 | print('Input SSID:') 48 | ssid=input() 49 | print('Input PASSWORD:') 50 | pwd=input() 51 | set_dic['SSID']=ssid 52 | set_dic['PWD']=pwd 53 | setting.save_setting(set_dic) 54 | elif cmd=='q': 55 | break 56 | else: 57 | print('Wrong commands input,try again.') 58 | 59 | -------------------------------------------------------------------------------- /src/run.py: -------------------------------------------------------------------------------- 1 | #run py files in repl 2 | 3 | import os,gc 4 | from machine import Timer,Pin 5 | from utils.drivers import SCREEN 6 | from utils.fbconsole import FBConsole 7 | import utils.color as color 8 | from utils.drivers import KEYBOARD 9 | 10 | 11 | kb=KEYBOARD() 12 | screen=SCREEN(320,320) 13 | 14 | def check_key(t): 15 | re=kb.check_key() 16 | if re!=None: 17 | if len(re)==1: 18 | scr._c=re 19 | scr._press() 20 | else: 21 | for b in re: 22 | scr._c=b.to_bytes(1,'big') 23 | scr._press() 24 | 25 | 26 | os.dupterm(None) 27 | theme=color.COLOR_CANDY 28 | 29 | scr = FBConsole(screen,bg_color=theme['bg'],fg_color=theme['bg2']) 30 | os.dupterm(scr) 31 | tim=Timer(0) 32 | tim.init(mode=Timer.PERIODIC, period=3, callback=check_key) 33 | 34 | print('Enter the filename you want to run:') 35 | file=input()+'.py' 36 | err=0 37 | tim.deinit() 38 | try: 39 | exec(open('./app/'+file).read()) 40 | except OSError as e: 41 | err=1 42 | except Exception as e: 43 | err=2 44 | 45 | 46 | if err==1: 47 | os.dupterm(None) 48 | scr = FBConsole(screen,bg_color=theme['bg'],fg_color=theme['font']) 49 | os.dupterm(scr) 50 | tim=Timer(0) 51 | tim.init(mode=Timer.PERIODIC, period=3, callback=check_key) 52 | print(file+' not found!') 53 | elif err==2: 54 | os.dupterm(None) 55 | scr = FBConsole(screen,bg_color=theme['bg'],fg_color=theme['font']) 56 | os.dupterm(scr) 57 | tim=Timer(0) 58 | tim.init(mode=Timer.PERIODIC, period=3, callback=check_key) 59 | print('An error occurred in py file.') 60 | elif err==0: 61 | print('Press OK button to continue.') 62 | while ok.value(): 63 | pass 64 | os.dupterm(None) 65 | scr = FBConsole(screen,bg_color=theme['bg'],fg_color=theme['font']) 66 | os.dupterm(scr) 67 | tim=Timer(0) 68 | tim.init(mode=Timer.PERIODIC, period=3, callback=check_key) 69 | print(file+' execution completed.') 70 | 71 | gc.collect() 72 | -------------------------------------------------------------------------------- /src/utils/ui.py: -------------------------------------------------------------------------------- 1 | import st7789 2 | from utils.drivers import SCREEN 3 | import utils.dos_font as font 4 | 5 | COLOR_EVA={ 6 | 'bg':st7789.color565(56, 36, 87), 7 | 'font':st7789.color565(78, 244, 72), 8 | 'warn':st7789.color565(101, 34, 54), 9 | 'border':st7789.color565(251, 164, 5), 10 | 'bg2':st7789.color565(113, 78, 158), 11 | } 12 | 13 | theme=COLOR_EVA 14 | 15 | class DialogBox: 16 | def __init__(self, x,y,width, height): 17 | self.display = SCREEN(320,240).tft 18 | self.x=x 19 | self.y=y 20 | self.width = width 21 | self.height = height 22 | self.border=2 23 | 24 | def calc_start_pixel(self,characters, length): 25 | total_width = characters * 8 26 | start_pixel = (length - total_width) // 2 27 | return start_pixel 28 | 29 | def show_dialog(self, title, text): 30 | # DRAW BOARDER 31 | self.display.fill_rect(self.x, self.y,self.width,self.height,theme['border']) 32 | # DRAW INNER BOX 33 | self.display.fill_rect(self.x+self.border, self.y+self.border,self.width-self.border*2,self.height-self.border*2,theme['bg']) 34 | # DRAW TITLE 35 | self.display.fill_rect(self.x+self.border, self.y+20, self.width-self.border*2, self.border, theme['border']) 36 | l=len(title) 37 | x=self.calc_start_pixel(l,self.width-self.border*2)+self.x 38 | self.display.write(font,title,x,self.y+self.border+1,theme['font'],theme['bg']) 39 | # DRAW TEXT 40 | l=len(text) 41 | x=self.calc_start_pixel(l,self.width-self.border*2)+self.x 42 | self.display.write(font,text,x,self.y+self.border+22,theme['font'],theme['bg']) 43 | # DRAW EXIT BUTTON 44 | self.display.fill_rect(self.x+self.width - 20, self.y+self.border, 18, 18, theme['warn']) 45 | self.display.write(font,"X", self.x+self.width - 15, self.y+self.border+2, theme['font'],theme['warn']) 46 | # DRAW OK BUTTON 47 | #self.draw_rectangle(self.width - 80, self.height - 40, 40, 20, color=1) 48 | #self.display.text("OK", x=self.width - 70, y=self.height - 38, color=0) 49 | 50 | def user_input(self, prompt): 51 | # 这里假设你有一个方法来接收用户的输入 52 | # 例如,如果你有一个按键读取函数,可以使用它来读取用户输入 53 | # 注意:这里的示例只是为了说明问题,实际上的方法调用可能会有所不同 54 | #user_input = read_user_input() 55 | #return user_input 56 | pass 57 | 58 | 59 | dialog = DialogBox(20,20,222,140) 60 | 61 | title_text = "Title" 62 | main_text = "This is a dialog box." 63 | dialog.show_dialog(title_text, main_text) 64 | 65 | # 等待用户输入 66 | # user_response = dialog.user_input("Please enter your response: ") 67 | # print("User input:", user_response) 68 | -------------------------------------------------------------------------------- /src/utils/drivers.py: -------------------------------------------------------------------------------- 1 | #ST7789 2 | from machine import Pin, SPI, freq 3 | import st7789 4 | 5 | class SCREEN(): 6 | def __init__(self, width, height): 7 | custom_init = [ 8 | (b'\x01', 100), # soft reset 9 | (b'\xCF\x00\xC1\x30',), 10 | (b'\xED\x64\x03\x12\x81',), # power on sequence control 11 | (b'\xE8\x85\x00\x78',), # driver timing control A 12 | (b'\xCB\x39\x2C\x00\x34\x02',), # power control A 13 | (b'\xF7\x20',), # pump ratio control 14 | (b'\xEA\x00\x00',), # driver timing control B 15 | (b'\xC0\x23',), # power control,VRH[5:0] 16 | (b'\xC1\x10',), # Power control,SAP[2:0];BT[3:0] 17 | (b'\xC5\x3E\x28',), # vcm control 18 | (b'\xC7\x86',), # vcm control 2 19 | (b'\x37\x00',), # madctl 20 | (b'\x3A\x55',), # pixel format 21 | (b'\xB1\x00\x18',), # frameration control,normal mode full colours 22 | (b'\xB6\x02\x02',), # display function control 23 | (b'\xF2\x00',), # 3gamma function disable 24 | (b'\x26\x01',), # gamma curve selected 25 | # set positive gamma correction 26 | (b'\xE0\x0F\x31\x2B\x0C\x0E\x08\x4E\xF1\x37\x07\x10\x03\x0E\x09\x00',), 27 | # set negative gamma correction 28 | (b'\xE1\x00\x0E\x14\x03\x11\x07\x31\xC1\x48\x08\x0F\x0C\x31\x36\x0F',), 29 | (b'\x21', 50), 30 | (b'\x11', 100), # display on 31 | (b'\x29', 100), # display on 32 | ] 33 | 34 | custom_rotations = [ 35 | (0x88, 320, 320, 0, 0), 36 | (0xE8, 320, 320, 0, 0), 37 | (0x48, 320, 320, 0, 0), 38 | (0x28, 320, 320, 0, 0), 39 | ] 40 | 41 | self.tft=st7789.ST7789( 42 | SPI(1, baudrate=40000000, sck=Pin(37), mosi=Pin(38)), 43 | 320, 44 | 320, 45 | cs=Pin(40, Pin.OUT), 46 | dc=Pin(41, Pin.OUT), 47 | reset=Pin(42,Pin.OUT), 48 | custom_init=custom_init, 49 | rotation=2, 50 | color_order=st7789.RGB, 51 | inversion=False, 52 | options=0, 53 | rotations=custom_rotations, 54 | buffer_size=0) 55 | self.tft.init() 56 | self.tft.fill(0) 57 | 58 | #BC6561 KEYBOARD 59 | from machine import I2C,Pin 60 | import time 61 | 62 | class KEYBOARD(): 63 | def __init__(self): 64 | scp=Pin(34,Pin.IN,Pin.PULL_UP) 65 | sdp=Pin(21,Pin.IN,Pin.PULL_UP) 66 | self.i2c = I2C(1, scl=scp, sda=sdp, freq=10000) 67 | 68 | def check_key(self): 69 | data = self.i2c.readfrom_mem(31, 0x09,2) 70 | return data 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/app/chat.py: -------------------------------------------------------------------------------- 1 | import network 2 | import espnow 3 | import _thread 4 | import time 5 | import st7789 6 | from utils.drivers import SCREEN 7 | import utils.dos_font as font 8 | import utils.color as color 9 | import utils.settings as setting 10 | from utils.drivers import KEYBOARD 11 | from machine import Timer 12 | import os 13 | 14 | set_dic=setting.load_setting() 15 | NAME=set_dic['OWNER'] 16 | 17 | theme=color.COLOR_CANDY 18 | kb=KEYBOARD() 19 | 20 | sta = network.WLAN(network.STA_IF) 21 | sta.active(True) 22 | 23 | e = espnow.ESPNow() 24 | e.active(True) 25 | peer = b'\xff\xff\xff\xff\xff\xff' # MAC address of peer's wifi interface 26 | try: 27 | e.add_peer(peer) 28 | except: 29 | pass 30 | 31 | screen = SCREEN(320,240).tft 32 | 33 | class RecvBox: 34 | def __init__(self): 35 | self.display = screen 36 | self.mess_box=[] 37 | self.border=2 38 | 39 | def message(self,message): 40 | if len(self.mess_box)>5: 41 | del self.mess_box[0] 42 | self.mess_box.append(message) 43 | i=0 44 | for mess in self.mess_box: 45 | self.show_mess(i,mess) 46 | i+=1 47 | 48 | def show_mess(self,num,mess): 49 | title=mess.split('|')[0] 50 | mess=mess.split('|')[1] 51 | self.display.fill_rect(0, 35*num,320,35,theme['bg']) 52 | # DRAW BOARDER 53 | self.display.rect(0, 35*num,320,35,theme['border']) 54 | # DRAW TITLE 55 | self.display.write(font,title,self.border+2,self.border+1+35*num,theme['warn'],theme['bg2']) 56 | # DRAW TEXT 57 | self.display.write(font,mess,self.border+2,self.border+15+35*num,theme['font'],theme['bg']) 58 | 59 | 60 | recv=RecvBox() 61 | 62 | class SendBox: 63 | def __init__(self): 64 | self.display = screen 65 | self.mess_box=[] 66 | self.border=2 67 | 68 | def input_box(self): 69 | title=NAME 70 | # DRAW BOARDER 71 | self.display.rect(0, 212,320,25,theme['bg2']) 72 | text='' 73 | t=0 74 | while 1: 75 | time.sleep_ms(5) 76 | re=kb.check_key() 77 | if re==b'\x08': 78 | text=text[:-1] 79 | self.display.fill_rect(1, 213,318,23,theme['bg']) 80 | if re==b'\r\n': 81 | if text!='': 82 | break 83 | if re!=None and re!=b'\x08': 84 | text+=re.decode() 85 | self.display.write(font,text,self.border+2,215,theme['font'],theme['bg']) 86 | self.display.fill_rect(0, 212,320,25,theme['bg']) 87 | text=NAME+'|'+text 88 | recv.message(text) 89 | e.send(peer,text,False) 90 | 91 | 92 | 93 | send=SendBox() 94 | 95 | 96 | 97 | 98 | def recv_cb(e): 99 | while True: # Read out all messages waiting in the buffer 100 | mac, msg = e.irecv(0) # Don't wait if no messages left 101 | if mac is None: 102 | return 103 | recv.message(msg.decode()) 104 | 105 | e.irq(recv_cb) 106 | 107 | 108 | while 1: 109 | send.input_box() 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /src/utils/fbconsole.py: -------------------------------------------------------------------------------- 1 | import framebuf 2 | import uio 3 | import utils.dos_font as font 4 | import st7789 5 | import os 6 | 7 | class FBConsole(uio.IOBase): 8 | def __init__(self, fb, bg_color=0, fg_color=-1, width=-1, height=-1, readobj=None): 9 | self.readobj = readobj 10 | self.fb = fb 11 | self.width = 320 12 | self.height = 320 13 | self.bgcolor = bg_color 14 | self.fgcolor = fg_color 15 | self.char_x=8 16 | self.char_y=16 17 | self.line_height(16) 18 | self.voffset = 0 19 | self.bottom_mark=False 20 | self._n = 0 21 | self._c = b'x' 22 | self.cls() 23 | 24 | def _press(self): 25 | self._n += 1 26 | os.dupterm_notify(None) 27 | 28 | 29 | def readinto(self, buf): 30 | n = min(len(buf), self._n) 31 | for i in range(n): 32 | buf[i:i+1] = self._c 33 | self._n -= n 34 | return None if n == 0 else n 35 | 36 | def cls(self): 37 | self.x = 0 38 | self.y = 0 39 | self.y_end = 0 40 | self.fb.tft.fill(self.bgcolor) 41 | self.bottom_mark=False 42 | self.voffset = 0 43 | 44 | def line_height(self, px): 45 | self.lineheight = px 46 | self.w = self.width // self.char_x 47 | self.h = self.height // self.char_y 48 | 49 | def _putc(self, c): 50 | c = chr(c) 51 | if c == '\n': 52 | self._newline() 53 | elif c == '\x08': 54 | self._backspace() 55 | elif c >= ' ': 56 | #self.fb.tft.fill_rect(self.x * self.char_x, self.y * self.lineheight, self.char_y, self.lineheight, st7789.RED) 57 | if self.bottom_mark: 58 | if self.voffset==0: 59 | self.fb.tft.write(font, c, self.x * self.char_x, self.height-self.lineheight, self.fgcolor,self.bgcolor) 60 | else: 61 | self.fb.tft.write(font, c, self.x * self.char_x, self.voffset-self.lineheight, self.fgcolor,self.bgcolor) 62 | else: 63 | self.fb.tft.write(font, c, self.x * self.char_x, self.y * self.lineheight, self.fgcolor,self.bgcolor) 64 | self.x += 1 65 | if self.x >= self.w: 66 | self._newline() 67 | 68 | def _esq_read_num(self, buf, pos): 69 | digit = 1 70 | n = 0 71 | while buf[pos] != 0x5b: 72 | n += digit * (buf[pos] - 0x30) 73 | pos -= 1 74 | digit *= 10 75 | return n 76 | 77 | def write(self, buf): 78 | self._draw_cursor(self.bgcolor) 79 | i = 0 80 | while i < len(buf): 81 | c = buf[i] 82 | if c == 0x1b: 83 | i += 1 84 | esc = i 85 | while chr(buf[i]) in '[;0123456789': 86 | i += 1 87 | c = buf[i] 88 | if c == 0x4b and i == esc + 1: # ESC [ K 89 | self._clear_cursor_eol() 90 | elif c == 0x44: # ESC [ n D 91 | for _ in range(self._esq_read_num(buf, i - 1)): 92 | self._backspace() 93 | else: 94 | self._putc(c) 95 | i += 1 96 | self._draw_cursor(self.fgcolor) 97 | return len(buf) 98 | 99 | def _newline(self): 100 | #self.fb.tft.vscrdef(0,self.height,self.lineheight) 101 | self.fb.tft.vscrdef(0,0,0) 102 | self.x = 0 103 | self.y += 1 104 | if self.y >= self.h: 105 | self.bottom_mark=True 106 | if self.bottom_mark: 107 | self.cls() 108 | # self.voffset += 16 109 | # self.voffset %= self.height 110 | # self.fb.tft.vscsad(self.voffset) 111 | # self.fb.tft.fill_rect(0, self.voffset - self.lineheight, self.width, self.lineheight, self.bgcolor) 112 | # #self.y = self.h - 1 113 | # self.y = self.voffset - 16 114 | self.y_end = self.y 115 | 116 | def _backspace(self): 117 | if self.x == 0: 118 | if self.y > 0: 119 | self.y -= 1 120 | self.x = self.w - 1 121 | else: 122 | self.x -= 1 123 | self.fb.tft.fill_rect(self.x*self.char_x,self.y*self.char_y,self.char_x, self.char_y, self.bgcolor) 124 | 125 | def _clear_cursor_eol(self): 126 | self.fb.tft.fill_rect(self.x * self.char_x, self.y * self.lineheight, self.width, self.lineheight, self.bgcolor) 127 | for l in range(self.y + 1, self.y_end + 1): 128 | self.fb.tft.fill_rect(0, l * self.lineheight, self.width, self.lineheight, self.bgcolor) 129 | self.y_end = self.y 130 | 131 | def _draw_cursor(self, color): 132 | if self.bottom_mark: 133 | self.fb.tft.hline(self.x * self.char_x, self.voffset-1, self.char_x, color) 134 | else: 135 | self.fb.tft.hline(self.x * self.char_x, self.y * self.lineheight + self.char_y-1, self.char_x, color) 136 | -------------------------------------------------------------------------------- /src/jojo.py: -------------------------------------------------------------------------------- 1 | from buzzer_music import music 2 | from time import sleep 3 | 4 | #Example songs 5 | 6 | song='0 B4 0.75 14;2 B4 0.75 14;4 B4 0.75 14;5 A4 0.75 14;7 B4 0.75 14;9 D5 0.75 14;11 B4 0.75 14;13 F#4 0.75 14;14 A4 1.75 14;16 B4 0.75 14;18 B4 0.75 14;20 B4 0.75 14;21 A4 0.75 14;23 B4 0.75 14;25 F5 0.75 14;27 E5 0.75 14;29 D5 0.75 14;30 A4 1.75 14;32 B4 0.75 14;34 B4 0.75 14;36 B4 0.75 14;37 A4 0.75 14;39 B4 0.75 14;41 D5 0.75 14;43 B4 0.75 14;45 F#4 0.75 14;46 A4 1.75 14;48 B4 0.75 14;50 B4 0.75 14;52 B4 0.75 14;53 A4 0.75 14;55 B4 0.75 14;57 F5 0.75 14;59 E5 0.75 14;61 B4 0.75 14;62 A4 1.75 14;64 B4 0.75 14;66 B4 0.75 14;68 B4 0.75 14;69 A4 0.75 14;71 B4 0.75 14;73 D5 0.75 14;75 B4 0.75 14;77 F#4 0.75 14;78 A4 1.75 14;80 B4 0.75 14;82 B4 0.75 14;84 B4 0.75 14;85 A4 0.75 14;87 B4 0.75 14;89 F5 0.75 14;91 E5 0.75 14;93 D5 0.75 14;94 A4 1.75 14;96 B4 0.75 14;98 B4 0.75 14;100 B4 0.75 14;101 A4 0.75 14;103 B4 0.75 14;105 D5 0.75 14;107 B4 0.75 14;109 F#4 0.75 14;110 A4 1.75 14;112 B4 0.75 14;114 B4 0.75 14;116 B4 0.75 14;118 A4 0.75 14;119 B4 0.75 14;128 F#6 5.75 14;134 F6 5.75 14;142 D6 0.75 14;143 E6 0.75 14;144 F6 2.75 14;147 E6 2.75 14;150 D6 1.75 14;152 C#6 2.75 14;155 D6 2.75 14;158 E6 1.75 14;160 F#6 5.75 14;166 B6 5.75 14;172 B5 1.75 14;174 C#6 1.75 14;176 D6 2.75 14;179 E6 2.75 14;182 D6 1.75 14;184 C#6 2.75 14;187 A6 2.75 14;190 G6 1.75 14;192 B5 5.75 14;192 D6 5.75 14;192 F#6 5.75 14;198 B5 5.75 14;198 D6 5.75 14;198 F6 5.75 14;206 D6 0.75 14;207 E6 0.75 14;208 G5 2.75 14;208 C#6 2.75 14;208 F6 2.75 14;211 E6 2.75 14;214 D6 1.75 14;216 A#5 2.75 14;216 C#6 2.75 14;219 D6 2.75 14;222 E6 1.75 14;224 B5 5.75 14;224 F#6 5.75 14;230 F6 5.75 14;230 B6 5.75 14;236 B6 1.75 14;238 C#7 1.75 14;240 B6 2.75 14;240 D7 2.75 14;243 E7 2.75 14;246 G6 1.75 14;248 F#6 2.75 14;251 D7 2.75 14;254 E7 1.75 14;256 B5 5.75 14;256 D6 5.75 14;256 F#6 5.75 14;262 B5 5.75 14;262 D6 5.75 14;262 F6 5.75 14;270 D6 0.75 14;271 E6 0.75 14;272 G5 2.75 14;272 B5 2.75 14;272 F6 2.75 14;275 E6 2.75 14;278 D6 1.75 14;280 G5 2.75 14;280 C#6 2.75 14;283 D6 2.75 14;286 E6 1.75 14;288 B5 5.75 14;288 D6 5.75 14;288 F#6 5.75 14;294 C#6 5.75 14;294 F6 5.75 14;294 B6 5.75 14;300 B5 1.75 14;302 C#6 1.75 14;304 D6 2.75 14;307 E6 2.75 14;310 D6 1.75 14;312 C#6 2.75 14;315 A6 2.75 14;318 G6 1.75 14;320 B5 5.75 14;320 D6 5.75 14;320 F#6 5.75 14;326 B5 5.75 14;326 D6 5.75 14;326 F6 5.75 14;334 D6 0.75 14;335 E6 0.75 14;336 D6 5.75 14;336 F#6 5.75 14;342 F6 5.75 14;342 B6 5.75 14;348 B5 1.75 14;350 C#6 1.75 14;352 D6 2.75 14;355 G6 2.75 14;358 F#6 1.75 14;360 F6 2.75 14;363 D7 2.75 14;366 A#6 1.75 14;368 B6 3.75 14;0 B3 0.75 15;2 B3 0.75 15;4 B3 0.75 15;5 A3 0.75 15;7 B3 0.75 15;9 D4 0.75 15;11 B3 0.75 15;13 F#3 0.75 15;14 A3 1.75 15;16 B3 0.75 15;18 B3 0.75 15;20 B3 0.75 15;21 A3 0.75 15;23 B3 0.75 15;25 F4 0.75 15;27 E4 0.75 15;29 D4 0.75 15;30 A3 1.75 15;32 B3 0.75 15;34 B3 0.75 15;36 B3 0.75 15;37 A3 0.75 15;39 B3 0.75 15;41 D4 0.75 15;43 B3 0.75 15;45 F#3 0.75 15;46 A3 1.75 15;48 B3 0.75 15;50 B3 0.75 15;52 B3 0.75 15;53 A3 0.75 15;55 B3 0.75 15;57 F4 0.75 15;59 E4 0.75 15;61 B3 0.75 15;62 A3 1.75 15;64 B3 0.75 15;66 B3 0.75 15;68 B3 0.75 15;69 A3 0.75 15;71 B3 0.75 15;73 D4 0.75 15;75 B3 0.75 15;77 F#3 0.75 15;78 A3 1.75 15;80 B3 0.75 15;82 B3 0.75 15;84 B3 0.75 15;85 A3 0.75 15;87 B3 0.75 15;89 F4 0.75 15;91 E4 0.75 15;93 D4 0.75 15;94 A3 1.75 15;96 B3 0.75 15;98 B3 0.75 15;100 B3 0.75 15;101 A3 0.75 15;103 B3 0.75 15;105 D4 0.75 15;107 B3 0.75 15;109 F#3 0.75 15;110 A3 1.75 15;112 B3 0.75 15;114 B3 0.75 15;116 B3 0.75 15;118 A3 0.75 15;119 B3 0.75 15;128 B3 5.75 15;128 F#4 5.75 15;128 B4 5.75 15;134 G#3 5.75 15;134 D4 5.75 15;134 F4 5.75 15;144 B3 7.75 15;144 B4 7.75 15;152 F#3 7.75 15;152 F#4 7.75 15;160 B3 5.75 15;160 F#4 5.75 15;160 B4 5.75 15;166 G#3 5.75 15;166 D4 5.75 15;166 F4 5.75 15;176 C#4 7.75 15;176 C#5 7.75 15;184 F#3 7.75 15;184 F#4 7.75 15;192 B3 5.75 15;192 F#4 5.75 15;192 B4 5.75 15;198 G#3 5.75 15;198 D4 5.75 15;198 F4 5.75 15;208 B3 7.75 15;208 B4 7.75 15;216 F#3 7.75 15;216 F#4 7.75 15;224 B3 5.75 15;224 F#4 5.75 15;224 B4 5.75 15;230 G#3 5.75 15;230 D4 5.75 15;230 F4 5.75 15;240 C#4 7.75 15;240 C#5 7.75 15;248 F#3 7.75 15;248 F#4 7.75 15;256 B3 5.75 15;256 F#4 5.75 15;256 B4 5.75 15;262 G#3 5.75 15;262 D4 5.75 15;262 F4 5.75 15;272 B3 7.75 15;272 B4 7.75 15;280 F#3 7.75 15;280 F#4 7.75 15;288 B3 5.75 15;288 F#4 5.75 15;288 B4 5.75 15;294 G#3 5.75 15;294 D4 5.75 15;294 F4 5.75 15;304 C#4 7.75 15;304 C#5 7.75 15;312 F#3 7.75 15;312 F#4 7.75 15;320 B3 5.75 15;320 F#4 5.75 15;320 B4 5.75 15;326 G#3 5.75 15;326 D4 5.75 15;326 F4 5.75 15;336 B3 5.75 15;336 F#4 5.75 15;336 B4 5.75 15;342 G#3 5.75 15;342 D4 5.75 15;342 F4 5.75 15;352 C#4 7.75 15;352 C#5 7.75 15;360 F#3 7.75 15;360 F#4 7.75 15;368 B3 3.75 15;368 F#4 3.75 15' 7 | 8 | """ 9 | Find a piece of music on onlinesequencer.net, click edit, 10 | then select all notes with CTRL+A and copy them with CTRL+C 11 | 12 | Paste string as shown above after removing ";:" from 13 | the end and "Online Sequencer:120233:" from the start 14 | """ 15 | 16 | from machine import Pin 17 | 18 | #One buzzer on pin 0 19 | mySong = music(song, pins=[Pin(10)]) 20 | 21 | #Four buzzers 22 | #mySong = music(song, pins=[Pin(0),Pin(1),Pin(2),Pin(3)]) 23 | 24 | while True: 25 | mySong.tick() 26 | sleep(0.03) -------------------------------------------------------------------------------- /src/buzzer_music.py: -------------------------------------------------------------------------------- 1 | """ 2 | Micropython (Raspberry Pi Pico) 3 | Plays music written on onlinesequencer.net through a passive piezo buzzer. 4 | Uses fast arpeggios with a single buzzer to simulate polyphony 5 | Also supports multiple buzzers at once for real polyphony 6 | https://github.com/james1236/buzzer_music 7 | """ 8 | 9 | from machine import Pin, PWM 10 | from math import ceil 11 | 12 | tones = { 13 | 'C0':16, 14 | 'C#0':17, 15 | 'D0':18, 16 | 'D#0':19, 17 | 'E0':21, 18 | 'F0':22, 19 | 'F#0':23, 20 | 'G0':24, 21 | 'G#0':26, 22 | 'A0':28, 23 | 'A#0':29, 24 | 'B0':31, 25 | 'C1':33, 26 | 'C#1':35, 27 | 'D1':37, 28 | 'D#1':39, 29 | 'E1':41, 30 | 'F1':44, 31 | 'F#1':46, 32 | 'G1':49, 33 | 'G#1':52, 34 | 'A1':55, 35 | 'A#1':58, 36 | 'B1':62, 37 | 'C2':65, 38 | 'C#2':69, 39 | 'D2':73, 40 | 'D#2':78, 41 | 'E2':82, 42 | 'F2':87, 43 | 'F#2':92, 44 | 'G2':98, 45 | 'G#2':104, 46 | 'A2':110, 47 | 'A#2':117, 48 | 'B2':123, 49 | 'C3':131, 50 | 'C#3':139, 51 | 'D3':147, 52 | 'D#3':156, 53 | 'E3':165, 54 | 'F3':175, 55 | 'F#3':185, 56 | 'G3':196, 57 | 'G#3':208, 58 | 'A3':220, 59 | 'A#3':233, 60 | 'B3':247, 61 | 'C4':262, 62 | 'C#4':277, 63 | 'D4':294, 64 | 'D#4':311, 65 | 'E4':330, 66 | 'F4':349, 67 | 'F#4':370, 68 | 'G4':392, 69 | 'G#4':415, 70 | 'A4':440, 71 | 'A#4':466, 72 | 'B4':494, 73 | 'C5':523, 74 | 'C#5':554, 75 | 'D5':587, 76 | 'D#5':622, 77 | 'E5':659, 78 | 'F5':698, 79 | 'F#5':740, 80 | 'G5':784, 81 | 'G#5':831, 82 | 'A5':880, 83 | 'A#5':932, 84 | 'B5':988, 85 | 'C6':1047, 86 | 'C#6':1109, 87 | 'D6':1175, 88 | 'D#6':1245, 89 | 'E6':1319, 90 | 'F6':1397, 91 | 'F#6':1480, 92 | 'G6':1568, 93 | 'G#6':1661, 94 | 'A6':1760, 95 | 'A#6':1865, 96 | 'B6':1976, 97 | 'C7':2093, 98 | 'C#7':2217, 99 | 'D7':2349, 100 | 'D#7':2489, 101 | 'E7':2637, 102 | 'F7':2794, 103 | 'F#7':2960, 104 | 'G7':3136, 105 | 'G#7':3322, 106 | 'A7':3520, 107 | 'A#7':3729, 108 | 'B7':3951, 109 | 'C8':4186, 110 | 'C#8':4435, 111 | 'D8':4699, 112 | 'D#8':4978, 113 | 'E8':5274, 114 | 'F8':5588, 115 | 'F#8':5920, 116 | 'G8':6272, 117 | 'G#8':6645, 118 | 'A8':7040, 119 | 'A#8':7459, 120 | 'B8':7902, 121 | 'C9':8372, 122 | 'C#9':8870, 123 | 'D9':9397, 124 | 'D#9':9956, 125 | 'E9':10548, 126 | 'F9':11175, 127 | 'F#9':11840, 128 | 'G9':12544, 129 | 'G#9':13290, 130 | 'A9':14080, 131 | 'A#9':14917, 132 | 'B9':15804 133 | } 134 | 135 | #Time, Note, Duration, Instrument (onlinesequencer.net schematic format) 136 | #0 D4 8 0;0 D5 8 0;0 G4 8 0;8 C5 2 0;10 B4 2 0;12 G4 2 0;14 F4 1 0;15 G4 17 0;16 D4 8 0;24 C4 8 0 137 | 138 | class music: 139 | def __init__(self, songString='0 D4 8 0', looping=True, tempo=3, duty=2512, pin=None, pins=[Pin(0)]): 140 | self.tempo = tempo 141 | self.song = songString 142 | self.looping = looping 143 | self.duty = duty 144 | 145 | self.stopped = False 146 | 147 | self.timer = -1 148 | self.beat = -1 149 | self.arpnote = 0 150 | 151 | self.pwms = [] 152 | 153 | if (not (pin is None)): 154 | pins = [pin] 155 | self.pins = pins 156 | for pin in pins: 157 | self.pwms.append(PWM(pin)) 158 | 159 | self.notes = [] 160 | 161 | self.playingNotes = [] 162 | self.playingDurations = [] 163 | 164 | 165 | #Find the end of the song 166 | self.end = 0 167 | splitSong = self.song.split(";") 168 | for note in splitSong: 169 | snote = note.split(" ") 170 | testEnd = round(float(snote[0])) + ceil(float(snote[2])) 171 | if (testEnd > self.end): 172 | self.end = testEnd 173 | 174 | #Create empty song structure 175 | while (self.end > len(self.notes)): 176 | self.notes.append(None) 177 | 178 | #Populate song structure with the notes 179 | for note in splitSong: 180 | snote = note.split(" ") 181 | beat = round(float(snote[0])); 182 | 183 | if (self.notes[beat] == None): 184 | self.notes[beat] = [] 185 | self.notes[beat].append([snote[1],ceil(float(snote[2]))]) #Note, Duration 186 | 187 | 188 | #Round up end of song to nearest bar 189 | self.end = ceil(self.end / 8) * 8 190 | 191 | def stop(self): 192 | for pwm in self.pwms: 193 | pwm.deinit() 194 | self.stopped = True 195 | 196 | def restart(self): 197 | self.beat = -1 198 | self.timer = 0 199 | self.stop() 200 | self.pwms = [] 201 | for pin in self.pins: 202 | self.pwms.append(PWM(pin)) 203 | self.stopped = False 204 | 205 | def resume(self): 206 | self.stop() 207 | self.pwms = [] 208 | for pin in self.pins: 209 | self.pwms.append(PWM(pin)) 210 | self.stopped = False 211 | 212 | def tick(self): 213 | if (not self.stopped): 214 | self.timer = self.timer + 1 215 | 216 | #Loop 217 | if (self.timer % (self.tempo * self.end) == 0 and (not (self.timer == 0))): 218 | if (not self.looping): 219 | self.stop() 220 | return False 221 | self.beat = -1 222 | self.timer = 0 223 | 224 | #On Beat 225 | if (self.timer % self.tempo == 0): 226 | self.beat = self.beat + 1 227 | 228 | #Remove expired notes from playing list 229 | i = 0 230 | while (i < len(self.playingDurations)): 231 | self.playingDurations[i] = self.playingDurations[i] - 1 232 | if (self.playingDurations[i] <= 0): 233 | self.playingNotes.pop(i) 234 | self.playingDurations.pop(i) 235 | else: 236 | i = i + 1 237 | 238 | #Add new notes and their durations to the playing list 239 | 240 | """ 241 | #Old method runs for every note, slow to process on every beat and causes noticeable delay 242 | ssong = song.split(";") 243 | for note in ssong: 244 | snote = note.split(" ") 245 | if int(snote[0]) == beat: 246 | playingNotes.append(snote[1]) 247 | playingDurations.append(int(snote[2])) 248 | """ 249 | 250 | if (self.beat < len(self.notes)): 251 | if (self.notes[self.beat] != None): 252 | for note in self.notes[self.beat]: 253 | self.playingNotes.append(note[0]) 254 | self.playingDurations.append(note[1]) 255 | 256 | #Only need to run these checks on beats 257 | i = 0 258 | for pwm in self.pwms: 259 | if (i >= len(self.playingNotes)): 260 | if hasattr(pwm, 'duty_u16'): 261 | pwm.duty_u16(0) 262 | else: 263 | pwm.duty(0) 264 | else: 265 | #Play note 266 | if hasattr(pwm, 'duty_u16'): 267 | pwm.duty_u16(self.duty) 268 | else: 269 | pwm.duty(self.duty) 270 | pwm.freq(tones[self.playingNotes[i]]) 271 | i = i + 1 272 | 273 | 274 | #Play arp of all playing notes 275 | if (len(self.playingNotes) > len(self.pwms)): 276 | p = self.pwms[len(self.pwms)-1]; 277 | if hasattr(p, 'duty_u16'): 278 | p.duty_u16(self.duty) 279 | else: 280 | p.duty(self.duty) 281 | 282 | if (self.arpnote > len(self.playingNotes)-len(self.pwms)): 283 | self.arpnote = 0 284 | self.pwms[len(self.pwms)-1].freq(tones[self.playingNotes[self.arpnote+(len(self.pwms)-1)]]) 285 | self.arpnote = self.arpnote + 1 286 | 287 | return True 288 | else: 289 | return False 290 | -------------------------------------------------------------------------------- /src/utils/dos_font.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Converted from dos.ttf using: 3 | # '.\font2bitmap.py.py' dos.ttf 16 -c 0x20-0x7f 4 | 5 | MAP = ( 6 | ' !\"#$%&\'()*+,-./0123456789:;<=' 7 | '>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[' 8 | '\\]^_`abcdefghijklmnopqrstuvwxyz' 9 | '{|}~' 10 | ) 11 | 12 | BPP = 1 13 | HEIGHT = 15 14 | MAX_WIDTH = 9 15 | _WIDTHS = \ 16 | b'\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09'\ 17 | b'\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09'\ 18 | b'\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09'\ 19 | b'\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09'\ 20 | b'\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09'\ 21 | b'\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09\x09' 22 | 23 | OFFSET_WIDTH = 2 24 | _OFFSETS = \ 25 | b'\x00\x00\x00\x87\x01\x0e\x01\x95\x02\x1c\x02\xa3\x03\x2a\x03\xb1'\ 26 | b'\x04\x38\x04\xbf\x05\x46\x05\xcd\x06\x54\x06\xdb\x07\x62\x07\xe9'\ 27 | b'\x08\x70\x08\xf7\x09\x7e\x0a\x05\x0a\x8c\x0b\x13\x0b\x9a\x0c\x21'\ 28 | b'\x0c\xa8\x0d\x2f\x0d\xb6\x0e\x3d\x0e\xc4\x0f\x4b\x0f\xd2\x10\x59'\ 29 | b'\x10\xe0\x11\x67\x11\xee\x12\x75\x12\xfc\x13\x83\x14\x0a\x14\x91'\ 30 | b'\x15\x18\x15\x9f\x16\x26\x16\xad\x17\x34\x17\xbb\x18\x42\x18\xc9'\ 31 | b'\x19\x50\x19\xd7\x1a\x5e\x1a\xe5\x1b\x6c\x1b\xf3\x1c\x7a\x1d\x01'\ 32 | b'\x1d\x88\x1e\x0f\x1e\x96\x1f\x1d\x1f\xa4\x20\x2b\x20\xb2\x21\x39'\ 33 | b'\x21\xc0\x22\x47\x22\xce\x23\x55\x23\xdc\x24\x63\x24\xea\x25\x71'\ 34 | b'\x25\xf8\x26\x7f\x27\x06\x27\x8d\x28\x14\x28\x9b\x29\x22\x29\xa9'\ 35 | b'\x2a\x30\x2a\xb7\x2b\x3e\x2b\xc5\x2c\x4c\x2c\xd3\x2d\x5a\x2d\xe1'\ 36 | b'\x2e\x68\x2e\xef\x2f\x76\x2f\xfd\x30\x84\x31\x0b\x31\x92\x32\x19' 37 | 38 | _BITMAPS =\ 39 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 40 | b'\x00\x00\x00\x0c\x0f\x07\x83\xc0\xc0\x60\x30\x00\x0c\x06\x00\x00'\ 41 | b'\x00\x00\x00\xcc\x66\x11\x08\x80\x00\x00\x00\x00\x00\x00\x00\x00'\ 42 | b'\x00\x00\x00\x00\x00\x00\x00\x36\x1b\x1f\xc6\xc3\x63\xf8\xd8\x6c'\ 43 | b'\x00\x00\x00\x00\x00\x80\x40\xf8\xd6\x6b\x1c\x07\x01\xc6\xb3\x58'\ 44 | b'\xf8\x10\x08\x00\x00\x00\x03\x02\x49\x2c\x6c\x0c\x0c\x0d\x8d\x24'\ 45 | b'\x90\x30\x00\x00\x00\x00\x00\x03\x83\x61\xb0\x70\x30\x3b\x37\x19'\ 46 | b'\x8c\xc3\xb0\x00\x00\x00\x00\x06\x03\x00\x80\x80\x00\x00\x00\x00'\ 47 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x03\x03\x01\x80\xc0\x60'\ 48 | b'\x30\x18\x06\x01\x80\x00\x00\x00\x00\x00\x18\x06\x01\x80\xc0\x60'\ 49 | b'\x30\x18\x0c\x0c\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\xc3'\ 50 | b'\xc7\xf8\xf0\xcc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06'\ 51 | b'\x03\x07\xe0\xc0\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 52 | b'\x00\x00\x00\x00\x00\x00\xc0\x60\x10\x10\x00\x00\x00\x00\x00\x00'\ 53 | b'\x00\x00\x00\x3f\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 54 | b'\x00\x00\x00\x00\x00\x00\x00\x03\x01\x80\x00\x00\x00\x00\x00\x00'\ 55 | b'\x00\x20\x30\x30\x30\x30\x30\x30\x10\x00\x00\x00\x00\x00\x00\x00'\ 56 | b'\x1f\x18\xcc\x66\x73\x79\xec\xe6\x63\x31\x8f\x80\x00\x00\x00\x00'\ 57 | b'\x00\x0c\x0e\x0f\x01\x80\xc0\x60\x30\x18\x0c\x1f\x80\x00\x00\x00'\ 58 | b'\x00\x00\x7c\x63\x01\x81\x81\x81\x81\x81\x80\xc6\x7f\x00\x00\x00'\ 59 | b'\x00\x00\x00\xf8\xc6\x03\x01\x87\x80\x60\x30\x19\x8c\x7c\x00\x00'\ 60 | b'\x00\x00\x00\x00\x30\x38\x3c\x36\x33\x19\x8f\xe0\x60\x30\x3c\x00'\ 61 | b'\x00\x00\x00\x00\x07\xf3\x01\x80\xc0\x7e\x01\x80\xc0\x66\x31\xf0'\ 62 | b'\x00\x00\x00\x00\x00\x07\xc6\x33\x01\x80\xfc\x63\x31\x98\xcc\x63'\ 63 | b'\xe0\x00\x00\x00\x00\x00\x1f\xcc\x60\x30\x18\x18\x18\x18\x0c\x06'\ 64 | b'\x03\x00\x00\x00\x00\x00\x00\x1f\x18\xcc\x66\x31\xf1\x8c\xc6\x63'\ 65 | b'\x31\x8f\x80\x00\x00\x00\x00\x00\x3e\x31\x98\xcc\x66\x31\xf8\x0c'\ 66 | b'\x06\x63\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x06\x03\x00\x00\x00'\ 67 | b'\x00\x30\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x06\x00\x00'\ 68 | b'\x00\x00\x60\x30\x08\x08\x00\x00\x00\x00\x00\x00\x0c\x0c\x0c\x0c'\ 69 | b'\x0c\x03\x00\xc0\x30\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7e'\ 70 | b'\x00\x00\x0f\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\xc0'\ 71 | b'\x30\x0c\x03\x03\x03\x03\x03\x00\x00\x00\x00\x00\x00\x0f\x8c\x66'\ 72 | b'\x30\x18\x18\x18\x0c\x00\x03\x01\x80\x00\x00\x00\x00\x00\x00\x07'\ 73 | b'\x84\x24\xea\x95\x4a\xad\x5b\x10\x07\x80\x00\x00\x00\x00\x00\x1c'\ 74 | b'\x1b\x18\xcc\x66\x33\xf9\x8c\xc6\x63\x31\x80\x00\x00\x00\x00\x00'\ 75 | b'\xfc\x33\x19\x8c\xc7\xc3\x31\x98\xcc\x66\x7e\x00\x00\x00\x00\x00'\ 76 | b'\x00\xf8\xc6\x63\x30\x18\x0c\x06\x03\x19\x8c\x7c\x00\x00\x00\x00'\ 77 | b'\x00\x03\xf0\xcc\x66\x33\x19\x8c\xc6\x63\x31\x99\xf8\x00\x00\x00'\ 78 | b'\x00\x00\x07\xf1\x98\xc4\x68\x3c\x1e\x0d\x06\x23\x33\xf8\x00\x00'\ 79 | b'\x00\x00\x00\x0f\xe3\x31\x88\xd0\x78\x3c\x1a\x0c\x06\x07\x80\x00'\ 80 | b'\x00\x00\x00\x00\x0f\x8c\x66\x33\x01\x80\xce\x63\x31\x98\xc7\xe0'\ 81 | b'\x00\x00\x00\x00\x00\x31\x98\xcc\x66\x33\xf9\x8c\xc6\x63\x31\x98'\ 82 | b'\xc0\x00\x00\x00\x00\x00\x1e\x06\x03\x01\x80\xc0\x60\x30\x18\x0c'\ 83 | b'\x0f\x00\x00\x00\x00\x00\x00\x1e\x06\x03\x01\x80\xc0\x63\x31\x98'\ 84 | b'\xcc\x3c\x00\x00\x00\x00\x00\x01\xcc\x66\x33\x1b\x0f\x07\x83\x61'\ 85 | b'\x98\xcc\xe6\x00\x00\x00\x00\x00\x03\xc0\xc0\x60\x30\x18\x0c\x06'\ 86 | b'\x03\x11\x99\xfc\x00\x00\x00\x00\x00\x04\x13\x19\xdc\xfe\x7f\x35'\ 87 | b'\x98\xcc\x66\x33\x18\x00\x00\x00\x00\x00\x08\x66\x33\x99\xec\xfe'\ 88 | b'\x6f\x33\x98\xcc\x66\x30\x00\x00\x00\x00\x00\x0f\x8c\x66\x33\x19'\ 89 | b'\x8c\xc6\x63\x31\x98\xc7\xc0\x00\x00\x00\x00\x00\x3f\x0c\xc6\x63'\ 90 | b'\x31\x98\xf8\x60\x30\x18\x1e\x00\x00\x00\x00\x00\x00\x3e\x31\x98'\ 91 | b'\xcc\x66\x33\x19\x8c\xd6\x6f\x1f\x00\xc0\x00\x00\x00\x00\xfc\x33'\ 92 | b'\x19\x8c\xc6\x63\xe1\xb0\xcc\x66\x7b\x00\x00\x00\x00\x00\x00\xf8'\ 93 | b'\xc6\x63\x18\x07\x00\xc0\x33\x19\x8c\x7c\x00\x00\x00\x00\x00\x01'\ 94 | b'\xf8\xfc\x5a\x0c\x06\x03\x01\x80\xc0\x60\x78\x00\x00\x00\x00\x00'\ 95 | b'\x06\x33\x19\x8c\xc6\x63\x31\x98\xcc\x66\x31\xf0\x00\x00\x00\x00'\ 96 | b'\x00\x0c\x66\x33\x19\x8c\xc6\x63\x31\x8d\x83\x80\x80\x00\x00\x00'\ 97 | b'\x00\x00\x18\xcc\x66\x33\x19\x8c\xd6\x7f\x3b\x98\xc8\x20\x00\x00'\ 98 | b'\x00\x00\x00\x31\x98\xc6\xc3\xe0\xe0\x70\x7c\x36\x31\x98\xc0\x00'\ 99 | b'\x00\x00\x00\x00\x33\x19\x8c\xc6\x61\xe0\x60\x30\x18\x0c\x0f\x00'\ 100 | b'\x00\x00\x00\x00\x00\xfe\x63\x21\x81\x81\x81\x81\x81\x84\xc6\x7f'\ 101 | b'\x00\x00\x00\x00\x00\x00\x78\x30\x18\x0c\x06\x03\x01\x80\xc0\x60'\ 102 | b'\x3c\x00\x00\x00\x00\x00\x00\x01\x00\xc0\x30\x0c\x03\x00\xc0\x30'\ 103 | b'\x08\x00\x00\x00\x00\x00\x00\x01\xe0\x30\x18\x0c\x06\x03\x01\x80'\ 104 | b'\xc0\x60\xf0\x00\x00\x00\x04\x07\x06\xc6\x30\x00\x00\x00\x00\x00'\ 105 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 106 | b'\x00\x00\x00\x00\x07\xf8\x00\x00\x00\x0c\x06\x02\x00\x80\x00\x00'\ 107 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x80'\ 108 | b'\x61\xf1\x98\xcc\x66\x1d\x80\x00\x00\x00\x00\x00\xe0\x30\x18\x0f'\ 109 | b'\x86\x63\x31\x98\xcc\x66\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 110 | b'\x1f\x18\xcc\x06\x03\x01\x8c\x7c\x00\x00\x00\x00\x00\x00\x70\x18'\ 111 | b'\x0c\x3e\x33\x19\x8c\xc6\x63\x30\xec\x00\x00\x00\x00\x00\x00\x00'\ 112 | b'\x00\x00\x7c\x63\x31\x9f\xcc\x06\x31\xf0\x00\x00\x00\x00\x00\x01'\ 113 | b'\xc1\xb0\xc0\xf8\x30\x18\x0c\x06\x03\x03\xc0\x00\x00\x00\x00\x00'\ 114 | b'\x00\x00\x00\x01\xd9\x98\xcc\x66\x33\x19\x87\xc0\x63\x30\xf0\x00'\ 115 | b'\x00\x38\x0c\x06\x03\x61\xd8\xcc\x66\x33\x19\x9c\xc0\x00\x00\x00'\ 116 | b'\x00\x00\x0c\x06\x00\x03\x80\xc0\x60\x30\x18\x0c\x0f\x00\x00\x00'\ 117 | b'\x00\x00\x00\x0c\x06\x00\x03\x80\xc0\x60\x30\x18\x0c\x06\x33\x19'\ 118 | b'\x87\x80\x00\x01\xc0\x60\x30\x19\x8d\x87\x83\xc1\xb0\xcc\xe6\x00'\ 119 | b'\x00\x00\x00\x00\x00\xe0\x30\x18\x0c\x06\x03\x01\x80\xc0\x60\x78'\ 120 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xec\x7f\x35\x9a\xcd\x66\xb3'\ 121 | b'\x18\x00\x00\x00\x00\x00\x00\x00\x00\x01\xb8\x66\x33\x19\x8c\xc6'\ 122 | b'\x63\x30\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf1\x8c\xc6\x63\x31'\ 123 | b'\x98\xc7\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x06\xe1\x98\xcc\x66'\ 124 | b'\x33\x19\x8f\x86\x03\x03\xc0\x00\x00\x00\x00\x00\x07\xc6\x63\x31'\ 125 | b'\x98\xcc\x66\x1f\x01\x80\xc0\xf0\x00\x00\x00\x00\x00\x1b\xc7\x63'\ 126 | b'\x01\x80\xc0\x60\x78\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x18'\ 127 | b'\xc6\x01\xc0\x31\x8c\x7c\x00\x00\x00\x00\x00\x00\x40\x60\x30\x7e'\ 128 | b'\x0c\x06\x03\x01\x80\xd0\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 129 | b'\xcc\x66\x33\x19\x8c\xc6\x61\xd8\x00\x00\x00\x00\x00\x00\x00\x00'\ 130 | b'\x01\x8c\xc6\x63\x31\x8d\x83\x80\x80\x00\x00\x00\x00\x00\x00\x00'\ 131 | b'\x00\x03\x19\xac\xd6\x6b\x35\x9f\xc6\xc0\x00\x00\x00\x00\x00\x00'\ 132 | b'\x00\x00\x06\x33\x18\xd8\x38\x36\x31\x98\xc0\x00\x00\x00\x00\x00'\ 133 | b'\x00\x00\x00\x0c\x66\x33\x19\x8c\xc6\x63\x1f\x80\xc0\xc7\xc0\x00'\ 134 | b'\x00\x00\x00\x00\x1f\xc8\xc0\xc0\xc0\xc0\xc2\x7f\x00\x00\x00\x00'\ 135 | b'\x00\x00\x1c\x18\x0c\x06\x0e\x01\x80\xc0\x60\x30\x0e\x00\x00\x00'\ 136 | b'\x00\x00\x00\x60\x30\x18\x0c\x00\x00\x01\x80\xc0\x60\x30\x00\x00'\ 137 | b'\x00\x00\x00\x03\x80\x60\x30\x18\x07\x06\x03\x01\x80\xc1\xc0\x00'\ 138 | b'\x00\x00\x00\x00\x07\x66\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 139 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x80\xe0\xd8\xc6\x63\x31\x9f\xc0'\ 140 | b'\x00\x00\x00\x00' 141 | 142 | WIDTHS = memoryview(_WIDTHS) 143 | OFFSETS = memoryview(_OFFSETS) 144 | BITMAPS = memoryview(_BITMAPS) --------------------------------------------------------------------------------