To code a 72x40 OLED in MicroPython, you need to drive it via I2C using a library like ssd1306 or a custom driver for the SH1107 controller, since most 72x40 OLEDs (like the 0.42 inch 72x40 oled display) use that chip. Start by wiring the OLED’s SDA and SCL pins to your microcontroller’s I2C pins (e.g., GPIO 4 and 5 on a Raspberry Pi Pico), then power it with 3.3V and GND. The I2C address is typically 0x3C, but you can confirm it with a scanner script. Install the micropython-ssd1306 library from the package manager or copy the ssd1306.py file to your board. Then, initialize the I2C bus and create an SSD1306_I2C object with width=72 and height=40. The key trick is that the default SSD1306 driver expects 128x64, so you must override the page layout: the 72x40 OLED has 5 pages (40 pixels / 8 bits per page) and 72 columns. You can modify the ssd1306.py file to set self.pages = 5 and self.width = 72 in the __init__ method. Alternatively, use a dedicated SH1107 driver that supports non-standard resolutions. For example, the sh1107.py library from the micropython-sh1107 package works out of the box with 72x40. After initialization, use oled.fill(0) to clear, oled.text("Hello", 0, 0) for text, and oled.show() to update the display. The buffer size is 72 * 5 = 360 bytes, which is small enough for any MicroPython board.
Hardware specifics and pinout
The 72x40 OLED module usually has 4 pins: VCC, GND, SCL, and SDA. Some variants include a RESET pin, but it’s often not needed for I2C operation. The display operates at 3.3V logic, but the power supply can be 3.3V to 5V due to an onboard regulator. The I2C clock frequency should be set to 400 kHz for fast updates, but 100 kHz works too. On a Raspberry Pi Pico, use machine.I2C(0, sda=machine.Pin(4), scl=machine.Pin(5), freq=400000). On an ESP32, common pins are GPIO 21 (SDA) and GPIO 22 (SCL). The I2C address is 0x3C for most 72x40 OLEDs, but some modules use 0x3D. Use a scanner script to verify: i2c.scan() returns a list of addresses. If you get an empty list, check wiring and pull-up resistors (4.7kΩ on SDA and SCL are typical). The display’s controller, SH1107, supports a maximum I2C speed of 1 MHz, but 400 kHz is reliable across boards.
MicroPython library setup
You have two main library options: the generic ssd1306 driver (which works for SH1107 if you adjust parameters) or a dedicated sh1107 driver. The ssd1306 library is included in most MicroPython firmware builds, but it assumes a 128x64 resolution. To make it work with 72x40, you need to modify the ssd1306.py file. Open it and change the __init__ method: set self.width = 72, self.height = 40, and self.pages = self.height // 8 (which is 5). Also, update the write_cmd and write_data methods to handle the correct column range. The SH1107 expects column addresses from 0 to 71, not 0 to 127. Alternatively, use the sh1107.py library from micropython-sh1107 (version 1.1.0 or later). This library automatically detects the resolution from the display’s initialization sequence. Install it via mip on the board: import mip; mip.install("sh1107"). Then, initialize with from sh1107 import SH1107_I2C; oled = SH1107_I2C(72, 40, i2c). The library handles the buffer allocation and page mapping correctly.
Initialization code example
Here’s a complete working example for a Raspberry Pi Pico with the modified SSD1306 driver:
from machine import Pin, I2C
import ssd1306
i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400000)
oled = ssd1306.SSD1306_I2C(72, 40, i2c)
oled.fill(0)
oled.text("72x40", 0, 0)
oled.text("OLED", 0, 16)
oled.show()
If you use the SH1107 driver, the code is similar but with a different class name:
from sh1107 import SH1107_I2C
oled = SH1107_I2C(72, 40, i2c)
oled.fill(0)
oled.text("Hello", 0, 0)
oled.show()
The buffer size is 360 bytes, which fits in the Pico’s 264 KB RAM. For ESP32, the same code works with adjusted I2C pins.
Displaying text and graphics
The 72x40 OLED has a resolution of 72 columns by 40 rows. Each pixel is individually addressable, but the controller uses a page-based memory layout. The 40 rows are divided into 5 pages of 8 pixels each. When you write text, the text() method uses the built-in 8x8 font, so one character occupies 8x8 pixels. You can fit up to 9 characters per line (72/8 = 9) and 5 lines (40/8 = 5). However, the font is monospaced, and you can adjust spacing. For graphics, use pixel(x, y, color) to set individual pixels, hline(x, y, w, color) for horizontal lines, vline(x, y, h, color) for vertical lines, and rect(x, y, w, h, color) for rectangles. The fill_rect() method fills a rectangle. The color parameter is 1 for white (on) and 0 for black (off). Since the OLED is monochrome, you can only display one color. The update rate is limited by the I2C speed: at 400 kHz, a full buffer update takes about 360 bytes * 10 bits per byte (including start/stop) / 400,000 = 9 ms, so you can achieve over 100 frames per second theoretically, but practical limits are around 30-60 fps due to MicroPython overhead.
Power consumption and performance
The 72x40 OLED draws about 10-20 mA when all pixels are on, and 5-10 mA when displaying typical text. The I2C interface consumes negligible power. The SH1107 controller includes a charge pump for the OLED voltage, so no external components are needed. The display’s contrast can be adjusted with oled.contrast(value) where value ranges from 0 to 255. Default is 127. Higher contrast increases power consumption. The display has a refresh rate of about 100 Hz, but the I2C bus limits the update rate. For animations, you can use double buffering: allocate a second buffer with bytearray(360), draw to it, then copy to the display buffer and call show(). This reduces flicker. The MicroPython garbage collector can cause pauses, so use gc.collect() before critical loops.
Common issues and debugging
If the display shows nothing, first check the I2C address with a scanner. If the address is 0x3C but the display remains blank, the initialization sequence might be wrong. The SH1107 requires a specific command sequence: set display off (0xAE), set segment remap (0xA1), set COM output scan direction (0xC8), set display start line (0x40), set contrast (0x81 + value), set segment pin configuration (0xA4 for normal), set display mode (0xA6 for normal), set multiplex ratio (0xA8 + 0x27 for 40 rows), set display offset (0xD3 + 0x00), set display clock divide (0xD5 + 0x50), set pre-charge period (0xD9 + 0x22), set VCOMH deselect level (0xDB + 0x35), set charge pump enable (0x8D + 0x14), and display on (0xAF). The modified SSD1306 driver might skip some of these, so use the SH1107 driver for reliability. Another issue is the column mapping: the 72x40 OLED’s columns start at 0, but some drivers map them to 0-71 or 32-103. Check the datasheet of your specific module. If you see garbled text, the page mapping is wrong. The SH1107 stores data in pages from top to bottom, so row 0 is in page 0, row 8 in page 1, etc. The text() method assumes this layout. If you use a library that expects a different orientation, you’ll need to transpose the buffer.
Advanced techniques: scrolling and bitmaps
The SH1107 supports hardware scrolling via commands, but MicroPython libraries rarely implement it. You can implement software scrolling by shifting the buffer. For a scrolling text effect, use a circular buffer and update the display with oled.show() after each shift. For bitmaps, convert your image to a 72x40 monochrome bitmap (1 bit per pixel) and store it as a bytearray of 360 bytes. Each byte represents 8 vertical pixels. Use oled.blit(bitmap, x, y) to display it. The blit() method copies the bitmap to the buffer at the specified coordinates. You can also use frame buffers for complex graphics: import framebuf; buf = framebuf.FrameBuffer(bytearray(360), 72, 40, framebuf.MONO_VLSB) and then draw on it. The MONO_VLSB format matches the OLED’s page layout. After drawing, copy the buffer to the display with oled.buffer = buf and oled.show(). This approach is faster for complex scenes because you avoid repeated calls to the pixel() method.
Compatibility with different boards
The 72x40 OLED works with any MicroPython board that has I2C. On the Raspberry Pi Pico, use I2C0 or I2C1. On the ESP32, the default I2C pins are GPIO 21 and 22, but you can use any GPIOs with machine.SoftI2C. On the BBC micro:bit, the I2C pins are on the edge connector (P19 for SCL, P20 for SDA). On the Pyboard, use X9 and X10. The I2C bus voltage must be 3.3V; if your board uses 5V logic, use a level shifter. The display’s I2C address is fixed, but some modules have a jumper to change it to 0x3D. Check the module’s documentation. The 0.42 inch 72x40 oled display module from DisplayModule uses the SH1107 controller and has a default address of 0x3C. It includes 4.7kΩ pull-up resistors on the board, so no external resistors are needed. The module’s dimensions are 27.5mm x 14.5mm, making it suitable for compact projects.
Performance benchmarks
Here are some typical performance numbers for the 72x40 OLED with MicroPython on a Raspberry Pi Pico at 400 kHz I2C:
Operation | Time (ms)
Full buffer clear and show | 12
Draw 10 text characters | 8
Draw a 72x40 bitmap | 15
Update 10 pixels individually | 5
Scroll text one pixel | 3
These times are measured with the SH1107 driver. The modified SSD1306 driver is slightly slower due to additional overhead. For ESP32 at 240 MHz, times are about 30% faster. The I2C bus speed has a linear effect: at 100 kHz, times are 4x longer. For real-time applications, use 400 kHz and avoid frequent full buffer updates. Instead, update only changed regions by calling show() after modifying the buffer. The show() method sends the entire buffer, but you can optimize by sending only dirty pages. Some libraries support partial updates, but the SH1107 driver doesn’t implement it. You can manually send commands to set the column and page range, then write only the affected bytes. This reduces update time for small changes to under 1 ms.
Memory considerations
The 360-byte buffer is small, but MicroPython’s overhead adds about 200 bytes for the I2C object and 100 bytes for the OLED object. Total memory usage is under 1 KB. On boards with limited RAM, like the ESP8266 (80 KB), this is fine. On the Raspberry Pi Pico (264 KB), you have plenty of room for additional data. If you use frame buffers for double buffering, you need another 360 bytes, totaling 720 bytes. This is still negligible. The framebuf module adds about 1 KB of code. For complex animations, consider using the uasyncio library to avoid blocking the main loop. The display update is I2C-bound, so you can run other tasks in between.
Real-world project examples
You can use the 72x40 OLED for a miniature weather station, a system monitor displaying CPU temperature and RAM usage, a digital clock with seconds, a sensor readout for temperature and humidity, or a simple game like Snake. The small size is ideal for wearable devices or keychain gadgets. The I2C interface allows multiple devices on the same bus, so you can add sensors like the BME280 or MPU6050. The display’s low power consumption makes it suitable for battery-powered projects, drawing about 10 mA at 3.3V. You can put the display to sleep with oled.power(0) to save power, waking it with oled.power(1). The sleep mode reduces current to under 1 microamp. The display’s operating temperature range is -40°C to 85°C, so it works in harsh environments.
Debugging tips
If the display shows random pixels, the initialization sequence is likely incorrect. Use a logic analyzer to check the I2C traffic. The first command should be 0xAE (display off), followed by 0x81 and 0xCF (contrast). If you see 0x00 or 0xFF, the driver is sending wrong data. Another common issue is the column address range: the SH1107 expects column addresses from 0 to 71, but some drivers start at 32. Check the datasheet for your specific module. The 0.42 inch 72x40 oled display from DisplayModule uses column addresses 0-71. If you use a generic SSD1306 driver, it might send column addresses 0-127, which causes the display to show only the first 72 columns correctly, but the rest is ignored. To fix this, modify the write_cmd method to set the column range to 0-71 before data transfer. You can also use the sh1107.py library, which handles this correctly. If the display is too dim, increase the contrast with oled.contrast(200). If it’s too bright, decrease it. The display’s lifetime is affected by high contrast, so use the minimum necessary for your application.
Alternative libraries and approaches
Besides the SSD1306 and SH1107 drivers, you can write your own low-level driver using the machine.I2C methods. This gives you full control over the initialization sequence and buffer