цифровая электроника
вычислительная техника
встраиваемые системы

 

Хостинг веб-страницы с помощью ESP32 и SD-карты

Автор: Mike(admin) от 20-03-2020, 23:55

Одна из самых больших проблем в создании объектов Интернета вещей – первая конфигурация. Открытие IDE просто для изменения сетевых учетных данных или HTML неудобно и занимает много времени. Другими вариантами будет выделенное мобильное приложение, которое вы будете использовать, вероятно, только один раз, или можно использовать CLI через последовательный порт.


Хостинг веб-страницы с помощью ESP32 и SD-карты

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


Файл конфигурации, использующий формат JSON, очень прост и выглядит так:



{	
    "husarnet":{
        "hostname":"esp32template",
        "joincode":"fc94:b01d:1803:8dd8:b293:5c7d:7639:932a/xxxxxxxxxxxxxxxxxxxxxx" 
    },	
    "wifi":[
        {
            "ssid":"mywifinet123",
            "pass":"qwerty"
        },
        {
            "ssid":"officenet",
            "pass":"admin1"		
        },
        {
            "ssid":"iPhone(Johny)",
            "pass":"12345"
        }
    ]
}

Здесь есть два блока. Первый – учетные данные Husarnet. Husarnet позволяет вам получать доступ к своим «вещам» через Интернет, как если бы они находились в одной сети LAN. Второй – учетные данные WiFi. Укажите одну или несколько пар SSID-пароля, поэтому вам не нужно будет менять конфигурацию, если вы перенесете свои «вещи» куда-нибудь в другую сеть.


Редактировать HTML-файлы как таблицы символов C/C ++ не очень удобно. Вот почему стоит переместить HTML-файл на SD-карту. Весь интерфейс между веб-страницей и оборудованием осуществляется через WebSockets, поэтому вам не нужно анализировать HTTP-запросы, и ваш код станет намного чище. WebSockets также намного быстрее, чем HTTP-запросы.


Также в данном случае есть несколько преимуществ подключения к Интернету через VPN. Во-первых, очень простая настройка ваших устройств (вам просто нужно указать код соединения и придумать название для вашего устройства). Просто взгляните на JSON. Joincode генерируется только один раз, и вы можете использовать его для соединения вашего компьютера и устройств ESP32. Во-вторых, легко получить доступ к вашему устройству, как это было бы в вашей локальной сети. Веб-сайт размещен на ESP32, и вы получаете доступ к этому веб-сайту напрямую через Интернет. Husarnet предоставляет только прозрачный слой подключения, работающий как в вашей локальной сети, так и через Интернет одинаково. Husarnet – это в основном VPN. И, в-третьих, низкая задержка. Ваш компьютер (с установленным клиентом Husarnet) и ESP32 подключены однорангово. Инфраструктура Husarnet помогает вашим устройствам находить друг друга только через Интернет и не используется для пересылки пользовательских данных после установления P2P-соединения. Если ваши устройства находятся в одной сети, вы будете управлять им так же, как по локальной сети.


Демонстрационный пример действительно простой.


Хостинг веб-страницы с помощью ESP32 и SD-карты

В нем можно управлять светодиодом, подключенным к ESP32, с помощью кнопки в веб-интерфейсе. ESP32 отправляет значение счетчика каждые 100 мс. ESP32 отправляет состояние кнопки, чтобы изменить цвет точки в веб-интерфейсе.


Схема подключения для данного примера приведена далее.


Хостинг веб-страницы с помощью ESP32 и SD-карты

Подключите кнопку между контактом G10 и GND. Подключите светодиод с последовательным резистором между контактом G11 и заземлением. Подключите модуль SD-карты к вашей плате ESP32: если у вас уже есть плата ESP32 для девайса с гнездом для SD-карты, могут быть использованы другие контакты, поэтому вам придется изменить также программную часть.


Вам необходимо отформатировать SD-карту в формате FAT16/FAT32. После этого скопируйте файлы setting.js и index.htm на SD-карту, которую вы только что отформатировали. Измените файл settings.js, указав свои собственные учетные данные сети Wi-Fi и учетные данные Husarnet. Чтобы найти свой код для подключения к Husarnet нужно зарегистрироваться на https://app.husarnet.com, создать новую сеть или выбрать существующую, в выбранной сети нажмите кнопку Add element и перейдите на вкладку «join code». Сохраните ваши файлы и подключите SD-карту к вашему ESP32.


Чтобы запустить проект, откройте Arduino IDE и выполните следующие действия. Установите пакет Husarnet для ESP32. Для этого нужно открыть Файл – Настройки (File - Preferences), в поле Дополнительные URL-адреса менеджера плат (Additional Board Manager URLs) добавьте ссылку https://files.husarion.com/arduino/beta/package_esp32_husarnet_index.json. Откройте Менеджер плат (Boards Manager), Найдите esp32-husarnet. Нажмите кнопку Установить (Install). Выберите ESP32 Dev Board. Откройте Инструменты – Плата (Tools - Board), выберите ESP32 Dev Module в разделе «ESP32 Arduino (Husarnet)».


Теперь нужно установить библиотеку ArduinoJson. Откройте Инструменты – Управление библиотеками (Tools – Manage Libraries), найдите ArduinoJson, нажмите кнопку установить. После этого следует установить библиотеку arduinoWebSockets (Husarnet fork). Загрузите https://github.com/husarnet/arduinoWebSockets в виде ZIP-файла (это совместимый с Husarnet форк arduinoWebSockets от Links2004 (Markus)). Откройте Скетч – Подключить библиотеку – Добавить ZIP библиотеку (Sketch – Include Library – Add .ZIP Library). Выберите файл arduinoWebSockets-master.zip, который вы только что скачали, и нажмите кнопку Открыть.


После этого можно приступить к программированию платы ESP32. Откройте проект ESP32-web-template-sd.ino, загрузить проект в свою плату ESP32. Откройте WebUI: есть два варианта:


  • войдите в свою учетную запись на https://app.husarnet.com, найдите устройство esp32template, которое вы только что подключили, и нажмите веб-кнопку UIbutton. Вы также можете щелкнуть элемент esp32template, чтобы открыть «Element settings» и выбрать «Make the Web UI public», если вы хотите иметь общедоступный адрес. В таком сценарии прокси-серверы Husarnet используются для предоставления вам веб-интерфейса.
  • вариант P2P – добавьте ваш ноутбук в ту же сеть Husarnet, что и плата ESP32. В этом сценарии прокси-серверы не используются, и вы подключаетесь к ESP32 с очень низкой задержкой напрямую, без переадресации портов на вашем маршрутизаторе. В настоящее время доступен только Linux-клиент, поэтому откройте свой Linux-терминал и введите для установки Husarnet «$ curl https://install.husarnet.com/install.sh | sudo bash». В «$ husarnet join XXXXXXXXXXXXXXXXXXXXXXX» замените XXX вашим собственным кодом соединения.

Чтобы найти свой код соединения, нужно зарегистрироваться или войти на https://app.husarnet.com, создать новую сеть или выбрать существующую, в выбранной сети нажмите кнопку Add element и перейдите на вкладку «join code».


На этом этапе ваш ESP32 и ваш ноутбук находятся в одной сети VLAN. Лучшая поддержка хоста – в веб-браузере Mozilla Firefox (другие браузеры также работают, но вы должны использовать IPv6-адрес вашего устройства, который вы найдете по адресу https://app.husarnet.com) и набрать: http://esp32template:8000, после этого вы должны увидеть веб-интерфейс для управления вашим ESP32.



#include <WiFi.h>
#include <WiFiMulti.h>

#include <WebSocketsServer.h>
#include <Husarnet.h>
#include <ArduinoJson.h>

#include "FS.h"
#include "SD.h"
#include <SPI.h>

#define HTTP_PORT 8000
#define WEBSOCKET_PORT 8001

const int BUTTON_PIN = 10;
const int LED_PIN = 11;

const int SDCS_PIN = 22;

typedef struct {
  char ssid[30];
  char pass[30];
} WifiNetwork;

struct WifiConfig {
  WifiNetwork net[10];
};

struct HusarnetConfig {
  char hostname[30];
  char joincode[30];
};

WifiConfig wifi_conf;
HusarnetConfig husarnet_conf;

int wifi_net_no = 0;

WiFiMulti wifiMulti;

WebSocketsServer webSocket = WebSocketsServer(WEBSOCKET_PORT);
HusarnetServer server(HTTP_PORT);

StaticJsonBuffer<200> jsonBufferTx;
StaticJsonBuffer<100> jsonBufferRx;
StaticJsonBuffer<500> jsonBufferSettings;

JsonObject& rootTx = jsonBufferTx.createObject();

char* html;
bool wsconnected = false;

void onWebSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) {
  switch (type) {
    case WStype_DISCONNECTED:
      {
        wsconnected = false;
        Serial.printf("ws [%u] Disconnected\r\n", num);
      }
      break;
    case WStype_CONNECTED:
      {
        wsconnected = true;
        Serial.printf("ws [%u] Connection from Husarnet\r\n", num);
      }
      break;

    case WStype_TEXT:
      {
        Serial.printf("ws [%u] Text:\r\n", num);
        for (int i = 0; i < length; i++) {
          Serial.printf("%c", (char)(*(payload + i)));
        }
        Serial.println();

        JsonObject& rootRx = jsonBufferRx.parseObject(payload);
        jsonBufferRx.clear();

        uint8_t ledState = rootRx["led"];

        Serial.printf("LED state = %d\r\n", ledState);
        if (ledState == 1) {
          digitalWrite(LED_PIN, HIGH);
        }
        if (ledState == 0) {
          digitalWrite(LED_PIN, LOW);
        }
      }
      break;

    case WStype_BIN:
    case WStype_ERROR:
    case WStype_FRAGMENT_TEXT_START:
    case WStype_FRAGMENT_BIN_START:
    case WStype_FRAGMENT:
    case WStype_FRAGMENT_FIN:
    default:
      Serial.printf("ws [%u] other event\r\n", num);
      break;
  }

}

void writeFile(fs::FS &fs, const char * path, const char * message);

void taskWifi( void * parameter );
void taskHTTP( void * parameter );
void taskWebSocket( void * parameter );
void taskStatus( void * parameter );

void setup()
{
  Serial.begin(115200);

  pinMode(BUTTON_PIN, INPUT_PULLUP);

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);

  SD.begin(SDCS_PIN);
  if (!SD.begin(SDCS_PIN)) {
    Serial.println("Attach SD card and restart");
    return;
  }

  uint8_t cardType = SD.cardType();
  switch (cardType) {
    case CARD_NONE:
    case CARD_UNKNOWN:
      Serial.println("Attach SD card and restart");
      return;
    case CARD_SD:
      Serial.println("SD card detected");
      break;
    case CARD_SDHC:
      Serial.println("SDHC card detected");
      break;
  }

  File file = SD.open("/index.htm");
  if (!file) {
    Serial.println("index.htm file doesn't exist. Save it on SD card and restart.");
    return;
  }
  Serial.printf("index.htm file size: %d\r\n", file.size());
  html = new char [file.size() + 1];
  file.read((uint8_t*)html, file.size());
  html[file.size()] = 0;
  file.close();

  file = SD.open("/settings.js");
  if (!file) {
    Serial.println("settings.js file doesn't exist. Save it on SD card and restart.");
    return;
  }
  Serial.printf("settings.js file size: %d\r\n", file.size());
  char* setting_json = new char [file.size() + 1];
  file.read((uint8_t*)setting_json, file.size());
  setting_json[file.size()] = 0;
  file.close();

  JsonObject& rootSettings = jsonBufferSettings.parseObject(setting_json);
  String husarnet_hostname = rootSettings["husarnet"]["hostname"];
  String husarnet_joincode = rootSettings["husarnet"]["joincode"];
  strcpy (husarnet_conf.hostname, husarnet_hostname.c_str());
  strcpy (husarnet_conf.joincode, husarnet_joincode.c_str());
  Serial.println();
  Serial.println("husarnet");
  Serial.printf("hostname: %s\r\n", husarnet_conf.hostname);
  Serial.printf("joincode: %s\r\n", husarnet_conf.joincode);

  JsonArray& wifinetworks = rootSettings["wifi"];
  wifi_net_no = wifinetworks.size();
  Serial.println();
  Serial.printf("number of Wi-Fi networks: %d\r\n", wifi_net_no);

  if (wifi_net_no > 10) {
    Serial.println("too many WiFi networks on SD card, 10 max");
    wifi_net_no = 10;
  }

  for (int i = 0; i < wifi_net_no; i++) {
    String ssid = wifinetworks[i]["ssid"];
    String pass = wifinetworks[i]["pass"];
    wifiMulti.addAP(ssid.c_str(), pass.c_str());

    Serial.printf("WiFi %d: SSID: \"%s\" ; PASS: \"%s\"\r\n", i, ssid.c_str(), pass.c_str());
  }

  xTaskCreate(
    taskWifi,
    "taskWifi",
    10000,
    NULL,
    1,
    NULL);

  xTaskCreate(
    taskHTTP,
    "taskHTTP",
    10000,
    NULL,
    2,
    NULL);

  xTaskCreate(
    taskWebSocket,
    "taskWebSocket",
    10000,
    NULL,
    1,
    NULL);

  xTaskCreate(
    taskStatus,
    "taskStatus",
    10000,
    NULL,
    1,
    NULL);
}

void taskWifi( void * parameter ) {
  while (1) {
    uint8_t stat = wifiMulti.run();
    if (stat == WL_CONNECTED) {
      Serial.println("");
      Serial.println("WiFi connected");
      Serial.println("IP address: ");
      Serial.println(WiFi.localIP());

      Husarnet.join(husarnet_conf.joincode, husarnet_conf.hostname);
      Husarnet.start();

      server.begin();

      while (WiFi.status() == WL_CONNECTED) {
        delay(500);
      }
    } else {
      Serial.printf("WiFi error: %d\r\n", (int)stat);
      delay(500);
    }
  }
}

void taskHTTP( void * parameter )
{
  String header;

  while (1) {
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
    }

    HusarnetClient client = server.available();

    if (client) {
      Serial.println("New Client.");
      String currentLine = "";
      Serial.printf("connected: %d\r\n", client.connected());
      while (client.connected()) {

        if (client.available()) {
          char c = client.read();
          Serial.write(c);
          header += c;
          if (c == '\n') {
            if (currentLine.length() == 0) {
              client.println("HTTP/1.1 200 OK");
              client.println("Content-type:text/html");
              client.println("Connection: close");
              client.println();

              client.println(html);
              break;
            } else {
              currentLine = "";
            }
          } else if (c != '\r') {
            currentLine += c;
          }
        }
      }

      header = "";

      client.stop();
      Serial.println("Client disconnected.");
      Serial.println("");
    } else {
      delay(200);
    }
  }
}

void taskWebSocket( void * parameter )
{
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
  webSocket.begin();
  webSocket.onEvent(onWebSocketEvent);

  while (1) {
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
    }
    webSocket.loop();
    delay(1);
  }
}

void taskStatus( void * parameter )
{
  String output;
  int cnt = 0;
  uint8_t button_status = 0;
  while (1) {
    if (wsconnected == true) {
      if (digitalRead(BUTTON_PIN) == LOW) {
        button_status = 1;
      } else {
        button_status = 0;
      }
      output = "";

      rootTx["counter"] = cnt++;
      rootTx["button"] = button_status;
      rootTx.printTo(output);

      Serial.print(F("Sending: "));
      Serial.print(output);
      Serial.println();

      webSocket.sendTXT(0, output);
    }
    delay(100);
  }
}

void loop()
{
  delay(5000);
}



© digitrode.ru


Теги: ESP32, Интернет вещей




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

Комментарии:

Оставить комментарий