Skip to content

Commit

Permalink
Merge 37afddc into 74de5aa
Browse files Browse the repository at this point in the history
  • Loading branch information
rm-hull committed Dec 21, 2018
2 parents 74de5aa + 37afddc commit 93fee47
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 21 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Expand Up @@ -4,6 +4,8 @@ ChangeLog
+------------+---------------------------------------------------------------------+------------+
| Version | Description | Date |
+============+=====================================================================+============+
| *upcoming* | * Add support for 128x64 monochrome OLED (SSD1309) | |
+------------+---------------------------------------------------------------------+------------+
| **3.0.1** | * Fix bug where SSD1325/1327 didn't handle ``framebuffer`` properly | |
+------------+---------------------------------------------------------------------+------------+
| **3.0.0** | * **BREAKING** Fix SSD1351 init sequence didn't set RGB/BGR color | 2018/12/02 |
Expand Down
8 changes: 4 additions & 4 deletions README.rst
Expand Up @@ -8,7 +8,7 @@ luma.oled

Luma.OLED
---------
**Display drivers for SSD1306 / SSD1322 / SSD1325 / SSD1327 / SSD1331 / SSD1351 / SH1106**
**Display drivers for SSD1306 / SSD1309 / SSD1322 / SSD1325 / SSD1327 / SSD1331 / SSD1351 / SH1106**

.. image:: https://travis-ci.org/rm-hull/luma.oled.svg?branch=master
:target: https://travis-ci.org/rm-hull/luma.oled
Expand All @@ -27,9 +27,9 @@ Luma.OLED

.. image:: https://img.shields.io/maintenance/yes/2018.svg?maxAge=2592000

Python library interfacing OLED matrix displays with the SSD1306, SSD1322,
SSD1325, SSD1327, SSD1331, SSD1351 or SH1106 driver using I2C/SPI on the
Raspberry Pi and other linux-based single-board computers - it provides a
Python library interfacing OLED matrix displays with the SSD1306, SSD1309,
SSD1322, SSD1325, SSD1327, SSD1331, SSD1351 or SH1106 driver using I2C/SPI on
the Raspberry Pi and other linux-based single-board computers - it provides a
Pillow-compatible drawing canvas, and other functionality to support:

* scrolling/panning capability,
Expand Down
4 changes: 2 additions & 2 deletions doc/index.rst
@@ -1,5 +1,5 @@
Luma.OLED: Display drivers for SSD1306 / SSD1322 / SSD1325 / SSD1327 / SSD1331 / SSD1351 / SH1106
=================================================================================================
Luma.OLED: Display drivers for SSD1306 / SSD1309 / SSD1322 / SSD1325 / SSD1327 / SSD1331 / SSD1351 / SH1106
===========================================================================================================
.. image:: https://travis-ci.org/rm-hull/luma.oled.svg?branch=master
:target: https://travis-ci.org/rm-hull/luma.oled

Expand Down
9 changes: 5 additions & 4 deletions doc/intro.rst
Expand Up @@ -2,10 +2,10 @@ Introduction
------------
Interfacing `OLED matrix displays
<https://github.com/rm-hull/luma.oled/wiki/Usage-&-Benchmarking>`_ with the
SSD1306, SSD1322, SSD1325, SSD1327, SSD1331, SSD1351 or SH1106 driver in Python
2 or 3 using I2C/SPI on the Raspberry Pi and other linux-based single-board
computers: the library provides a Pillow-compatible drawing canvas, and other
functionality to support:
SSD1306, SSD1309, SSD1322, SSD1325, SSD1327, SSD1331, SSD1351 or SH1106 driver
in Python 2 or 3 using I2C/SPI on the Raspberry Pi and other linux-based
single-board computers: the library provides a Pillow-compatible drawing
canvas, and other functionality to support:

* scrolling/panning capability,
* terminal-style printing,
Expand All @@ -27,6 +27,7 @@ and will fit neatly inside the RPi case.
in the following datasheets:

- :download:`SSD1306 <tech-spec/SSD1306.pdf>`
- :download:`SSD1309 <tech-spec/SSD1309.pdf>`
- :download:`SSD1322 <tech-spec/SSD1322.pdf>`
- :download:`SSD1325 <tech-spec/SSD1325.pdf>`
- :download:`SSD1327 <tech-spec/SSD1327.pdf>`
Expand Down
2 changes: 1 addition & 1 deletion doc/python-usage.rst
Expand Up @@ -11,7 +11,7 @@ First, import and initialise the device:
from luma.core.interface.serial import i2c, spi
from luma.core.render import canvas
from luma.oled.device import ssd1306, ssd1325, ssd1331, sh1106
from luma.oled.device import ssd1306, ssd1309, ssd1325, ssd1331, sh1106
# rev.1 users set port=0
# substitute spi(device=0, port=0) below if using that interface
Expand Down
Binary file added doc/tech-spec/SSD1309.pdf
Binary file not shown.
4 changes: 2 additions & 2 deletions luma/oled/__init__.py
Expand Up @@ -3,8 +3,8 @@
# See LICENSE.rst for details.

"""
OLED display driver for SSD1306, SSD1322, SSD1325, SSD1327, SSD1331, SSD1351
and SH1106 devices.
OLED display driver for SSD1306, SSD1309, SSD1322, SSD1325, SSD1327, SSD1331,
SSD1351 and SH1106 devices.
"""

__version__ = '3.0.1'
48 changes: 42 additions & 6 deletions luma/oled/device/__init__.py
Expand Up @@ -42,7 +42,7 @@
import luma.oled.const


__all__ = ["ssd1306", "ssd1322", "ssd1325", "ssd1327", "ssd1331", "ssd1351", "sh1106"]
__all__ = ["ssd1306", "ssd1309", "ssd1322", "ssd1325", "ssd1327", "ssd1331", "ssd1351", "sh1106"]


class sh1106(device):
Expand Down Expand Up @@ -129,6 +129,18 @@ class ssd1306(device):
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.
:param serial_interface: the serial interface (usually a
:py:class:`luma.core.interface.serial.i2c` instance) to delegate sending
data and commands through.
:param width: the number of horizontal pixels (optional, defaults to 128).
:type width: int
:param height: the number of vertical pixels (optional, defaults to 64).
:type height: int
:param rotate: an integer value of 0 (default), 1, 2 or 3 only, where 0 is
no rotation, 1 is rotate 90° clockwise, 2 is 180° rotation and 3
represents 270° rotation.
:type rotate: int
"""
def __init__(self, serial_interface=None, width=128, height=64, rotate=0, **kwargs):
super(ssd1306, self).__init__(luma.oled.const.ssd1306, serial_interface)
Expand Down Expand Up @@ -175,8 +187,8 @@ def __init__(self, serial_interface=None, width=128, height=64, rotate=0, **kwar

def display(self, image):
"""
Takes a 1-bit :py:mod:`PIL.Image` and dumps it to the SSD1306
OLED display.
Takes a 1-bit :py:mod:`PIL.Image` and dumps it to the OLED
display.
"""
assert(image.mode == self.mode)
assert(image.size == self.size)
Expand All @@ -202,6 +214,30 @@ def display(self, image):
self.data(list(buf))


class ssd1309(ssd1306):
"""
Serial interface to a monochrome SSD1309 OLED display.
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.
:param serial_interface: the serial interface (usually a
:py:class:`luma.core.interface.serial.spi` instance) to delegate sending
data and commands through.
:param width: the number of horizontal pixels (optional, defaults to 128).
:type width: int
:param height: the number of vertical pixels (optional, defaults to 64).
:type height: int
:param rotate: an integer value of 0 (default), 1, 2 or 3 only, where 0 is
no rotation, 1 is rotate 90° clockwise, 2 is 180° rotation and 3
represents 270° rotation.
:type rotate: int
.. versionadded:: 3.1.0
"""


class ssd1331(color_device):
"""
Serial interface to a 16-bit color (5-6-5 RGB) SSD1331 OLED display.
Expand All @@ -211,7 +247,7 @@ class ssd1331(color_device):
called to affect the brightness and other settings.
:param serial_interface: the serial interface (usually a
:py:class`luma.core.interface.serial.spi` instance) to delegate sending
:py:class:`luma.core.interface.serial.spi` instance) to delegate sending
data and commands through.
:param width: the number of horizontal pixels (optional, defaults to 96).
:type width: int
Expand Down Expand Up @@ -282,7 +318,7 @@ class ssd1351(color_device):
called to affect the brightness and other settings.
:param serial_interface: the serial interface (usually a
:py:class`luma.core.interface.serial.spi` instance) to delegate sending
:py:class:`luma.core.interface.serial.spi` instance) to delegate sending
data and commands through.
:param width: the number of horizontal pixels (optional, defaults to 128).
:type width: int
Expand Down Expand Up @@ -382,7 +418,7 @@ class ssd1322(greyscale_device):
called to affect the brightness and other settings.
:param serial_interface: the serial interface (usually a
:py:class`luma.core.interface.serial.spi` instance) to delegate sending
:py:class:`luma.core.interface.serial.spi` instance) to delegate sending
data and commands through.
:param width: the number of horizontal pixels (optional, defaults to 96).
:type width: int
Expand Down
5 changes: 3 additions & 2 deletions setup.py
Expand Up @@ -45,12 +45,13 @@ def find_version(*file_paths):
author="Richard Hull",
author_email="richard.hull@destructuring-bind.org",
description=("A small library to drive an OLED device with either "
"SSD1306, SSD1322, SSD1325, SSD1327, SSD1331, SSD1351 or SH1106 chipset"),
"SSD1306, SSD1309, SSD1322, SSD1325, SSD1327, SSD1331, "
"SSD1351 or SH1106 chipset"),
long_description="\n\n".join([README, CONTRIB, CHANGES]),
license="MIT",
keywords=("raspberry pi rpi oled display screen "
"rgb monochrome greyscale color "
"ssd1306 ssd1322 ssd1325 ssd1327 ssd1331 ssd1351 sh1106 "
"ssd1306 ssd1309 ssd1322 ssd1325 ssd1327 ssd1331 ssd1351 sh1106 "
"spi i2c 256x64 128x64 128x32 96x16"),
url="https://github.com/rm-hull/luma.oled",
download_url="https://github.com/rm-hull/luma.oled/tarball/" + version,
Expand Down
1 change: 1 addition & 0 deletions tests/reference/data/demo_ssd1309.json
@@ -0,0 +1 @@
[255, 1, 1, 1, 1, 1, 129, 97, 25, 9, 5, 5, 5, 5, 5, 9, 25, 97, 129, 1, 1, 1, 1, 1, 253, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 253, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 225, 29, 225, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 13, 113, 129, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 129, 113, 13, 1, 225, 225, 129, 225, 225, 33, 129, 193, 65, 193, 129, 1, 17, 17, 241, 241, 1, 1, 17, 17, 241, 241, 1, 1, 129, 193, 65, 193, 129, 1, 1, 1, 1, 1, 1, 1, 1, 255, 255, 0, 0, 0, 240, 14, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 14, 240, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 7, 0, 7, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 28, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 28, 3, 0, 0, 0, 7, 7, 0, 7, 7, 4, 3, 7, 5, 5, 5, 0, 4, 4, 7, 7, 4, 4, 4, 4, 7, 7, 4, 4, 3, 7, 4, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 192, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 192, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 128, 126, 1, 0, 0, 0, 1, 126, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 56, 192, 0, 0, 0, 0, 0, 192, 56, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 224, 31, 0, 0, 0, 0, 0, 0, 0, 31, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 14, 112, 128, 112, 14, 1, 0, 0, 0, 0, 0, 0, 0, 0, 30, 112, 62, 112, 30, 2, 56, 124, 68, 124, 56, 0, 68, 124, 120, 76, 4, 12, 65, 65, 127, 127, 64, 64, 56, 124, 69, 127, 127, 64, 0, 94, 94, 0, 0, 0, 0, 255, 255, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 248, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 112, 14, 1, 14, 112, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 1, 126, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 126, 1, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 128, 126, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 126, 128, 0, 0, 0, 0, 0, 0, 0, 0, 224, 28, 3, 0, 0, 0, 0, 0, 3, 28, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 0, 7, 56, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 56, 7, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 224, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 224, 0, 0, 0, 0, 192, 56, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 56, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 128, 128, 128, 128, 128, 128, 131, 140, 136, 144, 144, 144, 144, 144, 136, 140, 131, 128, 128, 128, 128, 128, 128, 191, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 191, 128, 184, 167, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 160, 167, 184, 128, 176, 142, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 129, 142, 176, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 255]
77 changes: 77 additions & 0 deletions tests/test_ssd1309.py
@@ -0,0 +1,77 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2014-18 Richard Hull and contributors
# See LICENSE.rst for details.

from luma.oled.device import ssd1309
from luma.core.render import canvas

from baseline_data import primitives, get_json_data
from helpers import serial, call, setup_function, assert_invalid_dimensions # noqa: F401


def test_init_128x64():
"""
SSD1309 OLED with a 128 x 64 resolution works correctly.
"""
ssd1309(serial)
serial.command.assert_has_calls([
# Initial burst are initialization commands
call(174, 213, 128, 168, 63, 211, 0, 64, 141, 20, 32, 0,
161, 200, 218, 18, 217, 241, 219, 64, 164, 166),
# set contrast
call(129, 207),
# reset the display
call(33, 0, 127, 34, 0, 7),
# called last, is a command to show the screen
call(175)
])

# Next are all data: zero's to clear the RAM
serial.data.assert_called_once_with([0] * (128 * 64 // 8))


def test_init_invalid_dimensions():
"""
SSD1309 OLED with an invalid resolution raises a
:py:class:`luma.core.error.DeviceDisplayModeError`.
"""
assert_invalid_dimensions(ssd1309, serial, 59, 22)


def test_hide():
"""
SSD1309 OLED screen content can be hidden.
"""
device = ssd1309(serial)
serial.reset_mock()
device.hide()
serial.command.assert_called_once_with(174)


def test_show():
"""
SSD1309 OLED screen content can be displayed.
"""
device = ssd1309(serial)
serial.reset_mock()
device.show()
serial.command.assert_called_once_with(175)


def test_display():
"""
SSD1309 OLED screen can draw and display an image.
"""
device = ssd1309(serial)
serial.reset_mock()

# Use the same drawing primitives as the demo
with canvas(device) as draw:
primitives(device, draw)

# Initial command to reset the display
serial.command.assert_called_once_with(33, 0, 127, 34, 0, 7)

# Next 1024 bytes are data representing the drawn image
serial.data.assert_called_once_with(get_json_data('demo_ssd1309'))

0 comments on commit 93fee47

Please sign in to comment.