Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic support for alternate screen (epd4in2b) #3

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -255,4 +255,5 @@ $RECYCLE.BIN/
*.lnk


# End of https://www.gitignore.io/api/node,macos,python,windows
# End of https://www.gitignore.io/api/node,macos,python,windows
black.png
Binary file removed black.png
Binary file not shown.
10 changes: 6 additions & 4 deletions config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
var config = {
"magicmirror_port": 8080, // magic mirror port
"display_width": 640, // eink display height in px
"display_height": 384, // eink display width in px
"display_width": 400, // eink display height in px
"display_height": 300, // eink display width in px
"display_type": "epd4in2b", // which eink display, currently supported: epd4in2b, epd7in5
"display_invert": true,
"wait_to_load": 18, // wait seconds to load the site and display all data
"refresh_interval": "0 */10 * * * *", // update eink every 10 minutes
"refresh_interval": "0 */1 * * * *", // update eink every 10 minutes
// https://github.com/kelektiv/node-cron#cron-ranges
"timezone": "Europe / Berlin" // https://www.inmotionhosting.com/support/website/general-server-setup/tz-ref-table
};

/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {module.exports = config;}
if (typeof module !== "undefined") {module.exports = config;}
169 changes: 169 additions & 0 deletions ePaperPython/epd4in2b.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
##
# @filename : epd4in2b.py
# @brief : Implements for Dual-color e-paper library
# @author : Yehui from Waveshare
#
# Copyright (C) Waveshare August 15 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 RPi.GPIO as GPIO

import epdif

EPD_WIDTH = 400
EPD_HEIGHT = 300

# EPD4IN2B 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
DATA_START_TRANSMISSION_2 = 0x13
VCOM_LUT = 0x20
W2W_LUT = 0x21
B2W_LUT = 0x22
W2B_LUT = 0x23
B2B_LUT = 0x24
PLL_CONTROL = 0x30
TEMPERATURE_SENSOR_CALIBRATION = 0x40
TEMPERATURE_SENSOR_SELECTION = 0x41
TEMPERATURE_SENSOR_WRITE = 0x42
TEMPERATURE_SENSOR_READ = 0x43
VCOM_AND_DATA_INTERVAL_SETTING = 0x50
LOW_POWER_DETECTION = 0x51
TCON_SETTING = 0x60
RESOLUTION_SETTING = 0x61
GSST_SETTING = 0x65
GET_STATUS = 0x71
AUTO_MEASURE_VCOM = 0x80
VCOM_VALUE = 0x81
VCM_DC_SETTING = 0x82
PARTIAL_WINDOW = 0x90
PARTIAL_IN = 0x91
PARTIAL_OUT = 0x92
PROGRAM_MODE = 0xA0
ACTIVE_PROGRAM = 0xA1
READ_OTP_DATA = 0xA2
POWER_SAVING = 0xE3


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(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(POWER_ON)
self.wait_until_idle()
self.send_command(PANEL_SETTING)
self.send_data(0x0F) # LUT from OTP

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] * int(self.width * self.height / 8)
# Set buffer to value of Python Imaging Library image.
# Image must be in mode 1.
image_monocolor = image.convert('1')
imwidth, imheight = image_monocolor.size
if imwidth != self.width or imheight != self.height:
raise ValueError('Image must be same dimensions as display. Expected:'
'({0}x{1}); actual: ({2}x{3}).'.format(self.width, self.height, imwidth, imheight))

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_black, frame_buffer_red = None):
if frame_buffer_black is not None:
self.send_command(DATA_START_TRANSMISSION_1)
self.delay_ms(2)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(frame_buffer_black[i])
self.delay_ms(2)
if frame_buffer_red is not None:
self.send_command(DATA_START_TRANSMISSION_2)
self.delay_ms(2)
for i in range(0, int(self.width * self.height / 8)):
self.send_data(frame_buffer_red[i])
self.delay_ms(2)

self.send_command(DISPLAY_REFRESH)
self.wait_until_idle()

# after this, call epd.init() to awaken the module
def sleep(self):
self.send_command(VCOM_AND_DATA_INTERVAL_SETTING)
self.send_data(0xF7) # border floating
self.send_command(POWER_OFF)
self.wait_until_idle()
self.send_command(DEEP_SLEEP)
self.send_data(0xA5) # check code
80 changes: 33 additions & 47 deletions ePaperPython/main.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,45 @@
##
# @filename : main.cpp
# @brief : 7.5inch e-paper display demo
# @author : Yehui from Waveshare
#
# Copyright (C) Waveshare July 28 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 argparse

import epd7in5
import Image
import ImageDraw
import ImageFont
#import imagedata
import epd4in2b

EPD_WIDTH = 640
EPD_HEIGHT = 384
from PIL import Image
import PIL.ImageOps


def invert(image):
if image.mode == 'RGBA':
r,g,b,a = image.split()
rgb_image = Image.merge('RGB', (r,g,b))
inverted_image = PIL.ImageOps.invert(rgb_image)
r2,g2,b2 = inverted_image.split()
return Image.merge('RGBA', (r2,g2,b2,a))
else:
return PIL.ImageOps.invert(image)

def main():
epd = epd7in5.EPD()
parser = argparse.ArgumentParser()
parser.add_argument("screen")
parser.add_argument("--invert", help="invert screen", action="store_true")
args = parser.parse_args()
if args.screen == "epd7in5":
imagecount = 1
epd = epd7in5.EPD()
elif args.screen == "epd4in2b":
imagecount = 2
epd = epd4in2b.EPD()
else:
raise Exception("Unsupported screen, please submit a code patch")
epd.init()

# For simplicity, the arguments are explicit numerical coordinates
# image = Image.new('1', (EPD_WIDTH, EPD_HEIGHT), 1) # 1: clear the frame
# draw = ImageDraw.Draw(image)
# font = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeMonoBold.ttf', 24)
# draw.rectangle((0, 6, 640, 30), fill = 0)
# draw.text((200, 10), 'e-Paper demo', font = font, fill = 255)
# draw.rectangle((200, 80, 600, 280), fill = 0)
# draw.arc((240, 120, 580, 220), 0, 360, fill = 255)
# draw.rectangle((0, 80, 160, 280), fill = 255)
# draw.arc((40, 80, 180, 220), 0, 360, fill = 0)
# epd.display_frame(epd.get_frame_buffer(image))

image = Image.open('black.png')
epd.display_frame(epd.get_frame_buffer(image))
if args.invert:
image = invert(image)

# You can get frame buffer from an image or import the buffer directly:
#epd.display_frame(imagedata.MONOCOLOR_BITMAP)
if imagecount == 1:
epd.display_frame(epd.get_frame_buffer(image))
elif imagecount == 2:
epd.display_frame(epd.get_frame_buffer(image), epd.get_frame_buffer(image))

if __name__ == '__main__':
main()
6 changes: 5 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ const job = new CronJob({

await browser.close();

const childPython = spawn('python', ['./ePaperPython/main.py']);
pythonArgs = ['./ePaperPython/main.py', global.gConfig.display_type]
if (global.gConfig.display_invert) {
pythonArgs.push("--invert")
}
const childPython = spawn('python', pythonArgs);

childPython.stdout.on('data', (data) => {
debugChildP(data.toString());
Expand Down
Binary file removed white.png
Binary file not shown.