#pragma once #include #include "data.h" const int mySD_CS=13; const int mySD_SCLK=14; const int mySD_MISO=2; const int mySD_MOSI=15; /* we write fixed-length CSV lines because, at some point in the future, I may need to seek "10 measurements back" or something like that; also, it's easier to read column ranges and sizes: * secs: up to ~36 hours = 130000, unsigned: 6 characters * co2: up to ~4000, unsigned: 4 characters * temp: -20.0 … +40.0, signed: 5 characters * humid: 0.0 … 100.0, unsigned: 5 characters * pm*: 0.0 … 9999.9, unsigned: 6 characters * batt: 3.00 … 4.30, unsigned: 4 characters */ // 123456, 1234, 12345, 12345, 123456, 123456, 123456, 123456, 1234\n const char csvHeader[]=" secs, co2, temp, humid, pm1, pm2.5, pm4, pm10, batt\n"; const char csvFormat[]="% 6lu, % 4u, % 5.1f, %5.1f, %6.1f, %6.1f, %6.1f, %6.1f, %4.2f\n"; const size_t lineLength=65; const char defaultFilename[] = "quality.csv"; class DataLog { private: File logfile; const char *filename; char line[100]; uint8_t linesSinceLastFlush; uint8_t flushThreshold; void openFile() { logfile = SD.open(filename,FILE_WRITE); Serial.print("# file: ");Serial.println(logfile.name()); uint32_t fileSize = logfile.size(); logfile.seek(fileSize); if (fileSize == 0) { logfile.write((uint8_t*)csvHeader, lineLength); linesSinceLastFlush=1; } else { linesSinceLastFlush=0; } } public: DataLog(uint8_t ft=4, const char *fn=defaultFilename) : filename(fn), flushThreshold(ft) {} void start() { bool sdok=SD.begin(mySD_CS, mySD_MOSI, mySD_MISO, mySD_SCLK); Serial.print("# sdok: ");Serial.println(sdok); openFile(); } void show(const SensorData *data) { snprintf( line, lineLength+1, // zero terminator! csvFormat, millis()/1000, data->co2, data->temperature, max(0.0f,data->humidity), max(0.0f,data->pm.mc_1p0), max(0.0f,data->pm.mc_2p5 - data->pm.mc_1p0), max(0.0f,data->pm.mc_4p0 - data->pm.mc_2p5), max(0.0f,data->pm.mc_10p0 - data->pm.mc_4p0), max(0.0f,data->batteryVoltage) ); size_t written=logfile.write((uint8_t *)line,lineLength); Serial.print("# ");Serial.write(line); Serial.print("# written: ");Serial.println(written); if (++linesSinceLastFlush > flushThreshold) { logfile.flush(); linesSinceLastFlush=0; Serial.println("# flushed"); } } bool serialCommand(const String &tag, const String &command) { if (command == "logcat") { Serial.print(tag);Serial.println(" begin"); uint8_t buffer[1024]; logfile.flush(); int remaining = logfile.position(); logfile.seek(0); while (remaining > 0) { int bytesRead = logfile.read(buffer, 1024); Serial.write(buffer, bytesRead); remaining -= bytesRead; } Serial.print(tag);Serial.println(" end"); return true; } else if (command == "logwipe") { logfile.close(); // ugh, why is it `remove(char*)`?? SD.remove(const_cast(filename)); openFile(); Serial.print(tag);Serial.println(" wiped"); } return false; } };