Нашли в сети статью одного автора о том, как он усовершенствовал свою версию часов из вольтметра — перевели её для вас.
В далёком 2019 году я собрал свои первые простые часы с вольтметром, получилась вот такая штука:

Как вы поняли из названия, в этих часах для отображения времени используются аналоговые вольтметры, а не привычные всем циферблаты. Идея не моя — в сети множество подобных проектов и инструкций, кто во что горазд. Тогда я просто сделал такие часы в деревянном корпусе и поставил их себе на рабочий стол.
Потом со временем понял, что таких поделок много, и выглядят они плюс-минус как моя. То есть коряво и довольно кустарно. Так что я решил сделать что-то красивое и современное, и задокументировать весь процесс создания. Он под катом.
Разработка началась с создания грубого макета в программе для 3D-моделирования. Я использовал Rhino3D.

Для новой версии часов я использовал три обычных панельных вольтметра, купленных на Amazon (ссылка, около 9 долларов за штуку). Я их разобрал, тщательно измерил циферблаты, а затем распечатал на самоклеящейся бумаге новые наклейки. Если вам нужны мои шаблоны в PDF — они здесь.

Обратите внимание, что на новом часовом циферблате 13 делений (от 0 до 12) а на минутном и секундном — 61 деление (от 00 до 60). Это потому, что я хотел реализовать непрерывное движение каждой стрелки. Иными словами — в 11:30 часовая стрелка должна была не просто стоять на отметке 11, а двигаться к двенадцатому делению, даже если бы она его не достигла.
Помимо кучи других проблем, у дешевых вольтметров Baomain 65C5, которые я купил, откровенно уродский пластиковый фланец. Я подумал, что будет круто его скрыть и использовать декоративный утопленный узор, чтобы передняя панель выглядела поинтереснее. Благодаря этой детали я смог вырезать переднюю и заднюю панели на станке с ЧПУ, а не делать корпус целиком вручную (как я сделал в первой, вишнёвой версии).

В качестве основного материала для корпуса я в этот раз взял кленовую доску, которую распиливают, строгают и обрабатывают обычными инструментами, а затем доводят до совершенства на станке с ЧПУ:

Что делать, если станка с ЧПУ у вас нет? Не печальтесь — проще всего будет собрать панель из двух склеенных частей, вырезав каждую из них по распечатанному (бумажному) шаблону. Чтобы идеально выровнять изгибы, можно использовать шлифовальный станок.
А вот изогнутая боковая стенка потребовала немного иного подхода. Чтобы добиться цельного вида, мне надо было согнуть плоский деревянный лист по шаблону. Для соблюдения нужного радиуса без использования станка для сгибания паром я решил сделать ряд надрезов с внутренней стороны.

Дерево надо сначала было увлажнить, а затем зажать его и дать высохнуть. Через пару дней я приклеил изогнутую боковую стенку к передней и задней панелям, ещё один шаблон, вырезанный из куска фанеры, помог добиться точного прилегания без необходимости возиться полдня с зажимами и ремнями с храповым механизмом:

В общем, вот так выглядит готовая деталь после шлифовки и покрытия нитроцеллюлозным лаком:

Вроде, круто вышло.
Что внутри
Внутрянка, само собой, будет вам гораздо менее интересна, чем корпус. Но я всё равно расскажу.
На сборку ушло около часа: я взял почтенный микроконтроллер AVR128DB28, запитал его от сетевого адаптера и подключил к кварцевому резонатору на 8 МГц (ECS-80-18-4X-CKM). Если что, кварцевый резонатор на 32,768 кГц тоже подойдет. Панели подключены к трем цифровым выводам (PC0, PC1, PC2). Два входных контакта (PD6 и PD7) я соединил с двумя небольшими кнопками на задней панели — они нужны для установки времени.

Обратите внимание, что для управления счетчиками не нужны цифро-аналоговые преобразователи или какие-либо другие дополнительные компоненты. Я просто использую относительно высокочастотную последовательность однобитных цифровых импульсов. Всё остальное делает инерция счетчика (ну, и индуктивность катушки электромагнита), приводя его в промежуточное положение, пропорциональное скважности сигнала, управляемой программным обеспечением.
Вот код.
Скрытый текст
/* Meter clock, version 2 ---------------------- Context: https://lcamtuf.substack.com/p/a-nicer-voltmeter-clock MCU: AVR128DA28 Pinout: PC0, PC1, PC2 - PWM outputs to meters (other side to gnd) PA0, PA1 - 8 MHz crystal + 18 pF caps to gnd PD6, PD7 - time adjustment buttons (other side to gnd) The only other MCU connections are power supply pins and the UPDI programming header. Meter faces: https://lcamtuf.coredump.cx/soft/embedded/meter_clock2.pdf Complaints to: <lcamtuf@coredump.cx> */ #define F_CPU 8000000 #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> /* User-friendly typedefs */ typedef int8_t s8; typedef uint8_t u8; typedef int16_t s16; typedef uint16_t u16; typedef int32_t s32; typedef uint32_t u32; /* Configure clock. External 8 MHz crystal on PA0, PA1 */ static void setup_clock() { CCP = 0xd8; /* Unlock register access */ CLKCTRL.XOSCHFCTRLA = 0b10000001; /* Enable external clock */ while(!(CLKCTRL.MCLKSTATUS & 0b10000)); CCP = 0xd8; /* Unlock register access */ CLKCTRL.MCLKCTRLA = 3; /* Switch to external clock */ while(!(CLKCTRL.MCLKSTATUS & 1)); } /* Configure ports. */ static void setup_ports() { PORTA.DIR = 0b11111111; PORTC.DIR = 0b11111111; PORTD.DIR = 0b00111111; /* PA6, PA7: buttons */ PORTF.DIR = 0b11111111; /* Pull-up for buttons */ PORTD.PIN6CTRL = 0b00001000; PORTD.PIN7CTRL = 0b00001000; /* Slew rate limit for PWM output */ PORTC.PORTCTRL = 1; } /* Configure tick update interrupt to run at 10 Hz */ static void setup_timer() { TCA0.SINGLE.INTCTRL = 0b00000001; /* OVF interrupt every PER cycles */ TCA0.SINGLE.CTRLA = 0b00001100; /* Clock prescaler / 256 (31.250 kHz) */ TCA0.SINGLE.PER = 3125 - 1; /* Effective frequency 10 Hz */ TCA0.SINGLE.CTRLA |= 1; /* Timer enable */ } /* Update time, handling wrap-around. */ static volatile u8 cur_hr, cur_min; /* Hour (0-11) and minute (0-59) */ static volatile u16 cur_secx10; /* Tenth of a second counter (0-599) */ ISR(TCA0_OVF_vect) { cur_secx10++; if (cur_secx10 == 600) { cur_secx10 = 0; cur_min++; if (cur_min == 60) { cur_min = 0; cur_hr++; if (cur_hr == 12) cur_hr = 0; } } TCA0.SINGLE.INTFLAGS = 1; /* Acknowledge interrupt */ } /* Main entry point */ int main(void) { setup_clock(); setup_ports(); setup_timer(); sei(); /* Main PWM loop. Synchronous counter running from 0 to 599 at several hundred kHz. */ u16 duty_ctr = 0; /* PWM counter */ u8 key_last = 0; /* Previous key state */ u16 adj_minx10, adj_hrx10, adj_secx10; /* Computed duty cycles for meters */ while (1) { /* Compute duty cycles. The minute gauge has divisions from 0 to 60. One minute corresponds to a duty cycle step of 10, so we multiply the minute counter accordingly, and then add another value between 0 and 9 based on the state of the second counter. For the hour gauge, we have divisions from 0 to 12, and one hour corresponds to an increment of 50. We multiply the hour counter by 50 and add another 0-49 depending on the minute counter. This is also where you can incorporate fudge factors if the meters aren't precise. */ if (!duty_ctr) { adj_minx10 = cur_min * 10 + cur_secx10 / 60, adj_hrx10 = cur_hr * 50 + adj_minx10 / 12, adj_secx10 = cur_secx10; } /* PWM actuation */ u8 pcval = 0; if (adj_secx10 > duty_ctr) pcval = 0b100; if (adj_minx10 > duty_ctr) pcval |= 0b010; if (adj_hrx10 > duty_ctr) pcval |= 0b001; PORTC.OUT = pcval; duty_ctr++; /* After 600 cycles, we reset the PWM counter and check for keypresses. */ if (duty_ctr == 600) { duty_ctr = 0; u8 pd = (PORTD.IN >> 6); /* Both inputs are high: reset state and continue */ if (pd == 0b11) { key_last = 0; continue; } /* At least one button pressed. If this is a continuation of a previous keypress, bail out. */ if (key_last) continue; key_last = 1; /* Key 1 advances the minute dial while zeroing the seconds. */ if (!(pd & 0b10)) { cur_secx10 = 0; if (++cur_min == 60) cur_min = 0; } /* Key 0 advances the hour dial without messing up any of the other dials. */ if (!(pd & 0b01)) { if (++cur_hr == 12) cur_hr = 0; } } } }
Основная идея заключается в том, чтобы с помощью прерывания таймера, синхронизированного с кварцевым резонатором, продвигать счётчик с частотой 10 Гц. После этого основной цикл обработки событий вычисляет соответствующий коэффициент заполнения и вручную переключает выходные контакты. Несмотря на то, что в микросхеме есть аппаратный модуль ШИМ, задача настолько проста, что использование схемы ШИМ ничего нам не даст.
А вот видео с «перелистыванием», снятое примерно в 11:59:59
Комментарии (19)

JeikiS
20.05.2026 11:19А где флайбэк диоды? Понимаю что в данной ситуации, видимо, обратная ЭДС настолько слаба, что затвор транзистора в авр-ке вывозит морщась, но надо, по уму. А корпус конечно бомба! Респектую!

S-trace
20.05.2026 11:19Обратите внимание, что на новом часовом циферблате 13 делений (от 0 до 12) а на минутном и секундном — 61 деление (от 00 до 60).
Про 60-ю секунду координации (когда время становится 23:59:60) я слышал (автор же, кажется, нет (судя по коду)).
А вот что он хотел сказать этой високосной минутой? В какой момент время может принять значение hh:60:ss?

Darkness_Paladin
20.05.2026 11:19Вы не поняли задумку автора. Тринадцатое часовое деление ("12") никогда не достигается: в течение 11го часа (от 11:00 до 11:59 и от 23:00 до 23:59) часовая стрелка ползёт от "11" к "12", а потом, в 12 часов, она падает на ноль и ползёт уже от нуля к часу.
С минутной стрелкой та же тема. Метка "60" нужна, чтоб ползти к ней в последнюю минуту часа, а когда минута кончится, стрелка упадёт на ноль.
Про 60-ю секунду координации (когда время становится 23:59:60) я слышал
Эту лишнюю секунду добавляют к суткам 30 июня или 31 декабря по решению Международной Службы Вращения Земли раз в несколько лет без календарной привязки, с целью синхронизации Единого Времени с реальным вращением Земли.
В часах без синхронизации через NTP состояние "23:59:60" не предусмотрено, ибо в стандартных сутках секунд в точности 60х60х24=86400 штук, не больше и не меньше.

DarkTiger
20.05.2026 11:19Точно! Понял, что свербело, пока статью читал: подсознательно ждал, когда же он до синхронизации по NTP дойдет.

Wlas
20.05.2026 11:19Я бы на одном индикаторе сделал, чтобы было три шкалы, как на старых стрелочных мультиметрах, и чтобы раз в секунду, например они по очереди показывали часы, минуты секунды и загорался индикатор на неонке, ну ладно, жёлтом светодиоде, который бы отображал, что сейчас выводится. Или, как вариант, ещё хуже, шкалу на три части поделить, часы, минуты, секунды, тогда и индикатор не нужен. Это же не время смотреть, а удивлять непосвящённых), так что важно, чтобы стрелка дергалась, а не удобство

MaFrance351
20.05.2026 11:19А я как-то думал на советском цифровом милливольтметре на ГРИ сделать. Всего-то двенадцатибитный ЦАП нужен и ОУ.

trikot
20.05.2026 11:19
Такие часы давным давно сделал мой друг из Питера Валерий Афанасьев. Кому интересно, загляните на его сайт. https://va-steam.ru/index.php

stalker_316
20.05.2026 11:19Можно ещё на тахометре и спидометре от авто сделать подобное...

Vsevo10d
20.05.2026 11:19Помню, мне было 20 с чем-то, и я (впервые на Земле, конечно же!) придумал, что прикольно было бы сделать на автомобиле задний дворник, синхронизированный со спидометром, ну и имеющий соответствующую наклейку на стекло.

FFLY
20.05.2026 11:19Покупал такой готовый набор, плата, микроконтроллер с программой и обвязка. Предполагалось подключать к вольтметрам-831

Darkness_Paladin
20.05.2026 11:19Дизайн забавный (хотя часы из шапки, с квадратыми головками, мне нравятся больше) -- но зачем в 26м году электронику часов на раритетной АВРке собирать? ИМХО, сейчас делать часы, не имеющие вайфая и потому не умеющие синхронизироваться с NTP -- как-то не айс. Да и ценник у AVR128DB28, мягко говоря, конский -- за цену этого камня можно взять esp8266 с вайфаем, и ещё останется на пиво.

mishkin79
20.05.2026 11:19Из кубика Рубика сделайте ещё) Можно капельный полив и модулируя стробоскопический эффект - цифры отображать. Или экзистенциальные часы - посадить дерево и с мыслью "когда оно вырастет мне точно пипец" пойти почитать книгу, или наконец заняться делами)

Zhabrozavr
20.05.2026 11:19Макет выглядит отлично, деревянный корпус как будто не очень сочетается с приборами.

Arhammon
Иногда ретро бывает перебор - первая версия на "современных" аналоговых головках выглядит намного читабельнее. Особенно важно учитывая экзотический способ обозначения времени.
KN_Dima
Красивое.
Но совершенно нефункциональное...