adafruit / Adafruit_CircuitPython_Requests

Requests-like interface for web interfacing
MIT License
51 stars 36 forks source link

OutOfRetries: Repeated socket failures #84

Closed pjockey closed 2 years ago

pjockey commented 2 years ago

Running a code for weather(https://learn.adafruit.com/weather-display-matrix) with the 64x32 LED board and and I keep getting the following error:

Retrieving data...Traceback (most recent call last):
  File "code.py", line 93, in <module>
  File "adafruit_portalbase/network.py", line 560, in fetch_data
  File "adafruit_portalbase/network.py", line 479, in fetch
  File "adafruit_requests.py", line 698, in get
  File "adafruit_requests.py", line 580, in request
OutOfRetries: Repeated socket failures

Code done running.

I'm running CP 7.0.0 with the libraries from 210928 and I had this same problem with CP 6.3.0.

Any ideas?

anecdata commented 2 years ago

Does it happen every time, or intermittently? If you catch the error and retry, does it continue to occur? Are you doing any other network activity outside of the MatrixPortal class?

pjockey commented 2 years ago

OK the script was start yesterday about 4:30 PM MDT and has stopped about 1:55 PM MDT. The same error showed up. Ctrl+D does nothing to get the script going again. The unit needs to be powered off and then on again. I have the device placed on a static IP address to try and keep it somewhat stable on the network.

I had the router/modem changed a couple of weeks ago and these drops do seem more frequent. The ISP person set up everything which contains a Cisco AP and 2 WiFi Boost. The system is on Fibre 150GB download and I'm a little frustrated with the drops. I have around 50+ items on the network, a 12 dozen or so wired and the rest on WiFi.

pjockey commented 2 years ago
# Matrix Weather display
# For Metro M4 Airlift with RGB Matrix shield, 64 x 32 RGB LED Matrix display

"""
This example queries the Open Weather Maps site API to find out the current
weather for your location... and display it on a screen!
if you can find something that spits out JSON data, we can display it
"""
import time
import board
import microcontroller
from digitalio import DigitalInOut, Direction, Pull
from adafruit_matrixportal.network import Network
from adafruit_matrixportal.matrix import Matrix
import openweather_graphics  # pylint: disable=wrong-import-position

# Get wifi details and more from a secrets.py file
try:
    from secrets import secrets
except ImportError:
    print("WiFi secrets are kept in secrets.py, please add them there!")
    raise

if hasattr(board, "D12"):
    jumper = DigitalInOut(board.D12)
    jumper.direction = Direction.INPUT
    jumper.pull = Pull.UP
    is_metric = jumper.value
elif hasattr(board, "BUTTON_DOWN") and hasattr(board, "BUTTON_UP"):
    button_down = DigitalInOut(board.BUTTON_DOWN)
    button_down.switch_to_input(pull=Pull.UP)

    button_up = DigitalInOut(board.BUTTON_UP)
    button_up.switch_to_input(pull=Pull.UP)
    if not button_down.value:
        print("Down Button Pressed")
        microcontroller.nvm[0] = 1
    elif not button_up.value:
        print("Up Button Pressed")
        microcontroller.nvm[0] = 0
    print(microcontroller.nvm[0])
    is_metric = microcontroller.nvm[0]
else:
    is_metric = true

if is_metric:
    UNITS = "metric"  # can pick 'imperial' or 'metric' as part of URL query
    print("Jumper set to metric")
else:
    UNITS = "imperial"
    print("Jumper set to imperial")

# Use cityname, country code where countrycode is ISO3166 format.
# E.g. "New York, US" or "London, GB"
LOCATION = "Cranbrook, CA"
print("Getting weather for {}".format(LOCATION))
# Set up from where we'll be fetching data
DATA_SOURCE = (
    "http://api.openweathermap.org/data/2.5/weather?q=" + LOCATION + "&units=" + UNITS
)
DATA_SOURCE += "&appid=" + secrets["openweather_token"]
# You'll need to get a token from openweather.org, looks like 'b6907d289e10d714a6e88b30761fae22'
# it goes in your secrets.py file on a line such as:
# 'openweather_token' : 'your_big_humongous_gigantor_token',
DATA_LOCATION = []
SCROLL_HOLD_TIME = 0  # set this to hold each line before finishing scroll

# --- Display setup ---
matrix = Matrix()
network = Network(status_neopixel=board.NEOPIXEL, debug=True)
if UNITS in ("imperial", "metric"):
    gfx = openweather_graphics.OpenWeather_Graphics(
        matrix.display, am_pm=False, units=UNITS
    )

print("gfx loaded")
localtime_refresh = None
weather_refresh = None
while True:
    # only query the online time once per hour (and on first run)
    if (not localtime_refresh) or (time.monotonic() - localtime_refresh) > 3600:
        try:
            print("Getting time from internet!")
            network.get_local_time()
            localtime_refresh = time.monotonic()
        except RuntimeError as e:
            print("Some error occured, retrying! -", e)
            continue

    # only query the weather every 10 minutes (and on first run)
    if (not weather_refresh) or (time.monotonic() - weather_refresh) > 900:
        try:
            value = network.fetch_data(DATA_SOURCE, json_path=(DATA_LOCATION,))
            print("Response is", value)
            gfx.display_weather(value)
            weather_refresh = time.monotonic()
        except RuntimeError as e:
            print("Some error occured, retrying! -", e)
            continue

    gfx.scroll_next_label()
    # Pause between labels
    time.sleep(SCROLL_HOLD_TIME)

Code.py

import time
import displayio
from adafruit_display_text.label import Label
from adafruit_bitmap_font import bitmap_font

TEMP_COLOR = 0xFFA800
MAIN_COLOR = 0x9000FF  # weather condition
DESCRIPTION_COLOR = 0x00D3FF
CITY_COLOR = 0x9000FF
HUMIDITY_COLOR = 0x0000AA
WIND_COLOR = 0xCCCCCC

cwd = ("/" + __file__).rsplit("/", 1)[
    0
]  # the current working directory (where this file is)

small_font = cwd + "/fonts/Arial-12.bdf"
medium_font = cwd + "/fonts/Arial-14.bdf"

icon_spritesheet = cwd + "/weather-icons.bmp"
icon_width = 16
icon_height = 16
scrolling_text_height = 24
scroll_delay = 0.03

class OpenWeather_Graphics(displayio.Group):
    def __init__(
            self,
            display,
            *,
            am_pm=True,
            units="imperial"
    ):
        super().__init__()
        self.am_pm = am_pm
        if units == "metric":
            self.celsius = True
            self.meters_speed = True
        else:
            self.celsius = False
            self.meters_speed = False
        self.display = display

        splash = displayio.Group()
        background = displayio.OnDiskBitmap(open("loading.bmp", "rb"))
        bg_sprite = displayio.TileGrid(
            background,
            pixel_shader=displayio.ColorConverter(),
        )
        splash.append(bg_sprite)
        display.show(splash)

        self.root_group = displayio.Group()
        self.root_group.append(self)
        self._icon_group = displayio.Group()
        self.append(self._icon_group)
        self._text_group = displayio.Group()
        self.append(self._text_group)
        self._scrolling_group = displayio.Group()
        self.append(self._scrolling_group)

        # The label index we're currently scrolling
        self._current_label = None

        # Load the icon sprite sheet
        icons = displayio.OnDiskBitmap(open(icon_spritesheet, "rb"))
        self._icon_sprite = displayio.TileGrid(
            icons,
            pixel_shader=displayio.ColorConverter(),
            width=1,
            height=1,
            tile_width=icon_width,
            tile_height=icon_height,
            default_tile=0,
            x=0,
            y=0,
        )
        self.set_icon(None)
        self._scrolling_texts = []

        self.small_font = bitmap_font.load_font(small_font)
        self.medium_font = bitmap_font.load_font(medium_font)
        glyphs = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-,.: "
        self.small_font.load_glyphs(glyphs)
        self.medium_font.load_glyphs(glyphs)
        self.medium_font.load_glyphs(("°",))  # a non-ascii character we need for sure

        self.city_text = None

        self.temp_text = Label(self.medium_font)
        self.temp_text.x = 20
        self.temp_text.y = 7
        self.temp_text.color = TEMP_COLOR
        self._text_group.append(self.temp_text)

        self.description_text = Label(self.small_font)
        self.description_text.color = DESCRIPTION_COLOR
        self._scrolling_texts.append(self.description_text)

        self.humidity_text = Label(self.small_font)
        self.humidity_text.color = HUMIDITY_COLOR  #
        self._scrolling_texts.append(self.humidity_text)

        self.wind_text = Label(self.small_font)
        self.wind_text.color = WIND_COLOR
        self._scrolling_texts.append(self.wind_text)

    def display_weather(self, weather):
        # set the icon
        self.set_icon(weather["weather"][0]["icon"])

        city_name = weather["name"] + ", " + weather["sys"]["country"]
        print(city_name)
        if not self.city_text:
            self.city_text = Label(self.small_font, text=city_name)
            self.city_text.color = CITY_COLOR
            self._scrolling_texts.append(self.city_text)

        temperature = weather["main"]["temp"]
        print(temperature)
        if self.celsius:
            self.temp_text.text = "%d°C" % temperature
        else:
            self.temp_text.text = "%d°F" % temperature

        description = weather["weather"][0]["description"]
        description = description[0].upper() + description[1:]
        print(description)
        self.description_text.text = description
        # "thunderstorm with heavy drizzle"

        humidity = weather["main"]["humidity"]
        print(humidity)
        self.humidity_text.text = "%d%% humidity" % humidity

        wind = weather["wind"]["speed"]
        print(wind)
        if self.meters_speed:
            self.wind_text.text = "%d kph" % wind
        else:
            self.wind_text.text = "%d mph" % wind

        self.display.show(self.root_group)

    def set_icon(self, icon_name):
        """Use icon_name to get the position of the sprite and update
        the current icon.

        :param icon_name: The icon name returned by openweathermap

        Format is always 2 numbers followed by 'd' or 'n' as the 3rd character
        """

        icon_map = ("01", "02", "03", "04", "09", "10", "11", "13", "50")

        print("Set icon to", icon_name)
        if self._icon_group:
            self._icon_group.pop()
        if icon_name is not None:
            row = None
            for index, icon in enumerate(icon_map):
                if icon == icon_name[0:2]:
                    row = index
                    break
            column = 0
            if icon_name[2] == "n":
                column = 1
            if row is not None:
                self._icon_sprite[0] = (row * 2) + column
                self._icon_group.append(self._icon_sprite)

    def scroll_next_label(self):
        # Start by scrolling current label off if not set to None
        if self._current_label is not None and self._scrolling_group:
            current_text = self._scrolling_texts[self._current_label]
            text_width = current_text.bounding_box[2]
            for _ in range(text_width + 1):
                self._scrolling_group.x = self._scrolling_group.x - 1
                time.sleep(scroll_delay)

        if self._current_label is not None:
            self._current_label += 1
        if self._current_label is None or self._current_label >= len(
                self._scrolling_texts
        ):
            self._current_label = 0

        # Setup the scrolling group by removing any existing
        if self._scrolling_group:
            self._scrolling_group.pop()
        # Then add the current label
        current_text = self._scrolling_texts[self._current_label]
        self._scrolling_group.append(current_text)

        # Set the position of the group to just off screen and centered vertically for lower half
        self._scrolling_group.x = self.display.width
        self._scrolling_group.y = 23

        # Run a loop until the label is offscreen again and leave function
        for _ in range(self.display.width):
            self._scrolling_group.x = self._scrolling_group.x - 1
            time.sleep(scroll_delay)
        # By blocking other code we will never leave the label half way scrolled

openweather_graphics.py

pjockey commented 2 years ago

These persist even with the newest Libraries.

anecdata commented 2 years ago

I do get these occasionally too, but catching and retrying usually works on the second time. You can also play with timeout. Generally, my escalation for network issues (because there are many potential causes, internal and external) is:

askpatrickw commented 2 years ago

I'm going to close this. It is the same issue that we're tracking and working on in #62