SSD1306 PBM Viewer
I recently bought a SSD1306 miniature OLED display. This communicates through I2C and can be connected to various boards, like the Raspberry Pi 3 which I have used.
As an initial experiment, I have made a Python script that will load a 2-color PBM image file and display it on the OLED. The image has to be in binary (P4) format and exactly 128x64 which is the same as the resolution. I have re-used the same initialization commands as mentioned here to get up and running quickly.
Enjoy:
#!/usr/bin/python
import smbus
class SSD1306(object):
def __init__(self, bus=1, address=0x3c):
self._address = address
self._bus = smbus.SMBus(bus)
self._command(0xae) # Display off.
self._command(0xa8) # Multiplex ratio...
self._command(0x3f) # ...63
self._command(0xd3) # Display offset...
self._command(0x00) # ...0
self._command(0x40) # Display start line at 0.
self._command(0xa1) # Segment Re-map with column 127 mapped to SEG0.
self._command(0xc8) # Remapped mode, scan from COM[N-1] to COM0.
self._command(0xda) # COM pins hardware configuration...
self._command(0x32) # ...Alternative and Left/Right
self._command(0xa4) # Entire display ON.
self._command(0xa6) # Inverse display mode.
self._command(0xd5) # Display clock...
self._command(0x80) # ...No clock divide ratio and max frequency.
self._command(0x8d) # Charge pump...
self._command(0x14) # ...Enabled.
self._command(0x20) # Memory addressing mode...
self._command(0x20) # ...Horizontal.
self._command(0xaf) # Display on.
def _command(self, command_byte):
self._bus.write_byte_data(self._address, 0x00, command_byte)
def _data(self, data_byte):
self._bus.write_byte_data(self._address, 0x40, data_byte)
def reset_cursor(self):
self._command(0x21) # Column address...
self._command(0x00) # ...start at 0...
self._command(0x7f) # ...end at 127.
self._command(0x22) # Page address...
self._command(0x00) # ...start at 0...
self._command(0x07) # ...end at 7.
def pbm(self, filename):
fh = open(filename, "r")
if not fh.readline().startswith("P4"):
raise Exception("Not a binary PBM image!")
header = fh.readline()
while header.startswith("#"):
header = fh.readline() # Ignore comments.
if not header.startswith("128 64"):
raise Exception("Dimensions must be 128x64!")
data = fh.read()
fh.close()
if len(data) != 1024:
raise Exception("Size of data is not 1024 bytes!")
self.reset_cursor()
for row_offset in [0, 128, 256, 384, 512, 640, 768, 896]:
for column in range(0, 128):
byte = ((ord(data[row_offset + (column / 8) ]) >> (7 - (column % 8))) & 1) << 1
byte += ((ord(data[row_offset + (column / 8) + 16 ]) >> (7 - (column % 8))) & 1) << 0
byte += ((ord(data[row_offset + (column / 8) + 32 ]) >> (7 - (column % 8))) & 1) << 3
byte += ((ord(data[row_offset + (column / 8) + 48 ]) >> (7 - (column % 8))) & 1) << 2
byte += ((ord(data[row_offset + (column / 8) + 64 ]) >> (7 - (column % 8))) & 1) << 5
byte += ((ord(data[row_offset + (column / 8) + 80 ]) >> (7 - (column % 8))) & 1) << 4
byte += ((ord(data[row_offset + (column / 8) + 96 ]) >> (7 - (column % 8))) & 1) << 7
byte += ((ord(data[row_offset + (column / 8) + 112]) >> (7 - (column % 8))) & 1) << 6
self._data(byte ^ 0xff)
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
print "Usage: %s <128x64 binary PBM image>" % (sys.argv[0])
sys.exit(1)
display = SSD1306()
display.pbm(sys.argv[1])
And here is how it looks with an image loaded: