LED - Make ClockClock
Do you know ClockClock24?
ClockClock24 is kinetic art by Humans since 1982. Twenty-four analog clocks form a large digital clock. Clever arrangement of the clock hands transforms the analog clocks into seven-segment displays.
This fantastic clock can also be purchased directly online Moma Design Store. But the price is not easy. The price is surprisingly 5,400 $
I explained how to make clock clock using opencv in this post. It is recommended to take a look at this post in advance. It is not difficult to output to RGB LED matrices as long as it is possible to output it using OpenCV.
Run the code.
You should see a clockclock24 animation that tells you the current time on the LED Matrices.
If you output the result on the screen using high resolution and compare it with the picture, the screen is not clean due to the low resolution.
<from my blog https://opencvcooking.blogspot.com/2020/03/creating-clockclock24-using-opencv.html>
You can download the source codes here(https://github.com/raspberry-pi-maker/IoT)
ClockClock24 is kinetic art by Humans since 1982. Twenty-four analog clocks form a large digital clock. Clever arrangement of the clock hands transforms the analog clocks into seven-segment displays.
<captured from https://www.humanssince1982.com/clockclock24>
Here's an introduction video.
This fantastic clock can also be purchased directly online Moma Design Store. But the price is not easy. The price is surprisingly 5,400 $
<Moma online store>
Make your own ClockClock
Now let's build our own clockclock.Materials
Here's what you need for this post:- Raspberry Pi 4B(2GB Memory) : 1EA
- Electrodragon RGB Matrix HAT : 1EA
- P3 64X64 RGB LED Matrix : 3EA
- Hub75 cable : 3EA
- LED Power cable : 2EA
- 5V 20A Power supply : 1EA
Jumper settings for 1/32 scan mode
64X64 RGB LED matrix has 1/32 scan modes. So you must use E signal pin. Henner Zeller explained about this topic here(https://github.com/hzeller/rpi-rgb-led-matrix/tree/master/adapter/active-3). If you use Electrodragon HAT, you can operate the E pin with one of the following pictures. It depends on the type of 64X64 matrix you use, so you can test the matrix yourself and configure the jumper for your matrix.
<Jumper settings of Electrodragon HAT for 1/32 scan mode>
Implementing ClockClock
I explained how to make clock clock using opencv in this post. It is recommended to take a look at this post in advance. It is not difficult to output to RGB LED matrices as long as it is possible to output it using OpenCV.
''' ''' import argparse import numpy as np import cv2 import time, sys import math from datetime import datetime from PIL import Image, ImageDraw import random from PIL import Image from PIL import ImageDraw from rgbmatrix import RGBMatrix, RGBMatrixOptions FPS = 30.0 SLEEP = 1.0 / FPS tmargin = 1 lmargin = 13 rmargin = 12 margin = 1 bmargin = 1 r = 10 arm1_length = int(r * 1.0) arm2_length = int(r * 0.8) circle_thickness = 1 arm_thickness = 1 circle_color = (128,128,128) arm_color = (96,128,255) arm_color2 = (96,255,96) Canvas_W = 192 #lmargin + rmargin + r * 2 * 8 + margin * 7 Canvas_H = 64 #tmargin + bmargin + r * 2 * 3 + margin * 2 step_angle = 1.8 options = RGBMatrixOptions() options.cols = 64 options.rows = 64 options.chain_length =3 options.parallel = 1 options.brightness = 80 options.pwm_bits = 11 options.gpio_slowdown = 4.0 options.show_refresh_rate = 1 options.hardware_mapping = 'regular' # If you have an Adafruit HAT: 'adafruit-hat' #options.hardware_mapping = 'adafruit-hat' # If you have an Adafruit HAT: 'adafruit-hat' options.pwm_dither_bits = 0 matrix = RGBMatrix(options = options) double_buffer = matrix.CreateFrameCanvas() digit_basic = [[(45, -45),(45, -45),(45, -45)], [(45, -45), ( 45, -45),( 45, -45)] ] digit_1 = [ [(225, 225),(225, 225),(225, 225)], [(180, 180),( 0, 180),( 0, 0)] ] digit_2 = [ [( 90, 90),( 90, 180),( 0, 90)], [(180, 270),( 0, 270),(270, 270)] ] digit_3 = [ [( 90, 90),( 90, 90),( 90, 90)], [(180, 270),( 0, 270),( 0, 270)] ] digit_4 = [ [(180, 180),( 0, 90),(225, 225)], [(180, 180),( 0, 180),( 0, 0)] ] digit_5 = [ [( 90, 180),( 0, 90),( 90, 90)], [(270, 270),(180, 270),( 0, 270)] ] digit_6 = [ [( 90, 180),( 0, 180),( 0, 90)], [(270, 270),(180, 270),( 0, 270)] ] digit_7 = [ [( 90, 90),(225, 225),(225, 225)], [(180, 270),( 0, 180),( 0, 0)] ] digit_8 = [ [( 90, 180),( 0, 90),( 0, 90)], [(180, 270),( 0, 270),( 0, 270)] ] digit_9 = [ [( 90, 180),( 0, 90),( 90, 90)], [(180, 270),( 0, 180),( 0, 270)] ] digit_0 = [ [( 90, 180),( 0, 180),( 0, 90)], [(180, 270),( 0, 180),( 0, 270)] ] clock_digits = [] clock_digits.append(digit_0) clock_digits.append(digit_1) clock_digits.append(digit_2) clock_digits.append(digit_3) clock_digits.append(digit_4) clock_digits.append(digit_5) clock_digits.append(digit_6) clock_digits.append(digit_7) clock_digits.append(digit_8) clock_digits.append(digit_9) class Clock(): def __init__(self, x, y, circle_color, circle_thick, arm_color, arm_thick, lmar): self.x = x self.y = y self.circle_color = circle_color self.circle_thickness = circle_thick self.arm_color = arm_color self.arm_thickness = arm_thick self.center = (lmar + r + (margin + 2*r) * x, tmargin + r + (margin + 2*r) * y) self.angle1 = 0.0 self.angle2 =0.0 self.step1 = step_angle self.step2 = step_angle self.arm1_dir = 1 #1:clockwise, 0:counter-clockwise self.arm2_dir = 1 self.finish_angle1 = False self.finish_angle2 = False def draw_circle(self, img): cv2.circle(img, self.center, r, self.circle_color, self.circle_thickness) def draw_arms(self, img): # print('1 cur[%f] target[%f] 2 cur[%f] target[%f]'%(self.angle1, self.target_angle1, self.angle2, self.target_angle2)) angle1 = np.radians(self.angle1) angle2 = np.radians(self.angle2) center = (lmargin + r + (margin + 2*r) * self.x, tmargin + r + (margin + 2*r) * self.y) pt = (self.center[0], self.center[1] - arm1_length) # 12 Hour direction qx = int(self.center[0] + math.cos(angle1) * (pt[0] - self.center[0]) - math.sin(angle1) * (pt[1] - self.center[1])) qy = int(self.center[1] + math.sin(angle1) * (pt[0] - self.center[0]) + math.cos(angle1) * (pt[1] - self.center[1])) cv2.line(img, self.center, (qx,qy), self.arm_color, self.arm_thickness) pt = (self.center[0], self.center[1] - arm2_length) # 12 Hour direction qx = int(self.center[0] + math.cos(angle1) * (pt[0] - self.center[0]) - math.sin(angle2) * (pt[1] - self.center[1])) qy = int(self.center[1] + math.sin(angle1) * (pt[0] - self.center[0]) + math.cos(angle2) * (pt[1] - self.center[1])) cv2.line(img, self.center, (qx,qy), self.arm_color, self.arm_thickness) def init_clock(self, img, step_cnt1, step_cnt2): self.set_angle(step_cnt1, step_cnt2) self.draw_circle(img) def set_angle(self, step_cnt1, step_cnt2): self.angle1 = step_cnt1 * self.step1 self.angle2 = step_cnt2 * self.step2 def get_angle(self): return self.angle1, self.angle2 def step_move(self, check, step1_move, step2_move): if check == True: if abs(self.angle1 - self.target_angle1) <= 0.001: self.finish_angle1 = True else: if step1_move == True: if self.arm1_dir == 1: self.angle1 += self.step1 if self.angle1 >= 360.0: self.angle1 -= 360.0 else: self.angle1 -= self.step1 if self.angle1 < 0.0: self.angle1 += 360.0 if abs(self.angle2 - self.target_angle2) <= 0.001: self.finish_angle2 = True else: if step2_move == True: if self.arm2_dir == 1: self.angle2 += self.step2 if self.angle2 >= 360.0: self.angle2 -= 360.0 else: self.angle2 -= self.step2 if self.angle2 < 0.0: self.angle2 += 360.0 else: if step1_move == True: if self.arm1_dir == 1: self.angle1 += self.step1 if self.angle1 >= 360.0: self.angle1 -= 360.0 else: self.angle1 -= self.step1 if self.angle1 < 0.0: self.angle1 += 360.0 if step2_move == True: if self.arm2_dir == 1: self.angle2 += self.step2 if self.angle2 >= 360.0: self.angle2 -= 360.0 else: self.angle2 -= self.step2 if self.angle2 < 0.0: self.angle2 += 360.0 return self.finish_angle1, self.finish_angle2 def set_target_angle(self, step_cnt1, step_cnt2, arm1_dir = 1, arm2_dir = 1): step_cnt1 %= 200 step_cnt2 %= 200 self.arm1_dir = 1 #1:clockwise, 0:counter-clockwise self.arm2_dir = 1 self.target_angle1 = step_cnt1 * self.step1 self.target_angle2 = step_cnt2 * self.step2 self.finish_angle1 = False self.finish_angle2 = False if arm1_dir == 1: angle = self.target_angle1 - self.angle1 else: angle = self.angle1 - self.target_angle1 if angle < 0.0: angle += 360.0 step1 = int(angle / self.step1) if arm2_dir == 1: angle = self.target_angle2 - self.angle2 else: angle = self.angle2 - self.target_angle2 if angle < 0.0: angle += 360.0 step2 = int(angle / self.step2) self.total_frames = max(step1, step2) self.current_frames = 0 class Clocks(): def __init__(self, shift): self.myclocks = [[[0] for i in range(3)] for i in range(2)] self.shift = shift def initialize_clocks(self, img): for x in range(2): for y in range(3): clock = Clock(self.shift + x, y, circle_color, circle_thickness, arm_color, arm_thickness, lmargin) clock.arm1_dir = 1 clock.arm2_dir = 1 clock.init_clock(img, 25, 175) self.myclocks[x][y] = clock def set_digit(self, n): for x in range(2): for y in range(3): self.myclocks[x][y].set_target_angle(int(clock_digits[n][x][y][0] / step_angle), int(clock_digits[n][x][y][1] / step_angle)) def step_move(self, img): rets = [] for x in range(2): for y in range(3): ret = self.myclocks[x][y].step_move(True, True, True) self.myclocks[x][y].draw_arms(img) rets.append(ret[0]) rets.append(ret[1]) if(all(rets) == True): return True , img else: return False , img ''' reset the clock arm target angle ''' def reset_design_clock(self): for x in range(2): for y in range(3): c = self.myclocks[x][y] c.set_target_angle(25, 175) def draw(self, img): for x in range(2): for y in range(3): c = self.myclocks[x][y] c.draw_arms(img) return img def make_canvas(h, w, color): canvas = np.zeros([h,w,3], dtype=np.uint8) canvas.fill(color) return canvas def initialize_clocks(img): H0_clocks.initialize_clocks(img) H1_clocks.initialize_clocks(img) M0_clocks.initialize_clocks(img) M1_clocks.initialize_clocks(img) def restore_clocks(img): global double_buffer H0_clocks.reset_design_clock() H1_clocks.reset_design_clock() M0_clocks.reset_design_clock() M1_clocks.reset_design_clock() while True: rets = [] s = time.time() tcanvas = img.copy() ret, tcanvas = H0_clocks.step_move(tcanvas) rets.append(ret) ret, tcanvas = H1_clocks.step_move(tcanvas) rets.append(ret) ret, tcanvas = M0_clocks.step_move(tcanvas) rets.append(ret) ret, tcanvas = M1_clocks.step_move(tcanvas) rets.append(ret) im = cv2.cvtColor(tcanvas, cv2.COLOR_BGR2RGB) im_pil = Image.fromarray(im) double_buffer.SetImage(im_pil) double_buffer.SetImage(im_pil, Canvas_W) double_buffer = matrix.SwapOnVSync(double_buffer) # cv2.imshow("clock", tcanvas) # cv2.waitKey(1) sleep_tm = SLEEP - (time.time() - s ) time.sleep(max(0, sleep_tm)) if(all(rets) == True): break H0_clocks = Clocks(0) H1_clocks = Clocks(2) M0_clocks = Clocks(4) M1_clocks = Clocks(6) canvas = make_canvas(Canvas_H, Canvas_W, 0) initialize_clocks(canvas) img = canvas.copy() img = H0_clocks.draw(img) img = H1_clocks.draw(img) img = M0_clocks.draw(img) img = M1_clocks.draw(img) im = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) im_pil = Image.fromarray(im) double_buffer.SetImage(im_pil) double_buffer.SetImage(im_pil, Canvas_W) double_buffer = matrix.SwapOnVSync(double_buffer) # cv2.imshow("clock", img) # cv2.waitKey(100) while True: now = datetime.now() H0_clocks.set_digit(int(now.hour / 10)) H1_clocks.set_digit(now.hour % 10) M0_clocks.set_digit(int(now.minute / 10)) M1_clocks.set_digit(now.minute % 10) while True: s = time.time() tcanvas = canvas.copy() rets = [] try: ret, tcanvas = H0_clocks.step_move(tcanvas) rets.append(ret) ret, tcanvas = H1_clocks.step_move(tcanvas) rets.append(ret) ret, tcanvas = M0_clocks.step_move(tcanvas) rets.append(ret) ret, tcanvas = M1_clocks.step_move(tcanvas) rets.append(ret) im = cv2.cvtColor(tcanvas, cv2.COLOR_BGR2RGB) im_pil = Image.fromarray(im) double_buffer.SetImage(im_pil) double_buffer.SetImage(im_pil, Canvas_W) double_buffer = matrix.SwapOnVSync(double_buffer) # cv2.imshow("clock", tcanvas) # cv2.waitKey(1) sleep_tm = SLEEP - (time.time() - s ) time.sleep(max(0, sleep_tm)) if(all(rets) == True): break except KeyboardInterrupt: break tcanvas = canvas.copy() time.sleep(3) restore_clocks(tcanvas) print('restore end') # cv2.waitKey(0) # cv2.destroyAllWindows()
Run the code.
root@DietPi:/usr/local/src/rpi-rgb-led-matrix/bindings/python/samples# python3 clock_clock.py
You should see a clockclock24 animation that tells you the current time on the LED Matrices.
If you output the result on the screen using high resolution and compare it with the picture, the screen is not clean due to the low resolution.
<from my blog https://opencvcooking.blogspot.com/2020/03/creating-clockclock24-using-opencv.html>
Wrapping up
To implement the ClockClock properly, you must use a higher resolution. Therefore, it is better to use a high-resolution LCD or OLED panel that supports Full HD or higher rather than the LED matrix. If you use the RGB LED Matrix, you should use at least 64X3 (Height) and 64X8 (Width) to make a clean screen possible.You can download the source codes here(https://github.com/raspberry-pi-maker/IoT)
댓글
댓글 쓰기