diff options
Diffstat (limited to 'door_lock_rfm')
| -rw-r--r-- | door_lock_rfm/Client.Makefile | 46 | ||||
| -rw-r--r-- | door_lock_rfm/Server.Makefile | 44 | ||||
| -rw-r--r-- | door_lock_rfm/client.c | 198 | ||||
| -rw-r--r-- | door_lock_rfm/fpm.c | 324 | ||||
| -rw-r--r-- | door_lock_rfm/fpm.h | 38 | ||||
| -rw-r--r-- | door_lock_rfm/nrfm.c | 303 | ||||
| -rw-r--r-- | door_lock_rfm/nrfm.h | 21 | ||||
| -rw-r--r-- | door_lock_rfm/server.c | 191 | ||||
| -rw-r--r-- | door_lock_rfm/uart.h | 8 | ||||
| -rw-r--r-- | door_lock_rfm/util.c | 89 | ||||
| -rw-r--r-- | door_lock_rfm/util.h | 27 |
11 files changed, 1289 insertions, 0 deletions
diff --git a/door_lock_rfm/Client.Makefile b/door_lock_rfm/Client.Makefile new file mode 100644 index 0000000..a6758fd --- /dev/null +++ b/door_lock_rfm/Client.Makefile @@ -0,0 +1,46 @@ +CC = avr-gcc +MCU = atmega328p +PORT = /dev/cuaU0 +TARGET = client + +SRC = client.c fpm.c nrfm.c util.c +OBJ = $(SRC:.c=.o) + +CFLAGS = -std=gnu99 +CFLAGS += -Os +CFLAGS += -Wall +CFLAGS += -mmcu=$(MCU) +CFLAGS += -DBAUD=57600 +CFLAGS += -DF_CPU=16000000UL +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 += -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_rfm/Server.Makefile b/door_lock_rfm/Server.Makefile new file mode 100644 index 0000000..f853a34 --- /dev/null +++ b/door_lock_rfm/Server.Makefile @@ -0,0 +1,44 @@ +CC = avr-gcc +MCU = atmega328p +PORT = /dev/cuaU0 +TARGET = server + +SRC = server.c nrfm.c util.c +OBJ = $(SRC:.c=.o) + +CFLAGS = -std=gnu99 +CFLAGS += -Os +CFLAGS += -Wall +CFLAGS += -mmcu=$(MCU) +CFLAGS += -DF_CPU=16000000UL +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 += -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_rfm/client.c b/door_lock_rfm/client.c new file mode 100644 index 0000000..91fe040 --- /dev/null +++ b/door_lock_rfm/client.c @@ -0,0 +1,198 @@ +/* Lock front, connected to the fingerprint scanner */ + +#include <stdint.h> +#include <stdlib.h> + +#include <avr/wdt.h> +#include <avr/sleep.h> +#include <avr/interrupt.h> +#include <util/delay.h> + +#include "fpm.h" +#include "nrfm.h" +#include "util.h" + +#define LOCK_PIN PD3 +#define UNLOCK_PIN PD2 +#define ENROLL_PIN PD4 + +#define RX_IRQ_PIN PC0 +#define RX_DDR DDRC +#define RX_PIN PINC +#define RX_ICR PCICR +#define RX_IE PCIE1 +#define RX_INT PCINT8 +#define RX_MSK PCMSK1 +#define RX_INTVEC PCINT1_vect + +#define VCC_MIN 4000 + +static volatile uint8_t rxd = 0; +static volatile uint8_t sync = 0; +static volatile uint8_t islock = 0; +static volatile uint8_t isunlock = 0; + +static inline void wdt_off(void) +{ + cli(); + wdt_reset(); + MCUSR &= ~(1 << WDRF); + WDTCSR |= (1 << WDCE) | (1 << WDE); + WDTCSR = 0x00; +} + +static inline void init_rx(void) +{ + RX_DDR &= ~(1 << RX_IRQ_PIN); + RX_ICR |= (1 << RX_IE); + RX_MSK |= (1 << RX_INT); +} + +static inline void init_btns(void) +{ + DDRD &= ~((1 << LOCK_PIN) | (1 << UNLOCK_PIN) | (1 << ENROLL_PIN)); + PORTD |= ((1 << LOCK_PIN) | (1 << UNLOCK_PIN) | (1 << ENROLL_PIN)); + + EICRA = 0b00000000; + EIMSK = (1 << INT0) | (1 << INT1); + + PCICR |= (1 << PCIE2); + PCMSK2 |= (1 << PCINT20); +} + +static inline void fpm_ok(void) +{ + fpm_led_on(BLUE); + _delay_ms(500); + fpm_led_off(); +} + +static inline void fpm_nok(void) +{ + fpm_led_on(RED); + _delay_ms(500); + fpm_led_off(); +} + +int main(void) +{ + int i, retries; + uint8_t rxaddr[ADDRLEN] = { 194, 178, 82 }; + uint8_t txaddr[ADDRLEN] = { 194, 178, 83 }; + + char buf[WDLEN], key[WDLEN]; + + wdt_off(); + led_init(); + + fpm_init(); + if (fpm_get_count() == 0) + fpm_enroll(); + + init_rx(); + init_btns(); + + radio_init(rxaddr); + + sei(); + radio_listen(); + + for (;;) { + if (!sync && (islock || isunlock)) { + if (isunlock) { + if (!fpm_match()) { + isunlock = 0; + fpm_nok(); + continue; + } + else + fpm_ok(); + } + + xor(KEY, SYN, buf, WDLEN); + retries = 0; + do { + sync = radio_sendto(txaddr, buf, WDLEN); + retries++; + } while (!sync && retries < 2); + + if (!sync) { + islock = 0; + isunlock = 0; + } + } + + if (rxd) { + radio_recv(buf, WDLEN); + rxd = 0; + if (sync && (islock || isunlock)) { + sync = 0; + xor(KEY, buf, key, WDLEN); + if (islock) { + islock = 0; + xor(key, LOCK, buf, WDLEN); + if (radio_sendto(txaddr, buf, WDLEN)) + led_locked(); + } else if (isunlock) { + isunlock = 0; + xor(key, UNLOCK, buf, WDLEN); + if (radio_sendto(txaddr, buf, WDLEN)) + led_unlocked(); + } + } + } + + if (!sync) { + if (getvcc() < VCC_MIN) { + for (i = 0; i < 5; i++) { + led_bat(); + _delay_ms(100); + } + } + + radio_pwr_dwn(); + sleep_bod_disable(); + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + sleep_mode(); + radio_listen(); + } + } + return 0; +} + +ISR(RX_INTVEC) +{ + if (!(RX_PIN & (1 << RX_IRQ_PIN))) + rxd = 1; +} + +ISR(INT0_vect) +{ + sync = 0; + isunlock = 1; +} + +ISR(INT1_vect) +{ + if (is_btn_pressed(PIND, LOCK_PIN)) { + sync = 0; + islock = 1; + } +} + +ISR(PCINT2_vect) +{ + uint16_t id; + + if (is_btn_pressed(PIND, ENROLL_PIN)) { + id = fpm_match(); + if (id == 1 || id == 2) { + fpm_ok(); + _delay_ms(1000); + if (fpm_enroll()) + fpm_ok(); + } else + fpm_nok(); + } +} + diff --git a/door_lock_rfm/fpm.c b/door_lock_rfm/fpm.c new file mode 100644 index 0000000..73a175a --- /dev/null +++ b/door_lock_rfm/fpm.c @@ -0,0 +1,324 @@ +#include <avr/io.h> +#include <util/delay.h> +#include <util/setbaud.h> + +#include "fpm.h" + +#define MAXPDLEN 64 +#define RST_DELAY_MS 500 + +#define HEADER_HO 0xEF +#define HEADER_LO 0x01 +#define ADDR 0xFFFFFFFF + +#define OK 0x00 +#define PACKID 0x01 + +static inline uint8_t read(void) +{ + while (!(UCSR0A & (1 << RXC0))) + ; + return UDR0; +} + +static inline void write(uint8_t c) +{ + while (!(UCSR0A & (1 << UDRE0))) + ; + UDR0 = 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: + // toss address + break; + case 6: + // toss 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; +} + +static inline void led_ctrl(uint8_t mode, COLOR color) +{ + uint8_t buf[MAXPDLEN]; + + buf[0] = 0x35; + buf[1] = mode; + buf[2] = 0x60; + buf[3] = color; + buf[4] = 0x00; + + 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; + led_ctrl(0x01, PURPLE); + + do { + buf[0] = 0x28; + send(buf, 1); + recv(buf); + if (buf[0] != OK) { + retries++; + _delay_ms(100); + } + } while(buf[0] != OK && retries < 100); + + led_ctrl(0x06, PURPLE); + 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; +} + +void fpm_led_on(COLOR color) +{ + led_ctrl(0x03, color); +} + +void fpm_led_off(void) +{ + led_ctrl(0x04, 0x00); +} + +uint8_t fpm_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); + + _delay_ms(RST_DELAY_MS); + 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_rfm/fpm.h b/door_lock_rfm/fpm.h new file mode 100644 index 0000000..31f53eb --- /dev/null +++ b/door_lock_rfm/fpm.h @@ -0,0 +1,38 @@ +#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 { + RED = 0x01, + BLUE = 0x02, + PURPLE = 0x03 +} COLOR; + +uint8_t fpm_init(void); + +uint8_t fpm_get_cfg(struct fpm_cfg *cfg); + +uint8_t fpm_set_pwd(uint32_t pwd); + +void fpm_led_on(COLOR color); + +void fpm_led_off(void); + +uint16_t fpm_get_count(void); + +uint8_t fpm_enroll(void); + +uint16_t fpm_match(void); + +#endif /* FPM_R50_H */ diff --git a/door_lock_rfm/nrfm.c b/door_lock_rfm/nrfm.c new file mode 100644 index 0000000..d1bb5c3 --- /dev/null +++ b/door_lock_rfm/nrfm.c @@ -0,0 +1,303 @@ +#include <stdio.h> +#include <stdlib.h> + +#include <avr/io.h> +#include <avr/interrupt.h> +#include <util/delay.h> + +#include "nrfm.h" + +#define SPI_SS PB2 +#define SPI_SCK PB5 +#define SPI_MISO PB4 +#define SPI_MOSI PB3 +#define SPI_DDR DDRB +#define SPI_PORT PORTB + +#define NRF_CE PC1 +#define NRF_CE_DDR DDRC +#define NRF_CE_PORT PORTC + +#define NOP 0xFF +#define R_REGISTER 0x1F +#define W_REGISTER 0x20 + +#define PWR_UP 1 +#define PRIM_RX 0 + +#define MODCHG_DELAY_MS 5 + +#define LEN(a) (sizeof(a) / sizeof(a[0])) + +const char *bittab[16] = { + [ 0] = "0000", [ 1] = "0001", [ 2] = "0010", [ 3] = "0011", + [ 4] = "0100", [ 5] = "0101", [ 6] = "0110", [ 7] = "0111", + [ 8] = "1000", [ 9] = "1001", [10] = "1010", [11] = "1011", + [12] = "1100", [13] = "1101", [14] = "1110", [15] = "1111", +}; + +static inline uint8_t read_reg(uint8_t reg) +{ + SPI_PORT &= ~(1 << SPI_SS); + SPDR = reg & R_REGISTER; + while (!(SPSR & (1 << SPIF))) + ; + SPDR = NOP; + while (!(SPSR & (1 << SPIF))) + ; + SPI_PORT |= (1 << SPI_SS); + return SPDR; +} + +static inline void write_reg(uint8_t reg, uint8_t val) +{ + SPI_PORT &= ~(1 << SPI_SS); + SPDR = (reg & 0x1F) | W_REGISTER; + while (!(SPSR & (1 << SPIF))) + ; + SPDR = val; + while (!(SPSR & (1 << SPIF))) + ; + SPI_PORT |= (1 << SPI_SS); +} + +static inline void read_reg_bulk(uint8_t reg, uint8_t *data, uint8_t n) +{ + uint8_t i; + + SPI_PORT &= ~(1 << SPI_SS); + SPDR = reg & R_REGISTER; + while (!(SPSR & (1 << SPIF))) + ; + for (i = 0; i < n; i++) { + SPDR = NOP; + while (!(SPSR & (1 << SPIF))) + ; + data[i] = SPDR; + } + SPI_PORT |= (1 << SPI_SS); +} + +static inline void setaddr(uint8_t reg, const uint8_t addr[ADDRLEN]) +{ + int i; + + SPI_PORT &= ~(1 << SPI_SS); + SPDR = (reg & 0x1F) | W_REGISTER; + while (!(SPSR & (1 << SPIF))) + ; + for (i = ADDRLEN - 1; i >= 0; i--) { + SPDR = addr[i]; + while (!(SPSR & (1 << SPIF))) + ; + } + SPI_PORT |= (1 << SPI_SS); +} + +static inline void reset_irqs(void) +{ + uint8_t rv; + + rv = read_reg(0x07); + if (rv != 0b00001110) + write_reg(0x07, 0b01111110); +} + +static inline void tx_mode(void) +{ + uint8_t rv; + + rv = read_reg(0x00); + if ((rv & 0x03) != 0x02) { + rv |= (1 << PWR_UP); + rv &= ~(1 << PRIM_RX); + write_reg(0x00, rv); + _delay_ms(MODCHG_DELAY_MS); + } +} + +static inline void rx_mode(void) +{ + uint8_t rv; + + rv = read_reg(0x00); + if ((rv & 0x03) != 0x02) { + rv |= (1 << PWR_UP); + rv |= (1 << PRIM_RX); + write_reg(0x00, rv); + _delay_ms(MODCHG_DELAY_MS); + } +} + +static inline void enable_chip(void) +{ + NRF_CE_PORT |= (1 << NRF_CE); + _delay_us(130); +} + +static inline void disable_chip(void) +{ + NRF_CE_PORT &= ~(1 << NRF_CE); +} + +static inline void flush_tx(void) +{ + SPI_PORT &= ~(1 << SPI_SS); + SPDR = 0b11100001; + while (!(SPSR & (1 << SPIF))) + ; + SPI_PORT |= (1 << SPI_SS); + + reset_irqs(); +} + +void radio_flush_rx(void) +{ + SPI_PORT &= ~(1 << SPI_SS); + SPDR = 0b11100010; + while (!(SPSR & (1 << SPIF))) + ; + SPI_PORT |= (1 << SPI_SS); + + reset_irqs(); +} + +static inline uint8_t rx_pdlen(void) +{ + SPI_PORT &= ~(1 << SPI_SS); + SPDR = 0b01100000; + while (!(SPSR & (1 << SPIF))) + ; + SPDR = NOP; + while (!(SPSR & (1 << SPIF))) + ; + SPI_PORT |= (1 << SPI_SS); + return SPDR; +} + +void radio_init(const uint8_t rxaddr[ADDRLEN]) +{ + SPI_DDR |= (1 << SPI_SS) | (1 << SPI_SCK) | (1 << SPI_MOSI); + SPI_PORT |= (1 << SPI_SS); + SPCR |= (1 << SPE) | (1 << MSTR); + + NRF_CE_DDR |= (1 << NRF_CE); + NRF_CE_PORT &= ~(1 << NRF_CE); + + _delay_ms(110); /* power on reset delay */ + + write_reg(0x00, 0b00111100); /* use 2-byte CRC, enable only the rx interrupt */ + write_reg(0x01, 0b00111111); /* enable auto ack on all pipes */ + write_reg(0x02, 0b00000011); /* enable rx address on pipes 0 and 1 */ + write_reg(0x03, 0b00000001); /* set address width to 3 bytes */ + write_reg(0x04, 0b11111111); /* 4000uS retransmission delay, 15 tries */ + write_reg(0x05, 0b01110011); /* use 2.515GHz channel */ + write_reg(0x06, 0b00001110); /* set data rate to 1Mbps */ + write_reg(0x1D, 0b00000100); /* enable dynamic payload length */ + write_reg(0x1C, 0b00111111); /* enable dynamic payload length for all pipes */ + + reset_irqs(); + setaddr(0x0B, rxaddr); /* pipe 1 for rx, pipe 0 for auto-ack */ +} + +void radio_listen(void) +{ + disable_chip(); + rx_mode(); + enable_chip(); +} + +void radio_pwr_dwn(void) +{ + uint8_t rv; + + disable_chip(); + rv = read_reg(0x00); + rv &= ~(1 << PWR_UP); + write_reg(0x00, rv); +} + +uint8_t radio_sendto(const uint8_t addr[ADDRLEN], const char *msg, uint8_t n) +{ + int i, imax; + uint8_t cfg, rv, maxrt, txds; + + disable_chip(); + + cfg = read_reg(0x00); + + tx_mode(); + flush_tx(); + + setaddr(0x10, addr); + setaddr(0x0A, addr); + + imax = n < MAXPDLEN ? n : MAXPDLEN; + + SPI_PORT &= ~(1 << SPI_SS); + SPDR = 0b10100000; + while (!(SPSR & (1 << SPIF))) + ; + for (i = 0; i < imax; i++) { + SPDR = msg[i]; + while (!(SPSR & (1 << SPIF))) + ; + } + SPI_PORT |= (1 << SPI_SS); + + enable_chip(); + _delay_us(12); + disable_chip(); + + txds = 0, maxrt = 0; + do { + rv = read_reg(0x07); + txds = rv & (1 << 5); + maxrt = rv & (1 << 4); + } while (txds == 0 && maxrt == 0); + + if (maxrt) + flush_tx(); + + // restore config, typically rx mode + write_reg(0x00, cfg); + enable_chip(); + return txds; +} + +uint8_t radio_recv(char *buf, uint8_t n) +{ + int readlen, pdlen, readmax; + + pdlen = 0; + disable_chip(); + + pdlen = rx_pdlen(); + if (pdlen == 0) { + radio_flush_rx(); + return 0; + } + + if (pdlen > MAXPDLEN) { + radio_flush_rx(); + return 0; + } + + readmax = n < pdlen ? n : pdlen; + + SPI_PORT &= ~(1 << SPI_SS); + SPDR = 0b01100001; + while (!(SPSR & (1 << SPIF))) + ; + for (readlen = 0; readlen < readmax; readlen++) { + SPDR = NOP; + while (!(SPSR & (1 << SPIF))) + ; + buf[readlen] = SPDR; + } + SPI_PORT |= (1 << SPI_SS); + + radio_flush_rx(); + enable_chip(); + return readlen; +} diff --git a/door_lock_rfm/nrfm.h b/door_lock_rfm/nrfm.h new file mode 100644 index 0000000..52d4edb --- /dev/null +++ b/door_lock_rfm/nrfm.h @@ -0,0 +1,21 @@ +#ifndef NRFM_H +#define NRFM_H + +#include <stdint.h> + +#define ADDRLEN 3 +#define MAXPDLEN 32 + +void radio_init(const uint8_t rxaddr[ADDRLEN]); + +void radio_listen(void); + +void radio_pwr_dwn(void); + +uint8_t radio_recv(char *buf, uint8_t n); + +void radio_flush_rx(void); + +uint8_t radio_sendto(const uint8_t addr[ADDRLEN], const char *msg, uint8_t n); + +#endif /* NRFM_H */ diff --git a/door_lock_rfm/server.c b/door_lock_rfm/server.c new file mode 100644 index 0000000..6d51c00 --- /dev/null +++ b/door_lock_rfm/server.c @@ -0,0 +1,191 @@ +/* Lock back, connected to the servo */ + +#include <stdint.h> +#include <string.h> + +#include <avr/wdt.h> +#include <avr/sleep.h> +#include <avr/interrupt.h> +#include <util/delay.h> + +#include "nrfm.h" +#include "util.h" + +#define PWM_MIN 500 +#define PWM_MID 1500 +#define PWM_MAX 2500 +#define PWM_TOP 20000 + +#define SERVO_PIN PB1 +#define LOCK_PIN PD3 +#define UNLOCK_PIN PD4 + +#define RX_IRQ_PIN PC0 +#define RX_DDR DDRC +#define RX_PIN PINC +#define RX_ICR PCICR +#define RX_IE PCIE1 +#define RX_INT PCINT8 +#define RX_MSK PCMSK1 +#define RX_INTVEC PCINT1_vect + +#define VCC_MIN 4000 + +static char tab[] = { + '0', '8', '3', '6', 'a', 'Z', '$', '4', 'v', 'R', '@', + 'E', '1', 'o', '#', ')', '2', '5', 'q', ';', '.', 'I', + 'c', '7', '9', '*', 'L', 'V', '&', 'k', 'K', '!', 'm', + 'N', '(', 'O', 'Q', 'A', '>', 'T', 't', '?', 'S', 'h', + 'w', '/', 'n', 'W', 'l', 'M', 'e', 'H', 'j', 'g', '[', + 'P', 'f', ':', 'B', ']', 'Y', '^', 'F', '%', 'C', 'x' +}; + +static volatile uint8_t rxd = 0; +static uint16_t tablen = sizeof(tab) / sizeof(tab[0]); + +static inline void keygen(char *buf, uint8_t n) +{ + int i, imax; + uint8_t sreg; + uint16_t seed; + + sreg = SREG; + cli(); + seed = TCNT1; + SREG = sreg; + + for (i = 0, imax = n - 1; i < imax; i++, seed++) + buf[i] = tab[(seed % tablen)]; + buf[imax] = '\0'; +} + +static inline void keydel(char *buf, uint8_t n) +{ + int i; + + for (i = 0; i < n; i++) + buf[i] = 0; +} + +static inline void init_wdt(void) +{ + cli(); + wdt_reset(); + + WDTCSR |= (1 << WDCE) | ( 1 << WDE); + WDTCSR = (1 << WDE) | (1 << WDP1); /* reset after 64ms */ +} + +static inline void init_rx(void) +{ + RX_DDR &= ~(1 << RX_IRQ_PIN); + RX_ICR |= (1 << RX_IE); + RX_MSK |= (1 << RX_INT); +} + +static inline void init_btns(void) +{ + DDRD &= ~((1 << LOCK_PIN) | (1 << UNLOCK_PIN)); + PORTD |= ((1 << LOCK_PIN) | (1 << UNLOCK_PIN)); + + EICRA = 0b00000000; + EIMSK = (1 << INT1); + + PCICR |= (1 << PCIE2); + PCMSK2 |= (1 << PCINT20); +} + +static inline void init_servo(void) +{ + ICR1 = PWM_TOP; + TCCR1A |= (1 << WGM11) | (1 << COM1A1); + TCCR1B |= (1 << WGM13) | (1 << CS11); + + DDRB |= (1 << SERVO_PIN); +} + +static inline void lock(void) +{ + OCR1A = PWM_MID; + _delay_ms(100); + OCR1A = PWM_TOP; +} + +static inline void unlock(void) +{ + OCR1A = PWM_MAX - 50; + _delay_ms(100); + OCR1A = PWM_TOP; +} + +int main(void) +{ + uint8_t rxaddr[ADDRLEN] = { 194, 178, 83 }; + uint8_t txaddr[ADDRLEN] = { 194, 178, 82 }; + + char buf[WDLEN], key[WDLEN], msg[WDLEN]; + + init_wdt(); + init_rx(); + init_btns(); + init_servo(); + + led_init(); + radio_init(rxaddr); + + sei(); + radio_listen(); + + for (;;) { + if (rxd) { + radio_recv(buf, WDLEN); + rxd = 0; + xor(KEY, buf, msg, WDLEN); + if (memcmp(msg, SYN, WDLEN) == 0) { + keygen(key, WDLEN); + xor(KEY, key, buf, WDLEN); + radio_sendto(txaddr, buf, WDLEN); + } else { + xor(key, buf, msg, WDLEN); + if (memcmp(msg, LOCK, WDLEN) == 0) { + lock(); + keydel(key, WDLEN); + } else if (memcmp(msg, UNLOCK, WDLEN) == 0) { + unlock(); + keydel(key, WDLEN); + } + } + } else { + radio_pwr_dwn(); + if (getvcc() < VCC_MIN) + led_bat(); + sleep_bod_disable(); + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + sleep_mode(); + } + } + return 0; +} + +ISR(RX_INTVEC) +{ + if (!(RX_PIN & (1 << RX_IRQ_PIN))) + rxd = 1; +} + +ISR(INT1_vect) +{ + if (is_btn_pressed(PIND, LOCK_PIN)) { + lock(); + led_locked(); + } +} + +ISR(PCINT2_vect) +{ + if (is_btn_pressed(PIND, UNLOCK_PIN)) { + unlock(); + led_unlocked(); + } +} + diff --git a/door_lock_rfm/uart.h b/door_lock_rfm/uart.h new file mode 100644 index 0000000..a88a3c6 --- /dev/null +++ b/door_lock_rfm/uart.h @@ -0,0 +1,8 @@ +#ifndef UART_H +#define UART_H + +void uart_init(void); +void uart_write(const char *s); +void uart_write_line(const char *s); + +#endif /* UART_H */ diff --git a/door_lock_rfm/util.c b/door_lock_rfm/util.c new file mode 100644 index 0000000..ec5369e --- /dev/null +++ b/door_lock_rfm/util.c @@ -0,0 +1,89 @@ +#include <avr/io.h> +#include <avr/interrupt.h> +#include <util/delay.h> + +#include "util.h" + +#define LOCK_LED PC3 +#define UNLOCK_LED PC4 +#define BATLOW_LED PC5 +#define LED_DDR DDRC +#define LED_PORT PORTC + +int is_btn_pressed(uint8_t pin, uint8_t btn) +{ + if (!((pin >> btn) & 0x01)) { + _delay_ms(100); + return !((pin >> btn) & 0x01); + } + return 0; +} + +void xor(const char *k, const char *s, char *d, uint8_t n) +{ + int i; + + for (i = 0; i < n; i++) + d[i] = s[i] ^ k[i]; +} + +/* Measure vcc by measuring known internal 1.1v bandgap + * reference voltage against AVCC. + * Place a 100nF bypass capacitor on AREF. + */ +uint16_t getvcc(void) +{ + uint16_t vcc; + + ADMUX |= (1 << REFS0); + ADMUX |= (1 << MUX3) | (1 << MUX2) | (1 << MUX1); + ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS0); + + // https://www.sciencetronics.com/greenphotons/?p=1521 + _delay_us(500); + + ADCSRA |= (1 << ADSC); + while (ADCSRA & (1 << ADSC)) + ; + vcc = (1100UL * 1023 / ADC); + + ADCSRA &= ~(1 << ADEN); + return vcc; +} + +void led_init(void) +{ + LED_DDR |= (1 << LOCK_LED) | (1 << UNLOCK_LED); + LED_DDR |= (1 << BATLOW_LED); + + LED_PORT &= ~(1 << LOCK_LED); + LED_PORT &= ~(1 << UNLOCK_LED); + LED_PORT &= ~(1 << BATLOW_LED); +} + +void led_locked(void) +{ + LED_PORT |= (1 << LOCK_LED); + _delay_ms(100); + LED_PORT &= ~(1 << LOCK_LED); + _delay_ms(100); + LED_PORT |= (1 << LOCK_LED); + _delay_ms(100); + LED_PORT &= ~(1 << LOCK_LED); +} + +void led_unlocked(void) +{ + LED_PORT |= (1 << UNLOCK_LED); + _delay_ms(70); + LED_PORT &= ~(1 << UNLOCK_LED); + _delay_ms(70); + LED_PORT |= (1 << UNLOCK_LED); + _delay_ms(70); + LED_PORT &= ~(1 << UNLOCK_LED); +} + +void led_bat(void) +{ + LED_PORT ^= (1 << BATLOW_LED); +} diff --git a/door_lock_rfm/util.h b/door_lock_rfm/util.h new file mode 100644 index 0000000..84584aa --- /dev/null +++ b/door_lock_rfm/util.h @@ -0,0 +1,27 @@ +#ifndef MY_UTIL_H +#define MY_UTIL_H + +#include <stdint.h> + +#define KEY "dM>}jdb,6gsnC$J^K 8(I5vyPemPs%;K" +#define SYN "dM>}jdb,6gsnC$J^K 8(I5vyPemPs%;O" +#define LOCK "R,I7l^E4j]KyLR9'*Q{Jd'zu.~!84}Ij" +#define UNLOCK "R,I7l^E4j]KyLR9'*Q{Jd'zu.~!84}IL" + +#define WDLEN 32 + +int is_btn_pressed(uint8_t pin, uint8_t btn); + +void xor(const char *k, const char *s, char *d, uint8_t n); + +uint16_t getvcc(void); + +void led_init(void); + +void led_locked(void); + +void led_unlocked(void); + +void led_bat(void); + +#endif /* MY_UTIL_H */ |
