GBC Watchdog: Building a Safety System for Great Ball Contraptions
Great Ball Contraption (GBC) builders know the pain of burnt motors. A single jam can destroy an expensive motor in minutes. This guide shows you how to build a universal safety module that detects jams through sensor fusion and automatically cuts power—saving your motors and your sanity.
Table of Contents
- The GBC Jam Problem
- Jam Detection Strategy
- Hardware Components
- Wiring Diagram
- Arduino Code
- Tuning Parameters
The GBC Jam Problem
What Happens During a Jam
- Ball gets stuck in mechanism
- Motor stalls (can't rotate)
- Motor draws maximum current (stall current)
- Current converts to heat in motor windings
- Within 1-5 minutes: melted insulation, burnt windings, dead motor
Why It's Expensive
- LEGO Power Functions XL Motor: ~$15
- LEGO Technic Large Motor: ~$25
- Large GBC modules may have 3-5 motors
- A single event can cause $50-100+ in damage
Why Manual Monitoring Fails
- GBC events run for hours
- Builders can't watch every module constantly
- Jams can happen in seconds
- By the time you smell burning, it's too late
Jam Detection Strategy: Sensor Fusion
A single sensor isn't reliable enough. We use two sensors together:
| Sensor | What It Measures | Jam Indicator |
|---|---|---|
| Hall Effect | Shaft rotation (RPM) | RPM = 0 |
| INA219 | Motor current draw | Current > threshold |
Detection Algorithm
IF (current > SAFE_LIMIT) AND (rotation_detected == FALSE)
THEN
JAM_CONFIRMED = TRUE
CUT_POWER()
SOUND_ALARM()
Why Both Sensors?
- Current alone: High current could mean heavy load (not jam)
- Rotation alone: Motor could be stopped intentionally
- Both together: High current + no rotation = definite jam
Hardware Components
Bill of Materials
| Component | Purpose | ~Cost |
|---|---|---|
| Arduino Nano | Main controller | $5 |
| INA219 Current Sensor | Measure motor current | $3 |
| Hall Effect Sensor (A3144) | Detect rotation | $1 |
| Small magnet | Attach to shaft | $0.50 |
| Relay Module (5V) | Cut motor power | $2 |
| Piezo Buzzer | Audible alarm | $1 |
| LED (Red) | Visual alarm | $0.10 |
INA219 Current Sensor
The INA219 is an I2C current/voltage monitor. It measures current by sensing voltage drop across a small shunt resistor.
- Current Range: ±3.2A (default), configurable to ±400mA for precision
- Resolution: 0.8mA
- Interface: I2C (address 0x40 default)
Hall Effect Sensor
The A3144 is a digital Hall effect sensor. It outputs LOW when a magnetic field is detected.
- Attach a small magnet to the motor shaft or a gear
- Each rotation triggers one pulse
- Count pulses over time = RPM
Wiring Diagram
┌─────────────────┐
│ Arduino Nano │
│ │
[Motor Power +] ──┬─────────────┤ VIN │
│ │ │
[INA219] │ A4 (SDA) ◄──────┤──── INA219 SDA
│ │ A5 (SCL) ◄──────┤──── INA219 SCL
│ │ │
[Motor Power -] ──┴─────────────┤ GND │
│ │
│ D2 ◄────────────┤──── Hall Sensor OUT
│ │
│ D7 ────────────►┤──── Relay IN
│ │
│ D8 ────────────►┤──── Buzzer +
│ │
│ D9 ────────────►┤──── LED +
│ │
└─────────────────┘
[Power Supply +] ───► [INA219 VIN+] ───► [Relay COM] ───► [Motor +]
[Power Supply -] ───► [INA219 VIN-] ─────────────────────► [Motor -]
[Relay NO] ◄─── (Normally Open)
Arduino Code
/*
* GBC Watchdog Safety System
* Detects motor jams and cuts power automatically
*/
#include <Wire.h>
#include <Adafruit_INA219.h>
// Pin definitions
#define HALL_PIN 2 // Hall sensor (interrupt capable)
#define RELAY_PIN 7 // Relay control
#define BUZZER_PIN 8 // Piezo buzzer
#define LED_PIN 9 // Status LED
// Thresholds (tune these for your motor)
#define CURRENT_LIMIT_MA 800 // mA - above this indicates stall
#define MIN_RPM 10 // Below this = not rotating
#define CHECK_INTERVAL_MS 500 // How often to check
#define ALARM_DURATION_MS 5000 // How long to sound alarm
Adafruit_INA219 ina219;
// Rotation tracking
volatile unsigned long pulseCount = 0;
unsigned long lastPulseCount = 0;
unsigned long lastCheckTime = 0;
// State
bool motorEnabled = true;
bool jamDetected = false;
void hallPulseISR() {
pulseCount++;
}
void setup() {
Serial.begin(115200);
// Initialize pins
pinMode(HALL_PIN, INPUT_PULLUP);
pinMode(RELAY_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
// Start with motor enabled
digitalWrite(RELAY_PIN, HIGH);
digitalWrite(LED_PIN, LOW);
// Initialize current sensor
if (!ina219.begin()) {
Serial.println("INA219 not found!");
while (1);
}
// Attach interrupt for Hall sensor
attachInterrupt(digitalPinToInterrupt(HALL_PIN), hallPulseISR, FALLING);
Serial.println("GBC Watchdog Started");
Serial.println("Monitoring for jams...");
}
void loop() {
unsigned long now = millis();
// Check every interval
if (now - lastCheckTime >= CHECK_INTERVAL_MS) {
lastCheckTime = now;
// Calculate RPM
unsigned long pulses = pulseCount - lastPulseCount;
lastPulseCount = pulseCount;
float rpm = (pulses * 60000.0) / CHECK_INTERVAL_MS;
// Read current
float current_mA = ina219.getCurrent_mA();
// Debug output
Serial.print("Current: ");
Serial.print(current_mA);
Serial.print(" mA, RPM: ");
Serial.println(rpm);
// JAM DETECTION
if (motorEnabled && !jamDetected) {
if (current_mA > CURRENT_LIMIT_MA && rpm < MIN_RPM) {
// JAM CONFIRMED!
jamDetected = true;
triggerAlarm();
}
}
}
// Handle alarm state
if (jamDetected) {
// Blink LED
digitalWrite(LED_PIN, (millis() / 250) % 2);
}
}
void triggerAlarm() {
Serial.println("!!! JAM DETECTED !!!");
// Cut power immediately
digitalWrite(RELAY_PIN, LOW);
motorEnabled = false;
// Sound alarm
for (int i = 0; i < 10; i++) {
tone(BUZZER_PIN, 2000, 200);
delay(300);
}
Serial.println("Motor power cut. Reset to restart.");
}
void resetSystem() {
// Call this to reset after clearing jam
jamDetected = false;
motorEnabled = true;
digitalWrite(RELAY_PIN, HIGH);
digitalWrite(LED_PIN, LOW);
Serial.println("System reset. Monitoring resumed.");
}
Tuning Parameters
Finding Your Current Threshold
- Run motor under normal load
- Note the current reading (e.g., 300mA)
- Briefly stall the motor by hand
- Note the stall current (e.g., 1200mA)
- Set threshold between them (e.g., 800mA)
Typical Values
| Motor | Normal Load | Stall Current | Suggested Threshold |
|---|---|---|---|
| LEGO M Motor | 100-200mA | 500-700mA | 400mA |
| LEGO L Motor | 150-300mA | 800-1000mA | 600mA |
| LEGO XL Motor | 200-400mA | 1000-1400mA | 800mA |
RPM Threshold
- Depends on your gear ratio and magnet placement
- Start with MIN_RPM = 10
- If false positives during slow operation, lower it
- If it misses slow jams, raise it
Enhancements
Multiple Motors
// Use multiple INA219 sensors on different I2C addresses
Adafruit_INA219 ina219_motor1(0x40);
Adafruit_INA219 ina219_motor2(0x41);
Adafruit_INA219 ina219_motor3(0x44);
// Change address with solder jumpers on INA219 board
WiFi Notifications (ESP32)
// Send alert to your phone when jam detected
#include <WiFi.h>
#include <HTTPClient.h>
void sendAlert() {
HTTPClient http;
http.begin("https://maker.ifttt.com/trigger/gbc_jam/with/key/YOUR_KEY");
http.POST("");
http.end();
}
Automatic Reset After Timeout
// Try restarting after 30 seconds (might clear minor jams)
#define AUTO_RESET_MS 30000
if (jamDetected && (millis() - jamTime > AUTO_RESET_MS)) {
resetSystem();
// If jam persists, it will trigger again
}
Community Value
This project solves a real, expensive problem for GBC builders. Consider:
- Sharing your build on GBC community forums
- Open-sourcing the design on GitHub
- Creating a modular PCB design others can order
- Documenting threshold values for common motors
Resources
Use Our Tools to Go Further
Get more insights about the sets mentioned in this article with our free LEGO tools