From 5cf305a2532f7a501ef3a63f83c1921282e06812 Mon Sep 17 00:00:00 2001 From: Ludovic Pouzenc Date: Fri, 17 Jun 2016 12:09:57 +0200 Subject: Ready to go Arduino prog template. Only needs io mapping and logic customization. --- ecl_maison.ino | 481 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ io_declarations.h | 34 ++++ 2 files changed, 515 insertions(+) create mode 100644 ecl_maison.ino create mode 100644 io_declarations.h diff --git a/ecl_maison.ino b/ecl_maison.ino new file mode 100644 index 0000000..c28112f --- /dev/null +++ b/ecl_maison.ino @@ -0,0 +1,481 @@ +#include +#include +#include +#include +#include + +/************** + * PARAMETRES * + **************/ +// Declaration des lignes, boutons, IO et enums associés +#include "io_declarations.h" +// Doit etre dans un fichier séparé car les prototypes des fonctions du fichier .ino +// sont générés automatiquement et placés au dessus des déclarations des enums sinon. + +// Activation debug (1 = oui, 0 = non) +#define DEBUG_VIA_SYSLOG 0 +#define DEBUG_VIA_SERIAL 0 + +// Définition de la sortie pour la LED de statut (13 = LED soudée sur l'arduino) +#define STATUS_LED 13 + +// Paramètres Syslog (ip/port du serveur syslog-ng) +static uint8_t ip_debug[] = { 192, 168, 1, 10 }; +static uint16_t port_debug = 514; + +// Paramètres Ethernet (mac et ip locales pour serveur web) +static uint8_t mac[] = { 0xFE, 0xED, 0xDE, 0xAD, 0xBE, 0xEF }; +static uint8_t ip[] = { 192, 168, 1, 178 }; + +// Paramètres détection alea +#define ALEA_NB_APPELS_MAX 20 +#define ALEA_FENETRE_MILLIS 10000 // 5 secondes +#define ALEA_BLOQUAGE_MILLIS 60000 // 1 minute + +// Pamètres détection entrées +#define DUREE_MIN_BOUTON_PRESSE 50 // millisecondes + +// Paramètres WebServer +#define PREFIX "" +#define NAMELEN 32 +#define VALUELEN 32 + +// Paramètres persistance +#define EEPROM_BASE_ADDR_DATA 15 +#define CHECKSUM_INIT 10 +#define CHECKSUM_INVALID 0xFF +#define CONSIGNE_PAR_DEFAUT HIGH + +/****************** + * OBJETS GLOBAUX * + *****************/ +WebServer webserver(PREFIX, 80); // Serveur Web +EthernetUDP Udp; // Socket UDP pour debug Syslog + +static uint8_t etat_entrees[NUM_ENTREES]; // Etat actuel des entrées +static unsigned long millis_precedent_front_descendant[NUM_ENTREES]; // Stockage timing pour filtrage des entrées +static uint8_t consigne_sorties[NUM_SORTIES]; // Consignes à appliquer sur les sorties + + +/************************ + * ROUTINES PRINCIPALES * + ************************/ +// Initialisation de l'ensemble des composants +void setup(){ + Ethernet.begin(mac, ip); + log_init(); + webserver_setup(); + + // Restaurer de l'EEPROM les consignes et positionner les bascules D des pins de sortie + // N'applique pas la tension de sortie immédiatement car le paramètrage des pin en OUTPUT n'est pas encore fait + charger_consignes(); + appliquer_consignes(false); + + // Paramétrer le mode de toutes les pins utilisées en entrée + for (int i=0; i inline Print &operator <<(Print &obj, T arg) { obj.print(arg); return obj; } + +// Code gérant la réponse à la requête HTTP permettant d'afficher l'état des consignes +void servlet_list(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) { + server.httpSuccess(); + + server << ""; + server << ""; + server << "Domotique étage"; + server << ""; + server << ""; + server << ""; + server << "

Domotique étage

"; + server << ""; + + sortie_t empty = (sortie_t) -1; + // Salon + html_tr_6_sorties(server, led_arduino, rl_ecl_ext_jardin, empty, empty, empty, empty); + //html_tr_6_sorties(server, rl_sal_spot_left, empty, rl_sal_ecl_plaf, empty, rl_sal_spot_right, empty); + //html_tr_6_sorties(server, empty, rl_sam_gu10_1, rl_sam_gu10_3, rl_sam_gu10_5, empty, empty); + //html_tr_6_sorties(server, empty, empty, rl_sam_gu10_2, rl_sam_gu10_4, empty, empty); + //html_tr_6_sorties(server, empty, empty, empty, empty, rl_sas_gu10_left, empty); + //html_tr_6_sorties(server, rl_sam_pc_left, empty, empty, empty, rl_sas_gu10_right, rl_sas_ecl_plaf); + //html_tr_6_sorties(server, empty, empty, rl_cuis_gu10_2, rl_cuis_gu10_1, empty, rl_coul_plaf); + //html_tr_6_sorties(server, empty, rl_cuis_ecl_evier, empty, rl_cuis_ecl_plaf, empty, empty); + + server << "
"; + server << ""; + server << ""; +} + +// Formattage d'une case de tableau +void html_td_sortie(Print &p, sortie_t s) { + if ( s == -1 ) { + p << " "; + } else { + p << ""; + p << ""; + p << ""; + } +} + +// Formattage d'une ligne de tableau à 6 colonnes +void html_tr_6_sorties(Print &p, sortie_t s1, sortie_t s2, sortie_t s3, sortie_t s4, sortie_t s5, sortie_t s6) { + p << ""; + html_td_sortie(p, s1); + html_td_sortie(p, s2); + html_td_sortie(p, s3); + html_td_sortie(p, s4); + html_td_sortie(p, s5); + html_td_sortie(p, s6); + p << ""; +} + +// Code gérant la réponse à la requête HTTP permettant de modifier l'état d'une consigne +void servlet_set(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) { + URLPARAM_RESULT rc; + char name[NAMELEN]; int name_len; + char value[VALUELEN]; int value_len; + + if (type == WebServer::HEAD) return; + + while (strlen(url_tail)) { + rc = server.nextURLparam(&url_tail, name, NAMELEN, value, VALUELEN); + if (rc != URLPARAM_EOS) { + int key = strtoul(name, NULL, 10); + int val = strtoul(value, NULL, 10); + if ( key >= 0 && key < NUM_SORTIES ) { + consigne_sorties[key] = (val==1)?HIGH:LOW; + } + } + } + appliquer_consignes(true); + server.httpSeeOther(PREFIX "/"); +} + +// Initialisation et paramétrage du serveur Web +void webserver_setup() { + webserver.begin(); + webserver.setDefaultCommand(&servlet_list); + webserver.addCommand("set", &servlet_set); +} + +/********************** + * I/O ET PERSISTENCE * + **********************/ +/* Lit les valeurs de consigne_sorties[] et : + - met à jour l'état des bascules de la sortie (pin) correspondante + - écrit cet état dans l'EEPROM + + forcer : permet d'outrepasser le temps de pause en cas d'une précédente détection d'aléa (cf protection_alea()) +*/ +void appliquer_consignes(boolean forcer) { + uint8_t checksum ; + int i; + + if (!protection_alea() && !forcer) return; + + // Invalide les infos stockées dans l'EEPROM + EEPROM.write(EEPROM_BASE_ADDR_DATA, CHECKSUM_INVALID); + + log_begin_line(); + log_print("appliquer_consignes() : Sorties "); + checksum = CHECKSUM_INIT; + for (i=0; i DUREE_MIN_BOUTON_PRESSE ) { + // L'entrée est restées assez longtemps en front bas + evenement_entree = (entree_t) i; + millis_precedent_front_descendant[i] = 0; + log_print("!"); + } else { + log_print("w"); + } + } else { + log_print("r"); + } + } + } + + log_print(" retour "); + log_printInt(evenement_entree); + log_end_line(); + + return (entree_t)evenement_entree; +} + + +/************ + * SECURITE * + ************/ +/* Comptabilise le nombre d'évènements qui se produit sur les sorties + ( = nbre d'appels de protection_alea() par appliquer_consignes() ) + - retourne false si un trop grand nombre de changements d'état à été détecté il y a moins de ALEA_BLOQUAGE_MILLIS ms + - retourne true si on est en situation normale +*/ +boolean protection_alea() { + static uint8_t curseur_appels = 0; + static unsigned long millis_precedents_appels[ALEA_NB_APPELS_MAX]; + static unsigned long millis_debut_blocage; + + unsigned long millis_oldest, maintenant; + + maintenant = millis(); + + // Ajout de cet appel ) protection_alea() dans le buffer rotatif millis_precedents_appels + millis_precedents_appels[curseur_appels] = maintenant; + curseur_appels = (curseur_appels + 1) % ALEA_NB_APPELS_MAX; + + // Si on est en période de blocage, retourner false tout de suite. Si en fin de période, oublier le blocage + if ( millis_debut_blocage > 0 ) { + if ( maintenant - millis_debut_blocage < ALEA_BLOQUAGE_MILLIS ) { + return false; + } else { + millis_debut_blocage = 0; + log_begin_line(); + log_print("protection_alea() : Fin de blocage"); + log_end_line(); + } + } + + // Si le plus viel appel enregistré est moins vieux que (maintenant - ALEA_FENETRE_MILLIS) + // Alors c'est qu'on a une trop grande fréquence d'évènements, bloquer le système pdt ALEA_BLOQUAGE_MILLIS + millis_oldest = millis_precedents_appels[curseur_appels]; + if ( millis_oldest > (maintenant - ALEA_FENETRE_MILLIS) ) { + millis_debut_blocage = maintenant; + + log_begin_line(); + log_print("protection_alea() : "); + log_printInt(ALEA_NB_APPELS_MAX); + log_print(" appels en moins de "); + log_printInt(ALEA_FENETRE_MILLIS); + log_print(" millisecondes. Blocage pendant "); + log_printInt(ALEA_BLOQUAGE_MILLIS / 1000); + log_print(" secondes"); + log_end_line(); + } + + return true; +} + + +/************* + * DEBUGGING * + *************/ +void log_init() { +#if DEBUG_VIA_SYSLOG == 1 + Udp.begin(port_debug); // port local comme le port de destination (rsyslog) +#endif +#if DEBUG_VIA_SERIAL == 1 + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for Leonardo only + } +#endif + + log_begin_line(); + log_print("log_init() : Mode debug initialisé"); + log_end_line(); +} + +void log_begin_line() { +#if DEBUG_VIA_SYSLOG == 1 + Udp.beginPacket(ip_debug, port_debug); + Udp.print("<15> "); // Syslog header : facility * 8 (1 == user) + severity (7 == debug) + Udp.print("millis()=="); + Udp.print(millis()); + Udp.print(" "); +#endif +#if DEBUG_VIA_SERIAL == 1 + Serial.print("millis()=="); + Serial.print(millis()); + Serial.print(" "); +#endif +} + +void log_print(const char *str) { +#if DEBUG_VIA_SYSLOG == 1 + Udp.print(str); +#endif +#if DEBUG_VIA_SERIAL == 1 + Serial.print(str); +#endif +} + +void log_printInt(long val) { +#if DEBUG_VIA_SYSLOG == 1 + Udp.print(val); +#endif +#if DEBUG_VIA_SERIAL == 1 + Serial.print(val); +#endif +} + +void log_end_line() { +#if DEBUG_VIA_SYSLOG == 1 + Udp.endPacket(); +#endif +#if DEBUG_VIA_SERIAL == 1 + Serial.println(""); +#endif +} + diff --git a/io_declarations.h b/io_declarations.h new file mode 100644 index 0000000..ea7ef74 --- /dev/null +++ b/io_declarations.h @@ -0,0 +1,34 @@ + +// ======================================== +// == INTERRUPTEURS ======================= +// ======================================== +typedef enum { + bt_test_arduino, bt_ext_cote_rue, + + NUM_ENTREES +} entree_t; + +static uint8_t pin_entrees[NUM_ENTREES] = { + 0, 4, +}; + + +// ======================================== +// == LIGNES ============================== +// ======================================== + +typedef enum { + led_arduino, rl_ecl_ext_jardin, + + NUM_SORTIES +} sortie_t; + + +static uint8_t pin_sorties[NUM_SORTIES] = { + 13, 41, +}; + +static char desc_sorties[NUM_SORTIES][16] = { + "LED Arduino", "Jardin", +}; + -- cgit v1.2.3