merged two projects
This commit is contained in:
		
							
								
								
									
										1
									
								
								dashboard_api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								dashboard_api/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| # Placeholder | ||||
							
								
								
									
										65
									
								
								dashboard_api/converter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								dashboard_api/converter.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| from PIL import Image, ImageDraw, ImageFont | ||||
| import numpy as np | ||||
| import datetime | ||||
| """Two colors: 1 main color and 1 accent color. These are labeled in the matrix as 1 and 2""" | ||||
|  | ||||
| def text_converter(text, height): | ||||
|     """Converts a text to a pixel-matrix | ||||
|     returns: np.array((16, x))""" | ||||
|  | ||||
|     font = ImageFont.truetype("verdanab.ttf", 16) | ||||
|     size = font.getsize(text) | ||||
|     img = Image.new("1",size,"black") | ||||
|     draw = ImageDraw.Draw(img) | ||||
|     draw.text((0, 0), text, "white", font=font) | ||||
|     pixels = np.array(img, dtype=np.uint8) | ||||
|     return pixels | ||||
|  | ||||
|  | ||||
| digits = { | ||||
|     1 : [[0,1,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1]], | ||||
|     2 : [[1,1,1],[0,0,1],[1,1,1],[1,0,0],[1,1,1]], | ||||
|     3 : [[1,1,1],[0,0,1],[1,1,1],[0,0,1],[1,1,1]], | ||||
|     4 : [[1,0,1],[1,0,1],[1,1,1],[0,0,1],[0,0,1]], | ||||
|     5 : [[1,1,1],[1,0,0],[1,1,1],[0,0,1],[1,1,1]], | ||||
|     6 : [[1,1,1],[1,0,0],[1,1,1],[1,0,1],[1,1,1]], | ||||
|     7 : [[1,1,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1]], | ||||
|     8 : [[1,1,1],[1,0,1],[1,1,1],[1,0,1],[1,1,1]], | ||||
|     9 : [[1,1,1],[1,0,1],[1,1,1],[0,0,1],[1,1,1]], | ||||
|     0 : [[1,1,1],[1,0,1],[1,0,1],[1,0,1],[1,1,1]] | ||||
| } | ||||
|  | ||||
| ##place of numbers, invariable | ||||
| digit_position = [[3,1], [3,9], [9,1], [9,9]] | ||||
|  | ||||
| def time_converter(): | ||||
|     """Converts 4-digit time to a pixel-matrix | ||||
|     returns: np.array((16, 16))""" | ||||
|     time = datetime.datetime.now().strftime("%H%M") | ||||
|     pixels = np.zeros((16,16),dtype=np.uint8) | ||||
|     time = "0" * (4 - len(str(time))) + str(time) | ||||
|     time_split = [int(i) for i in time] | ||||
|  | ||||
|     for i in range(4): | ||||
|         x = digit_position[i][0] | ||||
|         y = digit_position[i][1] | ||||
|         number = digits[time_split[i]] | ||||
|         pixels[x: x + 5, y: y + 3] = np.array(number) | ||||
|  | ||||
|      | ||||
|     return pixels | ||||
|  | ||||
|  | ||||
| days = np.append(np.zeros((15,16)), np.array([0,1,0,1,0,1,0,1,0,1,0,1,1,0,1,1])).reshape((16,16)) | ||||
|  | ||||
| def date_converter(): | ||||
|     today = datetime.datetime.today() | ||||
|     weekday = datetime.datetime.weekday(today) | ||||
|     # size of the reduced array according to weekday | ||||
|     size = [2,4,6,8,10,13,16] | ||||
|  | ||||
|     pixels = days #base color background | ||||
|     lrow = np.append(pixels[15,:size[weekday]], [0 for i in range(16 - size[weekday])]) | ||||
|     lrow = np.append(np.zeros((15,16)), lrow).reshape((16,16)) | ||||
|     pixels += lrow | ||||
|     return pixels | ||||
							
								
								
									
										1
									
								
								dashboard_api/hat/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								dashboard_api/hat/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| # Placeholder | ||||
							
								
								
									
										115
									
								
								dashboard_api/hat/sim.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								dashboard_api/hat/sim.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | ||||
| import sys | ||||
| import colorsys | ||||
| import pygame.gfxdraw | ||||
| import time | ||||
| import pygame | ||||
|  | ||||
| class UnicornHat(object): | ||||
|     def __init__(self, width, height, rotation_offset = 0): | ||||
|         # Compat with old library | ||||
|  | ||||
|         # Set some defaults | ||||
|         self.rotation_offset = rotation_offset | ||||
|         self.rotation(0) | ||||
|         self.pixels = [(0, 0, 0)] * width * height | ||||
|         self.pixel_size = 25 | ||||
|         self.height = height | ||||
|         self.width = width | ||||
|         self.window_width = width * self.pixel_size | ||||
|         self.window_height = height * self.pixel_size | ||||
|  | ||||
|         # Init pygame and off we go | ||||
|         pygame.init() | ||||
|         pygame.display.set_caption("Unicorn HAT simulator") | ||||
|         self.screen = pygame.display.set_mode([self.window_width, self.window_height]) | ||||
|         self.clear() | ||||
|  | ||||
|     def set_pixel(self, x, y, r, g, b): | ||||
|         i = (x * self.width) + y | ||||
|         self.pixels[i] = [int(r), int(g), int(b)] | ||||
|  | ||||
|     def draw(self): | ||||
|         for event in pygame.event.get(): # User did something | ||||
|             if event.type == pygame.QUIT: | ||||
|                 print("Exiting...") | ||||
|                 sys.exit() | ||||
|  | ||||
|         for x in range(self.width): | ||||
|             for y in range(self.height): | ||||
|                 self.draw_led(x, y) | ||||
|  | ||||
|     def show(self): | ||||
|         self.clear() | ||||
|         self.draw() | ||||
|         pygame.display.flip() | ||||
|         #time.sleep(5) | ||||
|  | ||||
|     def draw_led(self, x, y): | ||||
|         p = self.pixel_size | ||||
|         w_x = int(x * p + p / 2) | ||||
|         w_y = int((self.height - 1 - y) * p + p / 2) | ||||
|         r = int(p / 4) | ||||
|         color = self.pixels[self.index(x, y)] | ||||
|         pygame.gfxdraw.aacircle(self.screen, w_x, w_y, r, color) | ||||
|         pygame.gfxdraw.filled_circle(self.screen, w_x, w_y, r, color) | ||||
|  | ||||
|     def get_shape(self): | ||||
|         return (self.width, self.height) | ||||
|  | ||||
|     def brightness(self, *args): | ||||
|         pass | ||||
|  | ||||
|     def rotation(self, r): | ||||
|         self._rotation = int(round(r/90.0)) % 3 | ||||
|  | ||||
|     def clear(self): | ||||
|         self.screen.fill((0, 0, 0)) | ||||
|  | ||||
|     def get_rotation(self): | ||||
|         return self._rotation * 90 | ||||
|  | ||||
|     def set_layout(self, *args): | ||||
|         pass | ||||
|  | ||||
|     def set_pixel_hsv(self, x, y, h, s=1.0, v=1.0): | ||||
|         r, g, b = [int(n*255) for n in colorsys.hsv_to_rgb(h, s, v)] | ||||
|         self.set_pixel(x, y, r, g, b) | ||||
|  | ||||
|     def off(self): | ||||
|         print("Closing window") | ||||
|         #pygame.quit() | ||||
|  | ||||
|     def index(self, x, y): | ||||
|         # Offset to match device rotation | ||||
|         rot = (self.get_rotation() + self.rotation_offset) % 360 | ||||
|  | ||||
|         if rot == 0: | ||||
|             xx = x | ||||
|             yy = y | ||||
|         elif rot == 90: | ||||
|             xx = self.height - 1 - y | ||||
|             yy = x | ||||
|         elif rot == 180: | ||||
|             xx = self.width - 1 - x | ||||
|             yy = self.height - 1 - y | ||||
|         elif rot == 270: | ||||
|             xx = y | ||||
|             yy = self.width - 1 - x | ||||
|         return (xx * self.width) + yy | ||||
|  | ||||
|  | ||||
| """ | ||||
| # SD hats works as expected | ||||
| #unicornhat = UnicornHatSim(8,8) | ||||
| #unicornphat = UnicornHatSim(8, 4) | ||||
|  | ||||
| # Unicornhat HD seems to be the other way around (not that there's anything wrong with that), so we rotate it 180° | ||||
| # unicornhathd = UnicornHatSim(16, 16, 180) | ||||
| twohats = UnicornHatSim(16, 32, 180) | ||||
|  | ||||
|  | ||||
| for i in range(16): | ||||
|     twohats.set_pixel(i,i, 200,200,200) | ||||
|     twohats.show() | ||||
|     time.sleep(1) | ||||
| """ | ||||
							
								
								
									
										140
									
								
								dashboard_api/hat/unicorn.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								dashboard_api/hat/unicorn.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | ||||
| import colorsys | ||||
| import time | ||||
| import numpy | ||||
|  | ||||
| import RPi.GPIO as GPIO | ||||
|  | ||||
|  | ||||
| class UnicornHat(object): | ||||
|     def __init__(self, width, height, rotation_offset = 0): | ||||
|         self.PIN_CLK = 11 | ||||
|         ################################## | ||||
|         self.PINS_DAT = [10, 22] | ||||
|         ################################## | ||||
|         self.PIN_CS = 8 | ||||
|  | ||||
|         GPIO.setmode(GPIO.BCM) | ||||
|         GPIO.setwarnings(False) | ||||
|         GPIO.setup(self.PIN_CS, GPIO.OUT, initial=GPIO.HIGH) | ||||
|         GPIO.setup(self.PIN_CLK, GPIO.OUT, initial=GPIO.LOW) | ||||
|         GPIO.setup(self.PINS_DAT, GPIO.OUT, initial=GPIO.LOW) | ||||
|  | ||||
|         self.SOF = 0x72 | ||||
|         self.DELAY = 1.0/120 | ||||
|         self.WIDTH = width #32 | ||||
|         self.HEIGHT = height #16 | ||||
|  | ||||
|         self.rotation = 1 | ||||
|         self.brightness = 0.5 | ||||
|         self.buffer = numpy.zeros((self.WIDTH,self.HEIGHT,3), dtype=int) | ||||
|  | ||||
|  | ||||
|  | ||||
|     def spi_write(self, buf1, buf2): | ||||
|         GPIO.output(self.PIN_CS, GPIO.LOW) | ||||
|  | ||||
|         self.spi_write_byte(self.SOF, self.SOF) | ||||
|  | ||||
|         for x in range(len(buf1)): | ||||
|             b1, b2= buf1[x], buf2[x] | ||||
|             self.spi_write_byte(b1, b2) | ||||
|             time.sleep(0.0000001) | ||||
|  | ||||
|         GPIO.output(self.PIN_CS, GPIO.HIGH) | ||||
|  | ||||
|     def spi_write_byte(self, b1, b2): | ||||
|         for x in range(8): | ||||
|             GPIO.output(self.PINS_DAT[0], b1 & 0b10000000) | ||||
|             GPIO.output(self.PINS_DAT[1], b2 & 0b10000000) | ||||
|             GPIO.output(self.PIN_CLK, GPIO.HIGH) | ||||
|             b1 <<= 1 | ||||
|             b2 <<= 1 | ||||
|             time.sleep(0.00000001) | ||||
|             GPIO.output(self.PIN_CLK, GPIO.LOW) | ||||
|  | ||||
|     def brightness(self, b): | ||||
|         """Set the display brightness between 0.0 and 1.0. | ||||
|         :param b: Brightness from 0.0 to 1.0 (default 0.5) | ||||
|         """ | ||||
|         self.brightness = b | ||||
|  | ||||
|     def rotation(self, r): | ||||
|         """Set the display rotation in degrees. | ||||
|         Actual rotation will be snapped to the nearest 90 degrees. | ||||
|         """ | ||||
|         self.rotation = int(round(r/90.0)) | ||||
|  | ||||
|     def get_rotation(self): | ||||
|         """Returns the display rotation in degrees.""" | ||||
|         return self.rotation * 90 | ||||
|  | ||||
|     def set_layout(self, pixel_map=None): | ||||
|         """Does nothing, for library compatibility with Unicorn HAT.""" | ||||
|         pass | ||||
|  | ||||
|     def set_all(self, r, g, b): | ||||
|         self.buffer[:] = r, g, b | ||||
|  | ||||
|     def set_pixel(self, x, y, r, g, b): | ||||
|         """Set a single pixel to RGB colour. | ||||
|         :param x: Horizontal position from 0 to 15 | ||||
|         :param y: Veritcal position from 0 to 15 | ||||
|         :param r: Amount of red from 0 to 255 | ||||
|         :param g: Amount of green from 0 to 255 | ||||
|         :param b: Amount of blue from 0 to 255 | ||||
|         """ | ||||
|         self.buffer[x][y] = r, g, b | ||||
|  | ||||
|     def set_pixel_hsv(self, x, y, h, s=1.0, v=1.0): | ||||
|         """set a single pixel to a colour using HSV. | ||||
|          :param x: Horizontal position from 0 to 15 | ||||
|          :param y: Veritcal position from 0 to 15 | ||||
|          :param h: Hue from 0.0 to 1.0 ( IE: degrees around hue wheel/360.0 ) | ||||
|          :param s: Saturation from 0.0 to 1.0 | ||||
|          :param v: Value (also known as brightness) from 0.0 to 1.0 | ||||
|         """ | ||||
|  | ||||
|         r, g, b = [int(n*255) for n in colorsys.hsv_to_rgb(h, s, v)] | ||||
|         self.set_pixel(x, y, r, g, b) | ||||
|  | ||||
|     def get_pixel(self, x, y): | ||||
|         return tuple(self.buffer[x][y]) | ||||
|  | ||||
|     def shade_pixels(self, shader): | ||||
|         for x in range(self.WIDTH): | ||||
|             for y in range(self.HEIGHT): | ||||
|                 r, g, b = shader(x, y) | ||||
|                 self.set_pixel(x, y, r, g, b) | ||||
|  | ||||
|     def get_pixels(self): | ||||
|         return self.buffer | ||||
|  | ||||
|     def get_shape(self): | ||||
|         """Return the shape (width, height) of the display.""" | ||||
|  | ||||
|         return self.WIDTH, self.HEIGHT | ||||
|  | ||||
|     def clear(self): | ||||
|         """Clear the buffer.""" | ||||
|         self.buffer.fill(0) | ||||
|  | ||||
|     def off(self): | ||||
|         """Clear the buffer and immediately update Unicorn HAT HD. | ||||
|         Turns off all pixels. | ||||
|         """ | ||||
|         self.clear() | ||||
|         self.show() | ||||
|  | ||||
|     def show(self): | ||||
|         """Output the contents of the buffer to Unicorn HAT HD.""" | ||||
|         ########################################################## | ||||
|         ## Change to desire | ||||
|         _buf1 = numpy.rot90(self.buffer[:16,:self.HEIGHT],2) | ||||
|         _buf2 = numpy.rot90(self.buffer[16:32,:self.HEIGHT],2) | ||||
|         ########################################################## | ||||
|  | ||||
|         _buf1, _buf2 = _buf1, _buf2 = [(x.reshape(768) * self.brightness).astype(numpy.uint8).tolist() for x in (_buf1, _buf2)] | ||||
|  | ||||
|         self.spi_write(_buf1, _buf2) | ||||
|  | ||||
|         time.sleep(self.DELAY) | ||||
							
								
								
									
										78
									
								
								dashboard_api/led_out.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								dashboard_api/led_out.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| import time | ||||
| import numpy as np | ||||
| from threading import Thread | ||||
|  | ||||
| import converter | ||||
| try: | ||||
|     from hat import unicorn as HAT | ||||
| except ImportError: | ||||
|     print("Using the simulator") | ||||
|     from hat import sim as HAT | ||||
|  | ||||
|  | ||||
|  | ||||
| class OutputHandler(): | ||||
|     def __init__(self, width, height): | ||||
|         """width is presumed to be larger than height""" | ||||
|         self.width = width | ||||
|         self.height = height | ||||
|         self.output = HAT.UnicornHat(width, height) | ||||
|         self.threads = [] | ||||
|         self.running = False | ||||
|         self.primary = [200, 200, 200] | ||||
|         self.secondary = [10, 200, 10] | ||||
|         self.red = [200, 10, 10] | ||||
|  | ||||
|  | ||||
|  | ||||
|     def stop(self): | ||||
|         for t in threads: | ||||
|             t.stop() | ||||
|         self.output.off() | ||||
|  | ||||
|  | ||||
|     def run(self, func, args): | ||||
|         self.running = True | ||||
|         t = Thread(target=func,args=tuple(args)) | ||||
|         t.start() | ||||
|         self.threads.append(t) | ||||
|  | ||||
|  | ||||
|     def set_matrix(self, matrix): | ||||
|         """assumes 1 for primary, 2 for secondary color""" | ||||
|  | ||||
|         for x in range(matrix.shape[0]): | ||||
|             for y in range(matrix.shape[1]): | ||||
|                 if matrix[x,y] == 1: | ||||
|                     self.output.set_pixel(x,y,self.primary[0],self.primary[1],self.primary[2]) | ||||
|                 elif matrix[x,y] == 2: | ||||
|                     self.output.set_pixel(x,y,self.secondary[0],self.secondary[1],self.secondary[2]) | ||||
|         self.output.show() | ||||
|  | ||||
|     def clock_face(self): | ||||
|         hour = converter.time_converter() | ||||
|         day = converter.date_converter() | ||||
|  | ||||
|         self.set_matrix(hour + day) | ||||
|         self.running = False | ||||
|  | ||||
|  | ||||
|     def text_scroll(self, text, speed): | ||||
|         pixels = converter.text_converter(text,16) | ||||
|         sleep_time = 1 / speed | ||||
|         frames = pixels.shape[1] - 16 | ||||
|         if frames <= 0: | ||||
|             frames = 1 | ||||
|         for i in range(frames): | ||||
|             #self.output.clear() | ||||
|             #self.set_matrix(np.zeros((16,16))) | ||||
|             self.set_matrix(pixels[:,i:16+i]) | ||||
|             time.sleep(sleep_time) | ||||
|  | ||||
|  | ||||
|         self.clock_face() | ||||
|  | ||||
|  | ||||
| test = OutputHandler(16,32) | ||||
| test.clock_face() | ||||
| test.text_scroll("Hello world. How are you?",4) | ||||
		Reference in New Issue
	
	Block a user
	 Remy Moll
					Remy Moll