MicroPython:Платы/ESP32/Краткий справочник по ESP32

Материал из Онлайн справочника
Перейти к навигации Перейти к поиску

Перевод: Максим Кузьмин (Cubewriter) Контакты:</br>* Skype: cubewriter</br>* E-mail: cubewriter@gmail.com</br>* Максим Кузьмин на freelance.ru
Проверка/Оформление/Редактирование: Мякишев Е.А.


Краткий справочник по ESP32[1]

Esp32 esp-wroom-32 1.jpg

Плата разработчика Espressif ESP32 (источник изображения – Adafruit).

Ниже – краткий справочник по платам на базе чипа ESP32. Если вы раньше не работали с такими платами, возможно, для начала будет полезно ознакомиться с базовой информацией о порте MicroPython для ESP32:

Установка MicroPython

Ищите инструкции по установке в соответствующем разделе статьи «Подготовка к работе с MicroPython для ESP32». Там также есть подраздел о решении проблем с установкой MicroPython.

Общее управление платой

Работа MicroPython REPL осуществляется на порте UART0 (GPIO1=TX, GPIO3=RX) на скорости 115200 бод. Чтобы узнать, какие методы есть у объекта, используйте автозаполнение с помощью  Tab ⇆ . Чтобы вставлять в REPL большие куски Python-кода, используйте режим вставки ( Ctrl + E ).

Модуль machine:

import machine

machine.freq()          # прочесть текущую частоту CPU
machine.freq(240000000) # задать частоту CPU на 240 МГц

Модуль esp:

esp.osdebug(None)       # выключить отладочные сообщения ОС
esp.osdebug(0)          # перенаправить отладочные сообщения ОС
                        # на порт UART0

# низкоуровневые методы для взаимодействия с flash-памятью:
esp.flash_size()
esp.flash_user_start()
esp.flash_erase(sector_no)
esp.flash_write(byte_offset, buffer)
esp.flash_read(byte_offset, buffer)

Модуль esp32:

import esp32

esp32.hall_sensor()     # прочесть данные встроенного датчика Холла
esp32.raw_temperature() # прочесть внутреннюю температуру MCU
                        # (в Фаренгейтах)
esp32.ULP()             # получить доступ к ULP-сопроцессору
                        # (от англ. «ultra-low power», что значит
                        # «ультранизкое энергопотребление»)

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

Сеть

Модуль networking:

import network

wlan = network.WLAN(network.STA_IF) # создать интерфейс станции
wlan.active(True)       # активировать интерфейс
wlan.scan()             # сканировать точки доступа
wlan.isconnected()      # проверить, подключена ли станция
                        # к точке доступа
wlan.connect('essid', 'пароль') # подключиться к точке доступа 
wlan.config('mac-адрес')      # прочесть MAC-адрес устройства
wlan.ifconfig()         # прочесть IP-адрес, маску подсети,
                        # сетевой шлюз и DNS-сервер

ap = network.WLAN(network.AP_IF) # создать интерфейс точки доступа 
ap.config(essid='ESP-AP') # задать ESSID точки доступа
ap.config(max_clients=10) # задать количество клиентов,
                          # которые могут подключиться к сети
ap.active(True)         # активировать интерфейс

Полезная функция для подключения к локальной WiFi-сети:

def do_connect():
    import network
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    if not wlan.isconnected():
        print('подключение к сети...')
        wlan.connect('essid', 'пароль')
        while not wlan.isconnected():
            pass
    print('настройки сети:', wlan.ifconfig())

Когда связь будет установлена, можно воспользоваться модулем socket, чтобы как обычно создать и использовать сокеты TCP/UDP, а затем модулем urequests – для создания удобных HTTP-запросов.

Задержки и синхронизация

Используйте модуль time:

import time

time.sleep(1)           # включить режим сна на 1 секунду
time.sleep_ms(500)      # включить режим сна на 500 миллисекунд
time.sleep_us(10)       # включить режим сна на 10 микросекунд
start = time.ticks_ms() # прочесть показатель счетчика микросекунд
delta = time.ticks_diff(time.ticks_ms(), start) # вычислить разницу
                                                # во времени

Таймеры

В порте MicroPython для ESP32 поддерживаются виртуальные (RTOS) таймеры. Используйте класс machine.Timer с ID таймера «-1».

from machine import Timer

tim = Timer(-1)
tim.init(period=5000, mode=Timer.ONE_SHOT, callback=lambda t:print(1))
tim.init(period=2000, mode=Timer.PERIODIC, callback=lambda t:print(2))

Значение в period указывается в миллисекундах.

Контакты и GPIO

Используйте класс machine.Pin.

from machine import Pin

p0 = Pin(0, Pin.OUT)    # создать выходной контакт на GPIO0
p0.on()                 # задать контакту значение «вкл» 
p0.off()                # задать контакту значение «выкл»
p0.value(1)             # задать контакту значение «вкл» («1»)

p2 = Pin(2, Pin.IN)     # создать входной контакт на GPIO2
print(p2.value())       # прочесть значение («0» или «1»)

p4 = Pin(4, Pin.IN, Pin.PULL_UP) # включить встроенный
                                 # подтягивающий резистор
p5 = Pin(5, Pin.OUT, value=1) # создать выходной контакт
                              # со значением «вкл» («1»)

На ESP32 доступны следующие контакты (включительно): 0-19, 21-23, 25-27, 32-39. Эти номера соответствуют физическим номерам GPIO-контактов чипа ESP32. Имейте в виду, что у многих плат используется собственная нумерация контактов (например, D0, D1 и т.д.). О том, как эта логическая нумерация соответствует нумерации контактов самого ESP32-чипа, читайте в документации к своей плате. Примечания:

  • Контакты 1 и 3 – это, соответственно, TX- и RX-контакты UART-порта для REPL
  • Контакты 6, 7, 8, 11, 16 и 17 используются для подключения к встроенной flash-памяти, поэтому для других целей их использовать не рекомендуется
  • Контакты 34-39 можно использовать только в качестве входных контактов (поэтому у них нет встроенных подтягивающих резисторов)
  • В параметре для включения/выключения подтягивающего резистора также можно указать (помимо PULL_UP и PULL_DOWN) значение PULL_HOLD, что позволит снизить энергопотребление во время работы в режиме глубокого сна

ШИМ (широтно-импульсная модуляция)

ШИМ можно включить на всех контактах, которые можно использовать как выходные контакты. Базовая частота может варьироваться между 1 Гц и 40 МГц, но есть одно ограничение – при увеличении базовой частоты уменьшается разрешение коэффициента заполнения. Более подробно об этом читайте в этой статье. В данный момент диапазон значений коэффициента заполнения составляет 0-1023.

Используйте класс machine.PWM.

from machine import Pin, PWM

pwm0 = PWM(Pin(0))      # создать ШИМ-объект на контакте
pwm0.freq()             # прочесть текущую частоту
pwm0.freq(1000)         # задать частоту
pwm0.duty()             # прочесть текущий коэффициент заполнения
pwm0.duty(200)          # задать коэффициент заполнения
pwm0.deinit()           # выключить ШИМ на контакте

pwm2 = PWM(Pin(2), freq=20000, duty=512) # создать и настроить ШИМ
                                         # за один раз

АЦП (аналогово-цифровое преобразование)

На ESP32 аналогово-цифровое преобразование доступно на каналах 32-39. Помните, что при использовании настроек по умолчанию входное напряжение на АЦП-контакте должно быть в диапазоне между 0.0 и 1.0 вольтами (любое напряжение больше 1.0 вольта будет считываться как «4095»). Чтобы увеличить диапазон считываемого напряжения, нужно применить аттенюацию.

Используйте класс machine.ADC:

from machine import ADC

adc = ADC(Pin(32))          # создать АЦП-объект на АЦП-контакте 
adc.read()                  # прочесть значение в диапазоне 0-4095
                            # по напряжению между 0.0 и 1.0 вольтами

adc.atten(ADC.ATTN_11DB)    # включить входную аттенюацию на 11 дБ
                            # (диапазон сменится на 0.0-3.6 вольт) 
adc.width(ADC.WIDTH_9BIT)   # задать 9-битное разрешение
                            # возвращаемых значений (0-511) 
adc.read()                  # прочесть значение, используя
                            # новые аттенюацию и разрешение

Вот методы класса machine.ADC, отвечающие за аттенюацию и разрешение:

  • ADC.atten(attenuation). Этот метод позволяет включить аттенюацию на входе в АЦП-контакт, а следовательно – задать более широкий диапазон входящего напряжения, но ценой точности (потому что за этот более широкий диапазон отвечает прежнее количество битов). Вот возможные варианты для параметра attenuation:
    • ADC.ATTN_0DBаттенюация 0 дБ, делает максимальным порогом входящего напряжения 1.00 вольт (это настройка по умолчанию)
    • ADC.ATTN_2_5DB: аттенюация 2.5 дБ, делает максимальным порогом входящего напряжения примерно 1.34 вольт
    • ADC.ATTN_6DB: аттенюация 6 дБ, делает максимальным порогом входящего напряжения примерно 2.00 вольта
    • ADC.ATTN_11DB: аттенюация 11 дБ, делает максимальным порогом входящего напряжения примерно 3.6 вольт

Внимание: Хотя аттенюация в 11 дБ позволяет поднять диапазон считываемого напряжения до 3.6 вольт, помните, что максимально допустимое напряжение для входных контактов – это 3.6 вольт, и приближение к этому порогу может повредить схему.

  • ADC.width(width). Этот метод позволяет задать количество бит для данных, возвращаемых при считывании АЦП-информации. Возможные варианты:
    • ADC.WIDTH_9BIT: 9-битные данные
    • ADC.WIDTH_10BIT: 10-битные данные
    • ADC.WIDTH_11BIT: 11-битные данные
    • ADC.WIDTH_12BIT: 12-битные данные (это настройка по умолчанию)

Программная шина SPI

ESP32 оснащен двумя шинами SPI. Одна реализована программно (при помощи технологии «bit-banging»), работает на всех контактах, и доступ к ней можно получить через класс machine.SPI:

from machine import Pin, SPI

# конструируем шину SPI на заданных контактах;
# параметр «polarity» отвечает за то,
# находится ли линия SCK в состоянии простоя или нет;
# «phase=0» значит считывание на переднем фронте тактового сигнала,
# а «phase=1» - на заднем фронте.
spi = SPI(baudrate=100000, polarity=1, phase=0, sck=Pin(0), mosi=Pin(2), miso=Pin(4))

spi.init(baudrate=200000) # задаем скорость (в бодах)

spi.read(10)            # прочесть 10 байтов на MISO-линии
spi.read(10, 0xff)      # прочесть 10 байтов, попутно отправляя
                        # значение «0xff» на MOSI-линию

buf = bytearray(50)     # создать буфер
spi.readinto(buf)       # прочесть данные в заданный буфер
                        # (в данном случае – 50 байтов)
spi.readinto(buf, 0xff) # прочесть данные в заданный буфер 
                        # и отправить на MOSI-линию значение «0xff» 

spi.write(b'12345')     # записать 5 байтов на MOSI-линию

buf = bytearray(4)      # создать буфер
spi.write_readinto(b'1234', buf) # записать данные на MOSI-линию и
                                 # и сохранить данные
                                 # из MISO-линии в буфер
spi.write_readinto(buf, buf) # записать данные из буфера на MOSI-линию
                             # и сохранить данные из MISO-линии
                             # обратно в буфер

Внимание: В данный момент контакты для линий SCK, MOSI и MISO должны быть заданы при инициализации программной SPI-шины.

Аппаратная шина SPI

На ESP32 два аппаратных SPI-канала, скорость передачи данных на которых быстрее, чем у программной SPI-шины (до 80 МГц). Их можно задать на любых GPIO-контактах, поддерживающих нужное направление передачи данных и не используемых в других целях (см. раздел «Контакты и GPIO» выше), но если эти каналы заданы на «неродных» контактах (т.е. на контактах, по умолчанию предназначенных для этих аппаратных SPI-шин), будет задействовано GPIO-мультиплексирование, что может негативно сказаться на стабильности их работы при высокой скорости передачи данных. При использовании аппаратных SPI-шин на «неродных» контактах скорость передачи данных будет ограничена до 40 МГц. Вот таблица с «родными» контактами:

HSPI (id=1) VSPI (id=2)
SCK 14 18
MOSI 13 23
MISO 12 19

Для аппаратных SPI-шин используются те же методы, что и для программной SPI-шины (см. выше).

from machine import Pin, SPI

hspi = SPI(1, 10000000, sck=Pin(14), mosi=Pin(13), miso=Pin(12))
vspi = SPI(2, baudrate=80000000, polarity=0, phase=0, bits=8, firstbit=0, sck=Pin(18), mosi=Pin(23), miso=Pin(19))

Шина I2C

Шина I2C реализована и программно, и аппаратно. Две ее аппаратные шины имеют идентификаторы 0 и 1. Для линий SCL и SDA можно использовать любые контакты, которые можно использовать как выходные. Доступ к шине I2C осуществляется через класс machine.I2C:

from machine import Pin, I2C

# сконструировать программную I2C-шину:
i2c = I2C(scl=Pin(5), sda=Pin(4), freq=100000)

# сконструировать аппаратную I2C-шину:
i2c = I2C(0)
i2c = I2C(1, scl=Pin(5), sda=Pin(4), freq=400000)

i2c.scan()              # поискать ведомые устройства

i2c.readfrom(0x3a, 4)   # прочесть 4 байта с ведомого устройства
                        # с адресом «0x3a»
i2c.writeto(0x3a, '12') # записать «12» на ведомое устройство
                        # с адресом «0x3a»

buf = bytearray(10)     # создать буфер с 10 байтами
i2c.writeto(0x3a, buf)  # записать данные из заданного буфера
                        # на ведомое устройство

Часы реального времени (RTC)

См. класс machine.RTC.

from machine import RTC

rtc = RTC()
rtc.datetime((2017, 8, 23, 1, 12, 48, 0, 0)) # задать дату и время
rtc.datetime() # прочесть дату и время

Режим глубокого сна

Код ниже можно использовать для входа в режим глубокого сна, пробуждения и проверки того, был ли причиной включения устройства выход из режима глубокого сна.

import machine

# проверить, является ли причиной включения устройства
# выход из режима глубокого сна:
if machine.reset_cause() == machine.DEEPSLEEP_RESET:
    print('выход из режима глубокого сна')

# перевести устройство в режим сна на 10 секунд:
machine.deepsleep(10000)

Примечания:

  • Вызов deepsleep() без аргумента переведет устройство в бесконечный режим сна
  • Программный сброс не повлечет изменение причины сброса
  • Через включенные встроенные подтягивающие резисторы может происходить утечка тока. Чтобы снизить энергопотребление, встроенные подтягивающие резисторы можно выключить:
p1 = Pin(4, Pin.IN, Pin.PULL_HOLD)

После выхода из режима глубокого сна вам, возможно, нужно будет явно отключить настройку PULL_HOLD (например, если это выходной контакт) при помощи:

p1 = Pin(4, Pin.OUT, None)

RMT

RMT – это особая функция, позволяющая генерировать точные цифровые импульсы с разрешением 12.5 нс. Более подробно читайте в статье о классе esp32.RMT.

Используется она следующим образом:

import esp32
from machine import Pin

r = esp32.RMT(0, pin=Pin(18), clock_div=8)
r   # RMT(channel=0, pin=18, source_freq=80000000, clock_div=8)
# разрешение канала – 100 нс
# (1/(номинальная частота/делитель частоты))
r.write_pulses((1, 20, 2, 40), start=0) # это отправит «0101»,
                                        # где 0 – это 100 нс,
                                        # 1 – это 2000 нс,
                                        # 0 – это 200 нс,
                                        # 1 – это 4000 нс

Шина OneWire

Шина OneWire реализована программно и работает на всех контактах:

from machine import Pin
import onewire

ow = onewire.OneWire(Pin(12)) # создать шину OneWire на GPIO12
ow.scan()               # вернуть список устройств на шине
ow.reset()              # сбросить шину
ow.readbyte()           # прочесть байт
ow.writebyte(0x12)      # записать байт на шину
ow.write('123')         # записать байты на шину
ow.select_rom(b'12345678') # выбрать устройство по его ROM-коду

Вот специальный драйвер для устройств DS18S20 и DS18B20:

import time, ds18x20
ds = ds18x20.DS18X20(ow)
roms = ds.scan()
ds.convert_temp()
time.sleep_ms(750)
for rom in roms:
    print(ds.read_temp(rom))

Не забудьте включить подтягивающий резистор 4.7 кОм на линии передачи данных. Также помните, что вам нужно будет вызывать метод convert_temp() при каждом считывании температуры.

Драйвер для NeoPixel

Используйте модуль neopixel:

from machine import Pin
from neopixel import NeoPixel

pin = Pin(0, Pin.OUT)   # сделать GPIO0 выходным контактом для 
                        # управления устройствами NeoPixel
np = NeoPixel(pin, 8)   # создать драйвер NeoPixel на GPIO0
                        # для 8 пикселей
np[0] = (255, 255, 255) # сделать первый пиксель белым
np.write()              # записать данные на все пиксели
r, g, b = np[0]         # прочесть цвет первого пикселя

Для низкоуровневого управления устройствами NeoPixel:

import esp
esp.neopixel_write(pin, grb_buf, is800khz)

Внимание: По умолчанию NeoPixel настроен на управление устройствами, работающими на популярной частоте 800 КГц. Но вы можете задать и другую настройку для управления другими (обычно на 400 КГц) устройствами, указав timing=0 при конструировании объекта NeoPixel.

Определение касаний

Используйте класс Touchpad в модуле machine:

from machine import TouchPad, Pin

t = TouchPad(Pin(14))
t.read()              # при регистрации касания
                      # возвращает значение меньше, 
                      # чем если бы касания не было

Данные, возвращаемые методом TouchPad.read() – это данные об изменении электрической емкости. Маленькие числа (обычно не более 100), скорее всего, говорят о том, что до контакта дотронулись, а большие (больше тысячи) – о том, что нет. Но это относительные значения, и они могут сильно варьироваться в зависимости от платы и окружающей обстановки, поэтому советуем сделать некоторую калибровку.

На ESP32 есть десять контактов, поддерживающих определение касаний с помощью электрической емкости: 0, 2, 4, 12, 13, 14, 15, 27, 32, 33. Если попробовать измерить этот показатель на других контактах, вам выдаст ошибку ValueError.

Обратите внимание, что определение касаний также можно использовать для того, чтобы вывести ESP32 из режима глубокого сна:

import machine
from machine import TouchPad, Pin
import esp32

t = TouchPad(Pin(14))
t.config(500)               # настроить порог, после которого 
                            # будет регистрироваться касание
esp32.wake_on_touch(True)
machine.lightsleep()        # перевести MCU в режим сна до тех пор,
                            # пока кто-то не коснется контакта

Более подробно о сенсорных контактах читайте тут.

Драйвер для DHT-датчиков

Драйвер для DHT-датчиков реализован программно и работает на всех контактах:

import dht
import machine

d = dht.DHT11(machine.Pin(4))
d.measure()
d.temperature() # например, 23 (градуса Цельсия)
d.humidity()    # например, 41 (% относительной влажности)

d = dht.DHT22(machine.Pin(4))
d.measure()
d.temperature() # например, 23.6 (градуса Цельсия)
d.humidity()    # например, 41.3 (% относительной влажности)

WebREPL (интерактивная браузерная командная строка)

WebREPL (REPL, реализованная через WebSockets и доступная через браузер) – это экспериментальная функция порта MicroPython для ESP32. Ее веб-клиент можно загрузить отсюда (или можно воспользоваться онлайн-версией на сайте MicroPython), а затем настроить его при помощи:

import webrepl_setup

После этого следуйте инструкциям на экране. Подключиться к WebREPL можно будет после перезагрузки. Если вы отключили автоматический запуск WebREPL при загрузке, вы всегда можете включить демон WebREPL самостоятельно при помощи:

import webrepl
webrepl.start()

# или, если требуется пароль:
webrepl.start(password='мойпароль')

Демон WebREPL прослушивает все активные интерфейсы, будь то STA (интерфейс станции) или AP (интерфейс точки доступа). Это позволяет вам подключиться к ESP32 через роутер (STA) или напрямую, если ESP32 настроен как точка доступа (AP).

Вдобавок к функционалу командной строки (терминала) WebREPL также можно использовать для загрузки (и отправки, и получения) данных. Кнопки для соответствующих функций есть в веб-клиенте WebREPL, или же вы можете воспользоваться клиентом командной строки «webrepl_cli.py» из репозитория выше.

Но есть и другие способы получения/отправки файлов на ESP32-плату, разработанные и поддерживаемые сообществом MicroPython, ищите их на форуме проекта MicroPython.

См.также

Ссылки на полезные ресурсы

Внешние ссылки