2026-07-12 16:43:39 +10:00
2026-07-12 16:43:39 +10:00
2026-07-12 16:43:39 +10:00

CTA for ESP32

Notes

CTA API interface

The CTA API can be fairly trivially queries to get a JSON response with the required metadata.

    To query XML: https://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=<KEYID>&mapid=<mapID>
    To query JSON: https://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=<KEYID>&mapid=40380&outputType=JSON

    Simple HTTP POST queries use the '?' to define start of parameters, and each parameter is seperated by an '&'
    In the above, we are querying the 'ttarrivals' endpoint, used for the Arrivals API
    KEYID is your API key
    MAPID is the ID of a station on the CTA, such as "40380". These station numbers are available on the ttdocs website.
  • The below is an example returned of the JSON station for mapid 40380, station Clarke/Lake

Example Return

  • The Arrivals API returns all the future trains to arrive at a station.
  • JSON is typically an easier format to work with than XML, both are essentially ways to structure data that are nicely readable by applications (a tree structure).
  • In the above example, we have an array of next arrivals, starting at the top of the tree, we can pick each like you would in a folder. Some pseudocode (i.e. made up simplified language):
    • Say return json is output.json = https://lapi.transitchicago.com/api/1.0/ttarrivals.aspx?key=<KEYID>&mapid=40380&outputType=JSON
    • output->ctatt gives us tmst, errCd, errNm and eta 'folders'
    • output->ctatt->eta gives us a list of folders for all the next upcoming trains.
    • output->ctatt->eta->0 gives us the next train.
    • output->ctatt->eta->1 gives us the second to next train.
    • output->ctatt->eta->0-arrT gives us the time the next train will arrive in an ISO date format, i.e. "2026-07-12T00:50:16"
  • The problem with the Arrivals API, is you then need to query every station one by one, save the next arrival time of a train and then do something with the result (say, turn on an LED if it's less than 2 minutes away).
    • That's a lot of queries to check for the entire network. Especially for a little ESP32.
    • Very wasteful and abusive of the API for just your usage.
  • Enter the Locations API. Rather than query a station, we can query the entire line and get a list of trains on it.
    To query JSON: https://lapi.transitchicago.com/api/1.0/ttpositions.aspx?key=<KEYID>&rt=<route>&outputType=JSON

    KEYID is your API key
    RT is the identifier of the line, e.g. 'Blue'. These are in the appendix on the ttdocs website.
  • The below is an example returned of the JSON station for Blue route

Example Return

  • The Locations API install returns all trains along a route.
  • In the above example, we have an array of all trains, starting at the top of the tree, we can pick each like you would in a folder. Some pseudocode:
    • Say return json is output.json which = https://lapi.transitchicago.com/api/1.0/ttpositions.aspx?key=&rt=Blue&outputType=JSON
    • output->tmst gives us the time we made this query, this is handy to find the current time.
    • output->ctatt->route->0 gives us all the trains on the first route (which is Blue, as that's what we asked for)
    • output->ctatt->route->0->train gives us a list of folders for all the active trains.
    • output->ctatt->route->0->train->0 gives us the first train.
    • output->ctatt->route->0->train->1 gives us the second train.
    • output->ctatt->route->0->train->0->nextStaNm gives us the name of it's next station.
    • output->ctatt->route->0->train->0->nextStaId gives us the ID of it's next station.
    • output->ctatt->route->0->train->0->arrT gives us the time to the next station in ISO format. "2026-07-12T00:50:16"
  • So now, in a single query, we have all the information we need to know how far away "a train" is from any station on a line.
    • A single query tells us everything we need to know to light up a line.

So example pseudocode:

# Create a simple 2D array of all stations (imagine table with two columns) and when the next train arrives. Let's set arrival time to 999 for now (i.e. not any time soon).
# We can use the station number instead of the name to keep code simpler (don't have to deal with spaces or symbols)
stations_eta = <array>
stations_eta[40380] = 999
stations_eta[40390] = 999
.... etc ....

# Get route data in JSON format
output_json = http_query(https://lapi.transitchicago.com/api/1.0/ttpositions.aspx?key=<KEYID>&rt=Blue&outputType=JSON)

# Get that into a useful array format instead of just a blob
output = json(output_json)

# We need the time this query was generated to be used for math later. Fortunately the API response stores this
time_now = output->tmst

# Now we can loop through all the trains and match up to the stations
for train in output->ctatt->route->0->train:
   # 'train' will now refer to the first train in the folder, aka 0. 
   # So we can save the next stop of the train 
   next_stop = train->nextStaId
   # The time of the next stop
   time_to_stop = train->arrT
   # Get the time difference. 
   time_eta = time_to_stop - time_now
   # In our made up language, this is going to be in seconds.
   time_eta_mins = time_eta / 60

   # Now we can update the station array from earlier
   stations_eta[next_stop] = time_eta_mins

   # The loop will now run again, but next time it will refer to the second train (aka 1)

# We now have an array of all stations and the next train to arrive at them. For the ones with none any time soon, they'll still be 999 minutes away.
print(stations_eta[40380])
---> 6 

print(stations_eta[40390])
---> 11

ESP32 Implementation

  • Micropython appears to still be poorly documented for ESP32s (especially the aliexpress clone boards).
  • Recommendation is to use either the Arduino IDE (for quickly getting going) or using PlatformIO with VS Code.
    • Both of these are just fancy wrappers around C++ language, so plenty of documentation to google.
    • The latter is harder to initially get going (mostly cause of VS Code), but it's a much nicer environment to code in than Arduino IDE.
    • PlatformIO is completely compatable with Arduino code, so can always start in Arduino IDE and move from there.
  • Example PlatformIO code for the API is available in example/src/
S
Description
Notes and sample code for querying the CTA API for train routes and displaying the station mapping utilising an ESP32.
Readme 133 KiB