nullcon2023-chall-megavault

Atmega32u4-based vault keypad hardware challenge for NullCon 2023 Berlin
git clone https://git.sinitax.com/sinitax/nullcon2023-chall-megavault
Log | Files | Refs | sfeed.txt

sketch.ino (6240B)


      1#include <LCD_I2C.h>
      2
      3#define ARRSIZE(x) (sizeof(x)/sizeof((x)[0]))
      4#define BITAT(x) (1 << (x))
      5
      6#define BTN_COUNT 18
      7#define FEEL_DELAY 5
      8#define BLINK_LED 17
      9
     10enum { DISABLED, KEY, FUNC };
     11
     12enum {
     13	FUNC_NONE,
     14	FUNC_BS,
     15	FUNC_ENTER,
     16	FUNC_LEFT,
     17	FUNC_RIGHT,
     18	FUNC_HOME,
     19	FUNC_DEL
     20};
     21
     22enum {
     23	R1C1, R1C2, R1C3, R1C4,
     24	R2C1, R2C2, R2C3, R2C4,
     25	R3C1, R3C2, R3C3, R3C4,
     26	R4C1, R4C2, R4C3,
     27	  R5C12,    R5C3, R45C4
     28};
     29
     30enum {
     31	NUMLOCK  = 1 << 0,
     32};
     33
     34struct keycfg {
     35	uint8_t type;
     36	char c;
     37};
     38
     39const char *bnames[BTN_COUNT] = {
     40	"R1C1", "R1C2", "R1C3", "R1C4",
     41	"R2C1", "R2C2", "R2C3", "R2C4",
     42	"R3C1", "R3C2", "R3C3", "R3C4",
     43	"R4C1", "R4C2", "R4C3",
     44	   "R5C12",     "R5C3", "R45C4"
     45};
     46
     47const uint8_t pulsepins[] = { A2, A3, 9, A1, 8, A0 };
     48const uint8_t sensepins[] = { 6, 14, 4, 5, 7, 16 };
     49const uint8_t debugswitch = 10;
     50
     51const uint8_t senselut[][6][2] = {
     52	{ {14, R2C1 }, {16, R3C1}, { 4, R4C1} },
     53	{ { 6, R1C2 }, {14, R2C2}, {16, R3C2}, {4, R4C2}, {5, R5C12} },
     54	{ { 6, R1C3 }, {14, R2C3}, {16, R3C3}, {4, R4C3}, {5, R5C3}, {7, R2C4} },
     55	{ {16, R1C4 } },
     56	{ { 6, R1C1 } },
     57	{ { 4, R45C4}, {14, R3C4} }
     58};
     59
     60const uint8_t modkeys[] = { R1C1 };
     61
     62const struct keycfg base_layer[BTN_COUNT] = {
     63	[R1C1]  = { DISABLED },
     64	[R1C2]  = { KEY, '/' },
     65	[R1C3]  = { KEY, '*' },
     66	[R1C4]  = { FUNC, FUNC_BS },
     67	[R2C1]  = { KEY, '7' },
     68	[R2C2]  = { KEY, '8' },
     69	[R2C3]  = { KEY, '9' },
     70	[R2C4]  = { KEY, '-' },
     71	[R3C1]  = { KEY, '4' },
     72	[R3C2]  = { KEY, '5' },
     73	[R3C3]  = { KEY, '6' },
     74	[R3C4]  = { KEY, '+' },
     75	[R4C1]  = { KEY, '1' },
     76	[R4C2]  = { KEY, '2' },
     77	[R4C3]  = { KEY, '3' },
     78	[R5C12] = { KEY, '0' },
     79	[R5C3]  = { KEY, '.' },
     80	[R45C4] = { FUNC, FUNC_ENTER }
     81};
     82
     83const struct keycfg numlock_layer[BTN_COUNT] = {
     84	[R1C1]  = { DISABLED },
     85	[R1C2]  = { KEY, '/' },
     86	[R1C3]  = { KEY, '*' },
     87	[R1C4]  = { FUNC, FUNC_BS },
     88	[R2C1]  = { FUNC, FUNC_HOME },
     89	[R2C2]  = { DISABLED },
     90	[R2C3]  = { DISABLED },
     91	[R2C4]  = { KEY, '-' },
     92	[R3C1]  = { FUNC, FUNC_LEFT },
     93	[R3C2]  = { DISABLED },
     94	[R3C3]  = { FUNC, FUNC_RIGHT },
     95	[R3C4]  = { KEY, '+' },
     96	[R4C1]  = { DISABLED },
     97	[R4C2]  = { DISABLED },
     98	[R4C3]  = { DISABLED },
     99	[R5C12] = { DISABLED },
    100	[R5C3]  = { FUNC, FUNC_DEL },
    101	[R45C4] = { FUNC, FUNC_ENTER }
    102};
    103
    104const struct keycfg *layers[1 << ARRSIZE(modkeys)] = {
    105	[0] = base_layer,
    106	[NUMLOCK] = numlock_layer
    107};
    108
    109char pin[9] = { 0 };
    110
    111char linebuf[256] = { 0 };
    112
    113int16_t inputpos = 0;
    114char inputbuf[17] = { 0 };
    115char outputbuf[17] = { 0 };
    116
    117int debugmode = 0;
    118
    119int pbstates[BTN_COUNT] = { 0 };
    120int bstates[BTN_COUNT] = { 0 };
    121
    122LCD_I2C lcd(0x27, 16, 2);
    123
    124void
    125lcd_refresh(void)
    126{
    127	lcd.clear();
    128	lcd.noCursor();
    129	lcd.setCursor(0, 0);
    130	lcd.print(inputbuf);
    131	lcd.setCursor(0, 1);
    132	lcd.print(outputbuf);
    133	lcd.setCursor(inputpos, 0);
    134	lcd.cursor();
    135}
    136
    137void
    138call_func(char c)
    139{
    140	switch (c) {
    141	case FUNC_BS:
    142		input_del();
    143		break;
    144	case FUNC_ENTER:
    145		input_enter();
    146		break;
    147	case FUNC_LEFT:
    148		inputpos--;
    149		lcd_refresh();
    150		break;
    151	case FUNC_RIGHT:
    152		inputpos++;
    153		lcd_refresh();
    154		break;
    155	case FUNC_HOME:
    156		inputpos = 0;
    157		lcd_refresh();
    158		break;
    159	case FUNC_DEL:
    160		inputbuf[inputpos] = 0;
    161		lcd_refresh();
    162	}
    163}
    164
    165void
    166input_put(char c)
    167{
    168	if (inputpos >= 15)
    169		return;
    170	Serial.println("PUT");
    171
    172	inputbuf[inputpos] = c;
    173	inputpos++;
    174	lcd_refresh();
    175}
    176
    177void
    178input_del(void)
    179{
    180	int i;
    181
    182	if (!inputpos)
    183		return;
    184
    185	for (i = inputpos; i < sizeof(inputbuf); i++)
    186		inputbuf[i-1] = inputbuf[i];
    187	inputpos--;
    188	lcd_refresh();
    189}
    190
    191void
    192input_enter(void)
    193{
    194	if (!strncmp(inputbuf, pin, sizeof(inputbuf))) {
    195		comm_open();
    196	} else {
    197		snprintf(outputbuf, sizeof(outputbuf), "wrong!");
    198	}
    199	lcd_refresh();
    200	memset(outputbuf, 0, sizeof(outputbuf));
    201}
    202
    203void
    204blink(uint32_t ms)
    205{
    206	digitalWrite(BLINK_LED, LOW);
    207	delay(ms);
    208	digitalWrite(BLINK_LED, HIGH);
    209	delay(ms);
    210}
    211
    212void
    213comm_init(void)
    214{
    215	size_t len;
    216
    217	while (!Serial.available());
    218
    219	while (1) {
    220		memset(linebuf, 0, sizeof(linebuf));
    221		len = Serial.readBytesUntil('\r', linebuf, sizeof(linebuf));
    222		if (!len) continue;
    223		if (debugmode) {
    224			Serial.print("<");
    225			Serial.println(linebuf);
    226		}
    227		if (!strncmp(linebuf, "!INIT", len))
    228			break;
    229	}
    230
    231	Serial.println("!OK");
    232
    233	blink(1000);
    234}
    235
    236void
    237comm_open(void)
    238{
    239	size_t len;
    240
    241	blink(1000);
    242
    243	Serial.println("!FLAG");
    244	memset(linebuf, 0, sizeof(linebuf));
    245	len = Serial.readBytesUntil('\r', linebuf, sizeof(linebuf));
    246	if (debugmode) {
    247		Serial.print("<");
    248		Serial.println(linebuf);
    249	}
    250
    251	if (!strncmp(linebuf, "!OK", len)) {
    252		len = Serial.readBytesUntil('\r', linebuf, sizeof(linebuf));
    253		strncpy(outputbuf, linebuf, sizeof(outputbuf));
    254	} else {
    255		snprintf(outputbuf, sizeof(outputbuf), "error..");
    256	}
    257}
    258
    259void
    260getstates(void)
    261{
    262	int i, k;
    263
    264	memcpy(pbstates, bstates, sizeof(bstates));
    265	memset(bstates, 0, sizeof(bstates));
    266
    267	for (i = 0; i < ARRSIZE(pulsepins); i++) {
    268		for (k = 0; k < ARRSIZE(pulsepins); k++) {
    269			pinMode(pulsepins[k], k == i ? OUTPUT: INPUT);
    270			digitalWrite(pulsepins[k], LOW);
    271		}
    272		delay(FEEL_DELAY);
    273
    274		for (k = 0; k < 6 && senselut[i][k][0]; k++) {
    275			if (digitalRead(senselut[i][k][0]) == LOW)
    276				bstates[senselut[i][k][1]] = true;
    277		}
    278	}
    279}
    280
    281void
    282setup(void)
    283{
    284	int i;
    285
    286	snprintf(pin, 9, "13371337");
    287
    288	for (i = 0; i < ARRSIZE(sensepins); i++) {
    289		pinMode(sensepins[i], INPUT);
    290		digitalWrite(sensepins[i], HIGH);
    291	}
    292
    293	pinMode(debugswitch, INPUT);
    294	digitalWrite(debugswitch, HIGH);
    295	delay(10);
    296	debugmode = (digitalRead(debugswitch) == LOW);
    297
    298	lcd.begin();
    299	lcd_refresh();
    300
    301	lcd.noBacklight();
    302
    303	Serial.begin(9600);
    304	Serial.setTimeout(2000);
    305	comm_init();
    306
    307	lcd.backlight();
    308}
    309
    310void
    311loop(void)
    312{
    313	int i, k, layer;
    314	bool hit;
    315
    316	getstates();
    317
    318	layer = 0;
    319	for (i = 0; i < ARRSIZE(modkeys); i++)
    320		layer |= bstates[modkeys[i]] * (1 << i);
    321
    322	hit = false;
    323	for (i = 0; i < BTN_COUNT; i++) {
    324		if (bstates[i] && !pbstates[i]) {
    325			hit = true;
    326			if (debugmode) {
    327				Serial.print("PRESS: ");
    328				Serial.println(bnames[i]);
    329			}
    330			if (layers[layer]) {
    331				if (layers[layer][i].type == KEY) {
    332					input_put(layers[layer][i].c);
    333				} else if (layers[layer][i].type == FUNC) {
    334					call_func(layers[layer][i].c);
    335				}
    336			}
    337		} else if (!bstates[i] && pbstates[i]) {
    338			if (debugmode) {
    339				Serial.print("RELEASE: ");
    340				Serial.println(bnames[i]);
    341			}
    342		}
    343	}
    344
    345	if (debugmode && hit) {
    346		Serial.print("POS ");
    347		Serial.println(inputpos);
    348	}
    349}