code.py
  1  # SPDX-FileCopyrightText: 2019 Limor Fried for Adafruit Industries
  2  #
  3  # SPDX-License-Identifier: MIT
  4  
  5  """
  6  This example will access an API, grab a number like hackaday skulls, github
  7  stars, price of bitcoin, twitter followers... if you can find something that
  8  spits out JSON data, we can display it!
  9  """
 10  import gc
 11  import time
 12  import board
 13  import busio
 14  from digitalio import DigitalInOut, Direction
 15  from Adafruit_CircuitPython_ESP_ATcontrol import adafruit_espatcontrol
 16  from adafruit_ht16k33 import segments
 17  import neopixel
 18  import ujson
 19  
 20  # Get wifi details and more from a settings.py file
 21  try:
 22      from settings import settings
 23  except ImportError:
 24      print("WiFi settings are kept in settings.py, please add them there!")
 25      raise
 26  
 27  #              CONFIGURATION
 28  PLAY_SOUND_ON_CHANGE = True
 29  NEOPIXELS_ON_CHANGE = False
 30  TIME_BETWEEN_QUERY = 0  # in seconds
 31  
 32  # Some data sources and JSON locations to try out
 33  
 34  # Github stars! You can query 1ce a minute without an API key token
 35  DATA_SOURCE = "https://api.github.com/repos/adafruit/circuitpython"
 36  if 'github_token' in settings:
 37      DATA_SOURCE += "?access_token="+settings['github_token']
 38  DATA_LOCATION = ["stargazers_count"]
 39  
 40  
 41  
 42  uart = busio.UART(board.TX, board.RX, timeout=0.1)
 43  resetpin = DigitalInOut(board.D5)
 44  rtspin = DigitalInOut(board.D9)
 45  
 46  # Create the connection to the co-processor and reset
 47  esp = adafruit_espatcontrol.ESP_ATcontrol(uart, 115200, run_baudrate=115200,
 48                                            reset_pin=resetpin,
 49                                            rts_pin=rtspin, debug=True)
 50  esp.hard_reset()
 51  
 52  # Create the I2C interface.
 53  i2c = busio.I2C(board.SCL, board.SDA)
 54  # Attach a 7 segment display and display -'s so we know its not live yet
 55  display = segments.Seg7x4(i2c)
 56  display.print('----')
 57  
 58  powerpin = DigitalInOut(board.D10)
 59  powerpin.direction = Direction.OUTPUT
 60  powerpin.value = True
 61  starpin = DigitalInOut(board.D11)
 62  starpin.direction = Direction.OUTPUT
 63  starpin.value = False
 64  
 65  status = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2)
 66  
 67  # neopixels
 68  if NEOPIXELS_ON_CHANGE:
 69      pixels = neopixel.NeoPixel(board.A1, 16, brightness=0.4, pixel_order=(1, 0, 2, 3))
 70      pixels.fill(0)
 71  
 72  # music!
 73  if PLAY_SOUND_ON_CHANGE:
 74      import audioio
 75      import audiocore
 76      wave_file = open("coin.wav", "rb")
 77      wave = audiocore.WaveFile(wave_file)
 78  
 79  # we'll save the value in question
 80  last_value = value = 0
 81  the_time = None
 82  times = 0
 83  
 84  def chime_light():
 85      """Light up LEDs and play a tune"""
 86      if NEOPIXELS_ON_CHANGE:
 87          for i in range(0, 100, 10):
 88              pixels.fill((i, i, i))
 89      starpin.value = True
 90  
 91      if PLAY_SOUND_ON_CHANGE:
 92          with audioio.AudioOut(board.A0) as audio:
 93              audio.play(wave)
 94              while audio.playing:
 95                  pass
 96      starpin.value = False
 97  
 98      if NEOPIXELS_ON_CHANGE:
 99          for i in range(100, 0, -10):
100              pixels.fill((i, i, i))
101          pixels.fill(0)
102  
103  def get_value(response, location):
104      """Extract a value from a json object, based on the path in 'location'"""
105      try:
106          print("Parsing JSON response...", end='')
107          json = ujson.loads(response)
108          print("parsed OK!")
109          for x in location:
110              json = json[x]
111          return json
112      except ValueError:
113          print("Failed to parse json, retrying")
114          return None
115  
116  while True:
117      try:
118          status[0] = (0,0,100)
119          while not esp.is_connected:
120              # settings dictionary must contain 'ssid' and 'password' at a minimum
121              status[0] = (100, 0, 0) # red = not connected
122              esp.connect(settings)
123          # great, lets get the data
124          # get the time
125          #the_time = esp.sntp_time
126  
127          print("Retrieving data source...", end='')
128          status[0] = (100, 100, 0)   # yellow = fetching data
129          header, body = esp.request_url(DATA_SOURCE)
130          status[0] = (0, 0, 100)   # green = got data
131          print("Reply is OK!")
132      except (RuntimeError, adafruit_espatcontrol.OKError) as e:
133          print("Failed to get data, retrying\n", e)
134          continue
135      #print('-'*40, "Size: ", len(body))
136      #print(str(body, 'utf-8'))
137      #print('-'*40)
138      value = get_value(body, DATA_LOCATION)
139      if not value:
140          continue
141      print(times, the_time, "value:", value)
142      display.print(int(value))
143      status[0] = (0, 0, 0)  # off = ready for next
144      if last_value < value:
145          chime_light() # animate the neopixels
146          last_value = value
147      times += 1
148      # normally we wouldn't have to do this, but we get bad fragments
149      header = body = None
150      gc.collect()
151      print(gc.mem_free())  # pylint: disable=no-member
152      time.sleep(TIME_BETWEEN_QUERY)