Часы Nixie Clock: инструкция, примеры использования и документация
Используйте часы Nixie Clock в качестве стильного гаджета на своём столе дома, мастерской или на работе. Часы не просто показывают текущее время, а делает это максимально зрелищно благодаря прозрачному циферблату, который имитирует знаменитые газоразрядные индикаторы Nixie Tube. Только вместо олдскульных газоразрядных ламп наподобие ИН-12, ИН-14, ИН-18 — современная управляемая LED-подсветка на WS2812B.
Что такое Nixie Clock?
В конце 90-х было модно собирать часы на газоразрядных лампах Nixie Tube моделей ИН-12, ИН-14, ИН-18.
Мы решили дать возможность каждому собрать модные олдскульные часы. Однако вместо дефицитных газоразрядных ламп используем акриловые пластины с выгравированными цифрами, которые подсветим адресными светодиодами WS2812B.
Каждый разряд часов состоит из десяти вырезанных лазерным резаком прямоугольников из прозрачного акрила.
На каждом прямоугольнике выполнена гравировка цифры от 0 до 9.
Если посветить в торец прямоугольника, то выгравированная цифра начнет светиться цветом, которым светит светодиод. Собрав десять таких цифр в блок и подсвечивая каждую пластинку акрила отдельным светодиодом можно по-отдельности зажигать каждую цифру. Поскольку свет входит в акрил с края, происходит полное внутреннее отражение и акрил ведет себя как световод.
С теорией разобрались, вперёд собирать стильные часы.
Как собрать
Комплектация
Шаг 1
Удалите защитную плёнку с всех пластиковых деталей в наборе. Детали покрыты защитной плёнкой с двух сторон. Плёнку можно легко снять поддев её на краю детали используя острый инструмент.
Шаг 2
Вставьте батарейку типоразмера CR1225 в батарейный отсек платы Nixie Clock. Батарейка идет в комплекте с часами.
В часах Nixie Clock батарейка используется для хранения пользовательских настроек цвета, анимации и отсчета времени при отключении часов от питания.
Шаг 3
Установите пластины А
, B
и С
снизу платы Nixie Clock, используя пять винтов M3×25.
Шаг 4
Вставьте секундный рассеиватель в пластину Е
. Установите пластины E
и D
сверху платы Nixie Clock и закрепите их пятью алюминиевыми стойками.
Не затягивайте винты слишком сильно! Сильно затянутые винты создадут трение между пластинами А
, В
и С
что будет препятствовать нормальному нажатию кнопок.
Шаг 5
Через пластину F установите в часы четыре набора цифр. Один набор цифр состоит из 10 пластиковых деталей с цифрами от 0 до 9. Цифры устанавливаются по возрастанию. В каждом разряде цифра 0 находится ближе всего к лицевой стороне часов.
Для удобства установки цифр, пластину F
можно временно закрепить винтами к алюминиевым стойкам.
Шаг 6
Установите сверху часов пластину G
и закрепите её пятью винтами М3×10.
Шаг 7
Вуаля часы собраны, остаётся только подать питания. Подключите 5 В к часам через разъём Micro-USB. В качестве источника питания подойдёт USB-порт компьютера или наш сетевой адаптер 5 В.
Вот и всё, часы тикают и радуют ваш взгляд.
Если стандартной работы часов вам мало и чувствуете что можете улучшить гаджет, переходите в раздел разботчика.
Раздел разработчика
По умолчанию на Nixie Clock установлена заводская прошивка. Но вы можете запрограммировать гаджет по-своему.
- Подключите Nixie Clock к компьютеру
- Установите и настройте Arduino IDE.
Часы выполнены на микроконтроллере ATmega328P с зашитым загрузчиком от Arduino Uno. Однако на плате Nixie Clock распаяна дополнительная периферия: часы реального времени, кнопки и светодиоды.
Особенности распиновки вы найдёте ниже. И не бойтесь экспериментировать, код часов по умолчанию всегда можно восстановить, если что-то пошло не так.
Распиновка платы Nixie Clock
Для создания собственной прошивки часов вам понадобится информация о задействованных пинах микроконтроллера ATmega328P.
Прошивка платы
Часы Nixie Clock поставляются прошитыми, однако при желании вы можете изменить или дополнить оригинальную прошивку.
Необходимые библиотеки
В прошивке платы Nixie Clock используются следующие Arduino-библиотеки:
- Adafruit NeoPixel Library — библиотека для управления адресными светодиодами WS2812B.
- TroykaButton — библиотека для работы с кнопками.
- TroykaRTC — библиотека для работы с часами реального времени DS1307.
Убедитесь, что эти библиотеки установлены в ваше рабочее пространство Arduino IDE.
Обратите внимание
Если вы покупали Nixie Clock после 15.07.2024, то для корректной работы часов нужно раскомментировать 8-ю строку кода в штатной программе и прошить плату:
#define TIME_SOURCE_MCU
Штатный код часов
- Clock.ino
- #include <Adafruit_NeoPixel.h>
- #include <TroykaButton.h>
- #include <TroykaRTC.h>
- #include <Wire.h>
- // Uncomment the line below to source time from an MCU circuit rather than RTC.
- // Use one having best precision in hardware.
- // #define TIME_SOURCE_MCU
- // Pin definition
- constexpr uint8_t BUTTON_MODE_PIN = A3;
- constexpr uint8_t BUTTON_OK_PIN = A2;
- constexpr uint8_t BUTTON_UP_PIN = A1;
- constexpr uint8_t BUTTON_DOWN_PIN = A0;
- constexpr uint8_t WS2812_DOT_PIN = 9;
- constexpr uint8_t WS2812_MATRIX_PIN = 2;
- // Class objects
- Adafruit_NeoPixel dot = Adafruit_NeoPixel(1, WS2812_DOT_PIN, NEO_GRB + NEO_KHZ800);
- Adafruit_NeoPixel matrix = Adafruit_NeoPixel(40, WS2812_MATRIX_PIN, NEO_GRB + NEO_KHZ800);
- RTC clock;
- TroykaButton button_mode(BUTTON_MODE_PIN, 1000, true, 200);
- TroykaButton button_ok(BUTTON_OK_PIN, 1000, true, 200);
- TroykaButton button_up(BUTTON_UP_PIN, 1000, true, 200);
- TroykaButton button_down(BUTTON_DOWN_PIN, 1000, true, 200);
- // Time variables
- uint8_t current_hour = 0;
- uint8_t current_minute = 0;
- uint8_t target_hour = 0;
- uint8_t target_minute = 0;
- #ifdef TIME_SOURCE_MCU
- volatile uint16_t isr_counter = 0; // Used for clock source from AVR hardware timer
- volatile uint8_t isr_current_hour = 0; // Used for clock source from AVR hardware timer
- volatile uint8_t isr_current_minute = 0; // Used for clock source from AVR hardware timer
- #endif
- // LED variables
- uint32_t now_time = 0;
- uint32_t digit_color[4] = { 0, 0, 0, 0 };
- uint32_t digit_previous_color[4] = { 0, 0, 0, 0 };
- uint8_t digit_current_led[4] = { 255, 255, 255, 255 };
- uint8_t digit_previous_led[4] = { 255, 255, 255, 255 };
- uint8_t digit_old_led[4] = { 255, 255, 255, 255 };
- bool dot_blinking_state = false;
- #ifndef TIME_SOURCE_MCU
- uint32_t dot_blinking_last_time = 0;
- #endif
- constexpr uint16_t MATRIX_BLINKING_INTERVAL_MS = 250;
- uint32_t matrix_blinking_last_time = 0;
- bool matrix_blinking_state = false;
- // Modes variables and definitions
- enum WorkingMode {
- WORKING_MODE_CLOCK = 1,
- WORKING_MODE_SET_TIME_HOURS = 2,
- WORKING_MODE_SET_TIME_MINUTES = 3,
- WORKING_MODE_MENU_1 = 4,
- WORKING_MODE_MENU_2 = 5,
- WORKING_MODE_MENU_3 = 6,
- };
- uint8_t current_working_mode = WORKING_MODE_CLOCK;
- bool new_working_mode = false;
- uint8_t current_setting_1_value = 0;
- uint8_t current_setting_2_value = 0;
- uint8_t current_setting_3_value = 0;
- uint8_t target_setting_1_value = 0;
- uint8_t target_setting_2_value = 0;
- uint8_t target_setting_3_value = 0;
- constexpr uint8_t TOTAL_SETTING_1_VALUES = 17;
- constexpr uint8_t TOTAL_SETTING_2_VALUES = 4;
- constexpr uint8_t TOTAL_SETTING_3_VALUES = 3;
- // Color variables
- int16_t delta_hue[4] = { 0, 0, 0, 0 };
- uint8_t digit_brightness[4] = { 255, 255, 255, 255 };
- uint8_t digit_previous_brightness[4] = { 255, 255, 255, 255 };
- // Animation variables
- enum ColorAnimation {
- COLOR_ANIMATION_NONE = 0,
- COLOR_ANIMATION_PENDULUM = 1,
- COLOR_ANIMATION_WAVE_PENDULUM = 2,
- COLOR_ANIMATION_DEEPNESS = 3,
- };
- constexpr float COLOR_ANIMATION_RATE_MS = 12.0 / 60000.0; // Changes in ms
- constexpr float COLOR_ANIMATION_PHASE_SHIFT = 0.125;
- uint32_t previous_color_animation_time = 0;
- float color_animation_timer = 0;
- enum DigitAnimation {
- DIGIT_ANIMATION_NONE = 0,
- DIGIT_ANIMATION_RUNNING_LED = 1,
- DIGIT_ANIMATION_FADE = 2,
- };
- bool digit_animation_is_preview = false;
- uint32_t previous_digit_animation_fade_preview_time = 0;
- uint32_t previous_digit_animation_running_led_preview_time = 0;
- constexpr uint32_t DIGIT_ANIMATION_RUNNING_LED_PREVIEW_TIME_MS = 4000;
- constexpr uint32_t DIGIT_ANIMATION_FADE_PREVIEW_TIME_MS = 6500;
- uint8_t running_led[4] = { 255, 255, 255, 255 };
- constexpr uint32_t DIGIT_ANIMATION_RUNNING_LED_RATE_MS = 30;
- uint32_t previous_digit_animation_running_led_time = 0;
- constexpr float FADE_ANIMATION_RATE_MS = 3000.0; // 3 sec
- uint32_t previous_fade_animation_time[4] = { 0, 0, 0, 0 };
- float fade_animation_timer[4] = { 0, 0, 0, 0 };
- // Auxiliary
- uint16_t degree_hue_to_uint16_hue(int16_t degree) {
- return (float)((degree % 360) / 360.0 * 65535.0);
- }
- // CLOCK
- void read_time() {
- clock.read();
- current_hour = clock.getHour();
- current_minute = clock.getMinute();
- #ifdef TIME_SOURCE_MCU
- set_volatile_data();
- #endif
- }
- void read_ram_data() {
- uint8_t data = clock.getRAMData(0x0A); // Menu 1 setting
- current_setting_1_value = ((data > 0) && (data < TOTAL_SETTING_1_VALUES)) ? data : 0;
- data = clock.getRAMData(0x0B); // Menu 2 setting
- current_setting_2_value = ((data > 0) && (data < TOTAL_SETTING_2_VALUES)) ? data : 0;
- data = clock.getRAMData(0x0C); // Menu 3 setting
- current_setting_3_value = ((data > 0) && (data < TOTAL_SETTING_3_VALUES)) ? data : 0;
- }
- void set_ram_data() {
- clock.setRAMData(0x0A, current_setting_1_value); // Menu 1 setting
- clock.setRAMData(0x0B, current_setting_2_value); // Menu 2 setting
- clock.setRAMData(0x0C, current_setting_3_value); // Menu 3 setting
- }
- // MODE button
- void button_mode_handler() {
- button_mode.read();
- if (button_mode.isClick()) {
- switch (current_working_mode) {
- case WORKING_MODE_CLOCK:
- current_working_mode = WORKING_MODE_SET_TIME_HOURS;
- break;
- case WORKING_MODE_SET_TIME_HOURS:
- clock.setHour(target_hour);
- clock.setSecond(0);
- read_time();
- current_working_mode = WORKING_MODE_SET_TIME_MINUTES;
- break;
- case WORKING_MODE_SET_TIME_MINUTES:
- clock.setMinute(target_minute);
- clock.setSecond(0);
- read_time();
- current_working_mode = WORKING_MODE_CLOCK;
- break;
- case WORKING_MODE_MENU_1:
- current_setting_1_value = target_setting_1_value;
- set_ram_data();
- current_working_mode = WORKING_MODE_MENU_2;
- break;
- case WORKING_MODE_MENU_2:
- current_setting_2_value = target_setting_2_value;
- set_ram_data();
- current_working_mode = WORKING_MODE_MENU_3;
- break;
- case WORKING_MODE_MENU_3:
- current_setting_3_value = target_setting_3_value;
- set_ram_data();
- current_working_mode = WORKING_MODE_CLOCK;
- break;
- default:
- break;
- }
- new_working_mode = true;
- } else if (button_mode.isHold()) {
- switch (current_working_mode) {
- case WORKING_MODE_CLOCK:
- current_working_mode = WORKING_MODE_MENU_1;
- break;
- default:
- break;
- }
- new_working_mode = true;
- }
- }
- // UP button
- void button_up_handler() {
- button_up.read();
- if (!button_up.isClickOnHold() && !button_up.justPressed())
- return;
- switch (current_working_mode) {
- case WORKING_MODE_SET_TIME_HOURS:
- target_hour = (target_hour + 1) % 24;
- break;
- case WORKING_MODE_SET_TIME_MINUTES:
- target_minute = (target_minute + 1) % 60;
- break;
- case WORKING_MODE_MENU_1:
- target_setting_1_value = (target_setting_1_value + 1) % TOTAL_SETTING_1_VALUES;
- break;
- case WORKING_MODE_MENU_2:
- target_setting_2_value = (target_setting_2_value + 1) % TOTAL_SETTING_2_VALUES;
- break;
- case WORKING_MODE_MENU_3:
- target_setting_3_value = (target_setting_3_value + 1) % TOTAL_SETTING_3_VALUES;
- break;
- default:
- break;
- }
- }
- // DOWN button
- void button_down_handler() {
- button_down.read();
- if (!button_down.isClickOnHold() && !button_down.justPressed())
- return;
- switch (current_working_mode) {
- case WORKING_MODE_SET_TIME_HOURS:
- target_hour = (target_hour - 1 + 24) % 24;
- break;
- case WORKING_MODE_SET_TIME_MINUTES:
- target_minute = (target_minute - 1 + 60) % 60;
- break;
- case WORKING_MODE_MENU_1:
- target_setting_1_value = (target_setting_1_value - 1 + TOTAL_SETTING_1_VALUES) % TOTAL_SETTING_1_VALUES;
- break;
- case WORKING_MODE_MENU_2:
- target_setting_2_value = (target_setting_2_value - 1 + TOTAL_SETTING_2_VALUES) % TOTAL_SETTING_2_VALUES;
- break;
- case WORKING_MODE_MENU_3:
- target_setting_3_value = (target_setting_3_value - 1 + TOTAL_SETTING_3_VALUES) % TOTAL_SETTING_3_VALUES;
- break;
- default:
- break;
- }
- }
- // OK button
- void button_ok_handler() {
- button_ok.read();
- if (!button_ok.isClick())
- return;
- switch (current_working_mode) {
- case WORKING_MODE_SET_TIME_HOURS:
- case WORKING_MODE_SET_TIME_MINUTES:
- clock.setHour(target_hour);
- clock.setMinute(target_minute);
- clock.setSecond(0);
- read_time();
- current_working_mode = WORKING_MODE_CLOCK;
- break;
- case WORKING_MODE_MENU_1:
- case WORKING_MODE_MENU_2:
- case WORKING_MODE_MENU_3:
- current_setting_1_value = target_setting_1_value;
- current_setting_2_value = target_setting_2_value;
- current_setting_3_value = target_setting_3_value;
- set_ram_data();
- current_working_mode = WORKING_MODE_CLOCK;
- break;
- default:
- break;
- }
- new_working_mode = true;
- }
- void working_mode_handler() {
- if (!new_working_mode)
- return;
- switch (current_working_mode) {
- case WORKING_MODE_SET_TIME_HOURS:
- target_hour = current_hour;
- target_minute = current_minute;
- break;
- case WORKING_MODE_MENU_1:
- target_setting_1_value = current_setting_1_value;
- break;
- case WORKING_MODE_MENU_2:
- target_setting_2_value = current_setting_2_value;
- break;
- case WORKING_MODE_MENU_3:
- target_setting_3_value = current_setting_3_value;
- break;
- default:
- break;
- }
- new_working_mode = false;
- }
- uint32_t nth_preset_color(uint8_t current_color_setting, int16_t hue_change = 0, uint8_t brightness = 255) {
- return (current_color_setting < 16 && current_color_setting > 0) ? Adafruit_NeoPixel::ColorHSV(degree_hue_to_uint16_hue((current_color_setting - 1) * 24 + hue_change), 255, brightness) : 0xFFFFFFFF;
- }
- void dot_handler() {
- uint32_t dot_color;
- switch (current_working_mode) {
- case WORKING_MODE_CLOCK:
- if (dot_blinking_state) {
- dot_color = nth_preset_color(current_setting_1_value);
- dot.setPixelColor(0, dot_color);
- } else {
- dot.setPixelColor(0, 0);
- }
- break;
- default:
- dot.setPixelColor(0, 0);
- break;
- }
- dot.show();
- }
- float saw_wave(float x) {
- float fract = fmod(x, 1);
- float y = (fract < 0.5) ? (fract / 0.5) : (((fract - 0.5) / -0.5) + 1.0);
- return y = 2 * y - 1;
- }
- void color_animation_handler() {
- uint32_t dt = now_time - previous_color_animation_time;
- color_animation_timer = color_animation_timer + (dt * COLOR_ANIMATION_RATE_MS);
- float a = 0;
- previous_color_animation_time = now_time;
- if (target_setting_2_value == COLOR_ANIMATION_PENDULUM || current_setting_2_value == COLOR_ANIMATION_PENDULUM) {
- a = saw_wave(color_animation_timer);
- for (uint8_t i = 0; i < 4; i++)
- delta_hue[i] = 24 * a;
- } else if (target_setting_2_value == COLOR_ANIMATION_WAVE_PENDULUM || current_setting_2_value == COLOR_ANIMATION_WAVE_PENDULUM) {
- for (uint8_t i = 0; i < 4; i++) {
- a = saw_wave(color_animation_timer + (i + 1) * COLOR_ANIMATION_PHASE_SHIFT);
- delta_hue[i] = 24 * a;
- }
- } else if (target_setting_2_value == COLOR_ANIMATION_DEEPNESS || current_setting_2_value == COLOR_ANIMATION_DEEPNESS) {
- for (uint8_t i = 0; i < 4; i++)
- delta_hue[i] = (digit_current_led[i] - 10 * i) * 24.0 / 10.0;
- } else // COLOR_ANIMATION_NONE
- for (uint8_t i = 0; i < 4; i++)
- delta_hue[i] = 0;
- }
- void animation_running_led_handler() {
- if (now_time - previous_digit_animation_running_led_time > DIGIT_ANIMATION_RUNNING_LED_RATE_MS) {
- for (uint8_t i = 0; i < 4; i++)
- if (running_led[i] > digit_old_led[i])
- running_led[i]--;
- previous_digit_animation_running_led_time = now_time;
- }
- for (uint8_t i = 0; i < 4; i++) {
- if (digit_current_led[i] != digit_old_led[i]) {
- running_led[i] = 10 * (i + 1) - 1;
- digit_old_led[i] = digit_current_led[i];
- }
- }
- // Preview
- if (digit_animation_is_preview && (now_time - previous_digit_animation_running_led_preview_time > DIGIT_ANIMATION_RUNNING_LED_PREVIEW_TIME_MS)) {
- for (uint8_t i = 0; i < 4; i++)
- running_led[i] = 10 * (i + 1) - 1;
- previous_digit_animation_running_led_preview_time = now_time;
- }
- // Show
- for (uint8_t i = 0; i < 4; i++)
- digit_current_led[i] = running_led[i];
- }
- float fade_wave(float x) {
- float fract = fmod(x, 1);
- return pow(fract == 0 ? 1 : fract, 2);
- }
- void animation_fade_handler() {
- for (uint8_t i = 0; i < 4; i++) {
- if (digit_current_led[i] != digit_old_led[i]) {
- digit_previous_led[i] = digit_old_led[i];
- if (fade_animation_timer[i] >= 1.0) {
- fade_animation_timer[i] = 0;
- }
- }
- }
- for (uint8_t i = 0; i < 4; i++) {
- if (fade_animation_timer[i] < 1.0) {
- uint32_t dt = now_time - previous_fade_animation_time[i];
- fade_animation_timer[i] = fade_animation_timer[i] + (dt * (1.0 / FADE_ANIMATION_RATE_MS));
- }
- if (fade_animation_timer[i] >= 1.0) {
- fade_animation_timer[i] = 1.0;
- digit_old_led[i] = digit_current_led[i];
- }
- previous_fade_animation_time[i] = now_time;
- }
- // Preview
- if (digit_animation_is_preview && (now_time - previous_digit_animation_fade_preview_time > DIGIT_ANIMATION_FADE_PREVIEW_TIME_MS)) {
- for (uint8_t i = 0; i < 4; i++) {
- digit_previous_led[i] = 255;
- fade_animation_timer[i] = 0;
- }
- previous_digit_animation_fade_preview_time = now_time;
- }
- // Show
- for (uint8_t i = 0; i < 4; i++) {
- float a = fade_wave(fade_animation_timer[i]);
- digit_brightness[i] = 255 * a;
- digit_previous_brightness[i] = 255 * (1 - a);
- }
- }
- void matrix_handler() {
- matrix.clear();
- memset(digit_brightness, 255, 4 * sizeof(digit_brightness[0]));
- // Choose digits
- switch (current_working_mode) {
- case WORKING_MODE_SET_TIME_HOURS:
- case WORKING_MODE_SET_TIME_MINUTES:
- digit_current_led[0] = target_hour / 10;
- digit_current_led[1] = (target_hour % 10) + 10;
- digit_current_led[2] = (target_minute / 10) + 20;
- digit_current_led[3] = (target_minute % 10) + 30;
- break;
- case WORKING_MODE_CLOCK:
- digit_current_led[0] = current_hour / 10;
- digit_current_led[1] = (current_hour % 10) + 10;
- digit_current_led[2] = (current_minute / 10) + 20;
- digit_current_led[3] = (current_minute % 10) + 30;
- break;
- case WORKING_MODE_MENU_1:
- digit_current_led[0] = 1; // Menu 1
- digit_current_led[1] = 10;
- digit_current_led[2] = target_setting_1_value >= 9 ? (((target_setting_1_value + 1) / 10) + 20) : 20;
- digit_current_led[3] = ((target_setting_1_value + 1) % 10) + 30;
- break;
- case WORKING_MODE_MENU_2:
- digit_current_led[0] = 2; // Menu 2
- digit_current_led[1] = 10;
- digit_current_led[2] = target_setting_2_value >= 9 ? (((target_setting_2_value + 1) / 10) + 20) : 20;
- digit_current_led[3] = ((target_setting_2_value + 1) % 10) + 30;
- break;
- case WORKING_MODE_MENU_3:
- digit_current_led[0] = 3; // Menu 3
- digit_current_led[1] = 10;
- digit_current_led[2] = target_setting_3_value >= 9 ? (((target_setting_3_value + 1) / 10) + 20) : 20;
- digit_current_led[3] = ((target_setting_3_value + 1) % 10) + 30;
- break;
- default:
- break;
- }
- // Color animation
- color_animation_handler();
- switch (current_working_mode) {
- case WORKING_MODE_CLOCK:
- digit_animation_is_preview = false;
- if (current_setting_3_value == DIGIT_ANIMATION_RUNNING_LED)
- animation_running_led_handler();
- else if (current_setting_3_value == DIGIT_ANIMATION_FADE)
- animation_fade_handler();
- break;
- case WORKING_MODE_MENU_3:
- digit_animation_is_preview = true;
- if (target_setting_3_value == DIGIT_ANIMATION_RUNNING_LED)
- animation_running_led_handler();
- else if (target_setting_3_value == DIGIT_ANIMATION_FADE)
- animation_fade_handler();
- break;
- default:
- break;
- }
- // Choose colors
- switch (current_working_mode) {
- case WORKING_MODE_SET_TIME_HOURS:
- case WORKING_MODE_SET_TIME_MINUTES:
- case WORKING_MODE_CLOCK:
- for (uint8_t i = 0; i < 4; i++) {
- if (current_setting_1_value == 16) {
- digit_color[i] = Adafruit_NeoPixel::ColorHSV(degree_hue_to_uint16_hue(i * 48 + delta_hue[i]), 255, digit_brightness[i]);
- if (current_setting_3_value == DIGIT_ANIMATION_FADE)
- digit_previous_color[i] = Adafruit_NeoPixel::ColorHSV(degree_hue_to_uint16_hue(i * 48 + delta_hue[i]), 255, digit_previous_brightness[i]);
- } else {
- digit_color[i] = nth_preset_color(current_setting_1_value, delta_hue[i], digit_brightness[i]);
- if (current_setting_3_value == DIGIT_ANIMATION_FADE)
- digit_previous_color[i] = nth_preset_color(current_setting_1_value, delta_hue[i], digit_previous_brightness[i]);
- }
- }
- break;
- case WORKING_MODE_MENU_1:
- case WORKING_MODE_MENU_2:
- case WORKING_MODE_MENU_3:
- for (uint8_t i = 0; i < 4; i++)
- if (target_setting_1_value == 16)
- digit_color[i] = Adafruit_NeoPixel::ColorHSV(degree_hue_to_uint16_hue(i * 48 + delta_hue[i]), 255, digit_brightness[i]);
- else
- digit_color[i] = nth_preset_color(target_setting_1_value, delta_hue[i], digit_brightness[i]);
- break;
- default:
- break;
- }
- // Show digits
- switch (current_working_mode) {
- case WORKING_MODE_SET_TIME_HOURS:
- case WORKING_MODE_SET_TIME_MINUTES:
- if ((now_time - matrix_blinking_last_time) >= MATRIX_BLINKING_INTERVAL_MS) {
- matrix_blinking_last_time = now_time;
- matrix_blinking_state = !matrix_blinking_state;
- }
- if (button_up.isPressed() || button_down.isPressed())
- matrix_blinking_state = true;
- if (current_working_mode == WORKING_MODE_SET_TIME_HOURS) {
- matrix.setPixelColor(digit_current_led[0], matrix_blinking_state ? digit_color[0] : 0);
- matrix.setPixelColor(digit_current_led[1], matrix_blinking_state ? digit_color[1] : 0);
- matrix.setPixelColor(digit_current_led[2], digit_color[2]);
- matrix.setPixelColor(digit_current_led[3], digit_color[3]);
- } else if (current_working_mode == WORKING_MODE_SET_TIME_MINUTES) {
- matrix.setPixelColor(digit_current_led[0], digit_color[0]);
- matrix.setPixelColor(digit_current_led[1], digit_color[1]);
- matrix.setPixelColor(digit_current_led[2], matrix_blinking_state ? digit_color[2] : 0);
- matrix.setPixelColor(digit_current_led[3], matrix_blinking_state ? digit_color[3] : 0);
- }
- break;
- case WORKING_MODE_CLOCK:
- for (uint8_t i = 0; i < 4; i++) {
- matrix.setPixelColor(digit_current_led[i], digit_color[i]);
- if (current_setting_3_value == DIGIT_ANIMATION_FADE)
- matrix.setPixelColor(digit_previous_led[i], digit_previous_color[i]);
- }
- break;
- case WORKING_MODE_MENU_1:
- matrix.setPixelColor(digit_current_led[0], digit_color[0]);
- matrix.setPixelColor(digit_current_led[2], digit_color[2]);
- matrix.setPixelColor(digit_current_led[3], digit_color[3]);
- break;
- case WORKING_MODE_MENU_2:
- matrix.setPixelColor(digit_current_led[0], digit_color[0]);
- matrix.setPixelColor(digit_current_led[2], digit_color[2]);
- matrix.setPixelColor(digit_current_led[3], digit_color[3]);
- break;
- case WORKING_MODE_MENU_3:
- matrix.setPixelColor(digit_current_led[0], digit_color[0]);
- matrix.setPixelColor(digit_current_led[2], digit_color[2]);
- matrix.setPixelColor(digit_current_led[3], digit_color[3]);
- break;
- default:
- break;
- }
- matrix.setBrightness(255);
- matrix.show();
- }
- // 1Hz hardware timer
- #ifdef TIME_SOURCE_MCU
- ISR(TIMER1_COMPA_vect) {
- isr_counter++;
- if (isr_counter >= 60) {
- isr_counter = 0;
- isr_current_minute++;
- }
- if (isr_current_minute >= 60) {
- isr_current_minute = 0;
- isr_current_hour++;
- }
- if (isr_current_hour >= 24)
- isr_current_hour = 0;
- dot_blinking_state = !dot_blinking_state;
- }
- void start_blink_timer() {
- cli();
- TCCR1A = 0; // Set entire TCCR1A register to 0
- TCCR1B = 0; // Same for TCCR1B
- TCNT1 = 0; // Initialize counter value to 0
- // Set compare match register for 1hz increments
- OCR1A = 15624; // = (16*10^6) / (1*1024) - 1 (must be <65536)
- TCCR1B |= _BV(WGM12); // Turn on CTC mode
- TCCR1B |= _BV(CS12) | _BV(CS10); // Set CS12 and CS10 bits for 1024 prescaler
- TIMSK1 |= _BV(OCIE1A); // Enable blinking
- TIMSK1 |= _BV(OCIE1A);
- sei();
- }
- void stop_blink_timer() {
- TCCR1A = 0; // Set entire TCCR1A register to 0
- TCCR1B = 0; // Same for TCCR1B
- TCNT1 = 0; // Initialize counter value to 0
- }
- void fetch_volatile_data() {
- cli();
- current_hour = isr_current_hour;
- current_minute = isr_current_minute;
- sei();
- }
- void set_volatile_data() {
- cli();
- isr_current_hour = current_hour;
- isr_current_minute = current_minute;
- sei();
- }
- #endif
- // Setup
- void setup() {
- button_mode.begin();
- button_ok.begin();
- button_up.begin();
- button_down.begin();
- clock.begin();
- read_time();
- clock.set(current_hour, current_minute, 0, 3, 1, 2022, 1);
- read_ram_data();
- dot.begin();
- matrix.begin();
- dot.clear();
- dot.setBrightness(50);
- matrix.clear();
- #ifdef TIME_SOURCE_MCU
- start_blink_timer();
- #endif
- }
- void loop() {
- now_time = millis();
- #ifdef TIME_SOURCE_MCU
- fetch_volatile_data();
- #else
- read_time();
- if (now_time - dot_blinking_last_time >= 1000) {
- dot_blinking_state = !dot_blinking_state;
- dot_blinking_last_time = now_time;
- }
- #endif
- button_mode_handler();
- button_ok_handler();
- button_up_handler();
- button_down_handler();
- working_mode_handler();
- matrix_handler();
- dot_handler();
- }
Тест светодиодов
Если вам кажется что какой-то из светодиодов на плате Nixie Clock перестал работать как положено, вы можете проверить работу всех светодиодов загрузив на плату следующий код:
- TestLed.ino
- #include <Adafruit_NeoPixel.h>
- constexpr uint8_t DELAYVAL = 50;
- Adafruit_NeoPixel matrix(40, 2, NEO_GRB + NEO_KHZ800);
- Adafruit_NeoPixel dot(1, 9, NEO_GRB + NEO_KHZ800);
- void setup() {
- matrix.begin();
- dot.begin();
- }
- void loop() {
- matrix.clear();
- dot.clear();
- dot.setPixelColor(0, dot.Color(0, 150, 0));
- dot.show();
- for (int i = 0; i < 10; i++) {
- matrix.setPixelColor(i, matrix.Color(150, 150, 0));
- matrix.show();
- delay(DELAYVAL);
- }
- for (int i = 10; i < 20; i++) {
- matrix.setPixelColor(i, matrix.Color(150, 0, 150));
- matrix.show();
- delay(DELAYVAL);
- }
- for (int i = 20; i < 30; i++) {
- matrix.setPixelColor(i, matrix.Color(0, 150, 150));
- matrix.show();
- delay(DELAYVAL);
- }
- for (int i = 30; i < 40; i++) {
- matrix.setPixelColor(i, matrix.Color(0, 150, 0));
- matrix.show();
- delay(DELAYVAL);
- }
- delay(10000);
- }
Ресурсы
Часы Nixie Clock в магазине.