From 64006430392664ec4e62aaac6f4f0993e216e7cc Mon Sep 17 00:00:00 2001 From: Ace Date: Fri, 17 Jan 2020 16:46:07 +0100 Subject: [PATCH 01/15] replacing outdated driver files with latest ones --- modules/inkycal_drivers.py | 344 ------------------------------------- 1 file changed, 344 deletions(-) delete mode 100644 modules/inkycal_drivers.py diff --git a/modules/inkycal_drivers.py b/modules/inkycal_drivers.py deleted file mode 100644 index 3bd8e6e..0000000 --- a/modules/inkycal_drivers.py +++ /dev/null @@ -1,344 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- -""" -Drivers file for Inky-Calendar software. -Handles E-Paper display related tasks -""" - -from PIL import Image -import RPi.GPIO as GPIO -from settings import display_type -import numpy -import spidev -import RPi.GPIO as GPIO -from time import sleep - -RST_PIN = 17 -DC_PIN = 25 -CS_PIN = 8 -BUSY_PIN = 24 - -EPD_WIDTH = 640 -EPD_HEIGHT = 384 - -SPI = spidev.SpiDev(0, 0) - -def epd_digital_write(pin, value): - GPIO.output(pin, value) - -def epd_digital_read(pin): - return GPIO.input(BUSY_PIN) - -def epd_delay_ms(delaytime): - sleep(delaytime / 1000.0) - -def spi_transfer(data): - SPI.writebytes(data) - -def epd_init(): - GPIO.setmode(GPIO.BCM) - GPIO.setwarnings(False) - GPIO.setup(RST_PIN, GPIO.OUT) - GPIO.setup(DC_PIN, GPIO.OUT) - GPIO.setup(CS_PIN, GPIO.OUT) - GPIO.setup(BUSY_PIN, GPIO.IN) - SPI.max_speed_hz = 4000000 - SPI.mode = 0b00 - return 0; - -# EPD7IN5 commands -PANEL_SETTING = 0x00 -POWER_SETTING = 0x01 -POWER_OFF = 0x02 -POWER_OFF_SEQUENCE_SETTING = 0x03 -POWER_ON = 0x04 -POWER_ON_MEASURE = 0x05 -BOOSTER_SOFT_START = 0x06 -DEEP_SLEEP = 0x07 -DATA_START_TRANSMISSION_1 = 0x10 -DATA_STOP = 0x11 -DISPLAY_REFRESH = 0x12 -IMAGE_PROCESS = 0x13 -LUT_FOR_VCOM = 0x20 -LUT_BLUE = 0x21 -LUT_WHITE = 0x22 -LUT_GRAY_1 = 0x23 -LUT_GRAY_2 = 0x24 -LUT_RED_0 = 0x25 -LUT_RED_1 = 0x26 -LUT_RED_2 = 0x27 -LUT_RED_3 = 0x28 -LUT_XON = 0x29 -PLL_CONTROL = 0x30 -TEMPERATURE_SENSOR_COMMAND = 0x40 -TEMPERATURE_CALIBRATION = 0x41 -TEMPERATURE_SENSOR_WRITE = 0x42 -TEMPERATURE_SENSOR_READ = 0x43 -VCOM_AND_DATA_INTERVAL_SETTING = 0x50 -LOW_POWER_DETECTION = 0x51 -TCON_SETTING = 0x60 -TCON_RESOLUTION = 0x61 -SPI_FLASH_CONTROL = 0x65 -REVISION = 0x70 -GET_STATUS = 0x71 -AUTO_MEASUREMENT_VCOM = 0x80 -READ_VCOM_VALUE = 0x81 -VCM_DC_SETTING = 0x82 - -class EPD: - def __init__(self): - self.reset_pin = RST_PIN - self.dc_pin = DC_PIN - self.busy_pin = BUSY_PIN - self.width = EPD_WIDTH - self.height = EPD_HEIGHT - - def digital_write(self, pin, value): - epd_digital_write(pin, value) - - def digital_read(self, pin): - return epd_digital_read(pin) - - def delay_ms(self, delaytime): - epd_delay_ms(delaytime) - - def send_command(self, command): - self.digital_write(self.dc_pin, GPIO.LOW) - spi_transfer([command]) - - def send_data(self, data): - self.digital_write(self.dc_pin, GPIO.HIGH) - spi_transfer([data]) - - def init(self): - if (epd_init() != 0): - return -1 - self.reset() - self.send_command(POWER_SETTING) - self.send_data(0x37) - self.send_data(0x00) - self.send_command(PANEL_SETTING) - self.send_data(0xCF) - self.send_data(0x08) - self.send_command(BOOSTER_SOFT_START) - self.send_data(0xc7) - self.send_data(0xcc) - self.send_data(0x28) - self.send_command(POWER_ON) - self.wait_until_idle() - self.send_command(PLL_CONTROL) - self.send_data(0x3c) - self.send_command(TEMPERATURE_CALIBRATION) - self.send_data(0x00) - self.send_command(VCOM_AND_DATA_INTERVAL_SETTING) - self.send_data(0x77) - self.send_command(TCON_SETTING) - self.send_data(0x22) - self.send_command(TCON_RESOLUTION) - self.send_data(0x02) #source 640 - self.send_data(0x80) - self.send_data(0x01) #gate 384 - self.send_data(0x80) - self.send_command(VCM_DC_SETTING) - self.send_data(0x1E) #decide by LUT file - self.send_command(0xe5) #FLASH MODE - self.send_data(0x03) - - def wait_until_idle(self): - while(self.digital_read(self.busy_pin) == 0): # 0: busy, 1: idle - self.delay_ms(100) - - def reset(self): - self.digital_write(self.reset_pin, GPIO.LOW) # module reset - self.delay_ms(200) - self.digital_write(self.reset_pin, GPIO.HIGH) - self.delay_ms(200) - - def calibrate_display(self, no_of_cycles): - """Function for Calibration""" - - if display_type == 'colour': - packets = int(self.width / 2 * self.height) - if display_type == 'black_and_white': - packets = int(self.width / 4 * self.height) - - white, red, black = 0x33, 0x04, 0x00 - - self.init() - print('----------Started calibration of E-Paper display----------') - for _ in range(no_of_cycles): - self.send_command(DATA_START_TRANSMISSION_1) - print('Calibrating black...') - [self.send_data(black) for i in range(packets)] - self.send_command(DISPLAY_REFRESH) - self.wait_until_idle() - - if display_type == 'colour': - print('Calibrating red...') - self.send_command(DATA_START_TRANSMISSION_1) - [self.send_data(red) for i in range(packets)] - self.send_command(DISPLAY_REFRESH) - self.wait_until_idle() - - print('Calibrating white...') - self.send_command(DATA_START_TRANSMISSION_1) - [self.send_data(white) for i in range(packets)] - self.send_command(DISPLAY_REFRESH) - self.wait_until_idle() - - print('Cycle {0} of {1} complete'.format(_+1, no_of_cycles)) - - print('-----------Calibration complete----------') - self.sleep() - - def reduce_colours(self, image): - buffer = numpy.array(image) - r,g,b = buffer[:,:,0], buffer[:,:,1], buffer[:,:,2] - - if display_type == "colour": - buffer[numpy.logical_and(r <= 180, r == g)] = [0,0,0] #black - buffer[numpy.logical_and(r >= 150, g >= 150)] = [255,255,255] #white - buffer[numpy.logical_and(r >= 150, g <= 90)] = [255,0,0] #red - - image = Image.fromarray(buffer) - return image - - def clear(self, colour='white'): - if display_type == 'colour': - packets = int(self.width / 2 * self.height) - if display_type == 'black_and_white': - packets = int(self.width / 4 * self.height) - - if colour == 'white': data = 0x33 - if colour == 'red': data = 0x04 - if colour == 'black': data = 0x00 - - self.init() - self.send_command(DATA_START_TRANSMISSION_1) - [self.send_data(data) for _ in range(packets)] - self.send_command(DISPLAY_REFRESH) - print('waiting until E-Paper is not busy') - self.delay_ms(100) - self.wait_until_idle() - print('E-Paper free') - self.sleep() - - def get_frame_buffer(self, image): - imwidth, imheight = image.size - if imwidth == self.height and imheight == self.width: - image = image.rotate(270, expand = True) - print('Rotated image by 270 degrees...', end= '') - elif imwidth != self.width or imheight != self.height: - raise ValueError('Image must be same dimensions as display \ - ({0}x{1}).' .format(self.width, self.height)) - else: - print('Image size OK') - imwidth, imheight = image.size - - if display_type == 'colour': - buf = [0x00] * int(self.width * self.height / 4) - image_grayscale = image.convert('L') - pixels = image_grayscale.load() - - for y in range(self.height): - for x in range(self.width): - # Set the bits for the column of pixels at the current position. - if pixels[x, y] == 0: # black - buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2)) - elif pixels[x, y] == 76: # convert gray to red - buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2)) - buf[int((x + y * self.width) / 4)] |= 0x40 >> (x % 4 * 2) - else: # white - buf[int((x + y * self.width) / 4)] |= 0xC0 >> (x % 4 * 2) - - if display_type == 'black_and_white': - buf = [0x00] * int(self.width * self.height / 8) - image_monocolor = image.convert('1', dither = True) - - pixels = image_monocolor.load() - for y in range(self.height): - for x in range(self.width): - # Set the bits for the column of pixels at the current position. - if pixels[x, y] != 0: - buf[int((x + y * self.width) / 8)] |= 0x80 >> (x % 8) - - return buf - - def display_frame(self, frame_buffer): - self.send_command(DATA_START_TRANSMISSION_1) - if display_type == 'colour': - for i in range(0, int(self.width / 4 * self.height)): - temp1 = frame_buffer[i] - j = 0 - while (j < 4): - if ((temp1 & 0xC0) == 0xC0): - temp2 = 0x03 #white - elif ((temp1 & 0xC0) == 0x00): - temp2 = 0x00 #black - else: - temp2 = 0x04 #red - temp2 = (temp2 << 4) & 0xFF - temp1 = (temp1 << 2) & 0xFF - j += 1 - if((temp1 & 0xC0) == 0xC0): - temp2 |= 0x03 #white - elif ((temp1 & 0xC0) == 0x00): - temp2 |= 0x00 #black - else: - temp2 |= 0x04 #red - temp1 = (temp1 << 2) & 0xFF - self.send_data(temp2) - j += 1 - - if display_type == 'black_and_white': - for i in range(0, 30720): - temp1 = frame_buffer[i] - j = 0 - while (j < 8): - if(temp1 & 0x80): - temp2 = 0x03 #white - else: - temp2 = 0x00 #black - temp2 = (temp2 << 4) & 0xFF - temp1 = (temp1 << 1) & 0xFF - j += 1 - if(temp1 & 0x80): - temp2 |= 0x03 #white - else: - temp2 |= 0x00 #black - temp1 = (temp1 << 1) & 0xFF - self.send_data(temp2) - j += 1 - - self.send_command(DISPLAY_REFRESH) - self.delay_ms(100) - self.wait_until_idle() - - def show_image(self, image, reduce_colours = True): - print('Initialising E-Paper Display...', end='') - self.init() - sleep(5) - print('Done') - - if reduce_colours == True: - print('Optimising Image for E-Paper displays...', end = '') - image = self.reduce_colours(image) - print('Done') - else: - print('No colour optimisation done on image') - - print('Creating image buffer and sending it to E-Paper display...', end='') - data = self.get_frame_buffer(image) - print('Done') - print('Refreshing display...', end = '') - self.display_frame(data) - print('Done') - print('Sending E-Paper to deep sleep mode...',end='') - self.sleep() - print('Done') - - def sleep(self): - self.send_command(POWER_OFF) - self.wait_until_idle() - self.send_command(DEEP_SLEEP) - self.send_data(0xa5) From 5d714665a435a4cb92810880812847a9803a63a6 Mon Sep 17 00:00:00 2001 From: Ace Date: Fri, 17 Jan 2020 17:03:38 +0100 Subject: [PATCH 02/15] Minor improvements + Bugfixes Fixed a bug where the update interval would be calculated incorrectly. The display will now update as specified in the settings file. Instead of importing a single driver file, this module will automatically import the driver file for the specified E-Paper (in the settings file). This means that even though more E-Papers are now supported, the code will not become too complex. Calibration has been omitted for now to test if the E-Paper shows signs of ghosting. --- modules/inkycal.py | 84 +++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 45 deletions(-) diff --git a/modules/inkycal.py b/modules/inkycal.py index 3ee2f93..ac03ff5 100644 --- a/modules/inkycal.py +++ b/modules/inkycal.py @@ -1,28 +1,27 @@ #!/usr/bin/python3 # -*- coding: utf-8 -*- """ -Main script of Inky-Calendar software. +v1.7.1 + +Main file of Inky-Calendar software. Creates dynamic images for each section, +assembles them and sends it to the E-Paper + Copyright by aceisace """ from __future__ import print_function -from configuration import * -from settings import * import arrow from time import sleep import gc -import inkycal_drivers as drivers - import inkycal_rss as rss import inkycal_weather as weather import inkycal_calendar as calendar import inkycal_agenda as agenda +from configuration import * +import importlib +driver = importlib.import_module('drivers.'+model) -display = drivers.EPD() -skip_calibration = False - -"""Perepare for execution of main programm""" -calibration_countdown = 'initial' +"""Remove previously generated images""" image_cleanup() """Check time and calibrate display if time """ @@ -30,32 +29,12 @@ while True: now = arrow.now(tz=get_tz()) for _ in range(1): image = Image.new('RGB', (display_width, display_height), background_colour) - + """------------------Add short info------------------""" print('Current Date: {0} \nCurrent Time: {1}'.format(now.format( 'D MMM YYYY'), now.format('HH:mm'))) print('-----------Main programm started now----------') - - - """------------------Calibration check----------------""" - if skip_calibration != True: - print('Calibration..', end = ' ') - if now.hour in calibration_hours: - if calibration_countdown == 'initial': - print('required. Performing calibration now.') - calibration_countdown = 0 - display.calibrate_display(3) - else: - if calibration_countdown % (60 // int(update_interval)) == 0: - display.calibrate_display(3) - calibration_countdown = 0 - else: - print('not required. Continuing...') - else: - print('Calibration skipped!. Please note that not calibrating e-paper', - 'displays causes ghosting') - """----------------Generating and assembling images------""" if top_section == 'Weather': try: @@ -72,7 +51,7 @@ while True: image.paste(calendar_image, (0, middle_section_offset)) except: pass - + if middle_section == 'Agenda': try: agenda.main() @@ -80,7 +59,7 @@ while True: image.paste(agenda_image, (0, middle_section_offset)) except: pass - + if bottom_section == 'RSS': try: rss.main() @@ -89,29 +68,44 @@ while True: except: pass - image.save(image_path + 'canvas.png') + image.save(image_path + 'canvas.png') """---------Refreshing E-Paper with newly created image-----------""" - display.show_image(image, reduce_colours= True) + epaper = driver.EPD() + print('Initialising E-Paper...', end = '') + epaper.init() + print('Done') + + if three_colour_support == True: + print('Sending image data and refreshing display...', end='') + black_im, red_im = split_colours(image) + epaper.display(epaper.getbuffer(black_im), epaper.getbuffer(red_im)) + print('Done') + else: + print('Sending image data and refreshing display...', end='') + epaper.display(epaper.getbuffer(image.convert('1', dither=True))) + print('Done') + + print('Sending E-Paper to deep sleep...', end = '') + epaper.sleep() + print('Done') """--------------Post processing after main loop-----------------""" """Collect some garbage to free up some resources""" gc.collect() - """Adjust calibration countdowns""" - if calibration_countdown == 'initial': - calibration_countdown = 0 - calibration_countdown += 1 - """Calculate duration until next display refresh""" for _ in range(1): update_timings = [(60 - int(update_interval)*updates) for updates in - range(60//int(update_interval))] + range(60//int(update_interval))][::-1] - minutes = [i - now.minute for i in update_timings if i >= now.minute] - refresh_countdown = minutes[0]*60 + (60 - now.second) + for _ in update_timings: + if now.minute <= _: + minutes = _ - now.minute + break + + refresh_countdown = minutes*60 + (60 - now.second) + print('{0} Minutes left until next refresh'.format(minutes)) - print('{0} Minutes left until next refresh'.format(minutes[0])) - del update_timings, minutes, image sleep(refresh_countdown) From 1e3fcb28d03d99754fa65ebcee2d373b849ffab1 Mon Sep 17 00:00:00 2001 From: Ace Date: Fri, 17 Jan 2020 17:05:50 +0100 Subject: [PATCH 03/15] Added support for more E-Paper displays --- modules/drivers/epd_4_in_2.py | 478 ++++++++++++++++++++++++ modules/drivers/epd_4_in_2_colour.py | 148 ++++++++ modules/drivers/epd_5_in_83.py | 200 ++++++++++ modules/drivers/epd_5_in_83_colour.py | 200 ++++++++++ modules/drivers/epd_7_in_5.py | 202 ++++++++++ modules/drivers/epd_7_in_5_colour.py | 201 ++++++++++ modules/drivers/epd_7_in_5_v2.py | 170 +++++++++ modules/drivers/epd_7_in_5_v2_colour.py | 173 +++++++++ modules/drivers/epdconfig.py | 154 ++++++++ modules/drivers/init.py | 1 + 10 files changed, 1927 insertions(+) create mode 100644 modules/drivers/epd_4_in_2.py create mode 100644 modules/drivers/epd_4_in_2_colour.py create mode 100644 modules/drivers/epd_5_in_83.py create mode 100644 modules/drivers/epd_5_in_83_colour.py create mode 100644 modules/drivers/epd_7_in_5.py create mode 100644 modules/drivers/epd_7_in_5_colour.py create mode 100644 modules/drivers/epd_7_in_5_v2.py create mode 100644 modules/drivers/epd_7_in_5_v2_colour.py create mode 100644 modules/drivers/epdconfig.py create mode 100644 modules/drivers/init.py diff --git a/modules/drivers/epd_4_in_2.py b/modules/drivers/epd_4_in_2.py new file mode 100644 index 0000000..5a15fc5 --- /dev/null +++ b/modules/drivers/epd_4_in_2.py @@ -0,0 +1,478 @@ +# ***************************************************************************** +# * | File : epd4in2.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 +from PIL import Image +import RPi.GPIO as GPIO + +# Display resolution +EPD_WIDTH = 400 +EPD_HEIGHT = 300 + +GRAY1 = 0xff #white +GRAY2 = 0xC0 +GRAY3 = 0x80 #gray +GRAY4 = 0x00 #Blackest + +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 + self.GRAY1 = GRAY1 #white + self.GRAY2 = GRAY2 + self.GRAY3 = GRAY3 #gray + self.GRAY4 = GRAY4 #Blackest + + lut_vcom0 = [ + 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x17, 0x17, 0x00, 0x00, 0x02, + 0x00, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_ww = [ + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_bw = [ + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_wb = [ + 0x80, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, + 0x80, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + lut_bb = [ + 0x80, 0x17, 0x00, 0x00, 0x00, 0x02, + 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, + 0x80, 0x0A, 0x01, 0x00, 0x00, 0x01, + 0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ] + + #******************************gray*********************************/ + #0~3 gray + EPD_4IN2_4Gray_lut_vcom =[ + 0x00 ,0x0A ,0x00 ,0x00 ,0x00 ,0x01, + 0x60 ,0x14 ,0x14 ,0x00 ,0x00 ,0x01, + 0x00 ,0x14 ,0x00 ,0x00 ,0x00 ,0x01, + 0x00 ,0x13 ,0x0A ,0x01 ,0x00 ,0x01, + 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00, + 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00, + 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 + ] + #R21 + EPD_4IN2_4Gray_lut_ww =[ + 0x40 ,0x0A ,0x00 ,0x00 ,0x00 ,0x01, + 0x90 ,0x14 ,0x14 ,0x00 ,0x00 ,0x01, + 0x10 ,0x14 ,0x0A ,0x00 ,0x00 ,0x01, + 0xA0 ,0x13 ,0x01 ,0x00 ,0x00 ,0x01, + 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00, + 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00, + 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00, + ] + #R22H r + EPD_4IN2_4Gray_lut_bw =[ + 0x40 ,0x0A ,0x00 ,0x00 ,0x00 ,0x01, + 0x90 ,0x14 ,0x14 ,0x00 ,0x00 ,0x01, + 0x00 ,0x14 ,0x0A ,0x00 ,0x00 ,0x01, + 0x99 ,0x0C ,0x01 ,0x03 ,0x04 ,0x01, + 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00, + 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00, + 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00, + ] + #R23H w + EPD_4IN2_4Gray_lut_wb =[ + 0x40 ,0x0A ,0x00 ,0x00 ,0x00 ,0x01, + 0x90 ,0x14 ,0x14 ,0x00 ,0x00 ,0x01, + 0x00 ,0x14 ,0x0A ,0x00 ,0x00 ,0x01, + 0x99 ,0x0B ,0x04 ,0x04 ,0x01 ,0x01, + 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00, + 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00, + 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00, + ] + #R24H b + EPD_4IN2_4Gray_lut_bb =[ + 0x80 ,0x0A ,0x00 ,0x00 ,0x00 ,0x01, + 0x90 ,0x14 ,0x14 ,0x00 ,0x00 ,0x01, + 0x20 ,0x14 ,0x0A ,0x00 ,0x00 ,0x01, + 0x50 ,0x13 ,0x01 ,0x00 ,0x00 ,0x01, + 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00, + 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00, + 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00, + ] + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + self.send_command(0x71) + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + self.send_command(0x71) + epdconfig.delay_ms(100) + + def set_lut(self): + self.send_command(0x20) # vcom + for count in range(0, 44): + self.send_data(self.lut_vcom0[count]) + + self.send_command(0x21) # ww -- + for count in range(0, 42): + self.send_data(self.lut_ww[count]) + + self.send_command(0x22) # bw r + for count in range(0, 42): + self.send_data(self.lut_bw[count]) + + self.send_command(0x23) # wb w + for count in range(0, 42): + self.send_data(self.lut_bb[count]) + + self.send_command(0x24) # bb b + for count in range(0, 42): + self.send_data(self.lut_wb[count]) + + def Gray_SetLut(self): + self.send_command(0x20) #vcom + for count in range(0, 42): + self.send_data(self.EPD_4IN2_4Gray_lut_vcom[count]) + + self.send_command(0x21) #red not use + for count in range(0, 42): + self.send_data(self.EPD_4IN2_4Gray_lut_ww[count]) + + self.send_command(0x22) #bw r + for count in range(0, 42): + self.send_data(self.EPD_4IN2_4Gray_lut_bw[count]) + + self.send_command(0x23) #wb w + for count in range(0, 42): + self.send_data(self.EPD_4IN2_4Gray_lut_wb[count]) + + self.send_command(0x24) #bb b + for count in range(0, 42): + self.send_data(self.EPD_4IN2_4Gray_lut_bb[count]) + + self.send_command(0x25) #vcom + for count in range(0, 42): + self.send_data(self.EPD_4IN2_4Gray_lut_ww[count]) + + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER SETTING + self.send_data(0x03) # VDS_EN, VDG_EN + self.send_data(0x00) # VCOM_HV, VGHL_LV[1], VGHL_LV[0] + self.send_data(0x2b) # VDH + self.send_data(0x2b) # VDL + + self.send_command(0x06) # boost soft start + self.send_data(0x17) + self.send_data(0x17) + self.send_data(0x17) + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x00) # panel setting + self.send_data(0xbf) # KW-BF KWR-AF BWROTP 0f + self.send_data(0x0d) + + self.send_command(0x30) # PLL setting + self.send_data(0x3c) # 3A 100HZ 29 150Hz 39 200HZ 31 171HZ + + self.send_command(0x61) # resolution setting + self.send_data(0x01) + self.send_data(0x90) # 128 + self.send_data(0x01) + self.send_data(0x2c) + + self.send_command(0x82) # vcom_DC setting + self.send_data(0x28) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x97) # 97white border 77black border VBDF 17|D7 VBDW 97 VBDB 57 VBDF F7 VBDW 77 VBDB 37 VBDR B7 + + self.set_lut() + # EPD hardware init end + return 0 + + def Init_4Gray(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) #POWER SETTING + self.send_data (0x03) + self.send_data (0x00) #VGH=20V,VGL=-20V + self.send_data (0x2b) #VDH=15V + self.send_data (0x2b) #VDL=-15V + self.send_data (0x13) + + self.send_command(0x06) #booster soft start + self.send_data (0x17) #A + self.send_data (0x17) #B + self.send_data (0x17) #C + + self.send_command(0x04) + self.ReadBusy() + + self.send_command(0x00) #panel setting + self.send_data(0x3f) #KW-3f KWR-2F BWROTP 0f BWOTP 1f + + self.send_command(0x30) #PLL setting + self.send_data (0x3c) #100hz + + self.send_command(0x61) #resolution setting + self.send_data (0x01) #400 + self.send_data (0x90) + self.send_data (0x01) #300 + self.send_data (0x2c) + + self.send_command(0x82) #vcom_DC setting + self.send_data (0x12) + + self.send_command(0X50) #VCOM AND DATA INTERVAL SETTING + self.send_data(0x97) + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def getbuffer_4Gray(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width / 4) * self.height) + image_monocolor = image.convert('L') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + i=0 + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if(pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i= i+1 + if(i%4 == 0): + buf[int((x + (y * self.width))/4)] = ((pixels[x-3, y]&0xc0) | (pixels[x-2, y]&0xc0)>>2 | (pixels[x-1, y]&0xc0)>>4 | (pixels[x, y]&0xc0)>>6) + + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for x in range(imwidth): + for y in range(imheight): + newx = y + newy = x + if(pixels[x, y] == 0xC0): + pixels[x, y] = 0x80 + elif (pixels[x, y] == 0x80): + pixels[x, y] = 0x40 + i= i+1 + if(i%4 == 0): + buf[int((newx + (newy * self.width))/4)] = ((pixels[x, y-3]&0xc0) | (pixels[x, y-2]&0xc0)>>2 | (pixels[x, y-1]&0xc0)>>4 | (pixels[x, y]&0xc0)>>6) + + return buf + + def display(self, image): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(image[i]) + + self.send_command(0x12) + self.ReadBusy() + + def display_4Gray(self, image): + self.send_command(0x10) + for i in range(0, EPD_WIDTH * EPD_HEIGHT / 8): # EPD_WIDTH * EPD_HEIGHT / 4 + temp3=0 + for j in range(0, 2): + temp1 = image[i*2+j] + for k in range(0, 2): + temp2 = temp1&0xC0 + if(temp2 == 0xC0): + temp3 |= 0x01#white + elif(temp2 == 0x00): + temp3 |= 0x00 #black + elif(temp2 == 0x80): + temp3 |= 0x01 #gray1 + else: #0x40 + temp3 |= 0x00 #gray2 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1&0xC0 + if(temp2 == 0xC0): #white + temp3 |= 0x01 + elif(temp2 == 0x00): #black + temp3 |= 0x00 + elif(temp2 == 0x80): + temp3 |= 0x01 #gray1 + else : #0x40 + temp3 |= 0x00 #gray2 + if(j!=1 or k!=1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.send_command(0x13) + + for i in range(0, EPD_WIDTH * EPD_HEIGHT / 8): #5808*4 46464 + temp3=0 + for j in range(0, 2): + temp1 = image[i*2+j] + for k in range(0, 2): + temp2 = temp1&0xC0 + if(temp2 == 0xC0): + temp3 |= 0x01#white + elif(temp2 == 0x00): + temp3 |= 0x00 #black + elif(temp2 == 0x80): + temp3 |= 0x00 #gray1 + else: #0x40 + temp3 |= 0x01 #gray2 + temp3 <<= 1 + + temp1 <<= 2 + temp2 = temp1&0xC0 + if(temp2 == 0xC0): #white + temp3 |= 0x01 + elif(temp2 == 0x00): #black + temp3 |= 0x00 + elif(temp2 == 0x80): + temp3 |= 0x00 #gray1 + else: #0x40 + temp3 |= 0x01 #gray2 + if(j!=1 or k!=1): + temp3 <<= 1 + temp1 <<= 2 + self.send_data(temp3) + + self.Gray_SetLut() + self.send_command(0x12) + epdconfig.delay_ms(200) + self.ReadBusy() + # pass + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.module_exit() + +### END OF FILE ### + diff --git a/modules/drivers/epd_4_in_2_colour.py b/modules/drivers/epd_4_in_2_colour.py new file mode 100644 index 0000000..d29465a --- /dev/null +++ b/modules/drivers/epd_4_in_2_colour.py @@ -0,0 +1,148 @@ +# ***************************************************************************** +# * | File : epd4in2bc.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 = 400 +EPD_HEIGHT = 300 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logging.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data (0x17) + self.send_data (0x17) + self.send_data (0x17) # 07 0f 17 1f 27 2F 37 2f + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0x0F) # LUT from OTP + + return 0 + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imagered[i]) + + self.send_command(0x12) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xFF) + + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0xA5) # check code + + epdconfig.module_exit() +### END OF FILE ### + diff --git a/modules/drivers/epd_5_in_83.py b/modules/drivers/epd_5_in_83.py new file mode 100644 index 0000000..a0131b7 --- /dev/null +++ b/modules/drivers/epd_5_in_83.py @@ -0,0 +1,200 @@ +# ***************************************************************************** +# * | File : epd5in83.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 = 600 +EPD_HEIGHT = 448 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logging.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x37) + self.send_data(0x00) + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0xCF) + self.send_data(0x08) + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0xc7) + self.send_data(0xcc) + self.send_data(0x28) + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3c) + + self.send_command(0x41) # TEMPERATURE_CALIBRATION + self.send_data(0x00) + + self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0x77) + + self.send_command(0x60) # TCON_SETTING + self.send_data(0x22) + + self.send_command(0x61) # TCON_RESOLUTION + self.send_data(0x02) # source 600 + self.send_data(0x58) + self.send_data(0x01) # gate 448 + self.send_data(0xC0) + + self.send_command(0x82) # VCM_DC_SETTING + self.send_data(0x1E) # decide by LUT file + + self.send_command(0xe5) # FLASH MODE + self.send_data(0x03) + + # EPD hardware init end + return 0 + + def getbuffer(self, image): + buf = [0x00] * int(self.width * self.height / 4) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + logging.debug('imwidth = %d imheight = %d ',imwidth, imheight) + if(imwidth == self.width and imheight == self.height): + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] < 64: # black + buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2)) + elif pixels[x, y] < 192: # convert gray to red + buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2)) + buf[int((x + y * self.width) / 4)] |= 0x40 >> (x % 4 * 2) + else: # white + buf[int((x + y * self.width) / 4)] |= 0xC0 >> (x % 4 * 2) + elif(imwidth == self.height and imheight == self.width): + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] < 64: # black + buf[int((newx + newy*self.width) / 4)] &= ~(0xC0 >> (y % 4 * 2)) + elif pixels[x, y] < 192: # convert gray to red + buf[int((newx + newy*self.width) / 4)] &= ~(0xC0 >> (y % 4 * 2)) + buf[int((newx + newy*self.width) / 4)] |= 0x40 >> (y % 4 * 2) + else: # white + buf[int((newx + newy*self.width) / 4)] |= 0xC0 >> (y % 4 * 2) + return buf + + def display(self, image): + self.send_command(0x10) + for i in range(0, int(self.width / 4 * self.height)): + temp1 = image[i] + j = 0 + while (j < 4): + if ((temp1 & 0xC0) == 0xC0): + temp2 = 0x03 + elif ((temp1 & 0xC0) == 0x00): + temp2 = 0x00 + else: + temp2 = 0x04 + temp2 = (temp2 << 4) & 0xFF + temp1 = (temp1 << 2) & 0xFF + j += 1 + if((temp1 & 0xC0) == 0xC0): + temp2 |= 0x03 + elif ((temp1 & 0xC0) == 0x00): + temp2 |= 0x00 + else: + temp2 |= 0x04 + temp1 = (temp1 << 2) & 0xFF + self.send_data(temp2) + j += 1 + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width / 4 * self.height)): + for j in range(0, 4): + self.send_data(0x33) + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.module_exit() + +### END OF FILE ### + diff --git a/modules/drivers/epd_5_in_83_colour.py b/modules/drivers/epd_5_in_83_colour.py new file mode 100644 index 0000000..19c8a40 --- /dev/null +++ b/modules/drivers/epd_5_in_83_colour.py @@ -0,0 +1,200 @@ +# ***************************************************************************** +# * | File : epd5in83b.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 = 600 +EPD_HEIGHT = 448 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logging.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x37) + self.send_data(0x00) + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0xCF) + self.send_data(0x08) + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3A) # PLL: 0-15:0x3C, 15+:0x3A + self.send_command(0X82) # VCOM VOLTAGE SETTING + self.send_data(0x28) # all temperature range + + self.send_command(0x06) # boost + self.send_data(0xc7) + self.send_data(0xcc) + self.send_data(0x15) + + self.send_command(0X50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x77) + + self.send_command(0X60) # TCON SETTING + self.send_data(0x22) + + self.send_command(0X65) # FLASH CONTROL + self.send_data(0x00) + + self.send_command(0x61) # tres + self.send_data(0x02) # source 600 + self.send_data(0x58) + self.send_data(0x01) # gate 448 + self.send_data(0xc0) + + self.send_command(0xe5) # FLASH MODE + self.send_data(0x03) + self.send_data(0x03) + + return 0 + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + logging.debug('imwidth = %d imheight = %d ',imwidth, imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width / 8 * self.height)): + temp1 = imageblack[i] + temp2 = imagered[i] + j = 0 + while (j < 8): + if ((temp2 & 0x80) == 0x00): + temp3 = 0x04 #red + elif ((temp1 & 0x80) == 0x00): + temp3 = 0x00 #black + else: + temp3 = 0x03 #white + + temp3 = (temp3 << 4) & 0xFF + temp1 = (temp1 << 1) & 0xFF + temp2 = (temp2 << 1) & 0xFF + j += 1 + if((temp2 & 0x80) == 0x00): + temp3 |= 0x04 #red + elif ((temp1 & 0x80) == 0x00): + temp3 |= 0x00 #black + else: + temp3 |= 0x03 #white + temp1 = (temp1 << 1) & 0xFF + temp2 = (temp2 << 1) & 0xFF + self.send_data(temp3) + j += 1 + + self.send_command(0x04) # POWER ON + self.ReadBusy() + self.send_command(0x12) # display refresh + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width / 8 * self.height)): + self.send_data(0x33) + self.send_data(0x33) + self.send_data(0x33) + self.send_data(0x33) + + self.send_command(0x04) # POWER ON + self.ReadBusy() + self.send_command(0x12) # display refresh + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0xA5) # check code + + epdconfig.module_exit() +### END OF FILE ### + diff --git a/modules/drivers/epd_7_in_5.py b/modules/drivers/epd_7_in_5.py new file mode 100644 index 0000000..80f488d --- /dev/null +++ b/modules/drivers/epd_7_in_5.py @@ -0,0 +1,202 @@ +# ***************************************************************************** +# * | File : epd7in5.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 = 640 +EPD_HEIGHT = 384 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logging.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x37) + self.send_data(0x00) + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0xCF) + self.send_data(0x08) + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0xc7) + self.send_data(0xcc) + self.send_data(0x28) + + self.send_command(0x04) # POWER_ON + self.ReadBusy() + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3c) + + self.send_command(0x41) # TEMPERATURE_CALIBRATION + self.send_data(0x00) + + self.send_command(0x50) # VCOM_AND_DATA_INTERVAL_SETTING + self.send_data(0x77) + + self.send_command(0x60) # TCON_SETTING + self.send_data(0x22) + + self.send_command(0x61) # TCON_RESOLUTION + self.send_data(EPD_WIDTH >> 8) #source 640 + self.send_data(EPD_WIDTH & 0xff) + self.send_data(EPD_HEIGHT >> 8) #gate 384 + self.send_data(EPD_HEIGHT & 0xff) + + self.send_command(0x82) # VCM_DC_SETTING + self.send_data(0x1E) # decide by LUT file + + self.send_command(0xe5) # FLASH MODE + self.send_data(0x03) + + # EPD hardware init end + return 0 + + def getbuffer(self, image): + logging.debug("1234") + buf = [0x00] * int(self.width * self.height / 4) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + logging.debug('imwidth = %d imheight = %d ',imwidth, imheight) + if(imwidth == self.width and imheight == self.height): + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] < 64: # black + buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2)) + elif pixels[x, y] < 192: # convert gray to red + buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2)) + buf[int((x + y * self.width) / 4)] |= 0x40 >> (x % 4 * 2) + else: # white + buf[int((x + y * self.width) / 4)] |= 0xC0 >> (x % 4 * 2) + elif(imwidth == self.height and imheight == self.width): + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] < 64: # black + buf[int((newx + newy*self.width) / 4)] &= ~(0xC0 >> (y % 4 * 2)) + elif pixels[x, y] < 192: # convert gray to red + buf[int((newx + newy*self.width) / 4)] &= ~(0xC0 >> (y % 4 * 2)) + buf[int((newx + newy*self.width) / 4)] |= 0x40 >> (y % 4 * 2) + else: # white + buf[int((newx + newy*self.width) / 4)] |= 0xC0 >> (y % 4 * 2) + return buf + + def display(self, image): + self.send_command(0x10) + for i in range(0, int(self.width / 4 * self.height)): + temp1 = image[i] + j = 0 + while (j < 4): + if ((temp1 & 0xC0) == 0xC0): + temp2 = 0x03 + elif ((temp1 & 0xC0) == 0x00): + temp2 = 0x00 + else: + temp2 = 0x04 + temp2 = (temp2 << 4) & 0xFF + temp1 = (temp1 << 2) & 0xFF + j += 1 + if((temp1 & 0xC0) == 0xC0): + temp2 |= 0x03 + elif ((temp1 & 0xC0) == 0x00): + temp2 |= 0x00 + else: + temp2 |= 0x04 + temp1 = (temp1 << 2) & 0xFF + self.send_data(temp2) + j += 1 + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width / 4 * self.height)): + for j in range(0, 4): + self.send_data(0x33) + + self.send_command(0x12) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.module_exit() +### END OF FILE ### + diff --git a/modules/drivers/epd_7_in_5_colour.py b/modules/drivers/epd_7_in_5_colour.py new file mode 100644 index 0000000..7ec3959 --- /dev/null +++ b/modules/drivers/epd_7_in_5_colour.py @@ -0,0 +1,201 @@ +# ***************************************************************************** +# * | File : epd7in5bc.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 = 640 +EPD_HEIGHT = 384 + +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 + + # Hardware reset + def reset(self): + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + epdconfig.digital_write(self.reset_pin, 0) + epdconfig.delay_ms(10) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logging.debug("e-Paper busy") + while(epdconfig.digital_read(self.busy_pin) == 0): # 0: idle, 1: busy + epdconfig.delay_ms(100) + logging.debug("e-Paper busy release") + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x01) # POWER_SETTING + self.send_data(0x37) + self.send_data(0x00) + + self.send_command(0x00) # PANEL_SETTING + self.send_data(0xCF) + self.send_data(0x08) + + self.send_command(0x30) # PLL_CONTROL + self.send_data(0x3A) # PLL: 0-15:0x3C, 15+:0x3A + + self.send_command(0x82) # VCM_DC_SETTING + self.send_data(0x28) #all temperature range + + self.send_command(0x06) # BOOSTER_SOFT_START + self.send_data(0xc7) + self.send_data(0xcc) + self.send_data(0x15) + + self.send_command(0x50) # VCOM AND DATA INTERVAL SETTING + self.send_data(0x77) + + self.send_command(0x60) # TCON_SETTING + self.send_data(0x22) + + self.send_command(0x65) # FLASH CONTROL + self.send_data(0x00) + + self.send_command(0x61) # TCON_RESOLUTION + self.send_data(self.width >> 8) # source 640 + self.send_data(self.width & 0xff) + self.send_data(self.height >> 8) # gate 384 + self.send_data(self.height & 0xff) + + self.send_command(0xe5) # FLASH MODE + self.send_data(0x03) + + return 0 + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + logging.debug('imwidth = %d imheight = %d ',imwidth, imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width / 8 * self.height)): + temp1 = imageblack[i] + temp2 = imagered[i] + j = 0 + while (j < 8): + if ((temp2 & 0x80) == 0x00): + temp3 = 0x04 #red + elif ((temp1 & 0x80) == 0x00): + temp3 = 0x00 #black + else: + temp3 = 0x03 #white + + temp3 = (temp3 << 4) & 0xFF + temp1 = (temp1 << 1) & 0xFF + temp2 = (temp2 << 1) & 0xFF + j += 1 + if((temp2 & 0x80) == 0x00): + temp3 |= 0x04 #red + elif ((temp1 & 0x80) == 0x00): + temp3 |= 0x00 #black + else: + temp3 |= 0x03 #white + temp1 = (temp1 << 1) & 0xFF + temp2 = (temp2 << 1) & 0xFF + self.send_data(temp3) + j += 1 + + self.send_command(0x04) # POWER ON + self.ReadBusy() + self.send_command(0x12) # display refresh + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width / 8 * self.height)): + self.send_data(0x33) + self.send_data(0x33) + self.send_data(0x33) + self.send_data(0x33) + + self.send_command(0x04) # POWER ON + self.ReadBusy() + self.send_command(0x12) # display refresh + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.module_exit() +### END OF FILE ### + diff --git a/modules/drivers/epd_7_in_5_v2.py b/modules/drivers/epd_7_in_5_v2.py new file mode 100644 index 0000000..ceff4ce --- /dev/null +++ b/modules/drivers/epd_7_in_5_v2.py @@ -0,0 +1,170 @@ +# ***************************************************************************** +# * | File : epd7in5.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 = 800 +EPD_HEIGHT = 480 + +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 + + # 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(2) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logging.debug("e-Paper busy") + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + while(busy == 0): + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(200) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + # EPD hardware init start + self.reset() + + self.send_command(0x01) #POWER SETTING + self.send_data(0x07) + self.send_data(0x07) #VGH=20V,VGL=-20V + self.send_data(0x3f) #VDH=15V + self.send_data(0x3f) #VDL=-15V + + self.send_command(0x04) #POWER ON + epdconfig.delay_ms(100) + self.ReadBusy() + + self.send_command(0X00) #PANNEL SETTING + self.send_data(0x1F) #KW-3f KWR-2F BWROTP 0f BWOTP 1f + + self.send_command(0x61) #tres + self.send_data(0x03) #source 800 + self.send_data(0x20) + self.send_data(0x01) #gate 480 + self.send_data(0xE0) + + self.send_command(0X15) + self.send_data(0x00) + + self.send_command(0X50) #VCOM AND DATA INTERVAL SETTING + self.send_data(0x10) + self.send_data(0x07) + + self.send_command(0X60) #TCON SETTING + self.send_data(0x22) + + # EPD hardware init end + return 0 + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + # logging.debug("imwidth = %d, imheight = %d",imwidth,imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, image): + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(~image[i]); + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.module_exit() +### END OF FILE ### + diff --git a/modules/drivers/epd_7_in_5_v2_colour.py b/modules/drivers/epd_7_in_5_v2_colour.py new file mode 100644 index 0000000..9d631d3 --- /dev/null +++ b/modules/drivers/epd_7_in_5_v2_colour.py @@ -0,0 +1,173 @@ +# ***************************************************************************** +# * | File : epd7in5bc.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 = 800 +EPD_HEIGHT = 480 + +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 + + # 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(4) + epdconfig.digital_write(self.reset_pin, 1) + epdconfig.delay_ms(200) + + def send_command(self, command): + epdconfig.digital_write(self.dc_pin, 0) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([command]) + epdconfig.digital_write(self.cs_pin, 1) + + def send_data(self, data): + epdconfig.digital_write(self.dc_pin, 1) + epdconfig.digital_write(self.cs_pin, 0) + epdconfig.spi_writebyte([data]) + epdconfig.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + logging.debug("e-Paper busy") + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + while(busy == 0): + self.send_command(0x71) + busy = epdconfig.digital_read(self.busy_pin) + epdconfig.delay_ms(200) + + def init(self): + if (epdconfig.module_init() != 0): + return -1 + + self.reset() + + self.send_command(0x01); #POWER SETTING + self.send_data(0x07); + self.send_data(0x07); #VGH=20V,VGL=-20V + self.send_data(0x3f); #VDH=15V + self.send_data(0x3f); #VDL=-15V + + self.send_command(0x04); #POWER ON + epdconfig.delay_ms(100); + self.ReadBusy(); + + self.send_command(0X00); #PANNEL SETTING + self.send_data(0x0F); #KW-3f KWR-2F BWROTP 0f BWOTP 1f + + self.send_command(0x61); #tres + self.send_data(0x03); #source 800 + self.send_data(0x20); + self.send_data(0x01); #gate 480 + self.send_data(0xE0); + + self.send_command(0X15); + self.send_data(0x00); + + self.send_command(0X50); #VCOM AND DATA INTERVAL SETTING + self.send_data(0x11); + self.send_data(0x07); + + self.send_command(0X60); #TCON SETTING + self.send_data(0x22); + + return 0 + + def getbuffer(self, image): + # logging.debug("bufsiz = ",int(self.width/8) * self.height) + buf = [0xFF] * (int(self.width/8) * self.height) + image_monocolor = image.convert('1') + imwidth, imheight = image_monocolor.size + pixels = image_monocolor.load() + logging.debug('imwidth = %d imheight = %d ',imwidth, imheight) + if(imwidth == self.width and imheight == self.height): + logging.debug("Horizontal") + for y in range(imheight): + for x in range(imwidth): + # Set the bits for the column of pixels at the current position. + if pixels[x, y] == 0: + buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8)) + elif(imwidth == self.height and imheight == self.width): + logging.debug("Vertical") + for y in range(imheight): + for x in range(imwidth): + newx = y + newy = self.height - x - 1 + if pixels[x, y] == 0: + buf[int((newx + newy*self.width) / 8)] &= ~(0x80 >> (y % 8)) + return buf + + def display(self, imageblack, imagered): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]); + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(~imagered[i]); + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def Clear(self): + self.send_command(0x10) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xff) + + self.send_command(0x13) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00) + + self.send_command(0x12) + epdconfig.delay_ms(100) + self.ReadBusy() + + def sleep(self): + self.send_command(0x02) # POWER_OFF + self.ReadBusy() + + self.send_command(0x07) # DEEP_SLEEP + self.send_data(0XA5) + + epdconfig.module_exit() +### END OF FILE ### + diff --git a/modules/drivers/epdconfig.py b/modules/drivers/epdconfig.py new file mode 100644 index 0000000..7f50759 --- /dev/null +++ b/modules/drivers/epdconfig.py @@ -0,0 +1,154 @@ +# /***************************************************************************** +# * | File : epdconfig.py +# * | Author : Waveshare team +# * | Function : Hardware underlying interface +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2019-06-21 +# * | Info : +# ****************************************************************************** +# 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 os +import logging +import sys +import time + + +class RaspberryPi: + # Pin definition + RST_PIN = 17 + DC_PIN = 25 + CS_PIN = 8 + BUSY_PIN = 24 + + def __init__(self): + import spidev + import RPi.GPIO + + self.GPIO = RPi.GPIO + + # SPI device, bus = 0, device = 0 + self.SPI = spidev.SpiDev(0, 0) + + def digital_write(self, pin, value): + self.GPIO.output(pin, value) + + def digital_read(self, pin): + return self.GPIO.input(pin) + + def delay_ms(self, delaytime): + time.sleep(delaytime / 1000.0) + + def spi_writebyte(self, data): + self.SPI.writebytes(data) + + def module_init(self): + self.GPIO.setmode(self.GPIO.BCM) + self.GPIO.setwarnings(False) + self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) + self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) + self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) + self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) + self.SPI.max_speed_hz = 4000000 + self.SPI.mode = 0b00 + return 0 + + def module_exit(self): + logging.debug("spi end") + #self.SPI.close() #removed as it causes some problems + + logging.debug("close 5V, Module enters 0 power consumption ...") + self.GPIO.output(self.RST_PIN, 0) + self.GPIO.output(self.DC_PIN, 0) + + self.GPIO.cleanup() + + +class JetsonNano: + # Pin definition + RST_PIN = 17 + DC_PIN = 25 + CS_PIN = 8 + BUSY_PIN = 24 + + def __init__(self): + import ctypes + find_dirs = [ + os.path.dirname(os.path.realpath(__file__)), + '/usr/local/lib', + '/usr/lib', + ] + self.SPI = None + for find_dir in find_dirs: + so_filename = os.path.join(find_dir, 'sysfs_software_spi.so') + if os.path.exists(so_filename): + self.SPI = ctypes.cdll.LoadLibrary(so_filename) + break + if self.SPI is None: + raise RuntimeError('Cannot find sysfs_software_spi.so') + + import Jetson.GPIO + self.GPIO = Jetson.GPIO + + def digital_write(self, pin, value): + self.GPIO.output(pin, value) + + def digital_read(self, pin): + return self.GPIO.input(self.BUSY_PIN) + + def delay_ms(self, delaytime): + time.sleep(delaytime / 1000.0) + + def spi_writebyte(self, data): + self.SPI.SYSFS_software_spi_transfer(data[0]) + + def module_init(self): + self.GPIO.setmode(self.GPIO.BCM) + self.GPIO.setwarnings(False) + self.GPIO.setup(self.RST_PIN, self.GPIO.OUT) + self.GPIO.setup(self.DC_PIN, self.GPIO.OUT) + self.GPIO.setup(self.CS_PIN, self.GPIO.OUT) + self.GPIO.setup(self.BUSY_PIN, self.GPIO.IN) + self.SPI.SYSFS_software_spi_begin() + return 0 + + def module_exit(self): + logging.debug("spi end") + self.SPI.SYSFS_software_spi_end() + + logging.debug("close 5V, Module enters 0 power consumption ...") + self.GPIO.output(self.RST_PIN, 0) + self.GPIO.output(self.DC_PIN, 0) + + self.GPIO.cleanup() + + +if os.path.exists('/sys/bus/platform/drivers/gpiomem-bcm2835'): + implementation = RaspberryPi() +else: + implementation = JetsonNano() + +for func in [x for x in dir(implementation) if not x.startswith('_')]: + setattr(sys.modules[__name__], func, getattr(implementation, func)) + + +### END OF FILE ### diff --git a/modules/drivers/init.py b/modules/drivers/init.py new file mode 100644 index 0000000..9b2c532 --- /dev/null +++ b/modules/drivers/init.py @@ -0,0 +1 @@ +#nothing in here. What did you expect? \ No newline at end of file From 6079e1ff315dc75787a3c76aad47b3216ef9ab46 Mon Sep 17 00:00:00 2001 From: Ace Date: Fri, 17 Jan 2020 17:14:03 +0100 Subject: [PATCH 04/15] Added new options --- settings/settings.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/settings/settings.py b/settings/settings.py index 3a8b394..6a88dbe 100644 --- a/settings/settings.py +++ b/settings/settings.py @@ -6,8 +6,7 @@ update_interval = "60" # "15" # "30" # "60" api_key = "" # Your openweathermap API-KEY -> "api-key" location = "Stuttgart, DE" # "City name, Country code" week_starts_on = "Monday" # "Monday" # "Sunday" -calibration_hours = [0,12,18] # Do not change unless required -display_type = "colour" # "colour" # "black_and_white" +model = "epd_7_in_5_v2_colour" # Choose the E-Paper model (see below) language = "en" # "en" # "de" # "fr" # "jp" etc. units = "metric" # "metric" # "imperial" hours = "24" # "24" # "12" @@ -26,3 +25,12 @@ bottom_section = "RSS" # "RSS" # URLs should have this sign (") on both side -> "url1" # If more than one URL is used, separate each one with a comma -> "url1", "url2" +"""Supported E-Paper models""" +# epd_7_in_5_v2_colour # 7.5" high-res black-white-red/yellow +# epd_7_in_5_v2 # 7.5" high-res black-white +# epd_7_in_5_colour # 7.5" black-white-red/yellow +# epd_7_in_5 # 7.5" black-white +# epd_5_in_83_colour # 5.83" black-white-red/yellow +# epd_5_in_83 # 5.83" black-white +# epd_4_in_2_colour # 4.2" black-white-red/yellow +# epd_4_in_2 # 4.2" black-white From 63663ee56733f40319d25dc43589e7e3db5a07f1 Mon Sep 17 00:00:00 2001 From: Ace Date: Fri, 17 Jan 2020 17:17:41 +0100 Subject: [PATCH 05/15] Update configuration.py --- settings/configuration.py | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/settings/configuration.py b/settings/configuration.py index 895cc7d..006cfee 100644 --- a/settings/configuration.py +++ b/settings/configuration.py @@ -9,18 +9,27 @@ Copyright by aceisace """ from PIL import Image, ImageDraw, ImageFont, ImageColor import numpy +import arrow from urllib.request import urlopen -from settings import language +from settings import * from pytz import timezone import os from glob import glob +import importlib """Set the image background colour and text colour""" background_colour = 'white' text_colour = 'black' -"""Set the display height and width (in pixels)""" -display_height, display_width = 640, 384 +"""Set some display parameters""" +e_paper = importlib.import_module('drivers.'+model) +display_height, display_width = e_paper.EPD_WIDTH, e_paper.EPD_HEIGHT + +"""Check if the display supports 3 colours""" +if 'colour' in model: + three_colour_support = True +else: + three_colour_support = False """Create 3 sections of the display, based on percentage""" top_section_width = middle_section_width = bottom_section_width = display_width @@ -189,3 +198,21 @@ def image_cleanup(): for temp_files in glob(image_path+'*'): os.remove(temp_files) print('Done') + +def split_colours(image): + if three_colour_support == True: + """Split image into two, one for red pixels, the other for black pixels""" + buffer = numpy.array(image.convert('RGB')) + red, green = buffer[:, :, 0], buffer[:, :, 1] + buffer_red, buffer_black = numpy.array(image), numpy.array(image) + + buffer_red[numpy.logical_and(red >= 200, green <= 90)] = [0,0,0] #red->black + red1 = buffer_red[:,:,0] + buffer_red[red1 != 0] = [255,255,255] #white + red_im = Image.fromarray(buffer_red).convert('1',dither=True).rotate(270,expand=True) + + buffer_black[numpy.logical_and(red <= 180, red == green)] = [0,0,0] #black + red2 = buffer_black[:,:,0] + buffer_black[red2 != 0] = [255,255,255] # white + black_im = Image.fromarray(buffer_black).convert('1', dither=True).rotate(270,expand=True) + return black_im, red_im From 7945f1b2acd94d1f77fc6e3d09ff7b8cfa528df1 Mon Sep 17 00:00:00 2001 From: Ace Date: Sat, 18 Jan 2020 14:57:15 +0100 Subject: [PATCH 06/15] Update Installer.sh --- Installer.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Installer.sh b/Installer.sh index 3ee5004..6cfc1a5 100644 --- a/Installer.sh +++ b/Installer.sh @@ -180,5 +180,5 @@ EOF fi echo -e "\e[1;36m"You can test if the programm works by running:"\e[0m" - echo -e "\e[1;36m"python3 /home/"$USER"/Inky-Calendar/Calendar/inkycal.py"\e[0m" + echo -e "\e[1;36m"python3 /home/"$USER"/Inky-Calendar/modules/inkycal.py"\e[0m" fi From bf6addecbbdb2d4cdb54a4f068ffebc1f3c83cc4 Mon Sep 17 00:00:00 2001 From: Ace Date: Sat, 18 Jan 2020 16:02:49 +0100 Subject: [PATCH 07/15] Added option to choose E-Paper model --- settings/settings-UI.html | 84 ++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 19 deletions(-) diff --git a/settings/settings-UI.html b/settings/settings-UI.html index 0b62078..411ddcd 100644 --- a/settings/settings-UI.html +++ b/settings/settings-UI.html @@ -43,6 +43,10 @@ body{
+
+ + +
@@ -114,15 +118,39 @@ body{
- +
- - + +
- - + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
@@ -305,7 +333,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI