- Introduction
- Project Architecture Diagram
- Equipment List
- Project file structure
- Project set up instructions
- API endpoints and responses
Welcome to the repository for BusWhere?!, a e-ink display that displays bus arrival timings for Singapore on an e-ink display, based on user configurations.
BusWhere?! consists of both a webpage as well as hardware components.

- For the webpage, it is built using the MERN (MongoDB, Express, React, Node) Stack.
- For hardware components, the following components were used.
- MCU: XIAO ePaper Display Dev Board with XIAO ESP32-S3 Plus onboard
- Display: 7.5-inch Monocolor ePaper Display (800 x 480 resolution)
Here is an example of the display on the e-ink display. For the current version, users will be able to see the following data.
- Bus stop
- Bus service
- Bus service estimated arrival time in minutes. The timings are rounded down.
- Seat availability
- SEA (for Seats Available)
- SDA (for Standing Available)
- LSD (for Limited Standing)
- Bus type (Refer to logos for information)
The figure below shows how the different parts of the system work together.
The following items & knowledge will be required for this project.
- Hardware components (Comes as a set from SEEED studio under product TRMNL 7.5" (OG) DIY Kit, SKU number 104991005 HERE)
- MCU: XIAO ePaper Display Dev Board with XIAO ESP32-S3 Plus onboard
- Display: 7.5-inch Monocolor ePaper Display
- Battery: 2000mAh Rechargeable Li-ion Battery
- 3D printed enclosure: To be done by user.
- Software components (MERN Stack)
- Database: Knowledge of MongoDB
- Webpage Backend: Knowledge of Express
- Webpage Frontend: Knowledge of React
- Package manager: Knowledge of Node
The following displays the project file structure at a glance.
bus_where
├── README.md
├── backend
│ ├── index.js
│ ├── package-lock.json
│ ├── package.json
│ └── utils
│ ├── extractRelevantBusData.js
│ ├── generateSVG.js
│ ├── getBusStopDetails.js
│ └── packTo1Bit.js
└── frontend
└── app_name
├── README.md
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
└── src
├── App.css
├── App.js
├── App.test.js
├── components
│ ├── AddBusStopCode.jsx
│ ├── AddedBusStopCode.jsx
│ ├── ConvertedBinaryImage.jsx
│ ├── HeaderOne.jsx
│ └── PreviewBusTimings.jsx
├── index.css
├── index.js
├── logo.svg
├── reportWebVitals.js
└── setupTests.js
bus_where/backendcontains files for the backend of the webpage.bus_where/backend/utilscontains helper functions for the API calls.
bus_where/frontend/app_namecontains files for the frontend of the webpage.bus_where/frontend/app_name/src/componentscontains components for the react webpage.
# navigate to frontend directory
cd ./bus_where/frontend/app_name
# ensure node is installed by running the following command
# if node is installed you will get a result with version number
# example: v22.15.0
node -v
# install dependencies
npm install
# create a file named .env in the same directory
# insert the following line into your .env file
REACT_APP_BUS_ARRIVAL_BACKEND_LINK=http://localhost:8080
# start react application
npm start
# navigate to backend directory
cd ./bus_where/backend
# Install dependencies
npm install
# create a file named .env in the same directory
# insert the following lines into your .env file
# You need to obtain your lta_account_key and lta_sdk_key by following instructions for the LTA API User Guide found here: https://datamall.lta.gov.sg/content/dam/datamall/datasets/LTA_DataMall_API_User_Guide.pdf
# You need to obtain your MONGODB_URI by following the guide here: https://www.mongodb.com/docs/atlas/getting-started/
lta_account_key={lta_account_key}
lta_sdk_key={lta_sdk_key}
allowed_origin=http://localhost:3000
MONGODB_URI={MONGODB_URI}
# Run the backend
node index.js
5. Go to http://localhost:3000/ on web browser and configure desired bus stops. Refer to images for more information.
6.1 Look for your computers local ip address using windows command prompt. Look for the ipv4 address.
ipconfig
6.2 Ensure that the ESP32 and computer are connected to the same wifi, by following the configuration code on ESP32 below.
// In your ESP32 code
const char* ssid = "YOUR_WIFI_NAME"; // Must be same network as computer
const char* password = "YOUR_WIFI_PASSWORD";
// CHANGE THIS: Do not use "localhost"
// Use your computer's IP address
String serverName = "http://{REPLACE_WITH_YOUR_COMPUTER_IP_ADDRESS}:8080/api/create-bus-timings-image";
Receives the list of bus stop codes selected by the user on the frontend and saves them to the MongoDB database. This configuration is used to generate the final image for the display.
- URL:
/api/confirm-bus-codes - Method:
POST - Headers:
Content-Type: application/json - Request Body:
["46679", "46671"]
- Success Response (200):
{ "message": "Bus stop codes received and saved successfully" }
The core endpoint for the hardware. It retrieves the latest saved bus stop configuration, fetches live data for all stops, generates an SVG layout, and converts it into a raw 1-bit binary stream compatible with the e-ink display.
- URL:
/api/create-bus-timings-image - Method:
GET - Response Content-Type:
application/octet-stream - Response:
- Returns a binary stream of the image.
- Size: Exactly 48,000 bytes (for 800x480 resolution, 1-bit color depth).
- Format: Packed 1-bit bitmap (each byte represents 8 pixels).





