Аппаратная платформа Arduino отличная возможность начать программирование микроконтроллеров, не смотря на все её недостатки в виде упрощенного IDE и раздутого скомпилированного кода. Но рано или поздно возможностей ардуино перестает хватать для реализации своих идей и настает момент, когда надо двигаться дальше.
Если вам не хватает поддержки беспроводных сетей, то у вас два выхода: подключить к ардуино модуль ESP-01 (на Али) и остаться со всеми остальными параметрами ардуино, либо использовать платы на основе ESP-12(E), например NodeMCU, WeMos (на Ali) и многие другие китайские поделки, и получить при этом все преимущества и недостатки контроллера esp8266 (да-да, по сравнению с ардуино у esp8266 все же есть некоторые недостатки, но о них ниже).
В этой статье я буду рассматривать работу с NodeMCU с точки зрения пользователя ардуино, покажу быстрый способ перехода от ардуино к NodeMCU, проведу сравнение NodeMCU с ардуино, а так же покажу как получить доступ к устройству, построенному на NodeMCU, через интернет из любой точки мира (при условии что само устройство имеет доступ в интернет).
Но в самом начале несколько слов о параметрах и возможностях NodeMCU. Но перед этим стоит уточнить, что сравнивать NodeMCU и Arduino довольно сложно, потому что у них разная «весовая категория». При этом у каждой из плат есть свои преимущества и недостатки. Так NodeMCU имеет бОльшие вычислительные способности и частоту процессора, бОльший объем памяти и самое главное имеет встроенную поддержку Wi-Fi, но при этом уступает Arduino в количестве как цифровых, так и аналоговых выводов.
Файлы печатной платы приведены на гитхабе https://github.com/nodemcu/nodemcu-devkit-v1.0 , там же и распиновка печатной платы
А так же подробная принципиальная схема https://github.com/nodemcu/nodemcu-devkit-v1.0/blob/master/NODEMCU_DEVKIT_V1.0.PDF
Обратите внимание на два зарезервированных вывода слева сверху. На плате, что досталась мне, сюда выведены дополнительные выводы питания прямо с usb гнезда (см. изображение ниже), при этом при питании через Vin на выводе VUSB отсутствует напряжение. Не знаю, сделали ли это сами разработчики NodeMCU или же постарались китайские изготовители плат.
Модуль можно питать тремя различными способами с различным напряжением:
- 5-18 вольт через вывод Vin (согласно параметрам стабилизатора AMS1117-3.3)
- 5 вольт через вывод VUSB или USB-гнездо
- 3.3 вольта непосредственно через вывозы 3V
На плате установлен ESP-12E, который имеет следующие параметры (взяты из даташита на ESP-12E):
- протокол Wi-Fi 802.11 b/n/g
- частота 2.4 — 2.5 GHz (2400-2483.5 MHz)
- режим Wi-Fi: точка доступа, клиент
- защита Wi-Fi: WPA, WPA2
- шифрование Wi-Fi: WEP, TKIP, AES
- сетевые протоколы: IPv4, TCP, UDP, HTTP, FTP
- 80 MHz 32-bit процессор
- 11 доступных портов ввода/вывода UART, HSPI, I2C, I2S, GPIO, PWM
- рабочее напряжение 3.0 … 3.6 вольт
- максимальная нагрузка на вывод не более 12 mA
- максимальное потребление модуля 200 mA, среднее 80 mA (подробнее в 11 таблице даташита ESP-12E)
- рабочая температура -40 … 125 С
В том же даташите приведены назначения выводов. Выводы нумеруются против часовой стрелки начиная слева сверху, при этом антенна ESP-12E должна находиться вверху, а сам модуль должен быть повернут лицевой стороной.
NO. Pin Name Function
1 RST Reset the module
2 ADC АЦП 10 бит, диапазон входящего напряжения 0-1 вольт
3 EN Chip enable pin.Active high
4 IO16 GPIO16; может использоваться для вывода модуля из сна
5 IO14 GPIO14; HSPI_CLK
6 IO12 GPIO12; HSPI_MISO
7 IO13 GPIO13; HSPI_MOSI; UART0_CTS
8 VCC 3.3V power supply (VDD)
9 CS0 Chip selection
10 MISO Salve output master input
11 IO9 GPIO9
12 IO10 GBIO10
13 MOSI Master output slave input
14 SCLK Clock
15 GND GND
16 IO15 GPIO15; MTDO; HSPICS; UART0_RTS
17 IO2 GPIO2; UART1_TXD
18 IO0 GPIO0
19 IO4 GPIO4
20 IO5 GPIO5
21 RXD UART0_RXD; GPIO3
22 TXD UART0_TXD; GPIO1
И ещё одна наглядная картинка с распиновкой ESP-12
Так же даташит содержит таблицу описания интерфейсов.
В качестве преобразователя USB-UART используется микросхема CH340G.
NodeMCU и ArduinoIDE
Переход в программировании от ардуино к NodeMCU не является сложной задачей, потому что не придется осваивать иной язык программирования или иную IDE. Программировать NodeMCU можно из старой доброй ArduinoIDE, лишь необходимо добавить в нее поддержку esp8266. Для этого необходимо:
- запустить ArduinoIDE;
- перейти в пункт «Файл – Настройки»;
- в поле «Дополнительные ссылки для Менеджера плат» ввести адрес http://arduino.esp8266.com/stable/package_esp8266com_index.json и нажать кнопку «ОК» (на скриншоте устаревшая и уже не рабочая ссылка);
- перейти в пункт меню «Инструменты – Плата:… — Менеджер плат», откроется окно Менеджера плат;
- в самом низу списка появится новый пункт «esp8266…», нажать на него и далее на кнопку «Установка»;
- дождаться завершения загрузки и установки необходимых файлов (будут загружены несколько пакетов объемом не менее 160 мегабайт) и перезапустить ArduinoIDE.
В итоге в пункте меню «Инструменты – Плата:…» появляется 21 новый пункт. Для работы с NodeMCU я выбираю из списка плату «NodeMCU 1.0 (ESP-12E Моdule)». На этом настройка ArduinoIDE завершена.
Обратите внимание на ссылки в менеджере плат. Ссылка More info приведет нас на страницу проекта на github, там в первую очередь стоит взглянуть на файл https://github.com/esp8266/Arduino/blob/master/variants/nodemcu/pins_arduino.h в котором описано, как обращаться к выводам из среды ArduinoIDE и их соответствие выводам GPIO.
#ifndef Pins_Arduino_h
#define Pins_Arduino_h
#include «../generic/common.h»
#define PIN_WIRE_SDA (4)
#define PIN_WIRE_SCL (5)
static const uint8_t SDA = PIN_WIRE_SDA;
static const uint8_t SCL = PIN_WIRE_SCL;
static const uint8_t LED_BUILTIN = 16;
static const uint8_t BUILTIN_LED = 16;
static const uint8_t D0 = 16;
static const uint8_t D1 = 5;
static const uint8_t D2 = 4;
static const uint8_t D3 = 0;
static const uint8_t D4 = 2;
static const uint8_t D5 = 14;
static const uint8_t D6 = 12;
static const uint8_t D7 = 13;
static const uint8_t D8 = 15;
static const uint8_t D9 = 3;
static const uint8_t D10 = 1;
#endif /* Pins_Arduino_h */
Так же назначения выводов описаны в файле https://github.com/esp8266/Arduino/blob/master/variants/generic/common.h
#define PIN_SPI_SS (15)
#define PIN_SPI_MOSI (13)
#define PIN_SPI_MISO (12)
#define PIN_SPI_SCK (14)
static const uint8_t SS = PIN_SPI_SS;
static const uint8_t MOSI = PIN_SPI_MOSI;
static const uint8_t MISO = PIN_SPI_MISO;
static const uint8_t SCK = PIN_SPI_SCK;
#define PIN_A0 (17)
static const uint8_t A0 = PIN_A0;
Здесь видно, что SPI соответствует выводам D5-8.
Если нажать на ссылку Online help, то откроется страница http://esp8266.github.io/Arduino/versions/2.2.0/ что несколько странно, потому что установлена последняя версия 2.3.0 и страница http://esp8266.github.io/Arduino/versions/2.3.0/ так же существует и доступна. На этой странице можно найти много полезной информации на английском языке. Разберемся же что из того что там представлено будет полезно для нас.
Первым делом рассмотрим описание стандартных функций (приведены на странице http://esp8266.github.io/Arduino/versions/2.3.0/doc/reference.html). Здесь сказано что номера выводов в среде ArduinoIDE совпадают с нумерацией GPIO. То есть что бы установить единицу на выводе GPIO16 нужно использовать команду digitalWrite(16, HIGH) или digitalWrite(D0, HIGH). Все выводы (описанные в https://github.com/esp8266/Arduino/blob/master/variants/nodemcu/pins_arduino.h) могут работать с функциями pinMode, digitalRead, digitalWrite и analodWrite. Так же выводы, сконфигурированные на вход можно подтянуть к положительному выводу питания командой INPUT_PULLUP , вывод 16 можно притянуть к земле командой INPUT_PULLDOWN_16.
Прерывания доступны на всех GPIO, кроме GPIO16. Работа с прерываниями в NodeMCU ничем не отличается от работы с прерываниям в ардуино.
Аналоговый вход работает так же, как и на ардуино, analogRead(A0) считывает аналоговое значение с вывода GPIO17 (A0). Этот же АЦП может считывать напряжение VCC командой ESP.getVcc(), но перед этим в самом начале скетча (до функции setup) необходимо добавить строку ADC_MODE(ADC_VCC), а так же вывод А0 не должен быть никуда подключен.
Программный ШИМ работает на всех доступных GPIO, включается стандартной командой analogWrite(pin, value). Значение value лежит в диапазоне 0-1023 и может быть изменено функцией analogWriteRange(new_range). Стандартная частота ШИМ 1кГц, так же может быть изменена при помощи analogWriteFreq(new_frequency).
Порицаемая многими функция delay здесь играет особую роль. Дело в том, что кроме выполнения скетча, так же необходимо поддерживать связь по Wi-Fi. Это делается автоматически, после каждого выполнения loop или при вызове delay. Таким образом, если в функции loop есть фрагменты кода, которые выполняются более 50 микросекунд (например, выполняется какой-то большой цикл), то рекомендуют вызвать delay для нормальной работы Wi-Fi. Так же есть функция yield(), которая аналогична delay(0).
Последовательный порт Serial работает на GPIO1(TX) и GPIO3(RX). Последовательный порт Serial можно переназначить на GPIO15 (TX) и GPIO13 (RX) , для этого после вызова функции Serial.begin необходимо вызвать Serial.swap(). Для отмены переназначения выводов необходимо снова вызвать Serial.swap(). В Serial1 доступен лишь TX (GPIO2).
Так же в составе пакета для работы с esp8266 идут измененные библиотеки, работа с которыми отличается от стандартных библиотек.
EEPROM — перед началом работы с eeprom необходимо вызвать функцию EEPROM.begin(size), где необходимо указать, какой объем памяти eeprom необходимо использовать (от 4 до 4096 байт), функция EEPROM.write не записывает данные сразу,для записи данных надо использовать функцию EEPROM.commit, функция EEPROM.end так же записывает данные и завершает работу с eeprom.
I2C — перед началом работы с I2C необходимо вызвать функцию Wire.begin(int sda, int scl) , по умолчанию 4 (SDA) и 5 (SCL).
ESP8266 API :
- функция ESP.deepSleep(microseconds, mode) переводит модуль в сон (mode — WAKE_RF_DEFAULT, WAKE_RFCAL, WAKE_NO_RFCAL, WAKE_RF_DISABLED). Чтобы вывести модуль из режима сна необходимо соединить GPIO16 и RST
- ESP.restart() перезапускает процессор
- сторожевой таймер (watchdog) реализован функциями ESP.wdtEnable(), ESP.wdtDisable(), и ESP.wdtFeed()
ESP8266 WiFi очень похожа на библиотеку WiFi Shield (https://www.arduino.cc/en/Reference/WiFi), и на странице http://esp8266.github.io/Arduino/versions/2.3.0/doc/libraries.html#wifi-esp8266wifi-library указаны лишь различия между этими библиотеками. Рассматривать лишь различия между библиотеками в отрыве от основной библиотеки WiFi Shield не имеет смысла, поэтому здесь делать я этого не буду.
Перейдем к практике.
Аналогом скетча «блинк» является нижеприведённый скетч, который создает локальную сеть и позволяет управлять выводами NodeMCU через адресную строку браузера.
/*
* This sketch demonstrates how to set up a simple HTTP-like server.
* The server will set a GPIO pin depending on the request
* http://server_ip/gpio/0 will set the GPIO2 low,
* http://server_ip/gpio/1 will set the GPIO2 high
* server_ip is the IP address of the ESP8266 module, will be
* printed to Serial when the module is connected.
*/
#include <ESP8266WiFi.h>
const char* ssid = «your-ssid»;
const char* password = «your-password»;
// Create an instance of the server
// specify the port to listen on as an argument
WiFiServer server(80);
void setup() {
Serial.begin(115200);
delay(10);
// prepare GPIO2
pinMode(2, OUTPUT);
digitalWrite(2, 0);
// Connect to WiFi network
Serial.println();
Serial.println();
Serial.print(«Connecting to «);
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(«.»);
}
Serial.println(«»);
Serial.println(«WiFi connected»);
// Start the server
server.begin();
Serial.println(«Server started»);
// Print the IP address
Serial.println(WiFi.localIP());
}
void loop() {
// Check if a client has connected
WiFiClient client = server.available();
if (!client) {
return;
}
// Wait until the client sends some data
Serial.println(«new client»);
while(!client.available()){
delay(1);
}
// Read the first line of the request
String req = client.readStringUntil(‘r’);
Serial.println(req);
client.flush();
// Match the request
int val;
if (req.indexOf(«/gpio/0») != -1)
val = 0;
else if (req.indexOf(«/gpio/1») != -1)
val = 1;
else {
Serial.println(«invalid request»);
client.stop();
return;
}
// Set GPIO2 according to the request
digitalWrite(2, val);
client.flush();
// Prepare the response
String s = «HTTP/1.1 200 OKrnContent-Type: text/htmlrnrn<!DOCTYPE HTML>rn<html>rnGPIO is now «;
s += (val)?»high»:»low»;
s += «</html>n»;
// Send the response to the client
client.print(s);
delay(1);
Serial.println(«Client disonnected»);
// The client will actually be disconnected
// when the function returns and ‘client’ object is detroyed
}
Но если уж NodeMCU дает возможность управлять собой через интернет, то намного интереснее/удобнее/рациональнее создать устройство, управлять которым можно будет отовсюду, с любой точки планеты, а не лишь из локальной сети. Что бы реализовать такое соединение, необходимо использовать DNS серверы и настраивать модем, или использовать специальные сервисы, такие как Blynk или RemoteXY, например. Таких сервисов появляется всё больше и больше, выбор не ограничивается приведенными двумя примерами. Тем не менее, я покажу, как организовать доступ к NodeMCU из любой точки планеты именно при помощи сервиса RemoteXY.
В одной из своих предыдущих статей я рассказывал о способах подключения различных модулей связи к ардуино и работы с ними в сервисе RemoteXY, так же было рассмотрено подключение NodeMCU к локальной Wi-Fi сети (статья расположена по ссылке /me/arduino/arduino213.php). Облачный способ подключения был создан уже послу публикации той статьи, и я в постскриптуме обещал рассмотреть этот способ связи в одной из следующих статей. Итак, поморгаем светодиодом через интернет.
Первым делом необходимо произвести конфигурацию подключения (смотрим скриншот).
Выбираем соединение «Cloud server — beta test», устройство NodeMCU, встроенный модуль связи и ArduinoIDE в качестве среды программирования. Далее необходимо настроить конфигурацию подключения модуля.
Здесь необходимо указать имя и пароль сети, к которой необходимо подключиться, выбрать токен из списка, нижние поля с серым фоном заполняются автоматически. Если необходимо создать новый токен, то необходимо нажать на кнопку «Мои токены», откроется страница со списком токенов, привязаных к вашему аккаунту.
Жмем «Создать новый токен», появляется окошко, в котором необходимо ввести имя устройства, при нажатии кнопки «создать» токен будет сгенерирован автоматически и появится в списке токенов.
В дальнейшем можно изменить имя устройства, нажав ссылку «изменить» в крайнем правом столбце таблицы. Переходим обратно в редактор и продолжаем работу над проектом.
На рабочем поле редактора я поместил элемент «кнопка», который по умолчанию привязан к выводу со светодиодом.
Здесь больше ничего делать не надо, жмем кнопку «Получть исходный код», копируем его в ArduinoIDE, в менеджере плат выбираем NodeMCU 1.0 (ESP-12E Module), выбираем COM-порт, остальные настройки оставляем без изменений и жмем «Загрузка».
Далее переходим к смартфону и запускаем программу RemoteXY. на стартовом экране сверху справа жмем на «+» что бы добавить новое подключение,
В списке выбираем «Облачный сервер»
Жмем на поле «Токен устройства» и появившемся окне вводим токен
Жмем «Ок» и «Подключиться»
И сразу же мы получаем доступ к устройству (если оно конечно имеет связь с сервером).
Обратите внимание, на странице списка токенов можно отслеживать состояние каждого устройство. Так надпись «connected» означает, что устройство подключено к серверу, а надпись «used» — что установлена связь между смартфоном и устройством.
А теперь создадим что-то полезное — кодовый замок-головоломку «Железный Феликс».
Идея этой «головоломки» принадлежит не мне, я лишь её адаптировал. В конце 90-х годов на телеканале «РТР» (нынешний первый канал) выходила передача «Довгань-шоу», маленький аналог «Форт-Боярда» с испытаниями и одним участником. В самом начале игры при помощи лототрона определялось 9-значное число, игроку в течение 9 небольших испытаний необходимо было найти эти числа (испытания были не сложными и короткими по продолжительности — 60-90-120 секунд). В конце игры, добытые в испытаниях числа, игроку необходимо было ввести в огромную копию арифмометра «Феликс», а недостающие числа подобрать. На подбор комбинации отводилось 120 секунд. Если комбинация была подобрана правильно, то рычаг, находящийся с правой стороны устройства, поддавался, дверь открывалась, и игрок мог выйти из комнаты (естественно в роли победителя).
Итак, воспроизведем этот «аттракцион» при помощи NodeMCU и смартфона. Для чего это нужно? К примеру, для организации квестов (по аналогии с той самой передачей, игроку необходимо будет добывать части пароля, а недостающие подобрать). Контроллер, при правильно подобранном пароле, может открыть дверь, или вывести на экран пароль, от кодового замка, точки доступа Wi-Fi (которая в свою очередь может открыть доступ к следующему квестовому устройству) или bluetooth. На худой конец такое устройство можно использовать просто как кодовый замок для двери, который трудно будет взломать перебором значений (еще бы, устройство предполагает 1 000 000 000 комбинаций пароля).
Железный Феликс
Электронный Феликс
Интерфейс состоит из десяти переключателей по десять положений в каждом (при необходимости можно изменить количество), кнопки «Ок» и текстовой строки. Текстовая строка оповестит о правильно набранном пароле (или даст пароль от другого устройства). Подтверждать пароль следует нажатием кнопки «Ок». Принципиальную схему я приводить не буду.
Конфигурация подключения:
Дальнейшие пояснения в комментариях.
//////////////////////////////////////////////
// RemoteXY include library //
//////////////////////////////////////////////
// определение режима соединения и подключение библиотеки RemoteXY
#define REMOTEXY_MODE__ESP8266WIFI_LIB_POINT
#include <ESP8266WiFi.h>
#include <RemoteXY.h>
// настройки соединения
#define REMOTEXY_WIFI_SSID «FeliX» //создаем точку доступа с названием FeliX
#define REMOTEXY_WIFI_PASSWORD «» //точка доступа не защищена паролем
#define REMOTEXY_SERVER_PORT 6377
// конфигурация интерфейса
#pragma pack(push, 1)
uint8_t RemoteXY_CONF[] =
{ 255,11,0,17,0,117,0,6,8,0,
3,10,8,10,6,51,2,3,10,14,
10,6,51,2,3,10,20,10,6,51,
2,3,10,26,10,6,51,2,3,10,
32,10,6,51,2,3,10,38,10,6,
51,2,3,10,44,10,6,51,2,3,
10,50,10,6,51,2,3,10,56,10,
6,51,2,3,10,2,10,6,51,2,
1,1,64,10,34,11,6,79,75,0,
67,1,65,33,31,8,6,17,129,0,
6,1,21,9,14,70,101,108,105,88,
0,129,0,5,0,21,9,0,70,101,
108,105,88,0 };
// структура определяет все переменные вашего интерфейса управления
struct {
// input variable
uint8_t select_1; // Используем 10 переключателей для ввода пароля
uint8_t select_2; //
uint8_t select_3; //
uint8_t select_4; //
uint8_t select_5; //
uint8_t select_6; //
uint8_t select_7; //
uint8_t select_8; //
uint8_t select_9; //
uint8_t select_0; //
uint8_t ok; // кнопка подтверждения пароля.
// output variable
char answer[17]; //в этой строки отображается ответ контроллера.
// other variable
uint8_t connect_flag; // =1 if wire connected, else =0
} RemoteXY;
#pragma pack(pop)
/////////////////////////////////////////////
// END RemoteXY include //
/////////////////////////////////////////////
#define PIN_OK D4//вывод D4 будет использоваться для визуального подтверждения правильно набранного пароля, так же к нему можно подключить исполнительные механизмы (например, электрозамок).
int password[10]={ 5, 9, 0, 1, 6, 6, 9, 2, 3, 1};//в этом массиве хранится пароль.
boolean open=false;//булева переменная, true когда введен верный пароль.
void setup()
{
RemoteXY_Init ();
pinMode (PIN_OK, OUTPUT);
}
void loop()
{
RemoteXY_Handler ();
int enterPassword[10]={ RemoteXY.select_0, RemoteXY.select_1, RemoteXY.select_2, RemoteXY.select_3, RemoteXY.select_4, RemoteXY.select_5, RemoteXY.select_6, RemoteXY.select_7, RemoteXY.select_8, RemoteXY.select_9};
//переносим значения переключателей в массив.
for (int i=0; i<=9; i++){//начинаем сравнивать массивы (пароли и введенный пароль) в цикле.
if (password[i]!=enterPassword[i])//если элементы массива с одинаковыми порядковыми номерами равны, то
{
open=false;//опускаем флаг и прерываем проверку
break;
}
else {
open=true;//иначе поднимаем флаг, считаем пароль верным
}
}
if ((open)&&(RemoteXY.ok==1)){//если пароль введен верно (поднят флаг open) и нажата кнопка «Ок», то
digitalWrite(PIN_OK, HIGH);//зажигаем светодиод (открываем электрозамок),
strcpy(RemoteXY.answer, «Hello!»);//выводим на экран подтверждающую надпись (или другой пароль).
}
else{//иначе закрываем замок и выводим ничего не значащую надпись.
digitalWrite(PIN_OK, LOW);
strcpy(RemoteXY.answer, «OOOPS!»);
}
}
Что дальше? Программировать ESP8266 можно не лишь в ArduinoIDE, для ESP8266 создано уже много разных прошивок с различными языками программирования (basic, родной для NodeMCU lua, python, smart.js). Но ArduinoIDE уже обладает большим количеством библиотек на все случаи жизни и замечательно подходит для быстрого старта с NodeMCU, но есть одно НО — задерживаться на ArduinoIDE надолго не стоит.
P.S. Выражаю благодарность сервису «ЖелеZona» за предоставленную для написания статьи плату NodeMCU.
Прикрепленные файлы:
- felix.rar (2 Кб)