So you want to make a scorebug with an ESP32 and the output device of your choosing…
There are a couple of things you need before you can do this:
-
Some output device (I’m using two LCD screens. They both have the I2C thingy.)
-
Arduino ESP32 (Pretty much any model of ESP32 works, as long as it has Wi-Fi. I got a 3-pack for $15)
1. Making an API call
There are many sports APIs available that are free and available to the public; I use ESPN’s API. Unfortunately, no public documentation exists, so we need to rely on a third-party manual. There are plenty on GitHub, but this one’s my favorite.
1.1. Example: Getting a Blackhawks or Cubs game
Per the above documentation, the url we use to get data on the Blackhawks is:
…and for the Cubs:
If the team’s in season, you should see a nextEvent
tag which has your
club’s next game (if they’re in season).
For example, at time of writing, the Hawks' next game is against the Vegas Golden Knights,
and the API says that game has ID 401560528
.
The unofficial documentation doesn’t say it, but the URL for that game is:
If you look through this file, you’ll see a bunch of stats about the game. Specifically, you’ll find that the Hawks lost 3-1, among other things.
2. Ok, but what teams should we choose?
Answer: the teams in this Google sheet.
When the ESP32 does a GET request to here,
it gets a JSON file with the teams to check up on.
var ss = SpreadsheetApp.openById("1FaTrpoYyhqer46mwbFVc-kgYUvd_q0dY_XCuZ-Bn9uo")
var sheet = ss.getSheetByName("Sheet1")
function doGet(e) {
var json = {}
json.ncaab = []
json.nhl = []
json.nba = []
json.mlb = []
json.nfl = []
// add ncaab teams
var row = 2;
var col = 1;
var currCellValue;
while(1) {
currCellValue = sheet.getRange(row,col).getValue()
if(currCellValue == '') break;
json.ncaab.push(sheet.getRange(row,col).getValue())
row++
}
col++;
// add nhl teams
row = 2;
while(1) {
currCellValue = sheet.getRange(row,col).getValue()
if(currCellValue == '') break;
json.nhl.push(sheet.getRange(row,col).getValue())
row++
}
col++;
// add nba teams
row = 2;
while(1) {
currCellValue = sheet.getRange(row,col).getValue()
if(currCellValue == '') break;
json.nba.push(sheet.getRange(row,col).getValue())
row++
}
col++;
// add mlb teams
row = 2;
while(1) {
currCellValue = sheet.getRange(row,col).getValue()
if(currCellValue == '') break;
json.mlb.push(sheet.getRange(row,col).getValue())
row++
}
col++
// add nfl games
row = 2;
while(1) {
currCellValue = sheet.getRange(row,col).getValue()
if(currCellValue == '') break;
json.nfl.push(sheet.getRange(row,col).getValue())
row++
}
return ContentService.createTextOutput(JSON.stringify(json)).setMimeType(ContentService.MimeType.JSON);
}
3. Parsing the JSON

Cool, now we have a JSON file, but it’s a string. We can parse it as a JSON object then index it similarly to how we index Python dictionaries.
First we need the Arduino JSON library; we’ll use https://arduinojson.org/ (available in the Arduino IDE’s library manager). From there, you just need to know where the goods are (team names, scores, other facts about the game) and store them as variables.
4. Writing to (multiple) LCD screens (even with I2C?!)
Arduino’s I2C peripheral is quite odd;
it has default SCL and SDA pins, but
you can change this with the Wire.h
library and a bit of ingenuity:
-
Call
Wire.begin(SDA,SCL)
to add the new I2C pins -
Call
pinMode(OldSDAPin,OUTPUT)
andpinMode(OldSCLPin,OUTPUT)
to clobber the old I2C pins. Now when we write to the LCD, it’ll write to the display of our choice.
With this technique we can call the same write
and setCursor
functions
on the same LCD object, and we need only change the I2C pins.
5. Putting everything together
I used the HTTP Client library on Arduino. You just need to know how to make a GET request.
The general flow is (in the Blackhawks' case):
-
response = HTTP GET http://site.api.espn.com/apis/site/v2/sports/hockey/nhl/teams/chi
-
find game ID at
response["nextEvent"][0]["id"]
, store atgameID
-
HTTP GET http://site.api.espn.com/apis/site/v2/sports/hockey/nhl/scoreboard/
gameID
-
Write certain things to the LCD screen