В данном руководстве мы создадим веб-страницу для отображения показаний датчика в графической форме. Реализуем это через скрипт PHP и базу данных MySQL.
Для примера мы будем использовать датчик BME280, подключенный к плате ESP. Если вы хотите использовать другие датчики, то придется немного изменить код.
Для проекта нам потребуется:
- Плата ESP32 или ESP8266 и среда разработки Arduino;
- Зарегистрированный домен на хостинге;
- Скрипт PHP для записи данных в БД и отображения их на веб-странице;
- База данных для хранения показаний;
- Скрипт PHP для формирования графиков.
Регистрируем домен
Во-первых, нам необходимо зарегистрировать свой домен для доступа к нашему приложению. Для ясности, вот схема всего проекта:
Вы можете использовать любой хостинг для вашего домена, например, reg.ru или Bluehost. Данное руководство мы будем разбирать именно на хостинге Bluehost.
Выгрузка данных на хостинг полезна, если вы хотите получать доступ к ним откуда угодно. Однако, вы также можете создать LAMP (Linux, Apache, MySQL, PHP) сервер на Raspberry Pi для загрузки данных в локальной сети.
Подготовка базы данных
После регистрации аккаунта и выбора доменного имени, заходите в консоль. После этого следуйте следующим пунктам для создания БД, логина. пароля и таблицы SQL:
Создаем базу данных и пользователя
Открываем вкладку «Advanced»:
В поле поиска вводим «database» и ищем «MySQL Database Wizard»:
Вводим желаемое название БД. В нашем случае – esp_data. Жмем «Nest Step»:
Примечание: В дальнейшем вам придется добавлять к названию БД префикс, который присвоит хост (здесь он закрашен). Допустим, example_esp_data.
Создаем логин и пароль базы. Рекомендуется сохранить эти данные, они потребуются дальше.
Вот и всё! Сохраняем настройки и запоминаем, что нам пригодится:
- Название БД: example_esp_data;
- Имя пользователя: example_esp_board;
- Пароль: ваш_пароль;
Создаем SQL таблицу
После создания БД переходим к панели управления и ищем «phpMyAdmin»:
В левом меню выбираем нашу базу и открываем вкладку «SQL»:
Скопируйте следующие строки для создания таблицы:
1 2 3 4 5 |
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY, value1 VARCHAR(10), value2 VARCHAR(10), value3 VARCHAR(10), reading_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
И нажмите кнопку «Go»:
После этого вы должны увидеть таблицу с названием Sensor в вашей базе данных:
Скрипт PHP для записи показаний датчика в БД
В данном разделе мы создадим скрипт, который получает запросы от платы ESP32/ESP8266 и записывает полученные данные в БД.
Если на вашем хостинге есть cPanel, то введите в поле поиска «File Manager»:
Затем, выберите опцию public_html и нажмите «+ File»:
Примечание: Если вы не знакомы с PHP и MySQL, то советуем вам строго следовать данному руководству. Иначе, вам нужно будет вносить изменения в скетч.
Создаем новый файл post-data.php в папке /public_html:
Открываем файл и вставляем туда следующие строки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
<?php /* Rui Santos Complete project details at https://RandomNerdTutorials.com 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. */ $servername = "localhost"; // Здесь указываем название БД $dbname = "УКАЖИТЕ_НАЗВАНИЕ_БД"; // Указываем имя пользователя $username = "УКАЖИТЕ_ИМЯ_ПОЛЬЗОВАТЕЛЯ"; // Указываем пароль $password = "УКАЖИТЕ_ПАРОЛЬ"; // Рекомендуем не изменять данный API-ключ, он должен совпадать с ключом в скетче для платы $api_key_value = "tPmAT5Ab3j7F9"; $api_key = $value1 = $value2 = $value3 = ""; if ($_SERVER["REQUEST_METHOD"] == "POST") { $api_key = test_input($_POST["api_key"]); if($api_key == $api_key_value) { $value1 = test_input($_POST["value1"]); $value2 = test_input($_POST["value2"]); $value3 = test_input($_POST["value3"]); // Создаем соединение $conn = new mysqli($servername, $username, $password, $dbname); // Проверяем соединение if ($conn->connect_error) { die("Connection failed: " . $conn->connect_error); } $sql = "INSERT INTO Sensor (value1, value2, value3) VALUES ('" . $value1 . "', '" . $value2 . "', '" . $value3 . "')"; if ($conn->query($sql) === TRUE) { echo "New record created successfully"; } else { echo "Error: " . $sql . "<br>" . $conn->error; } $conn->close(); } 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; } |
Перед сохранением файла введите свои данные в следующие строки:
1 2 3 4 5 6 7 8 9 10 11 |
// Здесь указываем название БД $dbname = "УКАЖИТЕ_НАЗВАНИЕ_БД"; // Указываем имя пользователя $username = "УКАЖИТЕ_ИМЯ_ПОЛЬЗОВАТЕЛЯ"; // Указываем пароль $password = "УКАЖИТЕ_ПАРОЛЬ"; |
Сохраняем файл. Если вы попробуете зайти по следующему адресу (http://example.com/post-data.php), вы увидите следующее:
Визуализируем данные
Создадим еще один PHP файл в папке /public_html. Он будет рисовать график на веб-странице. Назовем его esp-chart.php
Добавляем в него следующий код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
<!-- Rui Santos Complete project details at https://RandomNerdTutorials.com 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"; // Здесь указываем название БД $dbname = "УКАЖИТЕ_НАЗВАНИЕ_БД"; // Указываем имя пользователя $username = "УКАЖИТЕ_ИМЯ_ПОЛЬЗОВАТЕЛЯ"; // Указываем пароль $password = "УКАЖИТЕ_ПАРОЛЬ"; // Создаем соединение $conn = new mysqli($servername, $username, $password, $dbname); // Проверяем соединение if ($conn->connect_error) { die("Connection failed: " . $conn->connect_error); } $sql = "SELECT id, value1, value2, value3, reading_time FROM Sensor order by reading_time desc limit 40"; $result = $conn->query($sql); while ($data = $result->fetch_assoc()){ $sensor_data[] = $data; } $readings_time = array_column($sensor_data, 'reading_time'); // ******* Раскомментируйте одну из строк для конвертации время для вашей зоны Московское – UTC-2, оставляем 0******** /*$i = 0; foreach ($readings_time as $reading){ // Для зоны UTC 1 $readings_time[$i] = date("Y-m-d H:i:s", strtotime("$reading - 1 hours")); // Для зоны UTC 6 //$readings_time[$i] = date("Y-m-d H:i:s", strtotime("$reading + 4 hours")); $i += 1; }*/ $value1 = json_encode(array_reverse(array_column($sensor_data, 'value1')), JSON_NUMERIC_CHECK); $value2 = json_encode(array_reverse(array_column($sensor_data, 'value2')), JSON_NUMERIC_CHECK); $value3 = json_encode(array_reverse(array_column($sensor_data, 'value3')), JSON_NUMERIC_CHECK); $reading_time = json_encode(array_reverse($readings_time), JSON_NUMERIC_CHECK); /*echo $value1; echo $value2; echo $value3; echo $reading_time;*/ $result->free(); $conn->close(); ?> <!DOCTYPE html> <html> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="https://code.highcharts.com/highcharts.js"></script> <style> body { min-width: 310px; max-width: 1280px; height: 500px; margin: 0 auto; } h2 { font-family: Arial; font-size: 2.5rem; text-align: center; } </style> <body> <h2>ESP Weather Station</h2> <div id="chart-temperature" class="container"></div> <div id="chart-humidity" class="container"></div> <div id="chart-pressure" class="container"></div> <script> var value1 = <?php echo $value1; ?>; var value2 = <?php echo $value2; ?>; var value3 = <?php echo $value3; ?>; var reading_time = <?php echo $reading_time; ?>; var chartT = new Highcharts.Chart({ chart:{ renderTo : 'chart-temperature' }, title: { text: 'BME280 Temperature' }, series: [{ showInLegend: false, data: value1 }], plotOptions: { line: { animation: false, dataLabels: { enabled: true } }, series: { color: '#059e8a' } }, xAxis: { type: 'datetime', categories: reading_time }, yAxis: { title: { text: 'Temperature (Celsius)' } //title: { text: 'Temperature (Fahrenheit)' } }, credits: { enabled: false } }); var chartH = new Highcharts.Chart({ chart:{ renderTo:'chart-humidity' }, title: { text: 'BME280 Humidity' }, series: [{ showInLegend: false, data: value2 }], plotOptions: { line: { animation: false, dataLabels: { enabled: true } } }, xAxis: { type: 'datetime', //dateTimeLabelFormats: { second: '%H:%M:%S' }, categories: reading_time }, yAxis: { title: { text: 'Humidity (%)' } }, credits: { enabled: false } }); var chartP = new Highcharts.Chart({ chart:{ renderTo:'chart-pressure' }, title: { text: 'BME280 Pressure' }, series: [{ showInLegend: false, data: value3 }], plotOptions: { line: { animation: false, dataLabels: { enabled: true } }, series: { color: '#18009c' } }, xAxis: { type: 'datetime', categories: reading_time }, yAxis: { title: { text: 'Pressure (hPa)' } }, credits: { enabled: false } }); </script> </body> </html> |
Здесь также необходимо добавить имя вашей БД, логин и пароль.
Теперь, если вы попытаетесь перейти по следующему адресу вы увидите следующую картину:
http://example.com/esp-chart.php
Если вы видите три пустых поля под графики, то все готово. В следующем разделе мы начнем загружать данные для графиков.
Для построения графиков мы будем использовать библиотеку Highcharts. Создадим три – графики температуры, влажности и давления в зависимости от времени. На графиках отображается максимум 40 точек, показания добавляются каждые 30 секунд.
Подготовка плат ESP32 (ESP8266)
Данный проект будет работать на обеих платах. Все, что потребуется – собрать несложную цепь и загрузить скетч для загрузки показаний датчика.
Требуемые детали
В нашем примере используется датчик BME280. Вот список деталей, которые потребуются для проекта:
- Плата ESP32 или ESP8266;
- Датчик BME280;
- Провода DuPont;
- Макетная плата
Схема
Датчик соединяем с контактами I2C платы.
Пины I2C для ESP32:
- GPIO 22 SCL (SDK);
- GPIO 21 SDA (SDI)
Подключаем модуль к плате как показано ниже:
Пины I2С для ESP8266:
- GPIO 5 (D1): SCL (SCK);
- GPIO 4 (D2): SDA (SDI)
Подключаем модуль по схеме:
Скетч
Мы используем Arduino IDE, поэтому требуется, чтобы платы были установлены. Также потребуются библиотеки BME280 и Adafruit_sensor
После установки требуемых дополнений скопируйте код, но пока не загружайте – потребуется внести несколько изменений.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
/* Rui Santos Complete project details at https://RandomNerdTutorials.com 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. */ #ifdef ESP32 #include <WiFi.h> #include <HTTPClient.h> #else #include <ESP8266WiFi.h> #include <ESP8266HTTPClient.h> #include <WiFiClient.h> #endif #include <Wire.h> #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> // Укажите свой учетные данные сети const char* ssid = "УКАЖИТЕ_СВОЙ_SSID"; const char* password = "УКАЖИТЕ_ПАРОЛЬ"; // Укажите свой домен и путь к файлу const char* serverName = "http://example.com/post-data.php"; // Оставьте ключ как есть, если вы его измените, то код не будет работать // Потому, что API должен совпадать с ключом в файле PHP String apiKeyValue = "tPmAT5Ab3j7F9"; /*#include <SPI.h> #define BME_SCK 18 #define BME_MISO 19 #define BME_MOSI 23 #define BME_CS 5*/ 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()); // Вы также можете использовать библиотеку Wire 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() { //Проверяем соединение WiFi if(WiFi.status()== WL_CONNECTED){ HTTPClient http; // Укажите URL или IP вашего домена http.begin(serverName); // Указываем тип данных http.addHeader("Content-Type", "application/x-www-form-urlencoded"); // Подготавливаем запрос String httpRequestData = "api_key=" + apiKeyValue + "&value1=" + String(bme.readTemperature()) + "&value2=" + String(bme.readHumidity()) + "&value3=" + String(bme.readPressure()/100.0F) + ""; Serial.print("httpRequestData: "); Serial.println(httpRequestData); // Для теста вы можете закомментировать переменную httpRequestData выше // и раскомментировать ту, то ниже (датчик ВМЕ280 не используется) //String httpRequestData = "api_key=tPmAT5Ab3j7F9&value1=24.75&value2=49.54&value3=1005.14"; // Отправляем запрос int httpResponseCode = http.POST(httpRequestData); // если вы делаете запрос с типом text/pain, раскомментируйте следующие строки //http.addHeader("Content-Type", "text/plain"); //int httpResponseCode = http.POST("Hello, World!"); // если вы делаете запрос с типом application/json, раскомментируйте следующие строки //http.addHeader("Content-Type", "application/json"); //int httpResponseCode = http.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); } // Освобождаем память http.end(); } else { Serial.println("WiFi Disconnected"); } //Отправляем запрос каждые 30 секунд delay(30000); } |
Для того, чтобы код работал у вас, требуется указать свой данные сети (SSID и пароль)
Также вам потребуется ввести название своего домена.
1 2 3 4 5 6 7 |
const char* ssid = "УКАЖИТЕ_SSID"; const char* password = "УКАЖИТЕ_ПАРОЛЬ"; <strong> </strong> const char* serverName = "http://example.com/post-data.php"; |
Как работает код?
Проект уже и так достаточно объемный, поэтому детально мы код разбирать не станем. Давайте бегло рассмотрим, что делает код:
- Импортирует необходимые для работы библиотеки (в зависимости от модели платы код сам подгрузит нужные);
- Задает переменные, которые можно изменить (apiKeyValue). Эта переменная всего лишь рандомная строка, однако она служит целям безопасности, т.е. ограничивает круг пользователей, которые могут загружать данные;
- Запускает последовательное соединение с платой;
- Устанавливает WiFi соединение с вашим роутером;
- Запускает датчик для снятия показаний.
Затем в loop() проводится запрос типа POST каждые 30 секунд с данными с датчика.
Демонстрация
После выполнения всех шагов, запустите плату ESP.
Если все работает правильно, то вы должны увидеть это:
Если открыть веб-страницу по этому URL:
http://example.com/esp-chart.php
То вы должны увидеть графики. Для обновления данных требуется перезагрузить веб-страницу:
Также вы можете проверить ваши данные в phpMyAdmin (также можно их изменять, удалять и т.д.)
Заключение
В данном руководстве мы научились записывать показания датчика в БД и выкладывать их на сервере, а также строить графики на основе этих данных.
Данный пример достаточно простой, но если вы освоитесь с ним, то вполне сможете применять эти знания для более сложных проектов.
Вопросы по прошивке и работе с кодом лучше писать напрямую автору в комментариях к статье (на англ. языке)
14 комментариев. Оставить новый
Добрый день!
Выдает ошибку 301. Данные не записываются на сервер
httpRequestData: api_key=tPmAT5Ab3j7F9&value1=24.50&value2=35.00&value3=765
HTTP Response code: 301
неправильный URL
Код SQL для создания таблицы:
CREATE TABLE Sensor (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
value1 VARCHAR(10),
value2 VARCHAR(10),
value3 VARCHAR(10),
reading_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
Подскажите пожалуйста, какой тип данных указывать
(строчки
// Указываем тип данных
http.addHeader(“Content-Type”, “application/x-www-form-urlencoded”);
При тесте в post-data.php данные с esp не поступают, (сайт открывается) с чем это мб связанно ?
Fatal error: Uncaught Error: Call to a member function fetch_assoc() on bool in /home/dht2841682/dhtespserverafanasev.ru/docs/dhtchart.php:34 Stack trace: #0 {main} thrown in /home/dht2841682/dhtespserverafanasev.ru/docs/dhtchart.php on line 34
Вот такая вот ошибка вылазит, что делать?
Добрый день.
не записывабится данные в таблицу.
а файл esp-chart.php при переходе на него то ошибка 500
С одной проблемой разобрался. нужно было выставить тип базы данных hoster.ru тип базы InnoDB сравнение выставить utf8mb4_unicode_ci однако страница esp-chart.php так и не открывается. ошибка 500
В мониторе порта выдает HTTP response code : 400
Что это может быть?
Привет. самое простое это взять другой код для выдергивания данных из бд.
выдает Error code: -1
Здравствуйте, где конкретно возникает ошибка?
ну вместо HTTP response code какого нибудь выдает Error code: -1
вместо какого либо “HTTP response code” В мониторе порта “Error code: -1”
разобрался, другим на будущее, если используете статический айпишник, то обязательно прописывайте DNS