# -*- coding: utf-8 -*- import RPi.GPIO as GPIO import time import threading import numpy as np import datetime from forecastiopy import * # File: Weather_Display.py # Author: N. Brent Burns # Deployed on a Raspberry Pi 3 B+ running Linux raspberrypi 4.19.42-v7+ # Using Python 2.7.13 (should be compatible with Python3 as well) # Started 6/14/2019 # Finished and mounted 6/18/2019 # Code revised to add Rain display and finished 6/20/2019 # In-depth project details under the "Projects" tab at http://crystal.uta.edu/~burns/ # USEFUL LINKS: # https://learn.adafruit.com/connecting-a-16x32-rgb-led-matrix-panel-to-a-raspberry-pi/experimental-python-code # https://www.raspberrypi.org/documentation/remote-access/ssh/scp.md # sudo python weather_v1.py # https://www.bigmessowires.com/2018/05/24/64-x-32-led-matrix-programming/ # https://github.com/Detrous/darksky # https://darksky.net/dev/docs # https://github.com/dvdme/forecastiopy # auto reboot: http://www.vk3erw.com/index.php/16-software/58-raspberry-pi-how-to-periodic-reboot-via-cron ''' TO-DO: DONE: Reduce LED current draw by going to sleep during certain hours DONE: Instead have IR control to turn LEDs ON/OFF using the OE_PIN Rain effect, Sun effect (UV) Maybe add sunrise/sunset vertical lines in 24-hour temperature graph Kill or reduce Linux/Pi stuff in the background, free up resources Might solve Pi Zero W issues Add severe weather alerts, maybe just flashing red LEDs or text message R1 (Red 1st bank) G1 (Green 1st bank) B1 (Blue 1st bank) R2 (Red 2nd bank) G2 (Green 2nd bank) B2 (Blue 2nd bank) A, B, C, D (Row address) OE- (neg. Output enable) CLK (Serial clock) STR (Strobe row data, also called LATCH) COLORS 0-7 (only 8 at the moment) 0 = BLACK (OFF) 1 = RED 2 = GREEN 3 = YELLOW 4 = BLUE 5 = MAGENTA 6 = CYAN 7 = WHITE 8+ = OFF/NOTHING ''' ### YOU NEED TO REPLACE THE apikey AND location VARS BELOW WITH ACTUAL VALID NUMBERS ### # these are the only variables you need to change to make this code work # as long as you followed all the steps at my website http://crystal.uta.edu/~burns/ # Dark Sky API variables apikey = '123456abcdef' # darksky code/key, free membership allows 1000 weather pulls a day location = [123.45, 123.45] # [latitude , longitude] of location to gather weather info about # STATES of Program: # 0 = Normal - Display Temperature Line Graph, 3 Temps, UV %, Precip % (LEDs ON) # 1 = Normal - Rain Graph (LEDs ON) # 2 = Paused - Blank Screen (All LEDs OFF) globalState = 0 # Always start on the temp display globalScreenToSave = 0 # 0 = screen var for temp display, 1 = ... for rain display globalScreenToPrint = 0 # 0 = screen var for temp display, 1 = ... for rain display GPIO.setwarnings(False) # Get rid of GPIO warning messages, I KIND OF KNOW WHAT I'M DOING! #delay = 0.0000001 #delay = 0.000001 # original delay = 0.00001 # best #delay = 0.0001 # slight brightness flicker #delay = 0.001 # constant flicker #delay = 0.01 # bad flicker and redrawing, too slow, too large a delay #delay = 0.1 # use the relative GPIO# below, not the actual 1-40 pinout numbers GPIO.setmode(GPIO.BCM) red1_pin = 11 green1_pin = 27 blue1_pin = 7 red2_pin = 8 green2_pin = 9 blue2_pin = 10 clock_pin = 17 a_pin = 22 b_pin = 23 c_pin = 24 d_pin = 25 latch_pin = 4 oe_pin = 18 IR_pin = 14 # Make all IO pins OUTPUTS (PI --> LED Matrix module) GPIO.setup(red1_pin, GPIO.OUT) GPIO.setup(green1_pin, GPIO.OUT) GPIO.setup(blue1_pin, GPIO.OUT) GPIO.setup(red2_pin, GPIO.OUT) GPIO.setup(green2_pin, GPIO.OUT) GPIO.setup(blue2_pin, GPIO.OUT) GPIO.setup(clock_pin, GPIO.OUT) GPIO.setup(a_pin, GPIO.OUT) GPIO.setup(b_pin, GPIO.OUT) GPIO.setup(c_pin, GPIO.OUT) GPIO.setup(d_pin, GPIO.OUT) GPIO.setup(latch_pin, GPIO.OUT) GPIO.setup(oe_pin, GPIO.OUT) GPIO.setup(IR_pin, GPIO.IN) # Clear all IO pins to ZERO initially GPIO.output(red1_pin, 0) GPIO.output(green1_pin, 0) GPIO.output(blue1_pin, 0) GPIO.output(red2_pin, 0) GPIO.output(green2_pin, 0) GPIO.output(blue2_pin, 0) GPIO.output(clock_pin, 0) GPIO.output(a_pin, 0) GPIO.output(b_pin, 0) GPIO.output(c_pin, 0) GPIO.output(d_pin, 0) GPIO.output(latch_pin, 0) GPIO.output(oe_pin, 0) # Define color numbers BLACK = 0 RED = 1 GREEN = 2 YELLOW = 3 BLUE = 4 MAGENTA = 5 CYAN = 6 WHITE = 7 # numbers 0-9 in order (index == number): 6 wide by 13 tall numbers = [[[1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1]], [[0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0]], [[1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1]], [[1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1]], [[1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1]], [[1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1]], [[1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1]], [[1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1]], [[1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1]], [[1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1]]] # numbers 0-9: 3wide by 5tall numbersSmall = [[[1, 1, 1], [1, 0, 1], [1, 0, 1], [1, 0, 1], [1, 1, 1]], [[1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0]], [[1, 1, 1], [0, 0, 1], [1, 1, 1], [1, 0, 0], [1, 1, 1]], [[1, 1, 1], [0, 0, 1], [1, 1, 1], [0, 0, 1], [1, 1, 1]], [[1, 0, 1], [1, 0, 1], [1, 1, 1], [0, 0, 1], [0, 0, 1]], [[1, 1, 1], [1, 0, 0], [1, 1, 1], [0, 0, 1], [1, 1, 1]], [[1, 1, 1], [1, 0, 0], [1, 1, 1], [1, 0, 1], [1, 1, 1]], [[1, 1, 1], [1, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1]], [[1, 1, 1], [1, 0, 1], [1, 1, 1], [1, 0, 1], [1, 1, 1]], [[1, 1, 1], [1, 0, 1], [1, 1, 1], [0, 0, 1], [0, 0, 1]]] # SCREEN '2D list' (top left corner is 0,0) # 64 wide x cols, 32 tall y rows screen0 = [[0 for x in range(64)] for x in range(32)] # for temp display screen1 = [[0 for x in range(64)] for x in range(32)] # for rain display def clear_screens(): global screen0 global screen1 screen0 = [[0 for x in range(64)] for x in range(32)] screen1 = [[0 for x in range(64)] for x in range(32)] def fill_screens(color): global screen0 global screen1 screen0 = [[color for x in range(64)] for x in range(32)] screen1 = [[color for x in range(64)] for x in range(32)] def clock(): GPIO.output(clock_pin, 1) GPIO.output(clock_pin, 0) def latch(): GPIO.output(latch_pin, 1) GPIO.output(latch_pin, 0) def bits_from_int(x): a_bit = x & 1 b_bit = x & 2 c_bit = x & 4 d_bit = x & 8 return (a_bit, b_bit, c_bit, d_bit) def set_row(row): a_bit, b_bit, c_bit, d_bit = bits_from_int(row) GPIO.output(a_pin, a_bit) GPIO.output(b_pin, b_bit) GPIO.output(c_pin, c_bit) GPIO.output(d_pin, d_bit) def set_color_top(color): red, green, blue, _ = bits_from_int(color) GPIO.output(red1_pin, red) GPIO.output(green1_pin, green) GPIO.output(blue1_pin, blue) def set_color_bottom(color): red, green, blue, _ = bits_from_int(color) GPIO.output(red2_pin, red) GPIO.output(green2_pin, green) GPIO.output(blue2_pin, blue) def draw_rectangle_fill(x1, y1, x2, y2, color): for x in range(x1, x2+1): for y in range(y1, y2+1): set_pixel(x, y, color) def draw_rectangle_outline(x1, y1, x2, y2, color): for x in range(x1, x2+1): set_pixel(x, y1, color) # TOP Border set_pixel(x, y2, color) # BOTTOM Border for y in range(y1, y2+1): set_pixel(x1, y, color) # LEFT Border set_pixel(x2, y, color) # RIGHT Border def set_pixel(x, y, color): global globalScreenToSave if(globalScreenToSave == 0): # Temperature Display (screen0) screen0[y][x] = color elif(globalScreenToSave == 1): # Rain Percentage Display (screen1) screen1[y][x] = color #--------------------------------------------------------------------------# def draw_custom_checkerboard(dimension, color): # This function draws a special LED pattern/sequence # Checkerboard pattern with "dimension" defining the checkerbox height/width # Just a crude function, could be improved # dimension must be non-zero and positive if(dimension <= 0): dimension = 1 x_max = 64 y_max = 32 x = 0 y = 0 while((x+dimension*2)-1 < x_max): y = 0 while((y+dimension*2)-1 < y_max): draw_rectangle_fill(x, y, x+dimension-1, y+dimension-1,color) draw_rectangle_fill(x+dimension, y+dimension, (x+dimension*2)-1, (y+dimension*2)-1,color) y = y + dimension*2 x = x + dimension*2 def draw_rain(percent): if(percent < 0): percent = 0 elif(percent > 100): percent = 100 offset = int(percent*15.0/100) if(offset <= 5): rainColor = GREEN elif(offset <= 10): rainColor = YELLOW else: # 11-15 rainColor = RED draw_rectangle_fill(1, 31-offset, 5, 30, rainColor) # x1 x2 y2 fixed, offset y1 def draw_uv(percent): if(percent < 0): percent = 0 elif(percent > 100): percent = 100 offset = int(percent*15.0/100) # Normal UV Value Percent offset # 0 0 0 GREEN # 1 9 1 GREEN # 2 18 2 GREEN # 3 27 4 YELLOW # 4 36 5 YELLOW # 5 45 6 YELLOW # 6 55 8 YELLOW # 7 64 9 YELLOW # 8 73 10 RED # 9 82 12 RED # 10 91 13 RED # 11+ 100 15 RED if(offset <= 2): uvColor = GREEN elif(offset <= 9): uvColor = YELLOW else: # 10-15 uvColor = RED draw_rectangle_fill(58, 31-offset, 62, 30, uvColor) # x1 x2 y2 fixed, offset y1 def draw_temperature_numbers(myType, degrees): # Can handle MAX 3 digits, don't print leading zeros, center whole number # Make sure to pass in degrees as an INT if(degrees < 0): degrees = 0 elif(degrees > 999): degrees = 666 if(myType == 'low'): color = BLUE mainOffset_x = 1 elif(myType == 'curr'): color = GREEN mainOffset_x = 22 elif(myType == 'high'): color = RED mainOffset_x = 43 mainOffset_y = 1 numDigits = len(str(degrees)) # digit3 digit2 digit1, d3d2d1, EX: degrees 102 d3=1 d2=0 d1=2 digit3 = int(degrees/100) digit2 = int((degrees%100)/10) digit1 = int(degrees%10) if(numDigits == 1): draw_number(mainOffset_x+7, mainOffset_y, digit1, color) elif(numDigits == 2): draw_number(mainOffset_x+3, mainOffset_y, digit2, color) draw_number(mainOffset_x+11, mainOffset_y, digit1, color) elif(numDigits == 3): draw_number(mainOffset_x, mainOffset_y, digit3, color) draw_number(mainOffset_x+7, mainOffset_y, digit2, color) draw_number(mainOffset_x+14, mainOffset_y, digit1, color) def draw_number(x1, y1, num, color): # x1 y1 are the top left corner of single number character of the 6x13 grid/box # font is 6x13, 6 wide x, 13 tall y global numbers for x in range(0, 6): for y in range(0, 13): if(numbers[num][y][x] == 0): set_pixel(x+x1, y+y1, BLACK) # BLACK == LED OFF else: set_pixel(x+x1, y+y1, color) # These two draw_number functions could be combined if you handle different "font" sizes correctly def draw_number_small(x1, y1, num, color): # x1 y1 are the top left corner of single number character of the 3x5 grid/box # font is 3x5, 3 wide x, 5 tall y global numbersSmall for x in range(0, 3): for y in range(0, 5): if(numbersSmall[num][y][x] == 0): set_pixel(x+x1, y+y1, BLACK) # BLACK == LED OFF else: set_pixel(x+x1, y+y1, color) def draw_temperature_line_graph(tempList): # tempList contains 48 degree values for 1 day (every 30 minutes) low = min(tempList) # Lowest temperature of the 24-hour period high = max(tempList) # Highest temperature of the 24-hour period diff = high-low # Range of temperature values for the day # In case weather data wasn't fetched properly and all templist is 0's, avoid dividing by zero below if(diff <= 0): diff = 1 tempList = np.array(tempList, dtype='f') # convert list to numpy array, easier to manipulate tempList_scaled = ((tempList - low)/diff)*15 # scale to fit into 48x16 box graph area tempList_scaled = abs((tempList_scaled-15)*-1) # - to flip eventual Y values tempList_scaled = np.round(tempList_scaled) tempList_scaled = tempList_scaled.astype(int) tempList_scaled = tempList_scaled.tolist() # convert back to list # Get current time of day to pick (make GREEN) the pixel (i index) currTime = datetime.datetime.now() elapsedMinutes = (currTime.hour*60) + currTime.minute halfHourIndex = int(round((float(elapsedMinutes / 1440.0))*47.0)) for i in range(0, len(tempList_scaled)): time.sleep(0.05) # this delay adds the drawing/animation effect for the temperature line graph, looks cooler if(i == halfHourIndex): set_pixel(i+8, tempList_scaled[i]+16, GREEN) else: set_pixel(i+8, tempList_scaled[i]+16, MAGENTA) def draw_rain_bar_graph(rainList): # rainList contains 25 precip % values for 1 day (int 0-100): midnight to midnight rainList = np.array(rainList, dtype='f') # convert list to numpy array, easier to manipulate rainList_scaled = (rainList/100.0)*24.0 # scale to fit into 0-24 bar graph area rainList_scaled = np.round(rainList_scaled) rainList_scaled = rainList_scaled.astype(int) rainList_scaled = rainList_scaled.tolist() # convert back to list # Get current time of day to pick (make GREEN) the bar graph index (i index) currTime = datetime.datetime.now() elapsedMinutes = (currTime.hour*60) + currTime.minute hourIndex = int(round((float(elapsedMinutes / 1440.0))*24.0)) #0-24 (25 len) # Each rectangle bar graph line is 2 pixels wide (x) for i in range(0, len(rainList_scaled)): time.sleep(0.05) # this delay adds the drawing/animation effect finalX = (i*2)+14 tempY = rainList_scaled[i] # Out of bounds error handling if(tempY < 0): tempY = 0 elif(tempY > 100): tempY = 100 finalY = 29-tempY if(i == hourIndex): draw_rectangle_outline(finalX, finalY, finalX+1, 29, GREEN) #curr time (curr hour) else: draw_rectangle_outline(finalX, finalY, finalX+1, 29, BLUE) def draw_basics(): # draw basic frames/outlines draw_rectangle_outline( 0, 0, 21, 14, MAGENTA) # LEFT box magenta draw_rectangle_outline(21, 0, 42, 14, MAGENTA) # CENTER box magenta draw_rectangle_outline(42, 0, 63, 14, MAGENTA) # RIGHT box magenta draw_rectangle_outline(0, 15, 6, 31, BLUE) # RAIN blue outline box bottom left corner (7x17) draw_rectangle_outline(57, 15, 63, 31, YELLOW) # UV yellow outline box bottom right corner (7x17) #draw_rectangle_outline( 0, 0, 63, 31, MAGENTA) # Entire Screen box magenta def draw_rain_basics(): # draw basic frames/outlines for RAIN screen localColor = MAGENTA draw_rectangle_outline(13, 5, 13, 29, localColor) # Left V line, 29 y tall # Draw/print percentage numbers (0% 25% 50% 75% 100%): 3wide x and 5tall y draw_number_small(6, 27, 0, localColor) # 0 of 0 draw_number_small(2, 21, 2, localColor) # 2 of 25 draw_number_small(6, 21, 5, localColor) # 5 of 25 draw_number_small(2, 15, 5, localColor) # 5 of 50 draw_number_small(6, 15, 0, localColor) # 0 of 50 draw_number_small(2, 9, 7, localColor) # 7 of 75 draw_number_small(6, 9, 5, localColor) # 5 of 75 draw_number_small(0, 3, 1, localColor) # 1 of 100 draw_number_small(2, 3, 0, localColor) # 0 of 100 draw_number_small(6, 3, 0, localColor) # 0 of 100 # Draw left/right percentage lines (5 H lines) (0% 25% 50% 75% 100%) for y in range(0,5): # 0-4 finalY = (y*6) + 5 draw_rectangle_outline(10, finalY, 12, finalY, localColor) # Left H line, 2 x wide def refresh(): # Only refreshes/involves LED graphics stuff, not weather grabs global globalScreenToPrint global screen0 global screen1 if(globalScreenToPrint == 0): screenFinal = screen0 elif(globalScreenToPrint == 1): screenFinal = screen1 for row in range(16): # 0-15 x2 RGB (top/bottom) = 32 rows total GPIO.output(oe_pin, 1) # disables LEDs set_row(row) for col in range(64): # 0-63 cols total set_color_top(screenFinal[row][col]) set_color_bottom(screenFinal[row+16][col]) clock() latch() GPIO.output(oe_pin, 0) # enables LEDs time.sleep(delay) # delay value is critical and defined at the top of this .py file def get_weather_info(): rain = 0 # 0-100 whole int percent uv = 0 # 0-100 whole int percent (original scale is 0-11) low = 0 # find MIN of tempListHourly 24-hour list in F curr = 0 # find CURRENTLY data for current temperature in F high = 0 # find MAX of tempListHourly 24-hour list in F tempList = [0 for x in range(48)] # temperature list with every 30 minute temps precipListHourly = [0 for x in range(25)] # precip % list for every 1 hour currTime = str(int(time.time())) # Get the CURRENT NOW time #currTime = str(1560427200) #test june 13, 2019 at noon #currTime = str(1560724224) #test june 16, 2019 # LINK: https://www.epochconverter.com/ try: # In case an internet connection error occurs fio = ForecastIO.ForecastIO(apikey, units=ForecastIO.ForecastIO.UNITS_US, lang=ForecastIO.ForecastIO.LANG_ENGLISH, time=currTime, latitude=location[0], longitude=location[1]) if fio.has_currently() is True: currently = FIOCurrently.FIOCurrently(fio) curr = int(round(currently.temperature)) rain = int(round(currently.precipProbability*100.0)) uv = ((int(round(currently.uvIndex)))/11.0)*100.0 if fio.has_hourly() is True: hourly = FIOHourly.FIOHourly(fio) tempListHourly = [] # sometimes 25 length or 24 length precipListHourly = [] # sometimes 25 length or 24 length for hour in range(0, hourly.hours()): tempListHourly.append(int(round(hourly.get_hour(hour).get('temperature')))) precipListHourly.append(int(round(hourly.get_hour(hour).get('precipProbability')*100.0))) # Special case to handle ONLY getting 24 values (midnight to 11pm) # instead of 25 values (midnight to midnight) # Since I built the code around expecting 25 values # Just copy the 11pm (last value) to the end if(len(tempListHourly) == 24): tempListHourly.append(tempListHourly[-1]) if(len(precipListHourly) == 24): precipListHourly.append(precipListHourly[-1]) # This program ignores negative temperatures (less than zero degrees F) for i in range(0,len(tempListHourly)): #0-24 if(tempListHourly[i] <= 0): tempListHourly[i] = 0 # This program ignores negative precip probabilites for i in range(0,len(precipListHourly)): #0-24 if(precipListHourly[i] <= 0): precipListHourly[i] = 0 high = max(tempListHourly) low = min(tempListHourly) for i in range(0,len(tempListHourly)): #0-24 if(i == len(tempListHourly)-1): # special end case tempList[(i*2)-1] = tempListHourly[i] else: tempList[i*2] = tempListHourly[i] # Put average of nextdoor hourly temps into half-hour temp if(i != 0 and i != len(tempListHourly)-1): tempList[(i*2)-1] = (tempListHourly[i] + tempListHourly[i-1])/2 except Exception as e: print("Exception(s) occured: ", e) return int(rain), int(uv), int(low), int(curr), int(high), tempList, precipListHourly def terminal_input(): global globalState while True: userInput = input("Enter 0 to toggle LEDs ON/OFF: ") if(userInput == 0): if(globalState == 0): globalState = 1 # Pause or Turn OFF the LEDs GPIO.output(oe_pin, 1) # disables LEDs else: globalState = 0 # Resumes normal or Turn ON the LEDs GPIO.output(oe_pin, 0) # enables LEDs else: print("Bitch, that aint right! Hoe.") def timer_display(): # 4:00 display timer for coffee brewing # Special color at 2:30 and 0:00 # numbers 0-9: 6wide by 13tall global globalScreenToSave globalScreenToSave = 0 # Save the following data to temp display screen0 time_in_secs = 240 # 240secs == 4min for curr_sec in range(time_in_secs, -1, -1): # 240 to 0 inclusive # min0 : sec1 sec0 (3:52 == 3=min0, 5=sec1, 2=sec0) min0 = int(curr_sec/60) sec1 = int((curr_sec%60)/10) sec0 = int((curr_sec%60)%10) if(curr_sec > 150): # First 1min30sec (timer hits 2:30) color = GREEN else: # last 2min30sec (after time hits 2:30) color = RED clear_screens() # Zeros out screen matrix/2D List draw_number(21, 9, min0, color) # min0 set_pixel(28, 12, color) # colon set_pixel(28, 18, color) # colon draw_number(30, 9, sec1, color) # sec1 draw_number(37, 9, sec0, color) # sec0 time.sleep(1) # seconds time.sleep(5) # Hold the 0:00 for 5 seconds and then leave clear_screens() # Zeros out screen matrix/2D List def IR_decode(): global globalState global globalScreenToPrint # STATES of Program: # 0 = Normal - Display Temperature Line Graph (LEDs ON) (INITIAL STATE VALUE) # 1 = Normal - Rain Graph (LEDs ON) # 2 = Paused - Blank Screen (All LEDs OFF) globalScreenToPrint = 0 while True: time.sleep(2) while(GPIO.input(IR_pin) == 1): # Wait while idle at 1 (logic HIGH) time.sleep(0.1) # Helps with hiccups of pulsing low time.sleep(1) # wait 1 sec for i in range(0,100): # Used to ignore "regular" IR signals intended for my TV only time.sleep(0.01) if(GPIO.input(IR_pin) == 0): # YES, switch state, long enough remote pulse if(globalState == 0): # currently in temp display, switch to rain globalState = 1 globalScreenToPrint = 1 time.sleep(2) # wait 2 sec so screen changes and release remote button break elif(globalState == 1): # currently in rain display, switch to LEDs OFF globalState = 2 time.sleep(2) # wait 2 sec so screen changes and release remote button break elif(globalState == 2): # currently in OFF, switch to temp display globalState = 0 # Resumes normal or Turn ON the LEDs globalScreenToPrint = 0 time.sleep(2) # wait 2 sec so screen changes and release remote button break def main_func(): # Weather updates every 4 minutes global globalScreenToSave while True: rain, uv, low, curr, high, tempList, rainListHourly = get_weather_info() clear_screens() # Zeros out screen matrix/2D List globalScreenToSave = 0 # Save the following data to temp display screen0 draw_basics() draw_temperature_numbers('low', low) draw_temperature_numbers('curr', curr) draw_temperature_numbers('high', high) draw_temperature_line_graph(tempList) # Have traveling animated green dot move to location for tempRain in range(0,rain+1): # Bar Graph animation effect draw_rain(tempRain) time.sleep(0.01) for tempUv in range(0,uv+1): # Bar Graph animation effect draw_uv(tempUv) time.sleep(0.01) globalScreenToSave = 1 # Save the following data to rain display screen1 draw_rain_basics() draw_rain_bar_graph(rainListHourly) time.sleep(240) # seconds (4 minute interval updates) # 1,440 minutes in one day, 1440/4 = 360 weather updates daily # DarkSky API free limit is 1000 per day #---------------------------Start Threads---------------------------# # Thread to update weather info and screen matrix values threading1 = threading.Thread(target=main_func) threading1.daemon = True threading1.start() # If an IR "pulse" lasts 0.5secs then toggle LEDs (oe_pin) threading2 = threading.Thread(target=IR_decode) threading2.daemon = True threading2.start() # Thread to toggle LEDs ON/OFF based off user keyboard/terminal input #threading3 = threading.Thread(target=terminal_input) #threading3.daemon = True #threading3.start() # 4 minute timer for coffee brewing #threading4 = threading.Thread(target=timer_display) #threading4.daemon = True #threading4.start() #--------------------------MAIN ROUTINE (Start)--------------------------# while True: refresh() # this constantly communicates with the LED matrix module, REQUIRED while(globalState == 2): # Paused (LEDs OFF) GPIO.output(oe_pin, 1) # disables LEDs time.sleep(0.1) #---------------------------MAIN ROUTINE (End)---------------------------#