한이음 공모전 — 몰카 탐지기 (ESP32 + 마그네틱 + WiFi 스캔)
공모전 주제로 몰카 탐지기를 만들었다. 마그네틱 + WiFi RF 두 채널로 가능성을 늘렸다.
목차
대학 한이음 공모전 주제로 휴대용 몰카 탐지기를 팀으로 만들었다.
스마트폰 앱과 BLE 통신, ESP32 보드에 마그네틱 + WiFi 스캐너 두 종류 센서, FastAPI 서버 + PostgreSQL로 데이터 적재.
왜 두 채널인가
상용 몰카 탐지기는 보통 렌즈 반사(IR LED + 카메라)나 RF 스캔 둘 중 하나만 한다. 둘 다 한계가 있어서 우리는 두 채널 + 알파를 섞었다.
| 방식 | 장점 | 단점 |
|---|---|---|
| 렌즈 반사 | 카메라 구분 없이 다 잡힘 | 시야 안에 정확히 있어야, 사용자 숙련 필요 |
| RF 스캔 (WiFi/Bluetooth) | 멀리서도 잡힘 | 송신 안 하는 카메라 (저장형)는 못 잡음 |
| 마그네틱 | 카메라 모듈의 모터/전자석 감지 | 강자성체 일반에도 반응 (오탐 ↑) |
세 채널을 종합하면 한 채널이 못 잡는 걸 다른 채널이 보완.
하드웨어 구성
ESP32-WROOM-32 보드
├─ HMC5883L 마그네틱 센서 (I2C)
├─ ESP32 내장 WiFi (RF 스캔용)
├─ BLE (스마트폰 통신)
└─ 진동 모터 + LED (탐지 알림)
ESP32는 WiFi + BLE가 한 칩에 다 들어있어서 그 자체가 RF 스캐너 + 컨트롤러. 가격은 5천원.
마그네틱 센서는 Adafruit HMC5883L. 3축 자기장 감지, μT 단위. I2C로 ESP32에 연결.
펌웨어 — Arduino-ESP32
#include <Wire.h>
#include <Adafruit_HMC5883_U.h>
#include <WiFi.h>
#include <BLEDevice.h>
Adafruit_HMC5883_Unified mag(12345);
const float BASELINE_uT = 50.0;
const float THRESHOLD_uT = 80.0;
void setup() {
mag.begin();
WiFi.mode(WIFI_STA);
WiFi.disconnect();
BLEDevice::init("HiddenCamDetector");
// BLE characteristic 설정 (스마트폰과 통신)
}
void loop() {
// 1) 마그네틱 측정
sensors_event_t event;
mag.getEvent(&event);
float strength = sqrt(pow(event.magnetic.x, 2) +
pow(event.magnetic.y, 2) +
pow(event.magnetic.z, 2));
bool magnetic_anomaly = (strength > THRESHOLD_uT);
// 2) WiFi 스캔
int n = WiFi.scanNetworks();
std::vector<String> suspicious;
for (int i = 0; i < n; i++) {
String ssid = WiFi.SSID(i);
int rssi = WiFi.RSSI(i);
// 의심 SSID 패턴: "IPCAM", "HC*", "CamHi" 등
if (matches_camera_pattern(ssid) && rssi > -60) {
suspicious.push_back(ssid + " (" + String(rssi) + " dBm)");
}
}
// 3) BLE로 결과 송신
send_ble_alert(magnetic_anomaly, suspicious);
delay(2000);
}
WiFi 스캔과 마그네틱을 1초씩 번갈아 한다.
의심 SSID 패턴
상용 몰카는 자주 다음 패턴의 AP를 띄움:
IPCAM-A1B2C3
HC-1234
CamHi_xxx
P2P_xxx
ANRAN_xxx
SUNLUXY_xxx
이런 패턴을 정규식으로 매칭. 100% 정답은 아니지만 false positive를 줄이는 데 효과적.
백엔드 — FastAPI + PostgreSQL
탐지 이벤트를 서버로 올려 시간/위치별 핫스팟 지도를 그릴 수 있게.
from fastapi import FastAPI
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
app = FastAPI()
@app.post("/detection")
async def report_detection(data: DetectionEvent):
db = SessionLocal()
try:
record = Detection(
user_id=data.user_id,
magnetic_uT=data.magnetic,
wifi_ssids=data.wifi_ssids,
location=data.gps, # 스마트폰에서 GPS 첨부
ts=datetime.utcnow()
)
db.add(record)
db.commit()
finally:
db.close()
return {"status": "ok"}
@app.get("/hotspots")
async def get_hotspots():
# 최근 24시간, 위경도 그리드별 탐지 빈도
db = SessionLocal()
return db.query(Detection).filter(...).all()
스마트폰 앱이 ESP32에서 BLE로 받은 이벤트 + 자기 GPS를 합쳐서 서버로 POST.
팀 협업에서 부딪힌 것들
git 충돌. Arduino IDE 사용자 한 명, PlatformIO 사용자 두 명, VS Code 사용자 세 명. 각자 IDE가 만드는 메타파일이 충돌. .gitignore 정비에 이틀 쓰임.
# Arduino
*.cache/
.vs/
build/
# PlatformIO
.pio/
.pioenvs/
.piolibdeps/
# VS Code (개인 설정만 무시)
.vscode/launch.json
.vscode/c_cpp_properties.json
.vscode/settings.json은 팀 공유, 나머지 IDE 메타는 .gitignore.
보드 매니저 버전 통일. ESP32 보드 매니저 2.0.x vs 3.0.x에서 WiFi.scanNetworks() 동작이 미묘하게 다름. 팀 공통 버전 픽스 필수. platformio.ini에 명시:
[env:esp32dev]
platform = [email protected]
framework = arduino
board = esp32dev
“서버”가 뭔지 처음 가르쳐야 했음. 팀원 두 명이 서버 개념이 처음. “Backend는 클라우드의 컴퓨터에서 돌고, FastAPI는 거기서 HTTP 요청을 받는 코드”부터 시작. 입문자에겐 너무 추상적이라 그림으로 그렸음.
만들면서 알게 된 것들
ESP32는 5천원짜리 만능 IoT 보드다. WiFi + BLE + I2C + ADC + DAC + 32bit MCU. 전공생이 첫 IoT 프로젝트로 쓰기 가장 좋음.
BLE characteristics 설계가 핵심. ESP32 ↔ 스마트폰 BLE 통신은 GATT characteristic 설계가 첫 단추. 너무 잘게 쪼개면 latency, 너무 크게 묶으면 유연성 손해. JSON 직렬화 후 한 characteristic에 쓰는 게 정석.
오탐 vs 미탐 트레이드오프. 마그네틱 임계값 80μT로 잡으면 모터/소형가전에 모두 반응. 100μT로 올리면 진짜 카메라 모듈만 반응하는데 위치를 정확히 갖다대야 함. 기본은 낮게 + 여러 차례 측정으로 보강.
팀 작업의 80%는 코딩이 아니라 의사소통이다. 환경 통일, 컨벤션, 그림 설명. 이게 깨지면 코드 한 줄도 못 나간다.
한 줄 결론
ESP32 + 멀티채널 센서 + BLE + FastAPI 백엔드는 공모전 규모 프로젝트의 기본기다. 비싼 도구 없이 5천원 + Python 서버로 충분히 실용적인 시스템이 나온다.
다음 단계: AI 모델로 SSID + 마그네틱 패턴 분류 → 오탐 줄이기. 학습 데이터 모으는 중.