Skip to content

Instantly share code, notes, and snippets.

@TheRayTracer
Last active August 4, 2019 00:03

Revisions

  1. TheRayTracer revised this gist Aug 4, 2019. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions SSD1306.py
    Original file line number Diff line number Diff line change
    @@ -210,14 +210,14 @@ def __OpenSPI(self):
    return

    def __WriteCommand(self, data):
    gpio.output(self.dc, self.COMMAND)
    if isinstance(data, list) or isinstance(data, tuple):
    gpio.output(self.dc, self.COMMAND)
    self.spi.xfer(data)
    return

    def __WriteData(self, data):
    gpio.output(self.dc, self.DATA)
    if isinstance(data, list) or isinstance(data, tuple):
    gpio.output(self.dc, self.DATA)
    self.spi.xfer(data)
    return

  2. TheRayTracer revised this gist Jan 18, 2019. 1 changed file with 0 additions and 0 deletions.
    Binary file modified activity3.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  3. TheRayTracer revised this gist May 1, 2016. No changes.
  4. TheRayTracer revised this gist Jan 31, 2016. 4 changed files with 153 additions and 39 deletions.
    111 changes: 72 additions & 39 deletions SSD1306.py
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,8 @@
    import struct
    import spidev
    import sys
    import time
    import random
    import RPi.GPIO as gpio

    ascii = [
    @@ -134,9 +136,11 @@
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ]
    ]

    MAX_WIDTH = 0x80
    MAX_HEIGHT = 0x40
    MAX_PAGE = 0x08
    MAX_WIDTH = 0x80
    MAX_HEIGHT_FULL = 0x40
    MAX_HEIGHT_HALF = 0x20
    MAX_PAGE_FULL = 0x08
    MAX_PAGE_HALF = 0x04

    H_SCROLL_ENABLE_TOP = 0x01
    H_SCROLL_ENABLE_BOTTOM = 0x02
    @@ -145,6 +149,8 @@
    MEMORY_MODE = 0x20
    SET_COLUMN = 0x21
    SET_PAGE = 0x22
    SET_RIGHT_SCROLL = 0x26
    SET_LEFT_SCROLL = 0x27
    SET_START_LINE = 0x40

    DEACTIVATE_SCROLLING = 0x2E
    @@ -153,7 +159,8 @@
    SET_CONTRAST = 0x81
    CHARGE_PUMP = 0x8D

    SET_SEGMENT_REMAP = 0xA1
    SET_SEGMENT_REMAP_A = 0xA0
    SET_SEGMENT_REMAP_B = 0xA1
    ENTIRE_DISPLAY_RESUME = 0xA4
    ENTIRE_DISPLAY_ON = 0xA5
    NORMAL_DISPLAY = 0xA6
    @@ -167,18 +174,20 @@

    SET_DISPLAY_OFFSET = 0xD3
    SET_DISPLAY_CLOCK_DIV = 0xD5
    SET_COM_PINS = 0xDA

    NO_OPERATION = 0xE3

    class SSD1306:
    COMMAND = gpio.LOW
    DATA = gpio.HIGH

    def __init__(self, dc, rst, cs):
    def __init__(self, dc, rst, cs, page_size):
    self.page_size = page_size
    self.rst = rst
    self.dc = dc
    self.cs = cs
    self._buffer = [0] * (MAX_WIDTH * MAX_PAGE)
    self._buffer = [0] * (MAX_WIDTH * self.page_size)
    # Setup GPIO.
    gpio.setmode(gpio.BCM)
    gpio.setup(self.dc, gpio.OUT)
    @@ -213,52 +222,61 @@ def __WriteData(self, data):
    return

    def __Setup(self):
    self.spi.cshigh = True
    self.spi.xfer([0])
    gpio.output(self.cs, gpio.LOW)
    time.sleep(0.1)
    time.sleep(0.2)
    gpio.output(self.rst, gpio.LOW)
    time.sleep(0.5)
    time.sleep(0.2)
    gpio.output(self.rst, gpio.HIGH)
    time.sleep(0.5)
    self.spi.cshigh = False
    self.spi.xfer([0])
    time.sleep(0.2)
    self.__WriteCommand([DISPLAY_OFF])
    self.__WriteCommand([SET_MULTIPLEX, 0x3F])
    if self.page_size == MAX_PAGE_FULL:
    self.__WriteCommand([SET_MULTIPLEX, 0x3F])
    else:
    self.__WriteCommand([SET_MULTIPLEX, 0x1F])
    self.__WriteCommand([SET_DISPLAY_OFFSET, 0x00])
    self.__WriteCommand([SET_START_LINE])
    self.__WriteCommand([SET_SEGMENT_REMAP])
    self.__WriteCommand([SET_SEGMENT_REMAP_B])
    self.__WriteCommand([SCAN_DIRECTION_UP])
    if self.page_size == MAX_PAGE_FULL:
    self.__WriteCommand([SET_COM_PINS, 0x12])
    else:
    self.__WriteCommand([SET_COM_PINS, 0x02])
    self.__WriteCommand([SET_CONTRAST, 0x7F])
    self.__WriteCommand([NORMAL_DISPLAY])
    self.__WriteCommand([SET_DISPLAY_CLOCK_DIV, 0x80])
    self.__WriteCommand([CHARGE_PUMP, 0x14]) # Enable Charge Pump.
    self.__WriteCommand([SCAN_DIRECTION_UP])
    self.__WriteCommand([MEMORY_MODE, 0x00]) # Setup the address mode.
    self.__WriteCommand([SET_COLUMN, 0x00, 0x7F])
    self.__WriteCommand([SET_PAGE, 0x00, 0x07])
    self.__WriteCommand([SET_PAGE, 0x00, self.page_size - 1])
    self.__WriteCommand([DISPLAY_ON])
    return

    def Reset(self):
    gpio.output(self.rst, gpio.LOW)
    time.sleep(0.2)
    gpio.output(self.rst, gpio.HIGH)
    return

    def Remove(self):
    gpio.cleanup()
    self.spi.close()
    return

    def DrawPixel(self, x, y):
    page_offset = (y / MAX_PAGE) * MAX_WIDTH
    t = 0x01 << (y % MAX_PAGE)
    page_offset = (y / 8) * MAX_WIDTH
    t = 0x01 << (y % 8)
    self._buffer[page_offset + x] |= t
    return

    def DrawHorizontalLine(self, y):
    page_offset = (y / MAX_PAGE) * MAX_WIDTH
    t = 0x01 << (y % MAX_PAGE)
    page_offset = (y / 8) * MAX_WIDTH
    t = 0x01 << (y % 8)
    for i in xrange(0, MAX_WIDTH, 1):
    self._buffer[page_offset + i] |= t
    return

    def DrawVerticalLine(self, x):
    for i in xrange(0, MAX_PAGE, 1):
    for i in xrange(0, self.page_size, 1):
    self._buffer[(i * MAX_WIDTH) + x] = 0xFF
    return

    @@ -343,7 +361,7 @@ def DrawString(self, x, y, str):
    return

    def Clear(self):
    for i in xrange(0, MAX_WIDTH * MAX_PAGE, 1):
    for i in xrange(0, MAX_WIDTH * self.page_size, 1):
    self._buffer[i] = 0x00
    return

    @@ -367,11 +385,11 @@ def EnableDisplay(self, enable):

    def SetScrollMode(self, horizontal):
    if horizontal == H_SCROLL_ENABLE_TOP:
    self.__WriteCommand([0x26, 0x00, 0x00, 0x00, 0x01, 0x00, 0xFF])
    self.__WriteCommand([SET_RIGHT_SCROLL, 0x00, 0x00, 0x00, 0x01, 0x00, 0xFF])
    elif horizontal == H_SCROLL_ENABLE_BOTTOM:
    self.__WriteCommand([0x26, 0x00, 0x02, 0x00, 0x07, 0x00, 0xFF])
    self.__WriteCommand([SET_RIGHT_SCROLL, 0x00, 0x02, 0x00, self.page_size - 1, 0x00, 0xFF])
    elif horizontal == H_SCROLL_ENABLE_ALL:
    self.__WriteCommand([0x26, 0x00, 0x00, 0x00, 0x07, 0x00, 0xFF])
    self.__WriteCommand([SET_RIGHT_SCROLL, 0x00, 0x00, 0x00, self.page_size - 1, 0x00, 0xFF])
    return

    def EnableScrollMode(self, enable):
    @@ -392,7 +410,10 @@ def TestModeTest(self):

    def LineTest(self):
    self.Clear()
    for y in xrange(0, 16, 1):
    m = 8
    if self.page_size == MAX_PAGE_FULL:
    m = 16
    for y in xrange(0, m, 1):
    self.DrawHorizontalLine(y * 4)
    self.Flip()
    time.sleep(4)
    @@ -405,32 +426,44 @@ def LineTest(self):

    def ShapeTest(self):
    self.Clear()
    self.DrawCircle(20, 32, 16)
    self.DrawTriangle(47, 48, 79, 48, 63, 16)
    self.DrawRect(90, 16, 122, 48)
    if self.page_size == MAX_PAGE_FULL:
    self.DrawCircle(20, 32, 16)
    self.DrawTriangle(47, 48, 79, 48, 63, 16)
    self.DrawRect(90, 16, 122, 48)
    else:
    self.DrawCircle(20, 16, 14)
    self.DrawTriangle(47, 30, 79, 30, 63, 2)
    self.DrawRect(92, 2, 120, 30)
    self.Flip()
    time.sleep(8)
    return

    def CharTest(self):
    self.Clear()
    i = 48
    for k in xrange(0, 16, 1):
    self.DrawChar(k * 8, 4, chr(i))
    i = i + 1
    i = 32
    for j in xrange(0, 6, 1):
    if self.page_size == MAX_PAGE_FULL:
    i = 48
    for k in xrange(0, 16, 1):
    self.DrawChar(k * 8, j * 8 + 16, chr(i))
    self.DrawChar(k * 8, 4, chr(i))
    i = i + 1
    i = 32
    for j in xrange(0, 6, 1):
    for k in xrange(0, 16, 1):
    self.DrawChar(k * 8, j * 8 + 16, chr(i))
    i = i + 1
    else:
    i = 32
    for j in xrange(0, 4, 1):
    for k in xrange(0, 16, 1):
    self.DrawChar(k * 8, j * 8, chr(i))
    i = i + 1
    self.Flip()
    time.sleep(8)
    return

    def ScrollTest(self):
    self.Clear()
    self.DrawString(4, 2, "Scrolling Test")
    self.DrawString(4, 28, "Scrolling Test")
    self.DrawString(4, 18, "Scrolling Test")
    self.Flip()
    self.SetScrollMode(H_SCROLL_ENABLE_TOP)
    self.EnableScrollMode(True)
    @@ -451,7 +484,7 @@ def ScrollTest(self):
    SSD1306_PIN_RST = 25

    if __name__ == '__main__':
    device = SSD1306(SSD1306_PIN_DC, SSD1306_PIN_RST, SSD1306_PIN_CS)
    device = SSD1306(SSD1306_PIN_DC, SSD1306_PIN_RST, SSD1306_PIN_CS, MAX_PAGE_FULL)
    try:
    device.EnableDisplay(True)
    device.TestModeTest()
    Binary file added activity3.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
    Binary file added activity4.jpg
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
    81 changes: 81 additions & 0 deletions dice.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,81 @@
    import time
    import random
    import SSD1306
    import RPi.GPIO as gpio

    class Dice:
    def __init__(self, device):
    self._device = device
    self._face = random.randint(1, 6)

    def Draw(self, i, debug):
    device.DrawRect(32 * i + 2, 2, 32 * i + 28, 30)
    if debug == True:
    device.DrawChar(32 * i + 13, 12, chr(self._face + 48))
    else:
    if self._face == 1:
    device.DrawRect(32 * i + 14, 14, 32 * i + 16, 16)
    elif self._face == 2:
    device.DrawRect(32 * i + 7, 7, 32 * i + 9, 9)
    device.DrawRect(32 * i + 21, 21, 32 * i + 23, 23)
    elif self._face == 3:
    device.DrawRect(32 * i + 7, 7, 32 * i + 9, 9)
    device.DrawRect(32 * i + 14, 14, 32 * i + 16, 16)
    device.DrawRect(32 * i + 21, 21, 32 * i + 23, 23)
    elif self._face == 4:
    device.DrawRect(32 * i + 7, 7, 32 * i + 9, 9)
    device.DrawRect(32 * i + 21, 7, 32 * i + 23, 9)
    device.DrawRect(32 * i + 7, 21, 32 * i + 9, 23)
    device.DrawRect(32 * i + 21, 21, 32 * i + 23, 23)
    elif self._face == 5:
    device.DrawRect(32 * i + 14, 14, 32 * i + 16, 16)
    device.DrawRect(32 * i + 7, 7, 32 * i + 9, 9)
    device.DrawRect(32 * i + 21, 7, 32 * i + 23, 9)
    device.DrawRect(32 * i + 7, 21, 32 * i + 9, 23)
    device.DrawRect(32 * i + 21, 21, 32 * i + 23, 23)
    elif self._face == 6:
    device.DrawRect(32 * i + 7, 7, 32 * i + 9, 9)
    device.DrawRect(32 * i + 21, 7, 32 * i + 23, 9)
    device.DrawRect(32 * i + 7, 14, 32 * i + 9, 16)
    device.DrawRect(32 * i + 21, 14, 32 * i + 23, 16)
    device.DrawRect(32 * i + 7, 21, 32 * i + 9, 23)
    device.DrawRect(32 * i + 21, 21, 32 * i + 23, 23)
    return

    def Roll(self):
    self._face = random.randint(1, 6)
    return

    DUMMY_PIN_A = 23
    SSD1306_PIN_DC = 24
    DUMMY_PIN_B = 25
    BUTTON_PIN = 25

    MAX_DICE = 4

    if __name__ == '__main__':
    device = SSD1306.SSD1306(SSD1306_PIN_DC, DUMMY_PIN_B, DUMMY_PIN_A, SSD1306.MAX_PAGE_HALF)
    gpio.setup(BUTTON_PIN, gpio.IN, pull_up_down = gpio.PUD_UP)
    dice = Dice(device)
    try:
    device.EnableDisplay(True)
    device.Clear()
    for i in xrange(0, MAX_DICE, 1):
    dice.Roll()
    dice.Draw(i, False)
    device.Flip()
    while True:
    input_state = gpio.input(BUTTON_PIN)
    if input_state == False:
    device.Clear()
    device.Flip()
    time.sleep(0.1)
    for i in xrange(0, MAX_DICE, 1):
    dice.Roll()
    dice.Draw(i, False)
    device.Flip()
    time.sleep(1.0)
    finally:
    device.EnableDisplay(False)
    device.Remove()

  5. TheRayTracer revised this gist Dec 30, 2015. 2 changed files with 0 additions and 0 deletions.
    Binary file added activity1.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
    Binary file added activity2.jpg
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  6. TheRayTracer revised this gist Dec 29, 2015. No changes.
  7. TheRayTracer created this gist Dec 29, 2015.
    464 changes: 464 additions & 0 deletions SSD1306.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,464 @@
    import spidev
    import sys
    import time
    import RPi.GPIO as gpio

    ascii = [
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ],
    [ 0x00, 0x00, 0x00, 0x00, 0x00 ], # sp
    [ 0x00, 0x00, 0x2f, 0x00, 0x00 ], # !
    [ 0x00, 0x07, 0x00, 0x07, 0x00 ], # "
    [ 0x14, 0x7f, 0x14, 0x7f, 0x14 ], # #
    [ 0x24, 0x2a, 0x7f, 0x2a, 0x12 ], # $
    [ 0x62, 0x64, 0x08, 0x13, 0x23 ], # %
    [ 0x36, 0x49, 0x55, 0x22, 0x50 ], # &
    [ 0x00, 0x05, 0x03, 0x00, 0x00 ], # '
    [ 0x00, 0x1c, 0x22, 0x41, 0x00 ], # (
    [ 0x00, 0x41, 0x22, 0x1c, 0x00 ], # )
    [ 0x14, 0x08, 0x3E, 0x08, 0x14 ], # *
    [ 0x08, 0x08, 0x3E, 0x08, 0x08 ], # +
    [ 0x00, 0x00, 0xA0, 0x60, 0x00 ], # ,
    [ 0x08, 0x08, 0x08, 0x08, 0x08 ], # -
    [ 0x00, 0x60, 0x60, 0x00, 0x00 ], # .
    [ 0x20, 0x10, 0x08, 0x04, 0x02 ], # /
    [ 0x3E, 0x51, 0x49, 0x45, 0x3E ], # 0
    [ 0x00, 0x42, 0x7F, 0x40, 0x00 ], # 1
    [ 0x42, 0x61, 0x51, 0x49, 0x46 ], # 2
    [ 0x21, 0x41, 0x45, 0x4B, 0x31 ], # 3
    [ 0x18, 0x14, 0x12, 0x7F, 0x10 ], # 4
    [ 0x27, 0x45, 0x45, 0x45, 0x39 ], # 5
    [ 0x3C, 0x4A, 0x49, 0x49, 0x30 ], # 6
    [ 0x01, 0x71, 0x09, 0x05, 0x03 ], # 7
    [ 0x36, 0x49, 0x49, 0x49, 0x36 ], # 8
    [ 0x06, 0x49, 0x49, 0x29, 0x1E ], # 9
    [ 0x00, 0x36, 0x36, 0x00, 0x00 ], # :
    [ 0x00, 0x56, 0x36, 0x00, 0x00 ], # ;
    [ 0x08, 0x14, 0x22, 0x41, 0x00 ], # <
    [ 0x14, 0x14, 0x14, 0x14, 0x14 ], # =
    [ 0x00, 0x41, 0x22, 0x14, 0x08 ], # >
    [ 0x02, 0x01, 0x51, 0x09, 0x06 ], # ?
    [ 0x32, 0x49, 0x59, 0x51, 0x3E ], # @
    [ 0x7C, 0x12, 0x11, 0x12, 0x7C ], # A
    [ 0x7F, 0x49, 0x49, 0x49, 0x36 ], # B
    [ 0x3E, 0x41, 0x41, 0x41, 0x22 ], # C
    [ 0x7F, 0x41, 0x41, 0x22, 0x1C ], # D
    [ 0x7F, 0x49, 0x49, 0x49, 0x41 ], # E
    [ 0x7F, 0x09, 0x09, 0x09, 0x01 ], # F
    [ 0x3E, 0x41, 0x49, 0x49, 0x7A ], # G
    [ 0x7F, 0x08, 0x08, 0x08, 0x7F ], # H
    [ 0x00, 0x41, 0x7F, 0x41, 0x00 ], # I
    [ 0x20, 0x40, 0x41, 0x3F, 0x01 ], # J
    [ 0x7F, 0x08, 0x14, 0x22, 0x41 ], # K
    [ 0x7F, 0x40, 0x40, 0x40, 0x40 ], # L
    [ 0x7F, 0x02, 0x0C, 0x02, 0x7F ], # M
    [ 0x7F, 0x04, 0x08, 0x10, 0x7F ], # N
    [ 0x3E, 0x41, 0x41, 0x41, 0x3E ], # O
    [ 0x7F, 0x09, 0x09, 0x09, 0x06 ], # P
    [ 0x3E, 0x41, 0x51, 0x21, 0x5E ], # Q
    [ 0x7F, 0x09, 0x19, 0x29, 0x46 ], # R
    [ 0x46, 0x49, 0x49, 0x49, 0x31 ], # S
    [ 0x01, 0x01, 0x7F, 0x01, 0x01 ], # T
    [ 0x3F, 0x40, 0x40, 0x40, 0x3F ], # U
    [ 0x1F, 0x20, 0x40, 0x20, 0x1F ], # V
    [ 0x3F, 0x40, 0x38, 0x40, 0x3F ], # W
    [ 0x63, 0x14, 0x08, 0x14, 0x63 ], # X
    [ 0x07, 0x08, 0x70, 0x08, 0x07 ], # Y
    [ 0x61, 0x51, 0x49, 0x45, 0x43 ], # Z
    [ 0x00, 0x7F, 0x41, 0x41, 0x00 ], # [
    [ 0x02, 0x04, 0x08, 0x10, 0x20 ], # \
    [ 0x00, 0x41, 0x41, 0x7F, 0x00 ], # ]
    [ 0x04, 0x02, 0x01, 0x02, 0x04 ], # ^
    [ 0x40, 0x40, 0x40, 0x40, 0x40 ], # _
    [ 0x00, 0x03, 0x02, 0x04, 0x00 ], # '
    [ 0x20, 0x54, 0x54, 0x54, 0x78 ], # a
    [ 0x7F, 0x48, 0x44, 0x44, 0x38 ], # b
    [ 0x38, 0x44, 0x44, 0x44, 0x20 ], # c
    [ 0x38, 0x44, 0x44, 0x48, 0x7F ], # d
    [ 0x38, 0x54, 0x54, 0x54, 0x18 ], # e
    [ 0x08, 0x7E, 0x09, 0x01, 0x02 ], # f
    [ 0x18, 0xA4, 0xA4, 0xA4, 0x7C ], # g
    [ 0x7F, 0x08, 0x04, 0x04, 0x78 ], # h
    [ 0x00, 0x44, 0x7D, 0x40, 0x00 ], # i
    [ 0x40, 0x80, 0x84, 0x7D, 0x00 ], # j
    [ 0x7F, 0x10, 0x28, 0x44, 0x00 ], # k
    [ 0x00, 0x41, 0x7F, 0x40, 0x00 ], # l
    [ 0x7C, 0x04, 0x18, 0x04, 0x78 ], # m
    [ 0x7C, 0x08, 0x04, 0x04, 0x78 ], # n
    [ 0x38, 0x44, 0x44, 0x44, 0x38 ], # o
    [ 0xFC, 0x24, 0x24, 0x24, 0x18 ], # p
    [ 0x18, 0x24, 0x24, 0x18, 0xFC ], # q
    [ 0x7C, 0x08, 0x04, 0x04, 0x08 ], # r
    [ 0x48, 0x54, 0x54, 0x54, 0x20 ], # s
    [ 0x04, 0x3F, 0x44, 0x40, 0x20 ], # t
    [ 0x3C, 0x40, 0x40, 0x20, 0x7C ], # u
    [ 0x1C, 0x20, 0x40, 0x20, 0x1C ], # v
    [ 0x3C, 0x40, 0x30, 0x40, 0x3C ], # w
    [ 0x44, 0x28, 0x10, 0x28, 0x44 ], # x
    [ 0x1C, 0xA0, 0xA0, 0xA0, 0x7C ], # y
    [ 0x44, 0x64, 0x54, 0x4C, 0x44 ], # z
    [ 0x00, 0x08, 0x36, 0x41, 0x00 ], # {
    [ 0x00, 0x00, 0x77, 0x00, 0x00 ], # |
    [ 0x00, 0x41, 0x36, 0x08, 0x00 ], # }
    [ 0x02, 0x01, 0x02, 0x04, 0x02 ], # ~
    [ 0x55, 0x00, 0x55, 0x00, 0x55 ]
    ]

    MAX_WIDTH = 0x80
    MAX_HEIGHT = 0x40
    MAX_PAGE = 0x08

    H_SCROLL_ENABLE_TOP = 0x01
    H_SCROLL_ENABLE_BOTTOM = 0x02
    H_SCROLL_ENABLE_ALL = 0x04

    MEMORY_MODE = 0x20
    SET_COLUMN = 0x21
    SET_PAGE = 0x22
    SET_START_LINE = 0x40

    DEACTIVATE_SCROLLING = 0x2E
    ACTIVATE_SCROLLING = 0x2F

    SET_CONTRAST = 0x81
    CHARGE_PUMP = 0x8D

    SET_SEGMENT_REMAP = 0xA1
    ENTIRE_DISPLAY_RESUME = 0xA4
    ENTIRE_DISPLAY_ON = 0xA5
    NORMAL_DISPLAY = 0xA6
    INVERSE_DISPLAY = 0xA7
    SET_MULTIPLEX = 0xA8
    DISPLAY_OFF = 0xAE
    DISPLAY_ON = 0xAF

    SCAN_DIRECTION_DOWN = 0xC0
    SCAN_DIRECTION_UP = 0xC8

    SET_DISPLAY_OFFSET = 0xD3
    SET_DISPLAY_CLOCK_DIV = 0xD5

    NO_OPERATION = 0xE3

    class SSD1306:
    COMMAND = gpio.LOW
    DATA = gpio.HIGH

    def __init__(self, dc, rst, cs):
    self.rst = rst
    self.dc = dc
    self.cs = cs
    self._buffer = [0] * (MAX_WIDTH * MAX_PAGE)
    # Setup GPIO.
    gpio.setmode(gpio.BCM)
    gpio.setup(self.dc, gpio.OUT)
    gpio.output(self.dc, gpio.LOW)
    gpio.setup(self.rst, gpio.OUT)
    gpio.output(self.rst, gpio.HIGH)
    gpio.setup(self.cs, gpio.OUT)
    gpio.output(self.cs, gpio.HIGH)
    self.__OpenSPI() # Setup SPI.
    self.__Setup() # Setup device screen.
    self.Clear() # Blank the screen.
    return

    def __OpenSPI(self):
    self.spi = spidev.SpiDev()
    self.spi.open(0, 0)
    self.spi.mode = 3
    self.spi.max_speed_hz = 9000000
    self.spi.cshigh = False
    return

    def __WriteCommand(self, data):
    gpio.output(self.dc, self.COMMAND)
    if isinstance(data, list) or isinstance(data, tuple):
    self.spi.xfer(data)
    return

    def __WriteData(self, data):
    gpio.output(self.dc, self.DATA)
    if isinstance(data, list) or isinstance(data, tuple):
    self.spi.xfer(data)
    return

    def __Setup(self):
    self.spi.cshigh = True
    self.spi.xfer([0])
    gpio.output(self.cs, gpio.LOW)
    time.sleep(0.1)
    gpio.output(self.rst, gpio.LOW)
    time.sleep(0.5)
    gpio.output(self.rst, gpio.HIGH)
    time.sleep(0.5)
    self.spi.cshigh = False
    self.spi.xfer([0])
    self.__WriteCommand([DISPLAY_OFF])
    self.__WriteCommand([SET_MULTIPLEX, 0x3F])
    self.__WriteCommand([SET_DISPLAY_OFFSET, 0x00])
    self.__WriteCommand([SET_START_LINE])
    self.__WriteCommand([SET_SEGMENT_REMAP])
    self.__WriteCommand([SET_CONTRAST, 0x7F])
    self.__WriteCommand([NORMAL_DISPLAY])
    self.__WriteCommand([SET_DISPLAY_CLOCK_DIV, 0x80])
    self.__WriteCommand([CHARGE_PUMP, 0x14]) # Enable Charge Pump.
    self.__WriteCommand([SCAN_DIRECTION_UP])
    self.__WriteCommand([MEMORY_MODE, 0x00]) # Setup the address mode.
    self.__WriteCommand([SET_COLUMN, 0x00, 0x7F])
    self.__WriteCommand([SET_PAGE, 0x00, 0x07])
    self.__WriteCommand([DISPLAY_ON])
    return

    def Remove(self):
    gpio.cleanup()
    self.spi.close()
    return

    def DrawPixel(self, x, y):
    page_offset = (y / MAX_PAGE) * MAX_WIDTH
    t = 0x01 << (y % MAX_PAGE)
    self._buffer[page_offset + x] |= t
    return

    def DrawHorizontalLine(self, y):
    page_offset = (y / MAX_PAGE) * MAX_WIDTH
    t = 0x01 << (y % MAX_PAGE)
    for i in xrange(0, MAX_WIDTH, 1):
    self._buffer[page_offset + i] |= t
    return

    def DrawVerticalLine(self, x):
    for i in xrange(0, MAX_PAGE, 1):
    self._buffer[(i * MAX_WIDTH) + x] = 0xFF
    return

    # Bresenham's line algorithm.
    def DrawLine(self, x0, y0, x1, y1):
    dx = x1 - x0
    if dx < 0:
    dx = x0 - x1
    sx = -1
    if x0 < x1:
    sx = 1
    dy = y1 - y0
    if dy < 0:
    dy = y0 - y1
    sy = -1
    if y0 < y1:
    sy = 1
    err = -dy / 2
    if dy < dx:
    err = dx / 2
    self.DrawPixel(x0, y0)
    while x0 != x1 or y0 != y1:
    e2 = err
    if e2 > -dx:
    err = err - dy
    x0 = x0 + sx
    if e2 < dy:
    err = err + dx
    y0 = y0 + sy
    self.DrawPixel(x0, y0)
    return

    def DrawTriangle(self, x0, y0, x1, y1, x2, y2):
    self.DrawLine(x0, y0, x1, y1)
    self.DrawLine(x1, y1, x2, y2)
    self.DrawLine(x0, y0, x2, y2)
    return

    def DrawRect(self, x0, y0, x1, y1):
    self.DrawLine(x0, y0, x1, y0)
    self.DrawLine(x1, y0, x1, y1)
    self.DrawLine(x1, y1, x0, y1)
    self.DrawLine(x0, y1, x0, y0)
    return

    def DrawCircle(self, x0, y0, r0):
    x = r0
    y = 0
    decision_over2 = 1 - x # Decision criterion divided by 2 evaluated at x = r, y = 0.
    while y <= x:
    self.DrawPixel( x + x0, y + y0) # Octant 1.
    self.DrawPixel( y + x0, x + y0) # Octant 2.
    self.DrawPixel(-x + x0, y + y0) # Octant 4.
    self.DrawPixel(-y + x0, x + y0) # Octant 3.
    self.DrawPixel(-x + x0, -y + y0) # Octant 5.
    self.DrawPixel(-y + x0, -x + y0) # Octant 6.
    self.DrawPixel( x + x0, -y + y0) # Octant 8.
    self.DrawPixel( y + x0, -x + y0) # Octant 7.
    y = y + 1
    if decision_over2 <= 0:
    decision_over2 = decision_over2 + 2 * y + 1 # Change in decision criterion for y -> y + 1.
    else:
    x = x - 1
    decision_over2 = decision_over2 + 2 * (y - x) + 1 # Change for y -> y + 1, x -> x - 1.
    return

    def DrawChar(self, x, y, ch):
    for i in xrange(0, 5, 1):
    line = ascii[ord(ch) & 0x7F][i]
    for j in xrange(0, 8, 1):
    if line & 0x1:
    self.DrawPixel(x + i, y + j)
    line >>= 1
    return

    def DrawString(self, x, y, str):
    for i in str:
    if x > 0x5F:
    break
    self.DrawChar(x, y, i)
    x = x + 6
    return

    def Clear(self):
    for i in xrange(0, MAX_WIDTH * MAX_PAGE, 1):
    self._buffer[i] = 0x00
    return

    def Flip(self):
    self.__WriteData(self._buffer)
    return

    def TestEntireDisplay(self, enable):
    if enable:
    self.__WriteCommand([ENTIRE_DISPLAY_ON])
    else:
    self.__WriteCommand([ENTIRE_DISPLAY_RESUME])
    return

    def EnableDisplay(self, enable):
    if enable:
    self.__WriteCommand([DISPLAY_ON])
    else:
    self.__WriteCommand([DISPLAY_OFF])
    return

    def SetScrollMode(self, horizontal):
    if horizontal == H_SCROLL_ENABLE_TOP:
    self.__WriteCommand([0x26, 0x00, 0x00, 0x00, 0x01, 0x00, 0xFF])
    elif horizontal == H_SCROLL_ENABLE_BOTTOM:
    self.__WriteCommand([0x26, 0x00, 0x02, 0x00, 0x07, 0x00, 0xFF])
    elif horizontal == H_SCROLL_ENABLE_ALL:
    self.__WriteCommand([0x26, 0x00, 0x00, 0x00, 0x07, 0x00, 0xFF])
    return

    def EnableScrollMode(self, enable):
    if enable:
    self.__WriteCommand([ACTIVATE_SCROLLING])
    else:
    self.__WriteCommand([DEACTIVATE_SCROLLING])
    return

    def TestModeTest(self):
    device.TestEntireDisplay(True)
    time.sleep(4)
    device.Clear()
    device.Flip()
    device.TestEntireDisplay(False)
    time.sleep(2)
    return

    def LineTest(self):
    self.Clear()
    for y in xrange(0, 16, 1):
    self.DrawHorizontalLine(y * 4)
    self.Flip()
    time.sleep(4)
    self.Clear()
    for x in xrange(0, 32, 1):
    self.DrawVerticalLine(x * 4)
    self.Flip()
    time.sleep(4)
    return

    def ShapeTest(self):
    self.Clear()
    self.DrawCircle(20, 32, 16)
    self.DrawTriangle(47, 48, 79, 48, 63, 16)
    self.DrawRect(90, 16, 122, 48)
    self.Flip()
    time.sleep(8)
    return

    def CharTest(self):
    self.Clear()
    i = 48
    for k in xrange(0, 16, 1):
    self.DrawChar(k * 8, 4, chr(i))
    i = i + 1
    i = 32
    for j in xrange(0, 6, 1):
    for k in xrange(0, 16, 1):
    self.DrawChar(k * 8, j * 8 + 16, chr(i))
    i = i + 1
    self.Flip()
    time.sleep(8)
    return

    def ScrollTest(self):
    self.Clear()
    self.DrawString(4, 2, "Scrolling Test")
    self.DrawString(4, 28, "Scrolling Test")
    self.Flip()
    self.SetScrollMode(H_SCROLL_ENABLE_TOP)
    self.EnableScrollMode(True)
    time.sleep(10)
    self.EnableScrollMode(False)
    self.SetScrollMode(H_SCROLL_ENABLE_BOTTOM)
    self.EnableScrollMode(True)
    time.sleep(10)
    self.EnableScrollMode(False)
    self.SetScrollMode(H_SCROLL_ENABLE_ALL)
    self.EnableScrollMode(True)
    time.sleep(10)
    self.EnableScrollMode(False)
    return

    SSD1306_PIN_CS = 23
    SSD1306_PIN_DC = 24
    SSD1306_PIN_RST = 25

    if __name__ == '__main__':
    device = SSD1306(SSD1306_PIN_DC, SSD1306_PIN_RST, SSD1306_PIN_CS)
    try:
    device.EnableDisplay(True)
    device.TestModeTest()
    device.LineTest()
    device.ShapeTest()
    device.ScrollTest()
    device.CharTest()
    device.EnableDisplay(False)
    finally:
    device.Remove()