From 16e310e37792f87481ab281d3e97a92781254557 Mon Sep 17 00:00:00 2001 From: Justin Richards <ratmandu@gmail.com> Date: Sun, 27 Oct 2019 02:18:42 -0500 Subject: [PATCH] Adding fast display update for waveshare 3-color Signed-off-by: Justin Richards <ratmandu@gmail.com> --- .../ui/hw/libs/waveshare/v1/epd2in13bcFAST.py | 359 ++++++++++++++++++ pwnagotchi/ui/hw/waveshare1.py | 17 +- 2 files changed, 370 insertions(+), 6 deletions(-) create mode 100644 pwnagotchi/ui/hw/libs/waveshare/v1/epd2in13bcFAST.py diff --git a/pwnagotchi/ui/hw/libs/waveshare/v1/epd2in13bcFAST.py b/pwnagotchi/ui/hw/libs/waveshare/v1/epd2in13bcFAST.py new file mode 100644 index 0000000..b770454 --- /dev/null +++ b/pwnagotchi/ui/hw/libs/waveshare/v1/epd2in13bcFAST.py @@ -0,0 +1,359 @@ +# ***************************************************************************** +# * | File : epd2in13d.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +# ============================================================================= +# THIS FILE HAS BEEN MODIFIED FROM ORIGINAL, IT HAS BEEN MODIFIED TO RUN THE +# THREE COLOR WAVESHARE 2.13IN DISPLAY AT A MUCH, MUCH FASTER RATE THAN NORMAL +# AND IT COULD DAMAGE YOUR DISPLAY. THERE IS NO WARRANTY INCLUDED AND YOU USE +# THIS CODE AT YOUR OWN RISK. WE ARE NOT RESPONSIBLE FOR ANYTHING THAT HAPPENS +# INCLUDING BUT NOT LIMITED TO: DESTRUCTION OF YOUR DISPLAY, PI, HOUSE, CAR, +# SPACE-TIME-CONTINUUM, OR IF THE CODE KILLS YOUR CAT. IF YOU AREN'T WILLING TO +# TAKE THESE RISKS, PLEASE DO NOT USE THIS CODE. +# ============================================================================= + + +import logging +from . import epdconfig +from PIL import Image +import RPi.GPIO as GPIO + +# Display resolution +EPD_WIDTH = 104 +EPD_HEIGHT = 212 + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + lut_vcomDC = [ + 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ] + + lut_ww = [ + 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, + 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bw = [ + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x0F, 0x0F, 0x00, 0x00, 0x03, + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_wb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bb = [ + 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x28, 0x28, 0x00, 0x00, 0x00, + 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, + 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_vcom1 = [ + 0xA0, 0x10, 0x10, 0x00, 0x00, 0x02, + 0x00, 0x10, 0x10, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + ] + + lut_ww1 = [ + 0x50, 0x01, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x42, 0x42, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bw1 = [ + 0x50, 0x01, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x42, 0x42, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_wb1 = [ + 0xA0, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x50, 0x42, 0x42, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + lut_bb1 = [ + 0xA0, 0x01, 0x01, 0x00, 0x00, 0x01, + 0x50, 0x42, 0x42, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + self.send_command(0x71) + epdconfig.delay_ms(100) + logging.debug("e-Paper busy release") + + def TurnOnDisplay(self): + self.send_command(0x12) + epdconfig.delay_ms(10) + self.ReadBusy() + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) + self.send_data(0x00) + self.send_data(0x2b) + self.send_data(0x2b) + self.send_data(0x03) + + self.send_command(0x06) # boost soft start + self.send_data(0x17) # A + self.send_data(0x17) # B + self.send_data(0x17) # C + + self.send_command(0x04) + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0xbf) # LUT from OTP,128x296 + self.send_data(0x0d) # VCOM to 0V fast + + self.send_command(0x30) # PLL setting + self.send_data(0x21) # 3a 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x61) # resolution setting + self.send_data(self.width) + self.send_data((self.height >> 8) & 0xff) + self.send_data(self.height& 0xff) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x28) + return 0 + + def SetFullReg(self): + self.send_command(0x82) + self.send_data(0x00) + self.send_command(0X50) + self.send_data(0x97) +# self.send_command(0x00) # panel setting +# self.send_data(0x9f) # LUT from OTP,128x296 + + def SetPartReg(self): +# self.send_command(0x00) # panel setting +# self.send_data(0xbf) # LUT from OTP,128x296 + self.send_command(0x82) + self.send_data(0x03) + self.send_command(0X50) + self.send_data(0x47) + + self.send_command(0x20) # vcom + for count in range(0, 44): + self.send_data(self.lut_vcom1[count]) + self.send_command(0x21) # ww -- + for count in range(0, 42): + self.send_data(self.lut_ww1[count]) + self.send_command(0x22) # bw r + for count in range(0, 42): + self.send_data(self.lut_bw1[count]) + self.send_command(0x23) # wb w + for count in range(0, 42): + self.send_data(self.lut_wb1[count]) + self.send_command(0x24) # bb b + for count in range(0, 42): + self.send_data(self.lut_bb1[count]) + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + if (Image == None): + return + + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(image[i]) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + def DisplayPartial(self, image): + if (Image == None): + return + + self.SetPartReg() + self.send_command(0x91) + self.send_command(0x90) + self.send_data(0) + self.send_data(self.width - 1) + + self.send_data(0) + self.send_data(0) + self.send_data(int(self.height / 256)) + self.send_data(self.height % 256 - 1) + self.send_data(0x28) + + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(image[i]) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(~image[i]) + epdconfig.delay_ms(10) + + self.TurnOnDisplay() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + epdconfig.delay_ms(10) + + self.SetFullReg() + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0X50) + self.send_data(0xf7) + self.send_command(0X02) # power off + self.send_command(0X07) # deep sleep + self.send_data(0xA5) + + epdconfig.module_exit() + +### END OF FILE ### diff --git a/pwnagotchi/ui/hw/waveshare1.py b/pwnagotchi/ui/hw/waveshare1.py index 47e55e1..72557e8 100644 --- a/pwnagotchi/ui/hw/waveshare1.py +++ b/pwnagotchi/ui/hw/waveshare1.py @@ -60,7 +60,14 @@ class WaveshareV1(DisplayImpl): self._display.init(self._display.lut_full_update) self._display.Clear(0xFF) self._display.init(self._display.lut_partial_update) - + elif self.config['color'] == 'fastAndFurious': + logging.info("initializing waveshare v1 3-color display in FAST MODE") + logging.info("THIS MAY BE POTENTIALLY DANGEROUS. NO WARRANTY IS PROVIDED") + logging.info("USE THIS DISPLAY IN THIS MODE AT YOUR OWN RISK") + from pwnagotchi.ui.hw.libs.waveshare.v1.epd2in13bcFAST import EPD + self._display = EPD() + self._display.init() + self._display.Clear(0xFF) else: logging.info("initializing waveshare v1 display 3-color mode") from pwnagotchi.ui.hw.libs.waveshare.v1.epd2in13bc import EPD @@ -72,13 +79,11 @@ class WaveshareV1(DisplayImpl): if self.config['color'] == 'black': buf = self._display.getbuffer(canvas) self._display.display(buf) + elif self.config['color'] == 'fastAndFurious': + buf_black = self._display.getbuffer(canvas) + self._display.DisplayPartial(buf_black) else: buf_black = self._display.getbuffer(canvas) - # emptyImage = Image.new('1', (self._display.height, self._display.width), 255) - # buf_color = self._display.getbuffer(emptyImage) - # self._display.display(buf_black,buf_color) - # Custom display function that only handles black - # Was included in epd2in13bc.py self._display.displayBlack(buf_black) def clear(self):