#include <TM1637.h>
#include <dht.h>
/* ================== PIN MAP ================== */
const uint8_t PIN_LED_Y = 7;
const uint8_t PIN_LED_B = 6;
const uint8_t PIN_LED_G = 5;
const uint8_t PIN_LED_R = 4;
const uint8_t PIN_BUZZ = 3;
const uint8_t PIN_TM_CLK = 10;
const uint8_t PIN_TM_DIO = 11;
const uint8_t PIN_BTN_K1 = 9; // Disarm (NO -> GND, INPUT_PULLUP)
const uint8_t PIN_BTN_K2 = 8; // Panic (NO -> GND, INPUT_PULLUP)
const uint8_t PIN_LDR = A2;
const uint8_t PIN_POT = A0;
const uint8_t PIN_DHT = 12;
/* =============== DEVICES/OBJECTS ============== */
TM1637 tm;
dht DHT;
/* ================== CONSTANTS ================= */
const float TEMP_LIMIT_C = 35.0;
const uint16_t DISARM_MIN = 500;
const uint16_t DISARM_MAX = 520;
const uint32_t DISARM_HOLD_MS = 2000;
const uint16_t LDR_DELTA_TRIP = 180;
const uint32_t LDR_MIN_HOLD_MS = 100; // <<< NEW: LDR ต้องเกิน threshold ต่อเนื่อง >=100ms
const uint32_t LDR_CAL_TIME_MS = 1500;
const uint32_t SIREN_TOGGLE_MS = 250;
const uint32_t BLINK_MS = 200;
const uint32_t DHT_MIN_INTERVAL_MS = 2200;
const uint32_t PRINT_INTERVAL_MS = 1000;
/* ==================== STATE =================== */
enum SysState { ARMED_SECURE=0, ALARM_TRIGGERED, DISARM_MODE };
SysState state = ARMED_SECURE, lastState = (SysState)255;
/* =================== RUNTIME ================== */
uint32_t tLastDHTRead=0, tLastPrint=0, tLastBlink=0, tLastSiren=0, tDisarmStart=0, tStart=0;
uint32_t ignoreButtonsUntil = 0;
bool sirenHigh=false, blinkFlag=false;
float lastTempC = NAN;
float lastOkTempC = 0;
int ldrBaseline = 0;
int lastLDRRaw = -1;
uint32_t ldrOverStart = 0; // <<< NEW
/* ================== HELPERS =================== */
// ดีบาวซ์ + ต้องกดค้าง >= hold_ms ถึงจะนับว่า "กด"
bool readButtonHeld(uint8_t pin, uint16_t hold_ms = 80) { // <<< NEW
if (millis() < ignoreButtonsUntil) return false;
static uint32_t tPressStart[32] = {0};
bool pressed = (digitalRead(pin) == LOW); // INPUT_PULLUP: LOW = กด
if (pressed) {
if (tPressStart[pin] == 0) tPressStart[pin] = millis();
return (millis() - tPressStart[pin]) >= hold_ms;
} else {
tPressStart[pin] = 0;
return false;
}
}
// (เดิม) แสดงผล TM1637
void showALRT() { char s[5]="ALrt"; tm.displayPChar(s); }
void showDashes() { char s[5]="----"; tm.displayPChar(s); }
void showTempC_int(int tC){
char b[5]=" ";
if (tC>=100){ snprintf(b,sizeof(b),"%03d",tC); b[3]='C'; }
else if (tC>=10){ snprintf(b,sizeof(b)," %2d",tC); b[3]='C'; }
else if (tC>=0){ snprintf(b,sizeof(b)," %d",tC); b[3]='C'; }
else { int v=-tC, d=v%10; b[0]='-'; b[1]='0'+d; b[2]=' '; b[3]='C'; }
tm.displayPChar(b);
}
const char* dhtStatusText(int rc){
if (rc==DHTLIB_OK) return "OK";
#ifdef DHTLIB_ERROR_CHECKSUM
if (rc==DHTLIB_ERROR_CHECKSUM) return "CHECKSUM";
#endif
#ifdef DHTLIB_ERROR_TIMEOUT
if (rc==DHTLIB_ERROR_TIMEOUT) return "TIMEOUT";
#endif
static char buf[16]; snprintf(buf,sizeof(buf),"ERR(%d)",rc); return buf;
}
bool readDHTIfDue(){
uint32_t now=millis();
if (now - tLastDHTRead < DHT_MIN_INTERVAL_MS) return false;
tLastDHTRead = now;
int rc = DHT.read22(PIN_DHT);
if (rc==DHTLIB_OK){
lastTempC = DHT.temperature;
if (!isnan(lastTempC) && lastTempC>-10 && lastTempC<80) lastOkTempC=lastTempC;
} else {
Serial.print(F("[DHT] status: ")); Serial.println(dhtStatusText(rc));
}
return true;
}
/* ---- Intrusion from LDR (with hold-time) ---- */
bool intrusionAlarmNow(){
lastLDRRaw = analogRead(PIN_LDR);
bool over = (abs(lastLDRRaw - ldrBaseline) >= LDR_DELTA_TRIP);
if (over) {
if (ldrOverStart == 0) ldrOverStart = millis();
return (millis() - ldrOverStart) >= LDR_MIN_HOLD_MS;
} else {
ldrOverStart = 0;
return false;
}
}
void sirenUpdate(bool on){
uint32_t now=millis();
if (!on){ noTone(PIN_BUZZ); return; }
if (now - tLastSiren >= SIREN_TOGGLE_MS){
tLastSiren = now; sirenHigh=!sirenHigh; tone(PIN_BUZZ, sirenHigh?1200:700);
}
}
void setSecureLights(){
digitalWrite(PIN_LED_G,HIGH);
digitalWrite(PIN_LED_R,LOW);
digitalWrite(PIN_LED_Y,LOW);
digitalWrite(PIN_LED_B,LOW);
}
void alarmBlinkLights(){
uint32_t now=millis();
if (now - tLastBlink >= BLINK_MS){
tLastBlink=now; blinkFlag=!blinkFlag;
digitalWrite(PIN_LED_Y, blinkFlag?HIGH:LOW);
digitalWrite(PIN_LED_R, blinkFlag?LOW:HIGH);
}
digitalWrite(PIN_LED_G,LOW); digitalWrite(PIN_LED_B,LOW);
}
void disarmLights(){ digitalWrite(PIN_LED_B,HIGH); }
bool potInDisarmWindow(){ int v=analogRead(PIN_POT); return (v>=DISARM_MIN && v<=DISARM_MAX); }
/* =================== SETUP =================== */
void setup(){
pinMode(PIN_LED_Y,OUTPUT); pinMode(PIN_LED_B,OUTPUT);
pinMode(PIN_LED_G,OUTPUT); pinMode(PIN_LED_R,OUTPUT);
pinMode(PIN_BUZZ,OUTPUT);
pinMode(PIN_BTN_K1,INPUT_PULLUP); pinMode(PIN_BTN_K2,INPUT_PULLUP);
tm.begin(PIN_TM_CLK, PIN_TM_DIO, 4);
tm.setBrightness(7); tm.displayClear(); showDashes();
Serial.begin(9600);
Serial.println("Security Box Started");
// คาลิเบรต LDR baseline ตอนบูต (มีเซ็นเซอร์จริง)
tStart=millis(); long acc=0; uint16_t n=0;
while (millis()-tStart < LDR_CAL_TIME_MS) { acc+=analogRead(PIN_LDR); n++; delay(5); }
if (n==0) n=1; ldrBaseline = acc/(int)n;
Serial.print(F("[LDR] baseline: ")); Serial.println(ldrBaseline);
delay(1500); readDHTIfDue();
Serial.print(F("[DHT] first temperature: ")); Serial.println(lastOkTempC,1);
setSecureLights(); // Green LED
ignoreButtonsUntil = millis() + 2000; // กันกลิตช์ช่วงบูต
}
/* ==================== LOOP ==================== */
void loop(){
// 0) PANIC (ต้องกดค้าง >=80ms)
if (readButtonHeld(PIN_BTN_K2, 80)) { // <<< NEW
Serial.println(F("[CAUSE] PANIC_K2")); // <<< NEW
state = ALARM_TRIGGERED;
}
// 1) Update sensors
readDHTIfDue();
bool tempAlarmActive = (lastOkTempC > TEMP_LIMIT_C);
bool intrusionAlarmActive = intrusionAlarmNow();
// 2) Telemetry ทุก 1 วิ (รวมสถานะปุ่มดิบ)
if (millis() - tLastPrint >= PRINT_INTERVAL_MS){
tLastPrint = millis();
int pot = analogRead(PIN_POT);
int k1Raw = digitalRead(PIN_BTN_K1);
int k2Raw = digitalRead(PIN_BTN_K2);
Serial.print(F("Temperature (C): ")); Serial.print(lastOkTempC,1);
Serial.print(F(" | Pot: ")); Serial.print(pot);
Serial.print(F(" | LDR: ")); Serial.print(lastLDRRaw);
Serial.print(F(" (base=")); Serial.print(ldrBaseline);
Serial.print(F(", Δ=")); Serial.print(abs(lastLDRRaw - ldrBaseline)); Serial.print(F(")"));
Serial.print(F(" | TEMP_ALARM=")); Serial.print(tempAlarmActive?"1":"0");
Serial.print(F(" | INTRUSION_ALARM=")); Serial.print(intrusionAlarmActive?"1":"0");
Serial.print(F(" | PANIC_ALARM=")); Serial.print(k2Raw==LOW?1:0); // raw
Serial.print(F(" | DISARM_BTN=")); Serial.println(k1Raw==LOW?1:0); // raw
}
// 3) Log mode change
if (state != lastState){
switch(state){
case ARMED_SECURE: Serial.println("===ARMED_SECURE==="); break;
case ALARM_TRIGGERED: Serial.println("===ALARM_TRIGGERED==="); break;
case DISARM_MODE: Serial.println("===DISARM_MODE==="); break;
}
lastState = state;
}
// 4) เข้าสู่ ALARM จากเซ็นเซอร์ (และพิมพ์เหตุ)
if (state == ARMED_SECURE && (tempAlarmActive || intrusionAlarmActive)){
if (tempAlarmActive) {
Serial.print(F("[CAUSE] TEMP ")); Serial.println(lastOkTempC,1);
}
if (intrusionAlarmActive) {
Serial.print(F("[CAUSE] LDR Δ=")); Serial.println(abs(lastLDRRaw - ldrBaseline));
}
Serial.println(">>> ALARM TRIGGERED <<<");
state = ALARM_TRIGGERED;
}
// 5) Per-mode behavior
switch(state){
case ARMED_SECURE: {
sirenUpdate(false);
setSecureLights();
showTempC_int((int)(lastOkTempC + 0.5f));
break;
}
case ALARM_TRIGGERED: {
alarmBlinkLights();
showALRT();
sirenUpdate(true);
if (readButtonHeld(PIN_BTN_K1, 80)) { // <<< NEW: Disarm ต้องกดจริง
state = DISARM_MODE;
tDisarmStart = 0;
showDashes();
}
break;
}
case DISARM_MODE: {
sirenUpdate(true);
disarmLights();
alarmBlinkLights();
showDashes();
if (potInDisarmWindow()){
if (tDisarmStart == 0) tDisarmStart = millis();
else if (millis() - tDisarmStart >= DISARM_HOLD_MS){
noTone(PIN_BUZZ);
state = ARMED_SECURE;
setSecureLights();
}
} else {
tDisarmStart = 0;
}
break;
}
}
}
Disarm
Panic