Skip to content

Commit

Permalink
Merge 640910b into 722def8
Browse files Browse the repository at this point in the history
  • Loading branch information
thijstriemstra committed Dec 15, 2016
2 parents 722def8 + 640910b commit 90b4c96
Show file tree
Hide file tree
Showing 9 changed files with 175 additions and 145 deletions.
9 changes: 8 additions & 1 deletion doc/api-documentation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ API Documentation
:undoc-members:
:show-inheritance:

:mod:`oled.error`
"""""""""""""""""""
.. automodule:: oled.error
:members:
:undoc-members:
:show-inheritance:

:mod:`oled.emulator`
""""""""""""""""""""
.. automodule:: oled.emulator
Expand Down Expand Up @@ -53,4 +60,4 @@ API Documentation
.. automodule:: oled.virtual
:members:
:undoc-members:
:show-inheritance:
:show-inheritance:
22 changes: 12 additions & 10 deletions examples/demo_opts.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
# ignore PIL debug messages
logging.getLogger('PIL').setLevel(logging.ERROR)


args = parser.parse_args()

if args.config:
Expand All @@ -62,15 +61,18 @@
parser.error('invalid address %s' % args.i2c_address)

Device = getattr(oled.device, args.display)
if (args.interface == 'i2c'):
serial = oled.serial.i2c(port=args.i2c_port, address=args.i2c_address)
elif (args.interface == 'spi'):
serial = oled.serial.spi(port=args.spi_port,
device=args.spi_device,
bus_speed_hz=args.spi_bus_speed,
bcm_DC=args.bcm_data_command,
bcm_RST=args.bcm_reset)
device = Device(serial, width=args.width, height=args.height)
try:
if (args.interface == 'i2c'):
serial = oled.serial.i2c(port=args.i2c_port, address=args.i2c_address)
elif (args.interface == 'spi'):
serial = oled.serial.spi(port=args.spi_port,
device=args.spi_device,
bus_speed_hz=args.spi_bus_speed,
bcm_DC=args.bcm_data_command,
bcm_RST=args.bcm_reset)
device = Device(serial, width=args.width, height=args.height)
except Exception as e:
parser.error(e)

elif args.display in ('capture', 'pygame', 'gifanim'):
Emulator = getattr(oled.emulator, args.display)
Expand Down
216 changes: 105 additions & 111 deletions oled/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ 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
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)
Expand All @@ -125,42 +128,38 @@ class sh1106(device):
"""

def __init__(self, serial_interface=None, width=128, height=64):
try:
super(sh1106, self).__init__(oled.const.sh1106, serial_interface)
self.capabilities(width, height)
self.bounding_box = (0, 0, width - 1, height - 1)
self.width = width
self.height = height
self._pages = self.height // 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 ValueError("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()

except IOError as e:
raise IOError(e.errno,
"Failed to initialize SH1106 display driver")
super(sh1106, self).__init__(oled.const.sh1106, serial_interface)
self.capabilities(width, height)
self.bounding_box = (0, 0, width - 1, height - 1)
self.width = width
self.height = height
self._pages = self.height // 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 ValueError("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):
"""
Expand Down Expand Up @@ -207,46 +206,42 @@ class ssd1306(device):
:class:`oled.render.canvas` context manager.
"""
def __init__(self, serial_interface=None, width=128, height=64):
try:
super(ssd1306, self).__init__(oled.const.ssd1306, serial_interface)
self.capabilities(width, height)
self._pages = self.height // 8
self._buffer = [0] * self.width * self._pages
self._offsets = [n * self.width 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(self.size)

if settings is None:
raise ValueError("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()

except IOError as e:
raise IOError(e.errno,
"Failed to initialize SSD1306 display driver")
super(ssd1306, self).__init__(oled.const.ssd1306, serial_interface)
self.capabilities(width, height)
self._pages = self.height // 8
self._buffer = [0] * self.width * self._pages
self._offsets = [n * self.width 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(self.size)

if settings is None:
raise ValueError("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):
"""
Expand Down Expand Up @@ -302,39 +297,35 @@ class ssd1331(device):
:class:`oled.render.canvas` context manager.
"""
def __init__(self, serial_interface=None, width=96, height=64):
try:
super(ssd1331, self).__init__(oled.const.ssd1331, serial_interface)
self.capabilities(width, height, mode="RGB")
self._buffer = [0] * self.width * self.height * 2

if width != 96 or height != 64:
raise ValueError("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()

except IOError as e:
raise IOError(e.errno,
"Failed to initialize SSD1331 display driver")
super(ssd1331, self).__init__(oled.const.ssd1331, serial_interface)
self.capabilities(width, height, mode="RGB")
self._buffer = [0] * self.width * self.height * 2

if width != 96 or height != 64:
raise ValueError("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):
"""
Expand Down Expand Up @@ -364,7 +355,10 @@ 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
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)
Expand Down
20 changes: 20 additions & 0 deletions oled/error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- 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.
"""
23 changes: 21 additions & 2 deletions oled/serial.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,43 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import errno

import oled.error


class i2c(object):
"""
Wrap an `I2C <https://en.wikipedia.org/wiki/I%C2%B2C>`_ 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.DeviceNotFoundError: I2C device could not be found.
.. 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
self._bus = bus or smbus2.SMBus(port)
self._addr = address
try:
self._bus = bus or smbus2.SMBus(port)
except OSError as e:
if e.errno == errno.ENOENT:
raise oled.error.DeviceNotFoundError(
'I2C device not found: {}'.format(e.filename))
else:
raise

def command(self, *cmd):
"""
Expand Down
9 changes: 9 additions & 0 deletions tests/test_serial.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
except ImportError:
from mock import patch, call, Mock

import pytest
import smbus2
from oled.serial import i2c, spi
import oled.error

smbus = Mock(unsafe=True)
spidev = Mock(unsafe=True)
Expand All @@ -33,6 +35,13 @@ def fib(n):
a, b = b, a + b


def test_i2c_init_device_not_found():
port = 200
with pytest.raises(oled.error.DeviceNotFoundError) as ex:
i2c(port=port, address=0x710)
assert str(ex.value) == 'I2C device not found: /dev/i2c-{}'.format(port)


def test_i2c_init_no_bus():
with patch.object(smbus2.SMBus, 'open') as mock:
i2c(port=2, address=0x71)
Expand Down

0 comments on commit 90b4c96

Please sign in to comment.