csv_writer.cpp 4.69 KB
Newer Older
1
2
#include "csv_writer.h"

Eric Duminil's avatar
TODO    
Eric Duminil committed
3
4
//TODO: Allow CSV download via USB Serial, when requested (e.g. via a Python script)

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
namespace config {
  // Values should be defined in config.h
  uint16_t csv_interval = CSV_INTERVAL; // [s]
}
namespace csv_writer {
  unsigned long last_written_at = 0;
  String last_successful_write = "";

#if defined(ESP8266)
  /**
   * SPECIFIC FUNCTIONS FOR LITTLEFS
   */
  FSInfo fs_info;

  bool mountFS() {
    return LittleFS.begin(); // format if needed.
  }

  void updateFsInfo() {
    FS_LIB.info(fs_info);
  }

  int getTotalSpace() {
    return fs_info.totalBytes;
  }

  int getUsedSpace() {
    return fs_info.usedBytes;
  }

  void showFilesystemContent() {
    Dir dir = FS_LIB.openDir("/");
    while (dir.next()) {
      Serial.print("  ");
      Serial.print(dir.fileName());
      Serial.print(" - ");
      if (dir.fileSize()) {
        File f = dir.openFile("r");
        Serial.println(f.size());
        f.close();
      } else {
        Serial.println("0");
      }
    }
  }
Eric Duminil's avatar
Eric Duminil committed
50
#elif defined(ESP32)
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
  /**
   * SPECIFIC FUNCTIONS FOR SPIFFS
   */
  bool mountFS() {
    return SPIFFS.begin(true); // format if needed.
  }

  void updateFsInfo() {
    // Nothing to do.
  }

  int getTotalSpace() {
    return SPIFFS.totalBytes();
  }

  int getUsedSpace() {
    return SPIFFS.usedBytes();
  }

  void showFilesystemContent() {
    File root = SPIFFS.open("/");
    File file = root.openNextFile();
    while (file) {
      Serial.print("  ");
      Serial.print(file.name());
      Serial.print(" - ");
      Serial.println(file.size());
      file = root.openNextFile();
    }
  }
#endif

  const String filename = "/" + SENSOR_ID + ".csv";

  int getAvailableSpace() {
    return getTotalSpace() - getUsedSpace();
  }

  void initialize() {
    Serial.print(F("Initializing FS..."));
    if (mountFS()) {
      Serial.println(F("done."));
    } else {
      Serial.println(F("fail."));
      return;
    }

    updateFsInfo();

    Serial.println(F("File system info:"));

    Serial.print(F("  Total space    : "));
    Serial.print(getTotalSpace() / 1024);
    Serial.println("kB");

    Serial.print(F("  Used space     : "));
    Serial.print(getUsedSpace() / 1024);
    Serial.println("kB");

    Serial.print(F("  Available space: "));
    Serial.print(getAvailableSpace() / 1024);
    Serial.println("kB");
    Serial.println();

    // Open dir folder
Eric Duminil's avatar
Eric Duminil committed
116
    Serial.println(F("Filesystem content:"));
117
    showFilesystemContent();
Eric Duminil's avatar
Eric Duminil committed
118
    Serial.println();
Eric Duminil's avatar
Eric Duminil committed
119

Eric Duminil's avatar
Eric Duminil committed
120
    sensor_commands::defineIntCallback("csv", setCSVinterval, " 60 (Sets CSV writing interval, in s)");
Eric Duminil's avatar
Eric Duminil committed
121
    sensor_commands::defineCallback("format_filesystem", formatFilesystem, " (Deletes the whole filesystem)");
122
123
124
125
126
127
128
129
130
131
132
133
134
135
  }

  File openOrCreate() {
    File csv_file;
    if (FS_LIB.exists(filename)) {
      csv_file = FS_LIB.open(filename, "a+");
    } else {
      csv_file = FS_LIB.open(filename, "w");
      csv_file.print(F("Sensor time;CO2 concentration;Temperature;Humidity\r\n"));
      csv_file.print(F("YYYY-MM-DD HH:MM:SS+ZZ;ppm;degC;%\r\n"));
    }
    return csv_file;
  }

Eric Duminil's avatar
Eric Duminil committed
136
  void log(const String &timeStamp, const int16_t &co2, const float &temperature, const float &humidity) {
137
    led_effects::onBoardLEDOn();
Eric Duminil's avatar
Eric Duminil committed
138
139
140
141
142
143
144
145
146
    File csv_file = openOrCreate();
    char csv_line[42];
    snprintf(csv_line, sizeof(csv_line), "%s;%d;%.1f;%.1f\r\n", timeStamp.c_str(), co2, temperature, humidity);
    if (csv_file) {
      size_t written_bytes = csv_file.print(csv_line);
      csv_file.close();
      if (written_bytes == 0) {
        Serial.println(F("Nothing written. Disk full?"));
      } else {
Eric Duminil's avatar
Eric Duminil committed
147
        Serial.print(F("CSV - Wrote : "));
Eric Duminil's avatar
Eric Duminil committed
148
149
150
151
152
153
154
155
156
        Serial.print(csv_line);
        last_successful_write = ntp::getLocalTime();
      }
      updateFsInfo();
      delay(50);
    } else {
      //NOTE: Can it ever happen that outfile is false?
      Serial.println(F("Problem on create file!"));
    }
157
    led_effects::onBoardLEDOff();
Eric Duminil's avatar
Eric Duminil committed
158
159
160
  }

  void logIfTimeHasCome(const String &timeStamp, const int16_t &co2, const float &temperature, const float &humidity) {
161
162
163
    unsigned long now = seconds();
    if (now - last_written_at > config::csv_interval) {
      last_written_at = now;
Eric Duminil's avatar
Eric Duminil committed
164
      log(timeStamp, co2, temperature, humidity);
165
166
    }
  }
167
168
169
170
171
172
173
174
175
176
177

  /*****************************************************************
   * Callbacks for sensor commands                                 *
   *****************************************************************/
  void setCSVinterval(int32_t csv_interval) {
    config::csv_interval = csv_interval;
    Serial.print(F("Setting CSV Interval to : "));
    Serial.print(config::csv_interval);
    Serial.println("s.");
    led_effects::showKITTWheel(color::green, 1);
  }
178
179
180
181
182

  void formatFilesystem() {
    FS_LIB.format();
    led_effects::showKITTWheel(color::blue, 2);
  }
183
}