├── .gitignore ├── LICENSE ├── README.md ├── epsimplelib.py ├── font └── FreeMonoBold.ttf └── waveshare_library ├── epd2in7.py └── epdconfig.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | #IntelliJ 104 | .idea 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nicolas SAGOT 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ePaperLibrary for Waveshare e-Paper Raspberry HAT 2 | 3 | Simple library to use the [Waveshare 2.7inch e-Paper HAT](https://www.waveshare.com/wiki/2.7inch_e-Paper_HAT). 4 | 5 | This ePaper hat is cheap compared to the PaPiRus e-Paper hat. The only problem is that this device hasn't simple library, so hello there. 6 | 7 | --- 8 | 9 | **Update v1.1**: 10 | - Latest Waveshare libraries (speed up of refresh time) 11 | - update_screen not working properly 12 | - Clear screen function 13 | 14 | --- 15 | 16 | 17 | **Features**: 18 | 19 | - Use screen in Landscape or Portrait 20 | - Write simple texts 21 | - Add ligne 22 | - Not physical refresh if same image 23 | 24 | 25 | **ToDo**: 26 | 27 | - Show the calculated image when you don't have the Hat available *Soon* 28 | - Add images 29 | ​ 30 | 31 | 32 | 33 | ## Installation 34 | 35 | W.I.P 36 | 37 | 38 | 39 | ## Usage 40 | 41 | ```python 42 | import epsimplelib 43 | 44 | def hello(): 45 | esp = epsimplelib.EPScreen('landscape') # eps = e-Ink Paper Screen 46 | esp.set_title("Sheldon Cooper") 47 | 48 | esp.add_line((100, 100, 150, 100)) 49 | esp.update_screen() 50 | 51 | hello() 52 | hello() # Physical screen not refreshed 53 | ``` 54 | 55 | 56 | 57 | ## Ressources 58 | 59 | - The library uses the Original Library written by Waveshare. It can be [downloaded here](https://www.waveshare.com/wiki/File:2.7inch-e-paper-hat-code.7z). Modified to run on Python3 60 | - FreeMonoBold by GNU FreeFont 61 | 62 | 63 | 64 | ## Author 65 | 66 | - Nicolas SAGOT - @Lyoko17220 67 | 68 | 69 | 70 | 71 | ## Licence 72 | 73 | MIT. See [LICENCE.md](LICENCE.md) -------------------------------------------------------------------------------- /epsimplelib.py: -------------------------------------------------------------------------------- 1 | """ 2 | ePaperLibrary for Waveshare e-Paper 2.7" Raspberry HAT 3 | 4 | Github: https://github.com/lyoko17220/ePaperLibrary 5 | """ 6 | import time 7 | 8 | from PIL import Image, ImageFont, ImageDraw, ImageChops 9 | import os 10 | 11 | import waveshare_library.epd2in7 12 | 13 | ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) 14 | 15 | # Default font 16 | FONT_PATH = os.path.join(ROOT_DIR, 'FreeMonoBold.ttf') 17 | FONT_SMALL_MAX = ImageFont.truetype(FONT_PATH, 12) 18 | FONT_SMALL = ImageFont.truetype(FONT_PATH, 14) 19 | FONT_NORMAL = ImageFont.truetype(FONT_PATH, 18) 20 | FONT_BIG = ImageFont.truetype(FONT_PATH, 22) 21 | 22 | # Colors 23 | BLACK = 0 24 | WHITE = 255 25 | 26 | # Size (Portrait mode) 27 | DEVICE_WIDTH = 176 28 | DEVICE_HEIGHT = 264 29 | 30 | 31 | class EPScreen(): 32 | """ 33 | Manager of the screen 34 | """ 35 | 36 | def __init__(self, screen_orientation): 37 | self.device = waveshare_library.epd2in7.EPD() 38 | self.device.init() 39 | 40 | self.width = None 41 | self.height = None 42 | 43 | self.image_live = None # Currently showed/worked 44 | self.image_old = None # Old copy to compare 45 | self.draw = None 46 | 47 | if screen_orientation in ('landscape', 'portrait'): 48 | self.screen_orientation = screen_orientation 49 | self.init_image() 50 | else: 51 | raise Exception('Screen orientation not recognized') 52 | 53 | def init_image(self): 54 | """ 55 | Set width and height according to screen orientation and generate empty Image and draw 56 | """ 57 | 58 | if self.screen_orientation == 'portrait': 59 | self.width = DEVICE_WIDTH 60 | self.height = DEVICE_HEIGHT 61 | elif self.screen_orientation == 'landscape': 62 | self.width = DEVICE_HEIGHT 63 | self.height = DEVICE_WIDTH 64 | 65 | if self.image_live is None: 66 | self.image_live = Image.new('1', (self.width, self.height), 255) 67 | 68 | self.draw = ImageDraw.Draw(self.image_live) 69 | 70 | def update_screen(self, force=False): 71 | """ 72 | Send the new image to HAT 73 | :return: Screen reloaded or not 74 | """ 75 | 76 | if self.image_old is None or self.need_to_refresh() or force: 77 | if self.screen_orientation == 'landscape': 78 | self.device.display(self.device.getbuffer(self.image_live.rotate(90, expand=1))) 79 | else: 80 | self.device.display(self.device.getbuffer(self.image_live)) 81 | self.image_old = self.image_live 82 | self.init_image() 83 | time.sleep(2) 84 | return True 85 | 86 | def need_to_refresh(self): 87 | """ 88 | Return if difference between previous image and the newest 89 | :return False or True 90 | """ 91 | 92 | return ImageChops.difference(self.image_live, self.image_old).getbbox() is not None 93 | 94 | def get_width(self): 95 | """ 96 | Return width 97 | :return: Width 98 | """ 99 | return self.width 100 | 101 | def get_height(self): 102 | """ 103 | Return height 104 | :return: Height 105 | """ 106 | 107 | return self.height 108 | 109 | ## Drawing functions 110 | 111 | def add_text_middle(self, y, text, font, fill, width=-1, offset_x=0): 112 | """ 113 | Add text to middle of block 114 | :param y: y in screen 115 | :param text: text to add 116 | :param font: font to use 117 | :param fill: fill of text 118 | :param width: width of block 119 | :param offset_x: offset in x of the block 120 | :return: 121 | """ 122 | 123 | if width == -1: 124 | width = self.get_width() 125 | 126 | h_origin = font.getsize('a')[1] 127 | w, h = font.getsize(text) 128 | 129 | # Vertical offset for symbol fonts 130 | offset_y = 0 131 | if h - h_origin > 5: 132 | offset_y = h - h_origin 133 | 134 | self.draw.text((offset_x + width / 2 - w / 2, y - offset_y), text, font=font, fill=fill) 135 | 136 | def set_title(self, text): 137 | """ 138 | Add title to screen 139 | :param text: 140 | :return: 141 | """ 142 | 143 | self.draw.rectangle((0, 0, self.width, 28), fill=BLACK) 144 | self.add_text_middle(5, text, FONT_NORMAL, WHITE) 145 | 146 | def add_text(self, pos, text, font=FONT_NORMAL): 147 | """ 148 | Add simple text 149 | :param pos: (x,y) 150 | :param text: Text to add 151 | :param font: Font to use 152 | :return: 153 | """ 154 | 155 | self.draw.text(pos, text, font=font, fill=BLACK) 156 | 157 | def add_line(self, pos): 158 | """ 159 | Add line 160 | :param pos: Position xy start/end point 161 | :return: 162 | """ 163 | 164 | self.draw.line((pos[0], pos[1], pos[2], pos[3]), fill=BLACK) 165 | 166 | def clean_screen(self): 167 | """ 168 | Clean content of screen 169 | """ 170 | self.image_live = None 171 | self.init_image() 172 | self.update_screen() -------------------------------------------------------------------------------- /font/FreeMonoBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nsagot/ePaperLibrary/4e41c80b1876ce566115ff914db30a8f91638388/font/FreeMonoBold.ttf -------------------------------------------------------------------------------- /waveshare_library/epd2in7.py: -------------------------------------------------------------------------------- 1 | # /***************************************************************************** 2 | # * | File : EPD_1in54.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V3.0 8 | # * | Date : 2018-11-06 9 | # * | Info : python2 demo 10 | # * 1.Remove: 11 | # digital_write(self, pin, value) 12 | # digital_read(self, pin) 13 | # delay_ms(self, delaytime) 14 | # set_lut(self, lut) 15 | # self.lut = self.lut_full_update 16 | # * 2.Change: 17 | # display_frame -> TurnOnDisplay 18 | # set_memory_area -> SetWindow 19 | # set_memory_pointer -> SetCursor 20 | # get_frame_buffer -> getbuffer 21 | # set_frame_memory -> display 22 | # * 3.How to use 23 | # epd = epd2in7.EPD() 24 | # epd.init(epd.lut_full_update) 25 | # image = Image.new('1', (epd1in54.EPD_WIDTH, epd1in54.EPD_HEIGHT), 255) 26 | # ... 27 | # drawing ...... 28 | # ... 29 | # epd.display(getbuffer(image)) 30 | # ******************************************************************************/ 31 | # Permission is hereby granted, free of charge, to any person obtaining a copy 32 | # of this software and associated documnetation files (the "Software"), to deal 33 | # in the Software without restriction, including without limitation the rights 34 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 35 | # copies of the Software, and to permit persons to whom the Software is 36 | # furished to do so, subject to the following conditions: 37 | # 38 | # The above copyright notice and this permission notice shall be included in 39 | # all copies or substantial portions of the Software. 40 | # 41 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 42 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 43 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 44 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 45 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 46 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 47 | # THE SOFTWARE. 48 | # 49 | 50 | 51 | import waveshare_library.epdconfig as epdconfig 52 | from PIL import Image 53 | import RPi.GPIO as GPIO 54 | 55 | # Display resolution 56 | EPD_WIDTH = 176 57 | EPD_HEIGHT = 264 58 | 59 | # EPD2IN7 commands 60 | PANEL_SETTING = 0x00 61 | POWER_SETTING = 0x01 62 | POWER_OFF = 0x02 63 | POWER_OFF_SEQUENCE_SETTING = 0x03 64 | POWER_ON = 0x04 65 | POWER_ON_MEASURE = 0x05 66 | BOOSTER_SOFT_START = 0x06 67 | DEEP_SLEEP = 0x07 68 | DATA_START_TRANSMISSION_1 = 0x10 69 | DATA_STOP = 0x11 70 | DISPLAY_REFRESH = 0x12 71 | DATA_START_TRANSMISSION_2 = 0x13 72 | PARTIAL_DATA_START_TRANSMISSION_1 = 0x14 73 | PARTIAL_DATA_START_TRANSMISSION_2 = 0x15 74 | PARTIAL_DISPLAY_REFRESH = 0x16 75 | LUT_FOR_VCOM = 0x20 76 | LUT_WHITE_TO_WHITE = 0x21 77 | LUT_BLACK_TO_WHITE = 0x22 78 | LUT_WHITE_TO_BLACK = 0x23 79 | LUT_BLACK_TO_BLACK = 0x24 80 | PLL_CONTROL = 0x30 81 | TEMPERATURE_SENSOR_COMMAND = 0x40 82 | TEMPERATURE_SENSOR_CALIBRATION = 0x41 83 | TEMPERATURE_SENSOR_WRITE = 0x42 84 | TEMPERATURE_SENSOR_READ = 0x43 85 | VCOM_AND_DATA_INTERVAL_SETTING = 0x50 86 | LOW_POWER_DETECTION = 0x51 87 | TCON_SETTING = 0x60 88 | TCON_RESOLUTION = 0x61 89 | SOURCE_AND_GATE_START_SETTING = 0x62 90 | GET_STATUS = 0x71 91 | AUTO_MEASURE_VCOM = 0x80 92 | VCOM_VALUE = 0x81 93 | VCM_DC_SETTING_REGISTER = 0x82 94 | PROGRAM_MODE = 0xA0 95 | ACTIVE_PROGRAM = 0xA1 96 | READ_OTP_DATA = 0xA2 97 | 98 | class EPD: 99 | def __init__(self): 100 | self.reset_pin = epdconfig.RST_PIN 101 | self.dc_pin = epdconfig.DC_PIN 102 | self.busy_pin = epdconfig.BUSY_PIN 103 | self.width = EPD_WIDTH 104 | self.height = EPD_HEIGHT 105 | 106 | lut_vcom_dc = [ 107 | 0x00, 0x00, 108 | 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 109 | 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, 110 | 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 111 | 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, 112 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 113 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 114 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 115 | ] 116 | lut_ww = [ 117 | 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, 118 | 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 119 | 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, 120 | 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, 121 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 122 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 123 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 124 | ] 125 | lut_bw = [ 126 | 0x40, 0x08, 0x00, 0x00, 0x00, 0x02, 127 | 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 128 | 0x40, 0x14, 0x00, 0x00, 0x00, 0x01, 129 | 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, 130 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 131 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 132 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 133 | ] 134 | lut_bb = [ 135 | 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 136 | 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 137 | 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, 138 | 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 139 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 140 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 141 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 142 | ] 143 | lut_wb = [ 144 | 0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 145 | 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 146 | 0x80, 0x14, 0x00, 0x00, 0x00, 0x01, 147 | 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 148 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 149 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 150 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 151 | ] 152 | # Hardware reset 153 | def reset(self): 154 | epdconfig.digital_write(self.reset_pin, GPIO.HIGH) 155 | epdconfig.delay_ms(200) 156 | epdconfig.digital_write(self.reset_pin, GPIO.LOW) # module reset 157 | epdconfig.delay_ms(200) 158 | epdconfig.digital_write(self.reset_pin, GPIO.HIGH) 159 | epdconfig.delay_ms(200) 160 | 161 | def send_command(self, command): 162 | epdconfig.digital_write(self.dc_pin, GPIO.LOW) 163 | epdconfig.spi_writebyte([command]) 164 | 165 | def send_data(self, data): 166 | epdconfig.digital_write(self.dc_pin, GPIO.HIGH) 167 | epdconfig.spi_writebyte([data]) 168 | 169 | def wait_until_idle(self): 170 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 171 | self.send_command(0x71) 172 | epdconfig.delay_ms(100) 173 | 174 | def set_lut(self): 175 | self.send_command(LUT_FOR_VCOM) # vcom 176 | for count in range(0, 44): 177 | self.send_data(self.lut_vcom_dc[count]) 178 | self.send_command(LUT_WHITE_TO_WHITE) # ww -- 179 | for count in range(0, 42): 180 | self.send_data(self.lut_ww[count]) 181 | self.send_command(LUT_BLACK_TO_WHITE) # bw r 182 | for count in range(0, 42): 183 | self.send_data(self.lut_bw[count]) 184 | self.send_command(LUT_WHITE_TO_BLACK) # wb w 185 | for count in range(0, 42): 186 | self.send_data(self.lut_bb[count]) 187 | self.send_command(LUT_BLACK_TO_BLACK) # bb b 188 | for count in range(0, 42): 189 | self.send_data(self.lut_wb[count]) 190 | 191 | def init(self): 192 | if (epdconfig.module_init() != 0): 193 | return -1 194 | # EPD hardware init start 195 | self.reset() 196 | self.send_command(POWER_SETTING) 197 | self.send_data(0x03) # VDS_EN, VDG_EN 198 | self.send_data(0x00) # VCOM_HV, VGHL_LV[1], VGHL_LV[0] 199 | self.send_data(0x2b) # VDH 200 | self.send_data(0x2b) # VDL 201 | self.send_data(0x09) # VDHR 202 | self.send_command(BOOSTER_SOFT_START) 203 | self.send_data(0x07) 204 | self.send_data(0x07) 205 | self.send_data(0x17) 206 | # Power optimization 207 | self.send_command(0xF8) 208 | self.send_data(0x60) 209 | self.send_data(0xA5) 210 | # Power optimization 211 | self.send_command(0xF8) 212 | self.send_data(0x89) 213 | self.send_data(0xA5) 214 | # Power optimization 215 | self.send_command(0xF8) 216 | self.send_data(0x90) 217 | self.send_data(0x00) 218 | # Power optimization 219 | self.send_command(0xF8) 220 | self.send_data(0x93) 221 | self.send_data(0x2A) 222 | # Power optimization 223 | self.send_command(0xF8) 224 | self.send_data(0xA0) 225 | self.send_data(0xA5) 226 | # Power optimization 227 | self.send_command(0xF8) 228 | self.send_data(0xA1) 229 | self.send_data(0x00) 230 | # Power optimization 231 | self.send_command(0xF8) 232 | self.send_data(0x73) 233 | self.send_data(0x41) 234 | self.send_command(PARTIAL_DISPLAY_REFRESH) 235 | self.send_data(0x00) 236 | self.send_command(POWER_ON) 237 | self.wait_until_idle() 238 | 239 | self.send_command(PANEL_SETTING) 240 | self.send_data(0xAF) # KW-BF KWR-AF BWROTP 0f 241 | self.send_command(PLL_CONTROL) 242 | self.send_data(0x3A) # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ 243 | self.send_command(VCM_DC_SETTING_REGISTER) 244 | self.send_data(0x12) 245 | self.set_lut() 246 | # EPD hardware init end 247 | return 0 248 | 249 | def getbuffer(self, image): 250 | # print "bufsiz = ",(self.width/8) * self.height 251 | buf = [0xFF] * ((self.width//8) * self.height) 252 | image_monocolor = image.convert('1') 253 | imwidth, imheight = image_monocolor.size 254 | pixels = image_monocolor.load() 255 | # print "imwidth = %d, imheight = %d",imwidth,imheight 256 | if(imwidth == self.width and imheight == self.height): 257 | for y in range(imheight): 258 | for x in range(imwidth): 259 | # Set the bits for the column of pixels at the current position. 260 | if pixels[x, y] == 0: 261 | buf[(x + y * self.width) // 8] &= ~(0x80 >> (x % 8)) 262 | elif(imwidth == self.height and imheight == self.width): 263 | for y in range(imheight): 264 | for x in range(imwidth): 265 | newx = y 266 | newy = self.height - x - 1 267 | if pixels[x, y] == 0: 268 | buf[(newx + newy*self.width) // 8] &= ~(0x80 >> (y % 8)) 269 | return buf 270 | 271 | def display(self, image): 272 | self.send_command(DATA_START_TRANSMISSION_1) 273 | for i in range(0, self.width * self.height // 8): 274 | self.send_data(0xFF) 275 | self.send_command(DATA_START_TRANSMISSION_2) 276 | for i in range(0, self.width * self.height // 8): 277 | self.send_data(image[i]) 278 | self.send_command(DISPLAY_REFRESH) 279 | self.wait_until_idle() 280 | 281 | def Clear(self, color): 282 | self.send_command(DATA_START_TRANSMISSION_1) 283 | for i in range(0, self.width * self.height // 8): 284 | self.send_data(0xFF) 285 | self.send_command(DATA_START_TRANSMISSION_2) 286 | for i in range(0, self.width * self.height // 8): 287 | self.send_data(0xFF) 288 | self.send_command(DISPLAY_REFRESH) 289 | self.wait_until_idle() 290 | 291 | def sleep(self): 292 | self.send_command(0X50) 293 | self.send_data(0xf7) 294 | self.send_command(0X02) 295 | self.send_command(0X07) 296 | self.send_data(0xA5) 297 | ### END OF FILE ### 298 | 299 | -------------------------------------------------------------------------------- /waveshare_library/epdconfig.py: -------------------------------------------------------------------------------- 1 | # /***************************************************************************** 2 | # * | File : EPD_1in54.py 3 | # * | Author : Waveshare team 4 | # * | Function : Hardware underlying interface 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V2.0 8 | # * | Date : 2018-11-01 9 | # * | Info : 10 | # * 1.Remove: 11 | # digital_write(self, pin, value) 12 | # digital_read(self, pin) 13 | # delay_ms(self, delaytime) 14 | # set_lut(self, lut) 15 | # self.lut = self.lut_full_update 16 | # ******************************************************************************/ 17 | # Permission is hereby granted, free of charge, to any person obtaining a copy 18 | # of this software and associated documnetation files (the "Software"), to deal 19 | # in the Software without restriction, including without limitation the rights 20 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | # copies of the Software, and to permit persons to whom the Software is 22 | # furished to do so, subject to the following conditions: 23 | # 24 | # The above copyright notice and this permission notice shall be included in 25 | # all copies or substantial portions of the Software. 26 | # 27 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 33 | # THE SOFTWARE. 34 | # 35 | 36 | 37 | import spidev 38 | import RPi.GPIO as GPIO 39 | import time 40 | 41 | # Pin definition 42 | RST_PIN = 17 43 | DC_PIN = 25 44 | CS_PIN = 8 45 | BUSY_PIN = 24 46 | 47 | # SPI device, bus = 0, device = 0 48 | SPI = spidev.SpiDev(0, 0) 49 | 50 | 51 | def digital_write(pin, value): 52 | GPIO.output(pin, value) 53 | 54 | 55 | def digital_read(pin): 56 | return GPIO.input(BUSY_PIN) 57 | 58 | 59 | def delay_ms(delaytime): 60 | time.sleep(delaytime / 1000.0) 61 | 62 | 63 | def spi_writebyte(data): 64 | SPI.writebytes(data) 65 | 66 | 67 | def module_init(): 68 | GPIO.setmode(GPIO.BCM) 69 | GPIO.setwarnings(False) 70 | GPIO.setup(RST_PIN, GPIO.OUT) 71 | GPIO.setup(DC_PIN, GPIO.OUT) 72 | GPIO.setup(CS_PIN, GPIO.OUT) 73 | GPIO.setup(BUSY_PIN, GPIO.IN) 74 | SPI.max_speed_hz = 2000000 75 | SPI.mode = 0b00 76 | return 0; 77 | 78 | ### END OF FILE ### 79 | --------------------------------------------------------------------------------