#!/usr/bin/env python3

from PIL import Image
from serial import Serial

port = Serial('/dev/buspirate', 115200, timeout=0.1)
image = Image.open('images/i2c_binary_skull.png')

def write(x):
    size = 1 + len(x)
    assert size <= 16

    # Start.
    port.write(b'\x02')
    if port.read(1) != b'\x01':
        raise RuntimeError()
    # Bulk write.
    port.write(bytes([0x10 + len(x)]))
    if port.read(1) != b'\x01':
        raise RuntimeError()
    # Data.
    port.write(b'\x78' + x)
    if port.read(size) != bytes(size):
        raise RuntimeError()
    # Stop.
    port.write(b'\x03')
    if port.read(1) != b'\x01':
        raise RuntimeError()

try:
    # "you must now enter 0x00 at least 20 times to enter raw bitbang mode"
    count = 0
    done = False
    while count < 30 and not done:
        count += 1
        port.write(b'\x00')
        if port.read(5)[0:4] == b'BBIO':
            done = True
    if not done:
        raise RuntimeError()

    # Switch to I2C mode.
    port.write(b'\x02')
    if port.read(4) != b'I2C1':
        raise RuntimeError()

    # Turn on power and nothing else.
    port.write(b'\x48')
    if port.read(1) != b'\x01':
        raise RuntimeError()

    # Turn on the display.
    write(b'\x80\x8d\x80\x14\x80\xaf')

    # Draw the image.
    for page in range(8):
        # Move to the start of a page.
        write(b'\x80' + bytes([0xb0 + 7 - page]) + b'\x80\x00\x80\x10')
        for block in reversed(range(16)):
            x = b'\x40'
            for column in reversed(range(8)):
                values = [1-image.getpixel((block*8+column, page*8+n)) for n in range(8)]
                x += bytes([int(''.join(str(x) for x in values), 2)])
            write(x)
finally:
    port.close()
