adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.09k stars 1.21k forks source link

Crash into HardFault_Handler #5400

Closed axelmagnus closed 2 years ago

axelmagnus commented 3 years ago

CircuitPython version

Well, im doing what im told:
I run CP 7.0.0
all libraries are up to date
the content of my CP (Matrix portal M4)
Found device at /Volumes/CIRCUITPY, running CircuitPython 7.0.0.
adafruit_fakerequests==None
neopixel==None
adafruit_requests==None
adafruit_bitmap_font==1.5.1
adafruit_display_shapes==2.3.0
adafruit_display_text==2.21.0
adafruit_esp32spi==3.5.11
adafruit_io==5.5.0
adafruit_matrixportal==3.0.1
adafruit_portalbase==1.9.2

note that the top three doesnt have a version number from Circup.

Code/REPL

4 files: (im sure there are some fonts missing, it'd be a lot easier if i could send you a zipped file with my entire CP on it :) )

secrets.py: (somewhat censored)

secrets = {
    'ssid': 'xxxxx',
    'password': 'xxxx',
    'latitude'  : 55.600520,
    'longitude' : 13.009430,
    'openweather_token' : '4acdd2457856e1ef6c064f1e928ea71e',
    'aio_username' : 'xxxxx',
    'aio_key' : 'xxxxxx'
}

code.py:
import displayio
import adafruit_display_text.label
from adafruit_bitmap_font import bitmap_font
while True:
    from weather_code import weather_code

weather_code.py:

import gc
import time
import board
from adafruit_matrixportal.network import Network
from adafruit_matrixportal.matrix import Matrix
import openweather_graphics  # pylint: disable=wrong-import-position
from secrets import secrets

UNITS = "metric"  # can pick 'imperial' or 'metric' as part of URL query
LOCATION = "Malmö, SE"
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"]
DATA_LOCATION = []
SCROLL_HOLD_TIME = 0  # set this to hold each line before finishing scroll

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

print("gfx loaded")
gc.collect()
print(gc.mem_free())
localtime_refresh = None
weather_refresh = None
while True:
    gc.collect()
    # only query the online time once per hour (and on first run)
    #print("While start")
    if (not localtime_refresh) or (time.monotonic() - localtime_refresh) > 3600:
        print("Time mem:{}".format(gc.mem_free()))
        try:
            print("Getting time from internet!")
            network.get_local_time()
            localtime_refresh = time.monotonic()
            now=time.localtime()
            print("Time int:" ,now[3],":",now[4] )
            print(now)
            print(localtime_refresh)
        except RuntimeError as e:
            print("Some error occured getting time, retrying! -", e)
            continue

    # only query the weather every 1 minutes (and on first run)
    if (not weather_refresh) or (time.monotonic() - weather_refresh) > 60:
        gfx.temp_text.text="Wait"
        gfx.temp_text.color = 0x90000F #RED
        print("Weather mem:{}".format(gc.mem_free()))
        try:
            value = network.fetch_data(DATA_SOURCE, json_path=DATA_LOCATION,timeout=5)
            #print("Weather Response is", value)
            print("weather fetched")
            gfx.temp_text.color = 0xFFE800 #TEMP_COLOR
            gfx.display_weather(value)
            weather_refresh = time.monotonic()
        except:
            # RuntimeError as e:
            print("Some error occured in fetch, continuing!")
            continue
        gfx.temp_text.color = 0xFFE800 #TEMP_COLOR
    gfx.scroll_next_label()

openweather_graphics.py:

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

TEMP_COLOR = 0xFFE800
MAIN_COLOR = 0x9000FF  # weather condition
DESCRIPTION_COLOR = 0x00D3FF
TIME_COLOR = 0x90000F
HUMIDITY_COLOR = 0x00F3AA
WIND_COLOR = 0xFCCCFC

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(max_size=1)
#         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()
#         (max_size=15)
        self.root_group.append(self)
        self._icon_group = displayio.Group()
        #(max_size=1)
        self.append(self._icon_group)
        self._text_group = displayio.Group()
        #(max_size=5)
        self.append(self._text_group)
        self._scrolling_group = displayio.Group()
        #(max_size=1)
        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 = []
        gc.collect()
        print("opwe mem {}".format(gc.mem_free()))
        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.timestamp = Label(self.small_font)
        self.timestamp.color = TIME_COLOR
        self._scrolling_texts.append(self.timestamp)

        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.maxtemp_text = Label(self.small_font)
        self.maxtemp_text.color = TEMP_COLOR  #
        self._scrolling_texts.append(self.maxtemp_text)

        self.mintemp_text = Label(self.small_font)
        self.mintemp_text.color = TEMP_COLOR  #
        self._scrolling_texts.append(self.mintemp_text)

        self.press_text = Label(self.small_font)
        self.press_text.color = DESCRIPTION_COLOR  #
        self._scrolling_texts.append(self.press_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 = TIME_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

        maxtemp = weather["main"]["temp_max"]
 #       print(maxtemp)
        self.maxtemp_text.text = "Max Temp: %d  C" % maxtemp

        mintemp = weather["main"]["temp_min"]
 #       print(mintemp)
        self.mintemp_text.text = "Min Temp: %d  C" % mintemp

        press = weather["main"]["pressure"]
 #       print(press)
        self.press_text.text = "Pressure: %d hPa" % press

        now=time.localtime()
 #       print(now)
 #       print("Timexy:" ,now[3],":",now[4] )
        if(now[4]<10):
            self.timestamp.text = '{}'.format(now[3])+":0"+'{}'.format(now[4])
        else:
            self.timestamp.text = '{}'.format(now[3])+":"+'{}'.format(now[4])
 #       print(self.timestamp.text)

        wind = weather["wind"]["speed"]
 #       print(wind)
        if self.meters_speed:
            self.wind_text.text = "Wind: %d m/s" % wind
        else:
            self.wind_text.text = "%d mph" % wind

        self.display.show(self.root_group)

    def loaddisplay(self, weather):
        self.temp_text.text="Updating"
        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):
        #update time
        now=time.localtime()
        if(now[4]<10):
            self.timestamp.text = '{}'.format(now[3])+":0"+'{}'.format(now[4])
        else:
            self.timestamp.text = '{}'.format(now[3])+":"+'{}'.format(now[4])
        #print(self.timestamp.text)

        # 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 = 24

        # 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

Behavior

Well, infortunately my output from Mu editor is in swedish, but im sure you can translate this standard message. (it goes in to safe mode, and oops, crash in to HardFault_Handler)

Autoladdning är avstängd. Kör i säkert läge! Sparad kod körs inte.

Du är i felsäkert läge eftersom: CircuitPython kärnkod kraschade hårt. Hoppsan! Krasch in i HardFault_Handler. Vänligen skapa ett ärende med innehållet i din CIRCUITPY-enhet på https://github.com/adafruit/circuitpython/issues

Tryck på valfri tangent för att gå in i REPL. Använd CTRL-D för att ladda om.

Description

No response

Additional information

No response

DavePutz commented 2 years ago

Tested this with CircuitPython 7.2.0-alpha-1 and (after adding the necessary libs like adafruit_portalbase) everything ran successfully. I changed the frequency to every five minutes and let it run for 1 hour with no issues. @axelmagnus - can you retest with the latest build?

mpicker90 commented 2 years ago

I'm experiencing the same issue calling the OpenWeather API and others running on 7.2.3. reducing the number of API calls makes it run longer on mine but it still ends up crashing eventually. When my Matrix Portal crashes I'm no longer able to set up a serial connection to the device.

dhalbert commented 2 years ago

Could you all retest again with 7.3.1 or later? We did fix a MatrixPortal-related bug that might have caused this.

DavePutz commented 2 years ago

Tested this for over an hour on a matrixportal_m4 using CP 8.0.0-alpha.1 with 8.0 libraries and everything worked properly.

dhalbert commented 2 years ago

@DavePutz - Thanks! I will close this as fixed.