01 · Objetivo
Un medio compartido
En un enlace punto a punto no hace falta decir para quién es un mensaje: hay un solo receptor posible. En cuanto el medio se comparte, aparece el problema. En un bus (RS-485, I²C, CAN), todos los nodos están conectados a los mismos hilos y oyen todo lo que cualquiera transmite. En una radio, todos los receptores en alcance y en el mismo canal captan la misma señal. En una red Ethernet con un concentrador, igual. La consecuencia es siempre la misma: todos reciben todo, y cada receptor tiene que decidir por su cuenta si lo que oyó era para él. Para eso hace falta, dentro de cada paquete, un campo que lo diga: la dirección de destino. Y para que el destinatario sepa a quién contestar, otro: la dirección de origen.
La dirección es entonces un identificador de un nodo en una red, un número que cada nodo conoce como propio y que los demás usan para llamarlo. El tema aborda lo que hace falta alrededor de ese número: cómo se llega a todos o a un grupo, qué tamaño tiene, quién lo asigna, dónde va en el paquete, qué hace el receptor con él y qué pasa cuando muchos nodos hablan sin coordinarse. Es el mecanismo que, en el curso, ya se vio funcionando en distintas formas: la dirección de 7 bits de un dispositivo I²C, el número de esclavo de Modbus, el identificador de un mensaje CAN, la dirección MAC de Ethernet o de BLE.
02 · Un repaso
Las direcciones en los protocolos del curso
Cada protocolo estudiado resolvió «quién es quién» a su manera, y la comparación muestra las decisiones posibles:
| Protocolo | Qué se identifica | Dirección | Cómo se asigna | Difusión |
|---|---|---|---|---|
| UART (tema 3) | Nada: es punto a punto | No hay | — | — |
| SPI (temas 12 a 15) | Un dispositivo, por un hilo | El pin de selección de chip (CS) | Por cableado: un hilo por esclavo | No |
| I²C (temas 16 a 20) | Un dispositivo | 7 bits (o 10) | Fija de fábrica, con pocos bits ajustables por pines | Llamada general (dirección 0) |
| 1-Wire (tema 21) | Un dispositivo | 64 bits (código ROM, con un CRC) | Única de fábrica; el maestro las descubre con una búsqueda | Comando «saltar ROM» |
| Modbus RTU (temas 25 y 26) | Un esclavo | 1 byte: 1 a 247 | Configurada por el instalador (interruptores o un registro) | Dirección 0, sin respuesta |
| CAN (temas 27 y 28) | El contenido del mensaje, no el nodo | 11 o 29 bits de identificador, que además dan la prioridad | Definida en el diseño del sistema | Todos reciben y filtran por identificador |
| Ethernet e IP (temas 29 a 31) | Una interfaz (MAC) y una red (IP) | 48 bits (MAC) y 32 (IPv4) | La MAC, de fábrica; la IP, por DHCP o a mano | FF:FF:FF:FF:FF:FF y 255.255.255.255; multidifusión 224.0.0.0/4 |
| MQTT (tema 32) | Un tema, no un nodo | Cadena de texto (jerarquía de temas) | Diseñada por el usuario | Cualquier suscriptor al tema |
| Wi-Fi y Bluetooth (temas 34 a 38) | Una interfaz de radio | 48 bits | De fábrica (o aleatoria y privada, en BLE) | Sí (ESP-NOW: FF:FF:FF:FF:FF:FF) |
| nRF24L01+ (tema 39) | Un canal lógico de recepción | 3 a 5 bytes; seis canales por receptor | Configurada en el programa | No |
| Zigbee (tema 39) | Un nodo en una red | 16 bits de red (PAN ID) y 16 de nodo, más 64 de fábrica | El coordinador asigna la corta al unirse | Sí, y por grupos |
| LoRaWAN (tema 39) | Un dispositivo | 64 bits de fábrica (DevEUI) y 32 de red (DevAddr) | La red asigna la de 32 al unirse | Sin difusión entre dispositivos |
Dos patrones se repiten. Primero, dos niveles de identidad: una identidad larga y única que viene de fábrica (el código ROM de 1-Wire, la MAC, el DevEUI) y una corta y local que se asigna al entrar a la red (la dirección Zigbee, la IP, el DevAddr). La larga es cómoda para identificar sin riesgo, pero cara de enviar en cada paquete; la corta es barata pero hay que asignarla y evitar que se repita. Segundo, dirección de nodo o dirección de contenido: la mayoría direcciona quién recibe, pero CAN y MQTT direccionan qué se dice, y cualquier nodo interesado lo toma. Es un cambio de punto de vista útil para sistemas donde los datos se producen sin saber quién los va a usar.
03 · A quién se habla
Unicast, difusión y multidifusión
Según cuántos receptores tenga un paquete, hay tres formas de direccionarlo:
| Forma | Destinatarios | Cómo se indica | Cuándo se usa | Ejemplos |
|---|---|---|---|---|
| Unicast | Un nodo | La dirección de ese nodo | Pedir una lectura, mandar una orden a un actuador | Consulta Modbus al esclavo 7; una conexión TCP |
| Difusión (broadcast) | Todos los del medio | Una dirección reservada (todos los bits en 1) | Anunciar algo a todos, pedir que alguien se presente, dar la hora o una orden de emergencia | Solicitud ARP; DHCP; la dirección 0 de Modbus |
| Multidifusión (multicast) | Un grupo de nodos | Una dirección de grupo; cada nodo sabe a cuáles pertenece | Una orden a todas las luces de un cuarto, una transmisión de video a quienes la pidieron | IP 224.0.0.0/4, mDNS; grupos de Zigbee |
También existe la anycast (un paquete para cualquiera de un grupo, el más cercano: IPv6 y DNS la usan) y el mensaje a sí mismo (loopback), pero con las tres formas de la tabla se cubre casi todo en un sistema embebido.
La difusión y la multidifusión ahorran tráfico (un paquete en lugar de n), pero tienen tres trampas:
- Las respuestas. Si todos los receptores de una difusión contestan, los n paquetes de respuesta salen a la vez y chocan, y el emisor queda saturado. La regla básica es que a los paquetes de difusión y de grupo no se contesta (así lo hace Modbus con su dirección 0), o se contesta con un retardo repartido entre los nodos (sección 7). Si se necesita saber quién escuchó, se hace después un unicast a cada uno.
- La falta de confirmación. Un unicast se puede confirmar (tema 45); una difusión no: el emisor no sabe cuántos la recibieron. Es útil para lo que se repite («latido» cada segundo) y arriesgado para lo que importa una sola vez.
- Las tormentas. Una difusión que dispara otras difusiones puede crecer sin freno (un bucle de reenvíos que satura el medio). Los protocolos con puentes y enrutadores agregan un contador de saltos o una lista de paquetes ya vistos para frenarlo.
En este curso, el paquete reserva los extremos del espacio de un byte: 0x00 no es de nadie, 0x01 a 0xEF son nodos, 0xF0 a 0xFE son quince grupos, y 0xFF es la difusión. Reservar un valor para «ninguno» (la dirección sin asignar) es una buena costumbre: permite que un nodo nuevo, sin dirección, exista sin confundirse con uno real. En Ethernet, la multidifusión se marca con un bit del primer byte de la MAC, y los grupos se identifican con el resto: una elección distinta para el mismo problema.
04 · Quién es quién
Tamaño, asignación y conflictos
El tamaño de la dirección
Cada bit de la dirección se paga en cada paquete, a la ida y a la vuelta (origen y destino), para siempre. El tamaño es un compromiso entre la cantidad de nodos posibles y el costo en bytes:
| Bits | Nodos posibles | Costo por paquete (destino y origen) | Se usa en |
|---|---|---|---|
| 7 u 8 | Unos 128 o 250 | 1 o 2 bytes | I²C, Modbus, buses de campo y redes pequeñas |
| 16 | Unos 65 000 | 4 bytes | Zigbee (dirección corta), redes de sensores |
| 32 | 4300 millones | 8 bytes | IPv4, LoRaWAN (DevAddr) |
| 48 | 281 billones | 12 bytes | MAC de Ethernet, Wi-Fi y Bluetooth |
| 64 | 1,8 × 1019 | 16 bytes | 1-Wire, el identificador IEEE de Zigbee |
| 128 | 3,4 × 1038 | 32 bytes | IPv6 |
Un byte alcanza para un bus de unas decenas de nodos y deja margen. Pasar de 8 a 16 bits suma 2 bytes por paquete: con datos de 4 bytes, es agregar el 20 %. Conviene elegir el mínimo que cubra el crecimiento previsible, con una reserva, y recordar que cambiar el tamaño después rompe todo: es una decisión del primer día, como la versión.
Cómo se asigna
| Método | Cómo funciona | Ventajas | Problemas |
|---|---|---|---|
| Fija en el hardware | Interruptores o puentes en la placa; o una resistencia de valor distinto | Sin configuración; el nodo se reemplaza sin tocar el programa | Hay que acordarse de ponerlos; los errores de instalación son comunes |
| Configurada | Se escribe en la memoria no volátil con un comando o una herramienta | Flexible; no necesita hardware extra | Un nodo con la dirección equivocada o repetida; hace falta una forma de configurarlo (sin dirección conocida) |
| Única de fábrica | Un identificador grabado en el chip (la MAC, el ID de 96 bits de un microcontrolador, el código ROM) | Sin conflictos ni configuración | Larga; no se sabe de antemano cuál es; el reemplazo cambia la dirección |
| Dinámica | El nodo nuevo pide una dirección corta a un coordinador (DHCP, la unión a una red Zigbee o LoRaWAN) y la recibe | Cortas y sin conflicto; el nodo no necesita configuración | Requiere un coordinador; la dirección cambia entre arranques salvo que se recuerde |
| Al azar | El nodo elige una dirección y verifica que nadie la use | Sin coordinador | Puede chocar (herramienta 3); hace falta un protocolo de detección y reintento |
| Por posición | Cada nodo recibe la dirección de su vecino y suma uno (cadena de pines, o por tiempo de arranque) | Automática y única en un bus lineal | Depende del cableado y se rompe si falla un nodo |
Un diseño frecuente combina varios: el nodo viene con un identificador único de fábrica, al arrancar lo anuncia por difusión (o por la dirección 0, «sin asignar»), y el maestro le responde con una dirección corta que el nodo guarda. Así no hay interruptores, no hay conflictos y los paquetes llevan direcciones de un byte. Es lo que hacen DHCP, Zigbee y LoRaWAN.
Los conflictos
Un conflicto de direcciones es un error de los peores, porque su síntoma no se parece a la causa: dos nodos contestan a la vez, sus bytes se mezclan en el bus y el maestro ve errores de suma intermitentes que parecen ruido. Se evita con unicidad por construcción (direcciones de fábrica, asignación central) o se verifica con una prueba al arrancar. Cuando se eligen al azar, la probabilidad de choque sube mucho antes de lo que parece (es la paradoja del cumpleaños): con n nodos y N direcciones posibles, es 1 − e−n(n−1)/2N. Con 8 bits, 20 nodos chocan con una probabilidad de más del 50 %; con 16 bits, hacen falta unos 300 para el mismo riesgo; con 48 bits, el riesgo es despreciable (la herramienta 3). Por eso las direcciones largas se pueden sortear, y las cortas se asignan.
05 · La estructura
Encabezado, carga útil y cola
Casi todo paquete de cualquier protocolo tiene tres zonas, y las tres tienen una función distinta:
- Encabezado (header): la información de control, que el receptor necesita antes de mirar los datos: dónde empieza el paquete, cuánto mide, a quién va, de quién viene y de qué tipo es. Es lo que se examina para decidir qué hacer.
- Carga útil (payload): lo que se quiere comunicar. El protocolo no la interpreta; la entrega al programa, que sabe qué significa según el tipo.
- Cola (trailer): lo que se calcula al final, con todo el paquete a la vista, y por eso va después: la suma de verificación o el CRC (temas 43 y 44). Ethernet termina cada trama con una de 4 bytes; los protocolos de este curso con un byte o dos.
| Zona | Campo | Tamaño | Contenido |
|---|---|---|---|
| Encabezado | SOF | 1 | Siempre 0xA5: marca el comienzo y permite resincronizar |
| LEN | 1 | Cuenta destino, origen, tipo y datos: de 3 a 35 | |
| DST | 1 | A quién va: nodo (01 a EF), grupo (F0 a FE) o todos (FF) | |
| SRC | 1 | Quién lo envía: siempre un nodo (01 a EF) | |
| TIPO | 1 | Qué mensaje es | |
| Carga útil | DATOS | 0 a 32 | Campos del mensaje, con tamaños y orden fijos |
| Cola | SUMA | 1 | Complemento a dos de la suma de LEN, DST, SRC, TIPO y datos |
Con la lectura de ejemplo, el paquete A5 07 02 01 01 2E 09 7C 15 2D dice: del nodo 1 al nodo 2, un mensaje de tipo lectura con datos 2E 09 7C 15 (23,50 °C y 55,00 %). Tiene 10 bytes, 6 de estructura: el 60 %, frente al 50 % del tema 41: las direcciones tienen un costo. La herramienta 1 lo calcula con cualquier valor.
Dos decisiones de diseño merecen atención. El origen va en el paquete porque el receptor lo necesita para contestar: sin él, cada respuesta tendría que saber de antemano a quién va. La suma cubre las direcciones: un bit cambiado en el destino haría que el paquete llegara al nodo equivocado; con la suma, el daño se detecta antes. Y el largo sigue contando todo lo que sigue, lo que mantiene el receptor del tema 41 casi idéntico: solo cambia la longitud mínima (3 en lugar de 1) y lo que se hace con el cuerpo.
06 · Atender o ignorar
Qué hace cada nodo con lo que oye
El filtro de recepción es la pieza que convierte un medio compartido en una red de mensajes dirigidos. Cada nodo, para cada paquete que valida (con el largo y la suma correctos), aplica una regla de tres condiciones. Se acepta si el destino es:
- La dirección propia (unicast).
- La dirección de difusión (
FF). - Un grupo al que el nodo pertenece: los grupos son los valores
F0aFEy cada nodo guarda una máscara de 15 bits, con un bit por grupo. El grupoF0es el bit 0;F2, el bit 2. El nodo pertenece al grupo si el bit está en 1.
Todo lo demás se descarta sin procesar los datos: ni se copian, ni se interpretan, ni se contestan. Es una regla corta, y la herramienta 2 la aplica a una lista de destinos. Por ejemplo, el nodo 3, en los grupos 0 y 1 (máscara 0x0003), acepta los destinos 03, FF, F0 y F1, y descarta 04, F2, 00 y EF.
Cuándo contestar
La segunda decisión es qué hacer con lo aceptado, y la regla es asimétrica: se contesta a los paquetes de unicast que piden una respuesta, y no a los de difusión ni a los de grupo. La razón es la saturación: si treinta nodos reciben la misma difusión y los treinta responden, las treinta respuestas se superponen en el medio y se pierden todas. Cuando de verdad hace falta una respuesta de todos (un censo, una lectura simultánea), hay tres salidas:
- Por turnos asignados: cada nodo contesta después de un retardo proporcional a su dirección (dirección × el tiempo de una respuesta, más un margen). El nodo 30 contesta 30 intervalos después de la solicitud. Es sencillo y no choca mientras los relojes sean parecidos.
- Por sondeo: el maestro pregunta a cada nodo por separado, con unicast. Es lo que hace Modbus, y es la solución más robusta, a costa de más tráfico.
- Con retardo al azar: cada nodo espera un tiempo aleatorio dentro de una ventana (como hace DHCP y las respuestas de multidifusión de IP). Simple y sin coordinación, con alguna colisión residual (sección 7).
Escuchar sin ser el destinatario
El filtro es una decisión del software, no del medio: el nodo oye todos los paquetes. Esto permite un modo promiscuo, en el que se entregan todos al programa sin filtrar: lo usan los analizadores de red (Wireshark) y es la herramienta más poderosa para depurar un bus. También es el motivo por el que una dirección no es un secreto ni una protección: cualquier nodo conectado al bus lee los paquetes de los demás, y cualquiera puede enviar un paquete con el origen de otro (suplantación). Para que un paquete sea confidencial o auténtico hace falta cifrarlo y firmarlo (tema 49), no solo direccionarlo.
Identificadores de mensaje y de transacción
Además del nodo, a veces se identifica el mensaje. En CAN, el identificador dice qué es el dato («régimen del motor», «pedal») y no quién lo manda; un nodo se interesa por los identificadores que le sirven con un filtro de hardware y descarta el resto. Esto cambia el modelo: no se le habla a un nodo sino se publica un dato (como en MQTT). Y en un diálogo de pregunta y respuesta se agrega un identificador de transacción (un número que el que pregunta pone y el que contesta repite): permite saber a qué consulta corresponde cada respuesta cuando hay varias en curso o llega una atrasada. Es la misma función del número de secuencia que se verá en el tema 45.
07 · Turnos
Quién habla y cuándo
Las direcciones dicen a quién va un paquete, pero no cuándo se puede enviar. En un medio compartido, si dos nodos transmiten a la vez, las señales se mezclan y las dos se pierden: una colisión. Cada tecnología resuelve el problema con una política de acceso al medio, y la elección determina la eficiencia y la complejidad:
| Método | Cómo funciona | Ventajas | Problemas | Ejemplos |
|---|---|---|---|---|
| Maestro y esclavos (sondeo) | Un maestro pregunta y el esclavo direccionado responde; nadie habla sin que le pregunten | Sin colisiones, simple, tiempos predecibles | Todo pasa por el maestro; un esclavo no puede avisar de un evento urgente; el tiempo de ciclo crece con los nodos | Modbus, I²C, SPI, USB |
| Turnos con testigo (token) | Un mensaje especial circula; solo quien lo tiene habla | Sin colisiones ni maestro único | Si se pierde el testigo hay que regenerarlo | Profibus entre maestros, token ring |
| Ranuras de tiempo (TDMA) | Cada nodo tiene una franja fija de tiempo | Sin colisiones, latencia fija | Exige relojes sincronizados; desperdicia ranuras vacías | GSM, redes industriales sincronizadas |
| Escuchar antes de hablar (CSMA) | El nodo oye el medio y habla si está libre; con espera aleatoria si estaba ocupado | Sin coordinación y eficiente con poca carga | Colisiones si dos empiezan juntos; en radio no se puede oír mientras se habla (CSMA/CA con confirmación) | Ethernet de medio compartido (CSMA/CD), Wi-Fi y 802.15.4 (CSMA/CA) |
| Arbitraje bit a bit | Varios empiezan juntos; el identificador de menor valor gana sin perderse y los otros se retiran | Sin pérdida y con prioridades | Solo funciona con una señal dominante y velocidades acotadas por la longitud del bus | CAN |
| Acceso aleatorio (ALOHA) | Cada nodo envía cuando tiene algo, sin mirar a nadie | Sin coordinación ni oído constante: ideal para nodos con batería | Colisiones sin recuperación; no sirve con mucha carga | LoRa y LoRaWAN, la publicidad de BLE, tarjetas y balizas |
Los nodos con batería de los temas 39 y 40 usan el último método porque no pueden gastar energía escuchando el medio. El precio se calcula: en el ALOHA puro, un paquete choca si el de otro nodo empieza en una ventana de dos duraciones del paquete a su alrededor. Con n nodos, una duración T y un envío cada P segundos, la probabilidad de que un paquete llegue sin chocar es (1 − 2T/P)n−1. Con los 400 medidores del desafío del tema 39 (144 ms cada 900 s), llega el 88 % y se pierde el 12 %; con 20 nodos que envían cada 300 s, llega el 98,2 %. Con ALOHA puro nunca se aprovecha más del 18 % del tiempo del medio, y la pérdida crece con la cantidad de nodos y con la duración de los paquetes: la herramienta 4 lo calcula. Se mejora con paquetes más cortos, períodos más largos, varios canales o una escucha previa breve.
Un detalle práctico para los buses con sondeo: aunque el maestro dirija el tráfico, hay que dar tiempo al esclavo para contestar y al bus para quedar libre. En RS-485 (tema 24), cada nodo pasa el transceptor a transmisión justo antes de hablar y a recepción justo después; si lo hace tarde, pisa el comienzo de la respuesta de otro. Un maestro que pregunta a un nodo que no está tiene que esperar un tiempo de espera (tema 11) antes de preguntar al siguiente.
08 · Paquetes dentro de paquetes
Capas y encapsulación
Los protocolos reales no son un solo paquete: son varios encapsulados, cada uno con su propio encabezado y su propia dirección. Cada capa trata lo que recibe de la de arriba como carga útil, le pone su encabezado (y a veces su cola) y lo pasa a la de abajo. En el receptor, cada capa quita el suyo y entrega el resto hacia arriba. Es el modelo por capas que organiza el curso: la aplicación (HTTP, MQTT), el transporte (TCP o UDP, que dicen qué programa con un puerto), la red (IP, que dice qué equipo en una red grande) y el enlace (Ethernet, Wi-Fi o BLE, que dicen qué interfaz en el medio local).
La encapsulación explica por qué hay direcciones en varios niveles. La MAC identifica a una interfaz en un tramo local; la IP, a un equipo en el conjunto de redes; el puerto, a un programa en el equipo. Cada salto entre redes (un enrutador) cambia la MAC y conserva la IP: por eso un paquete de un sensor llega hasta un servidor en otro continente. El mismo esquema se repite a escala pequeña en un sistema embebido con una puerta de enlace: un nodo RS-485 con su dirección de un byte habla con un maestro que, a su vez, publica los valores en MQTT sobre Wi-Fi. El maestro desarma el paquete del bus, extrae la carga útil, la arma de nuevo en un mensaje de otro protocolo y la reenvía. Para evitar que los paquetes den vueltas indefinidamente cuando hay varios caminos, los protocolos de red agregan un contador de saltos (el TTL de IP) que baja en cada reenvío y descarta el paquete al llegar a cero.
El costo de las capas es la estructura. Con Ethernet (18 bytes entre encabezado y cola), IPv4 (20) y UDP (8) son 46 bytes antes del primer dato; con TCP, 58. Una lectura de 4 bytes viaja en un paquete de 50 y el 92 % es estructura; con 1000 bytes de datos, solo el 4,4 %. No es un defecto: cada capa resuelve una parte y la información puede cruzar cualquier medio. Pero en un enlace lento, con batería o de baja velocidad, ese costo se paga en tiempo y energía. Por eso los protocolos para sensores (6LoWPAN, CoAP, LoRaWAN, el paquete de este tema) comprimen o eliminan capas y usan direcciones de uno o dos bytes. La herramienta 5 calcula la eficiencia con cualquier combinación.
09 · Con el microcontrolador
Un bus con un maestro y varios esclavos
El programa siguiente es el del tema 41 con dos cambios: el paquete lleva destino y origen, y el filtro de recepción decide qué se procesa. Se carga en una placa por nodo (un ESP32, o un Arduino con segundo puerto serie), todas conectadas a un mismo bus: con un transceptor RS-485 (tema 24) si el bus es largo o hay más de dos nodos, o, para probar en la mesa, dos placas con TX y RX cruzados. Se compila una vez por nodo cambiando MI_DIRECCION: el 1 es el maestro, y del 2 al 4, los esclavos. El maestro pregunta a cada esclavo por turno (un PEDIDO de unicast), el esclavo contesta con una LECTURA, y cada diez segundos el maestro manda un LATIDO de difusión que nadie contesta.
// ---------- Configuración ----------
const uint8_t MI_DIRECCION = 2; // 1 = maestro, 2 a 4 = esclavos
const uint16_t MIS_GRUPOS = 0x0001; // un bit por grupo: bit 0 = grupo 0 (F0)
const int PIN_DE = -1; // pin de habilitación del transmisor RS-485; -1 si no hay
const uint8_t SOF = 0xA5;
const uint8_t BROADCAST = 0xFF, GRUPO_0 = 0xF0;
const uint8_t MAX_LEN = 35; // destino, origen, tipo y hasta 32 bytes de datos
const uint8_t TIPO_PEDIDO = 0x02, TIPO_LECTURA = 0x01, TIPO_LATIDO = 0x10;
const uint32_t TIEMPO_BYTE_MS = 10;
const uint8_t ULTIMO_ESCLAVO = 4;
// ---------- Armado y envío ----------
void enviar(uint8_t dst, uint8_t tipo, const uint8_t *datos, uint8_t n) {
uint8_t p[MAX_LEN + 3];
p[0] = SOF;
p[1] = n + 3; // el largo cuenta destino, origen, tipo y datos
p[2] = dst;
p[3] = MI_DIRECCION; // el origen es siempre este nodo
p[4] = tipo;
memcpy(p + 5, datos, n);
uint8_t suma = 0;
for (uint8_t i = 1; i < 5 + n; i++) suma += p[i];
p[5 + n] = (uint8_t)(0x100 - suma);
if (PIN_DE >= 0) digitalWrite(PIN_DE, HIGH); // pasar el transceptor a transmisión
Serial1.write(p, n + 6);
Serial1.flush(); // esperar a que salga el último byte
if (PIN_DE >= 0) digitalWrite(PIN_DE, LOW); // y volver a recepción enseguida
}
// ---------- Filtro de recepción ----------
bool paraMi(uint8_t dst) {
if (dst == MI_DIRECCION || dst == BROADCAST) return true; // unicast propio o difusión
return dst >= GRUPO_0 && dst < BROADCAST && (MIS_GRUPOS & (1 << (dst - GRUPO_0))); // grupo al que pertenezco
}
// ---------- Aplicación ----------
uint8_t esclavoActual = 2;
unsigned long ultimoPedido = 0, ultimoLatido = 0, pedidoEn = 0;
bool esperando = false;
uint32_t ajenos = 0, fallos = 0;
void alPaquete(uint8_t dst, uint8_t src, uint8_t tipo, const uint8_t *d, uint8_t n) {
if (!paraMi(dst)) { ajenos++; return; } // se oyó pero no era para mí: se cuenta y se descarta
if (tipo == TIPO_PEDIDO && dst == MI_DIRECCION) { // solo contesto a los de unicast
int16_t t = 2350 + random(-50, 50);
uint16_t h = 5500;
uint8_t r[4] = { (uint8_t)(t & 0xFF), (uint8_t)((uint16_t)t >> 8), (uint8_t)(h & 0xFF), (uint8_t)(h >> 8) };
enviar(src, TIPO_LECTURA, r, sizeof(r)); // se contesta a quien preguntó: el origen del paquete
} else if (tipo == TIPO_LECTURA && MI_DIRECCION == 1 && n >= 4) {
esperando = false;
Serial.printf("nodo %u: %.2f °C, %.2f %%\n", src, (int16_t)(d[0] | (d[1] << 8)) / 100.0, (d[2] | (d[3] << 8)) / 100.0);
} else if (tipo == TIPO_LATIDO) {
Serial.printf("latido de %u (dst 0x%02X)\n", src, dst); // difusión: se procesa y no se contesta
}
}
// ---------- Receptor (el del tema 41, con destino y origen) ----------
class Decodificador {
public:
typedef void (*AlRecibir)(uint8_t dst, uint8_t src, uint8_t tipo, const uint8_t *datos, uint8_t n);
uint32_t errores = 0;
Decodificador(AlRecibir cb) : alPaq(cb) {}
void alimentar(uint8_t b) {
ultimoByte = millis();
switch (estado) {
case ESPERA_SOF: if (b == SOF) estado = ESPERA_LEN; break;
case ESPERA_LEN:
if (b >= 3 && b <= MAX_LEN) { buf[0] = b; n = 1; faltan = b + 1; estado = CUERPO; }
else { errores++; estado = ESPERA_SOF; alimentar(b); }
break;
case CUERPO:
buf[n++] = b;
if (--faltan == 0) {
estado = ESPERA_SOF;
uint8_t suma = 0;
for (uint8_t i = 0; i < n; i++) suma += buf[i];
if (suma == 0) alPaq(buf[1], buf[2], buf[3], buf + 4, n - 5);
else {
errores++;
uint8_t copia[MAX_LEN + 2];
uint8_t m = n;
memcpy(copia, buf, m);
n = 0;
for (uint8_t i = 0; i < m; i++) alimentar(copia[i]);
}
}
break;
}
}
void vigilarTiempo() {
if (estado != ESPERA_SOF && millis() - ultimoByte > TIEMPO_BYTE_MS) { errores++; estado = ESPERA_SOF; n = 0; }
}
private:
enum Estado { ESPERA_SOF, ESPERA_LEN, CUERPO } estado = ESPERA_SOF;
AlRecibir alPaq;
uint8_t buf[MAX_LEN + 2];
uint8_t n = 0, faltan = 0;
unsigned long ultimoByte = 0;
};
Decodificador receptor(alPaquete);
void setup() {
Serial.begin(115200);
Serial1.begin(9600, SERIAL_8N1, 16, 17);
if (PIN_DE >= 0) { pinMode(PIN_DE, OUTPUT); digitalWrite(PIN_DE, LOW); }
}
void loop() {
while (Serial1.available()) receptor.alimentar(Serial1.read());
receptor.vigilarTiempo();
if (MI_DIRECCION != 1) return; // lo que sigue es del maestro
if (esperando && millis() - pedidoEn > 300) { esperando = false; fallos++; Serial.printf("el nodo %u no contestó (fallos: %lu)\n", esclavoActual, fallos); }
if (!esperando && millis() - ultimoPedido >= 1000) {
ultimoPedido = pedidoEn = millis();
esclavoActual = esclavoActual >= ULTIMO_ESCLAVO ? 2 : esclavoActual + 1;
enviar(esclavoActual, TIPO_PEDIDO, nullptr, 0); // unicast: contesta solo ese nodo
esperando = true;
}
if (millis() - ultimoLatido >= 10000) {
ultimoLatido = millis();
enviar(BROADCAST, TIPO_LATIDO, nullptr, 0); // difusión: todos lo reciben y nadie contesta
}
}
Qué hay que notar:
- El filtro son tres líneas (
paraMi) y se aplica a cada paquete válido: primero se verifica el paquete (largo y suma) y después se decide si es para este nodo. Un paquete dañado se descarta antes de mirar el destino, porque un destino dañado no merece confianza. - Se contesta al origen del paquete (
src), no a una dirección fija: es lo que permite que cualquier nodo que pregunte reciba su respuesta. Y se contesta solo a unicast (dst == MI_DIRECCION): unPEDIDOpor difusión no se contestaría, y elLATIDOse procesa pero no genera ninguna respuesta. - El maestro lleva un tiempo de espera (300 ms) por esclavo y cuenta los fallos: un nodo que no contesta se detecta y el sondeo sigue con el siguiente, en lugar de quedar bloqueado. La respuesta tarda poco más de 10 ms a 9600 baudios (10 bytes), así que 300 ms dan un margen amplio.
- Los paquetes ajenos se cuentan (
ajenos): es un indicador del tráfico del bus que no es para este nodo, útil para saber qué tan cargado está. - El control de la dirección de RS-485 (
PIN_DE) pasa el transceptor a transmisión antes de enviar y a recepción apenas termina de salir el último byte (Serial1.flush()): si se queda en transmisión de más, tapa la respuesta del nodo siguiente. - Hay que cambiar
MI_DIRECCIONen cada placa: es el modelo de dirección «fija en el programa», el más simple y el más propenso a errores de instalación. Se mejora con una configuración en la memoria no volátil o con el identificador único de la placa (ESP.getEfuseMac()) y una asignación por parte del maestro. - En un Arduino sin
printfenSerial, se reemplaza porSerial.print; sin segundo puerto serie, se usaSoftwareSeriala 9600 baudios.
10 · Analogía con programación
El bus en Python
El armado y la lectura del paquete, el filtro de recepción con grupos, un bus simulado con varios nodos, las probabilidades de colisión de direcciones y de acceso aleatorio y el costo de las capas se prueban sin hardware. El modelo siguiente reproduce las reglas del programa y de la página: un bus es una lista de nodos que reciben todos el mismo paquete y cada uno decide si lo procesa.
Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
import math
SOF, BROADCAST, GRUPO_0 = 0xA5, 0xFF, 0xF0
TIPO_LECTURA, TIPO_ECO = 0x01, 0x20
def armar_paquete(dst, src, tipo, datos=b""):
cuerpo = bytes([len(datos) + 3, dst, src, tipo]) + datos # el largo cuenta destino, origen, tipo y datos
return bytes([SOF]) + cuerpo + bytes([(0x100 - sum(cuerpo)) & 0xFF])
def leer_paquete(trama):
"""Valida una trama completa y devuelve (destino, origen, tipo, datos), o None si está dañada."""
if len(trama) < 6 or trama[0] != SOF:
return None
if trama[1] < 3 or len(trama) != trama[1] + 3 or sum(trama[1:]) % 256 != 0:
return None
return trama[2], trama[3], trama[4], bytes(trama[5:-1])
def clase_destino(a):
if a == 0:
return "reservada"
if a == BROADCAST:
return "difusión"
return f"grupo {a - GRUPO_0}" if a >= GRUPO_0 else "unicast"
class Nodo:
def __init__(self, direccion, grupos=()):
self.direccion = direccion
self.mascara = sum(1 << g for g in grupos) # un bit por grupo (0 a 14)
def acepta(self, dst):
if dst in (self.direccion, BROADCAST):
return True
return GRUPO_0 <= dst < BROADCAST and bool(self.mascara >> (dst - GRUPO_0) & 1)
def recibir(self, trama):
"""Devuelve (procesó, respuesta). Solo contesta a los paquetes de unicast."""
p = leer_paquete(trama)
if p is None or not self.acepta(p[0]):
return False, None
dst, src, tipo, datos = p
if dst == self.direccion and tipo == TIPO_LECTURA:
return True, armar_paquete(src, self.direccion, TIPO_ECO, datos)
return True, None
def probabilidad_colision(n, bits):
return -math.expm1(-n * (n - 1) / 2 / 2 ** bits)
def exito_aloha(n, t_ms, periodo_s):
return (1 - 2 * t_ms / 1000 / periodo_s) ** (n - 1)
def eficiencia(datos, *capas):
return datos / (datos + sum(capas))
p = armar_paquete(0x02, 0x01, TIPO_LECTURA, bytes.fromhex("2E097C15"))
print("paquete:", p.hex(" ").upper(), len(p), "bytes", leer_paquete(p))
print("difusión sin datos:", armar_paquete(BROADCAST, 0x01, 0x10).hex(" ").upper())
bus = {2: Nodo(2, (0,)), 3: Nodo(3, (0, 1)), 4: Nodo(4, (1,))}
for dst in (0x03, BROADCAST, 0xF0, 0xF1, 0xF2, 0x00):
trama = armar_paquete(dst, 0x01, TIPO_LECTURA, b"\x07")
resultado = {n: bus[n].recibir(trama) for n in bus}
proc = [n for n, (ok, _) in resultado.items() if ok]
resp = [n for n, (_, r) in resultado.items() if r]
print(f"destino 0x{dst:02X} ({clase_destino(dst)}): procesan {proc}, contestan {resp}")
dañada = bytearray(p)
dañada[6] ^= 0x04
print("trama dañada:", leer_paquete(bytes(dañada)))
for n, bits in ((20, 8), (20, 16), (300, 16), (1000, 48)):
print(f"{n} nodos con {bits} bits: {probabilidad_colision(n, bits) * 100:.3g} % de colisión")
print(f"nodos para el 50 % con 8 bits: {math.sqrt(2 * math.log(2) * 2 ** 8):.1f}, con 16 bits: {math.sqrt(2 * math.log(2) * 2 ** 16):.0f}")
print(f"ALOHA, 400 nodos, 144 ms cada 900 s: llega el {exito_aloha(400, 144, 900) * 100:.1f} %")
print(f"ALOHA, 20 nodos, 144 ms cada 300 s: llega el {exito_aloha(20, 144, 300) * 100:.1f} %")
print(f"Ethernet + IPv4 + UDP con 4 bytes: {eficiencia(4, 18, 20, 8) * 100:.1f} % de datos; con 1000: {eficiencia(1000, 18, 20, 8) * 100:.1f} %")
print(f"Ethernet + IPv4 + TCP con 4 bytes: {eficiencia(4, 18, 20, 20) * 100:.1f} %")
print(f"paquete del tema con 4 bytes: {eficiencia(4, 6) * 100:.0f} %, con 32: {eficiencia(32, 6) * 100:.0f} %")
Resultado: el paquete del nodo 1 al nodo 2 con una lectura (2E 09 7C 15) es A5 07 02 01 01 2E 09 7C 15 2D, de 10 bytes, y al leerlo se recuperan destino 2, origen 1, tipo 1 y los datos; una difusión sin datos es A5 03 FF 01 10 ED. En el bus con los nodos 2 (grupo 0), 3 (grupos 0 y 1) y 4 (grupo 1): un paquete al nodo 3 lo procesa solo el 3 y lo contesta; a la difusión (FF) la procesan los tres y ninguno contesta; al grupo 0 (F0) lo procesan el 2 y el 3, y al grupo 1 (F1), el 3 y el 4, sin respuestas; al grupo 2 (F2) y a la dirección reservada 00, nadie. Una trama con un bit cambiado da None: se descarta. La colisión de direcciones al azar es del 52,4 % con 20 nodos y 8 bits, del 0,289 % con 20 nodos y 16 bits, del 49,6 % con 300 nodos y 16 bits, y prácticamente nula con 1000 nodos y 48 bits; el 50 % se alcanza con 18,8 nodos (8 bits) y 301 (16 bits). Con ALOHA, 400 nodos de 144 ms cada 900 s entregan el 88,0 % y 20 nodos de 144 ms cada 300 s, el 98,2 %. Una lectura de 4 bytes sobre Ethernet, IPv4 y UDP tiene un 8,0 % de datos (95,6 % con 1000 bytes); con TCP, 6,5 %; con el paquete del tema, 40 % con 4 bytes y 84 % con 32.
Dos lecciones del modelo. Primero, el filtro de recepción es una función de tres condiciones, y toda la complejidad del direccionamiento está en definirlas bien: qué valores son nodos, cuáles grupos, cuál es la difusión, cuál está reservada. Segundo, un bus simulado en pocas líneas permite probar el protocolo antes del hardware: se pueden agregar nodos, dañar paquetes y contar respuestas, y descubrir un diseño que tenía una tormenta de difusión sin haber conectado un cable.
11 · Exploración interactiva
Laboratorio: paquete, filtro, colisiones, medio y capas
El laboratorio tiene cinco herramientas. La primera arma un paquete con destino y origen. La segunda aplica el filtro de recepción de un nodo a una lista de destinos. La tercera calcula la probabilidad de colisión de direcciones elegidas al azar. La cuarta calcula las pérdidas de un acceso aleatorio al medio (ALOHA). La quinta mide el costo de la encapsulación por capas.
1. Armar un paquete
Escribí un destino, un origen y los datos.
2. Filtro de recepción de un nodo
Escribí la dirección del nodo y los destinos.
3. Colisión de direcciones elegidas al azar
Elegí la cantidad de nodos y los bits.
4. Acceso aleatorio al medio (ALOHA)
Elegí los nodos y la duración.
5. Costo de la encapsulación
Elegí los tamaños de cada capa.
En la herramienta 1, el largo cuenta destino, origen, tipo y datos, y el tiempo supone 8N1. En la 2, cada grupo es un bit de la máscara (el bit 0 es el grupo F0). En la 3, se usa la fórmula de la paradoja del cumpleaños. En la 4, se usa la probabilidad de que ningún otro nodo empiece dentro de una ventana de dos duraciones del paquete. En la 5, Ethernet cuenta 18 bytes (14 de encabezado y 4 de cola), IPv4 20, UDP 8 y TCP 20.
Recorrido guiado
- En la herramienta 1, con destino 02, origen 01, tipo 01 y datos 2E097C15:
A5 07 02 01 01 2E 09 7C 15 2D, 10 bytes, unicast, suma 0x2D, 6 bytes de estructura (el 60 %) y 10,4 ms a 9600 baudios. Con destino FF y sin datos:A5 03 FF 01 01 FC, difusión. Con destino F2 y origen FF: «origen inválido» (en rojo). Con destino y origen 01: «destino y origen iguales». - En la herramienta 2, con el nodo 03, los grupos 0003 y los destinos
03 04 FF F0 F1 F2 00 EF: acepta 03, FF, F0 y F1 (4 de 8), descarta 04, F2, 00 y EF y contesta solo al unicast. Con el nodo 04 y la máscara 4 (solo el grupo 2): acepta 04, FF y F2. Con la máscara 8000: inválida, solo hay 15 grupos. - En la herramienta 3, con 20 nodos y 8 bits: 52,4 % de probabilidad de choque, 0,742 pares iguales esperados y el 50 % se alcanza con 19 nodos. Con 16 bits: 0,29 % (y 302 nodos para el 50 %). Con 300 nodos y 16 bits: 49,6 %. Con 1000 nodos y 48 bits: menos de 0,01 %.
- En la herramienta 4, con 400 nodos, 144 ms y un envío cada 900 s: ocupación del 6,4 %, llega el 88 %, se pierde el 12 % (en rojo) y llegan 1408 de 1600 mensajes por hora. Con 20 nodos y 300 s: ocupación de 0,96 %, 98,2 % de entrega. Con 5000 nodos de 50 ms cada 60 s: el medio se satura (la ocupación supera el 18 %).
- En la herramienta 5, con 4 bytes de datos sobre Ethernet (18), IPv4 (20) y UDP (8): 50 bytes, 46 de estructura (92 %) y 8 % de datos (en rojo). Con 1000 bytes de datos: 95,6 % de eficiencia. Con 20 bytes de datos y solo 6 bytes de una capa de enlace propia: 76,9 %. Con TCP (20) en lugar de UDP: 62 bytes y 6,5 %.
Desafío: el bus de treinta sensores
Una planta tiene un bus RS-485 con 30 sensores de temperatura que el maestro debe leer cada 2 segundos. El diseño inicial: el maestro manda una difusión «contesten con su lectura» y espera. Resultado: el maestro recibe basura. Con las herramientas y lo visto: (1) El problema es la respuesta a la difusión: 30 nodos hablan a la vez y las 30 respuestas chocan. Aunque llegara una intacta, la suma de las demás la destruiría. Es la regla de oro de la sección 3: no se contesta a una difusión. (2) Solución por sondeo: el maestro pregunta a cada nodo con unicast y espera su respuesta. Con 6 bytes de pedido y 10 de respuesta a 9600 baudios (la herramienta 1), cada turno son unos 17 ms más el cambio de dirección del transceptor y el proceso del esclavo, digamos 30 ms: 30 nodos son 0,9 s. Cabe en los 2 s, con margen para fallos (300 ms de espera por nodo caído). (3) Solución por turnos: difusión de «lectura» y cada nodo contesta después de su dirección por 15 ms (el nodo 30 a los 450 ms): 0,5 s en total, sin repetir el pedido, pero sin forma de detectar a un nodo mudo, y exige relojes parecidos. (4) Direcciones: con 30 nodos y 8 bits, asignarlas al azar daría un conflicto casi seguro (la herramienta 3); se asignan por interruptores o por configuración, con una tabla en el maestro. (5) Un nodo nuevo sin dirección usa la 00 (reservada) y el maestro le asigna una libre tras un unicast de configuración. (6) Un nodo defectuoso que se queda transmitiendo bloquea el bus: se agrega un tiempo máximo de transmisión y un perro guardián. Es todo el contenido de los temas 42 a 47 aplicado a un mismo problema.
12 · Comprensión
Confusiones frecuentes
«Si el paquete tiene una dirección de destino, solo lo recibe ese nodo»
En un medio compartido lo reciben todos: la dirección solo permite que los demás lo ignoren. El filtrado es una decisión de software de cada receptor, y cualquier nodo conectado puede leer y registrar todo el tráfico. Una dirección no es una protección.
«La difusión es la forma más cómoda de hablar con todos, y todos contestan»
Si todos contestan, las respuestas chocan en el medio y se pierden. A la difusión no se contesta, o se contesta por turnos asignados o con un retardo al azar. Para saber quién escuchó, se hace después una consulta a cada uno.
«El origen del paquete no hace falta: el que contesta ya sabe a quién»
Solo en un enlace de dos extremos. En un bus, el nodo que recibe una pregunta necesita saber quién la hizo para contestarle, y sin origen cada respuesta tendría que saber de antemano a quién va. El origen va en cada paquete.
«Con direcciones al azar de 8 bits, 20 nodos casi seguro no chocan»
Es al revés: con 20 nodos y 256 direcciones la probabilidad de que dos coincidan es del 52 % (paradoja del cumpleaños). Las direcciones cortas se asignan; las largas, como la MAC de 48 bits, se pueden sortear.
«Un conflicto de direcciones se nota enseguida»
No: los dos nodos contestan a la vez y sus bytes se mezclan, y lo que se ve son errores de suma intermitentes que parecen ruido en el cable. Se evita con unicidad por construcción o con una verificación al arrancar, y se sospecha ante errores que aparecen solo con ciertos nodos.
«Una dirección larga es mejor porque no se repite nunca»
Cada bit se paga en cada paquete, dos veces (destino y origen). Con datos de 4 bytes, pasar de 1 a 6 bytes por dirección más que duplica el paquete. Se usa la corta que alcance, con una larga de fábrica solo para identificar al nodo al unirse.
«Mi sensor con ALOHA no puede perder paquetes si lo diseñé bien»
El acceso aleatorio siempre pierde algunos: es el precio de no coordinarse ni escuchar. Se calcula (herramienta 4) y se acepta si la pérdida es tolerable; si no, se agregan envíos repetidos, otro canal, paquetes más cortos o un método con turnos.
«Cuantas más capas, más lento: conviene no usar capas»
Cada capa cuesta bytes, pero resuelve algo (direcciones físicas, de red, puertos). El problema aparece con datos pequeños en enlaces lentos o con batería: ahí se usan protocolos con direcciones cortas y pocas capas (como el paquete de este tema o LoRaWAN), no ninguna.
«El modo promiscuo es un truco de hackers»
Es la forma normal de depurar un bus: un nodo que entrega al programa todos los paquetes, sin filtrar, deja ver qué circula y por qué un esclavo no contesta. También es el motivo de que haya que cifrar lo confidencial: cualquier equipo conectado puede hacer lo mismo.
13 · Práctica guiada
Predecí antes de comprobar
1. Armar un paquete
Armá el paquete de un PEDIDO (tipo 02) del nodo 1 al nodo 3, sin datos. ¿Cuánto vale LEN y cuánto la suma?
Ver solución
LEN = 3 (destino, origen y tipo). La suma de 03 + 03 + 01 + 02 = 9; el complemento a dos es 0x100 − 9 = 0xF7. El paquete es A5 03 03 01 02 F7, de 6 bytes (todo estructura: no hay carga útil).
2. El filtro
El nodo 5 pertenece a los grupos 2 y 3. ¿Cuál es su máscara, y qué hace con paquetes de destino 05, FF, F2, F3, F4, 06 y 00? ¿A cuáles contesta?
Ver solución
Máscara: bit 2 (valor 4) más bit 3 (valor 8) = 12 = 0x000C. Acepta 05 (propio), FF (difusión), F2 (grupo 2) y F3 (grupo 3): 4 de 7. Descarta F4 (grupo 4 ajeno), 06 (otro nodo) y 00 (reservada). Contesta solo a 05 (unicast); a la difusión y a los grupos, no.
3. Una máscara
Un nodo debe pertenecer a los grupos 0 y 4. ¿Qué máscara se le configura?
Ver solución
Bit 0 (valor 1) más bit 4 (valor 16) = 17 = 0x0011. Aceptará los destinos F0 (grupo 0) y F4 (grupo 4).
4. Cumpleaños
Diez nodos eligen su dirección de 8 bits al azar. ¿Qué probabilidad hay de que dos coincidan?
Ver solución
Pares: 10 · 9 / 2 = 45. Con 256 direcciones, 45 / 256 = 0,176 pares esperados. La probabilidad es 1 − e−0,176 ≈ 16,1 %: casi una de cada seis instalaciones tendría un conflicto, con solo diez nodos. Por eso se asignan.
5. Acceso aleatorio
Cincuenta nodos envían un paquete de 100 ms cada 60 s sin coordinarse. ¿Qué porcentaje se pierde por choques? ¿Y si el período pasa a 300 s?
Ver solución
Con 60 s: 2T/P = 0,2 / 60 = 0,00333; la probabilidad de no chocar es (1 − 0,00333)49 ≈ 0,849: se pierde el 15,1 %. Con 300 s: 0,2 / 300 = 0,000667 y (1 − 0,000667)49 ≈ 0,968: se pierde el 3,2 %. Espaciar los envíos cinco veces redujo la pérdida casi cinco veces.
6. Eficiencia por capas
Una aplicación manda 12 bytes. ¿Qué porcentaje del paquete son datos sobre Ethernet (18), IPv4 (20) y TCP (20)? ¿Y con el paquete de este tema (6 bytes de estructura)?
Ver solución
Ethernet + IPv4 + TCP: 12 + 58 = 70 bytes, datos 17,1 %. Con el paquete del tema: 12 + 6 = 18 bytes, datos 66,7 %. La diferencia es el costo de resolver direcciones globales, puertos y conexiones en cada paquete; en un bus local de pocos nodos, no hace falta.
7. Responder a una difusión
En un bus con 30 nodos, el maestro difunde un pedido de lectura y cada respuesta ocupa 10 ms. ¿Cómo evitás que choquen sin volver a preguntar uno por uno? ¿Cuándo termina la última respuesta?
Ver solución
Cada nodo contesta tras un retardo proporcional a su dirección: dirección × 15 ms (los 10 ms de la respuesta más 5 ms de margen). El nodo 1 responde a los 15 ms, el 2 a los 30 ms, y el 30 a los 450 ms; la última termina a los 460 ms. No chocan mientras los relojes de los nodos no se desvíen más de 5 ms en ese tiempo. A cambio, no se detecta a un nodo caído hasta que falta su turno, y hay que dar a cada dirección un lugar en la tabla.
14 · Síntesis
Ideas para recordar
- En un medio compartido (un bus, una radio, un cable con muchos equipos), todos reciben todo, y cada receptor decide por su cuenta si un paquete era para él. Para eso cada paquete lleva la dirección de destino, y la de origen para que el receptor sepa a quién contestar. Una dirección no es ni un secreto ni una protección.
- Los protocolos del curso identifican de formas distintas: por un hilo (SPI), por 7 bits (I²C), por un byte (Modbus), por 64 bits de fábrica (1-Wire), por 48 bits (Ethernet, Wi-Fi, BLE), por contenido (CAN, MQTT) o por una dirección corta que asigna una red (Zigbee, LoRaWAN). Se repiten dos patrones: una identidad larga de fábrica y una corta local, y direcciones de nodo o de contenido.
- Hay tres formas de direccionar: unicast (un nodo), difusión (todos, con una dirección reservada) y multidifusión (un grupo). La difusión y los grupos ahorran tráfico, pero no se confirman y no se contestan, porque las respuestas simultáneas chocarían. En el paquete del tema, 01 a EF son nodos, F0 a FE son 15 grupos, FF es la difusión y 00 está reservada.
- Cada bit de dirección se paga en cada paquete, dos veces. Se elige el tamaño mínimo que cubra el crecimiento, porque cambiarlo después rompe todo. Se asignan por hardware, configuración, identificador de fábrica, un coordinador (dinámica) o al azar. Con direcciones al azar la probabilidad de choque es 1 − e^(−n(n−1)/2N): con 8 bits, 20 nodos chocan más de la mitad de las veces.
- Un paquete tiene encabezado (qué hacer con él: inicio, largo, destino, origen, tipo), carga útil (el mensaje, que el protocolo no interpreta) y cola (la verificación, que se calcula al final). El paquete del tema tiene 6 bytes de estructura, y la suma cubre las direcciones.
- El filtro de recepción acepta un paquete válido si el destino es la dirección propia, la difusión o un grupo al que el nodo pertenece (un bit por grupo en una máscara). Se contesta solo a unicast, al origen del paquete. Un modo promiscuo entrega todo al programa y es la herramienta de depuración de un bus.
- Las direcciones no dicen cuándo hablar. Los métodos de acceso son maestro y esclavos (sondeo), testigo, ranuras de tiempo, escuchar antes de hablar, arbitraje bit a bit (CAN) y acceso aleatorio (ALOHA). En ALOHA, el paquete llega con probabilidad (1 − 2T/P)^(n−1): 400 nodos de 144 ms cada 900 s pierden el 12 %, y el medio nunca se aprovecha más del 18 %.
- Los protocolos reales encapsulan: cada capa pone su encabezado a lo que recibe de la de arriba (Ethernet, IP, UDP o TCP, aplicación), con direcciones en cada nivel: MAC, IP, puerto. Con 46 a 58 bytes de estructura, una lectura de 4 bytes es el 8 % del paquete; por eso los protocolos para sensores usan direcciones cortas y pocas capas.
¿Listo para el siguiente tema?
Antes de avanzar, calculá el paquete del nodo 1 al grupo 2 con tipo 10 y sin datos, y decí qué nodos lo procesan si el 3 está en los grupos 2 y 3 y el 4 en el grupo 1; explicá por qué un bus de treinta nodos no puede pedir una lectura con una difusión a la que todos contestan, y qué harías en su lugar; y calculá la probabilidad aproximada de que dos de diez nodos con direcciones de 8 bits al azar coincidan. Si podés justificar las tres respuestas, entendés cómo se direcciona un paquete.
En el próximo tema, errores de transmisión, se estudia lo que le pasa a un paquete por el camino: el ruido, las interferencias, los rebotes y las pérdidas de bits, cómo se mide una tasa de error y qué causas físicas la producen. Son los motivos de la suma de verificación que ya estamos usando, y de los CRC y las confirmaciones de los temas 44 y 45.