Skip to content

Instantly share code, notes, and snippets.

@HeroBrine1st
Last active October 8, 2020 16:14
Show Gist options
  • Save HeroBrine1st/d9e96ccacde4e4fda9dcedf7b1f19d31 to your computer and use it in GitHub Desktop.
Save HeroBrine1st/d9e96ccacde4e4fda9dcedf7b1f19d31 to your computer and use it in GitHub Desktop.
SSD1306 OLED Library for micropython (maybe SSD130x)
from framebuf import FrameBuffer, MONO_VLSB
from machine import I2C
# Я не ебу что за хуйня с юникодом в этом питоне, пришлось словарь сделать.
# Юзаем bytes, потому что занимает еще меньше
font = { # Шрифт спизжен у гайвера, но он реально классный (лучше, чем в framebuf)
# И еще чутка изменили некоторые элементы
" ": b"\x00\x00\x00\x00\x00", # 0x20 32
"!": b"\x00\x00\x6f\x00\x00", # ! 0x21 33
"\"": b"\x00\x07\x00\x07\x00", # " 0x22 34
"#": b"\x14\x7f\x14\x7f\x14", # # 0x23 35
"$": b"\x00\x07\x04\x1e\x00", # $ 0x24 36
"%": b"\x23\x13\x08\x64\x62", # % 0x25 37
"&": b"\x36\x49\x56\x20\x50", # & 0x26 38
"'": b"\x00\x00\x07\x00\x00", # ' 0x27 39
"(": b"\x00\x1c\x22\x41\x00", # ( 0x28 40
")": b"\x00\x41\x22\x1c\x00", # ) 0x29 41
"*": b"\x14\x08\x3e\x08\x14", # * 0x2a 42
"+": b"\x08\x08\x3e\x08\x08", # + 0x2b 43
",": b"\x00\x50\x30\x00\x00", # , 0x2c 44
"-": b"\x08\x08\x08\x08\x08", # - 0x2d 45
".": b"\x00\x60\x60\x00\x00", # . 0x2e 46
"/": b"\x00\x60\x18\x06\x00", # / 0x2f 47 OLD (0x20, 0x10, 0x08, 0x04, 0x02)
"0": b"\x3e\x51\x49\x45\x3e", # 0 0x30 48
"1": b"\x00\x42\x7f\x40\x00", # 1 0x31 49
"2": b"\x42\x61\x51\x49\x46", # 2 0x32 50
"3": b"\x21\x41\x45\x4b\x31", # 3 0x33 51
"4": b"\x18\x14\x12\x7f\x10", # 4 0x34 52
"5": b"\x27\x45\x45\x45\x39", # 5 0x35 53
"6": b"\x3c\x4a\x49\x49\x30", # 6 0x36 54
"7": b"\x01\x71\x09\x05\x03", # 7 0x37 55
"8": b"\x36\x49\x49\x49\x36", # 8 0x38 56
"9": b"\x06\x49\x49\x29\x1e", # 9 0x39 57
":": b"\x00\x6c\x6c\x00\x00", # : 0x3a 58 OLD (0x00, 0x36, 0x36, 0x00, 0x00)
";": b"\x00\x56\x36\x00\x00", # ; 0x3b 59
"<": b"\x08\x14\x22\x41\x00", # < 0x3c 60
"=": b"\x14\x14\x14\x14\x14", # = 0x3d 61
">": b"\x00\x41\x22\x14\x08", # > 0x3e 62
"?": b"\x02\x01\x51\x09\x06", # ? 0x3f 63
"@": b"\x3e\x41\x5d\x49\x4e", # @ 0x40 64
"A": b"\x7e\x09\x09\x09\x7e", # A 0x41 65
"B": b"\x7f\x49\x49\x49\x36", # B 0x42 66
"C": b"\x3e\x41\x41\x41\x22", # C 0x43 67
"D": b"\x7f\x41\x41\x41\x3e", # D 0x44 68
"E": b"\x7f\x49\x49\x49\x41", # E 0x45 69
"F": b"\x7f\x09\x09\x09\x01", # F 0x46 70
"G": b"\x3e\x41\x49\x49\x7a", # G 0x47 71
"H": b"\x7f\x08\x08\x08\x7f", # H 0x48 72
"I": b"\x00\x41\x7f\x41\x00", # I 0x49 73
"J": b"\x20\x40\x41\x3f\x01", # J 0x4a 74
"K": b"\x7f\x08\x14\x22\x41", # K 0x4b 75
"L": b"\x7f\x40\x40\x40\x40", # L 0x4c 76
"M": b"\x7f\x02\x0c\x02\x7f", # M 0x4d 77
"N": b"\x7f\x04\x08\x10\x7f", # N 0x4e 78
"O": b"\x3e\x41\x41\x41\x3e", # O 0x4f 79
"P": b"\x7f\x09\x09\x09\x06", # P 0x50 80
"Q": b"\x3e\x41\x51\x21\x5e", # Q 0x51 81
"R": b"\x7f\x09\x19\x29\x46", # R 0x52 82
"S": b"\x46\x49\x49\x49\x31", # S 0x53 83
"T": b"\x01\x01\x7f\x01\x01", # T 0x54 84
"U": b"\x3f\x40\x40\x40\x3f", # U 0x55 85
"V": b"\x0f\x30\x40\x30\x0f", # V 0x56 86
"W": b"\x3f\x40\x30\x40\x3f", # W 0x57 87
"X": b"\x63\x14\x08\x14\x63", # X 0x58 88
"Y": b"\x07\x08\x70\x08\x07", # Y 0x59 89
"Z": b"\x61\x51\x49\x45\x43", # Z 0x5a 90
"[": b"\x3c\x4a\x49\x29\x1e", # [ 0x5b 91
"\\": b"\x02\x04\x08\x10\x20", # \ 0x5c 92
"]": b"\x00\x41\x7f\x00\x00", # ] 0x5d 93
"^": b"\x04\x02\x01\x02\x04", # ^ 0x5e 94
"_": b"\x40\x40\x40\x40\x40", # _ 0x5f 95
"`": b"\x00\x00\x03\x04\x00", # ` 0x60 96
"a": b"\x20\x54\x54\x54\x78", # a 0x61 97
"b": b"\x7f\x48\x44\x44\x38", # b 0x62 98
"c": b"\x38\x44\x44\x44\x20", # c 0x63 99
"d": b"\x38\x44\x44\x48\x7f", # d 0x64 100
"e": b"\x38\x54\x54\x54\x18", # e 0x65 101
"f": b"\x08\x7e\x09\x01\x02", # f 0x66 102
"g": b"\x0c\x52\x52\x52\x3e", # g 0x67 103
"h": b"\x7f\x08\x04\x04\x78", # h 0x68 104
"i": b"\x00\x44\x7d\x40\x00", # i 0x69 105
"j": b"\x20\x40\x44\x3d\x00", # j 0x6a 106
"k": b"\x00\x7f\x10\x28\x44", # k 0x6b 107
"l": b"\x00\x41\x7f\x40\x00", # l 0x6c 108
"m": b"\x7c\x04\x18\x04\x78", # m 0x6d 109
"n": b"\x7c\x08\x04\x04\x78", # n 0x6e 110
"o": b"\x38\x44\x44\x44\x38", # o 0x6f 111
"p": b"\x7c\x14\x14\x14\x08", # p 0x70 112
"q": b"\x08\x14\x14\x18\x7c", # q 0x71 113
"r": b"\x7c\x08\x04\x04\x08", # r 0x72 114
"s": b"\x48\x54\x54\x54\x20", # s 0x73 115
"t": b"\x04\x3f\x44\x40\x20", # t 0x74 116
"u": b"\x3c\x40\x40\x20\x7c", # u 0x75 117
"v": b"\x1c\x20\x40\x20\x1c", # v 0x76 118
"w": b"\x3c\x40\x30\x40\x3c", # w 0x77 119
"x": b"\x44\x28\x10\x28\x44", # x 0x78 120
"y": b"\x0c\x50\x50\x50\x3c", # y 0x79 121
"z": b"\x44\x64\x54\x4c\x44", # z 0x7a 122
"{": b"\x00\x08\x36\x41\x41", # [ 0x7b 123
"|": b"\x00\x00\x7f\x00\x00", # | 0x7c 124
"}": b"\x41\x41\x36\x08\x00", # ] 0x7d 125
"~": b"\x04\x02\x04\x08\x04", # ~ 0x7e 126
"А": b"\x7E\x11\x11\x11\x7E", # А (0xC0).
"Б": b"\x7F\x49\x49\x49\x33", # Б (0xC1).
"В": b"\x7F\x49\x49\x49\x36", # В (0xC2).
"Г": b"\x7F\x01\x01\x01\x03", # Г (0xC3).
"Д": b"\xE0\x51\x4F\x41\xFF", # Д (0xC4).
"Е": b"\x7F\x49\x49\x49\x41", # Е (0xC5).
"Ж": b"\x77\x08\x7F\x08\x77", # Ж (0xC6).
"З": b"\x41\x49\x49\x49\x36", # З (0xC7).
"И": b"\x7F\x10\x08\x04\x7F", # И (0xC8).
"Й": b"\x7C\x21\x12\x09\x7C", # Й (0xC9).
"К": b"\x7F\x08\x14\x22\x41", # К (0xCA).
"Л": b"\x20\x41\x3F\x01\x7F", # Л (0xCB).
"М": b"\x7F\x02\x0C\x02\x7F", # М (0xCC).
"Н": b"\x7F\x08\x08\x08\x7F", # Н (0xCD).
"О": b"\x3E\x41\x41\x41\x3E", # О (0xCE).
"П": b"\x7F\x01\x01\x01\x7F", # П (0xCF).
"Р": b"\x7F\x09\x09\x09\x06", # Р (0xD0).
"С": b"\x3E\x41\x41\x41\x22", # С (0xD1).
"Т": b"\x01\x01\x7F\x01\x01", # Т (0xD2).
"У": b"\x47\x28\x10\x08\x07", # У (0xD3).
"Ф": b"\x1C\x22\x7F\x22\x1C", # Ф (0xD4).
"Х": b"\x63\x14\x08\x14\x63", # Х (0xD5).
"Ц": b"\x7F\x40\x40\x40\xFF", # Ц (0xD6).
"Ч": b"\x07\x08\x08\x08\x7F", # Ч (0xD7).
"Ш": b"\x7F\x40\x7F\x40\x7F", # Ш (0xD8).
"Щ": b"\x7F\x40\x7F\x40\xFF", # Щ (0xD9).
"Ъ": b"\x01\x7F\x48\x48\x30", # Ъ (0xDA).
"Ы": b"\x7F\x48\x30\x00\x7F", # Ы (0xDB).
"Ь": b"\x00\x7F\x48\x48\x30", # Ь (0xDC).
"Э": b"\x22\x41\x49\x49\x3E", # Э (0xDD).
"Ю": b"\x7F\x08\x3E\x41\x3E", # Ю (0xDE).
"Я": b"\x46\x29\x19\x09\x7F", # Я (0xDF).
"а": b"\x20\x54\x54\x54\x78", # а (0xE0).
"б": b"\x3C\x4A\x4A\x49\x31", # б (0xE1).
"в": b"\x7C\x54\x54\x28\x00", # в (0xE2).
"г": b"\x7C\x04\x04\x0C\x00", # г (0xE3).
"д": b"\xE0\x54\x4C\x44\xFC", # д (0xE4).
"е": b"\x38\x54\x54\x54\x18", # е (0xE5).
"ж": b"\x6C\x10\x7C\x10\x6C", # ж (0xE6).
"з": b"\x44\x54\x54\x28\x00", # з (0xE7).
"и": b"\x7C\x20\x10\x08\x7C", # и (0xE8).
"й": b"\x78\x42\x24\x12\x78", # й (0xE9).
"к": b"\x7C\x10\x28\x44\x00", # к (0xEA).
"л": b"\x20\x44\x3C\x04\x7C", # л (0xEB).
"м": b"\x7C\x08\x10\x08\x7C", # м (0xEC).
"н": b"\x7C\x10\x10\x10\x7C", # н (0xED).
"о": b"\x38\x44\x44\x44\x38", # о (0xEE).
"п": b"\x7C\x04\x04\x04\x7C", # п (0xEF).
"р": b"\x7C\x14\x14\x14\x08", # р (0xF0).
"с": b"\x38\x44\x44\x44\x00", # с (0xF1).
"т": b"\x04\x04\x7C\x04\x04", # т (0xF2).
"у": b"\x0C\x50\x50\x50\x3C", # у (0xF3).
"ф": b"\x30\x48\xFE\x48\x30", # ф (0xF4).
"х": b"\x44\x28\x10\x28\x44", # х (0xF5).
"ц": b"\x7C\x40\x40\x7C\xC0", # ц (0xF6).
"ч": b"\x0C\x10\x10\x10\x7C", # ч (0xF7).
"ш": b"\x7C\x40\x7C\x40\x7C", # ш (0xF8).
"щ": b"\x7C\x40\x7C\x40\xFC", # щ (0xF9).
"ъ": b"\x04\x7C\x50\x50\x20", # ъ (0xFA).
"ы": b"\x7C\x50\x50\x20\x7C", # ы (0xFB).
"ь": b"\x7C\x50\x50\x20\x00", # ь (0xFC).
"э": b"\x28\x44\x54\x54\x38", # э (0xFD).
"ю": b"\x7C\x10\x38\x44\x38", # ю (0xFE).
"я": b"\x08\x54\x34\x14\x7C", # я (0xFF).
}
def len_in_pages(string: str, monospaced=True):
result = 0
for char in string:
if char == "\n": continue
try:
data = font[char]
except KeyError:
data = font["?"]
if not monospaced and char is not " ":
data = data.strip(b"\x00")
result += len(data) + 1
return result
class SSD1306(FrameBuffer):
_init_commands = bytearray(
(0xAE, # oled off
0xD5, # CLOCK_DIV_RATIO
0x80,
0x8D, # Charge pump
0x14,
0x20, # Memory mode
0x00, # Horizontal OLED_ADDRESSing
0xA1, # Flip horizontally
0xC8, # Flip vertically
0x81, # Set contrast
0xCF, # brighter
0xDB, # Set vcom detect
0x40, # brighter
0xAF, # oled on
))
OLED_COMMAND_MODE = 0x00
OLED_ONE_COMMAND_MODE = 0x80
OLED_DATA_MODE = 0x40
OLED_ONE_DATA_MODE = 0xC0
OLED_DISPLAY_OFF = 0xAE
OLED_DISPLAY_ON = 0xAF
OLED128X64 = (128, 64)
OLED128X32 = (128, 32)
OLED_HEIGHT_32 = 0x02
OLED_HEIGHT_64 = 0x12
OLED_64 = 0x3F
OLED_32 = 0x1F
def __init__(self, i2c: I2C, type, address=0x3C):
self.i2c = i2c
self.width = type[0]
self.height = type[1]
self.address = address
self.cursorX = 0
self.cursorY = 0
temp = bytearray()
temp.append(self.OLED_COMMAND_MODE)
temp += self._init_commands
temp.append(0xDA)
if self.width == 32 or self.width == 16:
temp.append(self.OLED_HEIGHT_32)
else:
temp.append(self.OLED_HEIGHT_64)
temp.append(0xA8)
if self.width == 32 or self.width == 16:
temp.append(self.OLED_32)
else:
temp.append(self.OLED_64)
i2c.writeto(address, temp)
del temp
self._numRows = 4 if self.width == 32 or self.width == 16 else 8
self._buffer = bytearray(self.width * self.height // 8)
super().__init__(memoryview(self._buffer), self.width, self.height, MONO_VLSB)
def update(self):
self.set_window(0, self.width - 1, 0, self.height // 8 - 1)
temp = bytearray()
temp.append(self.OLED_DATA_MODE)
temp += self._buffer
self.i2c.writeto(self.address, temp)
def set_window(self, x0: int, x1: int, y0: int, y1: int):
temp = bytearray()
temp.append(self.OLED_COMMAND_MODE)
temp.append(0x21)
temp.append(x0)
temp.append(x1)
temp.append(0x22)
temp.append(y0)
temp.append(y1)
self.i2c.writeto(self.address, temp)
def dot(self, x: int, y: int, v: int):
return self.pixel(x, y, v)
def _lineInterrupt(self):
if self.cursorY >= self.height // 8 - 1:
raise OverflowError("Display is too small or data is too large")
self.cursorY += 1
self.cursorX = 0
def print(self, string: str, inverse: bool = False, monospaced=True, word_wrap=False):
if word_wrap:
self._word_wrap_print(string, inverse=inverse, monospaced=monospaced)
return
for char in string:
if char == "\n":
self._lineInterrupt()
continue
try:
data = font[char]
except KeyError:
data = font["?"]
if not monospaced and char is not " ":
data = data.strip(b"\x00")
if self.cursorX+len(data) > self.width: # Почему 128 (width), а не 127 (width - 1)? Заполнение идет с cursorX, включая само значение cursorX.
# Т.е. если cursorX = 127, то одну страницу еще можно вставить. (127 + 1 = 128)
self._lineInterrupt()
for i, byte in enumerate(data):
if inverse:
byte = ~byte
self._buffer[self.cursorY*self.width + self.cursorX + i] = byte
self.cursorX += 1 + len(data)
def _word_wrap_print(self, string: str, inverse: bool = False, monospaced=True):
words = string.split(" ")
for i, word in enumerate(words):
length = len_in_pages(word, monospaced=monospaced)
print(self.cursorX, length)
if self.cursorX + length > self.width:
self._lineInterrupt()
new_line = False
if len(words) - 1 > i:
if self.cursorX + length + 5 < self.width or length % self.width != 0: # and length % self.width + 5 < self.width):
word += " "
else:
new_line = True
self.print(word, inverse=inverse, monospaced=monospaced)
if new_line:
self._lineInterrupt()
def setCursor(self, x, y):
"""
X in pixels
Y in pages (8 pixel)
"""
self.cursorX = x
self.cursorY = y
def home(self):
self.setCursor(0, 0)
@property
def buffer(self):
return memoryview(self._buffer)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment