MicroPython: OLED-дисплей с ESP32 и ESP8266
В этом руководстве показано, как использовать OLED-дисплей SSD1306 с разрешением 0,96 дюйма и разрешением 128x64 пикселей совместно с микроконтроллерами ESP32 или ESP8266, работающими на прошивке MicroPython. В уроке рассматривается вывод сообщения «Hello, World!» и другие полезные функции дисплея.
Похожие руководства:
ESP32 OLED-дисплей с Arduino IDE — ESP32 OLED-дисплей с Arduino IDE
Необходимые условия
Для выполнения этого урока вам потребуется:
Прошивка MicroPython, установленная на ESP32 или ESP8266
Среда разработки (IDE) для написания и загрузки кода
Рекомендуемые IDE:
Thonny IDE:
Начало работы с Thonny MicroPython (Python) IDE для ESP32 и ESP8266 — Установка и начало работы с Thonny IDE
Прошивка MicroPython с помощью esptool.py на ESP32 и ESP8266 — Прошивка MicroPython с помощью esptool.py
uPyCraft IDE:
Начало работы с MicroPython на ESP32 и ESP8266 — Начало работы с uPyCraft IDE
Прошивка MicroPython на ESP32 и ESP8266 с помощью esptool.py — Загрузка прошивки MicroPython
Знакомство с OLED-дисплеем
В этом руководстве используется OLED-дисплей SSD1306 с диагональю 0,96 дюйма и разрешением 128x64 пикселей, который взаимодействует по протоколу I2C.
Подключение по I2C
Вывод дисплея |
ESP32 |
ESP8266 |
|---|---|---|
Vin |
3.3V |
3.3V |
GND |
GND |
GND |
SCL |
GPIO 22 |
GPIO 5 (D1) |
SDA |
GPIO 21 |
GPIO 4 (D2) |
Необходимые компоненты
OLED-дисплей 0,96 дюйма (128x64)
ESP32 или ESP8266
Макетная плата
Соединительные провода
Схема подключения — ESP32
Рекомендуемое чтение: Распиновка ESP32: какие GPIO пины следует использовать? — Справочник по распиновке ESP32.
Схема подключения — ESP8266
Библиотека SSD1306 OLED
Библиотека драйвера OLED SSD1306 не входит в стандартную прошивку MicroPython и должна быть загружена на плату отдельно.
Полный код библиотеки (ssd1306.py):
# MicroPython SSD1306 OLED driver, I2C and SPI interfaces created by Adafruit
import time
import framebuf
# register definitions
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xa4)
SET_NORM_INV = const(0xa6)
SET_DISP = const(0xae)
SET_MEM_ADDR = const(0x20)
SET_COL_ADDR = const(0x21)
SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xa0)
SET_MUX_RATIO = const(0xa8)
SET_COM_OUT_DIR = const(0xc0)
SET_DISP_OFFSET = const(0xd3)
SET_COM_PIN_CFG = const(0xda)
SET_DISP_CLK_DIV = const(0xd5)
SET_PRECHARGE = const(0xd9)
SET_VCOM_DESEL = const(0xdb)
SET_CHARGE_PUMP = const(0x8d)
class SSD1306:
def __init__(self, width, height, external_vcc):
self.width = width
self.height = height
self.external_vcc = external_vcc
self.pages = self.height // 8
# Note the subclass must initialize self.framebuf to a framebuffer.
# This is necessary because the underlying data buffer is different
# between I2C and SPI implementations (I2C needs an extra byte).
self.poweron()
self.init_display()
def init_display(self):
for cmd in (
SET_DISP | 0x00, # off
# address setting
SET_MEM_ADDR, 0x00, # horizontal
# resolution and layout
SET_DISP_START_LINE | 0x00,
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
SET_MUX_RATIO, self.height - 1,
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
SET_DISP_OFFSET, 0x00,
SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12,
# timing and driving scheme
SET_DISP_CLK_DIV, 0x80,
SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1,
SET_VCOM_DESEL, 0x30, # 0.83*Vcc
# display
SET_CONTRAST, 0xff, # maximum
SET_ENTIRE_ON, # output follows RAM contents
SET_NORM_INV, # not inverted
# charge pump
SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01): # on
self.write_cmd(cmd)
self.fill(0)
self.show()
def poweroff(self):
self.write_cmd(SET_DISP | 0x00)
def contrast(self, contrast):
self.write_cmd(SET_CONTRAST)
self.write_cmd(contrast)
def invert(self, invert):
self.write_cmd(SET_NORM_INV | (invert & 1))
def show(self):
x0 = 0
x1 = self.width - 1
if self.width == 64:
# displays with width of 64 pixels are shifted by 32
x0 += 32
x1 += 32
self.write_cmd(SET_COL_ADDR)
self.write_cmd(x0)
self.write_cmd(x1)
self.write_cmd(SET_PAGE_ADDR)
self.write_cmd(0)
self.write_cmd(self.pages - 1)
self.write_framebuf()
def fill(self, col):
self.framebuf.fill(col)
def pixel(self, x, y, col):
self.framebuf.pixel(x, y, col)
def scroll(self, dx, dy):
self.framebuf.scroll(dx, dy)
def text(self, string, x, y, col=1):
self.framebuf.text(string, x, y, col)
class SSD1306_I2C(SSD1306):
def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
# Add an extra byte to the data buffer to hold an I2C data/command byte
# to use hardware-compatible I2C transactions. A memoryview of the
# buffer is used to mask this byte from the framebuffer operations
# (without a major memory hit as memoryview doesn't copy to a separate
# buffer).
self.buffer = bytearray(((height // 8) * width) + 1)
self.buffer[0] = 0x40 # Set first byte of data buffer to Co=0, D/C=1
self.framebuf = framebuf.FrameBuffer1(memoryview(self.buffer)[1:], width, height)
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.temp[0] = 0x80 # Co=1, D/C#=0
self.temp[1] = cmd
self.i2c.writeto(self.addr, self.temp)
def write_framebuf(self):
# Blast out the frame buffer using a single I2C transaction to support
# hardware I2C interfaces.
self.i2c.writeto(self.addr, self.buffer)
def poweron(self):
pass
class SSD1306_SPI(SSD1306):
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
self.rate = 10 * 1024 * 1024
dc.init(dc.OUT, value=0)
res.init(res.OUT, value=0)
cs.init(cs.OUT, value=1)
self.spi = spi
self.dc = dc
self.res = res
self.cs = cs
self.buffer = bytearray((height // 8) * width)
self.framebuf = framebuf.FrameBuffer1(self.buffer, width, height)
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs.high()
self.dc.low()
self.cs.low()
self.spi.write(bytearray([cmd]))
self.cs.high()
def write_framebuf(self):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs.high()
self.dc.high()
self.cs.low()
self.spi.write(self.buffer)
self.cs.high()
def poweron(self):
self.res.high()
time.sleep_ms(1)
self.res.low()
time.sleep_ms(10)
self.res.high()
Примечание
Библиотека OLED-дисплея SSD1306 была создана компанией Adafruit и в настоящее время больше не обновляется. На данный момент она работает корректно.
A. Загрузка библиотеки OLED через uPyCraft IDE
Нажмите кнопку New File (Новый файл)
Скопируйте код библиотеки OLED
Нажмите Save (Сохранить)
Назовите файл «ssd1306.py» и нажмите ok
Нажмите Download and Run (Загрузить и запустить)
Файл будет сохранён в папке устройства с именем «ssd1306.py». После этого библиотеку можно импортировать в вашем коде.
B. Загрузка библиотеки OLED через Thonny IDE
Создайте новый файл и скопируйте код библиотеки OLED
Перейдите в File > Save as и выберите MicroPython device
Назовите файл «ssd1306.py» и нажмите OK для сохранения
Библиотека загружена и готова к использованию.
Код
После загрузки библиотеки скопируйте следующий код в файл main.py:
from machine import Pin, SoftI2C
import ssd1306
from time import sleep
# Назначение пинов ESP32
i2c = SoftI2C(scl=Pin(22), sda=Pin(21))
# Назначение пинов ESP8266
#i2c = SoftI2C(scl=Pin(5), sda=Pin(4))
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
oled.text('Hello, World 1!', 0, 0)
oled.text('Hello, World 2!', 0, 10)
oled.text('Hello, World 3!', 0, 20)
oled.show()
Как работает код
Импорт библиотек:
from machine import Pin, SoftI2C
import ssd1306
Импортируем классы Pin и SoftI2C из модуля machine для управления GPIO и I2C-коммуникации. Библиотека ssd1306 обеспечивает функциональность OLED-дисплея.
Настройка I2C:
Для ESP32:
i2c = SoftI2C(scl=Pin(22), sda=Pin(21))
Для ESP8266:
i2c = SoftI2C(scl=Pin(5), sda=Pin(4))
Инициализация дисплея:
oled_width = 128
oled_height = 64
oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)
Создаём объект SSD1306_I2C с указанными размерами и конфигурацией I2C.
Вывод текста:
oled.text('Hello, World 1!', 0, 0)
oled.text('Hello, World 2!', 0, 10)
oled.text('Hello, World 3!', 0, 20)
oled.show()
Метод text() принимает следующие параметры:
Сообщение: тип String (строка)
Позиция X: горизонтальная начальная точка
Позиция Y: вертикальная позиция
Цвет текста (необязательно): 0 = чёрный, 1 = белый (по умолчанию — белый)
Метод show() обновляет дисплей, отображая отрендеренное содержимое.
Демонстрация
После загрузки кода на OLED-дисплее отображаются три сообщения «Hello, World!».
Другие функции OLED
Заливка экрана
Заливка всего экрана белым цветом:
oled.fill(1)
oled.show()
Очистка экрана (установка всех пикселей в чёрный цвет):
oled.fill(0)
oled.show()
Рисование пикселя
oled.pixel(0, 0, 1)
oled.show()
Метод pixel() принимает:
Координата X: горизонтальная позиция пикселя
Координата Y: вертикальная позиция пикселя
Цвет пикселя: 0 = чёрный, 1 = белый
Инверсия цветов
Инвертировать все цвета дисплея:
oled.invert(True)
Вернуть исходные цвета:
oled.invert(False)
Отображение данных с датчиков
Функция text() требует сообщения типа String. Данные датчиков, хранящиеся в переменных типа int или float, необходимо преобразовать:
temperature = 12.34
temperature_string = str(temperature)
Вывод преобразованного значения:
oled.text(temperature_string, 0, 0)
oled.show()
Заключение
В этом руководстве были рассмотрены основные функции OLED-дисплея, включая вывод текста и рисование пикселей, при использовании ESP32 и ESP8266 с MicroPython. Теперь OLED-дисплей можно использовать в проектах для отображения сообщений и показаний датчиков.
Связанные руководства по MicroPython:
MicroPython: ESP32/ESP8266 с датчиком температуры и влажности DHT11/DHT22 — MicroPython: ESP32/ESP8266 с DHT11/DHT22
MicroPython: веб-сервер ESP32/ESP8266 с DHT11/DHT22 (метеостанция) — MicroPython: Веб-сервер DHT11/DHT22 на ESP32/ESP8266
MicroPython: Глубокий сон ESP32 и источники пробуждения — MicroPython: ESP32 глубокий сон и источники пробуждения