diff --git a/ampel-firmware/config.public.h b/ampel-firmware/config.public.h
index 544d80cb4cbe472ab32ddcd3bfb95f628a859cf5..7110d58bdfb2fe3e5b7e41389e982f5afd87558c 100644
--- a/ampel-firmware/config.public.h
+++ b/ampel-firmware/config.public.h
@@ -64,6 +64,8 @@
 // MIN_BRIGHTNESS, if defined, should be between 0 and MAX_BRIGHTNESS - 1
 // If MIN_BRIGHTNESS is not set, or if it is set to MAX_BRIGHTNESS, breathing is disabled.
 #  define MIN_BRIGHTNESS 60
+// How many LEDs in the ring? 12 and 16 are currently supported. If undefined, 12 is used as default.
+#  define LED_COUNT 12
 
 /**
  * WEB SERVER
diff --git a/ampel-firmware/led_effects.cpp b/ampel-firmware/led_effects.cpp
index 45b8c0b7ecbcecc5a91225e1ce7d055eb6da8ba4..f0b672c9d0c76e1cac6916d94783e85577f387fd 100644
--- a/ampel-firmware/led_effects.cpp
+++ b/ampel-firmware/led_effects.cpp
@@ -12,8 +12,28 @@ namespace config {
   const uint8_t brightness_amplitude = config::max_brightness - config::min_brightness;
   const int kitt_tail = 3; // How many dimmer LEDs follow in K.I.T.T. wheel
   const uint16_t poor_air_quality_ppm = 1600; // Above this threshold, LED breathing effect is faster.
-  //NOTE: Use a class instead? NightMode could then be another state.
-  bool night_mode = false;
+  bool night_mode = false; //NOTE: Use a class instead? NightMode could then be another state.
+
+#if !defined(LED_COUNT)
+#  define LED_COUNT 12
+#endif
+
+  const uint16_t led_count = LED_COUNT;
+
+#if LED_COUNT == 12
+  //NOTE: One value has been prepended, to make calculations easier and avoid out of bounds index.
+  const uint16_t co2_ticks[led_count + 1] = { 0, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000, 2200 }; // [ppm]
+  // For a given LED, which color should be displayed? First LED will be pure green (hue angle 120°),
+  // last 4 LEDs will be pure red (hue angle 0°), LEDs in-between will be yellowish.
+  const uint16_t led_hues[led_count] = { 21845U, 19114U, 16383U, 13653U, 10922U, 8191U, 5461U, 2730U, 0, 0, 0, 0 }; // [hue angle]
+#elif LED_COUNT == 16
+  const uint16_t co2_ticks[led_count + 1] = { 0, 400, 500, 600, 700, 800, 900, 1000, 1100,
+                                           1200, 1300, 1400, 1500, 1600, 1800, 2000, 2200 }; // [ppm]
+  const uint16_t led_hues[led_count] = { 21845U, 20024U, 18204U, 16383U, 14563U, 12742U, 10922U, 9102U,
+                                          7281U, 5461U, 3640U, 1820U, 0, 0, 0, 0 }; // [hue angle]
+#else
+#  error "Only 12 and 16 LEDs rings are currently supported."
+#endif
 }
 
 #if defined(ESP8266)
@@ -24,20 +44,7 @@ const int NEOPIXELS_PIN = 5;
 const int NEOPIXELS_PIN = 23;
 #endif
 
-const int NUMPIXELS = 12;
-//NOTE: One value has been prepended, to make calculations easier and avoid out of bounds index.
-const uint16_t CO2_TICKS[NUMPIXELS + 1] = { 0, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000, 2200 }; // [ppm]
-// const uint16_t CO2_TICKS[NUMPIXELS + 1] = { 0, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1800, 2000, 2200 }; // [ppm]
-// For a given LED, which color should be displayed? First LED will be pure green (hue angle 120°),
-// last 4 LEDs will be pure red (hue angle 0°), LEDs in-between will be yellowish.
-// For reference, this python code can be used to generate the array
-//    NUMPIXELS = 12
-//    RED_LEDS = 4
-//    hues = [ (2**16-1) // 3 * max(NUMPIXELS - RED_LEDS - i, 0) // (NUMPIXELS - RED_LEDS) for i in range(NUMPIXELS) ]
-//    '{' + ', '.join([str(hue) + ('U' if hue else '') for hue in hues]) + '}; // [hue angle]'
-const uint16_t LED_HUES[NUMPIXELS] = { 21845U, 19114U, 16383U, 13653U, 10922U, 8191U, 5461U, 2730U, 0, 0, 0, 0 }; // [hue angle]
-// const uint16_t LED_HUES[NUMPIXELS] = { 21845U, 20024U, 18204U, 16383U, 14563U, 12742U, 10922U, 9102U, 7281U, 5461U, 3640U, 1820U, 0, 0, 0, 0 }; // [hue angle]
-Adafruit_NeoPixel pixels(NUMPIXELS, NEOPIXELS_PIN, NEO_GRB + NEO_KHZ800);
+Adafruit_NeoPixel pixels(config::led_count, NEOPIXELS_PIN, NEO_GRB + NEO_KHZ800);
 
 namespace led_effects {
   //On-board LED on D4, aka GPIO02
@@ -97,14 +104,15 @@ namespace led_effects {
 
   //NOTE: basically one iteration of KITT wheel
   void showWaitingLED(uint32_t color) {
+    using namespace config;
     delay(80);
-    if (config::night_mode) {
+    if (night_mode) {
       return;
     }
     static uint16_t kitt_offset = 0;
     pixels.clear();
-    for (int j = config::kitt_tail; j >= 0; j--) {
-      int ledNumber = abs((kitt_offset - j + NUMPIXELS) % (2 * NUMPIXELS) - NUMPIXELS) % NUMPIXELS; // Triangular function
+    for (int j = kitt_tail; j >= 0; j--) {
+      int ledNumber = abs((kitt_offset - j + led_count) % (2 * led_count) - led_count) % led_count; // Triangular function
       pixels.setPixelColor(ledNumber, color * pixels.gamma8(255 - j * 76) / 255);
     }
     pixels.show();
@@ -116,7 +124,7 @@ namespace led_effects {
   // Takes approximately 1s for each direction.
   void showKITTWheel(uint32_t color, uint16_t duration_s) {
     pixels.setBrightness(config::max_brightness);
-    for (int i = 0; i < duration_s * NUMPIXELS; ++i) {
+    for (int i = 0; i < duration_s * config::led_count; ++i) {
       showWaitingLED(color);
     }
   }
@@ -126,10 +134,10 @@ namespace led_effects {
    * For example, for 1500ppm, every LED between 0 and 7 (500 -> 1400ppm) should be on, LED at 8 (1600ppm) should be half-on.
    */
   uint8_t getLedBrightness(uint16_t co2, int ledId) {
-    if (co2 >= CO2_TICKS[ledId + 1]) {
+    if (co2 >= config::co2_ticks[ledId + 1]) {
       return 255;
     } else {
-      if (2 * co2 >= CO2_TICKS[ledId] + CO2_TICKS[ledId + 1]) {
+      if (2 * co2 >= config::co2_ticks[ledId] + config::co2_ticks[ledId + 1]) {
         // Show partial LED if co2 more than halfway between ticks.
         return 27; // Brightness isn't linear, so 27 / 255 looks much brighter than 10%
       } else {
@@ -158,9 +166,9 @@ namespace led_effects {
       return;
     }
     pixels.setBrightness(config::max_brightness);
-    for (int ledId = 0; ledId < NUMPIXELS; ++ledId) {
+    for (int ledId = 0; ledId < config::led_count; ++ledId) {
       uint8_t brightness = getLedBrightness(co2, ledId);
-      pixels.setPixelColor(ledId, pixels.ColorHSV(LED_HUES[ledId], 255, brightness));
+      pixels.setPixelColor(ledId, pixels.ColorHSV(config::led_hues[ledId], 255, brightness));
     }
     pixels.show();
     if (config::brightness_amplitude > 0) {
@@ -177,8 +185,8 @@ namespace led_effects {
     unsigned long t0 = millis();
     pixels.setBrightness(config::max_brightness);
     while (millis() - t0 < duration_ms) {
-      for (int i = 0; i < NUMPIXELS; i++) {
-        pixels.setPixelColor(i, pixels.ColorHSV(i * 65535 / NUMPIXELS + wheel_offset));
+      for (int i = 0; i < config::led_count; i++) {
+        pixels.setPixelColor(i, pixels.ColorHSV(i * 65535 / config::led_count + wheel_offset));
         wheel_offset += (pixels.sine8(sine_offset++ / 50) - 127) / 2;
       }
       pixels.show();
@@ -215,7 +223,7 @@ namespace led_effects {
     pixels.fill(color::blue);
     pixels.show();
     int countdown;
-    for (countdown = NUMPIXELS; countdown >= 0 && !digitalRead(0); countdown--) {
+    for (countdown = config::led_count; countdown >= 0 && !digitalRead(0); countdown--) {
       pixels.setPixelColor(countdown, color::black);
       pixels.show();
       Serial.println(countdown);