Skip to content

Commit

Permalink
Python2 and 3 (#160)
Browse files Browse the repository at this point in the history
* Python 2 and Python 3 support

The code has been updated so it can run under both Python 2 and Python
3.
The Python 2 and 3 versions of the library can be installed side by
side.
Also some updates for Raspbian Stretch especially for the RTC-Hat
examples.

* Define __version__ and image.py

Version now defined in a single place in papirus/__init__py.
Changed image.py to resize and centre the image while keeping its
aspect ratio.

* README.md updated for Image API

* Revert to full update every 10 partial updates
  • Loading branch information
tvoverbeek committed Oct 7, 2017
1 parent 1cb28d3 commit 1fecf01
Show file tree
Hide file tree
Showing 28 changed files with 346 additions and 178 deletions.
53 changes: 37 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
67 changes: 33 additions & 34 deletions RTC-Hat-Examples/README.md
Original file line number Diff line number Diff line change
@@ -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).

Expand All @@ -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`:
```
Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion RTC-Hat-Examples/RTCreboot/bootinfo
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down
26 changes: 14 additions & 12 deletions RTC-Hat-Examples/RTCreboot/prtc.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down
10 changes: 9 additions & 1 deletion RTC-Hat-Examples/py-smbusf/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
47 changes: 45 additions & 2 deletions RTC-Hat-Examples/py-smbusf/smbusmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
}

Loading

0 comments on commit 1fecf01

Please sign in to comment.