물리적 버튼 상태LED제거 웹페이 조작 추가

This commit is contained in:
2025-10-08 20:44:38 +00:00
parent 5f53da6e6e
commit 5e2da19075
2 changed files with 71 additions and 69 deletions

View File

@@ -1,6 +1,6 @@
/*
* Byun CAN Logger with Web Interface
* Version: 1.0
* Version: 1.1
*/
#include <Arduino.h>
@@ -16,14 +16,11 @@
#include <freertos/semphr.h>
#include "index.h"
#include "transmit.h"
#include "graph.h" // 그래프 페이지 추가
#include "graph_viewer.h" // 새로 추가
#include "graph.h"
#include "graph_viewer.h"
// GPIO 핀 정의
#define CAN_INT_PIN 27
#define LOGGING_CONTROL_PIN 17
#define LOGGING_STATUS_LED 16
#define SD_READY_LED 26
// HSPI 핀 (CAN)
#define HSPI_MISO 12
@@ -85,7 +82,6 @@ QueueHandle_t canQueue;
SemaphoreHandle_t sdMutex;
TaskHandle_t canRxTaskHandle = NULL;
TaskHandle_t sdWriteTaskHandle = NULL;
TaskHandle_t controlTaskHandle = NULL;
TaskHandle_t webTaskHandle = NULL;
volatile bool loggingEnabled = false;
@@ -117,9 +113,10 @@ void changeCanSpeed(CAN_SPEED newSpeed);
void scanExistingFiles();
bool createNewLogFile();
bool flushBuffer();
void startLogging();
void stopLogging();
void canRxTask(void *pvParameters);
void sdWriteTask(void *pvParameters);
void controlTask(void *pvParameters);
void sdMonitorTask(void *pvParameters);
void sendFileList(uint8_t clientNum);
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length);
@@ -246,6 +243,49 @@ bool flushBuffer() {
return false;
}
// 로깅 시작
void startLogging() {
if (loggingEnabled) {
Serial.println("이미 로깅 중");
return;
}
if (!sdCardReady) {
Serial.println("SD 카드가 준비되지 않음");
return;
}
Serial.println("로깅 시작");
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(1000)) == pdTRUE) {
if (createNewLogFile()) {
loggingEnabled = true;
bufferIndex = 0;
}
xSemaphoreGive(sdMutex);
}
}
// 로깅 중지
void stopLogging() {
if (!loggingEnabled) {
Serial.println("로깅이 실행 중이 아님");
return;
}
Serial.println("로깅 정지");
loggingEnabled = false;
flushBuffer();
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(1000)) == pdTRUE) {
if (logFile) {
logFile.close();
}
xSemaphoreGive(sdMutex);
}
}
// CAN 수신 태스크
void canRxTask(void *pvParameters) {
struct can_frame frame;
@@ -316,15 +356,12 @@ void sdWriteTask(void *pvParameters) {
if (loggingEnabled && sdCardReady) {
if (bufferIndex + sizeof(CANMessage) > FILE_BUFFER_SIZE) {
if (!flushBuffer()) {
digitalWrite(LOGGING_STATUS_LED, LOW);
continue;
}
}
memcpy(&fileBuffer[bufferIndex], &msg, sizeof(CANMessage));
bufferIndex += sizeof(CANMessage);
digitalWrite(LOGGING_STATUS_LED, HIGH);
}
} else {
if (loggingEnabled && bufferIndex > 0) {
@@ -334,49 +371,6 @@ void sdWriteTask(void *pvParameters) {
}
}
// 제어 태스크
void controlTask(void *pvParameters) {
bool lastLoggingState = false;
Serial.println("제어 태스크 시작");
while (1) {
bool currentState = digitalRead(LOGGING_CONTROL_PIN);
if (currentState != lastLoggingState) {
if (currentState == HIGH && sdCardReady) {
Serial.println("로깅 시작");
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(1000)) == pdTRUE) {
if (createNewLogFile()) {
loggingEnabled = true;
bufferIndex = 0;
}
xSemaphoreGive(sdMutex);
}
} else if (currentState == LOW && loggingEnabled) {
Serial.println("로깅 정지");
loggingEnabled = false;
flushBuffer();
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(1000)) == pdTRUE) {
if (logFile) {
logFile.close();
}
xSemaphoreGive(sdMutex);
}
digitalWrite(LOGGING_STATUS_LED, LOW);
}
lastLoggingState = currentState;
}
vTaskDelay(pdMS_TO_TICKS(50));
}
}
// SD 모니터 태스크
void sdMonitorTask(void *pvParameters) {
Serial.println("SD 모니터 태스크 시작");
@@ -386,7 +380,6 @@ void sdMonitorTask(void *pvParameters) {
if (cardPresent != sdCardReady) {
sdCardReady = cardPresent;
digitalWrite(SD_READY_LED, sdCardReady ? HIGH : LOW);
if (sdCardReady) {
Serial.println("SD 카드 준비됨");
@@ -394,8 +387,7 @@ void sdMonitorTask(void *pvParameters) {
} else {
Serial.println("SD 카드 없음");
if (loggingEnabled) {
loggingEnabled = false;
digitalWrite(LOGGING_STATUS_LED, LOW);
stopLogging();
}
}
}
@@ -565,6 +557,12 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length
else if (msg.indexOf("\"cmd\":\"getFiles\"") >= 0) {
sendFileList(num);
}
else if (msg.indexOf("\"cmd\":\"startLogging\"") >= 0) {
startLogging();
}
else if (msg.indexOf("\"cmd\":\"stopLogging\"") >= 0) {
stopLogging();
}
else if (msg.indexOf("\"cmd\":\"sendCan\"") >= 0) {
handleCanTransmit(msg);
}
@@ -700,14 +698,8 @@ void setup() {
memset(recentData, 0, sizeof(recentData));
memset(txMessages, 0, sizeof(txMessages));
pinMode(LOGGING_CONTROL_PIN, INPUT);
pinMode(LOGGING_STATUS_LED, OUTPUT);
pinMode(SD_READY_LED, OUTPUT);
pinMode(CAN_INT_PIN, INPUT_PULLUP);
digitalWrite(LOGGING_STATUS_LED, LOW);
digitalWrite(SD_READY_LED, LOW);
hspi.begin(HSPI_SCLK, HSPI_MISO, HSPI_MOSI, HSPI_CS);
vspi.begin(VSPI_SCLK, VSPI_MISO, VSPI_MOSI, VSPI_CS);
@@ -718,7 +710,6 @@ void setup() {
if (SD.begin(VSPI_CS, vspi)) {
sdCardReady = true;
digitalWrite(SD_READY_LED, HIGH);
Serial.println("✓ SD 카드 초기화 완료");
scanExistingFiles();
} else {
@@ -744,7 +735,6 @@ void setup() {
server.send_P(200, "text/html", graph_html);
});
// ⭐ 새로 추가: 그래프 뷰어 페이지
server.on("/graph-view", HTTP_GET, []() {
server.send_P(200, "text/html", graph_viewer_html);
});
@@ -756,15 +746,12 @@ void setup() {
if (SD.exists(filename)) {
File file = SD.open(filename, FILE_READ);
if (file) {
// 파일명만 추출 (경로 제거)
String displayName = server.arg("file");
// Content-Disposition 헤더 추가 (원본 파일명 지정)
server.sendHeader("Content-Disposition",
"attachment; filename=\"" + displayName + "\"");
server.sendHeader("Content-Type", "application/octet-stream");
// 파일 전송
server.streamFile(file, "application/octet-stream");
file.close();
} else {
@@ -791,7 +778,6 @@ void setup() {
xTaskCreatePinnedToCore(canRxTask, "CAN_RX", 4096, NULL, 4, &canRxTaskHandle, 1);
xTaskCreatePinnedToCore(sdWriteTask, "SD_WRITE", 12288, NULL, 3, &sdWriteTaskHandle, 1);
xTaskCreatePinnedToCore(controlTask, "CONTROL", 8192, NULL, 2, &controlTaskHandle, 0);
xTaskCreatePinnedToCore(sdMonitorTask, "SD_MONITOR", 4096, NULL, 1, NULL, 0);
xTaskCreatePinnedToCore(webUpdateTask, "WEB_UPDATE", 8192, NULL, 2, &webTaskHandle, 0);

16
index.h
View File

@@ -241,6 +241,8 @@ const char index_html[] PROGMEM = R"rawliteral(
<span id="speed-status" style="color: #11998e; font-size: 0.85em; font-weight: 600;"></span>
</div>
<div class="control-row">
<button onclick="startLogging()" style="background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);">Start Logging</button>
<button onclick="stopLogging()" style="background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%);">Stop Logging</button>
<button onclick="refreshFiles()">Refresh Files</button>
<button onclick="clearMessages()">Clear Display</button>
</div>
@@ -518,6 +520,20 @@ const char index_html[] PROGMEM = R"rawliteral(
console.log('CAN speed set to:', speedName);
}
function startLogging() {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({cmd: 'startLogging'}));
console.log('Start logging command sent');
}
}
function stopLogging() {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({cmd: 'stopLogging'}));
console.log('Stop logging command sent');
}
}
function refreshFiles() {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({cmd: 'getFiles'}));