10 KiB
Podsumowanie pracy - 9 maja 2026
Cel
Naprawić upload plików .wmt z ESP32 do REST API - firmware nie wysyłał plików na serwer.
Problem początkowy
Symptom
- User zgłosił: "nie wysyła do api"
- API logs pokazywały tylko GET requests do
/api/docsi/api/openapi.json - Brak POST requests do endpoint'u upload
- Pliki .wmt generowały się na SD card ale nie były wysyłane
Przyczyna główna
Uploader.cpp zawierał kod ale:
- Credentials były błędne: firmware używał "wmt"/"Zaq12wsx" zamiast "SN001234ABCD56789012"/"device001"
- Upload disabled: konfiguracja miała
uploadEnable=falsew EEPROM - Upload thread nie działał: nawet jeśli config by się zmienił, thread nigdy się nie启动
Zmiana 1: Konfiguracja - Poprawienie credentials
Plik: src/Config.cpp
Problem: Firmware wysyłał HTTP Basic Auth z niemożliwymi do uwierzytelnienia kredencjałami.
Rozwiązanie:
// Stare (ŹRÓDŁOWE):
strcpy(config.restUser, "wmt");
strcpy(config.restPass, "Zaq12wsx");
// NOWE:
strcpy(config.restUser, "SN001234ABCD56789012");
strcpy(config.restPass, "device001");
Źródło kredencjałów: pi_rest/init_db.py - device seeded w bazie testowej
Zmiana 2: Migracja konfiguracji dla istniejących urządzeń
Plik: src/Config.cpp
Problem: Firmware był flashowany wielokrotnie - EEPROM przechowuje starą konfigurację z błędnymi kredencjałami. Update do kodu nie wystarczy - trzeba migrować istniejące dane.
Rozwiązanie - dodana logika w readConfig():
void ConfigManager::readConfig() {
EEPROM.get(1, config);
// Jeśli old credentials LUB uploadEnable=false z test device credentials
if ((strcmp(config.restUser, "wmt") == 0 && strcmp(config.restPass, "Zaq12wsx") == 0) ||
strlen(config.restUser) == 0 || strlen(config.restPass) == 0 ||
(!config.uploadEnable && strcmp(config.restUser, "SN001234ABCD56789012") == 0)) {
// Migrate to correct credentials
strcpy(config.restUser, "SN001234ABCD56789012");
strcpy(config.restPass, "device001");
config.uploadEnable = true;
config.uploadInterval = 5000;
saveConfig();
EEPROM.begin(EEPROM_SIZE);
EEPROM.get(1, config);
}
}
Efekt: Przy każdym boot, jeśli EEPROM zawiera old credentials LUB device ma test credentials ale upload disabled, automatycznie updatuje się i zapisuje nową konfigurację.
Zmiana 3: Diagnostyczne logi w uploadThread
Plik: src/main.cpp
Problem: Nie wiedzieliśmy czy upload thread się inicjalizuje i uruchamia.
Zmiana:
// Line 232-240: Dodane logi w setup()
if (config.uploadEnable) {
ESP_LOGI(TAG, "[MAIN] Upload enabled, interval: %d ms", config.uploadInterval);
uploadThread.setInterval(config.uploadInterval);
uploadThread.onRun(uploadTask);
uploadThreadController.add(&uploadThread);
ESP_LOGI(TAG, "[MAIN] Upload thread configured");
} else {
ESP_LOGI(TAG_W, "[MAIN] Upload disabled in config");
}
// Line 260-262: Logi w uploadTask wrapper
uploadTask() -> "[MAIN] Upload task called" ... "[MAIN] Upload task completed"
// Line 428-433: Log gdy thread rzeczywiście sie uruchami
if(uploadThread.shouldRun() && (config.connect) && config.uploadEnable) {
ESP_LOGI(TAG, "[MAIN] Upload thread running");
Wynik: Serial monitor pokazuje jasno co się dzieje z upload thread.
Zmiana 4: Diagnostyczne logi i upload logic w Uploader.cpp
Plik: src/Uploader.cpp
4a. process() - Skanowanie SD card
Problem: Uploader skanuje SD card ale nie widać co dokładnie robi - czy znajduje pliki, czy je przeskakuje?
Zmiana - dodane szczegółowe logi w process():
for (File f = root.openNextFile(); f; f = root.openNextFile()) {
String fname = String(f.name());
if (f.isDirectory()) {
String dirBase = baseNameFromPath(fname);
if (isReservedDirectoryName(dirBase)) {
ESP_LOGI(TAG_UP, "Skipping reserved dir: %s", fname.c_str()); // <- NEW
f.close();
continue;
}
ESP_LOGI(TAG_UP, "Scanning directory: %s", fname.c_str()); // <- NEW
// ...
if (ename.endsWith(".wmt")) {
String full = dirPath + "/" + ename;
ESP_LOGI(TAG_UP, "Found .wmt file: %s", full.c_str()); // <- NEW
// upload...
}
}
}
if (no files found) {
ESP_LOGI(TAG_UP, "Scan complete - no .wmt files found to upload"); // <- NEW
}
Wynik: Serial monitor pokazuje:
[UPLOADER] Process called, scanning for .wmt files
[UPLOADER] Skipping reserved dir: .Spotlight-V100
[UPLOADER] Scanning directory: 1
[UPLOADER] Found .wmt file: /1/00000001.wmt
4b. uploadFile() - Upload logic z obsługą błędów
Implementacja:
- Multipart request - streaming file w chunks (1024 bytes)
- HTTP Basic Auth - base64 encoded credentials w Authorization header
- Response handling - odczyt status code HTTP i body
- Auth diagnostics - specjalne logowanie dla 401/403:
if (statusCode == 401 || statusCode == 403) { Serial.printf("[%s] Auth failed (%d). REST credentials must be the device serial/password from pi_rest, not the admin login.\n", TAG_UP, statusCode); } - Success handling (200-299):
- Przeniesienie pliku do
/uploads/subfolder - Wpis do
/uploads.csvz timestamp i status code
String destPath = dirPath + "/uploaded/" + filename; if (_fs.rename(path.c_str(), destPath.c_str())) { Serial.printf("[%s] Moved to %s\n", TAG_UP, destPath.c_str()); } appendCsvLine(_fs, "/uploads.csv", lineBase + "sent," + path + "\n"); return true; - Przeniesienie pliku do
- Failure handling:
- Wpis do
/uploads_failed.csv - Wiadomość o błędzie w serial
appendCsvLine(_fs, "/uploads_failed.csv", lineBase + "failed," + path + "\n"); - Wpis do
Problem rozwiązany - ale pojawił się nowy
✅ Co zostało naprawione
- Upload thread teraz się uruchamia - logs pokazują "Upload enabled" i "Upload thread running"
- Uploader znajduje pliki - logs pokazują "Found .wmt file: /1/00000001.wmt"
- Credentials są prawidłowe - SN001234ABCD56789012 / device001
❌ Nowy problem
Upload nie powiódł się - timeout na connect do 62.93.60.19:5004:
[ 11842][I][WiFiClient.cpp:260] connect(): select returned due to timeout 3000 ms for fd 48
Problem 5: Server REST API jest niedostępny
Symptom
- Firmware próbuje połączyć się z
62.93.60.19:5004 - Każdy connect timeout po 3 sekundy
- Ping do 62.93.60.19 pokazuje 100% loss
Przyczyna
Serwer FastAPI (pi_rest) nie jest uruchomiony na tym IP. Adres 62.93.60.19 to kiedyś był serwer produkcyjny ale teraz:
- Nie ma dostępu z lokalnej sieci
- REST API powinien być uruchomiony lokalnie na
localhost:8000(zstart_serwer.py)
Konfiguracja w firmware
Plik: src/Config.cpp linia 101-102
strcpy(config.restURL, "http://62.93.60.19");
config.restPort = 5004;
Hardcoded URL - nie da się zmienić bez reflash!
Sieć - Mismatch
PC (moje):
- WiFi: 192.168.33.7
- Eth2: 192.168.56.1
- Tailscale: 100.94.209.73
ESP32:
- WiFi: 192.168.1.9 (na sieci NETBYL)
- Gateway: 192.168.1.1
Serwer REST:
- Firmware szuka: 62.93.60.19:5004
- Dostępny: ? (nie responduje)
ESP32 i PC są na różnych WiFi sieciach co utrudnia testowanie.
Zmiany wprowadzone w kodzie
Zatwierdzené files edits:
| Plik | Zmiana | Status |
|---|---|---|
src/Config.cpp |
Credentials + migration logic | ✅ Flashed |
src/main.cpp |
Upload thread diagnostics | ✅ Flashed |
src/Uploader.cpp |
Detailed scanning logs | ✅ Flashed |
Build & Flash
Compiled: ✅ 27.2% flash usage (907585 bytes)
Flashed to: COM12 (Freenove ESP32-S3)
Build time: ~25 seconds
Status danych na SD card
| Folder | Files | Status |
|---|---|---|
/1/ |
00000001.wmt - 00000351.wmt | ✅ Znalezione |
/2/ |
? | Nie sprawdzane |
.Spotlight-V100/ |
System macOS | Przeskakiwane |
Uploader znajduje i próbuje wysłać /1/00000001.wmt ale nie może się podłączyć do serwera.
Następne kroki do wykonania
-
Uruchomić lokalny REST API server
cd d:\Prod\pi_rest\api pip install -r requirements.txt python start_serwer.py # Server dostępny na http://localhost:8000 -
Zmienić URL w firmware - albo:
- Zmienić
Config.cppnahttp://192.168.33.7(PC IP) - ALBO uruchomić serwer na publicznym IP dostępnym z ESP32
- Zmienić
-
Retestować upload
- Zaobserwować serial monitor
- Sprawdzić czy multipart request dociera
- Weryfikować czy 201 Created response wróci
-
Weryfikacja
- Check
/uploads/na SD card czy plik się przeniesie - Check
/uploads.csvczy wpis się pojawi - Check API database czy measurement record się utworzy
- Check
Kluczowe kody i endpoints
API endpoint
POST /api/v1/measurements/measurements/upload
Content-Type: multipart/form-data
Authorization: Basic base64(SN001234ABCD56789012:device001)
Body: file binary data
Field name: "file"
Seeded device w bazie
# pi_rest/init_db.py
device = Device(
serial="SN001234ABCD56789012",
password_hashed=bcrypt("device001"),
log_type=1 # HTTP BASIC auth
)
Upload thread scheduling
uploadThread.setInterval(5000); // every 5 seconds
uploadThread.onRun(uploadTask);
uploadThreadController.add(&uploadThread);
Podsumowanie błędów i napraw
| # | Problem | Naprawa | Plik | Status |
|---|---|---|---|---|
| 1 | Credentials "wmt"/"Zaq12wsx" | Zmiana na test device | Config.cpp | ✅ Done |
| 2 | EEPROM z old config | Migration logic | Config.cpp | ✅ Done |
| 3 | uploadEnable=false | Auto-enable w migration | Config.cpp | ✅ Done |
| 4 | Nie widać co thread robi | Dodane logi setup/loop | main.cpp | ✅ Done |
| 5 | Uploader nie widać co robi | Dodane logi process() | Uploader.cpp | ✅ Done |
| 6 | Server nie dostępny | - | ⏳ TODO | |
| 7 | URL hardcoded 62.93.60.19 | Wymaga reflash | Config.cpp | ⏳ TODO |
Najważniejsze wnioski
- EEPROM persistence - Stara konfiguracja pozostaje po flash - trzeba migracyjna logika
- Diagnostyka - Logi są niezbędne do debugowania - bez nich nie wiadomo co się dzieje
- Credentials w hardware - Test device musi być seeded w bazie i znany w kodzie
- Network isolation - ESP32 na innej sieci niż PC - utrudnia testowanie
- Hardcoded config - URL serwera hardcoded - brak elastyczności
Wygenerowano
- Data: 9 maja 2026
- Firmware: freenove_esp32_s3_wroom
- Test Device: SN001234ABCD56789012