Example #1
0
    def draw_text8x8(self, x, y, text, color, landscape=False):
        """Draw text using built-in MicroPython 8x8 bit font.

        Args:
            x (int): Starting X position.
            y (int): Starting Y position.
            text (string): Text to draw.
            color (int): RGB565 color value.
            landscape (bool): Orientation (default: False = portrait)
        """
        text_length = len(text) * 8
        # Confirm coordinates in boundary
        if self.is_off_grid(x, y, x + 7, y + 7):
            return
        # Rearrange color
        r = (color & 0xF800) >> 8
        g = (color & 0x07E0) >> 3
        b = (color & 0x1F) << 3
        buf = bytearray(text_length * 16)
        fbuf = FrameBuffer(buf, text_length, 8, RGB565)
        fbuf.text(text, 0, 0, color565(b, r, g))
        if landscape:
            self.write_cmd(self.SET_REMAP, 0x77)  # Vertical address reverse
            self.block(self.width - (x + 8), y,
                       (self.width - (x + 8)) + 7, y + text_length - 1, buf)
            self.write_cmd(self.SET_REMAP, 0x74)  # Switch back to horizontal
        else:
            self.block(x, y, x + text_length - 1, y + 7, buf)
Example #2
0
def draw_icon(oled_display_obj: framebuf.FrameBuffer,
              icon_bytes: bytes,
              position_x: int,
              position_y: int,
              show: bool = False):
    width = int.from_bytes(icon_bytes[:2], 'big')
    height = int.from_bytes(icon_bytes[2:4], 'big')

    y = 0
    x = 0
    for m_byte in icon_bytes[4:]:
        for byte_index in range(8):
            pixel_value = m_byte & (1 << byte_index)
            oled_display_obj.pixel(position_x + x, position_y + y, pixel_value)

            x = x + 1

            if x >= width:
                x = 0
                y = y + 1

            if y >= height:
                if show:
                    oled_display_obj.show()
                return
Example #3
0
 def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False):
     self.i2c = i2c
     self.addr = addr
     self.temp = bytearray(2)
     self.width = width
     self.height = height
     self.external_vcc = external_vcc
     self.pages = self.height // 8
     self.buffer = bytearray(self.pages * self.width)
     from framebuf import FrameBuffer, MVLSB
     self.framebuf = FrameBuffer(self.buffer, self.width, self.height,
                                 MVLSB)
     self.init_display()
Example #4
0
 def scroll(self, s, c=0xFFFF, delay_ms=100):
     """ Make a text scrolling on the screen with the c color. delay_ms is the time between 2 successive frames """
     _w = (len(s) + 2) * 8
     _buf = bytearray(
         (len(s) + 2) * 8
     )  # 8 columns * 1 byte (8 rows) needed to display a char (each char = 8x8 pixels)
     _fb = FrameBuffer(_buf, _w, 8, MONO_VLSB)
     _fb.text(" %s " % s, 0, 0, 1)
     for cols in range(_w - 8):
         for x in range(8):
             for y in range(8):
                 self.pixel(x, y, c if _buf[cols + x] & (0x1 << y) else 0x0)
         self.update()
         time.sleep_ms(delay_ms)
     del (_fb)
     del (_buf)
Example #5
0
 def _pbm_decode(self, img_arrays):
     next_value = bytearray()
     pnm_header = []
     stat = True
     index = 3
     while stat:
         next_byte = bytes([img_arrays[index]])
         if next_byte == b"#":
             while bytes([img_arrays[index]]) not in [b"", b"\n"]:
                 index += 1
         if not next_byte.isdigit():
             if next_value:
                 pnm_header.append(
                     int("".join(["%c" % char for char in next_value])))
                 next_value = bytearray()
         else:
             next_value += next_byte
         if len(pnm_header) == 2:
             stat = False
         index += 1
     pixel_arrays = img_arrays[index:]
     if self.invert == 1:
         for i in range(len(pixel_arrays)):
             pixel_arrays[i] = (~pixel_arrays[i]) & 0xff
     return FrameBuffer(pixel_arrays, pnm_header[0], pnm_header[1], 3)
Example #6
0
    def __init__(self, filename, display):
        with MagickWand() as wand:
            wand.read_image(filename)

            # make the image fit
            img_w, img_h = wand.image_width, wand.image_height
            disp_w, disp_h = display._screen.width, display._screen.height
            if img_w < disp_w and img_h < disp_h:
                # if the image is smaller than the screen, extend it with a
                # white border
                # TODO: if the screen is >= 2x the image, double the image size
                # instead of adding a border
                x = -(disp_w - img_w) // 2
                y = -(disp_h - img_h) // 2
                wand.extent_image(disp_w, disp_h, x, y)
            elif img_w > disp_w and img_h > disp_h:
                # FIXME: figure out which resize method is best and shrink the
                # image
                pass

            # convert to native format (1bpp)
            # TODO: if source image is color, we should dither or make dither
            # an optional parameter
            wand.image_format = 'GRAY'
            wand.image_depth = 1

            # then convert to micropython framebuf so we can blit
            data = wand.image_blob
            self._framebuf = FrameBuffer(data, wand.image_width,
                                         wand.image_height, MONO_HLSB)
 def init_fb(self):
     if self.buffer is None:
         self.buffer = bytearray(self.width * self.height * 2)
     if self.fb is None:
         self.fb = FrameBuffer(self.buffer, self.width, self.height,
                               framebuf.RGB565)  # noqa: E501
     return self.fb
 def __init__(self, spi, cs=14, dc=27, rst=33, bl=32):
     self.buffer = bytearray(32)
     self.letter = FrameBuffer(bytearray(8), 8, 8, MONO_HLSB)
     self.spi = spi
     self.cs = Pin(cs, Pin.OUT)
     self.dc = Pin(dc, Pin.OUT)
     self.rst = Pin(rst, Pin.OUT)
     self.bl = Pin(bl, Pin.OUT)
     self.width = 320
     self.height = 240
     self.char_width = 16
     self.char_height = 16
     self.offset = 0
     self.background = color565(0, 0, 0)
     self._reset()
     self._setup()
Example #9
0
def large_centered_text(fb, s, avail_width, y):
    x = (avail_width - font.str_width(s)) // 2
    for c in s:
        ch, width = font.get_ch(c)
        chfb = FrameBuffer(bytearray(ch), width, font.height, MONO_VMSB)
        fb.blit(chfb, x, y)
        x += width
Example #10
0
    def _bmp_decode(self, img_arrays):

        file_size = int.from_bytes(img_arrays[2:6], 'little')
        offset = int.from_bytes(img_arrays[10:14], 'little')
        width = int.from_bytes(img_arrays[18:22], 'little')
        height = int.from_bytes(img_arrays[22:26], 'little')
        bpp = int.from_bytes(img_arrays[28:30], 'little')
        if bpp != 1:
            raise TypeError("Only support 1 bit color bmp")
        line_bytes_size = (bpp * width + 31) // 32 * 4
        array_size = width * abs(height) // 8
        pixel_arrays = bytearray(array_size)

        if width % 8:
            array_row = width // 8 + 1
        else:
            array_row = width // 8
        array_col = height

        # print("fileSize:{}, offset: {} ".format(file_size, offset))
        # print("width:{}, height: {},bit_count:{},line_bytes_size:{},array_size:{},".format(
        #     width, height, bpp, line_bytes_size, array_size))
        # print('array_col:{},array_row:{}'.format(array_col, array_row))

        for i in range(array_col):
            for j in range(array_row):
                index = -(array_row * (i + 1) - j)
                _offset = offset + i * line_bytes_size + j
                if self.invert == 0:
                    pixel_byte = (~img_arrays[_offset]) & 0xff
                else:
                    pixel_byte = img_arrays[_offset]
                pixel_arrays[index] = pixel_byte

        return FrameBuffer(pixel_arrays, width, height, 3)
def main():
    screen = LCD12864(("192.168.31.203", 10086))
    image_file = os.path.join(CURRENT_PATH, "test.pbm")
    with open(image_file, "rb") as f:
        w, h, type, data, comment = read_image(f)
    image = FrameBuffer(data, w, h, MONO_HLSB)
    screen.blit(image, (128 - w) // 2, 0)
    screen.show()
Example #12
0
    def __init__(self, gap_size):

        # 创建鸟和管道的framebuffer
        self.bird_fb = FrameBuffer(BIRD, bird_size[0], bird_size[1],
                                   framebuf.MONO_HLSB)
        self.pipe_top_fb = FrameBuffer(PIPE_TOP, pipe_size[0], pipe_size[1],
                                       framebuf.MONO_HLSB)
        self.pipe_down_fb = FrameBuffer(PIPE_DOWN, pipe_size[0], pipe_size[1],
                                        framebuf.MONO_HLSB)

        self.gap_size = gap_size
        self.high_score = 0
        self.pressed = False
        self.game_state = 0
        self.flappy_bird = None
        self.obstacle_1 = None
        self.obstacle_2 = None
Example #13
0
    def __init__(self, width, height, channels, brightness=1):
        self.height = height
        self.width = width
        self.channels = channels
        
        self.pixels = neopixel.NeoPixel(board.D18, width*height, bpp=channels, brightness=brightness, auto_write=False)

        self.frameBuffers = (
            FrameBuffer(bytearray(width * height * channels), width, height, channels),
            FrameBuffer(bytearray(width * height * channels), width, height, channels)
        )

        self.activeLock = threading.Lock()
        self.active = 0
        self.inactive = 1

        self.worker = threading.Thread(target=self.__write, daemon=True)
        self.worker.start()
Example #14
0
    def framebuffer(self):
        """Creates a new framebuffer for the screen

        returns a framebuf.FrameBuffer object used for drawing and a bytearray
        object to be passed to self.update()
        """
        data = bytearray(self._fix_info.line_length * self.height)
        if self._fix_info.visual in (_FB_VISUAL_MONO01, _FB_VISUAL_MONO10):
            format = MONO_HMSB
        fbuf = FrameBuffer(data, self.width, self.height, format,
                           self._fix_info.line_length // self.bpp * 8)
        return fbuf, data
Example #15
0
    def __init__(self):
        self.EN_DYN = Pin(16)
        self.EN_DYN.on()
        self.SPEED_PULSE = Pin(15)

        self.isAP = False

        self.oled = FrameBuffer(bytearray(128 * 64 // 8), 128, 64, MONO_HLSB,
                                True)

        self.ina = Ina219(None)
        self.btns = Btns([1, 2, 3, 4])
Example #16
0
    def framebuffer(self):
        """Creates a new frame buffer for the display

        Returns:
            A ``framebuf.FrameBuffer`` object used for drawing and a bytearray
            object to be passed to :py:meth:`update`
        """
        data = bytearray(self._fix_info.line_length * self.height)
        if self._fix_info.visual in (_FB_VISUAL_MONO01,
                                     _FB_VISUAL_MONO10):
            fmt = MONO_HMSB
        fbuf = FrameBuffer(data, self.width, self.height, fmt,
                           self._fix_info.line_length // self.bpp * 8)
        return fbuf, data
Example #17
0
    def __init__(self):

        self.textbuffer = TextBuffer(cols, rows)

        # make the framebuffer we draw into the size of one line of text as that's all we need
        self.buf = bytearray(screen_width * font_height // 8)
        # the screen defaults to portrait and we want to use it in landscape so we have to rotate as we go, unfortunately. that's why dimensions look swapped around
        self.fb = FrameBuffer(self.buf, font_height, screen_width, MONO_HLSB)

        sck = Pin(18, Pin.OUT)
        mosi = Pin(23, Pin.OUT)
        miso = Pin(19, Pin.IN)
        spi = SPI(2,
                  baudrate=80000000,
                  polarity=0,
                  phase=0,
                  sck=sck,
                  mosi=mosi,
                  miso=miso)

        cs = Pin(5, Pin.OUT)
        dc = Pin(17, Pin.OUT)
        rst = Pin(27, Pin.OUT)
        busy = Pin(35, Pin.IN)

        self.epd = EPD(spi, cs, dc, rst, busy)
        self.epd.init()

        self.clear_screen()

        # we were in slow mode for the initial clear on startup, we're now going to fast mode as that's the most likely one we'll need next
        self.mode = 'slow'
        self.set_fast()

        self.running = False

        self.last_change = None
Example #18
0
    def __init__(self):
        """
        Driver for Eink display. Provides helper functionality for displaying data.
        Assumes a hardware SPI connection on GPIO 14 (HSCLK) and GPIO 13 (HMOSI)/
        """
        self.spi = SPI(SPI_ID)
        self.frame_byte_array = bytearray(EPD_WIDTH * EPD_HEIGHT // NUMBER_OF_BITS)

        self.frame_buffer = FrameBuffer(
            self.frame_byte_array,
            EPD_WIDTH,
            EPD_HEIGHT,
            MONO_HLSB,
        )

        self.driver = EPD(self.spi, EINK_CS, EINK_DC, EINK_RESET, EINK_BUSY)
Example #19
0
 def chars(self, str, x, y):
     str_w = self._font.get_width(str)
     div, rem = divmod(self._font.height(), 8)
     nbytes = div + 1 if rem else div
     buf = bytearray(str_w * nbytes)
     pos = 0
     for ch in str:
         glyph, char_w = self._font.get_ch(ch)
         for row in range(nbytes):
             index = row * str_w + pos
             for i in range(char_w):
                 buf[index + i] = glyph[nbytes * i + row]
         pos += char_w
     fb = FrameBuffer(buf, str_w, self._font.height(), MONO_VLSB)
     self.blit(fb, x, y, str_w, self._font.height())
     return x + str_w
Example #20
0
    def draw_bitmap_raw(self, path, x, y, w, h, invert=False, rotate=0):
        """Load raw bitmap from disc and draw to screen.

        Args:
            path (string): Image file path.
            x (int): x-coord of image.
            y (int): y-coord of image.
            w (int): Width of image.
            h (int): Height of image.
            invert (bool): True = invert image, False (Default) = normal image.
            rotate(int): 0, 90, 180, 270
        Notes:
            w x h cannot exceed 2048
        """
        if rotate == 90 or rotate == 270:
            w, h = h, w  # Swap width & height if landscape

        buf_size = w * h
        with open(path, "rb") as f:
            if rotate == 0:
                buf = bytearray(f.read(buf_size))
            elif rotate == 90:
                buf = bytearray(buf_size)
                for x1 in range(w - 1, -1, -1):
                    for y1 in range(h):
                        index = (w * y1) + x1
                        buf[index] = f.read(1)[0]
            elif rotate == 180:
                buf = bytearray(buf_size)
                for index in range(buf_size - 1, -1, -1):
                    buf[index] = f.read(1)[0]
            elif rotate == 270:
                buf = bytearray(buf_size)
                for x1 in range(1, w + 1):
                    for y1 in range(h - 1, -1, -1):
                        index = (w * y1) + x1 - 1
                        buf[index] = f.read(1)[0]
            if invert:
                for i, _ in enumerate(buf):
                    buf[i] ^= 0xFF

            fbuf = FrameBuffer(buf, w, h, GS8)
            self.monoFB.blit(fbuf, x, y)
Example #21
0
 def get_matrix(self):
     if self.data_cache is None:
         self.make()
     
     if not self.border:
         return self.modules
     
     width = len(self.modules) + self.border * 2
     buf   = bytearray(width * (width + 7) // 8)
     fb    = FrameBuffer(buf, width, width, MONO_HLSB)
     fb.fill(0)
     
     y = self.border
     for module in self.modules:
         x = self.border
         for p in module:
             fb.pixel(x, y, p)
             x += 1
         y += 1
     
     return (fb, width)
Example #22
0
send(str(user_info))  # send user info to server

config = receiveJSON(recv_msg(client).decode(FORMAT))

inPins = config["in"]
outPins = config["out"]

while True:
    reads = []
    for pin in inPins:
        val = Pin(pin, Pin.IN, Pin.PULL_UP).value()
        reads.append(val)
    send(str(reads))

    resp = recv_msg(client)

    for i in (0, len(outPins) - 1):
        pinNumber = outPins[i]
        pinValue = resp[i]
        Pin(pinNumber, Pin.OUT).value(not pinValue)

    screen = resp[i + 1:]

    fbuf = FrameBuffer(screen, 128, 64, MONO_VLSB)
    oled.blit(fbuf, 0, 0)

    oled.rotate(True)
    oled.show()
    sleep(0.01)

send(DISCONNECT_MESSAGE)
Example #23
0
    def load_sprite(self, path, w, h, invert=False, rotate=0):
        """Load MONO_HMSB bitmap from disc to sprite.

        Args:
            path (string): Image file path.
            w (int): Width of image.
            h (int): Height of image.
            invert (bool): True = invert image, False (Default) = normal image.
            rotate(int): 0, 90, 180, 270
        Notes:
            w x h cannot exceed 2048
        """
        array_size = w * h
        with open(path, "rb") as f:
            buf = bytearray(f.read(array_size))
            fb = FrameBuffer(buf, w, h, MONO_HMSB)

            if rotate == 0 and invert is True:  # 0 degrees
                fb2 = FrameBuffer(bytearray(array_size), w, h, MONO_HMSB)
                for y1 in range(h):
                    for x1 in range(w):
                        fb2.pixel(x1, y1, fb.pixel(x1, y1) ^ 0x01)
                fb = fb2
            elif rotate == 90:  # 90 degrees
                byte_width = (w - 1) // 8 + 1
                adj_size = h * byte_width
                fb2 = FrameBuffer(bytearray(adj_size), h, w, MONO_HMSB)
                for y1 in range(h):
                    for x1 in range(w):
                        if invert is True:
                            fb2.pixel(y1, x1,
                                      fb.pixel(x1, (h - 1) - y1) ^ 0x01)
                        else:
                            fb2.pixel(y1, x1, fb.pixel(x1, (h - 1) - y1))
                fb = fb2
            elif rotate == 180:  # 180 degrees
                fb2 = FrameBuffer(bytearray(array_size), w, h, MONO_HMSB)
                for y1 in range(h):
                    for x1 in range(w):
                        if invert is True:
                            fb2.pixel(
                                x1, y1,
                                fb.pixel((w - 1) - x1, (h - 1) - y1) ^ 0x01)
                        else:
                            fb2.pixel(x1, y1,
                                      fb.pixel((w - 1) - x1, (h - 1) - y1))
                fb = fb2
            elif rotate == 270:  # 270 degrees
                byte_width = (w - 1) // 8 + 1
                adj_size = h * byte_width
                fb2 = FrameBuffer(bytearray(adj_size), h, w, MONO_HMSB)
                for y1 in range(h):
                    for x1 in range(w):
                        if invert is True:
                            fb2.pixel(y1, x1,
                                      fb.pixel((w - 1) - x1, y1) ^ 0x01)
                        else:
                            fb2.pixel(y1, x1, fb.pixel((w - 1) - x1, y1))
                fb = fb2

            return fb
Example #24
0
import machine
from framebuf import FrameBuffer, MONO_HLSB, RGB565
from time import sleep_ms

scl = machine.Pin('X9')
sda = machine.Pin('X10')
i2c = machine.I2C(scl=scl, sda=sda)

frame_size = [64, 32]

text = "Test" # Up to 8 characters in a 64px wide screen
text_hsize = len(text) * 8
centered_text_start = [int((frame_size[0] / 2) - (text_hsize / 2)), int((frame_size[1] / 2) - (8 / 2))]

# Frame buffers
main_frame = FrameBuffer(bytearray(frame_size[0] * frame_size[1] // 8), frame_size[0], frame_size[1], MONO_HLSB)
text_frame = FrameBuffer(bytearray(text_hsize * 8 // 8), text_hsize, 8, MONO_HLSB)
pixel_frame = FrameBuffer(bytearray(1), 1, 1, MONO_HLSB)
pixel_frame_black = FrameBuffer(bytearray(1), 1, 1, MONO_HLSB)

# Text
text_frame.fill(0)
text_frame.text(text, 0, 0, 1)

# Single-Pixels
pixel_frame.fill(0)
pixel_frame.pixel(0, 0, 1)

pixel_frame_black.pixel(0, 0, 0)

# Drawing text_frame on top of main_frame
Example #25
0
    def get_letter(self, letter, invert=False, rotate=0):
        """Convert letter byte data to pixels.

        Args:
            letter (string): Letter to return (must exist within font).
            invert (bool): True = white text, False (Default) black text.
            rotate (int): rotation (default: 0)
        Returns:
            (FrameBuffer): Pixel data in MONO_VLSB.
            (int, int): Letter width and height.
        """
        # Get index of letter
        letter_ord = ord(letter) - self.start_letter
        # Confirm font contains letter
        if letter_ord >= self.letter_count:
            print('Font does not contain character: ' + letter)
            return b'', 0, 0
        bytes_per_letter = self.bytes_per_letter
        offset = letter_ord * bytes_per_letter
        mv = memoryview(self.letters[offset:offset + bytes_per_letter])

        # Get width of letter (specified by first byte)
        width = mv[0]
        height = self.height
        byte_height = (height - 1) // 8 + 1  # Support fonts up to 5 bytes high
        if byte_height > 6:
            print("Error: maximum font byte height equals 6.")
            return b'', 0, 0
        array_size = width * byte_height
        ba = bytearray(mv[1:array_size + 1])
        # Set inversion and re-order bytes if height > 1 byte
        pos = 0
        ba2 = bytearray(array_size)
        if invert is True:  # 0 bit is black/red so inverted is default
            for i in range(0, array_size, byte_height):
                ba2[pos] = ba[i]
                if byte_height > 1:
                    ba2[pos + width] = ba[i + 1]
                if byte_height > 2:
                    ba2[pos + width * 2] = ba[i + 2]
                if byte_height > 3:
                    ba2[pos + width * 3] = ba[i + 3]
                if byte_height > 4:
                    ba2[pos + width * 4] = ba[i + 4]
                if byte_height > 5:
                    ba2[pos + width * 5] = ba[i + 5]
                pos += 1
        else:  # Use XOR to negate inversion
            for i in range(0, array_size, byte_height):
                ba2[pos] = ba[i] ^ 0xFF
                if byte_height > 1:
                    ba2[pos + width] = ba[i + 1] ^ 0xFF
                if byte_height > 2:
                    ba2[pos + width * 2] = ba[i + 2] ^ 0xFF
                if byte_height > 3:
                    ba2[pos + width * 3] = ba[i + 3] ^ 0xFF
                if byte_height > 4:
                    ba2[pos + width * 4] = ba[i + 4] ^ 0xFF
                if byte_height > 5:
                    ba2[pos + width * 5] = ba[i + 5] ^ 0xFF
                pos += 1

        fb = FrameBuffer(ba2, width, height, MONO_VLSB)

        if rotate == 0:  # 0 degrees
            return fb, width, height
        elif rotate == 90:  # 90 degrees
            byte_width = (width - 1) // 8 + 1
            adj_size = height * byte_width
            fb2 = FrameBuffer(bytearray(adj_size), height, width, MONO_VLSB)
            for y in range(height):
                for x in range(width):
                    fb2.pixel(y, x, fb.pixel(x, (height - 1) - y))
            return fb2, height, width
        elif rotate == 180:  # 180 degrees
            fb2 = FrameBuffer(bytearray(array_size), width, height, MONO_VLSB)
            for y in range(height):
                for x in range(width):
                    fb2.pixel(x, y, fb.pixel((width - 1) - x,
                                             (height - 1) - y))
            return fb2, width, height
        elif rotate == 270:  # 270 degrees
            byte_width = (width - 1) // 8 + 1
            adj_size = height * byte_width
            fb2 = FrameBuffer(bytearray(adj_size), height, width, MONO_VLSB)
            for y in range(height):
                for x in range(width):
                    fb2.pixel(y, x, fb.pixel((width - 1) - x, y))
            return fb2, height, width
Example #26
0
 def bitmap(self, bitmap, x, y, w, h):
     fb = FrameBuffer(bytearray(bitmap), w, h, MONO_VLSB)
     self.blit(fb, x, y, w, h)
     return x + w
class ILI9341:
    def __init__(self, spi, cs=14, dc=27, rst=33, bl=32):
        self.buffer = bytearray(32)
        self.letter = FrameBuffer(bytearray(8), 8, 8, MONO_HLSB)
        self.spi = spi
        self.cs = Pin(cs, Pin.OUT)
        self.dc = Pin(dc, Pin.OUT)
        self.rst = Pin(rst, Pin.OUT)
        self.bl = Pin(bl, Pin.OUT)
        self.width = 320
        self.height = 240
        self.char_width = 16
        self.char_height = 16
        self.offset = 0
        self.background = color565(0, 0, 0)
        self._reset()
        self._setup()

    def _reset(self):
        self.cs.value(1)
        self.dc.value(0)
        self.rst.value(0)
        utime.sleep_ms(50)
        self.rst.value(1)
        utime.sleep_ms(50)

    def _setup(self):
        for command, arguments in (
            (0xef, b'\x03\x80\x02'),
            (0xcf, b'\x00\xc1\x30'),
            (0xed, b'\x64\x03\x12\x81'),
            (0xe8, b'\x85\x00\x78'),
            (0xcb, b'\x39\x2c\x00\x34\x02'),
            (0xf7, b'\x20'),
            (0xea, b'\x00\x00'),
            (0xc0, b'\x23'),  # Power Control 1, VRH[5:0]
            (0xc1, b'\x10'),  # Power Control 2, SAP[2:0], BT[3:0]
            (0xc5, b'\x3e\x28'),  # VCM Control 1
            (0xc7, b'\x86'),  # VCM Control 2
            (0x36, b'\x48'),  # Memory Access Control
            (0x3a, b'\x55'),  # Pixel Format
            (0xb1, b'\x00\x18'),  # FRMCTR1
            (0xb6, b'\x08\x82\x27'),  # Display Function Control
            (0xf2, b'\x00'),  # Gamma Function Disable
            (0x26, b'\x01'),  # Gamma Curve Selected
            (0xe0,
             b'\x0f\x31\x2b\x0c\x0e\x08\x4e\xf1\x37\x07\x10\x03\x0e\x09\x00'
             ),  # Set Gamma
            (0xe1,
             b'\x00\x0e\x14\x03\x11\x07\x31\xc1\x48\x08\x0f\x0c\x31\x36\x0f')):
            self._write_command(command, arguments)
        self._write_command(_CMD_WAKE)
        utime.sleep_ms(120)

    def on(self):
        self._write_command(_CMD_DISPLAY_ON)
        self.bl.value(1)

    def off(self):
        self.bl.value(0)
        self._write_command(_CMD_DISPLAY_OFF)

    def set_inversion(self, inverse):
        if inverse:
            self._write_command(_CMD_DISPLAY_INVERSION_ON)
        else:
            self._write_command(_CMD_DISPLAY_INVERSION_OFF)

    def to_color(self, r, g, b):
        return color565(r, g, b)

    def set_background(self, background=color565(0, 0, 0)):
        self.fill_rectangle(0, 0, self.width - 1, self.height - 1, background)
        self.background = background

    def set_pixel(self, x, y, color=color565(255, 255, 255)):
        if x >= 0 and x < self.width and y >= 0 and y < self.height:
            self.fill_rectangle(x, y, x, y, color)

    def draw_line(self, x0, y0, x1, y1, color=color565(255, 255, 255)):
        dx = abs(x1 - x0)
        dy = -abs(y1 - y0)
        sx = 1 if x0 < x1 else -1
        sy = 1 if y0 < y1 else -1
        e = dx + dy
        while x0 != x1 or y0 != y1:
            self.set_pixel(x0, y0, color)
            e2 = e << 1
            if e2 > dy:
                e += dy
                x0 += sx
            if e2 < dx:
                e += dx
                y0 += sy

    def draw_polyline(self, x, y, points, color=color565(255, 255, 255)):
        last_point = None
        for point in points:
            if last_point is not None:
                self.draw_line(x + last_point[0], y + last_point[1],
                               x + point[0], y + point[1], color)
            last_point = point

    def draw_string(self,
                    x_origin,
                    y_origin,
                    chars,
                    color=color565(255, 255, 255)):
        for char in chars:
            self.letter.fill(0)
            self.letter.text(char, 0, 0)
            for x in range(0, 8):
                for y in range(0, 8):
                    x0 = x_origin + (x << 1)
                    y0 = y_origin + (y << 1)
                    x1 = x0 + 2
                    y1 = y0 + 2
                    self.fill_rectangle(
                        x0, y0, x1, y1, color
                        if self.letter.pixel(x, y) > 0 else self.background)
            x_origin += 16

    def rotate_up(self, delta=1):
        self.offset = (self.offset + delta) % self.height
        self._write_command(_CMD_LINE_SET, ustruct.pack('>H', self.offset))

    def scroll_up(self, delta):
        self.fill_rectangle(0, 0, self.width - 1, delta, color=self.background)
        self.rotate_up(delta)

    def fill_rectangle(self, x0, y0, x1, y1, color):
        x0, x1 = self.width - x1 - 1, self.width - x0 - 1
        if x0 < 0 and x1 < 0:
            return
        if x0 >= self.width and x1 >= self.width:
            return
        if y0 < 0 and y1 < 0:
            return
        if y0 >= self.height and y1 >= self.height:
            return
        if x0 < 0:
            x0 = 0
        if x0 >= self.width:
            x0 = self.width - 1
        if y0 < 0:
            y0 = 0
        if y0 >= self.height:
            y0 = self.height - 1
        if x1 < 0:
            x1 = 0
        if x1 >= self.width:
            x1 = self.width - 1
        if y1 < 0:
            y1 = 0
        if y1 >= self.height:
            y1 = self.height - 1
        w = x1 - x0 + 1
        h = y1 - y0 + 1
        pixel_count = min(16, w * h)
        color_msb = color >> 8
        color_lsb = color & 255
        position = 0
        for index in range(0, pixel_count):
            self.buffer[position] = color_msb
            position += 1
            self.buffer[position] = color_lsb
            position += 1
        if pixel_count == 16:
            self._fill_large_rectangle(x0, y0, x1, y1)
        else:
            self._fill_small_rectangle(x0, y0, x1, y1, position)

    def _fill_large_rectangle(self, x0, y0, x1, y1):
        y = y0
        while y <= y1:
            x = x0
            while x <= x1:
                x_right = min(x1, x + 15)
                self._fill_small_rectangle(x, y, x_right, y,
                                           (x_right - x + 1) << 1)
                x = x_right + 1
            y += 1

    def _fill_small_rectangle(self, x0, y0, x1, y1, position):
        y0 += self.offset
        y1 += self.offset
        y0 %= self.height
        y1 %= self.height
        self._write_command(_CMD_COLUMN_SET, ustruct.pack(">HH", x0, x1))
        self._write_command(_CMD_PAGE_SET, ustruct.pack(">HH", y0, y1))
        self._write_command(_CMD_RAM_WRITE,
                            memoryview(self.buffer)[0:position])

    def _write_command(self, command, arguments=None):
        self.dc.value(0)
        self.cs.value(0)
        self.spi.write(bytearray([command]))
        self.cs.value(1)
        if arguments is not None:
            self.dc.value(1)
            self.cs.value(0)
            self.spi.write(arguments)
            self.cs.value(1)
Example #28
0
class EPD:
    def __init__(self, spi, cs_pin, reset_pin, busy_pin, adt):

        self.resolution = RESOLUTION[0]
        self.width, self.height = RESOLUTION[0]
        self.cols, self.rows = RESOLUTION[1]

        self.buf = FrameBuffer(bytearray(self.width * self.height // 4),
                               self.width, self.height, GS2_HMSB)
        self.border_colour = 0

        self._reset_pin = reset_pin
        self._busy_pin = busy_pin
        self._cs_pin = cs_pin
        self._spi = spi
        self._adt = adt

        self._dirty = False

        self._luts = {
            'default': [
                # Phase 0     Phase 1     Phase 2     Phase 3     Phase 4     Phase 5     Phase 6
                # A B C D     A B C D     A B C D     A B C D     A B C D     A B C D     A B C D
                0b01001000,
                0b10100000,
                0b00010000,
                0b00010000,
                0b00010011,
                0b00000000,
                0b00000000,  # LUT0 - Black
                0b01001000,
                0b10100000,
                0b10000000,
                0b00000000,
                0b00000011,
                0b00000000,
                0b00000000,  # LUTT1 - White
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,  # IGNORE
                0b01001000,
                0b10100101,
                0b00000000,
                0b10111011,
                0b00000000,
                0b00000000,
                0b00000000,  # LUT3 - Red
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,  # LUT4 - VCOM

                # Duration            |  Repeat
                # A   B     C     D   |
                64,
                12,
                32,
                12,
                6,  # 0 Flash
                16,
                8,
                4,
                4,
                6,  # 1 clear
                4,
                8,
                8,
                16,
                16,  # 2 bring in the black
                2,
                2,
                2,
                64,
                32,  # 3 time for red
                2,
                2,
                2,
                2,
                2,  # 4 final black sharpen phase
                0,
                0,
                0,
                0,
                0,  # 5
                0,
                0,
                0,
                0,
                0  # 6
            ]
        }

    def setup(self):
        self._reset_pin.off()
        time.sleep(0.1)
        self._reset_pin.on()
        time.sleep(0.1)

        self._send_command(0x12)  # Soft Reset
        self._busy_wait()

    def _busy_wait(self):
        return
        while self._busy_pin.value():
            time.sleep(0.01)

    def _update(self, buf_a, buf_b):
        self.setup()

        packed_height = list(struct.pack('<H', self.rows))

        self._send_command(0x74, 0x54)  # Set Analog Block Control
        self._send_command(0x7e, 0x3b)  # Set Digital Block Control

        self._send_command(0x01, packed_height + [0x00])  # Gate setting

        self._send_command(0x03, [0b10000, 0b0001])  # Gate Driving Voltage

        self._send_command(0x3a, 0x07)  # Dummy line period
        self._send_command(0x3b, 0x04)  # Gate line width
        self._send_command(
            0x11, 0x03)  # Data entry mode setting 0x03 = X/Y increment

        self._send_command(0x04)  # Power On
        self._send_command(0x2c, 0x3c)  # VCOM Register, 0x3c = -1.5v?

        self._send_command(0x3c, 0x00)
        self._send_command(0x3c, 0xFF)

        self._send_command(0x32, self._luts['default'])  # Set LUTs

        self._send_command(0x44, [0x00,
                                  (self.cols // 8) - 1])  # Set RAM X Start/End
        self._send_command(0x45,
                           [0x00, 0x00] + packed_height)  # Set RAM Y Start/End

        # 0x24 == RAM B/W, 0x26 == RAM Red
        for data in ((0x24, buf_a), (0x26, buf_b)):
            cmd, buf = data
            self._send_command(0x4e, 0x00)  # Set RAM X Pointer Start
            self._send_command(0x4f, [0x00, 0x00])  # Set RAM Y Pointer Start
            self._send_command(cmd, buf)

        temp = self._adt.read_temp()
        temp_b = struct.pack(">h", int(temp * 16))
        temp0 = (temp_b[0] & 0xF) << 4
        temp1 = temp_b[1]
        self._send_command(0x1b, [temp1, temp0])

        self._send_command(0x22, 0xc7)  # Display Update Sequence
        self._send_command(0x20)  # Trigger Display Update
        time.sleep(0.05)
        self._busy_wait()
        self._send_command(0x10, 0x01)  # Enter Deep Sleep
        self._dirty = False

    def set_pixel(self, x, y, v):
        if v in (WHITE, BLACK, RED):
            self.buf.pixel(x, y, v)
            self._dirty = True

    def hline(self, x, y, w, v):
        if v in (WHITE, BLACK, RED):
            self.buf.hline(x, y, w, v)
            self._dirty = True

    def vline(self, x, y, h, v):
        if v in (WHITE, BLACK, RED):
            self.buf.hline(x, y, h, v)
            self._dirty = True

    @property
    def dirty(self):
        return self._dirty

    def show(self):
        def gen_buf_a():
            for row in range(self.rows):
                for col in range(0, self.cols, 8):
                    out = 0
                    for i in range(8):
                        if self.buf.pixel(col + i, row) == BLACK:
                            out += 1 << (7 - i)
                    yield out

        def gen_buf_b():
            for row in range(self.rows):
                for col in range(0, self.cols, 8):
                    out = 0
                    for i in range(8):
                        if self.buf.pixel(col + i, row) == RED:
                            out += 1 << (7 - i)
                    yield out

        self._update(FrameGen(self.cols, self.rows, BLACK, self.buf.pixel),
                     FrameGen(self.cols, self.rows, RED, self.buf.pixel))

    def _spi_write(self, dc, values):
        self._spi.write(values, dc)

    def _send_command(self, command, data=None):
        self._spi_write(_SPI_COMMAND, [command])
        if data is not None:
            self._send_data(data)

    def _send_data(self, data):
        if isinstance(data, int):
            data = [data]
        self._spi_write(_SPI_DATA, data)
Example #29
0
class Screen:
    def __init__(self):

        self.textbuffer = TextBuffer(cols, rows)

        # make the framebuffer we draw into the size of one line of text as that's all we need
        self.buf = bytearray(screen_width * font_height // 8)
        # the screen defaults to portrait and we want to use it in landscape so we have to rotate as we go, unfortunately. that's why dimensions look swapped around
        self.fb = FrameBuffer(self.buf, font_height, screen_width, MONO_HLSB)

        sck = Pin(18, Pin.OUT)
        mosi = Pin(23, Pin.OUT)
        miso = Pin(19, Pin.IN)
        spi = SPI(2,
                  baudrate=80000000,
                  polarity=0,
                  phase=0,
                  sck=sck,
                  mosi=mosi,
                  miso=miso)

        cs = Pin(5, Pin.OUT)
        dc = Pin(17, Pin.OUT)
        rst = Pin(27, Pin.OUT)
        busy = Pin(35, Pin.IN)

        self.epd = EPD(spi, cs, dc, rst, busy)
        self.epd.init()

        self.clear_screen()

        # we were in slow mode for the initial clear on startup, we're now going to fast mode as that's the most likely one we'll need next
        self.mode = 'slow'
        self.set_fast()

        self.running = False

        self.last_change = None

    def write(self, byteslike):
        self.textbuffer.write(byteslike)
        self.debounce_update()

    def set_slow(self):
        if self.mode == 'slow':
            return

        self.mode = 'slow'
        self.epd.set_slow()

    def set_fast(self):
        if self.mode == 'fast':
            return

        self.mode = 'fast'
        self.epd.set_fast()

    def plot(self, x, y):
        # rotate 90 degrees on the fly
        self.fb.pixel(font_height - 1 - y, x, 0)

    def plot_inverse(self, x, y):
        # rotate 90 degrees
        self.fb.pixel(font_height - 1 - y, x, 1)

    def debounce_update(self):
        self.last_change = time.ticks_ms()

    def update_screen(self, tmr=None):
        self.last_change = None

        if not self.running:
            return

        # TODO: keep some performance stats somewhere

        # the changed lines. keys are row indexes, values are line strings
        lines_dict = self.textbuffer.pop()

        # slow update if the entire screen changed (gives it a chance to remove the ghosting), otherwise fast for partial updates
        if len(lines_dict) == self.textbuffer.rows:
            self.set_slow()
        else:
            self.set_fast()

        cursor_x = self.textbuffer.x()
        cursor_y = self.textbuffer.y()

        # keep both buffers in the display's controller in sync
        for i in range(2):
            self._update_buffer(lines_dict, cursor_x, cursor_y)

            # display only one of the buffers
            if i == 0:
                self.epd.display_frame()

    def _update_buffer(self, lines_dict, cursor_x, cursor_y):
        for row_index, line in lines_dict.items():
            # clear the framebuffer because it now represents this row
            self.fb.fill(1)

            # print the text
            font.draw_line(line.encode('ascii'), self.plot)

            if row_index == cursor_y:
                # draw the cursor (rotated 90 degrees)
                self.fb.fill_rect(0, cursor_x * font_width, font_height,
                                  font_width, 0)

                # if the cursor is on a character, also draw that character inverted
                if cursor_x < len(line):
                    font.draw_line(line[cursor_x].encode('ascii'),
                                   self.plot_inverse, cursor_x * font_width, 1)

            # copy this row to the screen's buffer (rotated 90 degrees)
            self.epd.set_frame_memory(
                self.buf, screen_height - ((row_index + 1) * font_height), 0,
                font_height, screen_width)

    def clear_screen(self):
        self.fb.fill(1)

        # clear both buffers
        for i in range(2):
            for row_index in range(self.textbuffer.rows):
                self.epd.set_frame_memory(self.buf, row_index * font_height, 0,
                                          font_height, screen_width)

            # but only clear the screen once
            if i == 0:
                self.epd.display_frame()

    def clear(self):
        self.textbuffer.clear()
        self.debounce_update()
Example #30
0
    def __init__(self, spi, cs_pin, reset_pin, busy_pin, adt):

        self.resolution = RESOLUTION[0]
        self.width, self.height = RESOLUTION[0]
        self.cols, self.rows = RESOLUTION[1]

        self.buf = FrameBuffer(bytearray(self.width * self.height // 4),
                               self.width, self.height, GS2_HMSB)
        self.border_colour = 0

        self._reset_pin = reset_pin
        self._busy_pin = busy_pin
        self._cs_pin = cs_pin
        self._spi = spi
        self._adt = adt

        self._dirty = False

        self._luts = {
            'default': [
                # Phase 0     Phase 1     Phase 2     Phase 3     Phase 4     Phase 5     Phase 6
                # A B C D     A B C D     A B C D     A B C D     A B C D     A B C D     A B C D
                0b01001000,
                0b10100000,
                0b00010000,
                0b00010000,
                0b00010011,
                0b00000000,
                0b00000000,  # LUT0 - Black
                0b01001000,
                0b10100000,
                0b10000000,
                0b00000000,
                0b00000011,
                0b00000000,
                0b00000000,  # LUTT1 - White
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,  # IGNORE
                0b01001000,
                0b10100101,
                0b00000000,
                0b10111011,
                0b00000000,
                0b00000000,
                0b00000000,  # LUT3 - Red
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,
                0b00000000,  # LUT4 - VCOM

                # Duration            |  Repeat
                # A   B     C     D   |
                64,
                12,
                32,
                12,
                6,  # 0 Flash
                16,
                8,
                4,
                4,
                6,  # 1 clear
                4,
                8,
                8,
                16,
                16,  # 2 bring in the black
                2,
                2,
                2,
                64,
                32,  # 3 time for red
                2,
                2,
                2,
                2,
                2,  # 4 final black sharpen phase
                0,
                0,
                0,
                0,
                0,  # 5
                0,
                0,
                0,
                0,
                0  # 6
            ]
        }