summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSadeep Madurange <sadeep@asciimx.com>2025-09-07 17:04:34 +0800
committerSadeep Madurange <sadeep@asciimx.com>2025-09-07 17:04:34 +0800
commit276856de6c63bbbf3e56cc08dcca00ba10080b7e (patch)
treec68484bc312cc3a8abcea4fa9f4948a6f1f65cc6
parentf4b0b734a595919cf451ab9448b06274c8e609a4 (diff)
downloadsmart-home-276856de6c63bbbf3e56cc08dcca00ba10080b7e.tar.gz
Door lock with MOSFETs and and without RFM.HEADmaster
-rw-r--r--door_lock/Makefile47
-rw-r--r--door_lock/README.txt48
-rw-r--r--door_lock/fpm.c297
-rw-r--r--door_lock/fpm.h46
-rw-r--r--door_lock/main.c287
-rw-r--r--door_lock/uart.c31
-rw-r--r--door_lock/uart.h10
-rw-r--r--door_lock_rfm/Client.Makefile (renamed from lock/Client.Makefile)0
-rw-r--r--door_lock_rfm/Server.Makefile (renamed from lock/Server.Makefile)0
-rw-r--r--door_lock_rfm/client.c (renamed from lock/client.c)0
-rw-r--r--door_lock_rfm/fpm.c (renamed from lock/fpm.c)0
-rw-r--r--door_lock_rfm/fpm.h (renamed from lock/fpm.h)0
-rw-r--r--door_lock_rfm/nrfm.c (renamed from lock/nrfm.c)0
-rw-r--r--door_lock_rfm/nrfm.h (renamed from lock/nrfm.h)0
-rw-r--r--door_lock_rfm/server.c (renamed from lock/server.c)0
-rw-r--r--door_lock_rfm/uart.h (renamed from lock/uart.h)0
-rw-r--r--door_lock_rfm/util.c (renamed from lock/util.c)0
-rw-r--r--door_lock_rfm/util.h (renamed from lock/util.h)0
18 files changed, 766 insertions, 0 deletions
diff --git a/door_lock/Makefile b/door_lock/Makefile
new file mode 100644
index 0000000..ee25780
--- /dev/null
+++ b/door_lock/Makefile
@@ -0,0 +1,47 @@
+CC = avr-gcc
+MCU = atmega328p
+PORT = /dev/cuaU0
+TARGET = app
+
+SRC = main.c uart.c fpm.c
+OBJ = $(SRC:.c=.o)
+
+CFLAGS = -std=gnu99
+CFLAGS += -Os
+CFLAGS += -Wall
+CFLAGS += -mmcu=$(MCU)
+CFLAGS += -DBAUD=57600
+CFLAGS += -DF_CPU=8000000UL
+CFLAGS += -DFPM_PWD=$(FPM_PWD)
+CFLAGS += -ffunction-sections -fdata-sections
+
+LDFLAGS = -mmcu=$(MCU)
+LDFLAGS += -Wl,--gc-sections
+
+HEX_FLAGS = -O ihex
+HEX_FLAGS += -j .text -j .data
+
+AVRDUDE_FLAGS = -p $(MCU)
+AVRDUDE_FLAGS += -c arduino
+AVRDUDE_FLAGS += -P $(PORT)
+AVRDUDE_FLAGS += -b 57600
+AVRDUDE_FLAGS += -D -U
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+elf: $(OBJ)
+ $(CC) $(LDFLAGS) $(OBJ) -o $(TARGET).elf
+
+hex: elf
+ avr-objcopy $(HEX_FLAGS) $(TARGET).elf $(TARGET).hex
+
+upload: hex
+ avrdude $(AVRDUDE_FLAGS) flash:w:$(TARGET).hex:i
+
+.PHONY: clean
+
+clean:
+ rm -f *.o *.elf *.hex
+
+
diff --git a/door_lock/README.txt b/door_lock/README.txt
new file mode 100644
index 0000000..08f320f
--- /dev/null
+++ b/door_lock/README.txt
@@ -0,0 +1,48 @@
+CURRENT MEASUREMENTS
+
+PERIPHERALS:
+
+R503 FPM:
+
+When VCC is connected to a 3.31V supply, FPM draws 13.8mA of quiescent current. When
+VCC is disconnected (3.3VT connected to 3.31V supply), it draws 2.9uA.
+
+FS5106B high-torque servo:
+
+One of them draws 6.1mA when connected and stabilizes at 4.6mA. The other drew
+variable amounts initially (4.7mA, 5mA+, but mostly 4.7). Both draw 4.6mA of
+quiescent current with a 5.07V supply.
+
+TARGET CURRENT DRAW
+
+ 1. 2.7mA for 1 month
+ 2. 1.4mA for 2 months
+ 3. 900uA for 3 months
+ 3. 694uA for 4 months
+
+LINEAR REGULATORS
+
+ 1. When the ATmega328P is in normal mode, the MCU + FPM + Servo + linear
+ regulators draw 30.6mA of quiescent current at 5.07V.
+ 2. When the ATmega328P is in power down mode, the MCU + FPM + Servo + linear
+ regulators draw 26.2mA of quiescent current at 5.07V.
+ 3. When the ATmega328P is in power down mode, the MCU + linear regulators draw
+ 13.7mA of quiescent current at 5.07V (without MOSFETS).
+
+MP1584EN BUCK CONVERTER
+
+ 1. When the system is in power down mode, the MCU + FPM + Servo draw 8.9mA at
+ 3.3V. The real amount is likely about 8.9 + 4.6 = 13.5mA.
+ 2. When FETs are used quiescent current drops to 0uA.
+ 3. Active current draw (when the servo rotates) measured are 152.8mA, 162.8mA,
+ 170.3mA
+
+REMARKS
+
+ 1. Can't use 3.3V FPM modules with Arduino Uno. The RX and TX lines have
+ resistors.
+ 2. When measuring current with the buck chips on, the servo doesn't respond.
+ So, the value measured probably doesn't include the current the servo
+ draws.
+ 3. N-channel high-side switching doesn't work without a 9V supply. 7.2V+ with
+ batteries don't work either.
diff --git a/door_lock/fpm.c b/door_lock/fpm.c
new file mode 100644
index 0000000..862b938
--- /dev/null
+++ b/door_lock/fpm.c
@@ -0,0 +1,297 @@
+#include <util/delay.h>
+
+#include "fpm.h"
+#include "uart.h"
+
+#define MAXPDLEN 64
+
+#define HEADER_HO 0xEF
+#define HEADER_LO 0x01
+#define ADDR 0xFFFFFFFF
+
+#define OK 0x00
+#define PACKID 0x01
+
+static inline uint8_t read(void)
+{
+ return uart_recv();
+}
+
+static inline void write(uint8_t c)
+{
+ uart_send(c);
+}
+
+static inline void send(uint8_t *data, uint8_t n)
+{
+ int i;
+ uint16_t pktlen, sum;
+
+ write(HEADER_HO);
+ write(HEADER_LO);
+
+ write((uint8_t)(ADDR >> 24));
+ write((uint8_t)(ADDR >> 16));
+ write((uint8_t)(ADDR >> 8));
+ write((uint8_t)(ADDR & 0xFF));
+
+ write(PACKID);
+
+ pktlen = n + 2;
+ write((uint8_t)(pktlen >> 8));
+ write((uint8_t)pktlen);
+
+ sum = (pktlen >> 8) + (pktlen & 0xFF) + PACKID;
+ for (i = 0; i < n; i++) {
+ write(data[i]);
+ sum += data[i];
+ }
+
+ write((uint8_t)(sum >> 8));
+ write((uint8_t)sum);
+}
+
+static inline uint16_t recv(uint8_t buf[MAXPDLEN])
+{
+ int i;
+ uint16_t len;
+ uint8_t byte;
+
+ i = 0, len = 0;
+
+ for (;;) {
+ byte = read();
+ switch (i) {
+ case 0:
+ if (byte != HEADER_HO)
+ continue;
+ break;
+ case 1:
+ if (byte != HEADER_LO)
+ return 0;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ // address
+ break;
+ case 6:
+ // packet id
+ break;
+ case 7:
+ len = (uint16_t)byte << 8;
+ break;
+ case 8:
+ len |= byte;
+ break;
+ default:
+ if ((i - 9) < MAXPDLEN) {
+ buf[i - 9] = byte;
+ if ((i - 8) == len)
+ return len;
+ } else
+ return 0;
+ break;
+ }
+ i++;
+ }
+ return 0;
+}
+
+void fpm_led(LED_CTRL ctrl, LED_COLOR color, uint8_t count)
+{
+ uint8_t buf[MAXPDLEN];
+
+ buf[0] = 0x35;
+ buf[1] = ctrl;
+ buf[2] = 0x60;
+ buf[3] = color;
+ buf[4] = count;
+
+ send(buf, 5);
+ recv(buf);
+}
+
+static inline uint8_t check_pwd(void)
+{
+ uint8_t buf[MAXPDLEN];
+
+ buf[0] = 0x13;
+ buf[1] = (uint8_t)((uint32_t)FPM_PWD >> 24);
+ buf[2] = (uint8_t)((uint32_t)FPM_PWD >> 16);
+ buf[3] = (uint8_t)((uint32_t)FPM_PWD >> 8);
+ buf[4] = (uint8_t)((uint32_t)FPM_PWD & 0xFF);
+
+ send(buf, 5);
+ recv(buf);
+ return buf[0] == OK;
+}
+
+static inline uint8_t scan(void)
+{
+ uint16_t retries;
+ uint8_t buf[MAXPDLEN];
+
+ retries = 0;
+ fpm_led(BREATHE, PURPLE, 0);
+
+ do {
+ buf[0] = 0x28;
+ send(buf, 1);
+ recv(buf);
+ if (buf[0] != OK) {
+ retries++;
+ _delay_ms(100);
+ }
+ } while(buf[0] != OK && retries < 100);
+
+ fpm_led(GRAD_OFF, PURPLE, 0);
+ return buf[0] == OK;
+}
+
+static inline uint8_t img2tz(uint8_t bufid)
+{
+ uint8_t buf[MAXPDLEN];
+
+ buf[0] = 0x02;
+ buf[1] = bufid;
+ send(buf, 2);
+ recv(buf);
+ return buf[0] == OK;
+}
+
+uint8_t fpm_init(void)
+{
+ return check_pwd();
+}
+
+uint8_t fpm_get_cfg(struct fpm_cfg *cfg)
+{
+ uint16_t n;
+ uint8_t buf[MAXPDLEN];
+
+ buf[0] = 0x0F;
+ send(buf, 1);
+ n = recv(buf);
+
+ if (buf[0] == OK && n >= 17) {
+ cfg->status = ((uint16_t)buf[1] << 8) | buf[2];
+ cfg->sysid = ((uint16_t)buf[3] << 8) | buf[4];
+ cfg->cap = ((uint16_t)buf[5] << 8) | buf[6];
+ cfg->sec_level = ((uint16_t)buf[7] << 8) | buf[8];
+ cfg->addr[0] = buf[9];
+ cfg->addr[1] = buf[10];
+ cfg->addr[2] = buf[11];
+ cfg->addr[3] = buf[12];
+ cfg->pkt_size = ((uint16_t)buf[13] << 8) | buf[14];
+ cfg->pkt_size = 1 << (cfg->pkt_size + 5);
+ cfg->baud = (((uint16_t)buf[15] << 8) | buf[16]);
+
+ return 1;
+ }
+ return 0;
+}
+
+uint8_t fpm_set_pwd(uint32_t pwd)
+{
+ uint8_t buf[MAXPDLEN];
+
+ buf[0] = 0x12;
+ buf[1] = (uint8_t)(pwd >> 24);
+ buf[2] = (uint8_t)(pwd >> 16);
+ buf[3] = (uint8_t)(pwd >> 8);
+ buf[4] = (uint8_t)(pwd & 0xFF);
+
+ send(buf, 5);
+ recv(buf);
+ return buf[0] == OK;
+}
+
+uint16_t fpm_get_count(void)
+{
+ uint16_t n, count;
+ uint8_t buf[MAXPDLEN];
+
+ buf[0] = 0x1D;
+ send(buf, 1);
+ n = recv(buf);
+
+ count = 0;
+ if (buf[0] == OK && n >= 2) {
+ count = buf[1];
+ count <<= 8;
+ count |= buf[2];
+ }
+ return count;
+}
+
+uint8_t fpm_enroll(void)
+{
+ struct fpm_cfg cfg;
+ uint16_t n;
+ uint8_t buf[MAXPDLEN];
+
+ fpm_get_cfg(&cfg);
+ n = fpm_get_count() + 1;
+ if (n >= cfg.cap)
+ return 0;
+
+ if (!scan())
+ return 0;
+
+ if (!img2tz(1))
+ return 0;
+
+ _delay_ms(2000);
+
+ if (!scan())
+ return 0;
+
+ if (!img2tz(2))
+ return 0;
+
+ buf[0] = 0x05;
+ send(buf, 1);
+ recv(buf);
+ if (buf[0] != OK)
+ return 0;
+
+ buf[0] = 0x06;
+ buf[1] = 1;
+ buf[2] = (uint8_t)(n >> 8);
+ buf[3] = (uint8_t)(n & 0xFF);
+ send(buf, 4);
+ recv(buf);
+
+ return buf[0] == OK;
+}
+
+uint16_t fpm_match(void)
+{
+ struct fpm_cfg cfg;
+ uint8_t buf[MAXPDLEN];
+
+ if (!fpm_get_cfg(&cfg))
+ return 0;
+
+ if (!scan())
+ return 0;
+
+ if (!img2tz(1))
+ return 0;
+
+ buf[0] = 0x04;
+ buf[1] = 1;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+ buf[4] = (uint8_t)(cfg.cap >> 8);
+ buf[5] = (uint8_t)(cfg.cap & 0xFF);
+
+ send(buf, 6);
+ recv(buf);
+
+ if (buf[0] != OK)
+ return 0;
+
+ return ((uint16_t)buf[1] << 8) | buf[2];
+}
diff --git a/door_lock/fpm.h b/door_lock/fpm.h
new file mode 100644
index 0000000..f424c56
--- /dev/null
+++ b/door_lock/fpm.h
@@ -0,0 +1,46 @@
+#ifndef FPM_R503_H
+#define FPM_R503_H
+
+#include <stdint.h>
+
+struct fpm_cfg {
+ uint16_t status;
+ uint16_t sysid;
+ uint16_t cap;
+ uint16_t sec_level;
+ uint8_t addr[4];
+ uint16_t pkt_size;
+ uint16_t baud;
+};
+
+typedef enum {
+ NIL = 0x00,
+ RED = 0x01,
+ BLUE = 0x02,
+ PURPLE = 0x03
+} LED_COLOR;
+
+typedef enum {
+ BREATHE = 0x01,
+ FLASH = 0x02,
+ ON = 0x03,
+ OFF = 0x04,
+ GRAD_ON = 0x05,
+ GRAD_OFF = 0x06,
+} LED_CTRL;
+
+uint8_t fpm_init(void);
+
+uint8_t fpm_get_cfg(struct fpm_cfg *cfg);
+
+uint8_t fpm_set_pwd(uint32_t pwd);
+
+uint16_t fpm_get_count(void);
+
+uint8_t fpm_enroll(void);
+
+uint16_t fpm_match(void);
+
+void fpm_led(LED_CTRL ctrl, LED_COLOR color, uint8_t count);
+
+#endif /* FPM_R50_H */
diff --git a/door_lock/main.c b/door_lock/main.c
new file mode 100644
index 0000000..caab488
--- /dev/null
+++ b/door_lock/main.c
@@ -0,0 +1,287 @@
+#include <stdint.h>
+#include <avr/wdt.h>
+#include <avr/sleep.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+
+#include "fpm.h"
+#include "uart.h"
+
+#define BAT_MIN 5100
+
+#define SRVO_PIN PB1
+#define SRVO_DDR DDRB
+
+#define PWM_MIN 500
+#define PWM_MID 1600
+#define PWM_MAX 2550
+#define PWM_TOP 19999
+
+#define LED_FPM_PIN PD5
+#define LED_BACK_PIN PD6
+#define LED_DDR DDRD
+#define LED_PORT PORTD
+
+#define PWR_BAT PB2
+#define PWR_SRVO PB3
+#define PWR_FPM PB4
+#define PWR_DDR DDRB
+#define PWR_PORT PORTB
+
+#define FPM_UNLOCK_PIN PC1 /*back up for FPM's touch sensor */
+#define FPM_LOCK_PIN PC2
+#define ENROLL_PIN PC3
+#define BACK_LOCK_PIN PC4
+#define BACK_UNLOCK_PIN PC5
+#define INPUT_DDR DDRC
+#define INPUT_PORT PORTC
+
+#define FPM_UNLOCK_INT PCINT9
+#define FPM_LOCK_INT PCINT10
+#define ENROLL_INT PCINT11
+#define BACK_LOCK_INT PCINT12
+#define BACK_UNLOCK_INT PCINT13
+#define INPUT_INT PCIE1
+#define INPUT_INT_MSK PCMSK1
+#define INPUT_INT_VEC PCINT1_vect
+
+enum CTRL {
+ NONE = 0,
+ LOCK_FPM = 1,
+ LOCK_BACK = 2,
+ UNLOCK_FPM = 3,
+ UNLOCK_BACK = 4,
+ UNLOCK_FPM_2 = 5,
+ ENROLL = 6
+};
+
+static volatile enum CTRL cmd = NONE;
+
+static inline void pwron_bat(void)
+{
+ PWR_PORT &= ~(1 << PWR_BAT);
+}
+
+static inline void pwroff_bat(void)
+{
+ PWR_PORT |= (1 << PWR_BAT);
+}
+
+static inline void pwron_fpm(void)
+{
+ PWR_PORT &= ~(1 << PWR_FPM);
+ _delay_ms(50);
+}
+
+static inline void pwroff_fpm(void)
+{
+ PWR_PORT |= (1 << PWR_FPM);
+}
+
+static inline void pwron_srvo(void)
+{
+ PWR_PORT |= (1 << PWR_SRVO);
+}
+
+static inline void pwroff_srvo(void)
+{
+ PWR_PORT &= ~(1 << PWR_SRVO);
+}
+
+static inline void lock(void)
+{
+ pwron_srvo();
+ OCR1A = PWM_MID;
+ _delay_ms(500);
+ pwroff_srvo();
+}
+
+static inline void unlock(void)
+{
+ pwron_srvo();
+ OCR1A = PWM_MAX;
+ _delay_ms(500);
+ pwroff_srvo();
+}
+
+static inline void flash_led(void)
+{
+ TCCR0A = (1 << COM0A0) | (1 << COM0B0) | (1 << WGM01);
+ OCR0A = 255;
+ OCR0B = 255;
+ TCCR0B = (1 << CS02) | (1 << CS00);
+}
+
+static inline void stop_led(void)
+{
+ TCCR0B = 0;
+ TCCR0A = 0;
+ LED_PORT &= ~((1 << LED_FPM_PIN) | (1 << LED_BACK_PIN));
+}
+
+static void check_bat(void)
+{
+ uint16_t vbg, vcc;
+
+ pwron_bat();
+
+ ADMUX |= (1 << REFS1) | (1 << REFS0);
+ ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1); /* clk: 50-200 kHz */
+
+ _delay_us(500); /* https://www.sciencetronics.com/greenphotons/?p=1521 */
+
+ ADCSRA |= (1 << ADSC);
+ while (ADCSRA & (1 << ADSC))
+ ;
+
+ ADCSRA &= ~(1 << ADEN);
+ vbg = (1100UL * ADC) / 1024;
+ ADCSRA &= ~(1 << ADEN);
+
+ pwroff_bat();
+
+ vcc = (vbg * 66) / 10; /* 56k/10k divider */
+ if (vcc < BAT_MIN)
+ flash_led();
+}
+
+int main(void)
+{
+ uint16_t id;
+
+ /* disable wdt */
+ cli();
+ wdt_reset();
+ MCUSR &= ~(1 << WDRF);
+ WDTCSR |= (1 << WDCE) | (1 << WDE);
+ WDTCSR = 0x00;
+
+ PWR_DDR |= (1 << PWR_BAT) | (1 << PWR_FPM) | (1 << PWR_SRVO);
+ pwroff_bat();
+
+ uart_init();
+ pwron_fpm();
+ fpm_init();
+
+ /* servo */
+ TCCR1A |= (1 << WGM11);
+ TCCR1B |= (1 << WGM12) | (1 << WGM13);
+ TCCR1B |= (1 << CS11);
+ ICR1 = PWM_TOP;
+ TCCR1A |= (1 << COM1A1);
+ SRVO_DDR |= (1 << SRVO_PIN);
+
+ /* bat check */
+ LED_DDR |= (1 << LED_FPM_PIN) | (1 << LED_BACK_PIN);
+ LED_PORT &= ~((1 << LED_FPM_PIN) | (1 << LED_BACK_PIN));
+
+ DDRD &= ~(1 << PD2); /* FPM unlock pin */
+ PORTD |= (1 << PD2); /* FPM unlock pin internal pull-up */
+ EICRA = 0b00000000;
+ EIMSK = (1 << INT0); /* FPM unlock interrupt */
+
+ INPUT_DDR &= ~((1 << FPM_LOCK_PIN) | (1 << FPM_UNLOCK_PIN) |
+ (1 << BACK_LOCK_PIN) | (1 << BACK_UNLOCK_PIN) |
+ (1 << ENROLL_PIN));
+
+ INPUT_PORT |= ((1 << FPM_LOCK_PIN) | (1 << FPM_UNLOCK_PIN) |
+ (1 << BACK_LOCK_PIN) | (1 << BACK_UNLOCK_PIN) |
+ (1 << ENROLL_PIN));
+
+ PCICR |= (1 << INPUT_INT);
+ INPUT_INT_MSK |= ((1 << FPM_LOCK_INT) | (1 << FPM_UNLOCK_INT) |
+ (1 << BACK_LOCK_INT) | (1 << BACK_UNLOCK_INT) |
+ (1 << ENROLL_INT));
+
+ for (;;) {
+ check_bat();
+
+ switch(cmd) {
+ case LOCK_FPM:
+ lock();
+ fpm_led(FLASH, RED, 1);
+ break;
+ case LOCK_BACK:
+ lock();
+ break;
+ case UNLOCK_FPM:
+ case UNLOCK_FPM_2:
+ if (fpm_match()) {
+ fpm_led(BREATHE, BLUE, 1);
+ unlock();
+ } else {
+ fpm_led(BREATHE, RED, 1);
+ }
+ break;
+ case UNLOCK_BACK:
+ unlock();
+ fpm_led(FLASH, BLUE, 1);
+ break;
+ case ENROLL:
+ id = fpm_match();
+ if (id == 1 || id == 2) {
+ fpm_led(BREATHE, BLUE, 1);
+ _delay_ms(1000);
+ if (fpm_enroll())
+ fpm_led(BREATHE, BLUE, 1);
+ else
+ fpm_led(BREATHE, RED, 1);
+ }
+ break;
+ default:
+ break;
+ }
+
+ cmd = NONE;
+ _delay_ms(500);
+
+ pwroff_fpm();
+ stop_led();
+
+ set_sleep_mode(SLEEP_MODE_PWR_DOWN);
+ sleep_enable();
+ sleep_bod_disable();
+ sei();
+ sleep_cpu();
+
+ cli();
+ sleep_disable();
+ pwron_fpm();
+ fpm_init();
+ }
+ return 0;
+}
+
+static inline int is_pressed(uint8_t btn)
+{
+ if (!((PINC >> btn) & 0x01)) {
+ _delay_ms(50);
+ return !((PINC >> btn) & 0x01);
+ }
+ return 0;
+}
+
+ISR(INT0_vect)
+{
+ cmd = UNLOCK_FPM;
+}
+
+ISR(INPUT_INT_VEC)
+{
+ cli();
+
+ if (is_pressed(FPM_LOCK_PIN))
+ cmd = LOCK_FPM;
+ else if (is_pressed(FPM_UNLOCK_PIN))
+ cmd = UNLOCK_FPM_2;
+ else if (is_pressed(BACK_LOCK_PIN))
+ cmd = LOCK_BACK;
+ else if (is_pressed(BACK_UNLOCK_PIN))
+ cmd = UNLOCK_BACK;
+ else if (is_pressed(ENROLL_PIN))
+ cmd = ENROLL;
+ else
+ cmd = NONE;
+
+ sei();
+}
diff --git a/door_lock/uart.c b/door_lock/uart.c
new file mode 100644
index 0000000..1a9956d
--- /dev/null
+++ b/door_lock/uart.c
@@ -0,0 +1,31 @@
+#include <avr/io.h>
+#include <util/setbaud.h>
+
+#include "uart.h"
+
+void uart_init(void)
+{
+ UBRR0H = UBRRH_VALUE;
+ UBRR0L = UBRRL_VALUE;
+#if USE_2X
+ UCSR0A |= (1 << U2X0);
+#else
+ UCSR0A &= ~(1 << U2X0);
+#endif
+ UCSR0B = (1 << TXEN0) | (1 << RXEN0);
+ UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
+}
+
+uint8_t uart_recv(void)
+{
+ while (!(UCSR0A & (1 << RXC0)))
+ ;
+ return UDR0;
+}
+
+void uart_send(uint8_t c)
+{
+ while (!(UCSR0A & (1 << UDRE0)))
+ ;
+ UDR0 = c;
+}
diff --git a/door_lock/uart.h b/door_lock/uart.h
new file mode 100644
index 0000000..c5fc87e
--- /dev/null
+++ b/door_lock/uart.h
@@ -0,0 +1,10 @@
+#ifndef UART_H
+#define UART_H
+
+void uart_init(void);
+
+uint8_t uart_recv(void);
+
+void uart_send(uint8_t c);
+
+#endif /* UART_H */
diff --git a/lock/Client.Makefile b/door_lock_rfm/Client.Makefile
index a6758fd..a6758fd 100644
--- a/lock/Client.Makefile
+++ b/door_lock_rfm/Client.Makefile
diff --git a/lock/Server.Makefile b/door_lock_rfm/Server.Makefile
index f853a34..f853a34 100644
--- a/lock/Server.Makefile
+++ b/door_lock_rfm/Server.Makefile
diff --git a/lock/client.c b/door_lock_rfm/client.c
index 91fe040..91fe040 100644
--- a/lock/client.c
+++ b/door_lock_rfm/client.c
diff --git a/lock/fpm.c b/door_lock_rfm/fpm.c
index 73a175a..73a175a 100644
--- a/lock/fpm.c
+++ b/door_lock_rfm/fpm.c
diff --git a/lock/fpm.h b/door_lock_rfm/fpm.h
index 31f53eb..31f53eb 100644
--- a/lock/fpm.h
+++ b/door_lock_rfm/fpm.h
diff --git a/lock/nrfm.c b/door_lock_rfm/nrfm.c
index d1bb5c3..d1bb5c3 100644
--- a/lock/nrfm.c
+++ b/door_lock_rfm/nrfm.c
diff --git a/lock/nrfm.h b/door_lock_rfm/nrfm.h
index 52d4edb..52d4edb 100644
--- a/lock/nrfm.h
+++ b/door_lock_rfm/nrfm.h
diff --git a/lock/server.c b/door_lock_rfm/server.c
index 6d51c00..6d51c00 100644
--- a/lock/server.c
+++ b/door_lock_rfm/server.c
diff --git a/lock/uart.h b/door_lock_rfm/uart.h
index a88a3c6..a88a3c6 100644
--- a/lock/uart.h
+++ b/door_lock_rfm/uart.h
diff --git a/lock/util.c b/door_lock_rfm/util.c
index ec5369e..ec5369e 100644
--- a/lock/util.c
+++ b/door_lock_rfm/util.c
diff --git a/lock/util.h b/door_lock_rfm/util.h
index 84584aa..84584aa 100644
--- a/lock/util.h
+++ b/door_lock_rfm/util.h