# 1.12 SPI OLED . \> Micropython?

**URL:** <https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956>\
**Category:** Support\
**Created:** [July 10, 2026, 11:51am UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956 "2026-07-10T11:51:14Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![alphanumeric](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/alphanumeric/32/2729_2.png) [@alphanumeric](https://forums.pimoroni.com/u/alphanumeric)\
**Post date:** [July 10, 2026, 11:51am UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/1 "2026-07-10T11:51:15Z")

</div>

I have the 1.12 SPi OLED Breakout

> **[1.12" Mono OLED (128x128, white/black) Breakout](https://shop.pimoroni.com/en-us/products/1-12-oled-breakout?variant=12628508704851)**
>
> A crisp, bright 1.12" OLED that's ideal for adding a small display to your project. This 128x128 pixel, monochrome white/black display is ideal for graphing, readouts, and displaying basic icons. Now available in SPI or I2C flavours!

Plugged into Pico Breakout Garden Pack.

> **[Pico Breakout Garden Pack](https://shop.pimoroni.com/en-us/products/pico-breakout-garden-pack?variant=32369506254931)**
>
> An easy, solderless, swappable way to use breakouts with your Raspberry Pi Pico - add up to three Pimoroni breakouts (2 x I2C and 1 x SPI) to the sturdy slots and get coding and creating!

Wich I’m trying to get working in Micro Python. There is no Pimoroni support for this setup so I hunted around online and found this.

> **[GitHub - peter-l5/SH1107: MicroPython driver for SH1107 OLED displays...](https://github.com/peter-l5/SH1107)**
>
> MicroPython driver for SH1107 OLED displays (128x128 and 128x64 pixels)

I have the sh1107.py downloaded and saved to the Pico.  
And I’m attempting to modify this file to work with the Breakout garden setup.

```auto
from machine import Pin, SPI
import sh1107
import time

# 1. Setup SPI (adjust baudrate and pins for your board)
# Example for Raspberry Pi Pico
spi = SPI(0, baudrate=1000000, polarity=0, phase=0,
          sck=Pin(2), mosi=Pin(3), miso=Pin(4))

# 2. Setup Control Pins
reset_pin = Pin(14, Pin.OUT)
dc_pin = Pin(13, Pin.OUT)
cs_pin = Pin(15, Pin.OUT)

# 3. Initialize Display Driver
display = sh1107.SH1107_SPI(128, 128, spi, dc=dc_pin, res=reset_pin, cs=cs_pin)

# 4. Draw to Display
display.fill(0) # Clear screen
display.text('Hello MicroPython!', 0, 0, 1)
display.show()

```

The `dc_pin = Pin(13, Pin.OUT)` and `cs_pin = Pin(15, Pin.OUT)` don’t make sense to me?  
GP 2 is SCK SPIO 0  
GP 3 is TX SPIO 0  
GP 4 is RX SPIO 0  
But GP 13 is SPI 1 CSn?  
And GP 15 is SPI 1 TX?

So this is what I have so far.

```auto
from machine import Pin, SPI
import sh1107
import time

# 1. Setup SPI (adjust baudrate and pins for your board)
# Example for Raspberry Pi Pico
spi = SPI(0, baudrate=1000000, polarity=0, phase=0,
          sck=Pin(18), mosi=Pin(19), miso=Pin(16))

# 2. Setup Control Pins
#reset_pin = Pin(14, Pin.OUT)
dc_pin = Pin(22, Pin.OUT)
cs_pin = Pin(17, Pin.OUT)

# 3. Initialize Display Driver
display = sh1107.SH1107_SPI(128, 128, spi, dc=dc_pin, cs=cs_pin)

# 4. Draw to Display
display.fill(0) # Clear screen
display.text('Hello MicroPython!', 0, 0, 1)
display.show()

```

All I get is a flash to all black, and then snow.

The SPI slot on the pack uses the following  
GP 22 - Pin 29 - GPIO (DC)  
GP 19 - Pin 25 - MOSI (TX)  
GP 18 - Pin 24 - SCLK  
GP 17 - Pin 22 - CS  
GP 16 - Pin 21 - MISO (RX)

---

<div class="post-metadata">

**Author:** ![bablokb](https://avatars.discourse-cdn.com/v4/letter/b/ee59a6/32.png) [@bablokb](https://forums.pimoroni.com/u/bablokb)\
**Post date:** [July 10, 2026, 1:29pm UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/2 "2026-07-10T13:29:06Z")

</div>

DC and CS are normal GPIOs, they are not related to the hardware SPI units and can be any pins. So your choice is fine, but 13 and 15 are also ok unless you need SPI1 for something else.

The demo example from the Git-repo you linked to has an additional method call:

```auto
display.sleep(False)

```

directly after the display is created. Maybe that is the reason it does not work.

---

<div class="post-metadata">

**Author:** ![alphanumeric](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/alphanumeric/32/2729_2.png) [@alphanumeric](https://forums.pimoroni.com/u/alphanumeric)\
**Post date:** [July 10, 2026, 3:29pm UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/3 "2026-07-10T15:29:08Z")

</div>

> [@bablokb](#):
>
> `display.sleep(False)`

Added that and it made no difference. I’m trying to set my pin assignments to work with the Breakout Garden Pack.  
I did a forum search, and a few others here have used this driver with that display. I didn’t see any posted working code though?  
Pimoroni have working Micro Python code for the i2c version, but nothing listed for the SPI version. I hadn’t noticed that at time of purchase.  
I don’t need a color display but I may just swap in a 0.96" SPI Colour LCD (160x80) Breakout. I have a couple of those in my parts box.

---

<div class="post-metadata">

**Author:** ![bablokb](https://avatars.discourse-cdn.com/v4/letter/b/ee59a6/32.png) [@bablokb](https://forums.pimoroni.com/u/bablokb)\
**Post date:** [July 10, 2026, 4:41pm UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/4 "2026-07-10T16:41:46Z")

</div>

> [@alphanumeric](#):
>
> Pimoroni have working Micro Python code for the i2c version, but nothing listed for the SPI version.

They recommend a different library on the shop page. Maybe that is worth a try.

---

<div class="post-metadata">

**Author:** ![alphanumeric](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/alphanumeric/32/2729_2.png) [@alphanumeric](https://forums.pimoroni.com/u/alphanumeric)\
**Post date:** [July 10, 2026, 7:53pm UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/5 "2026-07-10T19:53:39Z")

</div>

Ah, I Don’t see any link to a **Micro** Python driver?  
Just the Python Luma stuff?

Thanks for trying.  
I’m going to go poke around on the Authers site of the driver i’m trying to use.

---

<div class="post-metadata">

**Author:** ![bablokb](https://avatars.discourse-cdn.com/v4/letter/b/ee59a6/32.png) [@bablokb](https://forums.pimoroni.com/u/bablokb)\
**Post date:** [July 11, 2026, 4:41am UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/6 "2026-07-11T04:41:11Z")

</div>

Oh yes, sorry. Another thing to try: play arround with polarity and phase. I don’t really think this is the problem, since your explicit values are the defaults and the author does not pass these parameters in his examples (so using the defaults).

Also worth trying: since this is a square display, you don’t know how it is physically mounted. Try passing `rotate=90` to the constructor. The rotation parameter defines how the pixels in the underlying framebuffer are encoded.

---

<div class="post-metadata">

**Author:** ![alphanumeric](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/alphanumeric/32/2729_2.png) [@alphanumeric](https://forums.pimoroni.com/u/alphanumeric)\
**Post date:** [July 11, 2026, 8:07am UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/7 "2026-07-11T08:07:19Z")

</div>

I think today I’ll do some hardware checks. The Breakout Garden Pack and OLED are brand new, never been used before. First i’m going to put a known good color SPI LCD in place of the OLED and make sure that the SPI works on the Pack.  
Then plug the OLED into a Pi and make sure it works.  
I also have this to use if i have to, for testing. Old school solder-less breadboard etc.

> **[SPI Breakout Garden Extender Kit (3 pairs)](https://shop.pimoroni.com/en-us/products/spi-breakout-garden-extender-kit)**
>
> Use these extenders with some female-to-female jumper jerky (available separately) to extend the reach of your Pimoroni SPI breakouts.

---

<div class="post-metadata">

**Author:** ![alphanumeric](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/alphanumeric/32/2729_2.png) [@alphanumeric](https://forums.pimoroni.com/u/alphanumeric)\
**Post date:** [July 11, 2026, 10:25am UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/8 "2026-07-11T10:25:13Z")

</div>

OK, I swapped in one of these,

> **[0.96" SPI Colour LCD (160x80) Breakout](https://shop.pimoroni.com/en-us/products/0-96-spi-colour-lcd-160x80-breakout?variant=21414301630547)**
>
> A handy lil' LCD to add readouts to your projects. This 0.96" SPI LCD has a 2:1 widescreen aspect ratio, has great viewing angles (IPS), and works with Raspberry Pi or Arduino.

And the balls demo ran just fine. Now I know there are no issues with the Pico Breakout Garden Pack. I have two Encoder Breakouts in the two i2c slots that are both working just fine. I did an address change on one of them. ;)

I’m going to just add to this post as i check things out.

Oh my god, could they have made this any more tedious, and complicated. =(  
Went here,  
[Software — Luma.OLED: Display drivers for SSD1305, SSD1306, SSD1309, SSD1315, SSD1316, SSD1322, SSD1322\_NHD, SSD1325, SSD1327, SSD1331, SSD1351, SSD1362, SH1106, SH1107, WS0010, WINSTAR\_WEH, CH1115 3.15.0 documentation](https://luma-oled.readthedocs.io/en/latest/software.html)  
and did all the install stuff.  
Then went here,  
[GitHub - rm-hull/luma.examples: Companion repo for running examples against the luma.oled, luma.lcd, luma.led\_matrix and luma.emulator display drivers. · GitHub](https://github.com/rm-hull/luma.examples)  
and did all that. That got me this and a blank display?

```auto
kyrie@raspberrypi:~ $ git clone https://github.com/rm-hull/luma.examples.git
Cloning into 'luma.examples'...
remote: Enumerating objects: 1124, done.
remote: Counting objects: 100% (378/378), done.
remote: Compressing objects: 100% (100/100), done.
remote: Total 1124 (delta 317), reused 278 (delta 276), pack-reused 746 (from 1)
Receiving objects: 100% (1124/1124), 5.61 MiB | 2.80 MiB/s, done.
Resolving deltas: 100% (728/728), done.
kyrie@raspberrypi:~ $ cd luma.examples
kyrie@raspberrypi:~/luma.examples $ python3 -m venv ~/luma-env
kyrie@raspberrypi:~/luma.examples $ ~/luma-env/bin/python -m pip install -e .
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Obtaining file:///home/kyrie/luma.examples
  Installing build dependencies ... done
  Checking if build backend supports build_editable ... done
  Getting requirements to build editable ... done
  Preparing editable metadata (pyproject.toml) ... done
Requirement already satisfied: luma.core>=2.4.0 in /home/kyrie/luma-env/lib/python3.13/site-packages (from luma.examples==0.0.0) (2.5.3)
Collecting luma.emulator>=1.3.0 (from luma.examples==0.0.0)
  Downloading https://www.piwheels.org/simple/luma-emulator/luma_emulator-1.7.0-py2.py3-none-any.whl.metadata (9.7 kB)
Requirement already satisfied: luma.oled>=3.6.0 in /home/kyrie/luma-env/lib/python3.13/site-packages (from luma.examples==0.0.0) (3.15.0)
Collecting luma.lcd>=2.5.0 (from luma.examples==0.0.0)
  Downloading https://www.piwheels.org/simple/luma-lcd/luma_lcd-2.13.0-py2.py3-none-any.whl.metadata (15 kB)
Collecting luma.led_matrix>=1.5.0 (from luma.examples==0.0.0)
  Downloading https://www.piwheels.org/simple/luma-led-matrix/luma_led_matrix-1.9.0-py2.py3-none-any.whl.metadata (17 kB)
Collecting argcomplete (from luma.examples==0.0.0)
  Downloading https://www.piwheels.org/simple/argcomplete/argcomplete-3.7.0-py3-none-any.whl.metadata (16 kB)
Collecting psutil (from luma.examples==0.0.0)
  Downloading psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl.metadata (22 kB)
Requirement already satisfied: pillow>=9.2.0 in /home/kyrie/luma-env/lib/python3.13/site-packages (from luma.core>=2.4.0->luma.examples==0.0.0) (12.3.0)
Requirement already satisfied: smbus2 in /home/kyrie/luma-env/lib/python3.13/site-packages (from luma.core>=2.4.0->luma.examples==0.0.0) (0.6.1)
Requirement already satisfied: cbor2 in /home/kyrie/luma-env/lib/python3.13/site-packages (from luma.core>=2.4.0->luma.examples==0.0.0) (6.1.3)
Collecting pygame (from luma.emulator>=1.3.0->luma.examples==0.0.0)
  Downloading pygame-2.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (12 kB)
Downloading https://www.piwheels.org/simple/luma-emulator/luma_emulator-1.7.0-py2.py3-none-any.whl (27 kB)
Downloading https://www.piwheels.org/simple/luma-lcd/luma_lcd-2.13.0-py2.py3-none-any.whl (34 kB)
Downloading https://www.piwheels.org/simple/luma-led-matrix/luma_led_matrix-1.9.0-py2.py3-none-any.whl (14 kB)
Downloading https://www.piwheels.org/simple/argcomplete/argcomplete-3.7.0-py3-none-any.whl (42 kB)
Downloading psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl (156 kB)
Downloading pygame-2.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (13.6 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 13.6/13.6 MB 2.8 MB/s eta 0:00:00
Building wheels for collected packages: luma.examples
  Building editable for luma.examples (pyproject.toml) ... done
  Created wheel for luma.examples: filename=luma_examples-0.0.0-0.editable-py3-none-any.whl size=9244 sha256=d4d33071d01132d37818f6c76fa1938b97d5e605b628e87714edb6bf31d86df1
  Stored in directory: /tmp/pip-ephem-wheel-cache-zy6kvq0o/wheels/74/74/7c/13dcf0ca90af68d5c920fe7f99d5a32da75464ecd13503c16b
Successfully built luma.examples
Installing collected packages: pygame, psutil, argcomplete, luma.led_matrix, luma.lcd, luma.emulator, luma.examples
Successfully installed argcomplete-3.7.0 luma.emulator-1.7.0 luma.examples-0.0.0 luma.lcd-2.13.0 luma.led_matrix-1.9.0 psutil-7.2.2 pygame-2.6.1
kyrie@raspberrypi:~/luma.examples $ ~/luma-env/bin/python 3d_box.py
/home/kyrie/luma-env/bin/python: can't open file '/home/kyrie/luma.examples/3d_box.py': [Errno 2] No such file or directory
kyrie@raspberrypi:~/luma.examples $ cd examples
kyrie@raspberrypi:~/luma.examples/examples $ ~/luma-env/bin/python 3d_box.py
usage: 3d_box.py [-h] [--config CONFIG] [--display DISPLAY] [--width WIDTH] [--height HEIGHT] [--rotate ROTATION] [--interface INTERFACE] [--i2c-port I2C_PORT] [--i2c-address I2C_ADDRESS] [--spi-port SPI_PORT] [--spi-device SPI_DEVICE]
                 [--spi-bus-speed SPI_BUS_SPEED] [--spi-transfer-size SPI_TRANSFER_SIZE] [--spi-mode SPI_MODE] [--spi-cs-high SPI_CS_HIGH] [--ftdi-device FTDI_DEVICE] [--framebuffer-device FRAMEBUFFER_DEVICE] [--gpio GPIO]
                 [--gpio-mode GPIO_MODE] [--gpio-data-command GPIO_DATA_COMMAND] [--gpio-chip-select GPIO_CHIP_SELECT] [--gpio-reset GPIO_RESET] [--gpio-backlight GPIO_BACKLIGHT] [--gpio-reset-hold-time GPIO_RESET_HOLD_TIME]
                 [--gpio-reset-release-time GPIO_RESET_RELEASE_TIME] [--block-orientation ORIENTATION] [--mode MODE] [--framebuffer FRAMEBUFFER] [--num-segments NUM_SEGMENTS] [--bgr] [--inverse] [--h-offset H_OFFSET]
                 [--v-offset V_OFFSET] [--backlight-active VALUE] [--debug] [--transform TRANSFORM] [--scale SCALE] [--duration DURATION] [--loop LOOP] [--max-frames MAX_FRAMES]
3d_box.py: error: I2C device not found on address: 0x3C
kyrie@raspberrypi:~/luma.examples/examples $ 

```

V\_V

EDIT: Apologies if I sound cranky. Just a bit frustrated. I wasn’t expecting this to be this complicated.

---

<div class="post-metadata">

**Author:** ![bablokb](https://avatars.discourse-cdn.com/v4/letter/b/ee59a6/32.png) [@bablokb](https://forums.pimoroni.com/u/bablokb)\
**Post date:** [July 12, 2026, 4:54am UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/9 "2026-07-12T04:54:27Z")

</div>

I think you have to tell the example program which interface to use. In your case this is spi. No idea on how exactly you have to pass this information, but there are a number of program options printed out (`--interface INTERFACE`).

This is one of the extremely big advantages of running CircuitPython instead of MicroPython: library management is so much simpler. Ok, doesn’t help you in this case, but there are regular comments in the forums here on how complicated CircuitPython is. But it really depends on what you are doing.

---

<div class="post-metadata">

**Author:** ![alphanumeric](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/alphanumeric/32/2729_2.png) [@alphanumeric](https://forums.pimoroni.com/u/alphanumeric)\
**Post date:** [July 12, 2026, 7:43am UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/10 "2026-07-12T07:43:51Z")

</div>

Ok, thanks for that interface info. I have chronic pain issues, and it acts up a bit after long stints at the keyboard. Then I get frustrated and make mistakes. I’ll give it another try but pace myself this time.

I have several of the i2c version of that OLED Breakout, and it was relatively easy getting them going. **My Bad** for not looking closer at the difference in setup etc for the SPI version.  
On the plus side, the 0.96" colour LCD (160x80 pixels) breakout is a nice fit, and looks better. And I think I’ll be able to fit the info I want in there. The two encoders will move two servo’s. Pan and Tilt. The LCD is going to display the position form +90 to -90 for each one. That’s the plan anyway. Basically 7 characters of text.

I tinkered with Circuit Python. I had an Adafruit LED Matrix Panel controller. I can’t remember what they called it, it was RP2040 based. I struggled quite badly with Adafruits graphics software. I switched to an Interstate 75 W.

---

<div class="post-metadata">

**Author:** ![alphanumeric](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/alphanumeric/32/2729_2.png) [@alphanumeric](https://forums.pimoroni.com/u/alphanumeric)\
**Post date:** [July 12, 2026, 10:24am UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/11 "2026-07-12T10:24:10Z")

</div>

I just recently built this Pan Tilt Camera setup.

 ![IMG_20260712_065911](https://us1.discourse-cdn.com/flex016/uploads/pimoroni/original/2X/6/66987fbdafb16a72f62084309f2b539e6d04031c.jpeg)

The Pi is running Motion Eye OS. And the Pan Tilt Servos are controlled by the Encoders via a Tiny 2040. I had an Idea of splitting up the Pan Tilt setup for remote control. The Encoders to a Pico W, then another Pico W controlling the servo’s. One talking to the other over WIFI, or maybe BlueTooth.

As a starting point I built this.

 ![IMG_20260712_070144](https://us1.discourse-cdn.com/flex016/uploads/pimoroni/original/2X/8/87bbf2081e185b28ea7f5552cff715913de93b64.jpeg)

The Pico Breakout garden Pack is i2c SPI i2c. Minimal soldering and running wires etc with the Omnibus. Which is why I went with the SPI OLED. I went OLED because I don’t need a color display, and I figured it would give me good viewing, even in bright light conditions.

Right now I’m just relaxing, sipping a coffee, contemplating my next move.

---

<div class="post-metadata">

**Author:** ![alphanumeric](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/alphanumeric/32/2729_2.png) [@alphanumeric](https://forums.pimoroni.com/u/alphanumeric)\
**Post date:** [July 13, 2026, 12:19pm UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/12 "2026-07-13T12:19:08Z")

</div>

> [@bablokb](#):
>
> I think you have to tell the example program which interface to use. In your case this is spi. No idea on how exactly you have to pass this information, but there are a number of program options printed out (`--interface INTERFACE`).

Doah, if I had read just the little bit futer, I would have spotted that.

```auto
kyrie@raspberrypi:~/luma.examples/examples $ ~/luma-env/bin/python demo.py --interface gpio_cs_spi
Traceback (most recent call last):
  File "/home/kyrie/luma.examples/examples/demo.py", line 107, in <module>
    main()
    ~~~~ ^^
  File "/home/kyrie/luma.examples/examples/demo.py", line 62, in main
    device = get_device()
  File "/home/kyrie/luma.examples/examples/demo_opts.py", line 59, in get_device
    device = cmdline.create_device(args)
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/cmdline.py", line 240, in create_device
    device = Device(serial_interface=interface(), **params)
                                     ~~~~~~~~~ ^^
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/cmdline.py", line 164, in gpio_cs_spi
    return gpio_cs_spi(port=self.opts.spi_port,
                       device=self.opts.spi_device,
    ...<8 lines>...
                       gpio_CS=self.opts.gpio_chip_select,
                       gpio=self.gpio or GPIO)
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/interface/serial.py", line 346, in __init__
    super(gpio_cs_spi, self). __init__ (*args, **kwargs)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^^^^^^^^^^^^^^^^^
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/interface/serial.py", line 302, in __init__
    bitbang. __init__ (self, gpio, transfer_size, reset_hold_time, reset_release_time, DC=gpio_DC, RST=gpio_RST)
    ~~~~~~~~~~~~~~~~ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/interface/serial.py", line 185, in __init__
    self._gpio = gpio or self. __rpi_gpio__ ()
                         ~~~~~~~~~~~~~~~~~ ^^
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/lib.py", line 23, in __rpi_gpio__
    import RPi.GPIO as GPIO
ModuleNotFoundError: No module named 'RPi'
kyrie@raspberrypi:~/luma.examples/examples $ 

```

```auto
kyrie@raspberrypi:~/luma.examples/examples $ ~/luma-env/bin/python demo.py --interface spi
Traceback (most recent call last):
  File "/home/kyrie/luma.examples/examples/demo.py", line 107, in <module>
    main()
    ~~~~ ^^
  File "/home/kyrie/luma.examples/examples/demo.py", line 62, in main
    device = get_device()
  File "/home/kyrie/luma.examples/examples/demo_opts.py", line 59, in get_device
    device = cmdline.create_device(args)
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/cmdline.py", line 240, in create_device
    device = Device(serial_interface=interface(), **params)
                                     ~~~~~~~~~ ^^
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/cmdline.py", line 150, in spi
    return spi(port=self.opts.spi_port,
               device=self.opts.spi_device,
    ...<6 lines>...
               gpio_RST=self.opts.gpio_reset,
               gpio=self.gpio or GPIO)
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/interface/serial.py", line 302, in __init__
    bitbang. __init__ (self, gpio, transfer_size, reset_hold_time, reset_release_time, DC=gpio_DC, RST=gpio_RST)
    ~~~~~~~~~~~~~~~~ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/interface/serial.py", line 185, in __init__
    self._gpio = gpio or self. __rpi_gpio__ ()
                         ~~~~~~~~~~~~~~~~~ ^^
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/lib.py", line 23, in __rpi_gpio__
    import RPi.GPIO as GPIO
ModuleNotFoundError: No module named 'RPi'
kyrie@raspberrypi:~/luma.examples/examples $ 

```

---

<div class="post-metadata">

**Author:** ![bablokb](https://avatars.discourse-cdn.com/v4/letter/b/ee59a6/32.png) [@bablokb](https://forums.pimoroni.com/u/bablokb)\
**Post date:** [July 13, 2026, 12:24pm UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/13 "2026-07-13T12:24:35Z")

</div>

This seems to be one of the old drivers that still use this old RPi-module.

Have you tried the rotation parameter on the MicroPython lib you tried first? Maybe that got lost.

---

<div class="post-metadata">

**Author:** ![hel](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/hel/32/6075_2.png) [@hel](https://forums.pimoroni.com/u/hel)\
**Post date:** [July 13, 2026, 3:12pm UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/14 "2026-07-13T15:12:55Z")

</div>

The information in the notes section on the shop page suggests you need to supply quite a few of those parameters:

> With the SPI version, you can run an example like so: `python3 bounce.py --display sh1106 --height 128 --rotate 2 --interface spi --gpio-data-command 9` (add `--spi-device 0` for the back slot, or `--spi-device 1` for the front slot)

If you’re still getting the error about `RPi.GPIO` then `pip install rpi-lgpio` might help perhaps, assuming you’re running a recent version of Pi OS?

(you might to activate the virtual environment first - `source ~/luma_env/bin/activate`)

---

<div class="post-metadata">

**Author:** ![alphanumeric](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/alphanumeric/32/2729_2.png) [@alphanumeric](https://forums.pimoroni.com/u/alphanumeric)\
**Post date:** [July 13, 2026, 3:33pm UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/15 "2026-07-13T15:33:02Z")

</div>

@hel Apologies, ops, I got as far as the git clone part, and started trying to **run** the examples and missed the rest of the info on the shop page. I was following this and didn’t go back to the shop page.

> **[GitHub - rm-hull/luma.examples: Companion repo for running examples against the...](https://github.com/rm-hull/luma.examples)**
>
> Companion repo for running examples against the luma.oled, luma.lcd, luma.led\_matrix and luma.emulator display drivers.

I am activating the virtual environment, and it’s the most recent Pi OS.

@bablokb I will revisit that and try the rotate. I have it plugged into my Pi 400 and I’m plugging away in Python at the moement.

---

<div class="post-metadata">

**Author:** ![alphanumeric](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/alphanumeric/32/2729_2.png) [@alphanumeric](https://forums.pimoroni.com/u/alphanumeric)\
**Post date:** [July 13, 2026, 3:54pm UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/16 "2026-07-13T15:54:40Z")

</div>

```auto
kyrie@raspberrypi:~/luma.examples/examples $ ~/luma-env/bin/python demo.py --display sh1106 --height 128 --rotate 2 --interface spi --gpio-data-command 9
Traceback (most recent call last):
  File "/home/kyrie/luma.examples/examples/demo.py", line 107, in <module>
    main()
    ~~~~ ^^
  File "/home/kyrie/luma.examples/examples/demo.py", line 62, in main
    device = get_device()
  File "/home/kyrie/luma.examples/examples/demo_opts.py", line 59, in get_device
    device = cmdline.create_device(args)
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/cmdline.py", line 240, in create_device
    device = Device(serial_interface=interface(), **params)
                                     ~~~~~~~~~ ^^
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/cmdline.py", line 150, in spi
    return spi(port=self.opts.spi_port,
               device=self.opts.spi_device,
    ...<6 lines>...
               gpio_RST=self.opts.gpio_reset,
               gpio=self.gpio or GPIO)
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/interface/serial.py", line 302, in __init__
    bitbang. __init__ (self, gpio, transfer_size, reset_hold_time, reset_release_time, DC=gpio_DC, RST=gpio_RST)
    ~~~~~~~~~~~~~~~~ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/interface/serial.py", line 185, in __init__
    self._gpio = gpio or self. __rpi_gpio__ ()
                         ~~~~~~~~~~~~~~~~~ ^^
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/lib.py", line 23, in __rpi_gpio__
    import RPi.GPIO as GPIO
ModuleNotFoundError: No module named 'RPi'

```

> [@hel](#):
>
> `pip install rpi-lgpio`

kyrie@raspberrypi:~/luma.examples/examples $ pip install rpi-lgpio  
error: externally-managed-environment

× This environment is externally managed  
╰─\> To install Python packages system-wide, try apt install  
python3-xyz, where xyz is the package you are trying to  
install.

```
If you wish to install a non-Debian-packaged Python package,
create a virtual environment using python3 -m venv path/to/venv.
Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
sure you have python3-full installed.

For more information visit http://rptl.io/venv

```

---

<div class="post-metadata">

**Author:** ![hel](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/hel/32/6075_2.png) [@hel](https://forums.pimoroni.com/u/hel)\
**Post date:** [July 13, 2026, 3:59pm UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/17 "2026-07-13T15:59:15Z")

</div>

I don’t think you have your venv active there?

> [@hel](#):
>
> (you might to activate the virtual environment first - `source ~/luma_env/bin/activate`)

---

<div class="post-metadata">

**Author:** ![alphanumeric](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/alphanumeric/32/2729_2.png) [@alphanumeric](https://forums.pimoroni.com/u/alphanumeric)\
**Post date:** [July 13, 2026, 4:21pm UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/18 "2026-07-13T16:21:46Z")

</div>

I didn’t clip and past that part. First thing after opening the terminal window I did

```auto
kyrie@raspberrypi:~ $ python3 -m venv ~/luma-env
kyrie@raspberrypi:~ $ cd luma.examples
kyrie@raspberrypi:~/luma.examples $ cd examples
kyrie@raspberrypi:~/luma.examples/examples $ ~/luma-env/bin/python demo.py --display sh1106 --height 128 --rotate 2 --interface spi --gpio-data-command 9
Traceback (most recent call last):
  File "/home/kyrie/luma.examples/examples/demo.py", line 107, in <module>
    main()
    ~~~~ ^^
  File "/home/kyrie/luma.examples/examples/demo.py", line 62, in main
    device = get_device()
  File "/home/kyrie/luma.examples/examples/demo_opts.py", line 59, in get_device
    device = cmdline.create_device(args)
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/cmdline.py", line 240, in create_device
    device = Device(serial_interface=interface(), **params)
                                     ~~~~~~~~~ ^^
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/cmdline.py", line 150, in spi
    return spi(port=self.opts.spi_port,
               device=self.opts.spi_device,
    ...<6 lines>...
               gpio_RST=self.opts.gpio_reset,
               gpio=self.gpio or GPIO)
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/interface/serial.py", line 302, in __init__
    bitbang. __init__ (self, gpio, transfer_size, reset_hold_time, reset_release_time, DC=gpio_DC, RST=gpio_RST)
    ~~~~~~~~~~~~~~~~ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/interface/serial.py", line 185, in __init__
    self._gpio = gpio or self. __rpi_gpio__ ()
                         ~~~~~~~~~~~~~~~~~ ^^
  File "/home/kyrie/luma-env/lib/python3.13/site-packages/luma/core/lib.py", line 23, in __rpi_gpio__
    import RPi.GPIO as GPIO
ModuleNotFoundError: No module named 'RPi'
kyrie@raspberrypi:~/luma.examples/examples $ 

```

```auto
kyrie@raspberrypi:~/luma.examples/examples $ python3 pip install - rpi-lgpioe 
python3: can't open file '/home/kyrie/luma.examples/examples/pip': [Errno 2] No such file or directory
kyrie@raspberrypi:~/luma.examples/examples $ cd
kyrie@raspberrypi:~ $ python3 pip install - rpi-lgpioe 
python3: can't open file '/home/kyrie/pip': [Errno 2] No such file or directory
kyrie@raspberrypi:~ $ 

kyrie@raspberrypi:~ $ python3 pip install rpi-lgpioe 
python3: can't open file '/home/kyrie/pip': [Errno 2] No such file or directory
kyrie@raspberrypi:~ $ 

```

Sorry for being such a nube, I such at this command line stuff.

---

<div class="post-metadata">

**Author:** ![alphanumeric](https://sea2.discourse-cdn.com/flex016/user_avatar/forums.pimoroni.com/alphanumeric/32/2729_2.png) [@alphanumeric](https://forums.pimoroni.com/u/alphanumeric)\
**Post date:** [July 13, 2026, 6:02pm UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/19 "2026-07-13T18:02:13Z")

</div>

> [@bablokb](#):
>
> Have you tried the rotation parameter on the MicroPython lib you tried first? Maybe that got lost.

`display = sh1107.SH1107_SPI(128, 128, spi, dc=dc_pin, cs=cs_pin, rotate=90)`

made no difference. Time to revisit the pins he used versus what I’m using? Maybe one or two want a GP number not a Pin number?

---

<div class="post-metadata">

**Author:** ![bablokb](https://avatars.discourse-cdn.com/v4/letter/b/ee59a6/32.png) [@bablokb](https://forums.pimoroni.com/u/bablokb)\
**Post date:** [July 13, 2026, 6:22pm UTC](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956/20 "2026-07-13T18:22:06Z")

</div>

No, he wants pins. But I found something spurios: `sh1107.SH1107_SPI()` calls `sh1107.SH1107()` with the default `delay_ms=0`. Note that this is the SPI-default, the default for I2C is 200ms. This delay is only used for the poweron command and it seems strange that the device does not need a delay when using the SPI-interface.

So another thing to try: pass an explicit `delay_ms=200` to the constructor.

[Next page](https://forums.pimoroni.com/t/1-12-spi-oled-micropython/28956.md?page=2)
