class Monitor(IOBase): def __init__(self, cols=40, rows=4): self.textbuffer = TextBuffer(cols, rows) def read(self, size): return None def write(self, byteslike): with open("write.txt", 'wa') as dumpfile: for byte in byteslike: dumpfile.write(str(byte) + ' ' + chr(byte) + '\n') self.textbuffer.write(byteslike) self.dump_screen() self.dump_lines() self.dump_wrapped() return len(byteslike) def dump_screen(self): lines = [] line_dict = self.textbuffer.pop() for y in range(self.textbuffer.rows): if y in line_dict: lines.append(line_dict[y] + '\n') else: lines.append('*' * self.textbuffer.cols + '\n') lines.append('\n') lines.append(str(self.textbuffer.offset) + '\n') lines.append(self.textbuffer.previous_char + '\n') lines.append(str(len(line_dict)) + '\n') with open("screen.txt", 'w') as dumpfile: for line in lines: dumpfile.write(line) def dump_lines(self): with open("lines.txt", 'w') as dumpfile: for line in self.textbuffer.lines: dumpfile.write(line + '\n') def dump_wrapped(self): with open("wrapped.txt", 'w') as dumpfile: for wrapped_lines in self.textbuffer.wrapped: for line in wrapped_lines: dumpfile.write(line + '\n')
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()