martedì 16 agosto 2016

NodeMCU: controllo di 4 relè tramite APP e WEB


Con questo post spero di dare delle indicazioni di base su come utilizzare questo piccolo dispositivo nato per "l'Internet delle cose". Ho avuto le mie belle difficoltà a capire come riuscire ad utilizzarlo in modalità bidirezionale: in Internet trovate l'esempio per il controllo di un singolo relè senza la possibilità di capire se effettivamente il segnale è stato ricevuto dal dispositivo da remoto o meglio tramite un APP.
Partendo da questo esempio base sono riuscito ad utilizzare più uscite ma in particolar modo ho trovato un sistema che mi permette di ricevere un messaggio dal NodMCU che mi indichi che "ha capito" cosa deve fare.
Per programmare il NodeMCU basato sul chip WiFi ESP8266 è sufficiente l'IDE per Arduino, in molti casi però va sostituito il firmware e l'IDE va integrato con il plugin per l'ESP8266.

In questo post non entrerò nel dettaglio di come farlo, in rete trovate di tutto di più come ad esempio qui o qui.
Per la programmazione dell'APP per Android ho usato la programmazione a blocchi messo a disposizione dal MIT a questo indirizzo http://ai2.appinventor.mit.edu/.

Passiamo ora al listato per "Arduino".


#include <ESP8266WiFi.h>

const char* ssid = "SSH NETWORK";   // Nome della rete alla quale collegarsi
const char* password = "PASSWORD";   // Password della rete


// Mappatura dei PIN digitali e analogico del NodeMCU

#ifndef Pins_Arduino_h
#define Pins_Arduino_h

#include "../generic/common.h"

static const uint8_t SDA = 4;
static const uint8_t SCL = 5;

static const uint8_t LED_BUILTIN = 16;
static const uint8_t BUILTIN_LED = 16;

static const uint8_t D0   = 16;
static const uint8_t D1   = 5;
static const uint8_t D2   = 4;
static const uint8_t D3   = 0;
static const uint8_t D4   = 2;
static const uint8_t D5   = 14;
static const uint8_t D6   = 12;
static const uint8_t D7   = 13;
static const uint8_t D8   = 15;
static const uint8_t D9   = 3;
static const uint8_t D10  = 1;

#endif /* Pins_Arduino_h */

  // Inizializzazione delle variabili riguardanti i PIN di controllo della scheda relè

int ledPin_A = D1;  // A = PIN D1
int ledPin_B = D2;  // B = PIN D2
int ledPin_C = D3;  // C = PIN D3
int ledPin_D = D5;  // D = PIN D5

int value_A = LOW;
int value_B = LOW;
int value_C = LOW;
int value_D = LOW;

String stato_led1;
String stato_led2;
String stato_led3;
String stato_led4;

WiFiServer server(8888);  // Attivazione del Server sulla porta 8888

  // Setup 
void setup() {
  Serial.begin(115200);  // Apertura della comunicazione seriale
  delay(10);

  // Configurazione dei PIN come uscita digitale

  pinMode(ledPin_A, OUTPUT);
  digitalWrite(ledPin_A, LOW);
  
  pinMode(ledPin_B, OUTPUT);
  digitalWrite(ledPin_B, LOW);
  
  pinMode(ledPin_C, OUTPUT);
  digitalWrite(ledPin_C, LOW);
  
  pinMode(ledPin_D, OUTPUT);
  digitalWrite(ledPin_D, LOW);

  // Connessione alla rete WiFi
  Serial.println();
  Serial.println();
  Serial.print("Connesso a ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connesso");

  // Avvio server
  server.begin();
  Serial.println("Server avviato");

  // Stampa IP address
  Serial.print("Usa questo URL per connetterti: ");
  Serial.print("http://192.168.1.104");  // indirizzo IP assegnato dal router
  Serial.print(WiFi.localIP());
  Serial.println("/");

}

void loop() {
  // Controllo se il server è connesso
  WiFiClient client = server.available();
  if (!client) {
    return;
  }

  // Attendo l'invio di dati dal server
  Serial.println("nuovo client");
  while(!client.available()){
    delay(1);
  }

  // Leggo la prima linea di richiesta
  String request = client.readStringUntil('\r');
  Serial.println(request);

    
  client.flush();

  // Verifico la richiesta di attivazione dell'uscita "A"

    if (request.indexOf("/LED=ON_A") != -1)  {
    digitalWrite(ledPin_A, HIGH);
    value_A = HIGH;
  }
  if (request.indexOf("/LED=OFF_A") != -1)  {
    digitalWrite(ledPin_A, LOW);
    value_A = LOW;
  }

  // Verifico la richiesta di attivazione dell'uscita "B"

  if (request.indexOf("/LED=ON_B") != -1)  {
    digitalWrite(ledPin_B, HIGH);
    value_B = HIGH;
  }
  if (request.indexOf("/LED=OFF_B") != -1)  {
    digitalWrite(ledPin_B, LOW);
    value_B = LOW;
  }

  // Verifico la richiesta di attivazione dell'uscita "C"
    
  if (request.indexOf("/LED=ON_C") != -1)  {
    digitalWrite(ledPin_C, HIGH);
    value_C = HIGH;
  }
  if (request.indexOf("/LED=OFF_C") != -1)  {
    digitalWrite(ledPin_C, LOW);
    value_C = LOW;
  }
  
  // Verifico la richiesta di attivazione dell'uscita "D"
  
  if (request.indexOf("/LED=ON_D") != -1)  {
    digitalWrite(ledPin_D, HIGH);
    value_D = HIGH;
  }
  if (request.indexOf("/LED=OFF_D") != -1)  {
    digitalWrite(ledPin_D, LOW);
    value_D = LOW;
  }

  // Assegno i valori LOW o HIGH ai 4 PIN
  // digitalWrite(ledPin, value);

  // Return the response
  // Pagina WEB di risposta
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println(""); //  do not forget this one
  client.println("<!DOCTYPE HTML>");
  client.println("<html>");

  client.print("Led A pin is now: ");

  if(value_A == HIGH) {
    client.print("On");
    stato_led1="A1";
  } else {
    client.print("Off");
    stato_led1="A0";
  }
  client.println("<br>");
  client.println("<a href=\"/LED=ON_A\"\"><button>Turn On </button></a>");
  client.println("<a href=\"/LED=OFF_A\"\"><button>Turn Off </button></a><br />");  
  client.println("<br>");
  client.println("</html>");

  delay(10);

  client.print("Led B pin is now: ");

  if(value_B == HIGH) {
    client.print("On");
    stato_led2="B1";
  } else {
    client.print("Off");
    stato_led2="B0";
  }
  client.println("<br>");
  client.println("<a href=\"/LED=ON_B\"\"><button>Turn On </button></a>");
  client.println("<a href=\"/LED=OFF_B\"\"><button>Turn Off </button></a><br />");  
  client.println("<br>");
  client.println("</html>");

  delay(10);
   
  client.print("Led C pin is now: ");

  if(value_C == HIGH) {
    client.print("On");
    stato_led3="C1";
  } else {
    client.print("Off");
    stato_led3="C0";
  }
  client.println("<br>");
  client.println("<a href=\"/LED=ON_C\"\"><button>Turn On </button></a>");
  client.println("<a href=\"/LED=OFF_C\"\"><button>Turn Off </button></a><br />");  
  client.println("<br>");
  client.println("</html>");

  delay(10);

  client.print("Led D pin is now: ");

  if(value_D == HIGH) {
    client.print("On");
    stato_led4="D1";
  } else {
    client.print("Off");
    stato_led4="D0";
  }
  client.println("<br>");
  client.println("<a href=\"/LED=ON_D\"\"><button>Turn On </button></a>");
  client.println("<a href=\"/LED=OFF_D\"\"><button>Turn Off </button></a><br />");  
  client.println("<br>");

  //cambia url in funzione dello stato degli output
  client.println("<meta http-equiv=\"refresh\" content=\"1;URL="+stato_led1+stato_led2+stato_led3+stato_led4+"\">");
  
  client.println("</html>");

  delay(10);

  
  Serial.println("Client disconnected");
  Serial.println("");

}

Per quanto riguarda invece la parte riguardante l'APP per Android potete collegarvi a questo link oppure scaricarlo per poi importarlo da qui.

25 commenti:

  1. Hello, sorry for my ignorance, but i be a new with arduino, how you connect the wires between nomedecu and rele shield?, how pins you use? Thanks!

    RispondiElimina
  2. Hola, disculpa mi ignorancia, pero como es el cableado entre los pines del nomedecu y la placa de reles?, yo tengo un nodemecu v3 y estoy experimentado, me gustaría armar tu proyecto. Saludos y gracias!

    RispondiElimina
  3. Hi, I just connected the pins D1, D2, D3, D5 and GND to the relay board

    RispondiElimina
    Risposte
    1. Thanks!!, can i use a nodemecu v3 for this proyect?

      Greetings!!

      Elimina
  4. Risposte
    1. Hello, i try to make the project, but i have this:

      Error abriendo puerto "COM13" (Port busy)
      warning: espcomm_sync failed
      error: espcomm_open failed
      error: espcomm_upload_mem failed
      error: espcomm_upload_mem failed

      Do you have some idea?

      Elimina
    2. Hi, have you change firmware? https://hackaday.io/project/3465-playing-with-esp8266/log/12683-downloading-and-installing-nodemcu-firmware

      Elimina
    3. I try with NodemCu Firmware Programmer, i put flash and i can see in the program "Require(GPIO)", "Require(Wifi)" and the boxes of MAC only show "Waiting Mac", i try 4 times and the same, in 10 minutes the program doesnt nothing.

      Elimina
    4. Well, i try with esptool, and i have this:

      "A fatal error ocurred: Failed to connect to ESP8266: Timed out waiting for packet header".

      I reinstall the CH341SER Drivers, and nothing happen.

      Elimina
    5. I'm sorry, I don't know... Ask to Google.

      Elimina
    6. I ask to google and nothing, i return to my arduino and esp, thanks!!!

      Elimina
  5. Grazie Marco,ottima guida ,avrei bisogno di un aiutino su come modificare il tuo listato.
    come modificare un relè interruttore e farlo diventare un pulsante per aprire una serratura di un cancello pedonale ?
    attivandolo per un paio di secondi e poi tornare il off automaticamente?

    grazie

    RispondiElimina
  6. Ciao Lucio, in questo momento ho pochissimo tempo vediamo se riesco a darti l'input... Crei un button ed un clock, premuto il tasto esegue l'azione sul relè e dopo il tempo da te impostato un'altra azione che riporta lo stato del relè nella posizione iniziale.

    RispondiElimina
    Risposte
    1. :-) è facile per te ,ma io non sono molto pratico.

      Elimina
    2. Lucio sono preso male in questo periodo, vedo nei prossimi giorni se riesco a darti un ulteriore aiutino. Buona serata.

      Elimina
  7. Grazie Marco,ho fatto il progetto e funziona alla grande.
    se si riuscisse a mettere on pulsante sarebbe grandioso così ognuno poi lo modifica come serve.

    RispondiElimina
  8. Ciao Marco,ti sei ancora impegnato? un aiutino per quel pulsante?

    grazie

    RispondiElimina
    Risposte
    1. Ciao Lucio, sinceramente no... Ho cambiato da poco lavoro e sono impegnato ad imparare nuovi software. Non posso garantirti nulla, sorry.

      Elimina
  9. Ciao Marco, complimenti per la guida, ma io ho avuto dei problemi con AppInventor,immagino che devo configurare l'ip su AppInventor, dove devo mettere Ip e Port?

    RispondiElimina
  10. Ciao Giorgio. Ho omesso la descrizione dell'App perché pensavo fosse chiara ripensandoci però forse dovevo dare qualche suggerimento...
    Nel tuo caso seleziona il componente in basso a dx denominato "txt_IP" e al posto di "PUBLIC IP" inserisci l'IP assegnato dal tuo router.

    RispondiElimina
    Risposte
    1. Ciao Marco, volevo confermarti che funziona, dopo aver messo Ip su AppInventor,vedo che funzione bene ma avolte va in crasc NodeMCU, volevo spare se normale.

      si puo migliorare la parte client-server in modo che arrivano le stringhe piu velocemente?

      Elimina
    2. Ciao Giorgio, si purtroppo capitava anche a me...
      In un altro progetto, non pubblicato, ho ottenuto qualche miglioramento rimuovendo il seguente controllo:

      // Controllo se il server è connesso
      WiFiClient client = server.available();
      if (!client) {
      return;
      }

      Elimina
  11. Questo commento è stato eliminato dall'autore.

    RispondiElimina
  12. Muchas gracias por compartir este proyecto. Funciona muy bien!

    RispondiElimina