Last active
January 28, 2020 12:39
-
-
Save slzatz/7c009736a50bf12c6df7 to your computer and use it in GitHub Desktop.
I2C driver for micropython esp8266 ssd1306 OLED (ssd1306a.py is paired down and works with the font.py file to allowing generating text)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Originally from https://github.com/guyc/py-gaugette/blob/master/gaugette/font5x8.py | |
# Contains ASCII 32 (space) through ASCII 90 (Z) | |
cols = 5 | |
rows = 8 | |
bytes = [ | |
0x00, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x5F, 0x00, 0x00, | |
0x00, 0x07, 0x00, 0x07, 0x00, | |
0x14, 0x7F, 0x14, 0x7F, 0x14, | |
0x24, 0x2A, 0x7F, 0x2A, 0x12, | |
0x23, 0x13, 0x08, 0x64, 0x62, | |
0x36, 0x49, 0x56, 0x20, 0x50, | |
0x00, 0x08, 0x07, 0x03, 0x00, | |
0x00, 0x1C, 0x22, 0x41, 0x00, | |
0x00, 0x41, 0x22, 0x1C, 0x00, | |
0x2A, 0x1C, 0x7F, 0x1C, 0x2A, | |
0x08, 0x08, 0x3E, 0x08, 0x08, | |
0x00, 0x80, 0x70, 0x30, 0x00, | |
0x08, 0x08, 0x08, 0x08, 0x08, | |
0x00, 0x00, 0x60, 0x60, 0x00, | |
0x20, 0x10, 0x08, 0x04, 0x02, | |
0x3E, 0x51, 0x49, 0x45, 0x3E, | |
0x00, 0x42, 0x7F, 0x40, 0x00, | |
0x72, 0x49, 0x49, 0x49, 0x46, | |
0x21, 0x41, 0x49, 0x4D, 0x33, | |
0x18, 0x14, 0x12, 0x7F, 0x10, | |
0x27, 0x45, 0x45, 0x45, 0x39, | |
0x3C, 0x4A, 0x49, 0x49, 0x31, | |
0x41, 0x21, 0x11, 0x09, 0x07, | |
0x36, 0x49, 0x49, 0x49, 0x36, | |
0x46, 0x49, 0x49, 0x29, 0x1E, | |
0x00, 0x00, 0x14, 0x00, 0x00, | |
0x00, 0x40, 0x34, 0x00, 0x00, | |
0x00, 0x08, 0x14, 0x22, 0x41, | |
0x14, 0x14, 0x14, 0x14, 0x14, | |
0x00, 0x41, 0x22, 0x14, 0x08, | |
0x02, 0x01, 0x59, 0x09, 0x06, | |
0x3E, 0x41, 0x5D, 0x59, 0x4E, | |
0x7C, 0x12, 0x11, 0x12, 0x7C, | |
0x7F, 0x49, 0x49, 0x49, 0x36, | |
0x3E, 0x41, 0x41, 0x41, 0x22, | |
0x7F, 0x41, 0x41, 0x41, 0x3E, | |
0x7F, 0x49, 0x49, 0x49, 0x41, | |
0x7F, 0x09, 0x09, 0x09, 0x01, | |
0x3E, 0x41, 0x41, 0x51, 0x73, | |
0x7F, 0x08, 0x08, 0x08, 0x7F, | |
0x00, 0x41, 0x7F, 0x41, 0x00, | |
0x20, 0x40, 0x41, 0x3F, 0x01, | |
0x7F, 0x08, 0x14, 0x22, 0x41, | |
0x7F, 0x40, 0x40, 0x40, 0x40, | |
0x7F, 0x02, 0x1C, 0x02, 0x7F, | |
0x7F, 0x04, 0x08, 0x10, 0x7F, | |
0x3E, 0x41, 0x41, 0x41, 0x3E, | |
0x7F, 0x09, 0x09, 0x09, 0x06, | |
0x3E, 0x41, 0x51, 0x21, 0x5E, | |
0x7F, 0x09, 0x19, 0x29, 0x46, | |
0x26, 0x49, 0x49, 0x49, 0x32, | |
0x03, 0x01, 0x7F, 0x01, 0x03, | |
0x3F, 0x40, 0x40, 0x40, 0x3F, | |
0x1F, 0x20, 0x40, 0x20, 0x1F, | |
0x3F, 0x40, 0x38, 0x40, 0x3F, | |
0x63, 0x14, 0x08, 0x14, 0x63, | |
0x03, 0x04, 0x78, 0x04, 0x03, | |
0x61, 0x59, 0x49, 0x4D, 0x43] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# The MIT License (MIT) | |
# | |
# Based on Kenneth Henderick's Micropython drivers: https://github.com/khenderick/micropython-drivers | |
# Copyright (c) 2014 Kenneth Henderick | |
# | |
# Permission is hereby granted, free of charge, to any person obtaining a copy | |
# of this software and associated documentation files (the "Software"), to deal | |
# in the Software without restriction, including without limitation the rights | |
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
# copies of the Software, and to permit persons to whom the Software is | |
# furnished to do so, subject to the following conditions: | |
# | |
# The above copyright notice and this permission notice shall be included in | |
# all copies or substantial portions of the Software. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
# THE SOFTWARE. | |
from time import sleep_ms | |
from machine import Pin, I2C | |
# Constants | |
DISPLAYOFF = 0xAE | |
SETCONTRAST = 0x81 | |
DISPLAYALLON_RESUME = 0xA4 | |
DISPLAYALLON = 0xA5 | |
NORMALDISPLAY = 0xA6 | |
INVERTDISPLAY = 0xA7 | |
DISPLAYON = 0xAF | |
SETDISPLAYOFFSET = 0xD3 | |
SETCOMPINS = 0xDA | |
SETVCOMDETECT = 0xDB | |
SETDISPLAYCLOCKDIV = 0xD5 | |
SETPRECHARGE = 0xD9 | |
SETMULTIPLEX = 0xA8 | |
SETLOWCOLUMN = 0x00 | |
SETHIGHCOLUMN = 0x10 | |
SETSTARTLINE = 0x40 | |
MEMORYMODE = 0x20 | |
COLUMNADDR = 0x21 | |
PAGEADDR = 0x22 | |
COMSCANINC = 0xC0 | |
COMSCANDEC = 0xC8 | |
SEGREMAP = 0xA0 | |
CHARGEPUMP = 0x8D | |
EXTERNALVCC = 0x10 | |
SWITCHCAPVCC = 0x20 | |
SETPAGEADDR = 0xB0 | |
SETCOLADDR_LOW = 0x00 | |
SETCOLADDR_HIGH = 0x10 | |
ACTIVATE_SCROLL = 0x2F | |
DEACTIVATE_SCROLL = 0x2E | |
SET_VERTICAL_SCROLL_AREA = 0xA3 | |
RIGHT_HORIZONTAL_SCROLL = 0x26 | |
LEFT_HORIZONTAL_SCROLL = 0x27 | |
VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29 | |
VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A | |
# I2C devices are accessed through a Device ID. This is a 7-bit | |
# value but is sometimes expressed left-shifted by 1 as an 8-bit value. | |
# A pin on SSD1306 allows it to respond to ID 0x3C or 0x3D. The board | |
# I bought from ebay used a 0-ohm resistor to select between "0x78" | |
# (0x3c << 1) or "0x7a" (0x3d << 1). The default was set to "0x78" | |
DEVID = 0x3c | |
# I2C communication here is either <DEVID> <CTL_CMD> <command byte> | |
# or <DEVID> <CTL_DAT> <display buffer bytes> <> <> <> <>... | |
# These two values encode the Co (Continuation) bit as b7 and the | |
# D/C# (Data/Command Selection) bit as b6. | |
CTL_CMD = 0x80 | |
CTL_DAT = 0x40 | |
class SSD1306(object): | |
def __init__(self, height=32, external_vcc=True, i2c_devid=DEVID): | |
self.external_vcc = external_vcc | |
self.height = 32 if height == 32 else 64 | |
self.pages = int(self.height / 8) | |
self.columns = 128 | |
self.devid = i2c_devid | |
self.offset = 1 | |
self.cbuffer = bytearray(2) | |
self.cbuffer[0] = CTL_CMD | |
self.i2c = I2C(scl=Pin(5), sda=Pin(4), freq=400000) | |
def clear(self): | |
self.buffer = bytearray(self.offset + self.pages * self.columns) | |
if self.offset == 1: | |
self.buffer[0] = CTL_DAT | |
def write_command(self, command_byte): | |
self.cbuffer[1] = command_byte | |
self.i2c.writeto(self.devid, self.cbuffer) | |
def invert_display(self, invert): | |
self.write_command(INVERTDISPLAY if invert else NORMALDISPLAY) | |
def display(self): | |
self.write_command(COLUMNADDR) | |
self.write_command(0) | |
self.write_command(self.columns - 1) | |
self.write_command(PAGEADDR) | |
self.write_command(0) | |
self.write_command(self.pages - 1) | |
self.i2c.writeto(self.devid, self.buffer) | |
def set_pixel(self, x, y, state): | |
index = x + (int(y / 8) * self.columns) | |
if state: | |
self.buffer[self.offset + index] |= (1 << (y & 7)) | |
else: | |
self.buffer[self.offset + index] &= ~(1 << (y & 7)) | |
def init_display(self): | |
chargepump = 0x10 if self.external_vcc else 0x14 | |
precharge = 0x22 if self.external_vcc else 0xf1 | |
multiplex = 0x1f if self.height == 32 else 0x3f | |
compins = 0x02 if self.height == 32 else 0x12 | |
contrast = 0xff # 0x8f if self.height == 32 else (0x9f if self.external_vcc else 0x9f) | |
data = [DISPLAYOFF, | |
SETDISPLAYCLOCKDIV, 0x80, | |
SETMULTIPLEX, multiplex, | |
SETDISPLAYOFFSET, 0x00, | |
SETSTARTLINE | 0x00, | |
CHARGEPUMP, chargepump, | |
MEMORYMODE, 0x00, | |
SEGREMAP | 0x10, | |
COMSCANDEC, | |
SETCOMPINS, compins, | |
SETCONTRAST, contrast, | |
SETPRECHARGE, precharge, | |
SETVCOMDETECT, 0x40, | |
DISPLAYALLON_RESUME, | |
NORMALDISPLAY, | |
DISPLAYON] | |
for item in data: | |
self.write_command(item) | |
self.clear() | |
self.display() | |
def poweron(self): | |
if self.offset == 1: | |
sleep_ms(10) | |
else: | |
self.res.high() | |
sleep_ms(1) | |
self.res.low() | |
sleep_ms(10) | |
self.res.high() | |
sleep_ms(10) | |
def poweroff(self): | |
self.write_command(DISPLAYOFF) | |
def contrast(self, contrast): | |
self.write_command(SETCONTRAST) | |
self.write_command(contrast) | |
def set_start_end_cols(self, start_col=0, end_col=None): | |
if end_col is None: | |
end_col = self.columns - 1 | |
if start_col < 0 or start_col > self.columns - 1: | |
raise ValueError('Start column must be between 0 and %d.' % (self.columns - 1,)) | |
if end_col < start_col or end_col > self.columns -1: | |
raise ValueError('End column must be between the start column (%d) and %d.' % (start_col, self.columns - 1)) | |
self.write_command(COLUMNADDR) | |
self.write_command(start_col) # Start column | |
self.write_command(end_col) # End column | |
def set_start_end_pages(self, start_page=0, end_page=None): | |
if end_page is None: | |
end_page = self.pages - 1 | |
if start_page < 0 or start_page > self.pages - 1: | |
raise ValueError('Start page must be between 0 and %d.' % (self.pages - 1,)) | |
if end_page < start_page or end_page > self.pages - 1: | |
raise ValueError('End page must be between the start page (%d) and %d.' % (start_page, self.pages - 1)) | |
self.write_command(PAGEADDR) | |
self.write_command(start_page) # Page start address. (0 = reset) | |
self.write_command(end_page) # Page end address. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# The MIT License (MIT) | |
# | |
# Based on Kenneth Henderick's Micropython drivers: https://github.com/khenderick/micropython-drivers | |
# Copyright (c) 2014 Kenneth Henderick | |
# | |
# Permission is hereby granted, free of charge, to any person obtaining a copy | |
# of this software and associated documentation files (the "Software"), to deal | |
# in the Software without restriction, including without limitation the rights | |
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
# copies of the Software, and to permit persons to whom the Software is | |
# furnished to do so, subject to the following conditions: | |
# | |
# The above copyright notice and this permission notice shall be included in | |
# all copies or substantial portions of the Software. | |
# | |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
# THE SOFTWARE. | |
from machine import Pin, I2C | |
import font | |
# Constants | |
DISPLAYOFF = 0xAE | |
SETCONTRAST = 0x81 | |
DISPLAYALLON_RESUME = 0xA4 | |
DISPLAYALLON = 0xA5 | |
NORMALDISPLAY = 0xA6 | |
INVERTDISPLAY = 0xA7 | |
DISPLAYON = 0xAF | |
SETDISPLAYOFFSET = 0xD3 | |
SETCOMPINS = 0xDA | |
SETVCOMDETECT = 0xDB | |
SETDISPLAYCLOCKDIV = 0xD5 | |
SETPRECHARGE = 0xD9 | |
SETMULTIPLEX = 0xA8 | |
SETLOWCOLUMN = 0x00 | |
SETHIGHCOLUMN = 0x10 | |
SETSTARTLINE = 0x40 | |
MEMORYMODE = 0x20 | |
COLUMNADDR = 0x21 | |
PAGEADDR = 0x22 | |
COMSCANINC = 0xC0 | |
COMSCANDEC = 0xC8 | |
SEGREMAP = 0xA0 | |
CHARGEPUMP = 0x8D | |
EXTERNALVCC = 0x10 | |
SWITCHCAPVCC = 0x20 | |
SETPAGEADDR = 0xB0 | |
SETCOLADDR_LOW = 0x00 | |
SETCOLADDR_HIGH = 0x10 | |
ACTIVATE_SCROLL = 0x2F | |
DEACTIVATE_SCROLL = 0x2E | |
SET_VERTICAL_SCROLL_AREA = 0xA3 | |
RIGHT_HORIZONTAL_SCROLL = 0x26 | |
LEFT_HORIZONTAL_SCROLL = 0x27 | |
VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29 | |
VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A | |
class SSD1306(object): | |
def __init__(self): | |
# assumes height=32, pages=4, columns=128, internal vcc | |
self.cbuffer = bytearray(2) | |
self.cbuffer[0] = 0x80 | |
self.i2c = I2C(scl=Pin(5), sda=Pin(4), freq=400000) | |
def clear(self): | |
self.buffer = bytearray(513) | |
self.buffer[0] = 0x40 | |
def write_command(self, command_byte): | |
self.cbuffer[1] = command_byte | |
self.i2c.writeto(0x3c, self.cbuffer) | |
def invert_display(self, invert): | |
self.write_command(INVERTDISPLAY if invert else NORMALDISPLAY) | |
def display(self): | |
self.write_command(COLUMNADDR) | |
self.write_command(0) | |
self.write_command(127) #(self.columns - 1) | |
self.write_command(PAGEADDR) | |
self.write_command(0) | |
self.write_command(3) #(self.pages - 1) | |
self.i2c.writeto(0x3c, self.buffer) | |
def set_pixel(self, x, y, state): | |
index = x + (int(y / 8) * 128) | |
if state: | |
self.buffer[1 + index] |= (1 << (y & 7)) | |
else: | |
self.buffer[1 + index] &= ~(1 << (y & 7)) | |
def init_display(self): | |
chargepump = 0x14 | |
precharge = 0xf1 | |
multiplex = 0x1f | |
compins = 0x02 | |
contrast = 0xff # 0x8f if self.height == 32 else (0x9f if self.external_vcc else 0x9f) | |
data = [DISPLAYOFF, | |
SETDISPLAYCLOCKDIV, 0x80, | |
SETMULTIPLEX, multiplex, | |
SETDISPLAYOFFSET, 0x00, | |
SETSTARTLINE | 0x00, | |
CHARGEPUMP, chargepump, | |
MEMORYMODE, 0x00, | |
SEGREMAP | 0x10, | |
COMSCANDEC, | |
SETCOMPINS, compins, | |
SETCONTRAST, contrast, | |
SETPRECHARGE, precharge, | |
SETVCOMDETECT, 0x40, | |
DISPLAYALLON_RESUME, | |
NORMALDISPLAY, | |
DISPLAYON] | |
for item in data: | |
self.write_command(item) | |
self.clear() | |
self.display() | |
def poweron(self): | |
pass | |
def poweroff(self): | |
self.write_command(DISPLAYOFF) | |
def contrast(self, contrast): | |
self.write_command(SETCONTRAST) | |
self.write_command(contrast) | |
def draw_text(self, x, y, string, size=1, space=1): | |
def pixel_x(char_number, char_column, point_row): | |
char_offset = x + char_number * size * font.cols + space * char_number | |
pixel_offset = char_offset + char_column * size + point_row | |
return 128 - pixel_offset | |
def pixel_y(char_row, point_column): | |
char_offset = y + char_row * size | |
return char_offset + point_column | |
def pixel_mask(char, char_column, char_row): | |
#char_index_offset = ord(char) * font.cols #original | |
char_index_offset = (ord(char)-32) * font.cols | |
return font.bytes[char_index_offset + char_column] >> char_row & 0x1 | |
pixels = ( | |
(pixel_x(char_number, char_column, point_row), | |
pixel_y(char_row, point_column), | |
pixel_mask(char, char_column, char_row)) | |
for char_number, char in enumerate(string) | |
for char_column in range(font.cols) | |
for char_row in range(font.rows) | |
for point_column in range(size) | |
for point_row in range(1, size + 1)) | |
for pixel in pixels: | |
self.set_pixel(*pixel) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment