From 12ba988bd369dbec58b36fc811cda3ced224c90c Mon Sep 17 00:00:00 2001 From: paspo Date: Mon, 16 Apr 2018 22:08:38 +0200 Subject: [PATCH] initial commit --- .gitignore | 3 + APKBUILD | 26 + usr/python2.7/site-packages/oled/__init__.py | 9 + usr/python2.7/site-packages/oled/__init__.pyc | Bin 0 -> 264 bytes usr/python2.7/site-packages/oled/const.py | 84 ++++ usr/python2.7/site-packages/oled/const.pyc | Bin 0 -> 2816 bytes usr/python2.7/site-packages/oled/device.py | 410 ++++++++++++++++ usr/python2.7/site-packages/oled/device.pyc | Bin 0 -> 13939 bytes usr/python2.7/site-packages/oled/emulator.py | 237 +++++++++ usr/python2.7/site-packages/oled/emulator.pyc | Bin 0 -> 10895 bytes usr/python2.7/site-packages/oled/error.py | 38 ++ usr/python2.7/site-packages/oled/error.pyc | Bin 0 -> 1543 bytes usr/python2.7/site-packages/oled/mixin.py | 35 ++ usr/python2.7/site-packages/oled/mixin.pyc | Bin 0 -> 1729 bytes usr/python2.7/site-packages/oled/render.py | 40 ++ usr/python2.7/site-packages/oled/render.pyc | Bin 0 -> 1844 bytes usr/python2.7/site-packages/oled/serial.py | 173 +++++++ usr/python2.7/site-packages/oled/serial.pyc | Bin 0 -> 6820 bytes .../site-packages/oled/threadpool.py | 55 +++ usr/python2.7/site-packages/oled/virtual.py | 371 ++++++++++++++ .../smbus2-0.2.0-py2.7.egg-info/PKG-INFO | 202 ++++++++ .../smbus2-0.2.0-py2.7.egg-info/SOURCES.txt | 9 + .../dependency_links.txt | 1 + .../installed-files.txt | 8 + .../smbus2-0.2.0-py2.7.egg-info/top_level.txt | 1 + .../site-packages/smbus2/__init__.py | 3 + .../site-packages/smbus2/__init__.pyc | Bin 0 -> 275 bytes usr/python2.7/site-packages/smbus2/smbus2.py | 451 ++++++++++++++++++ usr/python2.7/site-packages/smbus2/smbus2.pyc | Bin 0 -> 15393 bytes 29 files changed, 2156 insertions(+) create mode 100644 .gitignore create mode 100644 APKBUILD create mode 100644 usr/python2.7/site-packages/oled/__init__.py create mode 100644 usr/python2.7/site-packages/oled/__init__.pyc create mode 100644 usr/python2.7/site-packages/oled/const.py create mode 100644 usr/python2.7/site-packages/oled/const.pyc create mode 100644 usr/python2.7/site-packages/oled/device.py create mode 100644 usr/python2.7/site-packages/oled/device.pyc create mode 100644 usr/python2.7/site-packages/oled/emulator.py create mode 100644 usr/python2.7/site-packages/oled/emulator.pyc create mode 100644 usr/python2.7/site-packages/oled/error.py create mode 100644 usr/python2.7/site-packages/oled/error.pyc create mode 100644 usr/python2.7/site-packages/oled/mixin.py create mode 100644 usr/python2.7/site-packages/oled/mixin.pyc create mode 100644 usr/python2.7/site-packages/oled/render.py create mode 100644 usr/python2.7/site-packages/oled/render.pyc create mode 100644 usr/python2.7/site-packages/oled/serial.py create mode 100644 usr/python2.7/site-packages/oled/serial.pyc create mode 100644 usr/python2.7/site-packages/oled/threadpool.py create mode 100644 usr/python2.7/site-packages/oled/virtual.py create mode 100644 usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/PKG-INFO create mode 100644 usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/SOURCES.txt create mode 100644 usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/dependency_links.txt create mode 100644 usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/installed-files.txt create mode 100644 usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/top_level.txt create mode 100644 usr/python2.7/site-packages/smbus2/__init__.py create mode 100644 usr/python2.7/site-packages/smbus2/__init__.pyc create mode 100644 usr/python2.7/site-packages/smbus2/smbus2.py create mode 100644 usr/python2.7/site-packages/smbus2/smbus2.pyc diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6860d29 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +packages/ +*.apk +.on-save.json diff --git a/APKBUILD b/APKBUILD new file mode 100644 index 0000000..8746725 --- /dev/null +++ b/APKBUILD @@ -0,0 +1,26 @@ +# Contributor: Paolo Asperti +# Maintainer: Paolo Asperti +pkgname=openpdu-libs +pkgver=0.1.0 +pkgrel=1 +pkgdesc="OpenPDU project - misc python libraries" +url="https://github.com/openpdu/openpdu-libs" +arch="noarch" +license="GPL2" +depends="python" +makedepends="" +subpackages="" +source="" +options="!check" + +build() { + : +} + +package() { + mkdir -p "$pkgdir" + # create directory tree + install -Dd usr "$pkgdir"/usr + + cp -r usr "$pkgdir"/ +} diff --git a/usr/python2.7/site-packages/oled/__init__.py b/usr/python2.7/site-packages/oled/__init__.py new file mode 100644 index 0000000..d3b9dd6 --- /dev/null +++ b/usr/python2.7/site-packages/oled/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2016 Richard Hull and contributors +# See LICENSE.rst for details. + +""" +OLED display driver for SSD1306, SSD1325, SSD1331 and SH1106 devices. +""" + +__version__ = '1.5.0' diff --git a/usr/python2.7/site-packages/oled/__init__.pyc b/usr/python2.7/site-packages/oled/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6d142d009185bc2622192d5583994ef9fe6da83 GIT binary patch literal 264 zcmYL?!A`?442IKAz|;weN64`UNSaOCE(mcL+75^vdh5mN>TE=GO;u-;_8vSF;(hi4 zxG)Jz{`_0Ee-F;L$=Bz1QHe`LT%S4Vj)6iH%t#cGz*ywzpr0%m$@=wWA<H~GUb{pNE!n<$AYOcuz`_PuvN4c}4eQi+#xW)wl#alzZdfan-JtP1C literal 0 HcmV?d00001 diff --git a/usr/python2.7/site-packages/oled/const.py b/usr/python2.7/site-packages/oled/const.py new file mode 100644 index 0000000..bb13936 --- /dev/null +++ b/usr/python2.7/site-packages/oled/const.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2016 Richard Hull and contributors +# See LICENSE.rst for details. + + +class common(object): + DISPLAYOFF = 0xAE + DISPLAYON = 0xAF + DISPLAYALLON = 0xA5 + DISPLAYALLON_RESUME = 0xA4 + NORMALDISPLAY = 0xA6 + INVERTDISPLAY = 0xA7 + SETREMAP = 0xA0 + SETMULTIPLEX = 0xA8 + SETCONTRAST = 0x81 + + +class ssd1306(common): + CHARGEPUMP = 0x8D + COLUMNADDR = 0x21 + COMSCANDEC = 0xC8 + COMSCANINC = 0xC0 + EXTERNALVCC = 0x1 + MEMORYMODE = 0x20 + PAGEADDR = 0x22 + SETCOMPINS = 0xDA + SETDISPLAYCLOCKDIV = 0xD5 + SETDISPLAYOFFSET = 0xD3 + SETHIGHCOLUMN = 0x10 + SETLOWCOLUMN = 0x00 + SETPRECHARGE = 0xD9 + SETSEGMENTREMAP = 0xA1 + SETSTARTLINE = 0x40 + SETVCOMDETECT = 0xDB + SWITCHCAPVCC = 0x2 + + +sh1106 = ssd1306 + + +class ssd1331(common): + ACTIVESCROLLING = 0x2F + CLOCKDIVIDER = 0xB3 + CONTINUOUSSCROLLINGSETUP = 0x27 + DEACTIVESCROLLING = 0x2E + DISPLAYONDIM = 0xAC + LOCKMODE = 0xFD + MASTERCURRENTCONTROL = 0x87 + NORMALDISPLAY = 0xA4 + PHASE12PERIOD = 0xB1 + POWERSAVEMODE = 0xB0 + SETCOLUMNADDR = 0x15 + SETCONTRASTA = 0x81 + SETCONTRASTB = 0x82 + SETCONTRASTC = 0x83 + SETDISPLAYOFFSET = 0xA2 + SETDISPLAYSTARTLINE = 0xA1 + SETMASTERCONFIGURE = 0xAD + SETPRECHARGESPEEDA = 0x8A + SETPRECHARGESPEEDB = 0x8B + SETPRECHARGESPEEDC = 0x8C + SETPRECHARGEVOLTAGE = 0xBB + SETROWADDR = 0x75 + SETVVOLTAGE = 0xBE + + +class ssd1325(common): + SETCOLUMNADDR = 0x15 + SETROWADDR = 0x75 + SETCURRENT = 0x84 + SETSTARTLINE = 0xA1 + SETOFFSET = 0xA2 + NORMALDISPLAY = 0xA4 + DISPLAYALLOFF = 0xA6 + MASTERCONFIG = 0xAD + SETPRECHARGECOMPENABLE = 0xB0 + SETPHASELEN = 0xB1 + SETROWPERIOD = 0xB2 + SETCLOCK = 0xB3 + SETPRECHARGECOMP = 0xB4 + SETGRAYTABLE = 0xB8 + SETPRECHARGEVOLTAGE = 0xBC + SETVCOMLEVEL = 0xBE + SETVSL = 0xBF diff --git a/usr/python2.7/site-packages/oled/const.pyc b/usr/python2.7/site-packages/oled/const.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d653976db7d5602240aa8cb79fdeeb36ce133bc4 GIT binary patch literal 2816 zcmc(hOK;p%6vt08c|V%>w19+w4a0&=Cxx;>RUP|gV&RvrJ(K2cq9k{q14$-goJE%f zBqSai#0LPxYrz{9NFd4zb&ps8AA>a#=YMU_q)CJjtss$q<8wSd_ju0#9N*F((-S|v zeOv8P{#WA9t9*2K7@DX>^y~oX0eXe#Wui^R#vru@jJMSDvTKl3iS$r$ZMb)Bi1bL| z9qoCCNsm!n8Ruov5yoz3f4_6k%VU6Dt9*2?FgOXU%N#^1Scg~RxFu2?XNc4Y>0wf% zSV@hM9wjx7_-cY=wnqCij??}e_yBkx_z?IAcnA0xxXGwYGSV@|*4DxHerszhoiN6J zM<4AKrsKwuXKjXSYbl@Y~A6(D-CTkY(%bal6L&`p;QZ%9NyJ-YKa894c$j^bKZ*DhVWe7aZ%eDQ9I zrN>E4lAa(nMS7CdH0dc)=SWYJnjw9T)GX;4QgftdNzJ3UbG&5xP2geRHt-_=1+^an z9szy>egS?4W&u8^{VVV;a25C+7-UpXHHjz^rcr>sZppfc8h(_Sq1W&O%W-6G`mt>V zj6QAUDWHO?oQi%C0?qXR^nS6<|tq!&q*NiUH)Pr6L%0@Qk*(`r|NuYpGaG->-2;7{OHz>KFaarOnU1l$B(0(cNp z`#r$u_EVg_2D}cu0elA>0Y5M*_c4xX8AZ43#BGY$mZ8TruA7P$@^T#^(+m713>G(N zgpIiOMb<$hO3`+XI4eeP4mz%Hlnu!mnJ@A^K2Iqj?S_=BHZ!(DPhzGR@kZ-bENUx} zkZ$N?o^V6R*lKdKaHC*g^7@&UO}9Wa#*+;|t5AF~5m!$-Y+1%?Xl}kQPqwf0yxsCN z!(h#=H>4=Ku-7(mB!pv~?Or|GZ5M0yf;2-f;jTBsf^(7KhIyoXrZ@A;_n@;LS`sAXO55R{vKG!T8ynO-Zdctd;w*?UAaV4dY9^chd0MFA%1lFo2 ziZ{9G08eC_h)fiDH;iTXnieg_{~dt}rPrmknINXD@hycHv)dC*;hEhQM{dU6J&@KD zyoAliKOEXY1$zsVLTBgs)@~}_ZcExOL@I#iT L%8MhV^8J4Sx1AeW literal 0 HcmV?d00001 diff --git a/usr/python2.7/site-packages/oled/device.py b/usr/python2.7/site-packages/oled/device.py new file mode 100644 index 0000000..81070eb --- /dev/null +++ b/usr/python2.7/site-packages/oled/device.py @@ -0,0 +1,410 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2016 Richard Hull and contributors +# See LICENSE.rst for details. + +# Example usage: +# +# from oled.serial import i2c, spi +# from oled.device import ssd1306, sh1106 +# from oled.render import canvas +# from PIL import ImageDraw +# +# serial = i2c(port=1, address=0x3C) +# device = ssd1306(serial) +# +# with canvas(device) as draw: +# draw.rectangle(device.bounding_box, outline="white", fill="black") +# draw.text(30, 40, "Hello World", fill="white") +# +# As soon as the with-block scope level is complete, the graphics primitives +# will be flushed to the device. +# +# Creating a new canvas is effectively 'carte blanche': If you want to retain +# an existing canvas, then make a reference like: +# +# c = canvas(device) +# for X in ...: +# with c as draw: +# draw.rectangle(...) +# +# As before, as soon as the with block completes, the canvas buffer is flushed +# to the device + +import atexit + +from oled.serial import i2c +import oled.mixin as mixin +import oled.error +import oled.const + + +class device(mixin.capabilities): + """ + Base class for OLED driver classes + + .. warning:: + Direct use of the :func:`command` and :func:`data` methods are + discouraged: Screen updates should be effected through the + :func:`display` method, or preferably with the + :class:`oled.render.canvas` context manager. + """ + def __init__(self, const=None, serial_interface=None): + self._const = const or oled.const.common + self._serial_interface = serial_interface or i2c() + atexit.register(self.cleanup) + + def command(self, *cmd): + """ + Sends a command or sequence of commands through to the delegated + serial interface. + """ + self._serial_interface.command(*cmd) + + def data(self, data): + """ + Sends a data byte or sequence of data bytes through to the delegated + serial interface. + """ + self._serial_interface.data(data) + + def show(self): + """ + Sets the display mode ON, waking the device out of a prior + low-power sleep mode. + """ + self.command(self._const.DISPLAYON) + + def hide(self): + """ + Switches the display mode OFF, putting the device in low-power + sleep mode. + """ + self.command(self._const.DISPLAYOFF) + + def contrast(self, level): + """ + Switches the display contrast to the desired level, in the range + 0-255. Note that setting the level to a low (or zero) value will + not necessarily dim the display to nearly off. In other words, + this method is **NOT** suitable for fade-in/out animation. + + :param level: Desired contrast level in the range of 0-255. + :type level: int + """ + assert(level >= 0) + assert(level <= 255) + self.command(self._const.SETCONTRAST, level) + + def cleanup(self): + self.hide() + self.clear() + self._serial_interface.cleanup() + + +class sh1106(device): + """ + Encapsulates the serial interface to the monochrome SH1106 OLED display + hardware. On creation, an initialization sequence is pumped to the display + to properly configure it. Further control commands can then be called to + affect the brightness and other settings. + """ + def __init__(self, serial_interface=None, width=128, height=64, rotate=0): + super(sh1106, self).__init__(oled.const.sh1106, serial_interface) + self.capabilities(width, height, rotate) + self._pages = self._h // 8 + + # FIXME: Delay doing anything here with alternate screen sizes + # until we are able to get a device to test with. + if width != 128 or height != 64: + raise oled.error.DeviceDisplayModeError( + "Unsupported display mode: {0} x {1}".format(width, height)) + + self.command( + self._const.DISPLAYOFF, + self._const.MEMORYMODE, + self._const.SETHIGHCOLUMN, 0xB0, 0xC8, + self._const.SETLOWCOLUMN, 0x10, 0x40, + self._const.SETSEGMENTREMAP, + self._const.NORMALDISPLAY, + self._const.SETMULTIPLEX, 0x3F, + self._const.DISPLAYALLON_RESUME, + self._const.SETDISPLAYOFFSET, 0x00, + self._const.SETDISPLAYCLOCKDIV, 0xF0, + self._const.SETPRECHARGE, 0x22, + self._const.SETCOMPINS, 0x12, + self._const.SETVCOMDETECT, 0x20, + self._const.CHARGEPUMP, 0x14) + + self.contrast(0x7F) + self.clear() + self.show() + + def display(self, image): + """ + Takes a 1-bit :py:mod:`PIL.Image` and dumps it to the SH1106 + OLED display. + """ + assert(image.mode == self.mode) + assert(image.size == self.size) + + image = self.preprocess(image) + + set_page_address = 0xB0 + image_data = image.getdata() + pixels_per_page = self.width * 8 + buf = bytearray(self.width) + + for y in range(0, int(self._pages * pixels_per_page), pixels_per_page): + self.command(set_page_address, 0x02, 0x10) + set_page_address += 1 + offsets = [y + self.width * i for i in range(8)] + + for x in range(self.width): + buf[x] = \ + (image_data[x + offsets[0]] and 0x01) | \ + (image_data[x + offsets[1]] and 0x02) | \ + (image_data[x + offsets[2]] and 0x04) | \ + (image_data[x + offsets[3]] and 0x08) | \ + (image_data[x + offsets[4]] and 0x10) | \ + (image_data[x + offsets[5]] and 0x20) | \ + (image_data[x + offsets[6]] and 0x40) | \ + (image_data[x + offsets[7]] and 0x80) + + self.data(list(buf)) + + +class ssd1306(device): + """ + Encapsulates the serial interface to the monochrome SSD1306 OLED display + hardware. On creation, an initialization sequence is pumped to the display + to properly configure it. Further control commands can then be called to + affect the brightness and other settings. + """ + def __init__(self, serial_interface=None, width=128, height=64, rotate=0): + super(ssd1306, self).__init__(oled.const.ssd1306, serial_interface) + self.capabilities(width, height, rotate) + self._pages = self._h // 8 + self._buffer = [0] * self._w * self._pages + self._offsets = [n * self._w for n in range(8)] + + # Supported modes + settings = { + (128, 64): dict(multiplex=0x3F, displayclockdiv=0x80, compins=0x12), + (128, 32): dict(multiplex=0x1F, displayclockdiv=0x80, compins=0x02), + (96, 16): dict(multiplex=0x0F, displayclockdiv=0x60, compins=0x02) + }.get((width, height)) + + if settings is None: + raise oled.error.DeviceDisplayModeError( + "Unsupported display mode: {0} x {1}".format(width, height)) + + self.command( + self._const.DISPLAYOFF, + self._const.SETDISPLAYCLOCKDIV, settings['displayclockdiv'], + self._const.SETMULTIPLEX, settings['multiplex'], + self._const.SETDISPLAYOFFSET, 0x00, + self._const.SETSTARTLINE, + self._const.CHARGEPUMP, 0x14, + self._const.MEMORYMODE, 0x00, + self._const.SETREMAP, + self._const.COMSCANDEC, + self._const.SETCOMPINS, settings['compins'], + self._const.SETPRECHARGE, 0xF1, + self._const.SETVCOMDETECT, 0x40, + self._const.DISPLAYALLON_RESUME, + self._const.NORMALDISPLAY) + + self.contrast(0xCF) + self.clear() + self.show() + + def display(self, image): + """ + Takes a 1-bit :py:mod:`PIL.Image` and dumps it to the SSD1306 + OLED display. + """ + assert(image.mode == self.mode) + assert(image.size == self.size) + + image = self.preprocess(image) + + self.command( + # Column start/end address + self._const.COLUMNADDR, 0x00, self._w - 1, + # Page start/end address + self._const.PAGEADDR, 0x00, self._pages - 1) + + w = self._w + pix = list(image.getdata()) + step = w * 8 + buf = self._buffer + os0, os1, os2, os3, os4, os5, os6, os7 = self._offsets + j = 0 + for y in range(0, self._pages * step, step): + i = y + w - 1 + while i >= y: + buf[j] = \ + (0x01 if pix[i] > 0 else 0) | \ + (0x02 if pix[i + os1] > 0 else 0) | \ + (0x04 if pix[i + os2] > 0 else 0) | \ + (0x08 if pix[i + os3] > 0 else 0) | \ + (0x10 if pix[i + os4] > 0 else 0) | \ + (0x20 if pix[i + os5] > 0 else 0) | \ + (0x40 if pix[i + os6] > 0 else 0) | \ + (0x80 if pix[i + os7] > 0 else 0) + + i -= 1 + j += 1 + + self.data(buf) + + +class ssd1331(device): + """ + Encapsulates the serial interface to the 16-bit color (5-6-5 RGB) SSD1331 + OLED display hardware. On creation, an initialization sequence is pumped to + the display to properly configure it. Further control commands can then be + called to affect the brightness and other settings. + """ + def __init__(self, serial_interface=None, width=96, height=64, rotate=0): + super(ssd1331, self).__init__(oled.const.ssd1331, serial_interface) + self.capabilities(width, height, rotate, mode="RGB") + self._buffer = [0] * self._w * self._h * 2 + + if width != 96 or height != 64: + raise oled.error.DeviceDisplayModeError( + "Unsupported display mode: {0} x {1}".format(width, height)) + + self.command( + self._const.DISPLAYOFF, + self._const.SETREMAP, 0x72, + self._const.SETDISPLAYSTARTLINE, 0x00, + self._const.SETDISPLAYOFFSET, 0x00, + self._const.NORMALDISPLAY, + self._const.SETMULTIPLEX, 0x3F, + self._const.SETMASTERCONFIGURE, 0x8E, + self._const.POWERSAVEMODE, 0x0B, + self._const.PHASE12PERIOD, 0x74, + self._const.CLOCKDIVIDER, 0xD0, + self._const.SETPRECHARGESPEEDA, 0x80, + self._const.SETPRECHARGESPEEDB, 0x80, + self._const.SETPRECHARGESPEEDC, 0x80, + self._const.SETPRECHARGEVOLTAGE, 0x3E, + self._const.SETVVOLTAGE, 0x3E, + self._const.MASTERCURRENTCONTROL, 0x0F) + + self.contrast(0xFF) + self.clear() + self.show() + + def display(self, image): + """ + Takes a 24-bit RGB :py:mod:`PIL.Image` and dumps it to the SSD1331 OLED + display. + """ + assert(image.mode == self.mode) + assert(image.size == self.size) + + image = self.preprocess(image) + + self.command( + self._const.SETCOLUMNADDR, 0x00, self._w - 1, + self._const.SETROWADDR, 0x00, self._h - 1) + + i = 0 + buf = self._buffer + for r, g, b in image.getdata(): + # 65K format 1 + buf[i] = r & 0xF8 | g >> 5 + buf[i + 1] = g << 5 & 0xE0 | b >> 3 + i += 2 + + self.data(buf) + + def contrast(self, level): + """ + Switches the display contrast to the desired level, in the range + 0-255. Note that setting the level to a low (or zero) value will + not necessarily dim the display to nearly off. In other words, + this method is **NOT** suitable for fade-in/out animation. + + :param level: Desired contrast level in the range of 0-255. + :type level: int + """ + assert(level >= 0) + assert(level <= 255) + self.command(self._const.SETCONTRASTA, level, + self._const.SETCONTRASTB, level, + self._const.SETCONTRASTC, level) + + +class ssd1325(device): + """ + Encapsulates the serial interface to the 4-bit greyscale SSD1325 OLED + display hardware. On creation, an initialization sequence is pumped to the + display to properly configure it. Further control commands can then be + called to affect the brightness and other settings. + """ + def __init__(self, serial_interface=None, width=128, height=64, rotate=0): + super(ssd1325, self).__init__(oled.const.ssd1325, serial_interface) + self.capabilities(width, height, rotate, mode="RGB") + self._buffer = [0] * (self._w * self._h // 2) + + if width != 128 or height != 64: + raise oled.error.DeviceDisplayModeError( + "Unsupported display mode: {0} x {1}".format(width, height)) + + self.command( + self._const.DISPLAYOFF, + self._const.SETCLOCK, 0xF1, + self._const.SETMULTIPLEX, 0x3F, + self._const.SETOFFSET, 0x4C, + self._const.SETSTARTLINE, 0x00, + self._const.MASTERCONFIG, 0x02, + self._const.SETREMAP, 0x50, + self._const.SETCURRENT + 2, + self._const.SETGRAYTABLE, 0x01, 0x11, 0x22, 0x32, 0x43, 0x54, 0x65, 0x76) + + self.contrast(0xFF) + + self.command( + self._const.SETROWPERIOD, 0x51, + self._const.SETPHASELEN, 0x55, + self._const.SETPRECHARGECOMP, 0x02, + self._const.SETPRECHARGECOMPENABLE, 0x28, + self._const.SETVCOMLEVEL, 0x1C, + self._const.SETVSL, 0x0F, + self._const.NORMALDISPLAY) + + self.clear() + self.show() + + def display(self, image): + """ + Takes a 24-bit RGB :py:mod:`PIL.Image` and dumps it to the SSD1325 OLED + display, converting the image pixels to 4-bit greyscale using a + simplified Luma calculation, based on *Y'=0.299R'+0.587G'+0.114B'*. + """ + assert(image.mode == self.mode) + assert(image.size == self.size) + + image = self.preprocess(image) + + self.command( + self._const.SETCOLUMNADDR, 0x00, self._w - 1, + self._const.SETROWADDR, 0x00, self._h - 1) + + i = 0 + buf = self._buffer + for r, g, b in image.getdata(): + # RGB->Greyscale luma calculation into 4-bits + grey = (r * 306 + g * 601 + b * 117) >> 14 + + if i % 2 == 0: + buf[i // 2] = grey + else: + buf[i // 2] |= (grey << 4) + + i += 1 + + self.data(buf) diff --git a/usr/python2.7/site-packages/oled/device.pyc b/usr/python2.7/site-packages/oled/device.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef9e16b424a9663468fd1c558fd889734f0fd695 GIT binary patch literal 13939 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 literal 0 HcmV?d00001 diff --git a/usr/python2.7/site-packages/oled/emulator.py b/usr/python2.7/site-packages/oled/emulator.py new file mode 100644 index 0000000..cd26a02 --- /dev/null +++ b/usr/python2.7/site-packages/oled/emulator.py @@ -0,0 +1,237 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2016 Richard Hull and contributors +# See LICENSE.rst for details. + +import os +import sys +import atexit +import logging +from PIL import Image +from oled.device import device +from oled.serial import noop + +logger = logging.getLogger(__name__) + + +class emulator(device): + """ + Base class for emulated OLED driver classes + """ + def __init__(self, width, height, rotate, mode, transform, scale): + super(emulator, self).__init__(serial_interface=noop()) + try: + import pygame + except: + raise RuntimeError("Emulator requires pygame to be installed") + self._pygame = pygame + self.capabilities(width, height, rotate, mode) + self.scale = 1 if transform == "none" else scale + self._transform = getattr(transformer(pygame, width, height, scale), + "none" if scale == 1 else transform) + + def cleanup(self): + pass + + def to_surface(self, image): + """ + Converts a :py:mod:`PIL.Image` into a :class:`pygame.Surface`, + transforming it according to the ``transform`` and ``scale`` + constructor arguments. + """ + im = image.convert("RGB") + mode = im.mode + size = im.size + data = im.tobytes() + del im + + surface = self._pygame.image.fromstring(data, size, mode) + return self._transform(surface) + + +class capture(emulator): + """ + Pseudo-device that acts like an OLED display, except that it writes + the image to a numbered PNG file when the :func:`display` method + is called. + + While the capability of an OLED device is monochrome, there is no + limitation here, and hence supports 24-bit color depth. + """ + def __init__(self, width=128, height=64, rotate=0, mode="RGB", + transform="scale2x", scale=2, file_template="oled_{0:06}.png", + **kwargs): + super(capture, self).__init__(width, height, rotate, mode, transform, scale) + self._count = 0 + self._file_template = file_template + + def display(self, image): + """ + Takes a :py:mod:`PIL.Image` and dumps it to a numbered PNG file. + """ + assert(image.size == self.size) + + self._count += 1 + filename = self._file_template.format(self._count) + image = self.preprocess(image) + surface = self.to_surface(image) + logger.debug("Writing: {0}".format(filename)) + self._pygame.image.save(surface, filename) + + +class gifanim(emulator): + """ + Pseudo-device that acts like an OLED display, except that it collects + the images when the :func:`display` method is called, and on exit, + assembles them into an animated GIF image. + + While the capability of an OLED device is monochrome, there is no + limitation here, and hence supports 24-bit color depth, albeit with + an indexed color palette. + """ + def __init__(self, width=128, height=64, rotate=0, mode="RGB", + transform="scale2x", scale=2, filename="oled_anim.gif", + duration=0.01, loop=0, max_frames=None, **kwargs): + super(gifanim, self).__init__(width, height, rotate, mode, transform, scale) + self._images = [] + self._count = 0 + self._max_frames = max_frames + self._filename = filename + self._loop = loop + self._duration = duration + atexit.register(self.write_animation) + + def display(self, image): + """ + Takes an image, scales it according to the nominated transform, and + stores it for later building into an animated GIF. + """ + assert(image.size == self.size) + + image = self.preprocess(image) + surface = self.to_surface(image) + rawbytes = self._pygame.image.tostring(surface, "RGB", False) + im = Image.frombytes("RGB", (self._w * self.scale, self._h * self.scale), rawbytes) + self._images.append(im) + + self._count += 1 + logger.debug("Recording frame: {0}".format(self._count)) + + if self._max_frames and self._count >= self._max_frames: + sys.exit(0) + + def write_animation(self): + logger.debug("Please wait... building animated GIF") + with open(self._filename, "w+b") as fp: + self._images[0].save(fp, save_all=True, loop=self._loop, + duration=int(self._duration * 1000), + append_images=self._images[1:], + format="GIF") + + logger.debug("Wrote {0} frames to file: {1} ({2} bytes)".format( + len(self._images), self._filename, os.stat(self._filename).st_size)) + + +class pygame(emulator): + """ + Pseudo-device that acts like an OLED display, except that it renders + to an displayed window. The frame rate is limited to 60FPS (much faster + than a Raspberry Pi can acheive, but this can be overridden as necessary). + + While the capability of an OLED device is monochrome, there is no + limitation here, and hence supports 24-bit color depth. + + :mod:`pygame` is used to render the emulated display window, and it's + event loop is checked to see if the ESC key was pressed or the window + was dismissed: if so :func:`sys.exit()` is called. + """ + def __init__(self, width=128, height=64, rotate=0, mode="RGB", transform="scale2x", + scale=2, frame_rate=60, **kwargs): + super(pygame, self).__init__(width, height, rotate, mode, transform, scale) + self._pygame.init() + self._pygame.font.init() + self._pygame.display.set_caption("OLED Emulator") + self._clock = self._pygame.time.Clock() + self._fps = frame_rate + self._screen = self._pygame.display.set_mode((width * self.scale, height * self.scale)) + self._screen.fill((0, 0, 0)) + self._pygame.display.flip() + + def _abort(self): + keystate = self._pygame.key.get_pressed() + return keystate[self._pygame.K_ESCAPE] or self._pygame.event.peek(self._pygame.QUIT) + + def display(self, image): + """ + Takes a :py:mod:`PIL.Image` and renders it to a pygame display surface. + """ + assert(image.size == self.size) + + image = self.preprocess(image) + self._clock.tick(self._fps) + self._pygame.event.pump() + + if self._abort(): + self._pygame.quit() + sys.exit() + + surface = self.to_surface(image) + self._screen.blit(surface, (0, 0)) + self._pygame.display.flip() + + +class dummy(emulator): + """ + Pseudo-device that acts like an OLED display, except that it does nothing + other than retain a copy of the displayed image. It is mostly useful for + testing. While the capability of an OLED device is monochrome, there is no + limitation here, and hence supports 24-bit color depth. + """ + def __init__(self, width=128, height=64, rotate=0, mode="RGB", transform="scale2x", + scale=2, **kwargs): + super(dummy, self).__init__(width, height, rotate, mode, transform, scale) + self.image = None + + def display(self, image): + """ + Takes a :py:mod:`PIL.Image` and makes a copy of it for later + use/inspection. + """ + assert(image.size == self.size) + + self.image = self.preprocess(image).copy() + + +class transformer(object): + """ + Helper class used to dispatch transformation operations. + """ + def __init__(self, pygame, width, height, scale): + self._pygame = pygame + self._output_size = (width * scale, height * scale) + self.scale = scale + + def none(self, surface): + """ + No-op transform - used when ``scale`` = 1 + """ + return surface + + def scale2x(self, surface): + """ + Scales using the AdvanceMAME Scale2X algorithm which does a + 'jaggie-less' scale of bitmap graphics. + """ + assert(self.scale == 2) + return self._pygame.transform.scale2x(surface) + + def smoothscale(self, surface): + """ + Smooth scaling using MMX or SSE extensions if available + """ + return self._pygame.transform.smoothscale(surface, self._output_size) + + def identity(self, surface): + """ + Fast scale operation that does not sample the results + """ + return self._pygame.transform.scale(surface, self._output_size) diff --git a/usr/python2.7/site-packages/oled/emulator.pyc b/usr/python2.7/site-packages/oled/emulator.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69d5020c96a9209dbda04c0a768f91b6c760c045 GIT binary patch literal 10895 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 literal 0 HcmV?d00001 diff --git a/usr/python2.7/site-packages/oled/error.py b/usr/python2.7/site-packages/oled/error.py new file mode 100644 index 0000000..8254763 --- /dev/null +++ b/usr/python2.7/site-packages/oled/error.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2016 Richard Hull and contributors +# See LICENSE.rst for details. + +""" +Exceptions for this library. +""" + + +class Error(Exception): + """ + Base class for exceptions in this library. + """ + pass + + +class DeviceNotFoundError(Error): + """ + Exception raised when a device cannot be found. + """ + + +class DevicePermissionError(Error): + """ + Exception raised when permission to access the device is denied. + """ + + +class DeviceAddressError(Error): + """ + Exception raised when an invalid device address is detected. + """ + + +class DeviceDisplayModeError(Error): + """ + Exception raised when an invalid device display mode is detected. + """ diff --git a/usr/python2.7/site-packages/oled/error.pyc b/usr/python2.7/site-packages/oled/error.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f6f164607596c355b783686be69c7ce611f6f28d GIT binary patch literal 1543 zcmchX&2H2%5XYTlH`|5}2@uN(WQj{IBoabGNC=gx?EyrE(i3}$+<3}bWbG*Sw%s#4 z0?&kaA6|eNhh!0lN&%IuGO_>b%*=1%arEO>^6l&QMa6zy5kB|uwwGumV+*tagMgg{ zOor?WW1kuOun3uq7=&J(3~I#Wv{&OnoiI7;)ty0|GP&EU$)Lte-e4!`P0HXAn&j}Z zg2pMMEk83YcS~h?txj9fZnDHpamiuZns&%PKxLoazKsSje@q)~$cmGc{{Qpm)<+Cgx1FKQ^1mAl+3^5`3ovx5t5;DYzh77Z!n4O4&IiW z#*w>^j}G!Zv@2yTe)^G^Z_vdRn9bmqJHtg)0qy{|gq|KZm;|lhdbw%mKES)@zB_Qg zma@f^MnWH54J|ZR`drjX_L!mz!#%Ep%3U*Xj0ryc4R}5PA1K?@V)M>O7zzD$EVS%G zaqL<6Z{&0?Jl>KYrzH6Pal1Ggn$fp>K@CyR?kK2Y1+D$}HuLA!%kHFlMJ_EJEparT I&13rg1PjGfI{*Lx literal 0 HcmV?d00001 diff --git a/usr/python2.7/site-packages/oled/mixin.py b/usr/python2.7/site-packages/oled/mixin.py new file mode 100644 index 0000000..8deabc7 --- /dev/null +++ b/usr/python2.7/site-packages/oled/mixin.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2016 Richard Hull and contributors +# See LICENSE.rst for details. + +from PIL import Image + + +class capabilities(object): + def capabilities(self, width, height, rotate, mode="1"): + assert mode in ("1", "RGB", "RGBA") + assert rotate in (0, 1, 2, 3) + self._w = width + self._h = height + self.width = width if rotate % 2 == 0 else height + self.height = height if rotate % 2 == 0 else width + self.size = (self.width, self.height) + self.bounding_box = (0, 0, self.width - 1, self.height - 1) + self.rotate = rotate + self.mode = mode + + def clear(self): + """ + Initializes the device memory with an empty (blank) image. + """ + self.display(Image.new(self.mode, self.size)) + + def preprocess(self, image): + if self.rotate == 0: + return image + + angle = self.rotate * -90 + return image.rotate(angle, expand=True).crop((0, 0, self._w, self._h)) + + def display(self, image): + raise NotImplementedError() diff --git a/usr/python2.7/site-packages/oled/mixin.pyc b/usr/python2.7/site-packages/oled/mixin.pyc new file mode 100644 index 0000000000000000000000000000000000000000..28ce32c009fd6bae711f5df74d3279b37bd40860 GIT binary patch literal 1729 zcmbtU&2A($5H8P5CYdN!6ui2iFd|2ag7 z&qyUw8#ysz%7NH{oEj;I@>XOc^25nc%8?vTx8Uyn!ZS6{0*8lnerCfSv?A~Fg{xd} z*573s!0-yq&mgQYLCp6S$p(U4NWt)$>tw_{oY|5zrVGsUWx-Up?KR{525}Ad9a)XV zKa?Qyvyp2=n&`}@w=|Z+c;<$ZB zyQ6)oP8_DH(RWJmpUx8)!aEQzy|;aEZSzgvw|(IM*=6jOTE$COhGiIIeres=GUy__ zdlM_WXfK-5HD}qPy;7&X4SBHYRJWy7Uc_H)b(-!VOv>CTX_4+jOwpq72he60zMoZY zG3(Z0**0GtJezqJ>=#{LtUxC}Yb#sMYIo(DgKnJ?1IF`@A;dhyJHh)g-Y4cMw1OUN zgyJ=te+t1W001NBTBp0RZ3eO-&kgV=6#fWeH@SHZ8!$Mpuy>z?rA>=PBdTpUth>tA zwh6Y>v!n#l3Z}&VUMo%?Ai^hTv4Unwm;%?FWldh&EDPLImf_4@R2b^PNZ-R;`T+(1 z|7QiN{omuptP0oI?Cnj)lkK_IS}&>Z4v!+AxxRLBZi}ERxqoWi?DM$27v!xX@$T?% DDW^sF literal 0 HcmV?d00001 diff --git a/usr/python2.7/site-packages/oled/render.py b/usr/python2.7/site-packages/oled/render.py new file mode 100644 index 0000000..f8629ba --- /dev/null +++ b/usr/python2.7/site-packages/oled/render.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2016 Richard Hull and contributors +# See LICENSE.rst for details. + +from PIL import Image, ImageDraw + + +class canvas(object): + """ + A canvas returns a properly-sized :py:mod:`PIL.ImageDraw` object onto + which the caller can draw upon. As soon as the with-block completes, the + resultant image is flushed onto the device. + + By default, any color (other than black) will be treated as white and + displayed on the device. However, this behaviour can be changed by adding + ``dither=True`` and the image will be converted from RGB space into a 1-bit + monochrome image where dithering is employed to differentiate colors at the + expense of resolution. + """ + def __init__(self, device, dither=False): + self.draw = None + self.image = Image.new("RGB" if dither else device.mode, device.size) + self.device = device + self.dither = dither + + def __enter__(self): + self.draw = ImageDraw.Draw(self.image) + return self.draw + + def __exit__(self, type, value, traceback): + if type is None: + + if self.dither: + self.image = self.image.convert(self.device.mode) + + # do the drawing onto the device + self.device.display(self.image) + + del self.draw # Tidy up the resources + return False # Never suppress exceptions diff --git a/usr/python2.7/site-packages/oled/render.pyc b/usr/python2.7/site-packages/oled/render.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d888a485204311eba293f29d2177ed17011d2759 GIT binary patch literal 1844 zcmb_d&2A$_5Uv?J&d+9rgdhh%tG;2R#9rWl6aiubT8M-qG~t3Th7`E zD7Wk*@JxvJ;Q`>QYJ1_#GD>%1J zDvM@a_&0G`{|d{WW7EXp5FOzV3K(7k0Ai#ZaB{GVJQ+}SJM6QSg8hlY^Fl?(%IVO# z#;ZcL&bHc_?S-#@)s?zzx0fqhT~2?x{&6Je7}HeQ*;1E5*(O-=v{}^ULWPBfM`N^O z$Et#=>RQ{3)C;e?wT&vgC~fMnxR@DR-mB8CTBC#Z=ln*TIqf?WiYBO<>!E6|=BD!t z;NgJcq0;NR)T0wIxZ0{p&kGoxtD@P$r?E~AEgU*%1Io-4<^3mWQyZgZS_P+z0C#`` zHj_TT^T$(!b-VAM8f2wPTQgGt-M=UE40^35O+u+`($LRYg_R&7F9gPAep; z-B&-mPEV&CMV!X8_Fziez%#>{JG)ZZ_gBidMTxvQ*+PAKF{^|4TiM2z3n<~8RAvL` zTE!p;%9YY6r{x4-Th;S96q-;Y+gKI!I4}nBr(d$O ziFsoiEdmoK!v%>>qc?I39>&*9O&J#{f(%HcLlmXtL-sCaUYmJ#f>ZVc+Yl>mehg{c zc{euoY}{_c!Zx3cz8HJ(;R5x)2lf2e8eNT1!b-albdF$?=XFzuJbw?e=L<@XlaH~= zgk>zAPpR2Q5CYLM;p~?wJ&KT{aya%-3D&-msmxduz&v3Y7bW9Qf~aJtEMdI`boQ9% zAcJ;!{s9i2Yfj1gNy!4QH@I(cKtn5{`y+DK0yiP|`7RA^Jrd~C61|QorPK&F_!52J zXPd;^=^>$X`*bBYjwcTyFrM};tRRmjieMqy8f~-UdNhoS)^uR+eS*P3)96f zXmFUJ8T$B^b&D<6uejyU5YKZ^N>0<`B&UPp{*C;`$PnTCEfW=K#=j}?6E24T T0|1$fFJs1EGjBa3I_v!no;|NT literal 0 HcmV?d00001 diff --git a/usr/python2.7/site-packages/oled/serial.py b/usr/python2.7/site-packages/oled/serial.py new file mode 100644 index 0000000..fdd2f1d --- /dev/null +++ b/usr/python2.7/site-packages/oled/serial.py @@ -0,0 +1,173 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2016 Richard Hull and contributors +# See LICENSE.rst for details. + +import errno + +import oled.error + + +class i2c(object): + """ + Wrap an `I2C `_ interface to + provide data and command methods. + + :param bus: I2C bus instance. + :type bus: + :param port: I2C port number. + :type port: int + :param address: I2C address. + :type address: + :raises oled.error.DeviceAddressError: I2C device address is invalid. + :raises oled.error.DeviceNotFoundError: I2C device could not be found. + :raises oled.error.DevicePermissionError: Permission to access I2C device + denied. + + .. note:: + 1. Only one of ``bus`` OR ``port`` arguments should be supplied; + if both are, then ``bus`` takes precedence. + 2. If ``bus`` is provided, there is an implicit expectation + that it has already been opened. + """ + def __init__(self, bus=None, port=1, address=0x3C): + import smbus2 + self._cmd_mode = 0x00 + self._data_mode = 0x40 + + try: + self._addr = int(str(address), 0) + except ValueError: + raise oled.error.DeviceAddressError( + 'I2C device address invalid: {}'.format(address)) + + try: + self._bus = bus or smbus2.SMBus(port) + except (IOError, OSError) as e: + if e.errno == errno.ENOENT: + # FileNotFoundError + raise oled.error.DeviceNotFoundError( + 'I2C device not found: {}'.format(e.filename)) + elif e.errno == errno.EPERM or e.errno == errno.EACCES: + # PermissionError + raise oled.error.DevicePermissionError( + 'I2C device permission denied: {}'.format(e.filename)) + else: + raise + + def command(self, *cmd): + """ + Sends a command or sequence of commands through to the I2C address + - maximum allowed is 32 bytes in one go. + """ + assert(len(cmd) <= 32) + self._bus.write_i2c_block_data(self._addr, self._cmd_mode, list(cmd)) + + def data(self, data): + """ + Sends a data byte or sequence of data bytes through to the I2C + address - maximum allowed in one transaction is 32 bytes, so if + data is larger than this, it is sent in chunks. + """ + i = 0 + n = len(data) + write = self._bus.write_i2c_block_data + while i < n: + write(self._addr, self._data_mode, list(data[i:i + 32])) + i += 32 + + def cleanup(self): + """ + Clean up I2C resources + """ + self._bus.close() + + +class spi(object): + """ + Wraps an `SPI `_ + interface to provide data and command methods. + + * The DC pin (Data/Command select) defaults to GPIO 24 (BCM). + * The RST pin (Reset) defaults to GPIO 25 (BCM). + + :raises oled.error.DeviceNotFoundError: SPI device could not be found. + """ + def __init__(self, spi=None, gpio=None, port=0, device=0, + bus_speed_hz=8000000, bcm_DC=24, bcm_RST=25): + self._gpio = gpio or self.__rpi_gpio__() + self._spi = spi or self.__spidev__() + + try: + self._spi.open(port, device) + except (IOError, OSError) as e: + if e.errno == errno.ENOENT: + # FileNotFoundError + raise oled.error.DeviceNotFoundError('SPI device not found') + else: + raise + + self._spi.max_speed_hz = bus_speed_hz + self._bcm_DC = bcm_DC + self._bcm_RST = bcm_RST + self._cmd_mode = self._gpio.LOW # Command mode = Hold low + self._data_mode = self._gpio.HIGH # Data mode = Pull high + + self._gpio.setmode(self._gpio.BCM) + self._gpio.setup(self._bcm_DC, self._gpio.OUT) + self._gpio.setup(self._bcm_RST, self._gpio.OUT) + self._gpio.output(self._bcm_RST, self._gpio.HIGH) # Keep RESET pulled high + + def __rpi_gpio__(self): + # RPi.GPIO _really_ doesn't like being run on anything other than + # a Raspberry Pi... this is imported here so we can swap out the + # implementation for a mock + import RPi.GPIO + return RPi.GPIO + + def __spidev__(self): + # spidev cant compile on macOS, so use a similar technique to + # initialize (mainly so the tests run unhindered) + import spidev + return spidev.SpiDev() + + def command(self, *cmd): + """ + Sends a command or sequence of commands through to the SPI device. + """ + self._gpio.output(self._bcm_DC, self._cmd_mode) + self._spi.xfer2(list(cmd)) + + def data(self, data): + """ + Sends a data byte or sequence of data bytes through to the SPI device. + If the data is more than 4Kb in size, it is sent in chunks. + """ + self._gpio.output(self._bcm_DC, self._data_mode) + i = 0 + n = len(data) + write = self._spi.xfer2 + while i < n: + write(data[i:i + 4096]) + i += 4096 + + def cleanup(self): + """ + Clean up SPI & GPIO resources + """ + self._spi.close() + self._gpio.cleanup() + + +class noop(object): + """ + Does nothing, used for pseudo-devices / emulators, which dont have a serial + interface. + """ + def command(self, *cmd): + pass + + def data(self, data): + pass + + def cleanup(self): + pass diff --git a/usr/python2.7/site-packages/oled/serial.pyc b/usr/python2.7/site-packages/oled/serial.pyc new file mode 100644 index 0000000000000000000000000000000000000000..da2e06dfc372d4d18ef81f5ccb8afa4e01d4ec24 GIT binary patch literal 6820 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 self.interval + + def paste_into(self, image, xy): + super(snapshot, self).paste_into(image, xy) + self.last_updated = time.time() + + +class terminal(object): + """ + Provides a terminal-like interface to a device (or a device-like object + that has :class:`mixin.capabilities` characteristics). + """ + def __init__(self, device, font=None, color="white", bgcolor="black", tabstop=4, line_height=None, animate=True): + self._device = device + self.font = font or ImageFont.load_default() + self.color = color + self.bgcolor = bgcolor + self.animate = animate + self.tabstop = tabstop + + self._cw, self._ch = (0, 0) + for i in range(32, 128): + w, h = self.font.getsize(chr(i)) + self._cw = max(w, self._cw) + self._ch = max(h, self._ch) + + self._ch = line_height or self._ch + self.width = device.width // self._cw + self.height = device.height // self._ch + self.size = (self.width, self.height) + self._backing_image = Image.new(self._device.mode, self._device.size, self.bgcolor) + self._canvas = ImageDraw.Draw(self._backing_image) + self.clear() + + def clear(self): + """ + Clears the display and resets the cursor position to (0, 0). + """ + self._cx, self._cy = (0, 0) + self._canvas.rectangle(self._device.bounding_box, fill=self.bgcolor) + self.flush() + + def println(self, text=""): + """ + Prints the supplied text to the device, scrolling where necessary. + The text is always followed by a newline. + """ + self.puts(text) + self.newline() + + def puts(self, text): + """ + Prints the supplied text, handling special character codes for carriage + return (\\r), newline (\\n), backspace (\\b) and tab (\\t). + + If the ``animate`` flag was set to True (default), then each character + is flushed to the device, giving the effect of 1970's teletype device. + """ + for line in str(text).split("\n"): + for char in line: + if char == '\r': + self.carriage_return() + + elif char == '\n': + self.newline() + + elif char == '\b': + self.backspace() + + elif char == '\t': + self.tab() + + else: + self.putch(char, flush=self.animate) + + def putch(self, ch, flush=True): + """ + Prints the specific character, which must be a valid printable ASCII + value in the range 32..127 only. + """ + assert(32 <= ord(ch) <= 127) + + w = self.font.getsize(ch)[0] + if self._cx + w >= self._device.width: + self.newline() + + self.erase() + self._canvas.text((self._cx, self._cy), text=ch, font=self.font, fill=self.color) + + self._cx += w + if flush: + self.flush() + + def carriage_return(self): + """ + Returns the cursor position to the left-hand side without advancing + downwards. + """ + self._cx = 0 + + def tab(self): + """ + Advances the cursor position to the next (soft) tabstop. + """ + soft_tabs = self.tabstop - ((self._cx // self._cw) % self.tabstop) + for _ in range(soft_tabs): + self.putch(" ", flush=False) + + def newline(self): + """ + Advances the cursor position ot the left hand side, and to the next + line. If the cursor is on the lowest line, the displayed contents are + scrolled, causing the top line to be lost. + """ + self.carriage_return() + + if self._cy + (2 * self._ch) >= self._device.height: + # Simulate a vertical scroll + copy = self._backing_image.crop((0, self._ch, self._device.width, self._device.height)) + self._backing_image.paste(copy, (0, 0)) + self._canvas.rectangle((0, copy.height, self._device.width, self._device.height), fill=self.bgcolor) + else: + self._cy += self._ch + + self.flush() + if self.animate: + time.sleep(0.2) + + def backspace(self): + """ + Moves the cursor one place to the left, erasing the character at the + current position. Cannot move beyound column zero, nor onto the + previous line + """ + if self._cx + self._cw >= 0: + self.erase() + self._cx -= self._cw + + self.flush() + + def erase(self): + """ + Erase the contents of the cursor's current postion without moving the + cursor's position. + """ + self._canvas.rectangle((self._cx, self._cy, self._cx + self._cw, self._cy + self._ch), fill=self.bgcolor) + + def flush(self): + """ + Cause the current backing store to be rendered on the nominated device. + """ + self._device.display(self._backing_image) + + +class history(mixin.capabilities): + """ + Wraps a device (or emulator) to provide a facility to be able to make a + savepoint (a point at which the screen display can be "rolled-back" to). + + This is mostly useful for displaying transient error/dialog messages + which could be subsequently dismissed, reverting back to the previous + display. + """ + def __init__(self, device): + self.capabilities(device.width, device.height, rotate=0, mode=device.mode) + self._savepoints = [] + self._device = device + self._last_image = None + + def display(self, image): + self._last_image = image.copy() + self._device.display(image) + + def savepoint(self): + """ + Copies the last displayed image. + """ + if self._last_image: + self._savepoints.append(self._last_image) + self._last_image = None + + def restore(self, drop=0): + """ + Restores the last savepoint. If ``drop`` is supplied and greater than + zero, then that many savepoints are dropped, and the next savepoint is + restored. + """ + assert(drop >= 0) + while drop > 0: + self._savepoints.pop() + drop -= 1 + + img = self._savepoints.pop() + self.display(img) + + def __len__(self): + """ + Indication of the number of savepoints retained. + """ + return len(self._savepoints) diff --git a/usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/PKG-INFO b/usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/PKG-INFO new file mode 100644 index 0000000..49cda62 --- /dev/null +++ b/usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/PKG-INFO @@ -0,0 +1,202 @@ +Metadata-Version: 1.1 +Name: smbus2 +Version: 0.2.0 +Summary: smbus2 is a drop-in replacement for smbus-cffi/smbus-python in pure Python +Home-page: https://github.com/kplindegaard/smbus2 +Author: Karl-Petter Lindegaard +Author-email: kp.lindegaard@gmail.com +License: MIT +Description: smbus2 + ====== + A drop-in replacement for smbus-cffi/smbus-python in pure Python + + |travis| + + .. |travis| image:: https://travis-ci.org/kplindegaard/smbus2.svg?branch=master + :target: https://travis-ci.org/kplindegaard/smbus2 + + Introduction + ============ + + smbus2 is (yet another) pure Python implementation of the `python-smbus `_ package. + + It was designed from the ground up with two goals in mind: + + 1. It should be a drop-in replacement of smbus. The syntax shall be the same. + 2. Use the inherent i2c structs and unions to a greater extent than other pure Python implementations like `pysmbus `_ does. By doing so, it will be more feature complete and easier to extend. + + Currently supported features are: + + * Get i2c capabilities (I2C_FUNCS) + * read_byte + * write_byte + * read_byte_data + * write_byte_data + * read_word_data + * write_word_data + * read_i2c_block_data + * write_i2c_block_data + * i2c_rdwr - *combined write/read transactions with repeated start* + + It is developed on Python 2.7 but works without any modifications in Python 3.X too. + + SMBus code examples + =================== + + smbus2 installs next to smbus as the package, so it's not really a 100% replacement. You must change the module name. + + Example 1a: Read a byte + ----------------------- + + .. code:: python + + from smbus2 import SMBus + + # Open i2c bus 1 and read one byte from address 80, offset 0 + bus = SMBus(1) + b = bus.read_byte_data(80, 0) + print(b) + bus.close() + + Example 1b: Read a byte using 'with' + ------------------------------------ + + This is the very same example but safer to use since the smbus will be closed automatically when exiting the with block. + + .. code:: python + + from smbus2 import SMBusWrapper + + with SMBusWrapper(1) as bus: + b = bus.read_byte_data(80, 0) + print(b) + + Example 2: Read a block of data + ------------------------------- + + You can read up to 32 bytes at once. + + .. code:: python + + from smbus2 import SMBusWrapper + + with SMBusWrapper(1) as bus: + # Read a block of 16 bytes from address 80, offset 0 + block = bus.read_i2c_block_data(80, 0, 16) + # Returned value is a list of 16 bytes + print(block) + + Example 3: Write a byte + ----------------------- + + .. code:: python + + from smbus2 import SMBusWrapper + + with SMBusWrapper(1) as bus: + # Write a byte to address 80, offset 0 + data = 45 + bus.write_byte_data(80, 0, data) + + Example 4: Write a block of data + -------------------------------- + + It is possible to write 32 bytes at the time, but I have found that error-prone. Write less and add a delay in between if you run into trouble. + + .. code:: python + + from smbus2 import SMBusWrapper + + with SMBusWrapper(1) as bus: + # Write a block of 8 bytes to address 80 from offset 0 + data = [1, 2, 3, 4, 5, 6, 7, 8] + bus.write_i2c_block_data(80, 0, data) + + + I2C + === + + Starting with v0.2, the smbus2 library also has support for combined read and write transactions. *i2c_rdwr* is not really a SMBus feature but comes in handy when the master needs to: + + 1. read or write bulks of data larger than SMBus' 32 bytes limit. + 1. write some data and then read from the slave with a repeated start and no stop bit between. + + Each operation is represented by a *i2c_msg* message object. + + + Example 5: Single i2c_rdwr + -------------------------- + + .. code:: python + + from smbus2 import SMBus, ic_msg + + with SMBusWrapper(1) as bus: + # Read 64 bytes from address 80 + msg = i2c_msg.read(80, 64) + bus.i2c_rdwr(msg) + + # Write some bytes to address 80 + msg = i2c_msg.write(80, [65, 66, 67, 68]) + bus.i2c_rdwr(msg) + + + Example 6: Dual i2c_rdwr + ------------------------ + + To perform dual operations just add more i2c_msg instances to the bus call: + + .. code:: python + + from smbus2 import SMBus, ic_msg + + # Single transaction writing two bytes then read two at address 80 + write = i2c_msg.write(80, [40, 50]) + read = i2c_msg.read(80, 2) + with SMBusWrapper(1) as bus: + bus.i2c_rdwr(write, read) + + + Example 7: Access i2c_msg data + ------------------------------ + + All data is contained in the i2c_msg instances. Here are some data access alternatives. + + .. code:: python + + # 1: Convert message content to list + msg = i2c.write(60, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) + data = list(msg) # data = [1, 2, 3, ...] + print(len(data)) # => 10 + + # 2: i2c_msg is iterable + for value in msg: + print(value) + + # 3: Through i2c_msg properties + for k in range(msg.len): + print(msg.buf[k]) + + + Installation instructions + ========================= + + smbus2 is pure Python code and requires no compilation. Installation is easy: + + .. code:: bash + + python setup.py install + + Or just use pip + + .. code:: bash + + pip install smbus2 + +Keywords: smbus,smbus2,python,i2c,raspberrypi,linux +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Topic :: Utilities +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 diff --git a/usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/SOURCES.txt b/usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/SOURCES.txt new file mode 100644 index 0000000..c85dd15 --- /dev/null +++ b/usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/SOURCES.txt @@ -0,0 +1,9 @@ +README.rst +setup.cfg +setup.py +smbus2/__init__.py +smbus2/smbus2.py +smbus2.egg-info/PKG-INFO +smbus2.egg-info/SOURCES.txt +smbus2.egg-info/dependency_links.txt +smbus2.egg-info/top_level.txt \ No newline at end of file diff --git a/usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/dependency_links.txt b/usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/installed-files.txt b/usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/installed-files.txt new file mode 100644 index 0000000..f33ddd0 --- /dev/null +++ b/usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/installed-files.txt @@ -0,0 +1,8 @@ +../smbus2/smbus2.py +../smbus2/__init__.py +../smbus2/smbus2.pyc +../smbus2/__init__.pyc +top_level.txt +SOURCES.txt +PKG-INFO +dependency_links.txt diff --git a/usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/top_level.txt b/usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/top_level.txt new file mode 100644 index 0000000..fe5f39c --- /dev/null +++ b/usr/python2.7/site-packages/smbus2-0.2.0-py2.7.egg-info/top_level.txt @@ -0,0 +1 @@ +smbus2 diff --git a/usr/python2.7/site-packages/smbus2/__init__.py b/usr/python2.7/site-packages/smbus2/__init__.py new file mode 100644 index 0000000..c9bc2f1 --- /dev/null +++ b/usr/python2.7/site-packages/smbus2/__init__.py @@ -0,0 +1,3 @@ +from .smbus2 import SMBus, SMBusWrapper, i2c_msg + +__version__ = "0.2.0" diff --git a/usr/python2.7/site-packages/smbus2/__init__.pyc b/usr/python2.7/site-packages/smbus2/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b29d223210f5f747f5bffdd4ec579879b0b27ad7 GIT binary patch literal 275 zcmYL@K~BRk5JksvDTu-iSfgx+tFT~$5Mo~z>7r^jqcpLKMVv$#2Z?L2>OFgbehW}7 z`}zOT_l))N_ImMi*w*y765Oviat}1vidrJCs8r+=DiiXWN=>SPnNg!@oz4(-3P?2& zE`aswZIW>FuQp>f40a5cD7d_4FMElxWtL~l_cY2d2gL6t$qOhdV#6YSCoBNwFFT6s zJLVLY&X7k~Gs$RLw==_c_~^SldotpJeHg0xv)WrRznu-U>s(;Ykh{XWXWutdYhU6p IiNo%a6JL`y!~g&Q literal 0 HcmV?d00001 diff --git a/usr/python2.7/site-packages/smbus2/smbus2.py b/usr/python2.7/site-packages/smbus2/smbus2.py new file mode 100644 index 0000000..1c50314 --- /dev/null +++ b/usr/python2.7/site-packages/smbus2/smbus2.py @@ -0,0 +1,451 @@ +"""smbus2 - A drop-in replacement for smbus-cffi/smbus-python""" +# The MIT License (MIT) +# Copyright (c) 2017 Karl-Petter Lindegaard +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation 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 +# furnished 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 FOR 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 sys +from fcntl import ioctl +from ctypes import c_uint32, c_uint8, c_uint16, c_char, POINTER, Structure, Array, Union, create_string_buffer + + +# Commands from uapi/linux/i2c-dev.h +I2C_SLAVE = 0x0703 # Use this slave address +I2C_SLAVE_FORCE = 0x0706 # Use this slave address, even if it is already in use by a driver! +I2C_FUNCS = 0x0705 # Get the adapter functionality mask +I2C_RDWR = 0x0707 # Combined R/W transfer (one STOP only) +I2C_SMBUS = 0x0720 # SMBus transfer. Takes pointer to i2c_smbus_ioctl_data + +# SMBus transfer read or write markers from uapi/linux/i2c.h +I2C_SMBUS_WRITE = 0 +I2C_SMBUS_READ = 1 + +# Size identifiers uapi/linux/i2c.h +I2C_SMBUS_BYTE = 1 +I2C_SMBUS_BYTE_DATA = 2 +I2C_SMBUS_WORD_DATA = 3 +I2C_SMBUS_BLOCK_DATA = 5 # Can't get this one to work on my Raspberry Pi +I2C_SMBUS_I2C_BLOCK_DATA = 8 +I2C_SMBUS_BLOCK_MAX = 32 + +# To determine what functionality is present (uapi/linux/i2c.h) +I2C_FUNC_I2C = 0x00000001 +I2C_FUNC_10BIT_ADDR = 0x00000002 +I2C_FUNC_PROTOCOL_MANGLING = 0x00000004 # I2C_M_IGNORE_NAK etc. +I2C_FUNC_SMBUS_PEC = 0x00000008 +I2C_FUNC_NOSTART = 0x00000010 # I2C_M_NOSTART +I2C_FUNC_SLAVE = 0x00000020 +I2C_FUNC_SMBUS_BLOCK_PROC_CALL = 0x00008000 # SMBus 2.0 +I2C_FUNC_SMBUS_QUICK = 0x00010000 +I2C_FUNC_SMBUS_READ_BYTE = 0x00020000 +I2C_FUNC_SMBUS_WRITE_BYTE = 0x00040000 +I2C_FUNC_SMBUS_READ_BYTE_DATA = 0x00080000 +I2C_FUNC_SMBUS_WRITE_BYTE_DATA = 0x00100000 +I2C_FUNC_SMBUS_READ_WORD_DATA = 0x00200000 +I2C_FUNC_SMBUS_WRITE_WORD_DATA = 0x00400000 +I2C_FUNC_SMBUS_PROC_CALL = 0x00800000 +I2C_FUNC_SMBUS_READ_BLOCK_DATA = 0x01000000 +I2C_FUNC_SMBUS_WRITE_BLOCK_DATA = 0x02000000 +I2C_FUNC_SMBUS_READ_I2C_BLOCK = 0x04000000 # I2C-like block xfer +I2C_FUNC_SMBUS_WRITE_I2C_BLOCK = 0x08000000 # w/ 1-byte reg. addr. + +# i2c_msg flags from uapi/linux/i2c.h +I2C_M_RD = 0x0001 + +# Pointer definitions +LP_c_uint8 = POINTER(c_uint8) +LP_c_uint16 = POINTER(c_uint16) +LP_c_uint32 = POINTER(c_uint32) + + +############################################################# +# Type definitions as in i2c.h + + +class i2c_smbus_data(Array): + """ + Adaptation of the i2c_smbus_data union in i2c.h + + Data for SMBus messages. + """ + _length_ = I2C_SMBUS_BLOCK_MAX+2 + _type_ = c_uint8 + + +class union_i2c_smbus_data(Union): + _fields_ = [ + ("byte", c_uint8), + ("word", c_uint16), + ("block", i2c_smbus_data) + ] + +union_pointer_type = POINTER(union_i2c_smbus_data) + + +class i2c_smbus_ioctl_data(Structure): + """ + As defined in i2c-dev.h + """ + _fields_ = [ + ('read_write', c_uint8), + ('command', c_uint8), + ('size', c_uint32), + ('data', union_pointer_type)] + __slots__ = [name for name, type in _fields_] + + @staticmethod + def create(read_write=I2C_SMBUS_READ, command=0, size=I2C_SMBUS_BYTE_DATA): + u = union_i2c_smbus_data() + return i2c_smbus_ioctl_data( + read_write=read_write, command=command, size=size, + data=union_pointer_type(u)) + + +############################################################# +# Type definitions for i2c_rdwr combined transactions + + +class i2c_msg(Structure): + """ + As defined in i2c.h + """ + _fields_ = [ + ('addr', c_uint16), + ('flags', c_uint16), + ('len', c_uint16), + ('buf', POINTER(c_char))] + __slots__ = [name for name, type in _fields_] + + def __iter__(self): + return i2c_msg_iter(self) + + @staticmethod + def read(address, length): + """ + Prepares an i2c read transaction + :param address: Slave address + :param length: Number of bytes to read + :return: New i2c_msg instance for read operation + :rtype: i2c_msg + """ + arr = create_string_buffer(length) + return i2c_msg( + addr=address, flags=I2C_M_RD, len=length, + buf=arr) + + @staticmethod + def write(address, buf): + """ + Prepares an i2c write transaction + :param address: Slave address + :param buf: Bytes to write. Either list of values or string + :return: New i2c_msg instance for write operation + :rtype: i2c_msg + """ + if sys.version_info.major >= 3: + if type(buf) is str: + buf = bytes(buf, 'UTF-8') + else: + buf = bytes(buf) + else: + if type(buf) is not str: + buf = ''.join([chr(x) for x in buf]) + arr = create_string_buffer(buf, len(buf)) + return i2c_msg( + addr=address, flags=0, len=len(arr), + buf=arr) + + +class i2c_rdwr_ioctl_data(Structure): + """ + As defined in i2c-dev.h + """ + _fields_ = [ + ('msgs', POINTER(i2c_msg)), + ('nmsgs', c_uint32) + ] + __slots__ = [name for name, type in _fields_] + + @staticmethod + def create(*i2c_msg_instances): + """ + Factory method for creating a i2c_rdwr_ioctl_data struct that can + be called with ioctl(fd, I2C_RDWR, data) + :param i2c_msg_instances: Up to 42 i2c_msg instances + :return: + :rtype: i2c_rdwr_ioctl_data + """ + n_msg = len(i2c_msg_instances) + msg_array = (i2c_msg * n_msg)(*i2c_msg_instances) + return i2c_rdwr_ioctl_data( + msgs=msg_array, + nmsgs=n_msg + ) + +class i2c_msg_iter: + """ + i2c_msg iterator. For convenience. + """ + + def __init__(self, msg): + self.msg = msg + self.idx = 0 + + def __iter__(self): + return self + + def __next__(self): + if self.idx < self.msg.len: + val = ord(self.msg.buf[self.idx]) + self.idx += 1 + return val + else: + raise StopIteration() + + def next(self): + return self.__next__() + +############################################################# + + +class SMBus(object): + + def __init__(self, bus=None, force=False): + # type: (int, bool) -> None + """ + Initialize and (optionally) open an i2c bus connection. + :param bus: i2c bus number (e.g. 0 or 1). If not given, a subsequent call to open() is required. + :param force: force using the slave address even when driver is already using it + :type force: Boolean + """ + self.fd = None + self.funcs = 0 + if bus is not None: + self.open(bus) + self.address = None + self.force = force + + def open(self, bus): + # type: (int) -> None + """ + Open a given i2c bus. + :param bus: i2c bus number (e.g. 0 or 1) + """ + self.fd = os.open("/dev/i2c-{}".format(bus), os.O_RDWR) + self.funcs = self._get_funcs() + + def close(self): + """ + Close the i2c connection. + """ + if self.fd: + os.close(self.fd) + self.fd = None + + def _set_address(self, address): + # type: (int) -> None + """ + Set i2c slave address to use for subsequent calls. + :param address: + """ + if self.address != address: + self.address = address + if self.force: + ioctl(self.fd, I2C_SLAVE_FORCE, address) + else: + ioctl(self.fd, I2C_SLAVE, address) + + def _get_funcs(self): + """ + Returns a 32-bit value stating supported I2C functions. + :rtype: int + """ + f = c_uint32() + ioctl(self.fd, I2C_FUNCS, f) + return f.value + + def read_byte(self, i2c_addr): + # type: (int) -> int + """ + Read a single byte from a device + :rtype: int + :param i2c_addr: i2c address + :return: Read byte value + """ + self._set_address(i2c_addr) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_READ, command=0, size=I2C_SMBUS_BYTE + ) + ioctl(self.fd, I2C_SMBUS, msg) + return msg.data.contents.byte + + def write_byte(self, i2c_addr, value): + # type: (int, int) -> None + """ + Write a single byte to a device + :param i2c_addr: i2c address + :param value: value to write + """ + self._set_address(i2c_addr) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_WRITE, command=value, size=I2C_SMBUS_BYTE + ) + ioctl(self.fd, I2C_SMBUS, msg) + + def read_byte_data(self, i2c_addr, register): + # type: (int, int) -> int + """ + Read a single byte from a designated register. + :rtype: int + :param i2c_addr: i2c address + :param register: Register to read + :return: Read byte value + """ + self._set_address(i2c_addr) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_READ, command=register, size=I2C_SMBUS_BYTE_DATA + ) + ioctl(self.fd, I2C_SMBUS, msg) + return msg.data.contents.byte + + def write_byte_data(self, i2c_addr, register, value): + # type: (int, int, int) -> None + """ + Write a byte to a given register + :param i2c_addr: i2c address + :param register: Register to write to + :param value: Byte value to transmit + """ + self._set_address(i2c_addr) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_BYTE_DATA + ) + msg.data.contents.byte = value + ioctl(self.fd, I2C_SMBUS, msg) + + def read_word_data(self, i2c_addr, register): + # type: (int, int) -> int + """ + Read a single word (2 bytes) from a given register + :rtype: int + :param i2c_addr: i2c address + :param register: Register to read + :return: 2-byte word + """ + self._set_address(i2c_addr) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_READ, command=register, size=I2C_SMBUS_WORD_DATA + ) + ioctl(self.fd, I2C_SMBUS, msg) + return msg.data.contents.word + + def write_word_data(self, i2c_addr, register, value): + # type: (int, int, int) -> None + """ + Write a byte to a given register + :param i2c_addr: i2c address + :param register: Register to write to + :param value: Word value to transmit + """ + self._set_address(i2c_addr) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_WORD_DATA + ) + msg.data.contents.word = value + ioctl(self.fd, I2C_SMBUS, msg) + + def read_i2c_block_data(self, i2c_addr, register, length): + # type: (int, int, int) -> list + """ + Read a block of byte data from a given register + :rtype: list + :param i2c_addr: i2c address + :param register: Start register + :param length: Desired block length + :return: List of bytes + """ + if length > I2C_SMBUS_BLOCK_MAX: + raise ValueError("Desired block length over %d bytes" % I2C_SMBUS_BLOCK_MAX) + self._set_address(i2c_addr) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_READ, command=register, size=I2C_SMBUS_I2C_BLOCK_DATA + ) + msg.data.contents.byte = length + ioctl(self.fd, I2C_SMBUS, msg) + return msg.data.contents.block[1:length+1] + + def write_i2c_block_data(self, i2c_addr, register, data): + # type: (int, int, list) -> None + """ + Write a block of byte data to a given register + :param i2c_addr: i2c address + :param register: Start register + :param data: List of bytes + """ + length = len(data) + if length > I2C_SMBUS_BLOCK_MAX: + raise ValueError("Data length cannot exceed %d bytes" % I2C_SMBUS_BLOCK_MAX) + self._set_address(i2c_addr) + msg = i2c_smbus_ioctl_data.create( + read_write=I2C_SMBUS_WRITE, command=register, size=I2C_SMBUS_I2C_BLOCK_DATA + ) + msg.data.contents.block[0] = length + msg.data.contents.block[1:length + 1] = data + ioctl(self.fd, I2C_SMBUS, msg) + + def i2c_rdwr(self, *i2c_msgs): + # type: (i2c_msg) -> None + """ + Combine a series of i2c read and write operations in a single + transaction (with repeted start bits but no stop bits in between). + This method takes i2c_msg instances as input, which must be created + first with i2c_msg.create_read() or i2c_msg.create_write(). + :type i2c_msgs: i2c_msg + :param i2c_msgs: One or more i2c_msg class instances. + :return: None + """ + ioctl_data = i2c_rdwr_ioctl_data.create(*i2c_msgs) + ioctl(self.fd, I2C_RDWR, ioctl_data) + + +class SMBusWrapper: + """ + Wrapper class around the SMBus. Enables the user to wrap access to + the SMBus class in a "with" statement. Will automatically close the SMBus handle upon + exit of the with block. + """ + def __init__(self, bus_number=0, auto_cleanup=True, force=False): + """ + :param auto_cleanup: Close bus when leaving scope. + :type auto_cleanup: Boolean + :param force: Force using the slave address even when driver is already using it. + :type force: Boolean + """ + self.bus_number = bus_number + self.auto_cleanup = auto_cleanup + self.force = force + + def __enter__(self): + self.bus = SMBus(bus=self.bus_number, force=self.force) + return self.bus + + def __exit__(self, exc_type, exc_val, exc_tb): + if self.auto_cleanup: + self.bus.close() diff --git a/usr/python2.7/site-packages/smbus2/smbus2.pyc b/usr/python2.7/site-packages/smbus2/smbus2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c14706822d45ec8ecffb3a79645b05cdd7fbcc8c GIT binary patch literal 15393 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 literal 0 HcmV?d00001