Problema serratura con keypad

Ho scritto il seguente codice che tramite due psw dovrebbe muovere un servo che a sua volta muove un cardine che chiude una porta. Il problema che ho riscontrato è che nonostante immetta il codice di blocco della serratura giusto mi da errore (incorrect pin) mentre per quello di sblocco non ci sono problemi:

#include <Keypad.h>
#include <ShiftLCD.h>
#include<Servo.h>

ShiftLCD lcd(2, 4, 3);

Servo myservo;

const int pinServo = 5;
const int button = 14;
int retry = 0;  //variabile sulla quale viene memorizzato il numero delle volte che si sbaglia il codice
const byte ROWS = 4; //quattro righe
const byte COLS = 4; //quattro colonne
char keys[ROWS][COLS] =
 {{'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}};
byte rowPins[ROWS] = {13,12,11,10}; //pin arduino a cui sono connessi i pin delle righe del keypad
byte colPins[COLS] = {9, 8, 7, 6}; //pin arduino a cui sono connessi i pin delle colonne del keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

char PIN_lock[6]={'1','2','A','D','5','6'}; //PIN segreto
char PIN_unlock[6]={'A','1','B','2','C','3'};
char attempt[6]={0,0,0,0,0,0}; //array usato per il confronto

int z=0;

void setup()
{
  lcd.begin(16,2);         // inizzializzazione dell LCD
  lcd.print("  Enter PIN...");
  myservo.attach( pinServo );
  myservo.write(20);
}

void correctPIN_() //se il PIN è corretto
{
  lcd.setCursor(1,0);
  lcd.print("Unlocked door");
  myservo.write( 60 );
  delay(4000);
  lcd.clear();
  lcd.print("  Enter PIN...");
}

void correctPIN() 
{
  lcd.setCursor(3,0);
  lcd.print("Lock door");
  myservo.write(20);
  delay(4000);
  lcd.clear();
  lcd.print("  Enter PIN...");
  }

void incorrectPIN() //se il PIN è sbagliato
{
  lcd.setCursor(1,0);
  lcd.print("Incorrect PIN");
  delay(2000);
  lcd.clear();
  lcd.print("  Enter PIN...");
}

void riprova() {
   lcd.clear(); 
   lcd.setCursor(5,0);  
   lcd.print("Alarm!");
   delay(500);
   }

void checkPIN()
{
  int correct  = 0;
  int correct_ = 0;
  for (int q = 0; q < 6; q++)
  {
    if (attempt[q] == PIN_lock[q]) 
    {
      correct++;
    }
  }
  if (correct == 6) 
  {
    correctPIN();
  }
  for (int q_ = 0; q_ < 6; q_++)
  {
    if (attempt[q_] == PIN_unlock[q_]) 
    {
      correct_++;
    }
  }
  if (correct_ == 6) 
  {
    correctPIN_();
  }
 
 else
  {
    incorrectPIN();
    retry = retry++;
  }
  for (int zz = 0; zz < 6; zz++) //pulisco il tentativo
  {
    attempt[zz] = 0;
  }
  if (retry == 3) {
    while(retry == 3) {
      riprova();
   }
  }
 }


void readKeypad()
{
  char key = keypad.getKey();
  if (key != NO_KEY)
  {
    switch(key)
    {
    case '*':
      z = 0;
      break;
    case '#':
      delay(100); // for extra debounce
      lcd.clear();
      checkPIN();
      break;
    default:
      attempt[z] = key;
      z++;
    }
  }
}

void loop()
{
  readKeypad();
  if (digitalRead(button) == HIGH) {
    myservo.write(60);
    }
}

Dove è che sbaglio?
Grazie in anticipo per le risposte.

sicuro di premere * prima di ogni codice? percè azzeri la Z solo lì. io la azzererei dopo ogni check. E il check non lo farei quando premi # ma all'immisione del 6° carattere, così sei sicuro che non scrivi fuori dall'array, causando un bel pò di casini se sovrascrivi qualche altra variabile

Si prima di ogni codice premo *. Credo sia un problema nella funzione che confronta i codici checkPIN(). Ho notato che se tolgo la 2 comparazione il codice non mi da errore incorrect pin, cosa potrebbe essere?

Non è corretta la sequenza degli if e dell'else dentro checkpin.
Ti conviene usare una variabile di stato che setti true se il codice è corretto in uno dei due casi e al posto dell'else metti un altro if di controllo della variabile.
Altrimenti li devi concatenare correttamente.

Inoltre devi mettere un controllo su z tipo

default:
      attempt[z] = key;
      z++;
      if (z>5) z=0; // crea una stringa circolare

altrimenti fai la pipì fuori dal vasino e si incasina il micro.

Per concatenare correttamente cosa intendi?
Come li dovrei mettere?

Una cosa del genere

 int correct  = 0;
 int correct_ = 0;
 for (int q = 0; q < 6; q++)
  {
    if (attempt[q] == PIN_lock[q]) correct++;
  }
 for (int q_ = 0; q_ < 6; q_++)
  {
    if (attempt[q_] == PIN_unlock[q_]) correct_++;
  }
  if (correct == 6) correctPIN();
  else
  	if (correct_ == 6) correctPIN_();
  		else
  		{
    		incorrectPIN();
    		retry++;
  		}

Grazie mille della risposta ora funziona!!!!
Un' altra aggiunta che vorrei fare a questo progetto è far apparire a video sul display LCD l' orario e la data corrente.
La data avrei intenzione di mantenerla con uno shield RTC che da poco ho assemblato.
Il mio dubbio è dove devo piazzare il pezzo di codice che mi visualizza l' orario?

Hertz:
Il mio dubbio è dove devo piazzare il pezzo di codice che mi visualizza l' orario?

Nel loop. :grin:

void loop()
{
  displayTime();
  readKeypad();
  if (digitalRead(button) == HIGH) {
    myservo.write(60);
    }
}

Crei una funzione, esempio displayTime(), che ogni loop aggiorna l'orario.

Ci ho provato ma non me lo visualizza in cosa sbaglio?

non te lo visualizza o non te lo compila? ma almeno capisci il codice che stai usando? perchè non credo proprio che tu lo stia capendo

@PaoloP: gli stai facendo il lavoro a gratis, almeno assicurati che si sforzi un pò

Non me lo visualizza sull' LCD. Non sono un Genio dell' elettronica o dell' informatica e mi sono iscritto su questo forum appunto per ricevere supporto dove non riesco da persone più esperte di me. La logica del codice la capisco abbastanza e prima di postare queste domande ho sbattuto la testa per giorni quindi lo sforzo e l' impegno c' è!

Guarda qui --> RTC Arduino: usa arduino come orologio - Mauro Alfieri Elettronica
Poi cerca di integrarlo nel tuo sketch.

Hertz:
Non me lo visualizza sull' LCD.

inceve io credo che non te lo compili, e quindi non carichi il nuovo codice sull'arduino.

paoloP ha aggiunto una chiamata a funzione (displayTime():wink: che però non è scritta da nessuna parte (non è implementata); e questo è un errore di compilazione. Tu hai chiesto dove aggiornare l'ora e paoloP ti ha detto dove, ma non COME, ovvero il codice per leggere l'ora dall'RTC e scriverla nell'LCD dovrai implementarla nella funzione che ti tocca scrivere.

Gli LCD noralmente seguono degli standard di comunicazione, e ce ne sono vari. Gli RTC invece spesso sono un modo a se ogni modello, salvo non siano della stessa marca.

Lo compila e come, ho seguito il consiglio di paoloP di CREARE una funzione e al suo interno di caricare il codice per leggere l' orario e la data dal RTC, ed infatti funziona l' unico errore che commettevo era di non regolare il contrasto del LCD il quale non mi visualizzava niente.
Ora l' unico problema che mi è rimasto è che creando la funzione che mi legge e stampa l' orario e la data, e richiamandola in:

void loop() {
      displayTime();
      .....
}

entra nella funzione e non mi permette di inserire il PIN che sblocca o blocca la porta.

Hertz:
...
entra nella funzione e non mi permette di inserire il PIN che sblocca o blocca la porta.

Scusa, ma come hai fatto quella funzione ???

Una volta visualizzata data/ora spero torni indietro al loop() :wink:

Guglielmo

gpb01:
Scusa, ma come hai fatto quella funzione ???

ci possiamo basare sul codice che posti, la funzione non l'hai scritta da nessuna parte eppure hai chiesto come mai non funzionava.
Io come passo logico (errato) ho creduto che non l'avessi proprio scritta, scusa se ho pensato male ma di solito ci azzecco :slight_smile:

Se la posti vediamo cosa c'è che non funziona.

Il codice che uso normalmente per visualizzare l' orario e la data prelevati da uno shield RTC è il seguente:

#include <ShiftLCD.h>
#include "Wire.h"
#define DS1307_I2C_ADDRESS 0x68

ShiftLCD lcd(2,4,3);

// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

// 1) Sets the date and time on the ds1307
// 2) Starts the clock
// 3) Sets hour mode to 24 hour clock

// Assumes you're passing in valid numbers

void setDateDs1307(byte second,        // 0-59
byte minute,        // 0-59
byte hour,          // 1-23
byte dayOfWeek,     // 1-7
byte dayOfMonth,    // 1-28/29/30/31
byte month,         // 1-12
byte year)          // 0-99
{
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.write(0);
  Wire.write(decToBcd(second));    // 0 to bit 7 starts the clock
  Wire.write(decToBcd(minute));
  Wire.write(decToBcd(hour));     
  Wire.write(decToBcd(dayOfWeek));
  Wire.write(decToBcd(dayOfMonth));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.write(00010000); // sends 0x10 (hex) 00010000 (binary) to control register - turns on square wave
  Wire.endTransmission();
}

// Gets the date and time from the ds1307
void getDateDs1307(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
  // Reset the register pointer
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.write(0);
  Wire.endTransmission();

  Wire.requestFrom(DS1307_I2C_ADDRESS, 7);

  // A few of these need masks because certain bits are control bits
  *second     = bcdToDec(Wire.read() & 0x7f);
  *minute     = bcdToDec(Wire.read());
  *hour       = bcdToDec(Wire.read() & 0x3f);  // Need to change this if 12 hour am/pm
  *dayOfWeek  = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month      = bcdToDec(Wire.read());
  *year       = bcdToDec(Wire.read());
}

void setup()
{
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  Wire.begin();
  lcd.begin(16,2);

  // Change these values to what you want to set your clock to.
  // You probably only want to set your clock once and then remove
  // the setDateDs1307 call.

  second = 0;
  minute = 37;
  hour = 20;
  dayOfWeek = 3;
  dayOfMonth = 26;
  month = 9;
  year = 13;
 // setDateDs1307(second, minute, hour, dayOfWeek, dayOfMonth, month, year);
}

void loop()
{
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;

  getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  lcd.print(hour, DEC);// convert the byte variable to a decimal number when being displayed
  lcd.print(":");
  if (minute<10)
  {
      lcd.print("0");
  }
  lcd.print(minute, DEC);
  lcd.print(":");
  if (second<10)
  {
      lcd.print("0");
  }
  lcd.print(second, DEC);
  lcd.print("  ");
  lcd.setCursor(0,1);
  lcd.print(dayOfMonth, DEC);
  lcd.print("/");
  lcd.print(month, DEC);
  lcd.print("/");
  lcd.print(year, DEC);
  lcd.print(" ");
  switch(dayOfWeek){
  case 1: 
    lcd.println("Sunday");
    break;
  case 2: 
    lcd.print("Monday");
    break;
  case 3: 
    lcd.print("Tuesday");
    break;
  case 4: 
    lcd.print("Wednesday");
    break;
  case 5: 
    lcd.print("Thursday");
    break;
  case 6: 
    lcd.print("Friday");
    break;
  case 7: 
    lcd.print("Saturday");
    break;
  }
  //  lcd.print(dayOfWeek, DEC);
  delay(1000);
  lcd.clear();
}

Ora per implementarlo nello sketch della serratura il void loop() di questo codice lo inserisco nella funzione displayTime(), che viene richiamata nel void loop() del codice della serratura, subito dopo viene richiamata la funzione che legge il PIN:

// Sketch serratura
void loop()
{
  displayTime();  //richiamo la funzione che legge l' orario
  lcd.clear();      //cancello l' LCD
  readKeypad();  //richiamo funzione che esegue la lettura del PIN
    }
}

ecco la funzione displayTime();

void displayTime() {
   byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;

  getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  lcd.print(hour, DEC);// convert the byte variable to a decimal number when being displayed
  lcd.print(":");
  if (minute<10)
  {
      lcd.print("0");
  }
  lcd.print(minute, DEC);
  lcd.print(":");
  if (second<10)
  {
      lcd.print("0");
  }
  lcd.print(second, DEC);
  lcd.print("  ");
  lcd.setCursor(0,1);
  lcd.print(dayOfMonth, DEC);
  lcd.print("/");
  lcd.print(month, DEC);
  lcd.print("/");
  lcd.print(year, DEC);
  lcd.print(" ");
  switch(dayOfWeek) {
  case 1: 
    lcd.println("Sunday");
    break;
  case 2: 
    lcd.print("Monday");
    break;
  case 3: 
    lcd.print("Tuesday");
    break;
  case 4: 
    lcd.print("Wednesday");
    break;
  case 5: 
    lcd.print("Thursday");
    break;
  case 6: 
    lcd.print("Friday");
    break;
  case 7: 
    lcd.print("Saturday");
    break;
    }
    delay(1000);
    }

Ora per fare in modo che non appena premo un qualsiasi tasto sul keypad smetta di leggere l' orario e si prepari per leggere il PIN e confrontarlo come dovrei fare?
Scusate l' ignoranza

il problema è che nella funzione di display dell'LCD hai messo un delay(1000); durante questo delay di un secondo, arduino è "cieco" a ciò che gli accade (salvo interrupt, ma per ora lasciamo perdere)

puoi togleire il delay, ma il LCD flicchererà perchè cerchi di aggiornarlo troppo velocemente e non ce la fa. Quindi, devi usare un sistema tipo blickWithoutDelay, ovvero, usando la millis verifiche se sono passati 1000MS, se no proseguii a eseguire il resto del codice, se si aggiorni il LCD.

notare che in realtà arduino è "cieco" durante l'esecuzione di qualsiasi istruzione a tutti gli input esclusi gli interrupt, però il codice normalmente viene eseguito così in fretta che non ci si accorge di questi ritardi/buchi salvo quando serve fare cose molto particolari

Ho modificato il void loop() in questo modo:

void loop()
{
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis < interVal) {
    previousMillis = currentMillis;
    displayTime();
    lcd.clear();
    } else {
  readKeypad();
  if (digitalRead(button) == HIGH) {
    myservo.write(60);
    }
}
}

seguendo il tutorial del sistema blickWithoutDelay, ma non funziona: e come se l' ultima parte del display lampeggiasse. Come mai ?

if (currentMillis - previousMillis < interVal) {

fai i conti per esempio se
currentMillis è 15000
previusMillis è 14300
(non dovrebbe fare l'update, essendo trascorsi 700ms)

e se
currentMillis è 15000
previusMillis è 13000
(dovrebbe fare l'update, essendo trascorsi 2000ms)