diff --git a/README.md b/README.md index 22eb495..848b133 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,11 @@ You can find here a variety of software, hardware and other resources for the [P You can purchase one of the PaPiRus boards from [our webshop](https://www.pi-supply.com/?s=papirus&post_type=product&tags=1&limit=5&ixwps=1) or from a variety of resellers worldwide. +# Python 2 and Python 3 support +The library and examples work on both Python 2 and Python 3. +Currently (October 2017) Python 2 is still the default Python in Raspbian. +The Python 2 and Python 3 versions can be installed side by side. + # Setup PaPiRus ## Auto Installation Just run the following script in a terminal window and PaPiRus will be automatically setup. @@ -20,16 +25,25 @@ You can enable the SPI by typing `sudo raspi-config` at the command line and the #### Install Python API (best to run all of these commands as root using sudo) ```bash # Install dependencies -apt-get install git -y -apt-get install python-imaging -y -apt-get install python-smbus -y -apt-get install bc i2c-tools -y -apt-get install python-dateutil -y -apt-get install fonts-freefont-ttf -y - -git clone https://github.com/PiSupply/PaPiRus.git +sudo apt-get install git -y +sudo apt-get install bc i2c-tools -y +sudo apt-get install fonts-freefont-ttf -y +# For Python 2 +sudo apt-get install python-imaging -y +sudo apt-get install python-smbus -y +sudo apt-get install python-dateutil -y +# For Python 3 +sudo apt-get install python3-imaging -y +sudo apt-get install python3-smbus -y +sudo apt-get install python3-dateutil -y + +git clone --depth=1 https://github.com/PiSupply/PaPiRus.git cd PaPiRus + +# For Python 2 sudo python setup.py install # Install PaPirRus python library +# For Python 3 +sudo python3 setup.py install # Install PaPirRus python library ``` #### Install Driver (Option 1) (best to run all of these commands as root using sudo) @@ -187,7 +201,7 @@ from papirus import PapirusText text = PapirusText() # Write text to the screen, in this case forty stars alternating black and white -# note the use of u"" syntax to specify unicode +# note the use of u"" syntax to specify unicode (needed for Python 2, optional for Python 3 since unicode is default in Python 3) text.write(u"\u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606 \u2605 \u2606") ``` #### Note @@ -202,10 +216,6 @@ image = PapirusImage([rotation = rot]) # easy write image to screen # image.write(path) image.write('/path/to/image') - -# write image to the screen with size and position -# image.write(path, width, (x,y)) -image.write('/path/to/image', 20, (10, 10) ) # This is not confirmed to work correctly yet!! ``` #### The composite API (Text and image) @@ -273,7 +283,7 @@ papirus-clear ``` -#### Demos +### Demos All demos can be seen by running the following commands. Code can be found in the repo for the python bin directory. ```bash @@ -314,15 +324,26 @@ papirus-twitter papirus-composite-write # Display image sequences or slide-show -# The directory containing the pictures must have number sequenced images in the form 0.gif, 1.gif, 2.gif, etc. for an animation or pictures with random names (e.g. in the case of a slide-show). +# The directory containing the pictures must have number sequenced images in the form 0.gif, 1.gif, 2.gif, etc. +# for an animation or pictures with random names (e.g. in the case of a slide-show). papirus-animation [--delay DELAY] [--rotation ROTATION] [--fullupdate] [--loop] directory # Take a picture with the RPi camera using the PaPiRus screen as viewfinder papirus-cam ``` +### Demos for using the Real Time Clock of the Papirus HAT + +The Papirus HAT has a battery backed-up Real TIme Clock. For more information about the RTC and demos see the +[RTC-Hat-Examples](./RTC-Hat-Examples) directory and README files. + ### Tips for using images -The PaPiRus can only display Bitmap images (.BMP) in black and white (1 bit colour). If you pass an image to PaPiRus that is not a 1 Bit Bitmap, it will automatically be converted to this by the software. However, for best results and higher image quality we would recommend that you convert the image to a 1 Bit Bitmap before pushing to the PaPiRus screen using GIMP or Photoshop or similar photo editing tools like [the rePaper companion](https://github.com/aerialist/repaper_companion) to resize images and convert them to XBM format or [WIF (the WyoLum Image Format)](http://wyolum.com/introducing-wif-the-wyolum-image-format/). +The PaPiRus can only display Bitmap images (.BMP) in black and white (1 bit colour). If you pass an image to PaPiRus +that is not a 1 Bit Bitmap, it will automatically be converted to this by the software. However, for best results +and higher image quality we would recommend that you convert the image to a 1 Bit Bitmap before pushing to the +PaPiRus screen using GIMP or Photoshop or similar photo editing tools like +[the rePaper companion](https://github.com/aerialist/repaper_companion) to resize images and convert them to XBM format +or [WIF (the WyoLum Image Format)](http://wyolum.com/introducing-wif-the-wyolum-image-format/). ### Screen Resolutions The screens have the following screen resolutions: diff --git a/RTC-Hat-Examples/README.md b/RTC-Hat-Examples/README.md index 25caf97..aced239 100644 --- a/RTC-Hat-Examples/README.md +++ b/RTC-Hat-Examples/README.md @@ -1,12 +1,9 @@ # Using the MCP7940N Hardware Clock on the Papirus HAT -During the Papirus Kickstarter campaign a lot of time has been spent on finding a hardware clock -with a proper alarm function and the right pogo pin to connect the alarm output to the Pi reset header. -This to make a wake-on-alarm function possible. -However a wake-on-alarm software example was not provided. The two examples in [RTCreboot](RTCreboot) and [RTCgpio](RTCgpio) -should give you sufficient information to use the hardware clock. - The Papirus HAT (not the Papirus Zero) has a MCP7940N battery backed-up hardware clock. +This hardware clock has an alarm function. The alarm output is connected via the pogo-pin (when mounted) to the +Pi reset header. This makes a boot-on-alarm function possible. +The two examples in RTCreboot](RTCreboot) and [RTCgpio](RTCgpio) should give you sufficient information to use the hardware clock. The MCP7940N is controlled via the i2c bus. Its i2c address is 0x6f. For all the details on the MCP7940N see the [datasheet](mcp7940n.pdf). @@ -27,7 +24,7 @@ But first how to use the clock function to keep the time on the Pi between boots # Using the Hardware Clock -Raspbian Jessie has built-in support for the MCP7940N provided by the rtc_ds1307 module. +Raspbian Jessie has built-in support for the MCP7940N provided by the rtc-ds1307 module. This module supports various i2c based real time clocks including the MCP7940N. Add the following line to `/boot/config.txt`: ``` @@ -82,47 +79,49 @@ You can check the current hardware clock time with the command `sudo hwclock -r` For more info try `sudo hwclock -r --debug`. Here is a sample output: ``` pi@papirus-hat:~ $ sudo hwclock -r --debug -hwclock from util-linux 2.25.2 +hwclock from util-linux 2.29.2 Using the /dev interface to the clock. -Last drift adjustment done at 1470071156 seconds after 1969 -Last calibration done at 1470071156 seconds after 1969 -Hardware clock is on UTC time Assuming hardware clock is kept in UTC time. Waiting for clock tick... /dev/rtc does not have interrupt functions. Waiting in loop for time from /dev/rtc to change ...got clock tick -Time read from Hardware Clock: 2016/08/05 19:33:53 -Hw clock time : 2016/08/05 19:33:53 = 1470425633 seconds since 1969 -Fri 05 Aug 2016 21:33:53 CEST -0.133460 seconds +Time read from Hardware Clock: 2017/10/03 19:51:21 +Hw clock time : 2017/10/03 19:51:21 = 1507060281 seconds since 1969 +Time since last adjustment is 1507060281 seconds +Calculated Hardware Clock drift is 0.000000 seconds +2017-10-03 21:51:20.641423+0200 ``` Note the hardware clock is in UTC, but the time is presented in CEST (Central European Summer Time). You can set the hardware clock to the system clock with `sudo hwclock -w` When you are connected to the internet you can just wait about 15 minutes. -The system (provided the ntp daemon is enabled, which it normally is in Raspbian) will copy the system +The system (provided the systemd-timesyncd.service is running) will copy the system time to the hardware clock approximately once every 15 minutes. -Systemd provides a hwclock-save service which is run at shutdown. However when ntp is installed (/usr/sbin/ntpd -is an executable file) the system time is *not* copied to the hardware clock. Therefore I recommend to -comment out the ntpd check in `/lib/systemd/system/hwclock-save.service`: +The systemd-timesyncd.service is enabled by default in Raspbian Stretch. +It is available under Raspbian Jessie, but not enabled by default. +To enable it run the following commands: ``` -[Unit] -Description=Synchronise Hardware Clock to System Clock -DefaultDependencies=no -Before=shutdown.target -#ConditionFileIsExecutable=!/usr/sbin/ntpd -ConditionFileIsExecutable=!/usr/sbin/openntpd -ConditionFileIsExecutable=!/usr/sbin/chrony -ConditionVirtualization=!container - -[Service] -Type=oneshot -ExecStart=/sbin/hwclock -D --systohc - -[Install] -WantedBy=reboot.target halt.target poweroff.target +# Disable ntp server, will be repaced by systemd-timesyncd +$ sudo systemctl stop ntp +$ sudo systemctl disable ntp +# policykit is needed, but not installed in Jessie lite +$ sudo apt-get install policykit-1 +# Enable systemctl-timesyncd +$ sudo timedatectl set-ntp 1 +``` + +You can check the status by running the command `timedatectl`: +``` +pi@papirus-hat:~ $ timedatectl + Local time: Tue 2017-10-03 21:43:42 CEST + Universal time: Tue 2017-10-03 19:43:42 UTC + RTC time: Tue 2017-10-03 19:43:42 + Time zone: Europe/Amsterdam (CEST, +0200) + Network time on: yes +NTP synchronized: yes + RTC in local TZ: no ``` -This ensures the hardware clock is always set from the system clock at shutdown independent from the network and ntp. # Accessing the MCP7940N from Python with the kernel driver (rtc_ds1307) loaded diff --git a/RTC-Hat-Examples/RTCreboot/bootinfo b/RTC-Hat-Examples/RTCreboot/bootinfo index b409534..a5be8d8 100755 --- a/RTC-Hat-Examples/RTCreboot/bootinfo +++ b/RTC-Hat-Examples/RTCreboot/bootinfo @@ -13,7 +13,7 @@ i2cbus = SMBus(1) disablealm0(i2cbus) -upsince = check_output(["uptime", "-s"]) +upsince = check_output(["uptime", "-s"]).decode('utf-8') now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') rtc = readrtc(i2cbus).strftime('%Y-%m-%d %H:%M:%S') diff --git a/RTC-Hat-Examples/RTCreboot/prtc.py b/RTC-Hat-Examples/RTCreboot/prtc.py index 7befaa6..b17710f 100644 --- a/RTC-Hat-Examples/RTCreboot/prtc.py +++ b/RTC-Hat-Examples/RTCreboot/prtc.py @@ -1,5 +1,7 @@ # utility functions for Papirus Hat hardware clock (MCP7940N) +from __future__ import division + from datetime import datetime from calendar import isleap from smbusf import SMBus @@ -10,7 +12,7 @@ almbase = [0xa, 0x11] def tobcd(val): - return (val % 10) | (val / 10) << 4 + return (val % 10) | (val // 10) << 4 def writertc(i2cbus, dt): sec = dt.second @@ -65,12 +67,12 @@ def writealm(i2cbus, alm, dt): def readrtc(i2cbus): data=i2cbus.read_i2c_block_data(RTCADR, 0, 7) - sec = (data[0] & 0x7f) / 16 * 10 + (data[0] & 0x0f) - min = data[1] / 16 * 10 + (data[1] & 0x0f) - hour = data[2] / 16 * 10 + (data[2] & 0x0f) - day = data[4] / 16 * 10 + (data[4] & 0x0f) - month = (data[5] & 0x10) / 16 * 10 + (data[5] & 0x0f) - year = data[6] / 16 * 10 + (data[6] & 0x0f) + sec = (data[0] & 0x7f) // 16 * 10 + (data[0] & 0x0f) + min = data[1] // 16 * 10 + (data[1] & 0x0f) + hour = data[2] // 16 * 10 + (data[2] & 0x0f) + day = data[4] // 16 * 10 + (data[4] & 0x0f) + month = (data[5] & 0x10) // 16 * 10 + (data[5] & 0x0f) + year = data[6] // 16 * 10 + (data[6] & 0x0f) dt = datetime(2000+year, month, day, hour, min, sec) return dt @@ -81,11 +83,11 @@ def readalm(i2cbus, alm): alm = 0 data = i2cbus.read_i2c_block_data(RTCADR, almbase[alm], 6) - sec = data[0] / 16 * 10 + (data[0] & 0x0f) - min = data[1] / 16 * 10 + (data[1] & 0x0f) - hour = data[2] / 16 * 10 + (data[2] & 0x0f) - day = data[4] / 16 * 10 + (data[4] & 0x0f) - month = data[5] / 16 * 10 + (data[5] & 0x0f) + sec = data[0] // 16 * 10 + (data[0] & 0x0f) + min = data[1] // 16 * 10 + (data[1] & 0x0f) + hour = data[2] // 16 * 10 + (data[2] & 0x0f) + day = data[4] // 16 * 10 + (data[4] & 0x0f) + month = data[5] // 16 * 10 + (data[5] & 0x0f) # year not used in alarm time dt = datetime(2000, month, day, hour, min, sec) return dt diff --git a/RTC-Hat-Examples/py-smbusf/README.md b/RTC-Hat-Examples/py-smbusf/README.md index d3802ca..7901499 100644 --- a/RTC-Hat-Examples/py-smbusf/README.md +++ b/RTC-Hat-Examples/py-smbusf/README.md @@ -11,7 +11,15 @@ You need the python-devel package for building this module: To build and install: ``` + # For Python 2 + $ make clean $ make install + + # For Python 3 + $ make clean + $ PYTHON=python3 make install ``` -This will install the module in the directory: `/home/pi/.local/lib/python2.7/site-packages`. +This will install the module in the directory: `/home/pi/.local/lib/python2.7/site-packages` for Python 2 +and in the directori `/home/pi/.local/lib/python3.x/site-packages` for Python 3.. + Programs using this module therefore need to run as user pi. diff --git a/RTC-Hat-Examples/py-smbusf/smbusmodule.c b/RTC-Hat-Examples/py-smbusf/smbusmodule.c index 039eb6d..1a21d0e 100644 --- a/RTC-Hat-Examples/py-smbusf/smbusmodule.c +++ b/RTC-Hat-Examples/py-smbusf/smbusmodule.c @@ -92,7 +92,11 @@ SMBus_dealloc(SMBus *self) PyObject *ref = SMBus_close(self); Py_XDECREF(ref); +#if PY_MAJOR_VERSION >= 3 + Py_TYPE(self)->tp_free((PyObject *)self); +#else self->ob_type->tp_free((PyObject *)self); +#endif } #define MAXPATH 16 @@ -432,11 +436,19 @@ SMBus_list_to_data(PyObject *list, union i2c_smbus_data *data) for (ii = 0; ii < len; ii++) { PyObject *val = PyList_GET_ITEM(list, ii); +#if PY_MAJOR_VERSION >= 3 + if (!PyLong_Check(val)) { + PyErr_SetString(PyExc_TypeError, msg); + return 0; /* fail */ + } + data->block[ii+1] = (__u8)PyLong_AS_LONG(val); +#else if (!PyInt_Check(val)) { PyErr_SetString(PyExc_TypeError, msg); return 0; /* fail */ } data->block[ii+1] = (__u8)PyInt_AS_LONG(val); +#endif } return 1; /* success */ @@ -635,9 +647,14 @@ static PyGetSetDef SMBus_getset[] = { }; static PyTypeObject SMBus_type = { +#if PY_MAJOR_VERSION >= 3 + PyVarObject_HEAD_INIT(NULL, 0) + "SMBus", /* tp_name */ +#else PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "smbus.SMBus", /* tp_name */ +#endif sizeof(SMBus), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)SMBus_dealloc, /* tp_dealloc */ @@ -676,24 +693,50 @@ static PyTypeObject SMBus_type = { SMBus_new, /* tp_new */ }; +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef SMBusModule = { + PyModuleDef_HEAD_INIT, + "SMBus", /* m_name */ + SMBus_module_doc, /* m_doc */ + -1, /* m_size */ + NULL, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; +#define INIT_RETURN(m) return m +#define INIT_FNAME PyInit_smbusf +#else static PyMethodDef SMBus_module_methods[] = { {NULL} }; +#define INIT_RETURN(m) return +#define INIT_FNAME initsmbusf +#endif #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ #define PyMODINIT_FUNC void #endif PyMODINIT_FUNC -initsmbusf(void) +INIT_FNAME(void) { PyObject* m; if (PyType_Ready(&SMBus_type) < 0) - return; + INIT_RETURN(NULL); +#if PY_MAJOR_VERSION >= 3 + m = PyModule_Create(&SMBusModule); +#else m = Py_InitModule3("smbusf", SMBus_module_methods, SMBus_module_doc); +#endif + if (m == NULL) + INIT_RETURN(NULL); Py_INCREF(&SMBus_type); PyModule_AddObject(m, "SMBus", (PyObject *)&SMBus_type); + + INIT_RETURN(m); } diff --git a/bin/papirus-animation b/bin/papirus-animation old mode 100755 new mode 100644 index 88ca28d..adcd00d --- a/bin/papirus-animation +++ b/bin/papirus-animation @@ -1,10 +1,12 @@ #!/usr/bin/env python +from __future__ import print_function + import time import os import argparse -import Image +from PIL import Image from papirus import Papirus @@ -32,7 +34,7 @@ def main(): def animate(papirus, imagepath, extradelay, fullupdate, loop): """animation""" - reps = 0 # Counts the times the screen has been used even when in loop to ensure a refresh every 20 + reps = 0 # Counts the times the screen has been used even when in loop to ensure a refresh every 10 papirus.clear() @@ -52,7 +54,7 @@ def animate(papirus, imagepath, extradelay, fullupdate, loop): exit() except Exception as e: raise e - print "No file found" + print("No file found") try: while loop or reps == 0: @@ -75,7 +77,7 @@ def animate(papirus, imagepath, extradelay, fullupdate, loop): image.paste(fileimg, (xpadding, ypadding)) papirus.display(image) - if fullupdate or (reps > 0 and reps % 20 == 0): # Refresh every twenty partials + if fullupdate or reps % 10 == 0: # Refresh every ten partials papirus.update() else: papirus.partial_update() @@ -83,16 +85,18 @@ def animate(papirus, imagepath, extradelay, fullupdate, loop): reps = reps + 1 if extradelay > 0: time.sleep(extradelay) - if loop: - reps = 0 - papirus.clear() except KeyboardInterrupt: # quit pass - time.sleep(2) - papirus.clear() + if loop: + # When looping clear the display at exit + time.sleep(2) + papirus.clear() + else: + # Otherwise show the last image + papirus.update() # main if "__main__" == __name__: diff --git a/bin/papirus-buttons b/bin/papirus-buttons index a419e84..4f66eff 100644 --- a/bin/papirus-buttons +++ b/bin/papirus-buttons @@ -1,5 +1,7 @@ #!/usr/bin/env python +from __future__ import print_function + import os import sys import string @@ -13,7 +15,7 @@ import RPi.GPIO as GPIO # Check EPD_SIZE is defined EPD_SIZE=0.0 if os.path.exists('/etc/default/epd-fuse'): - execfile('/etc/default/epd-fuse') + exec(open('/etc/default/epd-fuse').read()) if EPD_SIZE == 0.0: print("Please select your screen size by running 'papirus-config'.") sys.exit() @@ -22,7 +24,7 @@ if EPD_SIZE == 0.0: if not (os.path.exists('/dev/gpiomem') and os.access('/dev/gpiomem', os.R_OK | os.W_OK)): user = os.getuid() if user != 0: - print("Please run script as root") + print('Please run script as root') sys.exit() # Command line usage @@ -44,13 +46,11 @@ SW5 = 26 # Check for HAT, and if detected redefine SW1 .. SW5 if (os.path.exists(hatdir + '/product')) and (os.path.exists(hatdir + '/vendor')) : - f = open(hatdir + '/product') - prod = f.read() - f.close() - f = open(hatdir + '/vendor') - vend = f.read() - f.close - if (string.find(prod, 'PaPiRus ePaper HAT') == 0) and (string.find(vend, 'Pi Supply') == 0) : + with open(hatdir + '/product') as f : + prod = f.read() + with open(hatdir + '/vendor') as f : + vend = f.read() + if (prod.find('PaPiRus ePaper HAT') == 0) and (vend.find('Pi Supply') == 0) : # Papirus HAT detected SW1 = 16 SW2 = 26 diff --git a/bin/papirus-cam b/bin/papirus-cam index 655be27..4ef77d6 100644 --- a/bin/papirus-cam +++ b/bin/papirus-cam @@ -22,6 +22,7 @@ # from __future__ import print_function + import io import os import sys @@ -63,7 +64,7 @@ if (os.path.exists(hatdir + '/product')) and (os.path.exists(hatdir + '/vendor') prod = f.read() with open(hatdir + '/vendor', 'r') as f: vend = f.read() - if (string.find(prod, 'PaPiRus ePaper HAT') == 0) and (string.find(vend, 'Pi Supply') == 0) : + if (prod.find('PaPiRus ePaper HAT') == 0) and (vend.find('Pi Supply') == 0) : # Papirus HAT detected SW1 = 16 SW2 = 26 @@ -72,12 +73,12 @@ if (os.path.exists(hatdir + '/product')) and (os.path.exists(hatdir + '/vendor') SW5 = -1 def setPicFlag(): - global picFlag - picFlag = True + global picFlag + picFlag = True -def do_exit(): - global exitFlag - exitFlag = True +def setExitFlag(): + global exitFlag + exitFlag = True def viewfinderSettings(camera): # Leave a 1 pixel border on the display to minimize streaking @@ -132,7 +133,7 @@ picButton = gpiozero.Button(SW1, pull_up=False) exitButton = gpiozero.Button(SW2, pull_up=False) picFlag = False -picButton.when_pressed = setPicFlag +picButton.when_released = setPicFlag for i in range (0, 50): if picFlag: break @@ -146,7 +147,7 @@ camera.vflip = True picFlag = False exitFlag = False -exitButton.when_pressed = do_exit +exitButton.when_released = setExitFlag viewfinderSettings(camera) while True: diff --git a/bin/papirus-clear b/bin/papirus-clear index ab923ab..1edb938 100644 --- a/bin/papirus-clear +++ b/bin/papirus-clear @@ -1,5 +1,7 @@ #!/usr/bin/env python +from __future__ import print_function + import os import sys from papirus import Papirus @@ -7,7 +9,7 @@ from papirus import Papirus # Check EPD_SIZE is defined EPD_SIZE=0.0 if os.path.exists('/etc/default/epd-fuse'): - execfile('/etc/default/epd-fuse') + exec(open('/etc/default/epd-fuse').read()) if EPD_SIZE == 0.0: print("Please select your screen size by running 'papirus-config'.") sys.exit() diff --git a/bin/papirus-clock b/bin/papirus-clock index bb42347..386c675 100644 --- a/bin/papirus-clock +++ b/bin/papirus-clock @@ -14,6 +14,8 @@ # express or implied. See the License for the specific language # governing permissions and limitations under the License. +from __future__ import print_function + import os import sys @@ -27,7 +29,7 @@ from papirus import Papirus # Check EPD_SIZE is defined EPD_SIZE=0.0 if os.path.exists('/etc/default/epd-fuse'): - execfile('/etc/default/epd-fuse') + exec(open('/etc/default/epd-fuse').read()) if EPD_SIZE == 0.0: print("Please select your screen size by running 'papirus-config'.") sys.exit() diff --git a/bin/papirus-composite-write b/bin/papirus-composite-write index 47305de..d1bcfd1 100644 --- a/bin/papirus-composite-write +++ b/bin/papirus-composite-write @@ -9,7 +9,7 @@ from papirus import PapirusComposite # Check EPD_SIZE is defined EPD_SIZE=0.0 if os.path.exists('/etc/default/epd-fuse'): - execfile('/etc/default/epd-fuse') + exec(open('/etc/default/epd-fuse').read()) if EPD_SIZE == 0.0: print("Please select your screen size by running 'papirus-config'.") sys.exit() diff --git a/bin/papirus-draw b/bin/papirus-draw index a64c55d..c8b945c 100755 --- a/bin/papirus-draw +++ b/bin/papirus-draw @@ -1,5 +1,7 @@ #!/usr/bin/env python +from __future__ import print_function + import os import sys import time @@ -20,7 +22,7 @@ if not (os.path.exists('/dev/gpiomem') and os.access('/dev/gpiomem', os.R_OK | o # Check EPD_SIZE is defined EPD_SIZE=0.0 if os.path.exists('/etc/default/epd-fuse'): - execfile('/etc/default/epd-fuse') + exec(open('/etc/default/epd-fuse').read()) if EPD_SIZE == 0.0: print("Please select your screen size by running 'papirus-config'.") sys.exit() diff --git a/bin/papirus-gol b/bin/papirus-gol index fbbf1dc..1f77b1f 100644 --- a/bin/papirus-gol +++ b/bin/papirus-gol @@ -1,5 +1,7 @@ #!/usr/bin/env python +from __future__ import (print_function, division) + import os import sys @@ -24,7 +26,7 @@ if not (os.path.exists('/dev/gpiomem') and os.access('/dev/gpiomem', os.R_OK | o # Check EPD_SIZE is defined EPD_SIZE=0.0 if os.path.exists('/etc/default/epd-fuse'): - execfile('/etc/default/epd-fuse') + exec(open('/etc/default/epd-fuse').read()) if EPD_SIZE == 0.0: print("Please select your screen size by running 'papirus-config'.") sys.exit() @@ -36,9 +38,9 @@ def colourGrid(draw, item, lifeDict): y = y * CELLSIZE # translates array into grid size x = x * CELLSIZE # translates array into grid size if lifeDict[item] == 0: - draw.rectangle(( (x, y), (x + CELLSIZE, y + CELLSIZE) ), fill=WHITE, outline=WHITE) + draw.rectangle(( (x, y), (x + CELLSIZE, y + CELLSIZE) ), fill=WHITE, outline=WHITE) if lifeDict[item] == 1: - draw.rectangle(( (x, y), (x + CELLSIZE, y + CELLSIZE) ), fill=BLACK, outline=WHITE) + draw.rectangle(( (x, y), (x + CELLSIZE, y + CELLSIZE) ), fill=BLACK, outline=WHITE) return None # Creates an dictionary of all the cells @@ -46,8 +48,8 @@ def colourGrid(draw, item, lifeDict): def generateGrid(height, width): gridDict = {} #creates dictionary for all cells - for y in range (height / CELLSIZE): - for x in range (width / CELLSIZE): + for y in range (height // CELLSIZE): + for x in range (width // CELLSIZE): gridDict[x,y] = 0 #Sets cells as dead return gridDict @@ -64,8 +66,8 @@ def getNeighbours(epd, item,lifeDict): for x in range (-1,2): for y in range (-1,2): checkCell = (item[0]+x,item[1]+y) - if checkCell[0] < (epd.width / CELLSIZE) and checkCell[0] >=0: - if checkCell[1] < (epd.height / CELLSIZE) and checkCell[1]>= 0: + if checkCell[0] < (epd.width // CELLSIZE) and checkCell[0] >=0: + if checkCell[1] < (epd.height // CELLSIZE) and checkCell[1]>= 0: if lifeDict[checkCell] == 1: if x == 0 and y == 0: # negate the central cell neighbours += 0 diff --git a/bin/papirus-radar b/bin/papirus-radar index a7c981d..7c345ca 100755 --- a/bin/papirus-radar +++ b/bin/papirus-radar @@ -9,6 +9,8 @@ # This demo is based on David Lowe's original demo (https://github.com/campag/eink-demo), # but uses the fast update mode where you can set the stagetime directly. +from __future__ import (print_function, division) + import os import sys @@ -17,10 +19,17 @@ from PIL import Image from PIL import ImageDraw, ImageFont from papirus import Papirus +if sys.version_info < (3,): + def b(x): + return x +else: + def b(x): + return x.encode('ISO-8859-1') + # Check EPD_SIZE is defined EPD_SIZE=0.0 if os.path.exists('/etc/default/epd-fuse'): - execfile('/etc/default/epd-fuse') + exec(open('/etc/default/epd-fuse').read()) if EPD_SIZE == 0.0: print("Please select your screen size by running 'papirus-config'.") sys.exit() @@ -71,8 +80,8 @@ def epd_draw(starttime, cycletime, packet_num): draw.text((SCREEN_WIDTH/2-4.0*CHRW_MED, SCREEN_HEIGHT/2-1.5*SIZE_MED), "%s %02ds" %(stagetime, seconds), fill=BLACK, font=FONT_MED) # fps - fpsint = 1000/cycletime - fpsfract = 10000/cycletime - fpsint*10 + fpsint = 1000//cycletime + fpsfract = 10000//cycletime - fpsint*10 draw.text((SCREEN_WIDTH/2-3.0*CHRW_MED, SCREEN_HEIGHT/2-0.5*SIZE_MED), "%02d.%1dfps" %(fpsint, fpsfract), fill=BLACK, font=FONT_MED) # Cycle time @@ -96,7 +105,7 @@ for time in times: epd.use_lm75b = False # Set stagetime with open('/dev/epd/pu_stagetime', 'wb') as f: - f.write(str(time)) + f.write(b(str(time))) counter=0 frametime=-1 # No screen update for first frame @@ -113,7 +122,7 @@ for time in times: frametime=(curr-prev).seconds*1000 + (curr - prev).microseconds/1000 counter += 1 except KeyboardInterrupt: - print "\nClearing panel for long term storage" + print("\nClearing panel for long term storage") break; epd.use_lm75b = True diff --git a/bin/papirus-system b/bin/papirus-system index 29cc1f9..8dd9d6c 100644 --- a/bin/papirus-system +++ b/bin/papirus-system @@ -1,5 +1,7 @@ #!/usr/bin/env python +from __future__ import (print_function, division) + import os import sys import subprocess @@ -25,7 +27,7 @@ if not (os.path.exists('/dev/gpiomem') and os.access('/dev/gpiomem', os.R_OK | o # Check EPD_SIZE is defined EPD_SIZE=0.0 if os.path.exists('/etc/default/epd-fuse'): - execfile('/etc/default/epd-fuse') + exec(open('/etc/default/epd-fuse').read()) if EPD_SIZE == 0.0: print("Please select your screen size by running 'papirus-config'.") sys.exit() @@ -41,6 +43,13 @@ PRODUCT = '/proc/device-tree/hat/product' UUID = '/proc/device-tree/hat/uuid' STATUS = '/dev/epd/error' +if sys.version_info < (3,): + def b(x): + return x +else: + def b(x): + return x.encode('ISO-8859-1') + def sysInfo(papirus): # initially set all white background image = Image.new('1', papirus.size, WHITE) @@ -65,7 +74,7 @@ def getIPAddress(ifname): # Return the IP address of interface try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - ip = socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24]) + ip = socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', b(ifname[:15])))[20:24]) except: ip = "0.0.0.0" return ip @@ -91,8 +100,8 @@ def main(stdscr): # Add title and footer exittxt = 'Press "q" to exit' title = '**** PaPiRus System Info ****' - stdscr.addstr(0, (curses.COLS - len(title)) / 2, title) - stdscr.addstr(22, (curses.COLS - len(exittxt)) / 2, exittxt) + stdscr.addstr(0, (curses.COLS - len(title)) // 2, title) + stdscr.addstr(22, (curses.COLS - len(exittxt)) // 2, exittxt) stdscr.refresh() papirus = Papirus() @@ -102,16 +111,19 @@ def main(stdscr): # Check for HAT if os.path.exists(PRODUCT): isHAT = True - vendor = str(subprocess.check_output(["cat", VENDOR])).rstrip(' \n\r\0') - product = str(subprocess.check_output(["cat", PRODUCT])).rstrip(' \n\r\0') - serial = str(subprocess.check_output(["cat", UUID])).rstrip(' \n\r\0') - + with open(VENDOR) as f : + vendor = f.read().rstrip('\0') + with open(PRODUCT) as f : + product = f.read().rstrip('\0') + with open(UUID) as f : + serial = f.read().rstrip('\0') panelwin = curses.newwin(10, curses.COLS - 6, 2, 3) else: isHAT = False panelwin = curses.newwin(9, curses.COLS - 6, 2, 3) - display_status = str(subprocess.check_output(["cat", STATUS])).rstrip(' \n\r\0') + with open(STATUS) as f : + display_status = f.read().rstrip('\n\0') netwin = curses.newwin(4, curses.COLS - 6, 12, 3) @@ -159,4 +171,4 @@ if "__main__" == __name__: main(sys.argv[1:]) except KeyboardInterrupt: sys.exit('interrupted') - pass \ No newline at end of file + pass diff --git a/bin/papirus-temp b/bin/papirus-temp index dd563b0..0c0461f 100644 --- a/bin/papirus-temp +++ b/bin/papirus-temp @@ -1,7 +1,10 @@ #!/usr/bin/env python +from __future__ import print_function + import os import sys +import codecs from PIL import Image from PIL import ImageDraw @@ -10,10 +13,16 @@ from PIL import ImageFont from papirus import Papirus from papirus import LM75B +# Force stdout to utf-8 even if redirected to pipe or file (e.g. when executed under cron) +if sys.version_info < (3,) : + sys.stdout = codecs.getwriter('utf-8')(sys.stdout) +else: + sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer) + # Check EPD_SIZE is defined EPD_SIZE=0.0 if os.path.exists('/etc/default/epd-fuse'): - execfile('/etc/default/epd-fuse') + exec(open('/etc/default/epd-fuse').read()) if EPD_SIZE == 0.0: print("Please select your screen size by running 'papirus-config'.") sys.exit() @@ -53,7 +62,7 @@ def main(argv): sensor = LM75B() tempC = '{c:.2f}'.format(c=sensor.getTempCFloat()) + u" \u00b0" + 'C' tempF = '{c:.2f}'.format(c=sensor.getTempFFloat()) + u" \u00b0" + 'F' - print(('Temperature from LM75B: ' + tempC + ' - ' + tempF).encode('utf-8')) + print('Temperature from LM75B: ' + tempC + ' - ' + tempF) # center the temperatures (txtwidth, txtheight) = draw.textsize(tempC, font=font) diff --git a/bin/papirus-test b/bin/papirus-test index b68e8c9..e56a092 100644 --- a/bin/papirus-test +++ b/bin/papirus-test @@ -1,5 +1,7 @@ #! /usr/bin/env python +from __future__ import (print_function, division) + import time import sys import os @@ -7,9 +9,7 @@ import smbus from datetime import datetime import RPi.GPIO as GPIO -import Image -import ImageDraw -import ImageFont +from PIL import Image, ImageDraw, ImageFont from papirus import Papirus from papirus import LM75B @@ -151,12 +151,12 @@ def i2c_detect(bus): def date_time_rtc(bus): """Reads time from Real Time Clock using I2C""" data = bus.read_i2c_block_data(MCP7940N_ADR, 0, 7) - sec = (data[0] & 0x7f) / 16 * 10 + (data[0] & 0x0f) - min = data[1] / 16 * 10 + (data[1] & 0x0f) - hour = data[2] / 16 * 10 + (data[2] & 0x0f) - day = data[4] / 16 * 10 + (data[4] & 0x0f) - month = (data[5] & 0x10) / 16 * 10 + (data[5] & 0x0f) - year = data[6] / 16 * 10 + (data[6] & 0x0f) + sec = (data[0] & 0x7f) // 16 * 10 + (data[0] & 0x0f) + min = data[1] // 16 * 10 + (data[1] & 0x0f) + hour = data[2] // 16 * 10 + (data[2] & 0x0f) + day = data[4] // 16 * 10 + (data[4] & 0x0f) + month = (data[5] & 0x10) // 16 * 10 + (data[5] & 0x0f) + year = data[6] // 16 * 10 + (data[6] & 0x0f) dt = datetime(2000+year, month, day, hour, min, sec) return dt diff --git a/bin/papirus-textfill b/bin/papirus-textfill index f6780f2..f6704b7 100644 --- a/bin/papirus-textfill +++ b/bin/papirus-textfill @@ -1,5 +1,7 @@ #!/usr/bin/env python +from __future__ import print_function + from papirus import Papirus from PIL import ImageFont, ImageDraw, Image import sys @@ -9,7 +11,7 @@ import time # Check EPD_SIZE is defined EPD_SIZE=0.0 if os.path.exists('/etc/default/epd-fuse'): - execfile('/etc/default/epd-fuse') + exec(open('/etc/default/epd-fuse').read()) if EPD_SIZE == 0.0: print("Please select your screen size by running 'papirus-config'.") sys.exit() @@ -69,16 +71,16 @@ if (len(sys.argv) >= 2): printString = sys.argv[1] if len(printString) > 40: - print 'WARNING: string length is too large for single line printing, truncating at 40 chars' + print('WARNING: string length is too large for single line printing, truncating at 40 chars') printString = printString[0:40] rot = sys.argv[2] if len(sys.argv) >= 3 else '0' my_papirus = Papirus(rotation = int(rot)) fontsize, dims= getFontSize(my_papirus, printString) - print "Writing to Papirus...." + print("Writing to Papirus....") drawWords(my_papirus, printString, fontsize, dims) - print "Finished!" + print("Finished!") else: - print "Usage: " + sys.argv[0] + " \"text to display\" [rotation]" + print("Usage: " + sys.argv[0] + " \"text to display\" [rotation]") diff --git a/bin/papirus-twitter b/bin/papirus-twitter index f7f98c5..c98f870 100644 --- a/bin/papirus-twitter +++ b/bin/papirus-twitter @@ -14,7 +14,8 @@ # by Ton van Overbeek March 2017 # ------------------------------------------------------ -# import libraries to make it work +from __future__ import print_function + import os import sys import string @@ -35,7 +36,7 @@ if not (os.path.exists('/dev/gpiomem') and os.access('/dev/gpiomem', os.R_OK | o # Check EPD_SIZE is defined EPD_SIZE=0.0 if os.path.exists('/etc/default/epd-fuse'): - execfile('/etc/default/epd-fuse') + exec(open('/etc/default/epd-fuse').read()) if EPD_SIZE == 0.0: print("Please select your screen size by running 'papirus-config'.") sys.exit() @@ -65,13 +66,11 @@ off_text = '5 = Off' # Check for HAT, and if detected redefine SW1 .. SW5 if (os.path.exists(hatdir + '/product')) and (os.path.exists(hatdir + '/vendor')) : - f = open(hatdir + '/product') - prod = f.read() - f.close() - f = open(hatdir + '/vendor') - vend = f.read() - f.close - if (string.find(prod, 'PaPiRus ePaper HAT') == 0) and (string.find(vend, 'Pi Supply') == 0) : + with open(hatdir + '/product') as f : + prod = f.read() + with open(hatdir + '/vendor') as f : + vend = f.read() + if (prod.find('PaPiRus ePaper HAT') == 0) and (vend.find('Pi Supply') == 0) : # Papirus HAT detected SW1 = 16 SW2 = 26 @@ -82,9 +81,8 @@ if (os.path.exists(hatdir + '/product')) and (os.path.exists(hatdir + '/vendor') def display_tweets(tweets): for tweet in tweets: - clean_tweet = '%s: %s' % (tweet['user']['screen_name'].encode('utf-8'), - tweet['text'].encode('utf-8')) - + clean_tweet = '%s: %s' % (tweet['user']['screen_name'], + tweet['text']) # These lines clear URLs from the tweets and tidies up other elements # the display doesn't seem to like clean_tweet = re.sub(r"(https?\://|http?\://|https?\:)\S+", "", clean_tweet) @@ -148,13 +146,13 @@ def main(): if (((SW5 != -1) and (GPIO.input(SW5) == False)) or ((SW5 == -1) and (GPIO.input(SW1) == False) and (GPIO.input(SW2) == False))): - # Says goodbye, clears the screen and shuts the Pi down - text.write('Goodbye..., Shutting down ...') + # Says goodbye, clears the screen and exits + text.write('... Goodbye ...') text.write(' ') - os.system("sudo shutdown -h now") + break # Small delay to allow for SW1 + SW2 detection on HAT - time.sleep(0.1) + time.sleep(0.3) if __name__ == '__main__': main() diff --git a/bin/papirus-write b/bin/papirus-write index 9d9ab7e..270ee97 100644 --- a/bin/papirus-write +++ b/bin/papirus-write @@ -16,7 +16,7 @@ if not (os.path.exists('/dev/gpiomem') and os.access('/dev/gpiomem', os.R_OK | o # Check EPD_SIZE is defined EPD_SIZE=0.0 if os.path.exists('/etc/default/epd-fuse'): - execfile('/etc/default/epd-fuse') + exec(open('/etc/default/epd-fuse').read()) if EPD_SIZE == 0.0: print("Please select your screen size by running 'papirus-config'.") sys.exit() diff --git a/install b/install index 5f7aa24..d217ec4 100644 --- a/install +++ b/install @@ -1,20 +1,56 @@ #!/usr/bin/env bash -# install required packages +P23="" +while [ "$P23" = "" ]; do + P23=$(whiptail --title "Please select Python version" --menu "" 12 50 4 \ + " 2" "Python 2 (default) " \ + " 3" "Python 3 " \ + "23" "Python 2 and 3 " 3>&1 1>&2 2>&3) + if [ $? = 1 ]; then + whiptail --msgbox "No version selected. Try again" 8 40 + fi +done +PY2=0 +PY3=0 +if [[ "$P23" == *"2"* ]]; then + PY2=1 +fi +if [[ "$P23" == *"3"* ]]; then + PY3=1 +fi + +# Install required packages apt-get install git -y -apt-get install python-imaging -y -apt-get install python-smbus -y +if [ $PY2 -eq 1 ]; then + apt-get install python-imaging -y + apt-get install python-smbus -y + apt-get install python-dateutil -y +fi +if [ $PY3 -eq 1 ]; then + apt-get install python3-imaging -y + apt-get install python3-smbus -y + apt-get install python3-dateutil -y +fi apt-get install bc i2c-tools -y -apt-get install python-dateutil -y apt-get install fonts-freefont-ttf -y -# enable SPI and I2C +# Enable SPI and I2C raspi-config nonint do_spi 0 raspi-config nonint do_i2c 0 -git clone https://github.com/PiSupply/PaPiRus.git +git clone --depth=1 https://github.com/PiSupply/PaPiRus.git cd PaPiRus -python setup.py install # Install PaPirRus python library -papirus-setup # Install drivers and setup epaper + +# Install Papirus Python library for Python 2 and/or 3 +if [ $PY2 -eq 1 ]; then + python setup.py install +fi +if [ $PY3 -eq 1 ]; then + python3 setup.py install +fi + +# Install drivers and setup epaper +papirus-setup + whiptail --msgbox "The system will now reboot" 8 40 reboot diff --git a/papirus/__init__.py b/papirus/__init__.py index 0719624..d74ce8d 100644 --- a/papirus/__init__.py +++ b/papirus/__init__.py @@ -1,3 +1,4 @@ +__version__ = '1.0.0' from papirus.lm75b import LM75B from papirus.epd import EPD as Papirus from papirus.text import PapirusText diff --git a/papirus/epd.py b/papirus/epd.py index 5fcc672..2715f21 100644 --- a/papirus/epd.py +++ b/papirus/epd.py @@ -18,7 +18,14 @@ from papirus import LM75B import re import os +import sys +if sys.version_info < (3,): + def b(x): + return x +else: + def b(x): + return x.encode('ISO-8859-1') class EPDError(Exception): def __init__(self, value): @@ -205,6 +212,6 @@ def clear(self): def _command(self, c): if self._uselm75b: with open(os.path.join(self._epd_path, 'temperature'), 'wb') as f: - f.write(repr(self._lm75b.getTempC())) + f.write(b(repr(self._lm75b.getTempC()))) with open(os.path.join(self._epd_path, 'command'), 'wb') as f: - f.write(c) + f.write(b(c)) diff --git a/papirus/image.py b/papirus/image.py index dc06093..860e119 100644 --- a/papirus/image.py +++ b/papirus/image.py @@ -2,27 +2,29 @@ import sys from PIL import Image -from PIL import ImageOps from papirus import Papirus +WHITE = 1 + class PapirusImage(): def __init__(self, rotation = 0): self.papirus = Papirus(rotation = rotation) - def write(self, image): - image = Image.open(image) - image = ImageOps.grayscale(image) + def write(self, imagefile): + fileimg = Image.open(imagefile) + + w,h = fileimg.size + + rsimg = fileimg + if w > self.papirus.width or h > self.papirus.height: + rsimg.thumbnail(self.papirus.size) - # crop to the middle - w,h = image.size - x = w / 2 - self.papirus.width / 2 - y = h / 2 - self.papirus.height / 2 + xpadding = (self.papirus.width - rsimg.size[0]) / 2 + ypadding = (self.papirus.height - rsimg.size[1]) / 2 - rs = image - if w != self.papirus.width or h != self.papirus.height: - rs = image.resize((self.papirus.width, self.papirus.height)) - bw = rs.convert("1", dither=Image.FLOYDSTEINBERG) + image = Image.new('1', self.papirus.size, WHITE) + image.paste(rsimg, (xpadding, ypadding)) - self.papirus.display(bw) + self.papirus.display(image) self.papirus.update() diff --git a/papirus/lm75b.py b/papirus/lm75b.py index ff7ba25..7ee76bd 100644 --- a/papirus/lm75b.py +++ b/papirus/lm75b.py @@ -4,6 +4,8 @@ # by this module # +from __future__ import (print_function, division) + import smbus LM75B_ADDRESS = 0x48 @@ -36,8 +38,8 @@ def getTempC(self): used to write to /dev/epd/temperature""" raw = self._bus.read_word_data(self._address, LM75B_TEMP_REGISTER) & 0xFFFF raw = ((raw << 8) & 0xFF00) + (raw >> 8) - return (raw + 128) / 256 # round to nearest integer + return (raw + 128) // 256 # round to nearest integer if __name__ == "__main__": sens = LM75B() - print (sens.getTempC()) + print(sens.getTempC(), sens.getTempFFloat()) diff --git a/papirus/readrtc.py b/papirus/readrtc.py index 5eb2274..b12a2b8 100644 --- a/papirus/readrtc.py +++ b/papirus/readrtc.py @@ -1,5 +1,8 @@ # Read time from real time clock using the ioctl interface for /dev/rtc # + +from __future__ import print_function + from collections import namedtuple from datetime import datetime from fcntl import ioctl @@ -87,5 +90,5 @@ def get_hwclock(devrtc="/dev/rtc"): return RtcTime.unpack(ret).to_datetime() if __name__ == "__main__": - print ("Date/Time from RTC: {d:s}".format(d= get_hwclock().strftime("%A %d %B %Y - %H:%M:%S"))) + print("Date/Time from RTC: {d:s}".format(d= get_hwclock().strftime("%A %d %B %Y - %H:%M:%S"))) diff --git a/setup.py b/setup.py index 6df5236..604494a 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,10 @@ #! /usr/bin/env python from distutils.core import setup +import papirus setup(name='papirus', - version='0.0.2', + version=papirus.__version__, description="PaPiRus API", author='PiSupply', author_email='sales@pi-supply.com',