diff options
| author | Sadeep Madurange <sadeep@asciimx.com> | 2025-09-07 17:04:34 +0800 |
|---|---|---|
| committer | Sadeep Madurange <sadeep@asciimx.com> | 2025-09-07 17:04:34 +0800 |
| commit | 276856de6c63bbbf3e56cc08dcca00ba10080b7e (patch) | |
| tree | c68484bc312cc3a8abcea4fa9f4948a6f1f65cc6 /door_lock | |
| parent | f4b0b734a595919cf451ab9448b06274c8e609a4 (diff) | |
| download | smart-home-276856de6c63bbbf3e56cc08dcca00ba10080b7e.tar.gz | |
Diffstat (limited to 'door_lock')
| -rw-r--r-- | door_lock/Makefile | 47 | ||||
| -rw-r--r-- | door_lock/README.txt | 48 | ||||
| -rw-r--r-- | door_lock/fpm.c | 297 | ||||
| -rw-r--r-- | door_lock/fpm.h | 46 | ||||
| -rw-r--r-- | door_lock/main.c | 287 | ||||
| -rw-r--r-- | door_lock/uart.c | 31 | ||||
| -rw-r--r-- | door_lock/uart.h | 10 |
7 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 */ |
