luma.oled icon indicating copy to clipboard operation
luma.oled copied to clipboard

support for ssd1107/sh1107

Open Pako2 opened this issue 3 years ago • 2 comments

I recently bought a new OLED display on Aliexpress: https://www.aliexpress.com/item/4000547865501.html The antistatic bag was labelled SH1107. The display has a resolution of 64x128 (it is vertical). Datasheet is here: https://www.displayfuture.com/Display/datasheet/controller/SH1107.pdf I tried to use the luma.oled library for control, but failed. Can you please add support for this display as well? I can test. I tried to make some modifications in the library myself, but to no avail. For example, I tried to add a resolution of 64x128 to the SH1106 class (because some image, but shifted, could be achieved using this class), but failed. I didn't understand what the multiplex and displayoffset parameters were for.

Thanks.

Pako2 avatar Apr 19 '22 05:04 Pako2

In the end, I did it for my own needs myself. If anyone is interested, here is my new class SH1107:

class sh1107(device):
    """
    Serial interface to a monochrome SH1107 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.
    """

    def __init__(self, serial_interface=None, width=64, height=128, rotate=0, **kwargs):
        super(sh1107, self).__init__(luma.oled.const.sh1107, serial_interface)
        self.capabilities(width, height, rotate)

        self._pages = self._h // 8
        self._pagelen = self._w

        settings = {
            (64, 128): dict(multiplex=0x7F, displayoffset=0x60),
        }.get((width, height))

        if settings is None:
            raise luma.core.error.DeviceDisplayModeError(
                f"Unsupported display mode: {width} x {height}")

        self.command(
            self._const.DISPLAYOFF,
            self._const.MEMORYMODE,
            self._const.NORMALDISPLAY,
            self._const.SETMULTIPLEX,       settings['multiplex'],
            self._const.DISPLAYALLON_RESUME,
            self._const.SETDISPLAYOFFSET,   settings['displayoffset'],
            self._const.SETDISPLAYCLOCKDIV, 0x80,
            self._const.SETPRECHARGE,       0x22,
            self._const.SETCOMPINS,         0x12,
            self._const.SETVCOMDETECT,      0x35,
        )

        self.contrast(0x7F)
        self.clear()
        self.show()


    def display(self, image):
        """
        Takes a 1-bit :py:mod:`PIL.Image` and dumps it to the SH1107
        OLED display.

        :param image: Image to display.
        :type image: :py:mod:`PIL.Image`
        """
        assert(image.mode == self.mode)
        assert(image.size == self.size)

        image = self.preprocess(image)
        pixmap = image.load()
        buf = bytearray(self._pagelen)

        for page in range(self._pages):
            for x in range(self._pagelen):
                tmp = 0
                for y in range(8):
                    tmp |= (pixmap[x, y + 8 * page] & 1) << y
                buf[x] = tmp
            self.command(0x10, 0x00, 0xb0 | page)
            self.data(buf)


There are probably other resolutions than 64x128. However, I only have this one and therefore this new class does not allow you to select any other resolution.

Pako2 avatar Apr 23 '22 09:04 Pako2

thanks for sharing @Pako2

@rm-hull is this something worth including in luma.oled?

thijstriemstra avatar Apr 23 '22 17:04 thijstriemstra

I bought a 0.78 inch SH1107 OLED (80x128) display that I'll test with your code @Pako2 when I find some time. It's a different screen than you linked but we'll see if it works.

I'll first give it a try with Arduino using something like https://github.com/adafruit/Adafruit_SH110x

display

specs

thijstriemstra avatar Dec 14 '22 22:12 thijstriemstra

I tested the 0.78 inch SH1107 OLED (80x128) documented in the comment above with your code @Pako2 and adjusted it for 80x128 by adding:

settings = {
            (64, 128): dict(multiplex=0x7F, displayoffset=0x60),
            (80, 128): dict(multiplex=0x7F, displayoffset=0x60),
        }.get((width, height))

I have no idea what the values for multiplex or displayoffset should be though..

And using the following code:

serial = i2c(port=1, address=0x3C)

device = sh1107(serial, width=80, height=128, rotate=0)

with canvas(device) as draw:
    draw.rectangle(device.bounding_box, outline="white", fill="black")
    draw.text((30, 40), "Hello World", fill="white")

sleep(40)

produces this image on the screen:

image

Adding rotate=1:

device = sh1107(serial, width=80, height=128, rotate=1)

produces this image:

image

Any idea how to get it to properly render?

thijstriemstra avatar Jan 11 '23 22:01 thijstriemstra

I would try experimenting with the multiplex and displayoffset parameters. For example like this:

settings = {
            (64, 128): dict(multiplex=0x7F, displayoffset=0x60),
            (80, 128): dict(multiplex=0x4F, displayoffset=0x68),
        }.get((width, height))

I am based on this source: u8x8_d_sh1107.c

Pako2 avatar Jan 12 '23 07:01 Pako2

thanks @Pako2, that worked!

With rotate=0 (default):

image

With rotate=1:

image

Would you be willing to make a pull request @Pako2? I can copy your code and make one as well, just let me know.

thijstriemstra avatar Jan 12 '23 15:01 thijstriemstra

nice work .. I was about to suggest trying tweaking the display offset 😁

rm-hull avatar Jan 12 '23 21:01 rm-hull

Would you be willing to make a pull request @Pako2? I can copy your code and make one as well, just let me know.

Hi. I am glad that my code works and that it can be used to complement such a great library. And I also prefer if I don't have to make another PR. Please do it yourself.

Pako2 avatar Jan 13 '23 06:01 Pako2

Sounds good @Pako2, I've opened #359

thijstriemstra avatar Jan 13 '23 10:01 thijstriemstra