#include #include #include "config.h" #include "modbus.h" #include "mqtt_handler.h" // --- [G1] Wi-Fi 연결 설정 --- void setup_wifi() { delay(10); Serial.println(); Serial.print("[WIFI] Connecting to "); Serial.println(ssid); WiFi.disconnect(true, true); delay(500); WiFi.mode(WIFI_STA); delay(500); esp_wifi_set_ps(WIFI_PS_NONE); WiFi.begin(ssid, password); int attempts = 0; while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); attempts++; if (attempts >= 20) { Serial.println("\n[WIFI] 연결 지연 발생. 무선 재시작 중..."); WiFi.disconnect(); delay(500); WiFi.begin(ssid, password); attempts = 0; } } Serial.println(""); Serial.println("[WIFI] connected"); Serial.print("[WIFI] IP address: "); Serial.println(WiFi.localIP()); } // --- [G7] 부팅 초기화 루틴 제어 함수 --- void handle_init_sequence() { if (!initSequenceStarted) { initSequenceStarted = true; setup_buffers(); Serial.println("[EVENT] id=999 Full-Sync 시작"); if (client.connected()) { client.publish("/line1/event", "{\"id\":999,\"val\":1}"); } } } // --- [G8] PLC 재부팅 감지 이벤트 처리 함수 --- void trigger_plc_reboot_event() { Serial.println("[EVENT] id=998 PLC_REBOOT 감지"); if (client.connected()) { client.publish("/line1/event", "{\"id\":998,\"val\":1}"); } currentState = STATE_INIT; lastStateChange = millis(); initSequenceStarted = false; } // --- [G8] PLC 통신 복구 감지 처리 함수 --- void trigger_plc_recovery() { Serial.println("[EVENT] PLC 통신 복구 감지 -> Full-Sync 재수행 준비"); currentState = STATE_INIT; lastStateChange = millis(); initSequenceStarted = false; } void setup() { Serial.begin(115200); Serial.printf("[SYS] Firmware version: %s\n", FIRMWARE_VERSION); setup_buffers(); setup_wifi(); setup_mqtt(); setup_modbus(); lastStateChange = millis(); Serial.println("[SYS] Setup 완료."); } // --- [G2] 타이밍 스케줄러 메인 루프 --- void loop() { maintain_mqtt(); unsigned long now = millis(); switch (currentState) { case STATE_INIT: // 초기화 시퀀스 핸들링 (버퍼 클리어 및 999 이벤트 송신) handle_init_sequence(); // 정상 주기적 PLC 읽기를 진행하지 않고 대기 (정상 로직 일시 중단) if (now - lastStateChange >= INIT_DURATION) { Serial.println("[STATE] INIT 완료 -> PLC 상태 조회 및 Full-Sync 데이터 전송 개시"); // 1회 PLC 데이터를 가져와 plcOnline 여부 및 버퍼 업데이트 run_plc_read(); if (plcOnline) { // full_sync_pending 플래그가 설정되어 있으므로 전체 스냅샷이 송신됨 if (process_read_changes()) { bool pub_ok = false; Serial.println("[EVENT] id=997 Full-Sync 완료 이벤트 발행 시도..."); if (client.connected()) { pub_ok = client.publish("/line1/event", "{\"id\":997,\"val\":1}"); } if (pub_ok) { full_sync_pending = false; // Full-Sync 완료 이벤트 발행 성공 시에만 일반 운전 상태로 전환 currentState = STATE_READ; lastStateChange = now; readExecuted = false; Serial.println("[EVENT] id=997 Full-Sync 완료 이벤트 발행 성공, 정상 루틴 진입"); } else { Serial.println("[WARN] Full-Sync 완료 이벤트(997) 발행 실패. 다음 주기에서 재발행을 재시도합니다."); // 상태 전환을 하지 않고 STATE_INIT 대기 유지하여 다음 주기에서 재시도 } } else { Serial.println("[WARN] Full-Sync 전송 실패. 다음 주기에서 재동기화 재시도합니다."); // 상태 전환을 하지 않고 STATE_INIT 대기 유지하여 다음 주기에서 재시도 } } else { Serial.println("[WARN] PLC 오프라인 상태 - Full-Sync 보류, 정상 루틴 진입"); full_sync_pending = false; // 오프라인이어도 플래그는 일단 정리하여 일반 모드 진입 준비 currentState = STATE_READ; lastStateChange = now; readExecuted = false; } } break; case STATE_READ: if (now - lastStateChange >= SLOT_DURATION) { currentState = STATE_WRITE; lastStateChange = now; writeExecuted = false; } else { if (!readExecuted) { run_plc_read(); process_read_changes(); readExecuted = true; } } break; case STATE_WRITE: if (now - lastStateChange >= SLOT_DURATION) { currentState = STATE_READ; lastStateChange = now; readExecuted = false; } else { if (!writeExecuted) { run_plc_write(); writeExecuted = true; } } break; } }