#ifndef _MQTTInterface_h_ #define _MQTTInterface_h_ #if __has_include("config.h") #include "config.h" #else #warning config.h not found. Using defaults from config.example.h #include "config.example.h" #endif #include "defines.h" #include #include #include #include #include #include #include #define MAXPAYLOAD 64 // max length of a payload recieved #define MAXDOMAINLENGTH 32 // domain name length for the broker e.g. test.mosquitto.org #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 #define MAXCONNECTID 40 // broker connection id length incl possible prefixes #define CLIENTIDSIZE 6 // max length of the clientid used for connection to the broker #define MAXRECONNECT 5 // reconnection tries before final failure #define MAXMQTTCONNECTIONS 20 // maximum number of unique tpoics available for subscribers #define OUT_BOUND_SIZE 256 // Size of the RingStream used to provide results from the parser and publish #define MAX_POOL_SIZE 32 // recieved command store size // Define Broker configurations; Values are provided in the following order // MQTT_BROKER_PORT 9883 // MQTT_BROKER_DOMAIN "dcclms.modelrailroad.ovh" // MQTT_BROKER_ADDRESS 51, 210, 151, 143 // MQTT_BROKER_USER "dcccs" // MQTT_BROKER_PASSWD "dcccs$3020" // MQTT_BROKER_CLIENTID_PREFIX "dcc$lms-" struct MQTTBroker { 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 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); }; }; /** * @brief dcc-ex command as recieved via MQ * */ typedef struct csmsg_t { char cmd[MAXPAYLOAD]; // recieved command message byte mqsocket; // from which mqsocket / subscriberid } csmsg_t; typedef struct csmqttclient_t { int distant; // random int number recieved from the subscriber byte mqsocket; // mqtt socket = subscriberid provided by the cs long topic; // cantor(subscriber,cs) encoded tpoic used to send / recieve commands bool open; // true as soon as we have send the id to the mq broker for the client to pickup } csmqttclient_t; enum MQTTInterfaceState { INIT, CONFIGURED, // server/client objects set CONNECTED, // mqtt broker is connected CONNECTION_FAILED // Impossible to get the connection set after MAXRECONNECT tries }; class MQTTInterface { private: // Methods MQTTInterface(); MQTTInterface(const MQTTInterface &); // non construction-copyable MQTTInterface &operator=(const MQTTInterface &); // non copyable void setup(const FSH *id, MQTTBroker *broker); // instantiates the broker void connect(); // (re)connects to the broker bool setupNetwork(); // sets up the network connection for the PubSub system void loop2(); // Members static MQTTInterface *singleton; // unique instance of the MQTTInterface object EthernetClient ethClient; // TCP Client object for the MQ Connection byte mac[6]; // simulated mac address IPAddress server; // MQTT server object MQTTBroker *broker; // Broker configuration object as set in config.h ObjectPool pool; // Pool of commands recieved for the CS Queue in; // Queue of indexes into the pool according to incomming cmds Queue subscriberQueue; // Queue for incomming subscribers; push the subscriber into the queue for setup in a loop cycle char clientID[(CLIENTIDSIZE * 2) + 1]; // unique ID of the commandstation; not to confused with the connectionID csmqttclient_t clients[MAXMQTTCONNECTIONS]; // array of connected mqtt clients char connectID[MAXCONNECTID]; // clientId plus possible prefix if required by the broker uint8_t subscriberid = 0; // id assigned to a mqtt client when recieving the inital handshake; +1 at each connection bool connected = false; // set to true if the ethernet connection is available MQTTInterfaceState mqState = INIT; // Status of the MQBroker connection RingStream *outboundRing; // Buffer for collecting the results from the command parser PubSubClient *mqttClient; // PubSub Endpoint for data exchange public: static MQTTInterface *get() noexcept { return singleton; } boolean subscribe(const char *topic); void publish(const char *topic, const char *payload); ObjectPool *getPool() { return &pool; }; Queue *getIncomming() { return ∈ }; Queue *getSubscriptionQueue() { return &subscriberQueue; }; char *getClientID() { return clientID; }; uint8_t getClientSize() { return subscriberid; } // initalized to 0 so that the first id comming back is 1 // index 0 in the clients array is not used therefore //! improvement here to be done to save some bytes uint8_t obtainSubscriberID() { if (subscriberid == MAXMQTTCONNECTIONS) { return 0; // no more subscriber id available } return (++subscriberid); } csmqttclient_t *getClients() { return clients; }; RingStream *getRingStream() { return outboundRing; }; // debug only static void setup(); static void loop(); ~MQTTInterface() = default; }; #endif