diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/README.md b/README.md index 9d15504..e6f6154 100644 --- a/README.md +++ b/README.md @@ -1,100 +1,10 @@ - +I've personalised the project for my own use. +I'm using the HD version of the screen, the existing code did not work for this. +I've therefore updated to use with the latest drivers -# Infowindow -Rapsberry pi powered e-ink display for displaying information in an always on state. There are several other iterations -of this project online, but they didnt do quite what I wanted them to. This is my version. Also keeping up my python -skills as they dont get used as much as they used to! +I've removed the weather functions as these were not useful to me. -The functionality is not meant to be an "end all solution for calendaring and Todo lists" The intent is to provide an -*always on* display to show me what is coming up next. I can then check in browser, phone, etc for details and updates -to the data. In your face reminder. -
- -## Features -* **Calendar** - * Google Calendar is the only calendar currently supported -* **Todo List** - * Todoist - * Teamwork.com -* **Weather** - * Open Weather Map current data only. Future plan for forecast data. - -## Installation -### Raspberry Pi setup -Activate SPI on your Raspberry Pi by using the `raspi-config` tool under Interface Options and reboot. - -### Get software -Clone this repo onto your raspberry pi. Does not really matter where it is, but good option is in the `pi` users home -directory: `/home/pi/InfoWindow` - -### Setup python modules -Run `pip install -r requirements.txt`. This should install all required modules. I stuck to basic standard modules for -ease of installation. - -## Configuration -You will need to configure a few things such as API Keys and location. Copy config.json-sample to config.json. Edit -config.json to add your api keys and other information. - -## Optional: Increase lifetime of your SD-Card -If you want to increase the lifetime of the SD-Card, add the following line to `/etc/fstab` and reboot: - -`tmpfs /tmp tmpfs defaults,noatime,nosuid,size=100m 0 0` - -With this line, the `/tmp` folder will be held in RAM and will not be written to the SD-Card. - -## Optional: Screen saver -Always displaying the same colors at the same spots might have some negative effect on your E-Ink screen. To remedy -this, there is a simple additional script, which displays all three colors on the whole screen: I recommend to let -this run once every night, i.e. at 1 minute past 5 with: -* Run `crontab -e` -* insert `1 5 * * * /usr/bin/python /home/pi/InfoWindow/screensaver.py > /dev/null 2>&1` - -### General -* rotation: 0 - This is the rotation of the display in degrees. Leave at zero if you use it as a desktop display. Change -to 180 if you have it mounted and hanging from a shelf. -* timeformat: 12h / 24h -* charset: utf-8 (or something else). I.e. to get äöü working, use latin1 - -### Todo (Module) -Todoist is the current active module in this code. It only requires `api_key`. Teamwork also requires a 'site' key. If -using google tasks, leave this as null `todo: null` -* api_key: Enter your todoist API key. - -### Weather (Module) -Open Weather Map is where the data is coming from in the default module. This requires a few keys. -* api_key: Get your api key from OWM website. -* city: Look at OWM docs to figure what your city name is. Mine is "Sacramento,US" -* units: This can either be `imperial` or `metric` - -### Google calendar and ToDo list (Modules) -To use the google APIs, you first have to login to the [google cloud console](https://console.cloud.google.com/apis/). -In the google cloud console, do the following things: -1) Create a project and give it a name, i.e. `infowindow` and switch to the context of this project if not already - active. -2) Create a [new oauth consent screen](https://console.cloud.google.com/apis/credentials/consent) (just enter a name - should be enough). -3) Create a [new oauth 2.0 client id](https://console.cloud.google.com/apis/credentials). Choosing type `other` should - work just fine. Finally, download the json file provided by the google cloud console and store it in the repo - directory (i.e. `/home/pi/InfoWindow/google_secret.json`) on the Raspberry Pi. - -#### Calendar -There are are additional sections in the config for this module: -* additional: A list of additional calendar names (summary) to fetch. To use i.e. birthdays, add "Contacts" (also if - you use google in german. -* ignored: A list of events to be removed from the calendar display. - -## Running -### First Run -You should run the script manually the first time so that Googles auth modules can run interactivly. Once that has -completed you will want to add this to CRON so it runs every few minutes automatically. - -### Cron Run (Normal use) -* Run `crontab -e` -* insert `*/6 * * * * /usr/bin/python /home/pi/InfoWindow/infowindow.py --cron` +I have added in a todo module to work with "Grocy" +I've left 3 columns each to be used with any todo or calendar app diff --git a/clearScreen.py b/clearScreen.py index af59ca7..67d9628 100644 --- a/clearScreen.py +++ b/clearScreen.py @@ -1,3 +1,24 @@ -from mod_infowindow import infowindow -iw = infowindow.InfoWindow() -iw.display() +#!/usr/bin/python +# -*- coding:utf-8 -*- +import sys +import os +picdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'pic') +libdir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'lib') +if os.path.exists(libdir): + sys.path.append(libdir) + +import logging +from driver import epd7in5b +import time +from PIL import Image,ImageDraw,ImageFont +import traceback + +logging.basicConfig(level=logging.DEBUG) + +logging.info("epd7in5b_HD Demo") + +epd = epd7in5b.EPD() + +logging.info("init and Clear") +epd.init() +epd.Clear() diff --git a/config.json-sample b/config.json-sample index 9362e48..65ce7fb 100644 --- a/config.json-sample +++ b/config.json-sample @@ -1,22 +1,22 @@ { "general": { - "rotation": 180, + "rotation": 0, "timeformat": "12h", - "charset": "latin1", + "charset": "utf-8", "cell_spacing": 2 }, "todo": { - "api_key": "1234" + "api_key": "" }, "calendar": { - "additional": ["Contacts", "Birthdays"], - "ignored": ["Buy ticket!"], - "today_text_color": "red", + "include": [""], + "ignored": [""], + "today_text_color": "black", "today_background_color": "white" }, - "weather": { - "api_key": "1234", - "city": "Sacramento,US", - "units": "imperial" + "grocy": { + "api_key": "", + "server": "", + "port": "" } } diff --git a/driver/epd7in5b-old.py b/driver/epd7in5b-old.py new file mode 100755 index 0000000..2791cf9 --- /dev/null +++ b/driver/epd7in5b-old.py @@ -0,0 +1,212 @@ +## + # @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 +from PIL import Image +import RPi.GPIO as GPIO +import logging + +# 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 + #logging.debug("DRIVER: (wait_until_idle)") + #self.delay_ms(100) + self.delay_ms(50) + + 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 = [0x00] * int(self.width * self.height / 4) + # Set buffer to value of Python Imaging Library image. + # Image must be in mode L. + image_grayscale = image.convert('L') + 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] < 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) + return buf + + def display_frame(self, frame_buffer): + self.send_command(DATA_START_TRANSMISSION_1) + logging.debug("DRIVER: ENTERING FOR LOOP") + 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 + 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 + logging.debug("SENDING DISPLAY_REFRESH COMMAND") + self.send_command(DISPLAY_REFRESH) + logging.debug("DELAY 100 MS") + self.delay_ms(100) + logging.debug("WAIT UNTIL IDLE") + 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 ### + diff --git a/driver/epd7in5b.py b/driver/epd7in5b.py index 2791cf9..5261770 100644 --- a/driver/epd7in5b.py +++ b/driver/epd7in5b.py @@ -1,212 +1,206 @@ -## - # @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 -from PIL import Image -import RPi.GPIO as GPIO -import logging - -# 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 - #logging.debug("DRIVER: (wait_until_idle)") - #self.delay_ms(100) - self.delay_ms(50) - - 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 = [0x00] * int(self.width * self.height / 4) - # Set buffer to value of Python Imaging Library image. - # Image must be in mode L. - image_grayscale = image.convert('L') - 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] < 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) - return buf - - def display_frame(self, frame_buffer): - self.send_command(DATA_START_TRANSMISSION_1) - logging.debug("DRIVER: ENTERING FOR LOOP") - 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 - 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 - logging.debug("SENDING DISPLAY_REFRESH COMMAND") - self.send_command(DISPLAY_REFRESH) - logging.debug("DELAY 100 MS") - self.delay_ms(100) - logging.debug("WAIT UNTIL IDLE") - 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 ### - +# ***************************************************************************** +# * | File : epd7in5bc_HD.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.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 = 880 +EPD_HEIGHT = 528 + +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") + busy = epdconfig.digital_read(self.busy_pin) + while(busy == 1): + 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(0x12); #SWRESET + self.ReadBusy(); #waiting for the electronic paper IC to release the idle signal + + self.send_command(0x46); # Auto Write RAM + self.send_data(0xF7); + self.ReadBusy(); #waiting for the electronic paper IC to release the idle signal + + self.send_command(0x47); # Auto Write RAM + self.send_data(0xF7); + self.ReadBusy(); #waiting for the electronic paper IC to release the idle signal + + self.send_command(0x0C); # Soft start setting + self.send_data(0xAE); + self.send_data(0xC7); + self.send_data(0xC3); + self.send_data(0xC0); + self.send_data(0x40); + + self.send_command(0x01); # Set MUX as 527 + self.send_data(0xAF); + self.send_data(0x02); + self.send_data(0x01); + + self.send_command(0x11); # Data entry mode + self.send_data(0x01); + + self.send_command(0x44); + self.send_data(0x00); # RAM x address start at 0 + self.send_data(0x00); + self.send_data(0x6F); # RAM x address end at 36Fh -> 879 + self.send_data(0x03); + self.send_command(0x45); + self.send_data(0xAF); # RAM y address start at 20Fh; + self.send_data(0x02); + self.send_data(0x00); # RAM y address end at 00h; + self.send_data(0x00); + + self.send_command(0x3C); # VBD + self.send_data(0x01); # LUT1, for white + + self.send_command(0x18); + self.send_data(0X80); + self.send_command(0x22); + self.send_data(0XB1); #Load Temperature and waveform setting. + self.send_command(0x20); + self.ReadBusy(); #waiting for the electronic paper IC to release the idle signal + + self.send_command(0x4E); + self.send_data(0x00); + self.send_data(0x00); + self.send_command(0x4F); + self.send_data(0xAF); + self.send_data(0x02); + + 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(0x4F); + self.send_data(0xAf); + + self.send_command(0x24) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(imageblack[i]); + + + self.send_command(0x26) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(~imagered[i]); + + self.send_command(0x22); + self.send_data(0xC7); #Load LUT from MCU(0x32) + self.send_command(0x20); + epdconfig.delay_ms(200); #!!!The delay here is necessary, 200uS at least!!! + self.ReadBusy(); + + def Clear(self): + self.send_command(0x4F); + self.send_data(0xAf); + + self.send_command(0x24) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0xff); + + + self.send_command(0x26) + for i in range(0, int(self.width * self.height / 8)): + self.send_data(0x00); + + self.send_command(0x22); + self.send_data(0xC7); #Load LUT from MCU(0x32) + self.send_command(0x20); + epdconfig.delay_ms(200); #!!!The delay here is necessary, 200uS at least!!! + self.ReadBusy(); + + def sleep(self): + self.send_command(0x10); #deep sleep + self.send_data(0x01); + + def Dev_exit(self): + epdconfig.module_exit() +### END OF FILE ### + diff --git a/driver/epdconfig.py b/driver/epdconfig.py new file mode 100755 index 0000000..861f43d --- /dev/null +++ b/driver/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() + + 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/infowindow.jpg b/infowindow.jpg deleted file mode 100644 index 6ea1040..0000000 Binary files a/infowindow.jpg and /dev/null differ diff --git a/infowindow.py b/infowindow.py index 89098bd..91d8294 100755 --- a/infowindow.py +++ b/infowindow.py @@ -5,17 +5,21 @@ import json import logging import string +import datetime +from PIL import Image +from PIL import ImageDraw +from PIL import ImageFont +from PIL import ImageChops from mod_infowindow import infowindow # Select pluggable module for todo list, calendar and weather. # Replace the mod_