From d6c74f1b06efc385556a0e88706099e970cd09ce Mon Sep 17 00:00:00 2001 From: byun Date: Sun, 9 Nov 2025 13:56:03 +0000 Subject: [PATCH] =?UTF-8?q?APSTA=EB=AA=A8=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ESP32_CAN_Logger.ino | 117 ++++++++++++++++++++++++++++++++++---- settings.h | 132 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 229 insertions(+), 20 deletions(-) diff --git a/ESP32_CAN_Logger.ino b/ESP32_CAN_Logger.ino index 290d3c6..5ed794c 100644 --- a/ESP32_CAN_Logger.ino +++ b/ESP32_CAN_Logger.ino @@ -159,6 +159,11 @@ enum MCP2515Mode { char wifiSSID[32] = "Byun_CAN_Logger"; char wifiPassword[64] = "12345678"; +// WiFi Station 모드 설정 (추가) +bool enableSTAMode = false; // APSTA 모드 활성화 여부 +char staSSID[32] = ""; // 연결할 WiFi SSID +char staPassword[64] = ""; // 연결할 WiFi 비밀번호 + // 전역 변수 SPIClass hspi(HSPI); SPIClass vspi(VSPI); @@ -233,10 +238,15 @@ int commentCount = 0; void loadSettings() { preferences.begin("can-logger", false); - // WiFi 설정 로드 + // WiFi AP 설정 로드 preferences.getString("wifi_ssid", wifiSSID, sizeof(wifiSSID)); preferences.getString("wifi_pass", wifiPassword, sizeof(wifiPassword)); + // WiFi STA 모드 설정 로드 (추가) + enableSTAMode = preferences.getBool("sta_enable", false); + preferences.getString("sta_ssid", staSSID, sizeof(staSSID)); + preferences.getString("sta_pass", staPassword, sizeof(staPassword)); + // 설정이 없으면 기본값 사용 if (strlen(wifiSSID) == 0) { strcpy(wifiSSID, "Byun_CAN_Logger"); @@ -260,21 +270,37 @@ void loadSettings() { } preferences.end(); + + // STA 모드 설정 출력 + if (enableSTAMode && strlen(staSSID) > 0) { + Serial.printf("✓ WiFi STA 모드: 활성화 (SSID: %s)\n", staSSID); + } } void saveSettings() { preferences.begin("can-logger", false); - // WiFi 설정 저장 + // WiFi AP 설정 저장 preferences.putString("wifi_ssid", wifiSSID); preferences.putString("wifi_pass", wifiPassword); + // WiFi STA 모드 설정 저장 (추가) + preferences.putBool("sta_enable", enableSTAMode); + preferences.putString("sta_ssid", staSSID); + preferences.putString("sta_pass", staPassword); + preferences.end(); Serial.println("\n✓ 설정 저장 완료:"); Serial.println("----------------------------------------"); - Serial.printf(" WiFi SSID : %s\n", wifiSSID); - Serial.printf(" WiFi Password : %s\n", wifiPassword); + Serial.printf(" WiFi AP SSID : %s\n", wifiSSID); + Serial.printf(" WiFi AP Password : %s\n", wifiPassword); + if (enableSTAMode && strlen(staSSID) > 0) { + Serial.printf(" STA Mode : 활성화\n"); + Serial.printf(" STA SSID : %s\n", staSSID); + } else { + Serial.printf(" STA Mode : 비활성화\n"); + } Serial.println("----------------------------------------"); Serial.println("⚠️ 재부팅 후 WiFi 설정이 적용됩니다."); } @@ -1287,7 +1313,12 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) // 설정 전송 String settings = "{\"type\":\"settings\","; settings += "\"ssid\":\"" + String(wifiSSID) + "\","; - settings += "\"password\":\"" + String(wifiPassword) + "\"}"; + settings += "\"password\":\"" + String(wifiPassword) + "\","; + settings += "\"staEnable\":" + String(enableSTAMode ? "true" : "false") + ","; + settings += "\"staSSID\":\"" + String(staSSID) + "\","; + settings += "\"staPassword\":\"" + String(staPassword) + "\","; + settings += "\"staConnected\":" + String(WiFi.status() == WL_CONNECTED ? "true" : "false") + ","; + settings += "\"staIP\":\"" + WiFi.localIP().toString() + "\"}"; webSocket.sendTXT(num, settings); @@ -1301,6 +1332,26 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) int passEnd = message.indexOf("\"", passStart); String password = message.substring(passStart, passEnd); + // STA 모드 설정 파싱 + int staEnableIdx = message.indexOf("\"staEnable\":"); + if (staEnableIdx >= 0) { + String staEnableStr = message.substring(staEnableIdx + 12, staEnableIdx + 16); + enableSTAMode = (staEnableStr.indexOf("true") >= 0); + + if (enableSTAMode) { + int staSSIDStart = message.indexOf("\"staSSID\":\"") + 11; + int staSSIDEnd = message.indexOf("\"", staSSIDStart); + String staSsid = message.substring(staSSIDStart, staSSIDEnd); + + int staPassStart = message.indexOf("\"staPassword\":\"") + 15; + int staPassEnd = message.indexOf("\"", staPassStart); + String staPass = message.substring(staPassStart, staPassEnd); + + strncpy(staSSID, staSsid.c_str(), sizeof(staSSID) - 1); + strncpy(staPassword, staPass.c_str(), sizeof(staPassword) - 1); + } + } + strncpy(wifiSSID, ssid.c_str(), sizeof(wifiSSID) - 1); strncpy(wifiPassword, password.c_str(), sizeof(wifiPassword) - 1); @@ -1638,18 +1689,60 @@ void setup() { Serial.println("✗ SD 카드 초기화 실패"); } - // WiFi AP 시작 - WiFi.softAP(wifiSSID, wifiPassword); + // WiFi 설정 - APSTA 모드 지원 + if (enableSTAMode && strlen(staSSID) > 0) { + // APSTA 모드 (AP + Station 동시 동작) + Serial.println("\n📶 WiFi APSTA 모드 시작..."); + + WiFi.mode(WIFI_AP_STA); + + // AP 모드 시작 + WiFi.softAP(wifiSSID, wifiPassword); + Serial.print("✓ AP SSID: "); + Serial.println(wifiSSID); + Serial.print("✓ AP IP: "); + Serial.println(WiFi.softAPIP()); + + // Station 모드로 WiFi 연결 시도 + Serial.printf("📡 WiFi 연결 시도: %s\n", staSSID); + WiFi.begin(staSSID, staPassword); + + // 연결 대기 (최대 10초) + int attempts = 0; + while (WiFi.status() != WL_CONNECTED && attempts < 20) { + delay(500); + Serial.print("."); + attempts++; + } + Serial.println(); + + if (WiFi.status() == WL_CONNECTED) { + Serial.println("✓ WiFi 연결 성공!"); + Serial.print("✓ STA IP: "); + Serial.println(WiFi.localIP()); + Serial.print("✓ Gateway: "); + Serial.println(WiFi.gatewayIP()); + Serial.print("✓ DNS: "); + Serial.println(WiFi.dnsIP()); + } else { + Serial.println("✗ WiFi 연결 실패 (AP 모드는 정상 동작)"); + } + } else { + // AP 모드만 사용 + Serial.println("\n📶 WiFi AP 모드 시작..."); + WiFi.mode(WIFI_AP); + WiFi.softAP(wifiSSID, wifiPassword); + + Serial.print("✓ AP SSID: "); + Serial.println(wifiSSID); + Serial.print("✓ AP IP: "); + Serial.println(WiFi.softAPIP()); + } // WiFi 성능 최적화 WiFi.setSleep(false); // WiFi 절전 모드 비활성화 (신호 강도 개선) esp_wifi_set_max_tx_power(84); // TX 출력 최대화 (20.5dBm = 84/4) - Serial.print("✓ AP SSID: "); - Serial.println(wifiSSID); - Serial.print("✓ AP IP: "); - Serial.println(WiFi.softAPIP()); - // WebSocket 시작 webSocket.begin(); webSocket.onEvent(webSocketEvent); diff --git a/settings.h b/settings.h index 23f4285..1d89f14 100644 --- a/settings.h +++ b/settings.h @@ -99,11 +99,11 @@ const char settings_html[] PROGMEM = R"rawliteral( input[type="password"] { width: 100%; padding: 12px 15px; - border: 2px solid #ddd; + border: 2px solid #e1e8ed; border-radius: 8px; - font-size: 1em; + font-size: 0.95em; transition: all 0.3s; - font-family: inherit; + background: white; } input[type="text"]:focus, @@ -113,6 +113,38 @@ const char settings_html[] PROGMEM = R"rawliteral( box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); } + input[type="checkbox"] { + width: 20px; + height: 20px; + cursor: pointer; + margin-right: 10px; + } + + .checkbox-group { + display: flex; + align-items: center; + margin-bottom: 15px; + } + + .checkbox-group label { + margin-bottom: 0; + cursor: pointer; + user-select: none; + } + + .sta-settings { + background: #f0f4f8; + padding: 20px; + border-radius: 8px; + margin-top: 15px; + border-left: 4px solid #667eea; + } + + .sta-settings.disabled { + opacity: 0.5; + pointer-events: none; + } + .button-group { display: flex; gap: 15px; @@ -273,17 +305,49 @@ const char settings_html[] PROGMEM = R"rawliteral( WiFi Configuration +

AP Mode (Access Point)

+
- +
ESP32가 생성할 WiFi 네트워크 이름입니다 (최대 31자)
- +
WiFi 접속 시 필요한 비밀번호입니다 (8-63자)
+ +
+ +

APSTA Mode (AP + Station)

+ +
+ + +
+
+ AP와 Station을 동시에 사용하여 인터넷 접속 가능 +
+ +
+
+ + +
연결할 외부 WiFi 네트워크 이름
+
+ +
+ + +
외부 WiFi 비밀번호
+
+ + +
@@ -298,8 +362,9 @@ const char settings_html[] PROGMEM = R"rawliteral(
• WiFi 설정을 변경한 경우, ESP32를 재부팅해야 새 SSID/비밀번호가 적용됩니다.
- • 시간 설정은 모니터 페이지에서 "Sync from Phone" 버튼을 눌러 핸드폰 시간과 동기화할 수 있습니다.
- • RTC 모듈이 연결된 경우, 시간이 자동으로 보정됩니다.
+ • APSTA 모드: Station 모드를 활성화하면 ESP32가 AP와 Station을 동시에 사용합니다.
+ • Station 모드로 외부 WiFi에 연결하면 인터넷 접속이 가능해집니다.
+ • Station 연결 실패 시에도 AP 모드는 정상 동작합니다.
• 설정 저장 후 ESP32의 리셋 버튼을 눌러 재부팅하세요.
@@ -335,6 +400,22 @@ const char settings_html[] PROGMEM = R"rawliteral( document.getElementById('wifi-ssid').value = data.ssid || 'Byun_CAN_Logger'; document.getElementById('wifi-password').value = data.password || ''; + // STA 모드 설정 로드 + document.getElementById('sta-enable').checked = data.staEnable || false; + document.getElementById('sta-ssid').value = data.staSSID || ''; + document.getElementById('sta-password').value = data.staPassword || ''; + + // STA 설정 표시/숨김 + toggleSTASettings(); + + // STA 연결 상태 표시 + if (data.staConnected && data.staIP && data.staIP !== '0.0.0.0') { + document.getElementById('sta-ip').textContent = data.staIP; + document.getElementById('sta-status').style.display = 'block'; + } else { + document.getElementById('sta-status').style.display = 'none'; + } + hideAlert('alert-loading'); console.log('Settings loaded:', data); } else if (data.type === 'settingsSaved') { @@ -359,6 +440,11 @@ const char settings_html[] PROGMEM = R"rawliteral( const ssid = document.getElementById('wifi-ssid').value.trim(); const password = document.getElementById('wifi-password').value; + // STA 모드 설정 + const staEnable = document.getElementById('sta-enable').checked; + const staSSID = document.getElementById('sta-ssid').value.trim(); + const staPassword = document.getElementById('sta-password').value; + // 입력 검증 if (ssid.length === 0) { alert('WiFi SSID를 입력하세요.'); @@ -380,10 +466,29 @@ const char settings_html[] PROGMEM = R"rawliteral( return; } + // STA 모드 검증 + if (staEnable) { + if (staSSID.length === 0) { + alert('Station 모드를 활성화하려면 WiFi SSID를 입력하세요.'); + return; + } + if (staSSID.length > 31) { + alert('Station WiFi SSID는 최대 31자까지 입력 가능합니다.'); + return; + } + if (staPassword.length > 0 && staPassword.length < 8) { + alert('Station WiFi 비밀번호는 최소 8자 이상이어야 합니다.'); + return; + } + } + const settings = { cmd: 'saveSettings', ssid: ssid, - password: password + password: password, + staEnable: staEnable, + staSSID: staSSID, + staPassword: staPassword }; if (ws && ws.readyState === WebSocket.OPEN) { @@ -422,6 +527,17 @@ const char settings_html[] PROGMEM = R"rawliteral( } } + function toggleSTASettings() { + const staEnable = document.getElementById('sta-enable').checked; + const staSettings = document.getElementById('sta-settings'); + + if (staEnable) { + staSettings.classList.remove('disabled'); + } else { + staSettings.classList.add('disabled'); + } + } + // 페이지 로드 시 WebSocket 연결 window.addEventListener('load', function() { initWebSocket();