From d7cf972497bb2a9dbe6d63e898537cca6f408bf7 Mon Sep 17 00:00:00 2001 From: Ton van Overbeek Date: Sat, 22 Jul 2017 14:52:32 +0200 Subject: [PATCH] papirus-test fixes and some more python 3 preparation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Handle case when the RTC kernel module is present (= no direct access to the RTC via I2C). - Some cleanup (avoid using subprocess, comments, …) - python3 preparation (lm75b.py and __init__.py) --- bin/papirus-test | 69 ++++++++++++++++++++++++---------- papirus/__init__.py | 13 ++++--- papirus/lm75b.py | 2 +- papirus/readrtc.py | 91 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 27 deletions(-) create mode 100644 papirus/readrtc.py diff --git a/bin/papirus-test b/bin/papirus-test index d07b49a..6b3a383 100644 --- a/bin/papirus-test +++ b/bin/papirus-test @@ -4,7 +4,6 @@ import time import sys import os import smbus -import subprocess from datetime import datetime import RPi.GPIO as GPIO @@ -14,31 +13,35 @@ import ImageFont from papirus import Papirus from papirus import LM75B - -global rtc_dt +from papirus import get_hwclock WHITE = 1 BLACK = 0 -LM75BD_ADR = 0x48 +LM75BD_ADR = 0x48 MCP7940N_ADR = 0x6F -FONT_FILE = '/usr/share/fonts/truetype/freefont/FreeMonoBold.ttf' +FONT_FILE = '/usr/share/fonts/truetype/freefont/FreeMonoBold.ttf' BITMAP_PATH = '/usr/local/bitmaps/' BITMAP_FILE = BITMAP_PATH + 'papirus-logo.bmp' -VENDOR = '/proc/device-tree/hat/vendor' +VENDOR = '/proc/device-tree/hat/vendor' PRODUCT = '/proc/device-tree/hat/product' -UUID = '/proc/device-tree/hat/uuid' +UUID = '/proc/device-tree/hat/uuid' GPIO.setmode(GPIO.BCM) # Check for HAT if os.path.exists(PRODUCT): isHAT = True - vendor = subprocess.check_output(["cat", VENDOR]) - product = subprocess.check_output(["cat", PRODUCT]) - serial = subprocess.check_output(["cat", UUID]) + with open(VENDOR, 'r') as fvendor, open(PRODUCT, 'r') as fproduct, open(UUID, 'r') as fuuid: + vendor = fvendor.read() + product = fproduct.read() + serial = fuuid.read() + if os.path.exists('/dev/rtc'): + devrtc = True + else: + devrtc = False SW1 = 16 SW2 = 26 SW3 = 20 @@ -61,19 +64,20 @@ else: GPIO.setup(SW4, GPIO.IN) GPIO.setup(SW5, GPIO.IN) +# Create an instance of papirus +papirus = Papirus() + def main(argv): """main program""" bus = smbus.SMBus(1) # 1 indicates /dev/i2c-1 if isHAT: - print("Assuming this is a PaPiRus HAT board") + print("Assuming this is a PaPiRus HAT board\n") else: - print("Assuming this is a PaPiRus Zero board") + print("Assuming this is a PaPiRus Zero board\n") i2c_detect(bus) # detect devices connected to the bus - papirus = Papirus() # create an instance of papirus - panel_info(papirus, bus) # provide board and screen information if not break_detect(papirus): # trying to use the screen @@ -87,8 +91,9 @@ def main(argv): time.sleep(1) print('Displaying information about the board and screen') display_papirusdata(papirus) + print('') - print('Press SW4 for board info, SW3 for animation, SW2 for single image, SW1 to exit') + # Test the switches if isHAT: print('Board layout SW4--SW3--SW2--SW1') print('Board pins 36 37 38 40') @@ -97,9 +102,10 @@ def main(argv): print('Board layout SW5--SW4--SW3--SW2--SW1') print('Board pins 40 36 38 35 37') print('BCM GPIO 21 16 20 19 26') - # get into demo mode and test the switches + print('Press SW4 for board info, SW3 for animation, SW2 for single image, SW1 to exit') + while True: - # Exit when SW1 and SW2 are pressed simultaneously + # Exit when SW1 is pressed if GPIO.input(SW1) == False: print('SW1 Pressed - Exiting') papirus.clear() @@ -123,6 +129,11 @@ def main(argv): def i2c_detect(bus): """Detects if the temp and RTC chips are available""" + print("---- Detect temperature sensor and RTC (RTC for HAT only) ----") + + if devrtc: + print("Real Time Clock detected via /dev/rtc (RTC kernel module)") + for device in range(128): try: bus.read_i2c_block_data(device, 0) @@ -134,8 +145,10 @@ def i2c_detect(bus): print("Other device found at: {d:x}".format(d=device)) except: # exception if I2C read fails pass + print("") 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) @@ -146,14 +159,27 @@ def date_time_rtc(bus): dt = datetime(2000+year, month, day, hour, min, sec) return dt +def date_time_devrtc(): + """Reads time from Real Time Clock using /dev/rtc (RTC kernel module)""" + return get_hwclock() + def panel_info(papirus, bus): """Retrieves information about the screen""" + + print("---- Screen Information ----") + if isHAT: print("Vendor: {v:s}".format(v=vendor)) print("Product: {p:s}".format(p=product)) print("Serial: {s:s}".format(s=serial)) - date_time = date_time_rtc(bus) - print("Date/Time from RTC: {d:s}".format(d=date_time.strftime("%A %d. %B %Y - %H:%M"))) + if devrtc == False: + date_time = date_time_rtc(bus) + print("Date/Time from RTC: {d:s}".format(d=date_time.strftime("%A %d %B %Y - %H:%M:%S"))) + elif os.getuid() != 0: + print("\n*** To read time from Real Time Clock via /dev/rtc run script as root ***\n") + else: + date_time = date_time_devrtc() + print("Date/Time from RTC: {d:s}".format(d=date_time.strftime("%A %d %B %Y - %H:%M.%S"))) sensor = LM75B() print("Temperature from sensor: {t:d} degrees Celsius" .format(t=sensor.getTempC())) @@ -161,6 +187,7 @@ def panel_info(papirus, bus): print("Panel info: {p:s} {w:d} x {h:d} version={v:s} COG={g:d} FILM={f:d}".format(p=papirus.panel, w=papirus.width, h=papirus.height, v=papirus.version, g=papirus.cog, f=papirus.film)) + print("") def break_detect(papirus): """Try using the screen to determine if it's broken""" @@ -174,13 +201,15 @@ def break_detect(papirus): papirus.display(image) papirus.update() # capture the status of the screen via fuse - display_status = str(subprocess.check_output(["cat", "/dev/epd/error"])).rstrip(' \n\r\0') + with open('/dev/epd/error', 'r') as errfile: + display_status = str(errfile.read()).rstrip(' \n\r\0') if display_status == 'OK': return False else: return True def display_papirusdata(papirus): + """Displaying information about the board and screen""" w = papirus.width h = papirus.height if h<=96: diff --git a/papirus/__init__.py b/papirus/__init__.py index 7715366..0719624 100644 --- a/papirus/__init__.py +++ b/papirus/__init__.py @@ -1,7 +1,8 @@ -from lm75b import LM75B -from epd import EPD as Papirus -from text import PapirusText -from image import PapirusImage -from textpos import PapirusTextPos -from composite import PapirusComposite +from papirus.lm75b import LM75B +from papirus.epd import EPD as Papirus +from papirus.text import PapirusText +from papirus.image import PapirusImage +from papirus.textpos import PapirusTextPos +from papirus.composite import PapirusComposite +from papirus.readrtc import get_hwclock diff --git a/papirus/lm75b.py b/papirus/lm75b.py index f128752..ff7ba25 100644 --- a/papirus/lm75b.py +++ b/papirus/lm75b.py @@ -40,4 +40,4 @@ def getTempC(self): if __name__ == "__main__": sens = LM75B() - print sens.getTempC() + print (sens.getTempC()) diff --git a/papirus/readrtc.py b/papirus/readrtc.py new file mode 100644 index 0000000..5eb2274 --- /dev/null +++ b/papirus/readrtc.py @@ -0,0 +1,91 @@ +# Read time from real time clock using the ioctl interface for /dev/rtc +# +from collections import namedtuple +from datetime import datetime +from fcntl import ioctl +import struct +from dateutil.tz import tzutc + +# From /usr/include/asm-generic/ioctl.h +_IOC_NRBITS = 8 +_IOC_TYPEBITS = 8 +_IOC_SIZEBITS = 14 +_IOC_DIRBITS = 2 + +_IOC_NRMASK = (1 << _IOC_NRBITS) - 1 +_IOC_TYPEMASK = (1 << _IOC_TYPEBITS) - 1 +_IOC_SIZEMASK = (1 << _IOC_SIZEBITS) - 1 +_IOC_DIRMASK = (1 << _IOC_DIRBITS) - 1 + +_IOC_NRSHIFT = 0 +_IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS +_IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS +_IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS + +_IOC_NONE = 0 +_IOC_WRITE = 1 +_IOC_READ = 2 + +def _IOC(dir, type, nr, size): + return ((dir << _IOC_DIRSHIFT) | + (type << _IOC_TYPESHIFT) | + (nr << _IOC_NRSHIFT) | + (size << _IOC_SIZESHIFT)) + +def _IOC_TYPECHECK(t): + return len(t) + +def _IO(type, nr): + return _IOC(_IOC_NONE, type, nr, 0) + +def _IOR(type, nr, size): + return _IOC(_IOC_READ, type, nr, _IOC_TYPECHECK(size)) + +def _IOW(type, nr, size): + return _IOC(_IOC_WRITE, type, nr, _IOC_TYPECHECK(size)) + +class RtcTime(namedtuple( + # man(4) rtc + "RtcTime", + "tm_sec tm_min tm_hour " + "tm_mday tm_mon tm_year " + "tm_wday tm_yday tm_isdst" # Last row is unused. +)): + + _fmt = 9 * "i" + + def __new__(cls, tm_sec=0, tm_min=0, tm_hour=0, + tm_mday=0, tm_mon=0, tm_year=0, + tm_wday=0, tm_yday=0, tm_isdst=0): + return super(RtcTime, cls).__new__(cls, tm_sec, tm_min, tm_hour, + tm_mday, tm_mon, tm_year, + tm_wday, tm_yday, tm_isdst) + + def to_datetime(self): + # From `hwclock.c`. + return datetime( + year=self.tm_year + 1900, month=self.tm_mon + 1, day=self.tm_mday, + hour=self.tm_hour, minute=self.tm_min, second=self.tm_sec, + tzinfo=tzutc()) + + def pack(self): + return struct.pack(self._fmt, *self) + + @classmethod + def unpack(cls, buffer): + return cls._make(struct.unpack(cls._fmt, buffer)) + +# From /usr/include/linux/rtc.h +rtc_time = RtcTime().pack() +RTC_RD_TIME = _IOR(ord("p"), 0x09, rtc_time) # 0x80247009 +RTC_SET_TIME = _IOW(ord("p"), 0x0a, rtc_time) # 0x4024700a +del rtc_time + +def get_hwclock(devrtc="/dev/rtc"): + with open(devrtc) as rtc: + ret = ioctl(rtc, RTC_RD_TIME, RtcTime().pack()) + 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"))) +