| Secure door lock with arduino and rolling code/code hopping with AES |
![]() |
| Rolling codes on arduino with a unique pattern send with every key press |
There is a pairing option to sync the controller and the remote. Once synced, the remote receiver will respond to the new commands and discards any used messages. In addition there is a safety window to avoid the syncing issues from accidental key presses. (Read keeloq from microchip for simple alternatives)
Wiring and Construction
Wiring is pretty simple. For the transmit:
Connect the rf modules as usual (vcc to 5volt, gnd to -ve and the DATA (ATAD) to digital pin 7. Connect the pin 8 to 3.3volt pin (removing this connection starts the pairing mode)
| RF modules and tx /rx sections |
Receiver is in the same way. Connect the data pin to A0 of an other arduino and the vcc / Gnd as described above for the transmit module. I used a keypad shield with lcd for showing the messages. But you can remove the lcd and comment the codes (use the serial monitor for debugging). Press any of the button on keypad shield to pair and sync with the remote
Source code for the transmitter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | // Secure tx with code hopping using AES // garage remote etc // 128bit key // credits to avr crypto library for aes // More details at http://riyas.org #define DEBUG_ENABLED #include <avr/eeprom.h> #include <VirtualWire.h> #include <AESLib.h> #pragma pack 1 typedef struct { unsigned long serial; long counter; char command; long extra; int code; char termin; } PackedData; typedef struct { long counter; unsigned long serial; int magic; } PairingData; typedef struct { long counter; unsigned long serial; uint8_t skey[16]; } Settings; #define SERIAL_NUMBER 123456 #define PMAGIC 555 #define STATUS_LED 13 #define PAIR_BUTTON 8 #define TX_PIN 7 #define SEND_BUTTON 9 uint8_t key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; //specific key for the fob PackedData sData; PairingData pData; unsigned char msg[16]; long key_counter; void pair(void) { long start =millis(); int ledState = LOW; long previousMillis = 0; // will store last time LED was updated do{ //sent the serial number and counter for syncing int len=sizeof(pData); vw_send((uint8_t *)&pData, len); // Send via rf vw_wait_tx(); delay(1000); unsigned long currentMillis = millis(); if(currentMillis - previousMillis > 100) { previousMillis = currentMillis; if (ledState == LOW) ledState = HIGH; else ledState = LOW; digitalWrite(STATUS_LED, ledState); } } while ((millis()-start)<10000); //for 5 seconds } void setup() { #ifdef DEBUG_ENABLED Serial.begin(9600); #endif pinMode(PAIR_BUTTON,INPUT_PULLUP); //switch for pairing pinMode(SEND_BUTTON,INPUT_PULLUP); //switch for sending pinMode(STATUS_LED,OUTPUT); pinMode(PAIR_BUTTON,INPUT); //switch for pairing vw_set_ptt_inverted(true); // vw_set_tx_pin(TX_PIN); vw_setup(4000);// speed of data transfer Kbps //read the stored counter & eepromdata eeprom_read_block((void*)&pData, (void*)0, sizeof(pData)); //if serial number is not set if(pData.serial!=SERIAL_NUMBER) { //in case eeprom is empty/garbage as in first try pData.counter=1000;//random(10000,99999); pData.magic=PMAGIC; pData.serial=SERIAL_NUMBER; } sData.code=PMAGIC; sData.serial=SERIAL_NUMBER; } void loop(){ if (digitalRead(PAIR_BUTTON)==0) { //pairing loop pair(); } if (digitalRead(SEND_BUTTON)==0) { //send the command sender(); } delay(1000); } void sender(){ //increment the counter pData.counter=pData.counter+1; sData.counter=pData.counter; //save the counter eeprom_write_block((const void*)&pData, (void*)0, sizeof(pData)); // Set the commands based on key // for testing i kept it 'a' but do a loop with analogue/digital read to get key state sData.command='a'; digitalWrite(STATUS_LED,1); int len = sizeof(sData); aes128_enc_single(key, &sData); #ifdef DEBUG_ENABLED Serial.print("Encrypted:"); memcpy(msg, &sData, len); for(int k=0;k<16;k++) Serial.print(msg[k],HEX); Serial.println(""); Serial.print("KEY:"); for(int k=0;k<len;k++) Serial.print(key[k],HEX); Serial.println(""); #endif vw_send((uint8_t *)&sData, len); // Send the 16 bytes vw_wait_tx(); aes128_dec_single(key, &sData); PackedData * sdData = (PackedData *)&sData; #ifdef DEBUG_ENABLED Serial.print("Test Decrypt:"); Serial.print(sdData->serial); Serial.println(""); #endif digitalWrite(STATUS_LED,0); } //END |
Source code for the receiver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | #include <avr/eeprom.h> #include <LiquidCrystal.h> #include <VirtualWire.h> #include <AESLib.h> const int SAFEWINDOW = 50; long lastcount; char *controller; unsigned char msg[16]; uint8_t key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; #pragma pack 1 //uint8_t key[16]; typedef struct { unsigned long serial; long counter; char command; long extra; int code; char termin; } PackedData; typedef struct { long counter; unsigned long serial; uint8_t skey[16]; } Settings; typedef struct { long counter; unsigned long serial; int magic; } PairingData; // select the pins used on the LCD panel LiquidCrystal lcd(8, 9, 4, 5, 6, 7); Settings StoredSettings; int errorcounter=0; int adc_key_in; // read the buttons int read_LCD_buttons() { adc_key_in = analogRead(0); // read the value from the sensor if (adc_key_in < 50) return 1;// btnRIGHT; if (adc_key_in < 250) return 2;// btnUP; if (adc_key_in < 450) return 3;//btnDOWN; if (adc_key_in < 650) return 4;//btnLEFT; if (adc_key_in < 850) return 5;//btnSELECT; return 0;//btnNONE; // when all others fail, return this... } void setup() { Serial.begin(9600); lcd.begin(16, 2); // start the library //memcpy (&StoredSettings.skey, &key[0], sizeof(key)); //eeprom_write_block((const void*)&StoredSettings, (void*)0, sizeof(StoredSettings)); delay(1000); eeprom_read_block((void*)&StoredSettings, (void*)0, sizeof(StoredSettings)); //memcpy (&key[0],&StoredSettings.skey, sizeof(key)); Serial.print("EEPROMKEY:"); for(int k=0;k<16;k++) Serial.print(StoredSettings.skey[k],HEX); Serial.println(""); lcd.setCursor(0,1); lcd.print(StoredSettings.counter); // print a simple message lcd.setCursor(0,0); lcd.print("SN: "); // print a simple message lcd.setCursor(4,0); lcd.print(StoredSettings.serial); pinMode(2, OUTPUT); vw_set_ptt_inverted(true); // Required for DR3100 vw_set_rx_pin(A5); vw_setup(4000); // Bits per sec vw_rx_start(); } void pairing(void) { uint8_t buf[VW_MAX_MESSAGE_LEN]; uint8_t buflen = VW_MAX_MESSAGE_LEN; lcd.setCursor(0,0); lcd.print(" Pairing! "); long start =millis(); int ledState = LOW; long previousMillis = 0; // will store last time LED was updated do{ //do pairing process and store serial and counter if (vw_get_message(buf, &buflen)) // Non-blocking { lcd.clear(); PairingData * pairData = (PairingData *)buf; if(pairData->magic==555){ lcd.clear(); lcd.setCursor(0,0); lcd.print(pairData->serial); lcd.setCursor(0,1); lcd.print(pairData->counter); StoredSettings.counter=pairData->counter; StoredSettings.serial=pairData->serial; eeprom_write_block((const void*)&StoredSettings, (void*)0, sizeof(StoredSettings)); delay(2000); lcd.clear(); lcd.setCursor(0,0); lcd.print(" Done!"); lcd.setCursor(0,1); lcd.print(" Synced"); delay(2000); break; } else { lcd.clear(); lcd.setCursor(0,0); lcd.print(" Error!"); lcd.setCursor(0,1); lcd.print("Try again!"); } } delay(100); //end of pairing unsigned long currentMillis = millis(); if(currentMillis - previousMillis > 100) { previousMillis = currentMillis; if (ledState == LOW) ledState = HIGH; else ledState = LOW; digitalWrite(2, ledState); } } while ((millis()-start)<10000); //for 5 seconds } void loop() { if (read_LCD_buttons()!=0) { pairing(); } uint8_t buf[VW_MAX_MESSAGE_LEN]; uint8_t buflen = VW_MAX_MESSAGE_LEN; if (vw_get_message(buf, &buflen)) // Non-blocking { Serial.print("Encrypted:"); memcpy(msg, buf, buflen); for(int k=0;k<16;k++) Serial.print(msg[k],HEX); Serial.println(""); Serial.print("KEY:"); for(int k=0;k<buflen;k++) Serial.print(key[k],HEX); Serial.println(""); aes128_dec_single(key, msg); PackedData * sdData = (PackedData *)msg; Serial.print("Test Decrypt:"); Serial.print(sdData->serial); Serial.println(""); lcd.clear(); lcd.setCursor(0,0); lcd.print(sdData->serial); lcd.setCursor(0,1); lcd.print(sdData->counter); digitalWrite(2,1); //for red led long currentcounter; if(sdData->code==555){ lcd.clear(); lcd.setCursor(0,0); lcd.print(sdData->serial); lcd.setCursor(0,1); lcd.print(sdData->counter); //do the job if the counter is with in safety window currentcounter= sdData->counter; if((currentcounter-StoredSettings.counter)<SAFEWINDOW && (currentcounter-StoredSettings.counter)>0) { lcd.setCursor(0,0); lcd.print("Access Granted!"); StoredSettings.counter=sdData->counter; eeprom_write_block((const void*)&StoredSettings, (void*)0, sizeof(StoredSettings)); //do the stuff here for eg running the servo or door lock motor char command = sdData->command; switch (command) { case 'a': lcd.clear(); lcd.setCursor(0,0); lcd.print("OPENING DOOR"); //do the servo here break; case 'b': lcd.clear(); lcd.setCursor(0,0); lcd.print("CLOSING DOOR"); //do the servo here break; case 'c': lcd.clear(); lcd.setCursor(0,0); lcd.print("NEXT THING"); //do the servo here break; } } else { errorcounter++; lcd.clear(); lcd.setCursor(0,0); lcd.print("Needs Pairing!"); lcd.setCursor(8,1); lcd.print(errorcounter); lcd.setCursor(12,1); lcd.print(StoredSettings.counter); lcd.setCursor(0,1); lcd.print(currentcounter); } } else { lcd.clear(); lcd.setCursor(0,0); lcd.setCursor(0,0); lcd.print(" Error!"); lcd.setCursor(0,1); lcd.print(" Wrong Key??"); } delay(100); digitalWrite(2,0); } delay(100); //just here to slow down a bit } |













