반응형
1개에 16달러 정도 하는 DFRobot C4001 (SEN0609) mmWave 센서를 구매했다.
https://fermium.tistory.com/1935
HLK-LD2410 (밀리미터파 레이더센서모듈)
보통의 PIR센서는 사람이 움직이지 않으면 없는 것으로 간주된다. 화장실에서 오래 앉아 있으면 불어 꺼져서 곤란했던 적도 있을 것이다. HLK-LD2410라는 센서에 흥미가 생겨서 구매를 했는데, 이건
fermium.tistory.com
이전에 구입한 센서와 다른 점은
25 m 거리까지 측정이 가능하다는 점이다.


마이크로파를 이용하여 사람의 존재 여부, 움직임 여부 등을 측정할 수 있다.
일반적인 PIR 센서는 사람이 움직이지 않으면 사람이 없는 것으로 간주하는데,
이것은 멈춰 있는지 움직이는지 전부 측정이 가능하다.
<ESP32-S3 SuperMini로 마이크로 센서를 이용할 수 있는 샘플>
|
/*******************************************************************************
* ESP32-S3 SuperMini + DFRobot C4001 (SEN0609) mmWave 센서
*
* 배선 (C4001 → ESP32-S3 SuperMini)
* 5V → 5V
* GND → GND
* OUT → GPIO13 (하드웨어 존재감 출력 핀)
* TX → GPIO44 (ESP32 RX)
* RX → GPIO43 (ESP32 TX)
*
* 검출 데이터
* ① Presence 존재 감지 (OUT 핀 + UART)
* ② Motion 움직임 감지
* ③ Distance 거리 (m)
* ④ Speed 속도 (m/s)
* ⑤ Power 신호 강도
* ⑥ Human State 인체 상태 5단계 추정
*
* UART 보고 포맷
* 존재감지 모드: $DFHPD,<motion>,<presence>,<distance>,<speed>,<power>*
* 속도거리 모드: $DFDMD,<motion>,<distance>,<speed>,<power>*
*
* Arduino IDE 설정
* 보드 : ESP32S3 Dev Module
* USB Mode: USB-OTG (TinyUSB) 또는 Hardware CDC
* Upload : USB
* Upload Speed: 921600
*
* 시리얼 모니터: 115200 baud
*******************************************************************************/
// ── 핀 정의 ──────────────────────────────────────────────────────────────────
#define OUT_PIN 13 // C4001 OUT → GPIO13 (하드웨어 존재감 출력)
#define SENSOR_RX 44 // C4001 TX → ESP32 RX
#define SENSOR_TX 43 // C4001 RX → ESP32 TX
// ── UART 설정 ─────────────────────────────────────────────────────────────────
#define SENSOR_BAUD 9600 // C4001 기본 Baud Rate
HardwareSerial SensorSerial(1); // UART1 사용
// ── 설정 파라미터 (필요에 따라 수정) ─────────────────────────────────────────
#define RANGE_MIN 0.6f // 최소 감지 거리 (m)
#define RANGE_MAX 6.0f // 최대 감지 거리 (m)
#define TRIG_RANGE 6.0f // 트리거 거리 (m)
#define HOLD_SENS 7 // 유지 감도 (0~9)
#define TRIG_SENS 5 // 트리거 감도 (0~9)
#define LATENCY_ON 0.5f // 확인 지연 (s)
#define LATENCY_OFF 15.0f // 소멸 지연 (s)
#define RUN_MODE 0 // 0=존재감지, 1=속도거리측정
// ── 데이터 구조체 ─────────────────────────────────────────────────────────────
struct SensorData {
bool presence = false; // 존재 감지
bool motion = false; // 움직임 감지
float distance = 0.0f; // 거리 (m)
float speed = 0.0f; // 속도 (m/s)
int power = 0; // 신호 강도
bool outPin = false; // OUT 핀 상태
String mode = ""; // 센서 동작 모드
String humanState = ""; // 인체 상태 추정
};
SensorData sData;
// ── UART 수신 버퍼 ────────────────────────────────────────────────────────────
char rxBuf[256];
int rxPos = 0;
// ── 인체 상태 추정 함수 ───────────────────────────────────────────────────────
String estimateHumanState(bool presence, bool motion, float dist, float spd) {
if (!presence && !motion) return "Absent";
if (presence && !motion) {
if (dist < 1.5f) return "Static (Near)";
if (dist < 4.0f) return "Static (Mid)";
return "Static (Far)";
}
if (spd < 0.3f) return "Slight Motion";
if (spd < 1.0f) return "Active";
return "Moving";
}
// ── $DFHPD 파싱 ──────────────────────────────────────────────────────────────
// 포맷: $DFHPD,<motion>,<presence>,<distance>,<speed>,<power>*
void parseHPD(const char* payload) {
int mot = 0, prs = 0;
float dst = 0, spd = 0;
int pwr = 0;
// '*' 이전까지만 파싱
char tmp[128];
strncpy(tmp, payload, sizeof(tmp) - 1);
tmp[sizeof(tmp) - 1] = '\0';
char* star = strchr(tmp, '*');
if (star) *star = '\0';
int n = sscanf(tmp, "%d,%d,%f,%f,%d", &mot, &prs, &dst, &spd, &pwr);
if (n < 2) return;
sData.motion = (mot == 1);
sData.presence = (prs == 1);
if (n >= 3) sData.distance = dst;
if (n >= 4) sData.speed = spd;
if (n >= 5) sData.power = pwr;
sData.mode = "Presence Detection";
sData.humanState = estimateHumanState(sData.presence, sData.motion,
sData.distance, sData.speed);
}
// ── $DFDMD 파싱 ──────────────────────────────────────────────────────────────
// 포맷: $DFDMD,<motion>,<distance>,<speed>,<power>*
void parseDMD(const char* payload) {
int mot = 0;
float dst = 0, spd = 0;
int pwr = 0;
char tmp[128];
strncpy(tmp, payload, sizeof(tmp) - 1);
tmp[sizeof(tmp) - 1] = '\0';
char* star = strchr(tmp, '*');
if (star) *star = '\0';
int n = sscanf(tmp, "%d,%f,%f,%d", &mot, &dst, &spd, &pwr);
if (n < 1) return;
sData.motion = (mot == 1);
if (n >= 2) sData.distance = dst;
if (n >= 3) sData.speed = spd;
if (n >= 4) sData.power = pwr;
sData.mode = "Speed/Distance";
sData.humanState = estimateHumanState(sData.presence, sData.motion,
sData.distance, sData.speed);
}
// ── 라인 파싱 ─────────────────────────────────────────────────────────────────
void parseLine(const char* line) {
if (strlen(line) < 4) return;
Serial.print("[RAW] "); Serial.println(line);
if (strncmp(line, "$DFHPD,", 7) == 0) {
parseHPD(line + 7);
printData();
} else if (strncmp(line, "$DFDMD,", 7) == 0) {
parseDMD(line + 7);
printData();
} else if (strncmp(line, "Response", 8) == 0) {
Serial.print("[CFG] "); Serial.println(line);
} else if (strcmp(line, "Done") == 0) {
Serial.println("[ACK] Done");
} else if (strcmp(line, "Error") == 0) {
Serial.println("[ERR] Command Error");
}
}
// ── 데이터 출력 ───────────────────────────────────────────────────────────────
void printData() {
// OUT 핀 갱신
sData.outPin = digitalRead(OUT_PIN);
Serial.println("┌─────────────────────────────────────┐");
Serial.print ("│ Mode : "); Serial.println(sData.mode);
Serial.print ("│ OUT Pin : "); Serial.println(sData.outPin ? "HIGH (존재)" : "LOW (없음)");
Serial.print ("│ Presence : "); Serial.println(sData.presence ? "YES" : "NO");
Serial.print ("│ Motion : "); Serial.println(sData.motion ? "YES" : "NO");
Serial.print ("│ Distance : "); Serial.print(sData.distance, 2); Serial.println(" m");
Serial.print ("│ Speed : "); Serial.print(sData.speed, 2); Serial.println(" m/s");
Serial.print ("│ Power : "); Serial.println(sData.power);
Serial.print ("│ Human State: "); Serial.println(sData.humanState);
Serial.println("└─────────────────────────────────────┘");
}
// ── 센서 명령 전송 헬퍼 ───────────────────────────────────────────────────────
void sendCmd(const char* cmd, uint32_t waitMs = 200) {
Serial.print("[CMD] "); Serial.println(cmd);
SensorSerial.print(cmd);
SensorSerial.print("\r\n");
delay(waitMs);
}
// ── 센서 초기화 시퀀스 ───────────────────────────────────────────────────────
void initSensor() {
Serial.println("\n[INIT] C4001 초기화 시작...");
delay(3000); // 센서 부팅 안정화 (공식 권장 3초)
sendCmd("sensorStop", 500);
// 동작 모드 설정 (0=존재감지, 1=속도거리)
char cmd[64];
snprintf(cmd, sizeof(cmd), "setRunApp %d", RUN_MODE);
sendCmd(cmd);
// 감지 거리 설정
snprintf(cmd, sizeof(cmd), "setRange %.1f %.1f", RANGE_MIN, RANGE_MAX);
sendCmd(cmd);
// 트리거 거리 설정
snprintf(cmd, sizeof(cmd), "setTrigRange %.1f", TRIG_RANGE);
sendCmd(cmd);
// 감도 설정 (유지 / 트리거)
snprintf(cmd, sizeof(cmd), "setSensitivity %d %d", HOLD_SENS, TRIG_SENS);
sendCmd(cmd);
// 출력 지연 설정 (확인지연 / 소멸지연)
snprintf(cmd, sizeof(cmd), "setLatency %.1f %.1f", LATENCY_ON, LATENCY_OFF);
sendCmd(cmd);
// Flash 저장
sendCmd("saveConfig", 500);
// 센서 시작
sendCmd("sensorStart", 300);
Serial.println("[INIT] 초기화 완료 - 데이터 수신 대기 중...\n");
}
// ── OUT 핀 인터럽트 (선택사항: 하드웨어 존재감 변화 즉시 감지) ───────────────
volatile bool outPinChanged = false;
void IRAM_ATTR onOutPinChange() {
outPinChanged = true;
}
// ── setup ─────────────────────────────────────────────────────────────────────
void setup() {
// USB 시리얼 (시리얼 모니터)
Serial.begin(115200);
delay(1000);
while (!Serial) delay(10);
Serial.println("====================================");
Serial.println(" DFRobot C4001 (SEN0609) mmWave");
Serial.println(" ESP32-S3 SuperMini");
Serial.println("====================================");
// OUT 핀 설정
pinMode(OUT_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(OUT_PIN), onOutPinChange, CHANGE);
// 센서 UART 시작
SensorSerial.begin(SENSOR_BAUD, SERIAL_8N1, SENSOR_RX, SENSOR_TX);
Serial.printf("[UART] Sensor UART 시작 (RX=%d TX=%d Baud=%d)\n",
SENSOR_RX, SENSOR_TX, SENSOR_BAUD);
// 센서 초기화
initSensor();
}
// ── loop ──────────────────────────────────────────────────────────────────────
void loop() {
// ── UART 수신 처리 ──────────────────────────────────────────────────────────
while (SensorSerial.available()) {
char c = SensorSerial.read();
if (c == '\n') {
rxBuf[rxPos] = '\0';
if (rxPos > 0) parseLine(rxBuf);
rxPos = 0;
} else if (c != '\r' && rxPos < (int)sizeof(rxBuf) - 1) {
rxBuf[rxPos++] = c;
}
}
// ── OUT 핀 변화 감지 ────────────────────────────────────────────────────────
if (outPinChanged) {
outPinChanged = false;
bool pinState = digitalRead(OUT_PIN);
Serial.printf("[OUT] OUT 핀 변화 → %s\n", pinState ? "HIGH (존재)" : "LOW (없음)");
sData.outPin = pinState;
}
// ── 시리얼 모니터 명령 입력 처리 ────────────────────────────────────────────
// 시리얼 모니터에서 명령어를 직접 센서로 전달 (디버깅용)
// 예: sensorStop, sensorStart, getRange, getTrigRange ...
if (Serial.available()) {
String input = Serial.readStringUntil('\n');
input.trim();
if (input.length() > 0) {
// 내장 단축 명령어
if (input == "stop") { sendCmd("sensorStop"); }
else if (input == "start") { sendCmd("sensorStart"); }
else if (input == "save") { sendCmd("saveConfig"); }
else if (input == "reset") { sendCmd("resetSystem 0"); }
else if (input == "hpd") { sendCmd("sensorStop"); delay(300);
sendCmd("setRunApp 0"); delay(200);
sendCmd("saveConfig"); delay(300);
sendCmd("sensorStart"); }
else if (input == "dmd") { sendCmd("sensorStop"); delay(300);
sendCmd("setRunApp 1"); delay(200);
sendCmd("saveConfig"); delay(300);
sendCmd("sensorStart"); }
else if (input == "status") { printData(); }
else if (input == "help") { printHelp(); }
else {
// 그 외 명령은 센서로 직접 전달
sendCmd(input.c_str());
}
}
}
}
// ── 도움말 출력 ───────────────────────────────────────────────────────────────
void printHelp() {
Serial.println("\n[HELP] 시리얼 모니터 단축 명령어");
Serial.println(" stop - 센서 정지 (sensorStop)");
Serial.println(" start - 센서 시작 (sensorStart)");
Serial.println(" save - 설정 저장 (saveConfig)");
Serial.println(" reset - 소프트 리셋 (resetSystem 0)");
Serial.println(" hpd - 존재감지 모드로 전환");
Serial.println(" dmd - 속도/거리 측정 모드로 전환");
Serial.println(" status - 현재 데이터 출력");
Serial.println(" help - 이 도움말");
Serial.println("[HELP] 그 외: 센서 명령어 직접 입력 가능");
Serial.println(" 예) setRange 0.6 10");
Serial.println(" 예) setSensitivity 8 6");
Serial.println(" 예) setLatency 0.5 30");
Serial.println(" 예) getRange / getTrigRange / getSensitivity\n");
}
|
일단 어떻게 작동하는지는 확인했다.
반응형