commit 88f34588b7a477c22925f8fe253cd64fd1a8bc67 Author: wieerwill Date: Mon Oct 9 19:24:09 2023 +0200 basic vcard diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0ce7787 --- /dev/null +++ b/.gitignore @@ -0,0 +1,168 @@ +# others +qr.bmp +ara.bmp +*.pdf +.vscode +*.code-workspace + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class +*.save + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a3edad5 --- /dev/null +++ b/Makefile @@ -0,0 +1,84 @@ +DIR_Config = ./lib/Config +DIR_EPD = ./lib/e-Paper +DIR_FONTS = ./lib/Fonts +DIR_GUI = ./lib/GUI +DIR_Examples = ./examples +DIR_BIN = ./bin + +OBJ_C = $(wildcard ${DIR_EPD}/*.c ${DIR_GUI}/*.c ${DIR_Examples}/*.c ${DIR_FONTS}/*.c ) +OBJ_O = $(patsubst %.c,${DIR_BIN}/%.o,$(notdir ${OBJ_C})) +RPI_DEV_C = $(wildcard $(DIR_BIN)/dev_hardware_SPI.o $(DIR_BIN)/RPI_sysfs_gpio.o $(DIR_BIN)/DEV_Config.o ) +JETSON_DEV_C = $(wildcard $(DIR_BIN)/sysfs_software_spi.o $(DIR_BIN)/sysfs_gpio.o $(DIR_BIN)/DEV_Config.o ) + + +DEBUG = -D DEBUG + + USELIB_RPI = USE_BCM2835_LIB +#USELIB_RPI = USE_WIRINGPI_LIB +# USELIB_RPI = USE_DEV_LIB + +LIB_RPI=-Wl,--gc-sections +ifeq ($(USELIB_RPI), USE_BCM2835_LIB) + LIB_RPI += -lbcm2835 -lm +else ifeq ($(USELIB_RPI), USE_WIRINGPI_LIB) + LIB_RPI += -lwiringPi -lm +else ifeq ($(USELIB_RPI), USE_DEV_LIB) + LIB_RPI += -lm +endif +DEBUG_RPI = -D $(USELIB_RPI) -D RPI + +USELIB_JETSONI = USE_DEV_LIB +# USELIB_JETSONI = USE_HARDWARE_LIB +ifeq ($(USELIB_JETSONI), USE_DEV_LIB) + LIB_JETSONI = -lm +else ifeq ($(USELIB_JETSONI), USE_HARDWARE_LIB) + LIB_JETSONI = -lm +endif +DEBUG_JETSONI = -D $(USELIB_JETSONI) -D JETSON + +.PHONY : RPI JETSON clean + +RPI:RPI_DEV RPI_epd +JETSON: JETSON_DEV JETSON_epd + +TARGET = epd +CC = gcc +MSG = -g -O -ffunction-sections -fdata-sections -Wall +CFLAGS += $(MSG) + +RPI_epd:${OBJ_O} + echo $(@) + $(CC) $(CFLAGS) -D RPI $(OBJ_O) $(RPI_DEV_C) -o $(TARGET) $(LIB_RPI) $(DEBUG) + +JETSON_epd:${OBJ_O} + echo $(@) + $(CC) $(CFLAGS) $(OBJ_O) $(JETSON_DEV_C) -o $(TARGET) $(LIB_JETSONI) $(DEBUG) + +$(shell mkdir -p $(DIR_BIN)) + +${DIR_BIN}/%.o:$(DIR_Examples)/%.c + $(CC) $(CFLAGS) -c $< -o $@ -I $(DIR_Config) -I $(DIR_GUI) -I $(DIR_EPD) $(DEBUG) + +${DIR_BIN}/%.o:$(DIR_EPD)/%.c + $(CC) $(CFLAGS) -c $< -o $@ -I $(DIR_Config) $(DEBUG) + +${DIR_BIN}/%.o:$(DIR_FONTS)/%.c + $(CC) $(CFLAGS) -c $< -o $@ $(DEBUG) + +${DIR_BIN}/%.o:$(DIR_GUI)/%.c + $(CC) $(CFLAGS) -c $< -o $@ -I $(DIR_Config) $(DEBUG) + +RPI_DEV: + $(CC) $(CFLAGS) $(DEBUG_RPI) -c $(DIR_Config)/dev_hardware_SPI.c -o $(DIR_BIN)/dev_hardware_SPI.o $(LIB_RPI) $(DEBUG) + $(CC) $(CFLAGS) $(DEBUG_RPI) -c $(DIR_Config)/RPI_sysfs_gpio.c -o $(DIR_BIN)/RPI_sysfs_gpio.o $(LIB_RPI) $(DEBUG) + $(CC) $(CFLAGS) $(DEBUG_RPI) -c $(DIR_Config)/DEV_Config.c -o $(DIR_BIN)/DEV_Config.o $(LIB_RPI) $(DEBUG) + +JETSON_DEV: + $(CC) $(CFLAGS) $(DEBUG_JETSONI) -c $(DIR_Config)/sysfs_software_spi.c -o $(DIR_BIN)/sysfs_software_spi.o $(LIB_JETSONI) $(DEBUG) + $(CC) $(CFLAGS) $(DEBUG_JETSONI) -c $(DIR_Config)/sysfs_gpio.c -o $(DIR_BIN)/sysfs_gpio.o $(LIB_JETSONI) $(DEBUG) + $(CC) $(CFLAGS) $(DEBUG_JETSONI) -c $(DIR_Config)/DEV_Config.c -o $(DIR_BIN)/DEV_Config.o $(LIB_JETSONI) $(DEBUG) + +clean : + rm $(DIR_BIN)/*.* + rm $(TARGET) + diff --git a/pic/2in13.bmp b/pic/2in13.bmp new file mode 100644 index 0000000..a579e2a Binary files /dev/null and b/pic/2in13.bmp differ diff --git a/pic/2in13_1.bmp b/pic/2in13_1.bmp new file mode 100644 index 0000000..84e6497 Binary files /dev/null and b/pic/2in13_1.bmp differ diff --git a/pic/2in13_2.bmp b/pic/2in13_2.bmp new file mode 100644 index 0000000..dd0c20f Binary files /dev/null and b/pic/2in13_2.bmp differ diff --git a/pic/Font.ttc b/pic/Font.ttc new file mode 100644 index 0000000..4cbb7c5 Binary files /dev/null and b/pic/Font.ttc differ diff --git a/py_examples/draw-example.py b/py_examples/draw-example.py new file mode 100644 index 0000000..a8dfff8 --- /dev/null +++ b/py_examples/draw-example.py @@ -0,0 +1,58 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- +import sys +import os +libdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib') +if os.path.exists(libdir): + sys.path.append(libdir) + +import logging +from waveshare_epd import epd2in13_V2 +from PIL import Image,ImageDraw + +logging.basicConfig(level=logging.DEBUG) + +try: + epd = epd2in13_V2.EPD() + epd.init(epd.FULL_UPDATE) + epd.Clear(0xFF) + + # clear the frame + image = Image.new('1', (epd.height, epd.width), 255) + draw = ImageDraw.Draw(image) + + # add text + # x/y coordinates start at left top with 0,0 + + # empty reactangle with crossed lines inside + draw.rectangle([(5,5),(55,55)], outline = 0) + draw.line([(5,5),(55,55)], fill = 0, width = 1) + draw.line([(5,55),(55,5)], fill = 0, width = 1) + + # filled rectangle + draw.rectangle([(60,5),(110,55)],fill = 0) + + # draw a chord rotating in the middle + draw.chord((10, 80, 55, 120), 0, 360, fill = 0) + + # draw a simple circe + draw.ellipse((55, 60, 95, 100), outline = 0) + + # draw four forth parts of a circle to create a single one + draw.pieslice((55, 60, 95, 100), 90, 180, outline = 0) + draw.pieslice((55, 60, 95, 100), 270, 360, fill = 0) + draw.polygon([(110,0),(110,50),(150,25)], outline = 0) + draw.polygon([(190,0),(190,50),(150,25)], fill = 0) + + # add new image to display + epd.display(epd.getbuffer(image)) + # set display to sleep mode + epd.sleep() + +except IOError as e: + logging.info(e) + +except KeyboardInterrupt: + logging.info("ctrl + c:") + epd2in13_V2.epdconfig.module_exit() + exit() \ No newline at end of file diff --git a/py_examples/image-example.py b/py_examples/image-example.py new file mode 100644 index 0000000..5740007 --- /dev/null +++ b/py_examples/image-example.py @@ -0,0 +1,45 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- +import sys +import os +libdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib') +if os.path.exists(libdir): + sys.path.append(libdir) + +import logging +from waveshare_epd import epd2in13_V2 +import time +from PIL import Image,ImageDraw,ImageFont +import traceback + +logging.basicConfig(level=logging.DEBUG) + +try: + epd = epd2in13_V2.EPD() + epd.init(epd.FULL_UPDATE) + epd.Clear(0xFF) + + image = Image.new('1', (epd.height, epd.width), 255) # 255: clear the frame + draw = ImageDraw.Draw(image) + + draw.rectangle([(0,0),(50,50)],outline = 0) + draw.rectangle([(55,0),(100,50)],fill = 0) + draw.line([(0,0),(50,50)], fill = 0,width = 1) + draw.line([(0,50),(50,0)], fill = 0,width = 1) + draw.chord((10, 60, 50, 100), 0, 360, fill = 0) + draw.ellipse((55, 60, 95, 100), outline = 0) + draw.pieslice((55, 60, 95, 100), 90, 180, outline = 0) + draw.pieslice((55, 60, 95, 100), 270, 360, fill = 0) + draw.polygon([(110,0),(110,50),(150,25)],outline = 0) + draw.polygon([(190,0),(190,50),(150,25)],fill = 0) + + epd.display(epd.getbuffer(image)) + epd.sleep() + +except IOError as e: + logging.info(e) + +except KeyboardInterrupt: + logging.info("ctrl + c:") + epd2in13_V2.epdconfig.module_exit() + exit() diff --git a/py_examples/minimalOnePage.py b/py_examples/minimalOnePage.py new file mode 100644 index 0000000..63bc98c --- /dev/null +++ b/py_examples/minimalOnePage.py @@ -0,0 +1,46 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- +import sys +import os +picdir = os.path.join(os.path.realpath(__file__), 'pic') +libdir = os.path.join(os.path.realpath(__file__), 'lib') +if os.path.exists(libdir): + sys.path.append(libdir) + +import logging +import lib.waveshare_epd.epd2in13_V2 as epd2in13_V2 +from PIL import Image,ImageDraw,ImageFont + +logging.basicConfig(level=logging.DEBUG) + +try: + epd = epd2in13_V2.EPD() + epd.init(epd.FULL_UPDATE) + epd.Clear(0xFF) + + # import and set font + font15 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 15) + font24 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 24) + + # clear the frame + image = Image.new('1', (epd.height, epd.width), 255) + draw = ImageDraw.Draw(image) + + # add text + draw.text((40, 5), 'Robert Jeutter', font = font24, fill = 0) + draw.text((10, 35), 'FullStack Software Engineer', font = font15, fill = 0) + draw.text((10, 55), 'Working @ AraCom IT Service AG', font = font15, fill = 0) + draw.text((5, 100), 'find me on github.com/WieErWill', font = font15, fill = 0) + + # add new image to display + epd.display(epd.getbuffer(image)) + # set display to sleep mode + epd.sleep() + +except IOError as e: + logging.info(e) + +except KeyboardInterrupt: + logging.info("ctrl + c:") + epd2in13_V2.epdconfig.module_exit() + exit() diff --git a/py_examples/simpleVCard.py b/py_examples/simpleVCard.py new file mode 100644 index 0000000..ff2b2b1 --- /dev/null +++ b/py_examples/simpleVCard.py @@ -0,0 +1,47 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- +import sys +import os +picdir = os.path.join(os.path.realpath(__file__), 'pic') +libdir = os.path.join(os.path.realpath(__file__), 'lib') +if os.path.exists(libdir): + sys.path.append(libdir) + +import logging +import lib.waveshare_epd.epd2in13_V2 as epd2in13_V2 +from PIL import Image,ImageDraw,ImageFont + +logging.basicConfig(level=logging.DEBUG) + +try: + epd = epd2in13_V2.EPD() + epd.init(epd.FULL_UPDATE) + epd.Clear(0xFF) + + # import and set font + font15 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 15) + font24 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 24) + + # clear the frame + image = Image.new('1', (epd.height, epd.width), 255) + draw = ImageDraw.Draw(image) + + # add text + draw.text((40, 5), 'Johny Doey', font = font24, fill = 0) + draw.text((10, 35), 'Programmer and Coffinated', font = font15, fill = 0) + draw.text((10, 55), 'Working @ Doe Does Beer Inc.', font = font15, fill = 0) + draw.text((10, 75), 'Mail: johny@doe.com.de', font = font15, fill = 0) + draw.text((5, 100), 'find me on github.com/WieErWill', font = font15, fill = 0) + + # add new image to display + epd.display(epd.getbuffer(image)) + # set display to sleep mode + epd.sleep() + +except IOError as e: + logging.info(e) + +except KeyboardInterrupt: + logging.info("ctrl + c:") + epd2in13_V2.epdconfig.module_exit() + exit() diff --git a/py_examples/text-example.py b/py_examples/text-example.py new file mode 100644 index 0000000..a9b0cc0 --- /dev/null +++ b/py_examples/text-example.py @@ -0,0 +1,46 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- +import sys +import os +picdir = os.path.join(os.path.realpath(__file__), 'pic') +libdir = os.path.join(os.path.realpath(__file__), 'lib') +if os.path.exists(libdir): + sys.path.append(libdir) + +import logging +import lib.waveshare_epd.epd2in13_V2 as epd2in13_V2 +from PIL import Image,ImageDraw,ImageFont + +logging.basicConfig(level=logging.DEBUG) + +try: + epd = epd2in13_V2.EPD() + epd.init(epd.FULL_UPDATE) + epd.Clear(0xFF) + + # import and set font + font15 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 15) + font24 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 24) + + # clear the frame + image = Image.new('1', (epd.height, epd.width), 255) + draw = ImageDraw.Draw(image) + + # add text + # x/y coordinates start at left top with 0,0 + draw.text((5, 5), 'Text Example on Waveshare', font = font15, fill = 0) + draw.text((10, 30), 'write text on e-Paper', font = font24, fill = 0) + draw.text((15, 100), 'find more on github.com/wieerwill', font = font15, fill = 0) + + # add new image to display + epd.display(epd.getbuffer(image)) + # set display to sleep mode + epd.sleep() + +except IOError as e: + logging.info(e) + +except KeyboardInterrupt: + logging.info("ctrl + c:") + epd2in13_V2.epdconfig.module_exit() + exit() \ No newline at end of file diff --git a/py_examples/vcard.py b/py_examples/vcard.py new file mode 100644 index 0000000..c2e819c --- /dev/null +++ b/py_examples/vcard.py @@ -0,0 +1,154 @@ +#!/usr/bin/python +# -*- coding:utf-8 -*- +import sys +import os +picdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'pic') +libdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib') +if os.path.exists(libdir): + sys.path.append(libdir) + +import logging +from waveshare_epd import epd2in13_V2 +import time +from PIL import Image,ImageDraw,ImageFont +import traceback + +logging.basicConfig(level=logging.DEBUG) + +min_x=0 +max_x=249 +min_y=0 +max_y=121 + +print_pause=10 + +try: + logging.info("vCARD startup") + epd = epd2in13_V2.EPD() + logging.info("init and Clear") + epd.init(epd.FULL_UPDATE) + epd.Clear(0xFF) + time.sleep(0.5) + logging.info("clear frame") + image = Image.new('1', (epd.height, epd.width), 255) # 255: clear the frame + draw = ImageDraw.Draw(image) + time.sleep(0.5) + logging.info("update frame") + epd.init(epd.FULL_UPDATE) + epd.Clear(0xFF) + time.sleep(0.5) + + # Settings FONTS + font15 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 15) + font20 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 20) + font25 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 25) + font30 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 30) + font35 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 35) + font45 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 45) + font50 = ImageFont.truetype(os.path.join(picdir, 'Font.ttc'), 60) + + image = Image.new('1', (epd.height, epd.width), 255) # 255: clear the frame + draw = ImageDraw.Draw(image) + + draw.rectangle([(min_x,min_y),(40,40)], outline = 0) #emtpy rectangle + draw.line([(min_x,min_y),(40,40)], fill = 0,width = 1) + draw.line([(min_x,40),(40,min_y)], fill = 0,width = 1) + draw.rectangle([(45,min_y),(85,40)],fill = 0) #filled rectangle + + draw.chord((90, min_y, 130, 40), 0, 360, fill = 0) #filled circle + draw.ellipse((135, min_y, 175, 40), outline = 0) + draw.pieslice((135, min_y, 175, 40), 90, 180, outline = 0) + draw.pieslice((135, min_y, 175, 40), 270, 360, fill = 0) + + draw.line([(min_x,max_y-60),(min_x,max_y)], fill = 0,width = 1) + draw.line([(min_x+4,max_y-50),(min_x+4,max_y)], fill = 0,width = 1) + draw.line([(min_x+8,max_y-30),(min_x+8,max_y)], fill = 0,width = 1) + draw.line([(min_x+12,max_y-10),(min_x+12,max_y)], fill = 0,width = 1) + + draw.line([(max_x,min_y),(max_x,max_y)], fill = 0,width = 1) + draw.line([(max_x-4,min_y),(max_x-4,max_y/2)], fill = 0,width = 1) + draw.line([(max_x-8,min_y),(max_x-8,max_y/3)], fill = 0,width = 1) + draw.line([(max_x-12,min_y),(max_x-12,max_y/4)], fill = 0,width = 1) + draw.line([(max_x-16,min_y),(max_x-16,max_y/5)], fill = 0,width = 1) + draw.line([(max_x-20,min_y),(max_x-20,max_y/6)], fill = 0,width = 1) + + draw.text((20, 40), 'e-Paper vCard', font = font25, fill = 0) + draw.text((30, 70), 'by WieErWill', font = font35, fill = 0) + epd.display(epd.getbuffer(image)) + time.sleep(3) + + while True: + # clear the frame + image = Image.new('1', (epd.height, epd.width), 255) + draw = ImageDraw.Draw(image) + draw.rectangle([(min_x,min_y),(max_x,max_y)], outline = 0) #emtpy rectangle + draw.rectangle([(min_x+3,min_y+3),(max_x-3,max_y-3)], outline = 0) #emtpy rectangle + draw.text((10, 10), 'Robert Jeutter', font = font35, fill = 0) + draw.text((10, 60), 'Software Engineer', font = font25, fill = 0) + draw.text((10, 85), 'working @ AraCom', font = font25, fill = 0) + epd.display(epd.getbuffer(image)) + time.sleep(print_pause) + + image = Image.new('1', (epd.height, epd.width), 255) + draw = ImageDraw.Draw(image) + draw.text((2, 5), 'Social Robotics', font = font35, fill = 0) + draw.text((2, 50), 'Machine Learning &', font = font25, fill = 0) + draw.text((2, 85), 'Human-Machine-Coop', font = font20, fill = 0) + epd.display(epd.getbuffer(image)) + time.sleep(print_pause) + + # clear the frame + imageARA = Image.new('1', (epd.height, epd.width), 255) + #bmp = Image.open(os.path.join(picdir, 'ara.bmp')) + #bmp2 = bmp.resize((250,100)) + #imageARA.paste(bmp, (2,5)) + draw = ImageDraw.Draw(imageARA) + draw.text((15, 5), 'AraCom', font = font50, fill = 0) + draw.text((13, 65), 'IT Services GmbH', font = font30, fill = 0) + draw.text((14, 100), 'Mail: robert.jeutter@aracom.de', font = font15, fill = 0) + epd.display(epd.getbuffer(imageARA)) + time.sleep(print_pause) + + # clear the frame + epd.init(epd.FULL_UPDATE) + image = Image.new('1', (epd.height, epd.width), 255) + draw = ImageDraw.Draw(image) + draw.text((50, 5), 'Write me', font = font25, fill = 0) + draw.text((20, 35), 'robert.jeutter', font = font35, fill = 0) + draw.text((20, 70), '@aracom.de', font = font35, fill = 0) + epd.display(epd.getbuffer(image)) + time.sleep(print_pause) + + # clear the frame + epd.init(epd.FULL_UPDATE) + image = Image.new('1', (epd.height, epd.width), 255) + draw = ImageDraw.Draw(image) + draw.text((25, 2), 'find me on', font = font25, fill = 0) + draw.text((15, 25), 'GitHub.com/', font = font35, fill = 0) + draw.text((20, 60), 'WieErWill', font = font45, fill = 0) + epd.display(epd.getbuffer(image)) + time.sleep(print_pause) + + imageQR = Image.new('1', (epd.height, epd.width), 255) # 255: clear the frame + bmp = Image.open(os.path.join(picdir, 'qr.bmp')) + imageQR.paste(bmp, (2,2)) + draw = ImageDraw.Draw(imageQR) + draw.text((130, 5), 'SCAN ME', font = font25, fill = 0) + draw.text((140, 45), '\(0^0)/', font = font25, fill = 0) + draw.text((130, 90), 'SCAN ME', font = font25, fill = 0) + epd.display(epd.getbuffer(imageQR)) + time.sleep(print_pause) + + # set display to sleep mode + epd.init(epd.FULL_UPDATE) + epd.Clear(0xFF) + epd.sleep() + time.sleep(print_pause) + +except IOError as e: + logging.info(e) + +except KeyboardInterrupt: + logging.info("ctrl + c:") + epd2in13_V2.epdconfig.module_exit() + exit() \ No newline at end of file diff --git a/py_lib/waveshare_epd/__init__.py b/py_lib/waveshare_epd/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/py_lib/waveshare_epd/epd2in13.py b/py_lib/waveshare_epd/epd2in13.py new file mode 100644 index 0000000..fcd4f39 --- /dev/null +++ b/py_lib/waveshare_epd/epd2in13.py @@ -0,0 +1,227 @@ +# ***************************************************************************** +# * | File : epd2in13.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from . import epdconfig + +# Display resolution +EPD_WIDTH = 122 +EPD_HEIGHT = 250 + +logger = logging.getLogger(__name__) + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + lut_full_update = [ + 0x22, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + + lut_partial_update = [ + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(100) + + def TurnOnDisplay(self): + self.send_command(0x22) # DISPLAY_UPDATE_CONTROL_2 + self.send_data(0xC4) + self.send_command(0x20) # MASTER_ACTIVATION + self.send_command(0xFF) # TERMINATE_FRAME_READ_WRITE + + logger.debug("e-Paper busy") + self.ReadBusy() + logger.debug("e-Paper busy release") + + def init(self, lut): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + self.send_command(0x01) # DRIVER_OUTPUT_CONTROL + self.send_data((EPD_HEIGHT - 1) & 0xFF) + self.send_data(((EPD_HEIGHT - 1) >> 8) & 0xFF) + self.send_data(0x00) # GD = 0 SM = 0 TB = 0 + + self.send_command(0x0C) # BOOSTER_SOFT_START_CONTROL + self.send_data(0xD7) + self.send_data(0xD6) + self.send_data(0x9D) + + self.send_command(0x2C) # WRITE_VCOM_REGISTER + self.send_data(0xA8) # VCOM 7C + + self.send_command(0x3A) # SET_DUMMY_LINE_PERIOD + self.send_data(0x1A) # 4 dummy lines per gate + + self.send_command(0x3B) # SET_GATE_TIME + self.send_data(0x08) # 2us per line + + self.send_command(0X3C) # BORDER_WAVEFORM_CONTROL + self.send_data(0x03) + + self.send_command(0X11) # DATA_ENTRY_MODE_SETTING + self.send_data(0x03) # X increment; Y increment + + # WRITE_LUT_REGISTER + self.send_command(0x32) + for count in range(30): + self.send_data(lut[count]) + + return 0 + +## + # @brief: specify the memory area for data R/W + ## + def SetWindows(self, x_start, y_start, x_end, y_end): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + self.send_data((x_start >> 3) & 0xFF) + self.send_data((x_end >> 3) & 0xFF) + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(y_start & 0xFF) + self.send_data((y_start >> 8) & 0xFF) + self.send_data(y_end & 0xFF) + self.send_data((y_end >> 8) & 0xFF) + +## + # @brief: specify the start point for data R/W + ## + def SetCursor(self, x, y): + self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data((x >> 3) & 0xFF) + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + self.ReadBusy() + + def getbuffer(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + buf = [0xFF] * (linewidth * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + + if(imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + if pixels[x, y] == 0: + # x = imwidth - x + buf[int(x / 8) + y * linewidth] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + # newy = imwidth - newy - 1 + buf[int(newx / 8) + newy*linewidth] &= ~(0x80 >> (y % 8)) + return buf + + + def display(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + self.SetWindows(0, 0, self.width, self.height); + for j in range(0, self.height): + self.SetCursor(0, j); + self.send_command(0x24); + for i in range(0, linewidth): + self.send_data(image[i + j * linewidth]) + self.TurnOnDisplay() + + def Clear(self, color): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + self.SetWindows(0, 0, self.width, self.height); + for j in range(0, self.height): + self.SetCursor(0, j); + self.send_command(0x24); + for i in range(0, linewidth): + self.send_data(color) + self.TurnOnDisplay() + + def sleep(self): + self.send_command(0x10) #enter deep sleep + self.send_data(0x01) + epdconfig.delay_ms(100) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### + diff --git a/py_lib/waveshare_epd/epd2in13_V2.py b/py_lib/waveshare_epd/epd2in13_V2.py new file mode 100644 index 0000000..1900654 --- /dev/null +++ b/py_lib/waveshare_epd/epd2in13_V2.py @@ -0,0 +1,322 @@ +# ***************************************************************************** +# * | File : epd2in13_V2.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V4.0 +# * | Date : 2019-06-20 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from . import epdconfig + +# Display resolution +EPD_WIDTH = 122 +EPD_HEIGHT = 250 + +logger = logging.getLogger(__name__) + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + FULL_UPDATE = 0 + PART_UPDATE = 1 + lut_full_update= [ + 0x80,0x60,0x40,0x00,0x00,0x00,0x00, #LUT0: BB: VS 0 ~7 + 0x10,0x60,0x20,0x00,0x00,0x00,0x00, #LUT1: BW: VS 0 ~7 + 0x80,0x60,0x40,0x00,0x00,0x00,0x00, #LUT2: WB: VS 0 ~7 + 0x10,0x60,0x20,0x00,0x00,0x00,0x00, #LUT3: WW: VS 0 ~7 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, #LUT4: VCOM: VS 0 ~7 + + 0x03,0x03,0x00,0x00,0x02, # TP0 A~D RP0 + 0x09,0x09,0x00,0x00,0x02, # TP1 A~D RP1 + 0x03,0x03,0x00,0x00,0x02, # TP2 A~D RP2 + 0x00,0x00,0x00,0x00,0x00, # TP3 A~D RP3 + 0x00,0x00,0x00,0x00,0x00, # TP4 A~D RP4 + 0x00,0x00,0x00,0x00,0x00, # TP5 A~D RP5 + 0x00,0x00,0x00,0x00,0x00, # TP6 A~D RP6 + + 0x15,0x41,0xA8,0x32,0x30,0x0A, + ] + + lut_partial_update = [ #20 bytes + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, #LUT0: BB: VS 0 ~7 + 0x80,0x00,0x00,0x00,0x00,0x00,0x00, #LUT1: BW: VS 0 ~7 + 0x40,0x00,0x00,0x00,0x00,0x00,0x00, #LUT2: WB: VS 0 ~7 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, #LUT3: WW: VS 0 ~7 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, #LUT4: VCOM: VS 0 ~7 + + 0x0A,0x00,0x00,0x00,0x00, # TP0 A~D RP0 + 0x00,0x00,0x00,0x00,0x00, # TP1 A~D RP1 + 0x00,0x00,0x00,0x00,0x00, # TP2 A~D RP2 + 0x00,0x00,0x00,0x00,0x00, # TP3 A~D RP3 + 0x00,0x00,0x00,0x00,0x00, # TP4 A~D RP4 + 0x00,0x00,0x00,0x00,0x00, # TP5 A~D RP5 + 0x00,0x00,0x00,0x00,0x00, # TP6 A~D RP6 + + 0x15,0x41,0xA8,0x32,0x30,0x0A, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(5) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(100) + + def TurnOnDisplay(self): + self.send_command(0x22) + self.send_data(0xC7) + self.send_command(0x20) + self.ReadBusy() + + def TurnOnDisplayPart(self): + self.send_command(0x22) + self.send_data(0x0c) + self.send_command(0x20) + self.ReadBusy() + + def init(self, update): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + if(update == self.FULL_UPDATE): + self.ReadBusy() + self.send_command(0x12) # soft reset + self.ReadBusy() + + self.send_command(0x74) #set analog block control + self.send_data(0x54) + self.send_command(0x7E) #set digital block control + self.send_data(0x3B) + + self.send_command(0x01) #Driver output control + self.send_data(0xF9) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) #data entry mode + self.send_data(0x01) + + self.send_command(0x44) #set Ram-X address start/end position + self.send_data(0x00) + self.send_data(0x0F) #0x0C-->(15+1)*8=128 + + self.send_command(0x45) #set Ram-Y address start/end position + self.send_data(0xF9) #0xF9-->(249+1)=250 + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) #BorderWavefrom + self.send_data(0x03) + + self.send_command(0x2C) #VCOM Voltage + self.send_data(0x55) # + + self.send_command(0x03) + self.send_data(self.lut_full_update[70]) + + self.send_command(0x04) # + self.send_data(self.lut_full_update[71]) + self.send_data(self.lut_full_update[72]) + self.send_data(self.lut_full_update[73]) + + self.send_command(0x3A) #Dummy Line + self.send_data(self.lut_full_update[74]) + self.send_command(0x3B) #Gate time + self.send_data(self.lut_full_update[75]) + + self.send_command(0x32) + for count in range(70): + self.send_data(self.lut_full_update[count]) + + self.send_command(0x4E) # set RAM x address count to 0 + self.send_data(0x00) + self.send_command(0x4F) # set RAM y address count to 0X127 + self.send_data(0xF9) + self.send_data(0x00) + self.ReadBusy() + else: + self.send_command(0x2C) #VCOM Voltage + self.send_data(0x26) + + self.ReadBusy() + + self.send_command(0x32) + for count in range(70): + self.send_data(self.lut_partial_update[count]) + + self.send_command(0x37) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x40) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x22) + self.send_data(0xC0) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x3C) #BorderWavefrom + self.send_data(0x01) + return 0 + + def getbuffer(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + buf = [0xFF] * (linewidth * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + + if(imwidth == self.width and imheight == self.height): + logger.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + if pixels[x, y] == 0: + x = imwidth - x + buf[int(x / 8) + y * linewidth] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logger.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + newy = imwidth - newy - 1 + buf[int(newx / 8) + newy*linewidth] &= ~(0x80 >> (y % 8)) + return buf + + + def display(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + self.send_command(0x24) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(image[i + j * linewidth]) + self.TurnOnDisplay() + + def displayPartial(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + self.send_command(0x24) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(image[i + j * linewidth]) + + + self.send_command(0x26) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(~image[i + j * linewidth]) + self.TurnOnDisplayPart() + + def displayPartBaseImage(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + self.send_command(0x24) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(image[i + j * linewidth]) + + + self.send_command(0x26) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(image[i + j * linewidth]) + self.TurnOnDisplay() + + def Clear(self, color): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + # logger.debug(linewidth) + + self.send_command(0x24) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(color) + + # self.send_command(0x26) + # for j in range(0, self.height): + # for i in range(0, linewidth): + # self.send_data(color) + + self.TurnOnDisplay() + + def sleep(self): + # self.send_command(0x22) #POWER OFF + # self.send_data(0xC3) + # self.send_command(0x20) + + self.send_command(0x10) #enter deep sleep + self.send_data(0x03) + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### + diff --git a/py_lib/waveshare_epd/epd2in13_V3.py b/py_lib/waveshare_epd/epd2in13_V3.py new file mode 100644 index 0000000..8ef814d --- /dev/null +++ b/py_lib/waveshare_epd/epd2in13_V3.py @@ -0,0 +1,396 @@ +# ***************************************************************************** +# * | File : epd2in13_V3.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.1 +# * | Date : 2021-10-30 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + + +import logging +from . import epdconfig + +# Display resolution +EPD_WIDTH = 122 +EPD_HEIGHT = 250 + +logger = logging.getLogger(__name__) + +class EPD: + def __init__(self): + self.reset_pin = epdconfig.RST_PIN + self.dc_pin = epdconfig.DC_PIN + self.busy_pin = epdconfig.BUSY_PIN + self.cs_pin = epdconfig.CS_PIN + self.width = EPD_WIDTH + self.height = EPD_HEIGHT + + lut_partial_update= [ + 0x0,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x80,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x40,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x14,0x0,0x0,0x0,0x0,0x0,0x0, + 0x1,0x0,0x0,0x0,0x0,0x0,0x0, + 0x1,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x22,0x22,0x22,0x22,0x22,0x22,0x0,0x0,0x0, + 0x22,0x17,0x41,0x00,0x32,0x36, + ] + + lut_full_update = [ + 0x80,0x4A,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x40,0x4A,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x80,0x4A,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x40,0x4A,0x80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0xF,0x0,0x0,0x0,0x0,0x0,0x0, + 0xF,0x0,0x0,0xF,0x0,0x0,0x2, + 0xF,0x0,0x0,0x0,0x0,0x0,0x0, + 0x1,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x0,0x0,0x0,0x0,0x0,0x0,0x0, + 0x22,0x22,0x22,0x22,0x22,0x22,0x0,0x0,0x0, + 0x22,0x17,0x41,0x0,0x32,0x36, + ] + + ''' + function :Hardware reset + parameter: + ''' + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(20) + + ''' + function :send command + parameter: + command : Command register + ''' + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + ''' + function :send data + parameter: + data : Write data + ''' + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + ''' + function :Wait until the busy_pin goes LOW + parameter: + ''' + def ReadBusy(self): + logger.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + epdconfig.delay_ms(10) + logger.debug("e-Paper busy release") + + ''' + function : Turn On Display + parameter: + ''' + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Turn On Display Part + parameter: + ''' + def TurnOnDisplayPart(self): + self.send_command(0x22) # Display Update Control + self.send_data(0x0f) # fast:0x0c, quality:0x0f, 0xcf + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Set lut + parameter: + lut : lut data + ''' + def Lut(self, lut): + self.send_command(0x32) + for i in range(0, 153): + self.send_data(lut[i]) + self.ReadBusy() + + ''' + function : Send lut data and configuration + parameter: + lut : lut data + ''' + def SetLut(self, lut): + self.Lut(lut) + self.send_command(0x3f) + self.send_data(lut[153]) + self.send_command(0x03) # gate voltage + self.send_data(lut[154]) + self.send_command(0x04) # source voltage + self.send_data(lut[155]) # VSH + self.send_data(lut[156]) # VSH2 + self.send_data(lut[157]) # VSL + self.send_command(0x2c) # VCOM + self.send_data(lut[158]) + + ''' + function : Setting the display window + parameter: + xstart : X-axis starting position + ystart : Y-axis starting position + xend : End position of X-axis + yend : End position of Y-axis + ''' + def SetWindow(self, x_start, y_start, x_end, y_end): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data((x_start>>3) & 0xFF) + self.send_data((x_end>>3) & 0xFF) + + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(y_start & 0xFF) + self.send_data((y_start >> 8) & 0xFF) + self.send_data(y_end & 0xFF) + self.send_data((y_end >> 8) & 0xFF) + + ''' + function : Set Cursor + parameter: + x : X-axis starting position + y : Y-axis starting position + ''' + def SetCursor(self, x, y): + self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER + # x point must be the multiple of 8 or the last 3 bits will be ignored + self.send_data(x & 0xFF) + + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(y & 0xFF) + self.send_data((y >> 8) & 0xFF) + + ''' + function : Initialize the e-Paper register + parameter: + ''' + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.ReadBusy() + self.send_command(0x12) #SWRESET + self.ReadBusy() + + self.send_command(0x01) #Driver output control + self.send_data(0xf9) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) #data entry mode + self.send_data(0x03) + + self.SetWindow(0, 0, self.width-1, self.height-1) + self.SetCursor(0, 0) + + self.send_command(0x3c) + self.send_data(0x05) + + self.send_command(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x80) + + self.send_command(0x18) + self.send_data(0x80) + + self.ReadBusy() + + self.SetLut(self.lut_full_update) + return 0 + + ''' + function : Display images + parameter: + image : Image data + ''' + def getbuffer(self, image): + img = image + imwidth, imheight = img.size + if(imwidth == self.width and imheight == self.height): + img = img.convert('1') + elif(imwidth == self.height and imheight == self.width): + # image has correct dimensions, but needs to be rotated + img = img.rotate(90, expand=True).convert('1') + else: + logger.warning("Wrong image dimensions: must be " + str(self.width) + "x" + str(self.height)) + # return a blank buffer + return [0x00] * (int(self.width/8) * self.height) + + buf = bytearray(img.tobytes('raw')) + return buf + + ''' + function : Sends the image buffer in RAM to e-Paper and displays + parameter: + image : Image data + ''' + def display(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + self.send_command(0x24) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(image[i + j * linewidth]) + self.TurnOnDisplay() + + ''' + function : Sends the image buffer in RAM to e-Paper and partial refresh + parameter: + image : Image data + ''' + def displayPartial(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(1) + epdconfig.digital_write(self.reset_pin, 1) + + self.SetLut(self.lut_partial_update) + self.send_command(0x37) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x40) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x3C) #BorderWavefrom + self.send_data(0x80) + + self.send_command(0x22) + self.send_data(0xC0) + self.send_command(0x20) + self.ReadBusy() + + self.SetWindow(0, 0, self.width - 1, self.height - 1) + self.SetCursor(0, 0) + + self.send_command(0x24) # WRITE_RAM + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(image[i + j * linewidth]) + self.TurnOnDisplayPart() + + ''' + function : Refresh a base image + parameter: + image : Image data + ''' + def displayPartBaseImage(self, image): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + + self.send_command(0x24) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(image[i + j * linewidth]) + + self.send_command(0x26) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(image[i + j * linewidth]) + self.TurnOnDisplay() + + ''' + function : Clear screen + parameter: + ''' + def Clear(self, color): + if self.width%8 == 0: + linewidth = int(self.width/8) + else: + linewidth = int(self.width/8) + 1 + # logger.debug(linewidth) + + self.send_command(0x24) + for j in range(0, self.height): + for i in range(0, linewidth): + self.send_data(color) + + self.TurnOnDisplay() + + ''' + function : Enter sleep mode + parameter: + ''' + def sleep(self): + self.send_command(0x10) #enter deep sleep + self.send_data(0x01) + + epdconfig.delay_ms(2000) + epdconfig.module_exit() + +### END OF FILE ### + diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..2d2e49d --- /dev/null +++ b/readme.md @@ -0,0 +1,76 @@ +# RaspberryPiZero 2.13in Display +This projects uses a single RaspberryPi Zero (v1) and a Waveshare 2.13" display, both connected via soldered GPIO-Hat. Languages used will be Python (C also possible). + +! Disclaimer: The libraries used here are not made by me. Those are the original library files from Waveshare (see below for sources). Any modifications will be annoted. + +## Next Steps and Roadmap +-[x] Text Examples: show single and multiple line texts +-[x] Image Examples: show pictures and modify pictures before show +-[x] Draw Examples: draw lines, circles and rectangles +-[x] Simple Card: a digital vCard you can always carry along and show off +-[x] Slideshow: Show multiple slides in a row repeatingly +-[ ] Partial Display Updates: only update partial display parts instead the whole screen +-[x] digital vCard with multiple pages, sliding and QR Codes +-[ ] start scripts with autostart while booting up +-[ ] get and show different RaspberryPi Stats + +## Set up your RPi +For the 2.13" display the RaspberryPi Zero fits perfect as they match each others size quite well. +With a proper cover you can get a nice display case you can place nearly everywhere without risking your hardware to get harmed. + +Install Raspbian or any other linux you like on your SD Card and set up your SSH connection. SSH will be the prefered way to connect up your RPi. +Working mainly on your home network, it can be handy to set up your pi with a local hostname like `raspberrypi.local` so you can reach it with `ssh username@raspberrypi.local` even if the RPis IP changes. + +### Connect your display +If you got the display or display hat without pinheader you have to connect it by hand. +Pin connections can be viewed in `\py_lib\epdconfig.py` and here: +``` +EPD => Jetson Nano/RPI(BCM) +VCC -> 3.3 +GND -> GND +DIN -> 10(SPI0_MOSI) +CLK -> 11(SPI0_SCK) +CS -> 8(SPI0_CS0) +DC -> 25 +RST -> 17 +BUSY -> 24 +``` + +## Use with python +### Install libraries +In order to access the display trough python you need some extra libraries: +```bash +sudo apt-get update +sudo apt-get install python3-pip +sudo apt-get install python3-pil +sudo pip3 install RPi.GPIO +``` + +### Basic and test use +The waveshare team already made some example code you can get from [Waveshare Github](https://github.com/waveshare/e-Paper) or look inside the `py_examples` folder (a small copy of the waveshare repo with changes). + +To run a script simply type: `sudo python epd_2in9bc_test.py` and switch the file name to what you want to start. +  +## Use with C +You need to compile the program, this will generate the executable file. After that you can run the executable to start your programm +```bash +make +sudo ./epd +``` + +If you modify the program, you need to re-compile +```bash +make clear +make +``` + +## Library Structure +- \c_lib\Config\: hardware interface layer files +- \c_lib\GUI\: basic image processing functions +- \c_lib\Fonts\: for some commonly used ascii fonts +- \c_lib\E-paper\: the ink screen driver functions + +## Sources +Display drivers and information from [Waveshare Wiki](https://www.waveshare.com/wiki/2.13inch_e-Paper_HAT) + +RaspberryPi Zero info from [raspberrypi.org/](https://www.raspberrypi.org/) \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..b8019aa --- /dev/null +++ b/setup.py @@ -0,0 +1,13 @@ +import sys, os +from setuptools import setup + +dependencies = ['Pillow', 'RPi.GPIO', 'spidev'] + +setup( + name='waveshare-epd', + description='Waveshare e-Paper Display', + author='Waveshare', + package_dir={'': 'lib'}, + packages=['waveshare_epd'], + install_requires=dependencies, +)