MicroPython: Прерывания (Interrupts) с ESP32 и ESP8266
Узнайте, как настроить и обработать прерывания (interrupts) с использованием прошивки MicroPython на платах ESP32 и ESP8266. Также мы создадим пример проекта с PIR-датчиком движения.
Необходимые условия
Для прохождения этого руководства вам потребуется прошивка MicroPython, установленная на ESP32 или ESP8266. Также вам нужна IDE для написания и загрузки кода на плату. Мы рекомендуем использовать Thonny IDE или uPyCraft IDE:
Thonny IDE:
uPyCraft IDE:
Введение в прерывания
Прерывания полезны для того, чтобы определённые действия происходили автоматически в программах микроконтроллеров, и могут помочь решить проблемы с таймингом. С помощью прерываний вам не нужно постоянно проверять текущее значение пина. Когда обнаруживается изменение, срабатывает событие (вызывается функция).
Когда происходит прерывание, процессор приостанавливает выполнение основной программы для выполнения задачи, а затем возвращается к основной программе, как показано на рисунке ниже.
Это особенно полезно для реагирования на обнаружение движения или нажатие кнопки без необходимости постоянно проверять их состояние.
Пины прерываний ESP32: вы можете использовать все GPIO как прерывания, за исключением GPIO 6 – GPIO 11.
Пины прерываний ESP8266: вы можете использовать все GPIO, за исключением GPIO 16.
Настройка прерывания в MicroPython
Для настройки прерывания в MicroPython необходимо выполнить следующие шаги:
1. Определите функцию обработки прерывания. Функция обработки прерывания должна быть максимально простой, чтобы процессор быстро вернулся к выполнению основной программы. Лучший подход — сигнализировать основному коду о том, что произошло прерывание, используя глобальную переменную. Функция обработки прерывания должна принимать параметр типа Pin. Этот параметр передаётся в функцию обратного вызова и ссылается на GPIO, вызвавший прерывание.
def handle_interrupt(pin):
2. Настройте GPIO, который будет действовать как пин прерывания, как вход. Например:
pir = Pin(14, Pin.IN)
3. Привяжите прерывание к этому пину, вызвав метод irq():
pir.irq(trigger=Pin.IRQ_RISING, handler=handle_interrupt)
Метод irq() принимает следующие аргументы:
trigger: определяет режим срабатывания. Существует 3 различных условия:
Pin.IRQ_FALLING: срабатывание прерывания при переходе пина из HIGH в LOW;
Pin.IRQ_RISING: срабатывание прерывания при переходе пина из LOW в HIGH;
3: срабатывание прерывания по обоим фронтам (то есть при любом изменении).
handler: это функция, которая будет вызвана при обнаружении прерывания, в данном случае функция
handle_interrupt().
Пример проекта с PIR-датчиком движения
Для демонстрации работы с прерываниями мы создадим простой проект с PIR-датчиком движения. При обнаружении движения светодиод будет гореть 20 секунд.
Необходимые компоненты
Список компонентов для сборки схемы:
ESP32 или ESP8266
Светодиод 5 мм
Резистор 330 Ом
Мини PIR-датчик движения (AM312) или PIR-датчик движения (HC-SR501)
Макетная плата
Соединительные провода
Схема подключения – ESP32
Используйте следующую схему подключения, если вы работаете с платой ESP32:
Схема подключения – ESP8266
Используйте следующую схему подключения, если вы работаете с платой ESP8266:
Важно: мини PIR-датчик движения AM312, используемый в этом проекте, работает от 3.3 В. Однако, если вы используете другой PIR-датчик, например HC-SR501, он работает от 5 В. Вы можете либо модифицировать его для работы от 3.3 В, либо просто запитать его через пин Vin.
На рисунке ниже показана распиновка мини PIR-датчика движения AM312. Если вы используете другой датчик движения, пожалуйста, проверьте его распиновку перед сборкой схемы.
Код
Вот скрипт, который обнаруживает движение и включает светодиод при каждом обнаружении движения. Этот код совместим как с ESP32, так и с ESP8266.
# Полная информация о проекте на https://RandomNerdTutorials.com
from machine import Pin
from time import sleep
motion = False
def handle_interrupt(pin):
global motion
motion = True
global interrupt_pin
interrupt_pin = pin
led = Pin(12, Pin.OUT)
pir = Pin(14, Pin.IN)
pir.irq(trigger=Pin.IRQ_RISING, handler=handle_interrupt)
while True:
if motion:
print('Motion detected! Interrupt caused by:', interrupt_pin)
led.value(1)
sleep(20)
led.value(0)
print('Motion stopped!')
motion = False
Как работает код
Для использования прерываний импортируйте класс Pin из модуля machine. Также импортируем метод sleep из модуля time для добавления задержки в скрипт.
from machine import Pin
from time import sleep
Создайте переменную motion, которая может быть True или False. Эта переменная будет указывать, было ли обнаружено движение (это глобальная переменная, которая будет изменяться в функции обработки прерывания).
motion = False
Затем создайте функцию handle_interrupt.
def handle_interrupt(pin):
global motion
motion = True
global interrupt_pin
interrupt_pin = pin
Эта функция будет вызываться каждый раз при обнаружении движения. Функция handle_interrupt принимает входной параметр (pin), в который будет передан объект класса Pin при возникновении прерывания (он указывает, какой пин вызвал прерывание).
Здесь мы сохраняем пин, вызвавший прерывание, в переменной interrupt_pin. В данном случае это не очень полезно, так как у нас только один пин прерывания. Однако это может быть полезно, если у нас несколько прерываний, вызывающих одну и ту же функцию обработки, и мы хотим знать, какой GPIO вызвал прерывание.
В нашем примере функция handle_interrupt просто изменяет переменную motion на True и сохраняет пин прерывания. Функции обработки прерываний должны быть максимально короткими, и следует избегать использования функции print() внутри них. Затем основной код должен содержать все действия, которые мы хотим выполнить при возникновении прерывания.
Примечание
Поскольку вы хотите, чтобы переменная motion была доступна как внутри функции, так и во всём коде, её необходимо объявить как global. В противном случае при обнаружении движения ничего не произойдёт, потому что переменная motion будет изменяться внутри функции, а не в основном теле кода.
Далее в коде нам нужно создать два объекта Pin. Один для светодиода на GPIO 12, а другой для PIR-датчика движения на GPIO 14.
led = Pin(12, Pin.OUT)
pir = Pin(14, Pin.IN)
Затем установите прерывание на pir, вызвав метод irq().
pir.irq(trigger=Pin.IRQ_RISING, handler=handle_interrupt)
В цикле, когда переменная motion равна True, мы включаем светодиод на 20 секунд и выводим сообщение о том, что обнаружено движение и какой пин вызвал прерывание.
if motion:
print('Motion detected! Interrupt caused by:', interrupt_pin)
led.value(1)
sleep(20)
Через 20 секунд выключаем светодиод и выводим сообщение о том, что движение прекратилось.
led.value(0)
print('Motion stopped!')
Наконец, устанавливаем переменную motion в False:
motion = False
Переменная motion может снова стать True только при обнаружении движения и вызове функции handle_interrupt.
Для простоты в этом примере мы используем задержку, чтобы светодиод горел 20 секунд. В идеале следует использовать таймеры.
Демонстрация
Загрузите код на плату ESP32/ESP8266. Светодиод должен загореться на 20 секунд при обнаружении движения, а в Shell должно появиться сообщение.
Через 20 секунд светодиод выключается.
Примечание
PIR-датчик движения AM312 имеет время задержки по умолчанию 8 секунд. Это означает, что он не сработает снова, пока не пройдёт 8 секунд с момента последнего срабатывания.
Заключение
Мы надеемся, что эта статья была для вас полезной. Мы узнали, как:
настроить пин как прерывание;
обработать это прерывание в коде;
определить, какой GPIO-пин вызвал прерывание.
В нашем примере мы использовали PIR-датчик движения для запуска прерывания. Но представленный пример также можно использовать для обнаружения нажатия кнопки, например.
Если вам нравится программировать платы ESP32 и ESP8266 с помощью MicroPython и вы хотите узнать больше, обратите внимание на следующие ресурсы: