domenica 8 dicembre 2013

Rilevatore presenze - parte 3 - il codice (client)

Il codice di controllo è molto banale, astraendoci dal funzionamento delle singole librerie.
Vediamo brevemente il controllo delle periferiche:

Buzzer

è basato su un'unico pin. Il buzzer si attiva inviando lo stato HIGH all'uscita. Per ottenerere un beep udibile è sufficiente un impulso di qualche decina di millisecondi. 

const int chipBuzzerPin =2; 

void beep()
{
   digitalWrite(2, HIGH);
   delay(120);   
   digitalWrite(2, LOW);
   delay(120);   
}

void beepLong()
{
   digitalWrite(2, HIGH);
   delay(2000);   
   digitalWrite(2, LOW);
   delay(120);   
}

void setupBuzzer()
{
  pinMode(chipBuzzerPin,OUTPUT);    
  digitalWrite(chipBuzzerPin, LOW);    
}

Display lcd

Il display è controllato con la libreria PCD8544.h . Per le esigenze speficiche di questo progetto, è stato scelto di adottare una semplice disposizione del testo su 3 righe. La funzione showMessage semplifica l'output su display, accettando le 3 stringhe da mostrare.

#include 

void showMessage(String s1,String s2, String s3){
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(s1);
  lcd.setCursor(0, 2);
  lcd.print(s2);
  lcd.setCursor(0, 4);
  lcd.print(s3);
}

void setupLcd()
{
  lcd.begin(84, 48);
}

RTC clock

Per il controllo dell'orologio viene usata la RTClib.h.
L'inizializzazione può settare anche l'ora corrente. Questo avviene con l'istruzione in grassetto, che va usata con molta cautela. Infatti il compilatore sostituire in fase di compilazione le due costanti  __DATE__, __TIME__ con la data e ora corrente. Apparentemente sembra la soluzione per avere sempre l'ora aggiornata. Bisogna però tener conto che essendo costanti cablate dal compilatore nel deploy sul dispositivo, al successivo riavvio Arduino leggerà questo valore e quindo ogni volta riporterà indietro la data e l'ora al momento della compilazione.
Va quindi fatto un caricamento usando tale istruzione, in modo da aggiornare l'ora, e poi commentarla ed effettuare un secondo caricamento, per evitare che al riavvio l'ora venga riportata ai valori iniziali.
Nel mio progetto ho implementato un'opzione, per far sì che tramite una tag rfid ad uso admin, venga chiamato un web service per farsi restituire l'ora corretta. In questo modo l'aggiornamento dell'ora viene fatto durante l'esecuzione dello sketch e non dipende dal valori cablati nel codice.

void setupRtc()
{
  Wire.begin();
  RTC.begin();
  //If we remove the comment from the following line, we will set up the module time and date with the computer one
  RTC.adjust(DateTime(__DATE__, __TIME__));
}

 DateTime now = RTC.now();

Loop 

Vediamo il ciclo principale dello sketch .
Il codice è un po' dispersivo perchè il dato della data/ora deve essere convertito in stringhe per essere stampate sul display.
La parte meno documentat, e che chiarirò in un successivo post, è la chiamata al webservice per la scrittura sul database server della registrazione di entrata/uscita.
Le chiamate vengono effettuate in logica REST, con la seguente sintassi

http://SERVER/presenze.aspx?code=CODICE_RFID&dataora=DATA_ORA

dove CODICE_RFID è il codice della tag rfid, e DATA_ORA è la data/ora di registrazione nel formato YYYYMMDDhhmmss
Di fatto il lettore tratta allo stesso modo ogni registrazione, effettuando la stessa chiamata al server sia per le entrate che per le uscite.
E' il server ad applicare le logiche necessarie a capire se si tratta di un'entrata o di un'uscita (di fatto basandosi sulla sequenza stessa delle registrazioni), restituendo nella pagina in valore "entrata" o "uscita" che viene interpretato da arduino per mostrare il relativo messaggio sul display ed emettendo 1 beep per l'entrata e 2 beep per l'uscita.
In caso di errore viene emesso un beep prolungato della durata di 2 sec.
La lettura dell'rfid è fatta in polling, su un ciclo di 700ms, quando viene rilevata una lettura si passa alla chiamata http per poi ritornare dopo aver ricevuto risposta al polling sul lettore rfid.
void loop()
{
  String mynum = "";
  String mydate ="";
//  Serial.println("loop1");
  // if there are incoming bytes available 
  // from the server, read them and print them:
  showMessage("Wait","","");


  boolean  aspetta=true;
  int oldSecond=-1;
  while (aspetta==true)
  {
    Serial.println("while");
    DateTime now = RTC.now();
    if (oldSecond != now.second())
    {
     String data="  " +String(now.day())+'/'+String(now.month())+'/'+String(now.year());
     String _sec;
     String _minu;
     String _hour;
     String _year;
     String _month;
     String _day;
      _year= String(now.year());

     if (now.month() < 10 )
     {
       _month='0'+ String(now.month());
     }
     else
      {
       _month= String(now.month());
     }
     if (now.day() < 10 )
     {
       _day='0'+ String(now.day());
     }
     else
      {
       _day= String(now.day());
     }
     if (now.hour() < 10 )
     {
       _hour='0'+ String(now.hour());
     }
     else
      {
       _hour= String(now.hour());
     }
     if (now.second() < 10 )
     {
       _sec='0'+ String(now.second());
     }
     else
      {
       _sec= String(now.second());
     }
     if (now.minute() < 10 )
     {
       _minu='0'+ String(now.minute());
     }
     else
      {
       _minu= String(now.minute());
     }
     String ora="  " +_hour+':'+_minu+':'+_sec;
     mydate= _year+_month+_day+_hour+_minu+_sec;
     showMessage("DIMENSION Srl",data,ora);  
     oldSecond=now.second();
    }
    delay(500);

    //verifica se è stato letto l'rfid
    uchar i,tmp;
    uchar status;
    uchar str[MAX_LEN];
    uchar RC_size;
    uchar blockAddr; 

    status = MFRC522_Request(PICC_REQIDL, str); 
    if (status == MI_OK)
    {
                        Serial.println("Card detected");
   Serial.print(str[0],BIN);
                        Serial.print(" , ");
   Serial.print(str[1],BIN);
                        Serial.println(" ");
    }
    status = MFRC522_Anticoll(str);
    memcpy(serNum, str, 5);
    if (status == MI_OK)
    {
      /*
      Serial.println("The card's number is  : ");
      Serial.print(serNum[0],HEX);
      Serial.print(" , ");
      Serial.print(serNum[1],HEX);
      Serial.print(" , ");
      Serial.print(serNum[2],HEX);
      Serial.print(" , ");
      Serial.print(serNum[3],HEX);
      Serial.print(" , ");
      Serial.print(serNum[4],HEX);
      Serial.println(" ");
      // Should really check all pairs, but for now we'll just use the first
      */
      beep();
      mynum +=String(serNum[0],HEX) + String(serNum[1],HEX) + String(serNum[2],HEX) + String(serNum[3],HEX) + String(serNum[4],HEX) ; 
      showMessage("Card #",mynum,"");
      delay(500);
      aspetta=false;
     }
     Serial.println(".");
     MFRC522_Halt();       
     delay(200);
     
  }
  
  connectOnHttp( mydate, mynum);

}

Si noti che utilizzando il protocollo SPI sia per l'Rfid che per la Ethernet Shield, viene fatta l'abilitazione di una o l'altra scheda in modo alternato, per evitare conflitti:
  //disabilita la scheda di rete
  digitalWrite(chipSelectPinETH, HIGH);     
  //abilita la scheda RFID
  digitalWrite(chipSelectPinRFID, LOW);   
Dato che lascheda RFID viene abilitata e disabilitata ad ogni ciclo di polling, ho utilizzato lo stesso pin di abilitazione per pilotare un led, e ottenere quindi un led lampeggiante a indicazione dello stato di funzionamento dell'apparecchiatura.
void connectOnHttp(String mydate,String mynum)
{
  String returnWebStr="";

  //disabilita la scheda rfid
  digitalWrite(chipSelectPinRFID, HIGH);     
  //abilita la scheda di rete
  digitalWrite(chipSelectPinETH, LOW);     
  
  showMessage("connecting","","");
  // if you get a connection, report back via serial:
  if (client.connect(server, 8999))    //PORTA 8999
  {
    showMessage("connected","","");
    
    /*Serial.println("*************");
    Serial.println(mydate);
*/
    // Make a HTTP request:
    client.println("GET /presenze.aspx?code=" + mynum + "&dataora=" + mydate);
    client.println("Connection: close");
    client.println();
  } 
  else {
    // kf you didn't get a connection to the server:
    showMessage("connection failed","","");
  }
  while (client.connected())
  {
   if (client.available()) 
   {
    char c = client.read();
    //Serial.print(c);
    returnWebStr+=c;
   }
  }
  // if the server's disconnected, stop the client:
  if (!client.connected()) {
    client.stop();
    
    if (returnWebStr.substring(0,1) == "E" )
    { 
       showMessage("Registrazione","ENTRATA","");
       beep();
    }
    else if (returnWebStr.substring(0,1) == "U" )
    {
          showMessage("Registrazione","USCITA","");
          beep();
          beep();
          //sistema l'ora solo con il badge di test
          if (mynum=="2c4174d4cd")
          {
           String dataOra;
           dataOra=returnWebStr.substring(1,15);
           int clock_anno=(dataOra.substring(0,4)).toInt();
           int clock_mese=(dataOra.substring(4,6)).toInt();
           int clock_giorno=(dataOra.substring(6,8)).toInt();
           int clock_ora=(dataOra.substring(8,10)).toInt();
           int clock_minuti=(dataOra.substring(10,12)).toInt();
           int clock_secondi=(dataOra.substring(12,14)).toInt();
      
           showMessage("ORA AGGIORNATA","CON IL SERVER",mynum);
           delay(3000);
           RTC.adjust(DateTime(clock_anno, clock_mese, clock_giorno, clock_ora, clock_minuti, clock_secondi));
          }
    }
    else 
    {
      beepLong();
    }
    
  }
  delay(1500);
  returnWebStr="";

  //disabilita la scheda di rete
  digitalWrite(chipSelectPinETH, HIGH);     
  //abilita la scheda RFID
  digitalWrite(chipSelectPinRFID, LOW);     

}

Setup

vediamo infine la sezione di setup. Oltre ad effettuare le chiamate per l'inizializzazione delle periferiche sopra citate, viene assegnato un ip statico alla scheda di rete.

IPAddress server(10,10,1,221);  

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  //il pin 10 serve a controllare la ethernet HIGH=disabilita , LOW=abilita
  pinMode(chipSelectPinETH,OUTPUT);    
  //il pin 8 serve a controllare la scheda Rfid, HIGH=disabilita, LOW=abilita
  pinMode(chipSelectPinRFID,OUTPUT);    
  
  setupBuzzer();
  //disabilita la scheda di rete (viene abilitata quando si inizializza)
  digitalWrite(chipSelectPinETH, HIGH);  
  //disabilita l'rfid
  digitalWrite(chipSelectPinRFID, HIGH);  

  showMessage("init.","","");
  setupLcd();
  delay(200);
  showMessage("init..","","");
  setupRtc();
  delay(200);
  showMessage("init...","","");
  delay(200);
  // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) {
    showMessage("Failed to configure","ethernet using DHCP","");
    // no point in carrying on, so do nothing forevermore:
    // try to congifure using IP address instead of DHCP:
    //Ethernet.begin(mac, ip);
    return;
  }
  char ip[4];
  snprintf(ip, 20, "%d.%d.%d.%d", Ethernet.localIP()[0], Ethernet.localIP()[1], Ethernet.localIP()[2], Ethernet.localIP()[3]);
  showMessage("init...v0.19",ip,"");
  // give the Ethernet shield a second to initialize:
  delay(2000);
  //disabilita la scheda di rete
  digitalWrite(chipSelectPinETH, HIGH);      //il valore è fisso per la Ethernet Shield 
   //inizializza la scheda rfid (abilitandola)
  setupRFID();
  
  beep();
  //Serial.println("end setup");
  
    
}

Nessun commento:

Posta un commento