Saltearse al contenido

Unidad 7

Introducción 📜

¿Qué aprenderás en esta unidad? 💡

Actividad 01

Observa funcionando el caso de estudio

👣 Pasos:

  1. Prepara el proyecto:
    • Descarga o clona el código del caso de estudio en tu computador. El código está en este repositorio.
    • Abre una terminal en la carpeta raíz del proyecto.
    • Ejecuta npm install para instalar las dependencias (express, socket.io). Haz esto solo la primera vez.
  2. Inicia el servidor local:
    • Abre la carpeta del proyecto en VS Code.
    • Abre una terminal integrada en VS Code (View > Terminal).
    • En la terminal, ejecuta npm start.
    • Deberías ver el mensaje: Server is listening on http://localhost:3000. ¡Pero aún no accedas a esa URL!
  3. Expón el servidor con Dev Tunnels:
    • Selecciona PORTS. Click en Forward a Port (Dev Tunnels).
    • En el número de puerto, selecciona 3000 (¿Por qué este?)
    • En la columna Visibility, selecciona Public. Esto permitirá que el túnel sea accesible desde cualquier lugar.
    • Copia la URL que aparece en la columna Forwarded Address. Esta URL es la que usarás para acceder al servidor desde tu celular.
    • Envía esta URL a tu celular. Se verá algo como https://TU-TENDRAS-UNA-DIFERNTE.use2.devtunnels.ms/.
  4. Accede a las aplicaciones:
    • En tu Computador: abre un navegador web y ve a la URL: http://localhost:3000/desktop/. Deberías ver el canvas de p5.js con un círculo rojo.
    • En tu Celular: abre un navegador web y ve a la URL que enviaste pero añadiendo /mobile/ al final. Algo así como esto: https://TU-TENDRAS-UNA-DIFERNTE.use2.devtunnels.ms//mobile/ (Asegúrate de añadir /mobile/ al final). Deberías ver el canvas de p5.js con el texto “Touch to move the circle”.
  5. Prueba la interacción:
    • Toca y mueve el dedo sobre la pantalla de tu celular.
    • Observa el navegador de tu computador. El círculo rojo debería moverse siguiendo tu dedo.
    • Observa la terminal donde corre server.js. Deberías ver mensajes “New client connected”, “Received message => …”, y “Client disconnected” cuando cierras las pestañas.
  6. Cierra el Port: una vez termines de hacer las pruebas NO OLVIDES CERRAR el puerto.
  7. ¿Si cerraste el puerto?

🚀 Tu solución:

Investigación 🔎

Actividad 02

Conceptos clave: Dev Tunnels, JSON y eventos táctiles

👣 Pasos: (Explicación conceptual)

  1. El problema de la conexión directa:

    • Cuando ejecutas npm start, el servidor escucha en localhost:3000. localhost (o 127.0.0.1) es una dirección especial que siempre se refiere a tu propia máquina.
    • Si intentaras acceder a http://localhost:3000 desde tu celular, este buscaría un servidor en el propio celular, no en tu computador.
    • Podrías usar la IP local de tu computador (ej. 192.168.1.X), pero esto solo funciona si ambos dispositivos están en la misma red Wi-Fi y no hay firewalls bloqueando. No funcionaría si tu celular usa datos móviles o está en otra red.
    • Necesitamos una dirección pública y accesible desde cualquier lugar.
  2. La solución: VS Code Dev Tunnels (Port Forwarding):

    • Dev Tunnels actúa como un intermediario seguro. Crea un túnel desde una URL pública en Internet (la que obtuviste, como https://TU-TENDRAS-UNA-DIFERNTE.use2.devtunnels.ms/) hasta el puerto 3000 de tu localhost.
    • Cuando tu celular (o cualquier cliente en Internet) se conecta a la URL pública de Dev Tunnels, el servicio de Dev Tunnels reenvía esa conexión de forma segura a través del túnel hasta tu servidor Node.js local.
    • Del mismo modo, las respuestas de tu servidor local viajan de vuelta por el túnel hasta el servicio Dev Tunnels, que las entrega al cliente (celular/escritorio).
    • Analogía: Es como tener un número de teléfono público (la URL de Dev Tunnels) que redirige las llamadas a tu teléfono privado en casa (localhost:3000), sin exponer directamente tu número privado.
  3. Enviando datos estructurados: JSON:

    • Queremos enviar más que un simple número o texto. Necesitamos enviar la posición táctil, que tiene coordenadas x e y. Podríamos enviarlos como “120,250”, pero es mejor estructurarlo. Recuerdas los protocolos ASCII y binario de las unidades anteriores?
    • Creamos un objeto JavaScript en el cliente móvil: let touchData = { type: 'touch', x: mouseX, y: mouseY };. Esto es claro y extensible (podríamos añadir más datos en el futuro).
    • Sin embargo, Socket.IO (y muchas comunicaciones en red) envían datos como strings (cadenas de texto). No podemos enviar un objeto JavaScript directamente.
    • JSON.stringify(touchData): Convierte el objeto JavaScript en una cadena de texto con formato JSON. Ejemplo: '{"type":"touch","x":120,"y":250}'. Esta cadena SÍ puede enviarse por la red.
    • JSON.parse(data): En el cliente receptor (escritorio), se recibe la cadena JSON. Esta función la convierte de nuevo en un objeto JavaScript utilizable: { type: 'touch', x: 120, y: 250 }.
  4. Capturando la entrada: eventos táctiles en p5.js (mobile/sketch.js):

    • p5.js ofrece funciones específicas que se ejecutan automáticamente cuando ocurren eventos táctiles en un dispositivo móvil (o simulación en escritorio).
    • touchMoved(): es la función clave aquí. Se llama continuamente mientras el usuario mantiene un dedo presionado y lo mueve sobre el canvas. Dentro de esta función, mouseX y mouseY contienen las coordenadas actuales del toque.
    • Optimización (threshold): el código no envía un mensaje en cada pequeño movimiento detectado por touchMoved(). Comprueba si el movimiento desde la última vez que se envió (lastTouchX, lastTouchY) supera un umbral (threshold). Esto evita inundar la red con mensajes si el dedo tiembla o se mueve mínimamente, enviando solo cambios significativos.
    • Otras funciones útiles (no usadas en este caso base, pero relevantes):
      • touchStarted(): se llama una vez cuando el usuario toca la pantalla por primera vez.
      • touchEnded(): se llama una vez cuando el usuario levanta el dedo de la pantalla.

🚀 Tu solución:

¿Por qué es necesario Dev Tunnels y cómo funciona? Mi servidor corre en localhost:3000, que solo existe dentro de mi máquina. Dev Tunnels me da una URL pública que cualquiera puede abrir; ese túnel toma las peticiones de Internet y las reenvía a mi puerto 3000. Así mi celular, esté donde esté, llega a mi servidor sin que yo abra puertos ni cambie el router.

¿Por qué uso JSON.stringify en el emisor y JSON.parse en el receptor? Socket.IO viaja con texto. Con JSON.stringify convierto mi objeto {x, y} en una cadena. Cuando llega, JSON.parse la vuelve a un objeto usable. Evito concatenar valores manualmente y puedo añadir más campos sin romper el formato.

Función de touchMoved y el umbral (threshold) touchMoved() se ejecuta cada vez que arrastro el dedo. Si mando un mensaje en cada pixel se saturaría la red; por eso guardo la última posición y solo emito si me moví más que threshold, enviando menos, pero relevantes.

Otros eventos táctiles en p5.js y posibles usos touchStarted() para detectar un tap o presionar un botón virtual. touchEnded() para soltar y, por ejemplo, disparar una acción al levantar el dedo. doubleClicked() (no táctil puro, pero útil) para gestos tipo doble tap. Con ellos puedo hacer botones, sliders o reconocer gestos sencillos sin librerías extras.

Dev Tunnels vs. IP local Con IP local funciona rápido y sin terceros, pero solo dentro de la misma red y a veces el firewall bloquea. Dev Tunnels atraviesa redes y datos móviles, no requiere configurar el router, pero depende de un servicio externo y añade un poco de latencia.

Actividad 03

Análisis del servidor puente (server.js)

👣 Pasos: (Análisis del código)

server.js
const express = require('express'); // Framework para servidor web
const http = require('http'); // Módulo HTTP base de Node.js
const socketIO = require('socket.io'); // Librería para WebSockets
const app = express(); // Crea la aplicación Express
const server = http.createServer(app); // Crea el servidor HTTP usando Express
const io = socketIO(server); // Vincula Socket.IO al servidor HTTP
const port = 3000; // Puerto en el que escuchará LOCALMENTE
// Sirve archivos estáticos desde la carpeta 'public'
app.use(express.static('public'));
// Evento: se dispara cuando un nuevo cliente (navegador) se conecta vía Socket.IO
io.on('connection', (socket) => {
console.log('New client connected - ID:', socket.id);
// Evento: se dispara cuando ESE cliente envía un mensaje llamado 'message'
socket.on('message', (message) => {
console.log(`Received message => ${message} from ID: ${socket.id}`);
// Retransmite el mensaje recibido a TODOS los OTROS clientes conectados
socket.broadcast.emit('message', message);
});
// Evento: se dispara cuando ESE cliente se desconecta
socket.on('disconnect', () => {
console.log('Client disconnected - ID:', socket.id);
});
});
// Inicia el servidor y lo pone a escuchar en el puerto local especificado
server.listen(port, () => {
console.log(`Server is listening on http://localhost:${port}`);
});

🚀 Tu solución:

Análisis del código server.js

Setup inicial del servidor

const express = require('express');
const http = require('http');
const socketIO = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketIO(server);
const port = 3000;

✅ ¿Qué está pasando aquí?

  1. express: crea el servidor web.
  2. http: permite crear un servidor base compatible con WebSockets.
  3. socket.io: añade soporte para comunicación en tiempo real.
  4. Se establece el puerto 3000 como punto de acceso local (que será publicado por Dev Tunnels).

Servir archivos estáticos

app.use(express.static('public'));

✅ ¿Qué hace esto?

Esta línea le dice a Express que sirva automáticamente los archivos de la carpeta public/. Es decir, si visitas http://localhost:3000/desktop/index.html, el servidor busca directamente public/desktop/index.html.

🔁 Comparación con app.get() (Unidad 6):

  • app.get('/ruta', ...) sirve rutas específicas y manuales.
  • express.static() automatiza todo el proceso: se sirve todo el contenido estático según las carpetas y archivos.

🔌 Conexión de clientes vía WebSockets

io.on('connection', (socket) => {
console.log('New client connected - ID:', socket.id);
socket.on('message', (message) => {
console.log(`Received message => ${message} from ID: ${socket.id}`);
socket.broadcast.emit('message', message);
});
socket.on('disconnect', () => {
console.log('Client disconnected - ID:', socket.id);
});
});

✅ Explicación paso a paso:

  1. io.on('connection'): se ejecuta cada vez que un cliente se conecta al servidor.

  2. El parámetro socket representa la conexión de ese cliente específico.

  3. socket.on('message'):

    • Se activa cuando ese cliente envía un mensaje (por ejemplo, desde el móvil).
    • El servidor recibe y retransmite el mensaje a los otros clientes, usando socket.broadcast.emit(...).
  4. socket.broadcast.emit('message', message):

    • Reenvía el mensaje a todos los clientes conectados excepto al que lo envió.
  5. socket.on('disconnect'):

    • Se ejecuta cuando el cliente se desconecta.

▶️ Iniciar el servidor

server.listen(port, () => {
console.log(`Server is listening on http://localhost:${port}`);
});

El servidor empieza a escuchar conexiones entrantes en el puerto 3000. Cuando lo hace correctamente, imprime un mensaje de confirmación.


🧩 Respuestas a preguntas de reflexión

❓ ¿Cuál es la función principal de express.static('public')?

Sirve automáticamente los archivos estáticos (HTML, JS, CSS, imágenes) ubicados en la carpeta public. Es mucho más simple que definir manualmente cada ruta con app.get(...), especialmente para proyectos con muchas páginas o archivos.


❓ Flujo del mensaje táctil

  1. Cliente móvil (JS): detecta un toque y envía un mensaje al servidor con socket.emit('message', datos).

  2. Servidor (server.js):

    • Recibe el mensaje mediante socket.on('message', callback).
    • Lo reenvía a los demás clientes con socket.broadcast.emit('message', datos).
  3. Cliente escritorio: está suscrito al evento socket.on('message', callback) y lo recibe.

🔁 ¿Por qué usar socket.broadcast.emit?

Porque queremos que todos los demás clientes (escritorio u otros móviles) reciban el mensaje, excepto el que lo envió (el móvil).

  • socket.emit: solo al cliente actual.
  • io.emit: a todos, incluido el que envió.
  • socket.broadcast.emit: a todos menos el que envió. ¡Perfecto para nuestro caso!

❓ ¿Qué pasa si conectas dos escritorios y un móvil?

Si el móvil envía un mensaje táctil:

  • Ambos escritorios recibirán el mensaje (porque no lo originaron).
  • El móvil no lo recibe de vuelta (ya lo tiene localmente).

Esto se debe a que se usa socket.broadcast.emit(...), que excluye al cliente emisor.


❓ ¿Para qué sirven los console.log?

  1. Informan cuando alguien se conecta o desconecta, lo cual ayuda a monitorear el estado del servidor.
  2. Muestran los mensajes recibidos y quién los envió (socket.id).
  3. Son útiles para depuración y verificar que el flujo de mensajes funcione correctamente.

Resumen general del server.js

SecciónFunción principal
express.static()Sirve archivos cliente desde public/ automáticamente
io.on('connection')Gestiona nuevos clientes y su comunicación
socket.on('message')Recibe mensajes del cliente
socket.broadcast.emit()Retransmite mensajes a otros clientes
server.listen()Inicia el servidor en el puerto 3000

Actividad 04

Análisis del cliente móvil (mobile/sketch.js) y de escritorio (desktop/sketch.js)

👣 Pasos: (Análisis del código)

1. Cliente móvil (mobile/sketch.js) - El Emisor
// mobile/sketch.js (partes clave)
let socket;
let lastTouchX = null;
let lastTouchY = null;
const threshold = 5; // Umbral para evitar enviar demasiados mensajes
function setup() {
// ... createCanvas, background ...
socket = io();
socket.on('connect', () => console.log('Connected to server'));
// ... otros listeners de socket ('message', 'disconnect', 'connect_error') ...
}
function touchMoved() { // Función especial de p5.js para eventos táctiles
if (socket && socket.connected) {
// Calcula si el movimiento supera el umbral
let dx = abs(mouseX - lastTouchX);
let dy = abs(mouseY - lastTouchY);
if (dx > threshold || dy > threshold || lastTouchX === null) { // Enviar si supera umbral o es el primer toque
let touchData = {
type: 'touch', // Tipo de mensaje (podríamos tener otros)
x: mouseX, // Coordenada X del toque (relativa al canvas móvil)
y: mouseY // Coordenada Y del toque
};
// Envía el objeto como una cadena JSON al servidor
socket.emit('message', JSON.stringify(touchData));
// Actualiza la última posición registrada
lastTouchX = mouseX;
lastTouchY = mouseY;
}
}
return false; // Evita comportamiento default del navegador en móviles
}
2. Cliente de Escritorio (desktop/sketch.js) - El Receptor
// desktop/sketch.js (partes clave)
let socket;
let circleX = 200; // Posición inicial X
let circleY = 200; // Posición inicial Y
function setup() {
// ... createCanvas, background ...
socket = io();
socket.on('connect', () => console.log('Connected to server'));
// Listener clave: se ejecuta cuando llega un mensaje del servidor
socket.on('message', (data) => {
console.log(`Received message: ${data}`);
try {
// Intenta convertir la cadena JSON de vuelta a un objeto
let parsedData = JSON.parse(data);
// Verifica si es un mensaje de tipo 'touch'
if (parsedData && parsedData.type === 'touch') {
// Actualiza las coordenadas del círculo con los datos recibidos
// ¡Ojo! Las coordenadas vienen del canvas móvil.
// Aquí simplemente las usamos, pero en un caso real podríamos necesitar mapearlas
// si los canvas tuvieran tamaños diferentes.
circleX = parsedData.x;
circleY = parsedData.y;
}
} catch (e) {
console.error("Error parsing received JSON:", e);
}
});
// ... otros listeners ('disconnect', 'connect_error') ...
}
function draw() {
background(220);
fill(255, 0, 0);
ellipse(circleX, circleY, 50, 50); // Dibuja el círculo en la posición actualizada
}

📝 Actividad pendiente por iniciar

El archivo student.md está vacío

Aplicación 🛠

Actividad 05

Aplica lo aprendido

👣 Pasos:

  1. Analiza la siguiente aplicación:
let particles = [];
function setup() {
createCanvas(windowWidth, windowHeight);
background(0, 20);
}
function draw() {
if (mouseIsPressed) {
particles.push(new Particle(mouseX, mouseY));
}
for (let i = particles.length - 1; i >= 0; i--) {
particles[i].update();
particles[i].display();
if (particles[i].isDead()) {
particles.splice(i, 1);
}
}
if (particles.length === 0) background(0, 20);
}
class Particle {
constructor(x, y) {
this.pos = createVector(x, y);
this.vel = p5.Vector.random2D();
this.lifespan = 255;
this.size = random(5, 20);
this.noiseOffset = random(1000);
}
update() {
// Se utiliza ruido Perlin para definir la dirección de la fuerza
let angle =
noise(this.pos.x * 0.005, this.pos.y * 0.005, this.noiseOffset) *
TWO_PI *
2;
let force = p5.Vector.fromAngle(angle);
force.mult(0.5);
// Se aplica la fuerza a la velocidad
this.vel.add(force);
this.vel.limit(4);
this.pos.add(this.vel);
// La vida de la partícula disminuye gradualmente
this.lifespan -= 3;
}
display() {
fill(150, 100, 255, this.lifespan);
ellipse(this.pos.x, this.pos.y, this.size);
}
isDead() {
return this.lifespan < 0;
}
}
function keyPressed() {
console.log(`particle size: ${particles.length}`);
}
  1. Vas a modificar la aplicación anterior para que se convierta en la aplicación de escritorio.

    • El móvil enviará la posición del toque (X/Y) al servidor.
    • El escritorio recibirá la posición y dibujará una partícula en esa posición.
    • La partícula se comportará como en el código original, pero ahora su posición inicial será la del toque del móvil.
    • El móvil enviará el color y si las partículas se pintan o no con stroke.
  2. Ten presente que la aplicación móvil enviará la posición del toque (X/Y), el color de la partícula y si se pinta o no con stroke. El escritorio recibirá estos datos y los usará para dibujar la partícula en la posición del toque.

📝 Actividad pendiente por iniciar

El archivo student.md está vacío

Consolidación y metacognición 🤔

Actividad 06

Consolidación de lo aprendido

👣 Pasos:

  1. Identifica los componentes clave: lista todos los elementos principales involucrados en la comunicación:
    • Aplicación cliente móvil (navegador + mobile/sketch.js)
    • Servidor Node.js (server.js + Express + Socket.IO)
    • Servicio de VS Code Dev Tunnels (actuando como proxy/puente público)
    • Aplicación cliente de escritorio (navegador + desktop/sketch.js)
    • El usuario (interactuando con el móvil)
  2. Dibuja el diagrama:
    • Representa cada componente como una caja o nodo en tu diagrama.
    • Usa flechas para indicar el flujo de la información principal (el evento de toque, color, stroke).
    • Etiqueta las flechas para indicar qué tipo de información o evento representan en cada paso (ej: “Evento touch (x, y)”, “socket.emit(‘message’, JSON)”, “Petición HTTP/WebSocket”, “socket.broadcast.emit(‘message’, JSON)”, “Actualización de coordenadas”, “Dibujo en canvas”).
    • Asegúrate de mostrar claramente cómo interviene el servicio Dev Tunnels entre internet y tu servidor local.
  3. Añade explicaciones: debajo o al lado del diagrama, escribe una breve descripción del rol de cada componente principal en el proceso general.

📝 Actividad pendiente por iniciar

El archivo student.md está vacío

Actividad 07

Autoevaluación: ¿Qué he aprendido?

👣 Pasos:

  1. Revisa los objetivos: vuelve a leer la sección ¿Qué aprenderás en esta unidad? 💡.
  2. Evalúa tu confianza: para cada objetivo de aprendizaje, evalúate honestamente usando una escala simple (ej: 2=Necesito repasar mucho, 3=Lo entiendo pero con dudas, 4=Lo entiendo bien, 5=Podría explicarlo a un compañero).
    • Configurar y usar VS Code Dev Tunnels: [Tu Puntuación]
    • Implementar arquitectura cliente-servidor (móvil->servidor): [Tu Puntuación]
    • Usar Socket.IO para retransmitir datos (servidor->escritorio): [Tu Puntuación]
    • Capturar y procesar eventos en el móvil (p5.js): [Tu Puntuación]
    • Modificar sistema interactivo para crear la experiencia: [Tu Puntuación]
    • Analizar y explicar flujo de datos completo (móvil->servidor->escritorio): [Tu Puntuación]
  3. Reflexiona sobre el proceso:
    • ¿Qué concepto o actividad de esta unidad te resultó más fácil de entender o realizar? ¿Por qué crees que fue así?
    • ¿Qué concepto o actividad te presentó mayor dificultad? ¿Qué pasos seguiste para intentar superarla? ¿Qué recursos o estrategias te fueron más útiles?
    • Describe con tus propias palabras, como si se lo explicaras a alguien que no tomó el curso, cuál es el flujo principal de información en la aplicación que construimos (desde la interacción del usuario en el móvil hasta la imagen en el escritorio). ¿Qué rol juega cada tecnología (Node.js, Socket.IO, Dev Tunnels, p5.js)?
    • ¿Cómo crees que podrías aplicar lo aprendido en esta unidad (usar un móvil como controlador, comunicación en tiempo real, túneles) en otros proyectos o contextos?

📝 Actividad pendiente por iniciar

El archivo student.md está vacío

Actividad 08

Mejorando juntos: feedback de la unidad

👣 Pasos:

  1. Reflexiona sobre la unidad en general: piensa en todo el proceso, desde la introducción hasta la consolidación.
  2. Considera los siguientes aspectos y proporciona comentarios específicos para cada uno:
    • Claridad de los objetivos: ¿Fueron claros los objetivos de aprendizaje desde el principio? ¿Sientes que los alcanzaste?
    • Caso de estudio: ¿Fue útil el caso de estudio proporcionado? ¿Fue demasiado simple, demasiado complejo o adecuado?
    • Conceptos nuevos (Dev Tunnels): ¿Fue clara la explicación y la necesidad de usar Dev Tunnels? ¿Tuviste dificultades con su configuración o uso?
    • Análisis del código (SEEK): ¿Fueron útiles las explicaciones detalladas del código del servidor y los clientes? ¿Los experimentos propuestos te ayudaron a entender mejor? ¿Algo fue confuso?
    • Actividad de aplicación (APPLY): ¿Te pareció interesante y motivadora la actividad de modificar la interacción? ¿Fue un reto adecuado?
    • Actividades de reflexión (REFLECT): ¿Te ayudaron las actividades de consolidación y autoevaluación a reforzar tu aprendizaje?
    • Ritmo y carga de trabajo: ¿Cómo te pareció el ritmo de la unidad? ¿La cantidad de trabajo fue razonable?
    • Recursos: ¿Fueron útiles los enlaces y recursos proporcionados? ¿Echaste en falta algún recurso adicional?
  3. Sugerencias de mejora: ¿Tienes alguna sugerencia específica sobre cómo se podría mejorar esta unidad? (Ej: más ejemplos, explicaciones alternativas, diferentes tipos de actividades, etc.)
  4. Comentario general: cualquier otro comentario o aspecto que quieras destacar sobre tu experiencia en esta unidad.

📝 Actividad pendiente por iniciar

El archivo student.md está vacío