Sambungkan lapan wayar keypad 4×4 ke pin D2–D9 Arduino Uno, baca kekunci dengan library Keypad, dan bandingkan kod yang ditaip dengan kod yang tersimpan dalam EEPROM — kalau padan, Uno hidupkan modul relay selama beberapa saat. Bahagian paling penting bukan kod yang betul, tetapi apa yang berlaku selepas tiga kali kod salah.
Kenapa kunci keypad, bukan RFID atau cap jari?
Keypad membran 4×4 memberi satu perkara yang kunci RFID RC522 dan kunci cap jari AS608 tidak boleh beri: kredensial yang boleh anda tukar dan kongsi dalam tiga puluh saat, tanpa menyerahkan apa-apa objek dan tanpa mendaftarkan sesiapa semula.
| Kredensial | Boleh tukar? | Boleh kongsi dari jauh? | Bila hilang atau bocor |
|---|---|---|---|
| Kod keypad | Ya, dalam beberapa saat | Ya — sebut sahaja | Tukar kod, selesai |
| Kad RFID | Ya, tapi perlu kad baharu | Tidak — kad kena diserah | Padam UID lama, keluarkan kad baharu |
| Cap jari | Tidak | Tidak | Tidak boleh ditarik balik |
Untuk projek RBT ada sebab tambahan: keypad membawa dua konsep yang boleh anda pertahankan semasa viva — imbasan matriks dan lockout (lihat juga idea projek elektronik RBT kami).
Senarai komponen
Parts list
| Item | Price | Qty | |
|---|---|---|---|
Power Supply Adapter DC Universal AC to DC Converter PSU 5V2A 5V3A 9V2A 12V2A - P.S. ADAPTOR (12V2A)PSA1202 | RM15.95 |
Kunci tidak sepatutnya bergantung pada laptop. Adapter 12V 2A dicucuk terus ke barrel jack Uno — 12V berada di hujung atas julat 7–12V yang disyorkan untuk Uno, jadi regulator board akan terasa suam. Itu normal.
Macam mana keypad matriks 4×4 baca 16 kekunci dengan 8 wayar sahaja?
Keypad membran MEMKEYP mempunyai enam belas suis di bawah lapisan plastiknya tetapi ekornya hanya membawa lapan konduktor — itulah sebabnya ia dipanggil matriks. Suis-suis itu disusun sebagai empat baris (row) melintang dan empat lajur (column) menegak, setiap satu duduk tepat di persilangan satu baris dengan satu lajur; menekan kekunci hanya menyambungkan wayar baris itu kepada wayar lajur itu.
Arduino membacanya dengan teknik bernama scanning, dan inilah yang library Keypad buat di belakang tabir. Empat pin baris ditetapkan sebagai INPUT_PULLUP: selagi tiada apa-apa ditekan, perintang dalaman Uno menarik setiap baris naik ke HIGH, jadi satu-satunya cara sesuatu baris boleh jatuh ke LOW ialah kalau ada yang menariknya ke bawah.
Uno kemudian memandu satu lajur sahaja ke LOW pada satu-satu masa, sementara tiga lajur lain ditinggalkan sebagai input impedans tinggi — secara elektrik seolah-olah tidak bersambung, jadi tiada baris boleh ditarik turun melaluinya. Kalau satu baris terbaca LOW, hanya ada satu jalan LOW itu boleh sampai ke situ: suis di persilangan baris tersebut dengan lajur yang sedang dipandu. Itulah kekunci yang ditekan. Ulang untuk empat lajur, dan enam belas kekunci disemak berpuluh kali sesaat.
Matematiknya mudah tetapi kuat: 4 + 4 = 8 wayar meliputi 4 × 4 = 16 kekunci, berbanding 16 wayar isyarat dan satu ground kalau setiap suis diwayar sendiri. Inilah jawapan viva yang sebenarnya — bukan “saya guna library Keypad”, tetapi “satu lajur dipandu ke LOW, dan baris yang ikut turun bersamanya itulah kekunci yang ditekan”.

Untuk melihat pad ini benar-benar disambung dan kekunci muncul di serial monitor:
Macam mana nak sambung keypad, OLED dan modul relay ke Uno?
Ekor MEMKEYP menggunakan susunan piawai pad membran 4×4: dibilang dari satu hujung soket, pin 1–4 ialah empat baris dan pin 5–8 ialah empat lajur. Sahkan hujung mana pin 1 dengan multimeter dalam mod continuity — tahan satu kekunci, dan dua pin yang berbunyi itulah baris dan lajur kekunci itu. OLED berkongsi bas I2C Uno pada A4 dan A5, dan modul relay perlukan satu pin digital.
| Sambungan | Pin Uno | Nota |
|---|---|---|
| Keypad pin 1 (baris 1) | D9 | Baris — dibaca sebagai INPUT_PULLUP semasa scan |
| Keypad pin 2 (baris 2) | D8 | — |
| Keypad pin 3 (baris 3) | D7 | — |
| Keypad pin 4 (baris 4) | D6 | — |
| Keypad pin 5 (lajur 1) | D5 | Lajur — dipandu LOW sebagai output semasa scan |
| Keypad pin 6 (lajur 2) | D4 | — |
| Keypad pin 7 (lajur 3) | D3 | — |
| Keypad pin 8 (lajur 4) | D2 | — |
| OLED GND | GND | Pin pertama pada modul ini — semak silkscreen |
| OLED VCC | 5V | Pin kedua, bukan pertama |
| OLED SCL | A5 | Clock I2C |
| OLED SDA | A4 | Data I2C |
| Relay DC+ | 5V | Terminal skru pada modul |
| Relay DC− | GND | — |
| Relay IN1 | D10 | Satu pin sahaja mengawal satu saluran |
Modul 2WRELAY mempunyai blok jumper pemilih bertanda Low – Com – High (dengan lajur S1 dan S2 untuk kedua-dua saluran) di sebelah terminal skru inputnya. Shunt yang merapatkan Com dengan Low bermakna modul itu low-level trigger: pin Uno yang LOW menghidupkan relay. Sketch di bawah menyimpan pilihan itu dalam satu pemalar.
Kenapa modul relay, bukan relay kosong?
Relay Songle SRD-05VDC-SL-C di atas modul 2WRELAY mempunyai gegelung 70 ohm, dan 5V merentasi 70 ohm bermakna kira-kira 71 mA mengalir setiap kali ia ditarik masuk (datasheet SRD-05VDC-SL-C), sedangkan satu pin Arduino Uno direka untuk 20 mA. Jadi relay kosong bukan sekadar “kurang selamat” dipandu terus dari pin — ia memang tidak akan berfungsi, dan pin itu berisiko rosak.
Modul 2WRELAY meletakkan optocoupler dan transistor pemandu di antara pin anda dan gegelung: pin Uno hanya menyalakan LED di dalam optocoupler dan menghantar beberapa milliamp sahaja, manakala 71 mA gegelung diambil terus dari talian 5V. Relay kosong tetap ada tempatnya untuk binaan yang sudah mempunyai tingkat pemandu sendiri — lihat panduan cara guna relay module dengan selamat. Panduan ini kekal pada DC voltan rendah: sisi yang dihidupkan relay ialah solenoid atau selak DC 12V, dan kontak relay itu dinilai 10A pada 30V DC. Selak itu diberi kuasa terus daripada bekalan 12V melalui kontak relay — bukan daripada pin 5V Uno, yang regulatornya tidak direka untuk beban solenoid.
Ekor MEMKEYP berakhir dengan soket female 8 pin dan header Uno juga female — sebab itulah wayar male-to-male berada dalam senarai utama, bukan senarai pilihan; hujung male yang sama boleh diapit di bawah skru terminal relay. Modul OLED 0.96″ pula datang dengan lubang berplat — solder header atau wayar terus ke situ, kerana sambungan geseran akan terlepas pada hari yang paling menyusahkan.
Apa jadi kalau kod salah dimasukkan tiga kali?
Kod empat digit bermaksud 10,000 kombinasi, dan nombor itu bunyinya besar sehinggalah anda kira berapa lama ia sebenarnya bertahan. Tanpa penalti, setiap cubaan yang gagal kosnya sifar: pada kadar satu cubaan setiap dua saat, seluruh 10,000 kombinasi habis dalam kurang enam jam. Kekuatan kunci anda bukan 10,000 — ia kesabaran orang yang berdiri di depan pad.
Lockout menukar kombinasi kepada masa. Selepas tiga kod salah berturut-turut, sketch di bawah berhenti menerima kekunci selama 30 saat dan memaparkan kiraan detik di OLED; tiga lagi menjadikannya 60 saat, kemudian 120 saat, dan di situ ia berhenti naik supaya tuan punya kotak yang tersilap taip tidak terkunci berjam-jam. Kaunter penalti hanya reset selepas satu kod betul, jadi kadar mampan seseorang di depan pad ialah tiga cubaan setiap dua minit — satu cubaan setiap 40 saat. Ruang 10,000 yang tadinya satu petang kini menjadi lebih empat hari.
Dua tabiat kecil lagi, dan kedua-duanya percuma. Kod lalai sepanjang enam digit, bukan empat: sejuta kombinasi pada kadar 40 saat setiap satu bermakna lebih setahun, kerana panjang kod dan lockout itu didarab, bukan dicampur. Dan mesej gagal tidak memberitahu digit mana yang salah — maklum balas separa ialah hadiah percuma kepada orang yang meneka, dan atas sebab sama OLED hanya memaparkan asterisk.

Bagaimana rupa sketch Arduino yang lengkap?
Sketch ini memerlukan dua pemasangan dalam Library Manager Arduino IDE: Keypad oleh Mark Stanley dan Alexander Brevig (rujukan library), dan Adafruit SSD1306 berserta dependency Adafruit GFX dan Adafruit BusIO. Board UNOCH34 menggunakan cip USB CH340, jadi kalau port COM tidak muncul, pasang driver CH340 dahulu.
Kod disimpan dalam EEPROM, bukan dalam sketch sahaja — itulah yang menjadikan janji di awal artikel ini nyata: tekan A, masukkan kod semasa, taip kod baharu, siap. Kod itu bertahan selepas reset dan selepas upload semula, kerana bootloader Arduino hanya menulis ke flash dan langsung tidak menyentuh EEPROM.
#include <Keypad.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {9, 8, 7, 6}; // ekor keypad pin 1-4
byte colPins[COLS] = {5, 4, 3, 2}; // ekor keypad pin 5-8
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
const byte RELAY_PIN = 10;
const uint8_t RELAY_ON = LOW; // shunt jumper pada Com-Low; tukar ke HIGH kalau Com-High
const uint8_t RELAY_OFF = HIGH;
const byte CODE_LEN = 6; // enam digit, bukan empat
const char DEFAULT_CODE[CODE_LEN + 1] = "246810"; // TUKAR kod ini selepas upload pertama
const byte EEPROM_MAGIC = 0xA5;
const byte MAX_TRIES = 3;
const unsigned long LOCKOUT_BASE_MS = 30000UL; // 30 s, kemudian 60 s, kemudian 120 s (had maksimum)
const unsigned long UNLOCK_MS = 5000UL; // berapa lama selak dibuka
enum Mode { MODE_ENTER, MODE_OLD, MODE_NEW };
Mode mode = MODE_ENTER;
char entry[CODE_LEN + 1];
char stored[CODE_LEN + 1];
byte filled = 0;
byte wrongTries = 0;
byte penalty = 0;
unsigned long lockoutUntil = 0;
unsigned long relayOffAt = 0;
unsigned int lastShown = 0;
void loadCode() {
// Kali pertama board dihidupkan, EEPROM masih kosong: tanam kod lalai
if (EEPROM.read(0) != EEPROM_MAGIC) {
for (byte i = 0; i < CODE_LEN; i++) EEPROM.update(1 + i, DEFAULT_CODE[i]);
EEPROM.update(0, EEPROM_MAGIC);
}
for (byte i = 0; i < CODE_LEN; i++) stored[i] = EEPROM.read(1 + i);
stored[CODE_LEN] = '\0';
}
void saveCode() {
for (byte i = 0; i < CODE_LEN; i++) {
stored[i] = entry[i];
EEPROM.update(1 + i, entry[i]); // update() hanya menulis bait yang berubah - EEPROM ada had ~100,000 kitaran tulis
}
stored[CODE_LEN] = '\0';
}
bool matches() {
for (byte i = 0; i < CODE_LEN; i++) {
if (entry[i] != stored[i]) return false;
}
return true;
}
void resetEntry() {
for (byte i = 0; i <= CODE_LEN; i++) entry[i] = '\0';
filled = 0;
}
void drawPrompt() {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
if (mode == MODE_ENTER) display.print(F("Masukkan kod"));
else if (mode == MODE_OLD) display.print(F("Kod semasa"));
else display.print(F("Kod baharu"));
display.setTextSize(3);
display.setCursor(0, 24);
for (byte i = 0; i < filled; i++) display.print('*'); // digit tidak pernah dipapar
display.setTextSize(1);
display.setCursor(0, 56);
display.print(F("* padam A tukar kod"));
display.display();
}
void drawBanner(const __FlashStringHelper *top, const __FlashStringHelper *big) {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.print(top);
display.setTextSize(2);
display.setCursor(0, 28);
display.print(big);
display.display();
}
void drawWrong(byte left) {
display.clearDisplay();
display.setTextSize(2);
display.setCursor(0, 0);
display.print(F("KOD SALAH"));
display.setTextSize(1);
display.setCursor(0, 34);
display.print(F("Baki cubaan: "));
display.print(left); // tidak pernah beritahu digit mana yang salah
display.display();
}
void drawLockout(unsigned int secs) {
display.clearDisplay();
display.setTextSize(2);
display.setCursor(0, 0);
display.print(F("TERKUNCI"));
display.setTextSize(3);
display.setCursor(0, 28);
display.print(secs);
display.setTextSize(1);
display.print(F(" s"));
display.display();
}
void grantAccess() {
wrongTries = 0;
penalty = 0; // kaunter penalti reset hanya selepas kod betul
resetEntry();
digitalWrite(RELAY_PIN, RELAY_ON);
relayOffAt = millis() + UNLOCK_MS;
drawBanner(F("Akses"), F("DIBUKA"));
}
void wrongEntry() {
resetEntry();
wrongTries++;
if (wrongTries >= MAX_TRIES) {
if (penalty < 3) penalty++;
lockoutUntil = millis() + (LOCKOUT_BASE_MS << (penalty - 1));
lastShown = 0;
} else {
drawWrong(MAX_TRIES - wrongTries);
delay(1200); // lengah kecil yang disengajakan
drawPrompt();
}
}
void submit() {
if (mode == MODE_ENTER) {
if (matches()) grantAccess();
else wrongEntry();
} else if (mode == MODE_OLD) {
if (matches()) {
mode = MODE_NEW;
resetEntry();
drawPrompt();
} else {
mode = MODE_ENTER;
wrongEntry(); // tukar kod dikira cubaan yang sama
}
} else {
saveCode();
mode = MODE_ENTER;
resetEntry();
drawBanner(F("Kod baharu"), F("DISIMPAN"));
delay(1500);
drawPrompt();
}
}
void setup() {
digitalWrite(RELAY_PIN, RELAY_OFF); // tetapkan aras dahulu...
pinMode(RELAY_PIN, OUTPUT); // ...barulah jadikan output, elak denyut relay
Wire.begin();
display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
loadCode();
resetEntry();
drawPrompt();
}
void loop() {
unsigned long now = millis();
// Kunci semula secara automatik selepas UNLOCK_MS
if (relayOffAt != 0 && (long)(now - relayOffAt) >= 0) {
digitalWrite(RELAY_PIN, RELAY_OFF);
relayOffAt = 0;
resetEntry();
drawPrompt();
}
char key = keypad.getKey(); // sentiasa diimbas, walaupun kekunci dibuang
if (lockoutUntil != 0) {
if ((long)(now - lockoutUntil) >= 0) {
lockoutUntil = 0;
wrongTries = 0;
drawPrompt();
} else {
unsigned int left = (unsigned int)((lockoutUntil - now) / 1000UL) + 1;
if (left != lastShown) {
lastShown = left;
drawLockout(left);
}
return; // semua kekunci diabaikan semasa lockout
}
}
if (key == NO_KEY) return;
if (key == '*') { // padam apa yang ditaip
resetEntry();
drawPrompt();
return;
}
if (key == '#') { // kunci semula serta-merta
digitalWrite(RELAY_PIN, RELAY_OFF);
relayOffAt = 0;
resetEntry();
drawPrompt();
return;
}
if (key == 'A' && mode == MODE_ENTER && filled == 0) {
mode = MODE_OLD; // tukar kod: kod semasa dahulu
resetEntry();
drawPrompt();
return;
}
if (key >= '0' && key <= '9') {
if (filled < CODE_LEN) {
entry[filled++] = key;
drawPrompt();
}
if (filled == CODE_LEN) submit();
}
}
Selepas upload pertama, masukkan kod lalai sekali, kemudian tekan A dan tukar kepada kod anda sendiri.
Apakah had keselamatan kunci keypad ini?
Kunci keypad ini jujur tentang dirinya. Shoulder-surfing ialah kelemahan pertama: sesiapa yang berdiri di belakang anda boleh melihat jari anda, dan tiada baris kod boleh membaikinya — lindungi pad dengan tangan, dan pasang ia menghadap arah yang tidak mudah dilihat orang lalu.
Kedua, kekunci lusuh. Pada pad membran, kekunci yang kerap ditekan menjadi berkilat berbanding jirannya: kalau kod empat digit memakai empat kekunci berlainan, kesan lusuh mendedahkan set digitnya dan yang tinggal cuma susunan — 24 susunan, bukan 10,000. Tukar kod secara berkala, dan biarkan kod anda mengulang sekurang-kurangnya satu digit.
Ketiga, kod berada dalam sketch sebagai teks biasa. EEPROM membantu — kod yang dihantar kepada cikgu bukan lagi kod yang membuka kotak — tetapi sesiapa yang boleh mencucuk programmer ke board tetap boleh membacanya. Sebab itu papan Uno patut berada di dalam kotak berkunci.
| Kegunaan | Sesuai? | Sebab |
|---|---|---|
| Loker sekolah, laci meja | Ya | Nilai rendah, akses berkongsi, kod boleh ditukar setiap penggal |
| Kabinet komponen, kotak alatan | Ya | Menghalang tangan yang cuai, bukan penceroboh serius |
| Projek RBT dan pameran | Ya | Konsep matriks dan lockout senang dipertahankan semasa viva |
| Pintu rumah | Hanya sebagai lapisan tambahan | Mesti duduk di atas kunci mekanikal sebenar yang masih ada anak kunci |
Syarat terakhir itu praktikal, bukan teori: mikrokontroler boleh hang, kuasa boleh putus, dan anda tetap perlu masuk. Selagi ada kunci mekanikal di bawahnya, kegagalan paling teruk hanyalah kesulitan.

Kesilapan biasa yang kami lihat daripada pelanggan
Corak yang paling kerap sampai kepada kami ialah susunan pin baris dan lajur yang tertukar. Keypad tidak mati — ia hanya memulangkan aksara yang salah, jadi menekan 2 memberi 4 dan menekan 3 memberi 7, tetapi 1, 5, 9 dan D kekal betul kerana ia duduk pada pepenjuru. Puncanya: matriks itu simetri, suis cuma menyambung dua wayar, dan nama baris atau lajur datang daripada sketch anda, bukan daripada pad. Jadi tiada apa yang rosak — tukar susunan rowPins dengan colPins.
Kedua ialah memandu relay kosong terus dari pin digital, kemudian menyimpulkan relay itu rosak. Ketiga ialah OLED yang dicucuk tanpa disolder: ia menyala semasa ujian di atas meja, mati apabila kotak digerakkan, dan sejam habis menyemak alamat I2C untuk masalah mekanikal.
Keempat, dan yang paling senyap: kod lalai tidak pernah ditukar. Sketch berjalan sempurna, projek disiapkan, dan kotak itu dilindungi oleh nombor yang tercetak dalam fail yang dikongsi sekelas.
FAQ
Kalau bekalan kuasa terputus, selak terbuka atau kekal terkunci?
Bergantung pada terminal relay dan jenis selak. Selak solenoid DC yang lazim ialah fail-secure: ia perlukan kuasa untuk membuka, jadi ia terkunci apabila kuasa hilang — pada terminal NO mahupun NC, kerana bekalan 12V selak itu turut mati. Sambungkan selak itu pada terminal NO (normally open) dan kotak kekal terkunci semasa bekalan putus. Sahkan selak anda memang fail-secure, kerana selak fail-safe seperti magnet elektrik berkelakuan terbalik.
Kod hilang tak kalau Uno reset atau saya upload semula sketch?
Tidak. Kod duduk dalam EEPROM ATmega328P, dan upload Arduino hanya menulis memori flash — EEPROM tidak disentuh. Untuk memaksa kod kembali kepada lalai, tukar nilai EEPROM_MAGIC dalam sketch dan upload sekali.
Berapa panjang kod yang patut saya guna?
Enam digit ialah titik seimbang yang baik: sejuta kombinasi, dan masih senang diingat. Empat digit hanya sepuluh ribu. Tetapi panjang sahaja tidak memadai — pasangkan dengan lockout, kerana kesan kedua-duanya didarab, bukan dicampur.
Boleh ke projek ini jalan atas ESP32?
Boleh. Keypad hanya perlukan lapan GPIO biasa, jadi library Keypad berjalan seadanya — tukar nombor pin dalam rowPins dan colPins, dan pindahkan OLED ke GPIO21 (SDA) dan GPIO22 (SCL). Satu perbezaan: ESP32 tiada EEPROM sebenar, jadi library EEPROM di sana meniru fungsinya dalam flash dan perlukan EEPROM.begin() serta EEPROM.commit().
Kemas kini terakhir Ogos 2026. Tersangkut? Chat dengan kami di WhatsApp.


Humidifier Module Atomization Board Ultrasonic Mist Maker Fogger Transducer Ceramic Plate DIY Project Parts
Sensor Shield V5.0 Arduino Uno R3 Shield For Arduino Protoype Application 
Arduino Uno Compatible SMD UNO R3 with Type B Cable - ATMEGA328P with CH340G-Microcontroller Project
Matrix Array 16 Key Membrane Keypad 4 x 4 Keypad Ultra Slim Keyboard For Arduino Application
Relay Module 3.3V 5V 12V 1/2/4/8 Ways Optocoupler Trigger Relay Module 1 2 4 8 Channel Relay Module - 5V RELAY MODULE(2WAY)
0.96‘ OLED Display Module Blue White Screen I2C IIC Serial 128X64 LCD Display Board - 0.96' OLED WHITE
40pcs Dupont Wire 10cm 20cm 30cm for Breadboard DIY Experiment Jumper Wire Breadboard wire - DUPONT WIRE M-M 30CM
Power Supply Adapter DC Universal AC to DC Converter PSU 5V2A 5V3A 9V2A 12V2A - P.S. ADAPTOR (12V2A)