from machine import Pin, PWM, I2C
from time import sleep
from ssd1306 import SSD1306_I2C
# ---------------- OLED SETUP ----------------
i2c = I2C(0, scl=Pin(22), sda=Pin(21))
oled = SSD1306_I2C(128, 64, i2c)
# ---------------- OUTPUT SETUP ----------------
led_green = Pin(18, Pin.OUT)
led_red = Pin(19, Pin.OUT)
servo = PWM(Pin(23), freq=50)
# Buzzer setup (PWM)
buzzer = PWM(Pin(5), freq=1000, duty=0)
# ---------------- INPUT SETUP ----------------
btn_fingerprint_valid = Pin(14, Pin.IN, Pin.PULL_UP)
btn_fingerprint_invalid = Pin(27, Pin.IN, Pin.PULL_UP)
btn_rfid_valid = Pin(32, Pin.IN, Pin.PULL_UP)
btn_rfid_invalid = Pin(33, Pin.IN, Pin.PULL_UP)
# PIR sensor
pir = Pin(26, Pin.IN)
# Slide switch sebagai pemilih mode
mode_switch = Pin(12, Pin.IN, Pin.PULL_UP)
# ---------------- SERVO POSITIONS ----------------
LOCKED_POS = 25
UNLOCKED_POS = 125
# ---------------- BUZZER PATTERNS ----------------
def buzzer_access_granted():
buzzer.freq(1500); buzzer.duty(512); sleep(0.3); buzzer.duty(0)
def buzzer_access_denied():
for _ in range(3):
buzzer.freq(800); buzzer.duty(512); sleep(0.2); buzzer.duty(0); sleep(0.2)
def buzzer_countdown(countdown):
mapping = {5: 2000, 4: 1700, 3: 1400, 2: 1100, 1: 800}
freq = mapping.get(countdown, 1200)
buzzer.freq(freq); buzzer.duty(512)
duration = 0.2 if countdown > 2 else 0.1
sleep(duration); buzzer.duty(0)
def buzzer_pir_presence():
# Gentle bip.. bip.. bip.. pattern
buzzer.freq(1500)
buzzer.duty(512)
sleep(0.1)
buzzer.duty(0)
sleep(0.4)
# ---------------- MODE & DISPLAY ----------------
def get_mode():
return "Two-Factor" if mode_switch.value() == 1 else "Single-Factor"
def show_status(mode, msg="", extra=""):
oled.fill(0)
oled.text("Mode: {}".format(mode), 0, 0)
if msg: oled.text("Msg: {}".format(msg), 0, 10)
if extra: oled.text(str(extra), 0, 20)
oled.show()
# Print berbaris ke bawah
print("Mode:", mode)
if msg: print("Msg:", msg)
if extra: print(extra)
print("-----")
# ---------------- ACTIONS ----------------
def unlock_door(source, mode):
show_status(mode, "ACCESS GRANTED")
led_green.on(); led_red.off()
buzzer_access_granted()
servo.duty(UNLOCKED_POS); sleep(3)
servo.duty(LOCKED_POS); led_green.off()
def deny_access(source, mode):
show_status(mode, "ACCESS DENIED", source)
led_red.on(); led_green.off()
buzzer_access_denied()
led_red.off()
# ---------------- MAIN LOOP ----------------
current_mode = get_mode()
show_status(current_mode, "System Ready", "Please place finger/RFID card")
pir_active = False # flag to track PIR state
while True:
mode = get_mode()
if mode != current_mode:
current_mode = mode
show_status(mode, "System Ready", "Please place finger/RFID card")
if mode == "Single-Factor":
if btn_fingerprint_valid.value() == 0 or btn_rfid_valid.value() == 0:
unlock_door("Fingerprint/RFID", mode); sleep(1)
elif btn_fingerprint_invalid.value() == 0 or btn_rfid_invalid.value() == 0:
deny_access("Invalid Input", mode); sleep(1)
elif mode == "Two-Factor":
if btn_fingerprint_valid.value() == 0:
for countdown in range(5, 0, -1):
if btn_fingerprint_invalid.value() == 0 or btn_rfid_invalid.value() == 0:
deny_access("Invalid Input", mode)
break
show_status(mode, "FP OK - Tap RFID", "Please place RFID card ({:d}s)".format(countdown))
buzzer_countdown(countdown); sleep(1)
if btn_rfid_valid.value() == 0:
unlock_door("FP + RFID", mode); break
else:
deny_access("RFID Timeout", mode); sleep(1)
elif btn_rfid_valid.value() == 0:
for countdown in range(5, 0, -1):
if btn_fingerprint_invalid.value() == 0 or btn_rfid_invalid.value() == 0:
deny_access("Invalid Input", mode)
break
show_status(mode, "RFID OK - Tap FP", "Please place finger ({:d}s)".format(countdown))
buzzer_countdown(countdown); sleep(1)
if btn_fingerprint_valid.value() == 0:
unlock_door("FP + RFID", mode); break
else:
deny_access("Fingerprint Timeout", mode); sleep(1)
elif btn_fingerprint_invalid.value() == 0 or btn_rfid_invalid.value() == 0:
deny_access("Invalid Input", mode); sleep(1)
# PIR presence notification
if pir.value() == 1:
if not pir_active: # only print once when PIR goes high
show_status(mode, "User Detected", "Near Scanner")
pir_active = True
buzzer_pir_presence()
else:
pir_active = False # reset flag when PIR goes low
sleep(0.1)