다운 수정 1bit
This commit is contained in:
@@ -2397,10 +2397,12 @@ void setup() {
|
||||
|
||||
initRTC();
|
||||
|
||||
// ★ 로깅: SDIO 4-bit (D0~D3) - 쓰기 속도 우선
|
||||
// 다운로드: SDIO 1-bit (D0만) - WiFi RF 간섭 방지
|
||||
Serial.println("SD 카드 초기화 (SDIO 4-bit)...");
|
||||
if (!SD_MMC.setPins(SDIO_CLK, SDIO_CMD, SDIO_D0, SDIO_D1, SDIO_D2, SDIO_D3)) {
|
||||
sdCardReady = false;
|
||||
} else if (SD_MMC.begin("/sdcard", false)) {
|
||||
} else if (SD_MMC.begin("/sdcard", false, false, 10000000)) { // false = 4-bit
|
||||
sdCardReady = true;
|
||||
Serial.printf("✓ SD 카드 초기화 완료: %llu MB\n", SD_MMC.cardSize()/(1024*1024));
|
||||
loadFileComments();
|
||||
@@ -2472,6 +2474,22 @@ void setup() {
|
||||
server.send(404, "text/plain", "File not found"); return;
|
||||
}
|
||||
|
||||
// ★ 다운로드 중 1-bit 모드 전환 (WiFi TX와 SD DMA 간섭 방지)
|
||||
// 4-bit: D0~D3 동시 → WiFi RF 노이즈에 취약 → 0x109 DMA 타임아웃
|
||||
// 1-bit: D0만 사용 → 노이즈 내성 대폭 향상
|
||||
// 전환 절차: end() → setPins(D0만) → begin(1-bit)
|
||||
{
|
||||
SD_MMC.end();
|
||||
delay(50);
|
||||
SD_MMC.setPins(SDIO_CLK, SDIO_CMD, SDIO_D0);
|
||||
if (!SD_MMC.begin("/sdcard", true, false, 10000000)) {
|
||||
xSemaphoreGive(sdMutex);
|
||||
if (wasLogging) loggingEnabled = true;
|
||||
server.send(500, "text/plain", "SD 1-bit init failed"); return;
|
||||
}
|
||||
Serial.println("✓ SD 1-bit 모드 전환 (다운로드용)");
|
||||
}
|
||||
|
||||
File file = SD_MMC.open(filename, FILE_READ);
|
||||
if (!file) {
|
||||
xSemaphoreGive(sdMutex);
|
||||
@@ -2487,39 +2505,120 @@ void setup() {
|
||||
server.sendHeader("Connection", "close");
|
||||
server.send(200, "application/octet-stream", "");
|
||||
|
||||
// ★ 청크 크기 8KB (512 → 8192): 전송 횟수 대폭 감소, Watchdog 여유
|
||||
const size_t CHUNK = 8192;
|
||||
// ★ CHUNK 4KB: DMA 전송 크기를 줄여 SD 내부 타이밍 부담 감소
|
||||
const size_t CHUNK = 4096;
|
||||
uint8_t* buf = (uint8_t*)heap_caps_malloc(CHUNK, MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
|
||||
if (!buf) buf = (uint8_t*)malloc(CHUNK);
|
||||
|
||||
if (buf) {
|
||||
size_t total = 0;
|
||||
WiFiClient client = server.client();
|
||||
client.setNoDelay(true);
|
||||
|
||||
while (file.available() && total < fileSize && client.connected()) {
|
||||
size_t r = file.read(buf, CHUNK);
|
||||
if (!r) break;
|
||||
// SD 읽기 실패 복구 람다 (SDMMC 페리페럴 완전 리셋)
|
||||
// 0x109(ESP_ERR_TIMEOUT) 발생 후 seek+read 재시도는 무의미:
|
||||
// SDMMC 하드웨어가 에러 상태로 굳어 있어서 end()+begin() 필수
|
||||
auto sdReinit = [&]() -> bool {
|
||||
Serial.println("⚠️ SDMMC 페리페럴 리셋 시도...");
|
||||
file.close();
|
||||
xSemaphoreGive(sdMutex); // 뮤텍스 해제 후 reinit
|
||||
delay(200);
|
||||
SD_MMC.end();
|
||||
delay(100);
|
||||
SD_MMC.setPins(SDIO_CLK, SDIO_CMD, SDIO_D0); // 1-bit
|
||||
if (!SD_MMC.begin("/sdcard", true, false, 10000000)) { // true = 1-bit
|
||||
Serial.println("✗ SDMMC 재초기화 실패");
|
||||
sdCardReady = false;
|
||||
return false;
|
||||
}
|
||||
sdCardReady = true;
|
||||
// 뮤텍스 재획득
|
||||
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(3000)) != pdTRUE) {
|
||||
Serial.println("✗ 뮤텍스 재획득 실패");
|
||||
return false;
|
||||
}
|
||||
// 파일 재오픈 후 이어받기 위치로 seek
|
||||
file = SD_MMC.open(filename, FILE_READ);
|
||||
if (!file) {
|
||||
Serial.println("✗ 파일 재오픈 실패");
|
||||
xSemaphoreGive(sdMutex);
|
||||
return false;
|
||||
}
|
||||
if (!file.seek(total)) {
|
||||
Serial.println("✗ 파일 seek 실패");
|
||||
file.close();
|
||||
xSemaphoreGive(sdMutex);
|
||||
return false;
|
||||
}
|
||||
Serial.printf("✓ SDMMC 리셋 완료, offset=%u 에서 재개", (unsigned)total);
|
||||
return true;
|
||||
};
|
||||
|
||||
int reinitCount = 0;
|
||||
while (total < fileSize && client.connected()) {
|
||||
size_t toRead = min((size_t)(fileSize - total), CHUNK);
|
||||
size_t r = file.read(buf, toRead);
|
||||
|
||||
if (r == 0) {
|
||||
// SDMMC 에러 상태 → 페리페럴 완전 리셋
|
||||
if (reinitCount >= 3) {
|
||||
Serial.println("✗ Download: SDMMC 리셋 3회 실패, 중단");
|
||||
goto download_done;
|
||||
}
|
||||
reinitCount++;
|
||||
Serial.printf("⚠️ SD 읽기 실패 (reinit %d), offset=%u",
|
||||
reinitCount, (unsigned)total);
|
||||
if (!sdReinit()) goto download_done;
|
||||
continue; // 리셋 성공 → 같은 offset에서 재시도
|
||||
}
|
||||
reinitCount = 0; // 읽기 성공 시 카운터 리셋
|
||||
|
||||
size_t w = 0;
|
||||
uint32_t chunkStart = millis();
|
||||
while (w < r && client.connected()) {
|
||||
uint32_t writeStart = millis();
|
||||
while (w < r) {
|
||||
if (!client.connected()) {
|
||||
Serial.println("⚠️ Download: 클라이언트 연결 끊김");
|
||||
goto download_done;
|
||||
}
|
||||
size_t wr = client.write(buf + w, r - w);
|
||||
if (wr > 0) {
|
||||
w += wr;
|
||||
writeStart = millis();
|
||||
} else {
|
||||
// 클라이언트 버퍼 가득 찬 경우 짧게 대기
|
||||
if (millis() - chunkStart > 5000) break; // 5초 타임아웃
|
||||
if (millis() - writeStart > 30000) {
|
||||
Serial.println("⚠️ Download: 30초 타임아웃");
|
||||
goto download_done;
|
||||
}
|
||||
yield();
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
total += r;
|
||||
yield(); // WiFi 스택에 처리 시간 양보
|
||||
yield();
|
||||
}
|
||||
download_done:
|
||||
free(buf);
|
||||
Serial.printf("Download OK: %s (%u bytes)\n", filename.c_str(), total);
|
||||
Serial.printf("Download %s: %s (%u / %u bytes)\n",
|
||||
(total == fileSize) ? "OK" : "PARTIAL",
|
||||
filename.c_str(), (unsigned)total, (unsigned)fileSize);
|
||||
}
|
||||
|
||||
if (file) file.close();
|
||||
|
||||
// ★ 다운로드 완료 후 4-bit 모드 복원 (로깅용)
|
||||
{
|
||||
SD_MMC.end();
|
||||
delay(50);
|
||||
SD_MMC.setPins(SDIO_CLK, SDIO_CMD, SDIO_D0, SDIO_D1, SDIO_D2, SDIO_D3);
|
||||
if (SD_MMC.begin("/sdcard", false, false, 10000000)) {
|
||||
sdCardReady = true;
|
||||
Serial.println("✓ SD 4-bit 모드 복원 (로깅용)");
|
||||
} else {
|
||||
sdCardReady = false;
|
||||
Serial.println("✗ SD 4-bit 복원 실패");
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
xSemaphoreGive(sdMutex);
|
||||
|
||||
// 로깅 재개
|
||||
|
||||
Reference in New Issue
Block a user