Assuming I already had an encoder for speed control or monitoring, I would use a low speed threshold and acceleration threshold to declare stall. A rapid deceleration of the wheel combined with a low wheel speed when wheel power is on will correlate to a stalled wheel.
Code:
wheel_speed = rpm_from_sensor();
d_wheel_speed = wheel_speed - wheel_speed_last;
wheel_speed_last = wheel_speed;
wheel_des_on_time++;
if(wheel_speed < wss_stall_cal_lt && d_wheel_speed < wss_stall_accel_cal && wheel_desired > 0 && wheel_des_on_time > wss_stall_on_cal)
{
wheel_stall = 1;
}
if(0 == wheel_desired)
{
wheel_stall = 0;
wheel_des_on_time = 0;
}
wss_stall_cal_lt is low, probably about 50% normal running speed
wss_stall_accel_cal is a negative number. I would play with it but I would guess something like -12 for a 10ms loop time. This number should be calibrated based on data traces of stalling the wheel and normal free running.
wss_stall_on_cal is probably around 1/2 the time it takes to spin up to full speed measured in loop iterations (this is determined by fixed iteration time).
No normal conditions except firing a frisbee should create significant negative acceleration. The low speed cal prevents the normal firing from tripping it, and the on time cal prevents battery voltage spikes/dips or other startup oddities from tripping it even if you don't think they would produce a large negative acceleration.