Управление GPIO ESP32 и ESP8266 из любой точки мира

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

Управление GPIO ESP32 и ESP8266 из любой точки мира

Этот проект также очень универсален. Через облачную панель управления вы можете легко управлять дополнительными выходами (без загрузки нового кода на плату), а также подключить несколько плат к своему серверу.

Ранее мы сохраняли показания датчиков в базу данных и использовали различные методы для отображения показаний на:

Теперь я создал новый проект, в котором вы можете создавать кнопки на панели управления и назначать их определённой плате и номеру GPIO. Затем вы можете использовать переключатели для управления выходами ESP32 или ESP8266 из любой точки мира.

Существует множество способов управления выходами удалённо, и хотя это рабочее решение, существуют другие методы, обеспечивающие двустороннюю связь с вашими устройствами. Я также рекомендую развить этот проект дальше и добавить больше функций для ваших собственных нужд.

Для создания этого проекта вы будете использовать следующие технологии:

  • ESP32 или ESP8266, программируемые в Arduino IDE

  • Хостинг-сервер и доменное имя

  • PHP-скрипты для сохранения и получения состояний выходов, хранящихся в базе данных MySQL

Оглавление

Этот проект разделён на следующие основные разделы:

  1. Размещение PHP-приложения и базы данных MySQL

  2. Подготовка базы данных MySQL

  3. Создание файлов панели управления

  4. PHP-скрипт – обновление и получение состояний выходов

  5. PHP-скрипт для функций базы данных

  6. PHP-скрипт – кнопки управления

  7. Настройка ESP32 или ESP8266

Смотрите видеоурок

Скачивание исходного кода

Для этого проекта вам понадобятся следующие файлы:

Размещение PHP-приложения и базы данных MySQL

Цель этого проекта – иметь собственное доменное имя и хостинг-аккаунт, позволяющий управлять GPIO вашего ESP32 или ESP8266 из любой точки мира.

Вот общий обзор работы проекта:

Обзор проекта управления выходами из любой точки мира
  1. У вас есть веб-страница с PHP-скриптом и переключателями, которые позволяют включать и выключать выходы;

  2. При нажатии кнопок обновляется состояние выхода и сохраняется в базе данных;

  3. Вы можете добавлять или удалять кнопки с панели управления;

  4. Затем ESP32 или ESP8266 (или даже несколько плат) выполняют HTTP GET запросы к серверу каждые X секунд;

  5. Наконец, в соответствии с результатом 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»:

Вкладка Advanced в Bluehost

1. Введите «database» в строке поиска и выберите «MySQL Database Wizard».

Выбор MySQL Database Wizard в cPanel

2. Введите желаемое имя базы данных. В моём случае имя базы данных – esp_data. Затем нажмите кнопку «Next Step»:

Создание базы данных MySQL в cPanel

Примечание

Позже вам нужно будет использовать имя базы данных с префиксом, который предоставляет ваш хостинг. Далее я буду ссылаться на него как example_esp_data.

3. Введите имя пользователя базы данных и установите пароль. Обязательно сохраните все эти данные, так как они понадобятся позже для установления соединения с базой данных через PHP-код.

Создание пользователя и пароля MySQL в cPanel

Готово! Ваша новая база данных и пользователь успешно созданы. Теперь сохраните все данные, так как они понадобятся позже:

  • Имя базы данных: example_esp_data

  • Имя пользователя: example_esp_board

  • Пароль: ваш пароль

Создание SQL-таблицы

После создания базы данных и пользователя вернитесь в панель управления cPanel и найдите «phpMyAdmin».

Открытие phpMyAdmin в cPanel

В левой боковой панели выберите имя вашей базы данных example_esp_data и откройте вкладку «SQL».

Открытие базы данных в phpMyAdmin

Важно: убедитесь, что вы открыли базу данных 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» для создания таблицы:

Создание таблиц MySQL для панели управления GPIO ESP32 ESP8266

После этого вы должны увидеть вновь созданные таблицы Boards и Outputs в базе данных example_esp_data, как показано на рисунке ниже:

Созданные таблицы MySQL для панели управления GPIO

Создание файлов панели управления

В этом разделе мы создадим файлы, отвечающие за работу панели управления. Вот эти файлы:

Если вы используете хостинг с cPanel, найдите «File Manager»:

Открытие File Manager в cPanel

Затем выберите public_html и нажмите кнопку «+ File» для создания нового файла.

Создание нового PHP-файла в cPanel

Примечание

Если вы следуете этому руководству и не знакомы с PHP, рекомендую создать именно эти файлы.

Создайте четыре новых файла в /public_html с этими точными именами и расширениями:

  • esp-database.php

  • esp-outputs-action.php

  • esp-outputs.php

  • esp-style.css

Созданные PHP-файлы для панели управления GPIO

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
Панель управления ESP32 ESP8266 с кнопкой по умолчанию

Готово! Вы должны увидеть эту веб-страницу с кнопкой по умолчанию. Кнопка по умолчанию называется Built-in LED, она назначена Board 1 и управляет GPIO 2.

Настройка ESP32 или ESP8266

Этот проект совместим как с ESP32, так и с ESP8266. Вам нужно просто собрать простую схему и загрузить предоставленные скетчи.

Платы разработки ESP32 и ESP8266

Необходимые компоненты

Для тестирования этого проекта мы подключим несколько светодиодов к GPIO ESP32 и ESP8266. Вот список компонентов, необходимых для сборки схемы:

Схемы подключения

В этом примере мы будем использовать ESP32 с 3 светодиодами и ESP8266 с 2 светодиодами. Вместо светодиодов вы можете подключить модуль реле или любое другое устройство к GPIO ESP.

Подключение светодиодов к ESP32 – плата #1

Схема подключения светодиодов к ESP32 для управления GPIO из любой точки мира

Рекомендуем прочитать: какие GPIO ESP32 следует использовать.

Подключение светодиодов к ESP8266 – плата #2

Схема подключения светодиодов к ESP8266 для управления GPIO из любой точки мира

Рекомендуем прочитать: какие GPIO ESP8266 следует использовать.

Код для ESP32 – плата #1

Мы будем программировать ESP32/ESP8266 с помощью Arduino IDE, поэтому у вас должно быть установлено дополнение ESP в Arduino IDE.

Следуйте одному из следующих руководств в зависимости от используемой платы:

Вам также необходимо установить библиотеку Arduino_JSON. Вы можете установить эту библиотеку в менеджере библиотек Arduino IDE. Просто перейдите в Sketch > Include Library > Manage Libraries… и найдите библиотеку по имени:

Установка библиотеки Arduino JSON в Arduino IDE

После установки необходимых дополнений и библиотек скопируйте следующий код в 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, и вы должны увидеть что-то подобное:

Пример Serial Monitor ESP32 в Arduino IDE

Запрос получает 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.

Схема подключения ESP32 и ESP8266 для управления GPIO из любой точки мира

Если вы откроете ваше доменное имя по следующему URL-адресу:

https://example.com/esp-outputs.php

Вы должны увидеть кнопку по умолчанию на панели управления:

Панель управления ESP32 ESP8266 с кнопкой по умолчанию

Если вы включите и выключите эту кнопку, вы сможете управлять GPIO 2 вашего ESP32 – плата #1.

Вы можете добавить больше кнопок в проект, ввести имя (LED 2), установить ID платы равным 1, затем ввести желаемый номер GPIO, которым хотите управлять (33).

Создание переключателей для управления GPIO ESP32 ESP8266

Создайте ещё одну кнопку для Board 1 для управления GPIO 32. Затем добавьте две кнопки для Board 2 (GPIO 2 и GPIO 4).

Панель управления с несколькими платами ESP32 ESP8266

В любой момент вы можете использовать ссылку «Delete» для удаления кнопок с панели управления или использовать форму внизу для создания новых.

Примечание

На некоторых устройствах может потребоваться обновить страницу, чтобы увидеть вновь созданные кнопки или удалить удалённые.

Наконец, есть раздел, показывающий время последнего запроса платы и обновления её выходов.

Время последнего запроса ESP32 ESP8266

Поскольку это не двусторонняя связь, при нажатии кнопок для управления выходами плата не обновляет выходы мгновенно. Потребуется несколько секунд, пока плата ESP выполнит новый HTTP GET запрос и обновит состояния выходов. В разделе Last Request Time вы можете увидеть, когда это произошло. Просто обновите страницу, чтобы увидеть актуальные значения.

Веб-страница также адаптивна для мобильных устройств, поэтому вы можете использовать любое устройство для доступа к серверу.

Демонстрация панели управления GPIO ESP32 ESP8266 из любой точки мира

Заключение

В этом руководстве вы узнали, как управлять выходами ESP32 и ESP8266 из любой точки мира. Для этого необходимо иметь собственный сервер и доменное имя (в качестве альтернативы можно использовать LAMP-сервер на Raspberry Pi для локального доступа).

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

Я рекомендую изменить внешний вид веб-страницы, добавить больше функций, таких как уведомления по электронной почте, публиковать данные с различных датчиков, использовать несколько плат ESP и многое другое.

Вам также может быть интересно:

Узнайте больше о ESP32 и ESP8266 из наших ресурсов:

Спасибо за чтение.


Источник: Control ESP32 and ESP8266 GPIOs from Anywhere in the World