From: Aus000Alex <143391931+Aus000Alex@users.noreply.github.com> Date: Wed, 14 Feb 2024 09:14:48 +0000 (+0100) Subject: initial commit X-Git-Url: https://vault307.fbx.one/gitweb/PicoTamachibi.git/commitdiff_plain/bb15dc0e61cdc9f827ebe3fadc4e89e590ec0227 initial commit Added files form localy working version --- diff --git a/baby.pbm b/baby.pbm new file mode 100644 index 0000000..6f5c079 Binary files /dev/null and b/baby.pbm differ diff --git a/baby_bounce01.pbm b/baby_bounce01.pbm new file mode 100644 index 0000000..1cbc4fc Binary files /dev/null and b/baby_bounce01.pbm differ diff --git a/baby_bounce02.pbm b/baby_bounce02.pbm new file mode 100644 index 0000000..b089a60 Binary files /dev/null and b/baby_bounce02.pbm differ diff --git a/baby_bounce03.pbm b/baby_bounce03.pbm new file mode 100644 index 0000000..7ceda96 Binary files /dev/null and b/baby_bounce03.pbm differ diff --git a/baby_bounce04.pbm b/baby_bounce04.pbm new file mode 100644 index 0000000..bae5c77 Binary files /dev/null and b/baby_bounce04.pbm differ diff --git a/baby_zzz01.pbm b/baby_zzz01.pbm new file mode 100644 index 0000000..17ec879 Binary files /dev/null and b/baby_zzz01.pbm differ diff --git a/baby_zzz02.pbm b/baby_zzz02.pbm new file mode 100644 index 0000000..d4bf401 Binary files /dev/null and b/baby_zzz02.pbm differ diff --git a/baby_zzz03.pbm b/baby_zzz03.pbm new file mode 100644 index 0000000..9da9e1d Binary files /dev/null and b/baby_zzz03.pbm differ diff --git a/baby_zzz04.pbm b/baby_zzz04.pbm new file mode 100644 index 0000000..25c8908 Binary files /dev/null and b/baby_zzz04.pbm differ diff --git a/book.pbm b/book.pbm new file mode 100644 index 0000000..566955d Binary files /dev/null and b/book.pbm differ diff --git a/bubble.pbm b/bubble.pbm new file mode 100644 index 0000000..88f1dba Binary files /dev/null and b/bubble.pbm differ diff --git a/bubble.xcf b/bubble.xcf new file mode 100644 index 0000000..13fa5e9 Binary files /dev/null and b/bubble.xcf differ diff --git a/button_test.py b/button_test.py new file mode 100644 index 0000000..fbf15c5 --- /dev/null +++ b/button_test.py @@ -0,0 +1,10 @@ +from machine import Pin + +button = Pin(15, Pin.IN, Pin.PULL_DOWN) + +while True: + print(button.value()) + if button.value() == 0: + print("0") + if button.value() == 1: + print("1") \ No newline at end of file diff --git a/call.pbm b/call.pbm new file mode 100644 index 0000000..2ef732d Binary files /dev/null and b/call.pbm differ diff --git a/call_animate01.pbm b/call_animate01.pbm new file mode 100644 index 0000000..1a0303e Binary files /dev/null and b/call_animate01.pbm differ diff --git a/call_animate03.pbm b/call_animate03.pbm new file mode 100644 index 0000000..deae954 Binary files /dev/null and b/call_animate03.pbm differ diff --git a/disc.pbm b/disc.pbm new file mode 100644 index 0000000..d9400ff Binary files /dev/null and b/disc.pbm differ diff --git a/display.py b/display.py new file mode 100644 index 0000000..872ef96 --- /dev/null +++ b/display.py @@ -0,0 +1,39 @@ +from machine import I2C, Pin +from ssd1306 import SSD1306_I2C +import framebuf +from time import sleep + +sda = Pin(0) +scl = Pin(1) +id = 0 + +i2c = I2C(id=id, sda=sda, scl=scl) + +oled = SSD1306_I2C(width=128, height=64, i2c=i2c) +oled.init_display() +oled.text("test", 1, 1) +oled.show() +sleep(1) + +images = [] +for n in range (1,5): + with open('frame0%s.pbm' % n, 'rb') as f: + f.readline() # magic number + f.readline() # creator comment + f.readline() # dimensions + data = bytearray(f.read()) + fbuf = framebuf.FrameBuffer(data, 128,64, framebuf.MONO_HLSB) + images.append(fbuf) + +while True: + for i in images: + oled.invert(1) + oled.blit(i,0,0) + oled.show() + sleep(0.1) + sleep(0.4) + for i in reversed(images): + oled.invert(1) + oled.blit(i,0,0) + oled.show() + sleep(0.1) diff --git a/eat01.pbm b/eat01.pbm new file mode 100644 index 0000000..c8dea3d Binary files /dev/null and b/eat01.pbm differ diff --git a/eat02.pbm b/eat02.pbm new file mode 100644 index 0000000..425f990 Binary files /dev/null and b/eat02.pbm differ diff --git a/eat03.pbm b/eat03.pbm new file mode 100644 index 0000000..7e28ec3 Binary files /dev/null and b/eat03.pbm differ diff --git a/eat04.pbm b/eat04.pbm new file mode 100644 index 0000000..ae04fa3 Binary files /dev/null and b/eat04.pbm differ diff --git a/eat05.pbm b/eat05.pbm new file mode 100644 index 0000000..6a82821 Binary files /dev/null and b/eat05.pbm differ diff --git a/eat06.pbm b/eat06.pbm new file mode 100644 index 0000000..a24c0a8 Binary files /dev/null and b/eat06.pbm differ diff --git a/eat07.pbm b/eat07.pbm new file mode 100644 index 0000000..87278ba Binary files /dev/null and b/eat07.pbm differ diff --git a/eat08.pbm b/eat08.pbm new file mode 100644 index 0000000..9b0db4c Binary files /dev/null and b/eat08.pbm differ diff --git a/firstaid.pbm b/firstaid.pbm new file mode 100644 index 0000000..424b902 Binary files /dev/null and b/firstaid.pbm differ diff --git a/food.pbm b/food.pbm new file mode 100644 index 0000000..cdd1e06 Binary files /dev/null and b/food.pbm differ diff --git a/game.pbm b/game.pbm new file mode 100644 index 0000000..9c7b680 Binary files /dev/null and b/game.pbm differ diff --git a/heart.pbm b/heart.pbm new file mode 100644 index 0000000..2f32140 Binary files /dev/null and b/heart.pbm differ diff --git a/heart_plus.pbm b/heart_plus.pbm new file mode 100644 index 0000000..76ddad1 Binary files /dev/null and b/heart_plus.pbm differ diff --git a/icon.py b/icon.py new file mode 100644 index 0000000..564bd2f --- /dev/null +++ b/icon.py @@ -0,0 +1,665 @@ +import framebuf +from machine import Pin +from time import sleep +from os import listdir +import gc + +class Icon(): + """ Models an icon and all the properties it requires """ + __image = None + __x = 0 + __y = 0 + __invert = False + __width = 16 + __height = 16 + __name = "Empty" + + + def __init__(self, filename:None, width=None, height=None, x=None, y=None, name=None): + """ Sets up the default values """ + if width: + self.__width = width + if height: + self.__height = height + if name: + self.__name = name + if x: + self.__x = x + if y: + self.__y = y + if filename is not None: + self.__image = self.loadicons(filename) + + @property + def image(self): + """ gets the icon image """ + + return self.__image + + @image.setter + def image(self, buf): + """ Sets the icon image """ + + self.__image = buf + + @property + def x(self)->int: + """ Gets the X value """ + + return self.__x + + + @x.setter + def x(self, value): + """ Sets the X value """ + + self.__x = value + + + @property + def width(self)->int: + """ Get the width""" + + return self.__width + + + @width.setter + def width(self, value): + """ Sets the icon width """ + + self.__width = value + + + @property + def height(self): + """ Returns height """ + return self.__height + + + @height.setter + def height(self, value): + """ Gets the height """ + + self.__height = value + + + @property + def name(self): + """ Gets the name """ + + return self.__name + + + @name.setter + def name(self, value): + """ Sets the icon name """ + + self.__name = value + + + @property + def y(self)->int: + """ Gets the y value """ + + return self.__y + + + @y.setter + def y(self, value): + """ Sets the Y value """ + + self.__y = value + + + @property + def invert(self)->bool: + """ Flips the bits in the image so white become black etc and returns the image """ + print("Invert is", self.__invert) + return self.__invert + + @invert.setter + def invert(self, value:bool): + """ Inverts the icon colour """ + + image = self.__image + for x in range(0,self.width): + for y in range(0, self.height): + pxl = image.pixel(x,y) + if pxl == 0: + image.pixel(x,y,1) + else: + image.pixel(x,y,0) + + self.__image = image + self.__invert = value + # print("Invert is", self.__invert) + + def loadicons(self, file): + # print(file) + with open(file, 'rb') as f: + f.readline() # magic number + f.readline() # creator comment + f.readline() # dimensions + data = bytearray(f.read()) + fbuf = framebuf.FrameBuffer(data, self.__width,self.__height, framebuf.MONO_HLSB) + # print(self.__name, self.__width, self.__height) + return fbuf + +class Toolbar(): + """ Models the toolbar """ + __icon_array = [] + __framebuf = framebuf.FrameBuffer(bytearray(160*64*1), 160, 16, framebuf.MONO_HLSB) + __spacer = 1 + __selected_item = None + __selected_index = -1 # -1 means no item selected + + def __init__(self): + # print("building toolbar") + self.__framebuf = framebuf.FrameBuffer(bytearray(160*64*8), 160, 16, framebuf.MONO_HLSB) + + def additem(self, icon): + self.__icon_array.append(icon) + print("mehr icons...") + + + def remove(self, icon): + self.__icon_array.remove(icon) + + def getlength(self): + return len(self.__icon_array) + + """ + @property + def data(self): + + x = 0 + count = 0 + for icon in self.__icon_array: + # print("x:",x) + count += 1 + if type(icon) == Icon: + self.__framebuf.blit(icon.image, x, 0) + if type(icon) == Animate: + self.__framebuf.blit(icon.__frames[icon.__current_frame].image, x, 0) + fb = self.__framebuf + x += icon.width + self.spacer + return fb + """ + + @property + def spacer(self): + """ returns the spacer value""" + return self.__spacer + + @spacer.setter + def spacer(self, value): + """ Sets the spacer value""" + self.__spacer = value + + def show(self, oled): + oled.blit(self.__framebuf, 0,0) + # oled.show() + + def select(self, index, oled): + """ Set the item in the index to inverted """ + # for item in self.__icon_array: + # item.invert = False + self.__icon_array[index].invert = True + self.__selected_index = index + offsetvalue = 3 + x=0 + for loopcount in range(len(self.__icon_array)): + offset = loopcount - offsetvalue + zeiger = (index + offset) % len(self.__icon_array) + # print("x:",x) + if type(self.__icon_array[zeiger]) == Icon: + self.__framebuf.blit(self.__icon_array[zeiger].image, x, 0) + if type(self.__icon_array[zeiger]) == Animate: + self.__framebuf.blit(self.__icon_array[zeiger].__frames[self.__icon_array[zeiger].__current_frame].image, x, 0) + fb = self.__framebuf + x += self.__icon_array[zeiger].width + self.spacer + return fb + + def unselect(self, index, oled): + self.__icon_array[index].invert = False + self.__selected_index = -1 + self.show(oled) + + @property + def selected_item(self): + """ Returns the name of the currently selected icon """ + self.__selected_item = self.__icon_array[self.__selected_index].name + return self.__selected_item + +class Animate(): + __frames = [] + __current_frame = 0 + __speed = "normal" # Other speeds are 'fast' and 'slow' - it just adds frames or skips frames + __speed_value = 0 + __done = False # Has the animation completed + __loop_count = 0 + __bouncing = False + __animation_type = "default" + __pause = 0 + __set = False + __x = 0 + __y = 0 + __width = 16 + __height = 16 + __cached = False + __filename = None + """ other animations types: + - loop + - bounce + - reverse + """ + @property + def set(self)->bool: + return self.__set + + @set.setter + def set(self, value:bool): + self.__set = value + if value == True: + self.load() + else: + self.unload() + + + @property + def speed(self): + """ Returns the current speed """ + return self.__speed + + @speed.setter + def speed(self, value:str): + if value in ['very slow','slow','normal','fast']: + self.__speed = value + if value == 'very slow': + self.__pause = 10 + self.__speed_value = 10 + if value == 'slow': + self.__pause = 1 + self.__speed_value = 1 + if value == "normal": + self.__pause = 0 + self.__speed_value = 0 + else: + print(value, "is not a valid value, try 'fast','normal' or 'slow'") + + @property + def animation_type(self): + return self.__animation_type + + @animation_type.setter + def animation_type(self, value): + if value in ['default','loop','bounce','reverse']: + self.__animation_type = value + else: + print(value," is not a valid Animation type - it should be 'loop','bounce','reverse' or 'default'") + + def __init__(self, frames=None, animation_type:str=None,x:int=None,y:int=None, width:int=None, height:int=None, filename=None): + """ setup the animation """ + + if x: + self.__x = x + if y: + self.__y = y + if width: + self.__width = width + if height: + self.__height = height + self.__current_frame = 0 + if frames is not None: + self.__frames = frames + self.__done = False + self.__loop_count = 1 + if animation_type is not None: + self.animation_type = animation_type + if filename: + self.__filename = filename + + @property + def filename(self): + """ Returns the current filename""" + return self.__filename + + @filename.setter + def filename(self, value): + """ Sets the filename """ + self.__filename = value + + def forward(self): + """ progress the current frame """ + if self.__speed == 'normal': + self.__current_frame +=1 + + if self.__speed in ['very slow','slow']: + if self.__pause > 0: + self.__pause -= 1 + else: + self.__current_frame +=1 + self.__pause = self.__speed_value + + if self.__speed == 'fast': + if self.__current_frame < self.frame_count +2: + self.__current_frame +=2 + else: + self.__current_frame +=1 + + def reverse(self): + if self.__speed == 'normal': + self.__current_frame -=1 + + if self.__speed in ['very slow','slow']: + if self.__pause > 0: + self.__pause -= 1 + else: + self.__current_frame -=1 + self.__pause = self.__speed_value + + if self.__speed == 'fast': + if self.__current_frame < self.frame_count +2: + self.__current_frame -=2 + else: + self.__current_frame -=1 + + def load(self): + """ load the animation files """ + + # load the files from disk + if not self.__cached: + files = listdir() + array = [] + for file in files: + if (file.startswith(self.__filename)) and (file.endswith('.pbm')): + array.append(Icon(filename=file, width=self.__width, height=self.__height, x=self.__x, y=self.__y, name=file)) + self.__frames = array + self.__cached = True + + def unload(self): + """ free up memory """ + + self.__frames = None + self.__cached = False + gc.collect() + + def animate(self, oled): + """ Animates the frames based on the animation type and for the number of times specified """ + + cf = self.__current_frame # Current Frame number - used to index the frames array + frame = self.__frames[cf] + oled.blit(frame.image, frame.x, frame.y) + + if self.__animation_type == "loop": + # Loop from the first frame to the last, for the number of cycles specificed, and then set done to True + self.forward() + + if self.__current_frame > self.frame_count: + self.__current_frame = 0 + self.__loop_count -=1 + if self.__loop_count == 0: + self.__done = True + + if self.__animation_type == "bouncing": + + # Loop from the first frame to the last, and then back to the first again, then set done to True + if self.__bouncing: + + if self.__current_frame == 0: + if self.__loop_count == 0: + self.__done == True + else: + if self.__loop_count >0: + self.__loop_count -=1 + self.forward() + self.__bouncing = False + if self.__loop_count == -1: + # bounce infinately + self.forward() + self.__bouncing = False + if (self.__current_frame < self.frame_count) and (self.__current_frame>0): + self.reverse() + else: + if self.__current_frame == 0: + if self.__loop_count == 0: + self.__done == True + elif self.__loop_count == -1: + # bounce infinatey + self.forward() + else: + self.forward() + self.__loop_count -= 1 + elif self.__current_frame == self.frame_count: + self.reverse() + self.__bouncing = True + else: + self.forward() + + if self.__animation_type == "default": + # loop through from first frame to last, then set done to True + + if self.__current_frame == self.frame_count: + self.__current_frame = 0 + self.__done = True + else: + self.forward() + + @property + def frame_count(self): + """ Returns the total number of frames in the animation """ + return len(self.__frames)-1 + + @property + def done(self): + """ Has the animation completed """ + if self.__done: + self.__done = False + return True + else: + return False + + def loop(self, no:int=None): + """ Loops the animation + if no is None or -1 the animation will continue looping until animate.stop() is called """ + + if no is not None: + self.__loop_count = no + else: + self.__loop_count = -1 + self.__animation_type = "loop" + + def stop(self): + self.__loop_count = 0 + self.__bouncing = False + self.__done = True + + def bounce(self, no:int=None): + """ Loops the animation forwared, then backward, the number of time specified in no, + if there is no number provided it will animate infinately """ + + self.__animation_type = "bouncing" + if no is not None: + self.__loop_count = no + else: + self.__loop_count = -1 + + @property + def width(self): + """ Gets the icon width """ + return self.__width + + @width.setter + def width(self, value): + """ Sets the icon width """ + self.__width = value + + @property + def height(self): + """ Gets the icon height """ + return self.__width + + @height.setter + def height(self, value): + """ Sets the icon height """ + self.__height = value + + +class Button(): + """ Models a button, check the status with is_pressed """ + + # The private variables + __pressed = False + __pin = 0 + __button_down = False + + def __init__(self, pin:int): + """ Sets up the button """ + + self.__pin = Pin(pin, Pin.IN, Pin.PULL_DOWN) + self.__pressed = False + + @property + def is_pressed(self)->bool: + """ Returns the current state of the button """ + + if self.__pin.value() == 0: + self.__button_down = False + return False + if self.__pin.value() == 1: + if not self.__button_down: + # print("button pressed") + self.__button_down = True + return True + else: + return False + +class Event(): + """ Models events that can happen, with timers and pop up messages """ + __name = "" + __value = 0 + __sprite = None + __timer = -1 # -1 means no timer set + __timer_ms = 0 + __callback = None + __message = "" + + def __init__(self, name=None, sprite=None, value=None, callback=None): + """ Create a new event """ + + if name: + self.__name = name + if sprite: + self.__sprite = sprite + if value: + self.__value = value + if callback is not None: + self.__callback = callback + + + @property + def name(self): + """ Return the name of the event""" + + return self.__name + + + @name.setter + def name(self, value): + """ Set the name of the Event""" + + self.__name = value + + + @property + def value(self): + """ Gets the current value """ + return self.__value + + + @value.setter + def value(self, value): + """ Sets the current value """ + self.__value = value + + + @property + def sprite(self): + """ Gets the image sprite """ + + return self.__sprite + + + @sprite.setter + def sprite(self, value): + """ Sets the image sprite """ + + self.__value = value + + + @property + def message(self): + """ Return the message """ + + return self.__message + + + @message.setter + def message(self, value): + """ Set the message """ + self.__message = value + + def popup(self, oled): + # display popup window + # show sprite + # show message + + fbuf = framebuf.FrameBuffer(bytearray(128 * 48 * 1), 128, 48, framebuf.MONO_HLSB) + fbuf.rect(0,0,128,48, 1) + fbuf.blit(self.sprite.image, 5, 10) + fbuf.text(self.message, 32, 18) + oled.blit(fbuf, 0, 16) + oled.show() + sleep(2) + + @property + def timer(self): + """ Gets the current timer value """ + + return self.__timer + + + @timer.setter + def timer(self, value): + """ Sets the current timer value """ + + self.__timer = value + + + @property + def timer_ms(self): + """ Get the timer in MS """ + + return self.__timer_ms + + + @timer_ms.setter + def timer_ms(self, value): + """ Set the timer in MS """ + self.__timer_ms = value + + + def tick(self): + """ Progresses the animation on frame """ + + self.__timer_ms += 1 + if self.__timer_ms >= self.__timer: + if self.__callback is not None: + print("poop check callback") + self.__callback + self.__timer = -1 + self.__timer_ms = 0 + else: + # print("Timer Alert!") + pass + diff --git a/lib/sdcard.py b/lib/sdcard.py new file mode 100644 index 0000000..cea5845 --- /dev/null +++ b/lib/sdcard.py @@ -0,0 +1,309 @@ +""" +MicroPython driver for SD cards using SPI bus. + +Requires an SPI bus and a CS pin. Provides readblocks and writeblocks +methods so the device can be mounted as a filesystem. + +Example usage on pyboard: + + import pyb, sdcard, os + sd = sdcard.SDCard(pyb.SPI(1), pyb.Pin.board.X5) + pyb.mount(sd, '/sd2') + os.listdir('/') + +Example usage on ESP8266: + + import machine, sdcard, os + sd = sdcard.SDCard(machine.SPI(1), machine.Pin(15)) + os.mount(sd, '/sd') + os.listdir('/') + +""" + +from micropython import const +import time + + +_CMD_TIMEOUT = const(100) + +_R1_IDLE_STATE = const(1 << 0) +# R1_ERASE_RESET = const(1 << 1) +_R1_ILLEGAL_COMMAND = const(1 << 2) +# R1_COM_CRC_ERROR = const(1 << 3) +# R1_ERASE_SEQUENCE_ERROR = const(1 << 4) +# R1_ADDRESS_ERROR = const(1 << 5) +# R1_PARAMETER_ERROR = const(1 << 6) +_TOKEN_CMD25 = const(0xFC) +_TOKEN_STOP_TRAN = const(0xFD) +_TOKEN_DATA = const(0xFE) + + +class SDCard: + def __init__(self, spi, cs, baudrate=1320000): + self.spi = spi + self.cs = cs + + self.cmdbuf = bytearray(6) + self.dummybuf = bytearray(512) + self.tokenbuf = bytearray(1) + for i in range(512): + self.dummybuf[i] = 0xFF + self.dummybuf_memoryview = memoryview(self.dummybuf) + + # initialise the card + self.init_card(baudrate) + + def init_spi(self, baudrate): + try: + master = self.spi.MASTER + except AttributeError: + # on ESP8266 + self.spi.init(baudrate=baudrate, phase=0, polarity=0) + else: + # on pyboard + self.spi.init(master, baudrate=baudrate, phase=0, polarity=0) + + def init_card(self, baudrate): + # init CS pin + self.cs.init(self.cs.OUT, value=1) + + # init SPI bus; use low data rate for initialisation + self.init_spi(100000) + + # clock card at least 100 cycles with cs high + for i in range(16): + self.spi.write(b"\xff") + + # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts) + for _ in range(5): + if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE: + break + else: + raise OSError("no SD card") + + # CMD8: determine card version + r = self.cmd(8, 0x01AA, 0x87, 4) + if r == _R1_IDLE_STATE: + self.init_card_v2() + elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND): + self.init_card_v1() + else: + raise OSError("couldn't determine SD card version") + + # get the number of sectors + # CMD9: response R2 (R1 byte + 16-byte block read) + if self.cmd(9, 0, 0, 0, False) != 0: + raise OSError("no response from SD card") + csd = bytearray(16) + self.readinto(csd) + if csd[0] & 0xC0 == 0x40: # CSD version 2.0 + self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024 + elif csd[0] & 0xC0 == 0x00: # CSD version 1.0 (old, <=2GB) + c_size = (csd[6] & 0b11) << 10 | csd[7] << 2 | csd[8] >> 6 + c_size_mult = (csd[9] & 0b11) << 1 | csd[10] >> 7 + read_bl_len = csd[5] & 0b1111 + capacity = (c_size + 1) * (2 ** (c_size_mult + 2)) * (2**read_bl_len) + self.sectors = capacity // 512 + else: + raise OSError("SD card CSD format not supported") + # print('sectors', self.sectors) + + # CMD16: set block length to 512 bytes + if self.cmd(16, 512, 0) != 0: + raise OSError("can't set 512 block size") + + # set to high data rate now that it's initialised + self.init_spi(baudrate) + + def init_card_v1(self): + for i in range(_CMD_TIMEOUT): + time.sleep_ms(50) + self.cmd(55, 0, 0) + if self.cmd(41, 0, 0) == 0: + # SDSC card, uses byte addressing in read/write/erase commands + self.cdv = 512 + # print("[SDCard] v1 card") + return + raise OSError("timeout waiting for v1 card") + + def init_card_v2(self): + for i in range(_CMD_TIMEOUT): + time.sleep_ms(50) + self.cmd(58, 0, 0, 4) + self.cmd(55, 0, 0) + if self.cmd(41, 0x40000000, 0) == 0: + self.cmd(58, 0, 0, -4) # 4-byte response, negative means keep the first byte + ocr = self.tokenbuf[0] # get first byte of response, which is OCR + if not ocr & 0x40: + # SDSC card, uses byte addressing in read/write/erase commands + self.cdv = 512 + else: + # SDHC/SDXC card, uses block addressing in read/write/erase commands + self.cdv = 1 + # print("[SDCard] v2 card") + return + raise OSError("timeout waiting for v2 card") + + def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False): + self.cs(0) + + # create and send the command + buf = self.cmdbuf + buf[0] = 0x40 | cmd + buf[1] = arg >> 24 + buf[2] = arg >> 16 + buf[3] = arg >> 8 + buf[4] = arg + buf[5] = crc + self.spi.write(buf) + + if skip1: + self.spi.readinto(self.tokenbuf, 0xFF) + + # wait for the response (response[7] == 0) + for i in range(_CMD_TIMEOUT): + self.spi.readinto(self.tokenbuf, 0xFF) + response = self.tokenbuf[0] + if not (response & 0x80): + # this could be a big-endian integer that we are getting here + # if final<0 then store the first byte to tokenbuf and discard the rest + if final < 0: + self.spi.readinto(self.tokenbuf, 0xFF) + final = -1 - final + for j in range(final): + self.spi.write(b"\xff") + if release: + self.cs(1) + self.spi.write(b"\xff") + return response + + # timeout + self.cs(1) + self.spi.write(b"\xff") + return -1 + + def readinto(self, buf): + self.cs(0) + + # read until start byte (0xff) + for i in range(_CMD_TIMEOUT): + self.spi.readinto(self.tokenbuf, 0xFF) + if self.tokenbuf[0] == _TOKEN_DATA: + break + time.sleep_ms(1) + else: + self.cs(1) + raise OSError("timeout waiting for response") + + # read data + mv = self.dummybuf_memoryview + if len(buf) != len(mv): + mv = mv[: len(buf)] + self.spi.write_readinto(mv, buf) + + # read checksum + self.spi.write(b"\xff") + self.spi.write(b"\xff") + + self.cs(1) + self.spi.write(b"\xff") + + def write(self, token, buf): + self.cs(0) + + # send: start of block, data, checksum + self.spi.read(1, token) + self.spi.write(buf) + self.spi.write(b"\xff") + self.spi.write(b"\xff") + + # check the response + if (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05: + self.cs(1) + self.spi.write(b"\xff") + return + + # wait for write to finish + while self.spi.read(1, 0xFF)[0] == 0: + pass + + self.cs(1) + self.spi.write(b"\xff") + + def write_token(self, token): + self.cs(0) + self.spi.read(1, token) + self.spi.write(b"\xff") + # wait for write to finish + while self.spi.read(1, 0xFF)[0] == 0x00: + pass + + self.cs(1) + self.spi.write(b"\xff") + + def readblocks(self, block_num, buf): + # workaround for shared bus, required for (at least) some Kingston + # devices, ensure MOSI is high before starting transaction + self.spi.write(b"\xff") + + nblocks = len(buf) // 512 + assert nblocks and not len(buf) % 512, "Buffer length is invalid" + if nblocks == 1: + # CMD17: set read address for single block + if self.cmd(17, block_num * self.cdv, 0, release=False) != 0: + # release the card + self.cs(1) + raise OSError(5) # EIO + # receive the data and release card + self.readinto(buf) + else: + # CMD18: set read address for multiple blocks + if self.cmd(18, block_num * self.cdv, 0, release=False) != 0: + # release the card + self.cs(1) + raise OSError(5) # EIO + offset = 0 + mv = memoryview(buf) + while nblocks: + # receive the data and release card + self.readinto(mv[offset : offset + 512]) + offset += 512 + nblocks -= 1 + if self.cmd(12, 0, 0xFF, skip1=True): + raise OSError(5) # EIO + + def writeblocks(self, block_num, buf): + # workaround for shared bus, required for (at least) some Kingston + # devices, ensure MOSI is high before starting transaction + self.spi.write(b"\xff") + + nblocks, err = divmod(len(buf), 512) + assert nblocks and not err, "Buffer length is invalid" + if nblocks == 1: + # CMD24: set write address for single block + if self.cmd(24, block_num * self.cdv, 0) != 0: + raise OSError(5) # EIO + + # send the data + self.write(_TOKEN_DATA, buf) + else: + # CMD25: set write address for first block + if self.cmd(25, block_num * self.cdv, 0) != 0: + raise OSError(5) # EIO + # send the data + offset = 0 + mv = memoryview(buf) + while nblocks: + self.write(_TOKEN_CMD25, mv[offset : offset + 512]) + offset += 512 + nblocks -= 1 + self.write_token(_TOKEN_STOP_TRAN) + + def ioctl(self, op, arg): + if op == 4: # get number of blocks + return self.sectors + if op == 5: # get block size in bytes + return 512 + + +__version__ = '0.1.0' diff --git a/lightbulb.pbm b/lightbulb.pbm new file mode 100644 index 0000000..91cdea7 Binary files /dev/null and b/lightbulb.pbm differ diff --git a/main.py b/main.py new file mode 100644 index 0000000..98d469b --- /dev/null +++ b/main.py @@ -0,0 +1,270 @@ +# from icons import food_icon +from machine import I2C, Pin +from ssd1306 import SSD1306_I2C +from icon import Animate, Icon, Toolbar, Button, Event +from time import sleep +from sys import exit +import framebuf +import gc + +sda = Pin(0) +scl = Pin(1) +id = 0 + +i2c = I2C(id=id, sda=sda, scl=scl) + +oled = SSD1306_I2C(width=128, height=64, i2c=i2c) +oled.init_display() + +health = 2 +happiness = 1 +energy = 1 + +# load icons +food = Icon('food.pbm', width=16, height=16, name="food") +lightbulb = Icon('lightbulb.pbm', width=16, height=16, name="lightbulb") +game = Icon('game.pbm', width=16, height=16, name="game") +firstaid = Icon('firstaid.pbm', width=16, height=16, name="firstaid") +toilet = Icon('toilet.pbm', width=16, height=16, name="toilet") +heart = Icon('heart.pbm', width=16, height=16, name="heart") +call = Icon('call.pbm', width=16, height=16, name="call") +save = Icon('disc.pbm', width=16, height=16, name="toilet") + +def clear(): + """ Clear the screen """ + oled.fill_rect(0,0,128,64,0) + +# def animate(frames, timer): +# for frame in frames: +# oled.blit(frame.image, frame.x, frame.y) +# oled.show() +# sleep(0.1) + +def build_toolbar(): + toolbar = Toolbar() + toolbar.spacer = 2 + toolbar.additem(food) + toolbar.additem(lightbulb) + toolbar.additem(game) + toolbar.additem(firstaid) + toolbar.additem(toilet) + toolbar.additem(heart) + toolbar.additem(call) + toolbar.additem(save) + return toolbar + +tb = build_toolbar() +poopy = Animate(x=96,y=48, width=16, height=16, filename='poop') +baby = Animate(x=48,y=16, width=48, height=48, filename='baby_bounce', animation_type='bounce') +eat = Animate(x=48,y=16, width=48, height=48, filename='eab: 20t') +babyzzz = Animate(animation_type="loop", x=48,y=16, width=48, height=48, filename='baby_zzz') +death = Animate(animation_type='bounce', x=40,y=16, width=16, height=16, filename="skull") +go_potty = Animate(filename="potty", animation_type='bounce',x=64,y=16, width=48, height=48) +call_animate = Animate(filename='call_animate', width=16, height=16, x=108, y=0) +call_animate.speed = 'very slow' + +#button_jleft = Button(20) +#button_jright = Button(19) +#button_jup = Button(22) +#button_jdown = Button(21) +#button_jmid = Button(18) +#button_jset = Button(17) +#button_jrst = Button(16) + +class ButtonConfig: + Configurations = { + "default":{ + "left":20, + "right":19, + "action":18, + "cancel":16 + }, + "set1":{ + "left":20, + "right":19, + "action":18 + } + } + __ActiveConfig={} + + def __init__(self, set="default"): + self.__ActiveConfig=self.Configurations[set] + + @property + def ActiveConfig(self): + return self.__ActiveConfig + + +class controller: + PinObjects={} + + def __init__(self,config): + for button in config.ActiveConfig: + self.PinObjects[button]=Pin(config.ActiveConfig[button]) + +ButtonSetup=ButtonConfig() +InputDevice=controller(ButtonSetup) + +index = 3 +tb.select(index, oled) +cancel = False +feeding_time = False +sleeping = False +death.set = True + +# Set up Events +energy_increase = Event(name="Increase Energy", sprite=heart, value=1) +firstaid = Event(name="First Aid", sprite=firstaid, value=0) +toilet = Event(name="Toilet", sprite=toilet, value=0) +greatgame = Event(name="Gaming", sprite=game, value=0) +# poop_event = Event(name="poop time", sprite=poop_sprite, callback=poop_check()) +sleep_time = Event(name="sleep time", sprite=lightbulb, value=1) +heart_status = Event(name="Status", sprite=heart) +# poop_event.timer = 3 +# poop_event.timer_ms = 1 + +baby.bounce() +poopy.bounce() +death.loop(no=-1) +death.speed='slow' +babyzzz.speed = 'very slow' +go_potty.loop(no=1) +go_potty.set = True +poopy.set = False +go_potty.load() + +while True: + if not cancel: + tb.unselect(index, oled) + if InputDevice.PinObjects["left"].value(): + index -= 1 + if index < 0: + index = tb.getlength()-1 + cancel = False + if InputDevice.PinObjects["right"].value(): + index += 1 + if index == tb.getlength(): + index = 0 + cancel = False + if InputDevice.PinObjects["cancel"].value(): + cancel = True + index = -1 + + if not cancel: + tb.select(index, oled) + + if InputDevice.PinObjects["action"].value(): + if tb.selected_item == "food": + feeding_time = True + sleeping = False + baby.unload() + + if tb.selected_item == "game": + greatgame.message = "Gaming!!!" + greatgame.popup(oled=oled) + happiness += 1 + energy -= 1 + greatgame.message = "happy = " + str(happiness) + greatgame.popup(oled) + greatgame.message = "energy = " + str(energy) + greatgame.popup(oled) + clear() + print("game:\n energy = " + str(energy)+"\n happy = " + str(happiness)) + if tb.selected_item == "toilet": + toilet.message = "Cleaning..." + toilet.popup(oled=oled) + poopy.set = False + baby.set = True + happiness += 1 + clear() + poopy.unload() + if tb.selected_item == "lightbulb": + if not sleeping: + sleeping = True + babyzzz.load() + sleep_time.message = "Night Night" + sleep_time.popup(oled) + clear() + # need to add an event that increases energy level after sleeping for 1 minute + else: + sleeping = False + babyzzz.unload() + print("lightbulb") + if tb.selected_item == "firstaid": + firstaid.message = "Vitamins" + firstaid.popup(oled=oled) + health += 1 + + clear() + if tb.selected_item == "heart": + heart_status.message = "health = " + str(health) + heart_status.popup(oled) + heart_status.message = "happy = " + str(happiness) + heart_status.popup(oled) + heart_status.message = "energy = " + str(energy) + heart_status.popup(oled) + clear() + if tb.selected_item == "call": + # call_animate.animate(oled) + # call_animate.set = False + print("call") + + # Time for Poop? + # poop_check() + # poop_event.tick() + + if feeding_time: + eat.load() + if not eat.done: + eat.animate(oled) + if feeding_time and eat.done: + feeding_time = False + energy_increase.message = "ENERGY + 1" + energy_increase.popup(oled=oled) + energy += 1 + + clear() + eat.unload() + baby.load() + else: + if sleeping: + babyzzz.animate(oled) + else: + if baby.set: + baby.load() + baby.animate(oled) + if go_potty.set: + go_potty.animate(oled) + if go_potty.done: + print("potty done") + go_potty.set = False + poopy.set = True + baby.load() + baby.bounce(no=-1) + baby.set = True + if (energy <= 1) and (happiness <= 1) and (health <=1): + death.set = True + else: + death.set = False + + #if (energy <= 1) or (happiness <= 1) or (health <= 1): + # set the toolbar call icon to flash + # call_animate.set = True + #else: + # call_animate.set = False + + if poopy.set: + poopy.load() + poopy.animate(oled) + if death.set: + death.animate(oled) + tb.show(oled) + if index == 6: + tb.select(index, oled) + #else: + # if call_animate.set: + # call_animate.animate(oled) + + oled.show() + sleep(0.05) + \ No newline at end of file diff --git a/poop01.pbm b/poop01.pbm new file mode 100644 index 0000000..a97abc9 Binary files /dev/null and b/poop01.pbm differ diff --git a/poop02.pbm b/poop02.pbm new file mode 100644 index 0000000..884be48 Binary files /dev/null and b/poop02.pbm differ diff --git a/poop03.pbm b/poop03.pbm new file mode 100644 index 0000000..a9b9b83 Binary files /dev/null and b/poop03.pbm differ diff --git a/poop04.pbm b/poop04.pbm new file mode 100644 index 0000000..532164d Binary files /dev/null and b/poop04.pbm differ diff --git a/potty01.pbm b/potty01.pbm new file mode 100644 index 0000000..0b78bcb Binary files /dev/null and b/potty01.pbm differ diff --git a/potty02.pbm b/potty02.pbm new file mode 100644 index 0000000..29e3843 Binary files /dev/null and b/potty02.pbm differ diff --git a/potty03.pbm b/potty03.pbm new file mode 100644 index 0000000..fd50091 Binary files /dev/null and b/potty03.pbm differ diff --git a/potty04.pbm b/potty04.pbm new file mode 100644 index 0000000..22df073 Binary files /dev/null and b/potty04.pbm differ diff --git a/potty05.pbm b/potty05.pbm new file mode 100644 index 0000000..0c7ea60 Binary files /dev/null and b/potty05.pbm differ diff --git a/potty06.pbm b/potty06.pbm new file mode 100644 index 0000000..a0401d4 Binary files /dev/null and b/potty06.pbm differ diff --git a/potty07.pbm b/potty07.pbm new file mode 100644 index 0000000..ef073fb Binary files /dev/null and b/potty07.pbm differ diff --git a/potty08.pbm b/potty08.pbm new file mode 100644 index 0000000..17b090d Binary files /dev/null and b/potty08.pbm differ diff --git a/potty09.pbm b/potty09.pbm new file mode 100644 index 0000000..c1e47f4 Binary files /dev/null and b/potty09.pbm differ diff --git a/potty10.pbm b/potty10.pbm new file mode 100644 index 0000000..d3e706c Binary files /dev/null and b/potty10.pbm differ diff --git a/potty11.pbm b/potty11.pbm new file mode 100644 index 0000000..499d2ce Binary files /dev/null and b/potty11.pbm differ diff --git a/potty12.pbm b/potty12.pbm new file mode 100644 index 0000000..02f5af3 Binary files /dev/null and b/potty12.pbm differ diff --git a/potty13.pbm b/potty13.pbm new file mode 100644 index 0000000..2f9da33 Binary files /dev/null and b/potty13.pbm differ diff --git a/potty14.pbm b/potty14.pbm new file mode 100644 index 0000000..ccb7c91 Binary files /dev/null and b/potty14.pbm differ diff --git a/raw_assets/baby_zzz01.xcf b/raw_assets/baby_zzz01.xcf new file mode 100644 index 0000000..f5106d2 Binary files /dev/null and b/raw_assets/baby_zzz01.xcf differ diff --git a/raw_assets/baby_zzz02.xcf b/raw_assets/baby_zzz02.xcf new file mode 100644 index 0000000..a8904a6 Binary files /dev/null and b/raw_assets/baby_zzz02.xcf differ diff --git a/raw_assets/baby_zzz03.xcf b/raw_assets/baby_zzz03.xcf new file mode 100644 index 0000000..d68080e Binary files /dev/null and b/raw_assets/baby_zzz03.xcf differ diff --git a/raw_assets/baby_zzz04.xcf b/raw_assets/baby_zzz04.xcf new file mode 100644 index 0000000..cc44139 Binary files /dev/null and b/raw_assets/baby_zzz04.xcf differ diff --git a/raw_assets/baby_zzz05.xcf b/raw_assets/baby_zzz05.xcf new file mode 100644 index 0000000..154b80d Binary files /dev/null and b/raw_assets/baby_zzz05.xcf differ diff --git a/raw_assets/bubble.xcf b/raw_assets/bubble.xcf new file mode 100644 index 0000000..13fa5e9 Binary files /dev/null and b/raw_assets/bubble.xcf differ diff --git a/raw_assets/call_animate01.xcf b/raw_assets/call_animate01.xcf new file mode 100644 index 0000000..13dec51 Binary files /dev/null and b/raw_assets/call_animate01.xcf differ diff --git a/raw_assets/call_animate02.xcf b/raw_assets/call_animate02.xcf new file mode 100644 index 0000000..8e93c11 Binary files /dev/null and b/raw_assets/call_animate02.xcf differ diff --git a/raw_assets/call_animate03.xcf b/raw_assets/call_animate03.xcf new file mode 100644 index 0000000..4aa82e1 Binary files /dev/null and b/raw_assets/call_animate03.xcf differ diff --git a/raw_assets/potty01.xcf b/raw_assets/potty01.xcf new file mode 100644 index 0000000..60c4227 Binary files /dev/null and b/raw_assets/potty01.xcf differ diff --git a/raw_assets/potty02.xcf b/raw_assets/potty02.xcf new file mode 100644 index 0000000..e22286f Binary files /dev/null and b/raw_assets/potty02.xcf differ diff --git a/raw_assets/potty03.xcf b/raw_assets/potty03.xcf new file mode 100644 index 0000000..804bbeb Binary files /dev/null and b/raw_assets/potty03.xcf differ diff --git a/raw_assets/potty04.xcf b/raw_assets/potty04.xcf new file mode 100644 index 0000000..ddebf73 Binary files /dev/null and b/raw_assets/potty04.xcf differ diff --git a/raw_assets/potty05.xcf b/raw_assets/potty05.xcf new file mode 100644 index 0000000..f1ac2ba Binary files /dev/null and b/raw_assets/potty05.xcf differ diff --git a/raw_assets/potty06.xcf b/raw_assets/potty06.xcf new file mode 100644 index 0000000..b904a0c Binary files /dev/null and b/raw_assets/potty06.xcf differ diff --git a/raw_assets/potty07.xcf b/raw_assets/potty07.xcf new file mode 100644 index 0000000..eacb424 Binary files /dev/null and b/raw_assets/potty07.xcf differ diff --git a/raw_assets/potty08.xcf b/raw_assets/potty08.xcf new file mode 100644 index 0000000..5f7ffff Binary files /dev/null and b/raw_assets/potty08.xcf differ diff --git a/raw_assets/potty09.xcf b/raw_assets/potty09.xcf new file mode 100644 index 0000000..6317d4e Binary files /dev/null and b/raw_assets/potty09.xcf differ diff --git a/raw_assets/potty10.xcf b/raw_assets/potty10.xcf new file mode 100644 index 0000000..b0cabf5 Binary files /dev/null and b/raw_assets/potty10.xcf differ diff --git a/raw_assets/potty11.xcf b/raw_assets/potty11.xcf new file mode 100644 index 0000000..56cc6ed Binary files /dev/null and b/raw_assets/potty11.xcf differ diff --git a/raw_assets/potty12.xcf b/raw_assets/potty12.xcf new file mode 100644 index 0000000..671a59b Binary files /dev/null and b/raw_assets/potty12.xcf differ diff --git a/raw_assets/skull01.xcf b/raw_assets/skull01.xcf new file mode 100644 index 0000000..a9595cc Binary files /dev/null and b/raw_assets/skull01.xcf differ diff --git a/raw_assets/skull02.xcf b/raw_assets/skull02.xcf new file mode 100644 index 0000000..9e3a7c2 Binary files /dev/null and b/raw_assets/skull02.xcf differ diff --git a/sdcardtest.py b/sdcardtest.py new file mode 100644 index 0000000..6729547 --- /dev/null +++ b/sdcardtest.py @@ -0,0 +1,104 @@ +import machine +import sdcard +import uos +import json + +mytestdata = { + "health":11, + "happiness":1, + "energy":1 + } + +# Create a file and write something to it +#with open("/sd/test01.txt", "w") as file: +# file.write(json.dumps(mytestdata)) + +# Open the file we just created and read from it +#with open("/sd/test01.txt", "r") as file: +# data = file.read() +# print(data) +# varx =json.loads(data) +# print(varx["health"]) + +class SaveGameManager: + __SaveGameData = {} + __FileSocket = None + __cs = None + __spi = None + __sd = None + + def __init__(self, savename="01"): + print("initializing") + try: + # Assign chip select (CS) pin (and start it high) + self.__cs = machine.Pin(9, machine.Pin.OUT) + + # Intialize SPI peripheral (start with 1 MHz) + self.__spi = machine.SPI(1, + baudrate=1000000, + polarity=0, + phase=0, + bits=8, + firstbit=machine.SPI.MSB, + sck=machine.Pin(10), + mosi=machine.Pin(11), + miso=machine.Pin(8)) + + # Initialize SD card + self.__sd = sdcard.SDCard(self.__spi, self.__cs) + + # Mount filesystem + self.__FileSocket = uos.VfsFat(self.__sd) + uos.mount(self.__FileSocket, "/sd") + + #load values from FS + self.LoadSaveData() + except: + pass + + + + @property + def CheckFilesystemReady(self): + if self.__FileSocket is None: + print("No FileSocket") + return False + return True + + def LoadSaveData(self): + print("loading") + # Open the file we just created and read from it + if not self.CheckFilesystemReady: + return # If we have no filesystem we cannot write or read anyways... + + with open("/sd/test00.txt", "rb") as file: + file.read(1) #ignore first byte, it containes the version number as binary... later :) + data = file.read() + self.__SaveGameData = json.loads(data) + + def WriteSaveData(self): + if not self.CheckFilesystemReady: + pass # If we have no filesystem we cannot write or read anyways... + # Create a file and write something to it + with open("/sd/test01.txt", "wb") as file: + file.write("0") + file.write(json.dumps(self.__SaveGameData)) + + def UpdateData(self, key, value): + print("updating internal data") + self.__SaveGameData[key]=value + + def GetData(self, key, defaultvalue=""): + print("getting internaldata") + if key in self.__SaveGameData: + return self.__SaveGameData + return defaultvalue + + +mysavegame = SaveGameManager() +mysavegame.UpdateData("bla","blub") +mysavegame.WriteSaveData() +mysavegame.LoadSaveData() +print(mysavegame.GetData("bla")) +print("x:"+mysavegame.GetData("something")) +print("x:"+mysavegame.GetData("something","defaultthing")) \ No newline at end of file diff --git a/skull01.pbm b/skull01.pbm new file mode 100644 index 0000000..b860ad3 --- /dev/null +++ b/skull01.pbm @@ -0,0 +1,4 @@ +P4 +# Created by GIMP version 2.10.22 PNM plug-in +16 16 +ð?üþÿÿÿÿÿÿá‡ÁƒÃÃÿÿþþþ?ü ° ° \ No newline at end of file diff --git a/skull02.pbm b/skull02.pbm new file mode 100644 index 0000000..219ddb7 --- /dev/null +++ b/skull02.pbm @@ -0,0 +1,4 @@ +P4 +# Created by GIMP version 2.10.22 PNM plug-in +16 16 +ð?üþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþ?ü ° ° \ No newline at end of file diff --git a/ssd1306.py b/ssd1306.py new file mode 100644 index 0000000..1bb6679 --- /dev/null +++ b/ssd1306.py @@ -0,0 +1,155 @@ +# MicroPython SSD1306 OLED driver, I2C and SPI interfaces + +from micropython import const +import framebuf + + +# register definitions +SET_CONTRAST = const(0x81) +SET_ENTIRE_ON = const(0xA4) +SET_NORM_INV = const(0xA6) +SET_DISP = const(0xAE) +SET_MEM_ADDR = const(0x20) +SET_COL_ADDR = const(0x21) +SET_PAGE_ADDR = const(0x22) +SET_DISP_START_LINE = const(0x40) +SET_SEG_REMAP = const(0xA0) +SET_MUX_RATIO = const(0xA8) +SET_COM_OUT_DIR = const(0xC0) +SET_DISP_OFFSET = const(0xD3) +SET_COM_PIN_CFG = const(0xDA) +SET_DISP_CLK_DIV = const(0xD5) +SET_PRECHARGE = const(0xD9) +SET_VCOM_DESEL = const(0xDB) +SET_CHARGE_PUMP = const(0x8D) + +# Subclassing FrameBuffer provides support for graphics primitives +# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html +class SSD1306(framebuf.FrameBuffer): + def __init__(self, width, height, external_vcc): + self.width = width + self.height = height + self.external_vcc = external_vcc + self.pages = self.height // 8 + self.buffer = bytearray(self.pages * self.width) + super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB) + self.init_display() + + def init_display(self): + for cmd in ( + SET_DISP | 0x00, # off + # address setting + SET_MEM_ADDR, + 0x00, # horizontal + # resolution and layout + SET_DISP_START_LINE | 0x00, + SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 + SET_MUX_RATIO, + self.height - 1, + SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 + SET_DISP_OFFSET, + 0x00, + SET_COM_PIN_CFG, + 0x02 if self.width > 2 * self.height else 0x12, + # timing and driving scheme + SET_DISP_CLK_DIV, + 0x80, + SET_PRECHARGE, + 0x22 if self.external_vcc else 0xF1, + SET_VCOM_DESEL, + 0x30, # 0.83*Vcc + # display + SET_CONTRAST, + 0xFF, # maximum + SET_ENTIRE_ON, # output follows RAM contents + SET_NORM_INV, # not inverted + # charge pump + SET_CHARGE_PUMP, + 0x10 if self.external_vcc else 0x14, + SET_DISP | 0x01, + ): # on + self.write_cmd(cmd) + self.fill(0) + self.show() + + def poweroff(self): + self.write_cmd(SET_DISP | 0x00) + + def poweron(self): + self.write_cmd(SET_DISP | 0x01) + + def contrast(self, contrast): + self.write_cmd(SET_CONTRAST) + self.write_cmd(contrast) + + def invert(self, invert): + self.write_cmd(SET_NORM_INV | (invert & 1)) + + def show(self): + x0 = 0 + x1 = self.width - 1 + if self.width == 64: + # displays with width of 64 pixels are shifted by 32 + x0 += 32 + x1 += 32 + self.write_cmd(SET_COL_ADDR) + self.write_cmd(x0) + self.write_cmd(x1) + self.write_cmd(SET_PAGE_ADDR) + self.write_cmd(0) + self.write_cmd(self.pages - 1) + self.write_data(self.buffer) + + +class SSD1306_I2C(SSD1306): + def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False): + self.i2c = i2c + self.addr = addr + self.temp = bytearray(2) + self.write_list = [b"\x40", None] # Co=0, D/C#=1 + super().__init__(width, height, external_vcc) + + def write_cmd(self, cmd): + self.temp[0] = 0x80 # Co=1, D/C#=0 + self.temp[1] = cmd + self.i2c.writeto(self.addr, self.temp) + + def write_data(self, buf): + self.write_list[1] = buf + self.i2c.writevto(self.addr, self.write_list) + + +class SSD1306_SPI(SSD1306): + def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): + self.rate = 10 * 1024 * 1024 + dc.init(dc.OUT, value=0) + res.init(res.OUT, value=0) + cs.init(cs.OUT, value=1) + self.spi = spi + self.dc = dc + self.res = res + self.cs = cs + import time + + self.res(1) + time.sleep_ms(1) + self.res(0) + time.sleep_ms(10) + self.res(1) + super().__init__(width, height, external_vcc) + + def write_cmd(self, cmd): + self.spi.init(baudrate=self.rate, polarity=0, phase=0) + self.cs(1) + self.dc(0) + self.cs(0) + self.spi.write(bytearray([cmd])) + self.cs(1) + + def write_data(self, buf): + self.spi.init(baudrate=self.rate, polarity=0, phase=0) + self.cs(1) + self.dc(1) + self.cs(0) + self.spi.write(buf) + self.cs(1) \ No newline at end of file diff --git a/toilet.pbm b/toilet.pbm new file mode 100644 index 0000000..07efcb3 Binary files /dev/null and b/toilet.pbm differ