ESP8266:Примеры/Веб-сервер на базе ESP8266 при помощи NodeMCU: различия между версиями

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску
Нет описания правки
 
(не показаны 4 промежуточные версии 2 участников)
Строка 2: Строка 2:
{{Перевод от Сubewriter}}
{{Перевод от Сubewriter}}
{{Myagkij-редактор}}
{{Myagkij-редактор}}
{{Черновик}}


=Веб-сервер на базе ESP8266 при помощи NodeMCU=
=Веб-сервер на базе ESP8266 при помощи NodeMCU=
Строка 35: Строка 33:
* '''Глобальные переменные.''' Все переменные по умолчанию считаются глобальными (если в коде специально не задано, что они локальные).
* '''Глобальные переменные.''' Все переменные по умолчанию считаются глобальными (если в коде специально не задано, что они локальные).


::<syntaxhighlight lang="lua" enclose="div">
::<syntaxhighlight lang="lua">
pin = 3
pin = 3
test = "It works!"
test = "It works!"
Строка 42: Строка 40:
* '''Локальные переменные.''' Если переменная задана локальной, область ее видимости будет ограничена лишь ее функцией.
* '''Локальные переменные.''' Если переменная задана локальной, область ее видимости будет ограничена лишь ее функцией.


::<syntaxhighlight lang="lua" enclose="div">
::<syntaxhighlight lang="lua">
local pin = 3
local pin = 3
local test = "It works!"
local test = "It works!"
Строка 49: Строка 47:
* '''Поля таблиц.''' Это особый тип переменных, в которых может храниться все, за исключением '''«nil»''' (мы это затрагивать не будем).
* '''Поля таблиц.''' Это особый тип переменных, в которых может храниться все, за исключением '''«nil»''' (мы это затрагивать не будем).


'''Примечание:''' [[Lua]] – это язык, чувствительный к регистру. Поэтому переменная '''«PIN»''' – это не то же самое, что '''«Pin»''' или '''«pin»'''.
{{Примечание1|1=[[Lua]] – это язык, чувствительный к регистру. Поэтому переменная '''«PIN»''' – это не то же самое, что '''«Pin»''' или '''«pin»'''.}}


==== Типы данных (типы значений) ====
==== Типы данных (типы значений) ====
Строка 57: Строка 55:
В таблице ниже показаны типы данных для значений в языке [[Lua]].
В таблице ниже показаны типы данных для значений в языке [[Lua]].


{| class="wikitable" style="margin:0 auto;"
{| class="wikitable" style="margin:0 auto" style="margin:0 auto;"
|-
|-
! Тип значения !! Описание
! Тип значения !! Описание
Строка 76: Строка 74:
Вот примеры некоторых из этих типов значений:
Вот примеры некоторых из этих типов значений:


<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
print(type("Hello world!")) –- строка
print(type("Hello world!")) –- строка
print(type(7))              -- число
print(type(7))              -- число
Строка 84: Строка 82:
</syntaxhighlight>
</syntaxhighlight>


'''Примечание:''' Работая с [[ESP8266]] и прошивкой [[NodeMCU]], вы изредка будете встречать тип данных '''«nil»'''. Это будет значить лишь, что такая переменная не определена. Также, если вы хотите удалить какое-то значение в какой-то переменной, просто присвойте ей значение '''«nil»'''.
{{Примечание1
|1=Работая с [[ESP8266]] и прошивкой [[NodeMCU]], вы изредка будете встречать тип данных '''«nil»'''. Это будет значить лишь, что такая переменная не определена. Также, если вы хотите удалить какое-то значение в какой-то переменной, просто присвойте ей значение '''«nil»'''.
}}


==== Комментарии ====
==== Комментарии ====
Строка 92: Строка 92:
* Однострочные комментарии:  
* Однострочные комментарии:  


::<syntaxhighlight lang="lua" enclose="div">
::<syntaxhighlight lang="lua">
print("Hello World!") –- комментарий в одну строчку
print("Hello World!") –- комментарий в одну строчку
</syntaxhighlight>
</syntaxhighlight>
Строка 98: Строка 98:
* Многострочные комментарии:
* Многострочные комментарии:


::<syntaxhighlight lang="lua" enclose="div">
::<syntaxhighlight lang="lua">
--[[
--[[
print("Hello World!") – это многострочный комментарий
print("Hello World!") – это многострочный комментарий
Строка 114: Строка 114:
Читая таблицы и примеры ниже, представьте, что имеете дело с двумя переменными: '''«А»''', в которой хранится значение '''«1»''', и '''«B»''', в которой хранится значение '''«2»'''.
Читая таблицы и примеры ниже, представьте, что имеете дело с двумя переменными: '''«А»''', в которой хранится значение '''«1»''', и '''«B»''', в которой хранится значение '''«2»'''.


<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
A = 1
A = 1
B = 2
B = 2
Строка 121: Строка 121:
===== Арифметические операторы =====
===== Арифметические операторы =====


{| class="wikitable" style="margin:0 auto;"
{| class="wikitable" style="margin:0 auto" style="margin:0 auto;"
|-
|-
! Оператор !! Пример !! Результат
! Оператор !! Пример !! Результат
Строка 142: Строка 142:
===== Операторы сравнения =====
===== Операторы сравнения =====


{| class="wikitable" style="margin:0 auto;"
{| class="wikitable" style="margin:0 auto" style="margin:0 auto;"
|-
|-
! Оператор !! Пример !! Результат
! Оператор !! Пример !! Результат
Строка 161: Строка 161:
===== Логические операторы =====
===== Логические операторы =====


{| class="wikitable" style="margin:0 auto;"
{| class="wikitable" style="margin:0 auto" style="margin:0 auto;"
|-
|-
! Оператор !! Пример !! Результат
! Оператор !! Пример !! Результат
Строка 176: Строка 176:
Теперь представьте, что у нас две новые переменные:
Теперь представьте, что у нас две новые переменные:


<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
a = "Hello!"
a = "Hello!"
b = "World!"
b = "World!"
</syntaxhighlight>
</syntaxhighlight>


{| class="wikitable" style="margin:0 auto;"
{| class="wikitable" style="margin:0 auto" style="margin:0 auto;"
|-
|-
! Оператор !! Пример !! Результат
! Оператор !! Пример !! Результат
Строка 192: Строка 192:
Цикл позволяет выполнить блок кода несколько раз, пока выполняется заданное условие. Во фрагменте кода ниже – пока значением в переменной '''«boolean_value»''' является '''«true»'''.
Цикл позволяет выполнить блок кода несколько раз, пока выполняется заданное условие. Во фрагменте кода ниже – пока значением в переменной '''«boolean_value»''' является '''«true»'''.


<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
-- цикл while
-- цикл while
while boolean_value
while boolean_value
Строка 210: Строка 210:
Операторы '''if... else''' (т.е. «если... иначе») – один из самых важных инструментов для управления программой. Они используются следующим образом:
Операторы '''if... else''' (т.е. «если... иначе») – один из самых важных инструментов для управления программой. Они используются следующим образом:


<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
if boolean_value then
if boolean_value then
     -- если в переменной «boolean_value» значение «true»
     -- если в переменной «boolean_value» значение «true»
Строка 226: Строка 226:
Ниже показано, как создать новую функцию, принимающую один параметр (температуру в градусах Кельвина) и преобразующую это значение в градусы Цельсия и Фаренгейта.
Ниже показано, как создать новую функцию, принимающую один параметр (температуру в градусах Кельвина) и преобразующую это значение в градусы Цельсия и Фаренгейта.


<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
function displayTemperature(kelvin)
function displayTemperature(kelvin)
   celsius = kelvin – 273.15
   celsius = kelvin – 273.15
Строка 244: Строка 244:
В таблице ниже показано, как номер контакта [[ESP8266]] в [[Lua-код]]е соотносится с его GPIO-номером. У платы [[ESP-01]] только два контакта: GPIO0 и GPIO2.
В таблице ниже показано, как номер контакта [[ESP8266]] в [[Lua-код]]е соотносится с его GPIO-номером. У платы [[ESP-01]] только два контакта: GPIO0 и GPIO2.


{| class="wikitable" style="margin:0 auto;"
{| class="wikitable" style="margin:0 auto" style="margin:0 auto;"
|-
|-
! Номер контакта в Lua-коде !! GPIO-номер контакта
! Номер контакта в Lua-коде !! GPIO-номер контакта
Строка 277: Строка 277:
==Код для загрузки(вариант без защиты)==
==Код для загрузки(вариант без защиты)==


<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
wifi.setmode(wifi.STATION)
wifi.setmode(wifi.STATION)
wifi.sta.config("YOUR_NETWORK_NAME","YOUR_NETWORK_PASSWORD")
wifi.sta.config("YOUR_NETWORK_NAME","YOUR_NETWORK_PASSWORD")
Строка 345: Строка 345:
Далее создаем две переменные ('''«led1»''' и '''«led2»''') для контактов GPIO5 и GPIO4, а также переключаем их в режим '''«OUTPUT»'''.
Далее создаем две переменные ('''«led1»''' и '''«led2»''') для контактов GPIO5 и GPIO4, а также переключаем их в режим '''«OUTPUT»'''.


<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
wifi.setmode(wifi.STATION)
wifi.setmode(wifi.STATION)
wifi.sta.config("SSID_ВАШЕЙ_СЕТИ", "ПАРОЛЬ_ОТ_ВАШЕЙ_СЕТИ")
wifi.sta.config("SSID_ВАШЕЙ_СЕТИ", "ПАРОЛЬ_ОТ_ВАШЕЙ_СЕТИ")
Строка 357: Строка 357:
Далее создаем [[веб-сервер]] на порте 80. Вот так:
Далее создаем [[веб-сервер]] на порте 80. Вот так:


<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
srv=net.createServer(net.TCP)
srv=net.createServer(net.TCP)
srv:listen(80,function(conn)
srv:listen(80,function(conn)
Строка 367: Строка 367:
Внутри веб-серверной функции '''conn:on()''' мы задаем, что произойдет при настройке соединения с клиентом. В частности, мы создаем несколько локальных переменных, хранящих данные веб-страницы и текущий [[URL]].
Внутри веб-серверной функции '''conn:on()''' мы задаем, что произойдет при настройке соединения с клиентом. В частности, мы создаем несколько локальных переменных, хранящих данные веб-страницы и текущий [[URL]].


<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
conn:on("receive", function(client,request)
conn:on("receive", function(client,request)
     local buf = ""
     local buf = ""
Строка 390: Строка 390:
Кнопки – это просто [[HTML-тег]]и '''<a href=””></a>''' с [[CSS-класс]]ом, который задает им внешний вид кнопок. Таким образом, когда вы нажимаете на кнопку, веб-сервер открывает другую страницу с другим [[URL]]. Именно так [[ESP8266]] понимает, что ей нужно сделать (т.е. включить или выключить [[светодиод]]).
Кнопки – это просто [[HTML-тег]]и '''<a href=””></a>''' с [[CSS-класс]]ом, который задает им внешний вид кнопок. Таким образом, когда вы нажимаете на кнопку, веб-сервер открывает другую страницу с другим [[URL]]. Именно так [[ESP8266]] понимает, что ей нужно сделать (т.е. включить или выключить [[светодиод]]).


<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
         buf = buf.."<head>";
         buf = buf.."<head>";
         buf = buf.."<meta charset=\"utf-8\">";
         buf = buf.."<meta charset=\"utf-8\">";
Строка 416: Строка 416:
Давайте разберем эту ситуацию на примере. Допустим, вы кликаете на кнопку '''«OFF»''' для контакта GPIO5, в результате чего открывается ссылка '''«http://192.168.7.2/?pin=OFF1»'''. Ваш [[Lua-код]] смотрит на этот [[URL]] и при помощи нескольких операторов '''«if... else»''' определяет, что вы хотите переключить контакт GPIO5 (т.е. '''«led1»''') в состояние «[[LOW]]».
Давайте разберем эту ситуацию на примере. Допустим, вы кликаете на кнопку '''«OFF»''' для контакта GPIO5, в результате чего открывается ссылка '''«http://192.168.7.2/?pin=OFF1»'''. Ваш [[Lua-код]] смотрит на этот [[URL]] и при помощи нескольких операторов '''«if... else»''' определяет, что вы хотите переключить контакт GPIO5 (т.е. '''«led1»''') в состояние «[[LOW]]».


<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
         local _on,_off = "",""
         local _on,_off = "",""
         if(_GET.pin == "ON1")then
         if(_GET.pin == "ON1")then
Строка 464: Строка 464:
[[File:esp8266 lua - sending commands.png|center]]
[[File:esp8266 lua - sending commands.png|center]]


'''Примечание:''' Файл '''«init.lua»''' можно без труда удалить с [[ESP8266]]. Просто впишите в [[IDE ESPlorer]] команду '''file.remove(“init.lua”)''' и нажмите на кнопку '''«Send»''' (как показано на скриншоте выше). Если вам нужно удалить все файлы на [[ESP8266]], воспользуйтесь командой '''file.format()'''.
{{Примечание1
|1=Файл '''«init.lua»''' можно без труда удалить с [[ESP8266]]. Просто впишите в [[IDE ESPlorer]] команду '''file.remove(“init.lua”)''' и нажмите на кнопку '''«Send»''' (как показано на скриншоте выше). Если вам нужно удалить все файлы на [[ESP8266]], воспользуйтесь командой '''file.format()'''.
}}


== Узнаем IP-адрес ESP8266 ==
== Узнаем IP-адрес ESP8266 ==
Строка 474: Строка 476:
[[File:esp8266 lua - ip address.png|center]]
[[File:esp8266 lua - ip address.png|center]]


'''Примечание:''' Если [[IP-адрес]] не напечатался в окне '''Output''', его можно узнать, отправив [[ESP8266]] команду '''print(wifi.sta.getip())'''.
{{Примечание1
|1=Если [[IP-адрес]] не напечатался в окне '''Output''', его можно узнать, отправив [[ESP8266]] команду '''print(wifi.sta.getip())'''.
}}


== Собираем цепь ==
== Собираем цепь ==
Строка 493: Строка 497:
[[File:esp8266_webserver_nodemcu_lua_two_leds_buttons_1.jpg|640px|center]]
[[File:esp8266_webserver_nodemcu_lua_two_leds_buttons_1.jpg|640px|center]]


'''Примечание:''' Чтобы подключиться к [[веб-сервер]]у, устройство с [[браузер]]ом должно быть подключено к тому же [[роутер]]у, что и [[ESP8266]].
{{Примечание1
|1=Чтобы подключиться к [[веб-сервер]]у, устройство с [[браузер]]ом должно быть подключено к тому же [[роутер]]у, что и [[ESP8266]].
}}


Вот и все! Удивительно, как маленький 4-долларовый [[WiFi-модуль]] может и служить [[веб-сервер]]ом, и обслуживать веб-страницы, созданные для экранов мобильных устройств
Вот и все! Удивительно, как маленький 4-долларовый [[WiFi-модуль]] может и служить [[веб-сервер]]ом, и обслуживать веб-страницы, созданные для экранов мобильных устройств
Строка 505: Строка 511:
Для того, чтобы оснастить [[веб-сервер]] механизмом аутентификации, в его код нужно лишь добавить вот эти 6 строчек:
Для того, чтобы оснастить [[веб-сервер]] механизмом аутентификации, в его код нужно лишь добавить вот эти 6 строчек:


<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
         local _, _, auth = string.find(request, "%cAuthorization: Basic ([%w=\+\/]+)");--Authorization:
         local _, _, auth = string.find(request, "%cAuthorization: Basic ([%w=\+\/]+)");--Authorization:
           if (auth == nil or auth ~= "dXNlcjpwYXNz")then --user:pass
           if (auth == nil or auth ~= "dXNlcjpwYXNz")then --user:pass
Строка 516: Строка 522:
===Код для загрузки (с механизмом аутентификации)===
===Код для загрузки (с механизмом аутентификации)===


<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS" enclose="div">
<syntaxhighlight lang="lua" line="GESHI_NORMAL_LINE_NUMBERS|GESHI_FANCY_LINE_NUMBERS">
-- Rui Santos
-- Rui Santos
-- Complete project details at http://randomnerdtutorials.com
-- Complete project details at http://randomnerdtutorials.com
Строка 594: Строка 600:
Соответственно, вместо '''«ваш_логин»''' и '''«ваш_пароль»''' впишите собственные логин и пароль.
Соответственно, вместо '''«ваш_логин»''' и '''«ваш_пароль»''' впишите собственные логин и пароль.


'''Примечание:''' Также обратите внимание, что между логином и паролем должно стоять '''«:»'''.
{{Примечание1
|1=Также обратите внимание, что между логином и паролем должно стоять '''«:»'''.
}}


Я для своего кода использовал вариант '''«user:pass»'''.
Я для своего кода использовал вариант '''«user:pass»'''.
Строка 602: Строка 610:
Скопируйте строку, которая получилась у вас, и вставьте ее в эту строчку [[Lua-скрипт]]а (вместо строчки '''"dXNlcjpwYXNz"'''):
Скопируйте строку, которая получилась у вас, и вставьте ее в эту строчку [[Lua-скрипт]]а (вместо строчки '''"dXNlcjpwYXNz"'''):


<syntaxhighlight lang="lua" enclose="div">
<syntaxhighlight lang="lua">
if (auth == nil or auth ~= "dXNlcjpwYXNz")then --user:pass
if (auth == nil or auth ~= "dXNlcjpwYXNz")then --user:pass
</syntaxhighlight>
</syntaxhighlight>
Строка 638: Строка 646:
Контакты [[PowerSwitch Tail II]] нужно подключить следующим образом:
Контакты [[PowerSwitch Tail II]] нужно подключить следующим образом:


{| class="wikitable" style="margin:0 auto;"
{| class="wikitable" style="margin:0 auto" style="margin:0 auto;"
|-
|-
! Контакт PowerSwitch Tail II !! Сигнал !! Контакт ESP8266
! Контакт PowerSwitch Tail II !! Сигнал !! Контакт ESP8266
Строка 656: Строка 664:


=См.также=
=См.также=
{{Ali}}


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


<references />
<references />
{{Навигационная таблица/Портал/ESP8266}}


[[Категория:ESP8266]]
[[Категория:ESP8266]]

Текущая версия от 12:59, 18 июня 2023

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Веб-сервер на базе ESP8266 при помощи NodeMCU

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

В этом проекте мы будем управлять двумя светодиодами. Этот проект – лишь шаблон, и идея в том, чтобы вы заменили эти светодиоды на Power Switch Tail или реле и с их помощью могли управлять любым другим электронным устройством.

Необходимое оборудование

Справочная информация

Основы программирования на 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) - эта строчка вызывает функцию, созданную выше

Как обращаться к контактам

В таблице ниже показано, как номер контакта 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

Код для загрузки(вариант без защиты)

wifi.setmode(wifi.STATION)
wifi.sta.config("YOUR_NETWORK_NAME","YOUR_NETWORK_PASSWORD")
print(wifi.sta.getip())
led1 = 1
led2 = 2
gpio.mode(led1, gpio.OUTPUT)
gpio.mode(led2, gpio.OUTPUT)
srv=net.createServer(net.TCP)
srv:listen(80,function(conn)
    conn:on("receive", function(client,request)
        local buf = "";
        buf = buf.."HTTP/1.1 200 OK\n\n"
        local _, _, method, path, vars = string.find(request, "([A-Z]+) (.+)?(.+) HTTP");
        if(method == nil)then
            _, _, method, path = string.find(request, "([A-Z]+) (.+) HTTP");
        end
        local _GET = {}
        if (vars ~= nil)then
            for k, v in string.gmatch(vars, "(%w+)=(%w+)&*") do
                _GET[k] = v
            end
        end
        buf = buf.."<head>";
        buf = buf.."<meta charset=\"utf-8\">";
        buf = buf.."<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">";
        buf = buf.."<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
        buf = buf.."<script src=\"https://code.jquery.com/jquery-2.1.3.min.js\"></script>";
        buf = buf.."<link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css\">";
        buf = buf.."</head><div class=\"container\">";

        buf = buf.."<h1>Web Server</h1>";
        buf = buf.."<h2>GPIO 5</h2>";
        buf = buf.."<div class=\"row\">";
        buf = buf.."<div class=\"col-md-2\"><a href=\"?pin=ON1\" class=\"btn btn-block btn-lg btn-success\" role=\"button\">ON</a></div>";
        buf = buf.."<div class=\"col-md-2\"><a href=\"?pin=OFF1\" class=\"btn btn-block btn-lg btn-danger\" role=\"button\">OFF</a></div>";
        buf = buf.."</div>";
        buf = buf.."<h2>GPIO 4</h2>";
        buf = buf.."<div class=\"row\">";
        buf = buf.."<div class=\"col-md-2\"><a href=\"?pin=ON2\" class=\"btn btn-block btn-lg btn-primary\" role=\"button\">ON</a></div>";
        buf = buf.."<div class=\"col-md-2\"><a href=\"?pin=OFF2\" class=\"btn btn-block btn-lg btn-warning\" role=\"button\">OFF</a></div>";
        buf = buf.."</div></div>";
        
        if(_GET.pin == "ON1")then
              gpio.write(led1, gpio.HIGH);
        elseif(_GET.pin == "OFF1")then
              gpio.write(led1, gpio.LOW);
        elseif(_GET.pin == "ON2")then
              gpio.write(led2, gpio.HIGH);
        elseif(_GET.pin == "OFF2")then
              gpio.write(led2, gpio.LOW);
        end
        client:send(buf);
        client:close();
        collectgarbage();
    end)
end)

Как работает Lua-скрипт

Давайте разберем, как работает код в этом скрипте.

Сначала переводим ESP8266 в режим станции. Затем задаем сетевые настройки (SSID и пароль) – они вставляются во вторую строчку – с помощью которых ESP8266 будет подключаться к сети. Функция print() в 3 строчке печатает в окне вывода данных (Output) IDE ESPlorer IP-адрес ESP8266. Он понадобится для получения доступа к веб-серверу.

Далее создаем две переменные («led1» и «led2») для контактов GPIO5 и GPIO4, а также переключаем их в режим «OUTPUT».

wifi.setmode(wifi.STATION)
wifi.sta.config("SSID_ВАШЕЙ_СЕТИ", "ПАРОЛЬ_ОТ_ВАШЕЙ_СЕТИ")
print(wifi.sta.getip())
led1 = 1
led2 = 2
gpio.mode(led1, gpio.OUTPUT)
gpio.mode(led2, gpio.OUTPUT)

Далее создаем веб-сервер на порте 80. Вот так:

srv=net.createServer(net.TCP)
srv:listen(80,function(conn)

    end)
end)

Внутри веб-серверной функции conn:on() мы задаем, что произойдет при настройке соединения с клиентом. В частности, мы создаем несколько локальных переменных, хранящих данные веб-страницы и текущий URL.

conn:on("receive", function(client,request)
    local buf = ""
    local _, _, method, path, vars = string.find(request, "([A-Z]+) (.+)?(.+) HTTP);
    if(method == nil)then
        _, _, method, path = string.find(request, "([A-Z]+) (.+) HTTP);
    end
    local _GET = {}
    if (vars ~= nil)then
        for k, v in string.gmatch(vars, "(%w+)=(%w+)&*") do
            _GET[k] = v
        end
    end

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

Более подробно о Bootstrap можно почитать тут.

На веб-странице имеется 4 кнопки – по две (для включения и выключения) на каждый светодиод. Светодиоды, напомню, подключены к контактам GPIO4 и GPIO5.

Кнопки – это просто HTML-теги <a href=””></a> с CSS-классом, который задает им внешний вид кнопок. Таким образом, когда вы нажимаете на кнопку, веб-сервер открывает другую страницу с другим URL. Именно так ESP8266 понимает, что ей нужно сделать (т.е. включить или выключить светодиод).

        buf = buf.."<head>";
        buf = buf.."<meta charset=\"utf-8\">";
        buf = buf.."<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">";
        buf = buf.."<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
        buf = buf.."<script src=\"https://code.jquery.com/jquery-2.1.3.min.js\"></script>";
        buf = buf.."<link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css\">";
        buf = buf.."</head><div class=\"container\">";

        buf = buf.."<h1>Web Server</h1>";
        buf = buf.."<h2>GPIO 5</h2>";
        buf = buf.."<div class=\"row\">";
        buf = buf.."<div class=\"col-md-2\"><a href=\"?pin=ON1\" class=\"btn btn-block btn-lg btn-success\" role=\"button\">ON</a></div>";
        buf = buf.."<div class=\"col-md-2\"><a href=\"?pin=OFF1\" class=\"btn btn-block btn-lg btn-danger\" role=\"button\">OFF</a></div>";
        buf = buf.."</div>";
        buf = buf.."<h2>GPIO 4</h2>";
        buf = buf.."<div class=\"row\">";
        buf = buf.."<div class=\"col-md-2\"><a href=\"?pin=ON2\" class=\"btn btn-block btn-lg btn-primary\" role=\"button\">ON</a></div>";
        buf = buf.."<div class=\"col-md-2\"><a href=\"?pin=OFF2\" class=\"btn btn-block btn-lg btn-warning\" role=\"button\">OFF</a></div>";
        buf = buf.."</div></div>";

Последний фрагмент кода ниже проверяет, какая именно кнопка на веб-странице была нажата. По сути, она определяет, по какому URL вы перешли.

Давайте разберем эту ситуацию на примере. Допустим, вы кликаете на кнопку «OFF» для контакта GPIO5, в результате чего открывается ссылка «http://192.168.7.2/?pin=OFF1». Ваш Lua-код смотрит на этот URL и при помощи нескольких операторов «if... else» определяет, что вы хотите переключить контакт GPIO5 (т.е. «led1») в состояние «LOW».

        local _on,_off = "",""
        if(_GET.pin == "ON1")then
              gpio.write(led1, gpio.HIGH);
        elseif(_GET.pin == "OFF1")then
              gpio.write(led1, gpio.LOW);
        elseif(_GET.pin == "ON2")then
              gpio.write(led2, gpio.HIGH); 
        elseif(_GET.pin == "OFF2")then
              gpio.write(led2, gpio.LOW);
        end
        client:send(buf);
        client:close();
        collectgarbage();
    end)
end)

Загружаем скрипт «init.lua»

Убедитесь, что ESP8266-плата подключена к ПК, и вернитесь к IDE ESPlorer. Посмотрите в правый верхний угол программы и выполните следующее:

  1. Нажмите на кнопку «Refresh»
  2. Выберите COM-порт, к которому подключена ESP8266-плата (через FTDI-программатор или напрямую)
  3. Выставьте скорость коммуникации на 9600 бод
  4. Нажмите на кнопку «Open»

Затем посмотрите в левый верхний угол IDE ESPlorer и сделайте следующее:

  1. Выберите вкладку «NodeMCU+MicroPython»
  2. Выберите вкладку «Scripts»
  3. Создайте новый файл «init.lua»

Скопируйте в окно кода скрипт из предыдущего раздела (как на скриншоте ниже):

Теперь нам нужно загрузить этот код на ESP8266.

Нажмите на кнопку «Save to ESP», которая находится в левом нижнем углу программы.

В окне Output будут показаны команды, отправляемые на ESP8266. Они должны быть примерно такими же, как на скриншоте ниже:

Примечание

Файл «init.lua» можно без труда удалить с ESP8266. Просто впишите в IDE ESPlorer команду file.remove(“init.lua”) и нажмите на кнопку «Send» (как показано на скриншоте выше). Если вам нужно удалить все файлы на ESP8266, воспользуйтесь командой file.format().

Узнаем IP-адрес ESP8266

После того, как вы загрузите веб-серверный Lua-скрипт на ESP8266, в окне Output будут напечатаны три IP-адреса.

Нам нужен самый первый IP-адрес, и в моем случае это «192.168.1.70» (у вас он, скорее всего, будет другим). Сохраните его, т.к. он понадобится нам уже совсем скоро.

Примечание

Если IP-адрес не напечатался в окне Output, его можно узнать, отправив ESP8266 команду print(wifi.sta.getip()).

Собираем цепь

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

Как получить доступ к веб-серверу

Чтобы получить доступ к веб-серверу, проделайте следующее:

  1. Перезапустите ESP8266-модуль
  2. Откройте браузер
  3. Впишите в адресную строку браузера IP-адрес, который сохранили ранее (в моем случае это «192.168.1.70»)

В результате должна загрузиться страница.

Примечание

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

Вот и все! Удивительно, как маленький 4-долларовый WiFi-модуль может и служить веб-сервером, и обслуживать веб-страницы, созданные для экранов мобильных устройств

Как защитить веб-сервер паролем

На данный момент ваш веб-сервер работает в локальной сети, и доступ к нему может получить любой, подключившийся к тому же роутеру и вписавший IP-адрес ESP8266 в браузер своего устройства.

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

Для того, чтобы оснастить веб-сервер механизмом аутентификации, в его код нужно лишь добавить вот эти 6 строчек:

        local _, _, auth = string.find(request, "%cAuthorization: Basic ([%w=\+\/]+)");--Authorization:
          if (auth == nil or auth ~= "dXNlcjpwYXNz")then --user:pass
               client:send("HTTP/1.0 401 Authorization Required\r\nWWW-Authenticate: Basic realm=\"ESP8266 Web Server\"\r\n\r\n<h1>Unauthorized Access</h1>");
               client:close();
               return;
          end

Код для загрузки (с механизмом аутентификации)

-- Rui Santos
-- Complete project details at http://randomnerdtutorials.com

wifi.setmode(wifi.STATION)
wifi.sta.config("YOUR_NETWORK_NAME","YOUR_NETWORK_PASSWORD")
print(wifi.sta.getip())
led1 = 1
led2 = 2
gpio.mode(led1, gpio.OUTPUT)
gpio.mode(led2, gpio.OUTPUT)
srv=net.createServer(net.TCP)
srv:listen(80,function(conn)
    conn:on("receive", function(client,request)
        local buf = "";
        buf = buf.."HTTP/1.1 200 OK\n\n";
        local _, _, method, path, vars = string.find(request, "([A-Z]+) (.+)?(.+) HTTP");
        if(method == nil)then
            _, _, method, path = string.find(request, "([A-Z]+) (.+) HTTP");
        end
        local _, _, auth = string.find(request, "%cAuthorization: Basic ([%w=\+\/]+)");--Authorization:
          if (auth == nil or auth ~= "dXNlcjpwYXNz")then --user:pass
               client:send("HTTP/1.0 401 Authorization Required\r\nWWW-Authenticate: Basic realm=\"ESP8266 Web Server\"\r\n\r\n<h1>Unauthorized Access</h1>");
               client:close();
               return;
          end
        local _GET = {}
        if (vars ~= nil)then
            for k, v in string.gmatch(vars, "(%w+)=(%w+)&*") do
                _GET[k] = v
            end
        end
        buf = buf.."<head>";
        buf = buf.."<meta charset=\"utf-8\">";
        buf = buf.."<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">";
        buf = buf.."<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
        buf = buf.."<script src=\"https://code.jquery.com/jquery-2.1.3.min.js\"></script>";
        buf = buf.."<link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css\">";
        buf = buf.."</head><div class=\"container\">";

        buf = buf.."<h1>Web Server</h1>";
        buf = buf.."<h2>GPIO 5</h2>";
        buf = buf.."<div class=\"row\">";
        buf = buf.."<div class=\"col-md-2\"><a href=\"?pin=ON1\" class=\"btn btn-block btn-lg btn-success\" role=\"button\">ON</a></div>";
        buf = buf.."<div class=\"col-md-2\"><a href=\"?pin=OFF1\" class=\"btn btn-block btn-lg btn-danger\" role=\"button\">OFF</a></div>";
        buf = buf.."</div>";
        buf = buf.."<h2>GPIO 4</h2>";
        buf = buf.."<div class=\"row\">";
        buf = buf.."<div class=\"col-md-2\"><a href=\"?pin=ON2\" class=\"btn btn-block btn-lg btn-primary\" role=\"button\">ON</a></div>";
        buf = buf.."<div class=\"col-md-2\"><a href=\"?pin=OFF2\" class=\"btn btn-block btn-lg btn-warning\" role=\"button\">OFF</a></div>";
        buf = buf.."</div></div>";
        
        if(_GET.pin == "ON1")then
              gpio.write(led1, gpio.HIGH);
        elseif(_GET.pin == "OFF1")then
              gpio.write(led1, gpio.LOW);
        elseif(_GET.pin == "ON2")then
              gpio.write(led2, gpio.HIGH);
        elseif(_GET.pin == "OFF2")then
              gpio.write(led2, gpio.LOW);
        end
        client:send(buf);
        client:close();
        collectgarbage();
    end)
end)

Задаем собственные имя пользователя и пароль

На этом этапе, если вы загрузили код из предыдущего раздела, именем пользователя будет «user», а паролем – «pass». Но вы наверняка захотите задать свои собственные учетные данные.

Пройдите по этой ссылке. В первом поле впишите следующее:

ваш_логин:ваш_пароль

Соответственно, вместо «ваш_логин» и «ваш_пароль» впишите собственные логин и пароль.

Примечание

Также обратите внимание, что между логином и паролем должно стоять «:».

Я для своего кода использовал вариант «user:pass».

Нажмите на кнопку «Encode», чтобы сгенерировать строку в кодировке base64. В моем случае это «dXNlcjpwYXNz».

Скопируйте строку, которая получилась у вас, и вставьте ее в эту строчку Lua-скрипта (вместо строчки "dXNlcjpwYXNz"):

if (auth == nil or auth ~= "dXNlcjpwYXNz")then --user:pass

Загружаем обновленный веб-серверный скрипт на ESP8266

Теперь, когда мы оснастили код механизмом аутентификации, его нужно загрузить на ESP8266.

Загрузив его, откройте веб-сервер, введя его IP-адрес в своем браузере.

В результате в браузере должно появиться новое окно с просьбой ввести логин и пароль от веб-сервера.

Что дальше

Надеюсь, проект вам понравился. Конечно, он очень простой, т.к. его суть лишь во включении и выключении светодиодов, но такой веб-сервер сам по себе – это очень полезная вещь.

К примеру, с его помощью можно управлять включением/выключением домашней техники (что гораздо интересней, чем управление светодиодами), питаемой от настенных розеток. Но для этого вам – помимо ESP8266 и управляемой техники – понадобится еще одно устройство...

И здесь может быть два варианта:

Вариант А – PowerSwitch Tail II

Это самый простой вариант. Вам понадобится устройство PowerSwitch Tail II, позволяющее безопасно работать с техникой, работающей от тока высокого напряжения.

Это устройство работает очень просто. Вместо подключения домашней техники напрямую в розетку вы сначала подключаете ее в PowerSwitch Tail II, а уже PowerSwitch Tail II – в розетку.

PowerSwitch Tail II оснащен 3 контактами, благодаря которым он работает на простой цифровой логике. PowerSwitch Tail II также нужно подключить к выходному GPIO-контакту ESP8266.

Этот выходной GPIO-контакт ESP8266 будет отправлять PowerSwitch Tail II либо «HIGH», либо «LOW». Если отправленный сигнал – это «HIGH», то PowerSwitch Tail II пустит ток между управляемой техникой и розеткой, а если «LOW», то PowerSwitch Tail II отключит ток, и управляемое устройство перестанет работать.

Контакты PowerSwitch Tail II нужно подключить следующим образом:

Контакт PowerSwitch Tail II Сигнал Контакт ESP8266
1 +in GPIO4 или GPIO5
2 -in GND
3 GND Не нужно подключать

Более подробно о подключении контактов PowerSwitch Tail II читайте в его документации.

Вариант Б – реле

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

См.также

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