WORKSMOD.01 / 03

Singapore Bus Companion

A CoreS3 arrival display that watches one Singapore bus service and sounds a timely reminder.

HARDWARE2026PUBLISHED

MOD.01 / ENTRY 03

A dedicated glanceable display for the bus that matters now—not every route in the city.

The CoreS3 joins Wi-Fi, requests the next three arrivals for one stop and service, and sounds once when the next bus enters the chosen alert window. The public sketch contains placeholders only; no Wi-Fi password or LTA key is stored on this website.

DEVICE
M5Stack CoreS3
PLATFORM
Arduino + LTA DataMall v3
SOURCE
Sanitised public sketch

[ 01 / SYSTEM SHAPE ]

03 FUNCTIONS

01

Live arrivals

Reads the next three estimates from LTA DataMall Bus Arrival v3.

02

One clear reminder

Sounds once inside the alert window, then rearms for the following bus.

03

Useful at a glance

Shows service, stop, arrival times, and available load information.

[ 02 / ARDUINO ]

Arrival request and alert loop

The excerpt shows the essential request. The complete downloadable sketch adds Singapore time parsing, the CoreS3 display, reconnect handling, and the speaker pattern.

  1. 01Select M5CoreS3 and install M5Unified plus ArduinoJson.
  2. 02Replace the three credential placeholders locally.
  3. 03Set your five-digit stop code, service number, and preferred alert window.
const char* WIFI_NAME = "YOUR_WIFI_NAME";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* LTA_ACCOUNT_KEY = "YOUR_LTA_ACCOUNT_KEY";
const char* BUS_STOP_CODE = "83139";
const char* BUS_SERVICE = "15";

const int ALERT_MINUTES = 3;
const unsigned long UPDATE_INTERVAL_MS = 20000;

void updateBusArrival() {
  String url =
    "https://datamall2.mytransport.sg/"
    "ltaodataservice/v3/BusArrival"
    "?BusStopCode=" + String(BUS_STOP_CODE) +
    "&ServiceNo=" + String(BUS_SERVICE);

  WiFiClientSecure secureClient;
  secureClient.setInsecure();  // Prototype only; install a CA for production.
  HTTPClient http;
  if (!http.begin(secureClient, url)) return;

  http.addHeader("AccountKey", LTA_ACCOUNT_KEY);
  http.addHeader("accept", "application/json");
  if (http.GET() != HTTP_CODE_OK) {
    http.end();
    return;
  }

  DynamicJsonDocument document(8192);
  if (deserializeJson(document, http.getStream())) {
    http.end();
    return;
  }

  JsonArray services = document["Services"].as<JsonArray>();
  if (!services.isNull() && services.size() > 0) {
    const char* arrival = services[0]["NextBus"]["EstimatedArrival"] | "";
    const int minutes = minutesUntil(arrival);
    drawArrival(minutes);
    if (minutes >= 0 && minutes <= ALERT_MINUTES && !alarmGiven) {
      soundReminder();
      alarmGiven = true;
    }
  }
  http.end();
}

NO LIVE CREDENTIALS FOUND · NOT HARDWARE-BUILD-VERIFIED HERE