Why we chose this challenge statement
Around 10,000 Singaporeans face a form of physical disability. Many of these 10,000 require a personal mobility aid (PMA) to carry out day-to-day activities. However, PMAs can cost upwards of $1200. While there are agencies such as the Seniors’ Mobility & Enabling Fund and Assistive Technology Fund that will help these people ensure subsidies on the PMA, a small group of individuals are not qualified for the subsidies. Hence, for our product, we are targeting people with only partial loss of bodily functions, mainly residents below the age of 60 who do not have any form of caregivers who will follow them around in their day-to-day life.
Our solution directly addresses this challenge by reimagining adaptive bicycles with a comprehensive set of hardware and software enhancements. We designed our solutions to support persons with disabilities, caregivers, and their communities, like Ageless Bicyclists, by focusing on four key pillars: Safety: Speed control utilising an anti-lock braking system, GPS integration, crash alerts for caregivers, harnesses and pedal straps, automatic lighting, and better awareness of the quality of the bike. Comfort: A robotic "step-stool", rider-friendly displays, adjustable ergonomics and quick-deploy weather protection. Efficiency: Wayfinding and a robotic cargo robot that utilises hardware that supports what they currently operate, like phones and plastic bags and E-bike pedal assistance.
Inspiration
We studied the role of "Buddies" in adaptive cycling events, such as Volunteer.sg. Buddies:
- Help participants mount/dismount
- Ensure safety when navigating slopes or obstacles
- Provide encouragement, communication and emergency response.
- Facilitate inclusion and social interaction.
Additionally, we obtained insights from conversations with key stakeholders such as Mr Malcolm Chen, the founder of Ageless Bicyclists, who highlighted the reliance on volunteers to prevent risky downhill cycling. He was concerned that expeditions could not be conducted without a huge amount of manpower, manpower they did not have access to. This led to the conclusion that adaptive bicycles must be independently safer, reducing over-reliance on human intervention, which was also prone to human error, especially during fatiguing expeditions. He also mentioned that the quality of the bicycle must be assessed regularly, as he would often find bicycles rusty and unsafe to ride on.
The idea of having an autonomous robot being able to carry cargo originated from a The Straits Times article titled “Drone whisks food orders from Sentosa to St John’s Island in delivery trial”. The article itself gave little to no special technical specifications of the food-carrying drones. However, it did push us to have the idea of having an autonomous cargo-carrying robot. Since the cargo will be disconnected from the main bicycle frame, it would make steering, pedalling, and overall mobility of the bicycle easier. Suitable and hugely relevant for our target audience. link
Our research also covered:
- The specific challenges faced by people with disabilities concerning adaptive bicycles (strength, etc.).
- Existing adaptive bike designs, for example, products by Freedom Concepts, which offer different bikes (handcycles, upright tricycles, etc.) and accessories that cater to every need.
- Other existing designs, such as adjustable seats found in gyms, dropper seat posts used by mountain bikers and step-through frames.
What it does (Usage)
A rider mounts the bike using the top platform of the robotic cargo robot as a step-stool. Meanwhile, the robot is analysing the bike using a Pi camera for any rust or issues with the bicycle. They strap into the bike with a supportive harness. As they cycle:
- Torque sensors detect when the pedalling has started, at the bike assists them with pedalling.
- The bike automatically adjusts to safe speeds downhill, all while communicating speed alerts and speed indicators to the rider.
- GPS and crash detection systems give caregivers peace of mind remotely
- LED strips activate in the evenings for visibility
- If rain starts, the poncho is readily available for deployment.
- The autonomous cargo robot will have a Raspberry Pi Zero camera that will be used to track a unique marker attached to the back of the bike. The robot will be programmed to follow the marker.
- The rider can then place cargo in the bags on the cargo robot.
How we built it
CODE
Tricycle:
// Define the analog pin connected to the LDR sensor
#include <ESP32Servo.h>
#include <WiFi.h>
#include <PubSubClient.h>
#define LDR_PIN 10
#define LED_PIN 11
#define SERVO_PIN1 12
#define SERVO_PIN2 13
#define DARKNESS_THRESHOLD 2500
#define SPEED_THRESHOLD 500
#define BLYNK_TEMPLATE_ID ""
#define BLYNK_TEMPLATE_NAME ""
#define BLYNK_AUTH_TOKEN ""
unsigned long previous_millis;
int speed = 500;
const char* ssid = "wifi name";
const char* password = "password";
const char* mqtt_server = "blynk.cloud";
const int mqtt_port = 1883;
WiFiClient espClient;
PubSubClient client(espClient);
Servo servo1;
Servo servo2;
String pubTopic = String("ds/") + "V1";
String subTopic = "downlink/#";
String payload;
void callback(char* topic, byte* message, unsigned int length) {
Serial.print("📩 Message arrived [");
Serial.print(topic);
Serial.print("] ");
String payload;
for (unsigned int i = 0; i < length; i++) {
payload += (char)message[i];
}
Serial.println(payload);
if (payload.equalsIgnoreCase("1")) {
Serial.println("LED turned on");
digitalWrite(LED_BUILTIN, HIGH);
} else if (payload.equalsIgnoreCase("0")) {
Serial.println("LED turned off");
digitalWrite(LED_BUILTIN, LOW);
}
}
void reconnect() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect("ESP32 IoT Device", "device", BLYNK_AUTH_TOKEN)) {
Serial.println(" connected ✅");
client.subscribe(subTopic.c_str());
Serial.print("Subscribed to: ");
Serial.println(subTopic);
} else {
Serial.print(" failed, rc=");
Serial.print(client.state());
Serial.println(" retry in 5s");
delay(5000);
}
}
}
void lighting(unsigned long current_millis){
int ldrValue = 0;
if (current_millis - previous_millis >= 9000){
ldrValue = analogRead(LDR_PIN);
// Print the LDR to the Serial Monitor
Serial.print("LDR Value = ");
Serial.println(ldrValue);
if (ldrValue >= DARKNESS_THRESHOLD){
digitalWrite(LED_PIN, HIGH);
}
else{
digitalWrite(LED_PIN,LOW);
}
previous_millis = current_millis;
}
}
void servo(int speed){
if(speed >= SPEED_THRESHOLD){
servo1.write(181);
servo2.write(181);
}
else{
servo1.write(1);
servo2.write(1);
}
}
void crash(){
bool state;
int deceleration = 2;
if (deceleration % 2 == 0){
state = true;
}
if (state == true){
payload = "crashed please send help";
client.publish(pubTopic.c_str(), payload.c_str());
Serial.println("published");
}
else{
payload = "No problems";
}
Serial.println(payload);
}
void setup() {
// Start the Serial Monitor at 115200 baud rate for printing values
Serial.begin(115200);
//non-iot setup
pinMode(LED_BUILTIN, OUTPUT);
pinMode(LDR_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
servo1.attach(SERVO_PIN1);
servo2.attach(SERVO_PIN2);
servo1.write(1);
servo2.write(1);
//IoT
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" connected");
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
}
void loop() {
unsigned long current_millis = millis();
if (!client.connected()) { // Keep connection between ESP32 and Blynk
reconnect();
}
client.loop();
crash();
lighting(current_millis);
servo(speed);
}
Cargo bot:
Ultrasonic Sensors
#include <esp_now.h>
#include <WiFi.h>
const int TRIG_PINS[4] = {5, 19, 3, 23};
const int ECHO_PINS[4] = {17, 18, 21, 22};
uint8_t robotAddress[] = {0x20, 0x43, 0xA8, 0x64, 0x39, 0x40};
typedef struct struct_message {
int distances[4];
} struct_message;
struct_message myData;
void setup() {
Serial.begin(115200);
Serial.println("Starting 4-ultrasonic sensor ESP-NOW sender...");
for (int i = 0; i < 4; i++) pinMode(TRIG_PINS[i], OUTPUT);
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
esp_now_peer_info_t peerInfo = {};
memcpy(peerInfo.peer_addr, robotAddress, 6);
peerInfo.channel = 0;
peerInfo.encrypt = false;
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
Serial.println("Failed to add peer");
return;
}
}
void loop() {
for (int i = 0; i < 4; i++) {
myData.distances[i] = readUltrasonicDistance(TRIG_PINS[i], ECHO_PINS[i]);
}
esp_err_t result = esp_now_send(robotAddress, (uint8_t *) &myData, sizeof(myData));
if (result == ESP_OK) {
Serial.printf("Sent: %d %d %d %d\n",
myData.distances[0],
myData.distances[1],
myData.distances[2],
myData.distances[3]);
} else {
Serial.println("Send error");
}
delay(100);
}
int readUltrasonicDistance(int trigPin, int echoPin) {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH, 30000);
if (duration == 0) return -1;
int distanceCm = (int)(0.0343 * duration / 2);
return distanceCm;
}
Motors
#include <WiFi.h>
#include <esp_now.h>
#include "CytronMotorDriver.h"
CytronMD motor1(PWM_PWM, 16, 17);
CytronMD motor2(PWM_PWM, 12, 13);
void onDataRecv(const esp_now_recv_info * info, const uint8_t *incomingData, int len);
int baseSpeed = 150;
int turnAmount = 50;
int sensorValues[4]; // d1, d2, d3, d4
int16_t X; // lateral offset
uint16_t Y; // forward distance
int16_t yaw; // slope/angle
void stopRobot() { motor1.setSpeed(0); motor2.setSpeed(0); }
void moveForward(int speed) { motor1.setSpeed(speed); motor2.setSpeed(speed); }
void rotateLeft(int speed) { motor1.setSpeed(speed); motor2.setSpeed(-speed); }
void rotateRight(int speed) { motor1.setSpeed(-speed); motor2.setSpeed(speed); }
void steerLeft(int baseSpeed, int turn) {
motor1.setSpeed(baseSpeed - turn);
motor2.setSpeed(baseSpeed + turn);
}
void steerRight(int baseSpeed, int turn) {
motor1.setSpeed(baseSpeed + turn);
motor2.setSpeed(baseSpeed - turn);
}
void computePosition() {
// X = ((sensorValues[2] - sensorValues[1]) - (sensorValues[1] - sensorValues[2])) / 2;
Y = (sensorValues[1] + sensorValues[2]) / 2;
int maxValidDistance = 50;
bool leftOuterValid = sensorValues[0] < maxValidDistance;
bool rightOuterValid = sensorValues[3] < maxValidDistance;
int16_t calculatedYaw = 0;
if (leftOuterValid && rightOuterValid) {
calculatedYaw = (sensorValues[3] - sensorValues[0]) * 50 / 35;
}
else if (leftOuterValid) {
calculatedYaw = (sensorValues[1] - sensorValues[0]) * 50 / 35;
}
else if (rightOuterValid) {
calculatedYaw = (sensorValues[3] - sensorValues[2]) * 50 / 35;
}
else {
calculatedYaw = (sensorValues[2] - sensorValues[1]) * 50 / 35;
}
// Optional: small yaw deadzone to reduce jitter
yaw = (abs(calculatedYaw) < 2) ? 0 : calculatedYaw;
}
void onDataRecv(const esp_now_recv_info * info, const uint8_t *incomingData, int len) {
if (len < sizeof(sensorValues)) return;
memcpy(sensorValues, incomingData, sizeof(sensorValues));
computePosition();
Serial.print("d1 d2 d3 d4: ");
for (int i = 0; i < 4; i++) { Serial.print(sensorValues[i]); Serial.print(" "); }
Serial.print(" | X: "); Serial.print(X);
Serial.print(" Y: "); Serial.print(Y);
Serial.print(" yaw: "); Serial.println(yaw);
if(Y < 15) stopRobot();
// else if(abs(X) > 20) {
// if(X > 0) steerLeft(baseSpeed, turnAmount);
// else steerRight(baseSpeed, turnAmount);
// }
else if(abs(yaw) > 1) {
if(yaw < 0) rotateLeft(baseSpeed);
else rotateRight(baseSpeed);
}
else moveForward(baseSpeed);
}
void setup() {
Serial.begin(115200);
Serial.println("Robot ready! Waiting for ESP-NOW sensor data...");
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) {
Serial.println("Error initializing ESP-NOW");
return;
}
esp_now_register_recv_cb(onDataRecv);
}
void loop() {
}
Challenges we ran into
Tricycle:
- Distance Detection Intially, the ultrasonic sensors were placed on the back of the bike and the reflective material was on the cargo bot itself. However, when the bikes turns, the signal will deflect off the reflective material and miss the ultrasonic sensor. This resulted in a the ultrasonic sensors not obtaining any readings.
Cargobot:
Distance Detection: Intially,we wanted to use Relative Signal Strength Indicator (RSSI) for the cargo robot to detect how far the cargo bot is from the bike. However, the signal given off by the antenna of the ESP32 was too strong. The signal given off by access point was roughly the same when the cargo bot was facing away from the bike and when the cargo bot was facing the bike. When that didn’t work we switched the ESP32 to a raspberry PI. Likewise, the strength of the signal produced was too strong and could not differentiate change in small distances easily
Power supply issues: Initially, we used AAA batteries to power the ESP32. However, the ESP32 can only use 3.3V. The AAA batteries were rated at 6V.
Accomplishments that we're proud of
- Our very first Hackthon
- We did not argue, fight or get angry at one another
What we learned
- We learnt how to laser cut and use a benchpress drilling machine
- We learnt how to use IOT to get the ESP32s to communicate with a cloud based platform(Blynk)
- We learnt how to use ESPNOW to get two ESP32s to communicate with each other
What's next for Untitled
Most of the solution is built using off-the-shelf, repairable components retrofitted directly onto existing adaptive bikes. This modular design enables easy replacements and upgrades, reducing e-waste. Building a weatherproof casing and reinforcing the harnesses and pedal straps would extend our solution's lifespan.
In the future, we hope to equip the cargo robot with more advanced features. Said features are voice/hand gesture controls, assisted dismounting, and using Simultaneous Localization and mapping (SLAM) (SLAM) to allow the robot to be fully autonomous in already mapped environments without the need for a marker to be around.
We also had a plan for our tricycle. Utilising machine learning, cameras or LIDAR, the bike could be able to avoid collisions using the existing motors and brakes. If we add steering assistance, it would be able to steer the user in the right direction instantaneously to avoid an incident.
In the long term, we plan to have potential partnerships with adaptive cycling groups, like Ageless Bicyclists. It will be applicable across diverse groups: people with disabilities, the elderly, stroke patients, or even for families. The revenue model would be: Retrofit kits for adaptive or conventional bicycles, service contracts for maintenance for companies and warranty contracts for normal consumers. By scaling, our solution can become cheaper, and therefore more accessible for those with disabilities. It would reduce manpower dependency, make adaptive cycling expeditions viable, and enhance inclusivity for entire communities overall.
Laws
Singapore law requires that autonomous bots capable of automatically following individuals are strictly regulated and, in most cases, prohibited in public spaces to protect personal privacy and safety. Any deployment of mobile robotic platforms, particularly those designed to follow or track people, must comply with privacy regulations and obtain specific permits from the relevant government agencies, usually the Infocomm Media Development Authority (IMDA) or Land Transport Authority (LTA). Unauthorized use of such technologies can lead to significant penalties under Singapore’s laws governing surveillance, harassment, and public safety.
Regarding e-bike assistive pedaling, Singapore enforces clear regulations for Power-Assisted Bicycles (PABs):
- All PABs must be approved by the Land Transport Authority (LTA), bear an official orange seal, and display a number plate.
- The motor must only provide assistance when the rider is pedaling; throttle-only models (where the motor can be activated without pedaling) are banned.
- The maximum continuous power is capped at 250W, and the motor must disengage once the speed reaches 25 km/h or when pedaling stops.
- Riders and passengers must be at least 16 years old and pass a mandatory online theory test.
- It is compulsory to wear a helmet when riding an e-bike, and riding on footpaths is prohibited; use is restricted to roads (excluding expressways), cycling paths, and park connectors.
- Unauthorized modifications, such as installing a throttle, will result in penalties including fines or prosecution.
Log in or sign up for Devpost to join the conversation.