ESP8266:Примеры/Взаимодействие с GPIO-контактами ESP8266 при помощи прошивки NodeMCU
Черновик |
Взаимодействие с GPIO-контактами ESP8266 при помощи прошивки NodeMCU
Аббревиатура «GPIO» означает «general purpose input/output», т.е. «Интерфейс ввода/вывода общего назначения». Эта фраза описывает то, что могут делать контакты, работающие в этом режиме: быть входными или выходными контактами во множестве различных ситуаций.
В этой статье мы познакомимся с API NodeMCU, отвечающим за работу с GPIO-контактами. Более подробно о нем можно узнать в его официальной документации.
В частности, ниже мы узнаем, как задать разные режимы для работы GPIO-контактов. Это будет проиллюстрировано фрагментами-примерами, применимыми для работы с ESP8266, так что можете проверить их и поэкспериментировать с ними в своих проектах!
Справочная информация
Основы программирования на Lua
Это скриптовый язык программирования, написанный на C. Разработка этого проекта началась в 1993 году силами Роберту Иерузалимски, Луиша Энрике де Фигейреду и Валдемара Келе, работавшими тогда в подразделении Tecgraf при Католическом университете Рио-де-Жанейро.
Более подробно об этом языке программирования можно почитать в «Википедии».
Прошивка NodeMCU для ESP8266 основана на Lua, поэтому если вы хотите писать собственные скрипты для ESP8266, вам важно знать основы этого языка.
Переменные
Переменные в языке Lua не делятся по типу данных, но делятся по области видимости. Это значит, что переменные в Lua могут быть глобальными или локальными.
- Глобальные переменные. Все переменные по умолчанию считаются глобальными (если в коде специально не задано, что они локальные).
pin = 3 test = "It works!"
- Локальные переменные. Если переменная задана локальной, область ее видимости будет ограничена лишь ее функцией.
local pin = 3 local test = "It works!"
- Поля таблиц. Это особый тип переменных, в которых может храниться все, за исключением «nil» (мы это затрагивать не будем).
Примечание: Lua – это язык, чувствительный к регистру. Поэтому переменная «PIN» – это не то же самое, что «Pin» или «pin».
Типы данных (типы значений)
Lua – это язык с динамической типизацией, поэтому у переменных нет типов данных. Типы данных есть только у значений. Значения могут храниться в переменных, передаваться в виде параметров и возвращаться в виде результатов.
В таблице ниже показаны типы данных для значений в языке Lua.
Тип значения | Описание |
---|---|
Строка (string) | Массив символов |
Число (number) | Действительное (двойной точности с плавающей запятой) число |
Булево значение (boolean) | Значение «true» или «false». Как правило, используется для проверки выполнения условий |
Функция (function) | Функция, написанная на Lua |
Неопределенное значение (nil) | В такой переменной не хранится никаких данных |
Таблица (table), пользовательские данные (userdata) и поток (thread) | Эти три типа данных мы затрагивать не будем |
Вот примеры некоторых из этих типов значений:
print(type("Hello world!")) –- строка
print(type(7)) -- число
print(type(true)) -- булево значение
print(type(print)) -- функция
print(type(nil)) -- неопределенное значение
Примечание: Работая с ESP8266 и прошивкой NodeMCU, вы изредка будете встречать тип данных «nil». Это будет значить лишь, что такая переменная не определена. Также, если вы хотите удалить какое-то значение в какой-то переменной, просто присвойте ей значение «nil».
Комментарии
Комментарии – это просто текст, в котором программист объясняет, как работает его код. Если какой-то фрагмент кода помечен как комментарий, то ESP-модуль проигнорирует его и не будет обрабатывать. Комментарии начинаются с двух тире («--»). Вот два типа комментариев:
- Однострочные комментарии:
print("Hello World!") –- комментарий в одну строчку
- Многострочные комментарии:
--[[ print("Hello World!") – это многострочный комментарий --]]
Операторы
Оператор – это символ, который говорит интерпретатору выполнить определенное математическое или логическое действие. В язык Lua встроено много операторов разных типов:
- Арифметические операторы;
- Операторы сравнения;
- Логические операторы;
- Прочие операторы;
Читая таблицы и примеры ниже, представьте, что имеете дело с двумя переменными: «А», в которой хранится значение «1», и «B», в которой хранится значение «2».
A = 1
B = 2
Арифметические операторы
Оператор | Пример | Результат |
---|---|---|
+ | A + B | 3 |
- | A - B | -1 |
* | A * B | 2 |
/ | B / A | 2 |
% | B % A | 0 |
^ | B^2 | 4 |
- | -A | -1 |
Операторы сравнения
Оператор | Пример | Результат |
---|---|---|
== | (A == B) | false |
~= | (A ~= B) | true |
> | (A > B) | false |
< | (A < B) | true |
>= | (A >= B) | false |
<= | (A <= B) | true |
Логические операторы
Оператор | Пример | Результат |
---|---|---|
and (и) | (A and B) | false |
or (или) | (A or B) | true |
not (не) | !(A and B) | true |
Оператор конкатенации
Теперь представьте, что у нас две новые переменные:
a = "Hello!"
b = "World!"
Оператор | Пример | Результат |
---|---|---|
.. | a..b | "Hello World!" |
Циклы
Цикл позволяет выполнить блок кода несколько раз, пока выполняется заданное условие. Во фрагменте кода ниже – пока значением в переменной «boolean_value» является «true».
-- цикл while
while boolean_value
do
-- код будет выполняться, пока в «boolean_value» будет «true»
end
-- и цикл for
for min, max, increment
do
-- код будет выполняться, пока не будет достигнуто макс. значение
end
Операторы if… else
Операторы if... else (т.е. «если... иначе») – один из самых важных инструментов для управления программой. Они используются следующим образом:
if boolean_value then
-- если в переменной «boolean_value» значение «true»
else
-- если в переменной «boolean_value» значение «false»
end
Названия этих операторов говорят сами за себя. Если выполнено условие «boolean_value=true», то программа выполнит код, идущий после «if». Но если условием является «boolean_value=false», то программа выполнит код, идущий после «else».
Функции
Функции – отличный способ организации кода. Если вы хотите сделать в программе что-либо несколько раз, вам необязательно по несколько раз прописывать этот код. Можно просто создать отдельную функцию, содержащую этот код, а затем вызывать ее, когда вам нужно.
Ниже показано, как создать новую функцию, принимающую один параметр (температуру в градусах Кельвина) и преобразующую это значение в градусы Цельсия и Фаренгейта.
function displayTemperature(kelvin)
celsius = kelvin – 273.15
print("Temperature in Celsius is: ", celsius)
fahrenheit = (celsius*9/5+32)
print("Temperature in Fahrenheit is: ", fahrenheit)
end
k = 294 –- температура в градусах Кельвина
displayTemperature(k) –- эта строчка вызывает функцию, созданную выше
Режим контакта
Чтобы воспользоваться GPIO-контактом, нам нужно задать режим его работы. Вы можете задать для него один из трех доступных режимов (контакт не может одновременно работать в нескольких режимах).
Режим | В коде | Описание |
---|---|---|
OUTPUT (вывод данных) | gpio.OUTPUT | Значение контакта можно задать как «LOW» или «HIGH» |
INPUT (ввод данных) | gpio.INPUT | Мы будем считывать текущее состояние контакта |
INTERRUPT (прерывание) | gpio.INT | Аналогично «INPUT», но ESP8266 также постоянно проверяет, изменилось ли состояние контакта. Если изменилось, ESP8266 выполнит заданную функцию |
Как обращаться к контактам
В таблице ниже показано, как номер контакта ESP8266 в Lua-коде соотносится с его GPIO-номером. У платы ESP-01 только два контакта: GPIO0 и GPIO2.
Номер контакта в Lua-коде | GPIO-номер контакта |
---|---|
0 [*] | GPIO16 |
1 | GPIO5 |
2 | GPIO4 |
3 | GPIO0 |
4 | GPIO2 |
5 | GPIO14 |
6 | GPIO12 |
7 | GPIO13 |
8 | GPIO15 |
9 | GPIO3 |
10 | GPIO1 |
11 | GPIO9 |
12 | GPIO10 |
Режим вывода данных (OUTPUT)
Если вы хотите переключить GPIO-контакт в состояние «HIGH» (3.3В) или «LOW» (0В), то воспользуйтесь для этого методом gpio.write(). С его помощью можно, к примеру, включить и выключить светодиод.
Во фрагменте ниже показано, как переключить контакт GPIO2 в состояние «HIGH» (3.3В):
pin = 4
gpio.mode(pin, gpio.OUTPUT)
gpio.write(pin, gpio.HIGH)
А вот так контакт GPIO2 переключается в состояние «LOW» (0В):
pin = 4
gpio.mode(pin, gpio.OUTPUT)
gpio.write(pin, gpio.LOW)
Режим ввода данных (INPUT)
Если вы хотите прочесть текущее состояние GPIO-контакта, воспользуйтесь методом gpio.read(). Во фрагменте ниже показано, как проверить, нажата ли кнопка, подключенная к контакту GPIO2.
pin = 4
gpio.mode(pin, gpio.INPUT)
print(gpio.read(pin))
Если «print(gpio.read(pin))=1», то это значит, что кнопка нажата, а если «print(gpio.read(pin))=0», то это значит, что кнопка отпущена (т.е. не нажата).
Режим прерывания (INTERRUPT)
Если вам нужно обнаружить изменение значения GPIO-контакта (например, если оно сменилось с «HIGH» на «LOW» или наоборот), воспользуйтесь методом gpio.trig(). Кроме того, одним из параметров gpio.trig() служит функция, запускаемая при изменении значения.
Представьте, что у вас есть датчик движения, и вам нужно, чтобы при обнаружении движения ESP8266 запускала функцию, выполняющую какое-либо действие (например, отправляющую письмо).
Ниже – фрагмент для создания прерывания:
pin = 4
gpio.mode(pin, gpio.INT)
function onChange ()
print(̍Motion Detected̍)
end
gpio.trig(pin, ̍up̍, onChange)
Вторым параметром метода gpio.trig() служит тип события, вызывающего прерывание. Всего их 5:
Название события | Что происходит при этом событии |
---|---|
up | Значение контакта сменяется на «HIGH» |
down | Значение контакта сменяется на «LOW» |
both | Значение контакта сменяется с «HIGH» на «LOW» или с «LOW» на «HIGH» |
low | Пока контакт находится в состоянии «LOW» |
high | Пока контакт находится в состоянии «HIGH» |