code.py
  1  # SPDX-FileCopyrightText: 2020 Limor Fried for Adafruit Industries
  2  #
  3  # SPDX-License-Identifier: MIT
  4  
  5  import time
  6  import random
  7  import audioio
  8  import audiocore
  9  import board
 10  import busio
 11  from digitalio import DigitalInOut
 12  import digitalio
 13  import neopixel
 14  from adafruit_esp32spi import adafruit_esp32spi
 15  from adafruit_esp32spi import adafruit_esp32spi_wifimanager
 16  import adafruit_fancyled.adafruit_fancyled as fancy
 17  
 18  print("ESP32 Open Weather API demo")
 19  
 20  button = digitalio.DigitalInOut(board.A1)
 21  button.switch_to_input(pull=digitalio.Pull.UP)
 22  
 23  wave_file = open("sound/Rain.wav", "rb")
 24  wave = audiocore.WaveFile(wave_file)
 25  audio = audioio.AudioOut(board.A0)
 26  
 27  
 28  # Get wifi details and more from a secrets.py file
 29  try:
 30      from secrets import secrets
 31  except ImportError:
 32      print("WiFi secrets are kept in secrets.py, please add them there!")
 33      raise
 34  
 35  # Use cityname, country code where countrycode is ISO3166 format.
 36  # E.g. "New York, US" or "London, GB"
 37  LOCATION = secrets['timezone']
 38  
 39  # Set up where we'll be fetching data from
 40  DATA_SOURCE = "http://api.openweathermap.org/data/2.5/weather?q="+secrets['timezone']
 41  DATA_SOURCE += "&appid="+secrets['openweather_token']
 42  
 43  # If you are using a board with pre-defined ESP32 Pins:
 44  esp32_cs = DigitalInOut(board.ESP_CS)
 45  esp32_ready = DigitalInOut(board.ESP_BUSY)
 46  esp32_reset = DigitalInOut(board.ESP_RESET)
 47  
 48  spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
 49  esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)
 50  status_light = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)  # Uncomment  for Most Boards
 51  wifi = adafruit_esp32spi_wifimanager.ESPSPI_WiFiManager(esp, secrets, status_light)
 52  pixels = neopixel.NeoPixel(board.D2, 150, brightness=1.0, auto_write=False)
 53  pixels.fill(0x050505)
 54  pixels.show()
 55  
 56  # clouds palette
 57  cloudy_palette = [fancy.CRGB(1.0, 1.0, 1.0),  # White
 58                    fancy.CRGB(0.5, 0.5, 0.5),  # gray
 59                    fancy.CRGB(0.5, 0.5, 1.0)]  # blue-gray
 60  # sunny palette
 61  sunny_palette = [fancy.CRGB(1.0, 1.0, 1.0),  # White
 62                   fancy.CRGB(1.0, 1.0, 0.0),  # Yellow
 63                   fancy.CRGB(1.0, 0.5, 0.0), ]  # Orange
 64  # thunderstorm palette
 65  thunder_palette = [fancy.CRGB(0.0, 0.0, 1.0),  # blue
 66                     fancy.CRGB(0.5, 0.5, 0.5),  # gray
 67                     fancy.CRGB(0.5, 0.5, 1.0)]  # blue-gray
 68  last_thunder_bolt = None
 69  
 70  palette = None  # current palette
 71  pal_offset = 0  # Positional offset into color palette to get it to 'spin'
 72  levels = (0.25, 0.3, 0.15)  # Color balance / brightness for gamma function
 73  raining = False
 74  snowing = False
 75  thundering = False
 76  has_sound = False
 77  
 78  weather_refresh = None
 79  weather_type = None
 80  button_mode = 4
 81  button_select = False
 82  
 83  cloud_on = True
 84  
 85  while True:
 86      if not button.value:
 87          button_mode = button_mode + 1
 88          print("Button Pressed")
 89          if button_mode > 4:
 90              button_mode = 0
 91          print("Mode is:", button_mode)
 92          pressed_time = time.monotonic()
 93          button_select = True
 94          weather_refresh = None
 95          while not button.value:  # Debounce
 96              audio.stop()
 97              if (time.monotonic() - pressed_time) > 4:
 98                  print("Turning OFF")
 99                  cloud_on = False
100                  pixels.fill(0x000000)  # bright white!
101                  pixels.show()
102                  while not cloud_on:
103                      while not button.value:  # Debounce
104                          pass
105                      if not button.value:
106                          pressed_time = time.monotonic()
107                          print("Button Pressed")
108                          cloud_on = True
109                          button_select = False
110                          weather_refresh = None
111  
112              if button_mode == 0:
113                  weather_type = 'Sunny'
114              if button_mode == 1:
115                  weather_type = 'Clouds'
116              if button_mode == 2:
117                  weather_type = 'Rain'
118              if button_mode == 3:
119                  weather_type = 'Thunderstorm'
120              if button_mode == 4:
121                  weather_type = 'Snow'
122  
123      # only query the weather every 10 minutes (and on first run)
124      if (not weather_refresh) or (time.monotonic() - weather_refresh) > 600:
125          try:
126              if not button_select:
127                  response = wifi.get(DATA_SOURCE).json()
128                  print("Response is", response)
129                  weather_type = response['weather'][0]['main']
130                  if weather_type == 'Clear':
131                      weather_type = 'Sunny'
132                  print(weather_type)  # See https://openweathermap.org/weather-conditions
133              # default to no rain or thunder
134              raining = snowing = thundering = has_sound = False
135              if weather_type == 'Sunny':
136                  palette = sunny_palette
137                  wave_file = open("sound/Clear.wav", "rb")
138                  wave = audiocore.WaveFile(wave_file)
139                  has_sound = True
140              if weather_type == 'Clouds':
141                  palette = cloudy_palette
142                  wave_file = open("sound/Clouds.wav", "rb")
143                  wave = audiocore.WaveFile(wave_file)
144                  has_sound = True
145              if weather_type == 'Rain':
146                  palette = cloudy_palette
147                  wave_file = open("sound/Rain.wav", "rb")
148                  wave = audiocore.WaveFile(wave_file)
149                  raining = True
150                  has_sound = True
151              if weather_type == 'Thunderstorm':
152                  palette = thunder_palette
153                  raining = thundering = True
154                  has_sound = True
155                  # pick next thunderbolt time now
156                  next_bolt_time = time.monotonic() + random.randint(1, 5)
157              if weather_type == 'Snow':
158                  palette = cloudy_palette
159                  wave_file = open("sound/Snow.wav", "rb")
160                  wave = audiocore.WaveFile(wave_file)
161                  snowing = True
162                  has_sound = True
163              weather_refresh = time.monotonic()
164          except RuntimeError as e:
165              print("Some error occured, retrying! -", e)
166              time.sleep(5)
167              continue
168  
169      if not audio.playing and has_sound:
170          if not thundering:
171              audio.play(wave)
172  
173      if palette:
174          for i in range(len(pixels)):
175              color = fancy.palette_lookup(palette, pal_offset + i / len(pixels))
176              color = fancy.gamma_adjust(color, brightness=levels)
177              pixels[i] = color.pack()
178          pixels.show()
179          pal_offset += 0.01  # Bigger number = faster spin
180  
181      if raining:
182          # don't have a droplet every time
183          for i in range(random.randint(1, 5)):  # up to 3 times...
184              pixels[random.randint(0, len(pixels)-1)] = 0x0000FF  # make a random pixel Blue
185          pixels.show()
186  
187      if snowing:
188          # don't have a droplet every time
189          for i in range(random.randint(1, 5)):  # up to 3 times...
190              pixels[random.randint(0, len(pixels)-1)] = 0xFFFFFF  # make a random pixel white
191          pixels.show()
192  
193      # if its time for a thunderbolt
194      if thundering and (time.monotonic() > next_bolt_time):
195          print("Ka Bam!")
196          # fill pixels white, delay, a few times
197          for i in range(random.randint(1, 3)):  # up to 3 times...
198              pixels.fill(0xFFFFFF)  # bright white!
199              pixels.show()
200              time.sleep(random.uniform(0.01, 0.2))  # pause
201              pixels.fill(0x0F0F0F)  # gray
202              pixels.show()
203              time.sleep(random.uniform(0.01, 0.3))  # pause
204          # pick next thunderbolt time now
205          Thunder = random.randint(0, 2)
206          if Thunder == 0:
207              wave_file = open("sound/Thunderstorm0.wav", "rb")
208          elif Thunder == 1:
209              wave_file = open("sound/Thunderstorm1.wav", "rb")
210          elif Thunder == 2:
211              wave_file = open("sound/Thunderstorm2.wav", "rb")
212          wave = audiocore.WaveFile(wave_file)
213          audio.play(wave)
214          next_bolt_time = time.monotonic() + random.randint(5, 15)  # between 5 and 15 s