Objetivos
En este laboratorio aprenderás a:
- Entender qué es un sensor y cómo es el sensor de luz por dentro.
- Leer valores del entorno físico con
cp.light. - Entender la sensibilidad, el muestreo y la calibración de un sensor.
- Tomar decisiones y crear un sistema que reacciona a la luz del ambiente.
- Construir el sistema en 3 fases: sentir → medir bien → adaptarse.
Material necesario
- Circuit Playground Express
- Cable USB
- Computadora con CircuitPython
- Ambiente con luz (una lámpara o luz natural) y, si puedes, una linterna
Introducción
Hasta ahora tus programas han hecho cosas interesantes:
- Encender luces
- Responder botones
- Crear animaciones
Pero todos tienen algo en común:
👉 tú les dices qué hacer directamente
Ahora vamos a dar un salto importante:
👉 hacer que el programa observe el mundo real
🌍 ¿Qué es un sensor?
Un sensor es un componente que permite que una máquina “perciba” el entorno.
Es como un sentido humano:
- Ojos → ven luz
- Oído → escucha sonido
- Piel → siente temperatura
La placa Circuit Playground Express tiene sensores que hacen lo mismo.
🧠 Ejemplo cotidiano
Cuando tu celular baja el brillo automáticamente:
👉 está usando un sensor de luz
💡 Curiosidad:
Los autos modernos usan más de 100 sensores diferentes.
🤖 La gran idea: Computación Física
Lo que estás a punto de hacer tiene un nombre de verdad, el que usan los ingenieros:
Computación Física.
🧩 Computación Física: es programar tu placa para que sienta lo que pasa a su
alrededor (la luz, el movimiento, el sonido), lo piense con su código y responda
en el mundo real: encendiendo sus luces o haciendo sonar su bocina.
Hasta ahora tus programas vivían dentro de la pantalla. Con la Computación Física, el
código sale al mundo: siente lo que le rodea y actúa sobre él.
🔄 El ciclo: sentir → pensar → responder
Toda la Computación Física es este ciclo, una y otra vez:
- Sensación (los sensores): la placa convierte el mundo real en números. Sus
"ojos" (el sensor de luz) o su "equilibrio" (el acelerómetro) le mandan valores al
código. - Mapeo (la regla en el código): una fórmula que traduce esos números en los
números que necesita la respuesta. - Respuesta (luces y música): con esos nuevos números, la placa enciende sus LEDs o
toca notas en su bocina.
👉 Es el mismo entrada → proceso → salida de una computadora, pero tocando el mundo real.
🔁 El concepto clave: el Mapeo
El corazón de todo es el mapeo.
🗺️ Mapeo (mapping): es la regla de tu código que traduce los números que la
placa siente a los números exactos que necesita para responder.
Un ejemplo que verás en tu misión: convertir la luz en un tono musical. La placa
siente la luz, una fórmula la traduce a una frecuencia, y la bocina la toca:
luz = cp.light # SIENTE: mide la luz del ambiente
tono = 200 + int(luz) * 3 # MAPEA: traduce esa luz a una nota
cp.play_tone(tono, 0.1) # RESPONDE: suena esa nota
Con más luz sale un número más grande, el mapeo lo convierte en un tono más agudo, y la
placa se vuelve un instrumento que tocas con la luz, como un theremin. ¡Cambiar la
fórmula del mapeo cambia por completo cómo se comporta la placa!
🌎 Esto se usa de verdad: dominar el mapeo es aprender la misma lógica de los
ingenieros profesionales:
- Sintetizadores: traducen qué tan fuerte pulsas una tecla en el volumen y el timbre
exactos de la nota.- Prótesis biónicas: traducen la fuerza de un músculo en la presión justa para cerrar
los dedos de una mano robótica.- Vehículos autónomos: traducen la distancia que miden sus sensores en la fuerza
exacta de frenado.- Robots espaciales: traducen la inclinación del suelo en la tracción de sus ruedas
para explorar Marte sin volcarse.
🔬 El sensor de luz por dentro
El sensor de luz es un componente chiquito de la placa, marcado con un dibujito de
ojo 👁️. Su nombre técnico es fototransistor (modelo ALS‑PT19). Sus características:
- Convierte luz en número: cuanta más luz le llega, más grande es el número (
cp.light
te da0a oscuras y valores altos con mucha luz). - Solo mide el brillo, no el color: para él, una luz roja y una azul del mismo brillo
dan casi el mismo número. - Mira hacia arriba: capta la luz que tiene enfrente; si lo tapas o lo apuntas a
una lámpara, el número cambia mucho. - Es muy rápido: puede darte cientos de lecturas por segundo.
💡 Curiosidad: es pariente del sensor que baja solo el brillo de la pantalla de tu
celular cuando entras a un lugar oscuro.
🧭 El laboratorio en 3 fases
Vamos a construir el sistema como lo haría un ingeniero: primero lo básico, luego lo
hacemos confiable, y al final lo hacemos adaptable.
- 🟢 Fase 1 — Sentir la luz: encender LEDs y cambiar de color según la luz.
- 🟡 Fase 2 — El muestreo: medir bien para que la lectura no titile.
- 🔵 Fase 3 — La calibración: que funcione en cualquier salón.
🟢 Fase 1 — Sentir la luz
Paso 1: recordar cómo encender un LED
Antes de sentir, recordemos responder. Esto enciende el primer LED en verde:
from adafruit_circuitplayground import cp
cp.pixels.brightness = 0.2
cp.pixels[0] = (0, 255, 0) # enciende el LED 0 en verde
Paso 2: leer el sensor
Ahora el ojo. cp.light te da un número con la luz del ambiente:
luz = cp.light # lee cuánta luz hay
print(luz) # míralo en la consola
Paso 3: color según la luz (4 niveles)
Juntamos las dos ideas: leer la luz y responder con un color distinto según su nivel.
import time
from adafruit_circuitplayground import cp
cp.pixels.brightness = 0.2
while True:
luz = cp.light
if luz < 5:
cp.pixels[0] = (0, 0, 255) # azul: oscuridad
elif luz < 9:
cp.pixels[0] = (0, 255, 255) # cian: poca luz
elif luz < 14:
cp.pixels[0] = (0, 255, 0) # verde: luz media
else:
cp.pixels[0] = (255, 255, 255) # blanco: mucha luz
time.sleep(0.2)
Cómo lo lee el programa: luz = cp.light guarda el número; cada if/elif es un
nivel (< 5 azul, < 9 cian, < 14 verde, si no blanco). Tápalo con el dedo y
acércalo a una lámpara para verlo cambiar.
🎚️ La sensibilidad
Sensibilidad es qué tanto reacciona el sensor a los cambios de luz.
Detalle importante: el sensor de luz no tiene una perilla de sensibilidad en el código.
(El sensor táctil sí, con cp.adjust_touch_threshold(...); el de luz no.) Entonces
la sensibilidad la decides tú, programando:
- Con más niveles (más
if/elif) notas cambios más pequeños de luz: es como
ponerle más peldaños a una escalera. Pasamos de 3 a 4; ¡podrías hacer 5 o 6!
👉 La sensibilidad no vive en el sensor: vive en cómo programas su respuesta.
🟡 Fase 2 — El muestreo
Antes de tocar la placa, hay una gran idea que lo cambia todo.
🌍 El muestreo está en todo (y parece magia)
El muestreo es una herramienta de la Computación Física (como el mapeo). Y una vez
que lo entiendes, lo empiezas a ver en todas partes:
🧩 El mundo cambia todo el tiempo, pero una computadora solo puede observarlo tomando
muestras. La ingeniería consiste en decidir qué medir, cada cuánto medirlo y qué
hacer con esas mediciones.
🚲 Andar en bici: la magia que ya llevas dentro
Imagínate: vas en tu bici a toda velocidad, el viento en la cara. De pronto… ¡una piedra! La
rueda se tuerce, el mundo se ladea, te vas de lado. Pero antes de que alcances a pensar
siquiera "¡me caigo!", tu cuerpo ya reaccionó solo: giraste el manubrio un poquito,
moviste tu peso… y sigues rodando como si nada.
¿Fue magia? No. Es que tu cuerpo, sin que te des cuenta, es una máquina de muestreo
alucinante: tus ojos y tu oído interno (el del equilibrio) revisan tu balance decenas de
veces por segundo, y con cada muestra haces una corrección diminuta:
siento → corrijo → siento → corrijo → siento → corrijo → ...
Todo pasa más rápido de lo que puedes pensarlo. ¿Y sabes por qué al principio te caías
tanto? Porque tu cuerpo todavía no muestreaba lo bastante rápido. Con la práctica
aprendió a medir y corregir sin parar, y un día… ¡ya no te caíste!
❓ El experimento mental: ¿y si solo pudieras revisar tu equilibrio una vez por
segundo, y el resto del tiempo con los ojos cerrados? En ese segundo a ciegas la
bici se inclina más… y más… y cuando abres los ojos, ya no hay nada que hacer. → 💥 ¡al
suelo!
Ese es el secreto: andar en bici ES muestrear. Una computadora hace exactamente lo
mismo —mide, corrige, mide, corrige, miles de veces por segundo— y tú se lo vas a enseñar a
tu CPX.
🚁 El dron que no se cae
Un dron es como tú en la bici, pero con sensores. Cuando lo golpea una ráfaga de viento,
toma cientos de muestras por segundo de si se inclina, gira, sube o cae, y con cada una
corrige sus motores:
muestra → calcular → corregir motores → muestra → calcular → ...
❓ ¿Y si el dron mirara su posición una sola vez por segundo? → 💥 ¡se cae!, igual
que tú en la bici.
Así se ve que el muestreo no es "guardar datos": sirve para tomar decisiones sobre el
mundo físico, muy rápido.
❓ ¿Qué tienen en común tú andando en bici y un dron?
Los dos convierten algo del mundo en datos y corrigen, siguiendo este mismo ciclo:
🌎 MUNDO
↓
SENSOR → MUESTRAS → COMPUTADORA
↓
DECISIÓN → ACCIÓN
↓
🌎 MUNDO
Y eso es exactamente lo que hace tu CPX:
sensor de luz → muestras → Python → decisión → LEDs
Tu placa no es solo "luces que prenden": es una pequeña computadora que observa el mundo
por muestreo y actúa sobre él.
🔧 Ahora sí, a tu placa
Deja el programa de la Fase 1 quieto, sin mover la placa… y verás algo raro: titila,
cambia de color solo. El sensor es tan rápido y sensible que capta cosas que tú no ves. Hay
que aprender a medir bien. A eso se le llama muestreo.
🧪 Nota clara — dos cosas engañan al sensor:
1) La propia luz de la placa. Cuando los NeoPixels se encienden, ¡el sensor también
los ve! Su luz rebota en una tapa, tu mano o la mesa y regresa al sensor, que la cuenta
como si fuera del ambiente. Se crea un lazo: mide luz → prende luces → mide de más →
cambia… sin parar. Es peor a oscuras y con colores brillantes.2) El parpadeo de los focos (y de las pantallas). Los focos del techo titilan ~120
veces por segundo (por la corriente eléctrica). Y las pantallas de computadora,
tablet o celular también parpadean rapidísimo: ajustan su brillo prendiéndose y
apagándose muchas veces por segundo. Tú no lo notas, pero el sensor sí. Una sola
lectura puede caer justo en un parpadeo brillante o en uno oscuro, y el número salta
(¡ojo si la placa está frente al monitor!).
⏱️ ¿Qué es el muestreo?
Muestrear es tomar lecturas del sensor cada ratito; cada lectura es una muestra. La
clave no es tomar una, sino muchas y bien tomadas. Se hace con tres trucos:
- Apagar las luces un instante antes de medir → así el sensor no se mide a sí mismo.
- Promediar varias muestras repartidas en el tiempo → los ~100 ms cubren muchos
parpadeos de los focos y los cancelan. - Suavizar con memoria → recordar un poco el valor anterior para que no brinque.
➗ ¿Qué es un promedio?
Antes de seguir: un promedio (o media) es el "valor típico" de varios números. Se saca
en dos pasos: súmalos todos y divide entre cuántos son.
Piénsalo con dulces: tú tienes 4, un amigo tiene 6 y otro tiene 2. Si los juntan
todos (4 + 6 + 2 = 12) y los reparten en partes iguales entre los 3, a cada quien le
tocan 4. Ese 4 es el promedio: es como si todos tuvieran lo mismo. (Igual que el
promedio de tus calificaciones: si sacas 8, 10 y 6, tu promedio es 24 / 3 = 8.)
Con las lecturas de luz funciona idéntico: 3 lecturas de 40, 60, 50 → la suma es150, y 150 / 3 = 50. Aunque una lectura suba y otra baje, el promedio queda en
medio, más estable. Por eso promediar muchas lecturas es más confiable que fiarte de
una sola.
💻 Código completo de la Fase 2
Aquí está el programa completo: junta la medición cuidadosa (muestreo) con la
respuesta de color por niveles. Cópialo tal cual; abajo lo explicamos paso a paso.
import time
from adafruit_circuitplayground import cp
cp.pixels.brightness = 0.2
luz = cp.light # valor inicial para suavizar
while True:
# --- MEDIR bien (muestreo) ---
cp.pixels.fill((0, 0, 0)) # apaga: no medirte a ti mismo
time.sleep(0.02)
suma = 0
for _ in range(20):
suma = suma + cp.light
time.sleep(0.005) # 20 lecturas en ~100 ms
promedio = suma / 20
luz = luz * 0.6 + promedio * 0.4 # suaviza con memoria
# --- RESPONDER con color según el nivel ---
if luz < 5:
cp.pixels.fill((0, 0, 255)) # azul: oscuridad
elif luz < 9:
cp.pixels.fill((0, 255, 255)) # cian: poca luz
elif luz < 14:
cp.pixels.fill((0, 255, 0)) # verde: luz media
else:
cp.pixels.fill((255, 255, 255)) # blanco: mucha luz
print(int(luz)) # míralo estable en la consola
time.sleep(0.3) # deja ver el color (si no, parpadea)
Con esto, quieto en un lugar, el número se queda estable y la placa deja de titilar.
⚠️ Dos cosas que quizá notes al probarlo:
- Parpadeaba si no ponías la pausa final: como se apagan las luces para medir,
necesitastime.sleep(0.3)al final para dejar ver el color antes de la siguiente
medición.- Valores más bajos que antes: ¡es lo correcto! Ahora mides con los LEDs apagados, así
que ves la luz real (antes, la luz de la placa inflaba el número). Conprint(int(luz))
mira tu mínimo y tu máximo y reparte los 3 umbrales entre ellos:
- salón normal (luz
~10..100) →10 / 30 / 60- salón oscuro (luz
~2..20) →5 / 9 / 14
Truco: un torch de celular justo sobre el ojo 👁️ sube mucho el número. Si tu máximo
es solo ~20, revisa que nada tape el sensor. (La Misión 4 evita esto calibrando.)
🔎 Explicación paso a paso
1. Preparar (antes del bucle).
cp.pixels.brightness = 0.2
luz = cp.light # primer valor, para poder suavizar después
Guardamos un primer valor de luz porque el suavizado (paso 4) necesita recordar el
valor anterior.
2. Apagar para no medirte a ti mismo.
cp.pixels.fill((0, 0, 0))
time.sleep(0.02)
Apagamos los LEDs un instante para que su propia luz no se cuele en la medición (el
"lazo" que vimos arriba).
3. Promediar en el tiempo.
suma = 0
for _ in range(20):
suma = suma + cp.light
time.sleep(0.005) # 20 lecturas en ~100 ms
promedio = suma / 20
Tomamos 20 muestras repartidas en ~100 ms y sacamos su promedio (súmalas todas y
divide entre 20). Esos 100 ms cubren muchos parpadeos de los focos, así que se cancelan.
4. Suavizar con memoria.
luz = luz * 0.6 + promedio * 0.4
Suavizar es que el número cambie poquito a poco, sin brincos. Esta línea es una
mezcla: 60 % del valor anterior + 40 % del promedio nuevo. Como confía más en lo
que ya sabía, una lectura rara casi no lo mueve. Ejemplo: si luz valía 50 y llega un
promedio raro de 100 → 50 * 0.6 + 100 * 0.4 = 70 (no brincó a 100, solo a 70), y en
las siguientes vueltas regresa de a poco a 50.
🔧 Los dos números suman 1 para no cambiar la escala. Más al anterior (
0.8/0.2) =
más suave pero más lento; más al nuevo (0.4/0.6) = más rápido pero más brincón.
5. Responder con color según el nivel.
if luz < 5:
cp.pixels.fill((0, 0, 255)) # azul: oscuridad
elif luz < 9:
cp.pixels.fill((0, 255, 255)) # cian: poca luz
...
Es el mismo mapeo por niveles de la Fase 1, pero ahora usando la luz ya limpia. Como
las luces se apagan para medir, al responder las volvemos a encender con cp.pixels.fill
(las 10 luces).
6. Verlo en la consola y dejar ver el color.
print(int(luz))
time.sleep(0.3) # deja ver el color antes de medir otra vez
Imprime el valor estable y espera un poco. Esa pausa es importante: como enseguida se
apagan las luces para volver a medir, sin ella el color parpadearía y casi no se vería.
🧠 Idea grande: sensibilidad y muestreo son palabras de ingeniería de verdad. Un
buen sistema no solo siente: sabe cada cuánto medir y cómo limpiar lo que mide.
🔵 Fase 3 — La calibración
Ya mides estable, pero queda un problema: cualquier umbral fijo que elijas (como 5,9, 14) solo sirve para un salón con cierta cantidad de luz. En un cuarto más
iluminado la placa se quedaría siempre en blanco; en uno más oscuro, siempre en azul. Cada
lugar es distinto. La solución es calibrar.
🧭 Calibrar es enseñarle al sensor cuáles son los extremos de tu lugar —lo más
oscuro y lo más brillante— para que entienda todo lo que hay en medio.
🚲 Otra vez la bici: el velocímetro
¿Cómo sabe el velocímetro de una bici a qué velocidad vas? Cuenta las vueltas de la
rueda (un imán pasa por un sensor una vez por vuelta… ¡eso es muestreo!). Pero contar
vueltas no basta: para saber la distancia necesita saber cuánto avanza la bici en UNA
vuelta de la rueda (lo que mide la llanta por fuera, su "circunferencia").
Ese número se lo tienes que enseñar, y eso es calibrar el velocímetro. Por ejemplo,
si en una vuelta la bici avanza 2 metros:
1 vuelta → 2 m
100 vueltas → 200 m
⚠️ Si lo calibras mal (le dices que la rueda es más chica de lo real), marcará una
velocidad y una distancia equivocadas. ¡Por eso hay que calibrar bien!
Tu placa calibra distinto (aprende el mínimo y el máximo de luz), pero la idea es la
misma: enseñarle un número de tu mundo real para que sus cuentas salgan bien.
Es como cuando alguien te da a probar lo más dulce y lo más agrio: después ya puedes
juzgar lo que está "a la mitad". Las básculas se ponen en cero, las pantallas táctiles se
calibran… ¡hasta los robots de Marte se calibran al llegar!
La idea en código es guardar el menor y el mayor valor mientras mueves la placa:
if luz < luz_min:
luz_min = luz # el más oscuro que has visto
if luz > luz_max:
luz_max = luz # el más brillante que has visto
Después, en vez de números fijos, usas el porcentaje de luz entre esos dos extremos, y
así funciona en cualquier salón.
💻 Código completo de la Fase 3
Aquí la nave se calibra sola: cada vuelta recuerda el mínimo y el máximo que ha
visto, y colorea según el porcentaje (nivel), no según números fijos. Ya no hay que
adivinar umbrales: funciona en cualquier salón.
import time
from adafruit_circuitplayground import cp
cp.pixels.brightness = 0.2
luz = cp.light
luz_min = 1000 # se irá bajando
luz_max = 0 # se irá subiendo
while True:
# --- MEDIR bien (apagar, promediar, suavizar) ---
cp.pixels.fill((0, 0, 0))
time.sleep(0.02)
suma = 0
for _ in range(20):
suma = suma + cp.light
time.sleep(0.005)
luz = luz * 0.6 + (suma / 20) * 0.4
# --- CALIBRAR: recordar el menor y el mayor ---
if luz < luz_min:
luz_min = luz
if luz > luz_max:
luz_max = luz
# --- NIVEL: el porcentaje entre el mínimo y el máximo (0 a 1) ---
rango = luz_max - luz_min
if rango < 1:
rango = 1 # evita dividir entre cero
nivel = (luz - luz_min) / rango
# --- RESPONDER según el nivel (ya no hay números fijos) ---
if nivel < 0.25:
cp.pixels.fill((0, 0, 255)) # azul: lo más oscuro
elif nivel < 0.5:
cp.pixels.fill((0, 255, 255)) # cian
elif nivel < 0.75:
cp.pixels.fill((0, 255, 0)) # verde
else:
cp.pixels.fill((255, 255, 255)) # blanco: lo más brillante
print(int(luz), round(nivel, 2))
time.sleep(0.3)
👉 Al arrancar, tápalo con el dedo y acércale una linterna un par de veces: así la
nave aprende su mínimo y su máximo. A partir de ahí, los colores se reparten solos
entre esos dos extremos.
En tu Misión 4 llevarás esto más allá: calibrarás con el botón A y buscarás con el
botón B.
La calibración también es una herramienta de la Computación Física, y se usa en todas
partes:
🌎 La calibración en proyectos reales:
- Básculas: se ponen en cero antes de pesar (si no, marcarían de más).
- Pantallas táctiles: se calibran para saber dónde tocas.
- Termómetros: se calibran con hielo (0 °C) y agua hirviendo (100 °C).
- Impresoras 3D y robots: calibran su posición de inicio al encender.
- Cámaras: el "balance de blancos" es calibrar qué es blanco según la luz del lugar.
- Robots de Marte: calibran sus instrumentos al llegar a un mundo nuevo.
Efecto: sin calibrar, las medidas salen corridas o erróneas; calibrando, el
aparato mide bien y funciona en cualquier condición.
Retos del laboratorio
🟢 Reto 1: Semáforo de luz
Cambia los colores para que:
- Oscuro → rojo
- Medio → amarillo
- Muy brillante → verde
💡 Objetivo: asociar lógica con señales visuales.
🟡 Reto 2: Luz inversa
Haz que:
- Más luz ambiental → menos brillo en el LED
- Menos luz ambiental → más brillo
💡 Objetivo: entender relaciones inversas.
🔴 Reto 3: Alarma de oscuridad
Haz que:
- Si está completamente oscuro → parpadee la luz roja
💡 Objetivo: crear un sistema de alerta.
Resumen del laboratorio
En este laboratorio aprendiste, en 3 fases:
- Sentir: qué es un sensor, cómo es el de luz por dentro y cómo leerlo con
cp.light. - Medir bien: la sensibilidad y el muestreo (apagar las luces para no medirte a
ti mismo, promediar en el tiempo contra el parpadeo, y suavizar con memoria). - Adaptarse: la calibración, para que tu sistema funcione en cualquier salón.
Ahora tu programa ya no solo responde a botones:
👉 ahora siente, mide con cuidado y se adapta al mundo que lo rodea
Valentina Tech