MicroPython: OLED-дисплей с ESP32 и ESP8266

В этом руководстве показано, как использовать OLED-дисплей SSD1306 с разрешением 0,96 дюйма и разрешением 128x64 пикселей совместно с микроконтроллерами ESP32 или ESP8266, работающими на прошивке MicroPython. В уроке рассматривается вывод сообщения «Hello, World!» и другие полезные функции дисплея.

Похожие руководства:

Необходимые условия

Для выполнения этого урока вам потребуется:

  • Прошивка MicroPython, установленная на ESP32 или ESP8266

  • Среда разработки (IDE) для написания и загрузки кода

Рекомендуемые IDE:

Thonny IDE:

uPyCraft IDE:

Знакомство с OLED-дисплеем

В этом руководстве используется OLED-дисплей SSD1306 с диагональю 0,96 дюйма и разрешением 128x64 пикселей, который взаимодействует по протоколу I2C.

OLED-дисплей SSD1306 0.96 дюйма 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

Подключение OLED-дисплея к ESP32 MicroPython

Рекомендуемое чтение: Распиновка ESP32: какие GPIO пины следует использовать? — Справочник по распиновке ESP32.

Схема подключения — ESP8266

Подключение OLED-дисплея к ESP8266 MicroPython

Библиотека 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()

Исходный код на GitHub

Примечание

Библиотека OLED-дисплея SSD1306 была создана компанией Adafruit и в настоящее время больше не обновляется. На данный момент она работает корректно.

A. Загрузка библиотеки OLED через uPyCraft IDE

  1. Нажмите кнопку New File (Новый файл)

  2. Скопируйте код библиотеки OLED

  3. Нажмите Save (Сохранить)

  4. Назовите файл «ssd1306.py» и нажмите ok

  5. Нажмите Download and Run (Загрузить и запустить)

Файл будет сохранён в папке устройства с именем «ssd1306.py». После этого библиотеку можно импортировать в вашем коде.

B. Загрузка библиотеки OLED через Thonny IDE

  1. Создайте новый файл и скопируйте код библиотеки OLED

  2. Перейдите в File > Save as и выберите MicroPython device

  3. Назовите файл «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()

Исходный код на GitHub

Как работает код

Импорт библиотек:

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 ESP32 ESP8266 MicroPython

После загрузки кода на OLED-дисплее отображаются три сообщения «Hello, World!».

Другие функции OLED

Заливка экрана

Заливка всего экрана белым цветом:

oled.fill(1)
oled.show()
OLED-дисплей заливка белым экраном ESP32 ESP8266 MicroPython

Очистка экрана (установка всех пикселей в чёрный цвет):

oled.fill(0)
oled.show()

Рисование пикселя

oled.pixel(0, 0, 1)
oled.show()

Метод pixel() принимает:

  • Координата X: горизонтальная позиция пикселя

  • Координата Y: вертикальная позиция пикселя

  • Цвет пикселя: 0 = чёрный, 1 = белый

OLED-дисплей отображение пикселя ESP32 ESP8266 MicroPython

Инверсия цветов

Инвертировать все цвета дисплея:

oled.invert(True)
OLED-дисплей инверсия цветов ESP32 ESP8266 MicroPython

Вернуть исходные цвета:

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: