Devices & Components
Arduino® UNO™ Q 4GB
Illuminated Push Button (16mm)
8mm Neopixel LEDs
Piezo Buzzers
90 Degree Angle Header Pins
ArduCam IMX219 USB Camera Module
Standard sized RC Servo (40 x 20 x 38mm) with side flange mounting
Software & Tools
Arduino App Lab
Project description
Code
capture_authorized
python
Python for additional face capture
1import cv2 2import os 3import time 4 5output_dir = "authorized_capture" 6os.makedirs(output_dir, exist_ok=True) 7 8cap = cv2.VideoCapture(2) 9 10count = 0 11max_images = 50 12capturing = False 13last_capture = 0 14 15print("Press SPACE to start automatic capture (1 photo per second). ESC to quit.") 16 17while True: 18 ret, frame = cap.read() 19 if not ret: 20 continue 21 22 cv2.imshow("Capture", frame) 23 key = cv2.waitKey(1) 24 25 if key == 32: # SPACE 26 capturing = True 27 print("Starting capture...") 28 29 if key == 27: # ESC 30 break 31 32 if capturing: 33 now = time.time() 34 if now - last_capture >= 1: 35 filename = f"{output_dir}/auth_{count:03d}.jpg" 36 cv2.imwrite(filename, frame) 37 print("Saved", filename) 38 39 count += 1 40 last_capture = now 41 42 if count >= max_images: 43 break 44 45cap.release() 46cv2.destroyAllWindows() 47print("Done.")
ws2812b-bitbang
cpp
Extra code for LED usage
1// ws2812b-bitbang.h 2 3// gpioa, pin 9 gives us D4 on the UNO Q board See: 4// https://github.com/arduino/ArduinoCore-zephyr/blob/main/variants/arduino_uno_q_stm32u585xx/arduino_uno_q_stm32u585xx.overlay 5#define DATA_GPIO_NODE DT_NODELABEL(gpiob) 6#define DATA_GPIO_PIN 9 7 8// Get the base address and create register access 9#define GPIO_BASE DT_REG_ADDR(DATA_GPIO_NODE) 10#define GPIO_BSRR (*(volatile uint32_t*)(GPIO_BASE + 0x18)) 11 12// Fast pin write 13#define PIN_HIGH() GPIO_BSRR = (1 << DATA_GPIO_PIN) 14#define PIN_LOW() GPIO_BSRR = (1 << (DATA_GPIO_PIN + 16)) 15 16// Get device handle for proper initialization 17const struct device *gpio_dev = DEVICE_DT_GET(DATA_GPIO_NODE); 18 19// Naive delay in CPU cycles 20inline void delay_cycles(uint32_t cycles) __attribute__((always_inline)); 21inline void delay_cycles(uint32_t cycles) { 22 while(cycles--) { 23 __asm__ __volatile__("nop"); 24 } 25} 26 27// Initialize pin 28void ws2812b_init() { 29 gpio_pin_configure(gpio_dev, DATA_GPIO_PIN, GPIO_OUTPUT_INACTIVE); 30 gpio_pin_set_raw(gpio_dev, DATA_GPIO_PIN, 0); 31} 32 33// Bitbang WS2812b data from the buffer 34void ws2812b_show(uint8_t (*buf)[3], int num_leds) { 35 36 // Disable interrupts while we send out WS2812b data 37 noInterrupts(); 38 39 // Loop through entire buffer 40 for (int led = 0; led < num_leds; led++) { 41 for (int channel = 0; channel < 3; channel++) { 42 uint8_t data = buf[led][channel]; 43 44 for (int i = 7; i >= 0; i--) { 45 if (data & (1 << i)) { 46 PIN_HIGH(); 47 delay_cycles(30); 48 PIN_LOW(); 49 delay_cycles(15); 50 } else { 51 PIN_HIGH(); 52 delay_cycles(12); 53 PIN_LOW(); 54 delay_cycles(33); 55 } 56 } 57 } 58 } 59 60 // Re-enable interrupts 61 interrupts(); 62 63 // Make sure that we send the reset signal 64 PIN_LOW(); 65 delayMicroseconds(60); 66}
FaceLockController_final
cpp
Project sketch code
1#include <Arduino_RouterBridge.h> 2#include "ws2812b-bitbang.h" 3#include <Servo.h> 4 5const int BUTTON_PIN = 8; 6const int BUTTON_LED_PIN = 11; 7const int SERVO_PIN = 9; 8const int PIEZO_PIN = 6; 9 10#define NUM_LEDS 2 11uint8_t framebuffer[NUM_LEDS][3]; 12 13Servo lidServo; 14 15bool lidOpen = false; 16bool processing = false; 17bool thinkingActive = false; 18bool thinkingState = false; 19unsigned long lastThinkingToggle = 0; 20unsigned long lastStatePoll = 0; 21const unsigned long thinkingInterval = 500; 22const unsigned long statePollInterval = 500; 23 24const int SERVO_CLOSED = 90; 25const int SERVO_OPEN = 170; 26 27String lastState = "idle"; 28 29// ================= LED ================= 30void setLedHex(uint8_t led, uint32_t hexColor) { 31 framebuffer[led][0] = (hexColor >> 16) & 0xFF; 32 framebuffer[led][1] = (hexColor >> 8) & 0xFF; 33 framebuffer[led][2] = hexColor & 0xFF; 34} 35 36void setBoth(uint32_t c0, uint32_t c1) { 37 setLedHex(0, c0); 38 setLedHex(1, c1); 39 ws2812b_show(framebuffer, NUM_LEDS); 40} 41 42void setLEDsOff() { setBoth(0,0); } 43void setLEDsGreen() { setBoth(0x00FF00,0x00FF00); } 44void setLEDsRed() { setBoth(0xFF0000,0xFF0000); } 45 46// ================= SOUNDS ================= 47void playSuccessSound() { 48 // Success melody: cheerful ascending notes 49 int melody[] = {262, 330, 392, 523}; // C, E, G, C (octave higher) 50 int durations[] = {150, 150, 150, 400}; 51 52 for (int i = 0; i < 4; i++) { 53 tone(PIEZO_PIN, melody[i], durations[i]); 54 delay(durations[i] + 50); 55 } 56 noTone(PIEZO_PIN); 57} 58 59void playFailureSound() { 60 // Failure sound: descending "sad trombone" style 61 int melody[] = {392, 349, 311, 262}; // G, F, Eb, C (descending) 62 int durations[] = {200, 200, 200, 500}; 63 64 for (int i = 0; i < 4; i++) { 65 tone(PIEZO_PIN, melody[i], durations[i]); 66 delay(durations[i] + 50); 67 } 68 noTone(PIEZO_PIN); 69} 70 71// ================= THINKING ================= 72void startThinking() { 73 thinkingActive = true; 74} 75 76void stopThinking() { 77 thinkingActive = false; 78 setLEDsOff(); 79} 80 81void updateThinking() { 82 if (!thinkingActive) return; 83 if (millis() - lastThinkingToggle > thinkingInterval) { 84 lastThinkingToggle = millis(); 85 thinkingState = !thinkingState; 86 if (thinkingState) 87 setBoth(0xFFFF00,0); 88 else 89 setBoth(0,0xFFFF00); 90 } 91} 92 93// ================= STATE HANDLING ================= 94void handleStateChange(String newState) { 95 if (newState == lastState) return; 96 97 lastState = newState; 98 99 if (newState == "thinking") { 100 startThinking(); 101 } 102 else if (newState == "success") { 103 thinkingActive = false; 104 setLEDsOff(); 105 delay(50); 106 setLEDsGreen(); 107 playSuccessSound(); // Play success melody 108 lidServo.write(SERVO_OPEN); 109 lidOpen = true; 110 } 111 else if (newState == "fail") { 112 thinkingActive = false; 113 setLEDsOff(); 114 delay(50); 115 setLEDsRed(); 116 playFailureSound(); // Play failure melody 117 delay(3000); 118 setLEDsOff(); 119 } 120 else if (newState == "idle") { 121 processing = false; 122 } 123} 124 125// ================= SETUP ================= 126void setup() { 127 Bridge.begin(); 128 129 pinMode(BUTTON_PIN, INPUT_PULLUP); 130 pinMode(BUTTON_LED_PIN, OUTPUT); 131 digitalWrite(BUTTON_LED_PIN, HIGH); 132 133 ws2812b_init(); 134 setLEDsOff(); 135 136 lidServo.attach(SERVO_PIN); 137 lidServo.write(SERVO_CLOSED); 138 139 pinMode(PIEZO_PIN, OUTPUT); 140 tone(PIEZO_PIN, 1000, 100); 141 delay(150); 142 tone(PIEZO_PIN, 1500, 100); 143 delay(150); 144 noTone(PIEZO_PIN); 145} 146 147// ================= LOOP ================= 148void loop() { 149 static bool lastButtonState = HIGH; 150 bool buttonState = digitalRead(BUTTON_PIN); 151 152 // Handle button press 153 if (lastButtonState == HIGH && buttonState == LOW) { 154 if (!processing && !lidOpen) { 155 processing = true; 156 Bridge.call("on_capture"); 157 } 158 else if (lidOpen) { 159 lidServo.write(SERVO_CLOSED); 160 lidOpen = false; 161 setLEDsOff(); 162 } 163 } 164 lastButtonState = buttonState; 165 166 // Poll for state changes from Python (only when processing) 167 if (processing && millis() - lastStatePoll > statePollInterval) { 168 lastStatePoll = millis(); 169 170 String state; 171 if (Bridge.call("get_state").result(state)) { 172 handleStateChange(state); 173 } 174 } 175 176 updateThinking(); 177 Bridge.update(); 178 179 delay(10); 180}
FaceLockVision_final
python
Project Python code
1import time 2import threading 3from arduino.app_utils import Bridge, App 4from arduino.app_bricks.video_objectdetection import VideoObjectDetection 5 6# Set back to 0.3 to prevent flooding with garbage boxes 7detector = VideoObjectDetection(confidence=0.5) 8 9authorized_score = 0.0 10face_score = 0.0 11collecting = False 12current_state = "idle" 13 14STRICT_THRESHOLD = 0.60 15 16# Variables to group detections by frame 17current_frame_auth = 0.0 18current_frame_face = 0.0 19last_detection_time = 0.0 20 21def process_frame(): 22 global authorized_score, face_score, current_frame_auth, current_frame_face 23 24 print(f"[FRAME] Auth Score: {current_frame_auth:.2f} | Face Score: {current_frame_face:.2f}") 25 26 # Process Authorized 27 if current_frame_auth >= STRICT_THRESHOLD: 28 authorized_score += current_frame_auth 29 print(f" -> ✓ Auth Accepted! (Total Auth: {authorized_score:.2f})") 30 elif current_frame_auth > 0.1: 31 print(f" -> ✗ Auth Ignored (Below {STRICT_THRESHOLD:.2f})") 32 33 # Process Face 34 if current_frame_face >= STRICT_THRESHOLD: 35 face_score += current_frame_face 36 print(f" -> ○ Face Accepted! (Total Face: {face_score:.2f})") 37 elif current_frame_face > 0.1: 38 print(f" -> ✗ Face Ignored (Below {STRICT_THRESHOLD:.2f})") 39 40 # Reset for next frame 41 current_frame_auth = 0.0 42 current_frame_face = 0.0 43 44def handle_detection(label, conf): 45 global last_detection_time, current_frame_auth, current_frame_face 46 47 if not collecting: 48 return 49 50 now = time.time() 51 52 # If it's been more than 50ms since the last detection, we assume it's a new frame! 53 if now - last_detection_time > 0.05 and last_detection_time != 0.0: 54 process_frame() 55 56 last_detection_time = now 57 58 if label == "authorized_person": 59 current_frame_auth = max(current_frame_auth, conf) 60 elif label == "face": 61 current_frame_face = max(current_frame_face, conf) 62 63def on_authorized(d): 64 if 'confidence' in d: 65 handle_detection("authorized_person", d['confidence']) 66 67def on_face(d): 68 if 'confidence' in d: 69 handle_detection("face", d['confidence']) 70 71detector.on_detect("authorized_person", on_authorized) 72detector.on_detect("face", on_face) 73 74def get_state(): 75 return current_state 76 77def run_detection_window(): 78 global authorized_score, face_score, collecting, current_state, last_detection_time 79 authorized_score = 0.0 80 face_score = 0.0 81 last_detection_time = 0.0 82 83 print("\n*** BUTTON PRESSED ***") 84 print("=== Starting detection window ===") 85 86 current_state = "thinking" 87 88 collecting = True 89 90 start = time.time() 91 while time.time() - start < 2: 92 time.sleep(0.05) 93 94 collecting = False 95 96 # Process the very last frame if there was one 97 if current_frame_auth > 0 or current_frame_face > 0: 98 process_frame() 99 100 print(f"=== Detection complete ===") 101 print(f"Final Authorized score: {authorized_score:.2f}") 102 print(f"Final Face score: {face_score:.2f}") 103 104 if authorized_score > face_score: 105 print("✓ Result: SUCCESS - Opening box!") 106 current_state = "success" 107 else: 108 print("✗ Result: FAIL - Access denied") 109 current_state = "fail" 110 111 time.sleep(3) 112 current_state = "idle" 113 114def on_capture(): 115 thread = threading.Thread(target=run_detection_window) 116 thread.daemon = True 117 thread.start() 118 return True 119 120Bridge.provide("on_capture", on_capture) 121Bridge.provide("get_state", get_state) 122 123print("Chocolate box ready! Press the button to unlock...") 124App.run()
Arduino App Lab
Arduino UNO Q Face-Locked Chocolate Box
This project uses an Arduino UNO Q to lock/unlock your chocolate (or any!) box using custom face detection. To get this running, you will need to
Arduino UNO Q Face-Locked Chocolate Box
Downloadable files
ChocolateBox_Sides
Chocolate Box Side Piece for 3D printing.
ChocolateBox_Sides.stl
ChocolateBox_ServoHarness
Servo harness 3D printing file.
ChocolateBox_ServoHarness.stl
ChocolateBox_UpperLayer
Chocolate Box Upper Layer 3D printing file.
ChocolateBox_UpperLayer.stl
ChocolateBox_BottomLid
Chocolate Box bottom lid 3D printing file.
ChocolateBox_BottomLid.stl
ChocolateBox_LowerLayer
Chocolate Box lower layer 3D printing file.
ChocolateBox_LowerLayer.stl
ChocolateBox_TopLid
Chocolate Box top lid 3D printing file.
ChocolateBox_TopLid.stl
ChocolateBox_Linkage
Servo linkage 3D printing file.
ChocolateBox_Linkage.stl
PushButtonForChocolate_BIG
Push Button for Chocolate graphic for top lid.
PushButtonForChocolate_BIG.png

Face Locked Chocolate Box
Complete CAD assembly of the Chocolate Box.
Face Locked Chocolate Box.step
Other Dataset
Photo data set for Other Faces
Other Dataset.zip
Comments
Only logged in users can leave comments