В прошлый раз, мы сравнивали введённый пароль с паролем, который был записан во flash память контроллера. Но ведь известно, что эту область памяти нельзя изменить в процессе работы устройства, и для хранения таких данных нужно бы использовать EEPROM. Сегодня разберём, как можно делать аппаратный сброс, где и как хранить эти данные, как реализовать первичный запуск устройства, с заданием характеристик. Самое простое, что мы можем использовать для сброса – джампер.
#define jumper 14 // Сброс и первичный запуск
Будем использовать для него 14 контакт arduino uno.

По сути это кнопка, только фиксированного действия, значит нужно настроить её на вход. Сделаем программную подтяжку, но я по старой привычке использую внешний подтягивающий резистор
pinMode(jumper, INPUT); // Конфигурируем контакт джампера на вход с подтяжкой
digitalWrite(jumper, HIGH); // Внутренний Pull-Up резистор
Проверять условия мы будем всего один раз – при каждом включении устройства. Значит вызов функции мы сделаем в теле setup, оболочки arduino ide.
void setup()
{
first_start(); // вызываем функцию проверки первого запуска
...
Давайте нарисуем упрощённый алгоритм, по которому будем действовать сегодня. Допустим новая прошивка загружена – и у нас происходит первый старт без jumper’а. Помимо него, мы можем ориентироваться на ячейку EEPROM. При задании пароля, мы помечаем её неким значением, и при последующих стартах, устройство понимает, что оно в рабочем режиме. Если же ячейка пустая или установлен jumper, то входим в процедуру сброса и добавления нового пароля.

С завода в микроконтроллерах avr, значения EEPROM составляют FF, попробуем это проверить
{
a = EEPROM.read(25); // Считаем значение ячейки 25 на новом МК
Serial.println(a, HEX); // Прописывая HEX, через запятую, выведем значение в шестнадцатеричном формате
}
Не знаю, почему исторически не 00, но нам нужно понимать, какое значение записано в новом контроллере, чтобы он мог сделать первичную инициализацию на новой загруженной прошивке

Выберем адрес ячейки 0, для хранения конфигурации устройства. В setup(), при включении устройства не забудем считывать её значение
firstStart = EEPROM.read(0); // Считываем переменную первого старта из EEPROM
Далее переходим к функции first_start.Читаем состояние джампера и переменной firstStart. Если какое-то условие удовлетворяет нас, то сбрасываем пароль и переходим к заданию нового
void first_start() // Первичная инициализация
{
if (digitalRead(jumper) == LOW || firstStart == 0xFF) // Если джампер установлен (сброс) или переменная первого старта == 1
{
Serial.println("Reset and Initialization");
password = 0; // Сбрасываем пароль
set_password(); // Вызов функции установки пароля
}
Теперь перейдём к функции установке нового пароля. Нужно понимать, что пароль нужно ввести будет два раза, для исключения ошибки. Так как у нас, это программа стартовой инициализации, допустимо сделать цикл while с условиями равенства паролей. Первый пароль, мы сохраним в новую переменную repeatPassword, а вторую, в уже знакомую переменную correctPassword. Одной из них дадим любое значение, чтобы это условие выполнилось.
repeatPassword = 1; // Назначаем переменной любое значение, отличное от нуля, чтобы заработал следующий цикл
while (correctPassword != repeatPassword)
{
}
Заполним эту функцию кодом ввода первого и второго пароля, чтобы исключить ошибку ввода. Для этой процедуры, введём переменную – флаг newPassword == 1, и пока она равна 1, будем вызывать уже известную нам функцию считывания значений клавиш и записи пароля в переменную key_scan(); Затем опять сбросим эту переменную, и повторим опрос пароля, который нужно ввести повторно. Но перед этим, запишем одну копию переменной пароля в repeatPassword = correctPassword;.
newPassword = 1; // Устанавливаем флаг установки нового пароля
while (newPassword == 1) // Пока новый пароль не установлен (флаг) - то обрабатываем клавиши в цикле
{
key_scan(); // Запускаем обработку кнопок
}
Serial.print("Password = ");
Serial.println(correctPassword);
Serial.println("Please repeat password");
newPassword = 1; // Сбрасываем флаг цикла опроса пароля
repeatPassword = correctPassword; // Записываем первый введённый пароль в другую переменную, для последующего сравнения
password = 0; // Обнуляем пароль
while (newPassword == 1) // Цикл повтора пароля. Пока новый пароль не установлен (флаг) - то обрабатываем клавиши в цикле
{
key_scan(); // Запускаем обработку кнопок
}
if (correctPassword == repeatPassword) // Если пароли совпали
{
Serial.println("Everything is ok. New password setting successful");
}
else // Если пароли не совпали
{
Serial.println("Passwords error. Please try again");
}
Если вы уже догадались, нам также надо отредактировать опрос клавиш, и что-то изменить в кнопке подтверждения пароля #. Если у нас есть флаг ввода нового пароля newPassword == 1, то надо воспользоваться ею, и добавить в условие. Также предлагаю ввести условия, по величине пароля, чтобы сохранение могло произойти только при значении > 999.
else if (keyboardResult == 243) // Если нажата #
{
if (newPassword == 0) // Если флаг нового пароля не установлен
{
check_password(); // Переходим на процедуру проверки пароля
}
else if (newPassword == 1 && password < 1000) // Если флаг нового пароля установлен и введён пароль > 3 символов
{
Serial.println("Error. password must be more than 4 characters");
password = 0;
}
else if (newPassword == 1 && password > 999)
{
save_new_password(); // Функция сохранения нового пароля
}
}
Отсюда, если пользователь подтвердил пароль 4 символа, будем вызывать функцию save_new_password. Здесь всё очень просто, значение пароля мы переносим в переменную correctPassword, а в случае сходства со вторым паролем, записываем значение в ячейки 1 и 2 EEPROM.
void save_new_password() // Функция сохранения нового пароля
{
correctPassword = password; // Устанавливаем новый пароль
if (correctPassword == repeatPassword)
{
byte *p = (byte*)&correctPassword; // Указываем на байты в int переменной correctPassword
EEPROM.write(1, p[0]); // записываем в ячейку 1 старший байт
EEPROM.write(2, p[1]); // записываем в ячейку 2 младший байт
}
password = 0; // Сбрасываем пароль
newPassword = 0; // Устанавливаем флаг нового пароля в 1, чтобы выйти из цикла
}
Код для записи в EEPROM, мы подробно разобрали в предыдущей статье, и реализовали в данном случае мы его с помощью указателей
byte *p = (byte*)&correctPassword; // Указываем на байты в int переменной correctPassword
EEPROM.write(1, p[0]); // записываем в ячейку 1 старший байт
EEPROM.write(2, p[1]); // записываем в ячейку 2 младший байт
После успешной инициализации нового пароля в функции set_password(). Мы запишем в 0 ячейку EEPROM любое значение, отличное от FF, чтобы при старте мы снова не попали на функцию первичной инициализации.
EEPROM.write(0, 0x01); // Записываем значение в 0 ячейку, отличное от FF
Теперь нужно не забыть самое главное – описать сценарий, если устройство запускается в обычном режиме, без джампера и с заполненной ячейкой EEPROM. Вернёмся к функции first_start, и пропишем условие else. Здесь наша задача считать с EEPROM значение пароля, и сохранить его в переменную correctPassword
else // Если не установлен джампер сброса (нормальный старт)
{
byte *r = (byte*)&correctPassword; // Указываем на байты в переменной типа int
r[0] = EEPROM.read(1); // Первый байт r[0] читаем из первой ячейки EEPROM
r[1] = EEPROM.read(2); // Второй байт r[1] читаем из второй ячейки EEPROM
Serial.print("Password in memory = ");
Serial.println(correctPassword); // Выводим в терминал пароль
}
Ну что-же, вроде мы описали все пункты нашей блок схемы, попробуем запустить на новом контроллере, с пустой ячейкой и посмотреть на результат. Чертой я отметил две ситуации. Первая – с неправильным подтверждающим паролем. Вторая – когда пароли введены верно.

У нас остался один минус – мы не можем поменять пароль прямо из программы. Но этот пароль у нас для прохода, получается, любой человек, сможет поменять пароль, а другие останутся без доступа. Для этого делается альтернативный пароль администратора, я предлагаю это сделать позже по карте + пароль. Также, мы до сих пор игнорируем код красного светодиода. В принципе, с ним всё понятно, он горит, когда замок закрыт. Сделаем функцию red_led
void red_led() // Функция проверки запрещающего светодиода
{
if (lockType == 1 && digitalRead(lock) == LOW) // Если замок электромеханический, то
{
digitalWrite(ledDenied, HIGH); // Включаем запрещающий диод
}
else if (lockType == 0 && digitalRead(lock) == HIGH) // Если замок электромагнитный
{
digitalWrite(ledDenied, HIGH); // Включаем запрещающий диод
}
else
{
digitalWrite(ledDenied, LOW); // Выключаем запрещающий диод
}
}
Пропишем два условия, для двух типов замка. Нам нужно проверять тип замка и условие закрытого замка, иначе выключаем диод. Мы уже получили достаточно рабочий контроль доступа с заданием пользовательского пароля, открытием двери с кнопки и клавиатуры, и понятной индикацией. В следующей статье добавим звуковой визуализации – сделаем код для зуммера. Код данной статьи, доступен на github.
Добавить комментарий
Для отправки комментария вам необходимо авторизоваться.