Файл: Московский институт электроники и математики им. А. Н. Тихонова.docx
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 25.10.2023
Просмотров: 26
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
Федеральное государственное автономное образовательное учреждение
высшего образования
«Национальный исследовательский университет «Высшая школа экономики»
Московский институт электроники и математики им. А.Н. Тихонова
Информатика и вычислительная техника
(Название ОП)
Бакалавриат
(уровень образования)
Конструкторская документация
к проекту
№19105 Система бронирования аудиторий
(Название проекта)
Выполнил студент гр. БИВ196
Корчагин Илья Александрович
askonstantinov@miem.hse.ru
Москва 2021
Содержание
Сборка 3
Компонентная база 3
Схемы 3
Пайка платы 5
Сборка замка 6
Подключение периферии 8
Прошивка 9
Описание функций 9
Подключение к системе бронирования аудиторий 10
Взаимодействие со считывателем 10
Взаимодействие с кодовой панелью 11
Приложение 12
Листинг прошивки 12
Сборка
Компонентная база
Компонент | Количество |
Arduino MEGA 2560 PRO Mini Embedded | 1 шт. |
Ethernet Module “W5500 lite” | 1 шт. |
Relay “TONGLING JQC-3FF-S-Z” | 1 шт. |
Понижатель напряжения 4,75-23V в 1-17V | 1 шт. |
PoE-splitter | 1 шт. |
Pin headers | 10 шт. |
JST XH 2.54 | 1 шт. |
Трехконтактный клеммник | 3 шт. |
Таблица 1. Список компонентов, необходимых для сборки устройства
Схемы
Плата была разработана в EasyEDA. Конструктивной особенностью данной версии замка является ограничение по размерам платы под 2DIN корпус.
Ниже приложены основные схемы устройства.
Рисунок. 1 Принципиальная схема
Рисунок. 2 Разведенная схема (вид сверху)
Рисунок. 3 Разведенная схема (вид снизу)
Пайка платы
Плата замка односторонняя и имеет дорожки только на нижнем слое. Все компоненты располагаются на стороне без дорожек.
Необходимо припаять напрямую на плату понижающий преобразователь, реле и разъем JST XH 2.54. Остальные компоненты являются съемными и крепятся к плате через Pin headers.
Рисунок. 4 Пайка контактов (вид снизу)
Если после пайки на плате остались следы флюса и канифоли, то необходимо их убрать методом ультразвуковой отчистки, с помощью соответствующего прибора.
Рисунок. 5 Очистка от излишек пайки в ультразвуковой ванне
Сборка замка
Необходимо вставить в соответствующие гнезда микроконтроллер и Ethernet module. Установить PoE-splitter: кабель, отвечающий за передачу данных, вставляется Ethernet module, а кабель питания в JST XH 2.54.
С помощью резистора выставить на понижающем преобразователе выходное напряжение 5V.
Рисунок. 6 Собранное устройство без внешней периферии
Собранная плата помещается в универсальный 2DIN корпус, после чего крепится внутрь щитового бокса.
Рисунок. 7 Собранное устройство в боксе
Подключение периферии
Вся внешняя периферия подключается строго через соответствующие клеммники. Нумерация входов/выходов представлена ниже:
Рисунок. 8 Расположение входов/выходов на плате
№ | Название | Что подключать |
1 | 12V | Кодовая панель |
2 | INT0 | |
3 | INT1 | |
4 | GND | |
5 | GND | Магнитный замок |
6 | RELAY_NC | |
7 | RELAY_NC | Запасной выход |
8 | GND | Кнопка |
9 | BTN |
Таблица 2. Распиновка устройства
Примечание: если вместо ожидаемых кодов на плату с панели/считывателя приходят лишние символы или не приходит ничего (при подключении Arduino к компьютеру и просмотре монитора порта), необходимо проверить подключение информационных пинов INT0 и INT1 считывателя. Часто такое происходит в случае, если они перепутаны.
Прошивка
Описание функций
setMasterCards() – функция, хранящая в себе коды мастер-карт.
getISOFormattedTime() – функция возвращающая реальное время.
printData() – функции, записывающие в файл с логами необходимую информацию о работе замка. При открытом Serial port, выводят информацию еще и в консоль.
reopenLog() – функция, открывающая/закрывающая файл с логами.
testConnection2Server() – функция, устанавливающая соединение с сервером. При успешном подключении выставляет connection_status в true, иначе в false.
sendLockID2Server() – функция, отправляющая на сервер ID замка.
changeLockState() – функция, вызываемая при взаимодействии с замком, определяющая состояние замка с помощью световых и звуковых сигналов.
buttonProcessing() – функция, обрабатывающая нажатия кнопки.
updateLock() - функция вызываемая при взаимодействии с замком, меняющая его состояние (открыто/закрыто) в различных ситуациях.
checkServerCommand() – функция, проверяющая команду, присланную с сервера.
readMessage() – функция, проверяющая прислал ли сервер команду.
sendEcho2Server() – функция, отправляющая на сервер состояние замка.
checkMasterCard() – функция, сравнивающая код приложенной карты с кодами мастер-карт.
isNumpudNumberInMassive() – функция, проверяющая есть ли символ, пришедший с кодовой панели, в массиве, хранящем символы шестнадцатеричной системы счисления.
receivedData() – функция, обрабатывающая информацию, пришедшую со считывателя.
Подключение к системе бронирования аудиторий
Прошитый замок автоматически подключается к серверу и начинает работу. При успешном подключении в консоль выведется следующее:
Рисунок 9. Успешное подключение замка к серверу
При некорректном подключении в консоль выведется соответствующая ошибка.
Взаимодействие со считывателем
При прикладывании к считывающей панели карты, сначала проверяется код карты среди мастер-карт. Если данного кода нет в списке, то устройство отправляет запрос на сервер, и в ответ получает команду открыть/не открыть дверь.
Ниже представлены примеры срабатывания замка по карте:
Рисунок 10. Ситуация: Приложенная карта является мастер-картой
(доступ разрешен)
Рисунок 11. Ситуация: Приложенная карта одобрена сервером
(доступ разрешен)
Рисунок 12. Ситуация: Приложенная карта отклонена сервером
(доступ запрещен)
Взаимодействие с кодовой панелью
Замок умеет взаимодействовать с кодовой панелью. Для того чтобы открыть замок по коду, необходимо ввести правильную 4х-значную комбинацию. Если пользователь ошибся с вводом символа и хочет обнулить ввод, то нужно нажать “#”. При вводе 4 символов на кодовой панели, замок сравнит код с мастер-кодом. Если они не совпали, то замок отправит запрос на сервер, и примет команду открыть/не открыть дверь.
Ниже представлены примеры срабатывания замка по коду:
Хуй пизда
Рисунок 13. Ситуация: введен мастер код
(доступ разрешен)
Рисунок 14. Ситуация: введен правильный код
(доступ разрешен)
Рисунок 15. Ситуация: введен неправильный код
(доступ запрещен)
Приложение
Листинг прошивки
#include
#include
#include
#include
#include
#include
#include
#include
#include
// These are the pins connected to the Wiegand D0 and D1 signals.
// Ensure your board supports external Interruptions on these pins
#define PIN_D0 2 // D0 Wiegand pin
#define PIN_D1 3 // D1 Wiegand pin
#define PIN_T A1 // Pin connected to relay
#define PIN_BUTT A0 // Pin connected to button
#define PIN_LED 5 // Pin connected to led
#define PIN_SPK 6 // Pin connected to spk
#define OPEN 10 // Why this numbers ???
#define CLOSED 11 // Why this numbers ???
#define SPK_DELAY 200 // Duration of speaker sound
#define ECHO_DELAY 600 // Count of loop cycles before sending echo
#define KEYCOUNT 4 // Count of keys in key sequence
//#define VERBOSE
//----------Network settings----------
byte mac[] = {0x90, 0xA2, 0xDA, 0xFF, 0xFF, 0xDA}; // NIC mac
char* remoteServer = "172.18.198.31"; // Server IP
int port = 8000; // Server port
Wiegand wiegand; // The object that handles the wiegand protocol
EthernetClient client; // The object that handles the ethernet
EthernetServer server(8000);
EthernetClient srvClient;
EthernetUDP UDP;
NTPClient timeClient(UDP, "213.141.154.170", 10800);
//----------Global variables----------
char* lockID = "908581ce-7860-11ec-90d6-0242ac120003"; // Lock ID
char* masterKey = "5962813a-2b27-422f-8ad8-84b5fd34ca8f"; // Master Key
char* versionSW = "1.20"; // Version of software
char* versionAPI = "1"; // Version of lock API on backend
bool lockState = 0; // Variable to set HIGH or LOW to PIN
bool access = 0; // Access flag for shitcode in answer from server check
bool connection_status = false; // Connection status
bool buttonActive = false; // Variable to save button state
bool longPressActive = false; // Variable to save unlock mode state
unsigned long buttonTimer = 0; // Variable to save start of pushing button
unsigned long last_switch = 0; // Variable to save time of last switch
unsigned long longPressTime = 2000; // Variable to save duration of long press
unsigned int systemState = CLOSED; // Variable to save state of door
unsigned long doorOpenDuration = 3000; // Variable to save door open time
unsigned int counter = 0; // Counter of loop for sending echo
char keySequence[KEYCOUNT+1]; // Readed keys from numpad
unsigned int keyCounter = 0; // Count of readed keys from numpad
char* logFilename = "log.log"; // Log filename
File logFile; // Log file
char key16[] = {'F','E','D','C','B','A','9','8','7','6','5','4','3','2','1','0'};
// Master cards, do not require network connection to open the lock
char* mastercards[10];
int cardSize = 0;
void setMasterCards() {
mastercards[cardSize++] = "0E72B8"; // Денис Александрович Королев
mastercards[cardSize++] = "7CC680"; // Петр Владимирович Рыбаков
//mastercards[cardSize++] = "28ABF8"; // Security
mastercards[cardSize++] = "70FE1E"; // Cleaner
mastercards[cardSize++] = "7B563C"; // Cleaner
mastercards[cardSize++] = "345E00"; // Cleaner
mastercards[cardSize++] = "5BECE5"; // Диспетчер
mastercards[cardSize++] = "9891A7"; // Корч
}
char* masterKeySequence = "1234"; // Master key to open lock with numpad
/*
* @brief Get ISO formatted time from NTP client
*
* Get ISO formatted time from NTP client
*
* @param timeClient - NTP client pointer
*
* @return char with time
*/
char* getISOFormattedTime(NTPClient* timeClient) {
// AVR implementation of localtime() uses midn. Jan 1 2000 as epoch
// so UNIX_OFFSET has to be applied to time returned by getEpochTime()
// More info here: https://www.nongnu.org/avr-libc/user-manual/group__avr__time.html
// https://forum.arduino.cc/index.php?topic=567637.0
timeClient->update();
time_t rawtime = timeClient->getEpochTime() - UNIX_OFFSET;
struct tm *ti;
static char buf[] = "0000-00-00T00:00:00Z";
ti = localtime (&rawtime);
//Serial.println(timeClient->getEpochTime());
if (strftime(buf, sizeof buf, "%FT%TZ", ti)) {
// Serial.print("Succsessfully formatted: ");
//Serial.println(buf);
} else {
//Serial.println("strftime failed");
}
//buf[strlen(buf)-1] = '\0';
return buf;
}
/*
* @brief Print line to file and serial
*
* If logFile then print data to file;
* If serial then print data to serial without VERBOSE;
*
* @param message - string/char/int/char* with data
* @param serial=0 - bool for print to serial without VERBOSE
*
* @return none
*/
void printData(char* message, bool serial = 0) {
if (logFile) {
logFile.print(message);
if (message[strlen(message)-1] == '\n') {
logFile.print(getISOFormattedTime(&timeClient));
logFile.print('\n');
}
}
if (serial)
Serial.print(message);
else {
#ifdef VERBOSE
Serial.print(message);
#endif
}
}
void printData(char message, bool serial = 0) {
if (logFile)
logFile.print(message);
if (serial)
Serial.print(message);
else {
#ifdef VERBOSE
Serial.print(message);
#endif
}
}
void printData(int message, bool serial = 0) {
if (logFile)
logFile.print(message);
if (serial)
Serial.print(message);
else {
#ifdef VERBOSE
Serial.print(message);
#endif
}
}
void printData(String message, bool serial = 0) {
if (logFile)
logFile.print(message);
if (serial)
Serial.print(message);
else {
#ifdef VERBOSE
Serial.print(message);
#endif
}
}
void printData(IPAddress message, bool serial = 0) {
if (logFile)
logFile.print(message);
if (serial)
Serial.print(message);
else {
#ifdef VERBOSE
Serial.print(message);
#endif
}
}
/*
* @brief Close and open log file
*
* Close and open log file
*
* @return none
*/
void reopenLog() {
if (logFile)
logFile.close();
logFile = SD.open(logFilename, FILE_WRITE);
}
/*
* @brief Try to connect to remote backend server
*
* If connection is success, then connection_status is true, else false
*
* @return none
*/
void testConnection2Server() {
// Test network connection
if (client.connect(remoteServer, port)) { // Connection successful
if (! connection_status)
printData("Get server connection\n", 1);
connection_status = true;
} else { // Connection failed
if (connection_status)
printData("No server connection\n", 1);
connection_status = false;
client.stop();
}
}
/*
* @brief Sending lockID to server
*
* POST request to backend server with lockID, masterKey & versionSW
*
* @return none
*/
void sendLockID2Server() {
String data = "{\"uuid\":\"";
data.concat(lockID);
data.concat("\",\"master\":\"");
data.concat(masterKey);
data.concat("\",\"version\":\"");
data.concat(versionSW);
data.concat("\"}");
client.flush();
client.print("POST /lock-api/v");
client.print(versionAPI);
client.println("/register-lock/ HTTP/1.1");
client.print("Host: ");
client.println(remoteServer);
client.println("Content-Type: application/json");
client.println("Connection:close");
client.print("Content-Length:");
client.println(data.length());
client.println();
client.print(data);
client.flush();
}
/*
* @brief Change lock state
*
* Change lock state with led and beep signals
*
* @return none
*/
void changeLockState(int newSystemState = 0) {
switch (newSystemState) {
case CLOSED :
systemState = CLOSED; // Set close mode
lockState = LOW;
digitalWrite(PIN_T, lockState);
printData("Closed mode ON\n", 1);
digitalWrite(PIN_LED, LOW);
digitalWrite(PIN_SPK, LOW);
delay(SPK_DELAY);
digitalWrite(PIN_SPK, HIGH);
break;
case OPEN :
systemState = OPEN; // Set open mode
lockState = HIGH;
digitalWrite(PIN_T, lockState);
printData("Open mode ON\n", 1);
digitalWrite(PIN_LED, HIGH);
digitalWrite(PIN_SPK, LOW);
delay(SPK_DELAY);
digitalWrite(PIN_SPK, HIGH);
break;
default :
if (systemState == CLOSED) {
systemState = OPEN; // Set open mode
lockState = HIGH;
digitalWrite(PIN_T, lockState);
printData("Open mode ON\n", 1);
digitalWrite(PIN_LED, HIGH);
digitalWrite(PIN_SPK, LOW);
delay(SPK_DELAY);
digitalWrite(PIN_SPK, HIGH);
} else {
systemState = CLOSED; // Set close mode
lockState = LOW;
digitalWrite(PIN_T, lockState);
printData("Closed mode ON\n", 1);
digitalWrite(PIN_LED, LOW);
digitalWrite(PIN_SPK, LOW);
delay(SPK_DELAY);
digitalWrite(PIN_SPK, HIGH);
}
break;
}
}
/*
* @brief Processing button pressing
*
* If button is pressed shortly, open lock;
* If button is pressed longly, call changeLockState;
*
* @return none
*/
void buttonProcessing() {
if (!digitalRead(PIN_BUTT)) {
if (buttonActive == false) {
buttonActive = true;
buttonTimer = millis(); // Save time when button is pressed
}
if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) {
longPressActive = true; // If it's long press
printData("Long press detected, switching mode\n");
changeLockState();
}
} else { // If it's not long press
if (buttonActive == true) {
if (longPressActive == true) {
longPressActive = false;// It's not long press
} else if (systemState == CLOSED) {
lockState = HIGH; // Unlock door by button
digitalWrite(PIN_T, lockState);
printData("Door unlocked by button\n", 1);
last_switch = millis();
digitalWrite(PIN_LED, HIGH);
digitalWrite(PIN_SPK, LOW);
delay(SPK_DELAY);
digitalWrite(PIN_SPK, HIGH);
}
buttonActive = false;
}
}
}
/*
* @brief Updating lock state
*
* If duration > 0 then open lock;
* If duration = 0 or not set then check time after door may be opened;
* If beep then beep;
* If led then led;
*
* @param duration - duration for opening lock, default 0
* @param beep - is beep signal is needed, default 1
* @param led - is led signal is needed, default 1
*
* @return none
*/
void updateLock(int duration = 0, bool beep = 1, bool led = 1) {
if (duration) {
if (systemState == CLOSED) {
printData("Open lock for ");
printData(duration);
printData(" seconds\n");
lockState = HIGH;
digitalWrite(PIN_T, lockState);
last_switch = millis();
if (led)
digitalWrite(PIN_LED, HIGH);
if (beep) {
digitalWrite(PIN_SPK, LOW);
delay(SPK_DELAY);
digitalWrite(PIN_SPK, HIGH);
}
} else {
printData("Door already open\n");
}
} else {
if (systemState == CLOSED) { // If door neccessary to be closed
if (millis() - last_switch > doorOpenDuration && lockState == HIGH) {
lockState = LOW; // and time from last switch is more
digitalWrite(PIN_T, lockState); // than door open time
if (led)
digitalWrite(PIN_LED, LOW);
if (beep)
digitalWrite(PIN_SPK, HIGH);
printData("Door locked\n"); // then close the door
}
}
}
}
/*
* @brief Open lock
*
* Open lock for duration or doorOpenDuration, calling updateLock;
*
* @param duration - duration for opening lock, default doorOpenDuration
* @param beep - is beep signal is needed, default 1
* @param led - is led signal is needed, default 1
*
* @return none
*/
void openLock(int duration = doorOpenDuration, bool beep = 1, bool led = 1) {
updateLock(duration, beep, led);
}
/*
* @brief String to integer
*
* Convert string to integer to compare in switch
*
* @param str - input string
* @param h - from which symbol start
*
* @return output converted integer
*/
constexpr unsigned int str2int(const char* str, int h = 0)
{
return !str[h] ? 5381 : (str2int(str, h+1) * 33) ^ str[h];
}
/*
* @brief Check command from server
*
* Check command which is send from backend server and send web answer
*
* @param command - command from server
* @param key - master key from server
*
* @return none
*/
void checkServerCommand(char* command, char* key, char* params) {
if (strcmp(key,masterKey) == 0) {
switch (str2int(command)) {
case str2int("openLock") :
openLock();
printData("---Response-200---\n");
srvClient.println("HTTP/1.1 200 OK");
srvClient.println("Content-Type: text/html");
srvClient.println("Connection: close");
srvClient.println();
break;
case str2int("changeState") :
if (strlen(params))
changeLockState((params[0]-'0')*10+params[1]-'0');
else
changeLockState();
printData("---Response-200---\n");
srvClient.println("HTTP/1.1 200 OK");
srvClient.println("Content-Type: text/html");
srvClient.println("Connection: close");
srvClient.println();
srvClient.print(systemState);
break;
default :
printData("---Response-404---\n");
srvClient.println("HTTP/1.1 404 Not found");
srvClient.println("Content-Type: text/html");
srvClient.println("Connection: close");
srvClient.println();
srvClient.print("Error: command not found");
}
} else {
printData("---Response-404---\n");
srvClient.println("HTTP/1.1 404 Not found");
srvClient.println("Content-Type: text/html");
srvClient.println("Connection: close");
srvClient.println();
srvClient.print("Error: key not found");
}
}
/*
* @brief Parse JSON from server
*
* Parse JSON with command which is send from backend server & call checkServerCommand
*
* @param str - JSON string from server
*
* @return none
*/
void parseJSONCommand(char* str) {
//…an Ethernet connection
//deserializeJson(doc, ethernetClient)
DynamicJsonDocument doc(256);
deserializeJson(doc, str);
printData("---Json-begin---\n");
printData(String(doc["command"].as
printData("\n");
printData(String(doc["key"].as
printData("\n");
printData("---Json-end--\n-");
checkServerCommand(doc["command"].as
}
/*
* @brief Read message from server
*
* Check if there's a message from backend server and call parseJSONCommand
*
* @return none
*/
void readMessage() {
if (connection_status) { // If connected to a server
char str[256];
//memset(str, '\0', 256*sizeof(char));
int i = 0;
int messageBegin = 0;
srvClient = server.available();
boolean currentLineIsBlank = true;
while (srvClient.connected()) {
if (srvClient.available()) {
char c = srvClient.read();
printData(c, 1);
if (c == '\n') {
// you're starting a new line
if (currentLineIsBlank) {
printData("---Start-message---\n");
messageBegin = 1;
} else
currentLineIsBlank = true;
} else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
if (messageBegin)
str[i++] = c;
}
} else {
if (messageBegin) {
str[i++] = '\0';
}
printData("\n");
printData("---Message-begin---\n");
printData(str);
printData("\n");
printData(int(strlen(str)));
printData("\n");
printData("---Message-end---\n");
parseJSONCommand(str);
break;
}
}
srvClient.stop();
srvClient.flush();
}
}
/*
* @brief Send echo to server
*
* Send echo signal by GET request to backend server
*
* @return none
*/
void sendEcho2Server() {
if (connection_status) { // If connected to a server
char str[30];
// Send GET request
printData("We connected\n");
client.print("GET /lock-api/v");
client.print(versionAPI);
client.print("/echo/");
client.print("?lock=");
// Get hash of lock ID
Sha1.init();
Sha1.print(lockID);
uint8_t* hash = Sha1.result();
char hashStr[40];
int k = 0;
for (int i = 0; i < 20; i++) {
sprintf(str, "%X", hash[i] >> 4);
hashStr[k++] = str[0];
sprintf(str, "%X", hash[i] & 0xF);
hashStr[k++] = str[0];
}
hashStr[40] = '\0';
client.print(hashStr);
client.print("&state=");
client.print(lockState);
client.println(" HTTP/1.1");
client.print("Host: ");
client.println(remoteServer);
client.println("Accept: */*");
client.println();
client.flush();
printData("We sent our stuff\n");
}
}
/*
* @brief Initialize Wiegand reader
*
* Set frequency for serial output;
* Disable microSD cardreader SPI;
* Set pins for LED and SPK;
* Set master cards;
* Install listeners on wiegand reader and initialize it;
* Set wiegand data pins as INPUT and attaches interruptions;
* Set pin for relay;
* Set pin for button;
* Check for test params;
* Test connection to backend server;
* Send lock ID to backend server;
* Send the initial pin state to the wiegand library;
*
* @return none
*/
void setup() {
Serial.begin(9600); //9600
Serial.println("Starting...");
delay(5000); // Delay for peripherals initialization
// Disabling the Ethernet shield SD cardreader SPI
if (!SD.begin(4))
Serial.println("Card failed, or not present");
else
Serial.println("SD Card initialized");
reopenLog();
Ethernet.begin(mac); // Network init
server.begin();
printData("My IP address: ");
printData(Ethernet.localIP());
printData("\n");
if (logFile) {
logFile.println("Starting...");
} else {
Serial.print("error opening ");
Serial.println(logFilename);
}
// Set pins for LED and SPK
pinMode(PIN_LED, OUTPUT);
pinMode(PIN_SPK, OUTPUT);
digitalWrite(PIN_LED, HIGH);
digitalWrite(PIN_SPK, HIGH);
setMasterCards();
printData("FOSS Electronic lock controller v", 1);
printData(versionSW, 1);
printData("\n", 1);
printData("Initialization...\n", 1);
// Install listeners and initialize Wiegand reader
wiegand.onReceive(receivedData, "Card readed: ");
wiegand.onStateChange(stateChanged, "State changed: ");
wiegand.begin(Wiegand::LENGTH_ANY, true);
// Intialize pins as INPUT and attaches interruptions
pinMode(PIN_D0, INPUT);
pinMode(PIN_D1, INPUT);
attachInterrupt(digitalPinToInterrupt(PIN_D0), pinStateChanged, CHANGE);
attachInterrupt(digitalPinToInterrupt(PIN_D1), pinStateChanged, CHANGE);
// Pin for relay
pinMode(PIN_T, OUTPUT);
// Pin for button
pinMode(PIN_BUTT, INPUT_PULLUP);
if (mac[3] == 0xFF)
if (mac[4] == 0xFF)
if (mac[5] == 0xFF)
printData("IT'S TEST LOCK MAC\n", 1);
if (lockID == "ffffffff-ffff-ffff-ffff-ffffffffffff")
printData("IT'S TEST LOCK ID\n", 1);
if (remoteServer == "172.18.198.34")
printData("IT'S TEST SERVER IP\n", 1);
testConnection2Server();
sendLockID2Server();
// Sends the initial pin state to the Wiegand library
pinStateChanged();
printData("Initialization completed!\n", 1);
}
/*
* @brief Main loop function
*
* Test connction to backend server;
* Check for pending messages on wiegand reader;
* Read message from backend server;
* Check if button is pressed;
* Update lock state;
* Send echo message to backend server;
*
* @return none
*/
void loop() {
reopenLog();
// Wiegand
noInterrupts();
wiegand.flush();
interrupts();
readMessage();
// Reading door button and changing modes
buttonProcessing();
// Timeout lock close after 3 seconds
updateLock();
if (counter == ECHO_DELAY) {
counter = 0;
testConnection2Server();
sendEcho2Server();
}
++counter;
delay(100); // Sleep a little - this doesn't have to run very often.
}
/*
* @brief Function which is called when wiegand data pins changed
*
* When any of the pins have changed, update the state of the wiegand library
*
* @return none
*/
void pinStateChanged() {
wiegand.setPin0State(digitalRead(PIN_D0));
wiegand.setPin1State(digitalRead(PIN_D1));
}
/*
* @brief Function which is called when wiegand reader state is changed
*
* Notifies when a reader has been connected or disconnected.
* Instead of a message, the seconds parameter can be anything you want -- Whatever you specify on `wiegand.onStateChange()`
*
* @param plugged - bool from wiegand lib
* @param message - that you set in wiegand.onStateChange()
*
* @return none
*/
void stateChanged(bool plugged, const char* message) {
printData(message, 1);
printData(plugged ? "CONNECTED" : "DISCONNECTED", 1);
printData("\n", 1);
}
/*
* @brief Check if given string is master card
*
* Check if given pass ID is in master card array
* @param rez - string with pass ID
*
* @return True if is a master card, else False
*/
bool checkMasterCard(char*const rez) {
for(int i = 0; i < cardSize; ++i)
if (strcmp(rez, mastercards[i]) == 0)
return true;
return false;
}
/*
* @brief Function which is called when data recieved from wiegand reader
*
* Notifies when a card or button was read.
* Instead of a message, the seconds parameter can be anything you want -- Whatever you specify on `wiegand.onReceive()`
* Convert bits to string;
* If it's button data, then check for reset button, then check sequence length, then check for master sequence, then send request to backend server;
* If it's card data, then check for master card, then send request to backend server;
*
* @param data - from wiegand lib
* @param bits - from wiegand lib
* @param message - that you set in wiegand.onStateChange()
*
* @return none
*/
bool isNumpudNumberInMassive(char k,char key[],int size)
{
for (int i=0;i
{
if (k==key[i])
{
return true;
}
}
return false;
}
void receivedData(uint8_t* data, uint8_t bits, const char* message) {
printData(message, 1);
char str[30];
char rez[6];
access = 0;
// Convert from byte to String
int j = 0;
uint8_t bytes = (bits + 7) / 8;
for (int i = 0; i < bytes; i++) {
sprintf(str, "%X", data[i] >> 4);
rez[j++] = str[0];
sprintf(str, "%X", data[i] & 0xF);
rez[j++] = str[0];
}
if (isNumpudNumberInMassive(rez[2],key16,16) == false){
printData("---NUMPAD---");
if (keyCounter < KEYCOUNT)
keySequence[keyCounter++] = rez[1];
if (rez[1] == 'B')
keyCounter = 0;
if (keyCounter == KEYCOUNT) {
keyCounter = 0;
keySequence[KEYCOUNT] = '\0';
printData("[", 1);
printData(keySequence, 1);
printData("]\n", 1);
if (strcmp(keySequence,masterKeySequence) == 0) {
printData("This key sequence is a master-key sequence.\n", 1);
access = 1;
} else {
printData("This key sequence is NOT a master-key sequence, trying to contact a server...\n", 1);
testConnection2Server();
if (connection_status) { // If connected to a server
// Send GET request
printData("We connected\n");
client.print("GET /lock-api/v");
client.print(versionAPI);
client.print("/check-access/");
client.print("?format=json&pass=");
// Get hash of lock ID
Sha1.init();
printData("Lock ID: [", 1);
printData(lockID, 1);
printData("]\n", 1);
Sha1.print(keySequence);
uint8_t* hash = Sha1.result();
char hashStr[40];
int k = 0;
for (int i = 0; i < 20; i++) {
sprintf(str, "%X", hash[i] >> 4);
hashStr[k++] = str[0];
sprintf(str, "%X", hash[i] & 0xF);
hashStr[k++] = str[0];
}
hashStr[40] = '\0';
printData("Hash of key sequence: [");
printData(hashStr);
printData("]\n");
client.print(hashStr);
client.print("&lock=");
// Get hash of key sequence
Sha1.init();
Sha1.print(lockID);
hash = Sha1.result();
k = 0;
for (int i = 0; i < 20; i++) {
sprintf(str, "%X", hash[i] >> 4);
hashStr[k++] = str[0];
sprintf(str, "%X", hash[i] & 0xF);
hashStr[k++] = str[0];
}
hashStr[40] = '\0';
printData("Hash of lock: [");
printData(hashStr);
printData("]\n");
client.print(hashStr);
client.println(" HTTP/1.1");
client.print("Host: ");
client.println(remoteServer);
client.println("Accept: */*");
client.println();
printData("We sent our stuff\n");
//Waiting for response
printData("Waiting for response", 1);
bool gotcha = 0;
int i = 0;
char buff = 'X';
for (i = 0; i < 1000 && !gotcha ; i++) {
delay(100);
gotcha = client.available();
printData(".", 1);
if (i == 999)
printData("Connection timeout", 1);
}
printData("\n");
printData("Answer returned after ");
printData(i);
printData(" ms\n");
printData("Start listening, is available:");
printData(client.available());
printData("\n");
printData("Incoming message:");
// Reading answer
while (client.available()) {
char c = client.read();
printData(c, 1);
if (c == '*') {
buff = c;
printData("Found *\n");
}
if (c == '#') {
buff = c;
printData("Found #\n");
}
}
printData("\n", 1);
printData("End of reading\n");
client.stop();
client.flush();
printData("We finished\n");
printData("Access analysis\n");
if (buff == '#')
access = 1;
else
access = 0;
}
}
}
} else {
rez[6] = '\0'; // String terminator
printData("[", 1);
printData(rez, 1);
printData("]\n", 1);
// Check given and converted pass ID
if (checkMasterCard(rez)) {
printData("This card is a master-card.\n", 1);
access = 1;
} else {
printData("This card is NOT a master-card, trying to contact a server...\n", 1);
testConnection2Server();
if (connection_status) { // If connected to a server
// Send GET request
printData("We connected\n");
client.print("GET /lock-api/v");
client.print(versionAPI);
client.print("/check-access/");
client.print("?format=json&lock=");
// Get hash of lock ID
Sha1.init();
printData("Lock ID: [", 1);
printData(lockID, 1);
printData("]\n", 1);
Sha1.print(lockID);
uint8_t* hash = Sha1.result();
char hashStr[40];
int k = 0;
for (int i = 0; i < 20; i++) {
sprintf(str, "%X", hash[i] >> 4);
hashStr[k++] = str[0];
sprintf(str, "%X", hash[i] & 0xF);
hashStr[k++] = str[0];
}
hashStr[40] = '\0';
printData("Hash of lock: [");
printData(hashStr);
printData("]\n");
client.print(hashStr);
client.print("&pass=");
// Get hash of pass ID
Sha1.init();
Sha1.print(rez);
hash = Sha1.result();
k = 0;
for (int i = 0; i < 20; i++) {
sprintf(str, "%X", hash[i] >> 4);
hashStr[k++] = str[0];
sprintf(str, "%X", hash[i] & 0xF);
hashStr[k++] = str[0];
}
hashStr[40] = '\0';
printData("Hash of card: [");
printData(hashStr);
printData("]\n");
client.print(hashStr);
client.println(" HTTP/1.1");
client.print("Host: ");
client.println(remoteServer);
client.println("Accept: */*");
client.println();
printData("We sent our stuff\n");
//Waiting for response
printData("Waiting for response", 1);
bool gotcha = 0;
int i = 0;
char buff = 'X';
for (i = 0; i < 1000 && !gotcha ; i++) {
delay(100);
gotcha = client.available();
printData(".", 1);
if (i == 999)
printData("Connection timeout\n", 1);
}
printData("\n");
printData("Answer returned after ");
printData(i);
printData(" ms\n");
printData("Start listening, is available:");
printData(client.available());
printData("\n");
printData("Incoming message:\n");
// Reading answer
while (client.available()) {
char c = client.read();
printData(c, 1);
if (c == '*') {
buff = c;
printData("Found *\n");
}
if (c == '#') {
buff = c;
printData("Found #\n");
}
}
printData("\n", 1);
printData("End of reading\n");
client.stop();
client.flush();
printData("We finished\n");
printData("Access analysis\n");
if (buff == '#')
access = 1;
else
access = 0;
}
}
}
if (access == 1) { // Opening lock if access granted
printData("ACCESS GRANTED\n", 1);
openLock();
} else
printData("ACCESS DENIED\n", 1);
//}
}