Управление GPIO ESP32 и ESP8266 из любой точки мира
В этом проекте вы узнаете, как управлять GPIO вашего ESP32 или ESP8266 из любой точки мира. Это может быть очень полезно для удалённого управления реле, термостатом или любым другим устройством.
Этот проект также очень универсален. Через облачную панель управления вы можете легко управлять дополнительными выходами (без загрузки нового кода на плату), а также подключить несколько плат к своему серверу.
Ранее мы сохраняли показания датчиков в базу данных и использовали различные методы для отображения показаний на:
Теперь я создал новый проект, в котором вы можете создавать кнопки на панели управления и назначать их определённой плате и номеру GPIO. Затем вы можете использовать переключатели для управления выходами ESP32 или ESP8266 из любой точки мира.
Существует множество способов управления выходами удалённо, и хотя это рабочее решение, существуют другие методы, обеспечивающие двустороннюю связь с вашими устройствами. Я также рекомендую развить этот проект дальше и добавить больше функций для ваших собственных нужд.
Для создания этого проекта вы будете использовать следующие технологии:
ESP32 или ESP8266, программируемые в Arduino IDE
Хостинг-сервер и доменное имя
PHP-скрипты для сохранения и получения состояний выходов, хранящихся в базе данных MySQL
Оглавление
Этот проект разделён на следующие основные разделы:
Смотрите видеоурок
Скачивание исходного кода
Для этого проекта вам понадобятся следующие файлы:
SQL-запрос для создания таблицы: Outputs_and_Boards_Table.sql
Вставка и доступ к базе данных: esp-database.php
Обработка HTTP-запросов: esp-outputs-action.php
CSS-файл для стилизации веб-страницы: esp-style.css
Отображение кнопок управления: esp-outputs.php
Скетч Arduino для ESP32 (с HTTPS): ESP32_HTTPS_GET_Request_JSON.ino
Скетч Arduino для ESP8266 (с HTTPS): ESP8266_HTTPS_GET_Request_JSON.ino
Скетч Arduino для ESP32 (без HTTPS): ESP32_HTTP_GET_Request_JSON.ino
Скетч Arduino для ESP8266 (без HTTPS): ESP8266_HTTP_GET_Request_JSON.ino
Скачать все файлы проекта: ZIP-архив
Размещение PHP-приложения и базы данных MySQL
Цель этого проекта – иметь собственное доменное имя и хостинг-аккаунт, позволяющий управлять GPIO вашего ESP32 или ESP8266 из любой точки мира.
Вот общий обзор работы проекта:
У вас есть веб-страница с PHP-скриптом и переключателями, которые позволяют включать и выключать выходы;
При нажатии кнопок обновляется состояние выхода и сохраняется в базе данных;
Вы можете добавлять или удалять кнопки с панели управления;
Затем ESP32 или ESP8266 (или даже несколько плат) выполняют HTTP GET запросы к серверу каждые X секунд;
Наконец, в соответствии с результатом HTTP GET запроса, плата ESP обновляет свои GPIO соответственно.
Хостинг-сервисы
Я рекомендую использовать один из следующих хостинг-сервисов, которые соответствуют всем требованиям проекта:
Bluehost (удобный с cPanel): бесплатное доменное имя при подписке на 3-летний план. Рекомендую выбрать вариант с неограниченным количеством сайтов;
Digital Ocean: Linux-сервер, которым вы управляете через командную строку. Рекомендую этот вариант только для опытных пользователей.
Эти два сервиса – те, которые я использую и лично рекомендую, но вы можете использовать любой другой хостинг. Любой хостинг-сервис, предлагающий PHP и MySQL, будет работать с этим руководством. Если у вас нет хостинг-аккаунта, я рекомендую зарегистрироваться на Bluehost.
При покупке хостинг-аккаунта вам также потребуется приобрести доменное имя. Именно это делает проект интересным: вы сможете перейти на ваш домен (http://example.com) и управлять платами.
Примечание
Вы также можете запустить LAMP-сервер (Linux, Apache, MySQL, PHP) на Raspberry Pi для управления платами в локальной сети. Однако цель этого руководства – управление выходами ESP с помощью собственного доменного имени, доступного из любой точки мира.
Подготовка базы данных MySQL
После регистрации хостинг-аккаунта и настройки доменного имени вы можете войти в cPanel или аналогичную панель управления. После этого выполните следующие шаги для создания базы данных, имени пользователя, пароля и SQL-таблицы.
Создание базы данных и пользователя
Откройте вкладку «Advanced»:
1. Введите «database» в строке поиска и выберите «MySQL Database Wizard».
2. Введите желаемое имя базы данных. В моём случае имя базы данных – esp_data. Затем нажмите кнопку «Next Step»:
Примечание
Позже вам нужно будет использовать имя базы данных с префиксом, который предоставляет ваш хостинг. Далее я буду ссылаться на него как example_esp_data.
3. Введите имя пользователя базы данных и установите пароль. Обязательно сохраните все эти данные, так как они понадобятся позже для установления соединения с базой данных через PHP-код.
Готово! Ваша новая база данных и пользователь успешно созданы. Теперь сохраните все данные, так как они понадобятся позже:
Имя базы данных: example_esp_data
Имя пользователя: example_esp_board
Пароль: ваш пароль
Создание SQL-таблицы
После создания базы данных и пользователя вернитесь в панель управления cPanel и найдите «phpMyAdmin».
В левой боковой панели выберите имя вашей базы данных example_esp_data и откройте вкладку «SQL».
Важно: убедитесь, что вы открыли базу данных example_esp_data. Затем нажмите вкладку SQL. Если вы не выполните эти точные шаги и запустите SQL-запрос, вы можете создать таблицу в неправильной базе данных.
Скопируйте SQL-запрос из следующего фрагмента:
CREATE TABLE Outputs (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(64),
board INT(6),
gpio INT(6),
state INT(6)
);
INSERT INTO `Outputs`(`name`, `board`, `gpio`, `state`) VALUES ("Built-in LED", 1, 2, 0);
CREATE TABLE Boards (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
board INT(6),
last_request TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
INSERT INTO `Boards`(`board`) VALUES (1);
Вставьте его в поле SQL-запроса (выделено красным прямоугольником) и нажмите кнопку «Go» для создания таблицы:
После этого вы должны увидеть вновь созданные таблицы Boards и Outputs в базе данных example_esp_data, как показано на рисунке ниже:
Создание файлов панели управления
В этом разделе мы создадим файлы, отвечающие за работу панели управления. Вот эти файлы:
Вставка и доступ к базе данных: esp-database.php
Обработка HTTP-запросов: esp-outputs-action.php
CSS-файл для стилизации веб-страницы: esp-style.css
Отображение кнопок управления: esp-outputs.php
Если вы используете хостинг с cPanel, найдите «File Manager»:
Затем выберите public_html и нажмите кнопку «+ File» для создания нового файла.
Примечание
Если вы следуете этому руководству и не знакомы с PHP, рекомендую создать именно эти файлы.
Создайте четыре новых файла в /public_html с этими точными именами и расширениями:
esp-database.php
esp-outputs-action.php
esp-outputs.php
esp-style.css
PHP-скрипт – обновление и получение состояний выходов
В этом разделе мы создадим PHP-скрипт, отвечающий за приём входящих запросов и взаимодействие с базой данных MySQL.
Отредактируйте вновь созданный файл (esp-outputs-action.php) и скопируйте следующий фрагмент:
<?php
include_once('esp-database.php');
$action = $id = $name = $gpio = $state = "";
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$action = test_input($_POST["action"]);
if ($action == "output_create") {
$name = test_input($_POST["name"]);
$board = test_input($_POST["board"]);
$gpio = test_input($_POST["gpio"]);
$state = test_input($_POST["state"]);
$result = createOutput($name, $board, $gpio, $state);
$result2 = getBoard($board);
if(!$result2->fetch_assoc()) {
createBoard($board);
}
echo $result;
}
else {
echo "No data posted with HTTP POST.";
}
}
if ($_SERVER["REQUEST_METHOD"] == "GET") {
$action = test_input($_GET["action"]);
if ($action == "outputs_state") {
$board = test_input($_GET["board"]);
$result = getAllOutputStates($board);
if ($result) {
while ($row = $result->fetch_assoc()) {
$rows[$row["gpio"]] = $row["state"];
}
}
echo json_encode($rows);
$result = getBoard($board);
if($result->fetch_assoc()) {
updateLastBoardTime($board);
}
}
else if ($action == "output_update") {
$id = test_input($_GET["id"]);
$state = test_input($_GET["state"]);
$result = updateOutput($id, $state);
echo $result;
}
else if ($action == "output_delete") {
$id = test_input($_GET["id"]);
$board = getOutputBoardById($id);
if ($row = $board->fetch_assoc()) {
$board_id = $row["board"];
}
$result = deleteOutput($id);
$result2 = getAllOutputStates($board_id);
if(!$result2->fetch_assoc()) {
deleteBoard($board_id);
}
echo $result;
}
else {
echo "Invalid HTTP request.";
}
}
function test_input($data) {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
return $data;
}
?>
PHP-скрипт для функций базы данных
Отредактируйте файл esp-database.php, который вставляет, удаляет и извлекает данные. Скопируйте следующий PHP-скрипт:
<?php
$servername = "localhost";
// Your Database name
$dbname = "REPLACE_WITH_YOUR_DATABASE_NAME";
// Your Database user
$username = "REPLACE_WITH_YOUR_USERNAME";
// Your Database user password
$password = "REPLACE_WITH_YOUR_PASSWORD";
function createOutput($name, $board, $gpio, $state) {
global $servername, $username, $password, $dbname;
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "INSERT INTO Outputs (name, board, gpio, state)
VALUES ('" . $name . "', '" . $board . "', '" . $gpio . "', '" . $state . "')";
if ($conn->query($sql) === TRUE) {
return "New output created successfully";
}
else {
return "Error: " . $sql . "<br>" . $conn->error;
}
$conn->close();
}
function deleteOutput($id) {
global $servername, $username, $password, $dbname;
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "DELETE FROM Outputs WHERE id='". $id . "'";
if ($conn->query($sql) === TRUE) {
return "Output deleted successfully";
}
else {
return "Error: " . $sql . "<br>" . $conn->error;
}
$conn->close();
}
function updateOutput($id, $state) {
global $servername, $username, $password, $dbname;
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "UPDATE Outputs SET state='" . $state . "' WHERE id='". $id . "'";
if ($conn->query($sql) === TRUE) {
return "Output state updated successfully";
}
else {
return "Error: " . $sql . "<br>" . $conn->error;
}
$conn->close();
}
function getAllOutputs() {
global $servername, $username, $password, $dbname;
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT id, name, board, gpio, state FROM Outputs ORDER BY board";
if ($result = $conn->query($sql)) {
return $result;
}
else {
return false;
}
$conn->close();
}
function getAllOutputStates($board) {
global $servername, $username, $password, $dbname;
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT gpio, state FROM Outputs WHERE board='" . $board . "'";
if ($result = $conn->query($sql)) {
return $result;
}
else {
return false;
}
$conn->close();
}
function getOutputBoardById($id) {
global $servername, $username, $password, $dbname;
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT board FROM Outputs WHERE id='" . $id . "'";
if ($result = $conn->query($sql)) {
return $result;
}
else {
return false;
}
$conn->close();
}
function updateLastBoardTime($board) {
global $servername, $username, $password, $dbname;
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "UPDATE Boards SET last_request=now() WHERE board='". $board . "'";
if ($conn->query($sql) === TRUE) {
return "Output state updated successfully";
}
else {
return "Error: " . $sql . "<br>" . $conn->error;
}
$conn->close();
}
function getAllBoards() {
global $servername, $username, $password, $dbname;
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT board, last_request FROM Boards ORDER BY board";
if ($result = $conn->query($sql)) {
return $result;
}
else {
return false;
}
$conn->close();
}
function getBoard($board) {
global $servername, $username, $password, $dbname;
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT board, last_request FROM Boards WHERE board='" . $board . "'";
if ($result = $conn->query($sql)) {
return $result;
}
else {
return false;
}
$conn->close();
}
function createBoard($board) {
global $servername, $username, $password, $dbname;
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "INSERT INTO Boards (board) VALUES ('" . $board . "')";
if ($conn->query($sql) === TRUE) {
return "New board created successfully";
}
else {
return "Error: " . $sql . "<br>" . $conn->error;
}
$conn->close();
}
function deleteBoard($board) {
global $servername, $username, $password, $dbname;
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "DELETE FROM Boards WHERE board='". $board . "'";
if ($conn->query($sql) === TRUE) {
return "Board deleted successfully";
}
else {
return "Error: " . $sql . "<br>" . $conn->error;
}
$conn->close();
}
?>
Перед сохранением файла вам нужно изменить переменные $dbname, $username и $password, указав ваши уникальные данные:
// Your Database name
$dbname = "example_esp_data";
// Your Database user
$username = "example_esp_board";
// Your Database user password
$password = "YOUR_USER_PASSWORD";
После добавления имени базы данных, имени пользователя и пароля сохраните файл и продолжайте следовать руководству.
PHP-скрипт – кнопки управления
Вам также нужно добавить CSS-файл для стилизации панели управления (esp-style.css). Скопируйте этот CSS в ваш файл и сохраните его:
/**
Rui Santos
Complete project details at https://RandomNerdTutorials.com/control-esp32-esp8266-gpios-from-anywhere/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
**/
html {
font-family: Arial;
display: inline-block;
text-align: center;
}
h2 {
font-size: 3.0rem;
}
body {
max-width: 600px;
margin:0px auto;
padding-bottom: 25px;
}
.switch {
position: relative;
display: inline-block;
width: 120px;
height: 68px;
}
.switch input {
display: none
}
.slider {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #949494;
border-radius: 34px;
}
.slider:before {
position: absolute;
content: "";
height: 52px;
width: 52px;
left: 8px; bottom: 8px;
background-color: #fff;
-webkit-transition: .4s;
transition: .4s;
border-radius: 68px;
}
input:checked+.slider {
background-color: #008B74;
}
input:checked+.slider:before {
-webkit-transform: translateX(52px);
-ms-transform: translateX(52px);
transform: translateX(52px);
}
input[type=text], input[type=number], select {
width: 100%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
input[type=submit] {
width: 100%;
background-color: #008B74;
color: white;
padding: 14px 20px;
margin: 8px 0;
border: none;
border-radius: 4px;
cursor: pointer;
}
input[type=submit]:hover {
background-color: #005a4c;
}
div {
text-align: left;
border-radius: 4px;
background-color: #efefef;
padding: 20px;
}
Наконец, скопируйте следующий PHP-скрипт в файл esp-outputs.php, который будет отображать кнопки управления и позволять создавать/удалять кнопки:
<!--
Rui Santos
Complete project details at https://RandomNerdTutorials.com/control-esp32-esp8266-gpios-from-anywhere/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
-->
<?php
include_once('esp-database.php');
$result = getAllOutputs();
$html_buttons = null;
if ($result) {
while ($row = $result->fetch_assoc()) {
if ($row["state"] == "1"){
$button_checked = "checked";
}
else {
$button_checked = "";
}
$html_buttons .= '<h3>' . $row["name"] . ' - Board '. $row["board"] . ' - GPIO ' . $row["gpio"] . ' (<i><a onclick="deleteOutput(this)" href="javascript:void(0);" id="' . $row["id"] . '">Delete</a></i>)</h3><label class="switch"><input type="checkbox" onchange="updateOutput(this)" id="' . $row["id"] . '" ' . $button_checked . '><span class="slider"></span></label>';
}
}
$result2 = getAllBoards();
$html_boards = null;
if ($result2) {
$html_boards .= '<h3>Boards</h3>';
while ($row = $result2->fetch_assoc()) {
$row_reading_time = $row["last_request"];
// Uncomment to set timezone to - 1 hour (you can change 1 to any number)
//$row_reading_time = date("Y-m-d H:i:s", strtotime("$row_reading_time - 1 hours"));
// Uncomment to set timezone to + 4 hours (you can change 4 to any number)
//$row_reading_time = date("Y-m-d H:i:s", strtotime("$row_reading_time + 7 hours"));
$html_boards .= '<p><strong>Board ' . $row["board"] . '</strong> - Last Request Time: '. $row_reading_time . '</p>';
}
}
?>
<!DOCTYPE HTML>
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="esp-style.css">
<title>ESP Output Control</title>
</head>
<body>
<h2>ESP Output Control</h2>
<?php echo $html_buttons; ?>
<br><br>
<?php echo $html_boards; ?>
<br><br>
<div><form onsubmit="return createOutput();">
<h3>Create New Output</h3>
<label for="outputName">Name</label>
<input type="text" name="name" id="outputName"><br>
<label for="outputBoard">Board ID</label>
<input type="number" name="board" min="0" id="outputBoard">
<label for="outputGpio">GPIO Number</label>
<input type="number" name="gpio" min="0" id="outputGpio">
<label for="outputState">Initial GPIO State</label>
<select id="outputState" name="state">
<option value="0">0 = OFF</option>
<option value="1">1 = ON</option>
</select>
<input type="submit" value="Create Output">
<p><strong>Note:</strong> in some devices, you might need to refresh the page to see your newly created buttons or to remove deleted buttons.</p>
</form></div>
<script>
function updateOutput(element) {
var xhr = new XMLHttpRequest();
if(element.checked){
xhr.open("GET", "esp-outputs-action.php?action=output_update&id="+element.id+"&state=1", true);
}
else {
xhr.open("GET", "esp-outputs-action.php?action=output_update&id="+element.id+"&state=0", true);
}
xhr.send();
}
function deleteOutput(element) {
var result = confirm("Want to delete this output?");
if (result) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "esp-outputs-action.php?action=output_delete&id="+element.id, true);
xhr.send();
alert("Output deleted");
setTimeout(function(){ window.location.reload(); });
}
}
function createOutput(element) {
var xhr = new XMLHttpRequest();
xhr.open("POST", "esp-outputs-action.php", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function() {
if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
alert("Output created");
setTimeout(function(){ window.location.reload(); });
}
}
var outputName = document.getElementById("outputName").value;
var outputBoard = document.getElementById("outputBoard").value;
var outputGpio = document.getElementById("outputGpio").value;
var outputState = document.getElementById("outputState").value;
var httpRequestData = "action=output_create&name="+outputName+"&board="+outputBoard+"&gpio="+outputGpio+"&state="+outputState;
xhr.send(httpRequestData);
}
</script>
</body>
</html>
Если вы попробуете открыть ваше доменное имя по следующему URL-адресу, вы увидите следующее:
https://example.com/esp-outputs.php
Готово! Вы должны увидеть эту веб-страницу с кнопкой по умолчанию. Кнопка по умолчанию называется Built-in LED, она назначена Board 1 и управляет GPIO 2.
Настройка ESP32 или ESP8266
Этот проект совместим как с ESP32, так и с ESP8266. Вам нужно просто собрать простую схему и загрузить предоставленные скетчи.
Необходимые компоненты
Для тестирования этого проекта мы подключим несколько светодиодов к GPIO ESP32 и ESP8266. Вот список компонентов, необходимых для сборки схемы:
Схемы подключения
В этом примере мы будем использовать ESP32 с 3 светодиодами и ESP8266 с 2 светодиодами. Вместо светодиодов вы можете подключить модуль реле или любое другое устройство к GPIO ESP.
Подключение светодиодов к ESP32 – плата #1
Рекомендуем прочитать: какие GPIO ESP32 следует использовать.
Подключение светодиодов к ESP8266 – плата #2
Рекомендуем прочитать: какие GPIO ESP8266 следует использовать.
Код для ESP32 – плата #1
Мы будем программировать ESP32/ESP8266 с помощью Arduino IDE, поэтому у вас должно быть установлено дополнение ESP в Arduino IDE.
Следуйте одному из следующих руководств в зависимости от используемой платы:
Вам также необходимо установить библиотеку Arduino_JSON. Вы можете установить эту библиотеку в менеджере библиотек Arduino IDE. Просто перейдите в Sketch > Include Library > Manage Libraries… и найдите библиотеку по имени:
После установки необходимых дополнений и библиотек скопируйте следующий код в Arduino IDE, но пока не загружайте его. Вам нужно внести некоторые изменения, чтобы он работал с вашими настройками.
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/control-esp32-esp8266-gpios-from-anywhere/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
#include <Arduino_JSON.h>
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
//Your IP address or domain name with URL path
const char* serverName = "https://example.com/esp-outputs-action.php?action=outputs_state&board=1";
// Update interval time set to 5 seconds
const long interval = 5000;
unsigned long previousMillis = 0;
String outputsState;
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println("Connecting");
while(WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to WiFi network with IP Address: ");
Serial.println(WiFi.localIP());
}
void loop() {
unsigned long currentMillis = millis();
if(currentMillis - previousMillis >= interval) {
// Check WiFi connection status
if(WiFi.status()== WL_CONNECTED ){
outputsState = httpGETRequest(serverName);
Serial.println(outputsState);
JSONVar myObject = JSON.parse(outputsState);
// JSON.typeof(jsonVar) can be used to get the type of the var
if (JSON.typeof(myObject) == "undefined") {
Serial.println("Parsing input failed!");
return;
}
Serial.print("JSON object = ");
Serial.println(myObject);
// myObject.keys() can be used to get an array of all the keys in the object
JSONVar keys = myObject.keys();
for (int i = 0; i < keys.length(); i++) {
JSONVar value = myObject[keys[i]];
Serial.print("GPIO: ");
Serial.print(keys[i]);
Serial.print(" - SET to: ");
Serial.println(value);
pinMode(atoi(keys[i]), OUTPUT);
digitalWrite(atoi(keys[i]), atoi(value));
}
// save the last HTTP GET Request
previousMillis = currentMillis;
}
else {
Serial.println("WiFi Disconnected");
}
}
}
String httpGETRequest(const char* serverName) {
WiFiClientSecure *client = new WiFiClientSecure;
// set secure client without certificate
client->setInsecure();
HTTPClient https;
// Your IP address with path or Domain name with URL path
https.begin(*client, serverName);
// Send HTTP POST request
int httpResponseCode = https.GET();
String payload = "{}";
if (httpResponseCode>0) {
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
payload = https.getString();
}
else {
Serial.print("Error code: ");
Serial.println(httpResponseCode);
}
// Free resources
https.end();
return payload;
}
Примечание
Большинство серверов требуют выполнения HTTPS-запросов. Приведённый выше код выполняет HTTPS-запросы для соответствия требованиям большинства современных облачных серверов.
Ваш сервер не поддерживает HTTPS? Используйте этот код вместо него.
Настройка сетевых учётных данных
Вам нужно изменить следующие строки, указав ваши сетевые учётные данные: SSID и пароль. В коде есть комментарии, указывающие, где именно нужно внести изменения.
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Настройка serverName
Вам также нужно указать ваше доменное имя, чтобы ESP выполнял HTTP GET запросы к вашему собственному серверу.
const char* serverName = "https://example.com/esp-outputs-action.php?action=outputs_state&board=1";
Обратите внимание, что в URL serverName есть параметр board=1. Он указывает ID платы. Если вы хотите добавить больше плат, измените этот ID. Он идентифицирует плату, которой вы хотите управлять.
Теперь вы можете загрузить код на плату. Он должен заработать сразу.
Этот проект уже достаточно объёмный, поэтому мы не будем подробно разбирать работу кода. Вкратце, ESP32 выполняет HTTP GET запрос к вашему серверу каждые X секунд для обновления состояний GPIO (по умолчанию установлено 5 секунд).
const long interval = 5000;
Затем плата обновляет свои выходы в соответствии с ответом на запрос.
Откройте Serial Monitor, и вы должны увидеть что-то подобное:
Запрос получает JSON-объект, содержащий номер GPIO и его состояние. В данном случае он сообщает, что GPIO 2 должен быть LOW {«2»:»0»}.
Код для ESP8266 – плата #2
В этом примере мы управляем выходами двух плат одновременно. Вы можете использовать следующий код для вашей платы ESP8266:
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/control-esp32-esp8266-gpios-from-anywhere/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecureBearSSL.h>
#include <Arduino_JSON.h>
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
//Your IP address or domain name with URL path
//const char* serverName = "https://example.com/esp-outputs-action.php?action=outputs_state&board=1";
// Update interval time set to 5 seconds
const long interval = 5000;
unsigned long previousMillis = 0;
String outputsState;
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.println("Connecting");
while(WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to WiFi network with IP Address: ");
Serial.println(WiFi.localIP());
}
void loop() {
unsigned long currentMillis = millis();
if(currentMillis - previousMillis >= interval) {
// Check WiFi connection status
if(WiFi.status()== WL_CONNECTED ){
outputsState = httpGETRequest(serverName);
Serial.println(outputsState);
JSONVar myObject = JSON.parse(outputsState);
// JSON.typeof(jsonVar) can be used to get the type of the var
if (JSON.typeof(myObject) == "undefined") {
Serial.println("Parsing input failed!");
return;
}
Serial.print("JSON object = ");
Serial.println(myObject);
// myObject.keys() can be used to get an array of all the keys in the object
JSONVar keys = myObject.keys();
for (int i = 0; i < keys.length(); i++) {
JSONVar value = myObject[keys[i]];
Serial.print("GPIO: ");
Serial.print(keys[i]);
Serial.print(" - SET to: ");
Serial.println(value);
pinMode(atoi(keys[i]), OUTPUT);
digitalWrite(atoi(keys[i]), atoi(value));
}
// save the last HTTP GET Request
previousMillis = currentMillis;
}
else {
Serial.println("WiFi Disconnected");
}
}
}
String httpGETRequest(const char* serverName) {
std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);
// Ignore SSL certificate validation
client->setInsecure();
HTTPClient https;
// Your IP address with path or Domain name with URL path
https.begin(*client, serverName);
// Send HTTP POST request
int httpResponseCode = https.GET();
String payload = "{}";
if (httpResponseCode>0) {
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
payload = https.getString();
}
else {
Serial.print("Error code: ");
Serial.println(httpResponseCode);
}
// Free resources
https.end();
return payload;
}
Для подготовки кода для ESP8266 просто введите SSID, пароль, доменное имя и ID платы (в данном случае это плата с ID номером 2).
Примечание
Большинство серверов требуют выполнения HTTPS-запросов. Приведённый выше код выполняет HTTPS-запросы для соответствия требованиям большинства современных облачных серверов.
Ваш сервер не поддерживает HTTPS? Используйте этот код вместо него.
Демонстрация
После выполнения всех шагов подключите питание к обеим платам ESP.
Если вы откроете ваше доменное имя по следующему URL-адресу:
https://example.com/esp-outputs.php
Вы должны увидеть кнопку по умолчанию на панели управления:
Если вы включите и выключите эту кнопку, вы сможете управлять GPIO 2 вашего ESP32 – плата #1.
Вы можете добавить больше кнопок в проект, ввести имя (LED 2), установить ID платы равным 1, затем ввести желаемый номер GPIO, которым хотите управлять (33).
Создайте ещё одну кнопку для Board 1 для управления GPIO 32. Затем добавьте две кнопки для Board 2 (GPIO 2 и GPIO 4).
В любой момент вы можете использовать ссылку «Delete» для удаления кнопок с панели управления или использовать форму внизу для создания новых.
Примечание
На некоторых устройствах может потребоваться обновить страницу, чтобы увидеть вновь созданные кнопки или удалить удалённые.
Наконец, есть раздел, показывающий время последнего запроса платы и обновления её выходов.
Поскольку это не двусторонняя связь, при нажатии кнопок для управления выходами плата не обновляет выходы мгновенно. Потребуется несколько секунд, пока плата ESP выполнит новый HTTP GET запрос и обновит состояния выходов. В разделе Last Request Time вы можете увидеть, когда это произошло. Просто обновите страницу, чтобы увидеть актуальные значения.
Веб-страница также адаптивна для мобильных устройств, поэтому вы можете использовать любое устройство для доступа к серверу.
Заключение
В этом руководстве вы узнали, как управлять выходами ESP32 и ESP8266 из любой точки мира. Для этого необходимо иметь собственный сервер и доменное имя (в качестве альтернативы можно использовать LAMP-сервер на Raspberry Pi для локального доступа).
Существует множество других функций, которые можно добавить к серверу. Вы можете объединить его с нашими предыдущими проектами для отображения показаний датчиков. Не стесняйтесь добавлять больше плат ESP для одновременной работы и определять другие выходы для управления.
Я рекомендую изменить внешний вид веб-страницы, добавить больше функций, таких как уведомления по электронной почте, публиковать данные с различных датчиков, использовать несколько плат ESP и многое другое.
Вам также может быть интересно:
Облачная метеостанция с ESP32/ESP8266 (база данных MySQL и PHP)
ESP32/ESP8266: отправка уведомлений по электронной почте с помощью PHP-скрипта
Визуализация показаний датчиков из любой точки мира (ESP32/ESP8266 + MySQL + PHP) с помощью графиков
Узнайте больше о ESP32 и ESP8266 из наших ресурсов:
Спасибо за чтение.
Источник: Control ESP32 and ESP8266 GPIOs from Anywhere in the World