├── .env.example ├── .github ├── funding.yml └── workflows │ └── build.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── config.py ├── controller.py ├── drivers ├── console │ └── console.py ├── display.py └── waveshare │ ├── .gitinclude │ ├── models │ └── waveshare_epd │ │ ├── epd13in3b.py │ │ ├── epd13in3k.py │ │ ├── epd1in02.py │ │ ├── epd1in54.py │ │ ├── epd1in54_V2.py │ │ ├── epd1in54b.py │ │ ├── epd1in54b_V2.py │ │ ├── epd1in54c.py │ │ ├── epd1in64g.py │ │ ├── epd2in13.py │ │ ├── epd2in13_V2.py │ │ ├── epd2in13_V3.py │ │ ├── epd2in13_V4.py │ │ ├── epd2in13b_V3.py │ │ ├── epd2in13b_V4.py │ │ ├── epd2in13bc.py │ │ ├── epd2in13d.py │ │ ├── epd2in13g.py │ │ ├── epd2in15b.py │ │ ├── epd2in15g.py │ │ ├── epd2in36g.py │ │ ├── epd2in66.py │ │ ├── epd2in66b.py │ │ ├── epd2in66g.py │ │ ├── epd2in7.py │ │ ├── epd2in7_V2.py │ │ ├── epd2in7b.py │ │ ├── epd2in7b_V2.py │ │ ├── epd2in9.py │ │ ├── epd2in9_V2.py │ │ ├── epd2in9b_V3.py │ │ ├── epd2in9b_V4.py │ │ ├── epd2in9bc.py │ │ ├── epd2in9d.py │ │ ├── epd3in0g.py │ │ ├── epd3in52.py │ │ ├── epd3in7.py │ │ ├── epd4in01f.py │ │ ├── epd4in2.py │ │ ├── epd4in26.py │ │ ├── epd4in2_V2.py │ │ ├── epd4in2b_V2.py │ │ ├── epd4in2b_V2_old.py │ │ ├── epd4in2bc.py │ │ ├── epd4in37g.py │ │ ├── epd5in65f.py │ │ ├── epd5in79.py │ │ ├── epd5in79b.py │ │ ├── epd5in79g.py │ │ ├── epd5in83.py │ │ ├── epd5in83_V2.py │ │ ├── epd5in83b_V2.py │ │ ├── epd5in83bc.py │ │ ├── epd7in3e.py │ │ ├── epd7in3f.py │ │ ├── epd7in3g.py │ │ ├── epd7in5.py │ │ ├── epd7in5_HD.py │ │ ├── epd7in5_V2.py │ │ ├── epd7in5_V2_old.py │ │ ├── epd7in5b_HD.py │ │ ├── epd7in5b_V2.py │ │ ├── epd7in5b_V2_old.py │ │ ├── epd7in5bc.py │ │ ├── epdbase.py │ │ ├── epdconfig.py │ │ └── epdsoftware.py │ └── waveshare.py ├── images ├── Font.ttc ├── ProxTag.bmp ├── ProxTag.png ├── ProxTag.xcf └── ProxTag_Screenshot.png ├── main.py ├── pve.py ├── readme.md └── requirements.txt /.env.example: -------------------------------------------------------------------------------- 1 | HOST= 2 | PORT= # defaults to 8006 if empty 3 | SSL= # defaults to true, if you run your server with a self-signed certificate enter 'false' here 4 | TOKEN 5 | 6 | DRIVER= # currently the only supported drivers are "console" and "waveshare" 7 | MODEL= # if you chose waveshare you must pick a model. Currently the only supported models are the "epd2in13v4" and "software", a software emulator of the same -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | github: josephdc96 2 | buy_me_a_coffee: josephdc96 3 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Docker Container 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | release: 10 | name: Release pushed tag 11 | runs-on: ubuntu-latest 12 | steps: 13 | - 14 | name: Set up QEMU 15 | uses: docker/setup-qemu-action@v3 16 | - 17 | name: Set up Docker Buildx 18 | uses: docker/setup-buildx-action@v3 19 | - 20 | name: Login to Docker Hub 21 | uses: docker/login-action@v3 22 | with: 23 | username: ${{ secrets.DOCKERHUB_USERNAME }} 24 | password: ${{ secrets.DOCKERHUB_TOKEN }} 25 | - 26 | name: Build and push 27 | uses: docker/build-push-action@v6 28 | with: 29 | platforms: linux/amd64,linux/arm64 30 | push: true 31 | tags: josephdc96/proxtag:latest,josephdc96/proxtag:${{ github.event.release.tag_name }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/python,venv 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=python,venv 3 | 4 | ### Python ### 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | cover/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | .pybuilder/ 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | .idea/ 90 | 91 | # pyenv 92 | # For a library or package, you might want to ignore these files since the code is 93 | # intended to run in multiple environments; otherwise, check them in: 94 | # .python-version 95 | 96 | # pipenv 97 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 98 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 99 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 100 | # install all needed dependencies. 101 | #Pipfile.lock 102 | 103 | # poetry 104 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 105 | # This is especially recommended for binary packages to ensure reproducibility, and is more 106 | # commonly ignored for libraries. 107 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 108 | #poetry.lock 109 | 110 | # pdm 111 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 112 | #pdm.lock 113 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 114 | # in version control. 115 | # https://pdm.fming.dev/#use-with-ide 116 | .pdm.toml 117 | 118 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 119 | __pypackages__/ 120 | 121 | # Celery stuff 122 | celerybeat-schedule 123 | celerybeat.pid 124 | 125 | # SageMath parsed files 126 | *.sage.py 127 | 128 | # Environments 129 | .env 130 | .venv 131 | env*/ 132 | venv*/ 133 | ENV*/ 134 | env.bak/ 135 | venv.bak/ 136 | 137 | # Spyder project settings 138 | .spyderproject 139 | .spyproject 140 | 141 | # Rope project settings 142 | .ropeproject 143 | 144 | # mkdocs documentation 145 | /site 146 | 147 | # mypy 148 | .mypy_cache/ 149 | .dmypy.json 150 | dmypy.json 151 | 152 | # Pyre type checker 153 | .pyre/ 154 | 155 | # pytype static type analyzer 156 | .pytype/ 157 | 158 | # Cython debug symbols 159 | cython_debug/ 160 | 161 | # PyCharm 162 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 163 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 164 | # and can be added to the global gitignore or merged into this file. For a more nuclear 165 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 166 | #.idea/ 167 | 168 | ### Python Patch ### 169 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 170 | poetry.toml 171 | 172 | # ruff 173 | .ruff_cache/ 174 | 175 | # LSP config files 176 | pyrightconfig.json 177 | 178 | ### venv ### 179 | # Virtualenv 180 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 181 | [Bb]in 182 | [Ii]nclude 183 | [Ll]ib 184 | [Ll]ib64 185 | [Ll]ocal 186 | [Ss]cripts 187 | pyvenv.cfg 188 | pip-selfcheck.json 189 | 190 | # End of https://www.toptal.com/developers/gitignore/api/python,venv 191 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12 2 | 3 | COPY requirements.txt . 4 | RUN pip install -r requirements.txt 5 | COPY . . 6 | CMD ["python", "main.py"] -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | class Config: 2 | def __init__(self, host, token, port, ssl, driver, model): 3 | self.host = host 4 | self.token = token 5 | self.port = port 6 | self.ssl = ssl 7 | self.driver = driver 8 | self.model = model -------------------------------------------------------------------------------- /controller.py: -------------------------------------------------------------------------------- 1 | from drivers.display import Display 2 | from config import Config 3 | from pve import PVE 4 | 5 | class Controller: 6 | def __init__(self, config: Config): 7 | self.node = None 8 | self.display = None 9 | self.config = config 10 | self.pve = PVE(config) 11 | 12 | def set_display(self, display: Display) -> None: 13 | self.display = display 14 | 15 | def launch(self, node = None): 16 | self.node = node 17 | self.display_vms() 18 | 19 | def display_vms(self): 20 | vms = self.pve.get_vms() 21 | self.display.display_vms(vms) 22 | 23 | def display_vm(self, node, id): 24 | vm = self.pve.get_vm_config(node, id) 25 | status = self.pve.get_vm_status(node, id) 26 | if vm is None or status is None: 27 | raise Exception("VM not found") 28 | self.display.display_vm(vm, status, node, id) 29 | 30 | def start_vm(self, node, id): 31 | return self.pve.start_vm(node, id) 32 | 33 | def stop_vm(self, node, id): 34 | return self.pve.stop_vm(node, id) 35 | 36 | def restart_vm(self, node, id): 37 | return self.pve.restart_vm(node, id) 38 | 39 | def shutdown_vm(self, node, id): 40 | return self.pve.shutdown_vm(node, id) 41 | 42 | def reset_vm(self, node, id): 43 | return self.pve.reset_vm(node, id) 44 | 45 | def pause_vm(self, node, id): 46 | return self.pve.pause_vm(node, id) 47 | 48 | def suspend_vm(self, node, id): 49 | return self.pve.suspend_vm(node, id) -------------------------------------------------------------------------------- /drivers/console/console.py: -------------------------------------------------------------------------------- 1 | from os import system, name 2 | 3 | from drivers.display import Display 4 | 5 | 6 | def clear(): 7 | 8 | # for windows 9 | if name == 'nt': 10 | _ = system('cls') 11 | 12 | # for mac and linux(here, os.name is 'posix') 13 | else: 14 | _ = system('clear') 15 | 16 | class Console(Display): 17 | def __init__(self, controller): 18 | self.controller = controller 19 | 20 | def initialize(self): 21 | clear() 22 | self.controller.launch() 23 | 24 | def display_vms(self, vms): 25 | clear() 26 | while True: 27 | print("Current Virtual Machines:") 28 | for vm in vms: 29 | try: 30 | name = vm["name"] 31 | except KeyError: 32 | name = "Unknown" 33 | print(f'{vm["vmid"]}\t{name}\t{vm["status"]}') 34 | print("\nEnter VM id or type 'exit' to exit") 35 | resp = input("> ") 36 | if resp == 'exit': 37 | break 38 | try: 39 | resp = int(resp) 40 | for vm in vms: 41 | if vm["vmid"] == resp: 42 | break 43 | else: 44 | x = None 45 | 46 | if vm is None: 47 | print("VM not found") 48 | continue 49 | 50 | self.controller.display_vm(vm["node"], vm["vmid"]) 51 | except ValueError: 52 | print("Please enter valid VM id") 53 | 54 | def display_vm(self, vm, status, node, id): 55 | clear() 56 | cont = True 57 | while cont: 58 | print(f'Current Virtual Machine: {vm["name"]}') 59 | print() 60 | print("Options:") 61 | opt = 1 62 | if status["status"] == "running": 63 | print("\t1: Shut Down VM") 64 | print("\t2: Reboot VM") 65 | print("\t3: Pause VM") 66 | print("\t4: Hibernate VM") 67 | print("\t5: Stop VM") 68 | print("\t6: Reset VM") 69 | opt = 7 70 | else: 71 | print("\t1: Start VM") 72 | opt = 2 73 | print(f"\t{opt}: Go Back") 74 | resp = input("> ") 75 | 76 | try: 77 | resp = int(resp) 78 | if status["status"] == "running": 79 | match resp: 80 | case 1: 81 | self.controller.shutdown_vm(node, id) 82 | case 2: 83 | self.controller.reboot_vm(node, id) 84 | case 3: 85 | self.controller.suspend_vm(node, id) 86 | case 4: 87 | self.controller.suspend_vm(node, id) 88 | case 5: 89 | self.controller.stop_vm(node, id) 90 | case 6: 91 | self.controller.reset_vm(node, id) 92 | case 7: 93 | cont = False 94 | case _: 95 | print("Invalid option") 96 | else: 97 | match resp: 98 | case 1: 99 | self.controller.start_vm(node, id) 100 | case 2: 101 | cont = False 102 | case _: 103 | print("Invalid option") 104 | except ValueError: 105 | print("Please enter valid option") -------------------------------------------------------------------------------- /drivers/display.py: -------------------------------------------------------------------------------- 1 | from abc import abstractmethod 2 | 3 | 4 | class Display: 5 | @abstractmethod 6 | def initialize(self): 7 | pass 8 | 9 | @abstractmethod 10 | def display_vms(self, vms): 11 | pass 12 | 13 | @abstractmethod 14 | def display_vm(self, vm, status, node, id): 15 | pass -------------------------------------------------------------------------------- /drivers/waveshare/.gitinclude: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephdc96/ProxTag/5982a3f0049ad79c1a81feb5598046a4ab1d0811/drivers/waveshare/.gitinclude -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd1in54b_V2.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd1in54b.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V4.1 8 | # * | Date : 2022-08-10 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | import logging 31 | from . import epdconfig 32 | 33 | # Display resolution 34 | EPD_WIDTH = 200 35 | EPD_HEIGHT = 200 36 | 37 | logger = logging.getLogger(__name__) 38 | 39 | class EPD: 40 | def __init__(self): 41 | self.reset_pin = epdconfig.RST_PIN 42 | self.dc_pin = epdconfig.DC_PIN 43 | self.busy_pin = epdconfig.BUSY_PIN 44 | self.cs_pin = epdconfig.CS_PIN 45 | self.width = EPD_WIDTH 46 | self.height = EPD_HEIGHT 47 | 48 | 49 | # Hardware reset 50 | def reset(self): 51 | epdconfig.digital_write(self.reset_pin, 1) 52 | epdconfig.delay_ms(200) 53 | epdconfig.digital_write(self.reset_pin, 0) # module reset 54 | epdconfig.delay_ms(5) 55 | epdconfig.digital_write(self.reset_pin, 1) 56 | epdconfig.delay_ms(200) 57 | 58 | def send_command(self, command): 59 | epdconfig.digital_write(self.dc_pin, 0) 60 | epdconfig.digital_write(self.cs_pin, 0) 61 | epdconfig.spi_writebyte([command]) 62 | epdconfig.digital_write(self.cs_pin, 1) 63 | 64 | def send_data(self, data): 65 | epdconfig.digital_write(self.dc_pin, 1) 66 | epdconfig.digital_write(self.cs_pin, 0) 67 | epdconfig.spi_writebyte([data]) 68 | epdconfig.digital_write(self.cs_pin, 1) 69 | 70 | # send a lot of data 71 | def send_data2(self, data): 72 | epdconfig.digital_write(self.dc_pin, 1) 73 | epdconfig.digital_write(self.cs_pin, 0) 74 | epdconfig.spi_writebyte2(data) 75 | epdconfig.digital_write(self.cs_pin, 1) 76 | 77 | def ReadBusy(self): 78 | logger.debug("e-Paper busy") 79 | while(epdconfig.digital_read(self.busy_pin) == 1): 80 | epdconfig.delay_ms(100) 81 | logger.debug("e-Paper busy release") 82 | 83 | def init(self): 84 | if (epdconfig.module_init() != 0): 85 | return -1 86 | # EPD hardware init start 87 | self.reset() 88 | 89 | self.ReadBusy() 90 | self.send_command(0x12) #SWRESET 91 | self.ReadBusy() 92 | 93 | self.send_command(0x01) #Driver output control 94 | self.send_data(0xC7) 95 | self.send_data(0x00) 96 | self.send_data(0x01) 97 | 98 | self.send_command(0x11) #data entry mode 99 | self.send_data(0x01) 100 | 101 | self.send_command(0x44) #set Ram-X address start/end position 102 | self.send_data(0x00) 103 | self.send_data(0x18) #0x18-->(24+1)*8=200 104 | 105 | self.send_command(0x45) #set Ram-Y address start/end position 106 | self.send_data(0xC7) #0xC7-->(199+1)=200 107 | self.send_data(0x00) 108 | self.send_data(0x00) 109 | self.send_data(0x00) 110 | 111 | self.send_command(0x3C) #BorderWavefrom 112 | self.send_data(0x05) 113 | 114 | self.send_command(0x18) #Read built-in temperature sensor 115 | self.send_data(0x80) 116 | 117 | self.send_command(0x4E) # set RAM x address count to 0 118 | self.send_data(0x00) 119 | self.send_command(0x4F) # set RAM y address count to 0X199 120 | self.send_data(0xC7) 121 | self.send_data(0x00) 122 | self.ReadBusy() 123 | return 0 124 | 125 | def getbuffer(self, image): 126 | buf = [0xFF] * int(self.width * self.height / 8) 127 | # Set buffer to value of Python Imaging Library image. 128 | # Image must be in mode 1. 129 | image_monocolor = image.convert('1') 130 | imwidth, imheight = image_monocolor.size 131 | if imwidth != self.width or imheight != self.height: 132 | raise ValueError('Image must be same dimensions as display \ 133 | ({0}x{1}).' .format(self.width, self.height)) 134 | 135 | pixels = image_monocolor.load() 136 | for y in range(self.height): 137 | for x in range(self.width): 138 | # Set the bits for the column of pixels at the current position. 139 | if pixels[x, y] == 0: 140 | buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) 141 | return buf 142 | 143 | def display(self, blackimage, redimage): 144 | 145 | if self.width%8 == 0: 146 | linewidth = int(self.width/8) 147 | else: 148 | linewidth = int(self.width/8) + 1 149 | 150 | buf = [0x00] * self.height * linewidth 151 | 152 | # send black data 153 | if (blackimage != None): 154 | self.send_command(0x24) # DATA_START_TRANSMISSION_1 155 | self.send_data2(blackimage) 156 | 157 | # send red data 158 | if (redimage != None): 159 | self.send_command(0x26) # DATA_START_TRANSMISSION_2 160 | for i in range(0, int(self.width * self.height / 8)): 161 | buf[i] = ~redimage[i] 162 | self.send_data2(buf) 163 | 164 | self.send_command(0x22) # DISPLAY_REFRESH 165 | self.send_data(0xF7) 166 | self.send_command(0x20) # DISPLAY_REFRESH 167 | self.ReadBusy() 168 | 169 | def Clear(self): 170 | if self.width%8 == 0: 171 | linewidth = int(self.width/8) 172 | else: 173 | linewidth = int(self.width/8) + 1 174 | 175 | self.send_command(0x24) # DATA_START_TRANSMISSION_1 176 | self.send_data2([0xff] * int(self.height * linewidth)) 177 | 178 | self.send_command(0x26) # DATA_START_TRANSMISSION_2 179 | self.send_data2([0x00] * int(self.height * linewidth)) 180 | 181 | self.send_command(0x22) # DISPLAY_REFRESH 182 | self.send_data(0xF7) 183 | self.send_command(0x20) # DISPLAY_REFRESH 184 | self.ReadBusy() 185 | 186 | 187 | def sleep(self): 188 | self.send_command(0x10) #enter deep sleep 189 | self.send_data(0x01) 190 | 191 | epdconfig.delay_ms(2000) 192 | epdconfig.module_exit() 193 | 194 | ### END OF FILE ### 195 | 196 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd1in54c.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd1in54c.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V4.0 8 | # * | Date : 2019-06-20 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | import logging 30 | from . import epdconfig 31 | 32 | # Display resolution 33 | EPD_WIDTH = 152 34 | EPD_HEIGHT = 152 35 | 36 | logger = logging.getLogger(__name__) 37 | 38 | class EPD: 39 | def __init__(self): 40 | self.reset_pin = epdconfig.RST_PIN 41 | self.dc_pin = epdconfig.DC_PIN 42 | self.busy_pin = epdconfig.BUSY_PIN 43 | self.cs_pin = epdconfig.CS_PIN 44 | self.width = EPD_WIDTH 45 | self.height = EPD_HEIGHT 46 | 47 | # Hardware reset 48 | def reset(self): 49 | epdconfig.digital_write(self.reset_pin, 1) 50 | epdconfig.delay_ms(10) 51 | epdconfig.digital_write(self.reset_pin, 0) 52 | epdconfig.delay_ms(1) 53 | epdconfig.digital_write(self.reset_pin, 1) 54 | epdconfig.delay_ms(10) 55 | 56 | def send_command(self, command): 57 | epdconfig.digital_write(self.dc_pin, 0) 58 | epdconfig.digital_write(self.cs_pin, 0) 59 | epdconfig.spi_writebyte([command]) 60 | epdconfig.digital_write(self.cs_pin, 1) 61 | 62 | def send_data(self, data): 63 | epdconfig.digital_write(self.dc_pin, 1) 64 | epdconfig.digital_write(self.cs_pin, 0) 65 | epdconfig.spi_writebyte([data]) 66 | epdconfig.digital_write(self.cs_pin, 1) 67 | 68 | def ReadBusy(self): 69 | logger.debug("e-Paper busy") 70 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 71 | epdconfig.delay_ms(200) 72 | logger.debug("e-Paper busy release") 73 | 74 | def init(self): 75 | if (epdconfig.module_init() != 0): 76 | return -1 77 | # EPD hardware init start 78 | self.reset() 79 | 80 | self.send_command(0x06) # boost soft start 81 | self.send_data(0x17) 82 | self.send_data(0x17) 83 | self.send_data(0x17) 84 | self.send_command(0x04) # power on 85 | 86 | self.ReadBusy() 87 | 88 | self.send_command(0x00) # panel setting 89 | self.send_data(0x0f) # LUT from OTP,160x296 90 | self.send_data(0x0d) # VCOM to 0V fast 91 | 92 | self.send_command(0x61) # resolution setting 93 | self.send_data(0x98) 94 | self.send_data(0x00) 95 | self.send_data(0x98) 96 | 97 | self.send_command(0x50) 98 | self.send_data(0x77) 99 | 100 | def getbuffer(self, image): 101 | buf = [0xFF] * (int(self.width/8) * self.height) 102 | image_monocolor = image.convert('1') 103 | imwidth, imheight = image_monocolor.size 104 | pixels = image_monocolor.load() 105 | if(imwidth == self.width and imheight == self.height): 106 | logger.debug("Horizontal") 107 | for y in range(imheight): 108 | for x in range(imwidth): 109 | # Set the bits for the column of pixels at the current position. 110 | if pixels[x, y] == 0: 111 | buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) 112 | elif(imwidth == self.height and imheight == self.width): 113 | logger.debug("Vertical") 114 | for y in range(imheight): 115 | for x in range(imwidth): 116 | newx = y 117 | newy = self.height - x - 1 118 | if pixels[x, y] == 0: 119 | buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) 120 | return buf 121 | 122 | def display(self, blackimage, yellowimage): 123 | self.send_command(0x10) 124 | logger.debug("blackimage") 125 | for i in range(0, int(self.width * self.height / 8)): 126 | self.send_data(blackimage[i]) 127 | self.send_command(0x13) 128 | logger.debug("yellowimage") 129 | for i in range(0, int(self.width * self.height / 8)): 130 | self.send_data(yellowimage[i]) 131 | 132 | self.send_command(0x12) 133 | self.ReadBusy() 134 | 135 | def Clear(self): 136 | self.send_command(0x10) 137 | for i in range(0, int(self.width * self.height / 8)): 138 | self.send_data(0xFF) 139 | self.send_command(0x13) 140 | for i in range(0, int(self.width * self.height / 8)): 141 | self.send_data(0xFF) 142 | 143 | self.send_command(0x12) 144 | self.ReadBusy() 145 | 146 | # after this, call epd.init() to awaken the module 147 | def sleep(self): 148 | self.send_command(0X02) # power off 149 | self.ReadBusy() 150 | self.send_command(0X07) # deep sleep 151 | self.send_data(0xA5) 152 | 153 | epdconfig.delay_ms(2000) 154 | epdconfig.module_exit() 155 | ### END OF FILE ### 156 | 157 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd1in64g.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd1in64g.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V1 8 | # * | Date : 2022-07-20 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # ******************************************************************************/ 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy 13 | # of this software and associated documnetation files (the "Software"), to deal 14 | # in the Software without restriction, including without limitation the rights 15 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | # copies of the Software, and to permit persons to whom the Software is 17 | # furished to do so, subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in 20 | # all copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | # THE SOFTWARE. 29 | # 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | from PIL import Image 35 | 36 | # Display resolution 37 | EPD_WIDTH = 168 38 | EPD_HEIGHT = 168 39 | 40 | logger = logging.getLogger(__name__) 41 | 42 | class EPD: 43 | def __init__(self): 44 | self.reset_pin = epdconfig.RST_PIN 45 | self.dc_pin = epdconfig.DC_PIN 46 | self.busy_pin = epdconfig.BUSY_PIN 47 | self.cs_pin = epdconfig.CS_PIN 48 | self.width = EPD_WIDTH 49 | self.height = EPD_HEIGHT 50 | self.BLACK = 0x000000 # 00 BGR 51 | self.WHITE = 0xffffff # 01 52 | self.YELLOW = 0x00ffff # 10 53 | self.RED = 0x0000ff # 11 54 | 55 | # Hardware reset 56 | def reset(self): 57 | epdconfig.digital_write(self.reset_pin, 1) 58 | epdconfig.delay_ms(200) 59 | epdconfig.digital_write(self.reset_pin, 0) # module reset 60 | epdconfig.delay_ms(2) 61 | epdconfig.digital_write(self.reset_pin, 1) 62 | epdconfig.delay_ms(200) 63 | 64 | def send_command(self, command): 65 | epdconfig.digital_write(self.dc_pin, 0) 66 | epdconfig.digital_write(self.cs_pin, 0) 67 | epdconfig.spi_writebyte([command]) 68 | epdconfig.digital_write(self.cs_pin, 1) 69 | 70 | def send_data(self, data): 71 | epdconfig.digital_write(self.dc_pin, 1) 72 | epdconfig.digital_write(self.cs_pin, 0) 73 | epdconfig.spi_writebyte([data]) 74 | epdconfig.digital_write(self.cs_pin, 1) 75 | 76 | def ReadBusyH(self): 77 | logger.debug("e-Paper busy H") 78 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 79 | epdconfig.delay_ms(5) 80 | logger.debug("e-Paper busy H release") 81 | 82 | def ReadBusyL(self): 83 | logger.debug("e-Paper busy L") 84 | while(epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle 85 | epdconfig.delay_ms(5) 86 | logger.debug("e-Paper busy L release") 87 | 88 | def TurnOnDisplay(self): 89 | self.send_command(0x12) # DISPLAY_REFRESH 90 | self.send_data(0x01) 91 | self.ReadBusyH() 92 | 93 | self.send_command(0x02) # POWER_OFF 94 | self.send_data(0X00) 95 | self.ReadBusyH() 96 | 97 | def init(self): 98 | if (epdconfig.module_init() != 0): 99 | return -1 100 | # EPD hardware init start 101 | 102 | self.reset() 103 | 104 | self.send_command(0x66) 105 | self.send_data(0x49) 106 | self.send_data(0x55) 107 | self.send_data(0x13) 108 | self.send_data(0x5D) 109 | 110 | self.send_command(0x66) 111 | self.send_data(0x49) 112 | self.send_data(0x55) 113 | 114 | self.send_command(0xB0) 115 | self.send_data(0x03) 116 | 117 | self.send_command(0x00) 118 | self.send_data(0x4F) 119 | self.send_data(0x6B) 120 | 121 | self.send_command(0x03) 122 | self.send_data(0x00) 123 | 124 | self.send_command(0xF0) 125 | self.send_data(0xF6) 126 | self.send_data(0x0D) 127 | self.send_data(0x00) 128 | self.send_data(0x00) 129 | self.send_data(0x00) 130 | 131 | self.send_command(0x06) 132 | self.send_data(0xCF) 133 | self.send_data(0xDF) 134 | self.send_data(0x0F) 135 | 136 | self.send_command(0x41) 137 | self.send_data(0x00) 138 | 139 | self.send_command(0x50) 140 | self.send_data(0x30) 141 | 142 | self.send_command(0x60) 143 | self.send_data(0x0C) 144 | self.send_data(0x05) 145 | 146 | self.send_command(0x61) 147 | self.send_data(0xA8) 148 | self.send_data(0x00) 149 | self.send_data(0xA8) 150 | 151 | self.send_command(0x84) 152 | self.send_data(0x01) 153 | return 0 154 | 155 | def getbuffer(self, image): 156 | # Create a pallette with the 4 colors supported by the panel 157 | pal_image = Image.new("P", (1,1)) 158 | pal_image.putpalette( (0,0,0, 255,255,255, 255,255,0, 255,0,0) + (0,0,0)*252) 159 | 160 | # Check if we need to rotate the image 161 | imwidth, imheight = image.size 162 | if(imwidth == self.width and imheight == self.height): 163 | image_temp = image 164 | elif(imwidth == self.height and imheight == self.width): 165 | image_temp = image.rotate(90, expand=True) 166 | else: 167 | logger.warning("Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height)) 168 | 169 | # Convert the soruce image to the 4 colors, dithering if needed 170 | image_4color = image_temp.convert("RGB").quantize(palette=pal_image) 171 | buf_4color = bytearray(image_4color.tobytes('raw')) 172 | 173 | # into a single byte to transfer to the panel 174 | buf = [0x00] * int(self.width * self.height / 4) 175 | idx = 0 176 | for i in range(0, len(buf_4color), 4): 177 | buf[idx] = (buf_4color[i] << 6) + (buf_4color[i+1] << 4) + (buf_4color[i+2] << 2) + buf_4color[i+3] 178 | idx += 1 179 | 180 | return buf 181 | 182 | def display(self, image): 183 | if self.width % 4 == 0 : 184 | Width = self.width // 4 185 | else : 186 | Width = self.width // 4 + 1 187 | Height = self.height 188 | 189 | self.send_command(0x68) 190 | self.send_data(0x01) 191 | 192 | self.send_command(0x04) 193 | self.ReadBusyH() 194 | 195 | self.send_command(0x10) 196 | for j in range(0, Height): 197 | for i in range(0, Width): 198 | self.send_data(image[i + j * Width]) 199 | 200 | self.send_command(0x68) 201 | self.send_data(0x00) 202 | 203 | self.TurnOnDisplay() 204 | 205 | def Clear(self, color=0x55): 206 | if self.width % 4 == 0 : 207 | Width = self.width // 4 208 | else : 209 | Width = self.width // 4 + 1 210 | Height = self.height 211 | 212 | self.send_command(0x68) 213 | self.send_data(0x01) 214 | 215 | self.send_command(0x04) 216 | self.ReadBusyH() 217 | 218 | self.send_command(0x10) 219 | for j in range(0, Height): 220 | for i in range(0, Width): 221 | self.send_data(color) 222 | 223 | self.send_command(0x68) 224 | self.send_data(0x00) 225 | 226 | self.TurnOnDisplay() 227 | 228 | def sleep(self): 229 | self.send_command(0x02) # POWER_OFF 230 | self.send_data(0x00) 231 | 232 | self.send_command(0x07) # DEEP_SLEEP 233 | self.send_data(0XA5) 234 | 235 | epdconfig.delay_ms(2000) 236 | epdconfig.module_exit() 237 | ### END OF FILE ### 238 | 239 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd2in13b_V3.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd2in13bc.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V4.0 8 | # * | Date : 2019-06-20 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | import logging 31 | from . import epdconfig 32 | 33 | # Display resolution 34 | EPD_WIDTH = 104 35 | EPD_HEIGHT = 212 36 | 37 | logger = logging.getLogger(__name__) 38 | 39 | class EPD: 40 | def __init__(self): 41 | self.reset_pin = epdconfig.RST_PIN 42 | self.dc_pin = epdconfig.DC_PIN 43 | self.busy_pin = epdconfig.BUSY_PIN 44 | self.cs_pin = epdconfig.CS_PIN 45 | self.width = EPD_WIDTH 46 | self.height = EPD_HEIGHT 47 | 48 | # Hardware reset 49 | def reset(self): 50 | epdconfig.digital_write(self.reset_pin, 1) 51 | epdconfig.delay_ms(200) 52 | epdconfig.digital_write(self.reset_pin, 0) 53 | epdconfig.delay_ms(2) 54 | epdconfig.digital_write(self.reset_pin, 1) 55 | epdconfig.delay_ms(200) 56 | 57 | def send_command(self, command): 58 | epdconfig.digital_write(self.dc_pin, 0) 59 | epdconfig.digital_write(self.cs_pin, 0) 60 | epdconfig.spi_writebyte([command]) 61 | epdconfig.digital_write(self.cs_pin, 1) 62 | 63 | def send_data(self, data): 64 | epdconfig.digital_write(self.dc_pin, 1) 65 | epdconfig.digital_write(self.cs_pin, 0) 66 | epdconfig.spi_writebyte([data]) 67 | epdconfig.digital_write(self.cs_pin, 1) 68 | 69 | def ReadBusy(self): 70 | logger.debug("e-Paper busy") 71 | self.send_command(0x71); 72 | while(epdconfig.digital_read(self.busy_pin) == 0): 73 | self.send_command(0x71); 74 | epdconfig.delay_ms(100) 75 | logger.debug("e-Paper busy release") 76 | 77 | def init(self): 78 | if (epdconfig.module_init() != 0): 79 | return -1 80 | 81 | self.reset() 82 | self.send_command(0x04); 83 | self.ReadBusy();#waiting for the electronic paper IC to release the idle signal 84 | 85 | self.send_command(0x00); #panel setting 86 | self.send_data(0x0f); #LUT from OTP,128x296 87 | self.send_data(0x89); #Temperature sensor, boost and other related timing settings 88 | 89 | self.send_command(0x61); #resolution setting 90 | self.send_data (0x68); 91 | self.send_data (0x00); 92 | self.send_data (0xD4); 93 | 94 | self.send_command(0X50); #VCOM AND DATA INTERVAL SETTING 95 | self.send_data(0x77); #WBmode:VBDF 17|D7 VBDW 97 VBDB 57 96 | # WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7 97 | 98 | return 0 99 | 100 | def getbuffer(self, image): 101 | # logger.debug("bufsiz = ",int(self.width/8) * self.height) 102 | buf = [0xFF] * (int(self.width/8) * self.height) 103 | image_monocolor = image.convert('1') 104 | imwidth, imheight = image_monocolor.size 105 | pixels = image_monocolor.load() 106 | # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) 107 | if(imwidth == self.width and imheight == self.height): 108 | logger.debug("Vertical") 109 | for y in range(imheight): 110 | for x in range(imwidth): 111 | # Set the bits for the column of pixels at the current position. 112 | if pixels[x, y] == 0: 113 | buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) 114 | elif(imwidth == self.height and imheight == self.width): 115 | logger.debug("Horizontal") 116 | for y in range(imheight): 117 | for x in range(imwidth): 118 | newx = y 119 | newy = self.height - x - 1 120 | if pixels[x, y] == 0: 121 | buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) 122 | return buf 123 | 124 | def display(self, imageblack, imagered): 125 | self.send_command(0x10) 126 | for i in range(0, int(self.width * self.height / 8)): 127 | self.send_data(imageblack[i]) 128 | 129 | self.send_command(0x13) 130 | for i in range(0, int(self.width * self.height / 8)): 131 | self.send_data(imagered[i]) 132 | 133 | self.send_command(0x12) # REFRESH 134 | epdconfig.delay_ms(100) 135 | self.ReadBusy() 136 | 137 | def Clear(self): 138 | self.send_command(0x10) 139 | for i in range(0, int(self.width * self.height / 8)): 140 | self.send_data(0xFF) 141 | 142 | self.send_command(0x13) 143 | for i in range(0, int(self.width * self.height / 8)): 144 | self.send_data(0xFF) 145 | 146 | self.send_command(0x12) # REFRESH 147 | epdconfig.delay_ms(100) 148 | self.ReadBusy() 149 | 150 | def sleep(self): 151 | self.send_command(0X50) 152 | self.send_data(0xf7) 153 | self.send_command(0X02) 154 | self.ReadBusy() 155 | self.send_command(0x07) # DEEP_SLEEP 156 | self.send_data(0xA5) # check code 157 | 158 | epdconfig.delay_ms(2000) 159 | epdconfig.module_exit() 160 | ### END OF FILE ### 161 | 162 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd2in13b_V4.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd2in13b_V4.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V1.0 8 | # * | Date : 2022-04-21 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | import logging 31 | from . import epdconfig 32 | 33 | # Display resolution 34 | EPD_WIDTH = 122 35 | EPD_HEIGHT = 250 36 | 37 | logger = logging.getLogger(__name__) 38 | 39 | class EPD: 40 | def __init__(self): 41 | self.reset_pin = epdconfig.RST_PIN 42 | self.dc_pin = epdconfig.DC_PIN 43 | self.busy_pin = epdconfig.BUSY_PIN 44 | self.cs_pin = epdconfig.CS_PIN 45 | self.width = EPD_WIDTH 46 | self.height = EPD_HEIGHT 47 | 48 | # hardware reset 49 | def reset(self): 50 | epdconfig.digital_write(self.reset_pin, 1) 51 | epdconfig.delay_ms(20) 52 | epdconfig.digital_write(self.reset_pin, 0) 53 | epdconfig.delay_ms(2) 54 | epdconfig.digital_write(self.reset_pin, 1) 55 | epdconfig.delay_ms(20) 56 | 57 | # send 1 byte command 58 | def send_command(self, command): 59 | epdconfig.digital_write(self.dc_pin, 0) 60 | epdconfig.digital_write(self.cs_pin, 0) 61 | epdconfig.spi_writebyte([command]) 62 | epdconfig.digital_write(self.cs_pin, 1) 63 | 64 | # send 1 byte data 65 | def send_data(self, data): 66 | epdconfig.digital_write(self.dc_pin, 1) 67 | epdconfig.digital_write(self.cs_pin, 0) 68 | epdconfig.spi_writebyte([data]) 69 | epdconfig.digital_write(self.cs_pin, 1) 70 | 71 | # send a lot of data 72 | def send_data2(self, data): 73 | epdconfig.digital_write(self.dc_pin, 1) 74 | epdconfig.digital_write(self.cs_pin, 0) 75 | epdconfig.spi_writebyte2(data) 76 | epdconfig.digital_write(self.cs_pin, 1) 77 | 78 | # judge e-Paper whether is busy 79 | def busy(self): 80 | logger.debug("e-Paper busy") 81 | while(epdconfig.digital_read(self.busy_pin) != 0): 82 | epdconfig.delay_ms(10) 83 | logger.debug("e-Paper busy release") 84 | 85 | # set the display window 86 | def set_windows(self, xstart, ystart, xend, yend): 87 | self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION 88 | self.send_data((xstart>>3) & 0xff) 89 | self.send_data((xend>>3) & 0xff) 90 | 91 | self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION 92 | self.send_data(ystart & 0xff) 93 | self.send_data((ystart >> 8) & 0xff) 94 | self.send_data(yend & 0xff) 95 | self.send_data((yend >> 8) & 0xff) 96 | 97 | # set the display cursor(origin) 98 | def set_cursor(self, xstart, ystart): 99 | self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER 100 | self.send_data(xstart & 0xff) 101 | 102 | self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER 103 | self.send_data(ystart & 0xff) 104 | self.send_data((ystart >> 8) & 0xff) 105 | 106 | # initialize 107 | def init(self): 108 | if (epdconfig.module_init() != 0): 109 | return -1 110 | 111 | self.reset() 112 | 113 | self.busy() 114 | self.send_command(0x12) # SWRESET 115 | self.busy() 116 | 117 | self.send_command(0x01) # Driver output control 118 | self.send_data(0xf9) 119 | self.send_data(0x00) 120 | self.send_data(0x00) 121 | 122 | self.send_command(0x11) # data entry mode 123 | self.send_data(0x03) 124 | 125 | self.set_windows(0, 0, self.width - 1, self.height - 1) 126 | self.set_cursor(0, 0) 127 | 128 | self.send_command(0x3C) # BorderWavefrom 129 | self.send_data(0x05) 130 | 131 | self.send_command(0x18) # Read built-in temperature sensor 132 | self.send_data(0x80) 133 | 134 | self.send_command(0x21) # Display update control 135 | self.send_data(0x80) 136 | self.send_data(0x80) 137 | 138 | self.busy() 139 | 140 | return 0 141 | 142 | # turn on display 143 | def ondisplay(self): 144 | self.send_command(0x20) 145 | self.busy() 146 | 147 | # image converted to bytearray 148 | def getbuffer(self, image): 149 | img = image 150 | imwidth, imheight = img.size 151 | if(imwidth == self.width and imheight == self.height): 152 | img = img.convert('1') 153 | elif(imwidth == self.height and imheight == self.width): 154 | # image has correct dimensions, but needs to be rotated 155 | img = img.rotate(90, expand=True).convert('1') 156 | else: 157 | logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) 158 | # return a blank buffer 159 | return [0x00] * (int(self.width/8) * self.height) 160 | 161 | buf = bytearray(img.tobytes('raw')) 162 | return buf 163 | 164 | # display image 165 | def display(self, imageblack, imagered): 166 | self.send_command(0x24) 167 | self.send_data2(imageblack) 168 | 169 | self.send_command(0x26) 170 | self.send_data2(imagered) 171 | 172 | self.ondisplay() 173 | 174 | # display white image 175 | def clear(self): 176 | if self.width%8 == 0: 177 | linewidth = int(self.width/8) 178 | else: 179 | linewidth = int(self.width/8) + 1 180 | 181 | buf = [0xff] * (int(linewidth * self.height)) 182 | 183 | self.send_command(0x24) 184 | self.send_data2(buf) 185 | 186 | self.send_command(0x26) 187 | self.send_data2(buf) 188 | 189 | self.ondisplay() 190 | 191 | # Compatible with older version functions 192 | def Clear(self): 193 | self.clear() 194 | 195 | # sleep 196 | def sleep(self): 197 | self.send_command(0x10) # DEEP_SLEEP 198 | self.send_data(0x01) # check code 199 | 200 | epdconfig.delay_ms(2000) 201 | epdconfig.module_exit() 202 | ### END OF FILE ### 203 | 204 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd2in13bc.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd2in13bc.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V4.0 8 | # * | Date : 2019-06-20 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | import logging 31 | from . import epdconfig 32 | 33 | # Display resolution 34 | EPD_WIDTH = 104 35 | EPD_HEIGHT = 212 36 | 37 | logger = logging.getLogger(__name__) 38 | 39 | class EPD: 40 | def __init__(self): 41 | self.reset_pin = epdconfig.RST_PIN 42 | self.dc_pin = epdconfig.DC_PIN 43 | self.busy_pin = epdconfig.BUSY_PIN 44 | self.cs_pin = epdconfig.CS_PIN 45 | self.width = EPD_WIDTH 46 | self.height = EPD_HEIGHT 47 | 48 | # Hardware reset 49 | def reset(self): 50 | epdconfig.digital_write(self.reset_pin, 1) 51 | epdconfig.delay_ms(200) 52 | epdconfig.digital_write(self.reset_pin, 0) 53 | epdconfig.delay_ms(5) 54 | epdconfig.digital_write(self.reset_pin, 1) 55 | epdconfig.delay_ms(200) 56 | 57 | def send_command(self, command): 58 | epdconfig.digital_write(self.dc_pin, 0) 59 | epdconfig.digital_write(self.cs_pin, 0) 60 | epdconfig.spi_writebyte([command]) 61 | epdconfig.digital_write(self.cs_pin, 1) 62 | 63 | def send_data(self, data): 64 | epdconfig.digital_write(self.dc_pin, 1) 65 | epdconfig.digital_write(self.cs_pin, 0) 66 | epdconfig.spi_writebyte([data]) 67 | epdconfig.digital_write(self.cs_pin, 1) 68 | 69 | def ReadBusy(self): 70 | logger.debug("e-Paper busy") 71 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 72 | epdconfig.delay_ms(100) 73 | logger.debug("e-Paper busy release") 74 | 75 | def init(self): 76 | if (epdconfig.module_init() != 0): 77 | return -1 78 | 79 | self.reset() 80 | 81 | self.send_command(0x06) # BOOSTER_SOFT_START 82 | self.send_data(0x17) 83 | self.send_data(0x17) 84 | self.send_data(0x17) 85 | 86 | self.send_command(0x04) # POWER_ON 87 | self.ReadBusy() 88 | 89 | self.send_command(0x00) # PANEL_SETTING 90 | self.send_data(0x8F) 91 | 92 | self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING 93 | self.send_data(0xF0) 94 | 95 | self.send_command(0x61) # RESOLUTION_SETTING 96 | self.send_data(self.width & 0xff) 97 | self.send_data(self.height >> 8) 98 | self.send_data(self.height & 0xff) 99 | return 0 100 | 101 | def getbuffer(self, image): 102 | # logger.debug("bufsiz = ",int(self.width/8) * self.height) 103 | buf = [0xFF] * (int(self.width/8) * self.height) 104 | image_monocolor = image.convert('1') 105 | imwidth, imheight = image_monocolor.size 106 | pixels = image_monocolor.load() 107 | # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) 108 | if(imwidth == self.width and imheight == self.height): 109 | logger.debug("Vertical") 110 | for y in range(imheight): 111 | for x in range(imwidth): 112 | # Set the bits for the column of pixels at the current position. 113 | if pixels[x, y] == 0: 114 | buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) 115 | elif(imwidth == self.height and imheight == self.width): 116 | logger.debug("Horizontal") 117 | for y in range(imheight): 118 | for x in range(imwidth): 119 | newx = y 120 | newy = self.height - x - 1 121 | if pixels[x, y] == 0: 122 | buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) 123 | return buf 124 | 125 | def display(self, imageblack, imagered): 126 | self.send_command(0x10) 127 | for i in range(0, int(self.width * self.height / 8)): 128 | self.send_data(imageblack[i]) 129 | # self.send_command(0x92) 130 | 131 | self.send_command(0x13) 132 | for i in range(0, int(self.width * self.height / 8)): 133 | self.send_data(imagered[i]) 134 | # self.send_command(0x92) 135 | 136 | self.send_command(0x12) # REFRESH 137 | self.ReadBusy() 138 | 139 | def Clear(self): 140 | self.send_command(0x10) 141 | for i in range(0, int(self.width * self.height / 8)): 142 | self.send_data(0xFF) 143 | self.send_command(0x92) 144 | 145 | self.send_command(0x13) 146 | for i in range(0, int(self.width * self.height / 8)): 147 | self.send_data(0xFF) 148 | self.send_command(0x92) 149 | 150 | self.send_command(0x12) # REFRESH 151 | self.ReadBusy() 152 | 153 | def sleep(self): 154 | self.send_command(0x02) # POWER_OFF 155 | self.ReadBusy() 156 | self.send_command(0x07) # DEEP_SLEEP 157 | self.send_data(0xA5) # check code 158 | 159 | epdconfig.delay_ms(2000) 160 | epdconfig.module_exit() 161 | ### END OF FILE ### 162 | 163 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd2in15b.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd2in15b.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V1.0 8 | # * | Date : 2024-08-07 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | import logging 31 | from . import epdconfig 32 | 33 | # Display resolution 34 | EPD_WIDTH = 160 35 | EPD_HEIGHT = 296 36 | 37 | logger = logging.getLogger(__name__) 38 | 39 | class EPD: 40 | def __init__(self): 41 | self.reset_pin = epdconfig.RST_PIN 42 | self.dc_pin = epdconfig.DC_PIN 43 | self.busy_pin = epdconfig.BUSY_PIN 44 | self.cs_pin = epdconfig.CS_PIN 45 | self.width = EPD_WIDTH 46 | self.height = EPD_HEIGHT 47 | 48 | # hardware reset 49 | def reset(self): 50 | epdconfig.digital_write(self.reset_pin, 1) 51 | epdconfig.delay_ms(20) 52 | epdconfig.digital_write(self.reset_pin, 0) 53 | epdconfig.delay_ms(2) 54 | epdconfig.digital_write(self.reset_pin, 1) 55 | epdconfig.delay_ms(20) 56 | 57 | # send 1 byte command 58 | def send_command(self, command): 59 | epdconfig.digital_write(self.dc_pin, 0) 60 | epdconfig.digital_write(self.cs_pin, 0) 61 | epdconfig.spi_writebyte([command]) 62 | epdconfig.digital_write(self.cs_pin, 1) 63 | 64 | # send 1 byte data 65 | def send_data(self, data): 66 | epdconfig.digital_write(self.dc_pin, 1) 67 | epdconfig.digital_write(self.cs_pin, 0) 68 | epdconfig.spi_writebyte([data]) 69 | epdconfig.digital_write(self.cs_pin, 1) 70 | 71 | # send a lot of data 72 | def send_data2(self, data): 73 | epdconfig.digital_write(self.dc_pin, 1) 74 | epdconfig.digital_write(self.cs_pin, 0) 75 | epdconfig.spi_writebyte2(data) 76 | epdconfig.digital_write(self.cs_pin, 1) 77 | 78 | # judge e-Paper whether is busy 79 | def busy(self): 80 | logger.debug("e-Paper busy") 81 | while(epdconfig.digital_read(self.busy_pin) != 0): 82 | epdconfig.delay_ms(10) 83 | epdconfig.delay_ms(10) 84 | logger.debug("e-Paper busy release") 85 | 86 | # set the display window 87 | def set_windows(self, xstart, ystart, xend, yend): 88 | self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION 89 | self.send_data((xstart>>3) & 0xff) 90 | self.send_data((xend>>3) & 0xff) 91 | 92 | self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION 93 | self.send_data(ystart & 0xff) 94 | self.send_data((ystart >> 8) & 0xff) 95 | self.send_data(yend & 0xff) 96 | self.send_data((yend >> 8) & 0xff) 97 | 98 | # set the display cursor(origin) 99 | def set_cursor(self, xstart, ystart): 100 | self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER 101 | self.send_data(xstart & 0xff) 102 | 103 | self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER 104 | self.send_data(ystart & 0xff) 105 | self.send_data((ystart >> 8) & 0xff) 106 | 107 | # initialize 108 | def init(self): 109 | if (epdconfig.module_init() != 0): 110 | return -1 111 | 112 | self.reset() 113 | 114 | self.busy() 115 | self.send_command(0x12) # SWRESET 116 | self.busy() 117 | 118 | self.send_command(0x11) # data entry mode 119 | self.send_data(0x03) 120 | 121 | self.set_windows(0, 0, self.width - 1, self.height - 1) 122 | self.set_cursor(0, 0) 123 | 124 | self.send_command(0x3C) # BorderWavefrom 125 | self.send_data(0x05) 126 | 127 | self.send_command(0x18) # Read built-in temperature sensor 128 | self.send_data(0x80) 129 | 130 | self.busy() 131 | 132 | return 0 133 | 134 | # turn on display 135 | def ondisplay(self): 136 | self.send_command(0x20) 137 | self.busy() 138 | 139 | # image converted to bytearray 140 | def getbuffer(self, image): 141 | img = image 142 | imwidth, imheight = img.size 143 | if(imwidth == self.width and imheight == self.height): 144 | img = img.convert('1') 145 | elif(imwidth == self.height and imheight == self.width): 146 | # image has correct dimensions, but needs to be rotated 147 | img = img.rotate(90, expand=True).convert('1') 148 | else: 149 | logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) 150 | # return a blank buffer 151 | return [0x00] * (int(self.width/8) * self.height) 152 | 153 | buf = bytearray(img.tobytes('raw')) 154 | return buf 155 | 156 | # display image 157 | def display(self, imageblack, imagered): 158 | if self.width%8 == 0: 159 | linewidth = int(self.width/8) 160 | else: 161 | linewidth = int(self.width/8) + 1 162 | 163 | self.send_command(0x24) 164 | self.send_data2(imageblack) 165 | 166 | for j in range(0, self.height): 167 | for i in range(0, linewidth): 168 | imagered[i+j*linewidth] = (~imagered[i+j*linewidth])&0xFF 169 | self.send_command(0x26) 170 | self.send_data2(imagered) 171 | 172 | self.ondisplay() 173 | 174 | # display white image 175 | def clear(self): 176 | if self.width%8 == 0: 177 | linewidth = int(self.width/8) 178 | else: 179 | linewidth = int(self.width/8) + 1 180 | 181 | buf = [0xff] * (int(linewidth * self.height)) 182 | 183 | self.send_command(0x24) 184 | self.send_data2(buf) 185 | 186 | buf = [0x00] * (int(linewidth * self.height)) 187 | self.send_command(0x26) 188 | self.send_data2(buf) 189 | 190 | self.ondisplay() 191 | 192 | # Compatible with older version functions 193 | def Clear(self): 194 | self.clear() 195 | 196 | # sleep 197 | def sleep(self): 198 | self.send_command(0x10) # DEEP_SLEEP 199 | self.send_data(0x01) # check code 200 | 201 | epdconfig.delay_ms(2000) 202 | epdconfig.module_exit() 203 | ### END OF FILE ### 204 | 205 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd2in36g.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd2in36g.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V1.0 8 | # * | Date : 2022-08-17 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # ******************************************************************************/ 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy 13 | # of this software and associated documnetation files (the "Software"), to deal 14 | # in the Software without restriction, including without limitation the rights 15 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | # copies of the Software, and to permit persons to whom the Software is 17 | # furished to do so, subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in 20 | # all copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | # THE SOFTWARE. 29 | # 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | from PIL import Image 35 | 36 | # Display resolution 37 | EPD_WIDTH = 168 38 | EPD_HEIGHT = 296 39 | 40 | logger = logging.getLogger(__name__) 41 | 42 | class EPD: 43 | def __init__(self): 44 | self.reset_pin = epdconfig.RST_PIN 45 | self.dc_pin = epdconfig.DC_PIN 46 | self.busy_pin = epdconfig.BUSY_PIN 47 | self.cs_pin = epdconfig.CS_PIN 48 | self.width = EPD_WIDTH 49 | self.height = EPD_HEIGHT 50 | self.BLACK = 0x000000 # 00 BGR 51 | self.WHITE = 0xffffff # 01 52 | self.YELLOW = 0x00ffff # 10 53 | self.RED = 0x0000ff # 11 54 | 55 | # Hardware reset 56 | def reset(self): 57 | epdconfig.digital_write(self.reset_pin, 1) 58 | epdconfig.delay_ms(200) 59 | epdconfig.digital_write(self.reset_pin, 0) # module reset 60 | epdconfig.delay_ms(2) 61 | epdconfig.digital_write(self.reset_pin, 1) 62 | epdconfig.delay_ms(200) 63 | 64 | def send_command(self, command): 65 | epdconfig.digital_write(self.dc_pin, 0) 66 | epdconfig.digital_write(self.cs_pin, 0) 67 | epdconfig.spi_writebyte([command]) 68 | epdconfig.digital_write(self.cs_pin, 1) 69 | 70 | def send_data(self, data): 71 | epdconfig.digital_write(self.dc_pin, 1) 72 | epdconfig.digital_write(self.cs_pin, 0) 73 | epdconfig.spi_writebyte([data]) 74 | epdconfig.digital_write(self.cs_pin, 1) 75 | 76 | def ReadBusyH(self): 77 | logger.debug("e-Paper busy H") 78 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 79 | epdconfig.delay_ms(5) 80 | logger.debug("e-Paper busy H release") 81 | 82 | def ReadBusyL(self): 83 | logger.debug("e-Paper busy L") 84 | while(epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle 85 | epdconfig.delay_ms(5) 86 | logger.debug("e-Paper busy L release") 87 | 88 | def TurnOnDisplay(self): 89 | self.send_command(0x12) # DISPLAY_REFRESH 90 | self.send_data(0x01) 91 | self.ReadBusyH() 92 | 93 | self.send_command(0x02) # POWER_OFF 94 | self.send_data(0X00) 95 | self.ReadBusyH() 96 | 97 | def init(self): 98 | if (epdconfig.module_init() != 0): 99 | return -1 100 | # EPD hardware init start 101 | 102 | self.reset() 103 | 104 | self.send_command(0x66) 105 | self.send_data(0x49) 106 | self.send_data(0x55) 107 | self.send_data(0x13) 108 | self.send_data(0x5D) 109 | 110 | self.send_command(0x66) 111 | self.send_data(0x49) 112 | self.send_data(0x55) 113 | 114 | self.send_command(0xB0) 115 | self.send_data(0x03) 116 | 117 | self.send_command(0x00) 118 | self.send_data(0x4F) 119 | self.send_data(0x69) 120 | 121 | self.send_command(0x03) 122 | self.send_data(0x00) 123 | 124 | self.send_command(0xF0) 125 | self.send_data(0xF6) 126 | self.send_data(0x0D) 127 | self.send_data(0x00) 128 | self.send_data(0x00) 129 | self.send_data(0x00) 130 | 131 | self.send_command(0x06) 132 | self.send_data(0xCF) 133 | self.send_data(0xDE) 134 | self.send_data(0x0F) 135 | 136 | self.send_command(0x41) 137 | self.send_data(0x00) 138 | 139 | self.send_command(0x50) 140 | self.send_data(0x30) 141 | 142 | self.send_command(0x60) 143 | self.send_data(0x0C) 144 | self.send_data(0x05) 145 | 146 | self.send_command(0x61) 147 | self.send_data(0xA8) 148 | self.send_data(0x01) 149 | self.send_data(0x28) 150 | 151 | self.send_command(0x84) 152 | self.send_data(0x01) 153 | return 0 154 | 155 | def getbuffer(self, image): 156 | # Create a pallette with the 4 colors supported by the panel 157 | pal_image = Image.new("P", (1,1)) 158 | pal_image.putpalette( (0,0,0, 255,255,255, 255,255,0, 255,0,0) + (0,0,0)*252) 159 | 160 | # Check if we need to rotate the image 161 | imwidth, imheight = image.size 162 | if(imwidth == self.width and imheight == self.height): 163 | image_temp = image 164 | elif(imwidth == self.height and imheight == self.width): 165 | image_temp = image.rotate(90, expand=True) 166 | else: 167 | logger.warning("Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height)) 168 | 169 | # Convert the soruce image to the 4 colors, dithering if needed 170 | image_4color = image_temp.convert("RGB").quantize(palette=pal_image) 171 | buf_4color = bytearray(image_4color.tobytes('raw')) 172 | 173 | # into a single byte to transfer to the panel 174 | buf = [0x00] * int(self.width * self.height / 4) 175 | idx = 0 176 | for i in range(0, len(buf_4color), 4): 177 | buf[idx] = (buf_4color[i] << 6) + (buf_4color[i+1] << 4) + (buf_4color[i+2] << 2) + buf_4color[i+3] 178 | idx += 1 179 | 180 | return buf 181 | 182 | def display(self, image): 183 | if self.width % 4 == 0 : 184 | Width = self.width // 4 185 | else : 186 | Width = self.width // 4 + 1 187 | Height = self.height 188 | 189 | self.send_command(0x68) 190 | self.send_data(0x01) 191 | 192 | self.send_command(0x04) 193 | self.ReadBusyH() 194 | 195 | self.send_command(0x10) 196 | for j in range(0, Height): 197 | for i in range(0, Width): 198 | self.send_data(image[i + j * Width]) 199 | 200 | self.send_command(0x68) 201 | self.send_data(0x00) 202 | 203 | self.TurnOnDisplay() 204 | 205 | def Clear(self, color=0x55): 206 | if self.width % 4 == 0 : 207 | Width = self.width // 4 208 | else : 209 | Width = self.width // 4 + 1 210 | Height = self.height 211 | 212 | self.send_command(0x68) 213 | self.send_data(0x01) 214 | 215 | self.send_command(0x04) 216 | self.ReadBusyH() 217 | 218 | self.send_command(0x10) 219 | for j in range(0, Height): 220 | for i in range(0, Width): 221 | self.send_data(color) 222 | 223 | self.send_command(0x68) 224 | self.send_data(0x00) 225 | 226 | self.TurnOnDisplay() 227 | 228 | def sleep(self): 229 | self.send_command(0x02) # POWER_OFF 230 | self.send_data(0x00) 231 | 232 | self.send_command(0x07) # DEEP_SLEEP 233 | self.send_data(0XA5) 234 | 235 | epdconfig.delay_ms(2000) 236 | epdconfig.module_exit() 237 | ### END OF FILE ### 238 | 239 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd2in66b.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd2in66b.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V1.1 8 | # * | Date : 2022-08-9 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | import logging 31 | from . import epdconfig 32 | 33 | # Display resolution 34 | EPD_WIDTH = 152 35 | EPD_HEIGHT = 296 36 | 37 | logger = logging.getLogger(__name__) 38 | 39 | class EPD: 40 | def __init__(self): 41 | self.reset_pin = epdconfig.RST_PIN 42 | self.dc_pin = epdconfig.DC_PIN 43 | self.busy_pin = epdconfig.BUSY_PIN 44 | self.cs_pin = epdconfig.CS_PIN 45 | self.width = EPD_WIDTH 46 | self.height = EPD_HEIGHT 47 | 48 | # Hardware reset 49 | def reset(self): 50 | epdconfig.digital_write(self.reset_pin, 1) 51 | epdconfig.delay_ms(200) 52 | epdconfig.digital_write(self.reset_pin, 0) 53 | epdconfig.delay_ms(5) 54 | epdconfig.digital_write(self.reset_pin, 1) 55 | epdconfig.delay_ms(200) 56 | 57 | 58 | def send_command(self, command): 59 | epdconfig.digital_write(self.dc_pin, 0) 60 | epdconfig.digital_write(self.cs_pin, 0) 61 | epdconfig.spi_writebyte([command]) 62 | epdconfig.digital_write(self.cs_pin, 1) 63 | 64 | 65 | def send_data(self, data): 66 | epdconfig.digital_write(self.dc_pin, 1) 67 | epdconfig.digital_write(self.cs_pin, 0) 68 | epdconfig.spi_writebyte([data]) 69 | epdconfig.digital_write(self.cs_pin, 1) 70 | 71 | # send a lot of data 72 | def send_data2(self, data): 73 | epdconfig.digital_write(self.dc_pin, 1) 74 | epdconfig.digital_write(self.cs_pin, 0) 75 | epdconfig.spi_writebyte2(data) 76 | epdconfig.digital_write(self.cs_pin, 1) 77 | 78 | 79 | def ReadBusy(self): 80 | logger.debug("e-Paper busy") 81 | while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy 82 | epdconfig.delay_ms(20) 83 | logger.debug("e-Paper busy release") 84 | 85 | 86 | def init(self): 87 | if (epdconfig.module_init() != 0): 88 | return -1 89 | # EPD hardware init start 90 | self.reset() 91 | 92 | self.send_command(0x12) 93 | epdconfig.delay_ms(30) 94 | self.ReadBusy() 95 | 96 | self.send_command(0x11) # setting gaet number 97 | self.send_data(0x03) 98 | 99 | self.setWindows(0, 0, self.width-1, self.height-1) 100 | 101 | self.send_command(0x21) 102 | self.send_data(0x00) 103 | self.send_data(0x80) 104 | 105 | self.setCursor(0, 0) 106 | self.ReadBusy() 107 | 108 | return 0 109 | 110 | def setWindows(self, Xstart, Ystart, Xend, Yend): 111 | self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION 112 | self.send_data((Xstart>>3) & 0x1F) 113 | self.send_data((Xend>>3) & 0x1F) 114 | 115 | self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION 116 | self.send_data(Ystart & 0xFF) 117 | self.send_data((Ystart >> 8) & 0x01) 118 | self.send_data(Yend & 0xFF) 119 | self.send_data((Yend >> 8) & 0x01) 120 | 121 | def setCursor(self, Xstart, Ystart): 122 | self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER 123 | self.send_data(Xstart & 0x1F) 124 | 125 | self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER 126 | self.send_data(Ystart & 0xFF) 127 | self.send_data((Ystart >> 8) & 0x01) 128 | 129 | def turnon_display(self): 130 | self.send_command(0x20) 131 | self.ReadBusy() 132 | 133 | def getbuffer(self, image): 134 | # logger.debug("bufsiz = ",int(self.width/8) * self.height) 135 | buf = [0xFF] * (int(self.width/8) * self.height) 136 | image_monocolor = image.convert('1') 137 | imwidth, imheight = image_monocolor.size 138 | pixels = image_monocolor.load() 139 | # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) 140 | if(imwidth == self.width and imheight == self.height): 141 | logger.debug("Vertical") 142 | for y in range(imheight): 143 | for x in range(imwidth): 144 | # Set the bits for the column of pixels at the current position. 145 | if pixels[x, y] == 0: 146 | buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) 147 | elif(imwidth == self.height and imheight == self.width): 148 | logger.debug("Horizontal") 149 | for y in range(imheight): 150 | for x in range(imwidth): 151 | newx = y 152 | newy = self.height - x - 1 153 | if pixels[x, y] == 0: 154 | buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) 155 | return buf 156 | 157 | def display(self, Blackimage, Redimage): 158 | if (Blackimage == None or Redimage == None): 159 | return 160 | Redimage_1 = [0x00] * len(Redimage) 161 | for i in range(len(Redimage)) : 162 | Redimage_1[i] = ~Redimage[i] 163 | self.send_command(0x24) 164 | self.send_data2(Blackimage) 165 | 166 | self.send_command(0x26) 167 | self.send_data2(Redimage_1) 168 | 169 | self.turnon_display() 170 | 171 | 172 | def Clear(self): 173 | if self.width%8 == 0: 174 | linewidth = int(self.width/8) 175 | else: 176 | linewidth = int(self.width/8) + 1 177 | 178 | self.send_command(0x24) 179 | self.send_data2([0xff] * int(self.height * linewidth)) 180 | 181 | self.send_command(0x26) 182 | self.send_data2([0x00] * int(self.height * linewidth)) 183 | 184 | self.turnon_display() 185 | 186 | 187 | def sleep(self): 188 | self.send_command(0X10) # DEEP_SLEEP_MODE 189 | self.send_data(0x01) 190 | 191 | epdconfig.delay_ms(2000) 192 | epdconfig.module_exit() 193 | 194 | ### END OF FILE ### 195 | 196 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd2in7b_V2.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd2in7b_V2.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V1.1 8 | # * | Date : 2022-08-10 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | # Display resolution 35 | EPD_WIDTH = 176 36 | EPD_HEIGHT = 264 37 | 38 | logger = logging.getLogger(__name__) 39 | 40 | class EPD: 41 | def __init__(self): 42 | self.reset_pin = epdconfig.RST_PIN 43 | self.dc_pin = epdconfig.DC_PIN 44 | self.busy_pin = epdconfig.BUSY_PIN 45 | self.cs_pin = epdconfig.CS_PIN 46 | self.width = EPD_WIDTH 47 | self.height = EPD_HEIGHT 48 | 49 | # Hardware reset 50 | def reset(self): 51 | epdconfig.digital_write(self.reset_pin, 1) 52 | epdconfig.delay_ms(200) 53 | epdconfig.digital_write(self.reset_pin, 0) 54 | epdconfig.delay_ms(2) 55 | epdconfig.digital_write(self.reset_pin, 1) 56 | epdconfig.delay_ms(200) 57 | 58 | # Send Command 59 | def send_command(self, command): 60 | epdconfig.digital_write(self.dc_pin, 0) 61 | epdconfig.digital_write(self.cs_pin, 0) 62 | epdconfig.spi_writebyte([command]) 63 | epdconfig.digital_write(self.cs_pin, 1) 64 | 65 | # Send Data 66 | def send_data(self, data): 67 | epdconfig.digital_write(self.dc_pin, 1) 68 | epdconfig.digital_write(self.cs_pin, 0) 69 | epdconfig.spi_writebyte([data]) 70 | epdconfig.digital_write(self.cs_pin, 1) 71 | 72 | # send a lot of data 73 | def send_data2(self, data): 74 | epdconfig.digital_write(self.dc_pin, 1) 75 | epdconfig.digital_write(self.cs_pin, 0) 76 | epdconfig.spi_writebyte2(data) 77 | epdconfig.digital_write(self.cs_pin, 1) 78 | 79 | # Read Busy 80 | def ReadBusy(self): 81 | logger.debug("e-Paper busy") 82 | while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy 83 | epdconfig.delay_ms(10) 84 | logger.debug("e-Paper busy release") 85 | 86 | # Setting the display window 87 | def SetWindows(self, Xstart, Ystart, Xend, Yend): 88 | self.send_command(0x44) 89 | self.send_data((Xstart >> 3) & 0xff) 90 | self.send_data((Xend >> 3) & 0xff) 91 | 92 | self.send_command(0x45) 93 | self.send_data(Ystart & 0xff) 94 | self.send_data((Ystart >> 8) & 0xff) 95 | self.send_data(Yend & 0xff) 96 | self.send_data((Yend >> 8) & 0xff) 97 | 98 | # Set Cursor 99 | def SetCursor(self, Xstart, Ystart): 100 | self.send_command(0x4E) 101 | self.send_data(Xstart & 0xff) 102 | self.send_command(0x4F) 103 | self.send_data(Ystart & 0xff) 104 | self.send_data((Ystart >> 8) & 0xff) 105 | 106 | # Initialize the e-Paper register 107 | def init(self): 108 | if (epdconfig.module_init() != 0): 109 | return -1 110 | 111 | self.reset() 112 | 113 | self.ReadBusy() 114 | self.send_command(0x12) 115 | self.ReadBusy() 116 | 117 | self.send_command(0x00) 118 | self.send_data(0x27) 119 | self.send_data(0x01) 120 | self.send_data(0x00) 121 | 122 | self.send_command(0x11) 123 | self.send_data(0x03) 124 | 125 | self.SetWindows(0, 0, self.width-1, self.height-1) 126 | self.SetCursor(0, 0) 127 | return 0 128 | 129 | def getbuffer(self, image): 130 | # logger.debug("bufsiz = ",int(self.width/8) * self.height) 131 | buf = [0xFF] * (int(self.width/8) * self.height) 132 | image_monocolor = image.convert('1') 133 | imwidth, imheight = image_monocolor.size 134 | pixels = image_monocolor.load() 135 | # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) 136 | if(imwidth == self.width and imheight == self.height): 137 | logger.debug("Vertical") 138 | for y in range(imheight): 139 | for x in range(imwidth): 140 | # Set the bits for the column of pixels at the current position. 141 | if pixels[x, y] == 0: 142 | buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) 143 | elif(imwidth == self.height and imheight == self.width): 144 | logger.debug("Horizontal") 145 | for y in range(imheight): 146 | for x in range(imwidth): 147 | newx = y 148 | newy = self.height - x - 1 149 | if pixels[x, y] == 0: 150 | buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) 151 | return buf 152 | 153 | # Sends the image buffer in RAM to e-Paper and displays 154 | def display(self, imageblack, imagered): 155 | Width = self.width / 8 156 | Height = self.height 157 | 158 | buf = [0x00] * int(Width * Height) 159 | for i in range(0, int(Width * Height)): 160 | buf[i] = ~imagered[i] 161 | 162 | self.send_command(0x24) 163 | self.send_data2(imageblack) 164 | 165 | self.send_command(0x26) 166 | self.send_data2(buf) 167 | 168 | self.TurnOnDisplay() 169 | 170 | # Clear the screen 171 | def Clear(self): 172 | self.send_command(0x24) 173 | self.send_data2([0xff] * int(self.width * self.height / 8)) 174 | 175 | self.send_command(0x26) 176 | self.send_data2([0x00] * int(self.width * self.height / 8)) 177 | 178 | self.TurnOnDisplay() 179 | 180 | # Turn on display 181 | def TurnOnDisplay(self): 182 | self.send_command(0x20) 183 | self.ReadBusy() 184 | 185 | # Enter sleep mode 186 | def sleep(self): 187 | self.send_command(0x10) 188 | self.send_data(0x01) 189 | 190 | epdconfig.delay_ms(2000) 191 | epdconfig.module_exit() 192 | ### END OF FILE ### 193 | 194 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd2in9b_V3.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd2in9b_V3.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V1.2 8 | # * | Date : 2022-08-10 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | # Display resolution 35 | EPD_WIDTH = 128 36 | EPD_HEIGHT = 296 37 | 38 | logger = logging.getLogger(__name__) 39 | 40 | class EPD: 41 | def __init__(self): 42 | self.reset_pin = epdconfig.RST_PIN 43 | self.dc_pin = epdconfig.DC_PIN 44 | self.busy_pin = epdconfig.BUSY_PIN 45 | self.cs_pin = epdconfig.CS_PIN 46 | self.width = EPD_WIDTH 47 | self.height = EPD_HEIGHT 48 | 49 | # Hardware reset 50 | def reset(self): 51 | epdconfig.digital_write(self.reset_pin, 1) 52 | epdconfig.delay_ms(200) 53 | epdconfig.digital_write(self.reset_pin, 0) 54 | epdconfig.delay_ms(2) 55 | epdconfig.digital_write(self.reset_pin, 1) 56 | epdconfig.delay_ms(200) 57 | 58 | def send_command(self, command): 59 | epdconfig.digital_write(self.dc_pin, 0) 60 | epdconfig.digital_write(self.cs_pin, 0) 61 | epdconfig.spi_writebyte([command]) 62 | epdconfig.digital_write(self.cs_pin, 1) 63 | 64 | def send_data(self, data): 65 | epdconfig.digital_write(self.dc_pin, 1) 66 | epdconfig.digital_write(self.cs_pin, 0) 67 | epdconfig.spi_writebyte([data]) 68 | epdconfig.digital_write(self.cs_pin, 1) 69 | 70 | # send a lot of data 71 | def send_data2(self, data): 72 | epdconfig.digital_write(self.dc_pin, 1) 73 | epdconfig.digital_write(self.cs_pin, 0) 74 | epdconfig.spi_writebyte2(data) 75 | epdconfig.digital_write(self.cs_pin, 1) 76 | 77 | def ReadBusy(self): 78 | logger.debug("e-Paper busy") 79 | self.send_command(0X71) 80 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 81 | self.send_command(0X71) 82 | epdconfig.delay_ms(200) 83 | logger.debug("e-Paper busy release") 84 | 85 | def init(self): 86 | if (epdconfig.module_init() != 0): 87 | return -1 88 | # EPD hardware init start 89 | self.reset() 90 | 91 | self.send_command(0x04) 92 | self.ReadBusy()#waiting for the electronic paper IC to release the idle signal 93 | 94 | self.send_command(0x00) #panel setting 95 | self.send_data(0x0f) #LUT from OTP,128x296 96 | self.send_data(0x89) #Temperature sensor, boost and other related timing settings 97 | 98 | self.send_command(0x61) #resolution setting 99 | self.send_data (0x80) 100 | self.send_data (0x01) 101 | self.send_data (0x28) 102 | 103 | self.send_command(0X50) #VCOM AND DATA INTERVAL SETTING 104 | self.send_data(0x77) #WBmode:VBDF 17|D7 VBDW 97 VBDB 57 105 | # WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7 106 | 107 | return 0 108 | 109 | def getbuffer(self, image): 110 | # logger.debug("bufsiz = ",int(self.width/8) * self.height) 111 | buf = [0xFF] * (int(self.width/8) * self.height) 112 | image_monocolor = image.convert('1') 113 | imwidth, imheight = image_monocolor.size 114 | pixels = image_monocolor.load() 115 | # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) 116 | if(imwidth == self.width and imheight == self.height): 117 | logger.debug("Vertical") 118 | for y in range(imheight): 119 | for x in range(imwidth): 120 | # Set the bits for the column of pixels at the current position. 121 | if pixels[x, y] == 0: 122 | buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) 123 | elif(imwidth == self.height and imheight == self.width): 124 | logger.debug("Horizontal") 125 | for y in range(imheight): 126 | for x in range(imwidth): 127 | newx = y 128 | newy = self.height - x - 1 129 | if pixels[x, y] == 0: 130 | buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) 131 | return buf 132 | 133 | def display(self, blackimage, ryimage): # ryimage: red or yellow image 134 | if (blackimage != None): 135 | self.send_command(0X10) 136 | self.send_data2(blackimage) 137 | if (ryimage != None): 138 | self.send_command(0X13) 139 | self.send_data2(ryimage) 140 | 141 | self.send_command(0x12) 142 | epdconfig.delay_ms(200) 143 | self.ReadBusy() 144 | 145 | def Clear(self): 146 | self.send_command(0X10) 147 | self.send_data2([0xff] * int(self.width * self.height / 8)) 148 | self.send_command(0X13) 149 | self.send_data2([0xff] * int(self.width * self.height / 8)) 150 | 151 | self.send_command(0x12) 152 | epdconfig.delay_ms(200) 153 | self.ReadBusy() 154 | 155 | def sleep(self): 156 | self.send_command(0X02) # power off 157 | self.ReadBusy() 158 | self.send_command(0X07) # deep sleep 159 | self.send_data(0xA5) 160 | 161 | epdconfig.delay_ms(2000) 162 | epdconfig.module_exit() 163 | ### END OF FILE ### 164 | 165 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd2in9bc.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd2in9bc.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V4.0 8 | # * | Date : 2019-06-20 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | # Display resolution 35 | EPD_WIDTH = 128 36 | EPD_HEIGHT = 296 37 | 38 | logger = logging.getLogger(__name__) 39 | 40 | class EPD: 41 | def __init__(self): 42 | self.reset_pin = epdconfig.RST_PIN 43 | self.dc_pin = epdconfig.DC_PIN 44 | self.busy_pin = epdconfig.BUSY_PIN 45 | self.cs_pin = epdconfig.CS_PIN 46 | self.width = EPD_WIDTH 47 | self.height = EPD_HEIGHT 48 | 49 | # Hardware reset 50 | def reset(self): 51 | epdconfig.digital_write(self.reset_pin, 1) 52 | epdconfig.delay_ms(200) 53 | epdconfig.digital_write(self.reset_pin, 0) 54 | epdconfig.delay_ms(5) 55 | epdconfig.digital_write(self.reset_pin, 1) 56 | epdconfig.delay_ms(200) 57 | 58 | def send_command(self, command): 59 | epdconfig.digital_write(self.dc_pin, 0) 60 | epdconfig.digital_write(self.cs_pin, 0) 61 | epdconfig.spi_writebyte([command]) 62 | epdconfig.digital_write(self.cs_pin, 1) 63 | 64 | def send_data(self, data): 65 | epdconfig.digital_write(self.dc_pin, 1) 66 | epdconfig.digital_write(self.cs_pin, 0) 67 | epdconfig.spi_writebyte([data]) 68 | epdconfig.digital_write(self.cs_pin, 1) 69 | 70 | def ReadBusy(self): 71 | logger.debug("e-Paper busy") 72 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 73 | epdconfig.delay_ms(200) 74 | logger.debug("e-Paper busy release") 75 | 76 | def init(self): 77 | if (epdconfig.module_init() != 0): 78 | return -1 79 | # EPD hardware init start 80 | self.reset() 81 | 82 | self.send_command(0x06) # boost 83 | self.send_data (0x17) 84 | self.send_data (0x17) 85 | self.send_data (0x17) 86 | self.send_command(0x04) # POWER_ON 87 | self.ReadBusy() 88 | self.send_command(0X00) # PANEL_SETTING 89 | self.send_data(0x8F) 90 | self.send_command(0X50) # VCOM_AND_DATA_INTERVAL_SETTING 91 | self.send_data(0x77) 92 | self.send_command(0x61) # TCON_RESOLUTION 93 | self.send_data (0x80) 94 | self.send_data (0x01) 95 | self.send_data (0x28) 96 | # self.send_command(VCM_DC_SETTING_REGISTER) 97 | # self.send_data (0x0A) 98 | 99 | return 0 100 | 101 | def getbuffer(self, image): 102 | # logger.debug("bufsiz = ",int(self.width/8) * self.height) 103 | buf = [0xFF] * (int(self.width/8) * self.height) 104 | image_monocolor = image.convert('1') 105 | imwidth, imheight = image_monocolor.size 106 | pixels = image_monocolor.load() 107 | # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) 108 | if(imwidth == self.width and imheight == self.height): 109 | logger.debug("Vertical") 110 | for y in range(imheight): 111 | for x in range(imwidth): 112 | # Set the bits for the column of pixels at the current position. 113 | if pixels[x, y] == 0: 114 | buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) 115 | elif(imwidth == self.height and imheight == self.width): 116 | logger.debug("Horizontal") 117 | for y in range(imheight): 118 | for x in range(imwidth): 119 | newx = y 120 | newy = self.height - x - 1 121 | if pixels[x, y] == 0: 122 | buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) 123 | return buf 124 | 125 | def display(self, blackimage, ryimage): # ryimage: red or yellow image 126 | if (blackimage != None): 127 | self.send_command(0X10) 128 | for i in range(0, int(self.width * self.height / 8)): 129 | self.send_data(blackimage[i]) 130 | if (ryimage != None): 131 | self.send_command(0X13) 132 | for i in range(0, int(self.width * self.height / 8)): 133 | self.send_data(ryimage[i]) 134 | 135 | self.send_command(0x12) 136 | self.ReadBusy() 137 | 138 | def Clear(self): 139 | self.send_command(0X10) 140 | for i in range(0, int(self.width * self.height / 8)): 141 | self.send_data(0xff) 142 | self.send_command(0X13) 143 | for i in range(0, int(self.width * self.height / 8)): 144 | self.send_data(0xff) 145 | 146 | self.send_command(0x12) 147 | self.ReadBusy() 148 | 149 | def sleep(self): 150 | self.send_command(0X02) # power off 151 | self.ReadBusy() 152 | self.send_command(0X07) # deep sleep 153 | self.send_data(0xA5) 154 | 155 | epdconfig.delay_ms(2000) 156 | epdconfig.module_exit() 157 | ### END OF FILE ### 158 | 159 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd3in0g.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd3in0g.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V1 8 | # * | Date : 2022-07-20 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # ******************************************************************************/ 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy 13 | # of this software and associated documnetation files (the "Software"), to deal 14 | # in the Software without restriction, including without limitation the rights 15 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | # copies of the Software, and to permit persons to whom the Software is 17 | # furished to do so, subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in 20 | # all copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | # THE SOFTWARE. 29 | # 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | from PIL import Image 35 | 36 | # Display resolution 37 | EPD_WIDTH = 168 38 | EPD_HEIGHT = 400 39 | 40 | logger = logging.getLogger(__name__) 41 | 42 | class EPD: 43 | def __init__(self): 44 | self.reset_pin = epdconfig.RST_PIN 45 | self.dc_pin = epdconfig.DC_PIN 46 | self.busy_pin = epdconfig.BUSY_PIN 47 | self.cs_pin = epdconfig.CS_PIN 48 | self.width = EPD_WIDTH 49 | self.height = EPD_HEIGHT 50 | self.BLACK = 0x000000 # 00 BGR 51 | self.WHITE = 0xffffff # 01 52 | self.YELLOW = 0x00ffff # 10 53 | self.RED = 0x0000ff # 11 54 | 55 | # Hardware reset 56 | def reset(self): 57 | epdconfig.digital_write(self.reset_pin, 1) 58 | epdconfig.delay_ms(200) 59 | epdconfig.digital_write(self.reset_pin, 0) # module reset 60 | epdconfig.delay_ms(2) 61 | epdconfig.digital_write(self.reset_pin, 1) 62 | epdconfig.delay_ms(200) 63 | 64 | def send_command(self, command): 65 | epdconfig.digital_write(self.dc_pin, 0) 66 | epdconfig.digital_write(self.cs_pin, 0) 67 | epdconfig.spi_writebyte([command]) 68 | epdconfig.digital_write(self.cs_pin, 1) 69 | 70 | def send_data(self, data): 71 | epdconfig.digital_write(self.dc_pin, 1) 72 | epdconfig.digital_write(self.cs_pin, 0) 73 | epdconfig.spi_writebyte([data]) 74 | epdconfig.digital_write(self.cs_pin, 1) 75 | 76 | def ReadBusyH(self): 77 | logger.debug("e-Paper busy H") 78 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 79 | epdconfig.delay_ms(5) 80 | logger.debug("e-Paper busy H release") 81 | 82 | def ReadBusyL(self): 83 | logger.debug("e-Paper busy L") 84 | while(epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle 85 | epdconfig.delay_ms(5) 86 | logger.debug("e-Paper busy L release") 87 | 88 | def TurnOnDisplay(self): 89 | self.send_command(0x12) # DISPLAY_REFRESH 90 | self.send_data(0x01) 91 | self.ReadBusyH() 92 | 93 | self.send_command(0x02) # POWER_OFF 94 | self.send_data(0X00) 95 | self.ReadBusyH() 96 | 97 | def init(self): 98 | if (epdconfig.module_init() != 0): 99 | return -1 100 | # EPD hardware init start 101 | 102 | self.reset() 103 | 104 | self.send_command(0x66) 105 | self.send_data(0x49) 106 | self.send_data(0x55) 107 | self.send_data(0x13) 108 | self.send_data(0x5D) 109 | self.send_data(0x05) 110 | self.send_data(0x10) 111 | 112 | self.send_command(0xB0) 113 | self.send_data(0x00) # 1 boost 114 | 115 | self.send_command(0x01) 116 | self.send_data(0x0F) 117 | self.send_data(0x00) 118 | 119 | self.send_command(0x00) 120 | self.send_data(0x4F) 121 | self.send_data(0x6B) 122 | 123 | self.send_command(0x06) 124 | self.send_data(0xD7) 125 | self.send_data(0xDE) 126 | self.send_data(0x12) 127 | 128 | self.send_command(0x61) 129 | self.send_data(0x00) 130 | self.send_data(0xA8) 131 | self.send_data(0x01) 132 | self.send_data(0x90) 133 | 134 | self.send_command(0x50) 135 | self.send_data(0x37) 136 | 137 | self.send_command(0x60) 138 | self.send_data(0x0C) 139 | self.send_data(0x05) 140 | 141 | self.send_command(0xE3) 142 | self.send_data(0xFF) 143 | 144 | self.send_command(0x84) 145 | self.send_data(0x00) 146 | return 0 147 | 148 | def getbuffer(self, image): 149 | # Create a pallette with the 4 colors supported by the panel 150 | pal_image = Image.new("P", (1,1)) 151 | pal_image.putpalette( (0,0,0, 255,255,255, 255,255,0, 255,0,0) + (0,0,0)*252) 152 | 153 | # Check if we need to rotate the image 154 | imwidth, imheight = image.size 155 | if(imwidth == self.width and imheight == self.height): 156 | image_temp = image 157 | elif(imwidth == self.height and imheight == self.width): 158 | image_temp = image.rotate(90, expand=True) 159 | else: 160 | logger.warning("Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height)) 161 | 162 | # Convert the soruce image to the 4 colors, dithering if needed 163 | image_4color = image_temp.convert("RGB").quantize(palette=pal_image) 164 | buf_4color = bytearray(image_4color.tobytes('raw')) 165 | 166 | # into a single byte to transfer to the panel 167 | buf = [0x00] * int(self.width * self.height / 4) 168 | idx = 0 169 | for i in range(0, len(buf_4color), 4): 170 | buf[idx] = (buf_4color[i] << 6) + (buf_4color[i+1] << 4) + (buf_4color[i+2] << 2) + buf_4color[i+3] 171 | idx += 1 172 | 173 | return buf 174 | 175 | def display(self, image): 176 | if self.width % 4 == 0 : 177 | Width = self.width // 4 178 | else : 179 | Width = self.width // 4 + 1 180 | Height = self.height 181 | 182 | self.send_command(0x04) 183 | self.ReadBusyH() 184 | 185 | self.send_command(0x10) 186 | for j in range(0, Height): 187 | for i in range(0, Width): 188 | self.send_data(image[i + j * Width]) 189 | 190 | self.TurnOnDisplay() 191 | 192 | def Clear(self, color=0x55): 193 | if self.width % 4 == 0 : 194 | Width = self.width // 4 195 | else : 196 | Width = self.width // 4 + 1 197 | Height = self.height 198 | 199 | self.send_command(0x04) 200 | self.ReadBusyH() 201 | 202 | self.send_command(0x10) 203 | for j in range(0, Height): 204 | for i in range(0, Width): 205 | self.send_data(color) 206 | 207 | self.TurnOnDisplay() 208 | 209 | def sleep(self): 210 | self.send_command(0x02) # POWER_OFF 211 | self.send_data(0x00) 212 | 213 | self.send_command(0x07) # DEEP_SLEEP 214 | self.send_data(0XA5) 215 | 216 | epdconfig.delay_ms(2000) 217 | epdconfig.module_exit() 218 | ### END OF FILE ### 219 | 220 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd4in2bc.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd4in2bc.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V4.0 8 | # * | Date : 2019-06-20 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | import logging 31 | from . import epdconfig 32 | 33 | # Display resolution 34 | EPD_WIDTH = 400 35 | EPD_HEIGHT = 300 36 | 37 | logger = logging.getLogger(__name__) 38 | 39 | class EPD: 40 | def __init__(self): 41 | self.reset_pin = epdconfig.RST_PIN 42 | self.dc_pin = epdconfig.DC_PIN 43 | self.busy_pin = epdconfig.BUSY_PIN 44 | self.cs_pin = epdconfig.CS_PIN 45 | self.width = EPD_WIDTH 46 | self.height = EPD_HEIGHT 47 | 48 | # Hardware reset 49 | def reset(self): 50 | epdconfig.digital_write(self.reset_pin, 1) 51 | epdconfig.delay_ms(200) 52 | epdconfig.digital_write(self.reset_pin, 0) 53 | epdconfig.delay_ms(5) 54 | epdconfig.digital_write(self.reset_pin, 1) 55 | epdconfig.delay_ms(200) 56 | 57 | def send_command(self, command): 58 | epdconfig.digital_write(self.dc_pin, 0) 59 | epdconfig.digital_write(self.cs_pin, 0) 60 | epdconfig.spi_writebyte([command]) 61 | epdconfig.digital_write(self.cs_pin, 1) 62 | 63 | def send_data(self, data): 64 | epdconfig.digital_write(self.dc_pin, 1) 65 | epdconfig.digital_write(self.cs_pin, 0) 66 | epdconfig.spi_writebyte([data]) 67 | epdconfig.digital_write(self.cs_pin, 1) 68 | 69 | def ReadBusy(self): 70 | logger.debug("e-Paper busy") 71 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 72 | epdconfig.delay_ms(100) 73 | logger.debug("e-Paper busy release") 74 | 75 | def init(self): 76 | if (epdconfig.module_init() != 0): 77 | return -1 78 | 79 | self.reset() 80 | 81 | self.send_command(0x06) # BOOSTER_SOFT_START 82 | self.send_data (0x17) 83 | self.send_data (0x17) 84 | self.send_data (0x17) # 07 0f 17 1f 27 2F 37 2f 85 | 86 | self.send_command(0x04) # POWER_ON 87 | self.ReadBusy() 88 | 89 | self.send_command(0x00) # PANEL_SETTING 90 | self.send_data(0x0F) # LUT from OTP 91 | 92 | return 0 93 | 94 | def getbuffer(self, image): 95 | # logger.debug("bufsiz = ",int(self.width/8) * self.height) 96 | buf = [0xFF] * (int(self.width/8) * self.height) 97 | image_monocolor = image.convert('1') 98 | imwidth, imheight = image_monocolor.size 99 | pixels = image_monocolor.load() 100 | # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) 101 | if(imwidth == self.width and imheight == self.height): 102 | logger.debug("Horizontal") 103 | for y in range(imheight): 104 | for x in range(imwidth): 105 | # Set the bits for the column of pixels at the current position. 106 | if pixels[x, y] == 0: 107 | buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) 108 | elif(imwidth == self.height and imheight == self.width): 109 | logger.debug("Vertical") 110 | for y in range(imheight): 111 | for x in range(imwidth): 112 | newx = y 113 | newy = self.height - x - 1 114 | if pixels[x, y] == 0: 115 | buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) 116 | return buf 117 | 118 | def display(self, imageblack, imagered): 119 | self.send_command(0x10) 120 | for i in range(0, int(self.width * self.height / 8)): 121 | self.send_data(imageblack[i]) 122 | 123 | self.send_command(0x13) 124 | for i in range(0, int(self.width * self.height / 8)): 125 | self.send_data(imagered[i]) 126 | 127 | self.send_command(0x12) 128 | self.ReadBusy() 129 | 130 | def Clear(self): 131 | self.send_command(0x10) 132 | for i in range(0, int(self.width * self.height / 8)): 133 | self.send_data(0xFF) 134 | 135 | self.send_command(0x13) 136 | for i in range(0, int(self.width * self.height / 8)): 137 | self.send_data(0xFF) 138 | 139 | self.send_command(0x12) 140 | self.ReadBusy() 141 | 142 | def sleep(self): 143 | self.send_command(0x02) # POWER_OFF 144 | self.ReadBusy() 145 | self.send_command(0x07) # DEEP_SLEEP 146 | self.send_data(0xA5) # check code 147 | 148 | epdconfig.delay_ms(2000) 149 | epdconfig.module_exit() 150 | ### END OF FILE ### 151 | 152 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd5in65f.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:utf-8 -*- 3 | # ***************************************************************************** 4 | # * | File : epd5in65f.py 5 | # * | Author : Waveshare team 6 | # * | Function : Electronic paper driver 7 | # * | Info : 8 | # *---------------- 9 | # * | This version: V1.0 10 | # * | Date : 2020-03-02 11 | # # | Info : python demo 12 | # ----------------------------------------------------------------------------- 13 | # Permission is hereby granted, free of charge, to any person obtaining a copy 14 | # of this software and associated documnetation files (the "Software"), to deal 15 | # in the Software without restriction, including without limitation the rights 16 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | # copies of the Software, and to permit persons to whom the Software is 18 | # furished to do so, subject to the following conditions: 19 | # 20 | # The above copyright notice and this permission notice shall be included in 21 | # all copies or substantial portions of the Software. 22 | # 23 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 | # THE SOFTWARE. 30 | # 31 | 32 | import logging 33 | from . import epdconfig 34 | 35 | from PIL import Image 36 | 37 | # Display resolution 38 | EPD_WIDTH = 600 39 | EPD_HEIGHT = 448 40 | 41 | logger = logging.getLogger(__name__) 42 | 43 | class EPD: 44 | def __init__(self): 45 | self.reset_pin = epdconfig.RST_PIN 46 | self.dc_pin = epdconfig.DC_PIN 47 | self.busy_pin = epdconfig.BUSY_PIN 48 | self.cs_pin = epdconfig.CS_PIN 49 | self.width = EPD_WIDTH 50 | self.height = EPD_HEIGHT 51 | self.BLACK = 0x000000 # 0000 BGR 52 | self.WHITE = 0xffffff # 0001 53 | self.GREEN = 0x00ff00 # 0010 54 | self.BLUE = 0xff0000 # 0011 55 | self.RED = 0x0000ff # 0100 56 | self.YELLOW = 0x00ffff # 0101 57 | self.ORANGE = 0x0080ff # 0110 58 | 59 | 60 | # Hardware reset 61 | def reset(self): 62 | epdconfig.digital_write(self.reset_pin, 1) 63 | epdconfig.delay_ms(600) 64 | epdconfig.digital_write(self.reset_pin, 0) 65 | epdconfig.delay_ms(2) 66 | epdconfig.digital_write(self.reset_pin, 1) 67 | epdconfig.delay_ms(200) 68 | 69 | def send_command(self, command): 70 | epdconfig.digital_write(self.dc_pin, 0) 71 | epdconfig.digital_write(self.cs_pin, 0) 72 | epdconfig.spi_writebyte([command]) 73 | epdconfig.digital_write(self.cs_pin, 1) 74 | 75 | def send_data(self, data): 76 | epdconfig.digital_write(self.dc_pin, 1) 77 | epdconfig.digital_write(self.cs_pin, 0) 78 | epdconfig.spi_writebyte([data]) 79 | epdconfig.digital_write(self.cs_pin, 1) 80 | 81 | # send a lot of data 82 | def send_data2(self, data): 83 | epdconfig.digital_write(self.dc_pin, 1) 84 | epdconfig.digital_write(self.cs_pin, 0) 85 | epdconfig.spi_writebyte2(data) 86 | epdconfig.digital_write(self.cs_pin, 1) 87 | 88 | def ReadBusyHigh(self): 89 | logger.debug("e-Paper busy") 90 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 91 | epdconfig.delay_ms(100) 92 | logger.debug("e-Paper busy release") 93 | 94 | def ReadBusyLow(self): 95 | logger.debug("e-Paper busy") 96 | while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy 97 | epdconfig.delay_ms(100) 98 | logger.debug("e-Paper busy release") 99 | 100 | def init(self): 101 | if (epdconfig.module_init() != 0): 102 | return -1 103 | # EPD hardware init start 104 | self.reset() 105 | 106 | self.ReadBusyHigh() 107 | self.send_command(0x00) 108 | self.send_data(0xEF) 109 | self.send_data(0x08) 110 | self.send_command(0x01) 111 | self.send_data(0x37) 112 | self.send_data(0x00) 113 | self.send_data(0x23) 114 | self.send_data(0x23) 115 | self.send_command(0x03) 116 | self.send_data(0x00) 117 | self.send_command(0x06) 118 | self.send_data(0xC7) 119 | self.send_data(0xC7) 120 | self.send_data(0x1D) 121 | self.send_command(0x30) 122 | self.send_data(0x3c) 123 | self.send_command(0x41) 124 | self.send_data(0x00) 125 | self.send_command(0x50) 126 | self.send_data(0x37) 127 | self.send_command(0x60) 128 | self.send_data(0x22) 129 | self.send_command(0x61) 130 | self.send_data(0x02) 131 | self.send_data(0x58) 132 | self.send_data(0x01) 133 | self.send_data(0xC0) 134 | self.send_command(0xE3) 135 | self.send_data(0xAA) 136 | 137 | epdconfig.delay_ms(100) 138 | self.send_command(0x50) 139 | self.send_data(0x37) 140 | # EPD hardware init end 141 | return 0 142 | 143 | def getbuffer(self, image): 144 | # Create a pallette with the 7 colors supported by the panel 145 | pal_image = Image.new("P", (1,1)) 146 | pal_image.putpalette( (0,0,0, 255,255,255, 0,255,0, 0,0,255, 255,0,0, 255,255,0, 255,128,0) + (0,0,0)*249) 147 | 148 | # Check if we need to rotate the image 149 | imwidth, imheight = image.size 150 | if(imwidth == self.width and imheight == self.height): 151 | image_temp = image 152 | elif(imwidth == self.height and imheight == self.width): 153 | image_temp = image.rotate(90, expand=True) 154 | else: 155 | logger.warning("Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height)) 156 | 157 | # Convert the soruce image to the 7 colors, dithering if needed 158 | image_7color = image_temp.convert("RGB").quantize(palette=pal_image) 159 | buf_7color = bytearray(image_7color.tobytes('raw')) 160 | 161 | # PIL does not support 4 bit color, so pack the 4 bits of color 162 | # into a single byte to transfer to the panel 163 | buf = [0x00] * int(self.width * self.height / 2) 164 | idx = 0 165 | for i in range(0, len(buf_7color), 2): 166 | buf[idx] = (buf_7color[i] << 4) + buf_7color[i+1] 167 | idx += 1 168 | 169 | return buf 170 | 171 | def display(self,image): 172 | self.send_command(0x61) #Set Resolution setting 173 | self.send_data(0x02) 174 | self.send_data(0x58) 175 | self.send_data(0x01) 176 | self.send_data(0xC0) 177 | self.send_command(0x10) 178 | 179 | self.send_data2(image) 180 | self.send_command(0x04) #0x04 181 | self.ReadBusyHigh() 182 | self.send_command(0x12) #0x12 183 | self.ReadBusyHigh() 184 | self.send_command(0x02) #0x02 185 | self.ReadBusyLow() 186 | epdconfig.delay_ms(500) 187 | 188 | def Clear(self): 189 | self.send_command(0x61) #Set Resolution setting 190 | self.send_data(0x02) 191 | self.send_data(0x58) 192 | self.send_data(0x01) 193 | self.send_data(0xC0) 194 | self.send_command(0x10) 195 | 196 | # Set all pixels to white 197 | buf = [0x11] * int(self.width * self.height / 2) 198 | self.send_data2(buf) 199 | 200 | self.send_command(0x04) #0x04 201 | self.ReadBusyHigh() 202 | self.send_command(0x12) #0x12 203 | self.ReadBusyHigh() 204 | self.send_command(0x02) #0x02 205 | self.ReadBusyLow() 206 | epdconfig.delay_ms(500) 207 | 208 | def sleep(self): 209 | epdconfig.delay_ms(500) 210 | self.send_command(0x07) # DEEP_SLEEP 211 | self.send_data(0XA5) 212 | epdconfig.digital_write(self.reset_pin, 0) 213 | 214 | epdconfig.delay_ms(2000) 215 | epdconfig.module_exit() 216 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd5in79g.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd5in79b.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V1.0 8 | # * | Date : 2024-03-05 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | from PIL import Image 35 | 36 | # Display resolution 37 | EPD_WIDTH = 792 38 | EPD_HEIGHT = 272 39 | 40 | logger = logging.getLogger(__name__) 41 | 42 | class EPD: 43 | def __init__(self): 44 | self.reset_pin = epdconfig.RST_PIN 45 | self.dc_pin = epdconfig.DC_PIN 46 | self.busy_pin = epdconfig.BUSY_PIN 47 | self.cs_pin = epdconfig.CS_PIN 48 | self.width = EPD_WIDTH 49 | self.height = EPD_HEIGHT 50 | self.BLACK = 0x000000 # 00 BGR 51 | self.WHITE = 0xffffff # 01 52 | self.YELLOW = 0x00ffff # 10 53 | self.RED = 0x0000ff # 11 54 | 55 | # Hardware reset 56 | def reset(self): 57 | epdconfig.digital_write(self.reset_pin, 1) 58 | epdconfig.delay_ms(200) 59 | epdconfig.digital_write(self.reset_pin, 0) 60 | epdconfig.delay_ms(1) 61 | epdconfig.digital_write(self.reset_pin, 1) 62 | epdconfig.delay_ms(200) 63 | 64 | def send_command(self, command): 65 | epdconfig.digital_write(self.dc_pin, 0) 66 | epdconfig.digital_write(self.cs_pin, 0) 67 | epdconfig.spi_writebyte([command]) 68 | epdconfig.digital_write(self.cs_pin, 1) 69 | 70 | def send_data(self, data): 71 | epdconfig.digital_write(self.dc_pin, 1) 72 | epdconfig.digital_write(self.cs_pin, 0) 73 | epdconfig.spi_writebyte([data]) 74 | epdconfig.digital_write(self.cs_pin, 1) 75 | 76 | # send a lot of data 77 | def send_data2(self, data): 78 | epdconfig.digital_write(self.dc_pin, 1) 79 | epdconfig.digital_write(self.cs_pin, 0) 80 | epdconfig.spi_writebyte2(data) 81 | epdconfig.digital_write(self.cs_pin, 1) 82 | 83 | def ReadBusyH(self): 84 | logger.debug("e-Paper busy H") 85 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 86 | epdconfig.delay_ms(5) 87 | epdconfig.delay_ms(200) 88 | logger.debug("e-Paper busy H release") 89 | 90 | def ReadBusyL(self): 91 | logger.debug("e-Paper busy L") 92 | while(epdconfig.digital_read(self.busy_pin) == 1): # 0: busy, 1: idle 93 | epdconfig.delay_ms(5) 94 | logger.debug("e-Paper busy L release") 95 | 96 | def TurnOnDisplay(self): 97 | self.send_command(0xA2) 98 | self.send_data(0x00) 99 | 100 | self.send_command(0x12) 101 | self.send_data(0x00) 102 | epdconfig.delay_ms(100) 103 | self.ReadBusyH() 104 | 105 | def init(self): 106 | if (epdconfig.module_init() != 0): 107 | return -1 108 | 109 | self.reset() 110 | 111 | self.ReadBusyH() 112 | 113 | self.send_command(0xA2) 114 | self.send_data(0x01) 115 | 116 | self.send_command(0x00) 117 | self.send_data(0x03) 118 | self.send_data(0x29) 119 | 120 | self.send_command(0xA2) 121 | self.send_data(0x02) 122 | 123 | self.send_command(0x00) 124 | self.send_data(0x07) 125 | self.send_data(0x29) 126 | 127 | self.send_command(0xA2) 128 | self.send_data(0x00) 129 | 130 | self.send_command(0x50) 131 | self.send_data(0x97) 132 | 133 | self.send_command(0x61) 134 | self.send_data(0x01) 135 | self.send_data(0x8c) 136 | self.send_data(0x01) 137 | self.send_data(0x10) 138 | 139 | self.send_command(0x06) 140 | self.send_data(0x38) 141 | self.send_data(0x38) 142 | self.send_data(0x38) 143 | self.send_data(0x00) 144 | 145 | 146 | self.send_command(0xE9) 147 | self.send_data(0x01) 148 | 149 | 150 | self.send_command(0xE0) 151 | self.send_data(0x01) 152 | 153 | self.send_command(0x04) 154 | self.ReadBusyH() 155 | return 0 156 | 157 | def getbuffer(self, image): 158 | # Create a pallette with the 4 colors supported by the panel 159 | pal_image = Image.new("P", (1,1)) 160 | pal_image.putpalette( (0,0,0, 255,255,255, 255,255,0, 255,0,0) + (0,0,0)*252) 161 | 162 | # Check if we need to rotate the image 163 | imwidth, imheight = image.size 164 | if(imwidth == self.width and imheight == self.height): 165 | image_temp = image 166 | elif(imwidth == self.height and imheight == self.width): 167 | image_temp = image.rotate(90, expand=True) 168 | else: 169 | logger.warning("Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height)) 170 | 171 | # Convert the soruce image to the 4 colors, dithering if needed 172 | image_4color = image_temp.convert("RGB").quantize(palette=pal_image) 173 | buf_4color = bytearray(image_4color.tobytes('raw')) 174 | 175 | # into a single byte to transfer to the panel 176 | buf = [0x00] * int(self.width * self.height / 4) 177 | idx = 0 178 | for i in range(0, len(buf_4color), 4): 179 | buf[idx] = (buf_4color[i] << 6) + (buf_4color[i+1] << 4) + (buf_4color[i+2] << 2) + buf_4color[i+3] 180 | idx += 1 181 | return buf 182 | 183 | def display(self, image): 184 | Width =int(self.width / 8) 185 | Width1 =int(self.width / 4) 186 | Height = self.height 187 | 188 | self.send_command(0xA2) 189 | self.send_data(0x02) 190 | self.send_command(0x10) 191 | for i in range(Height//2): 192 | self.send_data2(image[i * Width1 : i * Width1+Width]) 193 | self.send_data2(image[(Height-i-1) * Width1 : (Height-i-1) * Width1+Width]) 194 | 195 | self.send_command(0xA2) 196 | self.send_data(0x01) 197 | self.send_command(0x10) 198 | for i in range(Height//2): 199 | self.send_data2(image[i * Width1+Width : (i+1) * Width1]) 200 | self.send_data2(image[(Height-i-1) * Width1+Width : (Height-i) * Width1]) 201 | 202 | self.TurnOnDisplay() 203 | 204 | def Clear(self, color=0x55): 205 | self.send_command(0xA2) 206 | self.send_data(0x02) 207 | self.send_command(0x10) 208 | self.send_data2([color] * int(self.height) * int(self.width/8)) 209 | 210 | self.send_command(0xA2) 211 | self.send_data(0x01) 212 | self.send_command(0x10) 213 | self.send_data2([color] * int(self.height) * int(self.width/8)) 214 | 215 | self.TurnOnDisplay() 216 | 217 | def sleep(self): 218 | self.send_command(0X10) # deep sleep 219 | self.send_data(0x03) 220 | 221 | epdconfig.delay_ms(2000) 222 | epdconfig.module_exit() 223 | ### END OF FILE ### 224 | 225 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd5in83.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd5in83.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V4.0 8 | # * | Date : 2019-06-20 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | # Display resolution 35 | EPD_WIDTH = 600 36 | EPD_HEIGHT = 448 37 | 38 | logger = logging.getLogger(__name__) 39 | 40 | class EPD: 41 | def __init__(self): 42 | self.reset_pin = epdconfig.RST_PIN 43 | self.dc_pin = epdconfig.DC_PIN 44 | self.busy_pin = epdconfig.BUSY_PIN 45 | self.cs_pin = epdconfig.CS_PIN 46 | self.width = EPD_WIDTH 47 | self.height = EPD_HEIGHT 48 | 49 | # Hardware reset 50 | def reset(self): 51 | epdconfig.digital_write(self.reset_pin, 1) 52 | epdconfig.delay_ms(200) 53 | epdconfig.digital_write(self.reset_pin, 0) 54 | epdconfig.delay_ms(2) 55 | epdconfig.digital_write(self.reset_pin, 1) 56 | epdconfig.delay_ms(200) 57 | 58 | def send_command(self, command): 59 | epdconfig.digital_write(self.dc_pin, 0) 60 | epdconfig.digital_write(self.cs_pin, 0) 61 | epdconfig.spi_writebyte([command]) 62 | epdconfig.digital_write(self.cs_pin, 1) 63 | 64 | def send_data(self, data): 65 | epdconfig.digital_write(self.dc_pin, 1) 66 | epdconfig.digital_write(self.cs_pin, 0) 67 | epdconfig.spi_writebyte([data]) 68 | epdconfig.digital_write(self.cs_pin, 1) 69 | 70 | def ReadBusy(self): 71 | logger.debug("e-Paper busy") 72 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 73 | epdconfig.delay_ms(100) 74 | logger.debug("e-Paper busy release") 75 | 76 | def init(self): 77 | if (epdconfig.module_init() != 0): 78 | return -1 79 | # EPD hardware init start 80 | self.reset() 81 | 82 | self.send_command(0x01) # POWER_SETTING 83 | self.send_data(0x37) 84 | self.send_data(0x00) 85 | 86 | self.send_command(0x00) # PANEL_SETTING 87 | self.send_data(0xCF) 88 | self.send_data(0x08) 89 | 90 | self.send_command(0x06) # BOOSTER_SOFT_START 91 | self.send_data(0xc7) 92 | self.send_data(0xcc) 93 | self.send_data(0x28) 94 | 95 | self.send_command(0x04) # POWER_ON 96 | self.ReadBusy() 97 | 98 | self.send_command(0x30) # PLL_CONTROL 99 | self.send_data(0x3c) 100 | 101 | self.send_command(0x41) # TEMPERATURE_CALIBRATION 102 | self.send_data(0x00) 103 | 104 | self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING 105 | self.send_data(0x77) 106 | 107 | self.send_command(0x60) # TCON_SETTING 108 | self.send_data(0x22) 109 | 110 | self.send_command(0x61) # TCON_RESOLUTION 111 | self.send_data(0x02) # source 600 112 | self.send_data(0x58) 113 | self.send_data(0x01) # gate 448 114 | self.send_data(0xC0) 115 | 116 | self.send_command(0x82) # VCM_DC_SETTING 117 | self.send_data(0x1E) # decide by LUT file 118 | 119 | self.send_command(0xe5) # FLASH MODE 120 | self.send_data(0x03) 121 | 122 | # EPD hardware init end 123 | return 0 124 | 125 | def getbuffer(self, image): 126 | buf = [0x00] * int(self.width * self.height / 4) 127 | image_monocolor = image.convert('1') 128 | imwidth, imheight = image_monocolor.size 129 | pixels = image_monocolor.load() 130 | logger.debug('imwidth = %d imheight = %d ',imwidth, imheight) 131 | if(imwidth == self.width and imheight == self.height): 132 | for y in range(imheight): 133 | for x in range(imwidth): 134 | # Set the bits for the column of pixels at the current position. 135 | if pixels[x, y] < 64: # black 136 | buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2)) 137 | elif pixels[x, y] < 192: # convert gray to red 138 | buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2)) 139 | buf[int((x + y * self.width) / 4)] |= 0x40 >> (x % 4 * 2) 140 | else: # white 141 | buf[int((x + y * self.width) / 4)] |= 0xC0 >> (x % 4 * 2) 142 | elif(imwidth == self.height and imheight == self.width): 143 | for y in range(imheight): 144 | for x in range(imwidth): 145 | newx = y 146 | newy = self.height - x - 1 147 | if pixels[x, y] < 64: # black 148 | buf[int((newx + newy*self.width) / 4)] &= ~(0xC0 >> (y % 4 * 2)) 149 | elif pixels[x, y] < 192: # convert gray to red 150 | buf[int((newx + newy*self.width) / 4)] &= ~(0xC0 >> (y % 4 * 2)) 151 | buf[int((newx + newy*self.width) / 4)] |= 0x40 >> (y % 4 * 2) 152 | else: # white 153 | buf[int((newx + newy*self.width) / 4)] |= 0xC0 >> (y % 4 * 2) 154 | return buf 155 | 156 | def display(self, image): 157 | self.send_command(0x10) 158 | for i in range(0, int(self.width / 4 * self.height)): 159 | temp1 = image[i] 160 | j = 0 161 | while (j < 4): 162 | if ((temp1 & 0xC0) == 0xC0): 163 | temp2 = 0x03 164 | elif ((temp1 & 0xC0) == 0x00): 165 | temp2 = 0x00 166 | else: 167 | temp2 = 0x04 168 | temp2 = (temp2 << 4) & 0xFF 169 | temp1 = (temp1 << 2) & 0xFF 170 | j += 1 171 | if((temp1 & 0xC0) == 0xC0): 172 | temp2 |= 0x03 173 | elif ((temp1 & 0xC0) == 0x00): 174 | temp2 |= 0x00 175 | else: 176 | temp2 |= 0x04 177 | temp1 = (temp1 << 2) & 0xFF 178 | self.send_data(temp2) 179 | j += 1 180 | 181 | self.send_command(0x12) 182 | epdconfig.delay_ms(100) 183 | self.ReadBusy() 184 | 185 | def Clear(self): 186 | self.send_command(0x10) 187 | for i in range(0, int(self.width / 4 * self.height)): 188 | for j in range(0, 4): 189 | self.send_data(0x33) 190 | self.send_command(0x12) 191 | self.ReadBusy() 192 | 193 | def sleep(self): 194 | self.send_command(0x02) # POWER_OFF 195 | self.ReadBusy() 196 | self.send_command(0x07) # DEEP_SLEEP 197 | self.send_data(0XA5) 198 | 199 | epdconfig.delay_ms(2000) 200 | epdconfig.module_exit() 201 | 202 | ### END OF FILE ### 203 | 204 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd5in83_V2.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd5in83_V2.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V1.1 8 | # * | Date : 2022-08-10 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | import logging 31 | from . import epdconfig 32 | 33 | # Display resolution 34 | EPD_WIDTH = 648 35 | EPD_HEIGHT = 480 36 | 37 | logger = logging.getLogger(__name__) 38 | 39 | class EPD: 40 | def __init__(self): 41 | self.reset_pin = epdconfig.RST_PIN 42 | self.dc_pin = epdconfig.DC_PIN 43 | self.busy_pin = epdconfig.BUSY_PIN 44 | self.cs_pin = epdconfig.CS_PIN 45 | self.width = EPD_WIDTH 46 | self.height = EPD_HEIGHT 47 | 48 | # Hardware reset 49 | def reset(self): 50 | epdconfig.digital_write(self.reset_pin, 1) 51 | epdconfig.delay_ms(200) 52 | epdconfig.digital_write(self.reset_pin, 0) 53 | epdconfig.delay_ms(2) 54 | epdconfig.digital_write(self.reset_pin, 1) 55 | epdconfig.delay_ms(200) 56 | 57 | def send_command(self, command): 58 | epdconfig.digital_write(self.dc_pin, 0) 59 | epdconfig.digital_write(self.cs_pin, 0) 60 | epdconfig.spi_writebyte([command]) 61 | epdconfig.digital_write(self.cs_pin, 1) 62 | 63 | def send_data(self, data): 64 | epdconfig.digital_write(self.dc_pin, 1) 65 | epdconfig.digital_write(self.cs_pin, 0) 66 | epdconfig.spi_writebyte([data]) 67 | epdconfig.digital_write(self.cs_pin, 1) 68 | 69 | # send a lot of data 70 | def send_data2(self, data): 71 | epdconfig.digital_write(self.dc_pin, 1) 72 | epdconfig.digital_write(self.cs_pin, 0) 73 | epdconfig.spi_writebyte2(data) 74 | epdconfig.digital_write(self.cs_pin, 1) 75 | 76 | def ReadBusy(self): 77 | logger.debug("e-Paper busy") 78 | while(epdconfig.digital_read(self.busy_pin) == 0): 79 | epdconfig.delay_ms(20) 80 | logger.debug("e-Paper busy release") 81 | 82 | def TurnOnDisplay(self): 83 | self.send_command(0x12); #POWER ON 84 | epdconfig.delay_ms(100) 85 | self.ReadBusy(); 86 | 87 | def init(self): 88 | if (epdconfig.module_init() != 0): 89 | return -1 90 | # EPD hardware init start 91 | self.reset() 92 | 93 | self.send_command(0x01) #POWER SETTING 94 | self.send_data (0x07) 95 | self.send_data (0x07) #VGH=20V,VGL=-20V 96 | self.send_data (0x3f) #VDH=15V 97 | self.send_data (0x3f) #VDL=-15V 98 | 99 | self.send_command(0x04) #POWER ON 100 | epdconfig.delay_ms(100) 101 | self.ReadBusy() #waiting for the electronic paper IC to release the idle signal 102 | 103 | self.send_command(0X00) #PANNEL SETTING 104 | self.send_data(0x1F) #KW-3f KWR-2F BWROTP 0f BWOTP 1f 105 | 106 | self.send_command(0x61) #tres 107 | self.send_data (0x02) #source 648 108 | self.send_data (0x88) 109 | self.send_data (0x01) #gate 480 110 | self.send_data (0xE0) 111 | 112 | self.send_command(0X15) 113 | self.send_data(0x00) 114 | 115 | self.send_command(0X50) #VCOM AND DATA INTERVAL SETTING 116 | self.send_data(0x10) 117 | self.send_data(0x07) 118 | 119 | self.send_command(0X60) #TCON SETTING 120 | self.send_data(0x22) 121 | 122 | # EPD hardware init end 123 | return 0 124 | 125 | def getbuffer(self, image): 126 | # logger.debug("bufsiz = ",int(self.width/8) * self.height) 127 | buf = [0xFF] * (int(self.width/8) * self.height) 128 | image_monocolor = image.convert('1') 129 | imwidth, imheight = image_monocolor.size 130 | pixels = image_monocolor.load() 131 | # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) 132 | if(imwidth == self.width and imheight == self.height): 133 | logger.debug("Vertical") 134 | for y in range(imheight): 135 | for x in range(imwidth): 136 | # Set the bits for the column of pixels at the current position. 137 | if pixels[x, y] == 0: 138 | buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) 139 | elif(imwidth == self.height and imheight == self.width): 140 | logger.debug("Horizontal") 141 | for y in range(imheight): 142 | for x in range(imwidth): 143 | newx = y 144 | newy = self.height - x - 1 145 | if pixels[x, y] == 0: 146 | buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) 147 | return buf 148 | 149 | def display(self, image): 150 | buf = [0x00] * int(self.width * self.height / 8) 151 | for i in range(0, int(self.width * self.height / 8)): 152 | buf[i] = ~image[i] 153 | self.send_command(0x10) 154 | self.send_data2([0x00] * int(self.width * self.height / 8)) 155 | self.send_command(0x13) 156 | self.send_data2(buf) 157 | self.TurnOnDisplay() 158 | 159 | def Clear(self): 160 | self.send_command(0x10) 161 | self.send_data2([0x00] * int(self.width * self.height / 8)) 162 | self.send_command(0x13) 163 | self.send_data2([0x00] * int(self.width * self.height / 8)) 164 | self.TurnOnDisplay() 165 | 166 | def sleep(self): 167 | self.send_command(0x02) # POWER_OFF 168 | self.ReadBusy() 169 | self.send_command(0x07) # DEEP_SLEEP 170 | self.send_data(0XA5) 171 | 172 | epdconfig.delay_ms(2000) 173 | epdconfig.module_exit() 174 | 175 | ### END OF FILE ### 176 | 177 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd5in83b_V2.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd5in83b_V2.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V1.1 8 | # * | Date : 2022-08-10 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | # Display resolution 35 | EPD_WIDTH = 648 36 | EPD_HEIGHT = 480 37 | 38 | logger = logging.getLogger(__name__) 39 | 40 | class EPD: 41 | def __init__(self): 42 | self.reset_pin = epdconfig.RST_PIN 43 | self.dc_pin = epdconfig.DC_PIN 44 | self.busy_pin = epdconfig.BUSY_PIN 45 | self.cs_pin = epdconfig.CS_PIN 46 | self.width = EPD_WIDTH 47 | self.height = EPD_HEIGHT 48 | 49 | # Hardware reset 50 | def reset(self): 51 | epdconfig.digital_write(self.reset_pin, 1) 52 | epdconfig.delay_ms(200) 53 | epdconfig.digital_write(self.reset_pin, 0) 54 | epdconfig.delay_ms(1) 55 | epdconfig.digital_write(self.reset_pin, 1) 56 | epdconfig.delay_ms(200) 57 | 58 | def send_command(self, command): 59 | epdconfig.digital_write(self.dc_pin, 0) 60 | epdconfig.digital_write(self.cs_pin, 0) 61 | epdconfig.spi_writebyte([command]) 62 | epdconfig.digital_write(self.cs_pin, 1) 63 | 64 | def send_data(self, data): 65 | epdconfig.digital_write(self.dc_pin, 1) 66 | epdconfig.digital_write(self.cs_pin, 0) 67 | epdconfig.spi_writebyte([data]) 68 | epdconfig.digital_write(self.cs_pin, 1) 69 | 70 | # send a lot of data 71 | def send_data2(self, data): 72 | epdconfig.digital_write(self.dc_pin, 1) 73 | epdconfig.digital_write(self.cs_pin, 0) 74 | epdconfig.spi_writebyte2(data) 75 | epdconfig.digital_write(self.cs_pin, 1) 76 | 77 | def ReadBusy(self): 78 | logger.debug("e-Paper busy") 79 | self.send_command(0X71) 80 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 81 | self.send_command(0X71) 82 | epdconfig.delay_ms(200) 83 | logger.debug("e-Paper busy release") 84 | 85 | def init(self): 86 | if (epdconfig.module_init() != 0): 87 | return -1 88 | 89 | self.reset() 90 | 91 | self.send_command(0x01) #POWER SETTING 92 | self.send_data (0x07) 93 | self.send_data (0x07) #VGH=20V,VGL=-20V 94 | self.send_data (0x3f) #VDH=15V 95 | self.send_data (0x3f) #VDL=-15V 96 | 97 | self.send_command(0x04) #POWER ON 98 | epdconfig.delay_ms(100) 99 | self.ReadBusy() #waiting for the electronic paper IC to release the idle signal 100 | 101 | self.send_command(0X00) #PANNEL SETTING 102 | self.send_data(0x0F) #KW-3f KWR-2F BWROTP 0f BWOTP 1f 103 | 104 | self.send_command(0x61) #tres 105 | self.send_data (0x02) #source 648 106 | self.send_data (0x88) 107 | self.send_data (0x01) #gate 480 108 | self.send_data (0xe0) 109 | 110 | self.send_command(0X15) 111 | self.send_data(0x00) 112 | 113 | self.send_command(0X50) #VCOM AND DATA INTERVAL SETTING 114 | self.send_data(0x11) 115 | self.send_data(0x07) 116 | 117 | self.send_command(0X60) #TCON SETTING 118 | self.send_data(0x22) 119 | 120 | return 0 121 | 122 | def getbuffer(self, image): 123 | # logger.debug("bufsiz = ",int(self.width/8) * self.height) 124 | buf = [0xFF] * (int(self.width/8) * self.height) 125 | image_monocolor = image.convert('1') 126 | imwidth, imheight = image_monocolor.size 127 | pixels = image_monocolor.load() 128 | # logger.debug("imwidth = %d, imheight = %d",imwidth,imheight) 129 | if(imwidth == self.width and imheight == self.height): 130 | logger.debug("Vertical") 131 | for y in range(imheight): 132 | for x in range(imwidth): 133 | # Set the bits for the column of pixels at the current position. 134 | if pixels[x, y] == 0: 135 | buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) 136 | elif(imwidth == self.height and imheight == self.width): 137 | logger.debug("Horizontal") 138 | for y in range(imheight): 139 | for x in range(imwidth): 140 | newx = y 141 | newy = self.height - x - 1 142 | if pixels[x, y] == 0: 143 | buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) 144 | return buf 145 | 146 | def display(self, imageblack, imagered): 147 | buf = [0x00] * int(self.width * self.height / 8) 148 | for i in range(0, int(self.width * self.height / 8)): 149 | buf[i] = ~imagered[i] 150 | 151 | if (imageblack != None): 152 | self.send_command(0X10) 153 | self.send_data2(imageblack) 154 | if (imagered != None): 155 | self.send_command(0X13) 156 | self.send_data2(buf) 157 | 158 | self.send_command(0x12) 159 | epdconfig.delay_ms(200) 160 | self.ReadBusy() 161 | 162 | def Clear(self): 163 | self.send_command(0X10) 164 | self.send_data2([0xFF] * int(self.width * self.height / 8)) 165 | self.send_command(0X13) 166 | self.send_data2([0x00] * int(self.width * self.height / 8)) 167 | 168 | self.send_command(0x12) 169 | epdconfig.delay_ms(200) 170 | self.ReadBusy() 171 | 172 | def sleep(self): 173 | self.send_command(0X02) # power off 174 | self.ReadBusy() 175 | self.send_command(0X07) # deep sleep 176 | self.send_data(0xA5) 177 | 178 | epdconfig.delay_ms(2000) 179 | epdconfig.module_exit() 180 | ### END OF FILE ### 181 | 182 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd5in83bc.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd5in83b.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V4.0 8 | # * | Date : 2019-06-20 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | # Display resolution 35 | EPD_WIDTH = 600 36 | EPD_HEIGHT = 448 37 | 38 | logger = logging.getLogger(__name__) 39 | 40 | class EPD: 41 | def __init__(self): 42 | self.reset_pin = epdconfig.RST_PIN 43 | self.dc_pin = epdconfig.DC_PIN 44 | self.busy_pin = epdconfig.BUSY_PIN 45 | self.cs_pin = epdconfig.CS_PIN 46 | self.width = EPD_WIDTH 47 | self.height = EPD_HEIGHT 48 | 49 | # Hardware reset 50 | def reset(self): 51 | epdconfig.digital_write(self.reset_pin, 1) 52 | epdconfig.delay_ms(200) 53 | epdconfig.digital_write(self.reset_pin, 0) 54 | epdconfig.delay_ms(5) 55 | epdconfig.digital_write(self.reset_pin, 1) 56 | epdconfig.delay_ms(200) 57 | 58 | def send_command(self, command): 59 | epdconfig.digital_write(self.dc_pin, 0) 60 | epdconfig.digital_write(self.cs_pin, 0) 61 | epdconfig.spi_writebyte([command]) 62 | epdconfig.digital_write(self.cs_pin, 1) 63 | 64 | def send_data(self, data): 65 | epdconfig.digital_write(self.dc_pin, 1) 66 | epdconfig.digital_write(self.cs_pin, 0) 67 | epdconfig.spi_writebyte([data]) 68 | epdconfig.digital_write(self.cs_pin, 1) 69 | 70 | def ReadBusy(self): 71 | logger.debug("e-Paper busy") 72 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 73 | epdconfig.delay_ms(100) 74 | logger.debug("e-Paper busy release") 75 | 76 | def init(self): 77 | if (epdconfig.module_init() != 0): 78 | return -1 79 | 80 | self.reset() 81 | 82 | self.send_command(0x01) # POWER_SETTING 83 | self.send_data(0x37) 84 | self.send_data(0x00) 85 | 86 | self.send_command(0x00) # PANEL_SETTING 87 | self.send_data(0xCF) 88 | self.send_data(0x08) 89 | 90 | self.send_command(0x30) # PLL_CONTROL 91 | self.send_data(0x3A) # PLL: 0-15:0x3C, 15+:0x3A 92 | self.send_command(0X82) # VCOM VOLTAGE SETTING 93 | self.send_data(0x28) # all temperature range 94 | 95 | self.send_command(0x06) # boost 96 | self.send_data(0xc7) 97 | self.send_data(0xcc) 98 | self.send_data(0x15) 99 | 100 | self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING 101 | self.send_data(0x77) 102 | 103 | self.send_command(0X60) # TCON SETTING 104 | self.send_data(0x22) 105 | 106 | self.send_command(0X65) # FLASH CONTROL 107 | self.send_data(0x00) 108 | 109 | self.send_command(0x61) # tres 110 | self.send_data(0x02) # source 600 111 | self.send_data(0x58) 112 | self.send_data(0x01) # gate 448 113 | self.send_data(0xc0) 114 | 115 | self.send_command(0xe5) # FLASH MODE 116 | self.send_data(0x03) 117 | self.send_data(0x03) 118 | 119 | return 0 120 | 121 | def getbuffer(self, image): 122 | # logger.debug("bufsiz = ",int(self.width/8) * self.height) 123 | buf = [0xFF] * (int(self.width/8) * self.height) 124 | image_monocolor = image.convert('1') 125 | imwidth, imheight = image_monocolor.size 126 | pixels = image_monocolor.load() 127 | logger.debug('imwidth = %d imheight = %d ',imwidth, imheight) 128 | if(imwidth == self.width and imheight == self.height): 129 | logger.debug("Horizontal") 130 | for y in range(imheight): 131 | for x in range(imwidth): 132 | # Set the bits for the column of pixels at the current position. 133 | if pixels[x, y] == 0: 134 | buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) 135 | elif(imwidth == self.height and imheight == self.width): 136 | logger.debug("Vertical") 137 | for y in range(imheight): 138 | for x in range(imwidth): 139 | newx = y 140 | newy = self.height - x - 1 141 | if pixels[x, y] == 0: 142 | buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) 143 | return buf 144 | 145 | def display(self, imageblack, imagered): 146 | self.send_command(0x10) 147 | for i in range(0, int(self.width / 8 * self.height)): 148 | temp1 = imageblack[i] 149 | temp2 = imagered[i] 150 | j = 0 151 | while (j < 8): 152 | if ((temp2 & 0x80) == 0x00): 153 | temp3 = 0x04 #red 154 | elif ((temp1 & 0x80) == 0x00): 155 | temp3 = 0x00 #black 156 | else: 157 | temp3 = 0x03 #white 158 | 159 | temp3 = (temp3 << 4) & 0xFF 160 | temp1 = (temp1 << 1) & 0xFF 161 | temp2 = (temp2 << 1) & 0xFF 162 | j += 1 163 | if((temp2 & 0x80) == 0x00): 164 | temp3 |= 0x04 #red 165 | elif ((temp1 & 0x80) == 0x00): 166 | temp3 |= 0x00 #black 167 | else: 168 | temp3 |= 0x03 #white 169 | temp1 = (temp1 << 1) & 0xFF 170 | temp2 = (temp2 << 1) & 0xFF 171 | self.send_data(temp3) 172 | j += 1 173 | 174 | self.send_command(0x04) # POWER ON 175 | self.ReadBusy() 176 | self.send_command(0x12) # display refresh 177 | epdconfig.delay_ms(100) 178 | self.ReadBusy() 179 | 180 | def Clear(self): 181 | self.send_command(0x10) 182 | for i in range(0, int(self.width / 8 * self.height)): 183 | self.send_data(0x33) 184 | self.send_data(0x33) 185 | self.send_data(0x33) 186 | self.send_data(0x33) 187 | 188 | self.send_command(0x04) # POWER ON 189 | self.ReadBusy() 190 | self.send_command(0x12) # display refresh 191 | epdconfig.delay_ms(100) 192 | self.ReadBusy() 193 | 194 | def sleep(self): 195 | self.send_command(0x02) # POWER_OFF 196 | self.ReadBusy() 197 | self.send_command(0x07) # DEEP_SLEEP 198 | self.send_data(0xA5) # check code 199 | 200 | epdconfig.delay_ms(2000) 201 | epdconfig.module_exit() 202 | ### END OF FILE ### 203 | 204 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd7in3e.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd7in3f.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V1.0 8 | # * | Date : 2022-10-20 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # ******************************************************************************/ 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy 13 | # of this software and associated documnetation files (the "Software"), to deal 14 | # in the Software without restriction, including without limitation the rights 15 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | # copies of the Software, and to permit persons to whom the Software is 17 | # furished to do so, subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in 20 | # all copies or substantial portions of the Software. 21 | # 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | # THE SOFTWARE. 29 | # 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | from PIL import Image 35 | 36 | # Display resolution 37 | EPD_WIDTH = 800 38 | EPD_HEIGHT = 480 39 | 40 | logger = logging.getLogger(__name__) 41 | 42 | class EPD: 43 | def __init__(self): 44 | self.reset_pin = epdconfig.RST_PIN 45 | self.dc_pin = epdconfig.DC_PIN 46 | self.busy_pin = epdconfig.BUSY_PIN 47 | self.cs_pin = epdconfig.CS_PIN 48 | self.width = EPD_WIDTH 49 | self.height = EPD_HEIGHT 50 | self.BLACK = 0x000000 # 0000 BGR 51 | self.WHITE = 0xffffff # 0001 52 | self.YELLOW = 0x00ffff # 0010 53 | self.RED = 0x0000ff # 0011 54 | # self.ORANGE = 0x0080ff # 0100 55 | self.BLUE = 0xff0000 # 0101 56 | self.GREEN = 0x00ff00 # 0110 57 | 58 | 59 | # Hardware reset 60 | def reset(self): 61 | epdconfig.digital_write(self.reset_pin, 1) 62 | epdconfig.delay_ms(20) 63 | epdconfig.digital_write(self.reset_pin, 0) # module reset 64 | epdconfig.delay_ms(2) 65 | epdconfig.digital_write(self.reset_pin, 1) 66 | epdconfig.delay_ms(20) 67 | 68 | def send_command(self, command): 69 | epdconfig.digital_write(self.dc_pin, 0) 70 | epdconfig.digital_write(self.cs_pin, 0) 71 | epdconfig.spi_writebyte([command]) 72 | epdconfig.digital_write(self.cs_pin, 1) 73 | 74 | def send_data(self, data): 75 | epdconfig.digital_write(self.dc_pin, 1) 76 | epdconfig.digital_write(self.cs_pin, 0) 77 | epdconfig.spi_writebyte([data]) 78 | epdconfig.digital_write(self.cs_pin, 1) 79 | 80 | # send a lot of data 81 | def send_data2(self, data): 82 | epdconfig.digital_write(self.dc_pin, 1) 83 | epdconfig.digital_write(self.cs_pin, 0) 84 | epdconfig.spi_writebyte2(data) 85 | epdconfig.digital_write(self.cs_pin, 1) 86 | 87 | def ReadBusyH(self): 88 | logger.debug("e-Paper busy H") 89 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: busy, 1: idle 90 | epdconfig.delay_ms(5) 91 | logger.debug("e-Paper busy H release") 92 | 93 | def TurnOnDisplay(self): 94 | self.send_command(0x04) # POWER_ON 95 | self.ReadBusyH() 96 | 97 | self.send_command(0x12) # DISPLAY_REFRESH 98 | self.send_data(0X00) 99 | self.ReadBusyH() 100 | 101 | self.send_command(0x02) # POWER_OFF 102 | self.send_data(0X00) 103 | self.ReadBusyH() 104 | 105 | def init(self): 106 | if (epdconfig.module_init() != 0): 107 | return -1 108 | # EPD hardware init start 109 | self.reset() 110 | self.ReadBusyH() 111 | epdconfig.delay_ms(30) 112 | 113 | self.send_command(0xAA) 114 | self.send_data(0x49) 115 | self.send_data(0x55) 116 | self.send_data(0x20) 117 | self.send_data(0x08) 118 | self.send_data(0x09) 119 | self.send_data(0x18) 120 | 121 | self.send_command(0x01) 122 | self.send_data(0x3F) 123 | 124 | self.send_command(0x00) 125 | self.send_data(0x5F) 126 | self.send_data(0x69) 127 | 128 | self.send_command(0x03) 129 | self.send_data(0x00) 130 | self.send_data(0x54) 131 | self.send_data(0x00) 132 | self.send_data(0x44) 133 | 134 | self.send_command(0x05) 135 | self.send_data(0x40) 136 | self.send_data(0x1F) 137 | self.send_data(0x1F) 138 | self.send_data(0x2C) 139 | 140 | self.send_command(0x06) 141 | self.send_data(0x6F) 142 | self.send_data(0x1F) 143 | self.send_data(0x17) 144 | self.send_data(0x49) 145 | 146 | self.send_command(0x08) 147 | self.send_data(0x6F) 148 | self.send_data(0x1F) 149 | self.send_data(0x1F) 150 | self.send_data(0x22) 151 | 152 | self.send_command(0x30) 153 | self.send_data(0x03) 154 | 155 | self.send_command(0x50) 156 | self.send_data(0x3F) 157 | 158 | self.send_command(0x60) 159 | self.send_data(0x02) 160 | self.send_data(0x00) 161 | 162 | self.send_command(0x61) 163 | self.send_data(0x03) 164 | self.send_data(0x20) 165 | self.send_data(0x01) 166 | self.send_data(0xE0) 167 | 168 | self.send_command(0x84) 169 | self.send_data(0x01) 170 | 171 | self.send_command(0xE3) 172 | self.send_data(0x2F) 173 | 174 | self.send_command(0x04) 175 | self.ReadBusyH() 176 | return 0 177 | 178 | def getbuffer(self, image): 179 | # Create a pallette with the 7 colors supported by the panel 180 | pal_image = Image.new("P", (1,1)) 181 | pal_image.putpalette( (0,0,0, 255,255,255, 0,255,0, 0,0,255, 255,0,0, 255,255,0) + (0,0,0)*250) 182 | # pal_image.putpalette( (0,0,0, 255,255,255, 0,255,0, 0,0,255, 255,0,0, 255,255,0, 255,128,0) + (0,0,0)*249) 183 | 184 | # Check if we need to rotate the image 185 | imwidth, imheight = image.size 186 | if(imwidth == self.width and imheight == self.height): 187 | image_temp = image 188 | elif(imwidth == self.height and imheight == self.width): 189 | image_temp = image.rotate(90, expand=True) 190 | else: 191 | logger.warning("Invalid image dimensions: %d x %d, expected %d x %d" % (imwidth, imheight, self.width, self.height)) 192 | 193 | # Convert the soruce image to the 7 colors, dithering if needed 194 | image_7color = image_temp.convert("RGB").quantize(palette=pal_image) 195 | buf_7color = bytearray(image_7color.tobytes('raw')) 196 | 197 | # PIL does not support 4 bit color, so pack the 4 bits of color 198 | # into a single byte to transfer to the panel 199 | buf = [0x00] * int(self.width * self.height / 2) 200 | idx = 0 201 | for i in range(0, len(buf_7color), 2): 202 | buf[idx] = (buf_7color[i] << 4) + buf_7color[i+1] 203 | idx += 1 204 | 205 | return buf 206 | 207 | def display(self, image): 208 | self.send_command(0x10) 209 | self.send_data2(image) 210 | 211 | self.TurnOnDisplay() 212 | 213 | def Clear(self, color=0x11): 214 | self.send_command(0x10) 215 | self.send_data2([color] * int(self.height) * int(self.width/2)) 216 | 217 | self.TurnOnDisplay() 218 | 219 | def sleep(self): 220 | self.send_command(0x07) # DEEP_SLEEP 221 | self.send_data(0XA5) 222 | 223 | epdconfig.delay_ms(2000) 224 | epdconfig.module_exit() 225 | ### END OF FILE ### 226 | 227 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd7in5.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd7in5.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V4.0 8 | # * | Date : 2019-06-20 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | # Display resolution 35 | EPD_WIDTH = 640 36 | EPD_HEIGHT = 384 37 | 38 | logger = logging.getLogger(__name__) 39 | 40 | class EPD: 41 | def __init__(self): 42 | self.reset_pin = epdconfig.RST_PIN 43 | self.dc_pin = epdconfig.DC_PIN 44 | self.busy_pin = epdconfig.BUSY_PIN 45 | self.cs_pin = epdconfig.CS_PIN 46 | self.width = EPD_WIDTH 47 | self.height = EPD_HEIGHT 48 | 49 | # Hardware reset 50 | def reset(self): 51 | epdconfig.digital_write(self.reset_pin, 1) 52 | epdconfig.delay_ms(200) 53 | epdconfig.digital_write(self.reset_pin, 0) 54 | epdconfig.delay_ms(5) 55 | epdconfig.digital_write(self.reset_pin, 1) 56 | epdconfig.delay_ms(200) 57 | 58 | def send_command(self, command): 59 | epdconfig.digital_write(self.dc_pin, 0) 60 | epdconfig.digital_write(self.cs_pin, 0) 61 | epdconfig.spi_writebyte([command]) 62 | epdconfig.digital_write(self.cs_pin, 1) 63 | 64 | def send_data(self, data): 65 | epdconfig.digital_write(self.dc_pin, 1) 66 | epdconfig.digital_write(self.cs_pin, 0) 67 | epdconfig.spi_writebyte([data]) 68 | epdconfig.digital_write(self.cs_pin, 1) 69 | 70 | def send_data2(self, data): 71 | epdconfig.digital_write(self.dc_pin, 1) 72 | epdconfig.digital_write(self.cs_pin, 0) 73 | epdconfig.spi_writebyte2(data) 74 | epdconfig.digital_write(self.cs_pin, 1) 75 | 76 | def ReadBusy(self): 77 | logger.debug("e-Paper busy") 78 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 79 | epdconfig.delay_ms(100) 80 | logger.debug("e-Paper busy release") 81 | 82 | def init(self): 83 | if (epdconfig.module_init() != 0): 84 | return -1 85 | # EPD hardware init start 86 | self.reset() 87 | 88 | self.send_command(0x01) # POWER_SETTING 89 | self.send_data2([0x37, 0x00]) 90 | 91 | self.send_command(0x00) # PANEL_SETTING 92 | self.send_data2([0xCF, 0x08]) 93 | 94 | self.send_command(0x06) # BOOSTER_SOFT_START 95 | self.send_data2([0xc7, 0xcc, 0x28]) 96 | 97 | self.send_command(0x04) # POWER_ON 98 | self.ReadBusy() 99 | 100 | self.send_command(0x30) # PLL_CONTROL 101 | self.send_data(0x3c) 102 | 103 | self.send_command(0x41) # TEMPERATURE_CALIBRATION 104 | self.send_data(0x00) 105 | 106 | self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING 107 | self.send_data(0x77) 108 | 109 | self.send_command(0x60) # TCON_SETTING 110 | self.send_data(0x22) 111 | 112 | self.send_command(0x61) # TCON_RESOLUTION 113 | self.send_data(EPD_WIDTH >> 8) #source 640 114 | self.send_data(EPD_WIDTH & 0xff) 115 | self.send_data(EPD_HEIGHT >> 8) #gate 384 116 | self.send_data(EPD_HEIGHT & 0xff) 117 | 118 | self.send_command(0x82) # VCM_DC_SETTING 119 | self.send_data(0x1E) # decide by LUT file 120 | 121 | self.send_command(0xe5) # FLASH MODE 122 | self.send_data(0x03) 123 | 124 | # EPD hardware init end 125 | return 0 126 | 127 | def getbuffer(self, image): 128 | img = image 129 | imwidth, imheight = img.size 130 | halfwidth = int(self.width / 2) 131 | buf = [0x33] * halfwidth * self.height 132 | 133 | if(imwidth == self.width and imheight == self.height): 134 | img = img.convert('1') 135 | elif(imwidth == self.height and imheight == self.width): 136 | img = img.rotate(90, expand=True).convert('1') 137 | imwidth, imheight = img.size 138 | else: 139 | logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) 140 | # return a blank buffer 141 | return buf 142 | 143 | pixels = img.load() 144 | 145 | for y in range(imheight): 146 | offset = y * halfwidth 147 | for x in range(1, imwidth, 2): 148 | i = offset + x // 2 149 | if(pixels[x-1, y] > 191): 150 | if(pixels[x, y] > 191): 151 | buf[i] = 0x33 152 | else: 153 | buf[i] = 0x30 154 | else: 155 | if(pixels[x, y] > 191): 156 | buf[i] = 0x03 157 | else: 158 | buf[i] = 0x00 159 | return buf 160 | 161 | def display(self, image): 162 | self.send_command(0x10) 163 | self.send_data2(image) 164 | self.send_command(0x12) 165 | epdconfig.delay_ms(100) 166 | self.ReadBusy() 167 | 168 | def Clear(self): 169 | buf = [0x33] * int(self.width * self.height / 2) 170 | self.send_command(0x10) 171 | self.send_data2(buf) 172 | self.send_command(0x12) 173 | self.ReadBusy() 174 | 175 | def sleep(self): 176 | self.send_command(0x02) # POWER_OFF 177 | self.ReadBusy() 178 | 179 | self.send_command(0x07) # DEEP_SLEEP 180 | self.send_data(0XA5) 181 | 182 | epdconfig.delay_ms(2000) 183 | epdconfig.module_exit() 184 | ### END OF FILE ### 185 | 186 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd7in5_HD.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd7in5.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V4.0 8 | # * | Date : 2019-06-20 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | # Display resolution 35 | EPD_WIDTH = 880 36 | EPD_HEIGHT = 528 37 | 38 | logger = logging.getLogger(__name__) 39 | 40 | class EPD: 41 | def __init__(self): 42 | self.reset_pin = epdconfig.RST_PIN 43 | self.dc_pin = epdconfig.DC_PIN 44 | self.busy_pin = epdconfig.BUSY_PIN 45 | self.cs_pin = epdconfig.CS_PIN 46 | self.width = EPD_WIDTH 47 | self.height = EPD_HEIGHT 48 | 49 | # Hardware reset 50 | def reset(self): 51 | epdconfig.digital_write(self.reset_pin, 1) 52 | epdconfig.delay_ms(200) 53 | epdconfig.digital_write(self.reset_pin, 0) 54 | epdconfig.delay_ms(2) 55 | epdconfig.digital_write(self.reset_pin, 1) 56 | epdconfig.delay_ms(200) 57 | 58 | def send_command(self, command): 59 | epdconfig.digital_write(self.dc_pin, 0) 60 | epdconfig.digital_write(self.cs_pin, 0) 61 | epdconfig.spi_writebyte([command]) 62 | epdconfig.digital_write(self.cs_pin, 1) 63 | 64 | def send_data(self, data): 65 | epdconfig.digital_write(self.dc_pin, 1) 66 | epdconfig.digital_write(self.cs_pin, 0) 67 | epdconfig.spi_writebyte([data]) 68 | epdconfig.digital_write(self.cs_pin, 1) 69 | 70 | def send_data2(self, data): 71 | epdconfig.digital_write(self.dc_pin, 1) 72 | epdconfig.digital_write(self.cs_pin, 0) 73 | epdconfig.spi_writebyte2(data) 74 | epdconfig.digital_write(self.cs_pin, 1) 75 | 76 | def ReadBusy(self): 77 | logger.debug("e-Paper busy") 78 | busy = epdconfig.digital_read(self.busy_pin) 79 | while(busy == 1): 80 | busy = epdconfig.digital_read(self.busy_pin) 81 | epdconfig.delay_ms(200) 82 | 83 | def init(self): 84 | if (epdconfig.module_init() != 0): 85 | return -1 86 | # EPD hardware init start 87 | self.reset() 88 | 89 | self.ReadBusy() 90 | self.send_command(0x12) #SWRESET 91 | self.ReadBusy() 92 | 93 | self.send_command(0x46) # Auto Write Red RAM 94 | self.send_data(0xf7) 95 | self.ReadBusy() 96 | self.send_command(0x47) # Auto Write B/W RAM 97 | self.send_data(0xf7) 98 | self.ReadBusy() 99 | 100 | self.send_command(0x0C) # Soft start setting 101 | self.send_data2([0xAE, 0xC7, 0xC3, 0xC0, 0x40]) 102 | 103 | self.send_command(0x01) # Set MUX as 527 104 | self.send_data2([0xAF, 0x02, 0x01]) 105 | 106 | self.send_command(0x11) # Data entry mode 107 | self.send_data(0x01) 108 | 109 | self.send_command(0x44) 110 | self.send_data2([0x00, 0x00, 0x6F, 0x03]) # RAM x address start at 0 111 | self.send_command(0x45) 112 | self.send_data2([0xAF, 0x02, 0x00, 0x00]) 113 | 114 | self.send_command(0x3C) # VBD 115 | self.send_data(0x05) # LUT1, for white 116 | 117 | self.send_command(0x18) 118 | self.send_data(0X80) 119 | 120 | 121 | self.send_command(0x22) 122 | self.send_data(0XB1) #Load Temperature and waveform setting. 123 | self.send_command(0x20) 124 | self.ReadBusy() 125 | 126 | self.send_command(0x4E) # set RAM x address count to 0 127 | self.send_data2([0x00, 0x00]) 128 | self.send_command(0x4F) 129 | self.send_data2([0x00, 0x00]) 130 | # EPD hardware init end 131 | return 0 132 | 133 | def getbuffer(self, image): 134 | img = image 135 | imwidth, imheight = img.size 136 | if(imwidth == self.width and imheight == self.height): 137 | img = img.convert('1') 138 | elif(imwidth == self.height and imheight == self.width): 139 | img = img.rotate(90, expand=True).convert('1') 140 | else: 141 | logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) 142 | # return a blank buffer 143 | return [0xff] * int(self.width * self.height / 8) 144 | 145 | buf = bytearray(img.tobytes('raw')) 146 | return buf 147 | 148 | def display(self, image): 149 | self.send_command(0x4F) 150 | self.send_data2([0x00, 0x00]) 151 | self.send_command(0x24) 152 | self.send_data2(image) 153 | self.send_command(0x22) 154 | self.send_data(0xF7)#Load LUT from MCU(0x32) 155 | self.send_command(0x20) 156 | epdconfig.delay_ms(10) 157 | self.ReadBusy() 158 | 159 | def Clear(self): 160 | buf = [0xff] * int(self.width * self.height / 8) 161 | self.send_command(0x4F) 162 | self.send_data2([0x00, 0x00]) 163 | self.send_command(0x24) 164 | self.send_data2(buf) 165 | 166 | self.send_command(0x26) 167 | self.send_data2(buf) 168 | 169 | self.send_command(0x22) 170 | self.send_data(0xF7)#Load LUT from MCU(0x32) 171 | self.send_command(0x20) 172 | epdconfig.delay_ms(10) 173 | self.ReadBusy() 174 | 175 | def sleep(self): 176 | self.send_command(0x10) 177 | self.send_data(0x01) 178 | 179 | epdconfig.delay_ms(2000) 180 | epdconfig.module_exit() 181 | ### END OF FILE ### 182 | 183 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd7in5b_HD.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd7in5bc_HD.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V1.0 8 | # * | Date : 2019-06-20 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | # Display resolution 35 | EPD_WIDTH = 880 36 | EPD_HEIGHT = 528 37 | 38 | logger = logging.getLogger(__name__) 39 | 40 | class EPD: 41 | def __init__(self): 42 | self.reset_pin = epdconfig.RST_PIN 43 | self.dc_pin = epdconfig.DC_PIN 44 | self.busy_pin = epdconfig.BUSY_PIN 45 | self.cs_pin = epdconfig.CS_PIN 46 | self.width = EPD_WIDTH 47 | self.height = EPD_HEIGHT 48 | 49 | # Hardware reset 50 | def reset(self): 51 | epdconfig.digital_write(self.reset_pin, 1) 52 | epdconfig.delay_ms(200) 53 | epdconfig.digital_write(self.reset_pin, 0) 54 | epdconfig.delay_ms(4) 55 | epdconfig.digital_write(self.reset_pin, 1) 56 | epdconfig.delay_ms(200) 57 | 58 | def send_command(self, command): 59 | epdconfig.digital_write(self.dc_pin, 0) 60 | epdconfig.digital_write(self.cs_pin, 0) 61 | epdconfig.spi_writebyte([command]) 62 | epdconfig.digital_write(self.cs_pin, 1) 63 | 64 | def send_data(self, data): 65 | epdconfig.digital_write(self.dc_pin, 1) 66 | epdconfig.digital_write(self.cs_pin, 0) 67 | epdconfig.spi_writebyte([data]) 68 | epdconfig.digital_write(self.cs_pin, 1) 69 | 70 | def ReadBusy(self): 71 | logger.debug("e-Paper busy") 72 | busy = epdconfig.digital_read(self.busy_pin) 73 | while(busy == 1): 74 | busy = epdconfig.digital_read(self.busy_pin) 75 | epdconfig.delay_ms(200) 76 | 77 | def init(self): 78 | if (epdconfig.module_init() != 0): 79 | return -1 80 | 81 | self.reset() 82 | 83 | self.send_command(0x12) #SWRESET 84 | self.ReadBusy() #waiting for the electronic paper IC to release the idle signal 85 | 86 | self.send_command(0x46) # Auto Write RAM 87 | self.send_data(0xF7) 88 | self.ReadBusy() #waiting for the electronic paper IC to release the idle signal 89 | 90 | self.send_command(0x47) # Auto Write RAM 91 | self.send_data(0xF7) 92 | self.ReadBusy() #waiting for the electronic paper IC to release the idle signal 93 | 94 | self.send_command(0x0C) # Soft start setting 95 | self.send_data(0xAE) 96 | self.send_data(0xC7) 97 | self.send_data(0xC3) 98 | self.send_data(0xC0) 99 | self.send_data(0x40) 100 | 101 | self.send_command(0x01) # Set MUX as 527 102 | self.send_data(0xAF) 103 | self.send_data(0x02) 104 | self.send_data(0x01) 105 | 106 | self.send_command(0x11) # Data entry mode 107 | self.send_data(0x01) 108 | 109 | self.send_command(0x44) 110 | self.send_data(0x00) # RAM x address start at 0 111 | self.send_data(0x00) 112 | self.send_data(0x6F) # RAM x address end at 36Fh -> 879 113 | self.send_data(0x03) 114 | self.send_command(0x45) 115 | self.send_data(0xAF) # RAM y address start at 20Fh 116 | self.send_data(0x02) 117 | self.send_data(0x00) # RAM y address end at 00h 118 | self.send_data(0x00) 119 | 120 | self.send_command(0x3C) # VBD 121 | self.send_data(0x01) # LUT1, for white 122 | 123 | self.send_command(0x18) 124 | self.send_data(0X80) 125 | self.send_command(0x22) 126 | self.send_data(0XB1) #Load Temperature and waveform setting. 127 | self.send_command(0x20) 128 | self.ReadBusy() #waiting for the electronic paper IC to release the idle signal 129 | 130 | self.send_command(0x4E) 131 | self.send_data(0x00) 132 | self.send_data(0x00) 133 | self.send_command(0x4F) 134 | self.send_data(0xAF) 135 | self.send_data(0x02) 136 | 137 | return 0 138 | 139 | def getbuffer(self, image): 140 | # logger.debug("bufsiz = ",int(self.width/8) * self.height) 141 | buf = [0xFF] * (int(self.width/8) * self.height) 142 | image_monocolor = image.convert('1') 143 | imwidth, imheight = image_monocolor.size 144 | pixels = image_monocolor.load() 145 | logger.debug('imwidth = %d imheight = %d ',imwidth, imheight) 146 | if(imwidth == self.width and imheight == self.height): 147 | logger.debug("Horizontal") 148 | for y in range(imheight): 149 | for x in range(imwidth): 150 | # Set the bits for the column of pixels at the current position. 151 | if pixels[x, y] == 0: 152 | buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) 153 | elif(imwidth == self.height and imheight == self.width): 154 | logger.debug("Vertical") 155 | for y in range(imheight): 156 | for x in range(imwidth): 157 | newx = y 158 | newy = self.height - x - 1 159 | if pixels[x, y] == 0: 160 | buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) 161 | return buf 162 | 163 | def display(self, imageblack, imagered): 164 | self.send_command(0x4F) 165 | self.send_data(0xAf) 166 | 167 | self.send_command(0x24) 168 | for i in range(0, int(self.width * self.height / 8)): 169 | self.send_data(imageblack[i]) 170 | 171 | 172 | self.send_command(0x26) 173 | for i in range(0, int(self.width * self.height / 8)): 174 | self.send_data(~imagered[i]) 175 | 176 | self.send_command(0x22) 177 | self.send_data(0xC7) #Load LUT from MCU(0x32) 178 | self.send_command(0x20) 179 | epdconfig.delay_ms(200) #!!!The delay here is necessary, 200uS at least!!! 180 | self.ReadBusy() 181 | 182 | def Clear(self): 183 | self.send_command(0x4F) 184 | self.send_data(0xAf) 185 | 186 | self.send_command(0x24) 187 | for i in range(0, int(self.width * self.height / 8)): 188 | self.send_data(0xff) 189 | 190 | 191 | self.send_command(0x26) 192 | for i in range(0, int(self.width * self.height / 8)): 193 | self.send_data(0x00) 194 | 195 | self.send_command(0x22) 196 | self.send_data(0xC7) #Load LUT from MCU(0x32) 197 | self.send_command(0x20) 198 | epdconfig.delay_ms(200) #!!!The delay here is necessary, 200uS at least!!! 199 | self.ReadBusy() 200 | 201 | def sleep(self): 202 | self.send_command(0x10) #deep sleep 203 | self.send_data(0x01) 204 | 205 | epdconfig.delay_ms(2000) 206 | epdconfig.module_exit() 207 | ### END OF FILE ### 208 | 209 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd7in5b_V2_old.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd7in5b_V2.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V4.2 8 | # * | Date : 2022-01-08 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | # Display resolution 35 | EPD_WIDTH = 800 36 | EPD_HEIGHT = 480 37 | 38 | logger = logging.getLogger(__name__) 39 | 40 | class EPD: 41 | def __init__(self): 42 | self.reset_pin = epdconfig.RST_PIN 43 | self.dc_pin = epdconfig.DC_PIN 44 | self.busy_pin = epdconfig.BUSY_PIN 45 | self.cs_pin = epdconfig.CS_PIN 46 | self.width = EPD_WIDTH 47 | self.height = EPD_HEIGHT 48 | 49 | # Hardware reset 50 | def reset(self): 51 | epdconfig.digital_write(self.reset_pin, 1) 52 | epdconfig.delay_ms(200) 53 | epdconfig.digital_write(self.reset_pin, 0) 54 | epdconfig.delay_ms(4) 55 | epdconfig.digital_write(self.reset_pin, 1) 56 | epdconfig.delay_ms(200) 57 | 58 | def send_command(self, command): 59 | epdconfig.digital_write(self.dc_pin, 0) 60 | epdconfig.digital_write(self.cs_pin, 0) 61 | epdconfig.spi_writebyte([command]) 62 | epdconfig.digital_write(self.cs_pin, 1) 63 | 64 | def send_data(self, data): 65 | epdconfig.digital_write(self.dc_pin, 1) 66 | epdconfig.digital_write(self.cs_pin, 0) 67 | epdconfig.spi_writebyte([data]) 68 | epdconfig.digital_write(self.cs_pin, 1) 69 | 70 | def send_data2(self, data): #faster 71 | epdconfig.digital_write(self.dc_pin, 1) 72 | epdconfig.digital_write(self.cs_pin, 0) 73 | epdconfig.spi_writebyte2(data) 74 | epdconfig.digital_write(self.cs_pin, 1) 75 | 76 | def ReadBusy(self): 77 | logger.debug("e-Paper busy") 78 | self.send_command(0x71) 79 | busy = epdconfig.digital_read(self.busy_pin) 80 | while(busy == 0): 81 | self.send_command(0x71) 82 | busy = epdconfig.digital_read(self.busy_pin) 83 | epdconfig.delay_ms(200) 84 | logger.debug("e-Paper busy release") 85 | 86 | def init(self): 87 | if (epdconfig.module_init() != 0): 88 | return -1 89 | 90 | self.reset() 91 | 92 | # self.send_command(0x06) # btst 93 | # self.send_data(0x17) 94 | # self.send_data(0x17) 95 | # self.send_data(0x38) # If an exception is displayed, try using 0x38 96 | # self.send_data(0x17) 97 | 98 | self.send_command(0x01) # POWER SETTING 99 | self.send_data(0x07) 100 | self.send_data(0x07) # VGH=20V,VGL=-20V 101 | self.send_data(0x3f) # VDH=15V 102 | self.send_data(0x3f) # VDL=-15V 103 | 104 | self.send_command(0x04) # POWER ON 105 | epdconfig.delay_ms(100) 106 | self.ReadBusy() 107 | 108 | self.send_command(0X00) # PANNEL SETTING 109 | self.send_data(0x0F) # KW-3f KWR-2F BWROTP-0f BWOTP-1f 110 | 111 | self.send_command(0x61) # tres 112 | self.send_data(0x03) # source 800 113 | self.send_data(0x20) 114 | self.send_data(0x01) # gate 480 115 | self.send_data(0xE0) 116 | 117 | self.send_command(0X15) 118 | self.send_data(0x00) 119 | 120 | self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING 121 | self.send_data(0x11) 122 | self.send_data(0x07) 123 | 124 | self.send_command(0X60) # TCON SETTING 125 | self.send_data(0x22) 126 | 127 | self.send_command(0x65) 128 | self.send_data(0x00) 129 | self.send_data(0x00) 130 | self.send_data(0x00) 131 | self.send_data(0x00) 132 | 133 | return 0 134 | 135 | def getbuffer(self, image): 136 | img = image 137 | imwidth, imheight = img.size 138 | if(imwidth == self.width and imheight == self.height): 139 | img = img.convert('1') 140 | elif(imwidth == self.height and imheight == self.width): 141 | # image has correct dimensions, but needs to be rotated 142 | img = img.rotate(90, expand=True).convert('1') 143 | else: 144 | logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) 145 | # return a blank buffer 146 | return [0x00] * (int(self.width/8) * self.height) 147 | 148 | buf = bytearray(img.tobytes('raw')) 149 | # The bytes need to be inverted, because in the PIL world 0=black and 1=white, but 150 | # in the e-paper world 0=white and 1=black. 151 | for i in range(len(buf)): 152 | buf[i] ^= 0xFF 153 | return buf 154 | 155 | def display(self, imageblack, imagered): 156 | self.send_command(0x10) 157 | # The black bytes need to be inverted back from what getbuffer did 158 | for i in range(len(imageblack)): 159 | imageblack[i] ^= 0xFF 160 | self.send_data2(imageblack) 161 | 162 | self.send_command(0x13) 163 | self.send_data2(imagered) 164 | 165 | self.send_command(0x12) 166 | epdconfig.delay_ms(100) 167 | self.ReadBusy() 168 | 169 | def Clear(self): 170 | buf = [0x00] * (int(self.width/8) * self.height) 171 | buf2 = [0xff] * (int(self.width/8) * self.height) 172 | self.send_command(0x10) 173 | self.send_data2(buf2) 174 | 175 | self.send_command(0x13) 176 | self.send_data2(buf) 177 | 178 | self.send_command(0x12) 179 | epdconfig.delay_ms(100) 180 | self.ReadBusy() 181 | 182 | def sleep(self): 183 | self.send_command(0x02) # POWER_OFF 184 | self.ReadBusy() 185 | 186 | self.send_command(0x07) # DEEP_SLEEP 187 | self.send_data(0XA5) 188 | 189 | epdconfig.delay_ms(2000) 190 | epdconfig.module_exit() 191 | ### END OF FILE ### 192 | 193 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epd7in5bc.py: -------------------------------------------------------------------------------- 1 | # ***************************************************************************** 2 | # * | File : epd7in5bc.py 3 | # * | Author : Waveshare team 4 | # * | Function : Electronic paper driver 5 | # * | Info : 6 | # *---------------- 7 | # * | This version: V4.0 8 | # * | Date : 2019-06-20 9 | # # | Info : python demo 10 | # ----------------------------------------------------------------------------- 11 | # Permission is hereby granted, free of charge, to any person obtaining a copy 12 | # of this software and associated documnetation files (the "Software"), to deal 13 | # in the Software without restriction, including without limitation the rights 14 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | # copies of the Software, and to permit persons to whom the Software is 16 | # furished to do so, subject to the following conditions: 17 | # 18 | # The above copyright notice and this permission notice shall be included in 19 | # all copies or substantial portions of the Software. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | # FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | # LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | # THE SOFTWARE. 28 | # 29 | 30 | 31 | import logging 32 | from . import epdconfig 33 | 34 | # Display resolution 35 | EPD_WIDTH = 640 36 | EPD_HEIGHT = 384 37 | 38 | logger = logging.getLogger(__name__) 39 | 40 | class EPD: 41 | def __init__(self): 42 | self.reset_pin = epdconfig.RST_PIN 43 | self.dc_pin = epdconfig.DC_PIN 44 | self.busy_pin = epdconfig.BUSY_PIN 45 | self.cs_pin = epdconfig.CS_PIN 46 | self.width = EPD_WIDTH 47 | self.height = EPD_HEIGHT 48 | 49 | # Hardware reset 50 | def reset(self): 51 | epdconfig.digital_write(self.reset_pin, 1) 52 | epdconfig.delay_ms(200) 53 | epdconfig.digital_write(self.reset_pin, 0) 54 | epdconfig.delay_ms(5) 55 | epdconfig.digital_write(self.reset_pin, 1) 56 | epdconfig.delay_ms(200) 57 | 58 | def send_command(self, command): 59 | epdconfig.digital_write(self.dc_pin, 0) 60 | epdconfig.digital_write(self.cs_pin, 0) 61 | epdconfig.spi_writebyte([command]) 62 | epdconfig.digital_write(self.cs_pin, 1) 63 | 64 | def send_data(self, data): 65 | epdconfig.digital_write(self.dc_pin, 1) 66 | epdconfig.digital_write(self.cs_pin, 0) 67 | epdconfig.spi_writebyte([data]) 68 | epdconfig.digital_write(self.cs_pin, 1) 69 | 70 | def ReadBusy(self): 71 | logger.debug("e-Paper busy") 72 | while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy 73 | epdconfig.delay_ms(100) 74 | logger.debug("e-Paper busy release") 75 | 76 | def init(self): 77 | if (epdconfig.module_init() != 0): 78 | return -1 79 | 80 | self.reset() 81 | 82 | self.send_command(0x01) # POWER_SETTING 83 | self.send_data(0x37) 84 | self.send_data(0x00) 85 | 86 | self.send_command(0x00) # PANEL_SETTING 87 | self.send_data(0xCF) 88 | self.send_data(0x08) 89 | 90 | self.send_command(0x30) # PLL_CONTROL 91 | self.send_data(0x3A) # PLL: 0-15:0x3C, 15+:0x3A 92 | 93 | self.send_command(0x82) # VCM_DC_SETTING 94 | self.send_data(0x28) #all temperature range 95 | 96 | self.send_command(0x06) # BOOSTER_SOFT_START 97 | self.send_data(0xc7) 98 | self.send_data(0xcc) 99 | self.send_data(0x15) 100 | 101 | self.send_command(0x50) # VCOM AND DATA INTERVAL SETTING 102 | self.send_data(0x77) 103 | 104 | self.send_command(0x60) # TCON_SETTING 105 | self.send_data(0x22) 106 | 107 | self.send_command(0x65) # FLASH CONTROL 108 | self.send_data(0x00) 109 | 110 | self.send_command(0x61) # TCON_RESOLUTION 111 | self.send_data(self.width >> 8) # source 640 112 | self.send_data(self.width & 0xff) 113 | self.send_data(self.height >> 8) # gate 384 114 | self.send_data(self.height & 0xff) 115 | 116 | self.send_command(0xe5) # FLASH MODE 117 | self.send_data(0x03) 118 | 119 | return 0 120 | 121 | def getbuffer(self, image): 122 | # logger.debug("bufsiz = ",int(self.width/8) * self.height) 123 | buf = [0xFF] * (int(self.width/8) * self.height) 124 | image_monocolor = image.convert('1') 125 | imwidth, imheight = image_monocolor.size 126 | pixels = image_monocolor.load() 127 | logger.debug('imwidth = %d imheight = %d ',imwidth, imheight) 128 | if(imwidth == self.width and imheight == self.height): 129 | logger.debug("Horizontal") 130 | for y in range(imheight): 131 | for x in range(imwidth): 132 | # Set the bits for the column of pixels at the current position. 133 | if pixels[x, y] == 0: 134 | buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) 135 | elif(imwidth == self.height and imheight == self.width): 136 | logger.debug("Vertical") 137 | for y in range(imheight): 138 | for x in range(imwidth): 139 | newx = y 140 | newy = self.height - x - 1 141 | if pixels[x, y] == 0: 142 | buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) 143 | return buf 144 | 145 | def display(self, imageblack, imagered): 146 | self.send_command(0x10) 147 | for i in range(0, int(self.width / 8 * self.height)): 148 | temp1 = imageblack[i] 149 | temp2 = imagered[i] 150 | j = 0 151 | while (j < 8): 152 | if ((temp2 & 0x80) == 0x00): 153 | temp3 = 0x04 #red 154 | elif ((temp1 & 0x80) == 0x00): 155 | temp3 = 0x00 #black 156 | else: 157 | temp3 = 0x03 #white 158 | 159 | temp3 = (temp3 << 4) & 0xFF 160 | temp1 = (temp1 << 1) & 0xFF 161 | temp2 = (temp2 << 1) & 0xFF 162 | j += 1 163 | if((temp2 & 0x80) == 0x00): 164 | temp3 |= 0x04 #red 165 | elif ((temp1 & 0x80) == 0x00): 166 | temp3 |= 0x00 #black 167 | else: 168 | temp3 |= 0x03 #white 169 | temp1 = (temp1 << 1) & 0xFF 170 | temp2 = (temp2 << 1) & 0xFF 171 | self.send_data(temp3) 172 | j += 1 173 | 174 | self.send_command(0x04) # POWER ON 175 | self.ReadBusy() 176 | self.send_command(0x12) # display refresh 177 | epdconfig.delay_ms(100) 178 | self.ReadBusy() 179 | 180 | def Clear(self): 181 | self.send_command(0x10) 182 | for i in range(0, int(self.width / 8 * self.height)): 183 | self.send_data(0x33) 184 | self.send_data(0x33) 185 | self.send_data(0x33) 186 | self.send_data(0x33) 187 | 188 | self.send_command(0x04) # POWER ON 189 | self.ReadBusy() 190 | self.send_command(0x12) # display refresh 191 | epdconfig.delay_ms(100) 192 | self.ReadBusy() 193 | 194 | def sleep(self): 195 | self.send_command(0x02) # POWER_OFF 196 | self.ReadBusy() 197 | 198 | self.send_command(0x07) # DEEP_SLEEP 199 | self.send_data(0XA5) 200 | 201 | epdconfig.delay_ms(2000) 202 | epdconfig.module_exit() 203 | ### END OF FILE ### 204 | 205 | -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epdbase.py: -------------------------------------------------------------------------------- 1 | from abc import abstractmethod 2 | 3 | 4 | class EPDBase: 5 | def __init__(self, up_cb, down_cb, select_cb, width, height, lines): 6 | self.up_cb = up_cb 7 | self.down_cb = down_cb 8 | self.select_cb = select_cb 9 | self.width = width 10 | self.height = height 11 | self.lines = lines 12 | 13 | @abstractmethod 14 | def init(self, after): 15 | pass 16 | 17 | def getbuffer(self, image): 18 | img = image 19 | imwidth, imheight = img.size 20 | if imwidth == self.width and imheight == self.height: 21 | img = img.convert('1') 22 | elif imwidth == self.height and imheight == self.width: 23 | # image has correct dimensions, but needs to be rotated 24 | img = img.rotate(90, expand=True).convert('1') 25 | else: 26 | # return a blank buffer 27 | return [0x00] * (int(self.width / 8) * self.height) 28 | 29 | buf = bytearray(img.tobytes('raw')) 30 | return buf 31 | 32 | @abstractmethod 33 | def display(self, image): 34 | pass 35 | 36 | @abstractmethod 37 | def displayPartial(self, image): 38 | pass 39 | 40 | @abstractmethod 41 | def displayPartBaseImage(self, image): 42 | pass 43 | 44 | @abstractmethod 45 | def Clear(self, color): 46 | pass -------------------------------------------------------------------------------- /drivers/waveshare/models/waveshare_epd/epdsoftware.py: -------------------------------------------------------------------------------- 1 | from drivers.waveshare.models.waveshare_epd.epdbase import EPDBase 2 | from tkinter import * 3 | from PIL import ImageTk 4 | 5 | 6 | class EPD(EPDBase): 7 | def __init__(self, up_cb, down_cb, select_cb): 8 | super(EPD, self).__init__(up_cb, down_cb, select_cb, 122, 250, 3) 9 | self.window = Tk() 10 | self.wait_state = False 11 | self.init_events() 12 | self.window.geometry(f'{self.height}x{self.width}') 13 | self.window.title('WaveShare Emulator') 14 | self.canvas = Canvas(self.window) 15 | # self.canvas.create_rectangle(0, 0, self.height, self.width, fill='white') 16 | self.canvas.pack(fill=BOTH, expand=True) 17 | 18 | def init_events(self): 19 | self.window.bind('', self.wait) 20 | self.window.bind('', self.switch_wait_state) 21 | 22 | def init(self, after): 23 | self.window.after(1, lambda: after()) 24 | self.switch_wait_state() 25 | self.window.mainloop() 26 | 27 | def display(self, image): 28 | self.img = ImageTk.PhotoImage(image) 29 | self.canvas.create_image(0, 0, image=self.img, anchor=NW) 30 | 31 | def displayPartial(self, image): 32 | self.display(image) 33 | 34 | def displayPartBaseImage(self, image): 35 | self.display(image) 36 | 37 | def Clear(self, color): 38 | pass 39 | 40 | def switch_wait_state(self): 41 | self.wait_state = not self.wait_state 42 | 43 | def wait(self, event): 44 | if self.wait_state and any(key == event.keysym for key in ['Up']): 45 | self.up_cb() 46 | elif self.wait_state and any(key == event.keysym for key in ['Down']): 47 | self.down_cb() 48 | elif self.wait_state and any(key == event.keysym for key in ['Return']): 49 | self.select_cb() 50 | else: 51 | self.do_nhth() 52 | 53 | @staticmethod 54 | def do_nhth(): 55 | pass -------------------------------------------------------------------------------- /images/Font.ttc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephdc96/ProxTag/5982a3f0049ad79c1a81feb5598046a4ab1d0811/images/Font.ttc -------------------------------------------------------------------------------- /images/ProxTag.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephdc96/ProxTag/5982a3f0049ad79c1a81feb5598046a4ab1d0811/images/ProxTag.bmp -------------------------------------------------------------------------------- /images/ProxTag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephdc96/ProxTag/5982a3f0049ad79c1a81feb5598046a4ab1d0811/images/ProxTag.png -------------------------------------------------------------------------------- /images/ProxTag.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephdc96/ProxTag/5982a3f0049ad79c1a81feb5598046a4ab1d0811/images/ProxTag.xcf -------------------------------------------------------------------------------- /images/ProxTag_Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/josephdc96/ProxTag/5982a3f0049ad79c1a81feb5598046a4ab1d0811/images/ProxTag_Screenshot.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from dotenv import load_dotenv 4 | 5 | from config import Config 6 | from controller import Controller 7 | from drivers.console.console import Console 8 | from drivers.waveshare.waveshare import Waveshare 9 | 10 | load_dotenv() 11 | 12 | host = os.getenv('HOST') 13 | if host is None: 14 | raise ValueError('HOST environment variable not set') 15 | try: 16 | port = int(os.getenv('PORT')) 17 | except ValueError: 18 | port = 8006 19 | ssl = os.getenv('SSL') == 'true' 20 | token = os.getenv('TOKEN') 21 | if token is None: 22 | raise ValueError('TOKEN environment variable not set') 23 | driver = os.getenv('DRIVER') 24 | if driver is None: 25 | raise ValueError('DRIVER environment variable not set') 26 | model = os.getenv('MODEL') 27 | if driver == 'waveshare' and model is None: 28 | raise ValueError('MODEL is not set') 29 | 30 | conf = Config(host, token, port, ssl, driver, model) 31 | 32 | controller = Controller(conf) 33 | match driver: 34 | case "waveshare": 35 | display = Waveshare(controller, model) 36 | case _: 37 | display = Console(controller) 38 | 39 | controller.set_display(display=display) 40 | display.initialize() -------------------------------------------------------------------------------- /pve.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from config import Config 4 | 5 | 6 | class PVE: 7 | def __init__(self, config: Config): 8 | self.config = config 9 | self.client = requests.Session() 10 | self.client.headers = { 11 | 'Authorization': f'PVEAPIToken={self.config.token}' 12 | } 13 | self.client.verify = self.config.ssl 14 | self.api_url = f'{self.config.host}:{self.config.port}/api2/json' 15 | 16 | def get_vms(self): 17 | url = f'{self.api_url}/cluster/resources?type=vm' 18 | request = requests.get(url, headers=self.client.headers) 19 | json = request.json() 20 | return json['data'] 21 | 22 | def get_vm_config(self, node, id): 23 | url = f'{self.api_url}/nodes/{node}/qemu/{id}/config' 24 | request = requests.get(url) 25 | json = request.json() 26 | return json['data'] 27 | 28 | def get_vm_status(self, node, id): 29 | url = f'{self.api_url}/nodes/{node}/qemu/{id}/status/current' 30 | request = requests.get(url) 31 | json = request.json() 32 | return json['data'] 33 | 34 | def start_vm(self, node, id): 35 | url = f'{self.api_url}/nodes/{node}/qemu/{id}/status/start' 36 | request = requests.post(url) 37 | return request.status_code 38 | 39 | def shutdown_vm(self, node, id): 40 | url = f'{self.api_url}/nodes/{node}/qemu/{id}/status/shutdown' 41 | request = requests.post(url) 42 | return request.status_code 43 | 44 | def restart_vm(self, node, id): 45 | url = f'{self.api_url}/nodes/{node}/qemu/{id}/status/restart' 46 | request = requests.post(url) 47 | return request.status_code 48 | 49 | def pause_vm(self, node, id): 50 | url = f'{self.api_url}/nodes/{node}/qemu/{id}/status/pause' 51 | request = requests.post(url) 52 | return request.status_code 53 | 54 | def suspend_vm(self, node, id): 55 | url = f'{self.api_url}/nodes/{node}/qemu/{id}/status/suspend' 56 | request = requests.post(url) 57 | return request.status_code 58 | 59 | def stop_vm(self, node, id): 60 | url = f'{self.api_url}/nodes/{node}/qemu/{id}/status/stop' 61 | request = requests.post(url) 62 | return request.status_code 63 | 64 | def reset_vm(self, node, id): 65 | url = f'{self.api_url}/nodes/{node}/qemu/{id}/status/reset' 66 | request = requests.post(url) 67 | return request.status_code 68 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 21 | [![Contributors][contributors-shield]][contributors-url] 22 | [![Forks][forks-shield]][forks-url] 23 | [![Stargazers][stars-shield]][stars-url] 24 | [![Issues][issues-shield]][issues-url] 25 | [![GPL License][license-shield]][license-url] 26 | [![Buy me a coffee][coffee-shield]][coffee-url] 27 | 28 | 29 | 30 | 31 |
32 |
33 | 34 | Logo 35 | 36 | 37 |

ProxTag

38 | 39 |

40 | A Proxmox Manager designed for price tag-sized e-Ink displays 41 |
42 | 44 | Report Bug 45 | · 46 | Request Feature 47 |

48 |
49 | 50 | 51 | 52 | 53 |
54 | Table of Contents 55 |
    56 |
  1. 57 | About The Project 58 | 61 |
  2. 62 |
  3. 63 | Getting Started 64 | 69 |
  4. 70 |
  5. Usage
  6. 71 |
  7. Contributing
  8. 72 |
  9. License
  10. 73 |
  11. Contact
  12. 74 |
75 |
76 | 77 | 78 | 79 | 80 | ## About The Project 81 | 82 | 83 | Logo 84 | 85 | 86 | ProxTag is a Python project designed to offer VM power state management on a Raspberry Pi using an e-Ink display and a rotary encoder. 87 | 88 |

(back to top)

89 | 90 | 91 | 92 | ### Built With 93 | [![Python][Python]][Python-url] 94 | 95 |

(back to top)

96 | 97 | 98 | 99 | 100 | ## Getting Started 101 | 102 | This is an example of how you may give instructions on setting up your project locally. 103 | To get a local copy up and running follow these simple example steps. 104 | 105 | ### Prerequisites 106 | To run ProxTag you need a local install of Python or Docker, as well as a local Proxmox server with a service account. 107 | 108 | ### Installation (Local) 109 | 110 | 1. Clone the repo 111 | ```sh 112 | git clone https://github.com/josephdc96/ProxTag.git 113 | ``` 114 | 2. Set up virtual environment 115 | #### Linux 116 | ```shell 117 | python -m venv venv 118 | source ./venv/bin/activate 119 | ``` 120 | #### Windows (PowerShell) 121 | ``` 122 | python -m venv venv 123 | .\venv\bin\activate.ps1 124 | ``` 125 | #### Windows (Command Prompt) 126 | ```shell 127 | python -m venv venv 128 | .\venv\bin\activate.bat 129 | ``` 130 | 3. Install Python packages 131 | ```sh 132 | pip intall -r requirements.txt 133 | ``` 134 | 4. Fill out your .env file using the .env.example file 135 | 136 |

(back to top)

137 | 138 | ### Installation (Docker) 139 | 1. Clone the repo 140 | ```sh 141 | git clone https://github.com/josephdc96/ProxTag.git 142 | ``` 143 | 2. Build the docker image 144 | ```shell 145 | docker build -t proxtag:latest . 146 | ``` 147 | 148 | 149 | ## Usage 150 | 151 | ### Running (Local) 152 | ```shell 153 | python main.py 154 | ``` 155 | 156 | ### Running (Docker) 157 | 158 | To run list your environment variables in the Docker command 159 | ```shell 160 | docker run -e HOST= -e PORT= -e SSL= -e TOKEN='' -e DRIVER= -e MODEL= proxtag 161 | ``` 162 | 163 |

(back to top)

164 | 165 | 166 | 167 | 168 | ## Contributing 169 | 170 | Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. 171 | 172 | If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". 173 | Don't forget to give the project a star! Thanks again! 174 | 175 | 1. Fork the Project 176 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 177 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 178 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 179 | 5. Open a Pull Request 180 | 181 |

(back to top)

182 | 183 | ### Top contributors: 184 | 185 | 186 | contrib.rocks image 187 | 188 | 189 | 190 | 191 | 192 | ## License 193 | 194 | Distributed under the GPL3 License. See `LICENSE` for more information. 195 | 196 |

(back to top)

197 | 198 | 199 | 200 | [contributors-shield]: https://img.shields.io/github/contributors/josephdc96/PMManager.svg?style=for-the-badge 201 | [contributors-url]: https://github.com/josephdc96/PMManager/graphs/contributors 202 | [forks-shield]: https://img.shields.io/github/forks/josephdc96/PMManager.svg?style=for-the-badge 203 | [forks-url]: https://github.com/josephdc96/PMManager/network/members 204 | [stars-shield]: https://img.shields.io/github/stars/josephdc96/PMManager.svg?style=for-the-badge 205 | [stars-url]: https://github.com/josephdc96/PMManager/stargazers 206 | [issues-shield]: https://img.shields.io/github/issues/josephdc96/PMManager.svg?style=for-the-badge 207 | [issues-url]: https://github.com/josephdc96/PMManager/issues 208 | [license-shield]: https://img.shields.io/github/license/josephdc96/PMManager.svg?style=for-the-badge 209 | [license-url]: https://github.com/josephdc96/PMManager/blob/master/LICENSE 210 | [product-screenshot]: images/screenshot.png 211 | [Python]: https://img.shields.io/badge/python-000000?style=for-the-badge&logo=python 212 | [Python-url]: https://python.org 213 | [coffee-shield]: https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black 214 | [coffee-url]: https://www.buymeacoffee.com/josephdc96 215 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2024.8.30 2 | charset-normalizer==3.4.0 3 | colorzero==2.0 4 | gpiozero==2.0.1; sys_platform=='linux' 5 | idna==3.10 6 | lgpio==0.2.2.0; sys_platform=='linux' 7 | numpy==2.1.2 8 | pillow==10.4.0 9 | requests==2.32.3 10 | rpi-lgpio==0.6; sys_platform=='linux' 11 | spidev==3.6; sys_platform=='linux' 12 | urllib3==2.2.3 13 | python-dotenv==0.21.1 14 | --------------------------------------------------------------------------------