Once you've finished setting up your Matrix Portal S3 with CircuitPython, you can access the code and necessary libraries by downloading the Project Bundle.
To do this, click on the Download Project Bundle button in the window below. It will download to your computer as a zipped folder.
# SPDX-FileCopyrightText: 2023 Liz Clark for Adafruit Industries # # SPDX-License-Identifier: MIT import os import gc import ssl import time import wifi import socketpool import adafruit_requests import adafruit_display_text.label import board import terminalio import displayio import framebufferio import rgbmatrix import adafruit_json_stream as json_stream import microcontroller from adafruit_ticks import ticks_ms, ticks_add, ticks_diff from adafruit_datetime import datetime, timedelta import neopixel displayio.release_displays() # font color for text on matrix font_color = 0xFFFFFF # your timezone UTC offset and timezone name timezone_info = [-4, "EDT"] # the name of the sports you want to follow sport_name = ["football", "baseball", "soccer", "hockey", "basketball"] # the name of the corresponding leages you want to follow sport_league = ["nfl", "mlb", "usa.1", "nhl", "nba"] # the team names you want to follow # must match the order of sport/league arrays # include full name and then abbreviation (usually city/region) team0 = ["New England Patriots", "NE"] team1 = ["Boston Red Sox", "BOS"] team2 = ["New England Revolution", "NE"] team3 = ["Boston Bruins", "BOS"] team4 = ["Boston Celtics", "BOS"] # how often the API should be fetched fetch_timer = 300 # seconds # how often the display should update display_timer = 30 # seconds pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness = 0.3, auto_write=True) # matrix setup base_width = 64 base_height = 32 chain_across = 2 tile_down = 2 DISPLAY_WIDTH = base_width * chain_across DISPLAY_HEIGHT = base_height * tile_down matrix = rgbmatrix.RGBMatrix( width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, bit_depth=3, rgb_pins=[ board.MTX_R1, board.MTX_G1, board.MTX_B1, board.MTX_R2, board.MTX_G2, board.MTX_B2 ], addr_pins=[ board.MTX_ADDRA, board.MTX_ADDRB, board.MTX_ADDRC, board.MTX_ADDRD ], clock_pin=board.MTX_CLK, latch_pin=board.MTX_LAT, output_enable_pin=board.MTX_OE, tile=tile_down, serpentine=True, doublebuffer=False ) display = framebufferio.FramebufferDisplay(matrix) # connect to WIFI wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD")) print(f"Connected to {os.getenv('CIRCUITPY_WIFI_SSID')}") # add API URLs SPORT_URLS = [] for i in range(5): d = ( f"https://site.api.espn.com/apis/site/v2/sports/{sport_name[i]}/{sport_league[i]}/scoreboard" ) SPORT_URLS.append(d) context = ssl.create_default_context() pool = socketpool.SocketPool(wifi.radio) requests = adafruit_requests.Session(pool, context) # arrays for teams, logos and display groups teams = [] logos = [] groups = [] # add team to array teams.append(team0) # grab logo bitmap name logo0 = "/team0_logos/" + team0[1] + ".bmp" # add logo to array logos.append(logo0) # create a display group group0 = displayio.Group() # add group to array groups.append(group0) # repeat: teams.append(team1) logo1 = "/team1_logos/" + team1[1] + ".bmp" logos.append(logo1) group1 = displayio.Group() groups.append(group1) teams.append(team2) logo2 = "/team2_logos/" + team2[1] + ".bmp" logos.append(logo2) group2 = displayio.Group() groups.append(group2) teams.append(team3) logo3 = "/team3_logos/" + team3[1] + ".bmp" logos.append(logo3) group3 = displayio.Group() groups.append(group3) teams.append(team4) logo4 = "/team4_logos/" + team4[1] + ".bmp" logos.append(logo4) group4 = displayio.Group() groups.append(group4) # initial startup screen # shows the five team logos you are following def sport_startup(logo): try: group = displayio.Group() bitmap0 = displayio.OnDiskBitmap(logo[0]) grid0 = displayio.TileGrid(bitmap0, pixel_shader=bitmap0.pixel_shader, x = 0) bitmap1 = displayio.OnDiskBitmap(logo[1]) grid1 = displayio.TileGrid(bitmap1, pixel_shader=bitmap1.pixel_shader, x = 32) bitmap2 = displayio.OnDiskBitmap(logo[2]) grid2 = displayio.TileGrid(bitmap2, pixel_shader=bitmap2.pixel_shader, x = 64) bitmap3 = displayio.OnDiskBitmap(logo[3]) grid3 = displayio.TileGrid(bitmap3, pixel_shader=bitmap3.pixel_shader, x = 96) bitmap4 = displayio.OnDiskBitmap(logo[4]) grid4 = displayio.TileGrid(bitmap4, pixel_shader=bitmap4.pixel_shader, x = 48, y=32) group.append(grid0) group.append(grid1) group.append(grid2) group.append(grid3) group.append(grid4) display.root_group = group # pylint: disable=broad-except except Exception: print("Can't find bitmap. Did you run the get_team_logos.py script?") # takes UTC time from JSON and reformats how its displayed def convert_date_format(date, tz_information): # Manually extract year, month, day, hour, and minute from the string year = int(date[0:4]) month = int(date[5:7]) day = int(date[8:10]) hour = int(date[11:13]) minute = int(date[14:16]) # Construct a datetime object using the extracted values dt = datetime(year, month, day, hour, minute) # Adjust the datetime object for the target timezone offset dt_adjusted = dt + timedelta(hours=tz_information[0]) # Extract fields for output format month = dt_adjusted.month day = dt_adjusted.day hour = dt_adjusted.hour minute = dt_adjusted.minute # Convert 24-hour format to 12-hour format and determine AM/PM am_pm = "AM" if hour < 12 else "PM" hour_12 = hour if hour <= 12 else hour - 12 minute = f"{minute:02}" # Determine the timezone abbreviation based on the offset time_zone_str = tz_information[1] return f"{month}/{day} - {hour_12}:{minute} {am_pm} {time_zone_str}" # the actual API and display function # pylint: disable=too-many-locals, too-many-branches, too-many-statements def get_data(data, team, logo, group): pixel.fill((0, 0, 255)) print(f"Fetching data from {data}") playing = False names = [] scores = [] info = [] index = 0 # the team you are following's logo bitmap0 = displayio.OnDiskBitmap(logo) grid0 = displayio.TileGrid(bitmap0, pixel_shader=bitmap0.pixel_shader, x = 2) home_text = adafruit_display_text.label.Label(terminalio.FONT, color=font_color, text=" ") away_text = adafruit_display_text.label.Label(terminalio.FONT, color=font_color, text=" ") vs_text = adafruit_display_text.label.Label(terminalio.FONT, color=font_color, text=" ") vs_text.anchor_point = (0.5, 0.0) vs_text.anchored_position = (DISPLAY_WIDTH / 2, 14) info_text = adafruit_display_text.label.Label(terminalio.FONT, color=font_color, text=" ") info_text.anchor_point = (0.5, 1.0) info_text.anchored_position = (DISPLAY_WIDTH / 2, DISPLAY_HEIGHT) # make the request to the API resp = requests.get(data) # stream the json json_data = json_stream.load(resp.iter_content(32)) for event in json_data["events"]: # clear the date and then add the date to the array # the date for your game will remain info.clear() info.append(event["date"]) # check for your team playing if team[0] not in event["name"]: continue for competition in event["competitions"]: for competitor in competition["competitors"]: # if your team is playing: playing = True # get team names # index indicates home vs. away names.append(competitor["team"]["abbreviation"]) # the current score scores.append(competitor["score"]) # gets info on game info.append(event["status"]["type"]["shortDetail"]) break if playing and len(names) != 2: print("did not get expected response, fetching full JSON..") try: resp.close() # pylint: disable=broad-except except Exception as e: print(f"{e}, continuing..") # pylint: disable=unnecessary-pass pass names.clear() scores.clear() info.clear() resp = requests.get(data) response_as_json = resp.json() for e in response_as_json["events"]: if team[0] in e["name"]: print(index) info.append(response_as_json["events"][0]["date"]) names.append(response_as_json["events"][0]["competitions"] [0]["competitors"][0]["team"]["abbreviation"]) names.append(response_as_json["events"][0]["competitions"] [0]["competitors"][1]["team"]["abbreviation"]) scores.append(response_as_json["events"][0]["competitions"] [0]["competitors"][0]["score"]) scores.append(response_as_json["events"][0]["competitions"] [0]["competitors"][1]["score"]) info.append(response_as_json["events"][0]["status"]["type"]["shortDetail"]) else: index += 1 # debug printing print(names) print(scores) print(info) if playing and len(names) == 2: # pull out the date date = info[0] # convert it to be readable date = convert_date_format(date, timezone_info) print(date) # pull out the info info = info[1] # check if it's pre-game if str(info) == date or str(info) == "Scheduled": status = "pre" print("match, pre-game") else: status = info # home and away text # teams index determines which team is home or away home_text.text="HOME" away_text.text="AWAY" if team[1] is names[0]: home_game = True home_text.anchor_point = (0.0, 0.5) home_text.anchored_position = (5, 37) away_text.anchor_point = (1.0, 0.5) away_text.anchored_position = (124, 37) vs_team = names[1] else: home_game = False away_text.anchor_point = (0.0, 0.5) away_text.anchored_position = (5, 37) home_text.anchor_point = (1.0, 0.5) home_text.anchored_position = (124, 37) vs_team = names[0] # if it's pre-game, show "VS" if status == "pre": vs_text.text="VS" info_text.text=date # if it's active or final show score else: info_text.text=info if home_game: vs_text.text=f"{scores[0]} - {scores[1]}" else: vs_text.text=f"{scores[1]} - {scores[0]}" # load in logo from other team vs_logo = logo.replace(team[1], vs_team) # if there is no game matching your team: else: status = "pre" vs_logo = logo info_text.text="NO DATA AVAILABLE" # load in the other team's logo bitmap1 = displayio.OnDiskBitmap(vs_logo) grid1 = displayio.TileGrid(bitmap1, pixel_shader=bitmap1.pixel_shader, x = 94) print("done") # update the display group. try/except in case its the first time it's being added try: group[0] = grid0 group[1] = grid1 group[2] = home_text group[3] = away_text group[4] = vs_text group[5] = info_text except IndexError: group.append(grid0) group.append(grid1) group.append(home_text) group.append(away_text) group.append(vs_text) group.append(info_text) # close the response try: # sometimes an OSError is thrown: # "invalid syntax for integer with base 16" # the code can continue depite it though resp.close() # pylint: disable=broad-except except Exception as e: print(f"{e}, continuing..") # pylint: disable=unnecessary-pass pass pixel.fill((0, 0, 0)) # return that data was just fetched fetch_status = True return fetch_status # index and clock for fetching fetch_index = 0 fetch_timer = fetch_timer * 1000 # index and clock for updating display display_index = 0 display_timer = display_timer * 1000 # load logos sport_startup(logos) # initial data fetch for z in range(5): try: just_fetched = get_data(SPORT_URLS[z], teams[z], logos[z], groups[z]) display.root_group = groups[z] # pylint: disable=broad-except except Exception as Error: print(Error) time.sleep(10) gc.collect() time.sleep(5) microcontroller.reset() # start clocks just_fetched = True fetch_clock = ticks_ms() display_clock = ticks_ms() while True: try: if not just_fetched: # garbage collection for display groups gc.collect() # fetch the json for the next team just_fetched = get_data(SPORT_URLS[fetch_index], teams[fetch_index], logos[fetch_index], groups[fetch_index]) # advance index fetch_index = (fetch_index + 1) % len(teams) # reset clocks fetch_clock = ticks_add(fetch_clock, fetch_timer) display_clock = ticks_add(display_clock, display_timer) # update display seperate from API request if ticks_diff(ticks_ms(), display_clock) >= display_timer: print("updating display") display.root_group = groups[display_index] display_index = (display_index + 1) % len(teams) display_clock = ticks_add(display_clock, display_timer) # cleared for fetching after time has passed if ticks_diff(ticks_ms(), fetch_clock) >= fetch_timer: just_fetched = False # pylint: disable=broad-except except Exception as Error: print(Error) time.sleep(10) gc.collect() time.sleep(5) microcontroller.reset()
Upload the Code and Libraries to the Matrix Portal S3
After downloading the Project Bundle, plug your Matrix Portal S3 into the computer's USB port with a known good USB data+power cable. You should see a new flash drive appear in the computer's File Explorer or Finder (depending on your operating system) called CIRCUITPY. Unzip the folder and copy the following items to the Matrix Portal S3's CIRCUITPY drive.
- lib folder
- code.py
Your Matrix Portal S3 CIRCUITPY drive should look like this after copying the lib folder and the code.py file.
Add Your settings.toml File
As of CircuitPython 8.0.0, there is support for Environment Variables. Environment variables are stored in a settings.toml file. Similar to secrets.py, the settings.toml file separates your sensitive information from your main code.py file. Add your settings.toml file as described in the Create Your settings.toml File page earlier in this guide. You'll need to include your CIRCUITPY_WIFI_SSID
and CIRCUITPY_WIFI_PASSWORD
.
CIRCUITPY_WIFI_SSID = "your-ssid-here" CIRCUITPY_WIFI_PASSWORD = "your-ssid-password-here"
Add Your Team Logo Bitmaps
You'll need to run the get_team_logos.py script that is included on the Prep the Team Logos page earlier in this guide to download the team logo bitmaps to display on the RGB LED matrices.
When the script finishes, you'll see a folder called /sport_logos in the same directory where you have the get_team_logos.py script saved. If you go into the /sport_logos folder, you'll see the folders containing the team logos:
- /team0_logos
- /team1_logos
- /team2_logos
- /team3_logos
- /team4_logos
You'll drag and drop these five folders onto the main directory of your Matrix Portal S3 CIRCUITPY drive.
How the CircuitPython Code Works
At the top of the code you can find all of the parameters that you can edit to customize the project. You can add your time zone, sports, leagues and team names. There are also two timers: fetch_timer
determines how often the API should be pinged and display_timer
determines the speed of the team display rotation.
# font color for text on matrix font_color = 0xFFFFFF # your timezone UTC offset and timezone name timezone_info = [-4, "EDT"] # the name of the sports you want to follow sport_name = ["football", "baseball", "soccer", "hockey", "basketball"] # the name of the corresponding leages you want to follow sport_league = ["nfl", "mlb", "usa.1", "nhl", "nba"] # the team names you want to follow # must match the order of sport/league arrays # include full name and then abbreviation (usually city/region) team0 = ["New England Patriots", "NE"] team1 = ["Boston Red Sox", "BOS"] team2 = ["New England Revolution", "NE"] team3 = ["Boston Bruins", "BOS"] team4 = ["Boston Celtics", "BOS"] # how often the API should be fetched fetch_timer = 300 # seconds # how often the display should update display_timer = 30 # seconds
Matrix
An RGBMatrix
object is instantiated to be 128 pixels wide by 64 pixels high. It is then passed to be a FramebufferDisplay
.
# matrix setup base_width = 64 base_height = 32 chain_across = 2 tile_down = 2 DISPLAY_WIDTH = base_width * chain_across DISPLAY_HEIGHT = base_height * tile_down matrix = rgbmatrix.RGBMatrix( width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT, bit_depth=3, rgb_pins=[ board.MTX_R1, board.MTX_G1, board.MTX_B1, board.MTX_R2, board.MTX_G2, board.MTX_B2 ], addr_pins=[ board.MTX_ADDRA, board.MTX_ADDRB, board.MTX_ADDRC, board.MTX_ADDRD ], clock_pin=board.MTX_CLK, latch_pin=board.MTX_LAT, output_enable_pin=board.MTX_OE, tile=tile_down, serpentine=True, doublebuffer=False ) display = framebufferio.FramebufferDisplay(matrix)
URLs
The ESPN API URLs are added to the SPORT_URLS
array by passing the entries from the sport_name
and sport_league
arrays to the base URL with an f-string.
# add API URLs SPORT_URLS = [] for i in range(5): d = ( f"https://site.api.espn.com/apis/site/v2/sports/{sport_name[i]}/{sport_league[i]}/scoreboard" ) SPORT_URLS.append(d)
Logos
Each team logo is added to the logos
array. The logos are named for the team abbreviations, which allows the code to pass the second entry from the team#
array to find the logo in the appropriate logo folder.
# arrays for teams, logos and display groups teams = [] logos = [] groups = [] # add team to array teams.append(team0) # grab logo bitmap name logo0 = "/team0_logos/" + team0[1] + ".bmp" # add logo to array logos.append(logo0) # create a display group group0 = displayio.Group() # add group to array groups.append(group0) # repeat:
Start-Up Screen
The sport_startup()
function runs at the start of the code. It displays all five of your team logos.
# initial startup screen # shows the five team logos you are following def sport_startup(logo): try: group = displayio.Group() bitmap0 = displayio.OnDiskBitmap(logo[0]) grid0 = displayio.TileGrid(bitmap0, pixel_shader=bitmap0.pixel_shader, x = 0) bitmap1 = displayio.OnDiskBitmap(logo[1]) grid1 = displayio.TileGrid(bitmap1, pixel_shader=bitmap1.pixel_shader, x = 32) bitmap2 = displayio.OnDiskBitmap(logo[2]) grid2 = displayio.TileGrid(bitmap2, pixel_shader=bitmap2.pixel_shader, x = 64) bitmap3 = displayio.OnDiskBitmap(logo[3]) grid3 = displayio.TileGrid(bitmap3, pixel_shader=bitmap3.pixel_shader, x = 96) bitmap4 = displayio.OnDiskBitmap(logo[4]) grid4 = displayio.TileGrid(bitmap4, pixel_shader=bitmap4.pixel_shader, x = 48, y=32) group.append(grid0) group.append(grid1) group.append(grid2) group.append(grid3) group.append(grid4) display.root_group = group # pylint: disable=broad-except except Exception: print("Can't find bitmap. Did you run the get_team_logos.py script?")
UTC to Your Time Zone
The convert_date_format()
function is used to reformat the time that is fetched from the ESPN API. It rearranges the string to be formatted as "MM/DD - HH:MM AM/PM TZ" and converts the time from UTC to your defined time zone.
# takes UTC time from JSON and reformats how its displayed def convert_date_format(date, tz_information): # extract year, month, day, hour, and minute from the string year = int(date[0:4]) month = int(date[5:7]) day = int(date[8:10]) hour = int(date[11:13]) minute = int(date[14:16]) # make a datetime object using the extracted values dt = datetime(year, month, day, hour, minute) # adjust the datetime object for the time zone dt_adjusted = dt + timedelta(hours=tz_information[0]) # pull out the data from the datetime month = dt_adjusted.month day = dt_adjusted.day hour = dt_adjusted.hour minute = dt_adjusted.minute # convert 24 hour time to 12 hour time and determine AM/PM am_pm = "AM" if hour < 12 else "PM" hour_12 = hour if hour <= 12 else hour - 12 minute = f"{minute:02}" # bring in time zone name for display time_zone_str = tz_information[1] return f"{month}/{day} - {hour_12}:{minute} {am_pm} {time_zone_str}"
MVP of the Code: Fetch the Data
The get_data()
function takes care of making a request to the ESPN API and updating the display attributes for each team group. The URL, team, team logo and team display group are passed to the function.
The function starts by bringing in the team logo as an OnDiskBitmap
and prepping the different text elements.
def get_data(data, team, logo, group): pixel.fill((0, 0, 255)) print(f"Fetching data from {data}") playing = False names = [] scores = [] info = [] # the team you are following's logo bitmap0 = displayio.OnDiskBitmap(logo) grid0 = displayio.TileGrid(bitmap0, pixel_shader=bitmap0.pixel_shader, x = 2) home_text = adafruit_display_text.label.Label(terminalio.FONT, color=font_color, text=" ") away_text = adafruit_display_text.label.Label(terminalio.FONT, color=font_color, text=" ") vs_text = adafruit_display_text.label.Label(terminalio.FONT, color=font_color, text=" ") vs_text.anchor_point = (0.5, 0.0) vs_text.anchored_position = (DISPLAY_WIDTH / 2, 14) info_text = adafruit_display_text.label.Label(terminalio.FONT, color=font_color, text=" ") info_text.anchor_point = (0.5, 1.0) info_text.anchored_position = (DISPLAY_WIDTH / 2, DISPLAY_HEIGHT)
Then the request is made to the ESPN API using the json_stream
library. This streams the JSON rather than storing the entire JSON response. This is a lot faster and uses less memory. However, because the data is literally streaming by, you have to be thoughtful about how you are logging the information from the JSON. JSON entries have to be accessed in the order in which they are listed in the JSON response. This is why the date
entry is continually added and cleared from the info
array, since it is listed before you know if it matches your team's game.
# make the request to the API resp = requests.get(data) # stream the json json_data = json_stream.load(resp.iter_content(32)) for event in json_data["events"]: # clear the date and then add the date to the array # the date for your game will remain info.clear() info.append(event["date"]) # check for your team playing if team[0] not in event["name"]: continue for competition in event["competitions"]: for competitor in competition["competitors"]: # if your team is playing: playing = True # get team names # index indicates home vs. away names.append(competitor["team"]["abbreviation"]) # the current score scores.append(competitor["score"]) # gets info on game info.append(event["status"]["type"]["shortDetail"]) break
If your team shows up in the API response, the date is converted using the convert_date_format()
function. There are checks to see if the game is active and which team is home and away. These checks determine the content of the text elements. The opposing team's logo is accessed by replacing your team's abbreviation name with the opposing team's abbreviation.
if playing: # pull out the date date = info[0] # convert it to be readable date = convert_date_format(date, timezone_info) print(date) # pull out the info info = info[1] # check if it's pre-game if str(info) == date or str(info) == "Scheduled": status = "pre" print("match, pre-game") else: status = info # home and away text # teams index determines which team is home or away home_text.text="HOME" away_text.text="AWAY" if team[1] is names[0]: home_game = True home_text.anchor_point = (0.0, 0.5) home_text.anchored_position = (5, 37) away_text.anchor_point = (1.0, 0.5) away_text.anchored_position = (124, 37) vs_team = names[1] else: home_game = False away_text.anchor_point = (0.0, 0.5) away_text.anchored_position = (5, 37) home_text.anchor_point = (1.0, 0.5) home_text.anchored_position = (124, 37) vs_team = names[0] # if it's pre-game, show "VS" if status == "pre": vs_text.text="VS" info_text.text=date # if it's active or final show score else: info_text.text=info if home_game: vs_text.text=f"{scores[0]} - {scores[1]}" else: vs_text.text=f"{scores[1]} - {scores[0]}" # load in logo from other team vs_logo = logo.replace(team[1], vs_team)
If your team does not match any of the entries in the JSON response, then your team's logo is shown twice and the text "NO DATA AVAILABLE
" is shown.
# if there is no game matching your team: else: status = "pre" vs_logo = logo info_text.text="NO DATA AVAILABLE"
Finally the display group is updated with the logo and text elements and the response is closed.
# update the display group. try/except in case its the first time it's being added try: group[0] = grid0 group[1] = grid1 group[2] = home_text group[3] = away_text group[4] = vs_text group[5] = info_text except IndexError: group.append(grid0) group.append(grid1) group.append(home_text) group.append(away_text) group.append(vs_text) group.append(info_text) # close the response resp.close()
The Loop
Two processes are happening concurrently in the loop with the help of ticks
. The matrices are cycling through the five sport display groups by advancing through the groups
array.
# update display seperate from API request if ticks_diff(ticks_ms(), display_clock) >= display_timer: print("updating display") display.root_group = groups[display_index] display_index = (display_index + 1) % len(teams) display_clock = ticks_add(display_clock, display_timer)
The different ESPN API URLs are being fetched on a rotation. The display groups are updated in the get_data()
function. As a result, when a team display group is shown it will have the updated data.
if not just_fetched: # garbage collection for display groups gc.collect() # fetch the json for the next team just_fetched = get_data(SPORT_URLS[fetch_index], teams[fetch_index], logos[fetch_index], groups[fetch_index]) # advance index fetch_index = (fetch_index + 1) % len(teams) # reset clocks fetch_clock = ticks_add(fetch_clock, fetch_timer) display_clock = ticks_add(display_clock, display_timer) # cleared for fetching after time has passed if ticks_diff(ticks_ms(), fetch_clock) >= fetch_timer: just_fetched = False
All of this is wrapped in a try/except in case any errors occur while making a request to the ESPN API. If an error occurs, the Matrix Portal S3 resets itself.
try: ... except Exception as Error: print(Error) time.sleep(10) gc.collect() time.sleep(5) microcontroller.reset()
Text editor powered by tinymce.