- An Express.js server which parses current train data from mta-gtfs and provides an API for easy display of data
- A CircuitPython app to handle display of selected NYC subway station data on a 64x32 LED Matrix via the Adafruit Matrix Portal microcontroller.
- MTA API Key: https://api.mta.info/
- Adafruit Matrix Portal: https://www.adafruit.com/product/4745
- USB-C 5V 4A power supply (to power LED matrix + portal)
- Something like this: https://amzn.com/dp/B091DS2M8X
- 5V Power Cable
- In case the LED matrix didn't come with it, pick this up too: https://www.adafruit.com/product/4767
- 64x32 LED RGB Matrix
- Example: https://www.adafruit.com/product/2277
- Any 64x32 panel works as long as it's compatible with the HUB75 standard
Install the Matrix Portal directly onto the LED Matrix using the 16-pin HUB75 connector. Make sure that you install it with the Portal's USB-C connector facing outwards, and with the Matrix's arrows printed on the rear side pointing up and right (orientation matters).
Connect the forked ends of the power cable to the screw standoffs on the Matrix Portal, using the included screws. Attach the other end to the 4-pin power connector (make sure to connect the right side for 5V & ground).
This project is split in two parts:
- The API (
Server/
) which connects to the MTA's API, and offers an endpoint for the Matrix to retrieve data from over your local Wi-Fi network. - The CircuitPython code for the Matrix Portal (
Embedded/
), which connects to your local Wi-Fi network, queries the API server, and displays upcoming departures for a given MTA subway station.
- Node.js LTS & npm installed on the computer or server which will host the API
- (optional) Forever.js installed globally
sudo npm install forever -g
- Allows the API server to run continuously
- CircuitPython (see "Install CircuitPython" section)
- Adafruit libraries for CircuitPython. The below are needed for this project:
adafruit_matrixportal
adafruit_portalbase
adafruit_esp32spi
neopixel.mpy
adafruit_bus_device
adafruit_requests.mpy
adafruit_fakerequests.mpy
adafruit_io
adafruit_bitmap_font
adafruit_display_text
adafruit_minimqtt
adafruit_datetime
- Sign up for an account and generate an API key from the MTA Real-Time Data Feeds website: https://api.mta.info
- Set up any dependencies on the host machine you'll use for the API. A Raspberry Pi at home works great, or you could also set up on a cloud VPS or managed service that can run node.js.
You can easily spin up your own instance of this project on a service like render.com. Try it now with the button below:
Install dependencies:
# For the API server
$ cd mta-led-sign/Server
$ npm install
In Server/
, create a file called .env
and supply your MTA API token as shown below:
.env
example:
API_KEY=<YOUR_MTA_API_KEY>
After configuring the Server applicaton, you should test it by running it manually. This will be necessary as you will need to access the API in order to find your stationId
which will be required to configure the UI application.
# Run the server manually
$ cd mta-led-sign/Server
$ sudo node app.js
> Node.js server listening on 8080
Get the list of subway stations and find yours. Make note/write down of the stop_id
associated to your subway station:
# In an internet browser, go to the below address, or curl from a console:
# From your server host machine:
$ curl localhost:8080/api/station
# From a separate computer on the same Wi-Fi network:
$ curl <SERVER_IP>:8080/api/station
To quickly generate a secrets.py
config file, you can use the /config
page included in the API server.
Example: http://localhost:8080/config
.
Fill out the fields, click "Save to config", and simply download the file to your sign over USB.
Update the UI configuration file. Use your subway station's stop_id
you wrote down for the mta_station
. Supply a direction, "N", or "S", and the IP + Port or domain name of your API server from step 1:
# Update your secrets.py with Wi-Fi info & subway preferences, ex:
secrets = {
'ssid' : '<YOUR_WIFI_SSID>',
'password' : '<YOUR_WIFI_PASSWORD>',
# http://worldtimeapi.org/timezones
'timezone' : 'America/New_York',
# required for some dependency libraries,
# register for free at io.adafruit.com
'aio_username': '<YOUR_ADAFRUIT_IO_USERNAME>',
'aio_key': '<YOUR_ADAFRUIT_IO_KEY>',
# find this ID from your API endpoint:
# localhost:8080/api/station
'mta_station': 'R03',
# either N or S
'mta_train_direction': 'S',
# update this to the IP:Port or host domain
# that is running your API server
'mta_api_url': '<SERVER_URL_OR_IP:PORT>'
}
After you have flashed your Matrix Portal with CircuitPython, connect it to your computer via USB and copy over all the files inside the Embedded/
folder to its root directory.
The REST API for the server appliation is described below. Data formatted and parsed from mta-gtfs-jl, an NYC MTA API library.
GET /api/status/
[
{
"name": "123",
"status": "GOOD SERVICE",
"text": "",
"Date": "",
"Time": ""
},
{
"name": "456",
"status": "DELAYS",
"text": "<span class=\"TitleDelay\">Delays</span>
<span class=\"DateStyle\">
Posted: 08/16/2019 6:06PM
</span><br/><br/>
Northbound [4], [5] and [6] trains are delayed while crews work to correct signal problems at <b>59 St.</b>
<br/><br/>",
"Date": "08/16/2019",
"Time": " 6:06PM"
},
]
GET /api/status/:trainName/
- GET /api/status/3/
[
{
"name": "123",
"status": "GOOD SERVICE",
"text": "",
"Date": "",
"Time": ""
}
]
GET /api/station/
{
"101": {
"stop_id": "101",
"stop_code": "",
"stop_name": "Van Cortlandt Park - 242 St",
"stop_desc": "",
"stop_lat": "40.889248",
"stop_lon": "-73.898583",
"zone_id": "",
"stop_url": "",
"location_type": "1",
"parent_station": ""
},
"103": {
"stop_id": "103",
"stop_code": "",
"stop_name": "238 St",
"stop_desc": "",
"stop_lat": "40.884667",
"stop_lon": "-73.90087",
"zone_id": "",
"stop_url": "",
"location_type": "1",
"parent_station": ""
},
}
GET /api/station/:stationId/
- GET /api/station/249/
{
"stop_id": "249",
"stop_code": "",
"stop_name": "Kingston Av",
"stop_desc": "",
"stop_lat": "40.669399",
"stop_lon": "-73.942161",
"zone_id": "",
"stop_url": "",
"location_type": "1",
"parent_station": ""
}
GET /api/schedule/:stationId/
- GET /api/schedule/249/
{
"N": [
{
"routeId": "3",
"delay": null,
"arrivalTime": 1565958549,
"departureTime": 1565958549
},
{
"routeId": "3",
"delay": null,
"arrivalTime": 1566000654,
"departureTime": 1566000654
},
],
"S": [
{
"routeId": "3",
"delay": null,
"arrivalTime": 1566000745,
"departureTime": 1566000745
},
{
"routeId": "3",
"delay": null,
"arrivalTime": 1566001280,
"departureTime": 1566001280
},
]
}
GET /api/schedule/:stationId/:trainName/:direction/
- GET /api/schedule/249/3/N/
{
"N": [
{
"routeId": "3",
"delay": null,
"arrivalTime": 1565958549,
"departureTime": 1565958549
},
{
"routeId": "3",
"delay": null,
"arrivalTime": 1566000654,
"departureTime": 1566000654
},
]
}