
Chismes del Lince 19: El cuenta pasos del Lince (micro:bit, Bluetooth y móvil)
Hasta ahora todos nuestros chismes usaban lo que lleva el móvil dentro. En este damos un salto: el micro:bit se ata al tobillo, cuenta los pasos y se los manda al móvil por Bluetooth. El móvil los enseña en grande, calcula la cadencia (pasos por minuto) y la compara con el ritmo de un metrónomo.
Por qué en el tobillo: al pisar, el pie da un golpe seco que el acelerómetro detecta muy bien. En la muñeca los movimientos son más suaves y variados, y el contador falla mucho más.
Lo que necesitas
- Un micro:bit V2 con su portapilas.
- Una goma ancha, una muñequera o un calcetín alto para sujetarlo.
- Un móvil Android con Chrome. En iPhone, Safari no sirve: hay que abrir la página en el navegador Bluefy.
- El ordenador con MakeCode para programar.
Paso 1. Programa el micro:bit
⚙️️ Configuración previa en MakeCode (Muy importante)
Para que la micro:bit pueda comunicarse con esta página sin dar errores de conexión ni pedir un código PIN de emparejamiento, debes cambiar este ajuste en MakeCode antes de grabar el programa:
- Abre tu proyecto en MakeCode.
- Haz clic en el icono del engranaje ⚙️ (arriba a la derecha).
- Selecciona Configuración del proyecto (Project Settings).
- Busca la sección de Bluetooth y marca la opción:👉 «No Pairing Required: Anyone can connect via Bluetooth» (Sin emparejamiento).
- Haz clic en Guardar, vuelve a descargar el archivo
.hexy pásalo a tu micro:bit.
¡Listo! Con este ajuste, la micro:bit aceptará la conexión web al instante al pulsar el botón Conectar.
Crea un proyecto nuevo en MakeCode. Añade la extensión Bluetooth (Extensiones → bluetooth). Después, en Ajustes del proyecto, activa «Sin emparejamiento: cualquiera puede conectarse». Sin esto, la página no podrá conectar.
Crea tres variables: pasos, ultimo y ahora. El programa en bloques es este:
- al iniciar: Bluetooth → Más → bluetooth servicio uart.
- al conectar Bluetooth: mostrar icono ✔.
- al desconectar Bluetooth: mostrar icono ✘.
- al pulsar el botón A: dar a
pasosel valor 0 y mostrar el número 0. - al pulsar el botón B: mostrar el número
pasos. - para siempre:
- dar a
ahorael valor tiempo de ejecución (ms). - sifuerza de aceleración> 1500 y
ahora−ultimo> 300, entonces:- cambiar
pasospor 2. - dar a
ultimoel valorahora. - Bluetooth → Más → uart bluetooth escribir número
pasos. - Bluetooth → Más → uart bluetooth escribir línea con el texto vacío (así se marca el final del número).
- cambiar
- pausa 10 ms.
- dar a

Si prefieres JavaScript, cambia al modo JavaScript de MakeCode y pega esto (MakeCode lo convierte solo en los bloques de arriba):
javascript
let pasos = 0
let ultimo = 0
let ahora = 0
bluetooth.startUartService()
bluetooth.onBluetoothConnected(function () {
basic.showIcon(IconNames.Yes)
})
bluetooth.onBluetoothDisconnected(function () {
basic.showIcon(IconNames.No)
})
input.onButtonPressed(Button.A, function () {
pasos = 0
basic.showNumber(0)
})
input.onButtonPressed(Button.B, function () {
basic.showNumber(pasos)
})
basic.forever(function () {
ahora = input.runningTime()
if (input.acceleration(Dimension.Strength) > 1500 && ahora - ultimo > 300) {
pasos += 2
ultimo = ahora
bluetooth.uartWriteNumber(pasos)
bluetooth.uartWriteLine("")
}
basic.pause(10)
})
¿Por qué suma 2? Con el micro:bit en un solo tobillo solo notamos la pisada de ese pie. Cada golpe es una zancada, y una zancada son dos pasos.
Descarga el programa y pásalo al micro:bit (conectado por USB, se ve como una unidad).
Paso 2. Colócalo
Sujeta el micro:bit justo encima del tobillo, con la matriz de LEDs hacia fuera y bien apretado: si baila, cuenta de más. Enciéndelo con las pilas y comprueba que se ve el icono ✘ (todavía sin conexión).
Paso 3. Abre la página del móvil
Abre en Chrome: [ENLACE A LA WEBAPP]
- Pulsa Conectar micro:bit.
- Elige «BBC micro:bit […]» en la lista.
- Cuando el micro:bit muestre ✔, camina. El círculo marca cada paso, los números suben y la cadencia se actualiza.
- Activa el metrónomo y mueve el control para elegir un ritmo objetivo. La página te dice si vas al ritmo, o si conviene ir un poco más rápido o más despacio.
Paso 4. Calibra (esto es ciencia)
El número 1500 es el umbral: la fuerza que debe notar el sensor para decir «¡paso!».
- Camina 20 pasos contándolos tú.
- Mira cuántos cuenta el micro:bit.
- Si cuenta de menos, baja el umbral (1200). Si cuenta de más, súbelo (1800).
- Repite hasta que acierte. Prueba también andando despacio y deprisa.
Retos para clase
- ¿Cuál es el mejor umbral para cada persona? Haz una tabla con varios compañeros.
- ¿Y si el micro:bit va en el bolsillo o en la mochila? ¿Cuenta igual?
- ¿Qué pasa con la cadencia al subir una cuesta o unas escaleras?
- Reto avanzado: que el micro:bit pite cuando la cadencia se salga del ritmo objetivo.
Si algo falla
- No aparece en la lista: comprueba que está encendido, que el programa está descargado y que has activado «Sin emparejamiento».
- Se conecta pero no suma: baja el umbral o aprieta más la sujeción.
- Suma de más: sube el umbral o sujétalo mejor.
- En iPhone no conecta: abre la página con Bluefy.
Qué hemos aprendido
Un sensor (el acelerómetro) mide una fuerza. Un umbral convierte esa medida en una decisión. Bluetooth manda el resultado al móvil. Y la cadencia sale de dividir los pasos entre el tiempo que han tardado.
Próximo chisme: el micro:bit recibirá órdenes del móvil.
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Chisme 19 · El cuenta pasos del Lince</title>
<style>
:root{
--bg:#eef2ef; --ink:#10201c; --soft:#4d625b; --card:#ffffff; --line:#cfdad5;
--pine:#1f5b4a; --slow:#2563a8; --fast:#b4501a; --ok:#1f7a4d;
box-sizing:border-box;
padding-top:env(safe-area-inset-top,0px); padding-bottom:env(safe-area-inset-bottom,0px);
}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){
--bg:#0f1a17; --ink:#e8f1ed; --soft:#9db3aa; --card:#172622; --line:#2a3d37;
--pine:#5fc4a2; --slow:#7db3ee; --fast:#f0a06a; --ok:#6fd89f;}}
:root[data-theme="dark"]{--bg:#0f1a17; --ink:#e8f1ed; --soft:#9db3aa; --card:#172622; --line:#2a3d37;
--pine:#5fc4a2; --slow:#7db3ee; --fast:#f0a06a; --ok:#6fd89f;}
*{box-sizing:border-box}
html{scroll-padding-top:env(safe-area-inset-top,0px)}
body{margin:0;background:var(--bg);color:var(--ink);font:17px/1.45 "Trebuchet MS","Segoe UI",system-ui,sans-serif}
main{max-width:30rem;margin:0 auto;padding:1.2rem 1rem 2rem}
h1{font-size:1.3rem;margin:.2rem 0 .1rem}
.sub{color:var(--soft);margin:0 0 1rem;font-size:.95rem}
button{font:inherit;font-weight:700;border:2px solid var(--pine);background:var(--pine);color:var(--bg);
border-radius:.8rem;padding:.75rem 1.1rem;min-height:3rem;cursor:pointer}
button.alt{background:transparent;color:var(--pine)}
button:focus-visible,input:focus-visible{outline:3px solid var(--slow);outline-offset:2px}
#estado{margin:.8rem 0;color:var(--soft);min-height:1.5em}
.ring{position:relative;width:min(78vw,18rem);aspect-ratio:1;margin:1rem auto;border-radius:50%;
border:.5rem solid var(--line);display:grid;place-items:center;text-align:center;
transition:border-color .2s}
.ring.ok{border-color:var(--ok)} .ring.slow{border-color:var(--slow)} .ring.fast{border-color:var(--fast)}
.ring.beat::after,.ring.step::before{content:"";position:absolute;inset:-.5rem;border-radius:50%;
border:.5rem solid var(--pine);opacity:0;animation:p .45s ease-out}
.ring.step::before{border-color:var(--ink)}
@keyframes p{from{opacity:.7;transform:scale(1)}to{opacity:0;transform:scale(1.12)}}
@media (prefers-reduced-motion:reduce){.ring::after,.ring::before{animation:none!important}}
.pasos{font-size:4.6rem;font-weight:800;line-height:1;font-variant-numeric:tabular-nums}
.lbl{color:var(--soft);font-size:.95rem}
.fila{display:flex;gap:.8rem;justify-content:space-between;margin:1rem 0}
.dato{flex:1;background:var(--card);border:1px solid var(--line);border-radius:.8rem;padding:.7rem .9rem}
.dato b{display:block;font-size:2rem;font-variant-numeric:tabular-nums}
#aviso{font-weight:700;text-align:center;min-height:1.6em;margin:.4rem 0 1rem}
.objetivo{background:var(--card);border:1px solid var(--line);border-radius:.8rem;padding:.9rem}
input[type=range]{width:100%;margin:.6rem 0;accent-color:var(--pine)}
.botones{display:flex;gap:.6rem;flex-wrap:wrap}
</style>
</head>
<body>
<main>
<h1>El cuenta pasos del Lince</h1>
<p class="sub">Micro:bit en el tobillo, móvil en la mano.</p>
<div class="botones">
<button id="conectar">Conectar micro:bit</button>
<button id="cero" class="alt">Poner a cero</button>
</div>
<div id="estado" role="status">Sin conectar.</div>
<div class="ring" id="ring">
<div><div class="pasos" id="pasos">0</div><div class="lbl">pasos</div></div>
</div>
<div class="fila">
<div class="dato"><span class="lbl">Cadencia real</span><b id="cad">0</b><span class="lbl">pasos/min</span></div>
<div class="dato"><span class="lbl">Ritmo objetivo</span><b id="obj">100</b><span class="lbl">pasos/min</span></div>
</div>
<div id="aviso" aria-live="polite"></div>
<div class="objetivo">
<label for="bpm">Ritmo objetivo del metrónomo</label>
<input type="range" id="bpm" min="60" max="140" value="100">
<button id="metro" class="alt">Activar metrónomo</button>
</div>
</main>
<script>
const UART = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
const TX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
const $ = id => document.getElementById(id);
let ultimoN = 0, base = 0, ventana = [], ultimoT = 0, cadencia = 0, buffer = "", objetivo = 100;
function flash(cls){ const r=$("ring"); r.classList.remove(cls); void r.offsetWidth; r.classList.add(cls); }
function pintar(){
$("pasos").textContent = Math.max(0, ultimoN - base);
$("cad").textContent = Math.round(cadencia);
const aviso = $("aviso"), ring = $("ring");
ring.classList.remove("ok","slow","fast");
if (cadencia <= 0){ aviso.textContent = ""; return; }
const d = cadencia - objetivo;
if (Math.abs(d) <= 5){ aviso.textContent = "Vas al ritmo"; ring.classList.add("ok"); }
else if (d < 0){ aviso.textContent = "Un poco más rápido"; ring.classList.add("slow"); }
else { aviso.textContent = "Un poco más despacio"; ring.classList.add("fast"); }
}
function linea(texto){
const n = parseInt(texto.split(",")[0], 10);
if (isNaN(n)) return;
if (n < ultimoN){ ventana = []; base = 0; } // el micro:bit se ha puesto a cero (botón A)
ultimoN = n;
const t = performance.now();
ultimoT = t;
ventana.push({t, n});
if (ventana.length > 6) ventana.shift();
if (ventana.length >= 2){
const a = ventana[0], b = ventana[ventana.length-1];
cadencia = (b.n - a.n) / (b.t - a.t) * 60000;
}
flash("step"); pintar();
}
function recibir(ev){
buffer += new TextDecoder().decode(ev.target.value);
let i;
while ((i = buffer.indexOf("\n")) >= 0){
linea(buffer.slice(0, i).trim());
buffer = buffer.slice(i + 1);
}
}
setInterval(() => { // si no hay pasos durante 3 s, la cadencia vuelve a 0
if (cadencia > 0 && performance.now() - ultimoT > 3000){ cadencia = 0; ventana = []; pintar(); }
}, 500);
$("conectar").onclick = async () => {
if (!navigator.bluetooth){
$("estado").textContent = "Este navegador no permite Bluetooth. Usa Chrome en Android (o Bluefy en iPhone).";
return;
}
try {
$("estado").textContent = "Buscando micro:bit…";
const dev = await navigator.bluetooth.requestDevice({
filters: [{ namePrefix: "BBC micro:bit" }], optionalServices: [UART]
});
dev.addEventListener("gattserverdisconnected", () => { $("estado").textContent = "Desconectado. Pulsa Conectar para volver a empezar."; });
const srv = await dev.gatt.connect();
const svc = await srv.getPrimaryService(UART);
const ch = await svc.getCharacteristic(TX);
await ch.startNotifications();
ch.addEventListener("characteristicvaluechanged", recibir);
$("estado").textContent = "Conectado a " + dev.name + ". ¡Camina!";
} catch (e) {
$("estado").textContent = "No se pudo conectar. Comprueba que el programa tiene «Sin emparejamiento» y que el micro:bit está encendido.";
}
};
$("cero").onclick = () => { base = ultimoN; ventana = []; cadencia = 0; pintar(); };
/* Metrónomo */
let ac = null, activo = false, proximo = 0, timer = null;
function bip(t){
const o = ac.createOscillator(), g = ac.createGain();
o.frequency.value = 880; g.gain.setValueAtTime(0.4, t); g.gain.exponentialRampToValueAtTime(0.001, t + 0.08);
o.connect(g).connect(ac.destination); o.start(t); o.stop(t + 0.1);
setTimeout(() => { flash("beat"); if (navigator.vibrate) navigator.vibrate(30); }, Math.max(0, (t - ac.currentTime) * 1000));
}
function planificar(){
while (proximo < ac.currentTime + 0.15){ bip(proximo); proximo += 60 / objetivo; }
}
$("metro").onclick = () => {
if (!activo){
ac = ac || new (window.AudioContext || window.webkitAudioContext)();
ac.resume(); proximo = ac.currentTime + 0.1; timer = setInterval(planificar, 25);
activo = true; $("metro").textContent = "Parar metrónomo";
} else {
clearInterval(timer); activo = false; $("metro").textContent = "Activar metrónomo";
}
};
$("bpm").oninput = e => { objetivo = +e.target.value; $("obj").textContent = objetivo; pintar(); };
pintar();
</script>
</body>
</html>
Etiqueta:acelerómetro, Bluetooth, cadencia, Chismes del Lince, cuenta pasos, makecode, micro:bit, steam, webapp



