【发布时间】:2020-04-20 15:41:03
【问题描述】:
我为 ESP8266 写了一个草图。此草图读取一些传感器数据并通过 MQTT 发布。此外,我想让 Web 服务器提供与 HTML 或 JSON Web 服务相同的数据。
MQTT 发布是通过 TaskScheduler 计时器触发的。
MQTT 和 Web 服务器这两个功能都为自己工作,但遗憾的是不能一起工作。这是一个简化的草图:
#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 只能正常运行并发布数据(我使用 mosquitto 代理)。 如果注释掉第 177 行(因此不会触发 MQTT),运行 Web 服务器 (https://...) 也可以正常工作。
当这两个功能都处于活动状态时,一旦发送了第一条 MQTT 消息,Web 服务器就不再响应。我在 FF 中得到 PR_END_OF_FILE_ERROR,在 Chrome 中得到 ERR_CONNECTION_CLOSED。
我猜,这些库以某种方式相互混淆,或者某些东西与证书混淆。但是,指纹属于运行 mosquitto 的服务器,而 X509 证书属于运行在 ESP8266 上的 Web 服务器。这是两台不同的机器,彼此没有任何关系。
欢迎任何想法。
【问题讨论】:
-
不是直接回答您的问题,但为什么不在其他地方安装 Web 服务器?物联网规则#1:让传感器感知,让其他东西报告。我有许多传感器发布他们的读数,但我把我的网络服务器放在我的 Docker 服务器上。我有另一个 Docker 容器收集数据,然后将其作为 API 提供。然后一个 HTML 页面通过 JQuery 读取数据:github.com/john2exonets/MqttAPIServer
-
谢谢,JD Allen 也有这个想法,这将是一个有效的替代解决方案。但是难道不能在一台机器上同时运行两者吗?如果不能,为什么不呢?
-
当然,你可以在一台机器上同时运行这两个......但是第一次必须把传感器从原来的位置拖回来,只是为了烧一些新代码来添加一个空间一个网页,你很快就会意识到保持传感器感应的原因,而不是做任何额外的事情;)大约四次后,我不得不爬上去取回一个传感器/网络服务器节点,我决定让我的生活更轻松,拆分网络服务器分到一个 Docker 容器中,我可以坐在办公桌前进行我喜欢的所有更改;)
标签: webserver mqtt arduino-esp8266