ESP32

RD-03D 및 ESP32-C6을 이용한 레이더 기반 홈 시큐리티 시스템

모빌리티키즈 2025. 5. 30. 09:36
728x90
반응형

소개

전통적인 홈 보안 시스템은 PIR(수동 적외선) 센서에 의존하는 경우가 많지만, 애완동물, 움직이는 커튼, 온도 변화 등에 의해 발생하는 오경보 문제가 존재합니다. 또한 탐지 범위가 제한적이며, 느리게 움직이는 침입자나 두꺼운 옷을 입은 사람을 감지하는 데 어려움을 겪습니다.

RD-03D mmWave 레이더 센서와 Beetle ESP32-C6을 결합하면 더 정밀하고 스마트한 보안 시스템을 구축할 수 있습니다. 이 시스템은 실시간 침입자 감지, 스마트 경고, 레이더 시각화를 제공하며 Home Assistant와 완벽하게 통합됩니다.

 

 

 

 

PIR 문제

  1. 거짓 긍정: 애완 동물, 커튼, 온도 변화 및 햇빛으로 인해 지속적인 오경보가 발생합니다
  2. 제한된 범위: 대부분의 PIR 센서는 3-5미터 이내에서만 효과적으로 작동합니다.
  3. 좁은 감지: 일반적인 90° 시야각은 사각지대를 남깁니다.
  4. 날씨 민감도: 온도 변화는 성능에 큰 영향을 미칩니다.
  5. 패배하기 쉬움: 느린 움직임이나 열 차폐로 우회할 수 있습니다.

 

 

 

주요 기능들

  1. 레이더 기반 탐지(8m 범위, 120° 필드)
  2. 실시간 알림(LED + 부저)
  3. Web UI(ESP32에서 호스팅됨)
  4. 실시간 레이더 시각화
  5. 배터리로 구동되며 충전 가능(Type-C)
  6. 웹 인터페이스를 통한 임계값 구성
  7. Matter, Thread 및 Zigbee 지원
  8. Home Assistant 호환(MQTT/REST 통합 옵션)

 

 

 

 

 

 

부품 리스트

부품설명
ESP32-C6 비틀 마이크로컨트롤러
RD-03D 24GHz mmWave 레이더 센서
3.7V 1500mAh LiPo 배터리 충전식 배터리
RGB LED 시각적 알림
슬라이드 스위치 전원 관리
전선 및 커넥터 배선
3D 프린팅 인클로저 방수 설계

주요 기능

  • 레이더 기반 탐지 (8m 범위, 120° 시야각)
  • 실시간 알림 (LED 및 부저)
  • 웹 UI (ESP32에서 호스팅)
  • 실시간 레이더 시각화
  • 배터리 충전 가능 (Type-C)
  • Home Assistant 지원 (MQTT/REST)

회로 연결 방법

ESP32-C6과 RD-03D 연결:

  • RD-03D TX → ESP32 RX(GPIO 17)
  • RD-03D RX → ESP32 TX(GPIO 16)
  • GND → GND
  • VCC → 5V

RGB LED 연결:

  • 빨간색 LED 양극 → GPIO 21
  • 녹색 LED 양극 → GPIO 22
  • 파란색 LED 양극 → GPIO 23
  • 음극 → GND

배터리 연결:

  • LiPo 양극 → ESP32 배터리 핀
  • LiPo 접지 → ESP32 GND 핀

3D 프린팅 인클로저 제작

Fusion 360을 사용하여 설계

  • RD-03D 센서용 장착 슬롯
  • LED 및 부저용 구멍
  • USB-C 충전 포트 컷아웃
  • 스냅핏 방식 뚜껑

3D 프린팅

  • 재질: PLA 또는 PETG
  • 슬라이싱 소프트웨어: Cura
  • 추천 레이어 높이: 0.2mm
  • 프린팅 시간: 3시간 이내

후처리

  • LED 및 센서 장착
  • ESP32 고정 및 배선 정리

소스 코드 및 펌웨어 작성

ESP32의 펌웨어는 Arduino IDE를 사용하여 작성할 수 있습니다. RD-03D 레이더를 처리하기 위해 UART 통신을 활용합니다.

cpp
#include <SoftwareSerial.h>

SoftwareSerial radarSerial(16, 17); // RX, TX

void setup() {
  Serial.begin(115200);
  radarSerial.begin(115200);
}

void loop() {
  if (radarSerial.available()) {
    String radarData = radarSerial.readStringUntil('\n');
    Serial.println("Radar Data: " + radarData);
  }
}

웹 인터페이스 ESP32는 웹 서버를 호스팅하여 경보 상태를 표시하며, 사용자는 브라우저를 통해 데이터를 확인할 수 있습니다.

1단계: Autodesk Fusion 360에서 사례 설계

휴대성과 내구성을 보장하기 위해 Autodesk Fusion 360에서 소형 인클로저를 설계했습니다. 인클로저에는 다음이 포함됩니다.

1. 본문 : 여기에서 STL 파일을 다운로드합니다.

2. 상단 표지: 여기에서 STL 파일 다운로드

디자인에는 다음이 있습니다.

  1. RD-03D 센서용 장착 슬롯
  2. LED 구멍
  3. 버저용 구멍
  4. USB-C 프로그래밍 및 충전 액세스를 위한 컷아웃
  5. 스냅핏 뚜껑

디자인은 벽이나 천장에 장착할 수 있도록 최소한으로 컴팩트하게 유지됩니다.

 

 

 

 

2단계: 케이스 3D 프린팅

Fusion 360 설계를 STL로 내보냈습니다. FDM 프린터용 Cura를 사용하여 슬라이딩(0.2mm 레이어 높이 권장)

재질 : PLA 또는 PETGPrinted 3 시간 이내

후처리: LED를 삽입하고, 레이더를 장착하고, ESP32를 고정하고, 전선을 깔끔하게 배선합니다.

 

 

3단계: 회로 조립

ESP32-C6에 대한 레이더 센서

RD-03D TX → ESP32 RX(GPIO 17)

RD-03D RX → ESP32 TX(GPIO 16)

GND → GND

VCC → 5V

LED 표시 등

  1. 적색 LED 양극 → GPIO 21
  2. 녹색 LED 양극 → GPIO 22
  3. 파란색 LED 양극 → GPIO 23
  4. 음극에서 GND로

LiPo 배터리의 양극 단자를 Beetle ESP32C6의 배터리 핀에 납땜하고 접지 단자를 GND 핀에 PowerSolder합니다.

 

 

4단계: 펌웨어 및 웹 인터페이스

Arduino IDE를 사용하여 장치의 펌웨어를 작성하기로 선택한 이유는 단순성과 광범위한 커뮤니티 지원 때문입니다.

Arduino IDE에 ESP32-C6을 지원하는 최신 ESP32 Arduino Core가 있는지 확인하십시오. 코드를 컴파일하기 전에 이 RD-03D 라이브러리가 설치되어 있는지 확인하십시오. 그것을 다운로드하고 Arduino 라이브러리 폴더에 압축을 풉니다.

코드 개요

#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoJson.h>
#include "RadarSensor.h"

// #include "web_interface.h"
// WiFi credentials - Update these with your network details
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// Hardware pins
const int buzzerPin = 7;
const int ledPin = 15;    // Built-in LED
const int ledRPin = 21;   // Red LED
const int ledGPin = 22;   // Green LED
const int ledBPin = 23;   // Blue LED
// Radar sensor
RadarSensor radar(Serial1);
// Web server
WebServer server(80);
// System state variables
struct SystemConfig {
bool armed = true;
int detectionDistance = 1000;  // mm (1 meter default)
int alarmDuration = 10000;     // ms (10 seconds default)
bool alarmActive = false;
unsigned long alarmStartTime = 0;
bool systemEnabled = true;
};
SystemConfig config;
RadarTarget currentTarget;
bool targetDetected = false;
unsigned long lastBlinkTime = 0;
bool blinkState = false;
void setup() {
Serial.begin(115200);
Serial1.begin(256000, SERIAL_8N1, 17, 16); // D7 = RX, D6 = TX
// Initialize pins
pinMode(buzzerPin, OUTPUT);
pinMode(ledPin, OUTPUT);
pinMode(ledRPin, OUTPUT);
pinMode(ledGPin, OUTPUT);
pinMode(ledBPin, OUTPUT);
// Initialize radar
radar.begin();
Serial.println("Radar Sensor Started");
// Initialize WiFi
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println();
Serial.print("Connected! IP address: ");
Serial.println(WiFi.localIP());
// Setup web server routes
setupWebServer();
server.begin();
// Initial LED state
updateLEDs();
}
void loop() {
server.handleClient();
// Handle radar updates
if (radar.update()) {
currentTarget = radar.getTarget();
targetDetected = (currentTarget.distance > 0 &&
currentTarget.distance <= config.detectionDistance);
Serial.print("Distance: "); Serial.print(currentTarget.distance);
Serial.print("mm, Angle: "); Serial.print(currentTarget.angle);
Serial.print("°, Target: "); Serial.println(targetDetected ? "YES" : "NO");
}
// Handle alarm logic
handleAlarm();
// Update LEDs and buzzer
updateLEDs();
updateBuzzer();
delay(50);
}
void handleAlarm() {
if (!config.systemEnabled || !config.armed) {
config.alarmActive = false;
return;
}
if (targetDetected && !config.alarmActive) {
// Start alarm
config.alarmActive = true;
config.alarmStartTime = millis();
Serial.println("ALARM TRIGGERED!");
}
if (config.alarmActive) {
// Check if alarm duration exceeded
if (millis() - config.alarmStartTime >= config.alarmDuration) {
config.alarmActive = false;
Serial.println("Alarm timeout - stopping");
}
}
}
void updateLEDs() {
if (!config.systemEnabled) {
// System disabled - all LEDs off
digitalWrite(ledRPin, LOW);
digitalWrite(ledGPin, LOW);
digitalWrite(ledBPin, LOW);
return;
}
if (!config.armed) {
// System disarmed - blue LED on
digitalWrite(ledRPin, LOW);
digitalWrite(ledGPin, LOW);
digitalWrite(ledBPin, HIGH);
return;
}
if (config.alarmActive) {
// Alarm active - red LED blinks
unsigned long currentTime = millis();
if (currentTime - lastBlinkTime >= 250) {
blinkState = !blinkState;
lastBlinkTime = currentTime;
}
digitalWrite(ledRPin, blinkState ? HIGH : LOW);
digitalWrite(ledGPin, LOW);
digitalWrite(ledBPin, LOW);
} else if (targetDetected) {
// Target detected but not alarming - red LED solid
digitalWrite(ledRPin, HIGH);
digitalWrite(ledGPin, LOW);
digitalWrite(ledBPin, LOW);
} else {
// Normal operation - green LED on
digitalWrite(ledRPin, LOW);
digitalWrite(ledGPin, HIGH);
digitalWrite(ledBPin, LOW);
}
}
void updateBuzzer() {
if (config.alarmActive && config.systemEnabled) {
// Buzzer blinks at same rate as red LED
digitalWrite(buzzerPin, blinkState ? HIGH : LOW);
} else {
digitalWrite(buzzerPin, LOW);
}
}
void setupWebServer() {
// Serve main HTML page
server.on("/", HTTP_GET, []() {
server.send(200, "text/html", getMainHTML());
});
// API endpoints
server.on("/api/status", HTTP_GET, handleGetStatus);
server.on("/api/config", HTTP_POST, handleSetConfig);
server.on("/api/config", HTTP_GET, handleGetConfig);
server.on("/api/arm", HTTP_POST, handleArmDisarm);
server.on("/api/stop-alarm", HTTP_POST, handleStopAlarm);
server.on("/api/radar-data", HTTP_GET, handleGetRadarData);
// Enable CORS
server.enableCORS(true);
}
void handleGetStatus() {
StaticJsonDocument<300> doc;
doc["armed"] = config.armed;
doc["alarmActive"] = config.alarmActive;
doc["targetDetected"] = targetDetected;
doc["systemEnabled"] = config.systemEnabled;
doc["detectionDistance"] = config.detectionDistance;
doc["alarmDuration"] = config.alarmDuration;
doc["uptime"] = millis();
String response;
serializeJson(doc, response);
server.send(200, "application/json", response);
}
void handleGetConfig() {
StaticJsonDocument<200> doc;
doc["detectionDistance"] = config.detectionDistance;
doc["alarmDuration"] = config.alarmDuration;
doc["systemEnabled"] = config.systemEnabled;
String response;
serializeJson(doc, response);
server.send(200, "application/json", response);
}
void handleSetConfig() {
if (server.hasArg("plain")) {
StaticJsonDocument<200> doc;
deserializeJson(doc, server.arg("plain"));
if (doc.containsKey("detectionDistance")) {
config.detectionDistance = doc["detectionDistance"];
}
if (doc.containsKey("alarmDuration")) {
config.alarmDuration = doc["alarmDuration"];
}
if (doc.containsKey("systemEnabled")) {
config.systemEnabled = doc["systemEnabled"];
}
server.send(200, "application/json", "{\"success\":true}");
} else {
server.send(400, "application/json", "{\"error\":\"Invalid request\"}");
}
}
void handleArmDisarm() {
if (server.hasArg("plain")) {
StaticJsonDocument<100> doc;
deserializeJson(doc, server.arg("plain"));
if (doc.containsKey("armed")) {
config.armed = doc["armed"];
config.alarmActive = false; // Stop any active alarm
server.send(200, "application/json", "{\"success\":true}");
} else {
server.send(400, "application/json", "{\"error\":\"Missing armed parameter\"}");
}
} else {
server.send(400, "application/json", "{\"error\":\"Invalid request\"}");
}
}
void handleStopAlarm() {
config.alarmActive = false;
server.send(200, "application/json", "{\"success\":true}");
}
void handleGetRadarData() {
StaticJsonDocument<200> doc;
doc["distance"] = currentTarget.distance;
doc["angle"] = currentTarget.angle;
doc["x"] = currentTarget.x;
doc["y"] = currentTarget.y;
doc["speed"] = currentTarget.speed;
doc["detected"] = targetDetected;
String response;
serializeJson(doc, response);
server.send(200, "application/json", response);
}
String getMainHTML() {
return R"(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Radar Security System</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #1e3c72, #2a5298);
color: white;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
height: calc(100vh - 40px);
}
.panel {
background: rgba(255,255,255,0.1);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 20px;
border: 1px solid rgba(255,255,255,0.2);
}
.radar-panel {
display: flex;
flex-direction: column;
align-items: center;
}
.status {
text-align: center;
margin-bottom: 20px;
}
.status h1 {
font-size: 2.5em;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.status-indicator {
display: inline-block;
width: 20px;
height: 20px;
border-radius: 50%;
margin-left: 10px;
animation: pulse 2s infinite;
}
.armed { background: #4CAF50; }
.disarmed { background: #FF9800; }
.alarm { background: #F44336; animation: blink 0.5s infinite; }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
.radar-display {
width: 400px;
height: 400px;
position: relative;
margin: 20px 0;
}
.radar-svg {
width: 100%;
height: 100%;
background: radial-gradient(circle, rgba(0,255,0,0.1) 0%, rgba(0,100,0,0.05) 100%);
border-radius: 50%;
border: 2px solid #00ff00;
}
.controls {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-top: 20px;
}
.control-group {
background: rgba(255,255,255,0.05);
padding: 15px;
border-radius: 10px;
}
.control-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.control-group input, .control-group select {
width: 100%;
padding: 8px;
border: none;
border-radius: 5px;
background: rgba(255,255,255,0.1);
color: white;
margin-bottom: 10px;
}
.control-group input::placeholder {
color: rgba(255,255,255,0.7);
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
cursor: pointer;
font-weight: bold;
text-transform: uppercase;
transition: all 0.3s ease;
margin: 5px;
}
.btn-primary { background: #2196F3; color: white; }
.btn-success { background: #4CAF50; color: white; }
.btn-danger { background: #F44336; color: white; }
.btn-warning { background: #FF9800; color: white; }
.btn:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.3); }
.info-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-top: 20px;
}
.info-item {
background: rgba(255,255,255,0.05);
padding: 10px;
border-radius: 8px;
text-align: center;
}
.info-item .label { font-size: 0.9em; opacity: 0.8; }
.info-item .value { font-size: 1.2em; font-weight: bold; margin-top: 5px; }
@media (max-width: 768px) {
.container { grid-template-columns: 1fr; }
.radar-display { width: 300px; height: 300px; }
}
</style>
</head>
<body>
<div class="container">
<div class="panel">
<div class="status">
<h1>Radar Security <span id="statusIndicator" class="status-indicator armed"></span></h1>
<p id="statusText">System Armed</p>
</div>
<div class="controls">
<div class="control-group">
<label>Detection Distance (mm)</label>
<input type="number" id="detectionDistance" value="1000" min="100" max="8000">
<button class="btn btn-primary" onclick="updateConfig()">Update</button>
</div>
<div class="control-group">
<label>Alarm Duration (seconds)</label>
<input type="number" id="alarmDuration" value="10" min="1" max="300">
<button class="btn btn-primary" onclick="updateConfig()">Update</button>
</div>
<div class="control-group">
<label>System Control</label>
<button class="btn btn-success" id="armBtn" onclick="armSystem()">ARM</button>
<button class="btn btn-warning" id="disarmBtn" onclick="disarmSystem()">DISARM</button>
</div>
<div class="control-group">
<label>Alarm Control</label>
<button class="btn btn-danger" onclick="stopAlarm()">STOP ALARM</button>
<button class="btn btn-primary" onclick="toggleSystem()">ENABLE/DISABLE</button>
</div>
</div>
<div class="info-grid">
<div class="info-item">
<div class="label">Distance</div>
<div class="value" id="targetDistance">-- mm</div>
</div>
<div class="info-item">
<div class="label">Angle</div>
<div class="value" id="targetAngle">--°</div>
</div>
<div class="info-item">
<div class="label">Speed</div>
<div class="value" id="targetSpeed">-- cm/s</div>
</div>
<div class="info-item">
<div class="label">Status</div>
<div class="value" id="detectionStatus">Clear</div>
</div>
</div>
</div>
<div class="panel radar-panel">
<h2>Radar Display</h2>
<div class="radar-display">
<svg class="radar-svg" viewBox="0 0 400 400">
<!-- Radar grid -->
<defs>
<pattern id="radarGrid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="rgba(0,255,0,0.2)" stroke-width="1"/>
</pattern>
</defs>
<rect width="400" height="400" fill="url(#radarGrid)"/>
<!-- Distance circles -->
<circle cx="200" cy="400" r="50" fill="none" stroke="rgba(0,255,0,0.3)" stroke-width="1"/>
<circle cx="200" cy="400" r="100" fill="none" stroke="rgba(0,255,0,0.3)" stroke-width="1"/>
<circle cx="200" cy="400" r="150" fill="none" stroke="rgba(0,255,0,0.3)" stroke-width="1"/>
<circle cx="200" cy="400" r="200" fill="none" stroke="rgba(0,255,0,0.3)" stroke-width="1"/>
<!-- 120-degree arc -->
<path d="M 27 273 A 200 200 0 0 1 373 273" fill="none" stroke="#00ff00" stroke-width="2"/>
<!-- Center lines -->
<line x1="200" y1="400" x2="200" y2="200" stroke="rgba(0,255,0,0.5)" stroke-width="1"/>
<line x1="200" y1="400" x2="27" y2="273" stroke="rgba(0,255,0,0.5)" stroke-width="1"/>
<line x1="200" y1="400" x2="373" y2="273" stroke="rgba(0,255,0,0.5)" stroke-width="1"/>
<!-- Target dot -->
<circle id="targetDot" cx="200" cy="400" r="0" fill="#ff0000" stroke="#ffffff" stroke-width="2" opacity="0">
<animate attributeName="r" values="5;8;5" dur="1s" repeatCount="indefinite"/>
</circle>
<!-- Radar sweep -->
<line id="radarSweep" x1="200" y1="400" x2="200" y2="200" stroke="#00ff00" stroke-width="2" opacity="0.7">
<animateTransform attributeName="transform" attributeType="XML" type="rotate"
values="240 200 400;300 200 400;240 200 400" dur="3s" repeatCount="indefinite"/>
</line>
</svg>
</div>
<div style="text-align: center; margin-top: 10px;">
<small>Detection Range: 8m | Field of View: 120°</small>
</div>
</div>
</div>
<script>
let systemConfig = {
armed: true,
systemEnabled: true,
detectionDistance: 1000,
alarmDuration: 10000
};
function updateStatus() {
fetch('/api/status')
.then(response => response.json())
.then(data => {
const indicator = document.getElementById('statusIndicator');
const statusText = document.getElementById('statusText');
if (!data.systemEnabled) {
indicator.className = 'status-indicator disarmed';
statusText.textContent = 'System Disabled';
} else if (data.alarmActive) {
indicator.className = 'status-indicator alarm';
statusText.textContent = 'ALARM ACTIVE!';
} else if (data.armed) {
indicator.className = 'status-indicator armed';
statusText.textContent = 'System Armed';
} else {
indicator.className = 'status-indicator disarmed';
statusText.textContent = 'System Disarmed';
}
systemConfig = data;
});
}
function updateRadarData() {
fetch('/api/radar-data')
.then(response => response.json())
.then(data => {
document.getElementById('targetDistance').textContent = data.distance + ' mm';
document.getElementById('targetAngle').textContent = data.angle + '°';
document.getElementById('targetSpeed').textContent = data.speed + ' cm/s';
document.getElementById('detectionStatus').textContent = data.detected ? 'TARGET' : 'Clear';
updateRadarDisplay(data);
});
}
function updateRadarDisplay(data) {
const targetDot = document.getElementById('targetDot');
if (data.detected && data.distance > 0) {
// Convert distance to radar display coordinates
const maxDistance = 8000; // 8 meters in mm
const radarRadius = 200; // pixels
const scale = radarRadius / maxDistance;
// Convert angle and distance to x,y coordinates
const angleRad = (data.angle) * Math.PI / 180;
const distance = Math.min(data.distance, maxDistance);
const x = 200 + (distance * scale * Math.sin(angleRad));
const y = 400 - (distance * scale * Math.cos(angleRad));
targetDot.setAttribute('cx', x);
targetDot.setAttribute('cy', y);
targetDot.style.opacity = '1';
} else {
targetDot.style.opacity = '0';
}
}
function updateConfig() {
const distance = document.getElementById('detectionDistance').value;
const duration = document.getElementById('alarmDuration').value * 1000; // Convert to ms
fetch('/api/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
detectionDistance: parseInt(distance),
alarmDuration: parseInt(duration)
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Configuration updated successfully!');
}
});
}
function armSystem() {
fetch('/api/arm', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ armed: true })
});
}
function disarmSystem() {
fetch('/api/arm', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ armed: false })
});
}
function stopAlarm() {
fetch('/api/stop-alarm', { method: 'POST' });
}
function toggleSystem() {
fetch('/api/config', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ systemEnabled: !systemConfig.systemEnabled })
});
}
// Update data every 500ms
setInterval(() => {
updateStatus();
updateRadarData();
}, 500);
// Initial load
updateStatus();
updateRadarData();
</script>
</body>
</html>
)";
}



 

 

핵심 구성 요소

  1. WiFi 웹 서버: 원격 제어 및 모니터링 인터페이스
  2. 레이더 통합: RD-03D 센서 데이터(거리, 각도, 속도) 읽기
  3. 상태 관리: 무장/해제/경보 상태를 추적합니다.

 

 

 

주요 작업

루프 프로세스:

  1. 레이더 읽기: 목표 거리 및 위치 데이터 가져오기
  2. 위협 확인: 임계값 설정과 거리를 비교합니다.
  3. 제어 하드웨어: 상태에 따라 LED 및 부저 업데이트
  4. 웹 요청 처리: 웹 인터페이스에서 API 호출 처리

 

 

 

LED 상태

  1. 녹색: 무장 & 투명
  2. 빨간색: 대상이 감지됨(알람 중 깜박임)
  3. 파란색: 무장 해제
  4. 꺼짐: 시스템 비활성화

 

 

 

웹 API

  1. /api/status - 시스템 상태 및 센서 판독값
  2. /api/config - 감지 거리/알람 지속 시간 조정
  3. /api/arm - 시스템 시동/시동 해제
  4. /api/radar-data - 웹 레이더 디스플레이를 위한 라이브 데이터

 

 

 

스마트 로직

시스템이 무장되어 있고 범위 내에서 유효한 목표가 감지된 경우에만 경보를 트리거합니다. 구성 가능한 알람 타임아웃 및 실시간 120° 레이더 시각화 기능이 있는 전문 웹 인터페이스를 통한 원격 제어가 포함됩니다.

5단계: 액세스 Web 인터페이스

  1. 장치 켜기
  2. ESP32의 Wi-Fi 핫스팟 또는 로컬 네트워크 IP에 연결합니다.
  3. 브라우저에서 인터페이스 로드
  4. 설정: 최대 감지 거리(예: 3m)알람 기간(예: 5초)알람 켜기/끄기
  5. 레이더 아크에서 실시간 감지를 관찰합니다.

 

 

 

전력 및 배터리 수명

3.7V LiPo 셀은 Beetle 보드에 내장된 배터리 충전 관리 기능을 통해 충전되는 시스템에 전원을 공급합니다. RD-03D와 ESP32-C6의 저전력 소비로 인해 시스템은 한 번 충전으로 며칠 또는 몇 주 동안 작동할 수 있습니다. 딥 슬립 모드를 구현하면 배터리 수명을 몇 달 동안 연장할 수 있습니다.

 

 

 

레이더 보안 시스템의 작동 방식

이 시스템은 24GHz 전파를 방출하는 RD-03D 레이더 센서를 활용하여 120° 시야를 스캔하고 움직임을 감지하여 존재 여부뿐만 아니라 최대 8미터 떨어진 움직이는 물체의 속도, 각도 및 거리를 측정합니다. 열 신호에 의존하는 기존 PIR 센서와 달리 레이더는 완전한 어둠 속에서도 벽, 안개 또는 먼지를 통해 효과적으로 작동할 수 있으며 애완 동물이나 햇빛과 같은 일반적인 잘못된 트리거에 영향을 받지 않습니다. 이 시스템의 핵심은 들어오는 레이더 데이터를 실시간으로 처리하는 초고효율 소형 마이크로컨트롤러인 Beetle ESP32-C6입니다. 위협적이지 않은 움직임을 필터링하고 잠재적인 침입을 분류하는 스마트 탐지 엔진을 실행합니다. 위협이 감지되면 ESP32-C6는 시각 및 오디오 경고(LED 및 부저)를 트리거하고, 이벤트를 기록하고, 사용자가 실시간 레이더 시각화를 보고, 설정을 조정하고, 시스템을 원격으로 시동 또는 해제할 수 있는 웹 기반 대시보드를 업데이트합니다.

웹 인터페이스 및 시각화

ESP32는 웹 서버를 호스팅합니다. 사용자는 Wi-Fi를 통해 연결할 수 있으며 다음을 수행할 수 있습니다.

레이더 감지 거리 임계값 설정

  1. 레이더 감지 거리 임계값 설정
  2. 알람 지속 시간 구성(부저가 울리고 LED가 깜박이는 시간)
  3. 경보 시스템 켜기/끄기
  4. 레이더 감지 시각화를 120° 스윕으로 보기
  5. 실시간 감지 업데이트 모니터링

레이더 감지는 JavaScript 캔버스 요소에 그려져 해양 레이더와 유사한 호 기반 시각적 개체를 형성합니다.

Ai Thinker의 적절한 센서 장착 권장 사항

 

 

 

사용 사례 시나리오

  1. 집 입구를 모니터링하여 창문이나 유리를 통해 움직임이 있는지 확인합니다.
  2. 침입자가 가만히 있을 때도 존재 감지
  3. 스마트 에너지 관리(방에 아무도 없을 때 조명이 꺼짐)
  4. 카메라 없이 수용 인원을 감지하는 노인 요양 시스템

 

 

 

Home Assistant 통합(선택 사항)

MQTT 또는 REST API 엔드포인트를 사용하여 레이더를 홈어시스턴트 대시보드에 연결합니다.

  1. MQTT 또는 REST API 엔드포인트를 사용하여 레이더를 홈어시스턴트 대시보드에 연결합니다.
  2. 다음과 같은 자동화를 트리거합니다.
  3. 조명 켜기
  4. 알림 보내기
  5. 카메라 활성화

 

 

 

성능 최적화

  1. 센서 배치: 최상의 적용 범위를 위해 최적의 높이(2-3미터)에 장착
  2. 환경 요인: 실외 설치를 위한 날씨 차폐를 고려하십시오.
  3. 네트워크 최적화: 최상의 성능을 위해 전용 IoT 네트워크 사용

 

 

 

향후 개선 사항

이 프로젝트는 고급 보안 기능의 기초 역할을 합니다.

  1. 맞춘 PCB: 보다 강력한 시스템을 위해
  2. 드론 통합: 주변 모니터링을 위한 자동 드론 배치
  3. Professional Monitoring: 클라우드 기반 보안 서비스 통합
  4. 모바일 앱: 향상된 제어를 위한 전용 스마트폰 애플리케이션

 

 

 

 더 스마트하고 강력한 홈 보안

이 DIY 레이더 보안 시스템은 신뢰할 수 있는 고급 보호 기능이 높은 가격표와 함께 제공될 필요가 없다는 것을 증명합니다. RD-03D 센서와 ESP32-C6 Beetle을 사용하면 기존 PIR 설정을 능가하는 정확하고 비바람에 견디는 스마트 모션 감지를 얻을 수 있습니다. 컴팩트하고 배터리로 구동되며 스마트 홈과 완전히 통합됩니다. 가정, 작업장 또는 외딴 건물에 관계없이 이 프로젝트는 요구 사항에 맞게 조정되는 전문가 수준의 보안을 제공합니다.

 

결론

RD-03D 및 ESP32-C6을 활용하면 저비용으로 상용 솔루션에 버금가는 고급 보안 시스템을 구축할 수 있습니다. 기존 PIR 센서의 한계를 극복하고, 지능적인 필터링을 통해 더욱 신뢰할 수 있는 감지를 제공합니다.

이 프로젝트를 확장하여 더 많은 기능을 추가할 수도 있습니다. 예를 들면, 자동화된 스마트 경보, AI 기반 침입자 패턴 분석, 홈 네트워크와의 통합 등이 가능합니다.

728x90
반응형