class eInkDisplay(object): """ An object to manage the Waveshare e-ink display """ def __init__(self, vcom: float): self.display = AutoEPDDisplay(vcom=vcom) self.clear_display() self.dims = (self.display.width, self.display.height) def clear_display(self): """ Clears display by removing any image """ self.display.clear() def paste_image(self, img): """ Pastes a PIL image to the display """ self.display.frame_buf.paste( 0xFF, box=(0, 0, self.display.width, self.display.height) ) paste_coords = [self.dims[i] - img.size[i] for i in (0, 1)] self.display.frame_buf.paste(img, paste_coords) self.display.draw_full(constants.DisplayModes.GC16)
def displayImage(self, image): content = Image.open(image) # auto rotate depending on aspect ratio if content.width > content.height: rotation = None else: rotation = 'CW' # init screen; use vcom value of your screen screen = AutoEPDDisplay(vcom=-2.13, rotate=rotation) # make screen clear #screen.clear() # shrink image if necessary screen_dims = (screen.width, screen.height) content.thumbnail(screen_dims) # align image in center of screen paste_coords = [ int((screen_dims[i] - content.size[i]) / 2) for i in (0, 1) ] screen.frame_buf.paste(content, paste_coords) # display the image screen.draw_full(constants.DisplayModes.GC16)
def __init__(self, profile=False, ttyn=1, frame_rate=10, flip=False): self.ttyn = ttyn self.inv_frame_rate = 1 / frame_rate self.profile = profile if self.profile: self.pr = cProfile.Profile() # keep track of two displays: one for the terminal, the other for when # processes want to take it over epd = EPD(vcom=-2.06) self.term_display = AutoEPDDisplay(epd, flip=flip) self.controller_display = Controller(epd, flip=flip) print('Initializing...') self.term_display.clear() self.term = Terminal( (self.term_display.width, self.term_display.height), frame_buf=self.term_display.frame_buf) auto_resize_tty(self.ttyn, self.term.char_dims, (self.term_display.width, self.term_display.height)) # handle both of these the same way signal.signal(signal.SIGTERM, self.sigterm_handler) signal.signal(signal.SIGINT, self.sigterm_handler)
def __init__(self, vcom: float): if has_it8951: print('Setting up the display using VCOM=' + str(vcom)) self.display = AutoEPDDisplay(vcom=vcom, spi_hz=24000000) self.display.epd.wait_display_ready() self.display.clear() else: raise Exception("IT8951 driver not present") self.lock = threading.RLock() super().__init__(self.display.frame_buf)
def __init__(self, config, virtual=False, rotate=None): self.width = EPD7IN8WIDTH self.height = EPD7IN8HEIGHT if virtual: from IT8951.display import VirtualEPDDisplay self.disp = VirtualEPDDisplay(dims=(self.width, self.height), rotate=rotate) else: logging.info("Initializing 7.8 inch e-paper display.") self.disp = AutoEPDDisplay(vcom=-1.4, rotate=rotate, spi_hz=EPD7IN8FREQ) logging.info(f"VCOM set to {self.disp.epd.get_vcom()}")
def main(): from sys import path path += ['../../'] from IT8951.display import AutoEPDDisplay display = AutoEPDDisplay(vcom=-2.06) print('VCOM set to', display.epd.get_vcom()) # 1. Clear Display print('Clearing display...') display.clear() # 2. Draw demo image = Image.new('1', (display.width, display.height), 1) draw = ImageDraw.Draw(image) font = ImageFont.truetype( '/usr/share/fonts/truetype/freefont/FreeMonoBold.ttf', 24) draw.rectangle((0, 6, 1200, 30), fill=0) draw.text((200, 10), 'e-Paper demo', font=font, fill=255) draw.rectangle((200, 80, 600, 280), fill=0) draw.arc((240, 120, 580, 220), 0, 360, fill=255) draw.rectangle((0, 80, 160, 280), fill=255) draw.arc((40, 80, 180, 220), 0, 360, fill=0) color = 0x10 display.frame_buf.paste(color, box=image) display.draw_full(constants.DisplayModes.GC16) sleep(1) # 3. Clear Display print('Clearing display...') display.clear() # 4. Draw Image print('Draw Image...') image = Image.open('monocolor.bmp') dims = (display.width, display.height) image.thumbnail(dims) paste_coords = [dims[i] - image.size[i] for i in (0, 1)] # align image with bottom of display display.frame_buf.paste(image, paste_coords) display.draw_full(constants.DisplayModes.GC16)
class EPD7In8Driver: """Provide functions for showing figures on the screen.""" def __init__(self, config, virtual=False, rotate=None): self.width = EPD7IN8WIDTH self.height = EPD7IN8HEIGHT if virtual: from IT8951.display import VirtualEPDDisplay self.disp = VirtualEPDDisplay(dims=(self.width, self.height), rotate=rotate) else: logging.info("Initializing 7.8 inch e-paper display.") self.disp = AutoEPDDisplay(vcom=-1.4, rotate=rotate, spi_hz=EPD7IN8FREQ) logging.info(f"VCOM set to {self.disp.epd.get_vcom()}") def clear_frame(self): """Fill the frame with white dot Note: clear_frame is different from disp.clear(). The latter will write the data to the display hardware. """ self.disp.frame_buf.paste(0xFF, box=(0, 0, self.width, self.height)) def show_one_image(self, img_orig): """Input: img, an PIL object""" self.clear_frame() frame_width = EPD7IN8WIDTH - 72 # leave some margin for visibility. frame_height = EPD7IN8HEIGHT - 24 if img_orig.size[0]/img_orig.size[1] > frame_width/frame_height: # left and right are the lower bound ratio = frame_width / img_orig.size[0] new_width = frame_width new_height = int(img_orig.size[1] * ratio) if new_height % 10 == 8: new_height += 2 else: # top/bottom are the lower bound ratio = frame_height / img_orig.size[1] new_height = frame_height new_width = int(img_orig.size[0] * ratio) if new_width % 10 == 8: new_width += 2 img2 = img_orig.resize((new_width, new_height), Image.ANTIALIAS) dims = (self.width, self.height) paste_coords = [ (dims[i] - img2.size[i])//2 for i in (0, 1) ] # align image with bottom of display self.disp.frame_buf.paste(img2, paste_coords) self.disp.draw_full(constants.DisplayModes.GC16) def display_text(self): pass
def main(): args = parse_args() from sys import path path += ['../../'] tests = [] if not args.virtual: from IT8951.display import AutoEPDDisplay print('Initializing EPD...') display = AutoEPDDisplay(vcom=-2.06) print('VCOM set to', display.epd.get_vcom()) tests += [print_system_info] else: from IT8951.display import VirtualEPDDisplay display = VirtualEPDDisplay(dims=(800, 600)) tests += [ clear_display, display_gradient, partial_update, display_image_8bpp, ] for t in tests: t(display) sleep(1) print('Done!')
class DrawingAdapterEPD(DrawingAdapter): def __init__(self, vcom: float): if has_it8951: print('Setting up the display using VCOM=' + str(vcom)) self.display = AutoEPDDisplay(vcom=vcom, spi_hz=24000000) self.display.epd.wait_display_ready() self.display.clear() else: raise Exception("IT8951 driver not present") self.lock = threading.RLock() super().__init__(self.display.frame_buf) def draw(self): with self.lock: self.display.epd.run() self.display.draw_full(constants.DisplayModes.GC16) self.display.epd.wait_display_ready() self.display.epd.sleep()
def main(): print('Initializing...') display = AutoEPDDisplay(vcom=-2.06, spi_hz=24000000) display.clear() # so that we're not timing the previous operations display.epd.wait_display_ready() print('Doing update...') # draw all black display.frame_buf.paste(0x00, (0, 0, display.width, display.height)) p = Profiler() p.profile_func( display.draw_partial, constants.DisplayModes.DU # should see what best mode is here ) p.profile_func(display.epd.spi.wait_ready) p.print_results()
def __init__( self, display_mode=DisplayModes.GL16, image_filter=Image.NEAREST, debug=False ): global VCOM start = time.time() self.display_mode = display_mode self.image_filter = image_filter self.debug = debug self.epd = EPD() self.display = AutoEPDDisplay(epd=self.epd, vcom=VCOM) self.dims = (self.display.width, self.display.height) self.prev = None if self.debug: print(f"Time to initialize: {round(time.time() - start, 3)}s") start = time.time() self.display.clear() if self.debug: print(f"Time to clear: {round(time.time() - start, 3)}s")
def main(): print('Initializing...') display = AutoEPDDisplay(vcom=-2.06) display.clear() # so that we're not timing the previous operations display.epd.wait_display_ready() print('Doing partial update...') char_height = 20 char_width = 12 rows = display.height // char_height cols = display.width // char_width p = Profiler() text = 'partialupdate' start = default_timer() for n, c in enumerate(cycle(text)): row = n // cols col = n % cols place_text(display.frame_buf, c, x=col * char_width, y=row * char_height) p.profile_func( display.draw_partial, constants.DisplayModes.DU # should see what best mode is here ) # run for 10 seconds then stop if default_timer() - start > 10: print('total iterations: {}'.format(n + 1)) break p.print_results()
def init_display(args) -> AutoDisplay: # display.frame_buf is a 8 bit pixel B&W image if not args.virtual: Utils.log('Initializing EPaper Display...') # here, spi_hz controls the rate of data transfer to the device, so a higher # value means faster display refreshes. the documentation for the IT8951 device # says the max is 24 MHz (24000000), but my device seems to still work as high as # 80 MHz (80000000) display_ref = AutoEPDDisplay(vcom=-2.06, rotate=args.rotate, spi_hz=24000000) Utils.log('VCOM set to ' + str(display_ref.epd.get_vcom())) else: display_ref = VirtualEPDDisplay(dims=(800, 600), rotate=args.rotate) Utils.log("initializing virtual display") return display_ref
def main(): args = parse_args() todos = [] if not args.virtual: from IT8951.display import AutoEPDDisplay print('Initializing EPD...') # here, spi_hz controls the rate of data transfer to the device, so a higher # value means faster display refreshes. the documentation for the IT8951 device # says the max is 24 MHz (24000000), but my device seems to still work as high as # 80 MHz (80000000) display = AutoEPDDisplay(vcom=-2.36, rotate=args.rotate, spi_hz=24000000) print('VCOM set to', display.epd.get_vcom()) todos += [print_system_info] else: from IT8951.display import VirtualEPDDisplay display = VirtualEPDDisplay(dims=(800, 600), rotate=args.rotate) todos += [ clear_display, display_gradient, partial_update, display_image_8bpp, ] for t in todos: t(display) sleep(1) print('Done!')
def main(): print('Initializing...') display = AutoEPDDisplay(vcom=-2.06) display.clear() print('Writing initial image...') place_text(display.frame_buf, 'partial', x_offset=-200) display.draw_full(constants.DisplayModes.GC16) # so that we're not timing the previous operations display.epd.wait_display_ready() print('Doing partial update...') place_text(display.frame_buf, 'update', x_offset=+200) profile_func( display.draw_partial, constants.DisplayModes.DU # should see what best mode is here )
from IT8951.display import AutoEPDDisplay display = AutoEPDDisplay(vcom=-2.25, rotate=None, spi_hz=24000000) display.clear()
class TtyInk(): def __init__( self, display_mode=DisplayModes.GL16, image_filter=Image.NEAREST, debug=False ): global VCOM start = time.time() self.display_mode = display_mode self.image_filter = image_filter self.debug = debug self.epd = EPD() self.display = AutoEPDDisplay(epd=self.epd, vcom=VCOM) self.dims = (self.display.width, self.display.height) self.prev = None if self.debug: print(f"Time to initialize: {round(time.time() - start, 3)}s") start = time.time() self.display.clear() if self.debug: print(f"Time to clear: {round(time.time() - start, 3)}s") def __enter__(self): return self def __exit__(self, _type, _value, _traceback): pass def refresh(self, full=False, display_mode=None): start = time.time() self.display_to_screen(self.get_screen(), full=full, display_mode=display_mode) if self.debug: print(f"Time to refresh: {round(time.time() - start, 3)}s") def display_to_screen(self, image, full=False, display_mode=None): start = time.time() self.display.frame_buf.paste(image, [0, 0]) if full: self.display.draw_full(self.display_mode if display_mode is None else display_mode) else: self.display.draw_partial(self.display_mode if display_mode is None else display_mode) if self.debug: print(f"Time to display: {round(time.time() - start, 3)}s") def get_screen(self): global TTY_DEVICE start = time.time() output = subprocess.run( f"{SCRIPT_DIRECTORY}/tty.sh {TTY_DEVICE}".split(), stdout=subprocess.PIPE, stderr=subprocess.DEVNULL ) if self.debug: print(f"Time to capture image: {round(time.time() - start, 3)}s") start = time.time() image = Image.open(io.BytesIO(output.stdout)) image = image.resize(self.dims, self.image_filter) if self.debug: print(f"Time to transform image: {round(time.time() - start, 3)}s") return image def wait(self): self.epd.wait_display_ready()
def pocasi(): #url = "https://example.com/pocasi.json" #response = requests.get(url) #pocasi = json.loads(response.text) pocasi = json.loads(open('pocasi.json', 'r').read()) from IT8951.display import AutoEPDDisplay display = AutoEPDDisplay(vcom=-2.06) image = Image.new('L', (display.width, display.height), 1) draw = ImageDraw.Draw(image) fontdir = '/usr/share/fonts/truetype/roboto/unhinted/RobotoTTF/' big_font = ImageFont.truetype(fontdir + 'Roboto-Regular.ttf', 250) small_font = ImageFont.truetype(fontdir + 'Roboto-Bold.ttf', 30) draw.rectangle((0, 0, 1200, 825), fill=0) draw.text((50, 20), "Venkovní teplota [°C]:", font=small_font, fill=255) draw.text((650, 20), "Vnitřní teplota [°C]:", font=small_font, fill=255) draw.text((50, 80), str(pocasi["teplota"]), font=big_font, fill=255) draw.text((650, 80), str(pocasi["ds18b20"]), font=big_font, fill=255) draw.text((50, 410), "Zdánlivá teplota: " + str(pocasi["zdanlivateplota"]) + " °C", font=small_font, fill=255) draw.text((50, 480), "Rosný bod: " + str(pocasi["rosnybod"]) + " °C", font=small_font, fill=255) draw.text((50, 550), "Vlhkost vzduchu: " + str(pocasi["vlhkost"]) + " %", font=small_font, fill=255) draw.text((50, 630), "Srážky: " + str(pocasi["srazky"]) + " mm/den", font=small_font, fill=255) draw.text((650, 410), "Osvit: " + str(pocasi["osvit"]) + " W/m2", font=small_font, fill=255) draw.text((650, 480), "Tlak: " + str(pocasi["tlak"]) + " hPa", font=small_font, fill=255) draw.text((650, 550), "Vítr: " + str(pocasi["rychlostvetru"]) + " m/s, směr: " + pocasi["smervetru"], font=small_font, fill=255) draw.text((650, 630), "Nárazový vítr: " + str(pocasi["narazovyvitr"]) + " m/s", font=small_font, fill=255) draw.text((870, 775), pocasi["zmereno"], font=small_font, fill=255) color = 0x10 display.frame_buf.paste(color, box=image) display.draw_full(constants.DisplayModes.GC16)
from test_functions import print_system_info from sys import path path += ['../../'] from IT8951.display import AutoEPDDisplay display = AutoEPDDisplay(vcom=-2.06) print('VCOM set to', display.epd.get_vcom()) print_system_info(display)
def draw_thread(q): print('Initializing EPD...') logging.info('Initializing EPD...') # here, spi_hz controls the rate of data transfer to the device, so a higher # value means faster display refreshes. the documentation for the IT8951 device # says the max is 24 MHz (24000000), but my device seems to still work as high as # 80 MHz (80000000) rotate = None display = AutoEPDDisplay(vcom=-1.32, rotate=rotate, spi_hz=24000000) partial_update_draw_count = 0 last_drawn_graphics = None last_clear = datetime.now() last_total_reset = datetime.now() while True: try: graphics = q.pop() logging.error("drawing updated graphics") last_drawn_graphics = graphics partial_update_draw_count += 1 if partial_update_draw_count > 10: partial_update_draw_count = 0 display.frame_buf.paste(0xFF, box=(0, 0, display.width, display.height)) draw(display, graphics) display.draw_full(constants.DisplayModes.GC16) else: # clearing image to white display.frame_buf.paste(0xFF, box=(0, 0, display.width, display.height)) draw(display, graphics) display.draw_partial(constants.DisplayModes.DU) except IndexError: time.sleep(0.01) if (datetime.now() - last_total_reset).total_seconds() > 60*60*6: # 6 hours logging.error("REINIT EINK DISPLAY") display = AutoEPDDisplay(vcom=-1.32, rotate=rotate, spi_hz=24000000) last_total_reset = datetime.now() if (datetime.now() - last_clear).total_seconds() > 60: display.clear() if last_drawn_graphics is not None: draw(display, last_drawn_graphics) display.draw_full(constants.DisplayModes.GC16) last_clear = datetime.now()
else: table.append(1) print(" ", Tool.Log_time(), " | main:LUT 初始化完成") global cmd, run EPD.full_img = Image.open( "/home/pi/epaper_test/IT8951/test/images/white.png") print(" ", Tool.Log_time(), " | main:正在初始化 EPD 驱动程序...") display = AutoEPDDisplay( vcom=-1.83, rotate=None, spi_hz=124444449) # 设置Vcom值已经SPI时钟频率(部分设备可能无法支持高频SPI通信) dims = (display.width, display.height) print(" ", Tool.Log_time(), " | main:EPD 驱动程序初始化完成") print(" ", Tool.Log_time(), " | main:正在初始化 SMBus 总线...") bus = smbus.SMBus(1) bus.write_i2c_block_data(56, 225, [8, 0]) bus.read_byte(0x38) print(" ", Tool.Log_time(), " | main:SMBus 总线初始化完成")
class Runner: def __init__(self, profile=False, ttyn=1, frame_rate=10, flip=False): self.ttyn = ttyn self.inv_frame_rate = 1 / frame_rate self.profile = profile if self.profile: self.pr = cProfile.Profile() # keep track of two displays: one for the terminal, the other for when # processes want to take it over epd = EPD(vcom=-2.06) self.term_display = AutoEPDDisplay(epd, flip=flip) self.controller_display = Controller(epd, flip=flip) print('Initializing...') self.term_display.clear() self.term = Terminal( (self.term_display.width, self.term_display.height), frame_buf=self.term_display.frame_buf) auto_resize_tty(self.ttyn, self.term.char_dims, (self.term_display.width, self.term_display.height)) # handle both of these the same way signal.signal(signal.SIGTERM, self.sigterm_handler) signal.signal(signal.SIGINT, self.sigterm_handler) def sigterm_handler(self, sig=None, frame=None): self.running = False def on_exit(self): ''' Some things to do when we exit cleanly. We don't want to do them when we are not exiting cleanly, so they don't go in __del__ ''' print('Exiting...') if self.profile: s = io.StringIO() ps = pstats.Stats(self.pr, stream=s).sort_stats('cumulative') ps.print_stats() print(s.getvalue()) self.display_penguin() def update_callback(self, need_gray): ''' This function gets called whenever a character gets updated by term, in order to get quick updates on the screen ''' if need_gray: self.term_display.draw_partial(constants.DisplayModes.GL16) else: self.term_display.draw_partial(constants.DisplayModes.DU) def update(self): ''' Update the contents of the display ''' # if another process has decided to take the display, do that if self.controller_display.check_active(): self.controller_display.run() self.term_display.draw_full( constants.DisplayModes.GC16) # get our terminal back # currently just want to profile the updates done here if self.profile: self.pr.enable() cursor_pos, data = read_vcsa(self.ttyn) changed = self.term.update(cursor_pos, data, callback=self.update_callback) if self.profile: self.pr.disable() if changed: self.last_change = perf_counter() self.need_update = True elif self.need_update and perf_counter() - self.last_change > 10: # if it's been long time, clear out the ghosting self.term_display.draw_full(constants.DisplayModes.GC16) self.need_update = False def run(self): print('Running...') self.running = True self.need_update = False # TODO: it would be cool to trigger events off of changes # rather than just polling this file all the time. not sure # if there's a good way to do that while self.running: loop_start = perf_counter() self.update() # sleep for less time if the update took a while sleep_time = self.inv_frame_rate - (perf_counter() - loop_start) if sleep_time > 0: sleep(sleep_time) self.on_exit() def display_penguin(self): ''' Display a cute sleeping Tux to remain on the screen when we shut down the terminal. ''' img_path = join(dirname(__file__), 'images/sleeping_penguin.png') # clear image to white img_bounds = (0, 0, self.term_display.width, self.term_display.height) self.term_display.frame_buf.paste(0xFF, box=img_bounds) img = Image.open(img_path) dims = self.term_display.frame_buf.size # half of the display size img.thumbnail([x // 2 for x in dims]) paste_coords = ( # put it at the bottom of the display, centered (dims[0] - img.size[0]) // 2, dims[1] - img.size[1], ) self.term_display.frame_buf.paste(img, paste_coords) self.term_display.draw_full(constants.DisplayModes.GC16)
def __init__(self, vcom: float): self.display = AutoEPDDisplay(vcom=vcom) self.clear_display() self.dims = (self.display.width, self.display.height)
print("The current video is %s" % currentVideo) currentPosition = 0 # Open the log file and update the current position log = open(logdir + '%s<progress' % currentVideo) for line in log: currentPosition = float(line) if args.start: print('Start at frame %f' % float(args.start)) currentPosition = float(args.start) display = AutoEPDDisplay(vcom=-2.36, spi_hz=24000000) clear_display(display) # Ensure this matches your particular screen width = display.width height = display.height inputVid = viddir + currentVideo # Check how many frames are in the movie frameCount = int(ffmpeg.probe(inputVid)['streams'][0]['nb_frames']) print("there are %d frames in this video" % frameCount) while 1: display.epd.run()
import argparse # Ensure this is the correct import for your particular screen # from waveshare_epd import epd7in5_V2 as epd_driver from sys import path path += ['/'] from IT8951 import constants from IT8951.display import AutoEPDDisplay print('Initializing EPD...') # here, spi_hz controls the rate of data transfer to the device, so a higher # value means faster display refreshes. the documentation for the IT8951 device # says the max is 24 MHz (24000000), but my device seems to still work as high as # 80 MHz (80000000) display = AutoEPDDisplay(vcom=-2.06, rotate=None, spi_hz=24000000) print('VCOM set to', display.epd.get_vcom()) # Defaults defaultIncrement = 4 defaultDelay = 120 defaultContrast = 1.0 defaultDirectory = "Videos" # Compatible video file-extensions fileTypes = [".mp4", ".m4v", ".mkv"] # Handle when the program is killed and exit gracefully #def exithandler(signum, frame): # print('\nExiting Program')