Actividad avanzada. Cambio de contraseña. Memoria EEPROM

PROGRAMACIÓN EXTENDIDA


Memoria EEPROM

 

Teoría

EEPROM significa Electrically Erasable Programmable Read-Only Memory. Es un tipo de memoria no volátil, lo que significa que conserva su contenido incluso cuando se apaga el dispositivo. Esto es útil para aplicaciones que requieren la conservación de la configuración del sistema, como contraseñas, estados de dispositivos, valor de contadores, entre otros.

Características de la EEPROM

  • No volátil: La EEPROM mantiene su contenido sin necesidad de alimentación eléctrica.

  • Ciclos de escritura: La EEPROM tiene un límite de ciclos de escritura. En la mayoría de los microcontroladores Arduino, este límite es de aproximadamente 100,000 ciclos por cada celda de memoria.

  • Velocidad: La velocidad de lectura es generalmente rápida, pero la escritura es más lenta en comparación con la RAM.

  • Capacidad: Suele tener una capacidad limitada en comparación con otros tipos de memoria no volátil, como la memoria flash.

  • Acceso byte a byte: Permite la lectura y escritura de datos a nivel de byte, lo cual es ideal para almacenar configuraciones y pequeñas cantidades de datos.

La librería EEPROM

 

 En los microcontroladores Arduino, la EEPROM se encuentra integrada en el propio microcontrolador, usándose comúnmente para almacenar configuraciones y datos que deben persistir entre reinicios. La capacidad de la EEPROM varía según el modelo de Arduino, contando con 1KB (1024 bytes) en el modelo Arduino UNO y NANO.

La biblioteca EEPROM de Arduino facilita la lectura y escritura de datos en la memoria EEPROM. Las funciones principales incluyen:

  • EEPROM.read(address): Lee un byte desde la dirección especificada.
  • EEPROM.write(address, value): Escribe un byte en la dirección especificada.
  • EEPROM.update(address, value): Escribe un byte solo si el valor es diferente al valor almacenado (reduce el desgaste).
  • EEPROM.get(address, data): Lee un tipo de dato completo desde la EEPROM.
  • EEPROM.put(address, data): Escribe un tipo de dato completo en la EEPROM.


 


 

    Nota importante

Para maximizar la vida útil de la EEPROM, minimiza las operaciones de escritura. Utiliza EEPROM.update() en lugar de EEPROM.write() siempre que sea posible.

Programación

 

Explicación actividad a programar.

Para esta actividad partiremos del código elaborado hasta ahora, que permite la introducción de una combinación de 4 dígitos a través del dial y dos botones, contando también con el sistema de alarma. Haremos uso de la EEPROM interna del Arduino, por lo que no será necesaria ninguna conexión eléctrica adicional. La nueva funcionalidad a incorporar es la siguiente:

Después de abrir la caja con el código correcto, se deberá preguntar al usuario si desea cambiar la contraseña. Si el usuario indica que sí, se le solicitará que introduzca una nueva contraseña, almacenando la nueva contraseña en la EEPROM.

Tareas a realizar:

  • Durante la inicialización, se lee la contraseña correcta desde la EEPROM.

  • Después de abrir la caja fuerte con el código correcto, se le pregunta al usuario si desea cambiar la contraseña. Si el usuario decide cambiarla, se le pide que introduzca un nuevo código utilizando el dial y los botones. El nuevo código se guarda en la EEPROM.

Funciones Auxiliares

Además de las funciones ya creadas hasta el momento, entre las que se encuentran: updateLCD() ,resetCode(), checkCode(), openBox(), closeBox(), activateAlarm() y resetAttempts().Crearemos las siguientes:

  • readCodeFromEEPROM(): Lee la contraseña correcta desde la EEPROM.
  • askForCodeChange(): Pregunta al usuario si desea cambiar la contraseña y gestiona la introducción del nuevo código.

La EEPROM se utilizará para almacenar la contraseña correcta que desbloquea la caja. Al encender el sistema, se lee la contraseña almacenada en la EEPROM para permitir la verificación del código introducido por el usuario. Si el usuario decide cambiar la contraseña, el nuevo código se guarda en la EEPROM (sobre-escribiendo el anterior) para su uso futuro.

Lectura código

void readCodeFromEEPROM() {
    for (int i = 0; i < 4; i++) {
        correctCode[i] = EEPROM.read(i);
    }
}

Este fragmento de código lee los cuatro primeros bytes de la EEPROM y los almacena en el array correctCode, que contiene la contraseña correcta de la caja fuerte.

Escritura del código

for (int i = 0; i < 4; i++) {
    EEPROM.write(i, enteredCode[i]);
    correctCode[i] = enteredCode[i];
}
Este código escribe la nueva contraseña, almacenada en el array enteredCode, en la EEPROM. Al igual que en el fragmento de código anterior, la contraseña se "coloca físicamente" en los 4 primeros bytes de la memoria EEPROM.  Tras ello, se actualiza el array correctCode con la nueva contraseña.
#include <Servo.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

// Pines
const int potPin = A0;
const int enterButtonPin = 2;
const int clearButtonPin = 3;
const int ledRedPin = 9;
const int ledGreenPin = 10;
const int ledBluePin = 11;
const int servoPin = 6;
const int buzzerPin = 7;

Servo myServo;

// Variables de estado
bool isBoxOpen = false;
int enteredDigit = 0;
int enteredCode[4];
int currentDigitIndex = 0;
const int defaultCode[4] = {6, 6, 6, 6};
int correctCode[4];
int attemptsLeft = 10;
int incorrectAttempts = 0;
bool waitingForCodeChange = false;
bool enterButtonReleased = false;

void setup() {
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  lcd.clear();

  pinMode(potPin, INPUT);
  pinMode(enterButtonPin, INPUT_PULLUP);
  pinMode(clearButtonPin, INPUT_PULLUP);
  pinMode(ledRedPin, OUTPUT);
  pinMode(ledGreenPin, OUTPUT);
  pinMode(ledBluePin, OUTPUT);
  myServo.attach(servoPin);

  digitalWrite(ledRedPin, HIGH);
  digitalWrite(ledGreenPin, LOW);
  digitalWrite(ledBluePin, LOW);
  myServo.write(0);

  lcd.setCursor(0, 0);
  lcd.print("Dial:");
  lcd.setCursor(0, 1);
  lcd.print("Code:");

  readCodeFromEEPROM();

  // Si EEPROM estaba vacía, usar código por defecto
  for (int i = 0; i < 4; i++) {
    if (correctCode[i] < 0 || correctCode[i] > 9) {
      correctCode[i] = defaultCode[i];
    }
  }
}

void loop() {

    /* SI QUEREMOS VERIFICAR EL CÓDIGO CARGADO EN LA EEPROM
    Serial.print("Código cargado: ");
for (int i = 0; i < 4; i++) {
    Serial.print(correctCode[i]);
    Serial.print(" ");
}
Serial.println();
*/


  int potValue = analogRead(potPin);
  enteredDigit = map(potValue, 0, 1023, 0, 9);

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Dial:");
  lcd.setCursor(5, 0);
  lcd.print(enteredDigit);
  lcd.setCursor(0, 1);
  lcd.print("Code:");
  updateLCD();

  bool enterButtonState = digitalRead(enterButtonPin) == LOW;
  bool clearButtonState = digitalRead(clearButtonPin) == LOW;

  if (clearButtonState && !waitingForCodeChange) {
    resetCode();
    updateLCD();
    delay(300);
  }

  if (enterButtonState && currentDigitIndex < 4 && !waitingForCodeChange) {
    enteredCode[currentDigitIndex] = enteredDigit;
    currentDigitIndex++;
    updateLCD();
    delay(300);
  }

  if (currentDigitIndex == 4 && !waitingForCodeChange) {
    if (checkCode()) {
      openBox();
      resetAttempts();
      waitingForCodeChange = true;
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Change Code?");
      lcd.setCursor(0, 1);
      lcd.print("Enter=Yes Clr=No");
    } else {
      incorrectAttempts++;
      attemptsLeft--;
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Cod. incorrecto");
      delay(1000);
      lcd.setCursor(0, 1);
      lcd.print("Intentos: ");
      lcd.print(attemptsLeft);
      delay(1000);
      resetCode();
      updateLCD();
      if (incorrectAttempts >= 10) {
        activateAlarm();
      }
    }
  }

  if (enterButtonState && clearButtonState && !waitingForCodeChange) {
    closeBox();
    delay(300);
  }

  if (waitingForCodeChange) {
    if (!enterButtonState) {
      enterButtonReleased = true; //compruebo si el usuario quiere cambiar la contraseña
    }

    if (enterButtonState && enterButtonReleased) {
      enterButtonReleased = false;
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("New Code:");
      resetCode();
      updateLCD();
      delay(300);

      while (currentDigitIndex < 4) {
        potValue = analogRead(potPin);
        enteredDigit = map(potValue, 0, 1023, 0, 9);
        lcd.setCursor(10, 0);
        lcd.print(enteredDigit);

        if (digitalRead(enterButtonPin) == LOW) {
          enteredCode[currentDigitIndex] = enteredDigit;
          currentDigitIndex++;
          updateLCD();
          delay(300);
        }
      }

      for (int i = 0; i < 4; i++) {
        EEPROM.write(i, enteredCode[i]);
        correctCode[i] = enteredCode[i];
      }

      lcd.clear();
      lcd.setCursor(0, 1);
      lcd.print("Code Updated");
      delay(1000);
      waitingForCodeChange = false;
      resetCode();
    }

    if (clearButtonState) {
      waitingForCodeChange = false;
      resetCode();
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Dial:");
      lcd.setCursor(0, 1);
      lcd.print("Code:");
    }
  }

  delay(200);
}

void resetCode() {
  currentDigitIndex = 0;
  for (int i = 0; i < 4; i++) {
    enteredCode[i] = 0;
  }
}

void updateLCD() {
  lcd.setCursor(5, 1);
  for (int i = 0; i < 4; i++) {
    if (i < currentDigitIndex) {
      lcd.print(enteredCode[i]);
    } else {
      lcd.print("_");
    }
  }
}

bool checkCode() {
  for (int i = 0; i < 4; i++) {
    if (enteredCode[i] != correctCode[i]) {
      return false;
    }
  }
  return true;
}

void openBox() {
  isBoxOpen = true;
  digitalWrite(ledRedPin, LOW);
  digitalWrite(ledGreenPin, HIGH);
  digitalWrite(ledBluePin, LOW);
  myServo.write(90);
}

void closeBox() {
  isBoxOpen = false;
  digitalWrite(ledRedPin, HIGH);
  digitalWrite(ledGreenPin, LOW);
  digitalWrite(ledBluePin, LOW);
  myServo.write(0);
}

void activateAlarm() {
  for (int i = 0; i < 10; i++) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("ALARMA ACTIVA");
    tone(buzzerPin, 1000);
    delay(500);
    noTone(buzzerPin);
    delay(500);
  }
  incorrectAttempts = 0;
  attemptsLeft = 10;
}

void resetAttempts() {
  incorrectAttempts = 0;
  attemptsLeft = 10;
}

void readCodeFromEEPROM() {
  for (int i = 0; i < 4; i++) {
    correctCode[i] = EEPROM.read(i);
  }
}
Vistas
2 Número de vistas
2 Vistas de miembros
0 Vistas públicas
Acciones
0 Gustos
0 No me gusta
0 Comentarios
Compartir en redes sociales
Compartir enlace
Compartir por correo

Por favor iniciar sesión para compartir esto webpage por correo.