[{"element_type":"text","content":"\n        \u003cp\u003eThere might come a time when you want a project running 24/7 permanently online.\u0026nbsp; Weather and Real Time Clock (RTC) updates are common uses.\u0026nbsp; It's not enough to code a request to an API, you must know how to avoid every possible scenario that might crash your script. That's where error exception handlers come in.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eLearning how to use exception handlers is the most important aspect of an always online script.\u0026nbsp;\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eIf you're a beginner please read \u003ca href=\"https://learn.adafruit.com/networking-in-circuitpython\" target=\"_blank\"\u003eNetworking in Circuit Python\u003c/a\u003e then \u003ca href=\"https://adafruit-playground.com/u/DJDevon3/pages/web-api-s-you\" target=\"_blank\"\u003eWeb API's \u0026amp; You\u003c/a\u003e first. 24/7 Online Requests is a follow up guide for intermediate level Circuit Python coders. Some parts of this guide infer you already know the basics for getting your board online \u0026amp; interacting with Web API's.\u003c/p\u003e\n      ","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/097/original/Crashing_is_bad_mmkay.jpg?1723444931","metadata":{"caption":"Example of a script crashing on a TFT display (that I wrote) due to inadequate error handling. "}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eIn Circuit Python error handlers are based on a try/except paradigm. You'll often hear programmers refer to it as try/throw/catch for catching errors but that's referencing other languages such as C++ or Java. That terminology is fine to use when discussing help with errors. Just know in Circuit Python the syntax is try/except for the purposes of online API scripts.\u003c/p\u003e\n\u003ch2\u003eBasic WiFi Setup\u003c/h2\u003e\n\u003cp\u003eLet's say we want to connect an Adafruit ESP32-S3 Feather to WiFi.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"import os\nimport adafruit_connection_manager\nimport wifi\nimport adafruit_requests\n\n# Initalize Wifi, Socket Pool, Request Session\npool = adafruit_connection_manager.get_radio_socketpool(wifi.radio)\nSSL_CONTEXT = adafruit_connection_manager.get_radio_ssl_context(wifi.radio)\n# adafruit_requests.Session keep outside the main loop\n# otherwise you get Out of Socket errors.\nrequests = adafruit_requests.Session(pool, SSL_CONTEXT)\n\n# Use settings.toml for credentials\nssid = os.getenv(\"CIRCUITPY_WIFI_SSID\")\npassword = os.getenv(\"CIRCUITPY_WIFI_PASSWORD\")\n\n# Connect to WiFi\nwifi.radio.connect(ssid, password)","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eSettings.toml Credentials\u003c/h2\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"# Ensure your settings.toml has the following\n\nCIRCUITPY_WIFI_SSID = \"YourSSID\"\nCIRCUITPY_WIFI_PASSWORD = \"YourPassword\"","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eThe above code is as basic as it gets for attempting to connect to WiFi but what happens if your WiFi access point is down or the credentials are wrong?\u0026nbsp; Your script will cease to function and will crash with an error to a console.\u0026nbsp; The error could be any number of possible errors such as out of sockets, out of retries, failure to get host, and so on.\u003c/p\u003e\n\u003ch2\u003eBasics of Try/Except\u003c/h2\u003e\n\u003cp\u003eBy wrapping the WiFi connection request in a try/except, instead of crashing to REPL, it opens some important options which include (but not limited to):\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eprint the error to serial console\u003c/li\u003e\n\u003cli\u003eignore the error completely\u003c/li\u003e\n\u003cli\u003eretry from where try was initiated\u003c/li\u003e\n\u003cli\u003ereload code.py\u003c/li\u003e\n\u003cli\u003erestart the microcontroller\u003c/li\u003e\n\u003c/ul\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"try:\n\twifi.radio.connect(ssid, password)\nexcept ConnectionError as e:\n\tprint(f\"❌ Connection Error: {e}\")","metadata":{"language":"auto","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003ewifi.radio.connected\u003c/h2\u003e\n\u003cp\u003ewifi.radio.connected is a function in the wifi library that knows if it is connected to a WiFi access point or not.\u0026nbsp; It will return a boolean either True or False.\u0026nbsp; We can use this \"status\" to know if we should attempt to reconnect or not anytime in the script.\u003c/p\u003e\n\u003cp\u003eBy moving the WiFi connection attempt into the while True loop you can force it to continually check if it's connected... infinitely. The script will only continue past an exception if there is no error and a successful WiFi connection is made. In this example if you see the message\u003c/p\u003e\n\u003cp\u003e📡 Wifi!\u003c/p\u003e\n\u003cp\u003ethen the connection was successful and the script will continue.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"alert","content":"\u003cp\u003eEmoji support is built into the Mu code editor and Mu REPL/Console. Emoji's are an excellent way to make certain print statements really stand out.  Unfortunately not all code editors or terminal programs have emoji support such as PyCharm, VSCode, Sublime, Nano, TIO, etc..\u003c/p\u003e","metadata":{"class":"element row-fluid alert-element build-alert alert-primary","markdown":"Emoji support is built into the Mu code editor and Mu REPL/Console. Emoji's are an excellent way to make certain print statements really stand out.  Unfortunately not all code editors or terminal programs have emoji support such as PyCharm, VSCode, Sublime, Nano, TIO, etc..","alert_type":"primary"}},{"element_type":"code","content":"while True:\n\twhile not wifi.radio.connected:\n\t\ttry:\n\t\t\twifi.radio.connect(ssid, password)\n\t\texcept ConnectionError as e:\n\t\t\tprint(f\"❌ Connection Error: {e}\")\n    print(\"📡 Wifi!\")","metadata":{"language":"python","linenums":false}},{"element_type":"alert","content":"\u003cp\u003ePython \u0026amp; Circuit Python now encourages the use of f-strings instead of the older .format method. You can learn more about f-string formatting here: \u003ca href=\"https://docs.python.org/3/tutorial/inputoutput.html\"\u003ehttps://docs.python.org/3/tutorial/inputoutput.html\u003c/a\u003e\ne is a shorthand variable name for error. It's not an official variable simply one that is commonly used in Circuit Python scripts. You can name e whatever you want.\u003c/p\u003e","metadata":{"class":"element row-fluid alert-element build-alert alert-info","markdown":"Python \u0026 Circuit Python now encourages the use of f-strings instead of the older .format method. You can learn more about f-string formatting here: https://docs.python.org/3/tutorial/inputoutput.html\ne is a shorthand variable name for error. It's not an official variable simply one that is commonly used in Circuit Python scripts. You can name e whatever you want.","alert_type":"info"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eException Handling Types\u003c/h2\u003e\n\u003cp\u003eThere are many different error types. Sometimes they can be unique to a specific library. There are global error handlers built into Circuit Python such as:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eConnectionError\u003c/li\u003e\n\u003cli\u003eKeyError\u003c/li\u003e\n\u003cli\u003eOSError\u003c/li\u003e\n\u003cli\u003eRuntimeError\u003c/li\u003e\n\u003cli\u003eValueError\u003c/li\u003e\n\u003cli\u003eand many more...\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThe full list of \u003ca href=\"https://docs.circuitpython.org/en/latest/docs/library/builtins.html#exceptions\" target=\"_blank\"\u003eCircuit Python Exceptions are documented here\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eIt's perfectly fine to use multiple error handlers at the same time. For example:\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"while True:\n\twhile not wifi.radio.connected:\n\t\ttry:\n\t\t\twifi.radio.connect(ssid, password)\n\t\texcept (OSError, RuntimeError, ValueError, ConnectionError) as e:\n\t\t\tprint(f\"❌ Error: {e}\")\n    print(\"📡 Wifi!\")","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n        \u003cp\u003eUsing every exception handler is a shotgun approach. The downside is it will not print the line the error spawned from or which error handler it came from. It will only print the error message. Sometimes that's a good thing, sometimes that's a bad thing.\u003c/p\u003e\n\u003ch2\u003eMultiple Excepts\u003c/h2\u003e\n\u003cp\u003eIt is also possible to use multiple except statements with only 1 try.\u0026nbsp; This is a way you can split up errors to be more specific which handler is being triggered even if it doesn't print the line number.\u003c/p\u003e\n      \n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"while True:\n\twhile not wifi.radio.connected:\n\t\ttry:\n\t\t\twifi.radio.connect(ssid, password)\n\t\texcept OSError as e:\n\t\t\tprint(f\"❌ OSError: {e}\")\n        except RuntimeError as e:\n\t\t\tprint(f\"❌ RuntimeError: {e}\")\n        except (ValueError, ConnectionError) as e:\n\t\t\tprint(f\"❌ Value or Connection Error: {e}\")\n    print(\"📡 Wifi!\")","metadata":{"language":"python","linenums":false}},{"element_type":"alert","content":"\u003cp\u003eNotice parenthesis are only needed if you use multiple error handlers with a single except statement.\u003c/p\u003e","metadata":{"class":"element row-fluid alert-element build-alert alert-info","markdown":"Notice parenthesis are only needed if you use multiple error handlers with a single except statement.","alert_type":"info"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eLet's say in a hypothetical circumstance that you will never get a RuntimeError during wifi.radio.connect. If an error handler will never be triggered then it's pointless to add it to your except statement.\u0026nbsp; You'll eventually figure out which error handlers are best suited for different situations. How? By running your script until it fails. Every time you encounter a new error you add it to the list of errors to check for. This helps keep your script more efficient because every character of code costs against RAM usage. The less code you write, the less RAM you use. If you have a board with a ton of RAM to spare then absolutely, sure, you can add every exception handler in the list. Some lower power boards that can barely run Circuit Python do not have much RAM overhead. Being efficient with your code is a great habit to cultivate.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eConditional Clauses\u003c/h2\u003e\n\u003cp\u003eClauses are used within a while, for, or try loop.\u0026nbsp; Since an always online script will always be within a loop these can be used at any time. You will often see try/except used with conditional clauses.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003epass (does nothing and acts only as a placeholder while coding a new function)\u003c/li\u003e\n\u003cli\u003ebreak (breaks to try or beginning of script)\u003c/li\u003e\n\u003cli\u003econtinue (continues to retry the try until successful)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFor more information on conditional clauses please refer to \u003ca href=\"https://docs.python.org/3/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops\" target=\"_blank\"\u003ePython Documentation Section 4.4\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eHere is an example use of break within an exception:\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"while True:\n\twhile not wifi.radio.connected:\n    \ttry:\n\t\t\twifi.radio.connect(ssid, password)\n\t\texcept ConnectionError as e:\n\t\t\tprint(f\"❌ Connection Error: {e}\")\n        \tbreak  # this will start the script over from the beginning\n    print(\"📡 Wifi!\")","metadata":{"language":"auto","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eRequest Error Handling\u003c/h2\u003e\n\u003cp\u003eNow let's say we've connected to WiFi without any errors. Next let's do a get request to an API.\u0026nbsp; The SOURCE will be a url that you want to access, typically one that is formatted with a .json extension or file type.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"SOURCE = \"https://httpbin.org/get\"\nHEADER = {\"user-agent\": \"blinka/1.0.0\"}\n\nwhile True:\n\twhile not wifi.radio.connected:\n\t\ttry:\n\t\t\twifi.radio.connect(ssid, password)\n\t\texcept (OSError, RuntimeError, ValueError, ConnectionError) as e:\n\t\t\tprint(f\"❌ Error: {e}\")\n    print(\"📡 Wifi!\")\n    \n\twhile wifi.radio.connected:\n\t\ttry:\n\t\t\twith requests.get(url=SOURCE, headers=HEADER) as response:\n\t\t\t\tresponse_json = response.json()\n\t\texcept KeyError as e:\n\t\t\tprint(f\"❌ Key Error: {e}\")","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eUsing a try/except around the request in the same manner as the wifi connection. If a GET request fails it has its own exception handler. Splitting up exception handlers allows you to make more modular code. Using print statements for debugging will show which section an error is being thrown from. With a GET request a KeyError is a json formatting error.\u0026nbsp;\u003c/p\u003e\n\u003cp\u003eI'm walking you through each step of building up try/except layers intentionally so when you see the final script it won't be intimidating at all.\u0026nbsp;\u003c/p\u003e\n\u003cp\u003eThe small example above is straight forward to understand. If your script becomes 1000 lines long you will have many nested try/excepts.\u0026nbsp; The most important try/except is the first one that encompasses everything from directly below while True and ends at the very bottom of the entire script, so no matter what happens (in theory) any error that might slip through other error handlers will be caught at the end, preventing a crash, and allowing your script to retry from the beginning... forever.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eOSError errno\u003c/h2\u003e\n\u003cp\u003eerrno is a subset of the CPython implementation where you can catch very specifc error numbers from an error handler.\u0026nbsp; More information is available in the \u003ca href=\"https://docs.circuitpython.org/en/latest/docs/library/errno.html\" target=\"_blank\"\u003eCircuit Python Documentation on errno\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThanks to Deshipu in a discord discussion for figuring out how to catch and handle gaierror -2 specifically.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"while wifi.radio.connected:\n\ttry:\n\t\twith requests.get(url=SOURCE, headers=HEADER) as response:\n\t\t\tresponse_json = response.json()\n\texcept (OSError, KeyError) as e:\n\t\tif e.errno == -2:\n\t\t\tprint(f\"❌ gaierror (DNS failure), waiting to hard reset {e}\")\n\t\t\ttime.sleep(240)\n\t\t\tmicrocontroller.reset()\n\t\tprint(f\"❌ Key Error {e}\")","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eDNS Exception Handler (gaierror -2)\u003c/h2\u003e\n\u003cp\u003eThe OSError gaierror -2 is a quirky error in Circuit Python that stems from an upstream implementation of the ESP-IDF SDK. In my experience it can randomly occur when DNS resolution fails to resolve a host name. This is typically due to WiFi failing in the middle of a script because your router lost connection to the ISP, your DNS resolver failed, a failing ISP hop, or the host server is having an issue. You can catch the DNS error specifically using errno. The gaierror is an OSError you'll encounter seemingly randomly due to issues beyond your control.\u003c/p\u003e\n\u003cp\u003eThe error message associated with gaierror -2 is:\u0026nbsp;\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eName or service not known\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eMy personal preference to combat a gaierror is to force the script to sleep for a period of time. This provides time for whatever external issue is causing it, to sort itself out. Then hard reset the controller just in case the issue is wifi.radio socket related. It will take a while for your board to recover and reconnect with this method but the point is that it will recover instead of crashing to console.\u003c/p\u003e\n\u003ch2\u003eTraceback Module\u003c/h2\u003e\n\u003cp\u003eTraceback is a more advanced debugging helper and core module that is customizable and beyond the scope of this playground note. Here is the\u0026nbsp;\u003ca href=\"https://docs.circuitpython.org/en/latest/shared-bindings/traceback/index.html\" target=\"_blank\"\u003edocumentation on Traceback\u003c/a\u003e. If you become frustrated with exceptions (especially since they don't show the line # of the error) and want to print more verbose errors then look into the traceback module.\u003c/p\u003e\n\u003ch2\u003eRequest Example Script:\u003c/h2\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"import os\nimport time\nimport microcontroller\nimport adafruit_connection_manager\nimport wifi\nimport adafruit_requests\n\n# Initalize Wifi, Socket Pool, Request Session\npool = adafruit_connection_manager.get_radio_socketpool(wifi.radio)\nSSL_CONTEXT = adafruit_connection_manager.get_radio_ssl_context(wifi.radio)\n# adafruit_requests.Session keep outside the main loop\n# otherwise you get Out of Socket errors.\nrequests = adafruit_requests.Session(pool, SSL_CONTEXT)\n\n# Use settings.toml for credentials\nssid = os.getenv(\"CIRCUITPY_WIFI_SSID\")\npassword = os.getenv(\"CIRCUITPY_WIFI_PASSWORD\")\n\nSOURCE = \"https://httpbin.org/get\"\nHEADER = {\"user-agent\": \"blinka/1.0.0\"}\n\nwhile True:\n  # Connect to Wi-Fi\n  print(\"\\nConnecting to WiFi...\")\n  while not wifi.radio.connected:\n    try:\n      wifi.radio.connect(ssid, password)\n    except ConnectionError as e:\n      print(f\"❌ Connection Error: {e}\")\n      time.sleep(60)\n  print(\"📡 Wifi!\")\n\n  while wifi.radio.connected:\n    try:\n      \n      try:\n        with requests.get(url=SOURCE, headers=HEADER) as response:\n          response_json = response.json()\n      # Request error catcher. If script fails during request.\n      except (OSError, KeyError) as e:\n        if e.errno == -2:\n          print(f\"❌ gaierror (DNS failure), waiting to hard reset {e}\")\n          time.sleep(240)\n          microcontroller.reset()\n        print(f\"❌ Key Error: {e}\")\n        time.sleep(60)\n        \n      # Your online API section goes here\n      response_headers = response.headers\n      print(f\"Response Headers: {response_headers}\")\n      \n    # General error catcher. If script fails after a successful request.\n    except (OSError, ValueError, RuntimeError) as e:\n      print(f\"❌ General Exception Error: {e}\")\n      time.sleep(60)\n      break  # breaks to while True to re-check WiFi status\n    time.sleep(300)  # If everything succeeds, time until next poll update","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eThis is a basic boilerplate that I use in most of my online API scripts to start with.\u0026nbsp; All you have to do is provide the SOURCE, HEADER, and any API calls to the host in the relevant section.\u003c/p\u003e\n\u003cp\u003eHopefully you now have a better understanding of what it takes to keep a script online 24/7 with Circuit Python.\u0026nbsp; It's simply a matter of being able to control what happens when an error occurs so you can permanently prevent your script from crashing.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}}]