diff --git a/ampel-firmware/ampel-firmware.ino b/ampel-firmware/ampel-firmware.ino
index e8eef2f414953c155f0f2e781d0da75f35623c97..df16f22c271ec34490249651afde879be634aa12 100644
--- a/ampel-firmware/ampel-firmware.ino
+++ b/ampel-firmware/ampel-firmware.ino
@@ -87,8 +87,12 @@ void setup() {
   Serial.print(F("Board    : "));
   Serial.println(ampel.board);
 
+#ifdef AMPEL_CSV
+  csv_writer::initialize(ampel.sensorId);
+#endif
+
 #ifdef AMPEL_WIFI
-  WiFiConnect(ampel.sensorId);
+  wifi::connect(ampel.sensorId);
 
   Serial.print(F("WiFi - Status: "));
   Serial.println(WiFi.status());
@@ -100,7 +104,7 @@ void setup() {
 
     ntp::initialize();
 
-    if (MDNS.begin(ampel.sensorId.c_str())) { // Start the mDNS responder for SENSOR_ID.local
+    if (MDNS.begin(ampel.sensorId)) { // Start the mDNS responder for SENSOR_ID.local
       MDNS.addService("http", "tcp", 80);
       Serial.println(F("mDNS responder started"));
     } else {
@@ -108,15 +112,11 @@ void setup() {
     }
 
 #  ifdef AMPEL_MQTT
-    mqtt::initialize("CO2sensors/" + ampel.sensorId);
+    mqtt::initialize(ampel.sensorId);
 #  endif
   }
 #endif
 
-#ifdef AMPEL_CSV
-  csv_writer::initialize();
-#endif
-
 #if defined(AMPEL_LORAWAN) && defined(ESP32)
   lorawan::initialize();
 #endif
diff --git a/ampel-firmware/co2_sensor.h b/ampel-firmware/co2_sensor.h
index 275c6e755d84681230101df08bd5bd4060528bd1..318b9596ae50ce15476366c3481d45e238895291 100644
--- a/ampel-firmware/co2_sensor.h
+++ b/ampel-firmware/co2_sensor.h
@@ -22,7 +22,7 @@ namespace sensor {
   extern uint16_t co2;
   extern float temperature;
   extern float humidity;
-  extern char timestamp[23];
+  extern char timestamp[];
 
   void initialize();
   bool processData();
diff --git a/ampel-firmware/csv_writer.cpp b/ampel-firmware/csv_writer.cpp
index 199d9f39f38e99cd6e641a42e5690e07439522f0..7e30d44da9531d5a4022ce3aec2a3a5a9d51c987 100644
--- a/ampel-firmware/csv_writer.cpp
+++ b/ampel-firmware/csv_writer.cpp
@@ -78,13 +78,15 @@ namespace csv_writer {
   }
 #endif
 
-  const String filename = "/" + ampel.sensorId + ".csv";
+  char filename[15]; // "/ESPxxxxxx.csv\0"
 
   int getAvailableSpace() {
     return getTotalSpace() - getUsedSpace();
   }
 
-  void initialize() {
+  void initialize(const char *sensorId) {
+    snprintf(filename, sizeof(filename), "/%s.csv", sensorId);
+
     Serial.print(F("Initializing FS..."));
     if (mountFS()) {
       Serial.println(F("done."));
@@ -187,7 +189,7 @@ namespace csv_writer {
       }
       csv_file.close();
     }
-    Serial.println(F("################"));
+    Serial.println(F("######################"));
   }
 
   void formatFilesystem() {
diff --git a/ampel-firmware/csv_writer.h b/ampel-firmware/csv_writer.h
index ab4a87b2972e8b3ae597e637a6f309da0d829658..f85f34641aaf4f1915e773ad7c1675f846ecfa90 100644
--- a/ampel-firmware/csv_writer.h
+++ b/ampel-firmware/csv_writer.h
@@ -20,11 +20,11 @@ namespace config {
   extern uint16_t csv_interval; // [s]
 }
 namespace csv_writer {
-  extern char last_successful_write[23];
-  void initialize();
-  void logIfTimeHasCome(const char* timestamp, const int16_t &co2, const float &temperature, const float &humidity);
+  extern char last_successful_write[];
+  void initialize(const char *sensorId);
+  void logIfTimeHasCome(const char *timestamp, const int16_t &co2, const float &temperature, const float &humidity);
   int getAvailableSpace();
-  extern const String filename;
+  extern char filename[];
 
   void setCSVinterval(int32_t csv_interval);
   void showCSVContent();
diff --git a/ampel-firmware/mqtt.cpp b/ampel-firmware/mqtt.cpp
index fda75272a905792d62cb38f1e43b53c6bfd464b7..22a0e2def350a38b77336b954395ae5d55005a8e 100644
--- a/ampel-firmware/mqtt.cpp
+++ b/ampel-firmware/mqtt.cpp
@@ -23,13 +23,13 @@ namespace mqtt {
   unsigned long last_failed_at = 0;
   bool connected = false;
 
-  String publish_topic;
+  char publish_topic[21]; // e.g. "CO2sensors/ESPxxxxxx\0"
   const char *json_sensor_format;
   char last_successful_publish[23] = "";
 
-  void initialize(String &topic) {
+  void initialize(const char *sensorId) {
     json_sensor_format = PSTR("{\"time\":\"%s\", \"co2\":%d, \"temp\":%.1f, \"rh\":%.1f}");
-    publish_topic = topic;
+    snprintf(publish_topic, sizeof(publish_topic), "CO2sensors/%s", sensorId);
 #if defined(ESP8266)
     espClient.setInsecure(); // Sorry, we don't want to flash the sensors every 3 months.
 #endif
@@ -49,7 +49,7 @@ namespace mqtt {
       char payload[75]; // Should be enough for json...
       snprintf(payload, sizeof(payload), json_sensor_format, timestamp, co2, temperature, humidity);
       // Topic is the same as clientID. e.g. 'CO2sensors/ESP3d03da'
-      if (mqttClient.publish(publish_topic.c_str(), payload)) {
+      if (mqttClient.publish(publish_topic, payload)) {
         Serial.println(F("OK"));
         ntp::getLocalTime(last_successful_publish);
       } else {
@@ -96,7 +96,7 @@ namespace mqtt {
 
     led_effects::onBoardLEDOn();
     // Wait for connection, at most 15s (default)
-    mqttClient.connect(publish_topic.c_str(), config::mqtt_user, config::mqtt_password);
+    mqttClient.connect(publish_topic, config::mqtt_user, config::mqtt_password);
     led_effects::onBoardLEDOff();
 
     connected = mqttClient.connected();
@@ -104,7 +104,7 @@ namespace mqtt {
     if (connected) {
       if (config::allow_mqtt_commands) {
         char control_topic[60]; // Should be enough for "CO2sensors/ESPd03cc5/control"
-        snprintf(control_topic, sizeof(control_topic), "%s/control", publish_topic.c_str());
+        snprintf(control_topic, sizeof(control_topic), "%s/control", publish_topic);
         mqttClient.subscribe(control_topic);
         mqttClient.setCallback(controlSensorCallback);
       }
@@ -153,11 +153,11 @@ namespace mqtt {
   // The sensor will send the info to "CO2sensors/ESP123456/info".
   void sendInfoAboutLocalNetwork() {
     char info_topic[60]; // Should be enough for "CO2sensors/ESP123456/info"
-    snprintf(info_topic, sizeof(info_topic), "%s/info", publish_topic.c_str());
+    snprintf(info_topic, sizeof(info_topic), "%s/info", publish_topic);
 
     char payload[75]; // Should be enough for info json...
     const char *json_info_format = PSTR("{\"local_ip\":\"%s\", \"ssid\":\"%s\"}");
-    snprintf(payload, sizeof(payload), json_info_format, WiFi.localIP().toString().c_str(), WiFi.SSID().c_str());
+    snprintf(payload, sizeof(payload), json_info_format, wifi::local_ip, WIFI_SSID);
 
     mqttClient.publish(info_topic, payload);
   }
diff --git a/ampel-firmware/mqtt.h b/ampel-firmware/mqtt.h
index 9167c47dda720528f138ff048630d399e6656984..fb769d66ccddf6a3c390c2e976c52de5717564a6 100644
--- a/ampel-firmware/mqtt.h
+++ b/ampel-firmware/mqtt.h
@@ -13,7 +13,7 @@ namespace config {
 namespace mqtt {
   extern char last_successful_publish[];
   extern bool connected;
-  void initialize(String &topic);
+  void initialize(const char *sensorId);
   void keepConnection();
   void publishIfTimeHasCome(const char *timestamp, const int16_t &co2, const float &temp, const float &hum);
 
diff --git a/ampel-firmware/util.cpp b/ampel-firmware/util.cpp
index 8a593666ae0b4fa10fd9568ad29447a8680c0812..b1aeb86a4a828669a403537a4bc638b660db639c 100644
--- a/ampel-firmware/util.cpp
+++ b/ampel-firmware/util.cpp
@@ -13,20 +13,6 @@ const char *current_board = "ESP32";
 const char *current_board = "UNKNOWN";
 #endif
 
-// Get last 3 bytes of ESP MAC (worldwide unique)
-String macToID() {
-  uint8_t mac[6];
-  WiFi.macAddress(mac);
-  String result;
-  for (int i = 3; i < 6; i++) {
-    if (mac[i] < 16)
-      result += '0';
-    result += String(mac[i], HEX);
-  }
-  result.toLowerCase();
-  return result;
-}
-
 //NOTE: ESP32 sometimes couldn't access the NTP server, and every loop would take +1000ms
 // ifdefs could be used to define functions specific to ESP32, e.g. with configTime
 namespace ntp {
@@ -70,8 +56,18 @@ void Ampel::showFreeSpace() {
   Serial.println(F(" bytes."));
 }
 
+char sensorId[10]; // e.g "ESPxxxxxx\0"
+
+char* getSensorId() {
+  uint8_t mac[6];
+  WiFi.macAddress(mac);
+  // Get last 3 bytes of ESP MAC (worldwide unique)
+  snprintf(sensorId, sizeof(sensorId), "ESP%02x%02x%02x", mac[3], mac[4], mac[5]);
+  return sensorId;
+}
+
 Ampel::Ampel() :
-    board(current_board), sensorId("ESP" + macToID()), max_loop_duration(0) {
+    board(current_board), sensorId(getSensorId()), max_loop_duration(0) {
   sensor_console::defineIntCommand("set_time", ntp::setLocalTime, F(" 1618829570 (Sets time to the given UNIX time)"));
   sensor_console::defineCommand("free", Ampel::showFreeSpace, F(" (Displays available heap space)"));
   sensor_console::defineCommand("reset", []() {
diff --git a/ampel-firmware/util.h b/ampel-firmware/util.h
index b99a5d52d6c137c92372223da317ae7e2bf2ad64..a2673331e320174cba430c03c0bbf31caa68da06 100644
--- a/ampel-firmware/util.h
+++ b/ampel-firmware/util.h
@@ -37,7 +37,7 @@ private:
   static void showFreeSpace();
 public:
   const char *board;
-  const String sensorId;
+  const char *sensorId;
   uint32_t max_loop_duration;
   Ampel();
 };
diff --git a/ampel-firmware/web_server.cpp b/ampel-firmware/web_server.cpp
index ee20bf43cf57c5171a0b28a5915354c9cdf8dc48..531ca19cf7a3dd6534ccb93d553a7dd9561afc08 100644
--- a/ampel-firmware/web_server.cpp
+++ b/ampel-firmware/web_server.cpp
@@ -65,7 +65,7 @@ namespace web_server {
 #ifdef AMPEL_CSV
             "<li class='pure-menu-item'><a href='#graph' class='pure-menu-link'>Graph</a></li>\n"
             "<li class='pure-menu-item'><a href='#log' class='pure-menu-link'>Log</a></li>\n"
-            "<li class='pure-menu-item'><a href='./%s' class='pure-menu-link'>Download CSV</a></li>\n"
+            "<li class='pure-menu-item'><a href='%s' class='pure-menu-link'>Download CSV</a></li>\n"
 #endif
             "<li class='pure-menu-item' id='led'>&#11044;</li>\n" // LED
             "</ul></div></div>\n"
@@ -132,7 +132,7 @@ namespace web_server {
 #ifdef AMPEL_CSV
             "<script>\n"
             "document.body.style.cursor = 'default';\n"
-            "fetch('./%s',{credentials:'include'})\n"
+            "fetch('%s',{credentials:'include'})\n"
             ".then(response=>response.text())\n"
             ".then(csvText=>csvToTable(csvText))\n"
             ".then(htmlTable=>addLogTableToPage(htmlTable))\n"
@@ -177,7 +177,7 @@ namespace web_server {
     http.on("/", handleWebServerRoot);
     http.on("/command", handleWebServerCommand);
 #ifdef AMPEL_CSV
-    http.on("/" + csv_writer::filename, handleWebServerCSV);
+    http.on(csv_writer::filename, handleWebServerCSV); //NOTE: csv_writer should have been initialized first.
     http.on("/delete_csv", HTTP_POST, handleDeleteCSV);
 #endif
     http.onNotFound(handlePageNotFound);
@@ -212,10 +212,9 @@ namespace web_server {
     char content[2000]; // Update if needed
     // INFO - Header size : 1767 - Body size : 1812 - Script size : 1909
 
-    snprintf_P(content, sizeof(content), header_template, sensor::co2, ampel.sensorId.c_str(),
-        WiFi.localIP().toString().c_str()
+    snprintf_P(content, sizeof(content), header_template, sensor::co2, ampel.sensorId, wifi::local_ip
 #ifdef AMPEL_CSV
-        , csv_writer::filename.c_str()
+        , csv_writer::filename
 #endif
         );
 
@@ -225,7 +224,7 @@ namespace web_server {
     http.send_P(200, PSTR("text/html"), content);
 
     // Body
-    snprintf_P(content, sizeof(content), body_template, ampel.sensorId.c_str(), sensor::co2, sensor::temperature,
+    snprintf_P(content, sizeof(content), body_template, ampel.sensorId, sensor::co2, sensor::temperature,
         sensor::humidity, sensor::timestamp, config::measurement_timestep,
 #ifdef AMPEL_CSV
         csv_writer::last_successful_write, config::csv_interval, csv_writer::getAvailableSpace() / 1024,
@@ -237,9 +236,8 @@ namespace web_server {
         lorawan::connected ? "Yes" : "No", LMIC_FREQUENCY_PLAN, lorawan::last_transmission,
         config::lorawan_sending_interval,
 #endif
-        config::temperature_offset, config::auto_calibrate_sensor ? "Yes" : "No", ampel.sensorId.c_str(),
-        ampel.sensorId.c_str(), WiFi.localIP().toString().c_str(), WiFi.localIP().toString().c_str(),
-        get_free_heap_size(), ampel.max_loop_duration, ampel.board, dd, hh, mm, ss);
+        config::temperature_offset, config::auto_calibrate_sensor ? "Yes" : "No", ampel.sensorId, ampel.sensorId,
+        wifi::local_ip, wifi::local_ip, get_free_heap_size(), ampel.max_loop_duration, ampel.board, dd, hh, mm, ss);
 
     Serial.print(F(" - Body size : "));
     http.sendContent(content);
@@ -248,7 +246,7 @@ namespace web_server {
     // Script
     snprintf_P(content, sizeof(content), script_template
 #ifdef AMPEL_CSV
-        , csv_writer::filename.c_str(), ampel.sensorId.c_str()
+        , csv_writer::filename, ampel.sensorId
 #endif
         );
 
@@ -264,7 +262,9 @@ namespace web_server {
     }
     if (FS_LIB.exists(csv_writer::filename)) {
       fs::File csv_file = FS_LIB.open(csv_writer::filename, "r");
-      http.sendHeader("Content-Length", String(csv_file.size()));
+      char csv_size[10];
+      snprintf(csv_size, sizeof(csv_size), "%d", csv_file.size());
+      http.sendHeader("Content-Length", csv_size);
       http.streamFile(csv_file, F("text/csv"));
       csv_file.close();
     } else {
@@ -276,9 +276,9 @@ namespace web_server {
     if (!shouldBeAllowed()) {
       return http.requestAuthentication(DIGEST_AUTH);
     }
-    Serial.print("Removing CSV file...");
+    Serial.print(F("Removing CSV file..."));
     FS_LIB.remove(csv_writer::filename);
-    Serial.println(" Done!");
+    Serial.println(F(" Done!"));
     http.sendHeader("Location", "/");
     http.send(303);
   }
diff --git a/ampel-firmware/wifi_util.cpp b/ampel-firmware/wifi_util.cpp
index dc496293345f5b3f743e6ed369d5e792376514d9..a845140a8aa80e66c0279c7b74e486847aaf6355 100644
--- a/ampel-firmware/wifi_util.cpp
+++ b/ampel-firmware/wifi_util.cpp
@@ -12,34 +12,39 @@ namespace config {
 #endif
 }
 
-// Initialize Wi-Fi
-void WiFiConnect(const String &hostname) {
-  //NOTE: WiFi Multi could allow multiple SSID and passwords.
-  WiFi.persistent(false); // Don't write user & password to Flash.
-  WiFi.mode(WIFI_STA); // Set ESP to be a WiFi-client only
+namespace wifi {
+  char local_ip[16]; // "255.255.255.255\0"
+  // Initialize Wi-Fi
+  void connect(const char *hostname) {
+    //NOTE: WiFi Multi could allow multiple SSID and passwords.
+    WiFi.persistent(false); // Don't write user & password to Flash.
+    WiFi.mode(WIFI_STA); // Set ESP to be a WiFi-client only
 #if defined(ESP8266)
     WiFi.hostname(hostname);
 #elif defined(ESP32)
-  WiFi.setHostname(hostname.c_str());
+    WiFi.setHostname(hostname);
 #endif
 
-  Serial.print(F("WiFi - Connecting to "));
-  Serial.println(config::wifi_ssid);
-  WiFi.begin(config::wifi_ssid, config::wifi_password);
+    Serial.print(F("WiFi - Connecting to "));
+    Serial.println(config::wifi_ssid);
+    WiFi.begin(config::wifi_ssid, config::wifi_password);
 
-  // Wait for connection, at most wifi_timeout seconds
-  for (int i = 0; i <= config::wifi_timeout && (WiFi.status() != WL_CONNECTED); i++) {
-    led_effects::showRainbowWheel();
-    Serial.print(".");
-  }
-  if (WiFi.status() == WL_CONNECTED) {
-    led_effects::showKITTWheel(color::green);
-    Serial.println();
-    Serial.print(F("WiFi - Connected! IP address: "));
-    Serial.println(WiFi.localIP());
-  } else {
-    //TODO: Allow sensor to work as an Access Point, in order to define SSID & password?
-    led_effects::showKITTWheel(color::red);
-    Serial.println(F("Connection to WiFi failed"));
+    // Wait for connection, at most wifi_timeout seconds
+    for (int i = 0; i <= config::wifi_timeout && (WiFi.status() != WL_CONNECTED); i++) {
+      led_effects::showRainbowWheel();
+      Serial.print(".");
+    }
+    if (WiFi.status() == WL_CONNECTED) {
+      led_effects::showKITTWheel(color::green);
+      Serial.println();
+      Serial.print(F("WiFi - Connected! IP address: "));
+      IPAddress address = WiFi.localIP();
+      snprintf(local_ip, sizeof(local_ip), "%d.%d.%d.%d", address[0], address[1], address[2], address[3]);
+      Serial.println(local_ip);
+    } else {
+      //TODO: Allow sensor to work as an Access Point, in order to define SSID & password?
+      led_effects::showKITTWheel(color::red);
+      Serial.println(F("Connection to WiFi failed"));
+    }
   }
 }
diff --git a/ampel-firmware/wifi_util.h b/ampel-firmware/wifi_util.h
index 7520ed78e070cf552500441f4828bb7d339392b8..1cfe3da134de83f735bad7b8d58cd825fbd5e5d1 100644
--- a/ampel-firmware/wifi_util.h
+++ b/ampel-firmware/wifi_util.h
@@ -5,6 +5,9 @@
 #include "util.h"
 #include "led_effects.h"
 
-void WiFiConnect(const String &hostname);
+namespace wifi {
+  extern char local_ip[];
+  void connect(const char *hostname);
+}
 
 #endif