Изпълнение на WebServerSecure и PubSubClient на ESP8266

Написах скица за ESP8266. Тази скица чете някои сензорни данни и ги публикува чрез MQTT. Освен това искам да позволя на уеб сървър да предоставя същите данни като HTML или JSON уеб услуга.

Публикуването на MQTT се задейства чрез таймер на TaskScheduler.

И двете функции, MQTT и уеб сървърът, работят сами за себе си, но за съжаление не заедно. Ето една опростена скица:

#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServerSecure.h>
#include <PubSubClient.h>
#include <TaskScheduler.h>

#include <My_WLAN.h>                          // provices connection to local WLAN and network settings

const char DNS_NAME[] = "myserver.local";
const int  HTTPS_PORT = 443;                  // HTTPS
const char MQTT_SVR[] = "myserver.local";
const unsigned int MQTT_PORT = 8883;          // MQTTS

WiFiClientSecure  wifiClient;
PubSubClient      mqttClient(wifiClient);     // MQTT client instance
ESP8266WebServerSecure  server(HTTPS_PORT);   // web server instance

void t1Callback(void);                        // callback method prototypes
Task              t1(60000, TASK_FOREVER, &t1Callback);   // main loop task
Scheduler         timer;                      // task scheduler

static const uint8_t SVR_FINGERPRINT[20] PROGMEM = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20 };

static const char deviceCert[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
[... certificate ...]
-----END CERTIFICATE-----
)EOF";

static const char deviceKey[] PROGMEM = R"EOF(
-----BEGIN RSA PRIVATE KEY-----
[... key ...]
-----END RSA PRIVATE KEY-----
)EOF";


/* *****************************
      MQTT_connect
 * *****************************/
void MQTT_connect()
{
  int attempt = 0;
  /* loop until reconnected */
  while (!mqttClient.connected() && attempt < 10) {
    attempt++;
    Serial.print("Attempting MQTT connection ("); Serial.print(attempt); Serial.print(")...");

    mqttClient.setServer(MQTT_SVR, MQTT_PORT);

    if (mqttClient.connect(DNS_NAME)) {
      Serial.println("success");

    } else {
      Serial.print("failed, status code = "); Serial.print(mqttClient.state());
      Serial.println(". - Try again in 5 seconds...");
      delay(5000);
    }
  }
}


/* *****************************
      Web Server handleRoot
 * *****************************/
void handleRoot() {
  digitalWrite(LED_BUILTIN, LOW); // on

  Serial.println("WebServer ROOT");
  server.send(200, "text/html", "WebServer ROOT");

  digitalWrite(LED_BUILTIN, HIGH); // off
}

/* *****************************
      Web Server handleNotFound
 * *****************************/
void handleNotFound() {
  digitalWrite(LED_BUILTIN, LOW); // on

  String message = "File not found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";

  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }

  server.send(404, "text/plain", message);

  digitalWrite(LED_BUILTIN, HIGH); // off
}


/* *************************
      MQTT_publish_something
 * *************************/
void MQTT_publish_something() {
  digitalWrite(LED_BUILTIN, LOW); // on

  char payload[30] = "some_payload_data";

  if (!mqttClient.publish("MQTT/Test", payload, true)) {  // retain message
    Serial.println("MQTT message lost!");
  }

  digitalWrite(LED_BUILTIN, HIGH); // off
}


/* *************************
   t1: main timer (callback)
 * *************************/
void t1Callback() {
  my.WiFi_connect();    // check and re-connect to WLAN (in My_WLAN.h)

  if (WiFi.status() == WL_CONNECTED) {
    MQTT_connect();

    MQTT_publish_something();
  }
}


/* *************************
      setup
 * *************************/
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);     // internal LED
  digitalWrite(LED_BUILTIN, HIGH);  // off

  /* -----------------------
        open Serial        |
     ----------------------- */
  Serial.begin(74880);
  while (!Serial);   // wait for Serial being ready

  /* -----------------------
        connect to WLAN    |
     ----------------------- */
  my.WiFi_connect();  // this is connecting to WLAN & error handling (in My_WLAN.h)
  wifiClient.setFingerprint(SVR_FINGERPRINT);

  /* -----------------------
        set mDNS           |
     ----------------------- */
  if (MDNS.begin(DNS_NAME)) {
    Serial.printf("mDNS responder started for %s\n", DNS_NAME);
    MDNS.addService("https", "tcp", HTTPS_PORT);   // add service to MDNS-SD
    MDNS.addService("mqtt",  "tcp", MQTT_PORT);
  } else
    Serial.println("Error setting up mDNS responder!");

  /* -----------------------
        start HTTPS server |
     ----------------------- */
  server.getServer().setRSACert(new X509List(deviceCert), new PrivateKey(deviceKey));

  server.on("/", handleRoot);                 // standard HTML root
  server.onNotFound(handleNotFound);
  server.begin();

  Serial.println("HTTPS server started.");
  Serial.println();


  /* -----------------------
        start timer        |
     ----------------------- */
  timer.init();
  timer.addTask(t1);
  // line 177:
  timer.enableAll();
}

void loop() {
  MDNS.update();

  // line 184:
  server.handleClient();

  mqttClient.loop();

  timer.execute();
}

Изпълнението на MQTT работи добре и публикува данни (използвам брокера за комари). Изпълнението на уеб сървъра (https://...) също работи добре, ако се коментира ред 177 (така че MQTT прави не се задейства).

Когато и двете функции са активни, веднага щом първото MQTT съобщение е изпратено, уеб сървърът вече не отговаря. Получавам PR_END_OF_FILE_ERROR в FF и ERR_CONNECTION_CLOSED в Chrome.

Предполагам, че тези библиотеки по някакъв начин се забъркват помежду си или че нещо се обърква със сертификатите. Пръстовият отпечатък обаче принадлежи на сървъра, работещ с mosquitto, докато сертификатът X509 принадлежи на уеб сървъра, работещ на ESP8266. Това са две различни машини и нямат нищо общо.

Всяка идея е добре дошла.


person Thomas Z    schedule 20.04.2020    source източник
comment
Не е директен отговор на вашия въпрос, но защо да не разполагате с уеб сървъра някъде другаде? IOT Правило #1: Оставете сензорите да усетят, оставете нещо друго да докладва. Имам редица сензори, които публикуват показанията си, но поставих моя уеб сървър на моя Docker сървър. Имам друг контейнер на Docker, който събира данните и след това го прави достъпен като API. След това HTML страница чете данните чрез JQuery: github.com/john2exonets/MqttAPIServer   -  person JD Allen    schedule 20.04.2020
comment
Благодаря, JD Allen, също имах тази идея и това би било валидно алтернативно решение. Но не трябва ли да е възможно да стартирате и двете на една машина или ако не, защо не?   -  person Thomas Z    schedule 22.04.2020
comment
Разбира се, можете да стартирате и двете на една машина... но първия път, когато трябва да плъзнете този сензор обратно от мястото, където беше, просто за да запишете някакъв нов код, за да добавите интервал на уеб страница, бързо осъзнавате причините за да поддържам сензорите да усещат и да не правя нищо допълнително ;) След около четири пъти, когато трябваше да се изкача и да извлека възел на сензор/уеб сървър, реших да улесня живота си и да разделя частта на уеб сървъра в Docker контейнер, където Можех да правя всички промени, които харесвах, докато седях на бюрото си ;)   -  person JD Allen    schedule 22.04.2020


Отговори (1)


Подозирам, че и двете библиотеки използват порт 443 и можете да имате само един слушател на даден порт. Опитах се да създам обект BearSSL::ESP8266WebServerSecure с алтернативни портове, като 80 и 8443, но не мога да ги накарам да работят. Още по-лошо, изглежда, че няма начин да спрете слушател, след като BearSSL::ESP8266WebServerSecure обект е стартиран, така че не може да бъде освободен за по-късна повторна употреба.

В крайна сметка използвах HTTP, за да получа идентификационни данни за WiFi, след което HTTPS оттам нататък. Не е много задоволително решение, но работи.

person Sapient Hetero    schedule 20.01.2021