# Lighthouse Effects Code - v 1.0.0 # Using MetroM4 Airlift Lite to run sound effects and a touchscreen display # Currently enabled: # Touchscreen, Ethernet, Weather query, realtime clock, bitmap load, # digital output pins for sound effects, digital trigger of externally powered circuits (lighthouse lights and motor) # MetroM4 Airlift Lite with RA8875 and TT screen, with wifi connection # Credits: # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries - Secrets file loading and wifi setup sections # SPDX-FileCopyrightText: 2020 Limor Fried for Adafruit Industries - Weather data retrieval setup # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries - Code for setup and time retrieval from DS3231 RTC # SPDX-License-Identifier: MIT# import struct import time import random import busio import digitalio from digitalio import DigitalInOut import board import neopixel import adafruit_bus_device import adafruit_requests as requests from adafruit_ra8875 import ra8875 from adafruit_ra8875.ra8875 import color565 from analogio import AnalogIn import adafruit_esp32spi.adafruit_esp32spi_socket as socket from adafruit_esp32spi import adafruit_esp32spi_wifimanager from adafruit_esp32spi import adafruit_esp32spi import adafruit_ds3231 import supervisor # # # Section of the code for the wifi connect over SPI # # # Get wifi details and more from a secrets.py file try: from secrets import secrets except ImportError: print("secrets.py file not found") raise print() print("-" * 60) print("Jay Oyster Lighthouse Effects - Version 1.0.0") print("-" * 60) print() LOCATION = secrets["timezone"] SOUND_OFF = True SOUND_ON = False DST_ACTIVE = True # Is daylight saving time active? LIGHTON_Minutes_offset = 30 LIGHTON_Seconds_offset = ( LIGHTON_Minutes_offset * 60 ) # Number of seconds after sunset to turn on the beacon LIGHTOFF_Minutes_offset = 90 LIGHTOFF_Seconds_offset = ( LIGHTOFF_Minutes_offset * 60 ) # Number of seconds after sunset to turn off the beacon LIGHTON_OFFSET = 30 # number of minutes after sunset to turn on the light sunset_string = "Sunset: {}:{} {}" turnon_string = "Beacon on: {}:{} {}" turnoff_string = "Beacon off: {}:{} {}" DATA_SOURCE = ( "http://api.openweathermap.org/data/2.5/weather?lat=" + secrets["openweather_latitude"] ) DATA_SOURCE += "&lon=" + secrets["openweather_longitude"] DATA_SOURCE += "&appid=" + secrets["openweather_token"] # If you are using a board with pre-defined ESP32 Pins: esp32_cs = DigitalInOut(board.ESP_CS) esp32_ready = DigitalInOut(board.ESP_BUSY) esp32_reset = DigitalInOut(board.ESP_RESET) # Setting Metro M4 input/output pins and initial values i2c = board.I2C() ds3231 = adafruit_ds3231.DS3231(i2c) sound_of_calm_seas = DigitalInOut(board.D0) sound_of_heavy_rains = DigitalInOut(board.D1) sound_of_heavy_seas_nowind = DigitalInOut(board.D2) sound_of_distant_thunder = DigitalInOut(board.D3) sound_of_norway_harbor = DigitalInOut(board.D4) sound_of_windy_rough_shore = DigitalInOut(board.D5) sound_of_seashore_and_foghorn = DigitalInOut(board.D6) sound_of_sumberg_foghorn = DigitalInOut(board.D7) sound_of_SBSPTheme = DigitalInOut(board.D8) lighthouse_Motor = DigitalInOut(board.D13) lighthouse_Lights = DigitalInOut(board.D12) sound_of_calm_seas.direction = digitalio.Direction.OUTPUT sound_of_heavy_rains.direction = digitalio.Direction.OUTPUT sound_of_heavy_seas_nowind.direction = digitalio.Direction.OUTPUT sound_of_distant_thunder.direction = digitalio.Direction.OUTPUT sound_of_norway_harbor.direction = digitalio.Direction.OUTPUT sound_of_windy_rough_shore.direction = digitalio.Direction.OUTPUT sound_of_seashore_and_foghorn.direction = digitalio.Direction.OUTPUT sound_of_sumberg_foghorn.direction = digitalio.Direction.OUTPUT sound_of_SBSPTheme.direction = digitalio.Direction.OUTPUT lighthouse_Motor.direction = digitalio.Direction.OUTPUT lighthouse_Lights.direction = digitalio.Direction.OUTPUT sound_of_calm_seas.value = SOUND_OFF sound_of_heavy_rains.value = SOUND_OFF sound_of_heavy_seas_nowind.value = SOUND_OFF sound_of_distant_thunder.value = SOUND_OFF sound_of_norway_harbor.value = SOUND_OFF sound_of_windy_rough_shore.value = SOUND_OFF sound_of_seashore_and_foghorn.value = SOUND_OFF sound_of_sumberg_foghorn.value = SOUND_OFF sound_of_SBSPTheme.value = SOUND_OFF lighthouse_Motor.value = False lighthouse_Lights.value = False spi = busio.SPI(board.SCK, board.MOSI, board.MISO) esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset) status_light = neopixel.NeoPixel( board.NEOPIXEL, 1, brightness=0.2 ) # Uncomment for Most Boards wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light) # End of Setting Metro M4 input/output pins and initial values requests.set_socket(socket, esp) print("D0 pin value:sound_of_calm_seas: " + str(sound_of_calm_seas.value)) print("D1 pin value:sound_of_heavy_rains: " + str(sound_of_heavy_rains.value)) print("D2 pin value:sound_of_heavy_seas_nowind: " + str(sound_of_heavy_seas_nowind.value)) print("D3 pin value:sound_of_distant_thunder: " + str(sound_of_distant_thunder.value)) print("D4 pin value:sound_of_norway_harbor: " + str(sound_of_norway_harbor.value)) print("D5 pin value:sound_of_windy_rough_shore: " + str(sound_of_windy_rough_shore.value)) print( "D6 pin value:sound_of_seashore_and_foghorn: " + str(sound_of_seashore_and_foghorn.value) ) print("D7 pin value:sound_of_sumberg_foghorn: " + str(sound_of_sumberg_foghorn.value)) print("D8 pin value:SpongeBobTheme: " + str(sound_of_SBSPTheme.value)) print("D12 pin value:Lighthouse Lights: " + str(lighthouse_Lights.value)) print("D13 pin value:Lighthouse Motor: " + str(lighthouse_Motor.value)) if esp.status == adafruit_esp32spi.WL_IDLE_STATUS: print("ESP32 found and in idle mode") print("Connecting to AP...") while not esp.is_connected: try: esp.connect_AP(secrets["ssid"], secrets["password"]) except OSError as e: print("could not connect to AP, retry in 10 seconds. error: ", e) time.sleep(10.0) continue print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi) print("My IP address is", esp.pretty_ip(esp.ip_address)) print() print("-" * 60) # query the weather once response = wifi.get(DATA_SOURCE).json() weather_type = response["weather"][0]["main"] weather_vis = response["visibility"] weather_description = response["weather"][0]["description"] weather_temp = response["main"]["temp"] weather_temp_F = ((weather_temp - 273.15) * 9.0 / 5.0) + 32.0 weather_humid = response["main"]["humidity"] weather_windspeed = 2.237 * ( response["wind"]["speed"] ) # retrieve windspeed in (m/sec) and convert to mph weather_time = response[ "dt" ] # Note:This is the time coming from openweathermap.org, not coming from the onboard realtime clock. weather_datetime = time.localtime(weather_time) # ----------------------------------------------------------------------------------------Print initial conditions to console # print("Response is", response) print("Currently:", weather_type) # See https://openweathermap.org/weather-conditions print("Visibility is %d meters" % weather_vis) print("Current conditions: ", weather_description) print("Current temp is %6.2f °K" % weather_temp) print("Current temp is %5.1f °F" % weather_temp_F) print("Current humidity is %d%%" % weather_humid) print("Wind speed: %5.1f mph" % weather_windspeed) print("Current time: %d " % weather_time) print("DST Active %s " % DST_ACTIVE) print( "Last updated: {}/{}/{} {:02}:{:02}:{:02} UTC".format( weather_datetime.tm_mon, weather_datetime.tm_mday, weather_datetime.tm_year, weather_datetime.tm_hour, weather_datetime.tm_min, weather_datetime.tm_sec, ) ) current_time_snap = ds3231.datetime if DST_ACTIVE: current_hour = current_time_snap.tm_hour + 1 else: current_hour = current_time_snap.tm_hour print( "The current time is: {}/{}/{} {:02}:{:02}:{:02}".format( current_time_snap.tm_mon, current_time_snap.tm_mday, current_time_snap.tm_year, current_hour, current_time_snap.tm_min, current_time_snap.tm_sec, ) ) print("-" * 60) # ----------------------------------------------------------------------------------------Print initial conditions to console - END # # Section of the code for the touchscreen display over SPI # # analog_in = AnalogIn(board.A1) BLACK = color565(0, 0, 0) LTGRAY = color565(242, 242, 242) RED = color565(255, 0, 0) LTRED = color565(255, 230, 230) GREEN = color565(0, 255, 0) LTGREEN = color565(230, 255, 230) BLUE = color565(0, 0, 255) YELLOW = color565(255, 255, 0) CYAN = color565(0, 255, 255) MAGENTA = color565(255, 0, 255) WHITE = color565(255, 255, 255) ORANGE = color565(255, 100, 5) SOMECOLOR = color565(37, 180, 193) # Configuration for CS and RST pins: cs_pin = digitalio.DigitalInOut(board.D9) rst_pin = digitalio.DigitalInOut(board.D10) int_pin = digitalio.DigitalInOut(board.D11) # Config for display baudrate (default max is 6mhz): BAUDRATE = 6000000 # Setup SPI bus using hardware SPI: # spi = busio.SPI(clock=board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Create and setup the RA8875 display: display = ra8875.RA8875(spi, cs=cs_pin, rst=rst_pin, baudrate=BAUDRATE) display.init() display.fill(WHITE) time.sleep(0.500) # ---------------------------------------------------------- Bitmap load def convert_555_to_565(rgb): return (rgb & 0x7FE0) << 1 | 0x20 | rgb & 0x001F class BMP: def __init__(self, filename): self.filename = filename self.colors = None self.data = 0 self.data_size = 0 self.bpp = 0 self.width = 0 self.height = 0 self.read_header() def read_header(self): if self.colors: return with open(self.filename, "rb") as f: f.seek(10) self.data = int.from_bytes(f.read(4), "little") f.seek(18) self.width = int.from_bytes(f.read(4), "little") self.height = int.from_bytes(f.read(4), "little") f.seek(28) self.bpp = int.from_bytes(f.read(2), "little") f.seek(34) self.data_size = int.from_bytes(f.read(4), "little") f.seek(46) self.colors = int.from_bytes(f.read(4), "little") def draw(self, disp, x=0, y=0): print("{:d}x{:d} image".format(self.width, self.height)) print("{:d}-bit encoding detected".format(self.bpp)) line = 0 line_size = self.width * (self.bpp // 8) if line_size % 4 != 0: line_size += 4 - line_size % 4 current_line_data = b"" with open(self.filename, "rb") as f: f.seek(self.data) disp.set_window(x, y, self.width, self.height) for line in range(self.height): current_line_data = b"" line_data = f.read(line_size) for i in range(0, line_size, self.bpp // 8): if (line_size - i) < self.bpp // 8: break if self.bpp == 16: color = convert_555_to_565(line_data[i] | line_data[i + 1] << 8) if self.bpp in (24, 32): color = color565( line_data[i + 2], line_data[i + 1], line_data[i] ) current_line_data = current_line_data + struct.pack(">H", color) disp.setxy(x, self.height - line + y) disp.push_pixels(current_line_data) disp.set_window(0, 0, disp.width, disp.height) bitmap = BMP("/lighthouse-sm.bmp") # ----------------------------------------------------- End of Bitmap load # --------------------------------------Draw main window and status bar backgrounds display.fill(BLACK) time.sleep(0.100) display.rect(10, 10, 460, 250, WHITE) display.fill_rect(10, 240, 460, 20, WHITE) display.rect(10, 240, 460, 20, RED) display.line(220,240,220,260,BLACK) display.line(352,240,352,260,BLACK) # --------------------------------------Draw main window and status bar backgrounds - END display.touch_init(int_pin) display.touch_enable(True) dwidth = 480 # display.width dheight = 270 # display.height x_scale = 1024 / dwidth # display.width y_scale = 1024 / dheight # display.height def get_voltage(pin): return (pin.value * 3.3) / 65536 # ------------------------------------------------Populating Initial display display.fill_rect(100, 40, 180, 20, BLACK) display.txt_trans(WHITE) display.txt_size(0) display.txt_set_cursor(20, 20) display.txt_write("Temp: %4.1f F" % weather_temp_F) display.txt_set_cursor(135, 20) display.txt_write("Humidity: %d%%" % weather_humid) display.txt_set_cursor(20, 40) display.txt_write("Weather:") display.txt_trans(WHITE) display.txt_set_cursor(97, 41) display.txt_write(weather_description) display.txt_trans(WHITE) display.txt_set_cursor(20, 60) display.txt_write("Visibility: %d meters" % weather_vis) display.txt_set_cursor(20, 80) display.txt_write("Wind speed: %4.1f mph" % weather_windspeed) # ***************************************************************NEW LIGHTHOUSE TIME STUFF************* weather_shiftfortimezone = response["timezone"] weather_time = response["dt"] weather_time_in_zone = weather_time + weather_shiftfortimezone weather_datetime = time.localtime(weather_time_in_zone) weather_sunset = response["sys"]["sunset"] + weather_shiftfortimezone lighthouseStartTime = weather_sunset + LIGHTON_Seconds_offset lighthouseStopTime = weather_sunset + LIGHTOFF_Seconds_offset sunset = time.localtime(weather_sunset) beacon_on_after_sunset = time.localtime(weather_sunset + LIGHTON_Seconds_offset) beacon_off_after_sunset = time.localtime(weather_sunset + LIGHTOFF_Seconds_offset) if sunset.tm_hour > 12: sunset_hour = sunset.tm_hour - 12 else: sunset_hour = sunset.tm_hour print("Sunset : {:01}:{:02} {}".format(sunset_hour, sunset.tm_min, "PM")) if beacon_on_after_sunset.tm_hour > 12: beaconon_hour = beacon_on_after_sunset.tm_hour - 12 else: beaconon_hour = beacon_on_after_sunset.tm_hour print( "Beacon On : {:01}:{:02} {}".format( beaconon_hour, beacon_on_after_sunset.tm_min, "PM" ) ) if beacon_off_after_sunset.tm_hour > 12: beaconoff_hour = beacon_off_after_sunset.tm_hour - 12 else: beaconoff_hour = beacon_off_after_sunset.tm_hour if beaconoff_hour == 0: beaconoff_hour = 12 if beacon_off_after_sunset.tm_mday != sunset.tm_mday: beaconoffAMPM = "AM" else: beaconoffAMPM = "PM" print( "Beacon Off : {:01}:{:02} {}".format( beaconoff_hour, beacon_off_after_sunset.tm_min, beaconoffAMPM ) ) if (weather_time_in_zone >= lighthouseStartTime) and ( weather_time_in_zone < lighthouseStopTime ): print("Beacon currently: ON") else: print("Beacon currently: OFF") print("current time: " + str(weather_time_in_zone)) print("current start: " + str(lighthouseStartTime)) print("current stop: " + str(lighthouseStopTime)) # ***************************************************************NEW LIGHTHOUSE TIME STUFF************* display.txt_color(WHITE, BLACK) display.txt_size(0) # display.txt_set_cursor(20, 210) if sunset.tm_hour < 12: sunsethour = sunset.tm_hour sunsetampm = "AM" elif sunset.tm_hour == 12: sunsethour = sunset.tm_hour sunsetampm = "PM" elif sunset.tm_hour > 12: sunsethour = sunset.tm_hour - 12 sunsetampm = "PM" elif sunset.tm_hour > 23: sunsethour = sunset.tm_hour - 12 sunsetampm = "AM" # display.txt_write( # sunset_string.format(sunsethour, sunset.tm_min, sunsetampm) # ) # Draw the photo of the lighthouse print("Drawing lighthouse bitmap image") bitmap.draw(display, 314, 16) time.sleep(0.1) display.fill_rect(20, 130, 80, 20, LTGRAY) # Horn indicator display.rect(20, 100, 80, 30, GREEN) display.txt_trans(BLACK) display.txt_set_cursor(32, 131) display.txt_write("Foghorn") display.fill_rect(118, 130, 80, 20, LTGRAY) # Lighthouse light indicator display.rect(118, 100, 80, 30, RED) display.txt_trans(BLACK) display.txt_set_cursor(134, 131) display.txt_write("Beacon") display.fill_rect(210, 130, 80, 20, LTGRAY) # Sound ON/OFF indicator display.fill_rect(210, 100, 80, 30, LTGREEN) display.rect(210, 100, 80, 30, BLACK) display.txt_trans(BLACK) display.txt_set_cursor(230, 131) display.txt_write("Sound") display.txt_size(1) display.txt_set_cursor(236, 97) display.txt_write("ON") display.circle(295,25,9,WHITE) # Wifi trouble indicator display.rect(450, 221, 16, 16, WHITE) # DST On indicator box display.txt_color(WHITE, BLACK) display.txt_set_cursor(412, 221) display.txt_size(0) display.txt_write("DST:") if DST_ACTIVE: display.fill_circle(458,229,5,RED) else: display.fill_rect(451, 222, 14, 14, LTGRAY) # ---------------------------------------------------------- Beacon ON and OFF Time set boxes display.rect(20,189, 280,48,WHITE) display.txt_set_cursor(23,190) display.txt_write("Beacon: ON (sunset+5minutes) OFF ") display.fill_rect(24,209,65,25, WHITE) # minutes after sunset for beaconON display.fill_rect(170,209,65,25, WHITE) # minutes after sunset for beaconOFF display.rect(24,209,65,25, RED) # minutes after sunset for beaconON display.rect(170,209,65,25, RED) display.rect(94,208,28,28, WHITE) # subtract 5 minutes from ON time button display.rect(124,208,28,28,WHITE) # add 5 minutes to ON time button display.rect(238,208,28,28, WHITE) # subtract 5 minutes from OFF time button display.rect(268,208,28,28,WHITE) # add 5 minutes to OFF time button display.txt_size(1) display.txt_trans(WHITE) time.sleep(0.1) display.txt_set_cursor(99,204) display.txt_write("-") display.txt_set_cursor(130,204) time.sleep(0.1) display.txt_write("+") display.txt_set_cursor(243,204) time.sleep(0.1) display.txt_write("-") display.txt_set_cursor(274,204) time.sleep(0.1) display.txt_write("+") # ---------------------------------------------------------- Beacon ON and OFF Time set boxes - END # --------------------------- Initial LightON and LightOFF offset times display.txt_size(0) display.txt_trans(BLACK) display.txt_set_cursor(40,213) display.txt_write(str(int(LIGHTON_Seconds_offset / 60))) display.txt_set_cursor(186,213) display.txt_write(str(int(LIGHTOFF_Seconds_offset / 60))) # --------------------------- Initial LightON and LightOFF offset times - END # ------------------------------------------------End of populating initial display # ------------------------------------------------Initialalizing Operating values lighthouseNightTime = False foghorncall = False beaconcall = False soundON = True weatherLoopCount = 0 time_string = "{}:{}:{} {}" doubletouch_counter = 0 touchflag = False brightnesscountdown = 600 # ------------------------------------------------Initialalizing Operating values - End # -------------------------------------------------Main loop: while True: # start of outer 5 minute loop for x in range(2200): # ----------------------------start of inner loop, 2200 = ~300 seconds (5 mins) if display.touched(): display.brightness(255) brightnesscountdown = 6000 # 6000 = 10 minutes of no touches causes the display to dim coords = display.touch_read() display.txt_color(BLACK,WHITE) display.txt_size(0) display.txt_set_cursor(227, 241) display.txt_write( "Touch(" + str(coords[0]) + ", " + str(coords[1]) + ")" ) print("raw coords: " + str(coords[0]) + ", " + str(coords[1])) touch_x = int(coords[0] / 1024 * dwidth) touch_y = int(coords[1] / 1024 * dheight) print("touch coords: " + str(touch_x) + ", " + str(touch_y)) # display.fill_circle(touch_x, touch_y, 2, RED) # --------------------------------------- Set foghorn call if ( (touch_x >= 20 and touch_x <= 80) and (touch_y >= 100 and touch_y <= 150) and (touchflag == False) ): if foghorncall == False: display.fill_rect(21, 101, 78, 28, YELLOW) display.txt_set_cursor(30, 97) time.sleep(0.1) #give system time to relocate to right text display.txt_size(1) display.txt_trans(BLACK) display.txt_write("CALL") foghorncall = True else: # clear foghorn call display.fill_rect(21, 101, 78, 28, BLACK) foghorncall = False touchflag = True # ---------------------------------------Set foghorn call - END # ---------------------------------------Set beacon call if ( (touch_x >= 119 and touch_x <= 200) and (touch_y >= 100 and touch_y <= 150) and (touchflag == False) ): if beaconcall == False: display.fill_rect(119, 101, 78, 28, YELLOW) display.txt_set_cursor(129, 97) time.sleep(0.1) #give system time to relocate to right text display.txt_size(1) display.txt_trans(BLACK) display.txt_write("CALL") beaconcall = True else: # clear beacon call display.fill_rect(119, 101, 78, 28, BLACK) beaconcall = False lighthouse_Motor.value = False touchflag = True # ---------------------------------------Set beacon call - END # --------------------------------------- Play the tune if ((touch_x >= 315 and touch_x <= 460) and (touch_y >= 18 and touch_y <= 198) and (touchflag == False)): display.rect(314, 17, 150, 198, YELLOW) display.rect(316, 19, 146, 194, YELLOW) sound_of_SBSPTheme.value = False # play the spongebob theme time.sleep(10.0) bitmap.draw(display, 314, 16) time.sleep(0.1) touchflag = True sound_of_SBSPTheme.value = True # turn off the spongebob theme # --------------------------------------- Play the tune - END # --------------------------------------- DST setting change touches if ( (touch_x >= 410 and touch_x <= 450) and (touch_y >= 205 and touch_y <= 225) and (touchflag == False) ): if DST_ACTIVE == False: DST_ACTIVE = True else: DST_ACTIVE = False touchflag = True if DST_ACTIVE: display.fill_circle(458,229,5,RED) else: display.fill_rect(451, 222, 14, 14, LTGRAY) # --------------------------------------- DST setting change touches - END # ---------------------------------------React to Sound ON/OFF touches if ( (touch_x >= 210 and touch_x <= 290) and (touch_y >= 100 and touch_y <= 150) and (touchflag == False) ): if soundON == False: soundON = True display.fill_rect(211, 101, 78, 28, LTGREEN) #210, 80, 80, 30 display.rect(210, 100, 80, 30, BLACK) display.txt_size(1) display.txt_trans(BLACK) display.txt_set_cursor(234, 97) display.txt_write("ON") else: # clear beacon call soundON = False display.fill_rect(211, 101, 78, 28, LTRED) display.rect(210, 100, 80, 30, BLACK) display.txt_size(1) display.txt_trans(BLACK) display.txt_set_cursor(229, 97) display.txt_write("OFF") touchflag = True # --------------------------------------React to Sound ON/OFF touches - END # ---------------------------------------React to Reset touches if ((touch_x >= 280 and touch_x <= 310) and (touch_y >= 10 and touch_y <= 30)): display.fill_circle(295,25,7,YELLOW) print("Reset touch detected. Restarting the code.") time.sleep(1) # Wait 3 seconds supervisor.reload() # restart the code. To be used if wifi error. # ---------------------------------------React to Reset touches - END # ---------------------------React to LightON and LightOFF offset time touches if ((touch_x >= 90 and touch_x <= 122) and (touch_y >= 205 and touch_y <= 240) and touchflag==False): # ON minus5 button if (LIGHTON_Seconds_offset <= 300): LIGHTON_Seconds_offset = 0 display.fill_rect(25,210,63,23, WHITE) # minutes after sunset for beaconON display.txt_size(0) display.txt_trans(BLACK) display.txt_set_cursor(40,213) display.txt_write(str(int(LIGHTON_Seconds_offset / 60))) else: LIGHTON_Seconds_offset = LIGHTON_Seconds_offset - 300 display.fill_rect(25,210,63,23, WHITE) # minutes after sunset for beaconON display.txt_size(0) display.txt_trans(BLACK) display.txt_set_cursor(40,213) display.txt_write(str(int(LIGHTON_Seconds_offset / 60))) touchflag = True if ((touch_x >= 123 and touch_x <= 152) and (touch_y >= 205 and touch_y <= 240) and touchflag == False): # ON plus5 button if (LIGHTON_Seconds_offset <= 21300): #Turn on of beacon must happen within 6 hours of sunset LIGHTON_Seconds_offset = LIGHTON_Seconds_offset + 300 display.fill_rect(25,210,63,23, WHITE) # minutes after sunset for beaconON display.txt_size(0) display.txt_trans(BLACK) display.txt_set_cursor(40,213) display.txt_write(str(int(LIGHTON_Seconds_offset / 60))) touchflag = True if ((touch_x >= 235 and touch_x <= 266) and (touch_y >= 205 and touch_y <= 240) and touchflag == False): # OFF minus5 button if ((LIGHTOFF_Seconds_offset - 300) <= LIGHTON_Seconds_offset): LIGHTOFF_Seconds_offset = LIGHTON_Seconds_offset + 300 # minimum time on is 5 minutes display.fill_rect(171,210,63,23, WHITE) # minutes after sunset for beaconOFF display.txt_size(0) display.txt_trans(BLACK) display.txt_set_cursor(186,213) display.txt_write(str(int(LIGHTOFF_Seconds_offset / 60))) else: LIGHTOFF_Seconds_offset = LIGHTOFF_Seconds_offset - 300 display.fill_rect(171,210,63,23, WHITE) # minutes after sunset for beaconOFF display.txt_size(0) display.txt_trans(BLACK) display.txt_set_cursor(186,213) display.txt_write(str(int(LIGHTOFF_Seconds_offset / 60))) touchflag = True if ((touch_x >= 268 and touch_x <= 300) and (touch_y >= 205 and touch_y <= 240) and touchflag == False): # OFF plus5 button if (LIGHTOFF_Seconds_offset <= 54000): #Turn off beacon can happen up to 15 hours after sunset LIGHTOFF_Seconds_offset = LIGHTOFF_Seconds_offset + 300 display.fill_rect(171,210,63,23, WHITE) # minutes after sunset for beaconOFF display.txt_size(0) display.txt_trans(BLACK) display.txt_set_cursor(186,213) display.txt_write(str(int(LIGHTOFF_Seconds_offset / 60))) touchflag = True # ---------------------------React to LightON and LightOFF offset time touches - END if lighthouseNightTime: lighthouseNightTime = False # testing, turn off the light indicator else: lighthouseNightTime = True # ------------------------------------ End of touch detection loop brightnesscountdown -= 1 if (brightnesscountdown <= 0): display.brightness(0) if (touchflag == True) and (doubletouch_counter < 10): doubletouch_counter += 1 else: touchflag = False doubletouch_counter = 0 display.txt_size(0) # Place tenth-seconds counter on display display.txt_color(BLACK, WHITE) display.txt_set_cursor(435, 241) TenthCounter = "{:>4}" # format the tenth-second counter by right-aligning it in a 4 space area. display.txt_write(TenthCounter.format(str(x))) # Take updated time snapshot from real-time clock (DS3231) and display date/time current_time_snap = ds3231.datetime if DST_ACTIVE: current_hour = current_time_snap.tm_hour + 1 else: current_hour = current_time_snap.tm_hour # Display date in the status bar display.txt_color(BLACK, WHITE) display.txt_set_cursor(20, 241) if current_time_snap.tm_mon < 10: display.txt_write(" " + str(current_time_snap.tm_mon)) else: display.txt_write(str(current_time_snap.tm_mon)) display.txt_set_cursor(40, 241) display.txt_write("/") display.txt_set_cursor(50, 241) display.txt_write(str(current_time_snap.tm_mday)) display.txt_set_cursor(72, 241) display.txt_write("/") display.txt_set_cursor(84, 241) display.txt_write(str(current_time_snap.tm_year)) # -------------------------------------------------Display time in the status bar display.txt_color(BLACK, WHITE) display.txt_set_cursor(127, 241) if DST_ACTIVE: current_hour = current_time_snap.tm_hour + 1 else: current_hour = current_time_snap.tm_hour if current_hour < 10: time_hour = " " + str(current_hour) elif current_hour > 12: time_hour = str(current_hour - 12) else: time_hour = str(current_hour) if current_time_snap.tm_min < 10: time_minute = "0" + str(current_time_snap.tm_min) else: time_minute = str(current_time_snap.tm_min) if current_time_snap.tm_sec < 10: time_second = "0" + str(current_time_snap.tm_sec) else: time_second = str(current_time_snap.tm_sec) if current_hour > 11: time_ampm = "PM" else: time_ampm = "AM" display.txt_write( time_string.format(time_hour, time_minute, time_second, time_ampm) ) # -------------------------------------------------Display time in the status bar - END # Wait a tenth of a second between each loop - This is the (roughly) tenth second clock for # the main screen, time updates, and touchscreen inputs time.sleep(0.1) # -------------------------------------------------------end of inner tenth-second loop weatherLoopCount += 1 # print over the counter number display.txt_color(BLACK, WHITE) display.txt_set_cursor(436, 241) display.txt_write("####") # - Retrieve updated weather data in 5 minute loop try: # trying to catch intermittent wifi errors that crash the program response = wifi.get(DATA_SOURCE).json() except: # if there's an error, skip over updating the internet weather and time data print("********** Wifi is wonky again ***********") display.fill_circle(295,25,7,RED) else: # if there isn't an error reading wifi, update the weather and time data display.fill_circle(295,25,7,GREEN) # -----------------------------------------------Main loop Weather updates weather_type = response["weather"][0]["main"] weather_vis = response["visibility"] weather_description = response["weather"][0]["description"] display.fill_rect(95, 40, 180, 20, BLACK) weather_temp = response["main"]["temp"] weather_temp_F = ((weather_temp - 273.15) * 9.0 / 5.0) + 32.0 weather_humid = response["main"]["humidity"] # -----------------------------------------------Main loop Time updates weather_shiftfortimezone = response["timezone"] weather_time = response["dt"] weather_datetime = time.localtime(weather_time - weather_shiftfortimezone) weather_windspeed = 2.237 * (response["wind"]["speed"]) weather_sunset = response["sys"]["sunset"] + weather_shiftfortimezone sunset = time.localtime(weather_sunset) # - Retrieve updated weather data in 5 minute loop - END # - Print updated weather data to console print("-" * 60) print("WeatherLoop:", weatherLoopCount) print("Weather type is", weather_type) print("Visibility is %d meters" % weather_vis) print("Current conditions: ", weather_description) print("Current temp is %6.2f °K" % weather_temp) print("Current temp is %5.1f °F" % weather_temp_F) print("Current humidity is %d%%" % weather_humid) print("Current wind speed: %5.1f mph" % weather_windspeed) # - Print updated weather data to console - END if DST_ACTIVE: current_hour = current_time_snap.tm_hour + 1 else: current_hour = current_time_snap.tm_hour print( "The current time is: {}/{}/{} {:02}:{:02}:{:02}".format( current_time_snap.tm_mon, current_time_snap.tm_mday, current_time_snap.tm_year, current_hour, current_time_snap.tm_min, current_time_snap.tm_sec, ) ) print( "Last updated: {}/{}/{} {:02}:{:02}:{:02} UTC".format( weather_datetime.tm_mon, weather_datetime.tm_mday, weather_datetime.tm_year, weather_datetime.tm_hour, weather_datetime.tm_min, weather_datetime.tm_sec, ) ) # -------------------------------------print and display sunset time display.txt_color(WHITE, BLACK) display.txt_size(0) display.txt_set_cursor(20, 154) if sunset.tm_hour < 12: sunsethour = sunset.tm_hour sunsetampm = "AM" elif sunset.tm_hour == 12: sunsethour = sunset.tm_hour sunsetampm = "PM" elif sunset.tm_hour > 12: sunsethour = sunset.tm_hour - 12 sunsetampm = "PM" elif sunset.tm_hour > 23: sunsethour = sunset.tm_hour - 12 sunsetampm = "AM" if (DST_ACTIVE == False): #Adjust sunset time for daylight saving time sunsethour = sunsethour - 1 display.txt_write(sunset_string.format(sunsethour, sunset.tm_min, sunsetampm)) # -------------------------------------print and display sunset time - end # ------------------------------------ Updated 5 minute loop time info # UNIX times Weather_shiftfortimezone = response["timezone"] weather_time = response["dt"] weather_time_in_zone = weather_time + weather_shiftfortimezone weather_sunset = response["sys"]["sunset"] + weather_shiftfortimezone lighthouseStartTime = weather_sunset + LIGHTON_Seconds_offset lighthouseStopTime = weather_sunset + LIGHTOFF_Seconds_offset if (DST_ACTIVE == False): #Adjust times for daylight saving time weather_time_in_zone = weather_time_in_zone - 3600 weather_sunset = weather_sunset - 3600 lighthouseStartTime = lighthouseStartTime - 3600 lighthouseStopTime = lighthouseStopTime - 3600 # --------------------------- Update LightON and LightOFF offset times display.fill_rect(25,210,63,23, WHITE) # minutes after sunset for beaconON display.fill_rect(171,210,63,23, WHITE) # minutes after sunset for beaconOFF display.txt_size(0) display.txt_trans(BLACK) display.txt_set_cursor(40,213) display.txt_write(str(int(LIGHTON_Seconds_offset / 60))) display.txt_set_cursor(186,213) display.txt_write(str(int(LIGHTOFF_Seconds_offset / 60))) # --------------------------- Update LightON and LightOFF offset times - END # Structured times weather_datetime = time.localtime(weather_time_in_zone) sunset = time.localtime(weather_sunset) beacon_on_after_sunset = time.localtime(weather_sunset + LIGHTON_Seconds_offset) beacon_off_after_sunset = time.localtime(weather_sunset + LIGHTOFF_Seconds_offset) if sunset.tm_hour > 12: sunset_hour = sunset.tm_hour - 12 else: sunset_hour = sunset.tm_hour print("Sunset: {:01}:{:02} {}".format(sunset_hour, sunset.tm_min, "PM")) display.txt_color(WHITE, BLACK) display.txt_set_cursor(20, 154) display.txt_write( "Sunset: {:01}:{:02} {} ".format(sunset_hour, sunset.tm_min, "PM") ) if beacon_on_after_sunset.tm_hour > 12: beaconon_hour = beacon_on_after_sunset.tm_hour - 12 else: beaconon_hour = beacon_on_after_sunset.tm_hour print( "Beacon On: {:01}:{:02} {}".format( beaconon_hour, beacon_on_after_sunset.tm_min, "PM" ) ) display.txt_color(WHITE, BLACK) display.txt_size(0) display.txt_set_cursor(20, 170) display.txt_write( "Beacon ON: {:01}:{:02} {} ".format( beaconon_hour, beacon_on_after_sunset.tm_min, "PM" ) ) beaconofftesthour = beacon_off_after_sunset.tm_hour if beaconofftesthour > 12: beaconoff_hour = beaconofftesthour - 12 else: beaconoff_hour = beaconofftesthour if beaconoff_hour == 0: beaconoff_hour = 12 if beacon_off_after_sunset.tm_mday != sunset.tm_mday: beaconoffAMPM = "AM" else: beaconoffAMPM = "PM" print( "Beacon Off: {:01}:{:02} {} ".format( beaconoff_hour, beacon_off_after_sunset.tm_min, beaconoffAMPM ) ) display.txt_set_cursor(180, 170) display.txt_write( "| OFF: {:01}:{:02} {}".format( beaconoff_hour, beacon_off_after_sunset.tm_min, beaconoffAMPM ) ) if (weather_time_in_zone >= lighthouseStartTime) and ( weather_time_in_zone < lighthouseStopTime ): print("Beacon currently: ON") lighthouseNightTime = True else: print("Beacon currently: OFF") lighthouseNightTime = False print("current time: " + str(weather_time_in_zone)) print("current start: " + str(lighthouseStartTime)) print("current stop: " + str(lighthouseStopTime)) # -------------------------------------Updated 5 minute loop time info - END # -------------------------------5 Minute - Update Weather details display.txt_color(BLACK, WHITE) display.txt_size(0) display.txt_set_cursor(361, 241) display.txt_write("Loop:") display.txt_set_cursor(402, 241) display.txt_write(str(weatherLoopCount)) display.txt_color(WHITE, BLACK) display.txt_set_cursor(20, 20) display.txt_write("Temp: %4.1f F" % weather_temp_F) display.txt_set_cursor(135, 20) display.txt_write("Humidity: %d %%" % weather_humid) display.txt_set_cursor(20, 40) display.txt_write("Weather:") display.txt_trans(WHITE) display.txt_set_cursor(97, 41) #Test flags for foghorn and background sounds #weather_description = "drizzle" #End of test flags for foghorn and background sounds display.txt_write(weather_description) display.fill_rect(20, 60, 180, 20, BLACK) display.txt_trans(WHITE) display.txt_set_cursor(20, 60) display.txt_write("Visibility: %d meters" % weather_vis) display.fill_rect(20, 80, 180, 20, BLACK) display.txt_trans(WHITE) display.txt_set_cursor(20, 80) display.txt_write("Wind speed: %4.1f mph" % weather_windspeed) # Foghorn display ----------- if ( ("fog" in weather_description) or ("moderate rain" in weather_description) or ("heavy intensity rain" in weather_description) or (weather_vis < 1000) or foghorncall): display.fill_rect(20, 100, 80, 30, GREEN) time.sleep(0.1) #give system time to relocate to right text display.txt_set_cursor(28, 97) display.txt_size(1) display.txt_trans(BLACK) display.txt_write("HORN") weatherlights_flag = True if soundON: sound_of_sumberg_foghorn.value = False else: display.fill_rect(20, 100, 80, 30, BLACK) display.rect(20, 100, 80, 30, GREEN) sound_of_sumberg_foghorn.value = True weatherlights_flag = False # Foghorn display end ------- # Lighthouse Light display ----------- if lighthouseNightTime or (beaconcall == True): display.fill_rect(119, 100, 80, 30, RED) display.txt_set_cursor(122, 97) time.sleep(0.1) #give system time to relocate to right text display.txt_size(1) display.txt_trans(BLACK) display.txt_write("L") display.txt_set_cursor(132, 97) time.sleep(0.1) #give system time to relocate to right text display.txt_write("IGHT") lighthouse_Motor.value = True lighthouse_Lights.value = True else: display.fill_rect(119, 100, 80, 30, BLACK) display.rect(119, 100, 80, 30, RED) lighthouse_Motor.value = False lighthouse_Lights.value = False # Lighthouse Light display - END ------- ###############################################################Background sound effects if (soundON): if ("heavy intensity rain" in weather_description): sound_of_heavy_rains.value = SOUND_ON sound_of_calm_seas.value = SOUND_OFF sound_of_distant_thunder.value = SOUND_OFF sound_of_heavy_seas_nowind.value = SOUND_OFF sound_of_norway_harbor.value = SOUND_OFF sound_of_windy_rough_shore.value = SOUND_OFF elif ("thunderstorm" in weather_description): sound_of_heavy_rains.value = SOUND_OFF sound_of_calm_seas.value = SOUND_OFF sound_of_distant_thunder.value = SOUND_ON #Hello, Satyajit Ray sound_of_heavy_seas_nowind.value = SOUND_OFF sound_of_norway_harbor.value = SOUND_OFF sound_of_windy_rough_shore.value = SOUND_OFF elif ("drizzle" in weather_description): sound_of_heavy_rains.value = SOUND_OFF sound_of_calm_seas.value = SOUND_OFF sound_of_distant_thunder.value = SOUND_OFF sound_of_heavy_seas_nowind.value = SOUND_ON sound_of_norway_harbor.value = SOUND_OFF sound_of_windy_rough_shore.value = SOUND_OFF elif ("snow" in weather_description): sound_of_heavy_rains.value = SOUND_OFF sound_of_calm_seas.value = SOUND_OFF sound_of_distant_thunder.value = SOUND_OFF sound_of_heavy_seas_nowind.value = SOUND_OFF sound_of_norway_harbor.value = SOUND_ON sound_of_windy_rough_shore.value = SOUND_OFF elif (weather_windspeed > 12): sound_of_heavy_rains.value = SOUND_OFF sound_of_calm_seas.value = SOUND_OFF sound_of_distant_thunder.value = SOUND_OFF sound_of_heavy_seas_nowind.value = SOUND_OFF sound_of_norway_harbor.value = SOUND_OFF sound_of_windy_rough_shore.value = SOUND_ON else: # default to calm seas background sounds sound_of_calm_seas.value = SOUND_ON sound_of_heavy_rains.value = SOUND_OFF sound_of_distant_thunder.value = SOUND_OFF sound_of_heavy_seas_nowind.value = SOUND_OFF sound_of_norway_harbor.value = SOUND_OFF sound_of_windy_rough_shore.value = SOUND_OFF else: # turn off all sounds sound_of_calm_seas.value = SOUND_OFF sound_of_sumberg_foghorn.value = SOUND_OFF sound_of_SBSPTheme.value = SOUND_OFF sound_of_distant_thunder.value = SOUND_OFF sound_of_heavy_rains.value = SOUND_OFF sound_of_heavy_seas_nowind.value = SOUND_OFF sound_of_norway_harbor.value = SOUND_OFF sound_of_seashore_and_foghorn.value = SOUND_OFF sound_of_windy_rough_shore.value = SOUND_OFF # ######################################################Background sound effects - END ------- # Extra lights on/off within the lighthouse - Turn on when foghorn is active, whether sound is on or not if (weatherlights_flag == True): lighthouse_Lights.value = True else: lighthouse_Lights.value = False # Debugging output - sound effect pin outs print("Sound is turned ON if the pin is held 'False' (i.e. 'Low')") print("D0 pin value:sound_of_calm_seas: " + str(sound_of_calm_seas.value)) print("D1 pin value:sound_of_heavy_rains: " + str(sound_of_heavy_rains.value)) print("D2 pin value:sound_of_heavy_seas_nowind: " + str(sound_of_heavy_seas_nowind.value)) print("D3 pin value:sound_of_distant_thunder: " + str(sound_of_distant_thunder.value)) print("D4 pin value:sound_of_norway_harbor: " + str(sound_of_norway_harbor.value)) print("D5 pin value:sound_of_windy_rough_shore: " + str(sound_of_windy_rough_shore.value)) print("D6 pin value:sound_of_seashore_and_foghorn: " + str(sound_of_seashore_and_foghorn.value)) print("D7 pin value:sound_of_sumberg_foghorn: " + str(sound_of_sumberg_foghorn.value)) print("D8 pin value:SpongeBobTheme: " + str(sound_of_SBSPTheme.value)) print("-" * 60) print("D12 pin value:Lighthouse Lights: " + str(lighthouse_Lights.value)) print("D13 pin value:Lighthouse Motor: " + str(lighthouse_Motor.value)) print("-" * 60) print("soundON value is: "+str(soundON)) # -------------------------------End of 5 Minute - Update Weather details # -------------------------------------------------End of Main loop: