├── FreeMono.ttf ├── FreeMonoBold.ttf ├── FreeSans.ttf ├── FreeSerif.ttf ├── FreeSerifItalic.ttf ├── ReadMe.md ├── example1-oled96-rpi.py ├── example2-oled96-rpi.py ├── example2-oled96-vgpio.py ├── lib_oled96.py └── pi_logo.png /FreeMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BLavery/lib_oled96/c09bcbaa6575e0a5c4a426eeccd989dc93a72ee6/FreeMono.ttf -------------------------------------------------------------------------------- /FreeMonoBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BLavery/lib_oled96/c09bcbaa6575e0a5c4a426eeccd989dc93a72ee6/FreeMonoBold.ttf -------------------------------------------------------------------------------- /FreeSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BLavery/lib_oled96/c09bcbaa6575e0a5c4a426eeccd989dc93a72ee6/FreeSans.ttf -------------------------------------------------------------------------------- /FreeSerif.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BLavery/lib_oled96/c09bcbaa6575e0a5c4a426eeccd989dc93a72ee6/FreeSerif.ttf -------------------------------------------------------------------------------- /FreeSerifItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BLavery/lib_oled96/c09bcbaa6575e0a5c4a426eeccd989dc93a72ee6/FreeSerifItalic.ttf -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | ## May 2018 - This is no longer being maintained by the original author. 2 | ## Please feel free to fork, copy, adapt if you find it useable 3 | 4 | A python library for I2C 0.96" 128x64 OLED display using SSD1306 chip. 5 | A common library suiting both Raspberry Pi and Virtual-GPIO. 6 | 7 | Library version 0.5 supported python. Version 0.7 (June 2015) supports also python3. 8 | 9 | This oled display is the monochrome 4-pin type (I2C), not the SPI ones (identify by more pins). 10 | Typically about $5 on eBay from LOTS of suppliers. Count the PCB pins - don't necessarily trust the eBay headline! 11 | There are some "two-colour" ones, but these are simply a different (fixed) colour for the top 16 pixel lines. 12 | 13 | Interfacing is trivial, and they seem to work fine on 3.3V and 5V. 14 | On arduino (V-GPIO) the arduino's high-value pullups seem to work OK without anything added. 15 | 16 | The text, font, image and graphic work is handled by the Python Imaging Library, 17 | and ttf or other font files from anywhere work fine, at any scaling. 1-bit BMP or PNG images can be displayed. 18 | 19 | "PIL" is wonderfully versatile and competent for "writing/drawing" to a display like this. 20 | However, original PIL is now becoming obsolete. Instead use the clone ""Pillow"", now available for Python (2) and Python3. 21 | 22 | Installing Pillow: 23 | You need to "sudo apt-get install python-dev python-setuptools". 24 | Then Pillow can be installed for python: "sudo easy_install Pillow". Takes quite a while. 25 | Need also to "sudo apt-get install python3-dev python3-setuptools" 26 | Then Pillow can be installed for python3: "sudo easy_install3 Pillow". 27 | 28 | Getting I2C and SMBus working on recent Raspbian: 29 | Ensure "i2c-dev" is listed in the file /etc/modules. 30 | Use (new versions) raspi-config utility to turn on I2C functionality. 31 | Reboot. Enter "ls /dev" at a terminal, and i2c-1 should be listed as a working device. 32 | Now install smbus for python: "sudo apt-get install python-smbus python3-smbus". 33 | (Note the Raspbian image of 5/5/2015 does not list python3-smbus. Just do a "apt-get update" first.) 34 | 35 | This library has been evolved from RM Hull's very fine library at 36 | https://github.com/rm-hull/ssd1306 37 | If you simply want to use it on Raspberry Pi, the Hull version may well suit you better. 38 | 39 | And, oh yes, some eBay versions might look very similar, but they don't necessarily have same pin order. 40 | I have two with swapped VCC and GND. Oops! 41 | -------------------------------------------------------------------------------- /example1-oled96-rpi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from lib_oled96 import ssd1306 4 | 5 | from smbus import SMBus 6 | i2cbus = SMBus(1) # 1 = Raspberry Pi but NOT early REV1 board 7 | 8 | oled = ssd1306(i2cbus) # create oled object, nominating the correct I2C bus, default address 9 | 10 | # we are ready to do some output ... 11 | 12 | # put border around the screen: 13 | oled.canvas.rectangle((0, 0, oled.width-1, oled.height-1), outline=1, fill=0) 14 | 15 | # Write two lines of text. 16 | oled.canvas.text((40,15), 'Hello', fill=1) 17 | oled.canvas.text((40,40), 'World!', fill=1) 18 | 19 | # now display that canvas out to the hardware 20 | oled.display() 21 | -------------------------------------------------------------------------------- /example2-oled96-rpi.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # RASPBERRY PI VERSION 4 | 5 | # NOTE: You need to have PIL installed for your python at the Pi 6 | 7 | from lib_oled96 import ssd1306 8 | from time import sleep 9 | from PIL import ImageFont, ImageDraw, Image 10 | font = ImageFont.load_default() 11 | 12 | 13 | from smbus import SMBus # These are the only two variant lines !! 14 | i2cbus = SMBus(1) # 15 | # 1 = Raspberry Pi but NOT early REV1 board 16 | 17 | oled = ssd1306(i2cbus) 18 | draw = oled.canvas # "draw" onto this canvas, then call display() to send the canvas contents to the hardware. 19 | 20 | 21 | # Draw some shapes. 22 | # First define some constants to allow easy resizing of shapes. 23 | padding = 2 24 | shape_width = 20 25 | top = padding 26 | bottom = oled.height - padding - 1 27 | # Draw a rectangle of the same size of screen 28 | draw.rectangle((0, 0, oled.width-1, oled.height-1), outline=1, fill=0) 29 | # Move left to right keeping track of the current x position for drawing shapes. 30 | x = padding 31 | 32 | # Draw an ellipse. 33 | draw.ellipse((x, top, x+shape_width, bottom), outline=1, fill=0) 34 | x += shape_width + padding 35 | # Draw a filled rectangle. 36 | draw.rectangle((x, top, x+shape_width, bottom), outline=1, fill=1) 37 | x += shape_width + padding 38 | # Draw a triangle. 39 | draw.polygon([(x, bottom), (x+shape_width/2, top), (x+shape_width, bottom)], outline=1, fill=0) 40 | x += shape_width+padding 41 | # Draw an X. 42 | draw.line((x, bottom, x+shape_width, top), fill=1) 43 | draw.line((x, top, x+shape_width, bottom), fill=1) 44 | #x += shape_width+padding 45 | 46 | # Load default font. 47 | font = ImageFont.load_default() 48 | 49 | # Nah, second thoughts ... Alternatively load another TTF font. 50 | 51 | font = ImageFont.truetype('FreeSerif.ttf', 15) 52 | 53 | 54 | oled.display() 55 | sleep(3) 56 | 57 | # Write two lines of text. 58 | draw.text((x, top), 'Hello', font=font, fill=1) 59 | draw.text((x, top+40), 'World!', font=font, fill=1) 60 | oled.display() 61 | sleep(3) 62 | draw.rectangle((0, 0, oled.width-1, oled.height-1), outline=255, fill=1) 63 | oled.display() 64 | sleep(3) 65 | logo = Image.open('pi_logo.png') 66 | draw.bitmap((32, 0), logo, fill=0) 67 | 68 | oled.display() 69 | sleep(3) 70 | draw.rectangle((0, 0, oled.width-1, oled.height-1), outline=1, fill=0) 71 | font = ImageFont.truetype('FreeSerifItalic.ttf', 57) 72 | draw.text((18, 0), 'A5y', font=font, fill=1) 73 | oled.display() 74 | 75 | sleep(3) 76 | draw.rectangle((0, 0, oled.width-1, oled.height-1), outline=1, fill=0) 77 | font = ImageFont.truetype('FreeSans.ttf', 10) 78 | draw.text((0, 0), 'Hello me very good mateys ...', font=font, fill=1) 79 | draw.text((0, 10), 'Well now, what would you like', font=font, fill=1) 80 | draw.text((0, 20), 'to be told this sunny Sunday?', font=font, fill=1) 81 | draw.text((0, 30), 'Would a wild story amuse you?', font=font, fill=1) 82 | draw.text((0, 40), 'This is a very long statement,', font=font, fill=1) 83 | draw.text((0, 50), 'so believe it if you like.', font=font, fill=1) 84 | oled.display() 85 | 86 | 87 | sleep(3) 88 | draw.rectangle((0, 0, oled.width-1, oled.height-1), outline=20, fill=0) 89 | font = ImageFont.truetype('FreeSans.ttf', 14) 90 | draw.text((0, 0), 'Hello me good mateys', font=font, fill=1) 91 | draw.text((0, 15), 'What would you like', font=font, fill=1) 92 | draw.text((0, 30), 'to be told this day?', font=font, fill=1) 93 | draw.text((0, 45), 'This is a long story,', font=font, fill=1) 94 | oled.display() 95 | 96 | sleep(3) 97 | oled.onoff(0) # kill the oled. RAM contents still there. 98 | sleep(3) 99 | oled.onoff(1) # Wake it up again. Display contents intact 100 | 101 | sleep(3) 102 | oled.cls() # Oled still on, but screen contents now blacked out 103 | -------------------------------------------------------------------------------- /example2-oled96-vgpio.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # VIRTUAL-GPIO VERSION 4 | 5 | # NOTE: You need to have PIL installed for your python at the hosting PC 6 | 7 | from lib_oled96 import ssd1306 8 | from time import sleep 9 | from PIL import ImageFont, ImageDraw, Image 10 | font = ImageFont.load_default() 11 | 12 | 13 | import virtGPIO as GPIO # These are the only two variant lines !! 14 | i2cbus = GPIO.I2C() # 15 | 16 | 17 | oled = ssd1306(i2cbus) 18 | draw = oled.canvas # "draw" onto this canvas, then call display() to send the canvas contents to the hardware. 19 | 20 | 21 | # Draw some shapes. 22 | # First define some constants to allow easy resizing of shapes. 23 | padding = 2 24 | shape_width = 20 25 | top = padding 26 | bottom = oled.height - padding - 1 27 | # Draw a rectangle of the same size of screen 28 | draw.rectangle((0, 0, oled.width-1, oled.height-1), outline=1, fill=0) 29 | # Move left to right keeping track of the current x position for drawing shapes. 30 | x = padding 31 | 32 | # Draw an ellipse. 33 | draw.ellipse((x, top, x+shape_width, bottom), outline=1, fill=0) 34 | x += shape_width + padding 35 | # Draw a filled rectangle. 36 | draw.rectangle((x, top, x+shape_width, bottom), outline=1, fill=1) 37 | x += shape_width + padding 38 | # Draw a triangle. 39 | draw.polygon([(x, bottom), (x+shape_width/2, top), (x+shape_width, bottom)], outline=1, fill=0) 40 | x += shape_width+padding 41 | # Draw an X. 42 | draw.line((x, bottom, x+shape_width, top), fill=1) 43 | draw.line((x, top, x+shape_width, bottom), fill=1) 44 | #x += shape_width+padding 45 | 46 | # Load default font. 47 | font = ImageFont.load_default() 48 | 49 | # Nah, second thoughts ... Alternatively load another TTF font. 50 | 51 | font = ImageFont.truetype('FreeSerif.ttf', 15) 52 | 53 | 54 | oled.display() 55 | sleep(3) 56 | 57 | # Write two lines of text. 58 | draw.text((x, top), 'Hello', font=font, fill=1) 59 | draw.text((x, top+40), 'World!', font=font, fill=1) 60 | oled.display() 61 | sleep(3) 62 | draw.rectangle((0, 0, oled.width-1, oled.height-1), outline=255, fill=1) 63 | oled.display() 64 | sleep(3) 65 | logo = Image.open('pi_logo.png') 66 | draw.bitmap((32, 0), logo, fill=0) 67 | 68 | oled.display() 69 | sleep(3) 70 | draw.rectangle((0, 0, oled.width-1, oled.height-1), outline=1, fill=0) 71 | font = ImageFont.truetype('FreeSerifItalic.ttf', 57) 72 | draw.text((18, 0), 'A5y', font=font, fill=1) 73 | oled.display() 74 | 75 | sleep(3) 76 | draw.rectangle((0, 0, oled.width-1, oled.height-1), outline=1, fill=0) 77 | font = ImageFont.truetype('FreeSans.ttf', 10) 78 | draw.text((0, 0), 'Hello me very good mateys ...', font=font, fill=1) 79 | draw.text((0, 10), 'Well now, what would you like', font=font, fill=1) 80 | draw.text((0, 20), 'to be told this sunny Sunday?', font=font, fill=1) 81 | draw.text((0, 30), 'Would a wild story amuse you?', font=font, fill=1) 82 | draw.text((0, 40), 'This is a very long statement,', font=font, fill=1) 83 | draw.text((0, 50), 'so believe it if you like.', font=font, fill=1) 84 | oled.display() 85 | 86 | 87 | sleep(3) 88 | draw.rectangle((0, 0, oled.width-1, oled.height-1), outline=20, fill=0) 89 | font = ImageFont.truetype('FreeSans.ttf', 14) 90 | draw.text((0, 0), 'Hello me good mateys', font=font, fill=1) 91 | draw.text((0, 15), 'What would you like', font=font, fill=1) 92 | draw.text((0, 30), 'to be told this day?', font=font, fill=1) 93 | draw.text((0, 45), 'This is a long story,', font=font, fill=1) 94 | oled.display() 95 | 96 | sleep(3) 97 | oled.onoff(0) # kill the oled. RAM contents still there. 98 | sleep(3) 99 | oled.onoff(1) # Wake it up again. Display contents intact 100 | 101 | sleep(3) 102 | oled.cls() # Oled still on, but screen contents now blacked out 103 | -------------------------------------------------------------------------------- /lib_oled96.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Substantially derived from code by (c) 2015 Richard Hull The MIT License (MIT) 4 | # https://github.com/rm-hull/ssd1306: 5 | # "Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # "of this software and associated documentation files (the "Software"), to deal 7 | # "in the Software without restriction, including without limitation the rights 8 | # "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # "copies of the Software, and to permit persons to whom the Software is 10 | # "furnished to do so, subject to the following conditions: 11 | # "The above copyright notice and this permission notice shall be included in 12 | # "all copies or substantial portions of the Software." 13 | # 14 | # B Lavery 2015: 15 | # This derivative "library" module is not installed to the python system as Hull's version was: 16 | # it simply resides alongside your own python script. 17 | # In this version, the I2C bus object needs to be handed in as a parameter from your user code. 18 | # This makes this one same library file work with either Raspberry Pi or Virtual GPIO system. 19 | # Hull's (clever) auto-displaying "canvas" is replaced by a persistent draw object 20 | # which can be incrementally changed. This canvas needs coded "display()" calls to push to the hardware. 21 | 22 | from PIL import Image, ImageDraw 23 | 24 | 25 | class ssd1306(): 26 | 27 | def __init__(self, bus, address=0x3C): 28 | self.cmd_mode = 0x00 29 | self.data_mode = 0x40 30 | self.bus = bus 31 | self.addr = address 32 | self.width = 128 33 | self.height = 64 34 | self.pages = int(self.height / 8) 35 | self.image = Image.new('1', (self.width, self.height)) 36 | self.canvas = ImageDraw.Draw(self.image) # this is a "draw" object for preparing display contents 37 | 38 | self._command( 39 | const.DISPLAYOFF, 40 | const.SETDISPLAYCLOCKDIV, 0x80, 41 | const.SETMULTIPLEX, 0x3F, 42 | const.SETDISPLAYOFFSET, 0x00, 43 | const.SETSTARTLINE, 44 | const.CHARGEPUMP, 0x14, 45 | const.MEMORYMODE, 0x00, 46 | const.SEGREMAP, 47 | const.COMSCANDEC, 48 | const.SETCOMPINS, 0x12, 49 | const.SETCONTRAST, 0xCF, 50 | const.SETPRECHARGE, 0xF1, 51 | const.SETVCOMDETECT, 0x40, 52 | const.DISPLAYALLON_RESUME, 53 | const.NORMALDISPLAY, 54 | const.DISPLAYON) 55 | 56 | def _command(self, *cmd): 57 | """ 58 | Sends a command or sequence of commands through to the 59 | device - maximum allowed is 32 bytes in one go. 60 | LIMIT ON ARDUINO: CMD BYTE + 31 = 32, SO LIMIT TO 31 bl 61 | """ 62 | assert(len(cmd) <= 31) 63 | self.bus.write_i2c_block_data(self.addr, self.cmd_mode, list(cmd)) 64 | 65 | def _data(self, data): 66 | """ 67 | Sends a data byte or sequence of data bytes through to the 68 | device - maximum allowed in one transaction is 32 bytes, so if 69 | data is larger than this it is sent in chunks. 70 | In our library, only data operation used is 128x64 long, ie whole canvas. 71 | """ 72 | 73 | for i in range(0, len(data), 31): 74 | self.bus.write_i2c_block_data(self.addr, self.data_mode, list(data[i:i+31])) 75 | 76 | 77 | def display(self): 78 | """ 79 | The image on the "canvas" is flushed through to the hardware display. 80 | Takes the 1-bit image and dumps it to the SSD1306 OLED display. 81 | """ 82 | 83 | self._command( 84 | const.COLUMNADDR, 0x00, self.width-1, # Column start/end address 85 | const.PAGEADDR, 0x00, self.pages-1) # Page start/end address 86 | 87 | pix = list(self.image.getdata()) 88 | step = self.width * 8 89 | buf = [] 90 | for y in range(0, self.pages * step, step): 91 | i = y + self.width-1 92 | while i >= y: 93 | byte = 0 94 | for n in range(0, step, self.width): 95 | byte |= (pix[i + n] & 0x01) << 8 96 | byte >>= 1 97 | 98 | buf.append(byte) 99 | i -= 1 100 | 101 | self._data(buf) # push out the whole lot 102 | 103 | def cls(self): 104 | self.canvas.rectangle((0, 0, self.width-1, self.height-1), outline=0, fill=0) 105 | self.display() 106 | 107 | def onoff(self, onoff): 108 | if onoff == 0: 109 | self._command(const.DISPLAYOFF) 110 | else: 111 | self._command(const.DISPLAYON) 112 | 113 | 114 | class const: 115 | CHARGEPUMP = 0x8D 116 | COLUMNADDR = 0x21 117 | COMSCANDEC = 0xC8 118 | COMSCANINC = 0xC0 119 | DISPLAYALLON = 0xA5 120 | DISPLAYALLON_RESUME = 0xA4 121 | DISPLAYOFF = 0xAE 122 | DISPLAYON = 0xAF 123 | EXTERNALVCC = 0x1 124 | INVERTDISPLAY = 0xA7 125 | MEMORYMODE = 0x20 126 | NORMALDISPLAY = 0xA6 127 | PAGEADDR = 0x22 128 | SEGREMAP = 0xA0 129 | SETCOMPINS = 0xDA 130 | SETCONTRAST = 0x81 131 | SETDISPLAYCLOCKDIV = 0xD5 132 | SETDISPLAYOFFSET = 0xD3 133 | SETHIGHCOLUMN = 0x10 134 | SETLOWCOLUMN = 0x00 135 | SETMULTIPLEX = 0xA8 136 | SETPRECHARGE = 0xD9 137 | SETSEGMENTREMAP = 0xA1 138 | SETSTARTLINE = 0x40 139 | SETVCOMDETECT = 0xDB 140 | SWITCHCAPVCC = 0x2 141 | -------------------------------------------------------------------------------- /pi_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BLavery/lib_oled96/c09bcbaa6575e0a5c4a426eeccd989dc93a72ee6/pi_logo.png --------------------------------------------------------------------------------