ESP SPIFFS

01 ноября 2022

Всем привет, сегодня разберем что такое SPIFFS и как с ним работать, создадим веб-сервер и научимся выводить показания с датчика bme280.

Что такое SPIFFS?

SPIFFS – (Serial Peripheral Interface Flash File System) файловая система флеш-памяти, подключаемой по последовательному периферийному интерфейсу. У микроконтроллеров Esp есть встроенная перезаписываемая энергонезависимая NOR-память, в которой хранятся: настройки (Preferences), загрузчик (Bootloader), микропрограмма (скомпилированный скетч) и файловая система (SPIFFS). На картинке ниже видна структура микроконтроллера:

NOR-память – это вид энергонезависимой перезаписываемой памяти, который имеет высокую скорость считывания, но низкую скорость записи и стирания.

Объем памяти достаточно большой, но зависит от микроконтроллера, для ESP32 стандарт 4 мегабайта.

SPIFFS имеет некоторые недостатки например:

  • отсутствие поддержки папок;
  • одна и та же операция может занимать разное время;
  • отсутствие возможности нахождения и исправления битых блоков.

Но они не критичны по сравнению с теми плюсами которые предоставляет нам SPIFFS. Мы можем использовать SPIFFS для:

  • записи долговременных настроек;
  • хранить картинки;
  • хранить html и css, чтобы загружать страницы для веб-сервера;

Для начала работы с SPIFFS надо настроить Arduino IDE:

Версия IDE 2.0, к сожалению, пока не поддерживается.

Скачайте архив с инструментом для ESP32 тут. Или же для ESP8266 тут.

Распакуйте архив и перенесете папку arduino ide по адресу:

C:\Users\имя\Documents\Arduino\tools\ESP32FS(ESP8266FS)\tool\esp32fs(esp8266fs).jar

После перемещения откройте ardino ide и проверьте, во вкладке инструменты должен появиться доп вариант:

Если появился, значит все успешно, если нет, попробуйте еще раз.

Создайте новый скетч и поместите в него содержимое:

#include "SPIFFS.h" //Подключаем библиотеку
String data;
void setup() {
 Serial.begin(115200);
 if(!SPIFFS.begin(true)){ //Инициализируем SPIFFS
  Serial.println("An Error has occurred while mounting SPIFFS");
  return;
 }
 File file = SPIFFS.open("/test.txt"); //Открываем файл
 if(!file){ //Проверка наличия
  Serial.println("Failed to open file for reading");
  return;
 }
 data = file.readString(); //Конвертируем данные в нормальное представление
 uint8_t data1[data.length()];
 data.getBytes(data1, data.length()); //168
 file.close(); //Завершаем работу с файлом
 Serial.println(data); //Выводим информацию в монитор порта
}
void loop() {}

Далее, во вкладке Скетч выберите вариант показать папку скетча (или воспользуйтесь сочетанием клавиш CTRL+K). В открывшейся папке создайте новую папку с именем data и поместите в неё файл test.txt. В файл запишите любое текстовое послание.

После во вкладке инструменты выберите ESP32/8266 Sketch Data Upload. Должен начаться процесс загрузки данных из папки data в память esp (на некоторых платах надо зажать кнопку boot). Разберем возможные ошибки:

Traceback (most recent call last):
 File "site-packages\serial\serialwin32.py", line 62, in open
serial.serialutil.SerialException: could not open port 'COM3': WindowsError(5, '\xce\xf2\xea\xe0\xe7\xe0\xed\xee \xe2 \xe4\xee\xf1\xf2\xf3\xef\xe5.')
Failed to execute script esptool
SPIFFS Upload failed!

Если вы видите что-то похожее на это, необходимо проверить есть ли еще открытые экземпляры arduino ide или монитор порта. Оставьте одно окно и попробуйте еще раз. Эта ошибка возникает если порт занят другой программой (многие сталкивались с ней когда в фоне была запущена программа Cura).

Также если вы неправильно назвали папку или она отсутствует, среда предложит вам её создать, отвечайте Yes.

Если вы добавите в папку дата слишком большой файл (больше памяти esp) вам выдаст ошибку:

SPIFFS_write error(-10001): File system is full.
error adding file!
SPIFFS Create Failed!

При записи новых данных, все старые файлы в SPIFFS будут уничтожены!

Если все прошло успешно, вы увидите вывод:

Uploading stub...
Running stub...
Configuring flash size...
Auto-detected Flash size: 4MB
Compressed 1507328 bytes to 2933...
Writing at 0x00290000... (100 %)
Wrote 1507328 bytes (2933 compressed) at 0x00290000 in 0.0 seconds
Hash of data verified.

Теперь загрузите скетч и откройте монитор порта:

Вы должны увидеть содержимое файла test.txt

Попробуем придать этому немного смысла, создадим веб-страничку на которую esp будет выводить данные датчика bme280.

Для начала подключим датчик по стандартной схеме:

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

Создайте файл index.html со следующим содержимым:

<!DOCTYPE html>
<html lang="ru">
    <head>
        <title>BME280 TEST</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="bootstrap.min.css">
    </head>
    <body class="bg-dark text-light">
        <div class="position-absolute top-50 start-50 translate-middle">
            <div class="container-fluid text-dark">
                <center><h1 class="text-white">ESP32-BME280 Meteostation</h1><br></center>
                <div class="row row-cols-2 row-cols-md-2 g-2">
                    <div class="col">
                        <div class="card text-dark bg-danger mb-3">
                            <div class="card-body">
                                <h5 class="card-title">Температура</h5>
                                <p class="card-text"><h3>%TEMP%</h3></p>
                            </div>
                        </div>
                    </div>
                    <div class="col">
                        <div class="card text-dark bg-info mb-3">
                            <div class="card-body">
                                <h5 class="card-title">Влажность</h5>
                                <p class="card-text"><h3>%HUMID%</h3></p>
                            </div>
                        </div>
                    </div>
                    <div class="col">
                        <div class="card text-dark bg-success mb-3">
                            <div class="card-body">
                                <h5 class="card-title">Давление</h5>
                                <p class="card-text"><h3>%PRESS%</h3></p>
                            </div>
                        </div>
                    </div>
                    <div class="col">
                        <div class="card text-dark bg-warning mb-3">
                            <div class="card-body">
                                <h5 class="card-title">Высота над уровнем моря</h5>
                                <p class="card-text"><h3>%ALT%</h3></p>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </body>
    <script>
        setInterval(function ( ) {
            var xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function() {
                if (this.readyState == 4 && this.status == 200) {
                    document.getElementById("TEMP").innerHTML = this.responseText;
                }
            };
            xhttp.open("GET", "/temp", true);
            xhttp.send();
        }, 10000 ) ;

        setInterval(function ( ) {
            var xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function() {
                if (this.readyState == 4 && this.status == 200) {
                    document.getElementById("HUMID").innerHTML = this.responseText;
                }
            };
            xhttp.open("GET", "/humid", true);
            xhttp.send();
        }, 10000 ) ;

        setInterval(function ( ) {
            var xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function() {
                if (this.readyState == 4 && this.status == 200) {
                    document.getElementById("PRESS").innerHTML = this.responseText;
                }
            };
            xhttp.open("GET", "/press", true);
            xhttp.send();
        }, 10000 ) ;

        setInterval(function ( ) {
            var xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function() {
                if (this.readyState == 4 && this.status == 200) {
                    document.getElementById("ALT").innerHTML = this.responseText;
                }
            };
            xhttp.open("GET", "/alt", true);
            xhttp.send();
        }, 10000 ) ;
    </script>
</html>

В этом файле два раздела, первый это обычный html, а второй, это javascript который запрашивает переменные у esp.

Библиотеку bootstrap можно скачать тут.

Поместите два файла (index.html и bootstrap.min.css) в папку data и загрузите в SPIFFS.

Напишем скетч для обработки данных:

#include "SPIFFS.h" //Подключаем библиотеку
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h> //Библиотека датчика
#include <ESPAsyncWebServer.h> //Библиотека сервера
#include <WiFi.h> //wifi

const char* ssid = "12"; //Имя wifi сети
const char* password = "12345678"; //пароль wifi сети

#define SEALEVELPRESSURE_HPA (1013.25)
AsyncWebServer server(80);
Adafruit_BME280 bme; // I2C
String data;

void setup() {
 Serial.begin(115200);
 WiFi.begin(ssid, password); //Подключаемся к wifi
 if (WiFi.waitForConnectResult() != WL_CONNECTED) {
   return;
 }
  
 if(!SPIFFS.begin(true)){ //Инициализируем SPIFFS
  Serial.println("An Error has occurred while mounting SPIFFS");
  return;
 }

 bool status;
 status = bme.begin(0x76); //Инициализируем BME280
 if (!status) {
  Serial.println("Could not find a valid BME280 sensor, check wiring!");
  while (1);
 }
  
 server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ //Достаем index.html и отправляем на сервер
  request->send(SPIFFS, "/index.html", String(), false, processor);
 });
  
 server.on("/bootstrap.min.css", HTTP_GET, [](AsyncWebServerRequest *request){ //Подключаем стили
  request->send(SPIFFS, "/bootstrap.min.css", "text/css");
 });
  
 server.begin(); //сервер запускаем
}

void loop() {
 if (WiFi.status() == WL_CONNECTED) {
  delay(1000);
 }
}

String processor(const String& var){ //Функция которая передает переменные на сервер
 if (var == "TEMP"){
  return String(bme.readTemperature());
 }
 if (var == "HUMID"){
  return String(bme.readHumidity());
 }
 if (var == "PRESS"){
  return String(bme.readPressure() / 100.0F);
 }
 if (var == "ALT"){
  return String(bme.readAltitude(SEALEVELPRESSURE_HPA));
 }
 return String();
}

Загружаем, далее ip сканером либо в настройках роутера находим ip адрес и заходим по нему в браузер.

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

Чтобы сделать авто обновление данных, добавите строчку в index.html в <head>

<meta http-equiv="Refresh" content="1" />

На этом у меня все, Спасибо за внимание, удачи в ваших проектах!


Данная статья является собственностью Amperkot.ru. При перепечатке данного материала активная ссылка на первоисточник, не закрытая для индексации поисковыми системами, обязательна.


Поделиться: