#include #include #include #undef timeout #include "mqtt/async_client.h" #include using json = nlohmann::json; /* From Arduino.h */ #define HIGH 0x1 #define LOW 0x00 #define IO_INPUT 0x02 #define IO_OUTPUT 0x04 #define DIGITAL 0x08 #define ANALOG 0x10 #define PI 3.1415926535897932384626433832795 #define HALF_PI 1.5707963267948966192313216916398 #define true 1 #define false 0 // Temps // Timestamp unix en millisecondes // t_start : timestamp de départ // t_backup : timestamp de la précédente itération // t_elapsed : temps écoulé en secondes depuis le départ // dt (delta t) : temps écoulé en secondes depuis la dernière itération unsigned long t_start, t_backup; double t_elapsed, dt; unsigned long millis() { struct timespec now; clock_gettime(CLOCK_REALTIME, &now); return ((unsigned long)now.tv_sec) * 1000 + ((unsigned long)now.tv_nsec) / 1000000; } #define OP_DIGITAL_READ 0 #define OP_DIGITAL_WRITE 1 #define OP_ANALOG_READ 2 #define OP_ANALOG_WRITE 3 #define OP_PIN_MODE 4 int marche = 0; int arret = 1; int reset = 0; int b0, b1, b2, b3, b4, b5, b6, b7; int i0, i1, i2, i3, i4, i5, i6, i7; int s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11; int m0, m1, m2, m3; int v0; int p0, p1, p2, p3; int a0, a1, a2, a3, a4, a5, a6, a7; int c0, c1, c2, c3, c4, c5, c6, c7; int bouton[8]; int interrupteur[8]; int capteur[8]; int sortie[12]; int moteur[4]; int niveau[4]; int consigne[4]; int actionneur[8]; void process(); /* ******************************************************** * MQTT * * * ******************************************************** */ /* Configuration MQTT */ const std::string ADDRESS = "tcp://rabbitmq:1883"; const std::string CLIENTID = "CppClientTP"; const std::string TOPIC = "geii/in/#"; const int QOS = 1; const int CYCLE_MS = 100; mqtt::async_client client(ADDRESS, CLIENTID); void mqtt_start(mqtt::async_client *client); void mqtt_send(mqtt::async_client *client); // Réception des messages MQTT // ************************************************************ class callback : public virtual mqtt::callback { public: void message_arrived(mqtt::const_message_ptr msg) override { std::string payload = msg->to_string(); try { json j = json::parse(payload); marche = 0; arret = 1; b0 = b1 = b2 = b3 = b4 = b5 = b6 = b7 = 0; // Ne rien faire si l'objet JSON est vide if (j.empty()) return; if (j.contains("reset")) { reset = j["reset"].get() != 0; } if (j.contains("marche")) { marche = j["marche"].get() != 0; } if (j.contains("arret")) { arret = j["arret"].get() != 0; } if (j.contains("reset")) { reset = j["reset"].get() != 0; } if (j.contains("b0") && j["b0"].is_number()) { bouton[0] = b0 = j["b0"].get(); } if (j.contains("b1") && j["b1"].is_number()) { bouton[1] = b1 = j["b1"].get(); } if (j.contains("b2") && j["b2"].is_number()) { bouton[2] = b2 = j["b2"].get(); } if (j.contains("b3") && j["b3"].is_number()) { bouton[3] = b3 = j["b3"].get(); } if (j.contains("b4") && j["b4"].is_number()) { bouton[4] = b4 = j["b4"].get(); } if (j.contains("b5") && j["b5"].is_number()) { bouton[5] = b5 = j["b5"].get(); } if (j.contains("b6") && j["b6"].is_number()) { bouton[6] = b6 = j["b6"].get(); } if (j.contains("b7") && j["b7"].is_number()) { bouton[7] = b7 = j["b7"].get(); } if (j.contains("i0")) interrupteur[0] = i0 = j["i0"].get(); if (j.contains("i1")) interrupteur[1] = i1 = j["i1"].get(); if (j.contains("i2")) interrupteur[2] = i2 = j["i2"].get(); if (j.contains("i3")) interrupteur[3] = i3 = j["i3"].get(); if (j.contains("i4")) interrupteur[4] = i4 = j["i4"].get(); if (j.contains("i5")) interrupteur[5] = i5 = j["i5"].get(); if (j.contains("i6")) interrupteur[6] = i6 = j["i6"].get(); if (j.contains("i7")) interrupteur[7] = i7 = j["i7"].get(); if (j.contains("c0") && j["c0"].is_number()) capteur[0] = c0 = j["c0"].get(); if (j.contains("c1") && j["c1"].is_number()) capteur[1] = c1 = j["c1"].get(); if (j.contains("c2") && j["c2"].is_number()) capteur[2] = c2 = j["c2"].get(); if (j.contains("c3") && j["c3"].is_number()) capteur[3] = c3 = j["c3"].get(); if (j.contains("c4") && j["c4"].is_number()) capteur[4] = c4 = j["c4"].get(); if (j.contains("c5") && j["c5"].is_number()) capteur[5] = c5 = j["c5"].get(); if (j.contains("c6") && j["c6"].is_number()) capteur[6] = c6 = j["c6"].get(); if (j.contains("c7") && j["c7"].is_number()) capteur[7] = c7 = j["c7"].get(); if (j.contains("v0") && j["v0"].is_number()) v0 = j["v0"].get(); if (j.contains("p0") && j["p0"].is_number()) { niveau[0] = p0 = j["p0"].get(); } if (j.contains("p1") && j["p1"].is_number()) { niveau[1] = p1 = j["p1"].get(); } if (j.contains("p2") && j["p2"].is_number()) { niveau[2] = p2 = j["p2"].get(); } if (j.contains("p3") && j["p3"].is_number()) { niveau[3] = p3 = j["p3"].get(); } process(); mqtt_send(&client); } catch (const json::parse_error &e) { std::cerr << "Erreur JSON : " << e.what() << "\n"; } } }; callback cb; void mqtt_open(mqtt::async_client *client) { client->set_callback(cb); mqtt::connect_options connOpts; connOpts.set_clean_session(true); connOpts.set_user_name("admin"); connOpts.set_password("geii2025"); try { client->connect(connOpts)->wait(); client->start_consuming(); client->subscribe(TOPIC, QOS)->wait(); mqtt_start(client); } catch (const mqtt::exception &exc) { std::cerr << "Erreur MQTT: " << exc.what() << "\n"; } } void mqtt_send(mqtt::async_client *client) { json obj = { }; obj["moteurs"] = json::array(); for (int i = 0; i < 8; ++i) { obj["moteurs"].push_back(moteur[i]); } obj["actionneurs"] = json::array(); for (int i = 0; i < 8; ++i) { obj["actionneurs"].push_back(actionneur[i]); } obj["sorties"] = json::array(); for (int i = 0; i < 12; ++i) { obj["sorties"].push_back(sortie[i]); } std::string payload = obj.dump(); auto msg = mqtt::make_message("geii/out", payload); msg->set_qos(1); client->publish(msg); } void mqtt_start(mqtt::async_client *client) { auto msg = mqtt::make_message("geii/out", "reset"); msg->set_qos(2); client->publish(msg); } void mqtt_close() { try { client.unsubscribe(TOPIC)->wait(); client.stop_consuming(); client.disconnect()->wait(); } catch (const mqtt::exception &exc) { std::cerr << "Erreur déconnexion MQTT: " << exc.what() << std::endl; } } // ************************************************************ /* ******************************************************** * TEMPORISATION RETARD A LA MONTEE * * La sortie passe à 1 au bout de la tempo * ******************************************************** */ class TemporisationRetardMontee { // methodes public: // Contructeur qui prend la duree souhaitee de la temporisation TemporisationRetardMontee(unsigned long duree) { this->duree = duree; sortie = false; captureTemps = false; } // Activation de la temporisation. Doit etre fait tout le temps de la duree de la tempo void activation() { // Capture du temps de reference if (!captureTemps) { debut = millis(); captureTemps = true; } // Calcul du temps ecoule depuis le temps de reference tempsEcoule = millis() - debut; // Mise a 1 de la fin de tempo if (tempsEcoule >= duree) { sortie = true; captureTemps = false; } else { sortie = false; } } // Precharge de la temporisation void setDuree(unsigned long majduree) { duree = majduree; sortie = false; captureTemps = false; } // Interrogation du bit de fin de tempo bool getSortie() { return (sortie); } // Recuperation du temps ecoule depuis le debut si necessaire unsigned long getTempsEcoule() { return (tempsEcoule); } // Attributs private: unsigned long duree; unsigned long debut; unsigned long tempsEcoule; bool captureTemps; bool sortie; }; /******************************************************** * TEMPORISATION RETARD A LA DESCENTE * * La sortie passe à 0 au bout de la tempo * *********************************************************/ class TemporisationRetardDescente { public: // Contructeur qui prend la duree souhaitee de la temporisation TemporisationRetardDescente(unsigned long duree) { this->duree = duree; sortie = false; captureTemps = false; } // Activation de la temporisation. Doit etre fait tout le temps de la duree de la tempo void activation() { // Capture du temps de reference if (!captureTemps) { debut = millis(); captureTemps = true; sortie = true; } // Calcul du temps ecoule depuis le temps de reference tempsEcoule = millis() - debut; // Mise a 0 de la fin de tempo if (tempsEcoule >= duree) { sortie = false; captureTemps = false; } } // Precharge de la temporisation void setDuree(unsigned long majduree) { duree = majduree; sortie = false; captureTemps = false; } // Interrogration du bit de fin de tempo bool getSortie() { return (sortie); } // Recuperation du temps ecoule depuis le debut si necessaire unsigned long getTempsEcoule() { return (tempsEcoule); } private: unsigned long duree; unsigned long debut; unsigned long tempsEcoule; bool captureTemps; bool sortie; }; /******************************************************** **** CLIGNOTEUR ************************************* *********************************************************/ class Clignoteur { // methodes public: // Construteur qui prend en parametre le temps haut ou bas souhaitee Clignoteur(int baseDeTemps) { this->baseDeTemps = baseDeTemps; } // Fonction qui renvoie true si le clignoteur est ├á l'├®tat haut et false s'il est ├á l'├®tat bas bool statut() { return ((millis() / baseDeTemps) % 2 == 1); } // Attributs private: int baseDeTemps; }; /******************************************************** **** COMPTEUR ************************************* **** ATTENTION : Il faut gerer le front montant dans le programme *********************************************************/ class Compteur { // methodes public: // Constructeur qui prend en parametre la valeur de preselection Compteur(int valeurPreselection) { this->valeurPreselection = valeurPreselection; valeur = 0; } // Incrementation du compteur void incrementer() { valeur++; } // Decrementation du compteur void decrementer() { valeur--; } // remise a zero du compteur void remettreAZero() { valeur = 0; } // recuperation de la valeur courante int getValeurCourante() { return (valeur); } // est-ce que la preselection est atteinte (sortie Q compteur Siemens ou Schnieder) bool getSortie() { return (valeur == valeurPreselection); } // Attributs private: int valeur; int valeurPreselection; }; /******************************************************** **** MISE A L'ECHELLE DE VALEUR ************************ *********************************************************/ class MiseAEchelle { public: // Constructeur qui ne prend en parametre la plage d'entree et la plage de sortie MiseAEchelle(float minEntree, float maxEntree, float minSortie, float maxSortie) { this->minEntree = minEntree; this->maxEntree = maxEntree; this->minSortie = minSortie; this->maxSortie = maxSortie; } // fonction de conversion qui prend la valeur a convertir et renvoie la valeur convertie float convertir(float valeurAConvertir) { if (valeurAConvertir >= minEntree && valeurAConvertir <= maxEntree) { float norm = (1 / (maxEntree - minEntree)) * (valeurAConvertir - minEntree); float scale = (maxSortie - minSortie) * norm + minSortie; return (scale); } return (-1000); } // Attributs private: float minEntree; float minSortie; float maxEntree; float maxSortie; };