mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-22 23:56:13 +01:00
MQTT firt send/recv ok
This commit is contained in:
parent
da85e4e245
commit
6bd9e28be4
|
@ -76,7 +76,7 @@ void setup()
|
||||||
#endif // ETHERNET_ON
|
#endif // ETHERNET_ON
|
||||||
|
|
||||||
#if MQTT_ON
|
#if MQTT_ON
|
||||||
DccMQTT::get()->setup(LOCAL_MQTT_BROKER);
|
DccMQTT::get()->setup();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
@ -128,6 +128,10 @@ void loop()
|
||||||
#if ETHERNET_ON
|
#if ETHERNET_ON
|
||||||
EthernetInterface::loop();
|
EthernetInterface::loop();
|
||||||
#endif
|
#endif
|
||||||
|
#if MQTT_ON
|
||||||
|
DccMQTT::get()->loop();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if defined(RMFT_ACTIVE)
|
#if defined(RMFT_ACTIVE)
|
||||||
RMFT::loop();
|
RMFT::loop();
|
||||||
|
|
294
DccMQTT.cpp
294
DccMQTT.cpp
|
@ -19,29 +19,25 @@
|
||||||
* GNU General Public License for more details <https://www.gnu.org/licenses/>.
|
* GNU General Public License for more details <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if __has_include ( "config.h")
|
#if __has_include("config.h")
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#else
|
#else
|
||||||
#warning config.h not found. Using defaults from config.example.h
|
#warning config.h not found. Using defaults from config.example.h
|
||||||
#include "config.example.h"
|
#include "config.example.h"
|
||||||
#endif
|
#endif
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
#include <EthernetInterface.h>
|
#include <EthernetInterface.h>
|
||||||
#include <PubSubClient.h> // Base (sync) MQTT library
|
#include <PubSubClient.h> // Base (sync) MQTT library
|
||||||
#include <DIAG.h>
|
#include <DIAG.h>
|
||||||
|
#include <Ethernet.h>
|
||||||
|
#include <Dns.h>
|
||||||
|
#include <DCCTimer.h>
|
||||||
|
|
||||||
#include <DccMQTT.h>
|
#include <DccMQTT.h>
|
||||||
|
|
||||||
//---------
|
|
||||||
// Defines
|
|
||||||
//---------
|
|
||||||
|
|
||||||
#define MAXTBUF 50 //!< max length of the buffer for building the topic name ;to be checked
|
|
||||||
#define MAXTMSG 120 //!< max length of the messages for a topic ;to be checked PROGMEM ?
|
|
||||||
#define MAXTSTR 30 //!< max length of a topic string
|
|
||||||
|
|
||||||
//---------
|
//---------
|
||||||
// Variables
|
// Variables
|
||||||
//---------
|
//---------
|
||||||
|
@ -52,108 +48,198 @@ char topicMessage[MAXTMSG];
|
||||||
|
|
||||||
DccMQTT DccMQTT::singleton;
|
DccMQTT DccMQTT::singleton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copies an byte array to a hex representation as string; used for generating the unique Arduino ID
|
||||||
|
*
|
||||||
|
* @param array array containing bytes
|
||||||
|
* @param len length of the array
|
||||||
|
* @param buffer buffer to which the string will be written; make sure the buffer has appropriate length
|
||||||
|
*/
|
||||||
|
static void array_to_string(byte array[], unsigned int len, char buffer[])
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
byte nib1 = (array[i] >> 4) & 0x0F;
|
||||||
|
byte nib2 = (array[i] >> 0) & 0x0F;
|
||||||
|
buffer[i * 2 + 0] = nib1 < 0xA ? '0' + nib1 : 'A' + nib1 - 0xA;
|
||||||
|
buffer[i * 2 + 1] = nib2 < 0xA ? '0' + nib2 : 'A' + nib2 - 0xA;
|
||||||
|
}
|
||||||
|
buffer[len * 2] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback when a message arrives from the broker; push cmd into the incommming queue
|
||||||
// callback when a message arrives from the broker
|
|
||||||
void mqttCallback(char *topic, byte *payload, unsigned int length)
|
void mqttCallback(char *topic, byte *payload, unsigned int length)
|
||||||
{
|
{
|
||||||
topicName[0] = '\0';
|
topicName[0] = '\0';
|
||||||
topicMessage[0] = '\0';
|
topicMessage[0] = '\0';
|
||||||
strcpy(topicName, topic);
|
strcpy(topicName, topic);
|
||||||
strlcpy(topicMessage, (char *)payload, length + 1);
|
strlcpy(topicMessage, (char *)payload, length + 1);
|
||||||
|
Serial.println("some msg arrived");
|
||||||
DIAG(F("MQTT Message arrived [%s]: %s"), topicName, topicMessage);
|
DIAG(F("MQTT Message arrived [%s]: %s"), topicName, topicMessage);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief MQTT broker connection / reconnection
|
* @brief MQTT broker connection / reconnection
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static void reconnect()
|
void DccMQTT::connect()
|
||||||
{
|
{
|
||||||
DIAG(F("MQTT (re)connecting ..."));
|
|
||||||
|
|
||||||
while (!mqttClient.connected())
|
char *connectID = new char[MAXCONNECTID];
|
||||||
|
connectID[0] = '\0';
|
||||||
|
|
||||||
|
int reconnectCount = 0;
|
||||||
|
|
||||||
|
// if(broker->prefix != nullptr) {
|
||||||
|
// char tmp[20];
|
||||||
|
// strcpy_P(tmp, (const char *)broker->prefix);
|
||||||
|
// Serial.println(tmp);
|
||||||
|
// Serial.println(broker->prefix);
|
||||||
|
// connectID[0] = '\0';
|
||||||
|
// strcat(connectID, tmp);
|
||||||
|
// }
|
||||||
|
|
||||||
|
strcat(connectID, clientID);
|
||||||
|
|
||||||
|
|
||||||
|
DIAG(F("MQTT %s (re)connecting ..."), connectID);
|
||||||
|
// Build the connect ID : Prefix + clientID
|
||||||
|
|
||||||
|
while (!mqttClient.connected() && reconnectCount < MAXRECONNECT)
|
||||||
{
|
{
|
||||||
DIAG(F("Attempting MQTT Broker connection..."));
|
DIAG(F("Attempting MQTT Broker connection[%d]..."), broker->cType);
|
||||||
// Attempt to connect
|
switch (broker->cType)
|
||||||
#ifdef CLOUDBROKER
|
|
||||||
char *connectID = new char[40];
|
|
||||||
|
|
||||||
connectID[0] = '\0';
|
|
||||||
strcat(connectID, MQTT_BROKER_CLIENTID_PREFIX);
|
|
||||||
strcat(connectID,DccMQTT::getDeviceID());
|
|
||||||
|
|
||||||
INFO(F("ConnectID: %s %s %s"), connectID, MQTT_BROKER_USER, MQTT_BROKER_PASSWD);
|
|
||||||
#ifdef MQTT_BROKER_USER
|
|
||||||
DBG(F("MQTT (re)connecting (Cloud/User) ..."));
|
|
||||||
if (mqttClient.connect(connectID, MQTT_BROKER_USER, MQTT_BROKER_PASSWD, "$connected", 0, true, "0", 0))
|
|
||||||
#else
|
|
||||||
DBG(F("MQTT (re)connecting (Cloud) ..."));
|
|
||||||
if (mqttClient.connect(DccMQTT::getDeviceID()))
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#ifdef MQTT_BROKER_USER
|
|
||||||
DBG(F("MQTT (re)connecting (Local/User) ..."));
|
|
||||||
if (mqttClient.connect(DccMQTT::getDeviceID(), MQTT_BROKER_USER, MQTT_BROKER_PASSWD))
|
|
||||||
#else
|
|
||||||
DBG(F("MQTT (re)connecting (Local) ..."));
|
|
||||||
if (mqttClient.connect(DccMQTT::getDeviceID()))
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
INFO(F("MQTT broker connected ..."));
|
case 6:
|
||||||
// publish on the $connected topic
|
case 1:
|
||||||
DccMQTT::subscribe(); // required in case of a connection loss to do it again (this causes a mem leak !! of 200bytes each time!!)
|
{ // port(p), ip(i), domain(d),
|
||||||
|
if (mqttClient.connect(clientID))
|
||||||
|
{
|
||||||
|
DIAG(F("MQTT broker connected ..."));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DIAG(F("MQTT broker connection failed, rc=%d, trying to reconnect"), mqttClient.state());
|
||||||
|
reconnectCount++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else
|
case 2:
|
||||||
{
|
{ // port(p), ip(i), domain(d), user(uid), pwd(pass),
|
||||||
INFO(F("MQTT broker connection failed, rc=%d, trying to reconnect"), mqttClient.state());
|
break;
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
{ // port(p), ip(i), domain(d), user(uid), pwd(pass), prefix(pfix)
|
||||||
|
// mqttClient.connect(connectID, MQTT_BROKER_USER, MQTT_BROKER_PASSWD, "$connected", 0, true, "0", 0))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
{ // port(p), domain(d), user(uid), pwd(pass), prefix(pfix)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 5:
|
||||||
|
{ // port(p), domain(d), user(uid), pwd(pass)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// case 6:
|
||||||
|
// { // port(p), domain(d)
|
||||||
|
// mqttClient.connect()
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
if (reconnectCount == MAXRECONNECT) {
|
||||||
|
DIAG(F("MQTT Connection aborted after %d tries"), MAXRECONNECT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for the time being only one topic at the root which os the unique clientID from the MCU
|
||||||
|
// QoS is 0 by default
|
||||||
|
boolean DccMQTT::subscribe() {
|
||||||
|
return mqttClient.subscribe(clientID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Public part of the MQTT setup function. Will call the secondary private setup function following the broker
|
||||||
|
* configuration from config.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
void DccMQTT::setup()
|
void DccMQTT::setup()
|
||||||
{
|
{
|
||||||
//Create the MQTT environment and establish inital connection to the Broker
|
setup(CSMQTTBROKER);
|
||||||
|
}
|
||||||
|
|
||||||
// get a eth client session
|
/**
|
||||||
ethClient = EthernetInterface::get()->getServer()->available();
|
* @brief Private part of the MQTT setup function. Realizes all required actions for establishing the MQTT connection.
|
||||||
|
*
|
||||||
|
* @param id Name provided to the broker configuration
|
||||||
|
* @param b MQTT broker object containing the main configuration parameters
|
||||||
|
*/
|
||||||
|
void DccMQTT::setup(const FSH *id, MQTTBroker *b)
|
||||||
|
{
|
||||||
|
|
||||||
|
//Create the MQTT environment and establish inital connection to the Broker
|
||||||
|
broker = b;
|
||||||
|
// get a eth client session
|
||||||
|
|
||||||
|
DIAG(F("MQTT Connect to %S at %S/%d.%d.%d.%d:%d"), id, broker->domain, broker->ip[0], broker->ip[1], broker->ip[2], broker->ip[3], broker->port);
|
||||||
|
|
||||||
|
// ethClient = EthernetInterface::get()->getServer()->available();
|
||||||
|
ethClient = EthernetInterface::get()->getServer()->accept();
|
||||||
// initalize MQ Broker
|
// initalize MQ Broker
|
||||||
mqttClient = PubSubClient(ethClient);
|
|
||||||
mqttClient.setServer(IPAddress(MQTT_BROKER_ADDRESS), MQTT_BROKER_PORT);
|
|
||||||
|
|
||||||
DIAG(F("MQTT Client : Server ok ..."));
|
mqttClient = PubSubClient(ethClient);
|
||||||
|
mqttClient.setServer(broker->ip, broker->port);
|
||||||
|
|
||||||
|
DIAG(F("MQTT Client : Server ok ...%x/%x"), ethClient, mqttClient);
|
||||||
|
|
||||||
mqttClient.setCallback(mqttCallback); // Initalize callback function for incomming messages
|
mqttClient.setCallback(mqttCallback); // Initalize callback function for incomming messages
|
||||||
|
|
||||||
DIAG(F("MQTT Client : Callback set ..."));
|
DIAG(F("MQTT Client : Callback set ..."));
|
||||||
|
|
||||||
// DccMQTT::setDeviceID(); // set the unique device ID to bu used for creating / listening to topic
|
byte mqbid[CLIENTIDSIZE] = {0};
|
||||||
|
DCCTimer::getSimulatedMacAddress(mqbid);
|
||||||
|
|
||||||
|
array_to_string(mqbid, CLIENTIDSIZE, clientID);
|
||||||
|
DIAG(F("MQTT Client ID : %s"), clientID);
|
||||||
|
|
||||||
reconnect(); // inital connection as well as reconnects
|
connect(); // inital connection as well as reconnects
|
||||||
// DccMQTT::subscribe(); // set up all subscriptionn
|
auto sub = DccMQTT::subscribe(); // set up all subscriptions
|
||||||
DIAG(F("MQTT subscriptons done..."));
|
|
||||||
|
DIAG(F("MQTT subscriptons %s..."), sub ? "ok":"failed");
|
||||||
|
|
||||||
|
mqttClient.publish(clientID, "Hello from DccEX");
|
||||||
|
|
||||||
// sprintf_P(_csidMsg, csidfmt, DccMQTT::getDeviceID());
|
// sprintf_P(_csidMsg, csidfmt, DccMQTT::getDeviceID());
|
||||||
// mqttClient.publish(DccMQTT::topics[ADMIN], _csidMsg); // say hello to the broker and the API who listens to this topic
|
// mqttClient.publish(DccMQTT::topics[ADMIN], _csidMsg); // say hello to the broker and the API who listens to this topic
|
||||||
|
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @todo set the connect status with a retained message on the $connected topic /admin/<csid>/$connected as used in the connect
|
// * @todo set the connect status with a retained message on the $connected topic /admin/<csid>/$connected as used in the connect
|
||||||
// *
|
// *
|
||||||
// */
|
// */
|
||||||
|
|
||||||
// mqttDccExParser = p;
|
// mqttDccExParser = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DccMQTT::loop()
|
||||||
|
{
|
||||||
|
|
||||||
|
// DccTelemetry::deltaT(1);
|
||||||
|
|
||||||
|
if (!mqttClient.connected())
|
||||||
|
{
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
if (!mqttClient.loop())
|
||||||
|
{
|
||||||
|
DIAG(F("mqttClient returned with error; state: %d"), mqttClient.state());
|
||||||
|
};
|
||||||
|
// DccMQTTProc::loop(); //!< give time to the command processor to handle msg ..
|
||||||
|
// DccMQTT::publish(); //!< publish waiting messages from the outgoing queue
|
||||||
|
// DccTelemetry::deltaT(1);
|
||||||
|
}
|
||||||
|
|
||||||
// #include <avr/pgmspace.h> // for PROGMEM use
|
// #include <avr/pgmspace.h> // for PROGMEM use
|
||||||
|
|
||||||
// #include <Diag/DIAG.h> // Diagnostig output to the serial terminal
|
// #include <Diag/DIAG.h> // Diagnostig output to the serial terminal
|
||||||
|
@ -221,7 +307,7 @@ void DccMQTT::setup()
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief Copies an array to a string; used for generating the unique Arduino ID
|
// * @brief Copies an array to a string; used for generating the unique Arduino ID
|
||||||
// *
|
// *
|
||||||
// * @param array array containing bytes
|
// * @param array array containing bytes
|
||||||
// * @param len length of the array
|
// * @param len length of the array
|
||||||
// * @param buffer buffer to which the string will be written; make sure the buffer has appropriate length
|
// * @param buffer buffer to which the string will be written; make sure the buffer has appropriate length
|
||||||
|
@ -240,7 +326,7 @@ void DccMQTT::setup()
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief Maps a command recieved from the JSON string to the corresponding enum value
|
// * @brief Maps a command recieved from the JSON string to the corresponding enum value
|
||||||
// *
|
// *
|
||||||
// * @param c string containing the command
|
// * @param c string containing the command
|
||||||
// * @return Commands enum value from the Command enum
|
// * @return Commands enum value from the Command enum
|
||||||
// * @throw Returns an INVALID_C - invalid command for unknown Commands send
|
// * @throw Returns an INVALID_C - invalid command for unknown Commands send
|
||||||
|
@ -278,7 +364,7 @@ void DccMQTT::setup()
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief Maps parameters names recieved from the JSON string to the corresponding enum value
|
// * @brief Maps parameters names recieved from the JSON string to the corresponding enum value
|
||||||
// *
|
// *
|
||||||
// * @param c string containing the parameter name
|
// * @param c string containing the parameter name
|
||||||
// * @return parameters enum value from the Parameter enum
|
// * @return parameters enum value from the Parameter enum
|
||||||
// */
|
// */
|
||||||
|
@ -308,9 +394,9 @@ void DccMQTT::setup()
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief Maps Parameters enum values to the corresponding string
|
// * @brief Maps Parameters enum values to the corresponding string
|
||||||
// *
|
// *
|
||||||
// * @param p : Enum value of the Parameter
|
// * @param p : Enum value of the Parameter
|
||||||
// * @return const char*
|
// * @return const char*
|
||||||
// */
|
// */
|
||||||
// const char *resolveParameters(Parameters p)
|
// const char *resolveParameters(Parameters p)
|
||||||
// {
|
// {
|
||||||
|
@ -342,11 +428,11 @@ void DccMQTT::setup()
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief Callback executed upon reception of a message by the PubSubClient
|
// * @brief Callback executed upon reception of a message by the PubSubClient
|
||||||
// *
|
// *
|
||||||
// * @param topic topic string
|
// * @param topic topic string
|
||||||
// * @param payload serialized content of the message
|
// * @param payload serialized content of the message
|
||||||
// * @param length length of the recieved message
|
// * @param length length of the recieved message
|
||||||
// *
|
// *
|
||||||
// */
|
// */
|
||||||
// void mqttCallback(char *topic, byte *payload, unsigned int length)
|
// void mqttCallback(char *topic, byte *payload, unsigned int length)
|
||||||
// {
|
// {
|
||||||
|
@ -391,12 +477,12 @@ void DccMQTT::setup()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief Central nervous system. All messages are passed through here parse and a pool item gets build. Syntax and Semantic checks are done here on all commands parameters etc ...
|
// * @brief Central nervous system. All messages are passed through here parse and a pool item gets build. Syntax and Semantic checks are done here on all commands parameters etc ...
|
||||||
// * All error get printed to Serial for now but will be send over MQ as well in the future for further processing by the client
|
// * All error get printed to Serial for now but will be send over MQ as well in the future for further processing by the client
|
||||||
// *
|
// *
|
||||||
// * @param topicName : Topic on which the message has been recieved
|
// * @param topicName : Topic on which the message has been recieved
|
||||||
// * @param topicMessage : Message that has been recieved
|
// * @param topicMessage : Message that has been recieved
|
||||||
// *
|
// *
|
||||||
// */
|
// */
|
||||||
// void dccmqttCommandHandler(char *topicName, char *topicMessage)
|
// void dccmqttCommandHandler(char *topicName, char *topicMessage)
|
||||||
// {
|
// {
|
||||||
|
@ -552,12 +638,12 @@ void DccMQTT::setup()
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief Set the parameters in the pool item to be send to the processor
|
// * @brief Set the parameters in the pool item to be send to the processor
|
||||||
// *
|
// *
|
||||||
// * @param mdix : Index of the pool item
|
// * @param mdix : Index of the pool item
|
||||||
// * @param p : Parameter to be set
|
// * @param p : Parameter to be set
|
||||||
// * @param v : Json object of the parameter value recieved
|
// * @param v : Json object of the parameter value recieved
|
||||||
// * @param mandatory : if the parameter is mandatory
|
// * @param mandatory : if the parameter is mandatory
|
||||||
// * @return true
|
// * @return true
|
||||||
// * @return false the value is 0 return false
|
// * @return false the value is 0 return false
|
||||||
// */
|
// */
|
||||||
// bool setMsgParamsByObj(int mdix, Parameters p, JsonObject v, bool mandatory)
|
// bool setMsgParamsByObj(int mdix, Parameters p, JsonObject v, bool mandatory)
|
||||||
|
@ -611,10 +697,10 @@ void DccMQTT::setup()
|
||||||
// char st[4] = {0};
|
// char st[4] = {0};
|
||||||
// strncpy(st, v[kw], 3);
|
// strncpy(st, v[kw], 3);
|
||||||
// /**
|
// /**
|
||||||
// * @todo validate ON/OFF keywords here so that we don't handle any garbage comming in
|
// * @todo validate ON/OFF keywords here so that we don't handle any garbage comming in
|
||||||
// *
|
// *
|
||||||
// */
|
// */
|
||||||
// DBG(F("State keyword [%s] [%s]"), kw, st);
|
// DBG(F("State keyword [%s] [%s]"), kw, st);
|
||||||
// if (st[1] == 'N') {
|
// if (st[1] == 'N') {
|
||||||
// DccMQTTCommandMsg::msg[midx].params[p] = 1;
|
// DccMQTTCommandMsg::msg[midx].params[p] = 1;
|
||||||
// } else {
|
// } else {
|
||||||
|
@ -644,7 +730,7 @@ void DccMQTT::setup()
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief Validates permitted values for Parameters
|
// * @brief Validates permitted values for Parameters
|
||||||
// *
|
// *
|
||||||
// * @param p : Parameter
|
// * @param p : Parameter
|
||||||
// * @param v : value to validate for the given Parameter
|
// * @param v : value to validate for the given Parameter
|
||||||
// * @return true : if value is allowed
|
// * @return true : if value is allowed
|
||||||
|
@ -714,7 +800,7 @@ void DccMQTT::setup()
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief Retrieves a keyword from PROGMEM
|
// * @brief Retrieves a keyword from PROGMEM
|
||||||
// *
|
// *
|
||||||
// * @param k keyword to retrieve
|
// * @param k keyword to retrieve
|
||||||
// * @return char* string copied into SRAM
|
// * @return char* string copied into SRAM
|
||||||
// */
|
// */
|
||||||
|
@ -726,7 +812,7 @@ void DccMQTT::setup()
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief Pushes a message into the the in comming queue; locks the pool item
|
// * @brief Pushes a message into the the in comming queue; locks the pool item
|
||||||
// *
|
// *
|
||||||
// * @param midx the index of the message in the pool to be pushed into the queue
|
// * @param midx the index of the message in the pool to be pushed into the queue
|
||||||
// */
|
// */
|
||||||
// void DccMQTT::pushIn(uint8_t midx)
|
// void DccMQTT::pushIn(uint8_t midx)
|
||||||
|
@ -751,7 +837,7 @@ void DccMQTT::setup()
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief Pushes a message into the the outgoing queue; locks the pool item;
|
// * @brief Pushes a message into the the outgoing queue; locks the pool item;
|
||||||
// *
|
// *
|
||||||
// * @param midx the index of the message in the pool to be pushed into the queue
|
// * @param midx the index of the message in the pool to be pushed into the queue
|
||||||
// */
|
// */
|
||||||
// void DccMQTT::pushOut(uint8_t midx)
|
// void DccMQTT::pushOut(uint8_t midx)
|
||||||
|
@ -774,7 +860,7 @@ void DccMQTT::setup()
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief pops a message from the the in comming queue; The pool item used is still in use and stays locked
|
// * @brief pops a message from the the in comming queue; The pool item used is still in use and stays locked
|
||||||
// *
|
// *
|
||||||
// * @param midx the index of the message in the pool to be poped
|
// * @param midx the index of the message in the pool to be poped
|
||||||
// * @return index in the message pool of the message which has been poped; -1 if the queue is empty
|
// * @return index in the message pool of the message which has been poped; -1 if the queue is empty
|
||||||
// */
|
// */
|
||||||
|
@ -792,7 +878,7 @@ void DccMQTT::setup()
|
||||||
// }
|
// }
|
||||||
// /**
|
// /**
|
||||||
// * @brief pops a message from the the outgoing queue; The pool item used is freed and can be reused
|
// * @brief pops a message from the the outgoing queue; The pool item used is freed and can be reused
|
||||||
// *
|
// *
|
||||||
// * @param midx the index of the message in the pool to be poped
|
// * @param midx the index of the message in the pool to be poped
|
||||||
// * @return index in the message pool of the message which has been poped; -1 if the queue is empty
|
// * @return index in the message pool of the message which has been poped; -1 if the queue is empty
|
||||||
// */
|
// */
|
||||||
|
@ -813,7 +899,7 @@ void DccMQTT::setup()
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief in case we lost the connection to the MQTT broker try to restablish a conection
|
// * @brief in case we lost the connection to the MQTT broker try to restablish a conection
|
||||||
// *
|
// *
|
||||||
// */
|
// */
|
||||||
// static void reconnect()
|
// static void reconnect()
|
||||||
// {
|
// {
|
||||||
|
@ -825,7 +911,7 @@ void DccMQTT::setup()
|
||||||
// // Attempt to connect
|
// // Attempt to connect
|
||||||
// #ifdef CLOUDBROKER
|
// #ifdef CLOUDBROKER
|
||||||
// char *connectID = new char[40];
|
// char *connectID = new char[40];
|
||||||
|
|
||||||
// connectID[0] = '\0';
|
// connectID[0] = '\0';
|
||||||
// strcat(connectID, MQTT_BROKER_CLIENTID_PREFIX);
|
// strcat(connectID, MQTT_BROKER_CLIENTID_PREFIX);
|
||||||
// strcat(connectID,DccMQTT::getDeviceID());
|
// strcat(connectID,DccMQTT::getDeviceID());
|
||||||
|
@ -849,7 +935,7 @@ void DccMQTT::setup()
|
||||||
// #endif
|
// #endif
|
||||||
// {
|
// {
|
||||||
// INFO(F("MQTT broker connected ..."));
|
// INFO(F("MQTT broker connected ..."));
|
||||||
// // publish on the $connected topic
|
// // publish on the $connected topic
|
||||||
// DccMQTT::subscribe(); // required in case of a connection loss to do it again (this causes a mem leak !! of 200bytes each time!!)
|
// DccMQTT::subscribe(); // required in case of a connection loss to do it again (this causes a mem leak !! of 200bytes each time!!)
|
||||||
// }
|
// }
|
||||||
// else
|
// else
|
||||||
|
@ -861,7 +947,7 @@ void DccMQTT::setup()
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief Test if the mqtt client is connected
|
// * @brief Test if the mqtt client is connected
|
||||||
// *
|
// *
|
||||||
// * @return true - connected
|
// * @return true - connected
|
||||||
// * @return false - not connected
|
// * @return false - not connected
|
||||||
// */
|
// */
|
||||||
|
@ -872,9 +958,9 @@ void DccMQTT::setup()
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief builds the topic strings for the intents together with the unique ID and the Ressource letter
|
// * @brief builds the topic strings for the intents together with the unique ID and the Ressource letter
|
||||||
// *
|
// *
|
||||||
// * @param c char for specific chnalle under the intent; if '_' it will not be used
|
// * @param c char for specific chnalle under the intent; if '_' it will not be used
|
||||||
// * @param t index for the topic in the list of topics
|
// * @param t index for the topic in the list of topics
|
||||||
// * @return char* of the topic string build
|
// * @return char* of the topic string build
|
||||||
// */
|
// */
|
||||||
// static char *buildTopicID(char c, DccTopics t)
|
// static char *buildTopicID(char c, DccTopics t)
|
||||||
|
@ -996,12 +1082,11 @@ void DccMQTT::setup()
|
||||||
// }
|
// }
|
||||||
// /**
|
// /**
|
||||||
// * @brief Initalizes the MQTT broker connection; subcribes to all reqd topics and sends the deviceID to the broker on the /admin channel
|
// * @brief Initalizes the MQTT broker connection; subcribes to all reqd topics and sends the deviceID to the broker on the /admin channel
|
||||||
// *
|
// *
|
||||||
// */
|
// */
|
||||||
// #define PUB_CSID_FMT "{\"csid\":\"%s\"}"
|
// #define PUB_CSID_FMT "{\"csid\":\"%s\"}"
|
||||||
// PROGMEM const char csidfmt[] = {PUB_CSID_FMT};
|
// PROGMEM const char csidfmt[] = {PUB_CSID_FMT};
|
||||||
|
|
||||||
|
|
||||||
// void DccMQTT::setup(DCCEXParser p)
|
// void DccMQTT::setup(DCCEXParser p)
|
||||||
// {
|
// {
|
||||||
// char _csidMsg[64]{'\0'}; //!< string buffer for the serialized message to return
|
// char _csidMsg[64]{'\0'}; //!< string buffer for the serialized message to return
|
||||||
|
@ -1020,11 +1105,10 @@ void DccMQTT::setup()
|
||||||
// INFO(F("MQTT subscriptons done..."));
|
// INFO(F("MQTT subscriptons done..."));
|
||||||
// sprintf_P(_csidMsg, csidfmt, DccMQTT::getDeviceID());
|
// sprintf_P(_csidMsg, csidfmt, DccMQTT::getDeviceID());
|
||||||
// mqttClient.publish(DccMQTT::topics[ADMIN], _csidMsg); // say hello to the broker and the API who listens to this topic
|
// mqttClient.publish(DccMQTT::topics[ADMIN], _csidMsg); // say hello to the broker and the API who listens to this topic
|
||||||
|
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @todo set the connect status with a retained message on the $connected topic /admin/<csid>/$connected as used in the connect
|
// * @todo set the connect status with a retained message on the $connected topic /admin/<csid>/$connected as used in the connect
|
||||||
// *
|
// *
|
||||||
// */
|
// */
|
||||||
|
|
||||||
// DccTelemetry::setup();
|
// DccTelemetry::setup();
|
||||||
|
|
160
DccMQTT.h
160
DccMQTT.h
|
@ -1,94 +1,139 @@
|
||||||
#ifndef _DccMQTT_h_
|
#ifndef _DccMQTT_h_
|
||||||
#define _DccMQTT_h_
|
#define _DccMQTT_h_
|
||||||
|
|
||||||
#if __has_include ( "config.h")
|
#if __has_include("config.h")
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#else
|
#else
|
||||||
#warning config.h not found. Using defaults from config.example.h
|
#warning config.h not found. Using defaults from config.example.h
|
||||||
#include "config.example.h"
|
#include "config.example.h"
|
||||||
#endif
|
#endif
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
|
|
||||||
#include <PubSubClient.h>
|
#include <PubSubClient.h>
|
||||||
#include <DCCEXParser.h>
|
#include <DCCEXParser.h>
|
||||||
#include <Queue.h>
|
#include <Queue.h>
|
||||||
#include <Ethernet.h>
|
#include <Ethernet.h>
|
||||||
|
#include <Dns.h>
|
||||||
|
|
||||||
#define MAXPAYLOAD 64
|
#define MAXPAYLOAD 64
|
||||||
|
#define MAXDOMAINLENGTH 32
|
||||||
|
|
||||||
// Define Broker configurations; Values are provided in the following order
|
#define MAXTBUF 50 //!< max length of the buffer for building the topic name ;to be checked
|
||||||
// MQTT_BROKER_PORT 9883
|
#define MAXTMSG 120 //!< max length of the messages for a topic ;to be checked PROGMEM ?
|
||||||
|
#define MAXTSTR 30 //!< max length of a topic string
|
||||||
|
#define MAXCONNECTID 40
|
||||||
|
#define CLIENTIDSIZE 6
|
||||||
|
#define MAXRECONNECT 5
|
||||||
|
|
||||||
|
// Define Broker configurations; Values are provided in the following order
|
||||||
|
// MQTT_BROKER_PORT 9883
|
||||||
// MQTT_BROKER_DOMAIN "dcclms.modelrailroad.ovh"
|
// MQTT_BROKER_DOMAIN "dcclms.modelrailroad.ovh"
|
||||||
// MQTT_BROKER_ADDRESS 51, 210, 151, 143
|
// MQTT_BROKER_ADDRESS 51, 210, 151, 143
|
||||||
// MQTT_BROKER_USER "dcccs"
|
// MQTT_BROKER_USER "dcccs"
|
||||||
// MQTT_BROKER_PASSWD "dcccs$3020"
|
// MQTT_BROKER_PASSWD "dcccs$3020"
|
||||||
// MQTT_BROKER_CLIENTID_PREFIX "dcc$lms-"
|
// MQTT_BROKER_CLIENTID_PREFIX "dcc$lms-"
|
||||||
|
struct MQTTBroker
|
||||||
|
{
|
||||||
#define LOCAL_MQTT_BROKER F("LOCAL_MQTT_BROKER"), new MQTTBroker( 1883, {10, 0, 0, 2}, "my.local.server", MQ_UNUSED, MQ_UNUSED, MQ_UNUSED)
|
|
||||||
#define DCCEX_MQTT_BROKER F("DCCEX_MQTT_BROKER"), new MQTTBroker( 9883, {51, 210, 151, 143}, "dcclms.modelrailroad.ovh", "dcccs", "dcccs$3020", "dcc$lms-")
|
|
||||||
|
|
||||||
struct MQTTBroker {
|
|
||||||
int port;
|
int port;
|
||||||
|
IPAddress ip;
|
||||||
|
const FSH *domain = nullptr;
|
||||||
|
const FSH *user = nullptr;
|
||||||
|
const FSH *pwd = nullptr;
|
||||||
|
const FSH *prefix = nullptr;
|
||||||
|
byte cType; // connection type to identify valid params
|
||||||
|
|
||||||
MQTTBroker(int p)(port(p)){};
|
IPAddress resovleBroker(const FSH *d){
|
||||||
|
DNSClient dns;
|
||||||
|
IPAddress bip;
|
||||||
|
|
||||||
|
char domain[MAXDOMAINLENGTH];
|
||||||
|
strcpy_P(domain, (const char *)d);
|
||||||
|
|
||||||
|
dns.begin(Ethernet.dnsServerIP());
|
||||||
|
if (dns.getHostByName(domain, bip) == 1)
|
||||||
|
{
|
||||||
|
DIAG(F("MQTT Broker/ %s = %d.%d.%d.%d"), domain, bip[0], bip[1],bip[2],bip[3]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DIAG(F("MQTT Dns lookup for %s failed"), domain);
|
||||||
|
}
|
||||||
|
return bip;
|
||||||
|
}
|
||||||
|
|
||||||
|
MQTTBroker(int p, IPAddress i, const FSH *d) : port(p), ip(i), domain(d), cType(1) {};
|
||||||
|
MQTTBroker(int p, IPAddress i, const FSH *d, const FSH *uid, const FSH *pass) : port(p), ip(i), domain(d), user(uid), pwd(pass), cType(2){};
|
||||||
|
MQTTBroker(int p, IPAddress i, const FSH *d, const FSH *uid, const FSH *pass, const FSH *pfix) : port(p), ip(i), domain(d), user(uid), pwd(pass), prefix(pfix), cType(3){};
|
||||||
|
MQTTBroker(int p, const FSH *d, const FSH *uid, const FSH *pass, const FSH *pfix) : port(p), domain(d), user(uid), pwd(pass), prefix(pfix), cType(4)
|
||||||
|
{
|
||||||
|
ip = resovleBroker(d);
|
||||||
|
};
|
||||||
|
MQTTBroker(int p, const FSH *d, const FSH *uid, const FSH *pass) : port(p), domain(d), user(uid), pwd(pass), cType(5)
|
||||||
|
{
|
||||||
|
ip = resovleBroker(d);
|
||||||
|
};
|
||||||
|
MQTTBroker(int p, const FSH *d) : port(p), domain(d), cType(6)
|
||||||
|
{
|
||||||
|
ip = resovleBroker(d);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
struct DccMQTTMsg {
|
|
||||||
|
struct DccMQTTMsg
|
||||||
|
{
|
||||||
char payload[MAXPAYLOAD];
|
char payload[MAXPAYLOAD];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum DccMQTTState
|
||||||
enum DccMQTTState {
|
{
|
||||||
INIT,
|
INIT,
|
||||||
CONFIGURED, // server/client objects set
|
CONFIGURED, // server/client objects set
|
||||||
CONNECTED // mqtt broker is connected
|
CONNECTED // mqtt broker is connected
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DccMQTT
|
||||||
class DccMQTT
|
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
static DccMQTT singleton;
|
static DccMQTT singleton;
|
||||||
DccMQTT() = default;
|
DccMQTT() = default;
|
||||||
DccMQTT(const DccMQTT&); // non construction-copyable
|
DccMQTT(const DccMQTT &); // non construction-copyable
|
||||||
DccMQTT& operator=( const DccMQTT& ); // non copyable
|
DccMQTT &operator=(const DccMQTT &); // non copyable
|
||||||
|
|
||||||
EthernetClient ethClient; // TCP Client object for the MQ Connection
|
void setup(const FSH *id, MQTTBroker *broker);
|
||||||
IPAddress server; // MQTT server object
|
void connect(); // (re)connects to the broker
|
||||||
PubSubClient mqttClient; // PubSub Endpoint for data exchange
|
boolean subscribe();
|
||||||
|
|
||||||
// EthernetClient ethClient = ETHNetwork::getServer().available();
|
EthernetClient ethClient; // TCP Client object for the MQ Connection
|
||||||
|
IPAddress server; // MQTT server object
|
||||||
Queue<DccMQTTMsg> in;
|
PubSubClient mqttClient; // PubSub Endpoint for data exchange
|
||||||
|
MQTTBroker *broker; // Broker configuration object as set in config.h
|
||||||
|
|
||||||
|
Queue<DccMQTTMsg> in;
|
||||||
Queue<DccMQTTMsg> out;
|
Queue<DccMQTTMsg> out;
|
||||||
|
|
||||||
|
char clientID[(CLIENTIDSIZE*2)+1];
|
||||||
|
|
||||||
DccMQTTState mqState = INIT;
|
DccMQTTState mqState = INIT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static DccMQTT *get() noexcept
|
||||||
static DccMQTT *get() noexcept {
|
{
|
||||||
return &singleton;
|
return &singleton;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isConfigured() { return mqState == CONFIGURED; };
|
bool isConfigured() { return mqState == CONFIGURED; };
|
||||||
bool isConnected() { return mqState == CONNECTED; };
|
bool isConnected() { return mqState == CONNECTED; };
|
||||||
void setState(DccMQTTState s) { mqState = s; };
|
void setState(DccMQTTState s) { mqState = s; };
|
||||||
|
|
||||||
void setup(); // called at setup in the main ino file
|
void setup(); // called at setup in the main ino file
|
||||||
void loop();
|
void loop();
|
||||||
|
|
||||||
~DccMQTT() = default;
|
~DccMQTT() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief MQTT broker configuration done in config.h
|
// * @brief MQTT broker configuration done in config.h
|
||||||
// */
|
// */
|
||||||
|
|
||||||
|
|
||||||
// // Class for setting up the MQTT connection / topics / queues for processing commands and sendig back results
|
// // Class for setting up the MQTT connection / topics / queues for processing commands and sendig back results
|
||||||
|
|
||||||
// #define MAXDEVICEID 20 // maximum length of the unique id / device id
|
// #define MAXDEVICEID 20 // maximum length of the unique id / device id
|
||||||
|
@ -104,7 +149,7 @@ public:
|
||||||
|
|
||||||
// #define NOOFDCCTOPICS 11
|
// #define NOOFDCCTOPICS 11
|
||||||
// enum DccTopics {
|
// enum DccTopics {
|
||||||
// CMD_L, // L is Loco or Layout(power on/off)
|
// CMD_L, // L is Loco or Layout(power on/off)
|
||||||
// CMD_T,
|
// CMD_T,
|
||||||
// CMD_S,
|
// CMD_S,
|
||||||
// CMD_A,
|
// CMD_A,
|
||||||
|
@ -118,7 +163,7 @@ public:
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief List of keywords used in the command protocol
|
// * @brief List of keywords used in the command protocol
|
||||||
// *
|
// *
|
||||||
// */
|
// */
|
||||||
// #define MAX_KEYWORD_LENGTH 11
|
// #define MAX_KEYWORD_LENGTH 11
|
||||||
// PROGMEM const char _kRead[] = {"read"};
|
// PROGMEM const char _kRead[] = {"read"};
|
||||||
|
@ -137,11 +182,11 @@ public:
|
||||||
// PROGMEM const char _kBit[] = {"bit"};
|
// PROGMEM const char _kBit[] = {"bit"};
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * @brief The ingoin and outgoing queues can hold 20 messages each; this should be bigger than the number
|
// * @brief The ingoin and outgoing queues can hold 20 messages each; this should be bigger than the number
|
||||||
// * of statically allocated pool items whose pointers are getting pushed into the queues.
|
// * of statically allocated pool items whose pointers are getting pushed into the queues.
|
||||||
// *
|
// *
|
||||||
// */
|
// */
|
||||||
// #define MAXQUEUE 20 // MAX message queue length
|
// #define MAXQUEUE 20 // MAX message queue length
|
||||||
|
|
||||||
// class DccMQTT
|
// class DccMQTT
|
||||||
// {
|
// {
|
||||||
|
@ -152,31 +197,30 @@ public:
|
||||||
// static Queue<int> outGoing; // outgoing messages queue; the queue only contains indexes to the message pool
|
// static Queue<int> outGoing; // outgoing messages queue; the queue only contains indexes to the message pool
|
||||||
|
|
||||||
// public:
|
// public:
|
||||||
// static char **topics; // list of pub/sub topics
|
// static char **topics; // list of pub/sub topics
|
||||||
// static PubSubClient *mqClient;
|
// static PubSubClient *mqClient;
|
||||||
|
|
||||||
// static void setup(DCCEXParser p); // main entry to get things going
|
// static void setup(DCCEXParser p); // main entry to get things going
|
||||||
// static void loop(); // recieveing commands / processing commands / publish results
|
// static void loop(); // recieveing commands / processing commands / publish results
|
||||||
// static bool connected(); // true if the MQ client is connected
|
// static bool connected(); // true if the MQ client is connected
|
||||||
|
|
||||||
// static char *getDeviceID();
|
// static char *getDeviceID();
|
||||||
// static void setDeviceID();
|
// static void setDeviceID();
|
||||||
// static void subscribe(); // subscribes to all relevant topics
|
// static void subscribe(); // subscribes to all relevant topics
|
||||||
// static void subscribeT(char *topic);// subscribe to a particular topic for other than the std ones in subscribe (e.g. telemetry)
|
// static void subscribeT(char *topic);// subscribe to a particular topic for other than the std ones in subscribe (e.g. telemetry)
|
||||||
// static void publish(); // publishes a JSON message constructed from the outgoing queue (cid and result)
|
// static void publish(); // publishes a JSON message constructed from the outgoing queue (cid and result)
|
||||||
// static void printTopics(); // prints the list of subscribed topics - debug use
|
// static void printTopics(); // prints the list of subscribed topics - debug use
|
||||||
// static bool inIsEmpty(); // test if the incomming queue is empty
|
// static bool inIsEmpty(); // test if the incomming queue is empty
|
||||||
// static bool outIsEmpty(); // test if the outgoing queue is empty
|
// static bool outIsEmpty(); // test if the outgoing queue is empty
|
||||||
// static void pushIn(uint8_t midx); // push a command struct into the incomming queue for processing
|
// static void pushIn(uint8_t midx); // push a command struct into the incomming queue for processing
|
||||||
// static void pushOut(uint8_t midx); // push a command struct into the incomming queue for processing
|
// static void pushOut(uint8_t midx); // push a command struct into the incomming queue for processing
|
||||||
// static uint8_t popOut(); // pop a command struct with the result to be published
|
// static uint8_t popOut(); // pop a command struct with the result to be published
|
||||||
// static uint8_t popIn(); // pop a command struct from the in comming queue for processing
|
// static uint8_t popIn(); // pop a command struct from the in comming queue for processing
|
||||||
|
|
||||||
// static void pub_free_memory(int fm);
|
// static void pub_free_memory(int fm);
|
||||||
|
|
||||||
// DccMQTT();
|
// DccMQTT();
|
||||||
// ~DccMQTT();
|
// ~DccMQTT();
|
||||||
// };
|
// };
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -35,6 +35,7 @@ bool Diag::WIFI=false;
|
||||||
bool Diag::WITHROTTLE=false;
|
bool Diag::WITHROTTLE=false;
|
||||||
bool Diag::ETHERNET=false;
|
bool Diag::ETHERNET=false;
|
||||||
bool Diag::LCN=false;
|
bool Diag::LCN=false;
|
||||||
|
bool Diag::MQTT=false;
|
||||||
|
|
||||||
|
|
||||||
void StringFormatter::diag( const FSH* input...) {
|
void StringFormatter::diag( const FSH* input...) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user