2c8a4664a4b6be742c4d1181d53e4adcc931f32b
CTA for ESP32
Notes
CTA API interface
The CTA API can be fairly trivially queries to get a JSON response with the required metadata.
- Docs are available here: https://www.transitchicago.com/developers/ttdocs/
- API consists of two main queries
- 'Arrivals' - which lets you query all arrivals for a station available at https://lapi.transitchicago.com/api/1.0/ttarrivals.aspx
- 'Locations' - Which lets you query all stations along a line, and returns the location of all trains. Available at https://lapi.transitchicago.com/api/1.0/ttpositions.aspx
- The API is fairly simple and robust, there's no need for tokens or 2FA or oauth, a simple custom string will provide the data needed:
- The API in this instance is simply a HTTPS query with some parameters, you can see this by pasting the following into a browser:
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 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
- 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->ctattgives us tmst, errCd, errNm and eta 'folders'output->ctatt->etagives us a list of folders for all the next upcoming trains.output->ctatt->eta->0gives us the next train.output->ctatt->eta->1gives us the second to next train.output->ctatt->eta->0-arrTgives us the time the next train will arrive in an ISO date format, i.e. "2026-07-12T00:50:16"
- Say return json is
- 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
- 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->tmstgives us the time we made this query, this is handy to find the current time.output->ctatt->route->0gives us all the trains on the first route (which is Blue, as that's what we asked for)output->ctatt->route->0->traingives us a list of folders for all the active trains.output->ctatt->route->0->train->0gives us the first train.output->ctatt->route->0->train->1gives us the second train.output->ctatt->route->0->train->0->nextStaNmgives us the name of it's next station.output->ctatt->route->0->train->0->nextStaIdgives us the ID of it's next station.output->ctatt->route->0->train->0->arrTgives 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 Arduino IDE code for the API is available in example/src/
Description
Notes and sample code for querying the CTA API for train routes and displaying the station mapping utilising an ESP32.

