Open aahondema opened 2 years ago
I created a simplified version of my sketch, hoping it will help you understand better what I am trying to do. I am using a NodeMCU minicomputer which has two LED signals connected. Each signal has a red and a green LED. The signal object handles all the logic: switching the LED on and of. When the state of a LED changes, a MQTT message is published from within the class. This works well for about 10 to 15 seconds, then the MQTT connection fails, leading to a new initialization of MQTT after which it works again for the same amount of time. It is obvious that I would prefer a more stable solution....
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
/*
Signal Class definition
*/
#include "Signal.h"
#define wifiStatusLEDPin D0
#define signal1Pin1 D1
#define signal1Pin2 D2
#define signal2Pin1 D5
#define signal2Pin2 D6
#define WIFI_SSID "SSID"
#define WIFI_PASSWORD "PASSWORD"
#define SKETCH_ID "MLTC"
#define MQTT_SERVER "192.168.68.104"
#define MQTT_SERVERPORT 1883
#define MQTT_USERNAME ""
#define MQTT_PASSWORD ""
#define MQTT_CLIENTID "MLTC_signal1"
/*
Create the MLTC objects
*/
Signal MLTCsignal1;
Signal MLTCsignal2;
/*
Create an ESP8266 WiFiClient class to connect to the MQTT server.
*/
WiFiEventHandler wifiConnectHandler;
WiFiEventHandler wifiDisconnectHandler;
WiFiClient espClient;
/*
PubSubClient
*/
PubSubClient MQTT(espClient);
void setup() {
Serial.begin(9600);
while (!Serial && millis() < 10000)
;
/*
Init WiFi
Register event handlers
*/
wifiConnectHandler = WiFi.onStationModeGotIP(onWifiConnect);
wifiDisconnectHandler = WiFi.onStationModeDisconnected(onWifiDisconnect);
InitWiFi();
/*
Init MQTT
*/
MQTT.setServer(MQTT_SERVER, MQTT_SERVERPORT);
MQTT.setCallback(callback);
/*
Init objects
*/
initSignal1(MQTT_CLIENTID, "signalA1", 1, signal1Pin1, signal1Pin2);
initSignal2(MQTT_CLIENTID, "signalA2", 2, signal2Pin1, signal2Pin2);
}
void loop() {
if (!MQTT.connected()) {
reconnect();
}
MQTT.loop();
MLTCsignal1.mqttLoop();
MLTCsignal1.red();
delay(2000);
MLTCsignal1.mqttLoop();
MLTCsignal1.green();
delay(2000);
MLTCsignal2.mqttLoop();
MLTCsignal2.red();
delay(2000);
MLTCsignal2.mqttLoop();
MLTCsignal2.green();
delay(2000);
}
void initSignal1(char* hostName, char* name, int signalNr, byte signalPin1, byte signalPin2) {
MLTCsignal1.init(hostName, name, signalNr, signalPin1, signalPin2, espClient, MQTT);
}
void initSignal2(char* hostName, char* name, int signalNr, byte signalPin1, byte signalPin2) {
MLTCsignal2.init(hostName, name, signalNr, signalPin1, signalPin2, espClient, MQTT);
}
void InitWiFi()
{
digitalWrite(wifiStatusLEDPin, HIGH);
Serial.println("WiFi initialization...");
WiFi.hostname(MQTT_CLIENTID);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
}
void onWifiConnect(const WiFiEventStationModeGotIP& event) {
Serial.println("Connected to WiFi successfully.");
digitalWrite(wifiStatusLEDPin, LOW);
Serial.println("WiFi initialization completed");
}
void onWifiDisconnect(const WiFiEventStationModeDisconnected& event) {
Serial.println("Disconnected from WiFi, trying to connect...");
digitalWrite(wifiStatusLEDPin, HIGH);
WiFi.disconnect();
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}
void callback(char* topic, byte* payload, unsigned int length)
{
/*
Select only the characters according to the length of the payload
*/
payload[length] = '\0';
Serial.print("MQTT [");
Serial.print(topic);
Serial.print("]: ");
Serial.println((char *)payload);
/*
Signal 1
*/
if (String(topic) == String(MLTCsignal1.getMQTTSetStateTopic()))
{
switch (atoi((char *)payload))
{
case 1:
{
MLTCsignal1.green();
break;
}
case 0:
{
MLTCsignal1.red();
break;
}
}
MQTT.publish(MLTCsignal1.getMQTTStateTopic(), MLTCsignal1.getState() ? "1" : "0");
}
/*
Signal 2
*/
if (String(topic) == String(MLTCsignal2.getMQTTSetStateTopic()))
{
switch (atoi((char *)payload))
{
case 1:
{
MLTCsignal2.green();
break;
}
case 0:
{
MLTCsignal2.red();
break;
}
}
MQTT.publish(MLTCsignal2.getMQTTStateTopic(), MLTCsignal2.getState() ? "1" : "0");
}
}
void reconnect()
{
if (WiFi.status() != WL_CONNECTED)
{
InitWiFi();
}
while (!MQTT.connected())
{
Serial.println("MQTT initialization...");
if (MQTT.connect(MQTT_CLIENTID, MQTT_USERNAME, MQTT_PASSWORD, "mltc/emergency", 0, false, "1"))
{
Serial.println("MQTT initialization completed!");
Serial.print("MQTT ClientID: ");
Serial.println(MQTT_CLIENTID);
MQTT.subscribe("mltc/emergency");
MQTT.subscribe("mltc/signalA1/setstate");
MQTT.subscribe("mltc/signalA2/setstate");
}
else
{
Serial.print("failed, rc=");
Serial.print(String(MQTT.state()));
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
My class (.h) definition is as follows:
#ifndef MLTC_SIGNAL_h
#define MLTC_SIGNAL_h
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
class Signal {
private:
char* _hostName;
char* _name;
int _number = -1;
byte _redpin;
byte _greenpin;
int _state;
int _previousState = -1;
WiFiClient _espClient;
PubSubClient _MQTT;
/*
MQTT Topics
*/
char _pub_State[100] = "";
char _sub_SetState[100] = "";
public:
Signal();
void init(char* hostName, char* name, int signalNr, byte redpin, byte greenpin, WiFiClient& espclient, PubSubClient& MQTT);
void red();
void green();
void mqttLoop();
void mqttPublish(char* Topic, char* payload);
void setState(int state);
char* getName();
int getNumber();
int getState();
char* getMQTTStateTopic();
char* getMQTTSetStateTopic();
};
#endif
The .cpp file is as follows:
#include "Signal.h"
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#define SKETCH_ID "MLTC"
#define MQTT_SERVER "192.168.68.104"
#define MQTT_SERVERPORT 1883
#define MQTT_USERNAME ""
#define MQTT_PASSWORD ""
#define MQTT_CLIENTID "MLTC_signal1"
Signal::Signal() {}
void Signal::init(char* hostName, char* name, int number, byte redpin, byte greenpin, WiFiClient& espClient, PubSubClient& MQTT)
{
_hostName = hostName;
_name = name;
_number = number;
_redpin = redpin;
_greenpin = greenpin;
pinMode(_redpin, OUTPUT);
pinMode(_greenpin, OUTPUT);
_espClient = espClient;
_MQTT = MQTT;
/*
MQTT Topics
*/
sprintf(_pub_State, "mltc/%s/state", _name);
sprintf(_sub_SetState, "mltc/%s/setstate", _name);
/*
Set signal to default position
*/
red();
}
void Signal::red() {
setState(0);
}
void Signal::green() {
setState(1);
}
/*
Setters
*/
void Signal::setState(int state) {
_state = state;
if (state != _previousState)
{
_previousState = state;
switch (_state) {
case 0:
digitalWrite(_redpin, HIGH);
digitalWrite(_greenpin, LOW);
mqttPublish(_pub_State, "0");
break;
case 1:
digitalWrite(_redpin, LOW);
digitalWrite(_greenpin, HIGH);
mqttPublish(_pub_State, "1");
break;
}
Serial.print(_name);
Serial.print(" set to ");
Serial.println(_state ? "1 (Green)" : "0 (Red)");
}
}
void Signal::mqttLoop()
{
if (!_MQTT.connected())
{
_MQTT.setServer(MQTT_SERVER, MQTT_SERVERPORT);
_MQTT.connect(_name, MQTT_USERNAME, MQTT_PASSWORD, "mltc/emergency", 0, false, "1");
}
_MQTT.loop();
}
void Signal::mqttPublish(char* Topic, char* Payload)
{
_MQTT.publish(Topic, Payload);
// _MQTT.disconnect();
Serial.print("_MQTT.publish: ");
Serial.print(Topic);
Serial.print(", ");
Serial.println(Payload);
Serial.print("MQTT state: ");
Serial.println(_MQTT.state());
}
char* Signal::getName() {
return _name;
}
int Signal::getNumber() {
return _number;
}
int Signal::getState() {
return _state;
}
char* Signal::getMQTTStateTopic() {
return _pub_State;
}
char* Signal::getMQTTSetStateTopic() {
return _sub_SetState;
}
Serial Monitor output:
09:25:56.451 -> _MQTT.publish: mltc/signalA1/state, 0
09:25:56.499 -> MQTT state: 0
09:25:56.499 -> signalA1 set to 0 (Red)
09:25:58.453 -> _MQTT.publish: mltc/signalA1/state, 1
09:25:58.499 -> MQTT state: 0
09:25:58.499 -> signalA1 set to 1 (Green)
09:26:00.459 -> _MQTT.publish: mltc/signalA2/state, 0
09:26:00.506 -> MQTT state: 0
09:26:00.506 -> signalA2 set to 0 (Red)
09:26:02.450 -> _MQTT.publish: mltc/signalA2/state, 1
09:26:02.498 -> MQTT state: 0
09:26:02.498 -> signalA2 set to 1 (Green)
09:26:04.500 -> _MQTT.publish: mltc/signalA1/state, 0
09:26:04.547 -> MQTT state: 0
09:26:04.547 -> signalA1 set to 0 (Red)
09:26:06.511 -> _MQTT.publish: mltc/signalA1/state, 1
09:26:06.559 -> MQTT state: 0
09:26:06.559 -> signalA1 set to 1 (Green)
09:26:08.492 -> _MQTT.publish: mltc/signalA2/state, 0
09:26:08.534 -> MQTT state: 0
09:26:08.534 -> signalA2 set to 0 (Red)
09:26:10.481 -> _MQTT.publish: mltc/signalA2/state, 1
09:26:10.532 -> MQTT state: 0
09:26:10.532 -> signalA2 set to 1 (Green)
09:26:12.485 -> MQTT initialization... <- FAILURE AND RECONNECT
09:26:27.500 -> failed, rc=-4 try again in 5 seconds
09:26:32.488 -> MQTT initialization...
09:26:32.535 -> MQTT initialization completed!
09:26:32.571 -> MQTT ClientID: MLTC_signal1
09:26:32.618 -> _MQTT.publish: mltc/signalA1/state, 0
09:26:32.656 -> MQTT state: 0 <- AND THEN IT WORKS FOR ANOTHER +/- 10 SECONDS
09:26:32.656 -> signalA1 set to 0 (Red)
09:26:34.556 -> _MQTT.publish: mltc/signalA1/state, 1
09:26:34.602 -> MQTT state: 0
09:26:34.602 -> signalA1 set to 1 (Green)
09:26:36.557 -> _MQTT.publish: mltc/signalA2/state, 0
09:26:36.610 -> MQTT state: 0
Hi, I am passing the reference of a WiFiClient and a PubSubClient to a class definition because I want to be able to send an MQTT publish message from within the class. I don't need callbacks. This statement is used to initialize the object:
MLTCsignal1.init(hostName, name, signalNr, signalPin1, signalPin2, espClient, MQTT);
the method inside the class has this format:
void init(char hostName, char name, int signalNr, byte redpin, byte greenpin, WiFiClient& espclient, PubSubClient& MQTT);
As I use a reference, both espClient and MQTT point to the objects in the mail program.
_MQTT is the local PubSubClient definition in the class. MQTT is moved to _MQTT.
Technically, this works. Problem is that I can use MQTT.publish in my class, but only for a few seconds. This is the code for publishing a message inside my class:
_MQTT.loop(); if (!_MQTT.connected()) { _MQTT.setServer(MQTT_SERVER, MQTT_SERVERPORT); _MQTT.connect(_name, MQTT_USERNAME, MQTT_PASSWORD, "mltc/emergency", 0, false, "1"); } _MQTT.publish(Topic, Payload); _MQTT.disconnect();
I need to disconnect because more objects of the same type use MQTT. If I don't connect, only the first created object is able to publish. Once the connection is created it works for about 10 seconds, then the connection fails (-4) and the publish stops. After the main program has re-established the connection it again works for a few seconds. But it is not stable at all. Any ideas on this?
Adrian Hondema, The Netherlands.