물리적 버튼 상태LED제거 웹페이 조작 추가
This commit is contained in:
@@ -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
16
index.h
@@ -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'}));
|
||||
|
||||
Reference in New Issue
Block a user