Облачная метеостанция на ESP32/ESP8266 (база данных MySQL и PHP)

Создайте панель мониторинга облачной метеостанции для визуализации показаний датчиков вашего ESP32 или ESP8266 из любой точки мира. Вы будете визуализировать данные датчиков, отображаемые на шкалах и в таблице. ESP32 или ESP8266 будет выполнять HTTP POST запрос к PHP-скрипту для вставки данных в базу данных MySQL.

Облачная метеостанция на ESP32/ESP8266 с базой данных MySQL и PHP

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

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

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

  • ESP32 или ESP8266, программируемый с помощью Arduino IDE

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

  • PHP-скрипт для вставки данных в MySQL и их отображения на веб-странице

  • База данных MySQL для хранения показаний

Оглавление

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

  1. `Хостинг вашего PHP-приложения и базы данных MySQL`_

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

  3. `PHP-скрипт HTTP POST -- прием и вставка данных в базу данных MySQL`_

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

  5. `PHP-скрипт -- отображение показаний базы данных на шкалах и в таблице`_

  6. `Настройка ESP32 или ESP8266`_

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

0. Скачать исходный код

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

1. Хостинг вашего PHP-приложения и базы данных MySQL

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

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

Обзор проекта облачной метеостанции ESP32 ESP8266
  1. У вас есть ESP32 или ESP8266, который отправляет показания датчиков на ваш собственный сервер. Для этого ваша плата подключена к вашему роутеру;

  2. На вашем сервере есть PHP-скрипт, который позволяет сохранять показания в базе данных MySQL;

  3. Затем другой PHP-скрипт отображает веб-страницу со шкалами, таблицей и всей другой информацией;

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

Хостинг-сервисы

Я рекомендую использовать один из следующих хостинг-сервисов, которые могут обеспечить все требования проекта:

  • Bluehost (удобный с cPanel): бесплатное доменное имя при подписке на 3-летний план. Я рекомендую выбрать опцию с неограниченным количеством сайтов;

  • Digital Ocean: Linux-сервер, которым вы управляете через командную строку. Я рекомендую эту опцию только для продвинутых пользователей.

Эти два сервиса – те, которые я использую и лично рекомендую, но вы можете использовать любой другой хостинг-сервис. Любой хостинг-сервис, предлагающий PHP и MySQL, будет работать с этим руководством. Если у вас нет хостинг-аккаунта, я рекомендую зарегистрироваться на Bluehost.

Получить хостинг и доменное имя с Bluehost >>

При покупке хостинг-аккаунта вам также нужно будет приобрести доменное имя. Именно это делает проект интересным: вы сможете перейти по своему доменному имени (https://example.com) и увидеть показания ESP.

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

Примечание

Вы также можете запустить LAMP-сервер (Linux, Apache, MySQL, PHP) на Raspberry Pi для доступа к данным в локальной сети. Однако цель этого руководства – публикация показаний на вашем собственном домене, к которому вы можете получить доступ из любой точки мира. Это позволяет легко получить доступ к показаниям ESP без зависимости от сторонней IoT-платформы.

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

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

Создание базы данных и пользователя

Откройте вкладку «Advanced»:

Вкладка Advanced в Bluehost

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

Выбор MySQL Database Wizard в CPanel

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

Создание базы данных MySQL в CPanel ESP32 ESP8266

Примечание

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

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

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

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

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

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

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

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

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

Открытие phpMyAdmin в CPanel ESP32 ESP8266

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

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

Важно: убедитесь, что вы открыли базу данных example_esp_data. Затем нажмите вкладку SQL. Если вы не выполните эти шаги точно и запустите SQL-запрос, вы можете создать таблицу в неправильной базе данных.

Скопируйте SQL-запрос из следующего фрагмента:

CREATE TABLE SensorData (
    id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    sensor VARCHAR(30) NOT NULL,
    location VARCHAR(30) NOT NULL,
    value1 VARCHAR(10),
    value2 VARCHAR(10),
    value3 VARCHAR(10),
    reading_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)

Посмотреть исходный код

Вставьте его в поле SQL-запроса (выделено красным прямоугольником) и нажмите кнопку «Go» для создания таблицы:

Создание SQL-таблицы в phpMyAdmin ESP32 ESP8266

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

Просмотр SQL-базы данных в phpMyAdmin ESP32 ESP8266

3. PHP-скрипт HTTP POST – прием и вставка данных в базу данных MySQL

В этом разделе мы создадим PHP-скрипт, который отвечает за прием входящих запросов от ESP32 или ESP8266 и вставку данных в базу данных MySQL.

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

Открытие File Manager в CPanel для редактирования PHP-файлов ESP32 ESP8266

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

Создание нового PHP-файла в CPanel ESP32 ESP8266

Примечание

Если вы следуете этому руководству и не знакомы с PHP или MySQL, я рекомендую создать именно эти файлы. В противном случае вам нужно будет изменить скетч ESP, предоставленный с другими URL-путями.

Создайте новый файл в /public_html с таким точным именем и расширением: esp-post-data.php

Создание файла esp-post-data.php

Отредактируйте вновь созданный файл (esp-post-data.php) и скопируйте следующий фрагмент:

<!--
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/cloud-weather-station-esp32-esp8266/

  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');

  // Keep this API Key value to be compatible with the ESP code provided in the project page. If you change this value, the ESP sketch needs to match
  $api_key_value = "tPmAT5Ab3j7F9";

  $api_key= $sensor = $location = $value1 = $value2 = $value3 = "";

  if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $api_key = test_input($_POST["api_key"]);
    if($api_key == $api_key_value) {
      $sensor = test_input($_POST["sensor"]);
      $location = test_input($_POST["location"]);
      $value1 = test_input($_POST["value1"]);
      $value2 = test_input($_POST["value2"]);
      $value3 = test_input($_POST["value3"]);

      $result = insertReading($sensor, $location, $value1, $value2, $value3);
      echo $result;
    }
    else {
      echo "Wrong API Key provided.";
    }
  }
  else {
    echo "No data posted with HTTP POST.";
  }

  function test_input($data) {
    $data = trim($data);
    $data = stripslashes($data);
    $data = htmlspecialchars($data);
    return $data;
  }

Посмотреть исходный код

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

Создайте новый файл в /public_html, который отвечает за вставку и доступ к данным в вашей базе данных. Назовите файл: esp-database.php

Создание файла esp-database.php

Скопируйте этот PHP-скрипт:

<!--
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/cloud-weather-station-esp32-esp8266/

  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
  $servername = "localhost";

  // REPLACE with your Database name
  $dbname = "REPLACE_WITH_YOUR_DATABASE_NAME";
  // REPLACE with Database user
  $username = "REPLACE_WITH_YOUR_USERNAME";
  // REPLACE with Database user password
  $password = "REPLACE_WITH_YOUR_PASSWORD";

  function insertReading($sensor, $location, $value1, $value2, $value3) {
    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 SensorData (sensor, location, value1, value2, value3)
    VALUES ('" . $sensor . "', '" . $location . "', '" . $value1 . "', '" . $value2 . "', '" . $value3 . "')";

    if ($conn->query($sql) === TRUE) {
      return "New record created successfully";
    }
    else {
      return "Error: " . $sql . "<br>" . $conn->error;
    }
    $conn->close();
  }

  function getAllReadings($limit) {
    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, sensor, location, value1, value2, value3, reading_time FROM SensorData order by reading_time desc limit " . $limit;
    if ($result = $conn->query($sql)) {
      return $result;
    }
    else {
      return false;
    }
    $conn->close();
  }
  function getLastReadings() {
    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, sensor, location, value1, value2, value3, reading_time FROM SensorData order by reading_time desc limit 1" ;
    if ($result = $conn->query($sql)) {
      return $result->fetch_assoc();
    }
    else {
      return false;
    }
    $conn->close();
  }

  function minReading($limit, $value) {
     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 MIN(" . $value . ") AS min_amount FROM (SELECT " . $value . " FROM SensorData order by reading_time desc limit " . $limit . ") AS min";
    if ($result = $conn->query($sql)) {
      return $result->fetch_assoc();
    }
    else {
      return false;
    }
    $conn->close();
  }

  function maxReading($limit, $value) {
     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 MAX(" . $value . ") AS max_amount FROM (SELECT " . $value . " FROM SensorData order by reading_time desc limit " . $limit . ") AS max";
    if ($result = $conn->query($sql)) {
      return $result->fetch_assoc();
    }
    else {
      return false;
    }
    $conn->close();
  }

  function avgReading($limit, $value) {
     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 AVG(" . $value . ") AS avg_amount FROM (SELECT " . $value . " FROM SensorData order by reading_time desc limit " . $limit . ") AS avg";
    if ($result = $conn->query($sql)) {
      return $result->fetch_assoc();
    }
    else {
      return false;
    }
    $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";

После добавления имени базы данных, имени пользователя и пароля сохраните файл и продолжайте с этим руководством. Если вы попробуете обратиться к вашему доменному имени по следующему URL-пути, вы увидите следующее:

https://example.com/esp-post-data.php
Пример PHP-файла esp-post-data при HTTP запросе

5. PHP-скрипт – отображение показаний базы данных на шкалах и в таблице

Вам также нужно добавить CSS-файл для стилизации вашей панели мониторинга, назовите его: esp-style.css:

Создание CSS-файла esp-style

Скопируйте этот CSS в ваш файл и сохраните его:

/**
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/cloud-weather-station-esp32-esp8266/

  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.
**/
body {
    width: 60%;
    margin: auto;
    text-align: center;
    font-family: Arial;
    top: 50%;
    left: 50%;
}

@media screen and (max-width: 800px) {
    body {
        width: 100%;
    }
}

table {
    margin-left: auto;
    margin-right: auto;
}

div {
    margin-left: auto;
    margin-right: auto;
}

h2 { font-size: 2.5rem; }

.header {
     padding: 1rem;
     margin: 0 0 2rem 0;
     background: #f2f2f2;
}

h1 {
    font-size: 2rem;
    font-family: Arial, sans-serif;
    text-align: center;
    text-transform: uppercase;
}

.content {
    display: flex;
}

@media screen and (max-width: 500px) /* Mobile */ {
    .content {
        flex-direction: column;
    }
}

.mask {
    position: relative;
    overflow: hidden;
    display: block;
    width: 12.5rem;
    height: 6.25rem;
    margin: 1.25rem;
}

.semi-circle {
    position: relative;
    display: block;
    width: 12.5rem;
    height: 6.25rem;
    background: linear-gradient(to right, #3498db 0%, #05b027 33%, #f1c40f 70%, #c0392b 100%);
    border-radius: 50% 50% 50% 50% / 100% 100% 0% 0%;
}

.semi-circle::before {
    content: "";
    position: absolute;
    bottom: 0;
    left: 50%;
    z-index: 2;
    display: block;
    width: 8.75rem;
    height: 4.375rem;
    margin-left: -4.375rem;
    background: #fff;
    border-radius: 50% 50% 50% 50% / 100% 100% 0% 0%;
}

.semi-circle--mask {
    position: absolute;
    top: 0;
    left: 0;
    width: 12.5rem;
    height: 12.5rem;
    background: transparent;
    transform: rotate(120deg) translate3d(0, 0, 0);
    transform-origin: center center;
    backface-visibility: hidden;
    transition: all 0.3s ease-in-out;
}

.semi-circle--mask::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0%;
    z-index: 2;
    display: block;
    width: 12.625rem;
    height: 6.375rem;
    margin: -1px 0 0 -1px;
    background: #f2f2f2;
    border-radius: 50% 50% 50% 50% / 100% 100% 0% 0%;
}

.gauge--2 .semi-circle { background: #3498db; }

.gauge--2 .semi-circle--mask { transform: rotate(20deg) translate3d(0, 0, 0); }

#tableReadings { border-collapse: collapse; }

#tableReadings td, #tableReadings th {
    border: 1px solid #ddd;
    padding: 10px;
}

#tableReadings tr:nth-child(even){ background-color: #f2f2f2; }

#tableReadings tr:hover { background-color: #ddd; }

#tableReadings th {
    padding: 10px;
    background-color: #2f4468;
    color: white;
}

Посмотреть исходный код

Наконец, создайте еще один PHP-файл в директории /public_html, который будет отображать все содержимое базы данных на веб-странице. Назовите новый файл: esp-weather-station.php

Создание PHP-файла esp-weather-station

Отредактируйте вновь созданный файл (esp-weather-station.php) и скопируйте следующий код:

<!--
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/cloud-weather-station-esp32-esp8266/

  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');
    if (isset($_GET["readingsCount"])){
      $data = $_GET["readingsCount"];
      $data = trim($data);
      $data = stripslashes($data);
      $data = htmlspecialchars($data);
      $readings_count = $_GET["readingsCount"];
    }
    // default readings count set to 20
    else {
      $readings_count = 20;
    }

    $last_reading = getLastReadings();
    $last_reading_temp = $last_reading["value1"];
    $last_reading_humi = $last_reading["value2"];
    $last_reading_time = $last_reading["reading_time"];

    // Uncomment to set timezone to - 1 hour (you can change 1 to any number)
    //$last_reading_time = date("Y-m-d H:i:s", strtotime("$last_reading_time - 1 hours"));
    // Uncomment to set timezone to + 7 hours (you can change 7 to any number)
    //$last_reading_time = date("Y-m-d H:i:s", strtotime("$last_reading_time + 7 hours"));

    $min_temp = minReading($readings_count, 'value1');
    $max_temp = maxReading($readings_count, 'value1');
    $avg_temp = avgReading($readings_count, 'value1');

    $min_humi = minReading($readings_count, 'value2');
    $max_humi = maxReading($readings_count, 'value2');
    $avg_humi = avgReading($readings_count, 'value2');
?>

<!DOCTYPE html>
<html>
    <head><meta http-equiv="Content-Type" content="text/html; charset=utf-8">

        <link rel="stylesheet" type="text/css" href="esp-style.css">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    </head>
    <header class="header">
        <h1>ESP Weather Station</h1>
        <form method="get">
            <input type="number" name="readingsCount" min="1" placeholder="Number of readings (<?php echo $readings_count; ?>)">
            <input type="submit" value="UPDATE">
        </form>
    </header>
<body>
    <p>Last reading: <?php echo $last_reading_time; ?></p>
    <section class="content">
        <div class="box gauge--1">
        <h3>TEMPERATURE</h3>
                  <div class="mask">
                  <div class="semi-circle"></div>
                  <div class="semi-circle--mask"></div>
                </div>
            <p style="font-size: 30px;" id="temp">--</p>
            <table cellspacing="5" cellpadding="5">
                <tr>
                    <th colspan="3">Temperature <?php echo $readings_count; ?> readings</th>
                </tr>
                <tr>
                    <td>Min</td>
                    <td>Max</td>
                    <td>Average</td>
                </tr>
                <tr>
                    <td><?php echo $min_temp['min_amount']; ?> &deg;C</td>
                    <td><?php echo $max_temp['max_amount']; ?> &deg;C</td>
                    <td><?php echo round($avg_temp['avg_amount'], 2); ?> &deg;C</td>
                </tr>
            </table>
        </div>
        <div class="box gauge--2">
            <h3>HUMIDITY</h3>
            <div class="mask">
                <div class="semi-circle"></div>
                <div class="semi-circle--mask"></div>
            </div>
            <p style="font-size: 30px;" id="humi">--</p>
            <table cellspacing="5" cellpadding="5">
                <tr>
                    <th colspan="3">Humidity <?php echo $readings_count; ?> readings</th>
                </tr>
                <tr>
                    <td>Min</td>
                    <td>Max</td>
                    <td>Average</td>
                </tr>
                <tr>
                    <td><?php echo $min_humi['min_amount']; ?> %</td>
                    <td><?php echo $max_humi['max_amount']; ?> %</td>
                    <td><?php echo round($avg_humi['avg_amount'], 2); ?> %</td>
                </tr>
            </table>
        </div>
    </section>
<?php
    echo   '<h2> View Latest ' . $readings_count . ' Readings</h2>
            <table cellspacing="5" cellpadding="5" id="tableReadings">
                <tr>
                    <th>ID</th>
                    <th>Sensor</th>
                    <th>Location</th>
                    <th>Value 1</th>
                    <th>Value 2</th>
                    <th>Value 3</th>
                    <th>Timestamp</th>
                </tr>';

    $result = getAllReadings($readings_count);
        if ($result) {
        while ($row = $result->fetch_assoc()) {
            $row_id = $row["id"];
            $row_sensor = $row["sensor"];
            $row_location = $row["location"];
            $row_value1 = $row["value1"];
            $row_value2 = $row["value2"];
            $row_value3 = $row["value3"];
            $row_reading_time = $row["reading_time"];
            // 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 + 7 hours (you can change 7 to any number)
            //$row_reading_time = date("Y-m-d H:i:s", strtotime("$row_reading_time + 7 hours"));

            echo '<tr>
                    <td>' . $row_id . '</td>
                    <td>' . $row_sensor . '</td>
                    <td>' . $row_location . '</td>
                    <td>' . $row_value1 . '</td>
                    <td>' . $row_value2 . '</td>
                    <td>' . $row_value3 . '</td>
                    <td>' . $row_reading_time . '</td>
                  </tr>';
        }
        echo '</table>';
        $result->free();
    }
?>

<script>
    var value1 = <?php echo $last_reading_temp; ?>;
    var value2 = <?php echo $last_reading_humi; ?>;
    setTemperature(value1);
    setHumidity(value2);

    function setTemperature(curVal){
        //set range for Temperature in Celsius -5 Celsius to 38 Celsius
        var minTemp = -5.0;
        var maxTemp = 38.0;
        //set range for Temperature in Fahrenheit 23 Fahrenheit to 100 Fahrenheit
        //var minTemp = 23;
        //var maxTemp = 100;

        var newVal = scaleValue(curVal, [minTemp, maxTemp], [0, 180]);
        $('.gauge--1 .semi-circle--mask').attr({
            style: '-webkit-transform: rotate(' + newVal + 'deg);' +
            '-moz-transform: rotate(' + newVal + 'deg);' +
            'transform: rotate(' + newVal + 'deg);'
        });
        $("#temp").text(curVal + ' ºC');
    }

    function setHumidity(curVal){
        //set range for Humidity percentage 0 % to 100 %
        var minHumi = 0;
        var maxHumi = 100;

        var newVal = scaleValue(curVal, [minHumi, maxHumi], [0, 180]);
        $('.gauge--2 .semi-circle--mask').attr({
            style: '-webkit-transform: rotate(' + newVal + 'deg);' +
            '-moz-transform: rotate(' + newVal + 'deg);' +
            'transform: rotate(' + newVal + 'deg);'
        });
        $("#humi").text(curVal + ' %');
    }

    function scaleValue(value, from, to) {
        var scale = (to[1] - to[0]) / (from[1] - from[0]);
        var capped = Math.min(from[1], Math.max(from[0], value)) - from[0];
        return ~~(capped * scale + to[0]);
    }
</script>
</body>
</html>

Посмотреть исходный код

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

https://example.com/esp-weather-station.php
Пустая панель мониторинга метеостанции ESP32 ESP8266

Вот и все! Если вы видите эту веб-страницу с пустыми значениями в вашем браузере, значит все готово. В следующем разделе вы узнаете, как вставить данные с вашего ESP32 или ESP8266 в базу данных.

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

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

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

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

Для этого примера мы будем получать показания с датчика BME280. Вот список компонентов, необходимых для сборки схемы этого проекта:

Вы можете использовать ссылки выше или перейти непосредственно на MakerAdvisor.com/tools, чтобы найти все компоненты для ваших проектов по лучшей цене!

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

Модуль датчика BME280, который мы используем, обменивается данными по протоколу I2C, поэтому его нужно подключить к выводам I2C ESP32 или ESP8266.

Подключение BME280 к ESP32

Выводы I2C ESP32:

  • GPIO 22: SCL (SCK)

  • GPIO 21: SDA (SDI)

Соберите схему, как показано на следующей диаграмме (Руководство по ESP32 с BME280 и Веб-сервер ESP32 BME280).

Подключение BME280 к ESP32

Рекомендуемое чтение: Справочник по выводам ESP32

Подключение BME280 к ESP8266

Выводы I2C ESP8266:

  • GPIO 5 (D1): SCL (SCK)

  • GPIO 4 (D2): SDA (SDI)

Соберите схему, как показано на следующей диаграмме, если вы используете плату ESP8266 (читайте Руководство по ESP8266 с BME280).

Подключение BME280 к ESP8266

Рекомендуемое чтение: Справочник по выводам ESP8266

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

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

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

Код для ESP32

Следуйте этому разделу, если вы используете ESP32. Для ESP8266 нажмите здесь.

После установки необходимых дополнений платы скопируйте следующий код в вашу Arduino IDE, но пока не загружайте его. Вам нужно внести некоторые изменения, чтобы он работал для вас.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-esp8266-mysql-database-php/

  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 <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// REPLACE with your Domain name and URL path or IP address with path
const char* serverName = "https://example.com/esp-post-data.php";

// Keep this API Key value to be compatible with the PHP code provided in the project page.
// If you change the apiKeyValue value, the PHP file /post-esp-data.php also needs to have the same key
String apiKeyValue = "tPmAT5Ab3j7F9";

String sensorName = "BME280";
String sensorLocation = "Office";

/*#include <SPI.h>
#define BME_SCK 18
#define BME_MISO 19
#define BME_MOSI 23
#define BME_CS 5*/

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme;  // I2C
//Adafruit_BME280 bme(BME_CS);  // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);  // software SPI

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());

  // (you can also pass in a Wire library object like &Wire2)
  bool status = bme.begin(0x76);
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring or change I2C address!");
    while (1);
  }
}

void loop() {
  //Check WiFi connection status
  if(WiFi.status()== WL_CONNECTED){
    WiFiClientSecure *client = new WiFiClientSecure;
    client->setInsecure(); //don't use SSL certificate
    HTTPClient https;

    // Your Domain name with URL path or IP address with path
    https.begin(*client, serverName);

    // Specify content-type header
    https.addHeader("Content-Type", "application/x-www-form-urlencoded");

    // Prepare your HTTP POST request data
    String httpRequestData = "api_key=" + apiKeyValue + "&sensor=" + sensorName
                          + "&location=" + sensorLocation + "&value1=" + String(bme.readTemperature())
                          + "&value2=" + String(bme.readHumidity()) + "&value3=" + String(bme.readPressure()/100.0F) + "";
    Serial.print("httpRequestData: ");
    Serial.println(httpRequestData);

    // You can comment the httpRequestData variable above
    // then, use the httpRequestData variable below (for testing purposes without the BME280 sensor)
    //String httpRequestData = "api_key=tPmAT5Ab3j7F9&sensor=BME280&location=Office&value1=24.75&value2=49.54&value3=1005.14";

    // Send HTTP POST request
    int httpResponseCode = https.POST(httpRequestData);

    // If you need an HTTP request with a content type: text/plain
    //https.addHeader("Content-Type", "text/plain");
    //int httpResponseCode = https.POST("Hello, World!");

    // If you need an HTTP request with a content type: application/json, use the following:
    //https.addHeader("Content-Type", "application/json");
    //int httpResponseCode = https.POST("{\"value1\":\"19\",\"value2\":\"67\",\"value3\":\"78\"}");

    if (httpResponseCode>0) {
      Serial.print("HTTP Response code: ");
      Serial.println(httpResponseCode);
    }
    else {
      Serial.print("Error code: ");
      Serial.println(httpResponseCode);
    }
    // Free resources
    https.end();
  }
  else {
    Serial.println("WiFi Disconnected");
  }
  //Send an HTTP POST request every 30 seconds
  delay(30000);
}

Посмотреть исходный код

Настройка сетевых учетных данных

Вам нужно изменить следующие строки, указав ваши сетевые учетные данные: SSID и пароль. В коде есть комментарии, указывающие, где нужно внести изменения.

// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Настройка serverName

Вам также нужно указать ваше доменное имя, чтобы ESP публиковал показания на ваш собственный сервер.

const char* serverName = "https://example.com/esp-post-data.php";

Теперь вы можете загрузить код на вашу плату.

Примечание

Большинство серверов требуют выполнения HTTPS-запросов. Код выше выполняет HTTPS-запросы для соответствия требованиям большинства современных облачных серверов.

Ваш сервер не поддерживает HTTPS? Используйте этот код вместо этого.

Как работает код

Этот проект уже достаточно объемный, поэтому мы не будем подробно описывать работу кода, но вот краткое изложение:

  • Импорт всех библиотек для работы;

  • Настройка переменных, которые вы можете изменить (apiKeyValue, sensorName, sensorLocation);

  • apiKeyValue – это просто случайная строка, которую вы можете изменить. Она используется в целях безопасности, чтобы только тот, кто знает ваш API-ключ, мог публиковать данные в вашу базу данных;

  • Инициализация последовательного соединения для целей отладки;

  • Установление Wi-Fi соединения с вашим роутером;

  • Инициализация BME280 для получения показаний;

  • Инициализация защищенного WiFi-клиента.

Затем в функции loop() выполняется HTTP POST запрос каждые 10 минут с последними показаниями BME280:

// Your Domain name with URL path or IP address with path
http.begin(serverName);

// Specify content-type header
http.addHeader("Content-Type", "application/x-www-form-urlencoded");

// Prepare your HTTP POST request data
String httpRequestData = "api_key=" + apiKeyValue + "&sensor=" + sensorName                      + "&location=" + sensorLocation + "&value1=" + String(bme.readTemperature())                      + "&value2=" + String(bme.readHumidity()) + "&value3=" + String(bme.readPressure()/100.0F) + "";

int httpResponseCode = http.POST(httpRequestData);

Вы можете закомментировать переменную httpRequestData выше, которая объединяет все показания BME280, и использовать переменную httpRequestData ниже для тестовых целей:

String httpRequestData = "api_key=tPmAT5Ab3j7F9&sensor=BME280&location=Office&value1=24.75&value2=49.54&value3=1005.14";

Узнайте больше о HTTPS-запросах с ESP32: ESP32 HTTPS Requests (Arduino IDE).

Код для ESP8266

Следуйте этому разделу, если вы используете ESP8266. Для ESP32 смотрите раздел выше.

После установки необходимых дополнений платы и библиотек скопируйте следующий код в вашу Arduino IDE, но пока не загружайте его. Вам нужно внести некоторые изменения, чтобы он работал для вас.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-esp8266-mysql-database-php/

  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 <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// REPLACE with your Domain name and URL path or IP address with path
const char* serverName = "https://example.com/esp-post-data.php";

// Keep this API Key value to be compatible with the PHP code provided in the project page.
// If you change the apiKeyValue value, the PHP file /post-esp-data.php also needs to have the same key
String apiKeyValue = "tPmAT5Ab3j7F9";

String sensorName = "BME280";
String sensorLocation = "Office";

/*#include <SPI.h>
#define BME_SCK 18
#define BME_MISO 19
#define BME_MOSI 23
#define BME_CS 5*/

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme;  // I2C
//Adafruit_BME280 bme(BME_CS);  // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);  // software SPI

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());

  // (you can also pass in a Wire library object like &Wire2)
  bool status = bme.begin(0x76);
  if (!status) {
    Serial.println("Could not find a valid BME280 sensor, check wiring or change I2C address!");
    while (1);
  }
}

void loop() {
  //Check WiFi connection status
  if(WiFi.status()== WL_CONNECTED){

    std::unique_ptr<BearSSL::WiFiClientSecure>client(new BearSSL::WiFiClientSecure);

    // Ignore SSL certificate validation
    client->setInsecure();

    //create an HTTPClient instance
    HTTPClient https;

    // Your Domain name with URL path or IP address with path
    https.begin(*client, serverName);

    // Specify content-type header
    https.addHeader("Content-Type", "application/x-www-form-urlencoded");

    // Prepare your HTTP POST request data
    String httpRequestData = "api_key=" + apiKeyValue + "&sensor=" + sensorName
                          + "&location=" + sensorLocation + "&value1=" + String(bme.readTemperature())
                          + "&value2=" + String(bme.readHumidity()) + "&value3=" + String(bme.readPressure()/100.0F) + "";
    Serial.print("httpRequestData: ");
    Serial.println(httpRequestData);

    // You can comment the httpRequestData variable above
    // then, use the httpRequestData variable below (for testing purposes without the BME280 sensor)
    //String httpRequestData = "api_key=tPmAT5Ab3j7F9&sensor=BME280&location=Office&value1=24.75&value2=49.54&value3=1005.14";

    // Send HTTP POST request
    int httpResponseCode = https.POST(httpRequestData);

    // If you need an HTTP request with a content type: text/plain
    //http.addHeader("Content-Type", "text/plain");
    //int httpResponseCode = https.POST("Hello, World!");

    // If you need an HTTP request with a content type: application/json, use the following:
    //http.addHeader("Content-Type", "application/json");
    //int httpResponseCode = https.POST("{\"value1\":\"19\",\"value2\":\"67\",\"value3\":\"78\"}");

    if (httpResponseCode>0) {
      Serial.print("HTTP Response code: ");
      Serial.println(httpResponseCode);
    }
    else {
      Serial.print("Error code: ");
      Serial.println(httpResponseCode);
    }
    // Free resources
    https.end();
  }
  else {
    Serial.println("WiFi Disconnected");
  }
  //Send an HTTP POST request every 30 seconds
  delay(30000);
}

Посмотреть исходный код

Настройка сетевых учетных данных (ESP8266)

Вам нужно изменить следующие строки, указав ваши сетевые учетные данные: SSID и пароль. В коде есть комментарии, указывающие, где нужно внести изменения.

// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Настройка serverName (ESP8266)

Вам также нужно указать ваше доменное имя, чтобы ESP публиковал показания на ваш собственный сервер.

const char* serverName = "https://example.com/esp-post-data.php";

Теперь вы можете загрузить код на вашу плату.

Примечание

Большинство серверов требуют выполнения HTTPS-запросов. Код выше выполняет HTTPS-запросы для соответствия требованиям большинства современных облачных серверов.

Ваш сервер не поддерживает HTTPS? Используйте этот код вместо этого.

Узнайте больше о HTTPS-запросах с ESP8266: ESP8266 NodeMCU HTTPS Requests (Arduino IDE).

Демонстрация

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

ESP32 BME280 Arduino IDE MySQL

Если все правильно, вот что вы должны увидеть в мониторе последовательного порта Arduino IDE:

Вывод монитора последовательного порта Arduino IDE для метеостанции ESP32 ESP8266

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

https://example.com/esp-weather-station.php

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

Обновите веб-страницу, чтобы увидеть последние показания:

Данные метеостанции ESP32 ESP8266 со шкалами температуры и влажности

Есть поле, в которое вы можете ввести количество показаний для визуализации, а также количество показаний для статистики: минимум, максимум и среднее. По умолчанию установлено значение 20. Например, если вы введете 30 и нажмете кнопку обновления, вы увидите, что ваша веб-страница обновится и пересчитает все значения.

Поле ввода для выбора количества показаний метеостанции ESP32 ESP8266

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

Адаптивная мобильная веб-страница метеостанции ESP32 ESP8266

Вы также можете перейти в phpMyAdmin для управления данными, хранящимися в таблице SensorData. Вы можете удалять, редактировать данные и т.д.

Просмотр показаний датчиков в phpMyAdmin SQL-базе данных ESP32 ESP8266

Заключение

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

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

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

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

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


Источник: DIY Cloud Weather Station with ESP32/ESP8266 (MySQL Database and PHP)