/* souris_pot: Souris Wifi à base d'esp8266 pour la centrale DCC D17 Historique: - 2018/12/27 Ulysse Delmas-Begue: creation Notes: - Du debug est présent sur la liaison série TBD: - Ajouter une commande dans la centrale pour avoir juste l'état d'AU - Supporter en optionel un ecran OLED sur le bus I2C pour afficher l'adresse, le sens, la vitesse, les fonctions auxiliaires activées Utilisation: - A la mise sous tension la souris se met dans l'etat attente (>ATT) la LED est eteinte - Une pression sur le bouton poussoir (BP) la fait en mode de selection d'adresse de locomotive (>SEL_LOCO) La LED s'allume et reste allumée L'adresse est initialement à 0 Un appuie court (moins de 300ms) incremente l'adresse de 1 Un appuie moyen (entre 300ms et 3s) incremente l'adresse de 10 Ex: Si dans l'état ATT on appuie sur BP: MOY + MOY + COURT + COURT + COURT Au début de MOY, on passe en SEL_LOCO, la LED s'allume A la fin du 1er MOY l'adresse vaut 10, puis 20, puis 21, puis 22, puis 23 - Si le bouton est relaché plus d'une seconde, l'adresse est validée et on passe en mode de recherche WIFI (>SEARCH) La LED emet de bref flashs (On 1/5) Si au bout de 30s la souris n'a pas pu se connecter à la centrale, elle repasse dans l'état ATT - Des que la souris arrive à se connecter à la centrale, elle passe en mode ON (>ON) - Le potentiomètre permet de choisir le sens ainsi que la vitesse (vitesse nulle au millieu)Il est conseiller de commencer potentiomètre au centre pour une vitesse nulle - Un appuie court sur BP change l'état de l'arrêt d'urgence En mode normal, la LED clignote lentement (1Hz) En Arrêt d'urgence, elle clignote vite (5Hz) - Un appuie moyen sur BP entre dans un mode pour jouer avec les fonction auxiliaires des decodeurs Le numéro de la fonction est initialement à 0. F0=FL=les phares Un appuie court (moins de 300ms) incremente le numéro de la fonction de 1 Un appuie moyen (entre 300ms et 3s) incremente le numéro de la fonction de 10 La fonction est changée lorsque BP est relaché pendant 1s Les 29 fonctions F0-F28 sont suportées Ex pour F0 : MOY Ex pour F21: MOY + MOY + MOY + COURT D'accord, ce n'est pas super intuitif, mais cela permet d'essayer toutes les fonctions Pour rester simple, on utilisera souvent que COURT pour AU ou MOY pour F0, c'est a dire les phares - Un appuie long jusqu'a ce que le LED s'éteigne (ou 10s si vous n'avez pas de LED) déconnecte la souris de la centrale, éteint le WIFI et repasse en mode ATT */ // Parametres utilisateur const char* ssid = "D17-0001"; // nom du réseau WIFI de la centrale D17 (D17-0001 par defaut) const char* password = "ulysse31"; // mot de passe WIFI de la centrale D17 (ulysse31 par defaut) const int port = 1234; // port TCP de D17 (1234 par defaut) const byte allow_analog = 1; // 0 ou 1. 1 autorise l'utilisation de l'adresse 1 (qui passe D17 en analogique) const byte read_au = 1; // 0 ou 1. 1 lit l'arret d'urgence de la centrale. bien pour se synchroniser avec les autres souris mais demande un peu plus de traitement const byte inv_pot = 0; // 0 ou 1. 1 pour inverser le sens du potentiomètre // Fin des parametres utilisateur // Ne pas modifier le reste du programme (sauf si vous savez ce que vous faite) #define TITLE "D17 WIFI souris v2018-12-27" #include WiFiClient client; #define ST_ATT 0 #define ST_SEL_LOCO 1 #define ST_SEARCH 2 #define ST_ON 3 byte state = ST_ATT; byte first = 1; // A 1 lorsque l'on rentre dans un état char rx[120] = ""; byte rx_au = 0; byte adr = 0; byte f0 = 1; //phares allumés par défaut byte sel_fct = 0; byte f_0_28[] = { 0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 }; int f_num = -1; byte f_val = 0; char sz_msg[30] = ""; char sz_old_msg[30] = ""; // Cette fonction est a appeller toutes les 100ms // Cette fonction met a jour la LED en fonction de l'etat de la souris void led_100ms(void) { static byte cpt_led = 0; if(state == ST_ATT) digitalWrite(LED_BUILTIN, HIGH); // LED OFF if(state == ST_SEL_LOCO) digitalWrite(LED_BUILTIN, LOW); // LED ON if(state == ST_SEARCH) { if(cpt_led > 5) { cpt_led=0; digitalWrite(LED_BUILTIN, LOW); } else digitalWrite(LED_BUILTIN, HIGH); cpt_led++; } if(state == ST_ON) { if(rx_au == 0) { if(cpt_led == 5) digitalWrite(LED_BUILTIN, HIGH); if(cpt_led > 10) { cpt_led=0; digitalWrite(LED_BUILTIN, LOW); } cpt_led++; } else { if(cpt_led == 1) digitalWrite(LED_BUILTIN, HIGH); if(cpt_led >= 2) { cpt_led=0; digitalWrite(LED_BUILTIN, LOW); } cpt_led++; } } } #define BP_RIEN 0 #define BP_COURT 1 #define BP_MOY 2 #define BP_LONG 3 #define BP_PAUSE 4 byte bp_action = BP_RIEN; byte bp_old = HIGH; byte bp_cpt = 0; byte bp_sel = 0; byte bp_long = 0; // Cette fonction est a appeller toutes les 100ms // Elle detecte si on effectue un appuie court/moyen/long sur le bp void bp_100ms(void) { byte bp_new = digitalRead(D3); if(bp_old == LOW && bp_cpt > 30) bp_long = 1; if(bp_new == HIGH) bp_long = 0; if(bp_new == HIGH && bp_old == LOW) //relachement du bouton { if(bp_cpt > 30) { bp_action = BP_LONG; } else if (bp_cpt < 3) { bp_action = BP_COURT; bp_sel++; } else { bp_action = BP_MOY; bp_sel+=10; } } else bp_action = BP_RIEN; if(bp_new == HIGH && bp_cpt == 10) bp_action = BP_PAUSE; if(bp_new != bp_old) bp_cpt = 0; bp_cpt++; if(bp_cpt>100) bp_cpt=100; bp_old = bp_new; } // NON UTLISEE: Fonction qui peut être utilisee pour verifier le fonctionnement de bp_100ms void loop_bp() { bp_100ms(); delay(100); Serial.print("bp="); Serial.print(bp_action); Serial.print(" sel="); Serial.println(bp_sel); } int cpt_search = 0; byte cpt_on = 0; void loop() { delay(100); bp_100ms(); led_100ms(); /*--------------------------------------*/ if(state == ST_ATT) { if(first) { first = 0; Serial.println(">ATT"); bp_sel = 0; } if(bp_old) { state = ST_SEL_LOCO; first = 1; } } /*--------------------------------------*/ if(state == ST_SEL_LOCO) { if(first) { first = 0; Serial.println(">SEL_LOCO"); } if(bp_action == BP_MOY || bp_action == BP_COURT ) { adr = bp_sel; Serial.print("adr>"); Serial.println(adr); } if(bp_action == BP_PAUSE) { adr = bp_sel; bp_sel = 0; Serial.print("adr="); Serial.println(adr); if(adr == 1 && allow_analog == 0) state = ST_ATT; else if(adr >= 1 && adr <= 99) state = ST_SEARCH; else state = ST_ATT; first = 1; } } /*--------------------------------------*/ if(state == ST_SEARCH) { if(first) { first = 0; Serial.println(">SEARCH"); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); cpt_search = 0; } if (WiFi.status() != WL_CONNECTED) { cpt_search++; if((cpt_search % 5) == 0) Serial.print("."); if(cpt_search == 300) // 30s { state = ST_ATT; first = 1; } return; } Serial.println(""); Serial.println("WiFi connected"); Serial.print("Local IP address: "); Serial.println(WiFi.localIP()); Serial.print("D17 IP address: "); Serial.println(WiFi.gatewayIP()); if (!client.connect(WiFi.gatewayIP(), port)) { Serial.println("tcp connection failed"); state = ST_ATT; first = 1; return; } Serial.println("tcp connection ok"); while(!client.available()) delay(50); String line = client.readStringUntil('\n'); line.toCharArray(rx, 119); Serial.print("D17 id: "); Serial.println(rx); client.print("souris_d17_esp8266 v1\n"); state = ST_ON; first = 1; } /*--------------------------------------*/ if(state == ST_ON) { if(first) { first = 0; Serial.println(">ON"); sel_fct = 0; strcpy(sz_old_msg, ""); bp_long = 0; } //if(bp_action == BP_LONG) //1 (use 1 or 2) 2 is better for user experience if(bp_long) //2 { state = ST_ATT; first = 1; while(bp_long) { delay(100); bp_100ms(); led_100ms(); } //2 client.stop(); Serial.println("close tcp connection ok"); delay(1000); // laisse le temps a la deconnection tcp WiFi.disconnect(); Serial.println("Wifi OFF"); return; } if(bp_action == BP_COURT && sel_fct == 0) { char sz_au[10]; sprintf(sz_au,"au%d\n", rx_au ^ 1); client.print(sz_au); if(read_au == 0) rx_au ^= 1; } if(bp_action == BP_MOY && sel_fct == 0) { sel_fct = 1; bp_sel = 0; } if(bp_action == BP_PAUSE && sel_fct) { sel_fct = 0; Serial.println("change F"); Serial.println(bp_sel); if(bp_sel <= 28) { f_0_28[bp_sel] ^= 1; if(bp_sel == 0) { f0 ^= 1; f_num = -1; } else { f_num = bp_sel; f_val = f_0_28[bp_sel]; } } } if(read_au == 1) if(client.available()) { String line = client.readStringUntil('\n'); line.toCharArray(rx, 119); rx[119] = '\0'; if(rx[0] == 'a' && rx[1] == 'u') { if(rx[2]=='0') rx_au = 0; else rx_au = 1; } } cpt_on++; if(cpt_on & 1) return; // ne transmettre qu'une fois sur 2, toute les 200ms if(read_au) if(cpt_on & 4) client.print("?\n"); // demande l'etat de la centrale de temps en temps (toutes les 800ms) pour avoir l'au int an, an_old, diff; an_old = analogRead(A0); for(int i = 0; i < 30; i++) // filtre pour les potentiometres de mauvaise qualité ou sans condo { delay(2); an = analogRead(A0); diff = an - an_old; an_old = an; if(diff < 0) diff = - diff; if(diff <= 4) break; } int v = 500 - an; if(inv_pot) v = -v; char s = '+'; if(v < 0) { v = -v; s = '-'; } v = v - 50; if(v<0) v = 0; // zone du potentiometre pour la vitesse nulle v = map(v, 0, 450, 0, 99); char cf0; if(f0) cf0 = '+'; else cf0 = '-'; char cfn; if(f_val) cfn = '+'; else cfn = '-'; if(f_num == -1) sprintf(sz_msg,"a%d s%c v%d f0%c\n", adr, s, v, cf0); else sprintf(sz_msg,"a%d s%c v%d f0%cf%d%c\n", adr, s, v, cf0, f_num, cfn); if(strcmp(sz_msg, sz_old_msg)) // ne transmet le message que s'il change { Serial.print("an="); Serial.print(an); Serial.print(" "); strcpy(sz_old_msg, sz_msg); // sz_old_msg = sz_msg Serial.println(sz_msg); client.print(sz_msg); } } } void setup() { pinMode(LED_BUILTIN, OUTPUT); //D4 digitalWrite(LED_BUILTIN, LOW); //ON à LOW //pinMode(D3, INPUT_PULLUP); // pas la peine car D3 a une pull-up de 10K sur le Wemos D1 mini Serial.begin(115200); delay(10); Serial.println(); Serial.println(); Serial.println(TITLE); Serial.println(); }