sd card support enhancements

This commit is contained in:
Hansi, dl9rdz 2024-08-13 13:25:59 +00:00
parent 8674098b65
commit 71781723f4
7 changed files with 170 additions and 45 deletions

View File

@ -34,6 +34,7 @@
#include "src/pmu.h"
/* Data exchange connectors */
#if FEATURE_CHASEMAPPER
#include "src/conn-chasemapper.h"
@ -287,7 +288,7 @@ void setupChannelList() {
file.close();
}
const char *HTMLHEAD = "<html><head> <meta charset=\"UTF-8\"> <link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">";
const char *HTMLHEAD = "<!DOCTYPE html><html><head> <meta charset=\"UTF-8\"> <link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">";
void HTMLBODY(char *ptr, const char *which) {
strcat(ptr, "<body><form class=\"wrapper\" action=\"");
strcat(ptr, which);
@ -657,7 +658,15 @@ struct st_configitems config_list[] = {
{"mqtt.password", 63, &sonde.config.mqtt.password},
{"mqtt.prefix", 63, &sonde.config.mqtt.prefix},
#endif
#if FEATURE_SDCARD
/* SD-Card settings */
{"sd.cs", 0, &sonde.config.sd.cs},
{"sd.miso", 0, &sonde.config.sd.miso},
{"sd.mosi", 0, &sonde.config.sd.mosi},
{"sd.clk", 0, &sonde.config.sd.clk},
{"sd.sync", 0, &sonde.config.sd.sync},
{"sd.name", 0, &sonde.config.sd.name},
#endif
/* Hardware dependeing settings */
{"disptype", 0, &sonde.config.disptype},
{"norx_timeout", 0, &sonde.config.norx_timeout},
@ -1038,7 +1047,7 @@ const char *handleEditPost(AsyncWebServerRequest * request) {
// will be removed. its now in data/upd.html (for GET; POST to update.html still handled here)
const char *createUpdateForm(boolean run) {
char *ptr = message;
strcpy(ptr, "<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"></head><body><form action=\"update.html\" method=\"post\">");
strcpy(ptr, "<!DOCTYPE html><html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"></head><body><form action=\"update.html\" method=\"post\">");
if (run) {
strcat(ptr, "<p>Doing update, wait until reboot</p>");
} else {
@ -1230,16 +1239,38 @@ void SetupAsyncServer() {
}
request->send(SPIFFS, filename, "text/plain");
});
server.on("/file", HTTP_POST, [](AsyncWebServerRequest * request) {
request->send(200);
}, handleUpload);
#if FEATURE_SDCARD
server.on("/sd/data.csv", HTTP_GET, [](AsyncWebServerRequest *request) {
Serial.println("Opening SD card file\n");
const File SDFile = SD.open("/data.csv", FILE_READ);
if(SDFile) { Serial.printf("SD file opened\n"); }
else { Serial.printf("SD file does not exist"); request->send(404); return; }
AsyncWebServerResponse *response = request->beginChunkedResponse("text/plain", [SDFile](uint8_t *buf, size_t maxLen, size_t index) -> size_t {
File SDLambdaFile = SDFile;
// if(maxLen>1024) maxLen=1024;
Serial.printf("[HTTP]\t[%d]\tINDEX [%d]\tBUFFER_MAX_LENGHT [%d]\r\n", index, SDLambdaFile.size(), maxLen);
return SDLambdaFile.read(buf, maxLen);
});
request->send(response);
});
server.serveStatic("/sd/", SD, "/");
#endif
server.on("/edit.html", HTTP_GET, [](AsyncWebServerRequest * request) {
// new version:
// Open file
// store file object in request->_tempObject
//request->send(200, "text/html", createEditForm(request->getParam(0)->value()));
const String filename = request->getParam(0)->value();
AsyncWebParameter *param = request->getParam(0);
if(!param) {
request->send(404);
return;
}
const String filename = param->value();
File file = SPIFFS.open("/" + filename, "r");
int state = 0;
request->send("text/html", 0, [state, file, filename](uint8_t *buffer, size_t maxLen, size_t index) mutable -> size_t {
@ -1252,9 +1283,13 @@ void SetupAsyncServer() {
if (ret == NULL)
request->send(200, "text/html", "<html><head>ERROR</head><body><p>Something went wrong (probably ESP32 out of memory). Uploaded file is empty.</p></body></hhtml>");
else {
String f = request->getParam(0)->value();
AsyncWebParameter *param = request->getParam(0);
if(!param) {
request->send(404);
return;
}
String f = param->value();
request->redirect("/edit.html?file=" + f);
//request->send(200, "text/html", createEditForm(request->getParam(0)->value()));
}
},
NULL,
@ -1265,8 +1300,12 @@ void SetupAsyncServer() {
// Route to load style.css file
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest * request) {
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, "/style.css", "text/css");
response->addHeader("Cache-Control", "max-age=86400");
request->send(response);
if(response) {
response->addHeader("Cache-Control", "max-age=86400");
request->send(response);
} else {
request->send(404);
}
});
server.on("/live.kml", HTTP_GET, [](AsyncWebServerRequest * request) {
@ -1296,7 +1335,10 @@ void SetupAsyncServer() {
// This happens with concurrent requests, notably if a browser fetches rdz.js and cfg.js concurrently for config.html
// With the cache, rdz.js is likely already in the cache...0
Serial.printf("URL is %s\n", url.c_str());
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, url, "text/html");
const char *type = "text/html";
if(url.endsWith(".js")) type="text/javascript";
Serial.printf("Responding with type %si (url %s)\n", type, url.c_str());
AsyncWebServerResponse *response = request->beginResponse(SPIFFS, url, type);
if(response) {
response->addHeader("Cache-Control", "max-age=900");
request->send(response);
@ -1693,14 +1735,13 @@ void setup()
char buf[12];
// Open serial communications and wait for port to open:
Serial.begin(/*921600 */115200);
Serial.begin(115200);
for (int i = 0; i < 39; i++) {
int v = gpio_get_level((gpio_num_t)i);
Serial.printf("%d:%d ", i, v);
}
//NimBLEDevice::init("NimBLE-Arduino");
//NimBLEServer* pServer = NimBLEDevice::createServer();;
#ifndef REMOVE_ALL_FOR_TESTING
Serial.println("");
#ifdef ESP_MEM_DEBUG
@ -1810,10 +1851,8 @@ void setup()
setupWifiList();
Serial.printf("before disp.initFromFile... layouts is %p\n", disp.layouts);
Serial.printf("test\n");
disp.initFromFile(sonde.config.screenfile);
Serial.printf("disp.initFromFile... layouts is %p", disp.layouts);
Serial.printf("disp.initFromFile... layouts is %p\n", disp.layouts);
// == show initial values from config.txt ========================= //
@ -1929,9 +1968,25 @@ void setup()
#if FEATURE_APRS
connAPRS.init();
#endif
#if FEATURE_SDCARD
connSDCard.init();
#endif
WiFi.onEvent(WiFiEvent);
getKeyPress(); // clear key buffer
#else
/// DEBUG ONLY
WiFi.begin("Dinosauro", "03071975");
while(!WiFi.isConnected()) { delay(500); Serial.print(":"); }
Serial.println("... WiFi is connected!\n");
SetupAsyncServer();
sonde.config.sd.cs = 13;
sonde.config.sd.clk = 14;
sonde.config.sd.miso = 2;
sonde.config.sd.mosi = 15;
connSDCard.init();
#endif
}
void enterMode(int mode) {
@ -2882,6 +2937,8 @@ int fetchHTTPheader(int *validType) {
void loop() {
Serial.printf("\nMAIN: Running loop in state %d [currentDisp:%d, lastDisp:%d]. free heap: %d, unused stack: %d\n",
mainState, currentDisplay, lastDisplay, ESP.getFreeHeap(), uxTaskGetStackHighWaterMark(0));
#ifndef REMOVE_ALL_FOR_TESTING
switch (mainState) {
case ST_DECODER:
#ifndef DISABLE_MAINRX
@ -2911,7 +2968,9 @@ void loop() {
sonde.updateDisplay();
lastDisplay = currentDisplay;
}
#else
delay(1000);
#endif
}

View File

@ -74,6 +74,13 @@ var cfgs = [
[ "sondehub.fiinterval", "Import frequency (minutes, &geq; 5)" ],
[ "sondehub.fimaxdist", "Import maximum distance (km, &leq; 700)" ],
[ "sondehub.fimaxage", "Import maximum age (hours, &leq; 48)" ],
[ "", "SD card logger configuration", "https://github.com/dl9rdz/rdz_ttgo_sonde/wiki/SDcard-configuration"],
[ "cs", "SD card CS" ],
[ "miso", "SD card MISO/DI" ],
[ "mosi", "SD card MOSI/DO" ],
[ "clk", "SD card CLK" ],
[ "sync", "SD card sync interval [s]" ],
[ "name", "SD card naming (0=by sondeid, 1=by day)" ],
[ "", "Hardware configuration (requires reboot)", "https://github.com/dl9rdz/rdz_ttgo_sonde/wiki/Hardware-configuration"],
[ "disptype", "Display type (0=OLED/SSD1306, 1=ILI9225, 2=OLED/SH1106, 3=ILI9341, 4=ILI9342, 5=ST7789)"],
[ "oled_sda", "OLED SDA/TFT SDA"],

View File

@ -34,7 +34,7 @@
// Safeboot image is a minimalistic version that only includes WiFi / OTA functionality
#define FEATURE_SONDEHUB 0
#define FEATURE_MQTT 0
#define FEATURE_SDARD 0
#define FEATURE_SDCARD 0
#define FEATURE_APRS 0
#define LEGACY_FONTS_IN_CODEBIN 0
#endif

View File

@ -106,6 +106,10 @@ void Sonde::defaultConfig() {
config.sx1278_sck = SCK;
// config.oled_rst = 16;
config.oled_rst = -1; // GPIO16 is Flash CS on Lora32 v1.6
config.sd.cs = -1;
config.sd.miso = -1;
config.sd.mosi = -1;
config.sd.clk = -1;
config.disptype = 0;
config.dispcontrast = -1;
config.tft_orient = 1;
@ -225,6 +229,10 @@ void Sonde::defaultConfig() {
config.button2_pin = 14 + 128; // GPIO14 / T6
config.led_pout = 25;
config.batt_adc = 35;
config.sd.cs = 13;
config.sd.miso = 2;
config.sd.mosi = 15;
config.sd.clk = 14;
}
}
//

View File

@ -229,6 +229,15 @@ struct st_sondehub {
double fimaxage;
};
struct st_sdcard {
int cs;
int miso;
int mosi;
int clk;
int sync;
int name;
};
// to be extended
enum { TYPE_TTGO, TYPE_M5_CORE2 };
@ -297,6 +306,7 @@ typedef struct st_rdzconfig {
struct st_mqtt mqtt;
struct st_sondehub sondehub;
struct st_cm cm;
struct st_sdcard sd;
} RDZConfig;

View File

@ -4,45 +4,84 @@
#include "conn-sdcard.h"
// TODO: Move into config
#define CS 13
#define SYNC_INTERVAL 10
static SPIClass sdspi(HSPI);
void ConnSDCard::init() {
/* Initialize SD card */
initok = SD.begin(CS);
Serial.printf("SD card init: %s\n", initok?"OK":"Failed");
if(sonde.config.sd.clk==-1)
return;
/* Initialize SD card */
// SPI (==VSPI) is used by SX127x.
// On LoRa32, SD-Card is on different pins, so cannot share VSPI
// Use HSPI (if using a TFT with SPI, you have to make sure that the same pins are used for both (MISO/MOSI/CLK)
sdspi.begin(sonde.config.sd.clk, sonde.config.sd.miso, sonde.config.sd.mosi, sonde.config.sd.cs);
initok = SD.begin(sonde.config.sd.cs, sdspi);
Serial.printf("SD card init: %s\n", initok ? "OK" : "Failed");
uint8_t cardType = SD.cardType();
if (cardType == CARD_NONE) {
Serial.println("No SD card attached");
return;
}
Serial.print("SD Card Type: ");
if (cardType == CARD_MMC) {
Serial.println("MMC");
} else if (cardType == CARD_SD) {
Serial.println("SDSC");
} else if (cardType == CARD_SDHC) {
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %luMB\n", cardSize);
uint64_t usedSize = SD.usedBytes() / (1024 * 1024);
//uint64_t totalSize = SD.totalBytes() / (1024 * 1024);
uint64_t totalSize = SD.totalBytes();
Serial.printf("SD Card used/total: %lu/%lu MB\n", usedSize, totalSize);
file = SD.open("/data.csv", FILE_APPEND);
if (!file) {
Serial.println("Cannot open file");
return;
}
file.printf("Hello word... test\n");
file.close();
//sdf = SD.open("/data.csv", FILE_READ);
}
void ConnSDCard::netsetup() {
/* empty function, we don't use any network here */
/* empty function, we don't use any network here */
}
String ConnSDCard::getStatus() {
return String("");
return String("");
}
// Rotation by time or by id.
void ConnSDCard::updateSonde( SondeInfo *si ) {
if (!initok) return;
if (!file) {
file = SD.open("/data.csv", FILE_APPEND);
}
if (!file) {
Serial.println("Error opening file");
return;
}
SondeData *sd = &si->d;
file.printf("%d,%s,%s,%d,"
"%f,%f,%f,%f,%f,%f,%d,%d,"
"%d,%d,%d,%d\n",
sd->validID, sd->ser, sd->typestr, sd->subtype,
sd->lat, sd->lon, sd->alt, sd->vs, sd->hs, sd->dir, sd->sats, sd->validPos,
sd->time, sd->frame, sd->vframe, sd->validTime);
wcount++;
if(wcount >= SYNC_INTERVAL) {
file.flush();
wcount = 0;
}
if (!initok) return;
if (!file) {
file = SD.open("/data.csv", FILE_APPEND);
}
if (!file) {
Serial.println("Error opening file");
return;
}
SondeData *sd = &si->d;
file.printf("%d,%s,%s,%d,"
"%f,%f,%f,%f,%f,%f,%d,%d,"
"%d,%d,%d,%d\n",
sd->validID, sd->ser, sd->typestr, sd->subtype,
sd->lat, sd->lon, sd->alt, sd->vs, sd->hs, sd->dir, sd->sats, sd->validPos,
sd->time, sd->frame, sd->vframe, sd->validTime);
wcount++;
if (wcount >= sonde.config.sd.sync) {
file.flush();
wcount = 0;
}
}

View File

@ -39,5 +39,7 @@ private:
};
extern ConnSDCard connSDCard;
#endif