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

JSON Bourne

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) and pinMode(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):

6. Source code


1. Applying the inverse Laplace transform, we find that L1Screen(s)=screen(t).