So one day whilst endlessly scrolling TikTok, I came across this clock, which I thought looked pretty cool. so I bought it.
After a week or so, I decided I liked it, but I would like it more if i could dim the digits. There is an auto dimming feature, which only dims the digits and not the second dots.
So in true Big Clive fashion… I took it to bits. Pulled out its little brain and reverse engineered it.
The yellow box shows the modification to the second flashing dots, so they aren’t just powered direct from the 5v rail and grounded at the main IC. I had to grind away any connection to the LEDS leaving enough pad to solder a copper wire to the positive side of the transistor which supplies +5v to the positive side of all the LEDS to modulate them for the auto dimming function. This is an Analogue reference that I don’t currently have connected , so far we are at proof of concept.
At position 1 we had an unknown IC, unmarked or demarked or otherwise unknown.
At positions 2 → 7 we have 6 x TC5020D 16 bit Constant Current IC Drivers. Which are basically shift registers. These are daisy chained from their input to output in the order shown.
So I managed to grind away some of the conformal coating on the rear and get access to the DI (Data input to chip 2) CLK (clock) and (LE) Latch Enable. feed the base of the LED Digit Darlington Array and grab some power and ground from some unused capacitor through holes, and start tapping away to send data to it.
As said this is currently first stage development, I’m not sure if it was pin assignment, failing to use a level shifter, burning though the data line with the 5v rail while soldering, or a definite combination of all 3. But I used my stock of Lolin ESP-32 S2 Development boards which has more available GPIO pins to use all the functions that are supplied with the clock IE,
A piezo buzzer, Analogue Light sensor (that will possibly need changing to 1v resolution), AMT30 Temp and humidity sensor (that will either need its data lines logic shifting, or which I’m planning to do, is cut its 5v line and feed it from 3.3V to save space.
Here is the ESPHOME configuration that updates the clock at second 0 of each minute.
globals:
- id: light_timeout
type: int
restore_value: no
initial_value: '0'
- id: segment_map
type: int[63]
restore_value: no
initial_value: |-
// Segment mapping to map clock segments to register output pins
// ** Do not take time to fill this in until you are sure you have LSB or MSB correct **
// A B C D E F G - Segment to register bits
{
18, 17, 23, 24, 21, 19, 20, // Digit 1 (clock hour 10s)
30, 29, 26, 25, 22, 16, 31, // Digit 2 (clock hour unit)
33, 39, 38, 37, 36, 34, 35, // Digit 3 (clock minute 10s)
46, 45, 44, 43, 41, 40, 42, // Digit 4 (clock minute unit)
75, 76, 78, 79, 56, 57, 77, // Digit 5 (humidity 10s)
92, 93, 95, 72, 73, 74, 94, // Digit 6 (humidity units)
-1, 51, 50, -1, -1, -1, -1, // Digit 7 (temp 100s - only segments B, C for "1")
55, 66, 68, 52, 53, 54, 67, // Digit 8 (temp 10s)
80, 81, 83, 84, 69, 70, 82 // Digit 9 (temp units)
}
- id: digit_segments
type: int[77]
restore_value: no
initial_value: |-
// Font mapping to create the numbers on the display - Using standard segment order A -> G
// 1 = Segment Active, 0 = Segment not active
// A B C D E F G
{
1, 1, 1, 1, 1, 1, 0, // 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
0, 0, 0, 0, 0, 0, 0 // -1 (Blank)
}
- id: day_map
type: int[14]
restore_value: no
initial_value: |-
// Map for day LED indicators Blue Left and White Right
// B W
{
9, 8, // Sunday
0, 1, // Monday
2, 3, // Tuesday
4, 5, // Wednesday
6, 7, // Thursday
12, 13, // Friday
10, 11 // Saturday
}
# Any change to the backlight will trigger a 4 hour clock that resets it back to 50%
light:
- platform: monochromatic
name: "Clock Light"
id: clock_light
output: output_component4
restore_mode: RESTORE_DEFAULT_ON
on_state:
then:
- script.stop: auto_off_timer # Stop any existing count down timers
- script.execute: auto_off_timer # Start new 4 Hour Timer
# Define pins for operation
output:
- platform: esp8266_pwm
id: output_component4
pin: D2 # Clock Back Light
- platform: esp8266_pwm
id: second_timing_led
pin: D1 # Clock Seconds indicator
- platform: gpio
id: sr_data
pin: D7 # Data (DI)
- platform: gpio
id: sr_clock
pin: D6 # Clock (CLK)
- platform: gpio
id: sr_latch
pin: D5 # Latch (LE)
# This flashes the seconds indicators
interval:
- interval: 500ms
then:
- lambda: |-
static bool led_state = false;
led_state = !led_state;
// Depending on state flip the output
if (led_state) {
id(second_timing_led)->turn_on();
} else {
id(second_timing_led)->turn_off();
}
# Sensors for the connection status to home assistant and also the statis of Deekee Alarm
binary_sensor:
- platform: status
name: "Deekee Clock Status"
- platform: homeassistant
id: deekee_alarm
entity_id: input_boolean.deekee_alarm
on_state:
- script.execute: display_digits
# Wifi Strength sensor
sensor:
- platform: wifi_signal
name: "Deekee Clock WiFi"
update_interval: 60s
# Switches to restart the module from the front end and switch between 12-24 Hour formats
switch:
- platform: restart
name: "Deekee Clock Restart"
- platform: template
name: "24-Hour Format"
id: time_format_24h
restore_mode: RESTORE_DEFAULT_ON # Default is 24-hour format
optimistic: true
on_turn_on:
- script.execute: display_digits
on_turn_off:
- script.execute: display_digits
# Time platform to grab RTC data and also update the display every minute at second 00
time:
- platform: homeassistant
id: esphome_time
on_time:
- seconds: 0
minutes: /1 # Runs every minute at second 0
then:
- script.execute: display_digits
# Scrip to reset the backlight after 4 hours and Logic to run clock display
script:
- id: auto_off_timer
then:
- delay: 4h
- light.turn_on:
id: clock_light
brightness: 50%
- id: display_digits
then:
- lambda: |-
// Get current time from ESPHome's RTC
auto time_now = id(esphome_time).now();
// Set Variables to make comparison and maths easier
int hour = time_now.hour; // Current Minute
int minute = time_now.minute; // Current hour
int day = time_now.day_of_week - 1; // 0 = Sunday, 6 = Saturday - C++ Retutns 1 = Sunday, 7 = Saturday
int date = time_now.day_of_month; // Day of the month (1-31)
int month = time_now.month; // Month (1-12)
bool dst_active = time_now.is_dst; // Daylight savint time
// Check if 24-hour format switch is ON
bool is_24h = id(time_format_24h).state;
bool pm = false;
// 12-hour format
if (!is_24h) {
if (hour >= 12) {
pm = true;
if (hour > 12) hour -= 12; // Convert to 12-hour
}
if (hour == 0) hour = 12; // Midnight = 12 AM
}
// Get individual digits for time + Remove leading zero in hour
int digits[4] = { hour / 10, hour % 10, minute / 10, minute % 10 };
if (digits[0] == 0) digits[0] = -1;
// Get individual digits for date (day of the month) + Remove leading zero
int date_digits[2] = { date / 10, date % 10 };
if (date_digits[0] == 0) date_digits[0] = -1;
// Get individual digits for month + Remove leading zero
int month_digits[3] = { month / 10, month % 10, -1};
if (month_digits[0] == 0) month_digits[0] = -1;
// Clear shift register bits
int bits[96] = {0}; // 96 bits for 6 shift registers
// Add 4 TIME digits using segment_map + digit_segemnts to the register (Digits 1 -> 4)
for (int d = 0; d < 4; d++) {
for (int s = 0; s < 7; s++) {
// This fetches the bit position for the current digit + segment
int bit_pos = id(segment_map)[(d * 7) + s];
// Use blank digit pattern if the value is -1
int digit_to_display = (digits[d] == -1) ? 10 : digits[d];
// This writes the bit to the register at the correct positon dependant on current digit and segment map
bits[bit_pos] = id(digit_segments)[(digit_to_display * 7) + s];
}
}
// Add 2 digits DATE/HUMIDITY digits to the register (Digits 5 and 6)
for (int d = 0; d < 2; d++) {
for (int s = 0; s < 7; s++) {
int bit_pos = id(segment_map)[(4 + d) * 7 + s];
int digit_to_display = (date_digits[d] == -1) ? 10 : date_digits[d];
bits[bit_pos] = id(digit_segments)[digit_to_display * 7 + s];
}
}
// Add 2/(3) digits MONTH/TEMP digits to the register (Digits (7), 8 and 9)
for (int d = 0; d < 2; d++) {
for (int s = 0; s < 7; s++) {
int bit_pos = id(segment_map)[(6 + d) * 7 + s];
int digit_to_display = (month_digits[d] == -1) ? 10 : month_digits[d];
bits[bit_pos] = id(digit_segments)[digit_to_display * 7 + s];
}
}
// AM/PM Indicator
bits[14] = pm ? 1 : 0;
// DST Indicator
bits[58] = dst_active ? 1 : 0;
// Set Alarm 1 based on room alarm (treated as a global as its out of scope)
bits[59] = id(deekee_alarm).state ? 1 : 0;
// Turn On all Weekday WHITE LEDs
for (int i = 0; i < 7; i++) {
bits[id(day_map)[i * 2]] = 0; // Blue LED Off
bits[id(day_map)[i * 2 + 1]] = 1; // White LED On
}
// Override current day: Turn On Blue, Turn Off White
bits[id(day_map)[day * 2]] = 1; // Blue LED On
bits[id(day_map)[day * 2 + 1]] = 0; // White LED Off
// Designed to be sent to 6 x 16bit TC5020D Shift Registers
// Send data to shift registers in reverse order
for (int i = 95; i >= 0; i--) {
id(sr_data)->set_state(bits[i]); // Set the DATA line HIGH
id(sr_clock)->turn_on(); // Set the CLK line HIGH to clock the data into the register
delayMicroseconds(5); // Delay to ensure we're not sending data too fast to the register
id(sr_clock)->turn_off(); // Set CLK Low to complete DATA transfer
}
// Latch the data to the shift registers
id(sr_latch)->turn_on(); // Set LE High To latch the data through all shift registers
delayMicroseconds(5); // Delay to not overload the registers
id(sr_latch)->turn_off(); // Set LE Low to complete transfer
I will Update/Edit/Add to this post, when I have a delivery of new parts to show final functionality. And at the very least It can hopefully help someone who wants to design their own Network Time Clock, or modify their existing clock.
Many Thanks
Dave