initial upload

redesign
Josef Dabrowski 7 years ago
commit 0059dda861

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 KiB

Binary file not shown.

Binary file not shown.

@ -0,0 +1,82 @@
from PIL import Image, ImageDraw, ImageFont, ImageOps
import re
import time
import widget
import argparse
try:
import epd7in5b
testMode = False
except:
testMode = True
parser = argparse.ArgumentParser(description="EinkPaper display")
parser.add_argument('-t', '--test', help='enable test mode', action='store_true')
args = parser.parse_args()
if args.test:
testMode = True
print("RUNNING IN TESTMODE: "+str(testMode))
if not testMode:
epd = epd7in5b.EPD()
epd.init()
width = 640
height = 384
cwidth = int(round(width/3))
cheight = int(round(height/3))
image_yellow = Image.new('1', (width, height), 255) # 255: clear the frame
draw_yellow = ImageDraw.Draw(image_yellow)
image_black = Image.new('1', (width, height), 255) # 255: clear the frame
draw_black = ImageDraw.Draw(image_black)
#str = """abcdefghijkmnop test test test hello Current temperature: 17C, Feels like: 20C Traffic is light. Should take 21 minutes to get to work. BREAKING NEWS: news headline goes here. To do: 1) Fix MHDoorLock 2) Work on waveshare epaper device"""
str1 = """Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."""
str2 = """Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."""
str3 = """But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?"""
strList = []
strList.append(str1)
strList.append(str2)
strList.append(str3)
def initWidgets():
widgetList = []
widgetList.append(widget.TextWidget(cwidth, cheight, 0, 0, 1, 2, str1))
widgetList.append(widget.TextWidget(cwidth, cheight, 1, 0, 2, 1, str2))
#widgetList.append(widget.ImageWidget(cwidth, cheight, 2, 1, 1, 1, "mono", "faces.jpg"))
widgetList.append(widget.ImageWidget(cwidth, cheight, 1, 1, 2, 2, "mono", "img1.jpg"))
widgetList.append(widget.ImageWidget(cwidth, cheight, 0, 2, 1, 1, "mono", "img1.jpg"))
return widgetList
def drawWidget(w):
coordX = w.cellX*cwidth
coordY = w.cellY*cheight
image_black.paste(w.image_black, (coordX, coordY))
image_yellow.paste(w.image_yellow, (coordX, coordY))
def render(index):
for widg in widgetList:
drawWidget(widg)
if not testMode:
print("pushing to display frame")
epd.display_frame(epd.get_frame_buffer(image_black),epd.get_frame_buffer(image_yellow))
print("done")
else:
print("saving canvas images")
image_black.save("imgBlack.bmp")
image_yellow.save("imgYellow.bmp")
print("done")
widgetList = initWidgets()
i = 0
while(True):
render(i)
i = (i + 1)%3
time.sleep(10)

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

@ -0,0 +1,204 @@
##
# @filename : epd7in5.py
# @brief : Implements for Dual-color e-paper library
# @author : Yehui from Waveshare
#
# Copyright (C) Waveshare July 10 2017
#
# 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 epdif
import Image
import RPi.GPIO as GPIO
# Display resolution
EPD_WIDTH = 640
EPD_HEIGHT = 384
# 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 = epdif.RST_PIN
self.dc_pin = epdif.DC_PIN
self.busy_pin = epdif.BUSY_PIN
self.width = EPD_WIDTH
self.height = EPD_HEIGHT
def digital_write(self, pin, value):
epdif.epd_digital_write(pin, value)
def digital_read(self, pin):
return epdif.epd_digital_read(pin)
def delay_ms(self, delaytime):
epdif.epd_delay_ms(delaytime)
def send_command(self, command):
self.digital_write(self.dc_pin, GPIO.LOW)
# the parameter type is list but not int
# so use [command] instead of command
epdif.spi_transfer([command])
def send_data(self, data):
self.digital_write(self.dc_pin, GPIO.HIGH)
# the parameter type is list but not int
# so use [data] instead of data
epdif.spi_transfer([data])
def init(self):
if (epdif.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 get_frame_buffer(self, image):
buf = [0xFF] * (self.width * self.height / 8)
# Set buffer to value of Python Imaging Library image.
# Image must be in mode L.
image_grayscale = image.convert('1')
imwidth, imheight = image_grayscale.size
if imwidth != self.width or imheight != self.height:
raise ValueError('Image must be same dimensions as display \
({0}x{1}).' .format(self.width, self.height))
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:
buf[(x + y * self.width) / 8] &= ~(0x80 >> (x % 8))
return buf
def display_frame(self, frame_buffer_black, frame_buffer_red):
self.send_command(DATA_START_TRANSMISSION_1)
for i in range(0, self.width / 8 * self.height):
temp1 = frame_buffer_black[i]
temp2 = frame_buffer_red[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(DISPLAY_REFRESH)
self.delay_ms(100)
self.wait_until_idle()
def sleep(self):
self.send_command(POWER_OFF)
self.wait_until_idle()
self.send_command(DEEP_SLEEP)
self.send_data(0xa5)
### END OF FILE ###

Binary file not shown.

@ -0,0 +1,63 @@
##
# @filename : epdif.py
# @brief : EPD hardware interface implements (GPIO, SPI)
# @author : Yehui from Waveshare
#
# Copyright (C) Waveshare July 10 2017
#
# 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 spidev
import RPi.GPIO as GPIO
import time
# Pin definition
RST_PIN = 17
DC_PIN = 25
CS_PIN = 8
BUSY_PIN = 24
# SPI device, bus = 0, device = 0
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):
time.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 = 2000000
SPI.mode = 0b00
return 0;
### END OF FILE ###

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

@ -0,0 +1,26 @@
import epd7in5b
from PIL import Image, ImageDraw, ImageFont, ImageOps
import re
from renderer import Rend
WIDTH = 640
HEIGHT = 384
def main():
epd = epd7in5b.EPD()
epd.init()
rend = Rend()
rend.render()
print("start display frame")
epd.display_frame(epd.get_frame_buffer(rend.image_black),epd.get_frame_buffer(rend.image_yellow))
print("finish display frame")
#display images
#frame_black = epd.get_frame_buffer(Image.open('black.bmp'))
#frame_yellow = epd.get_frame_buffer(Image.open('red2.bmp'))
#epd.display_frame(frame_black, frame_yellow)
if __name__ == '__main__':
main()

@ -0,0 +1,7 @@
Run EinkPaper.py to start the program. To run in test mode, add argument -t or --test.
Test mode will save generated images to the working directory rather than export them to an external display.
Test mode will auto run if no external display package is detected.
EinkPaper.py will construct widget classes from widgets.py. Widgets are constructed in the initWidgets() function in EinkPaper.py.
The image widget is currently set to resize (to fit the whole image on the widget w/ no cropping), then to either monochrome or greyscale dither the image as specified in initWidgets(). The zoom function is currently broken and to be fixed.

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

@ -0,0 +1,164 @@
from __future__ import division
from PIL import Image, ImageDraw, ImageFont, ImageOps
import re
def textBox(text, font, position, limits, colour, draw_black):
str = ""
limit = [0, 0]
limit[0] = limits[0] - position[0]
limit[1] = limits[1] - position[1]
words = text.split(" ")
i = 0 #index of first word
sizex, sizey = draw_black.multiline_textsize(str, font) #size of the textbox
#while more words to be added to string AND string textbox height < height limit
for word in words:
sizex, sizey = draw_black.multiline_textsize(str + word, font)
sizex2, sizey2 = draw_black.multiline_textsize(str, font)
if sizex < limit[0]: #if width of gen textbox < width limit
str += word + " "
else:
sizex, sizey = draw_black.multiline_textsize(str + "\n" + word, font)
if(sizey > limit[1]):
break
else:
str = str.rstrip(" ")
str += "\n" + word + " "
draw_black.text(position, str, font = font, fill = 0)
def putImage(imagepath, maskpath, position, colour):
img = Image.open(imagepath)
mask = Image.open(maskpath).convert(mode='1')
if colour is "black":
image_black.paste(img, position, mask)
if colour is "yellow":
image_yellow.paste(img, position, mask)
black_invert = ImageOps.invert(image_black.convert('L'))
draw_yellow.bitmap((0,0), black_invert, 255)
class TextWidget():
#this example widget will have a set size: 3x1 cells (one column)
#an instance of this widget class can be created and pasted to canvas
def __init__(self, cwidth, cheight, x = 0, y = 0, width = 1, height = 1, str = ""):
self.width = width
self.height = height
self.wt = cwidth*width
self.ht = cheight*height
self.cellX = x
self.cellY = y
self.image_yellow = Image.new('1', (self.wt, self.ht), 255) #255: clear the frame
self.draw_yellow = ImageDraw.Draw(self.image_yellow)
self.image_black = Image.new('1', (self.wt, self.ht), 255) #255: clear the frame
self.draw_black = ImageDraw.Draw(self.image_black)
self.font = ImageFont.truetype('DejaVuSans.ttf', 16)
self.updateWidget(str)
def drawText(self, str):
#define parameters for text placement
position = (0,0)
limit = (self.wt, self.ht)
#write the string in the widget
textBox(str, self.font, position, limit, "black", self.draw_black)
def saveImages(self):
print("saving widget images")
self.image_black.save("imgBlackWidget.bmp")
self.image_yellow.save("imgYellowWidget.bmp")
def updateWidget(self, str):
self.draw_black.rectangle((0, 0, self.wt, self.ht), fill=255)
self.draw_yellow.rectangle((0, 0, self.wt, self.ht), fill=255)
self.drawText(str)
self.saveImages()
class ImageWidget():
#this widget displays an image
#needs to recieve an image file as input. Then resize and convert to monochrome.
def __init__(self, cwidth, cheight, x = 0, y = 0, width = 1, height = 1, mode = "mono", img = ""):
self.width = width
self.height = height
self.wt = cwidth*width
self.ht = cheight*height
self.cellX = x
self.cellY = y
self.image_yellow = Image.new('1', (self.wt, self.ht), 255) #255: clear the frame
self.draw_yellow = ImageDraw.Draw(self.image_yellow)
self.image_black = Image.new('1', (self.wt, self.ht), 255) #255: clear the frame
self.draw_black = ImageDraw.Draw(self.image_black)
self.mode = mode
self.updateWidget(Image.open(img))
def resizeImg(self, img):
#scale image down until whole image fits inside the cell
iRatio = img.size[0]/img.size[1] #image ratio
cRatio = self.wt/self.ht #canvas ratio
if cRatio > iRatio: #height is limiting dimension
fixedImg = img.resize((int(self.ht*iRatio), self.ht))
print(" limited by height")
return fixedImg
if cRatio < iRatio: #width is limiting dimension
fixedImg = img.resize((self.wt, int(self.wt/iRatio)))
print(" limited by width")
return fixedImg
fixedImg = img
print(" ratios are equal, not limited")
return fixedImg
def zoomImg(self, img):
#enlarge the image and crop to fill the widget canvas
iRatio = img.size[0]/img.size[1] #image ratio
cRatio = self.wt/self.ht #canvas ratio
if cRatio > iRatio:
print("new sizes as calcd in resize: "+str(self.wt)+" "+str(self.wt/iRatio))
fixedImg = img.resize((self.wt, int(self.wt/iRatio))) #width = wt, calc new height w/ ratio
offset = 0.5*(fixedImg.size[1] - self.ht) #centre by height
cropped = fixedImg.crop((0, offset, self.wt, offset+self.ht))
cropped.load()
print("zoomed to fill height")
return cropped
if cRatio < iRatio:
fixedImg = img.resize((int(self.ht*iRatio), self.ht)) #height = ht, calc new width w/ ratio
offset = 0.5*(fixedImg.size[0] - self.wt) #centre by width
cropped = fixedImg.crop((offset, 0, offset+self.wt, self.ht))
cropped.load()
print("zoomed to fill width")
return cropped
print("not zoomed")
return img.resize((self.wt, self.ht))
def threshold(self, val):
if(val>100):
return 1
else:
return 0
def bwImg(self, img):
if self.mode is "mono":
return img.convert('L').point(self.threshold, '1')
if self.mode is "dither":
return img.convert(mode='1')
def pasteImg(self, img):
iRatio = img.size[0]/img.size[1] #image ratio
cRatio = self.wt/self.ht #canvas ratio
if cRatio > iRatio: #needs to be centred horizontally
offset = int(0.5*(self.wt - img.size[0]))
self.image_black.paste(img, (offset, 0))
if cRatio < iRatio: #needs to be centred vertically
offset = int(0.5*(self.ht - img.size[1]))
self.image_black.paste(img, (0, offset))
def saveImages(self):
print("saving widget images")
self.image_black.save("imgBlackWidget.bmp")
self.image_yellow.save("imgYellowWidget.bmp")
def updateWidget(self, img):
rsImg = self.resizeImg(img)
rsImg.save("rsImg.bmp")
newImg = self.bwImg(rsImg)
newImg.save("bwImg.bmp")
print(" Dimensions are now: "+str(newImg.size[0])+"x"+str(newImg.size[1]))
print(" Dimensions of widget canvas: "+str(self.wt)+"x"+str(self.ht))
self.pasteImg(newImg)
self.saveImages()

Binary file not shown.
Loading…
Cancel
Save