Un semplice antifurto: gestiamo al meglio la memoria

Si è da tanto che non mi collego…ma ve lo giuro,  mettere insieme il codice e soprattutto studiarmi quel minimo Eagle per scrivere la libreria finale del progetto non è stato semplice.

Devo ringraziare il forum di Arduino per tutti i preziosi aiuti!

La parte più difficile è stata la gestione della memoria: sono abituato a programmare in Net ed avere a disposizione pochi Kb mi ha fatto sudare.
Ho deciso di leggere la documentazione per capire come sfruttarla un pochino meglio.

LA MEMORIA IN ARDUINO

Il micro controllore presente sulla scheda Arduino UNO, da me utilizzato, è  ATmega328P con le seguenti tipologie di memoria:

  • Flash (program space), è dove lo sketch di arduino  (programma)  viene memorizzato.
  • SRAM (static random access memory) è dove lo sketch  crea e gestisce le variabili quando è in esecuzione.
  • EEPROM (già la conosciamo) è un tipo di memoria non volatile, una memoria in grado di mantenere le informazioni anche quando non viene alimentata.

Per ogni micro controllore si ha a disposizione una diversa quantità di memoria:

ATMega168 ATMega328P ATmega1280 ATmega2560
Flash
(1 Kbyte used
for bootloader)
16 KBytes 32 KBytes 128 KBytes 256 KBytes
SRAM 1024 bytes 2048 bytes 8 KBytes 8 KBytes
EEPROM 512 bytes 1024 bytes 4 KBytes 4 KBytes

Per Arduino UNO quindi:

Flash 32k bytes
SRAM 2k bytes
EEPROM 1k byte

Le stringhe in particolare utilizzano in grosso quantitativo di memoria e nel mio sketch ne faccio grande uso per scrivere sul client web (client.println) e sulla console (Serial.print).

Ad esempio il comando : Serial.println(“Trying to connect”); occupa 18 bytes (1 char = 1 byte più il carattere di fine riga) della SRAM che ne ha a disposizione 2048. Sembra poco….ma ho utilizzato tantissime volte le stringhe nel mio sketch!
Dalla versione 1.0 l’IDE di Arduino permette l’utilizzo della funzione F() che permette di storare le stringhe nella memoria FLASH e non nella SRAM.
Ho quindi deciso di storare proprio nella memoria FLASH tutte le stringhe utilizzate nel costrutto client.println, risolvendo i miei problemi di memoria SRAM (gli int, float e le stringhe non raggruppate con F() naturalmente saranno inizializzare e gestite nella SRAM).

Consiglio comunque una bella lettura di questo  articolo .

Dai che arriva il weekend e finalmente (spero) avrò tempo di pubblicare le modifiche al codice e il file di progetto di Eagle.

Quindi BUON WEEKEND!

Un semplice antifurto: salvare i codici sensori tramite interfaccia web

Non ci posso credere! E’ quasi Natale!! Sono troppo contento…finalmente un po di relax!

Torniamo al nostro arduino…come detto la volta scorsa voglio fare in modo che i codici sensori possano essere memorizzati nelle EEPROM, il tutto da interfaccia web!
Ecco il codice che esegue questa cosa:

#include <String.h>
#include <Time.h>
#include <SPI.h>
#include <Ethernet.h>
#include <EEPROM.h>
#include <RCSwitch.h>

byte mac[] = {
  0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 };
int SERIAL_BAUD        = 9600;
EthernetServer server(8081);
RCSwitch mySwitch = RCSwitch();
int RECEIVE_PIN       = 0;
int nSensori = 5;

void setup() {
  Serial.begin(SERIAL_BAUD);
  setupComm();
  mySwitch.enableReceive(RECEIVE_PIN);
}

void loop() {
  getClientConnection();
}

void getClientConnection(){

  EthernetClient client = server.available();
  if (client) {
    String postString ="";
    Serial.println("nuova richiesta");
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if(postString.length()<15){
          postString +=c;
        }

        if (c == 'n' && currentLineIsBlank) {
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");
          client.println("<h1>Configurazione</h1>");
          client.print("<br>");

          for (int i=0; i<nSensori; i++)
          {
            String linkCompleto = "";
            linkCompleto = "<a href="./?save"+ String(i);
            linkCompleto +="">Salva Sensore " + String(i);
            linkCompleto += "</a><br/>";
            client.println(linkCompleto);
            Serial.println(linkCompleto);
            //client.println("<a href="./?save1">Salva Sensore 1</a>");
          }

          client.println("<br/>");
          client.println("<a href="./?elenco">Visualizza dati sensori</a>");
          client.println("</html>");
          break;
          //}
        }

        if (c == 'n') {
          // you're starting a new line
          currentLineIsBlank = true;
        }
        else if (c != 'r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }

      }
    }  //fine client.connected 

    Serial.println("-------------");
    Serial.println(postString);
    Serial.println("-------------");

    if(postString.indexOf("?save") > 0){ 

      int indexSave = postString.indexOf("?save");
      Serial.println("indexOf");
      Serial.println(indexSave);
      //cerco valore del sensore
      String sSensore = postString.substring(indexSave+5 ,indexSave+6);
      Serial.println("Sensore");
      Serial.println(sSensore);
      int iSensore = sSensore.toInt();
      long valore = salvaSensore(iSensore);
      client.println("<br/>");
      client.println("<p>Salvataggio sensore effettuato</p>");
      client.println("<br/>");
      client.println("Codice sensore " + sSensore);
      client.print(valore);
    }   

    if(postString.indexOf("?elenco") > 0){
      for (int i=0; i<nSensori; i++)
      {
        client.println("<p>Sensore " + String(i)+" : </p>");
        client.print(String(EEPROMReadlong(i*4)));
        Serial.println(EEPROMReadlong(i*4));
      }
    }

    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disonnected");
  }
}

long salvaSensore(int iSensore)
{
  int addressTosSave = iSensore*4;
  long valore = 0;
  Serial.println("salvaSensore");
  if (mySwitch.available()) {
    Serial.println("mySwitch.available");
    valore = mySwitch.getReceivedValue();
    Serial.println("valore ");
    Serial.print(valore);
    EEPROMWritelong(addressTosSave,valore);
  }
  delay(1000);
  return valore;
} 

void EEPROMWritelong(int address, long value)
{
  //Decomposition from a long to 4 bytes by using bitshift.
  //One = Most significant -> Four = Least significant byte
  byte four = (value & 0xFF);
  byte three = ((value >> 8) & 0xFF);
  byte two = ((value >> 16) & 0xFF);
  byte one = ((value >> 24) & 0xFF);

  //Write the 4 bytes into the eeprom memory.
  EEPROM.write(address, four);
  EEPROM.write(address + 1, three);
  EEPROM.write(address + 2, two);
  EEPROM.write(address + 3, one);
}

void setupComm()
{
  Serial.println("Trying to connect");
  Serial.print("n");
  if (!Ethernet.begin(mac)){
    Serial.println("Failed to DHCP");
    // no point in carrying on, so do nothing forevermore:
    while(true);
  }

  // print your local IP address:
  Serial.print("My IP address: ");
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    // print the value of each byte of the IP address:
    Serial.print(Ethernet.localIP()[thisByte], DEC);
    Serial.print(".");
  }
  Serial.println("fine");
}

long EEPROMReadlong(long address)
{
  //Read the 4 bytes from the eeprom memory.
  long four = EEPROM.read(address);
  long three = EEPROM.read(address + 1);
  long two = EEPROM.read(address + 2);
  long one = EEPROM.read(address + 3);

  //Return the recomposed long by using bitshift.
  return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}

L’interfaccia risultante su web è la seguente: (evitate commenti 🙂 )

Ricordo che per accedere alla pagina internet è necessario scrivere nel browser l’indirizzo ip che viene stampato nella console di arduino, specificando inoltre la porta 8081.
Nel mio caso, ad esempio: 192.168.1.136:8081

Sono presenti 5 link che permettono di salvare i dati di 5 sensori: premendo il link “Salva sensore 0” ed azionando il sensore (aprendo la porta per esempio) viene memorizzato il dato nella eeprom.

Schermata 2014-12-17 alle 22.31.29

Una volta salvati i dati è possibile visualizzare un elenco riepilogativo di quanto è presente nella EEPROM, cliccando “Visualizza dati sensori”.
Schermata 2014-12-17 alle 22.31.01

Se riesco in serata posto un video dove si vede meglio il funzionamento.
Il codice è relativamente semplice.
Tralasciando la parte di collegamento alla ethernet shield (già spiegata nei precedenti articoli) passiamo a come vengono generati i link e la loro gestione.

riga 14: viene dichiarato nSensori, cioè il numero di sensori che voglio gestire
riga 53-61: viene creato il link in modo dinamico secondo la sintassi html. Il link che viene richiamato sarà del tipo save0, save1, save2..., a seconda del link che si vuole premere.
riga 63-65: creazione del link per elenco dei sensori memorizzati
riga 81-97: gestione del salvataggio del codice sensore.
Nel dettaglio:
riga 81: nella querystring è presente “salva”, quindi è stato richiesto un salvataggio
riga 87: ricavo quale sensore deve essere salvato (sensore0, sensore1, sensore2..)
riga 91: chiamo la funzione salvaSensore: attiva la funzione mySwitch.getReceivedValue() per stabilire il codice del sensore per poi salvarlo nelle EEPROM
riga 92-96: stampa del codice sensore salvato
riga 100-107: viene visualizzalo l’elenco dei sensori salvati; è un semplice ciclo for che va a leggere con la funzione EEPROMReadlong ogni singolo sensore.
A questo punto, nel prossimo articolo, implementeremo quanto visto oggi nel nostro antifurto!

Dai divertente no???? 🙂

Parliamo di cose più serie ora…che dirvi…

BUON NATALE!!!!!!!!!

Un semplice antifurto: cerchiamo di mettere tutto insieme

Eccomi di nuovo…costretto a casa dalla piena del Seveso ne approfitto per aggiornare il blog.

Nei precedenti post abbiamo:

  • letto i dati dai sensori di movimento
  • letto i dati dal sensore di rumore
  • collegato la ethernet shield ed inviata una mail

A questo punto possiamo mettere insieme i vari pezzi.

Inoltre avendo a casa  una telecamera D-Link DCS-930L ho deciso di inserirla in questo progetto.

Per non tenere accesa la telecamera tutto il giorno vorrei fare in modo che si accenda solo nel momento in cui viene individuato un movimento.
Non solo: poterla accenderla da remoto nel caso in cui ho voglia di verificare cosa succede in casa.
Per non complicarmi la vita ho deciso di comprare una ciabatta wireless (sempre a 433 Mhz) tipo queste. Collegata la webcam alla ciabatta posso comandarla tramite arduino, basta solo scoprire il codice di trasmissione come fatto precedentemente con i sensori di movimento!
Utilizzando il telecomando in dotazione alla prese quindi ho determinato:

Codice accensione ciabatta Codice spegnimento ciabatta
1394001 1394004

Ho quindi tutti i codici sensori che necessito, posso aggiungere il modulo di trasmissione ad Arduino.

Il risultato finale è questo:

IMG_4507IMG_4506IMG_4505

I collegamenti, rispetto a quanto fatto nelle precedenti “puntate”, li ho dovuti modificare in quanto ho scoperto che i pin 10, 11, 12 e 13 vengono utilizzati in modo esclusivo dall’ethernet shield.

Quindi riepilogando i collegamenti sono:

Sensore Rumore

Vcc 5 V
GND GND
SNG Pin 5 (digitale)

Sensore ricevente 433 Mhz

Pin Ricevitore Pin Arduino
Vcc 5 V
GND GND
DATA Pin 2 (digitale)

Sensore trasmittente 433 Mhz

 

Pin Ricevitore Pin Arduino
Vcc 5 V
GND GND
DATA Pin 9 (digitale)

 

Il codice per verificare se il tutto funziona quindi :

 

/*
 Ricezione dai sensori porte
 Trasmissione verso telecamera
 rivelazione rumore

 http://code.google.com/p/rc-switch/

 Need help? http://forum.ardumote.com
 */

#include <String.h>
#include <RCSwitch.h>
#include <Time.h>
#include <SPI.h>
#include <Ethernet.h>

/* Informazioni Ethernet*/

byte mac[] = {
  0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 };
int SERIAL_BAUD        = 9600;
int TRASMIT_PIN       = 9;
int RECEIVE_PIN       = 0;
int NOISE_DIGITAL_PIN =    5;
//Sensori
long PORTA_INGRESSO_SENSORE  = 3557625;
long PORTA_CUCINA_SENSORE = 10521177;
long SEGNALE_ACCENZIONE_WEBCAM = 1394001;
long SEGNALE_SPEGNIMENTO_WEBCAM= 1394004;

EthernetClient client;
EthernetServer server(8081);
char smtpServer[] = "smtpcorp.com";
RCSwitch mySwitch = RCSwitch();

void setup() {
  Serial.begin(SERIAL_BAUD);
  pinMode(SERIAL_BAUD, INPUT);
  mySwitch.enableReceive(RECEIVE_PIN);
  mySwitch.enableTransmit(TRASMIT_PIN);
  setupComm();
}

void loop() {

  getClientConnection();
  if (detectNoise()){
    Serial.print("Rumore");
    email("Attenzione, rilevato rumore in casa!");
    accendiCam(SEGNALE_ACCENZIONE_WEBCAM) ;
  }

  if (mySwitch.available()) {

    int value = mySwitch.getReceivedValue();

    Serial.print(value);
    if (value == 0) {
      Serial.print("Unknown encoding");
      Serial.print("\n");
    }
    else {
      long receivedValue = mySwitch.getReceivedValue();
      if (receivedValue == PORTA_INGRESSO_SENSORE) {
        Serial.print("Attenzione! Porta ingresso aperta!");
        Serial.print("\n");
        email("Attenzione, porta ingresso aperta!");
        accendiCam(SEGNALE_ACCENZIONE_WEBCAM) ;
        delay(1000);
      }
      else if(receivedValue == PORTA_CUCINA_SENSORE) {
        Serial.print("Attenzione! Porta cucina aperta!");
        Serial.print("\n");
        email("Attenzione, porta cucina aperta!");
        accendiCam(SEGNALE_ACCENZIONE_WEBCAM) ;
        delay(1000);
      }  

      Serial.print("Received ");
      Serial.print( receivedValue);
      Serial.print(" / ");
      Serial.print( mySwitch.getReceivedBitlength() );
      Serial.print("bit ");
      Serial.print("Protocol: ");
      Serial.println( mySwitch.getReceivedProtocol() );
    }

    mySwitch.resetAvailable();
  }

}

bool detectNoise ()
{
  bool rit = false;
  if (digitalRead(NOISE_DIGITAL_PIN) == HIGH)
  {
    rit = true;
  }
  return rit;
} 

void accendiCam(long value)
{
  mySwitch.send(value, 24);
  mySwitch.send(value, 24);
  mySwitch.send(value, 24);
  mySwitch.send(value, 24);
  mySwitch.send(value, 24);
} 

void setupComm()
{
  Serial.println("Trying to connect");
  Serial.print("\n");
  if (!Ethernet.begin(mac)){
    Serial.println("Failed to DHCP");
    while(true);
  }

  Serial.print("My IP address: ");
  for (byte thisByte = 0; thisByte < 4; thisByte++) {
    Serial.print(Ethernet.localIP()[thisByte], DEC);
    Serial.print(".");
  }
  Serial.println("fine");
}

// Call to send an email.
bool email(char* text)
{
  bool success = false;
  Serial.println("Sending email...");
  Serial.print("\n");
  Serial.println("SMTP server...");
  Serial.print(smtpServer); 

  if (client.connect(smtpServer, 2525)){
    Serial.println("connected");
    delay(100);
    client.println("EHLO arduino");
    for(int i=0; i<999; ++i){
      if(client.read() > 0)
        break;
    }
    Serial.println("responded");
    Serial.print("\n");
    client.println("AUTH LOGIN");
    client.println("xxxxxxx");           //vedi precedente post per la spiegazione
    client.println("yyyyyy");   //vedi precedenti post per la spiegazione     

    client.println("MAIL FROM:<dumm@gmail.com>");
    for(int i=0; i<999; ++i){
      if(client.read() > 0)
        break;
    } 

    client.println("RCPT TO:<giuseppe.scola@gmail.com>"); 

    for(int i=0; i<999; ++i){
      if(client.read() > 0)
        break;
    } 

    client.println("DATA");
    for(int i=0; i<999; ++i){
      if(client.read() > 0)
        break;
    }

    client.println("from: giuseppe.scola@gmail.com");
    client.println("to: giuseppe.scola@gmail.com");
    client.println("SUBJECT: ArduAlarm");
    client.println("");
    client.println(text);
    client.println(".");
    client.println("QUIT");
    for(int i=0; i<999; ++i){
      if(i > 998){
        Serial.println("error: No response");
      }
      if(client.read() > 0)
        break;
    }
    success = true;
    client.println();
    Serial.println("end");
    Serial.print("\n");
  }
  else {
    Serial.println("Failed");
    Serial.print("\n");
    client.println("QUIT");
  }
  client.stop();
  return success;
}

void getClientConnection(){

  EthernetClient client = server.available();
  if (client) {
    String postString ="";
    Serial.println("nuova richiesta");
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        //postString.concat(c);
        if(postString.length()<10){
          postString +=c;
        }
        // Serial.write(c);
        if (c == '\n' && currentLineIsBlank) {
          //if(readString.indexOf("id=1") > 0){
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          //client.println("Refresh: 5");  // refresh the page automatically every 5 sec
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html>");
          //client.println("<h1>Settaggi</h1><br>");
          client.println("<h1>AllarDuino</h1>");
          client.print("<br>");
          client.println("<a href=\"./?on\">Accendi CAM</a>");
          client.println("<a href=\"./?off\">Spegni CAM</a>");
          client.println("</html>");
          break;
          //}
        }

        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        }
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }

      }
    }  //fine client.connected 

    Serial.println("-------------");
    Serial.println(postString);
    Serial.println("-------------");

    if(postString.indexOf("?on") > 0){
      Serial.println("accendi CAM");
      accendiCam(SEGNALE_ACCENZIONE_WEBCAM);
      client.println("<br/>");
      client.println("<p>Cam accesa</p>");

    }
    if(postString.indexOf("?off") > 0){
      accendiCam(SEGNALE_SPEGNIMENTO_WEBCAM);
      client.println("<br/>");
      client.println("<p>Cam spenta</p>");
    } 

    delay(1);
    // close the connection:
    client.stop();
    Serial.println("client disonnected");
  }
}

Domani o dopo spero di riuscire a commentare un po il codice, soprattutto la parte in cui viene generato l’html.