Features/digital safe firmware (#2)
* Added digital safe firmware * Adjusted build pipelines --------- Co-authored-by: Andriy Malyshenko <andriy@malyshenko.com>
This commit is contained in:
committed by
GitHub
parent
706d6f5419
commit
4fafe270f9
41
.github/workflows/build-safe.yml
vendored
Normal file
41
.github/workflows/build-safe.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: Build t1616-password-entry
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- ./firmware/t1616-password-entry
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-
|
||||
|
||||
- name: Cache PlatformIO
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
|
||||
- name: Install PlatformIO
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install --upgrade platformio
|
||||
|
||||
- name: Run PlatformIO
|
||||
working-directory: ./firmware/t1616-password-entry
|
||||
run: pio run
|
||||
|
@@ -1,6 +1,10 @@
|
||||
name: PlatformIO CI
|
||||
name: Build t1616-starter
|
||||
|
||||
on: [ push, pull_request, workflow_dispatch ]
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- ./firmware/t1616-starter
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
5
firmware/t1616-password-entry/.gitignore
vendored
Normal file
5
firmware/t1616-password-entry/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
10
firmware/t1616-password-entry/.vscode/extensions.json
vendored
Normal file
10
firmware/t1616-password-entry/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"platformio.platformio-ide"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"ms-vscode.cpptools-extension-pack"
|
||||
]
|
||||
}
|
29
firmware/t1616-password-entry/platformio.ini
Normal file
29
firmware/t1616-password-entry/platformio.ini
Normal file
@@ -0,0 +1,29 @@
|
||||
[env]
|
||||
platform = atmelmegaavr
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
monitor_port = /dev/ttyUSB1
|
||||
upload_port = /dev/ttyUSB0
|
||||
board_build.f_cpu = 16000000ul
|
||||
; Serial pins are used on Keypad
|
||||
; build_flags =
|
||||
; -D DEBUG
|
||||
lib_deps =
|
||||
SPI
|
||||
Wire
|
||||
LiquidCrystal
|
||||
Keypad
|
||||
|
||||
[env:tiny1616-serialupdi]
|
||||
board = ATtiny1616
|
||||
upload_speed = 230400
|
||||
upload_flags =
|
||||
--tool
|
||||
uart
|
||||
--device
|
||||
attiny1616
|
||||
--uart
|
||||
$UPLOAD_PORT
|
||||
--clk
|
||||
$UPLOAD_SPEED
|
||||
upload_command = pymcuprog write --erase $UPLOAD_FLAGS --filename $SOURCE
|
51
firmware/t1616-password-entry/src/Icons.h
Normal file
51
firmware/t1616-password-entry/src/Icons.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
Arduino Electronic Safe
|
||||
|
||||
Copyright (C) 2020, Uri Shaked.
|
||||
Released under the MIT License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
|
||||
// Our custom icon numbers
|
||||
#define ICON_LOCKED_CHAR (byte)0
|
||||
#define ICON_UNLOCKED_CHAR (byte)1
|
||||
|
||||
// This is a standard icon on the LCD1602 character set
|
||||
#define ICON_RIGHT_ARROW (byte)126
|
||||
|
||||
const byte iconLocked[8] PROGMEM = {
|
||||
0b01110,
|
||||
0b10001,
|
||||
0b10001,
|
||||
0b11111,
|
||||
0b11011,
|
||||
0b11011,
|
||||
0b11111,
|
||||
};
|
||||
|
||||
const byte iconUnlocked[8] PROGMEM = {
|
||||
0b01110,
|
||||
0b10000,
|
||||
0b10000,
|
||||
0b11111,
|
||||
0b11011,
|
||||
0b11011,
|
||||
0b11111,
|
||||
};
|
||||
|
||||
class Icons
|
||||
{
|
||||
|
||||
public:
|
||||
Icons(LiquidCrystal_I2C &lcd)
|
||||
{
|
||||
byte icon[8];
|
||||
memcpy_P(icon, iconLocked, sizeof(icon));
|
||||
lcd.createChar(ICON_LOCKED_CHAR, icon);
|
||||
memcpy_P(icon, iconUnlocked, sizeof(icon));
|
||||
lcd.createChar(ICON_UNLOCKED_CHAR, icon);
|
||||
}
|
||||
};
|
91
firmware/t1616-password-entry/src/Light.h
Normal file
91
firmware/t1616-password-entry/src/Light.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#include <tinyNeoPixel.h>
|
||||
#include "Tone.h"
|
||||
|
||||
#define PIN_RGB PIN_PA7
|
||||
#define LED_COUNT 1
|
||||
|
||||
class Light
|
||||
{
|
||||
private:
|
||||
tinyNeoPixel *pixels = new tinyNeoPixel(LED_COUNT, PIN_RGB, NEO_GRB + NEO_KHZ800);
|
||||
Tone *tone;
|
||||
|
||||
uint32_t seq_starts = 0;
|
||||
uint32_t seq_ends = 0;
|
||||
uint8_t seq_max = 0;
|
||||
uint8_t seq = 0;
|
||||
uint32_t seq_color = 0;
|
||||
|
||||
void setColor(uint32_t color);
|
||||
void flash(uint32_t color, uint8_t count, uint32_t delay_ms);
|
||||
|
||||
public:
|
||||
Light(Tone *tone) : tone(tone)
|
||||
{
|
||||
pixels->begin();
|
||||
};
|
||||
|
||||
void lock();
|
||||
void denied();
|
||||
void unlock();
|
||||
|
||||
void loop();
|
||||
};
|
||||
|
||||
void Light::setColor(uint32_t color)
|
||||
{
|
||||
pixels->clear();
|
||||
if (color != 0)
|
||||
for (uint8_t i = 0; i < LED_COUNT; i++)
|
||||
pixels->setPixelColor(i, color);
|
||||
pixels->show();
|
||||
}
|
||||
|
||||
void Light::flash(uint32_t color, uint8_t count, uint32_t delay_ms)
|
||||
{
|
||||
seq = 0;
|
||||
seq_max = count * 2 - 1;
|
||||
seq_starts = millis();
|
||||
seq_ends = seq_starts + delay_ms;
|
||||
seq_color = color;
|
||||
pinMode(PIN_RGB, OUTPUT);
|
||||
setColor(color);
|
||||
}
|
||||
|
||||
void Light::lock()
|
||||
{
|
||||
auto blue = pixels->Color(0, 0, 0xff);
|
||||
flash(blue, 1, 500);
|
||||
}
|
||||
|
||||
void Light::denied()
|
||||
{
|
||||
tone->beep(500, 500);
|
||||
auto red = pixels->Color(0xff, 0, 0);
|
||||
flash(red, 5, 200);
|
||||
}
|
||||
|
||||
void Light::unlock()
|
||||
{
|
||||
tone->beep(500, 150, true);
|
||||
auto green = pixels->Color(0, 0xff, 0);
|
||||
flash(green, 3, 250);
|
||||
}
|
||||
|
||||
void Light::loop()
|
||||
{
|
||||
if (seq <= seq_max)
|
||||
{
|
||||
if (millis() > seq_ends)
|
||||
{
|
||||
uint32_t color = (seq % 2 == 0) ? seq_color : pixels->Color(0, 0, 0);
|
||||
setColor(color);
|
||||
seq++;
|
||||
uint32_t delay_ms = seq_ends - seq_starts;
|
||||
seq_starts = seq_ends;
|
||||
seq_ends += delay_ms;
|
||||
}
|
||||
}
|
||||
}
|
82
firmware/t1616-password-entry/src/SafeState.h
Normal file
82
firmware/t1616-password-entry/src/SafeState.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
Arduino Electronic Safe
|
||||
|
||||
Copyright (C) 2020, Uri Shaked.
|
||||
Released under the MIT License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <EEPROM.h>
|
||||
|
||||
/* Safe state */
|
||||
#define EEPROM_ADDR_LOCKED 0
|
||||
#define EEPROM_ADDR_CODE_LEN 1
|
||||
#define EEPROM_ADDR_CODE 2
|
||||
#define EEPROM_EMPTY 0xff
|
||||
|
||||
#define SAFE_STATE_OPEN (char)0
|
||||
#define SAFE_STATE_LOCKED (char)1
|
||||
|
||||
class SafeState {
|
||||
public:
|
||||
SafeState();
|
||||
void lock();
|
||||
bool unlock(String code);
|
||||
bool locked();
|
||||
bool hasCode();
|
||||
void setCode(String newCode);
|
||||
|
||||
private:
|
||||
void setLock(bool locked);
|
||||
bool _locked;
|
||||
};
|
||||
|
||||
SafeState::SafeState() {
|
||||
this->_locked = EEPROM.read(EEPROM_ADDR_LOCKED) == SAFE_STATE_LOCKED;
|
||||
}
|
||||
|
||||
void SafeState::lock() {
|
||||
this->setLock(true);
|
||||
}
|
||||
|
||||
bool SafeState::locked() {
|
||||
return this->_locked;
|
||||
}
|
||||
|
||||
bool SafeState::hasCode() {
|
||||
auto codeLength = EEPROM.read(EEPROM_ADDR_CODE_LEN);
|
||||
return codeLength != EEPROM_EMPTY;
|
||||
}
|
||||
|
||||
void SafeState::setCode(String newCode) {
|
||||
EEPROM.write(EEPROM_ADDR_CODE_LEN, newCode.length());
|
||||
for (byte i = 0; i < newCode.length(); i++) {
|
||||
EEPROM.write(EEPROM_ADDR_CODE + i, newCode[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool SafeState::unlock(String code) {
|
||||
auto codeLength = EEPROM.read(EEPROM_ADDR_CODE_LEN);
|
||||
if (codeLength == EEPROM_EMPTY) {
|
||||
// There was no code, so unlock always succeeds
|
||||
this->setLock(false);
|
||||
return true;
|
||||
}
|
||||
if (code.length() != codeLength) {
|
||||
return false;
|
||||
}
|
||||
for (byte i = 0; i < code.length(); i++) {
|
||||
auto digit = EEPROM.read(EEPROM_ADDR_CODE + i);
|
||||
if (digit != code[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this->setLock(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SafeState::setLock(bool locked) {
|
||||
this->_locked = locked;
|
||||
EEPROM.write(EEPROM_ADDR_LOCKED, locked ? SAFE_STATE_LOCKED : SAFE_STATE_OPEN);
|
||||
}
|
55
firmware/t1616-password-entry/src/Tone.h
Normal file
55
firmware/t1616-password-entry/src/Tone.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#define PIN_BUZZER PIN_PC2
|
||||
#define NO_TONE \
|
||||
noTone(PIN_BUZZER); \
|
||||
digitalWrite(PIN_BUZZER, HIGH);
|
||||
|
||||
class Tone
|
||||
{
|
||||
private:
|
||||
uint32_t beep_start = 0;
|
||||
uint32_t beep_end = 0;
|
||||
uint32_t beep_freq = 0;
|
||||
bool beeping = false;
|
||||
bool isHappy = false;
|
||||
|
||||
public:
|
||||
Tone()
|
||||
{
|
||||
digitalWrite(PIN_BUZZER, HIGH);
|
||||
};
|
||||
|
||||
void beep(uint32_t how_long, uint32_t freq, bool happy = false);
|
||||
void happyBeep(uint32_t how_long, uint32_t freq);
|
||||
void loop();
|
||||
};
|
||||
|
||||
void Tone::beep(uint32_t how_long, uint32_t freq, bool happy)
|
||||
{
|
||||
beep_start = millis();
|
||||
beep_end = beep_start + how_long;
|
||||
beep_freq = freq;
|
||||
tone(PIN_BUZZER, beep_freq);
|
||||
isHappy = happy;
|
||||
beeping = true;
|
||||
}
|
||||
|
||||
void Tone::loop()
|
||||
{
|
||||
if (beeping)
|
||||
{
|
||||
if (isHappy)
|
||||
{
|
||||
tone(PIN_BUZZER, beep_freq + (millis() - beep_start));
|
||||
}
|
||||
|
||||
if (millis() > beep_end)
|
||||
{
|
||||
NO_TONE;
|
||||
beeping = false;
|
||||
}
|
||||
}
|
||||
}
|
326
firmware/t1616-password-entry/src/UI.h
Normal file
326
firmware/t1616-password-entry/src/UI.h
Normal file
@@ -0,0 +1,326 @@
|
||||
#pragma once
|
||||
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
#include <Keypad.h>
|
||||
#include "SafeState.h"
|
||||
#include "Icons.h"
|
||||
#include "Light.h"
|
||||
#include "Tone.h"
|
||||
|
||||
#define PIN_LENGTH 4
|
||||
|
||||
#define DELAY_LOCK 800
|
||||
#define DELAY_UNLOCK 800
|
||||
#define DELAY_MISMATCH 800
|
||||
#define DELAY_DENIED 1600
|
||||
#define DELAY_WELCOME 500
|
||||
#define DELAY_UNLOCKED 1000
|
||||
#define DELAY_STEPS 8
|
||||
|
||||
#define KEY_BEEP_MS 50
|
||||
#define IDLE_TIMEOUT 8000
|
||||
|
||||
enum State
|
||||
{
|
||||
WELCOME_1,
|
||||
WELCOME_2,
|
||||
|
||||
CHECK_LOCKED,
|
||||
SAFE_LOCKED,
|
||||
SAFE_LOCKED_INPUTKEY,
|
||||
SAFE_LOCKED_VALIDATE,
|
||||
SAFE_LOCKED_DOUNLOCK,
|
||||
SAFE_LOCKED_DENIED,
|
||||
SAFE_UNLOCKED,
|
||||
SAFE_UNLOCKED_ASKKEY,
|
||||
SAFE_UNLOCKED_INPUTKEY,
|
||||
SAFE_UNLOCKED_REPEATKEY,
|
||||
SAFE_UNLOCKED_MATCH,
|
||||
SAFE_UNLOCKED_MISMATCH,
|
||||
SAFE_UNLOCKED_DOLOCK,
|
||||
|
||||
WAIT_SCREEN_START,
|
||||
WAIT_SCREEN_PROGRESS,
|
||||
WAIT_SCREEN_NONBLOCKING,
|
||||
INPUT_PIN,
|
||||
};
|
||||
|
||||
class UI
|
||||
{
|
||||
private:
|
||||
LiquidCrystal_I2C *lcd;
|
||||
Keypad *keypad;
|
||||
SafeState *safeState;
|
||||
Light *light;
|
||||
Tone *tone;
|
||||
|
||||
State state = WELCOME_1;
|
||||
void setState(State _state)
|
||||
{
|
||||
// Serial.printf("State) { %d -> %d\n", state, _state);
|
||||
state = _state;
|
||||
};
|
||||
|
||||
uint32_t idle_since = 0;
|
||||
|
||||
// Nonblocking wait
|
||||
uint32_t wait_from = 0;
|
||||
uint32_t wait_till = 0;
|
||||
uint8_t wait_step = 0;
|
||||
State state_after_break;
|
||||
void wait(uint32_t how_long, State state_after, bool showProgress = true);
|
||||
|
||||
// Nonblocking pin reading
|
||||
char key[PIN_LENGTH + 1] = {0};
|
||||
char pin[PIN_LENGTH + 1] = {0};
|
||||
uint8_t pin_keys;
|
||||
State state_after_pin;
|
||||
void askpin(State state_after);
|
||||
|
||||
protected:
|
||||
void showWaitScreen(int delayMillis);
|
||||
|
||||
public:
|
||||
UI(LiquidCrystal_I2C *lcd, Keypad *keypad, SafeState *safeState, Light *light, Tone *tone)
|
||||
: lcd(lcd), keypad(keypad), safeState(safeState), light(light), tone(tone)
|
||||
{
|
||||
idle_since = millis();
|
||||
};
|
||||
|
||||
void loop();
|
||||
};
|
||||
|
||||
void UI::loop()
|
||||
{
|
||||
static bool newCodeNeeded;
|
||||
|
||||
if (state == WELCOME_1)
|
||||
{
|
||||
lcd->setCursor(4, 0);
|
||||
lcd->print("Welcome!");
|
||||
wait(DELAY_WELCOME, WELCOME_2, false);
|
||||
}
|
||||
|
||||
else if (state == WELCOME_2)
|
||||
{
|
||||
lcd->setCursor(0, 2);
|
||||
String message = "Tiny1616 Safe";
|
||||
for (byte i = 0; i < message.length(); i++)
|
||||
{
|
||||
lcd->print(message[i]);
|
||||
delay(50);
|
||||
}
|
||||
wait(DELAY_WELCOME, CHECK_LOCKED, false);
|
||||
}
|
||||
|
||||
else if (state == CHECK_LOCKED)
|
||||
{
|
||||
setState(safeState->locked() ? SAFE_LOCKED : SAFE_UNLOCKED);
|
||||
}
|
||||
|
||||
else if (state == SAFE_LOCKED)
|
||||
{
|
||||
lcd->clear();
|
||||
lcd->setCursor(0, 0);
|
||||
lcd->write(ICON_LOCKED_CHAR);
|
||||
lcd->print(" Safe Locked! ");
|
||||
lcd->write(ICON_LOCKED_CHAR);
|
||||
setState(SAFE_LOCKED_INPUTKEY);
|
||||
}
|
||||
|
||||
else if (state == SAFE_UNLOCKED)
|
||||
{
|
||||
lcd->clear();
|
||||
|
||||
lcd->setCursor(0, 0);
|
||||
lcd->write(ICON_UNLOCKED_CHAR);
|
||||
lcd->setCursor(2, 0);
|
||||
lcd->print(" # to lock");
|
||||
lcd->setCursor(15, 0);
|
||||
lcd->write(ICON_UNLOCKED_CHAR);
|
||||
|
||||
newCodeNeeded = true;
|
||||
|
||||
if (safeState->hasCode())
|
||||
{
|
||||
lcd->setCursor(0, 1);
|
||||
lcd->print(" A = new code");
|
||||
newCodeNeeded = false;
|
||||
}
|
||||
|
||||
setState(SAFE_UNLOCKED_ASKKEY);
|
||||
}
|
||||
|
||||
else if (state == SAFE_LOCKED_INPUTKEY)
|
||||
{
|
||||
askpin(SAFE_LOCKED_VALIDATE);
|
||||
}
|
||||
|
||||
else if (state == SAFE_LOCKED_VALIDATE)
|
||||
{
|
||||
bool unlockedSuccessfully = safeState->unlock(String(pin));
|
||||
wait(DELAY_UNLOCK, unlockedSuccessfully ? SAFE_LOCKED_DOUNLOCK : SAFE_LOCKED_DENIED);
|
||||
}
|
||||
|
||||
else if (state == SAFE_LOCKED_DOUNLOCK)
|
||||
{
|
||||
light->unlock();
|
||||
lcd->clear();
|
||||
lcd->setCursor(0, 0);
|
||||
lcd->write(ICON_UNLOCKED_CHAR);
|
||||
lcd->setCursor(4, 0);
|
||||
lcd->print("Unlocked!");
|
||||
lcd->setCursor(15, 0);
|
||||
lcd->write(ICON_UNLOCKED_CHAR);
|
||||
wait(DELAY_UNLOCKED, SAFE_UNLOCKED, false);
|
||||
}
|
||||
|
||||
else if (state == SAFE_LOCKED_DENIED)
|
||||
{
|
||||
lcd->clear();
|
||||
lcd->setCursor(0, 0);
|
||||
lcd->print("Access Denied!");
|
||||
light->denied();
|
||||
wait(DELAY_DENIED, SAFE_LOCKED);
|
||||
}
|
||||
|
||||
else if (state == SAFE_UNLOCKED_INPUTKEY)
|
||||
{
|
||||
lcd->clear();
|
||||
lcd->setCursor(0, 0);
|
||||
lcd->print("Enter new code) {");
|
||||
askpin(SAFE_UNLOCKED_REPEATKEY);
|
||||
}
|
||||
|
||||
else if (state == SAFE_UNLOCKED_REPEATKEY)
|
||||
{
|
||||
strcpy(key, pin);
|
||||
lcd->clear();
|
||||
lcd->setCursor(0, 0);
|
||||
lcd->print("Confirm new code");
|
||||
askpin(SAFE_UNLOCKED_MATCH);
|
||||
}
|
||||
|
||||
else if (state == SAFE_UNLOCKED_MATCH)
|
||||
{
|
||||
if (strcmp(key, pin) == 0)
|
||||
{
|
||||
safeState->setCode(String(pin));
|
||||
setState(SAFE_UNLOCKED_DOLOCK);
|
||||
}
|
||||
else
|
||||
{
|
||||
lcd->clear();
|
||||
lcd->setCursor(1, 0);
|
||||
lcd->print("Code mismatch");
|
||||
lcd->setCursor(0, 1);
|
||||
lcd->print("Safe not locked!");
|
||||
wait(DELAY_MISMATCH, SAFE_UNLOCKED);
|
||||
}
|
||||
}
|
||||
|
||||
else if (state == SAFE_UNLOCKED_DOLOCK)
|
||||
{
|
||||
lcd->clear();
|
||||
lcd->setCursor(5, 0);
|
||||
lcd->write(ICON_UNLOCKED_CHAR);
|
||||
lcd->print(" ");
|
||||
lcd->write(ICON_RIGHT_ARROW);
|
||||
lcd->print(" ");
|
||||
lcd->write(ICON_LOCKED_CHAR);
|
||||
|
||||
light->lock();
|
||||
safeState->lock();
|
||||
wait(DELAY_LOCK, CHECK_LOCKED);
|
||||
}
|
||||
|
||||
else if (state == WAIT_SCREEN_PROGRESS)
|
||||
{
|
||||
uint32_t total = wait_till - wait_from;
|
||||
uint32_t passed = millis() - wait_from;
|
||||
uint32_t wait_step_ms = total / DELAY_STEPS;
|
||||
if (passed > wait_step * wait_step_ms)
|
||||
{
|
||||
wait_step++;
|
||||
lcd->print("#");
|
||||
}
|
||||
|
||||
if (millis() > wait_till)
|
||||
setState(state_after_break);
|
||||
}
|
||||
|
||||
else if (state == WAIT_SCREEN_NONBLOCKING)
|
||||
{
|
||||
if (millis() > wait_till)
|
||||
setState(state_after_break);
|
||||
}
|
||||
|
||||
else if (state == INPUT_PIN)
|
||||
{
|
||||
{
|
||||
char key = keypad->getKey();
|
||||
if (key != NO_KEY)
|
||||
idle_since = millis();
|
||||
|
||||
if (key >= '0' && key <= '9')
|
||||
{
|
||||
tone->beep(KEY_BEEP_MS, 2000);
|
||||
lcd->print('*');
|
||||
pin[pin_keys] = key;
|
||||
pin_keys++;
|
||||
}
|
||||
|
||||
if (pin_keys >= PIN_LENGTH)
|
||||
setState(state_after_pin);
|
||||
}
|
||||
}
|
||||
|
||||
else if (state == SAFE_UNLOCKED_ASKKEY)
|
||||
{
|
||||
auto key = keypad->getKey();
|
||||
if (key != NO_KEY)
|
||||
idle_since = millis();
|
||||
|
||||
if (key == 'A' || key == '#')
|
||||
{
|
||||
tone->beep(KEY_BEEP_MS, 2000);
|
||||
if (key == 'A' || newCodeNeeded)
|
||||
setState(SAFE_UNLOCKED_INPUTKEY);
|
||||
else
|
||||
setState(SAFE_UNLOCKED_DOLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
lcd->setBacklight(idle_since + IDLE_TIMEOUT > millis());
|
||||
}
|
||||
|
||||
void UI::wait(uint32_t how_long, State state_after, bool showProgress)
|
||||
{
|
||||
wait_step = 0;
|
||||
wait_from = millis();
|
||||
wait_till = wait_from + how_long;
|
||||
state_after_break = state_after;
|
||||
if (showProgress)
|
||||
{
|
||||
lcd->setCursor(0, 1);
|
||||
lcd->print(" [ ] ");
|
||||
lcd->setCursor(4, 1);
|
||||
setState(WAIT_SCREEN_PROGRESS);
|
||||
}
|
||||
else
|
||||
{
|
||||
setState(WAIT_SCREEN_NONBLOCKING);
|
||||
}
|
||||
};
|
||||
|
||||
void UI::askpin(State state_after)
|
||||
{
|
||||
// todo: 4 symbols only?
|
||||
lcd->setCursor(5, 1);
|
||||
lcd->print("[____]");
|
||||
lcd->setCursor(6, 1);
|
||||
|
||||
pin_keys = 0;
|
||||
state_after_pin = state_after;
|
||||
setState(INPUT_PIN);
|
||||
}
|
76
firmware/t1616-password-entry/src/main.cpp
Normal file
76
firmware/t1616-password-entry/src/main.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <Wire.h>
|
||||
|
||||
/* Display */
|
||||
#include <LiquidCrystal_I2C.h>
|
||||
|
||||
#define PIN_LCD_EN 2
|
||||
#define PIN_LCD_RW 1
|
||||
#define PIN_LCD_RES 0
|
||||
#define PIN_LCD_LED 3
|
||||
#define PIN_LCD_D4 4
|
||||
#define PIN_LCD_D5 5
|
||||
#define PIN_LCD_D6 6
|
||||
#define PIN_LCD_D7 7
|
||||
|
||||
#define LCD_I2C_ADDR 0x3f
|
||||
|
||||
LiquidCrystal_I2C _lcd(
|
||||
LCD_I2C_ADDR, PIN_LCD_EN, PIN_LCD_RW, PIN_LCD_RES,
|
||||
PIN_LCD_D4, PIN_LCD_D5, PIN_LCD_D6, PIN_LCD_D7,
|
||||
PIN_LCD_LED, POSITIVE);
|
||||
|
||||
/* Keypad setup */
|
||||
#include <Keypad.h>
|
||||
|
||||
const byte KEYPAD_ROWS = 4;
|
||||
const byte KEYPAD_COLS = 4;
|
||||
byte rowPins[KEYPAD_ROWS] = {PIN_PC1, PIN_PC0, PIN_PB0, PIN_PB1};
|
||||
byte colPins[KEYPAD_COLS] = {PIN_PB2, PIN_PB3, PIN_PB4, PIN_PB5};
|
||||
char keys[KEYPAD_ROWS][KEYPAD_COLS] = {
|
||||
{'1', '2', '3', 'A'},
|
||||
{'4', '5', '6', 'B'},
|
||||
{'7', '8', '9', 'C'},
|
||||
{'*', '0', '#', 'D'}};
|
||||
|
||||
Keypad _keypad = Keypad(makeKeymap(keys), rowPins, colPins, KEYPAD_ROWS, KEYPAD_COLS);
|
||||
|
||||
/* SafeState stores the secret code in EEPROM */
|
||||
#include "SafeState.h"
|
||||
SafeState _safeState;
|
||||
|
||||
#include "Tone.h"
|
||||
Tone _tone;
|
||||
|
||||
#include "Light.h"
|
||||
Light _light(&_tone);
|
||||
|
||||
#include "Icons.h"
|
||||
Icons _icons(&_lcd);
|
||||
|
||||
#include "UI.h"
|
||||
UI _ui(&_lcd, &_keypad, &_safeState, &_light, &_tone);
|
||||
|
||||
void setup()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
Serial.begin(115200);
|
||||
Serial.println("Started");
|
||||
#endif
|
||||
Wire.swap();
|
||||
|
||||
_lcd.begin(16, 2);
|
||||
|
||||
/* Make sure the physical lock is sync with the EEPROM state */
|
||||
if (_safeState.locked())
|
||||
_light.lock();
|
||||
else
|
||||
_light.unlock();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
_ui.loop();
|
||||
_tone.loop();
|
||||
_light.loop();
|
||||
delay(50);
|
||||
}
|
@@ -18,7 +18,7 @@ upload_protocol = jtag2updi
|
||||
|
||||
[env:tiny1616-serialupdi]
|
||||
board = ATtiny1616
|
||||
upload_speed = 57600
|
||||
upload_speed = 230400
|
||||
upload_flags =
|
||||
--tool
|
||||
uart
|
||||
|
Reference in New Issue
Block a user