Подключаем Web страницы FSWebServer

Настало время поместить в ESP8266 web страницы. Здесь нам поможет библиотека для работы с файловой системой FS.h

Библиотека входит в состав пакета и устанавливать ее не нужно. 

Arduino ESP8266 filesystem uploader: https://github.com/esp8266/arduino-esp8266fs-plugin
Скетч с папкой web сервера: WiFiManager-IR-FS

#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino

//needed for library
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager
#include <ESP8266SSDP.h>
#include <aREST.h>
#include <IRremoteESP8266.h>
#include <FS.h>

// Web интерфейс для устройства
ESP8266WebServer HTTP(80);
// aREST и сервер для него
WiFiServer SERVERaREST(8080);
aREST rest = aREST();
// Подключаем Ик передатчик к ноге
IRsend irsend(14);

File fsUploadFile;
void setup() {
 // Настраиваем вывод отладки
 Serial.begin(115200);
 //Включаем WiFiManager
 WiFiManager wifiManager;
 //Если не удалось подключиться клиентом запускаем режим AP
 // доступ к настройкам по адресу http://192.168.4.1
 wifiManager.autoConnect("AutoConnectAP");
 //если подключение к точке доступа произошло сообщаем
 Serial.println("connected...yeey :)");
 //настраиваем HTTP интерфейс
 HTTP_init();
 //запускаем SSDP сервис
 Serial.printf("Starting SSDP...\n");
 SSDP_init();
 Serial.printf("SSDP Ready!\n");
 // регистрируем в aRest функции irControl
 rest.function("irc", irControl);
 // включаем aREST и сервер к нему
 AREST_init();
 Serial.println("aREST Server started");
 // Включаем ИК передатчик
 irsend.begin();
}
void loop() {
 // put your main code here, to run repeatedly:
 HTTP.handleClient();
 delay(1);
 // Handle REST calls
 WiFiClient client = SERVERaREST.available();
 if (!client) {
 return;
 }
 while (!client.available()) {
 delay(1);
 }
 rest.handle(client);
}
void SSDP_init(void) {
 SSDP.setSchemaURL("description.xml");
 SSDP.setHTTPPort(80);
 SSDP.setName("FSWebServer");
 SSDP.setSerialNumber("001788102201");
 SSDP.setURL("/");
 SSDP.setModelName("FSWebServer");
 SSDP.setModelNumber("000000000001");
 SSDP.setModelURL("http://esp8266-arduinoide.ru/fswebserver/");
 SSDP.setManufacturer("Tretyakov Sergey");
 SSDP.setManufacturerURL("http://www.esp8266-arduinoide.ru");
 SSDP.begin();
}
void HTTP_init(void) {
 // SSDP дескриптор
 HTTP.on("/description.xml", HTTP_GET, []() {
 SSDP.schema(HTTP.client());
 });
 // Инициализация FFS
 SPIFFS.begin();
 {
 Dir dir = SPIFFS.openDir("/");
 while (dir.next()) {
 String fileName = dir.fileName();
 size_t fileSize = dir.fileSize();
 }
 }

 //HTTP страницы для работы с FFS
 //list directory
 HTTP.on("/list", HTTP_GET, handleFileList);
 //загрузка редактора editor
 HTTP.on("/edit", HTTP_GET, []() {
 if (!handleFileRead("/edit.htm")) HTTP.send(404, "text/plain", "FileNotFound");
 });
 //Создание файла
 HTTP.on("/edit", HTTP_PUT, handleFileCreate);
 //Удаление файла
 HTTP.on("/edit", HTTP_DELETE, handleFileDelete);
 //first callback is called after the request has ended with all parsed arguments
 //second callback handles file uploads at that location
 HTTP.on("/edit", HTTP_POST, []() {
 HTTP.send(200, "text/plain", "");
 }, handleFileUpload);
 //called when the url is not defined here
 //use it to load content from SPIFFS
 HTTP.onNotFound([]() {
 if (!handleFileRead(HTTP.uri()))
 HTTP.send(404, "text/plain", "FileNotFound");
 });
 //Обновление с web страницы
 HTTP.on("/update", HTTP_POST, []() {
 HTTP.sendHeader("Connection", "close");
 HTTP.sendHeader("Access-Control-Allow-Origin", "*");
 HTTP.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
 ESP.restart();
 }, []() {
 HTTPUpload& upload = HTTP.upload();
 if (upload.status == UPLOAD_FILE_START) {
 Serial.setDebugOutput(true);
 WiFiUDP::stopAll();
 Serial.printf("Update: %s\n", upload.filename.c_str());
 uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
 if (!Update.begin(maxSketchSpace)) { //start with max available size
 Update.printError(Serial);
 }
 } else if (upload.status == UPLOAD_FILE_WRITE) {
 if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
 Update.printError(Serial);
 }
 } else if (upload.status == UPLOAD_FILE_END) {
 if (Update.end(true)) { //true to set the size to the current progress
 Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
 } else {
 Update.printError(Serial);
 }
 Serial.setDebugOutput(false);
 }
 yield();
 });

 HTTP.begin();
}
void AREST_init(void) {
 // Определяем имя name и ИД ID устройства aREST
 rest.set_id("1");
 rest.set_name("aRest");
 // Запускаем сервер
 SERVERaREST.begin();
}
//Здесь функции для aREST обработки
// Отправка кода через IRremote
int irControl(String command) {
 // Get state from command
 unsigned long state = command.toInt();
 irsend.sendNEC(state, 36);
 return 1;
}
// Здесь функции для работы с файловой системой
String getContentType(String filename) {
 if (HTTP.hasArg("download")) return "application/octet-stream";
 else if (filename.endsWith(".htm")) return "text/html";
 else if (filename.endsWith(".html")) return "text/html";
 else if (filename.endsWith(".css")) return "text/css";
 else if (filename.endsWith(".js")) return "application/javascript";
 else if (filename.endsWith(".png")) return "image/png";
 else if (filename.endsWith(".gif")) return "image/gif";
 else if (filename.endsWith(".jpg")) return "image/jpeg";
 else if (filename.endsWith(".ico")) return "image/x-icon";
 else if (filename.endsWith(".xml")) return "text/xml";
 else if (filename.endsWith(".pdf")) return "application/x-pdf";
 else if (filename.endsWith(".zip")) return "application/x-zip";
 else if (filename.endsWith(".gz")) return "application/x-gzip";
 return "text/plain";
}
bool handleFileRead(String path) {
 if (path.endsWith("/")) path += "index.htm";
 String contentType = getContentType(path);
 String pathWithGz = path + ".gz";
 if (SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)) {
 if (SPIFFS.exists(pathWithGz))
 path += ".gz";
 File file = SPIFFS.open(path, "r");
 size_t sent = HTTP.streamFile(file, contentType);
 file.close();
 return true;
 }
 return false;
}
void handleFileUpload() {
 if (HTTP.uri() != "/edit") return;
 HTTPUpload& upload = HTTP.upload();
 if (upload.status == UPLOAD_FILE_START) {
 String filename = upload.filename;
 if (!filename.startsWith("/")) filename = "/" + filename;

 fsUploadFile = SPIFFS.open(filename, "w");
 filename = String();
 } else if (upload.status == UPLOAD_FILE_WRITE) {
 //DBG_OUTPUT_PORT.print("handleFileUpload Data: "); DBG_OUTPUT_PORT.println(upload.currentSize);
 if (fsUploadFile)
 fsUploadFile.write(upload.buf, upload.currentSize);
 } else if (upload.status == UPLOAD_FILE_END) {
 if (fsUploadFile)
 fsUploadFile.close();
 }
}
void handleFileDelete() {
 if (HTTP.args() == 0) return HTTP.send(500, "text/plain", "BAD ARGS");
 String path = HTTP.arg(0);
 if (path == "/")
 return HTTP.send(500, "text/plain", "BAD PATH");
 if (!SPIFFS.exists(path))
 return HTTP.send(404, "text/plain", "FileNotFound");
 SPIFFS.remove(path);
 HTTP.send(200, "text/plain", "");
 path = String();
}
void handleFileCreate() {
 if (HTTP.args() == 0)
 return HTTP.send(500, "text/plain", "BAD ARGS");
 String path = HTTP.arg(0);
 if (path == "/")
 return HTTP.send(500, "text/plain", "BAD PATH");
 if (SPIFFS.exists(path))
 return HTTP.send(500, "text/plain", "FILE EXISTS");
 File file = SPIFFS.open(path, "w");
 if (file)
 file.close();
 else
 return HTTP.send(500, "text/plain", "CREATE FAILED");
 HTTP.send(200, "text/plain", "");
 path = String();
}
void handleFileList() {
 if (!HTTP.hasArg("dir")) {
 HTTP.send(500, "text/plain", "BAD ARGS");
 return;
 }
 String path = HTTP.arg("dir");
 Dir dir = SPIFFS.openDir(path);
 path = String();
 String output = "[";
 while (dir.next()) {
 File entry = dir.openFile("r");
 if (output != "[") output += ',';
 bool isDir = false;
 output += "{\"type\":\"";
 output += (isDir) ? "dir" : "file";
 output += "\",\"name\":\"";
 output += String(entry.name()).substring(1);
 output += "\"}";
 entry.close();
 }
 output += "]";
 HTTP.send(200, "text/json", output);
}

 

49 мыслей о “Подключаем Web страницы FSWebServer”

    1. Скорее всего так и есть. Я как то пытался пользоваться новой версией тоже не шло. Поэтому остаюсь пока на 1.6.5. Даже 1.6.6 пока не пробовал.

      1. Кстати я понял почему не компилируется в среде выше 1,6,5
        Разработчики почему-то вспомнили про синтаксис языка Си
        Все функции нужно объявлять перед setup
        По этому ваш код и не компилируются в каком виде есть.

        1. Да, это так. В 1.6.8 версии такой код снова компилируется, но теперь там другие библиотеки валятся. Пока остаюсь на 1.6.5.

    2. В описалове к esp8266 плагину для ардуино написано, что стабильная версия (2.0.0) работает в 1.6.5, а текущая бета (2.1.0-rc1/) в 1.6.7. Поэтому надо либо ардуино поставить версии 1.6.5, либо плагин с гитхаба последней версии скачать.
      Тут описание установки: https://github.com/esp8266/Arduino/blob/master/README.md

      Кстати в последней версии интересные добавления, касающиеся вывода отладочной инфы. Прямо из среды ардуино можно выбирать какую инфу посылать в серийный порт а какую нет. Причем есть отладка разных методов типа post и т.п. Очень удобно!

  1. Привет!

    Очень полезное видео. Как раз сам добрался до wifi manager. Правда все делаю в eclipse. Для меня привычнее.
    Очень полезно про обновление прошивки по воздуху.
    Есть вопрос. WifiManager работает хорошо, но не получается его вызвать принудительно, когда модуль уже подключился к сети, чтобы переподключить его к другой, например по кнопке:

    if ( digitalRead(TRIGGER_PIN) == LOW ) {
    WiFiManager wifiManager;
    wifiManager.startConfigPortal(“OnDemandAP”);
    Serial.println(“connected…yeey :)”);
    }
    Почему-то виснет модуль на этом.

    Было бы интересно увидеть видео на эту тему!

    У меня под инфраструктуру умного дома выделена отдельная вайфай сеть, поэтому и вопрос возник.
    Спасибо!

  2. Здравствуйте. Подскажите способы как обмениваться данными между страницей и контроллером, судя по примеру FSBrowser, получаем данные с помощью javascript функции XMLHttprequest, а отправка через get запрос? может есть еще варианты?

    1. Как раз в примере FSBrowser, javascript функции XMLHttprequest не используется. Там простые HTML страницы. AJAX позволяет обмениваться данными между страницей и контроллером, там как раз javascript функции. Буквально сегодня будет пример об использовании AJAX.

  3. Почему же, может правда примеры разные. В папке data лежит index.htm в нем используется XMLHttprequest.
    Вот часть кода из этого страницы:
    function loadValues(){
    if(!running) return;
    var xh = new XMLHttpRequest();
    xh.onreadystatechange = function(){
    if (xh.readyState == 4){
    if(xh.status == 200) {
    var res = JSON.parse(xh.responseText);
    heap.add(res.heap);
    temp.add(res.analog);
    digi.add(res.gpio);
    if(running) setTimeout(loadValues, reloadPeriod);
    } else running = false;
    }
    };
    xh.open(“GET”, “/all”, true);
    xh.send(null);
    };

    Они парсят страницу /all, заведомо подготовленную в скетче контроллера. Но мне пока не понятно как обратно отправить данные, с кнопки например..

  4. Здравствуйте. Вы не подскажете, почему ,при запросе 192.168.1.108/edit в списке файлов обрезаны имена? И при загрузке через /edit именя файлов так же обрезаются….

    affixm~1.js
    font-a~1.css
    fontaw~1.otf
    fontaw~1.eot
    fontaw~1.svg
    fontaw~1.ttf
    fontaw~1.wof
    fontaw~2.wof
    jquery~1.js

  5. Я извиняюсь, не совсем туда написал. Заливаю SDWEBSERVER_V2. esp8266 amica.
    Скидываю файлы на sd карту.Захожу через /edit и вижу измененные имена =(

    1. Скорее всего в библиотеке работы с SD картами есть ограничение на имена и расширения. Так как для Arduino такие ограничения были.

      1. Видимо да. Поскольку все имена обрезаны – 8 символов до точки. Но где изменить это в коде, так и не увидел =(

          1. Покопав просторы интернета, выяснилось, что подобное ограничение(8 символов и три символа расширения файла), из – за файловой системы и библиотеки SD. Нашел библиотеку SDFat. Она может работать с длинными именами файлов.Было сказано, что ее можно использовать вместо библиотеки SD. Но после ее применения страницы загружаться перестали, грузится какой – то мусор…..
            Пока тупик.

  6. Приветствую!
    Можно ли подключить к проекту в IDE уже готовый файл, который в ходе заливки будет положен в SPIFFS, и к которому можно будет обращаться из прошивки?
    Вставлять его inline теоретически можно, но сами понимаете – криво.

    1. Вопрос не совсем понятен. С одной стороны можно сделать сервис для загрузки образа диска из веб интерфейса одним файлом. С другой стороны любой файл в файловой системе доступен из скетча. Каждый отдельный файл в файловую систему тоже можно загрузить через web интерфейс.

  7. Здравствуйте. Пробовал загрузить Ваш скетч, на стадии компиляции выдаётся ошибка:
    exit status 1
    ‘HTTP_init’ was not declared in this scope

    И подсвечивает в скетче строку:
    HTTP_init();

    Установки у меня такие:
    Arduino: 1.6.8 (Windows 7), Плата:”Generic ESP8266 Module, 80 MHz, 40MHz, DIO, 115200, 4M (1M SPIFFS), ck, Serial, None”

    1. Работает только с IDE 1.6.5 Версия ESP ядра 2.0.0. В более поздних версиях SSDP не работает с Windows в Android устройствах работает. Баг в библиотеке.

  8. WM:
    *WM: AutoConnect
    *WM: Connecting as wifi client…
    *WM: Using last saved values, should be faster
    *WM: Connection result:
    *WM: 3
    *WM: IP Address:
    *WM: 192.168.0.7
    connected…yeey 🙂
    ets Jan 8 2013,rst cause:4, boot mode:(1,7)
    wdt reset
    выдает в мониторе порта после загрузки, подскажите в чем может быть проблема?

    1. Это происходит с моем сктче? Версия Arduino IDE и esp866 сообщите пожалуйста. Проверю и буду готов ответить.

    2. Cкорее всего у вашей ESP недостатчно флеш памяти. B моем варианте била микросхема winbond 25q80bvsig, а ето 8Mbit(1MB) – 1M(512K SPIFS)

  9. Добрый день.
    Подскажите, а как добавить страницу с индикацией состояния? (вывести показания датчиков, состояния реле и т.д.) Т.е как данные от esp прикрутить к html странице?

  10. Добрый день!
    Подскажите пожалуйста в чем может быть дело, все работало, но после попытки обновления “по воздуху” произошла ошибка.
    Теперь ESP выдает ошибку
    exception 29. пробовал перепрошивать, не помогает.
    Как с этим можно бороться?

  11. Добрый день !
    Arduino ide 1.6.8, библиотека 2.0.0.
    Не работает ссылка edit , т.е. загружается ну ничего не отображает, просто рамка отделяющая редактор от списка файлов и строка меню , но никаких надписей и т.п. нет :'( подскажите что не так!
    Спасибо автору , на его примерах освоил намного ajax ))
    Сделал нечто подобное pytt только через веб интерфейс )))

  12. Пробовал , итог оказался тот же 🙁 а причину нашел ))) скачал с другого места фалы edit и скрипты , все заработало !

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

  14. Такая проблема: после заливки файлов в память ESP обнаружил, что кое-где в этих файлах есть ошибки. Исправив их на компьютере, попытался перезалить, но, как оказалось, остаются старые файлы, то есть копирование без замены. Хорошо, что можно редактировать тексты прямо на ESP. А картинки, архивы как перезаливать?

  15. Сегодня случилось чудо. Я взял здесь скетч и папку data, а потом залил их по инструкции в отладочный “бутерброд” на ESP12-E, недавно приобретенный у китайцев. Сервер запустился и подключился к моему wifi, а ведь я ничего нигде не менял и не прописывал свои ssid и пароль. Я просто сразу не нашел куда их прописать и попробовал просто так как было. Теперь я в недоумении – откуда это чудо берет данные моей сети? Я, правда, перед этим заливал туда другие примеры тоже с сервером и криво .

    1. Блин, такая же фигня произошла… Сидел голову ломал, откуда. Решил, что так как прежде указывал переменные с именем и паролем точки доступа, они сохранились либо в библиотеке либо как то менеджер плат на себя взял эту функцию… надо бы поразмыслить)

  16. Подскажи пожалуйста, не компилируется твой скетч:

    C:\Users\anton\Downloads\WiFiManager-IR-FS\WiFiManager-IR-FS.ino:6:72: fatal error: WiFiManager.h: No such file or directory

    #include //https://github.com/tzapu/WiFiManager

    compilation terminated.

    При этом файлик по ссылки https://github.com/tzapu/WiFiManager
    я скачивал, WiFiManager.h куда только не клал…

    1. Вроде разобрался, библиотеки надо подключать оказывается:) А я не знал как это проделывать в среде Arduino (

  17. Здравствуйте! Спасибо большое за то что делитесь полезной информацией. Очень понравилась идея обновления прошивки, через web интерфейс. Все делаю по инструкции, но почему-то прошивка не завершается. Делаю экспорт бинарного файла, создается файл объемом 367kb , при попытке обновить прошивку процесс зависает.
    Вот что в терминале:
    Update: SMART_ROOM_AJAX.ino.nodemcu.bin
    sleep disable
    Update Success: 374944
    Rebooting…

    ets Jan 8 2013,rst cause:2, boot mode:(3,6)

    load 0x4010f000, len 3656, room 16
    tail 8
    chksum 0x0c
    csum 0x0c
    v9c56ed1f
    @cp:0
    ld
    e:
    ets Jan 8 2013,rst cause:3, boot mode:(3,6)

    ets_main.c

    Контроллер перестает отвечать, опять загружаю прошивку через Arduino IDE, все работает.

    У вас на сайте, нашел файл blank4.bin (объем 4096kb) и попробовал его “скормить” через web интерфейс и все получилось.

    Получается, что дело в бинарном файле, который я экспортирую из Arduino IDE (версия Arduino IDE 1.8.12)? Интересно, как создан файл blank4.bin и его отличие от того, что я экспортирую?

    Спасибо!

    1. Так и не удалось обновить скетч по воздуху, ни через OTA Arduino IDE ни через web. Чтение log показало, что файл blank4.bin просто не влазит по размеру и прошивка прерывается, и просто создавалась иллюзия.

      Переключение параметров памяти DOUT-QOUT тоже не дает результатов.

      Прошивка самого контроллера, тоже не помогла.

Добавить комментарий для admin Отменить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.