Button A

Armar + Cargar

Calcula las estadísticas, luego espera un balón dentro de 70 cm. Cuando se detecta, pausa brevemente y ejecuta la secuencia de carga.

Button B

Ciclar tiempo del partido

Avanza por 0 → 60 → 120 → 180 segundos de tiempo de partido simulado. Valores más altos aplican un decaimiento de endurance más fuerte a cada carga.

·
Punto simple
Escaneando — esperando que el balón entre en el rango
○
Círculo
Balón detectado — la carga está por comenzar
C
C
Cargando a la potencia de carga efectiva
F
F
Seguimiento — empuje sostenido después del contacto
B
B
Borde alcanzado — carga o seguimiento detenidos
R
R
Retrocediendo — retirada de 300 ms después del seguimiento
OK
OK
Secuencia completa — listo para ejecutar de nuevo
🔘
Presionar A
Calcular estadísticas de la asignación actual
→
🔍
Escanear
Sondear ultrasónico cada 50 ms, mostrar punto
→
⚽
Balón dentro
Distancia < 70 cm — mostrar círculo, pausar 500 ms
→
⚡
Cargar
chargePower completo hasta borde o contacto (2× sonar ≤ 5 cm)
→
Borde alcanzado
🛑
Detener
Mostrar B · listo
Contacto
💥
Seguimiento
Empujar a currentPower durante FOLLOW_THROUGH_MS
→
↩️
Retroceder
Retirada de 300 ms · mostrar R
→
✅
OK
Secuencia completa
Fase de Carga
  • Se ejecuta a effectiveCharge (chargePower reducido por endurance)
  • Sondea sensores de borde + sonar cada 20 ms
  • Termina al alcanzar el borde O al confirmar contacto
  • Dos lecturas consecutivas de sonar ≤ 5 cm = contacto
Fase de Seguimiento
  • Solo se ejecuta si se hizo contacto (no borde alcanzado)
  • Cambia a currentPower (velocidad crucero)
  • Duración = 120 + (POWER × 35) ms
  • Aún verifica el borde — se detiene de inmediato si lo alcanza
  • Retrocede 300 ms y luego se detiene
on start — Asignación de Estadísticas
Cargando bloques…
let SPEED = 0
let ENDURANCE = 0
let TURNING = 0
let POWER = 0
              
Los cuatro números que editas antes de cada prueba. Deben sumar 20. Ingresa tu valor real de TURNING — cuenta hacia el total — pero la prueba de power no lo usa. Solo SPEED, ENDURANCE y POWER afectan lo que sucede en el campo.
on button A pressed — Armar y Cargar
Cargando bloques…
function computeStats() {}
function waitForBall() {}
function chargeAndFollowThrough() {}
input.onButtonPressed(Button.A, function () {
    computeStats()
    waitForBall()
    basic.pause(500)
    chargeAndFollowThrough()
})
              
Tres pasos en secuencia. Primero, computeStats() convierte tus estadísticas en valores de motor funcionales. Luego waitForBall() mantiene el robot en su lugar hasta que algo entra en el rango. Después de una pausa de 500 ms (para que puedas alejarte), chargeAndFollowThrough() ejecuta la secuencia de carga completa.
on button B pressed — Ciclar Tiempo del Partido
Cargando bloques…
let simulatedMatchTime = 0
input.onButtonPressed(Button.B, function () {
    if (simulatedMatchTime == 0) {
        simulatedMatchTime = 60
        basic.showString("60s")
    } else if (simulatedMatchTime == 60) {
        simulatedMatchTime = 120
        basic.showString("120s")
    } else if (simulatedMatchTime == 120) {
        simulatedMatchTime = 180
        basic.showString("180s")
    } else {
        simulatedMatchTime = 0
        basic.showString("0s")
    }
    basic.pause(500)
    basic.clearScreen()
})
              
Simula qué tan cansado está el robot. Presiona B antes de A para probar cómo se ve una carga al final del partido. A 180 s, las altas tasas de decaimiento reducen significativamente tanto currentPower como effectiveCharge. Presiona antes de armar — no tiene efecto en medio de la carga.
computeStats — Convertir Estadísticas en Valores del Motor
Cargando bloques…
let SPEED = 0
let ENDURANCE = 0
let POWER = 0
let simulatedMatchTime = 0
let currentPower = 0
let chargePower = 0
let FOLLOW_THROUGH_MS = 0
let DECAY_RATE = 0
let endurancePercent = 0
let enduranceFactor = 0
function computeStats () {
    currentPower = SPEED * 100 / 20
    chargePower = Math.min(POWER * POWER, 100)
    FOLLOW_THROUGH_MS = 120 + POWER * 35
    DECAY_RATE = 20 - ENDURANCE
    endurancePercent = 100 - DECAY_RATE * simulatedMatchTime / 40
    endurancePercent = Math.max(endurancePercent, 20)
    enduranceFactor = endurancePercent / 100
    currentPower = Math.round(currentPower * enduranceFactor)
    currentPower = Math.max(currentPower, 15)
}
              
Todo lo derivado de tus cuatro estadísticas sucede aquí. currentPower es tu velocidad crucero — usada durante el seguimiento. chargePower es POWER al cuadrado, limitado en 100. FOLLOW_THROUGH_MS es 120 + (POWER × 35) ms. Luego el decaimiento de endurance reduce todos los valores de potencia según qué tan avanzado esté el partido. Ambos tienen un piso mínimo de 15%.
waitForBall — Escanear Hasta que Algo Entre en el Rango
Cargando bloques…
let dist = 0
let BALL_DETECT_CM = 0
function waitForBall () {
    basic.showLeds(`
        . . . . .
        . . . . .
        . . # . .
        . . . . .
        . . . . .
        `)
    while (true) {
        dist = CutebotPro.ultrasonic(SonarUnit.Centimeters)
        if (dist > 0 && dist < BALL_DETECT_CM) {
            basic.showLeds(`
                . . . . .
                . # # # .
                # . . . #
                # . . . #
                . # # # .
                `)
            basic.pause(300)
            basic.clearScreen()
            return
        }
        basic.pause(50)
    }
}
              
Sondea el sensor ultrasónico cada 50 ms. El punto en el LED significa que está escaneando. Cuando algo entra en el rango (dist < BALL_DETECT_CM, por defecto 70 cm), muestra el círculo y regresa — devolviendo el control al Botón A, que luego espera 500 ms y ejecuta la carga. La verificación dist > 0 filtra las lecturas de ruido de 0 cm que obtienes cuando el sensor está demasiado cerca para obtener un ping limpio.
chargeAndFollowThrough — La Secuencia de Carga Completa
Cargando bloques…
let chargeHitBorder = false
let chargePower = 0
let enduranceFactor = 0
let effectiveCharge = 0
let borderSide = 0
let dist = 0
let contactCount = 0
let contactMade = false
let currentPower = 0
let followStart = 0
let FOLLOW_THROUGH_MS = 0
let CHARGE_CHECK_MS = 0
function checkBorder() { return 0 }
function chargeAndFollowThrough () {
    chargeHitBorder = false
    effectiveCharge = Math.round(chargePower * enduranceFactor)
    effectiveCharge = Math.max(effectiveCharge, 15)
    basic.showString("C")
    CutebotPro.pwmCruiseControl(effectiveCharge, effectiveCharge)
    while (!(chargeHitBorder) && !(contactMade)) {
        borderSide = checkBorder()
        if (borderSide > 0) {
            CutebotPro.stopImmediately(CutebotProMotors.ALL)
            chargeHitBorder = true
            basic.showString("B")
            break
        }
        dist = CutebotPro.ultrasonic(SonarUnit.Centimeters)
        if (dist <= 5) {
            contactCount += 1
            if (contactCount >= 2) {
                contactMade = true
            }
        } else {
            contactCount = 0
        }
        basic.pause(CHARGE_CHECK_MS)
    }
    CutebotPro.stopImmediately(CutebotProMotors.ALL)
    if (!(chargeHitBorder)) {
        basic.showString("F")
        CutebotPro.pwmCruiseControl(currentPower, currentPower)
        followStart = input.runningTime()
        while (input.runningTime() - followStart < FOLLOW_THROUGH_MS) {
            borderSide = checkBorder()
            if (borderSide > 0) {
                CutebotPro.stopImmediately(CutebotProMotors.ALL)
                basic.showString("B")
                break
            }
            basic.pause(CHARGE_CHECK_MS)
        }
        CutebotPro.stopImmediately(CutebotProMotors.ALL)
        basic.showString("R")
        CutebotPro.pwmCruiseControl(-1 * currentPower, -1 * currentPower)
        basic.pause(300)
        CutebotPro.stopImmediately(CutebotProMotors.ALL)
    }
    basic.showString("OK")
    basic.pause(500)
    basic.clearScreen()
}
              
Dos fases seguidas. La fase de carga conduce a effectiveCharge (chargePower reducido por endurance) y verifica los sensores cada 20 ms. Sale al alcanzar el borde O con dos lecturas de sonar consecutivas ≤ 5 cm. Si se hizo contacto y no se alcanzó el borde, se ejecuta la fase de seguimiento — empujando a currentPower durante FOLLOW_THROUGH_MS. Luego retrocede 300 ms y muestra OK.
checkBorder — Leer los Sensores de Línea
Cargando bloques…
let sensor1State = 0
let sensor2State = 0
let sensor3State = 0
let sensor4State = 0
let BORDER_THRESHOLD = 0
function checkBorder () {
    CutebotPro.trackbitStateValue()
    sensor1State = CutebotPro.trackbitgetGray(TrackbitChannel.One)
    sensor2State = CutebotPro.trackbitgetGray(TrackbitChannel.Two)
    sensor3State = CutebotPro.trackbitgetGray(TrackbitChannel.Three)
    sensor4State = CutebotPro.trackbitgetGray(TrackbitChannel.Four)
    if (sensor1State > BORDER_THRESHOLD) {
        return 1
    } else if (sensor4State > BORDER_THRESHOLD) {
        return 2
    } else if (sensor2State > BORDER_THRESHOLD || sensor3State > BORDER_THRESHOLD) {
        return 3
    }
    return 0
}
              
Lee los cuatro sensores y devuelve un código de dirección. Se llama dentro de los bucles de carga y seguimiento cada 20 ms. Devuelve 0 si todos los sensores están despejados. Devuelve 1 (izquierda), 2 (derecha) o 3 (directo) cuando se detecta el borde blanco. El código llamador detiene los motores de inmediato ante cualquier retorno distinto de cero.
Frente del robot (dirección de desplazamiento)
S1
S2
S3
S4
S1 = extremo izquierdo S2 = centro izquierdo S3 = centro derecho S4 = extremo derecho
Retorno Significado Activado por
0 Despejado Ningún sensor sobre el umbral
1 Borde izquierdo S1 > 200
2 Borde derecho S4 > 200
3 Directo S2 or S3 > 200
ℹ️
La verificación del borde durante la carga detiene el robot independientemente del lado que active. No esquiva los bordes — simplemente se detiene y muestra "B".

El sensor ultrasónico puede dar una lectura de 0 cuando está demasiado cerca para obtener un ping limpio — pero un solo 0 puede ser ruido. El programa confirma el contacto con dos lecturas bajas consecutivas.

1
El ultrasónico lee ≤ 5 cm — contactCount se incrementa a 1. Aún no confirmado.
2
La siguiente lectura (20 ms después) sigue siendo ≤ 5 cm — contactCount = 2. Contacto confirmado, el bucle de carga sale.
✕
Si alguna lectura es > 5 cm, contactCount se reinicia a 0. La secuencia lo trata como ruido y sigue cargando.
Potencia Actual (velocidad crucero)
currentPower = SPEED × 5 (% motor power)
Luego escalada por endurance: currentPower × enduranceFactor. Mínimo 15%.
Potencia de Carga
chargePower = min(POWER², 100)
POWER al cuadrado — los pequeños incrementos en valores altos tienen un gran efecto. POWER=5→25%, POWER=8→64%, POWER=10→100%. Luego escalado por enduranceFactor durante la carga.
Duración del Seguimiento
FOLLOW_THROUGH_MS = 120 + POWER × 35 ms
POWER más alto da tanto un golpe inicial más fuerte como un empuje sostenido más largo después del contacto.
Decaimiento de Endurance
decayRate = 20 − ENDURANCE
endurancePercent = max(100 − decayRate × time / 40, 20)
ENDURANCE menor = decayRate mayor = pérdida de potencia más rápida con el tiempo. El piso es 20% — el robot nunca se queda completamente sin potencia. ENDURANCE=20 significa decayRate=0 — sin decaimiento en absoluto.

⚡ POWER es cuadrático. Pasar de POWER=5 a POWER=6 agrega 11 puntos porcentuales de potencia de carga (36−25=11). Pasar de POWER=9 a POWER=10 agrega 19 puntos (100−81=19). Cada punto de POWER que agregas vale más que el anterior. Invertir fuertemente en POWER tiene retornos crecientes marcados — pero consume presupuesto de estadísticas que no puede ir a SPEED o ENDURANCE.

Una pregunta abierta: la fórmula anterior de follow-through hacía que el robot rebasara el balón con POWER alto, fijando el punto óptimo práctico en P9. La nueva fórmula, 120 + (POWER × 35), produce follow-throughs mucho más cortos en todo el rango (P9 → 435 ms, P10 → 470 ms). Si el rebase sigue siendo un problema en la parte alta — o si el punto óptimo se movió — es una pregunta para la Temporada 1. Compara tus distancias de tiro en cada valor de POWER y deja que los datos del partido decidan.

⚠️
TURNING está declarado pero no se usa. Ingresa tu valor real de TURNING para que las cuatro estadísticas sigan sumando 20 — pero no tiene ningún efecto en esta prueba. No es necesario establecerlo en 0 ni ajustar tu configuración alrededor de él.
Asignación de Estadísticas Presupuesto: 20 / 20
SPEED 8
Potencia de motor crucero = SPEED × 5%
ENDURANCE 7
Tasa de decaimiento = 20 − ENDURANCE (0 = sin decaimiento)
POWER 5
Potencia de carga = POWER² (limitado al 100%) · Seguimiento = 120 + (POWER × 35) ms
Tiempo de partido simulado:
Potencia Crucero
—
% (después de endurance)
Charge Power
—
% (después de endurance)
Follow-Through
—
ms
Endurance Restante
—
% de la potencia base
Carga Base
—
% (POWER², antes del decaimiento)
Tasa de Decaimiento
—
pts / 40 s
Potencia de Carga Efectiva en Cada Momento del Partido
Tiempo del Partido Endurance % Carga Efectiva (mín 15%)
Constante Valor Propósito
BORDER_THRESHOLD 200 Valor en escala de grises por encima del cual un sensor reporta "borde blanco"
BALL_DETECT_CM 70 Distancia máxima (cm) para la detección de balón durante la fase de escaneo
CHARGE_CHECK_MS 20 Intervalo de sondeo durante los bucles de carga y seguimiento
CHARGE_DURATION_MS 800 Declarado pero no usado — constante heredada de una versión anterior
Contact confirm count 2 Lecturas consecutivas de sonar ≤ 5 cm requeridas para confirmar el contacto
Min motor power 15% Piso para currentPower y effectiveCharge
Endurance floor 20% endurancePercent nunca cae por debajo del 20% independientemente del tiempo o el valor de ENDURANCE
Post-scan pause 500 ms Retraso entre la detección del balón y el inicio de la carga
Reverse duration 300 ms Cuánto tiempo retrocede el robot después de completar el seguimiento
  1. 1 Configura tus estadísticas en el código. Edita SPEED, ENDURANCE, POWER y TURNING al final del archivo. Los cuatro deben sumar 20 — ingresa tu valor real de TURNING aunque esta prueba no lo use. Vuelve a programar el micro:bit.
  2. 2 Presiona B para configurar el tiempo del partido. Si quieres probar el comportamiento al final del partido, presiona B para ir a 120 s o 180 s antes de armar.
  3. 3 Posiciona el robot en el campo, mirando la zona objetivo. Deja espacio libre adelante — cargará directo hacia adelante.
  4. 4 Presiona A para armar. Aparece el punto. Rueda el balón hacia el robot desde adelante hasta que esté dentro de 70 cm.
  5. 5 Observa la secuencia. Círculo → C → F (o B) → R → OK. Verifica cuánto se movió el balón y si el robot se detuvo limpiamente.
⛔
Este programa carga al detectar. Una vez que presionas A y un balón (o cualquier objeto) entra en el rango de 70 cm, el robot avanzará a plena potencia de carga. Mantén manos y pies alejados de su trayectoria.