1 · la unidad de techo (Nodo 1)
se encarga de escuchar
Cuatro micrófonos, un ESP32-S3, su propia batería. Va en el techo como un GPS
gordo. Todo lo acústico pasa aquí arriba — la captura, la IA que decide "¿es esto una
sirena?", y la matemática que calcula de qué dirección vino. Luego solo radia
la respuesta a la pantalla dentro del coche.
la cúpula en Blender. los ejes aún visibles
porque olvidé ocultarlos jaja
en el techo — misma huella
que una antena de aleta
placa principal
cerebroESP32-S3-WROOM-1-N16R8 — 16MB flash, 8MB PSRAM
placa90×90mm, 1.6mm, ENIG (la dorada)
I2S0 (maestro)BCLK=G6, WS=G5, DIN=G4 — lo comparten Este+Oeste
I2S1 (esclavo)DIN=G14 — Norte+Sur. reloj cableado a mano (ver §5)
pin de calib.GPIO10
tempDS18B20 en el brazo este
frec. muestreo44.1kHz, 32-bit, 4 canales
el PCB del nodo 1 recién salido de KiCad. sí, el logo TB está en la serigrafía.
el truco de los micrófonos
Los 4 micros forman una cruz — dos en la línea Este/Oeste, dos en Norte/Sur. Cada
uno va en su propia placa redonda de 14mm (los discos dorados de abajo) conectado con un
cable plano fino. Aquí va lo que me enorgullece: en vez de necesitar cuatro buses de audio
separados, puse dos micros en un bus asignando uno al canal izquierdo y otro
al derecho. Truco de estéreo puro. Hecho en cobre, no en código — el pin de selección del micro izquierdo a
masa, el del derecho a alto.
placa de micro — ese agujero central es
el puerto acústico de Ø1mm
el otro lado. "lft5" = micro izq #5,
mi propio etiquetado
Los dos micros opuestos están a 190mm. Ese hueco es la razón de que la localización de
dirección funcione — el sonido llega a un micro un pelín antes que al otro y mido ese pelín
(cómo, en §4).
placa micro14×14mm, 1.0mm, ENIG
agujero acústicoØ1.0mm justo en el centro, Ø4mm de keepout alrededor
impermeabledisco de GoreTex de Ø5mm pegado bajo el agujero — IP67
distancia base190mm entre micros opuestos, ambos ejes
resolución angular2.3° en bruto → 0.29° tras interpolación 8×
la textura geodésica de la cúpula no es decoración — el PETG plano así de
fino flexa y el viento silba a 120km/h. los triángulos lo rigidizan.
2 · la unidad de cabina (Nodo 2)
la parte que de verdad miras
Va en el salpicadero. Recibe el ángulo del techo por un enlace 2.4GHz de corto
alcance (ESP-NOW), dibuja una flecha en una pantallita, te pita, y también vigila
la radio LoRa por si hay ambulancias emitiendo desde hasta 2km. Registra cada
evento en una tarjeta SD con GPS + hora, por si algún día necesitas las pruebas.
placa del nodo 2 — ese texto "THIS IS FOR THE RECORD" + "b.m.s" es mío. no juzgues.
cerebroESP32-S3-DevKitC
pantalla2.8" ILI9341 TFT por SPI — flecha + grados + etiqueta
radio largo alcanceSX1278 LoRa 433MHz, AES-128, ~2km
GPSBN-880, 10Hz
almacenamientomicroSD — registra cada detección con posición
sonidoMAX98357A amp → altavocito
alimentación12V del coche (USB u OBD)
LoRa y la tarjeta SD comparten un bus SPI para ahorrar pines. bien mientras
no pongas ambos chip-select en bajo a la vez. el firmware nunca lo hace, así que — bien.
3 · el transpondedor
va en la ambulancia
Dispositivo súper simple. Va en un vehículo de emergencia y grita su posición GPS por
LoRa, cifrada, sin parar. Cualquier coche con un Copilot a 2km lo oye y avisa al
conductor 30–60 segundos antes de que la ambulancia se vea o se oiga. €149, para
flotas.
cerebroESP32-S3-WROOM-1-N16R8
radioSX1278 LoRa 433MHz, payload AES-128
GPSBN-880
paqueteID de vehículo + lat/lon + hora UTC + tipo de evento
cuándo emitecada 800–1500ms, aleatorio (ALOHA)
alcance1–2km en ciudad
por qué 433MHz y no las bandas más sofisticadas
La frecuencia más baja rodea mejor los edificios. Las ambulancias trabajan justo en los peores
entornos RF — calles estrechas, camiones aparcados, pasos subterráneos. 433MHz a 100mW me da
2km a través de ese lío. Sin SIM, sin servidor, sin cuota mensual. Esa es toda la idea:
funciona en el túnel donde tu móvil no.
por qué el temporizado aleatorio
Si diez ambulancias lanzan un paquete en un horario fijo, chocan en el aire
siempre y no oyes nada. Variar el intervalo de cada una a un aleatorio 0.8–1.5s
baja la probabilidad de colisión a básicamente cero incluso con una flota apiñada en un
cruce. Truco viejo (se llama literalmente ALOHA, de los 70), sigue siendo el
correcto.
sin handshake, sin ACK. cada paquete va solo con el estado completo del
vehículo. si pierdes uno, el siguiente está a ~1s. dispara y olvida.
4 · cómo funciona de verdad la detección de sirenas
la parte buena
Dos etapas. Primero una red neuronal diminuta corre todo el rato, solo preguntando "sirena: sí/no".
Solo cuando dice sí arranco la matemática pesada de localización. Mantiene el chip
frío y la batería viva mientras solo conduces.
-
capturar audio — 44.1kHz, los 4 micros
Los dos buses I2S vuelcan a memoria sin parar. Divido cada buffer estéreo en
4 flujos mono separados.
~23ms por trozo
-
convertir 1 segundo de sonido en 40 números (MFE)
Energía de banco de filtros Mel — básicamente "cuánta energía en cada banda de tono, ponderada
como oye el oído". Las sirenas tienen un aullido muy concreto de 700–1800Hz que enciende esto
en un patrón reconocible.
ventana de 1s, desliza cada 0.5s
-
la red neuronal adivina
Esos 40 números entran en una pequeña red Conv1D que entrené en Edge Impulse. Cuatro
respuestas posibles: sirena / fondo / motor / claxon.
Solo importa "sirena"; las otras tres existen para no dar falsa alarma con un taxi
pitando.
~50ms, 90% de acierto en mi set de prueba
-
calcular la dirección (GCC-PHAT)
Ahora entra la matemática. Mido cuánto más tarde llegó el sonido al micro lejano frente
al cercano, en ambos ejes, y la trigonometría da el ángulo. Detalle abajo.
corre ~10× por segundo una vez disparado
-
suavizar y enviar la flecha
El ángulo en bruto baila ±2°, así que promedio las últimas 5 lecturas (con cuidado, para que 359° y
1° no promedien a 180°). Luego ESP-NOW dispara el ángulo final a la unidad del salpicadero.
todo, sirena→flecha, en menos de medio segundo
la matemática de dirección, en serio
El sonido desde tu izquierda llega al micro Este antes que al Oeste. El desfase es minúsculo
— microsegundos — pero a 44.1kHz lo mido hasta una fracción de muestra.
Multiplica ese desfase por la velocidad del sonido y obtienes la diferencia de distancia, que
con la separación de 190mm da el ángulo. Hazlo en ambos ejes, mete los dos en
atan2, y obtienes los 360° completos.
// este/oeste en el mismo bus, ya están sincronizados
tdoa_EW = gcc_phat_lag(east, west, N) / Fs;
// norte/sur necesitan quitar el offset medido en arranque (ver §5)
tdoa_NS = (gcc_phat_lag(north, south, N) - pipeline_offset) / Fs;
c = 331.3 + 0.606 * temp; // velocidad del sonido, corregida por temp.
angle = atan2f(tdoa_EW*c, tdoa_NS*c) * 180/PI;
if (angle < 0) angle += 360;
Por qué GCC-PHAT y no correlación normal: los ecos. Dentro de un coche, en paredes,
en furgonetas aparcadas — la correlación cruzada normal se engaña con los reflejos y apunta la
flecha a una pared. PHAT blanquea la señal primero para que el pico real siga nítido y los
picos de eco se aplanen. Movimiento estándar para esto, pero hay que saber hacerlo.
la corrección de temperatura importa más de lo que crees — la velocidad del
sonido cambia ~12% entre una mañana de invierno y una tarde de verano. ignórala y tu
flecha se desvía varios grados en un día caluroso. para eso está el DS18B20.
5 · el problema del I2S que se comió dos semanas
si vas a clonar esto, LEE
Este es el bug que casi mata el proyecto. Los dos buses de audio no arrancan en
el mismo instante exacto — se desfasan una muestra o dos. Para el par Este/Oeste no
importa (mismo bus). Para Norte/Sur es un error fijo permanente metido en
cada ángulo. La flecha está mal, siempre, y no ves por qué.
arreglo parte 1 — cablea el reloj del esclavo A MANO
El segundo bus es esclavo; necesita el reloj del maestro. Poner los pines en
"unused" no los conecta solos como sugiere la documentación. Tienes que cablear
la matriz GPIO explícitamente o el esclavo se queda ahí produciendo silencio y
pierdes un día pensando que el micro está muerto.
// sin estas dos líneas, bus 2 = silencio. pregúntame cómo lo sé.
esp_rom_gpio_connect_in_signal(BCLK, i2s_periph_signal[1].s_rx_bck_sig, false);
esp_rom_gpio_connect_in_signal(WS, i2s_periph_signal[1].s_rx_ws_sig, false);
esos nombres de campo (s_rx_bck_sig etc) cambian entre versiones de ESP-IDF.
revisa tu propio soc/i2s_periph.h antes de flashear o ni siquiera compilará.
arreglo parte 2 — habilita el esclavo primero, el maestro después
El esclavo tiene que estar en su estado de espera antes de que llegue el primer flanco de reloj,
o se pierde el arranque y el offset se hace mayor y aleatorio. Haz los dos enables dentro de
una sección crítica para que nada interrumpa entre ellos.
taskENTER_CRITICAL(&i2s_mux);
i2s_channel_enable(rx1); // esclavo primero, espera el reloj
i2s_channel_enable(rx0); // maestro después, su primer tick despierta al esclavo
taskEXIT_CRITICAL(&i2s_mux);
arreglo parte 3 — mide el offset restante en cada arranque
Aun con todo eso, quedan 0, 1 o 2 muestras de holgura, y puede ser
distinta en cada encendido. Así que en cada arranque meto un pulso de 100µs en ambos buses a la
vez por GPIO10, correlaciono las dos grabaciones, y eso me dice el offset
exacto. Lo guardo, lo resto de cada lectura Norte/Sur para siempre.
Nunca lo hardcodees — cambia entre arranques.
// pulso ambas entradas, releo, correlaciono
pipeline_offset = gcc_phat_lag(s0, s1, 512);
// debería volver 0, 1 o 2 — y el MISMO número 10 arranques seguidos.
// si salta, tu orden de enable está mal. vuelve a la parte 2.
dos semanas en esto. el arreglo final son ~6 líneas. así es lo
embebido, amigo.
resumen rápido — la cadena de radio
unidad de techo
──ESP-NOW 2.4GHz, ~1ms──▶
unidad salpicadero
◀──LoRa 433MHz, ~2km──
transpondedor ambulancia
Tres saltos, tres radios, cada una elegida para su tarea: ESP-NOW porque es
instantánea y no necesita router; LoRa porque llega a 2km por una ciudad sin
infraestructura; y todo funciona sin nada de internet porque el túnel es
justo donde más lo necesitas.