Подключаем 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);
}

 

Поддержать сайт

48 мыслей о “Подключаем 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 тоже не дает результатов.

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

Добавить комментарий

Ваш адрес email не будет опубликован.

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