1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-04-20 20:21:18 +02:00

start adding MQTT client channel implementation

This commit is contained in:
Gregor Baues 2021-05-05 11:23:59 +02:00
parent c042240019
commit 595b6bad93
4 changed files with 140 additions and 28 deletions

View File

@ -50,7 +50,29 @@ char topicName[MAXTBUF];
char topicMessage[MAXTMSG]; char topicMessage[MAXTMSG];
// char keyword[MAX_KEYWORD_LENGTH]; // char keyword[MAX_KEYWORD_LENGTH];
// pairing functions used for creating a client identifier
// when a external system connects via MQ to the CS i.e. subscribes to the main channel the first message to be published
// shall be a admin message with a random number
// the cs will based on a counter use a second number to create the cantor encoding of both numbers and publish the cantor code
// this message will be seen by all throttles and they can decode the number which provides the first number they send and the
// second number to be used as tpoic for the external system from then on. The CS will recieve on all topics the commands and
// during processing then send the replies to the topic from which the command was recieved.
// Thus the main channel shall not be used for any p2p coms ev just for broadcast from the CS to the subscribed clients
int cantorEncode(int a, int b)
{
return (((a + b) * (a + b + 1)) / 2) + b;
}
void cantorDecode(int c, int *a, int *b)
{
int w = floor((sqrt(8 * c + 1) - 1) / 2);
int t = (w * (w + 1)) / 2;
*b = c - t;
*a = w - *b;
}
/** /**
* @brief Copies an byte array to a hex representation as string; used for generating the unique Arduino ID * @brief Copies an byte array to a hex representation as string; used for generating the unique Arduino ID
@ -78,6 +100,11 @@ void mqttCallback(char *topic, byte *payload, unsigned int length)
topicMessage[0] = '\0'; topicMessage[0] = '\0';
strcpy(topicName, topic); strcpy(topicName, topic);
switch (payload[0])
{
case '<':
{
// DCC-EX command
auto pool = mqtt->getPool(); auto pool = mqtt->getPool();
auto q = mqtt->getIncomming(); auto q = mqtt->getIncomming();
@ -85,13 +112,31 @@ void mqttCallback(char *topic, byte *payload, unsigned int length)
strlcpy(tm.cmd, (char *)payload, length + 1); strlcpy(tm.cmd, (char *)payload, length + 1);
// Add the recieved command to the pool // Add the recieved command to the pool
int idx = pool->setItem(tm); int idx = pool->setItem(tm);
if ( idx == -1) { if (idx == -1)
{
DIAG(F("MQTT Command pool full. Could not handle recieved command.")); DIAG(F("MQTT Command pool full. Could not handle recieved command."));
return; return;
} }
// Add the index of the pool item to the incomming queue // Add the index of the pool item to the incomming queue
q->push(idx); q->push(idx);
DIAG(F("MQTT Message arrived [%s]: [%s]"), topicName, tm.cmd); DIAG(F("MQTT Message arrived [%s]: [%s]"), topicName, tm.cmd);
break;
}
case '(':
{
// MQTT Ctrl command
payload[length] = '\0';
DIAG(F("MQTT Ctrl Message arrived [%s]: [%s]"), topicName, (char *)payload);
break;
}
default:
{
// invalid command
payload[length] = '\0';
DIAG(F("MQTT Invalid DCC-EX command: %s"), (char *)payload);
break;
}
}
} }
/** /**
@ -106,7 +151,8 @@ void DccMQTT::connect()
int reconnectCount = 0; int reconnectCount = 0;
if(broker->prefix != nullptr) { if (broker->prefix != nullptr)
{
char tmp[20]; char tmp[20];
strcpy_P(tmp, (const char *)broker->prefix); strcpy_P(tmp, (const char *)broker->prefix);
connectID[0] = '\0'; connectID[0] = '\0';
@ -162,7 +208,8 @@ while (!mqttClient.connected() && reconnectCount < MAXRECONNECT)
// for the time being only one topic at the root which os the unique clientID from the MCU // for the time being only one topic at the root which os the unique clientID from the MCU
// QoS is 0 by default // QoS is 0 by default
boolean DccMQTT::subscribe() { boolean DccMQTT::subscribe()
{
return mqttClient.subscribe(clientID); return mqttClient.subscribe(clientID);
} }
@ -228,12 +275,9 @@ void DccMQTT::setup(const FSH *id, MQTTBroker *b)
// mqttDccExParser = p; // mqttDccExParser = p;
} }
void DccMQTT::loop() void DccMQTT::loop()
{ {
// DccTelemetry::deltaT(1);
if (!mqttClient.connected()) if (!mqttClient.connected())
{ {
connect(); connect();
@ -242,9 +286,30 @@ void DccMQTT::loop()
{ {
DIAG(F("mqttClient returned with error; state: %d"), mqttClient.state()); DIAG(F("mqttClient returned with error; state: %d"), mqttClient.state());
}; };
// read incomming queue for processing; one per loop
bool state;
if (in.count() > 0)
{
auto idx = in.peek();
auto c = pool.getItem(in.pop(), &state);
DIAG(F("MQTT Processing pool: %d with command: %s"), idx, c->cmd);
}
// read outgoing queue for publishing replies; one per loop
if (out.count() > 0)
{
auto m = pool.getItem(out.pop(), &state);
DIAG(F("MQTT Publish reply from command %s"), m->cmd);
}
// DccMQTTProc::loop(); //!< give time to the command processor to handle msg .. // DccMQTTProc::loop(); //!< give time to the command processor to handle msg ..
// take a command from the incomming queue
// execute it
// store the results in the outgoing queue
// DccMQTT::publish(); //!< publish waiting messages from the outgoing queue // DccMQTT::publish(); //!< publish waiting messages from the outgoing queue
// DccTelemetry::deltaT(1); // if there is someting in the outgoing queue publish on response
} }
// #include <avr/pgmspace.h> // for PROGMEM use // #include <avr/pgmspace.h> // for PROGMEM use

View File

@ -22,9 +22,10 @@
#define MAXTBUF 50 //!< max length of the buffer for building the topic name ;to be checked #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 MAXTMSG 120 //!< max length of the messages for a topic ;to be checked PROGMEM ?
#define MAXTSTR 30 //!< max length of a topic string #define MAXTSTR 30 //!< max length of a topic string
#define MAXCONNECTID 40 #define MAXCONNECTID 40 // Broker connection id length incl possible prefixes
#define CLIENTIDSIZE 6 #define CLIENTIDSIZE 6 //
#define MAXRECONNECT 5 #define MAXRECONNECT 5 // reconnection tries before final failure
#define MAXMQTTCONNECTIONS 20 // maximum number of unique tpoics available for subscribers
// Define Broker configurations; Values are provided in the following order // Define Broker configurations; Values are provided in the following order
// MQTT_BROKER_PORT 9883 // MQTT_BROKER_PORT 9883
@ -79,10 +80,20 @@ struct MQTTBroker
}; };
}; };
/**
* @brief dcc-ex command as recieved via MQ
*
*/
typedef struct csmsg_t { typedef struct csmsg_t {
char cmd[MAXPAYLOAD]; char cmd[MAXPAYLOAD];
} csmsg_t; } csmsg_t;
typedef struct csmqttclient_t {
uint8_t subscriber;
uint8_t cs;
} csmqttclient_t
enum DccMQTTState enum DccMQTTState
{ {
INIT, INIT,
@ -101,6 +112,8 @@ private:
void setup(const FSH *id, MQTTBroker *broker); void setup(const FSH *id, MQTTBroker *broker);
void connect(); // (re)connects to the broker void connect(); // (re)connects to the broker
boolean subscribe(); boolean subscribe();
// static int cantorEncode(int a, int b);
// static void cantorDecode(int c, int *a, int *b);
EthernetClient ethClient; // TCP Client object for the MQ Connection EthernetClient ethClient; // TCP Client object for the MQ Connection
IPAddress server; // MQTT server object IPAddress server; // MQTT server object
@ -115,6 +128,9 @@ private:
DccMQTTState mqState = INIT; DccMQTTState mqState = INIT;
public: public:
static DccMQTT *get() noexcept static DccMQTT *get() noexcept
{ {

16
DccMQTTProc.cpp Normal file
View File

@ -0,0 +1,16 @@
/**
* @file DccMQTTProc.cpp
* @author Gregor Baues (gr2bba@gmail.com)
* @brief Eexecuting DCC commands recieved through MQTT.
* @version 0.1
* @date 08-07-2020
*
* @copyright Copyright (c) 2021
*
* Licenced under GPLv3
*/
#include <Arduino.h>
#include <Diag.h>

15
DccMQTTProc.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef _DccMQTTProc_h_
#define _DccMQTTProc_h_
class DccMQTTProc
{
private:
public:
static void loop();
DccMQTTProc() = default;
~DccMQTTProc() = default;
};
#endif