Как сделать ретро-часы Nixie Clock

В этой статье мы разберёмся, как сделать модные олдскульные часы Nixie Clock и при этом обойтись без дефицитных газоразрядных ламп. Вместо них используем акриловые пластины с выгравированными цифрами, которые подсветим адресными светодиодами WS2812B.

Если вы хотите приобрести светодиодные часы Nixie себе или в подарок, обратите внимание на готовый набор Nixie Clock.

Видеообзор

Что это?

Каждая цифра — это вырезанный лазерным резаком из прозрачного акрила толщиной 3 мм прямоугольник, на котором сделана гравировка. Если посветить в торец такого прямоугольника цветным светодиодом, то выгравированная цифра также начнёт светиться. Собрав 10 таких цифр в блок и подсвечивая каждую пластинку акрила отдельным светодиодом, можно по отдельности зажигать каждую цифру.

В качестве подсветки будем использовать адресные светодиоды WS2812B. Они занимают всего один контакт управляющей платы, при этом их можно объединить в цепочку и отдельно задавать яркость и цвет свечения каждому из них. Блоки цифр объединим по парам для отображения часов и минут. Управлять светодиодами будет Iskra Mini с Troyka-модулем часов реального времени.

Что понадобится?

Заказать лазерную резку акрила по своим чертежам можно на сервисе Figuro. А если хотите поработать на лазере самостоятельно — обратитесь, например, в ФАБ ЛАБ.

Как собрать?

А вот и векторные файлы CorelDRAW для резки. Обратите внимание, что я исходил из конкретной толщины прозрачного акрила (2,93 мм) и допусков, которые варьируются от материала к материалу и модели лазера.

  1. Каждая цифра будущих часов — отдельная пластинка из прозрачного акрила с двумя ножками для крепления и световодом, куда будет светить светодиод.
  2. Чтобы светодиоды не засвечивали соседние пластинки в блоке цифр, сделаем панель из чёрного непрозрачного акрила с отверстиями, ограничивающими световой поток.
  3. Вставим пластинки с цифрами в светоограничивающую панель. Туда же отправим разделительные точки для индикации хода часов. Снизу в панели будут видны торчащие световоды каждой из цифр. Длина этих световодов такая, чтобы светодиод прилегал к ним вплотную.
  4. Сделаем площадку для адресных светодиодов так, чтобы каждый из них светил точно в световод пластинки с цифрой, и наденем её на торчащие световоды.
  5. Обратите внимание, что у светодиодов есть один срезанный угол. Он нужен, чтобы сориентироваться, где какой контакт. Распиновка светодиода WS2812B:
  6. Самое время установить светодиоды на свои места. Отогнём контактные ножки светодиодов, чтобы к ним было удобнее подпаяться, и вставим их в посадочные места.
  7. Спаяйте все светодиоды между собой, как показано на схеме, и подключите сигнальные провода к 11 и 12 пинам Iskra Mini.

    Нажмите на схему спайки, чтобы увеличить картинку.

  8. Для точной работы часов добавим к Iskra Mini Troyka-модуль RTC. Это удобно сделать с помощью платы расширения Troyka Mini IO.
  9. Подключим кнопку и потенциометр.
  10. Теперь питание и линии DI светодиодов.

Исходный код

Осталось прошить Iskra Mini кодом программы.

ledClock.ino
// библиотека для работы с адресными светодиодами
#include <Adafruit_NeoPixel.h>
// библиотека для работы I²C
#include <Wire.h>
// библиотека для работы с часами реального времени
#include "TroykaRTC.h"
// библиотека для работы с кнопками
#include "TroykaButton.h"
 
// инициализируем подключенную кнопку
TroykaButton button(10);
 
// номер пина, к которому подключена RGB-матрица
#define MATRIX_PIN    11
 
// количество светодиодов в матрице
#define LED_COUNT 40
 
// инициализация цепочки светодиодов подсветки цифр
Adafruit_NeoPixel matrix = Adafruit_NeoPixel(LED_COUNT, MATRIX_PIN, NEO_GRB + NEO_KHZ800);
 
// инициализация светодиода разграничителя
Adafruit_NeoPixel dot = Adafruit_NeoPixel(1, 12, NEO_GRB + NEO_KHZ800);
 
// размер массива для времени
#define LEN_TIME 12
// размер массива для даты
#define LEN_DATE 12
// размер массива для дня недели
#define LEN_DOW 12
 
// создаём объект для работы с часами реального времени
RTC clock;
 
// переменные для мигания
int ledState = 0;
unsigned long previousMillis = 0; 
unsigned long currentMillis ;
 
// массив для хранения текущего времени
char time[LEN_TIME];
// массив для хранения текущей даты
char date[LEN_DATE];
// массив для хранения текущего дня недели
char weekDay[LEN_DOW];
 
int hour;
int minute;
 
int one;
int two;
int three;
int four;
 
int displaysettings=0;
 
int red=150;
int green=200;
int blue= 50;
 
 
void setup()
{
// инициализация
  button.begin();
  matrix.begin();
  dot.begin();
  clock.begin();
 
  // метод установки времени и даты в модуль вручную
  // clock.set(hour,minute,0,27,07,2005,THURSDAY);    
  // метод установки времени и даты автоматически при компиляции
  clock.set(__TIMESTAMP__);
  // что бы время не менялось при прошивки или сбросе питания
  // закоментируйте оба метода clock.set();
 
  pinMode(10, INPUT_PULLUP);
}
 
void loop()
{
  // считываем состояние кнопки
  button.read();
  // запрашиваем данные с часов  
  clock.read();
 
 
 
  // считаем нажатия чтоб переходить из режима в режим  
  if (button.justPressed()){
    displaysettings = displaysettings + 1;
  }
 
  if(displaysettings==0){
    // сохраняем текущее время 
    hour = clock.getHour();
    minute = clock.getMinute();
  }
 
  if(displaysettings == 1){
    // присваиваем значение считываемое с потенциометра
    hour = map (analogRead(A0),2, 1020, 0, 23);
    clock.set(hour,minute,0,27,07,2005,THURSDAY);
  }
 
  if(displaysettings == 2){
    // присваиваем значение считываемое с потенциометра
    minute = map (analogRead(A0), 2, 1018, 0, 59);
    clock.set(hour,minute,0,27,07,2005,THURSDAY);
  }
 
  if(displaysettings == 3){
    // присваиваем значение считываемое с потенциометра
    red = map (analogRead(A0),0, 1023, 0, 255);
  }
 
  if(displaysettings == 4){
    // присваиваем значение считываемое с потенциометра
    green = map (analogRead(A0),0, 1023, 0, 255);
  }
 
  if(displaysettings == 5){
    // присваиваем значение считываемое с потенциометра
    blue = map (analogRead(A0),0, 1023, 0, 255);
  }
 
  if(displaysettings == 6){
    displaysettings = 0;
  }
 
  // делим минуты и часы на разряды
  one = hour / 10;
  two = hour % 10;
  three = minute / 10;
  four = minute % 10;
 
  matrix.clear();
 
  // зажигаем нужные светодиоды
  matrix.setPixelColor(four, red, green, blue);
  matrix.setPixelColor(three + 10,  red, green, blue);
  matrix.setPixelColor(two + 20,  red, green, blue);
  matrix.setPixelColor(one + 30,  red, green, blue);
  matrix.show();
 
  // ждём одну секунду
  delay(10);
 
  // мигание разделителя
  blinking();
}
 
void blinking(){
  if (millis() - previousMillis >= 1000) {
    previousMillis = millis();
    if (ledState == 0) {
      ledState = 1;
      dot.setPixelColor(0, red, green, blue);
    } else {
      ledState = 0;
      dot.setPixelColor(0, 0, 0, 0);
      }
     dot.show();
    }
}