MicroPython: SSD1306 OLED-дисплей — функции прокрутки и рисование фигур (ESP32/ESP8266)

В этом руководстве показаны дополнительные функции для управления OLED-дисплеем на MicroPython с использованием ESP32 или ESP8266. Вы узнаете, как прокручивать весь экран по горизонтали и вертикали, а также как рисовать фигуры.

MicroPython: SSD1306 OLED-дисплей — функции прокрутки и рисование фигур ESP32 ESP8266

Мы рекомендуем сначала ознакомиться с начальным руководством по работе с OLED-дисплеем на MicroPython: MicroPython: OLED-дисплей с ESP32 и ESP8266.

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

Для выполнения этого руководства вам необходимо установить прошивку MicroPython на плату ESP32 или ESP8266. Также вам нужна IDE для написания и загрузки кода на плату. Мы рекомендуем использовать Thonny IDE или uPyCraft IDE:

Необходимые компоненты

Список компонентов для этого урока:

  • ESP32 или ESP8266

  • OLED-дисплей 0.96 дюйма (SSD1306, 128x64)

  • Макетная плата

  • Соединительные провода

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

Используйте следующую схему подключения, если вы работаете с платой ESP32:

Схема подключения OLED SSD1306 к ESP32 MicroPython I2C SCL SDA

Рекомендуемое чтение: Справочник по выводам ESP32

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

Используйте следующую схему подключения, если вы работаете с платой ESP8266 NodeMCU:

Схема подключения OLED SSD1306 к ESP8266 NodeMCU MicroPython I2C SCL SDA

Библиотека SSD1306 OLED

Библиотека для записи на OLED-дисплей не входит в стандартную библиотеку MicroPython по умолчанию. Поэтому вам необходимо загрузить библиотеку на плату ESP32/ESP8266.

# 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

Следуйте приведённым ниже инструкциям для вашей IDE:

    1. Загрузка библиотеки OLED с помощью uPyCraft IDE

    1. Загрузка библиотеки OLED с помощью Thonny IDE

A. Загрузка библиотеки OLED с помощью uPyCraft IDE

В этом разделе показано, как загрузить библиотеку с помощью uPyCraft IDE. Если вы используете Thonny IDE, перейдите к следующему разделу.

1. Создайте новый файл, нажав кнопку New File.

uPyCraft IDE — создание нового файла

2. Скопируйте код библиотеки OLED в этот файл. Код библиотеки OLED можно найти здесь.

Примечание

Библиотека SSD1306 OLED-дисплея была создана Adafruit и больше не обновляется. На данный момент она работает нормально. Однако мы обновим это руководство, если найдём аналогичную библиотеку, которая работает так же хорошо.

3. После копирования кода сохраните файл, нажав кнопку Save.

uPyCraft IDE — сохранение файла

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

Импорт библиотеки uPyCraft IDE — сохранение файла ssd1306.py

5. Нажмите кнопку Download and Run.

uPyCraft IDE — загрузка и запуск

Файл должен быть сохранён в папке устройства с именем ssd1306.py, как показано на следующем изображении.

Импорт и сохранение библиотеки uPyCraft IDE — файл скрипта

Теперь вы можете использовать функции библиотеки в своём коде, импортировав библиотеку.

B. Загрузка библиотеки OLED с помощью Thonny IDE

Если вы используете Thonny IDE, выполните следующие шаги:

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

2. Сохраните этот файл как ssd1306.py.

3. Перейдите в Device > Upload current script with the current name.

Thonny IDE — загрузка файла скрипта библиотеки MicroPython

Вот и всё. Библиотека загружена на вашу плату. Чтобы убедиться, что она успешно загружена, в Shell вы можете ввести:

%lsdevice

Должен отобразиться список файлов, сохранённых на вашей плате. Один из них должен быть файлом ssd1306.py.

Отображение папки устройства в Thonny IDE

После загрузки библиотеки на плату вы можете использовать функции библиотеки в своём коде, импортировав библиотеку.

Функции прокрутки OLED на MicroPython

Библиотека ssd1306.py содержит функцию scroll(x, y). Она прокручивает экран на x пикселей вправо и y пикселей вниз.

Горизонтальная прокрутка экрана OLED

Иногда нужно отображать разные экраны на OLED-дисплее. Например, первый экран показывает показания датчиков, а второй — состояния GPIO.

Прокрутка экрана внутрь по горизонтали

Следующая функция scroll_in_screen(screen) прокручивает содержимое всего экрана (справа налево).

def scroll_in_screen(screen):
  for i in range (0, oled_width+1, 4):
    for line in screen:
      oled.text(line[2], -oled_width+i, line[1])
    oled.show()
    if i!= oled_width:
      oled.fill(0)

Эта функция принимает в качестве аргумента список списков. Например:

screen1 = [[0, 0 , screen1_row1], [0, 16, screen1_row2], [0, 32, screen1_row3]]

Каждый список в списке содержит координату x, координату y и сообщение [x, y, message].

В качестве примера мы отобразим три строки на первом экране со следующими сообщениями:

screen1_row1 = "Screen 1, row 1"
screen1_row2 = "Screen 1, row 2"
screen1_row3 = "Screen 1, row 3"

Затем, чтобы прокрутить экран слева направо, просто вызовите функцию scroll_in_screen() и передайте в качестве аргумента список списков:

scroll_in_screen(screen1)

Вы получите примерно следующее:

Прокрутка экрана наружу по горизонтали

Чтобы прокрутить экран наружу, используйте функцию scroll_out_screen(speed), которая прокручивает весь экран за пределы OLED. Она принимает в качестве аргумента число, управляющее скоростью прокрутки. Скорость должна быть делителем 128 (oled_width).

def scroll_out_screen(speed):
  for i in range ((oled_width+1)/speed):
    for j in range (oled_height):
      oled.pixel(i, j, 0)
    oled.scroll(speed,0)
    oled.show()

Теперь вы можете использовать обе функции для переключения между экранами. Например:

scroll_in_screen(screen1)
scroll_out_screen(4)
scroll_in_screen(screen2)
scroll_out_screen(4)

Непрерывная горизонтальная прокрутка

Если вы хотите непрерывно прокручивать экран внутрь и наружу, используйте функцию scroll_screen_in_out(screen).

def scroll_screen_in_out(screen):
  for i in range (0, (oled_width+1)*2, 1):
    for line in screen:
      oled.text(line[2], -oled_width+i, line[1])
    oled.show()
    if i!= oled_width:
      oled.fill(0)

Вы можете использовать эту функцию для переключения между экранами или для непрерывной прокрутки одного и того же экрана.

scroll_screen_in_out(screen1)
scroll_screen_in_out(screen2)
scroll_screen_in_out(screen3)

Вертикальная прокрутка экрана OLED

Мы также создали аналогичные функции для вертикальной прокрутки экрана.

Прокрутка экрана внутрь по вертикали

Функция scroll_in_screen_v(screen) прокручивает содержимое всего экрана по вертикали.

def scroll_in_screen_v(screen):
  for i in range (0, (oled_height+1), 1):
    for line in screen:
      oled.text(line[2], line[0], -oled_height+i+line[1])
    oled.show()
    if i!= oled_height:
      oled.fill(0)

Прокрутка экрана наружу по вертикали

Используйте функцию scroll_out_screen_v(speed) для вертикальной прокрутки экрана наружу. Аналогично горизонтальной функции, она принимает в качестве аргумента скорость прокрутки, которая должна быть делителем 64 (oled_height).

def scroll_out_screen_v(speed):
  for i in range ((oled_height+1)/speed):
    for j in range (oled_width):
      oled.pixel(j, i, 0)
    oled.scroll(0,speed)
    oled.show()

Непрерывная вертикальная прокрутка

Если вы хотите непрерывно прокручивать экран внутрь и наружу по вертикали, используйте функцию scroll_screen_in_out_v(screen).

def scroll_screen_in_out_v(screen):
  for i in range (0, (oled_height*2+1), 1):
    for line in screen:
      oled.text(line[2], line[0], -oled_height+i+line[1])
    oled.show()
    if i!= oled_height:
      oled.fill(0)

Полный скрипт прокрутки экрана OLED на MicroPython

Следующий скрипт применяет все функции прокрутки, описанные выше. Вы можете загрузить этот код на свою плату, чтобы увидеть все эффекты прокрутки.

# Полный проект: https://RandomNerdTutorials.com/micropython-ssd1306-oled-scroll-shapes-esp32-esp8266/

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)

screen1_row1 = "Screen 1, row 1"
screen1_row2 = "Screen 1, row 2"
screen1_row3 = "Screen 1, row 3"

screen2_row1 = "Screen 2, row 1"
screen2_row2 = "Screen 2, row 2"

screen3_row1 = "Screen 3, row 1"

screen1 = [[0, 0 , screen1_row1], [0, 16, screen1_row2], [0, 32, screen1_row3]]
screen2 = [[0, 0 , screen2_row1], [0, 16, screen2_row2]]
screen3 = [[0, 40 , screen3_row1]]

# Горизонтальная прокрутка экрана внутрь (слева направо)
def scroll_in_screen(screen):
  for i in range (0, oled_width+1, 4):
    for line in screen:
      oled.text(line[2], -oled_width+i, line[1])
    oled.show()
    if i!= oled_width:
      oled.fill(0)

# Горизонтальная прокрутка экрана наружу (слева направо)
def scroll_out_screen(speed):
  for i in range ((oled_width+1)/speed):
    for j in range (oled_height):
      oled.pixel(i, j, 0)
    oled.scroll(speed,0)
    oled.show()

# Непрерывная горизонтальная прокрутка
def scroll_screen_in_out(screen):
  for i in range (0, (oled_width+1)*2, 1):
    for line in screen:
      oled.text(line[2], -oled_width+i, line[1])
    oled.show()
    if i!= oled_width:
      oled.fill(0)

# Вертикальная прокрутка экрана внутрь
def scroll_in_screen_v(screen):
  for i in range (0, (oled_height+1), 1):
    for line in screen:
      oled.text(line[2], line[0], -oled_height+i+line[1])
    oled.show()
    if i!= oled_height:
      oled.fill(0)

# Вертикальная прокрутка экрана наружу
def scroll_out_screen_v(speed):
  for i in range ((oled_height+1)/speed):
    for j in range (oled_width):
      oled.pixel(j, i, 0)
    oled.scroll(0,speed)
    oled.show()

# Непрерывная вертикальная прокрутка
def scroll_screen_in_out_v(screen):
  for i in range (0, (oled_height*2+1), 1):
    for line in screen:
      oled.text(line[2], line[0], -oled_height+i+line[1])
    oled.show()
    if i!= oled_height:
      oled.fill(0)

while True:

  # Прокрутка внутрь, пауза, прокрутка наружу (горизонтальная)
  scroll_in_screen(screen1)
  sleep(2)
  scroll_out_screen(4)

  scroll_in_screen(screen2)
  sleep(2)
  scroll_out_screen(4)

  scroll_in_screen(screen3)
  sleep(2)
  scroll_out_screen(4)

  # Непрерывная горизонтальная прокрутка
  scroll_screen_in_out(screen1)
  scroll_screen_in_out(screen2)
  scroll_screen_in_out(screen3)

  # Прокрутка внутрь, пауза, прокрутка наружу (вертикальная)
  scroll_in_screen_v(screen1)
  sleep(2)
  scroll_out_screen_v(4)

  scroll_in_screen_v(screen2)
  sleep(2)
  scroll_out_screen_v(4)

  scroll_in_screen_v(screen3)
  sleep(2)
  scroll_out_screen_v(4)

  # Непрерывная вертикальная прокрутка
  scroll_screen_in_out_v(screen1)
  scroll_screen_in_out_v(screen2)
  scroll_screen_in_out_v(screen3)

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

Рисование фигур на OLED с помощью MicroPython

Для рисования фигур на OLED-дисплее с помощью MicroPython мы будем использовать библиотеку Adafruit GFX для MicroPython.

Библиотека Adafruit GFX

Для рисования фигур на OLED-дисплее мы будем использовать библиотеку Adafruit GFX. Эта библиотека не входит в стандартную библиотеку MicroPython по умолчанию. Поэтому вам необходимо загрузить библиотеку на плату ESP32/ESP8266.

# Port of Adafruit GFX Arduino library to MicroPython.
# Based on: https://github.com/adafruit/Adafruit-GFX-Library
# Author: Tony DiCola (original GFX author Phil Burgess)
# License: MIT License (https://opensource.org/licenses/MIT)

class GFX:

    def __init__(self, width, height, pixel, hline=None, vline=None):
        # Create an instance of the GFX drawing class.  You must pass in the
        # following parameters:
        #  - width = The width of the drawing area in pixels.
        #  - height = The height of the drawing area in pixels.
        #  - pixel = A function to call when a pixel is drawn on the display.
        #            This function should take at least an x and y position
        #            and then any number of optional color or other parameters.
        #  You can also provide the following optional keyword argument to
        #  improve the performance of drawing:
        #  - hline = A function to quickly draw a horizontal line on the display.
        #            This should take at least an x, y, and width parameter and
        #            any number of optional color or other parameters.
        #  - vline = A function to quickly draw a vertical line on the display.
        #            This should take at least an x, y, and height paraemter and
        #            any number of optional color or other parameters.
        self.width = width
        self.height = height
        self._pixel = pixel
        # Default to slow horizontal & vertical line implementations if no
        # faster versions are provided.
        if hline is None:
            self.hline = self._slow_hline
        else:
            self.hline = hline
        if vline is None:
            self.vline = self._slow_vline
        else:
            self.vline = vline

    def _slow_hline(self, x0, y0, width, *args, **kwargs):
        if y0 < 0 or y0 > self.height or x0 < -width or x0 > self.width:
            return
        for i in range(width):
            self._pixel(x0+i, y0, *args, **kwargs)

    def _slow_vline(self, x0, y0, height, *args, **kwargs):
        if y0 < -height or y0 > self.height or x0 < 0 or x0 > self.width:
            return
        for i in range(height):
            self._pixel(x0, y0+i, *args, **kwargs)

    def rect(self, x0, y0, width, height, *args, **kwargs):
        if y0 < -height or y0 > self.height or x0 < -width or x0 > self.width:
            return
        self.hline(x0, y0, width, *args, **kwargs)
        self.hline(x0, y0+height-1, width, *args, **kwargs)
        self.vline(x0, y0, height, *args, **kwargs)
        self.vline(x0+width-1, y0, height, *args, **kwargs)

    def fill_rect(self, x0, y0, width, height, *args, **kwargs):
        if y0 < -height or y0 > self.height or x0 < -width or x0 > self.width:
            return
        for i in range(x0, x0+width):
            self.vline(i, y0, height, *args, **kwargs)

    def line(self, x0, y0, x1, y1, *args, **kwargs):
        steep = abs(y1 - y0) > abs(x1 - x0)
        if steep:
            x0, y0 = y0, x0
            x1, y1 = y1, x1
        if x0 > x1:
            x0, x1 = x1, x0
            y0, y1 = y1, y0
        dx = x1 - x0
        dy = abs(y1 - y0)
        err = dx // 2
        ystep = 0
        if y0 < y1:
            ystep = 1
        else:
            ystep = -1
        while x0 <= x1:
            if steep:
                self._pixel(y0, x0, *args, **kwargs)
            else:
                self._pixel(x0, y0, *args, **kwargs)
            err -= dy
            if err < 0:
                y0 += ystep
                err += dx
            x0 += 1

    def circle(self, x0, y0, radius, *args, **kwargs):
        f = 1 - radius
        ddF_x = 1
        ddF_y = -2 * radius
        x = 0
        y = radius
        self._pixel(x0, y0 + radius, *args, **kwargs)
        self._pixel(x0, y0 - radius, *args, **kwargs)
        self._pixel(x0 + radius, y0, *args, **kwargs)
        self._pixel(x0 - radius, y0, *args, **kwargs)
        while x < y:
            if f >= 0:
                y -= 1
                ddF_y += 2
                f += ddF_y
            x += 1
            ddF_x += 2
            f += ddF_x
            self._pixel(x0 + x, y0 + y, *args, **kwargs)
            self._pixel(x0 - x, y0 + y, *args, **kwargs)
            self._pixel(x0 + x, y0 - y, *args, **kwargs)
            self._pixel(x0 - x, y0 - y, *args, **kwargs)
            self._pixel(x0 + y, y0 + x, *args, **kwargs)
            self._pixel(x0 - y, y0 + x, *args, **kwargs)
            self._pixel(x0 + y, y0 - x, *args, **kwargs)
            self._pixel(x0 - y, y0 - x, *args, **kwargs)

    def fill_circle(self, x0, y0, radius, *args, **kwargs):
        self.vline(x0, y0 - radius, 2*radius + 1, *args, **kwargs)
        f = 1 - radius
        ddF_x = 1
        ddF_y = -2 * radius
        x = 0
        y = radius
        while x < y:
            if f >= 0:
                y -= 1
                ddF_y += 2
                f += ddF_y
            x += 1
            ddF_x += 2
            f += ddF_x
            self.vline(x0 + x, y0 - y, 2*y + 1, *args, **kwargs)
            self.vline(x0 + y, y0 - x, 2*x + 1, *args, **kwargs)
            self.vline(x0 - x, y0 - y, 2*y + 1, *args, **kwargs)
            self.vline(x0 - y, y0 - x, 2*x + 1, *args, **kwargs)

    def triangle(self, x0, y0, x1, y1, x2, y2, *args, **kwargs):
        self.line(x0, y0, x1, y1, *args, **kwargs)
        self.line(x1, y1, x2, y2, *args, **kwargs)
        self.line(x2, y2, x0, y0, *args, **kwargs)

    def fill_triangle(self, x0, y0, x1, y1, x2, y2, *args, **kwargs):
        if y0 > y1:
            y0, y1 = y1, y0
            x0, x1 = x1, x0
        if y1 > y2:
            y2, y1 = y1, y2
            x2, x1 = x1, x2
        if y0 > y1:
            y0, y1 = y1, y0
            x0, x1 = x1, x0
        a = 0
        b = 0
        y = 0
        last = 0
        if y0 == y2:
            a = x0
            b = x0
            if x1 < a:
                a = x1
            elif x1 > b:
                b = x1
            if x2 < a:
                a = x2
            elif x2 > b:
                b = x2
            self.hline(a, y0, b-a+1, *args, **kwargs)
            return
        dx01 = x1 - x0
        dy01 = y1 - y0
        dx02 = x2 - x0
        dy02 = y2 - y0
        dx12 = x2 - x1
        dy12 = y2 - y1
        if dy01 == 0:
            dy01 = 1
        if dy02 == 0:
            dy02 = 1
        if dy12 == 0:
            dy12 = 1
        sa = 0
        sb = 0
        if y1 == y2:
            last = y1
        else:
            last = y1-1
        for y in range(y0, last+1):
            a = x0 + sa // dy01
            b = x0 + sb // dy02
            sa += dx01
            sb += dx02
            if a > b:
                a, b = b, a
            self.hline(a, y, b-a+1, *args, **kwargs)
        sa = dx12 * (y - y1)
        sb = dx02 * (y - y0)
        while y <= y2:
            a = x1 + sa // dy12
            b = x0 + sb // dy02
            sa += dx12
            sb += dx02
            if a > b:
                a, b = b, a
            self.hline(a, y, b-a+1, *args, **kwargs)
            y += 1

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

Следуйте предыдущим инструкциям по установке библиотеки, но для библиотеки GFX. Сохраните файл библиотеки GFX как gfx.py. После этого вы сможете использовать функции библиотеки, импортировав её в свой код.

Итак, вот как рисовать фигуры. Сначала нужно подключить библиотеки ssd1306 и gfx, а также модули Pin и SoftI2C.

from machine import Pin, SoftI2C
import ssd1306
from time import sleep
import gfx

Затем определите выводы для ESP32:

i2c = SoftI2C(scl=Pin(22), sda=Pin(21))

Если вы используете ESP8266, используйте следующие выводы:

i2c = SoftI2C(scl=Pin(5), sda=Pin(4))

Мы используем OLED-дисплей 128x64. Если вы используете OLED-дисплей с другими размерами, измените следующие строки:

oled_width = 128
oled_height = 64

Создайте объект ssd1306 с именем oled:

oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c)

Затем нужно создать объект gfx для рисования фигур. В данном случае он называется graphics. Он принимает в качестве аргументов ширину и высоту области рисования. В нашем случае мы хотим рисовать на всём OLED, поэтому передаём ширину и высоту OLED. Также нужно передать функцию дисплея, которая рисует пиксели — в нашем случае это oled.pixel.

graphics = gfx.GFX(oled_width, oled_height, oled.pixel)

После этого вы можете использовать функции рисования, показанные далее, для отображения фигур.

Рисование линии

ESP32 ESP8266 OLED-дисплей — линия

Используйте метод line(x0, y0, x1, y1, color) объекта gfx для создания линии. Координаты (x0, y0) указывают начало линии, а координаты (x1, y1) — конец. Не забывайте вызывать oled.show() для фактического отображения фигур на OLED. Пример:

graphics.line(0, 0, 127, 20, 1)
oled.show()

Прямоугольник

ESP32 ESP8266 OLED-дисплей — прямоугольник

Для рисования прямоугольника используйте метод rect(x0, y0, width, height, color) объекта gfx. Координаты (x0, y0) указывают верхний левый угол прямоугольника. Затем нужно указать ширину, высоту и цвет. Пример:

graphics.rect(10, 10, 50, 30, 1)
oled.show()

Закрашенный прямоугольник

ESP32 ESP8266 OLED-дисплей — закрашенный прямоугольник

Используйте метод fill_rect(x0, y0, width, height, color) для рисования закрашенного прямоугольника. Этот метод принимает те же аргументы, что и rect().

graphics.fill_rect(10, 10, 50, 30, 1)
oled.show()

Круг

ESP32 ESP8266 OLED-дисплей — круг

Нарисуйте круг с помощью метода circle(x0, y0, radius, color). Координаты (x0, y0) указывают центр круга. Пример:

graphics.circle(64, 32, 10, 1)
oled.show()

Закрашенный круг

ESP32 ESP8266 OLED-дисплей — закрашенный круг

Нарисуйте закрашенный круг с помощью метода fill_circle(x0, y0, radius, color).

graphics.fill_circle(64, 32, 10, 1)
oled.show()

Треугольник

ESP32 ESP8266 OLED-дисплей — треугольник

Также есть метод для рисования треугольника: triangle(x0, y0, x1, y1, x2, y2, color). Этот метод принимает координаты каждой вершины и цвет.

graphics.triangle(10,10,55,20,5,40,1)
oled.show()

Закрашенный треугольник

ESP32 ESP8266 OLED-дисплей — закрашенный треугольник

Используйте метод fill_triangle(x0, y0, x1, y1, x2, y2, color) для рисования закрашенного треугольника.

graphics.fill_triangle(10,10,55,20,5,40,1)
oled.show()

Скрипт MicroPython — рисование фигур

Следующий скрипт реализует все методы рисования, показанные ранее.

# Полный проект: https://RandomNerdTutorials.com/micropython-ssd1306-oled-scroll-shapes-esp32-esp8266/

from machine import Pin, SoftI2C
import ssd1306
from time import sleep
import gfx

# Назначение выводов 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)

graphics = gfx.GFX(oled_width, oled_height, oled.pixel)

while True:

  graphics.line(0, 0, 127, 20, 1)
  oled.show()
  sleep(1)
  oled.fill(0)

  graphics.rect(10, 10, 50, 30, 1)
  oled.show()
  sleep(1)
  oled.fill(0)

  graphics.fill_rect(10, 10, 50, 30, 1)
  oled.show()
  sleep(1)
  oled.fill(0)

  graphics.circle(64, 32, 10, 1)
  oled.show()
  sleep(1)
  oled.fill(0)

  graphics.fill_circle(64, 32, 10, 1)
  oled.show()
  sleep(1)
  oled.fill(0)

  graphics.triangle(10,10,55,20,5,40,1)
  oled.show()
  sleep(1)
  oled.fill(0)

  graphics.fill_triangle(10,10,55,20,5,40,1)
  oled.show()
  sleep(1)
  oled.fill(0)

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

Заключение

В этом руководстве вы узнали, как использовать расширенные функции для прокрутки экрана OLED и рисования фигур с помощью MicroPython на ESP32 или ESP8266. Для рисования фигур необходимо импортировать библиотеку Adafruit GFX для MicroPython.

Надеемся, это руководство было полезным. Если вы впервые работаете с OLED-дисплеем на MicroPython, рекомендуем сначала ознакомиться с начальным руководством:

У нас есть похожие руководства, но с использованием Arduino IDE: