Для проекта требуется лента на 86 адресных светодиодов и плата микроконтроллера ESP32 

Первым делом на какой-то фанерке надо поклеить наши будущие сегменты индикаторов, на мой взгляд самый оптимальный вариант размещения такой

WS2812b часы один разряд

Для красоты циферки сделал немного под наклоном

Общий вид индикатора часов на WS2812b

В качестве контроллера использован ESP32-S3-Mini

ESP32-S3-Mini
В принципе скетч не критичен микроконтроллеру, можно использовать любой ESP32 и даже ESP8266. Даже больше скажу, изначально планировалось использование платы ESP-01, но по случаю купил на Али пачку этих мини по цене 01 и не стал заморачиваться с той. В принципе отличие только в отсутствии стабилизатора 3.3В, ну и она за счет распаянных ножек габаритнее.

Сигнал с 8 ноги ESP32 подается на начало адресной ленты через резистор 220 Ом, он нужен чтобы не спалить порт в микроконтроллере, если случайно там будет земля или плюс. Не первый раз так использую, не нужно никакого преобразователя уровня!

Перед компиляцией скетча исправьте параметры вашего WiFi роутера.

В настройках Arduino.IDE я выставлял плату ESP32-C3-Dev


Как добавлять в Ардуино поддержку ESP32 и ESP8266 ищите в интернете, если еще не добавили

В написании скетча мне помогал Gemeni от Гугля, спасибо ему за подсказки.

Пока все запитывается от тайп-с на плате микроконтроллера, с минимальной яркостью ток всей схемы 100 мА, так что не страшно. В планах сделать батарейное питание в случае отсутствия света. Платки типа упс-а (вход 5В, выход 5В 1А) вчера получил на почте. Плюс будет схема определения наличия сетевого напряжения, снижение яркости ночью и когда света нет, а также подумываю сделать отключение запросов к WiFi ночью и без света. Платы микроконтроллера имеют не сильно точный резонатор, но тут точность и не нужна, как появится свет, произойдет коррекция времени и расхождения в несколько секунд никто не заметит.

Код скетча. 

  1
  2
  3
  4
  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
 50
 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
#include <WiFi.h>
#include <time.h>
#include <FastLED.h>

// --- НАСТРОЙКИ ---
const char* ssid     = "wifi";
const char* password = "pass";

#define LED_PIN     8         
#define NUM_LEDS    86        // (21 * 4) + 2 = 86
#define BRIGHTNESS  60        
#define LED_TYPE    WS2812B
#define COLOR_ORDER GRB

// Правило времени для Украины (Киев)
const char* TZ_INFO = "EET-2EEST,M3.5.0/3,M10.5.0/4";

CRGB leds[NUM_LEDS];

// Маска сегментов (0 - выкл, 1 - вкл)
// Порядок в массиве: G, B, A, F, E, D, C
const byte segments[10][7] = {
  {0,1,1,1,1,1,1}, // 0
  {0,1,1,0,0,0,0}, // 1
  {1,1,0,1,1,0,1}, // 2
  {1,1,1,1,0,0,1}, // 3
  {0,1,1,0,0,1,1}, // 4
  {1,0,1,1,0,1,1}, // 5
  {1,0,1,1,1,1,1}, // 6
  {1,1,1,0,0,0,0}, // 7
  {1,1,1,1,1,1,1}, // 8
  {1,1,1,1,0,1,1}  // 9
};

void setup() {
  Serial.begin(115200);
  Serial.print("ESP32 on WS2812 Clock (C) 2025.12.24");
  
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();
  // Test вывод цифр
  drawDigit(0, 8);
  drawDigit(1, 8);
  drawDigit(2, 8);
  drawDigit(3, 8);

  FastLED.show();

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  // Настройка NTP для Украины
  configTzTime(TZ_INFO, "pool.ntp.org", "europe.pool.ntp.org");
}

void loop() {
  time_t now;
  struct tm timeinfo;

  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }

  FastLED.clear();

  int h = timeinfo.tm_hour;
  int m = timeinfo.tm_min;
  bool dotState = timeinfo.tm_sec % 2; // Мигание раз в секунду

  // Отрисовка
  drawDigit(0, h / 10); // Часы (десятки)
  drawDigit(1, h % 10); // Часы (единицы)
  
  // Двоеточие (светодиоды под индексами 42 и 43, после первых двух цифр)
  if (dotState) {
    leds[42] = CRGB::DeepSkyBlue;
    leds[43] = CRGB::DeepSkyBlue;
  }

  drawDigit(2, m / 10); // Минуты (десятки)
  drawDigit(3, m % 10); // Минуты (единицы)

  FastLED.show();
  delay(1000);
}

void drawDigit(int pos, int num) {
  // Вычисляем начальный индекс для каждой цифры
  // Цифра 0 и 1 идут подряд (0..20, 21..41)
  // Между 1 и 2 цифрой стоят 2 диода двоеточия (+2 к смещению)
  int startIdx = pos * 21;
  if (pos >= 2) startIdx += 2;

  CRGB color = CRGB::DeepSkyBlue;

  // Проходим по всем 7 сегментам цифры
  for (int seg = 0; seg < 7; seg++) {
    if (segments[num][seg]) {
      // Если сегмент должен гореть, зажигаем 3 светодиода этого сегмента
      for (int i = 0; i < 3; i++) {
        leds[startIdx + (seg * 3) + i] = color;
      }
    }
  }
}

Раскраска кода в Блоггер - https://hilite.me/