--- /dev/null
+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
--- /dev/null
+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)
--- /dev/null
+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
+
--- /dev/null
+"""
+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'
--- /dev/null
+# 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
--- /dev/null
+import machine\r
+import sdcard\r
+import uos\r
+import json\r
+\r
+mytestdata = {\r
+ "health":11,\r
+ "happiness":1,\r
+ "energy":1\r
+ }\r
+\r
+# Create a file and write something to it\r
+#with open("/sd/test01.txt", "w") as file:\r
+# file.write(json.dumps(mytestdata))\r
+\r
+# Open the file we just created and read from it\r
+#with open("/sd/test01.txt", "r") as file:\r
+# data = file.read()\r
+# print(data)\r
+# varx =json.loads(data)\r
+# print(varx["health"])\r
+ \r
+class SaveGameManager:\r
+ __SaveGameData = {}\r
+ __FileSocket = None\r
+ __cs = None\r
+ __spi = None\r
+ __sd = None\r
+ \r
+ def __init__(self, savename="01"):\r
+ print("initializing")\r
+ try:\r
+ # Assign chip select (CS) pin (and start it high)\r
+ self.__cs = machine.Pin(9, machine.Pin.OUT)\r
+\r
+ # Intialize SPI peripheral (start with 1 MHz)\r
+ self.__spi = machine.SPI(1,\r
+ baudrate=1000000,\r
+ polarity=0,\r
+ phase=0,\r
+ bits=8,\r
+ firstbit=machine.SPI.MSB,\r
+ sck=machine.Pin(10),\r
+ mosi=machine.Pin(11),\r
+ miso=machine.Pin(8))\r
+\r
+ # Initialize SD card\r
+ self.__sd = sdcard.SDCard(self.__spi, self.__cs)\r
+\r
+ # Mount filesystem\r
+ self.__FileSocket = uos.VfsFat(self.__sd)\r
+ uos.mount(self.__FileSocket, "/sd")\r
+ \r
+ #load values from FS\r
+ self.LoadSaveData()\r
+ except:\r
+ pass\r
+\r
+ \r
+ \r
+ @property\r
+ def CheckFilesystemReady(self):\r
+ if self.__FileSocket is None:\r
+ print("No FileSocket")\r
+ return False\r
+ return True \r
+\r
+ def LoadSaveData(self):\r
+ print("loading")\r
+ # Open the file we just created and read from it\r
+ if not self.CheckFilesystemReady:\r
+ return # If we have no filesystem we cannot write or read anyways...\r
+ \r
+ with open("/sd/test00.txt", "rb") as file:\r
+ file.read(1) #ignore first byte, it containes the version number as binary... later :)\r
+ data = file.read()\r
+ self.__SaveGameData = json.loads(data)\r
+ \r
+ def WriteSaveData(self):\r
+ if not self.CheckFilesystemReady:\r
+ pass # If we have no filesystem we cannot write or read anyways...\r
+ # Create a file and write something to it\r
+ with open("/sd/test01.txt", "wb") as file:\r
+ file.write("0")\r
+ file.write(json.dumps(self.__SaveGameData))\r
+ \r
+ def UpdateData(self, key, value):\r
+ print("updating internal data")\r
+ self.__SaveGameData[key]=value\r
+ \r
+ def GetData(self, key, defaultvalue=""):\r
+ print("getting internaldata")\r
+ if key in self.__SaveGameData:\r
+ return self.__SaveGameData \r
+ return defaultvalue\r
+ \r
+ \r
+mysavegame = SaveGameManager()\r
+mysavegame.UpdateData("bla","blub")\r
+mysavegame.WriteSaveData()\r
+mysavegame.LoadSaveData()\r
+print(mysavegame.GetData("bla"))\r
+print("x:"+mysavegame.GetData("something"))\r
+print("x:"+mysavegame.GetData("something","defaultthing"))
\ No newline at end of file
--- /dev/null
+P4
+# Created by GIMP version 2.10.22 PNM plug-in
+16 16
+\ fð?ü\7fþÿÿÿÿÿÿá\87Á\83ÃÃÿÿþ\7fþ\7f\7fþ?ü\r°\r°
\ No newline at end of file
--- /dev/null
+P4
+# Created by GIMP version 2.10.22 PNM plug-in
+16 16
+\ fð?ü\7fþÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ\7fþ\7f\7fþ?ü\r°\r°
\ No newline at end of file
--- /dev/null
+# MicroPython SSD1306 OLED driver, I2C and SPI interfaces\r
+\r
+from micropython import const\r
+import framebuf\r
+\r
+\r
+# register definitions\r
+SET_CONTRAST = const(0x81)\r
+SET_ENTIRE_ON = const(0xA4)\r
+SET_NORM_INV = const(0xA6)\r
+SET_DISP = const(0xAE)\r
+SET_MEM_ADDR = const(0x20)\r
+SET_COL_ADDR = const(0x21)\r
+SET_PAGE_ADDR = const(0x22)\r
+SET_DISP_START_LINE = const(0x40)\r
+SET_SEG_REMAP = const(0xA0)\r
+SET_MUX_RATIO = const(0xA8)\r
+SET_COM_OUT_DIR = const(0xC0)\r
+SET_DISP_OFFSET = const(0xD3)\r
+SET_COM_PIN_CFG = const(0xDA)\r
+SET_DISP_CLK_DIV = const(0xD5)\r
+SET_PRECHARGE = const(0xD9)\r
+SET_VCOM_DESEL = const(0xDB)\r
+SET_CHARGE_PUMP = const(0x8D)\r
+\r
+# Subclassing FrameBuffer provides support for graphics primitives\r
+# http://docs.micropython.org/en/latest/pyboard/library/framebuf.html\r
+class SSD1306(framebuf.FrameBuffer):\r
+ def __init__(self, width, height, external_vcc):\r
+ self.width = width\r
+ self.height = height\r
+ self.external_vcc = external_vcc\r
+ self.pages = self.height // 8\r
+ self.buffer = bytearray(self.pages * self.width)\r
+ super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)\r
+ self.init_display()\r
+\r
+ def init_display(self):\r
+ for cmd in (\r
+ SET_DISP | 0x00, # off\r
+ # address setting\r
+ SET_MEM_ADDR,\r
+ 0x00, # horizontal\r
+ # resolution and layout\r
+ SET_DISP_START_LINE | 0x00,\r
+ SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0\r
+ SET_MUX_RATIO,\r
+ self.height - 1,\r
+ SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0\r
+ SET_DISP_OFFSET,\r
+ 0x00,\r
+ SET_COM_PIN_CFG,\r
+ 0x02 if self.width > 2 * self.height else 0x12,\r
+ # timing and driving scheme\r
+ SET_DISP_CLK_DIV,\r
+ 0x80,\r
+ SET_PRECHARGE,\r
+ 0x22 if self.external_vcc else 0xF1,\r
+ SET_VCOM_DESEL,\r
+ 0x30, # 0.83*Vcc\r
+ # display\r
+ SET_CONTRAST,\r
+ 0xFF, # maximum\r
+ SET_ENTIRE_ON, # output follows RAM contents\r
+ SET_NORM_INV, # not inverted\r
+ # charge pump\r
+ SET_CHARGE_PUMP,\r
+ 0x10 if self.external_vcc else 0x14,\r
+ SET_DISP | 0x01,\r
+ ): # on\r
+ self.write_cmd(cmd)\r
+ self.fill(0)\r
+ self.show()\r
+\r
+ def poweroff(self):\r
+ self.write_cmd(SET_DISP | 0x00)\r
+\r
+ def poweron(self):\r
+ self.write_cmd(SET_DISP | 0x01)\r
+\r
+ def contrast(self, contrast):\r
+ self.write_cmd(SET_CONTRAST)\r
+ self.write_cmd(contrast)\r
+\r
+ def invert(self, invert):\r
+ self.write_cmd(SET_NORM_INV | (invert & 1))\r
+\r
+ def show(self):\r
+ x0 = 0\r
+ x1 = self.width - 1\r
+ if self.width == 64:\r
+ # displays with width of 64 pixels are shifted by 32\r
+ x0 += 32\r
+ x1 += 32\r
+ self.write_cmd(SET_COL_ADDR)\r
+ self.write_cmd(x0)\r
+ self.write_cmd(x1)\r
+ self.write_cmd(SET_PAGE_ADDR)\r
+ self.write_cmd(0)\r
+ self.write_cmd(self.pages - 1)\r
+ self.write_data(self.buffer)\r
+\r
+\r
+class SSD1306_I2C(SSD1306):\r
+ def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):\r
+ self.i2c = i2c\r
+ self.addr = addr\r
+ self.temp = bytearray(2)\r
+ self.write_list = [b"\x40", None] # Co=0, D/C#=1\r
+ super().__init__(width, height, external_vcc)\r
+\r
+ def write_cmd(self, cmd):\r
+ self.temp[0] = 0x80 # Co=1, D/C#=0\r
+ self.temp[1] = cmd\r
+ self.i2c.writeto(self.addr, self.temp)\r
+\r
+ def write_data(self, buf):\r
+ self.write_list[1] = buf\r
+ self.i2c.writevto(self.addr, self.write_list)\r
+\r
+\r
+class SSD1306_SPI(SSD1306):\r
+ def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):\r
+ self.rate = 10 * 1024 * 1024\r
+ dc.init(dc.OUT, value=0)\r
+ res.init(res.OUT, value=0)\r
+ cs.init(cs.OUT, value=1)\r
+ self.spi = spi\r
+ self.dc = dc\r
+ self.res = res\r
+ self.cs = cs\r
+ import time\r
+\r
+ self.res(1)\r
+ time.sleep_ms(1)\r
+ self.res(0)\r
+ time.sleep_ms(10)\r
+ self.res(1)\r
+ super().__init__(width, height, external_vcc)\r
+\r
+ def write_cmd(self, cmd):\r
+ self.spi.init(baudrate=self.rate, polarity=0, phase=0)\r
+ self.cs(1)\r
+ self.dc(0)\r
+ self.cs(0)\r
+ self.spi.write(bytearray([cmd]))\r
+ self.cs(1)\r
+\r
+ def write_data(self, buf):\r
+ self.spi.init(baudrate=self.rate, polarity=0, phase=0)\r
+ self.cs(1)\r
+ self.dc(1)\r
+ self.cs(0)\r
+ self.spi.write(buf)\r
+ self.cs(1)
\ No newline at end of file