Autor: ciberkaos

  • 1.4 – Editores de texto en Ubuntu: nano y vi

    1.4 – Editores de texto en Ubuntu: nano y vi

    Cuando trabajamos en Linux, no siempre tendremos una interfaz gráfica disponible. En muchos casos estaremos dentro de una terminal, conectados por SSH a un servidor o trabajando en una máquina sin entorno visual. En esas situaciones, saber usar un editor de texto en consola deja de ser “algo opcional” y pasa a ser una habilidad básica.

    En Ubuntu existen varios editores de texto para terminal, pero dos de los más conocidos y clásicos son:

    • nano
    • vi (o su versión mejorada, vim, en muchos sistemas)

    Aunque ambos sirven para editar archivos de texto desde la terminal, su forma de uso es muy diferente.
    Nano está pensado para ser sencillo e intuitivo.
    Vi es más potente y rápido para usuarios con experiencia, pero al principio puede parecer un pequeño artefacto caótico sacado de una nave alienígena.

    En esta guía vamos a ver qué son, para qué sirven, cómo abrir archivos, editar, guardar, salir y en qué situaciones conviene usar uno u otro.


    1. ¿Qué es un editor de texto en terminal?

    Un editor de texto en terminal es un programa que permite:

    • crear archivos de texto,
    • modificar su contenido,
    • guardar cambios,
    • editar archivos de configuración del sistema,
    • escribir scripts,
    • cambiar pequeños fragmentos de código o documentación.

    En Linux, los editores en terminal son especialmente útiles para:

    • modificar archivos de configuración en /etc,
    • editar scripts .sh,
    • crear notas rápidas,
    • trabajar en servidores remotos,
    • solucionar errores cuando no hay entorno gráfico.

    Por ejemplo, si queremos cambiar la configuración de red, revisar un archivo de Apache o editar un script de mantenimiento, es muy habitual usar un editor en consola.


    2. Editor nano

    2.1. ¿Qué es nano?

    Nano es un editor de texto en terminal sencillo, directo y fácil de aprender.
    Suele ser la mejor opción para principiantes porque muestra en pantalla los atajos principales y su funcionamiento es bastante intuitivo.

    Es muy usado para:

    • editar archivos rápidos,
    • modificar configuraciones simples,
    • crear scripts básicos,
    • trabajar en prácticas de clase sin complicarse demasiado.

    2.2. Abrir o crear un archivo con nano

    Para abrir un archivo existente o crear uno nuevo:

    nano archivo.txt

    Ejemplo:

    nano notas.txt

    Si notas.txt no existe, nano lo creará cuando lo guardemos.

    También podemos abrir archivos con ruta completa:

    nano /home/usuario/documentos/prueba.txt

    O editar archivos del sistema con permisos de administrador:

    sudo nano /etc/hosts

    2.3. Escribir y editar en nano

    Una vez dentro de nano, simplemente escribimos como si fuera un editor normal.
    No hace falta cambiar de modo ni activar nada especial.

    Podemos:

    • escribir texto directamente,
    • borrar con retroceso,
    • movernos con las flechas,
    • cortar y pegar texto con combinaciones de teclas.

    Esto lo hace muy cómodo para empezar.


    2.4. Atajos básicos de nano

    En la parte inferior de la pantalla suelen aparecer los atajos más importantes.
    El símbolo ^ significa Ctrl.

    Por ejemplo:

    • ^O significa Ctrl + O
    • ^X significa Ctrl + X

    Comandos más usados en nano

    AcciónAtajo
    Guardar archivoCtrl + O
    SalirCtrl + X
    Buscar textoCtrl + W
    Cortar líneaCtrl + K
    Pegar líneaCtrl + U
    Mostrar ayudaCtrl + G

    2.5. Guardar un archivo en nano

    Para guardar:

    Ctrl + O

    Nano pedirá confirmación del nombre del archivo.
    Pulsamos Enter para aceptar.


    2.6. Salir de nano

    Para salir:

    Ctrl + X

    Si hay cambios sin guardar, nano preguntará si queremos guardarlos.

    Opciones habituales:

    • Y → sí, guardar
    • N → no guardar

    2.7. Buscar texto en nano

    Para buscar una palabra o fragmento:

    Ctrl + W

    Escribimos el texto a buscar y pulsamos Enter.

    Esto es útil cuando editamos archivos largos de configuración.


    2.8. Ejemplo práctico con nano

    Vamos a crear un pequeño archivo de prueba:

    nano ejemplo_nano.txt

    Dentro escribimos:

    Hola, este es un archivo creado con nano.
    Estoy aprendiendo a editar desde la terminal.

    Después:

    1. Pulsamos Ctrl + O
    2. Pulsamos Enter
    3. Pulsamos Ctrl + X

    Para comprobar el contenido:

    cat ejemplo_nano.txt

    3. Editor vi

    3.1. ¿Qué es vi?

    Vi es un editor de texto clásico de Unix y Linux.
    Es muy potente, muy rápido y muy utilizado en administración de sistemas.
    El problema, para quien empieza, es que no funciona como nano ni como un editor tradicional.

    En vi existen distintos modos de trabajo. Y aquí está la trampa elegante del asunto: si no entiendes los modos, parece que el editor está roto; en realidad no está roto, solo está siendo vi.


    3.2. ¿Por qué vi puede parecer difícil?

    Porque en vi no se escribe directamente al abrir el archivo.
    Primero hay que entender que tiene varios modos.

    Los dos más importantes son:

    • modo normal: sirve para moverse, borrar, copiar, pegar, ejecutar órdenes
    • modo inserción: sirve para escribir texto

    Cuando abrimos vi, normalmente entramos en modo normal, no en modo escritura.


    3.3. Abrir o crear un archivo con vi

    vi archivo.txt

    Ejemplo:

    vi notas_vi.txt

    También podemos abrir archivos del sistema:

    sudo vi /etc/hosts

    3.4. Modos básicos de vi

    Modo normal

    Es el modo inicial.
    Aquí no escribimos texto directamente.
    Sirve para movernos por el archivo y lanzar comandos.

    Modo inserción

    Permite escribir texto.

    Para entrar en modo inserción se suele usar:

    i

    Una vez pulsamos i, ya podemos escribir.

    Para salir del modo inserción y volver al modo normal:

    Esc

    Esta tecla es importantísima. Muchísimo. Casi filosófica.


    3.5. Escribir en vi

    Pasos básicos:

    1. Abrimos el archivo: vi prueba.txt
    2. Pulsamos: i
    3. Escribimos el contenido.
    4. Pulsamos: Esc
    5. Guardamos y salimos con: :wq
    6. Pulsamos Enter

    3.6. Comandos básicos de vi

    Estos comandos se escriben en modo normal.

    AcciónComando
    Entrar en modo insercióni
    Volver a modo normalEsc
    Guardar:w
    Salir:q
    Guardar y salir:wq
    Salir sin guardar:q!
    Borrar una líneadd
    Copiar una líneayy
    Pegarp
    Buscar texto/texto

    3.7. Guardar en vi

    Para guardar el archivo:

    :w

    Y pulsamos Enter.


    3.8. Salir de vi

    Para salir si no hay cambios pendientes:

    :q

    Si queremos salir sin guardar cambios:

    :q!

    Esto es útil cuando hemos tocado algo por error y no queremos salvarlo.


    3.9. Guardar y salir en vi

    La combinación más habitual:

    :wq

    Después pulsamos Enter.


    3.10. Buscar texto en vi

    En modo normal:

    /texto

    Ejemplo:

    /ubuntu

    Esto buscará la palabra ubuntu dentro del archivo.


    3.11. Ejemplo práctico con vi

    Abrimos el archivo:

    vi ejemplo_vi.txt

    Pasos:

    1. Pulsar i
    2. Escribir:
    Este archivo ha sido creado con vi.
    Estoy aprendiendo a usar un editor modal.
    1. Pulsar Esc
    2. Escribir:
    :wq
    1. Pulsar Enter

    Para verificar:

    cat ejemplo_vi.txt

    4. Diferencias entre nano y vi

    Aunque ambos editores permiten hacer lo mismo en lo esencial, la experiencia de uso cambia bastante.

    Característicananovi
    Facilidad para principiantesMuy altaBaja al principio
    Escritura directaSíNo, hay que entrar en modo inserción
    Curva de aprendizajeSuaveMás pronunciada
    Rapidez para usuarios avanzadosCorrectaMuy alta
    Uso habitual en servidoresSíSí, muchísimo
    Ayuda visible en pantallaSíNo de forma tan directa
    Ideal para empezarSíNo especialmente
    Ideal para administración avanzadaA vecesSí

    5. ¿Cuál conviene usar?

    Usa nano si…

    • estás empezando en Linux,
    • quieres editar archivos rápidamente,
    • no quieres aprender comandos complejos todavía,
    • necesitas algo fácil para prácticas de clase.

    Usa vi si…

    • quieres aprender herramientas clásicas de administración,
    • trabajarás en muchos servidores,
    • te interesa dominar entornos Unix/Linux de forma más profesional,
    • quieres velocidad y potencia a largo plazo.

    La recomendación más sensata para empezar suele ser:

    • primero aprender nano,
    • después conocer vi al menos a nivel básico.

    Porque tarde o temprano te lo vas a encontrar. Vi aparece en servidores, distribuciones mínimas, sistemas de recuperación y contextos donde no siempre tendrás “tu editor favorito”. Es un poco como una llave inglesa vieja: no es glamurosa, pero cuando hace falta, hace falta.



    10. Tabla resumen final

    Tareananovi
    Abrir archivonano archivo.txtvi archivo.txt
    Escribir textoDirectamentePulsar i
    GuardarCtrl + O:w
    SalirCtrl + X:q
    Guardar y salirGuardar y luego salir:wq
    Salir sin guardarResponder N al salir:q!
    Buscar textoCtrl + W/texto

    11. Ejercicios propuestos

    Ejercicio 1

    Crea un archivo llamado practica_nano.txt con nano y escribe tres líneas explicando qué es Linux.

    Ejercicio 2

    Edita el archivo anterior y añade una cuarta línea.

    Ejercicio 3

    Crea un archivo llamado practica_vi.txt con vi, escribe dos líneas y guárdalo correctamente.

    Ejercicio 4

    Abre un archivo con vi, escribe algo y sal sin guardar usando :q!.

    Ejercicio 5

    Busca una palabra dentro de un archivo usando:

    • Ctrl + W en nano
    • /texto en vi

    Conclusión

    Saber usar un editor en terminal es una competencia básica en Linux.
    Aunque hoy en día existan muchos editores gráficos y herramientas modernas, en administración de sistemas, servidores y tareas técnicas sigue siendo muy habitual editar archivos desde consola.

    Nano es la puerta de entrada ideal para empezar.
    Vi es una herramienta más exigente, pero también más potente y muy importante en el mundo Linux.

    Lo recomendable es no complicarse al principio:
    aprende primero a trabajar con nano, y después familiarízate con vi aunque solo sea en sus comandos esenciales. Con eso ya tendrás una base muy útil para seguir avanzando.


  • 1.5 – Comando find en Ubuntu

    1.5 – Comando find en Ubuntu

    El comando find es una de las herramientas más potentes de Linux para buscar archivos y directorios dentro del sistema.

    A diferencia de otros comandos más simples, find no solo permite localizar elementos por su nombre, sino también filtrarlos por muchas otras características, como por ejemplo:

    • nombre
    • tipo de archivo
    • tamaño
    • fecha de modificación
    • usuario propietario
    • permisos
    • contenido combinado con otros comandos
    • y mucho más

    Es un comando muy útil tanto para tareas cotidianas como para administración de sistemas, automatización y mantenimiento.


    ¿Para qué sirve find?

    find se utiliza para recorrer directorios y buscar elementos que cumplan unas condiciones concretas.

    Algunos usos habituales son:

    • localizar un archivo cuyo nombre no recordamos bien
    • buscar todos los archivos .txt o .log
    • encontrar carpetas concretas
    • detectar archivos grandes
    • localizar archivos modificados recientemente
    • buscar archivos vacíos
    • combinar la búsqueda con acciones automáticas, como borrar o ejecutar otro comando

    Sintaxis básica

    find [ruta] [condiciones]

    Estructura

    • ruta: lugar desde el que comienza la búsqueda
    • condiciones: filtros que debe cumplir el archivo o directorio

    Ejemplo mínimo

    find /home

    Este comando recorre todo el directorio /home y muestra todo lo que encuentra: archivos, carpetas, enlaces, etc.


    Primeros ejemplos sencillos

    Buscar en el directorio actual

    find .

    El punto . significa directorio actual.

    Buscar dentro de /home

    find /home

    Buscar un archivo por nombre exacto

    find /home -name "documento.txt"

    Busca un archivo llamado exactamente documento.txt.


    Buscar por nombre

    Opción -name

    Permite buscar por nombre respetando mayúsculas y minúsculas.

    find /home -name "foto.jpg"

    Esto encontrará solo archivos o directorios con ese nombre exacto.

    Opción -iname

    Permite buscar ignorando mayúsculas y minúsculas.

    find /home -iname "foto.jpg"

    Esto encontraría:

    • foto.jpg
    • Foto.jpg
    • FOTO.JPG

    Muy útil cuando no recordamos bien cómo estaba escrito el nombre.


    Uso de comodines

    find permite usar comodines entre comillas.

    Buscar todos los archivos .txt

    find /home -name "*.txt"

    Buscar todos los archivos .log

    find /var/log -name "*.log"

    Buscar nombres que empiecen por “tema”

    find /home -name "tema*"

    Buscar nombres que terminen en “2025”

    find /home -name "*2025"

    Buscar por tipo

    Con la opción -type podemos indicar qué queremos encontrar.

    Tipos más usados

    • f → archivo normal
    • d → directorio
    • l → enlace simbólico

    Buscar solo archivos

    find /home -type f

    Buscar solo directorios

    find /home -type d

    Buscar solo enlaces simbólicos

    find /home -type l

    Buscar archivos .pdf

    find /home -type f -name "*.pdf"

    Aquí combinamos dos condiciones:

    • que sea archivo
    • que termine en .pdf

    Buscar por tamaño

    La opción -size permite buscar por tamaño.

    Unidades comunes

    • c → bytes
    • k → kilobytes
    • M → megabytes
    • G → gigabytes

    Ejemplos

    Buscar archivos de exactamente 10 MB

    find /home -type f -size 10M

    Buscar archivos mayores de 100 MB

    find /home -type f -size +100M

    Buscar archivos menores de 1 MB

    find /home -type f -size -1M

    El signo cambia mucho la película:

    • 10M → exactamente 10 MB
    • +10M → más de 10 MB
    • -10M → menos de 10 MB

    Buscar por fecha

    find también puede buscar según el momento en que un archivo fue modificado.

    Opción -mtime

    Busca por días desde la última modificación.

    Archivos modificados hace menos de 7 días

    find /home -type f -mtime -7

    Archivos modificados hace más de 30 días

    find /home -type f -mtime +30

    Archivos modificados exactamente hace 1 día

    find /home -type f -mtime 1

    Opción -mmin

    Busca por minutos desde la última modificación.

    Archivos modificados en los últimos 60 minutos

    find /home -type f -mmin -60

    Esto es muy útil para tareas recientes o diagnósticos.


    Buscar por usuario o grupo

    Buscar archivos de un usuario concreto

    find /home -user alumno1

    Buscar archivos de un grupo concreto

    find /home -group profesores

    Esto puede venir muy bien en sistemas multiusuario.


    Buscar por permisos

    La opción -perm permite localizar archivos con permisos específicos.

    Buscar archivos con permiso 644

    find /home -type f -perm 644

    Buscar directorios con permiso 755

    find /home -type d -perm 755

    Esto es útil para revisar seguridad, configuraciones o errores de permisos.


    Buscar archivos vacíos

    Archivos vacíos

    find /home -type f -empty

    Directorios vacíos

    find /home -type d -empty

    Muy útil para limpieza del sistema o revisión de estructuras.


    Limitar la profundidad de búsqueda

    A veces no queremos que find recorra infinitamente subcarpetas como si estuviera poseído por un espíritu del caos. Para eso usamos estas opciones.

    -maxdepth

    Limita cuántos niveles de profundidad puede bajar.

    find /home -maxdepth 1

    Muestra solo lo que hay en /home sin entrar en subdirectorios.

    -mindepth

    Indica desde qué nivel empezar a mostrar resultados.

    find /home -mindepth 1

    Evita mostrar el directorio inicial.

    Ejemplo combinado

    find /home -mindepth 1 -maxdepth 2 -type f

    Busca archivos desde el primer nivel hasta el segundo.


    Ejecutar acciones con los resultados

    Una de las funciones más potentes de find es usar -exec, que permite ejecutar comandos sobre cada resultado encontrado.

    Sintaxis

    find [ruta] [condición] -exec comando {} \;
    • {} representa el archivo encontrado
    • \; indica el final del comando

    Ejemplo: mostrar información detallada

    find /home -name "*.txt" -exec ls -l {} \;

    Ejemplo: borrar archivos

    find /home -type f -name "*.tmp" -exec rm {} \;

    Cuidado con esto. Aquí ya estamos jugando con fuego elegante.

    Ejemplo: cambiar permisos

    find /home/proyecto -type f -name "*.sh" -exec chmod +x {} \;

    Borrar directamente con -delete

    También se puede borrar sin usar -exec.

    find /home -type f -name "*.tmp" -delete

    Esto es muy útil, pero también peligroso si no hemos probado antes la búsqueda.

    Recomendación importante

    Primero ejecuta la búsqueda sin borrar:

    find /home -type f -name "*.tmp"

    Y cuando compruebes que el resultado es correcto, entonces usas:

    find /home -type f -name "*.tmp" -delete

    Combinar varias condiciones

    find permite combinar condiciones para hacer búsquedas mucho más precisas.

    Buscar archivos .log de más de 10 MB

    find /var/log -type f -name "*.log" -size +10M

    Buscar archivos .txt modificados en los últimos 2 días

    find /home -type f -name "*.txt" -mtime -2

    Buscar directorios vacíos

    find /home -type d -empty

    Uso de operadores lógicos

    Opción -o (OR)

    Busca una cosa o la otra.

    find /home -name "*.jpg" -o -name "*.png"

    Busca archivos .jpg o .png.

    Opción ! (NOT)

    Niega una condición.

    find /home -type f ! -name "*.txt"

    Busca archivos que no terminen en .txt.


    Ejemplos prácticos

    1. Buscar todos los archivos PDF de un usuario

    find /home/alumno -type f -name "*.pdf"

    2. Buscar carpetas llamadas backup

    find /home -type d -name "backup"

    3. Buscar archivos grandes en todo el sistema

    find / -type f -size +500M

    4. Buscar archivos modificados hoy aproximadamente

    find /home -type f -mtime -1

    5. Buscar archivos temporales y eliminarlos

    find /tmp -type f -name "*.tmp" -delete

    6. Buscar scripts de shell

    find /home -type f -name "*.sh"

    7. Buscar directorios vacíos dentro del proyecto

    find /home/proyecto -type d -empty

    Buenas prácticas al usar find

    1. Empezar por una ruta concreta

    No uses / si no hace falta. Buscar desde la raíz puede tardar bastante y además mostrar errores por falta de permisos.

    Mejor algo como:

    find /home/usuario/documentos -name "*.txt"

    2. Probar primero antes de borrar

    Antes de usar -delete o -exec rm, ejecuta la búsqueda sin borrar nada.

    3. Usar comillas en los patrones

    Correcto:

    find /home -name "*.txt"

    No recomendable:

    find /home -name *.txt

    Sin comillas, el shell puede interpretar el patrón antes que find, y ahí empiezan los disgustos.

    4. Combinar condiciones para ser más preciso

    Cuanto más concreta sea la búsqueda, menos resultados basura tendrás.


    Errores comunes

    Error 1: olvidar la ruta

    find -name "*.txt"

    Aunque puede funcionar en algunos casos desde el directorio actual, es mejor escribirlo claramente:

    find . -name "*.txt"

    Error 2: no poner comillas

    find /home -name *.txt

    Puede dar resultados inesperados.

    Error 3: usar -delete sin comprobar antes

    Esto puede eliminar archivos importantes. Linux no te va a mirar con pena. Simplemente lo hará.

    Error 4: confundir -name con contenido del archivo

    find busca por nombre, tamaño, fechas, tipo, etc., pero no busca texto dentro de archivos.
    Para buscar texto dentro de archivos se usa normalmente grep.

    Ejemplo combinado:

    find /home -type f -name "*.txt" -exec grep "error" {} \;

    Opciones principales de find

    OpciónSignificadoEjemplo
    .Directorio actualfind .
    -nameBusca por nombre exacto respetando mayúsculasfind /home -name "foto.jpg"
    -inameBusca por nombre ignorando mayúsculasfind /home -iname "foto.jpg"
    -type fSolo archivosfind /home -type f
    -type dSolo directoriosfind /home -type d
    -type lSolo enlaces simbólicosfind /home -type l
    -sizeBusca por tamañofind /home -size +100M
    -mtimeBusca por días desde modificaciónfind /home -mtime -7
    -mminBusca por minutos desde modificaciónfind /home -mmin -60
    -userBusca por usuario propietariofind /home -user alumno1
    -groupBusca por grupo propietariofind /home -group profesores
    -permBusca por permisosfind /home -perm 644
    -emptyBusca archivos o directorios vacíosfind /home -empty
    -maxdepthLimita profundidad máximafind /home -maxdepth 2
    -mindepthLimita profundidad mínimafind /home -mindepth 1
    -execEjecuta un comando sobre cada resultadofind /home -name "*.txt" -exec ls -l {} \;
    -deleteBorra directamente los resultadosfind /tmp -name "*.tmp" -delete
    -oOperador ORfind /home -name "*.jpg" -o -name "*.png"
    !Negaciónfind /home ! -name "*.txt"


    Ejercicios

    Ejercicio 1

    Busca todos los archivos .txt dentro de tu carpeta personal.

    Ejercicio 2

    Busca todos los directorios dentro de /home.

    Ejercicio 3

    Encuentra archivos mayores de 50 MB.

    Ejercicio 4

    Busca archivos modificados en los últimos 3 días.

    Ejercicio 5

    Busca archivos vacíos dentro de una carpeta de pruebas.

    Ejercicio 6

    Busca todos los archivos .log y muestra solo los que sean archivos normales.

    Ejercicio 7

    Busca archivos .tmp dentro de una carpeta temporal y elimínalos solo después de comprobar el resultado.



    Soluciones

    EjercicioComando
    1find /home/$USER -type f -name "*.txt"
    2find /home -type d
    3find /home -type f -size +50M
    4find /home -type f -mtime -3
    5find /home/$USER/pruebas -type f -empty
    6find /home -type f -name "*.log"
    7find /tmp -type f -name "*.tmp" y después find /tmp -type f -name "*.tmp" -delete
  • 1.6 – Comando grep en Ubuntu

    1.6 – Comando grep en Ubuntu

    grep sirve para filtrar texto y mostrar solo las líneas que coinciden con un patrón.

    Por ejemplo, permite:

    • buscar una palabra dentro de un archivo
    • localizar errores en logs
    • encontrar líneas que contengan una IP, usuario o ruta
    • buscar texto ignorando mayúsculas
    • contar coincidencias
    • mostrar números de línea
    • buscar en varios archivos a la vez
    • combinarse con otros comandos mediante tuberías (|)

    Sintaxis básica

    grep [opciones] "patrón" archivo

    Estructura

    • opciones: modifican el comportamiento del comando
    • patrón: texto o expresión que queremos buscar
    • archivo: fichero o ficheros donde se realizará la búsqueda

    Primer ejemplo

    Supongamos que tenemos un archivo llamado notas.txt.

    Ana
    Pedro
    Lucía
    Pedro García
    María

    Si ejecutamos:

    grep "Pedro" notas.txt

    El resultado será:

    Pedro
    Pedro García

    Porque grep muestra las líneas donde aparece la palabra Pedro.


    Búsqueda básica en archivos

    Buscar una palabra en un archivo

    grep "error" sistema.log

    Muestra todas las líneas del archivo sistema.log que contienen la palabra error.

    Buscar en varios archivos a la vez

    grep "usuario" archivo1.txt archivo2.txt archivo3.txt

    Busca la palabra usuario en varios archivos.


    Ignorar mayúsculas y minúsculas

    Opción -i

    Permite buscar sin diferenciar entre mayúsculas y minúsculas.

    grep -i "ubuntu" documento.txt

    Esto encontraría:

    • ubuntu
    • Ubuntu
    • UBUNTU

    Muy útil cuando no sabemos cómo está escrito exactamente el texto.


    Mostrar números de línea

    Opción -n

    Muestra el número de línea donde aparece la coincidencia.

    grep -n "error" sistema.log

    Ejemplo de salida:

    15:error de conexión
    28:error al iniciar servicio

    Esto facilita localizar rápidamente la línea en el archivo.


    Contar coincidencias

    Opción -c

    Cuenta cuántas líneas contienen el patrón.

    grep -c "warning" sistema.log

    Devuelve un número, por ejemplo:

    7

    Esto significa que hay 7 líneas con la palabra warning.


    Mostrar solo nombres de archivos

    Opción -l

    Muestra solo los nombres de los archivos donde aparece el patrón.

    grep -l "mysql" *.txt

    En vez de mostrar las líneas, muestra qué archivos contienen esa palabra.

    Opción -L

    Hace lo contrario: muestra los archivos donde no aparece el patrón.

    grep -L "mysql" *.txt

    Buscar palabras exactas

    Opción -w

    Busca coincidencias de palabra completa.

    grep -w "sol" texto.txt

    Esto evita que aparezcan coincidencias como:

    • solitario
    • solución

    y muestra solo la palabra sol como término independiente.


    Invertir la búsqueda

    Opción -v

    Muestra las líneas que no coinciden con el patrón.

    grep -v "admin" usuarios.txt

    Muy útil para excluir líneas.


    Buscar recursivamente en carpetas

    Opción -r

    Busca dentro de todos los archivos de un directorio y sus subdirectorios.

    grep -r "localhost" /etc

    Busca la palabra localhost en todos los archivos dentro de /etc.

    Opción -rn

    Muy usada para búsqueda recursiva con número de línea.

    grep -rn "puerto" /etc

    Mostrar coincidencias exactas dentro de una línea

    Opción -o

    Muestra solo la parte de la línea que coincide.

    grep -o "error" sistema.log

    Si una línea tiene varias veces la palabra error, puede mostrar cada coincidencia por separado.


    Expresiones regulares básicas

    grep puede trabajar con patrones más avanzados mediante expresiones regulares.

    Buscar líneas que empiecen por una palabra

    grep "^root" usuarios.txt

    El símbolo ^ indica inicio de línea.

    Buscar líneas que terminen con una palabra

    grep "bash$" usuarios.txt

    El símbolo $ indica final de línea.

    Buscar líneas que contengan números

    grep "[0-9]" datos.txt

    Esto busca cualquier línea que contenga al menos un dígito.

    Buscar vocales

    grep "[aeiou]" texto.txt

    Buscar letras mayúsculas

    grep "[A-Z]" texto.txt

    Aquí empieza la magia útil. Con moderación, porque las expresiones regulares también tienen fama de convertirse en jeroglíficos con malas pulgas.


    Uso con tuberías

    Una de las cosas más potentes de grep es usarlo junto con otros comandos.

    Buscar un proceso en ejecución

    ps aux | grep apache

    Muestra las líneas del listado de procesos donde aparece apache.

    Buscar un servicio activo

    systemctl list-units | grep ssh

    Filtrar resultados de un listado

    ls -l | grep ".txt"

    Aquí grep filtra la salida de otro comando.


    Buscar en logs

    grep se usa muchísimo para revisar logs.

    Buscar errores en un archivo de log

    grep -i "error" /var/log/syslog

    Buscar intentos fallidos

    grep -i "failed" /var/log/auth.log

    Buscar accesos SSH

    grep "sshd" /var/log/auth.log

    Esto es especialmente útil en administración y ciberseguridad.


    Usar grep con find

    Una combinación muy habitual es usar find para localizar archivos y grep para buscar texto dentro de ellos.

    Buscar la palabra “password” en archivos .conf

    find /etc -name "*.conf" -exec grep -H "password" {} \;

    Explicación

    • find localiza archivos .conf
    • grep busca la palabra password
    • -H hace que aparezca también el nombre del archivo

    Mostrar nombre del archivo junto al resultado

    Opción -H

    grep -H "localhost" /etc/hosts

    Si se buscan varios archivos, esto ayuda a saber de cuál sale cada coincidencia.


    Ocultar nombre del archivo

    Opción -h

    grep -h "usuario" archivo1.txt archivo2.txt

    Muestra solo las líneas encontradas, sin el nombre del archivo.


    Buscar coincidencias exactas con expresiones ampliadas

    Opción -E

    Activa expresiones regulares extendidas.

    grep -E "error|failed|warning" sistema.log

    Busca líneas que contengan cualquiera de esas palabras.

    Muy útil porque evita tener que hacer búsquedas separadas.


    Buscar coincidencias fijas sin interpretar regex

    Opción -F

    Busca texto literal, sin interpretar caracteres especiales como expresiones regulares.

    grep -F "a+b" formulas.txt

    Esto busca exactamente a+b.


    Resaltar coincidencias

    En muchas terminales, grep resalta las coincidencias automáticamente. También se puede forzar:

    grep --color=auto "error" sistema.log

    Esto mejora mucho la lectura.


    Ejemplos

    1. Buscar la palabra linux en un archivo

    grep "linux" apuntes.txt

    2. Buscar la palabra linux ignorando mayúsculas

    grep -i "linux" apuntes.txt

    3. Mostrar las líneas donde aparece admin con su número

    grep -n "admin" usuarios.txt

    4. Contar cuántas líneas contienen error

    grep -c "error" sistema.log

    5. Mostrar líneas que no contienen root

    grep -v "root" usuarios.txt

    6. Buscar recursivamente la palabra puerto en una carpeta

    grep -r "puerto" /etc

    7. Buscar varias palabras con una sola orden

    grep -E "error|warning|failed" sistema.log

    8. Buscar procesos relacionados con mysql

    ps aux | grep mysql

    Buenas prácticas al usar grep

    1. Usar comillas en el patrón

    Correcto:

    grep "error de red" log.txt

    Esto evita que el shell haga cosas raras con espacios o caracteres especiales.

    2. Usar -i cuando no importe cómo está escrito

    Si buscas nombres, usuarios o palabras que pueden ir en mayúsculas o minúsculas, ahorra disgustos.

    3. Usar -n para localizar mejor

    Muy recomendable cuando luego se va a editar el archivo.

    4. Probar primero búsquedas simples

    Antes de montar una expresión regular digna de un grimorio arcano, comprueba que el texto base aparece.

    5. Tener cuidado con búsquedas recursivas en rutas grandes

    Buscar en / o en directorios enormes puede tardar bastante y generar mucho ruido.


    Errores comunes

    Error 1: pensar que grep busca archivos por nombre

    No.
    grep busca texto dentro de archivos o dentro de la salida de comandos.

    Para buscar archivos por nombre se usa find.

    Error 2: olvidar las comillas

    grep error de red log.txt

    Esto no se interpreta como una sola búsqueda, sino como varios argumentos.

    Lo correcto sería:

    grep "error de red" log.txt

    Error 3: confundir coincidencia parcial con palabra exacta

    grep "sol" texto.txt

    Puede encontrar:

    • sol
    • solución
    • solitario

    Si queremos solo la palabra completa:

    grep -w "sol" texto.txt

    Error 4: usar expresiones regulares sin darse cuenta

    Caracteres como ., *, ^, $, [ ] tienen significado especial.
    Si quieres buscar texto literal con símbolos, a veces conviene usar -F.


    Opciones principales de grep

    OpciónSignificadoEjemplo
    -iIgnora mayúsculas y minúsculasgrep -i "ubuntu" archivo.txt
    -nMuestra número de líneagrep -n "error" log.txt
    -cCuenta líneas coincidentesgrep -c "warning" log.txt
    -lMuestra solo nombres de archivos con coincidenciagrep -l "mysql" *.txt
    -LMuestra archivos sin coincidenciagrep -L "mysql" *.txt
    -wBusca palabra exactagrep -w "sol" texto.txt
    -vInvierte la búsquedagrep -v "admin" usuarios.txt
    -rBusca recursivamente en carpetasgrep -r "localhost" /etc
    -rnRecursivo con número de líneagrep -rn "puerto" /etc
    -oMuestra solo la coincidenciagrep -o "error" log.txt
    -HMuestra nombre del archivogrep -H "root" archivo.txt
    -hOculta nombre del archivogrep -h "root" archivo1.txt archivo2.txt
    -EUsa expresiones regulares extendidas`grep -E «error
    -FBusca texto literalgrep -F "a+b" archivo.txt
    --color=autoResalta coincidenciasgrep --color=auto "error" log.txt

    Diferencia entre grep y find

    Esto conviene dejarlo muy claro a los alumnos, porque se confunden muchísimo al principio.

    Comando¿Qué busca?Ejemplo
    findArchivos y directorios por nombre, tipo, tamaño, fecha, etc.find /home -name "*.txt"
    grepTexto dentro de archivos o salida de comandosgrep "error" log.txt

    Ejemplo práctico

    • find responde a: “¿Dónde están los archivos .txt?”
    • grep responde a: “¿Qué archivos contienen la palabra error?”

    Uno busca cosas.
    El otro busca texto dentro de las cosas.
    Cada uno hace su hechicería.


    Ejercicios

    Ejercicio 1

    Busca la palabra linux dentro de un archivo llamado apuntes.txt.

    Ejercicio 2

    Busca la palabra ubuntu ignorando mayúsculas y minúsculas.

    Ejercicio 3

    Muestra en qué líneas aparece la palabra admin dentro de usuarios.txt.

    Ejercicio 4

    Cuenta cuántas líneas contienen la palabra error en sistema.log.

    Ejercicio 5

    Muestra todas las líneas que no contienen la palabra root.

    Ejercicio 6

    Busca recursivamente la palabra servidor dentro de la carpeta /etc.

    Ejercicio 7

    Busca cualquiera de estas palabras en sistema.log: error, warning o failed.

    Ejercicio 8

    Muestra los procesos en ejecución relacionados con ssh.


    Soluciones de los ejercicios

    Ejercicio 1

    Enunciado: Busca la palabra linux dentro de un archivo llamado apuntes.txt.

    grep "linux" apuntes.txt

    Busca las líneas del archivo apuntes.txt que contienen la palabra linux.


    Ejercicio 2

    Enunciado: Busca la palabra ubuntu ignorando mayúsculas y minúsculas.

    grep -i "ubuntu" apuntes.txt

    La opción -i permite encontrar ubuntu, Ubuntu, UBUNTU, etc.


    Ejercicio 3

    Enunciado: Muestra en qué líneas aparece la palabra admin dentro de usuarios.txt.

    grep -n "admin" usuarios.txt

    La opción -n añade el número de línea a cada coincidencia.


    Ejercicio 4

    Enunciado: Cuenta cuántas líneas contienen la palabra error en sistema.log.

    grep -c "error" sistema.log

    La opción -c cuenta el número de líneas coincidentes.


    Ejercicio 5

    Enunciado: Muestra todas las líneas que no contienen la palabra root.

    grep -v "root" usuarios.txt

    La opción -v invierte la búsqueda y muestra las líneas que no contienen ese patrón.


    Ejercicio 6

    Enunciado: Busca recursivamente la palabra servidor dentro de la carpeta /etc.

    grep -r "servidor" /etc

    La opción -r busca en todos los archivos dentro del directorio y sus subdirectorios.


    Ejercicio 7

    Enunciado: Busca cualquiera de estas palabras en sistema.log: error, warning o failed.

    grep -E "error|warning|failed" sistema.log

    La opción -E permite usar expresiones regulares extendidas y el operador | significa “o”.


    Ejercicio 8

    Enunciado: Muestra los procesos en ejecución relacionados con ssh.

    ps aux | grep ssh

    ps aux lista los procesos y grep ssh filtra los que contienen ssh.

  • 1.7 – Uso combinado de grep y find

    1.7 – Uso combinado de grep y find

    Los comandos find y grep son muy potentes por separado, pero cuando se combinan permiten hacer búsquedas mucho más completas.

    Recordatorio rápido

    • find sirve para buscar archivos y directorios
    • grep sirve para buscar texto dentro de archivos

    Cuando los usamos juntos, podemos hacer cosas como:

    • localizar archivos de un tipo concreto y buscar texto dentro de ellos
    • encontrar configuraciones específicas en muchos archivos
    • revisar proyectos completos
    • analizar logs
    • buscar palabras sensibles dentro de archivos de configuración
    • localizar cadenas de texto en estructuras grandes de carpetas

    ¿Por qué combinarlos?

    Porque muchas veces no basta con saber dónde está un archivo ni basta con saber qué texto contiene un archivo concreto.

    Lo que realmente queremos es algo como esto:

    • “Busca todos los archivos .txt y dime cuáles contienen la palabra error”
    • “Encuentra todos los .conf y mira si aparece password”
    • “Busca dentro de todos los scripts .sh cuáles usan sudo”
    • “Revisa todos los archivos PHP y encuentra dónde se usa mysqli”

    Ahí es donde find localiza los archivos y grep inspecciona su contenido.


    Forma más común de combinarlos

    La combinación más habitual se hace con -exec.

    Sintaxis general

    find [ruta] [condición de archivos] -exec grep [opciones] "patrón" {} \;
    • find localiza los archivos
    • grep se ejecuta sobre cada archivo encontrado
    • {} representa el archivo actual
    • \; indica el final del comando que ejecuta find

    Primer ejemplo básico

    Buscar la palabra error en todos los archivos .log

    find /var/log -type f -name "*.log" -exec grep "error" {} \;

    ¿Qué hace este comando?

    • busca en /var/log
    • solo archivos normales (-type f)
    • cuyo nombre termine en .log
    • y en cada uno ejecuta grep "error"

    Mostrar también el nombre del archivo

    En muchos casos conviene usar grep -H para que aparezca el nombre del fichero junto al resultado.

    find /var/log -type f -name "*.log" -exec grep -H "error" {} \;

    Así no solo vemos la línea encontrada, sino también en qué archivo aparece.


    Ejemplos prácticos de find + grep

    1. Buscar la palabra password en archivos .conf

    find /etc -type f -name "*.conf" -exec grep -H "password" {} \;

    Uso típico

    Revisar configuraciones donde se haya escrito la palabra password.


    2. Buscar localhost en archivos .conf

    find /etc -type f -name "*.conf" -exec grep -H "localhost" {} \;

    Uso típico

    Buscar configuraciones locales o referencias a servicios en la propia máquina.


    3. Buscar la palabra sudo en scripts .sh

    find /home/$USER -type f -name "*.sh" -exec grep -H "sudo" {} \;

    Uso típico

    Detectar scripts que usan privilegios elevados.


    4. Buscar mysqli en archivos PHP

    find /var/www/html -type f -name "*.php" -exec grep -H "mysqli" {} \;

    Uso típico

    Encontrar dónde se está usando esa librería o función en un proyecto web.


    5. Buscar líneas con import en archivos Python

    find /home/$USER/proyecto -type f -name "*.py" -exec grep -H "import" {} \;

    Uso típico

    Revisar módulos importados dentro de un proyecto Python.


    6. Buscar la palabra TODO en archivos de código

    find /home/$USER/proyecto -type f -name "*.js" -exec grep -H "TODO" {} \;

    También podríamos ampliar a varios tipos de archivo por separado.

    Por ejemplo para Java:

    find /home/$USER/proyecto -type f -name "*.java" -exec grep -H "TODO" {} \;

    Uso típico

    Localizar tareas pendientes dejadas por el programador.


    7. Buscar root dentro de archivos .txt

    find /home/$USER -type f -name "*.txt" -exec grep -H "root" {} \;

    Uso típico

    Revisar documentación, apuntes o ficheros exportados.


    8. Buscar líneas que contengan error ignorando mayúsculas

    find /var/log -type f -name "*.log" -exec grep -Hi "error" {} \;
    • -H muestra el nombre del archivo
    • -i ignora mayúsculas y minúsculas

    Esto encontraría error, Error, ERROR, etc.


    9. Buscar coincidencias con número de línea

    find /etc -type f -name "*.conf" -exec grep -Hn "Listen" {} \;
    • -H muestra el archivo
    • -n muestra el número de línea

    Muy útil para editar luego el archivo correcto.


    10. Buscar palabras exactas dentro de archivos localizados por find

    find /home/$USER -type f -name "*.txt" -exec grep -Hw "linux" {} \;

    La opción -w hace que grep busque la palabra exacta linux, no partes de otras palabras.

    Usar find + grep con borrado o acciones posteriores

    A veces queremos localizar archivos cuyo contenido cumpla una condición y luego actuar sobre ellos. Aquí hay que ir con mucho cuidado, porque el pingüino no perdona los comandos mal puestos.

    Ejemplo: buscar scripts que contienen rm

    find /home/$USER/scripts -type f -name "*.sh" -exec grep -H "rm" {} \;

    Antes de ejecutar un script o revisarlo, puede interesar detectar si contiene órdenes de borrado.


    Alternativa con xargs

    También se puede combinar find con grep usando xargs.

    Ejemplo

    find /etc -type f -name "*.conf" | xargs grep -H "localhost"

    ¿Qué ocurre aquí?

    • find genera una lista de archivos
    • xargs se la pasa a grep

    Ventaja

    Puede resultar más rápido o más cómodo en algunos casos.

    Inconveniente

    Puede dar problemas si los nombres de archivos tienen espacios o caracteres raros.

    Por eso, en documentación básica para alumnos, suele ser más seguro enseñar primero -exec.


    Variante segura con espacios en nombres

    Si más adelante quieres mostrar una versión más robusta:

    find /etc -type f -name "*.conf" -print0 | xargs -0 grep -H "localhost"
    • -print0 separa resultados con carácter nulo
    • xargs -0 los interpreta correctamente

    Esto evita errores con nombres de archivo que contienen espacios.


    Diferencia entre usar solo grep -r y usar find + grep

    Esto conviene explicarlo porque los alumnos pueden pensar:
    “¿Para qué combinar nada si grep ya busca en carpetas?”

    La respuesta es: depende del control que quieras tener.

    Con grep -r

    grep -r "localhost" /etc

    Busca recursivamente en todos los archivos del directorio.

    Con find + grep

    find /etc -type f -name "*.conf" -exec grep -H "localhost" {} \;

    Busca solo en archivos .conf.

    Conclusión

    • grep -r es más rápido de escribir
    • find + grep da un control mucho más preciso sobre qué archivos analizar

    Casos reales donde se usan juntos

    1. Administración de sistemas

    Buscar una directiva concreta en archivos de configuración:

    find /etc -type f -name "*.conf" -exec grep -Hn "ServerName" {} \;

    2. Desarrollo web

    Buscar credenciales hardcodeadas o funciones concretas:

    find /var/www/html -type f -name "*.php" -exec grep -Hn "password" {} \;

    3. Python

    Buscar imports concretos:

    find /home/$USER/proyecto -type f -name "*.py" -exec grep -Hn "requests" {} \;

    4. Shell scripting

    Localizar scripts que usan curl:

    find /home/$USER/scripts -type f -name "*.sh" -exec grep -Hn "curl" {} \;

    5. Revisión de logs

    Buscar palabras clave en logs concretos:

    find /var/log -type f -name "*.log" -exec grep -Hi "failed" {} \;

    Resumen: find + grep

    ObjetivoComando
    Buscar error en .logfind /var/log -type f -name "*.log" -exec grep -H "error" {} \;
    Buscar password en .conffind /etc -type f -name "*.conf" -exec grep -H "password" {} \;
    Buscar sudo en scripts .shfind /home/$USER -type f -name "*.sh" -exec grep -H "sudo" {} \;
    Buscar mysqli en .phpfind /var/www/html -type f -name "*.php" -exec grep -H "mysqli" {} \;
    Buscar import en .pyfind /home/$USER/proyecto -type f -name "*.py" -exec grep -H "import" {} \;
    Buscar error ignorando mayúsculasfind /var/log -type f -name "*.log" -exec grep -Hi "error" {} \;
    Buscar coincidencia con número de líneafind /etc -type f -name "*.conf" -exec grep -Hn "Listen" {} \;
    Buscar palabra exacta linux en .txtfind /home/$USER -type f -name "*.txt" -exec grep -Hw "linux" {} \;

    Recomendaciones

    1. Empieza probando find solo

    Antes de combinar, comprueba qué archivos se están seleccionando.

    find /etc -type f -name "*.conf"

    2. Después añade grep

    Cuando veas que los archivos son los correctos, añades la búsqueda de texto.

    3. Usa -H casi siempre

    Así sabrás en qué archivo aparece cada coincidencia.

    4. Usa -n cuando quieras editar luego

    Te ahorra tiempo porque muestra la línea exacta.

    5. No busques a ciegas en /

    Buscar desde la raíz puede generar muchísimo ruido y errores de permisos.


    Ejercicios propuestos: combinar find y grep

    Ejercicio 1

    Busca la palabra import dentro de todos los archivos .py de una carpeta llamada proyecto.

    Ejercicio 2

    Busca la palabra error dentro de todos los archivos .log de /var/log.

    Ejercicio 3

    Busca la palabra localhost dentro de todos los archivos .conf de /etc.

    Ejercicio 4

    Busca la palabra sudo dentro de todos los scripts .sh de tu carpeta personal.

    Ejercicio 5

    Busca la palabra linux como palabra exacta dentro de todos los archivos .txt de tu carpeta personal.


    Soluciones de los ejercicios

    Ejercicio 1

    find /home/$USER/proyecto -type f -name "*.py" -exec grep -H "import" {} \;

    Ejercicio 2

    find /var/log -type f -name "*.log" -exec grep -H "error" {} \;

    Ejercicio 3

    find /etc -type f -name "*.conf" -exec grep -H "localhost" {} \;

    Ejercicio 4

    find /home/$USER -type f -name "*.sh" -exec grep -H "sudo" {} \;

    Ejercicio 5

    find /home/$USER -type f -name "*.txt" -exec grep -Hw "linux" {} \;
  • 1.8 – Cómo usar echo, >, tee

    1.8 – Cómo usar echo, >, tee

    Este comando:

    echo "Ruta secreta al sector Omega" | sudo tee /srv/andromeda/pilotos/ruta_estelar.txt

    sirve para escribir un texto dentro de un archivo con permisos de administrador.

    Explicación por partes

    1. echo "Ruta secreta al sector Omega"

    El comando echo muestra un texto por pantalla.

    En este caso, genera esta salida:

    Ruta secreta al sector Omega

    2. |

    La barra vertical se llama pipe.

    Sirve para enviar la salida de un comando al siguiente.

    Aquí lo que hace es mandar el texto generado por echo al comando tee.


    3. sudo tee /srv/andromeda/pilotos/ruta_estelar.txt

    El comando tee recoge lo que le llega por la entrada estándar y lo:

    • muestra por pantalla,
    • y además lo guarda en un archivo.

    Como va precedido de sudo, tee se ejecuta con permisos de administrador, por lo que puede escribir en una ruta protegida.

    En este caso, escribe el texto en:

    /srv/andromeda/pilotos/ruta_estelar.txt

    Qué hace en conjunto

    El comando completo:

    1. genera el texto "Ruta secreta al sector Omega",
    2. lo envía a tee,
    3. tee lo escribe dentro del archivo indicado,
    4. y además lo muestra en pantalla.

    Resultado

    El archivo ruta_estelar.txt quedará con este contenido:

    Ruta secreta al sector Omega

    ¿Por qué no usar directamente >?

    Podrías pensar en hacer:

    sudo echo "Ruta secreta al sector Omega" > /srv/andromeda/pilotos/ruta_estelar.txt

    Pero eso no funciona como se espera en muchos casos.

    La razón es que:

    • sudo se aplica a echo,
    • pero la redirección > la intenta hacer el usuario actual,
    • y ese usuario quizá no tiene permisos para escribir en esa carpeta.

    Por eso tee es muy útil: porque quien escribe realmente en el archivo es tee, y como lleva sudo, sí tiene permisos.


    Diferencia entre crear y añadir contenido

    Sobrescribir el archivo

    echo "Texto nuevo" | sudo tee archivo.txt

    Esto reemplaza el contenido anterior.

    Añadir al final del archivo

    echo "Otra línea" | sudo tee -a archivo.txt

    La opción -a significa append, es decir, añadir al final sin borrar lo que ya había.


    Ejemplo

    Crear el archivo con una línea

    echo "Primera línea" | sudo tee /tmp/prueba.txt

    Añadir otra línea

    echo "Segunda línea" | sudo tee -a /tmp/prueba.txt

    Ver el contenido

    cat /tmp/prueba.txt

    Salida:

    Primera línea
    Segunda línea

    Resumen

    ParteFunción
    echo "texto"genera un texto
    ``
    tee archivomuestra el texto y lo guarda en un archivo
    sudo tee archivoguarda el texto en un archivo con permisos de administrador
  • 1 – “Operación Cartografía de Sensores”

    1 – “Operación Cartografía de Sensores”

    Investigación y caracterización técnica de sensores

    Antes de integrar múltiples sensores en un sistema centralizado de adquisición de datos, es necesario comprender el comportamiento individual de cada módulo.

    En esta actividad, cada grupo trabajará con un único sensor del kit basado en Arduino Mega 2560.

    El objetivo no es simplemente “hacerlo funcionar”, sino:

    • Comprender qué magnitud física mide.
    • Analizar cómo genera la señal.
    • Probar su funcionamiento real.
    • Documentar técnicamente su comportamiento.
    • Proponer su integración futura en RaspiAlarm.

    Cada grupo deberá:

    • Conectar el sensor al Arduino.
    • Desarrollar un programa mínimo funcional.
    • Obtener lecturas reales.
    • Documentar todo el proceso en un README técnico.
    • Entregar código y pruebas.

    El resultado final de esta fase será un catálogo técnico de sensores que servirá como base para la arquitectura global del sistema.


    2️⃣ PLANTILLA E INSTRUCCIONES PARA EL README

    Todos los grupos deben usar la MISMA estructura.

    Formato:
    Repositorio Git o carpeta con README.md


    📄 Plantilla obligatoria

    ## Sensor: Nombre
    
    ### 1. Descripción
    ...
    
    ### 2. Especificaciones técnicas
    ...
    
    ### 3. Conexionado
    ...
    
    ### 4. Código de prueba
    ...
    
    ### 5. Pruebas realizadas
    ...
    
    ### 6. Aplicación en RaspiAlarm
    ...

    📌 Normas importantes

    • No copiar documentación sin entenderla.
    • Explicar con palabras propias.
    • Incluir capturas reales.
    • Código comentado línea por línea.
    • Usar formato claro y profesional.

    Evaluación basada en:

    • Rigor técnico
    • Claridad
    • Funcionamiento real
    • Capacidad de integración

    📘 3️⃣ LISTA DE SENSORES DEL KIT (Asignación por grupo)

    Utilizando exclusivamente material típico de un kit compatible con Arduino Mega 2560.

    Lista propuesta:

    Grupo 1 – Sensor DHT11 (Temperatura y Humedad)
    Grupo 2 – HC-SR04 (Ultrasonidos)
    Grupo 3 – Sensor PIR (Movimiento)
    Grupo 4 – Sensor de llama
    Grupo 5 – Sensor de sonido (KY-038)
    Grupo 6 – Sensor LDR (Luz)
    Grupo 7 – Sensor de humedad del suelo
    Grupo 8 – Sensor de inclinación (Tilt Switch)

    Cada grupo solo puede trabajar con su sensor asignado.


    📘 4️⃣ Almacenamiento de datos

    Arduino Mega 2560 envíe datos por USB y que Windows los guarde automáticamente en el PC.

    Nada de herramientas raras. Solo:

    • Windows
    • IDE de Arduino
    • Cable USB
    • Python (para capturar y guardar datos)

    Arquitectura mental:

    Sensor → Arduino → USB (Serial) → PC → Archivo CSV

    Eso es adquisición de datos básica. Ciencia aplicada.

  • 2 – Lectura y almacenamiento de datos de sensores en Windows

    2 – Lectura y almacenamiento de datos de sensores en Windows

    PARTE 1 – Preparar el Arduino para enviar datos correctamente

    1.1 Conectar el Arduino

    1. Conectar el cable USB tipo A-B.
    2. Abrir Arduino IDE.
    3. Ir a:

    Herramientas → Placa → Arduino Mega 2560
    Herramientas → Puerto → Seleccionar el puerto COM correcto.

    Si no sabes cuál es:

    • Desconecta el cable.
    • Mira qué puerto desaparece.
    • Vuelve a conectar.
    • Ese es el correcto.

    1.2 Código base para enviar datos estructurados

    Es FUNDAMENTAL que los datos tengan formato fijo.

    Ejemplo usando un sensor analógico en A0:

    void setup() {
      Serial.begin(9600);
    }void loop() {
      int valor = analogRead(A0);  
    Serial.print("S1;ANALOGICO;");
      Serial.println(valor);  
      delay(2000);
    }

    Esto genera una línea clara:

    S1;ANALOGICO;523

    Siempre el mismo patrón. Sin texto decorativo.

    Subir el programa.

    ⚠️ Muy importante:
    NO abrir el Monitor Serie después. El puerto solo puede usarse por un programa a la vez.


    PARTE 2 – Preparar Windows para capturar datos

    Windows no guarda automáticamente lo que sale por USB. Necesitamos un programa que lea el puerto serie y lo escriba en un archivo.

    La solución más limpia: Python.


    PARTE 3 – Instalar Python en Windows

    3.1 Descargar

    Ir a:
    https://www.python.org/downloads/

    Descargar versión para Windows.

    3.2 Instalación correcta

    Durante la instalación:

    ✔ Marcar “Add Python to PATH”
    ✔ Install Now

    Finalizar instalación.


    PARTE 4 – Instalar librería para comunicación serie

    Abrir:

    Inicio → escribir “cmd” → Enter

    Ejecutar:

    pip install pyserial

    Si aparece “Successfully installed”, está correcto.


    PARTE 5 – Crear el programa que guarda datos

    5.1 Crear archivo

    En el escritorio:

    Crear archivo llamado:

    guardar_datos.py

    Abrir con Bloc de notas y pegar:

    import serial
    import csv
    from datetime import datetime
    
    # CAMBIAR ESTE PUERTO POR EL CORRECTO
    PUERTO = "COM3"
    BAUDIOS = 9600
    
    try:
        ser = serial.Serial(PUERTO, BAUDIOS, timeout=1)
    except:
        print("No se puede abrir el puerto. Verifica el COM.")
        exit()
    
    archivo = open("datos_sensores.csv", mode="a", newline="")
    writer = csv.writer(archivo, delimiter=";")
    
    print("Capturando datos... Presiona Ctrl+C para detener.")
    
    try:
        while True:
            linea = ser.readline().decode("utf-8").strip()
    
            if linea:
                timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                datos = linea.split(";")
    
                fila = datos + [timestamp]
                writer.writerow(fila)
                archivo.flush()
    
                print(fila)
    
    except KeyboardInterrupt:
        print("Finalizando captura...")
        archivo.close()
        ser.close()

    Guardar archivo.


    PARTE 6 – Ejecutar captura de datos

    1. Cerrar el Monitor Serie del IDE.
    2. Abrir CMD.
    3. Ir al escritorio:
    cd Desktop
    1. Ejecutar:
    python guardar_datos.py

    Si todo funciona, verás:

    [‘S1’, ‘ANALOGICO’, ‘523’, ‘2026-03-02 21:30:10’]

    Y se creará automáticamente:

    datos_sensores.csv


    PARTE 7 – Verificar archivo generado

    Abrir el archivo con Excel.

    Formato típico:

    S1;ANALOGICO;523;2026-03-02 21:30:10
    S1;ANALOGICO;517;2026-03-02 21:30:12

    Ya tienes registro histórico con timestamp.

    Eso significa:

    • Persistencia de datos
    • Trazabilidad
    • Capacidad de análisis

    PARTE 8 – Problemas comunes y solución

    Error: “Access denied”
    → El Monitor Serie está abierto.

    Error: “No se puede abrir el puerto”
    → Puerto incorrecto.

    Símbolos extraños
    → Baudrate incorrecto (debe coincidir con Serial.begin).

    No se guardan datos
    → El Arduino no está enviando líneas con println.


  • 3 – Instalación de Raspberry Pi OS y conexión por WiFi (en remoto)

    3 – Instalación de Raspberry Pi OS y conexión por WiFi (en remoto)

    En esta práctica aprenderás a:

    • Preparar una tarjeta microSD con Raspberry Pi OS.
    • Configurar la Raspberry Pi sin monitor ni teclado (modo headless).
    • Conectar la Raspberry Pi a una red WiFi.
    • Acceder a ella desde tu portátil mediante SSH.

    La Raspberry Pi funcionará como un pequeño servidor Linux conectado por WiFi.


    1. Material necesario

    Cada grupo necesitará:

    • 1 Raspberry Pi
    • 1 tarjeta microSD (16GB o más)
    • 1 lector de tarjetas microSD
    • 1 portátil
    • 1 router WiFi del aula
    • alimentación para la Raspberry Pi

    No se utilizarán:

    • monitor
    • teclado
    • ratón

    Toda la administración se hará desde el portátil mediante SSH.


    2. Arquitectura de red de la práctica

    La red del aula tendrá esta estructura:

               Router WiFi Aula
    192.168.1.1
    ~~~~~~~~~~~~~~~~~
    )) ((
    )) ((
    )) ((
    ┌──────────────┐ ┌──────────────┐
    │ Portátil │ │ Raspberry Pi │
    │ del alumno │ │ │
    │ │ │ Servidor │
    │ SSH Client │──▶│ SSH Server │
    └──────────────┘ └──────────────┘

    La Raspberry Pi se conectará al router por WiFi y el alumno accederá mediante SSH.


    3. Descarga del sistema operativo

    El sistema operativo oficial es:

    Raspberry Pi OS

    Descargar la herramienta:

    Raspberry Pi Imager

    desde:

    https://www.raspberrypi.com/software

    Disponible para:

    • Windows
    • Linux
    • macOS

    4. Instalación de Raspberry Pi Imager

    En Windows:

    1. Descargar el instalador.
    2. Ejecutar el archivo .exe.
    3. Instalar el programa.
    4. Abrir Raspberry Pi Imager.

    5. Insertar la tarjeta microSD

    1. Introducir la tarjeta en el lector.
    2. Conectarla al ordenador.
    3. Abrir Raspberry Pi Imager.

    Verás tres botones principales:

    Choose OS
    Choose Storage
    Write

    6. Seleccionar el sistema operativo

    Pulsar:

    Choose OS

    Seleccionar:

    Raspberry Pi OS Lite (64-bit)

    Elegimos esta versión porque:

    • no tiene entorno gráfico
    • consume menos recursos
    • es ideal para servidores
    • todo se controla por terminal

    7. Seleccionar la tarjeta microSD

    Pulsar:

    Choose Storage

    Elegir la tarjeta microSD conectada al ordenador.


    8. Configuración avanzada (muy importante)

    Antes de escribir la tarjeta, pulsar el icono del engranaje ⚙.

    Aquí configuraremos la Raspberry antes de arrancarla por primera vez.


    Nombre de la máquina

    Hostname:

    raspberry01 (deben ser diferentes en casa maquina)

    Activar SSH

    Activar:

    Enable SSH

    Elegir:

    Use password authentication

    Usuario y contraseña

    Crear usuario:

    usuario: alumno
    contraseña: alumno123

    Configuración WiFi

    Activar WiFi e introducir:

    SSID: nombre_wifi
    Password: passwordwifi

    Configurar también:

    Country: ES

    Esto es importante para que la Raspberry pueda usar correctamente el WiFi.


    Configuración regional

    Configurar:

    Time zone: Europe/Madrid
    Keyboard layout: es

    9. Escribir el sistema en la tarjeta

    Pulsar:

    Write

    El programa:

    1. descargará el sistema
    2. lo copiará a la tarjeta
    3. verificará los datos

    Este proceso puede tardar varios minutos.


    10. Primer arranque de la Raspberry Pi

    Cuando termine:

    1. sacar la tarjeta microSD
    2. insertarla en la Raspberry Pi
    3. conectar la alimentación

    La Raspberry Pi arrancará automáticamente.

    Durante el arranque:

    • se conectará al WiFi
    • arrancará el servidor SSH
    • quedará disponible en la red

    El proceso tarda aproximadamente 1 minuto.


    11. Descubrir la IP de la Raspberry Pi

    Necesitamos saber qué IP le ha asignado el router.

    Hay varias formas.



    Escaneo de red

    En Linux o macOS:

    arp -a

    o

    nmap -sn 192.168.1.0/24

    12. Conectarse por SSH

    Abrir una terminal en el portátil.

    Ejecutar:

    ssh alumno@192.168.1.37

    (sustituir la IP por la real).

    La primera vez aparecerá un aviso de seguridad:

    The authenticity of host can't be established

    Escribir:

    yes

    Después introducir la contraseña:

    alumno123

    Si usas Windows

    Puedes usar:

    Terminal de Windows

    ssh alumno@192.168.1.37

    o el programa:

    PuTTY


    13. Primera sesión en la Raspberry Pi

    Si todo funciona verás algo como:

    alumno@raspberry01:~ $

    Ya estás dentro de la Raspberry Pi.

    Estás controlando un ordenador remoto desde tu portátil.


    14. Comandos básicos de prueba

    Información del sistema:

    uname -a

    Versión del sistema:

    cat /etc/os-release

    Ver la IP:

    ip a

    Actualizar el sistema:

    sudo apt update
    sudo apt upgrade

  • 4 – Práctica guiada: varios Arduinos con sensores conectados a una Raspberry Pi y almacenamiento en CSV o JSON

    4 – Práctica guiada: varios Arduinos con sensores conectados a una Raspberry Pi y almacenamiento en CSV o JSON

    1. Introducción

    En esta práctica vais a construir un pequeño sistema de adquisición de datos distribuido. En lugar de trabajar con un único sensor conectado a un solo dispositivo, vais a montar una arquitectura formada por varios Arduinos, cada uno con uno o varios sensores, y una Raspberry Pi que actuará como centro de recepción y almacenamiento.

    El objetivo no es solo «leer sensores», sino comprender cómo se organiza un sistema real donde varios nodos capturan información y un equipo central la recibe, la procesa y la guarda para su análisis posterior.

    Este tipo de arquitectura se parece mucho a sistemas reales de monitorización, automatización, IoT, domótica o control industrial.


    2. Objetivos de la práctica

    Al finalizar la práctica, debéis ser capaces de:

    • Conectar sensores a varios Arduinos.
    • Leer datos de sensores desde Arduino.
    • Enviar esos datos por comunicación serie a través de USB.
    • Conectar varios Arduinos a una Raspberry Pi.
    • Identificar los puertos serie de cada Arduino en Linux.
    • Crear en la Raspberry Pi un programa en Python que reciba datos desde varios Arduinos.
    • Guardar los datos recibidos en archivos CSV o JSON.
    • Documentar correctamente el montaje, el código y el funcionamiento del sistema.

    3. Descripción general del sistema

    La arquitectura general de la práctica será la siguiente:

    [Sensores] → [Arduino 1] ┐
    [Sensores] → [Arduino 2] ├── USB ──> [Raspberry Pi] ──> [CSV / JSON]
    [Sensores] → [Arduino 3] ┘

    Cada Arduino actuará como nodo de captura de datos.

    La Raspberry Pi actuará como:

    • receptor central,
    • sistema de almacenamiento,
    • y punto de análisis de los datos.

    4. Material necesario

    Cada grupo deberá disponer, como mínimo, de:

    • 1 Raspberry Pi con Raspberry Pi OS.
    • 2 o más placas Arduino.
    • Sensores asignados por el profesor.
    • Cables USB para conectar los Arduinos a la Raspberry Pi.
    • Protoboard y cables Dupont.
    • Resistencias si el sensor lo necesita.
    • Tarjeta microSD con sistema operativo funcional en la Raspberry Pi.
    • Acceso al terminal de la Raspberry Pi.

    Material opcional recomendable

    • Hub USB alimentado, si se van a usar muchos Arduinos.
    • Teclado, ratón y monitor para la Raspberry Pi, o acceso por SSH.

    5. Requisitos mínimos del proyecto

    El sistema desarrollado debe cumplir, al menos, con los siguientes requisitos:

    1. Usar varios Arduinos conectados a la Raspberry Pi.
    2. Cada Arduino debe leer al menos un sensor.
    3. Cada Arduino debe enviar sus datos por puerto serie.
    4. La Raspberry Pi debe recibir datos de todos los Arduinos.
    5. Los datos deben guardarse en un archivo CSV o JSON.
    6. Cada dato debe quedar identificado con:
      • fecha y hora,
      • identificador del Arduino,
      • nombre del sensor,
      • valor recibido.
    7. El grupo debe entregar documentación técnica y capturas.


    FASE 1. Diseño del sistema

    6.1. Planificación previa

    Antes de montar nada, debéis diseñar vuestro sistema.

    Debéis definir:

    • Cuántos Arduinos vais a usar.
    • Qué sensor o sensores irá conectado a cada Arduino.
    • Qué tipo de dato enviará cada uno.
    • Qué formato de mensaje vais a usar.
    • Si guardaréis los datos en CSV, JSON o ambos.

    Ejemplo de reparto

    • Arduino 1: temperatura y humedad
    • Arduino 2: luz
    • Arduino 3: movimiento

    6.2. Formato común de los datos

    Todos los grupos debéis usar un formato de texto común para enviar los datos desde Arduino a la Raspberry Pi.

    Formato recomendado

    arduino=A1;sensor=temp;valor=23.4
    arduino=A1;sensor=hum;valor=41.2
    arduino=A2;sensor=luz;valor=820
    arduino=A3;sensor=mov;valor=1

    Reglas del formato

    • Cada línea representa una lectura.
    • Cada dato estará compuesto por pares clave=valor.
    • Los pares estarán separados por ;.
    • Cada línea terminará con salto de línea.

    Este formato es muy sencillo de leer y muy fácil de procesar en Python.


    FASE 2. Montaje del hardware

    7. Conexión de sensores a los Arduinos

    Cada grupo conectará los sensores asignados a sus placas Arduino.

    Recomendaciones

    • Revisad siempre el voltaje de trabajo del sensor.
    • Comprobad si necesita resistencia pull-up o pull-down.
    • Verificad qué pin usaréis: digital o analógico.
    • Etiquetad en vuestra documentación qué sensor está conectado a qué pin.

    Ejemplo de tabla de montaje

    ArduinoSensorTipo de pinPin usado
    A1Sensor de temperaturaAnalógicoA0
    A1Sensor de humedadAnalógicoA1
    A2LDRAnalógicoA0
    A3PIRDigitalD2

    8. Conexión de los Arduinos a la Raspberry Pi

    Una vez que cada Arduino funcione correctamente con su sensor, debéis conectarlos a la Raspberry Pi mediante USB.

    Importante

    • Cada Arduino se conectará con su propio cable USB.
    • Si no hay suficientes puertos USB en la Raspberry, usad un hub USB.
    • Si usáis varios Arduinos, es recomendable que el hub sea alimentado externamente.

    FASE 3. Programación de cada Arduino

    9. Objetivo del código Arduino

    Cada Arduino debe:

    1. Inicializar su comunicación serie.
    2. Leer el valor del sensor.
    3. Construir un mensaje con el formato definido.
    4. Enviar ese mensaje por el puerto serie.
    5. Repetir el proceso cada cierto tiempo.

    10. Estructura general del programa Arduino

    Ejemplo genérico

    const String ID_ARDUINO = "A1";
    
    void setup() {
      Serial.begin(9600);
    }
    
    void loop() {
      int valorSensor = analogRead(A0);
    
      Serial.print("arduino=");
      Serial.print(ID_ARDUINO);
      Serial.print(";sensor=luz;valor=");
      Serial.println(valorSensor);
    
      delay(2000);
    }

    11. Recomendaciones para el código Arduino

    • No enviéis datos demasiado rápido.
    • Para empezar, una lectura cada 1 o 2 segundos es suficiente.
    • No mezcléis mensajes de depuración con mensajes de datos.
    • Usad siempre el mismo formato.

    Mal ejemplo

    Serial.println("Leyendo sensor...");
    Serial.println(valor);

    Buen ejemplo

    Serial.println("arduino=A1;sensor=luz;valor=830");

    FASE 4. Comprobación de los Arduinos en la Raspberry Pi

    12. Detectar los puertos serie

    Cuando conectéis los Arduinos a la Raspberry, debéis comprobar qué puertos se han creado.

    Comandos útiles

    ls /dev/ttyACM*
    ls /dev/ttyUSB*

    También podéis usar:

    dmesg | tail

    Posible resultado

    /dev/ttyACM0
    /dev/ttyACM1
    /dev/ttyACM2

    Cada uno suele corresponder a un Arduino distinto.


    13. Identificación más estable de los dispositivos

    En algunos casos, el nombre /dev/ttyACM0 puede cambiar al reiniciar o reconectar.

    Para identificar los puertos de forma más estable, podéis usar:

    ls /dev/serial/by-id/

    Esto mostrará nombres más largos, pero normalmente más estables.


    14. Prueba manual del puerto serie

    Antes de escribir el programa final en Python, comprobad que la Raspberry recibe datos.

    Podéis usar herramientas como:

    cat /dev/ttyACM0

    O instalar herramientas como screen o minicom.


    FASE 5. Preparación del entorno Python en Raspberry Pi

    15. Instalar Python y pyserial

    Normalmente Python ya estará instalado. Debéis instalar la librería pyserial.

    sudo apt update
    sudo apt install python3-pip -y
    pip3 install pyserial

    Si el sistema os da problemas con permisos, podéis usar entorno virtual o instalar el paquete del sistema si está disponible.


    FASE 6. Programa receptor en Raspberry Pi

    16. Objetivo del programa Python

    El script en Python debe:

    • abrir varios puertos serie,
    • escuchar los datos de varios Arduinos,
    • interpretar cada línea recibida,
    • añadir fecha y hora,
    • guardar los datos en CSV o JSON.

    17. Formato final recomendado de almacenamiento

    Opción CSV

    timestamp,arduino,sensor,valor
    2026-03-10 10:15:00,A1,temp,23.4
    2026-03-10 10:15:01,A1,hum,41.2
    2026-03-10 10:15:03,A2,luz,820

    Opción JSON (un registro por línea)

    {"timestamp":"2026-03-10 10:15:00","arduino":"A1","sensor":"temp","valor":"23.4"}
    {"timestamp":"2026-03-10 10:15:01","arduino":"A1","sensor":"hum","valor":"41.2"}
    {"timestamp":"2026-03-10 10:15:03","arduino":"A2","sensor":"luz","valor":"820"}

    18. Script Python de ejemplo para varios Arduinos y CSV

    import serial
    import threading
    import csv
    import os
    from datetime import datetime
    
    PUERTOS = [
        "/dev/ttyACM0",
        "/dev/ttyACM1",
        "/dev/ttyACM2"
    ]
    
    BAUDIOS = 9600
    ARCHIVO_CSV = "datos_sensores.csv"
    CAMPOS = ["timestamp", "arduino", "sensor", "valor"]
    lock_csv = threading.Lock()
    
    
    def parsear_linea(linea):
        datos = {}
        partes = linea.strip().split(";")
        for parte in partes:
            if "=" in parte:
                clave, valor = parte.split("=", 1)
                datos[clave.strip()] = valor.strip()
        return datos
    
    
    def inicializar_csv():
        if not os.path.exists(ARCHIVO_CSV) or os.path.getsize(ARCHIVO_CSV) == 0:
            with open(ARCHIVO_CSV, "w", newline="", encoding="utf-8") as f:
                writer = csv.DictWriter(f, fieldnames=CAMPOS)
                writer.writeheader()
    
    
    def guardar_csv(registro):
        with lock_csv:
            with open(ARCHIVO_CSV, "a", newline="", encoding="utf-8") as f:
                writer = csv.DictWriter(f, fieldnames=CAMPOS)
                writer.writerow(registro)
    
    
    def escuchar_puerto(puerto):
        try:
            ser = serial.Serial(puerto, BAUDIOS, timeout=1)
            print(f"Escuchando {puerto}")
    
            while True:
                linea = ser.readline().decode("utf-8", errors="ignore").strip()
    
                if linea:
                    print(f"{puerto} -> {linea}")
                    datos = parsear_linea(linea)
    
                    if "arduino" in datos and "sensor" in datos and "valor" in datos:
                        registro = {
                            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                            "arduino": datos["arduino"],
                            "sensor": datos["sensor"],
                            "valor": datos["valor"]
                        }
                        guardar_csv(registro)
        except Exception as e:
            print(f"Error en {puerto}: {e}")
    
    
    def main():
        inicializar_csv()
        hilos = []
    
        for puerto in PUERTOS:
            hilo = threading.Thread(target=escuchar_puerto, args=(puerto,), daemon=True)
            hilo.start()
            hilos.append(hilo)
    
        for hilo in hilos:
            hilo.join()
    
    
    if __name__ == "__main__":
        main()

    19. Script Python de ejemplo para guardar en JSON

    import serial
    import threading
    import json
    from datetime import datetime
    
    PUERTOS = [
        "/dev/ttyACM0",
        "/dev/ttyACM1"
    ]
    
    BAUDIOS = 9600
    ARCHIVO_JSON = "datos_sensores.jsonl"
    lock_json = threading.Lock()
    
    
    def parsear_linea(linea):
        datos = {}
        partes = linea.strip().split(";")
        for parte in partes:
            if "=" in parte:
                clave, valor = parte.split("=", 1)
                datos[clave.strip()] = valor.strip()
        return datos
    
    
    def guardar_json(registro):
        with lock_json:
            with open(ARCHIVO_JSON, "a", encoding="utf-8") as f:
                f.write(json.dumps(registro, ensure_ascii=False) + "\n")
    
    
    def escuchar_puerto(puerto):
        try:
            ser = serial.Serial(puerto, BAUDIOS, timeout=1)
            print(f"Escuchando {puerto}")
    
            while True:
                linea = ser.readline().decode("utf-8", errors="ignore").strip()
    
                if linea:
                    print(f"{puerto} -> {linea}")
                    datos = parsear_linea(linea)
    
                    if "arduino" in datos and "sensor" in datos and "valor" in datos:
                        registro = {
                            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                            "arduino": datos["arduino"],
                            "sensor": datos["sensor"],
                            "valor": datos["valor"]
                        }
                        guardar_json(registro)
        except Exception as e:
            print(f"Error en {puerto}: {e}")
    
    
    def main():
        hilos = []
    
        for puerto in PUERTOS:
            hilo = threading.Thread(target=escuchar_puerto, args=(puerto,), daemon=True)
            hilo.start()
            hilos.append(hilo)
    
        for hilo in hilos:
            hilo.join()
    
    
    if __name__ == "__main__":
        main()

    FASE 7. Ejecución del programa

    20. Guardar y ejecutar el script

    Guardad el script con un nombre como:

    nano receptor_sensores.py

    Pegad el código, guardadlo y ejecutadlo:

    python3 receptor_sensores.py

    Si todo funciona, deberíais ver en pantalla líneas como estas:

    /dev/ttyACM0 -> arduino=A1;sensor=temp;valor=23.7
    /dev/ttyACM1 -> arduino=A2;sensor=luz;valor=801
    /dev/ttyACM2 -> arduino=A3;sensor=mov;valor=1

    21. Comprobar los archivos generados

    Para CSV

    cat datos_sensores.csv

    Para JSON

    cat datos_sensores.jsonl

    FASE 8. Validación del sistema

    22. Qué debéis comprobar

    Debéis verificar que:

    • todos los Arduinos envían datos,
    • la Raspberry recibe los mensajes,
    • los datos se guardan correctamente,
    • cada línea incluye el identificador del Arduino,
    • los archivos se actualizan conforme llegan nuevas lecturas.

    23. Pruebas recomendadas

    Realizad pruebas como estas:

    1. Desconectar un Arduino y comprobar qué ocurre.
    2. Volver a conectarlo.
    3. Modificar el valor del sensor (por ejemplo, tapar una LDR o mover un PIR).
    4. Verificar que el cambio se refleja en el CSV o JSON.
    5. Comprobar si el sistema sigue funcionando con varios Arduinos enviando datos al mismo tiempo.

    FASE 9. Posibles errores y soluciones

    24. Error: no aparece /dev/ttyACM0

    Posibles causas

    • El cable USB no funciona.
    • El Arduino no está correctamente conectado.
    • El sistema no ha detectado la placa.

    Soluciones

    • Probar otro cable.
    • Ejecutar dmesg | tail.
    • Probar con otro puerto USB.

    25. Error: permiso denegado al abrir el puerto serie

    Solución posible

    Añadir el usuario al grupo adecuado:

    sudo usermod -a -G dialout $USER

    Después debéis cerrar sesión y volver a entrar.


    26. Error: el script recibe líneas vacías o mal formadas

    Posibles causas

    • El Arduino está enviando texto extra.
    • La velocidad en baudios no coincide.
    • El formato del mensaje no es correcto.

    Soluciones

    • Revisar Serial.begin(9600) en Arduino.
    • Comprobar que Python usa la misma velocidad.
    • Revisar el formato clave=valor;clave=valor.

    Entrega del trabajo

    27. Qué debéis entregar

    Cada grupo debe entregar:

    1. Código de todos los Arduinos.
    2. Código Python de la Raspberry Pi.
    3. Archivo README o memoria técnica.
    4. Esquema del sistema.
    5. Fotografías del montaje.
    6. Capturas del terminal mostrando la recepción de datos.
    7. Ejemplo del CSV o JSON generado.
    8. Explicación de problemas encontrados y cómo los resolvisteis.

    28. Estructura recomendada de carpetas

    practica-raspberry-arduinos/
    ├── README.md
    ├── arduino/
    │   ├── arduino_1/
    │   │   └── arduino_1.ino
    │   ├── arduino_2/
    │   │   └── arduino_2.ino
    │   └── arduino_3/
    │       └── arduino_3.ino
    ├── raspberry/
    │   ├── receptor_csv.py
    │   └── receptor_json.py
    ├── evidencias/
    │   ├── foto_montaje_1.jpg
    │   ├── terminal_recepcion.png
    │   └── csv_generado.png
    └── datos_ejemplo/
        ├── datos_sensores.csv
        └── datos_sensores.jsonl




    Conclusión

    Esta práctica os permitirá trabajar con una arquitectura completa de adquisición de datos usando varios microcontroladores y un sistema Linux central.

    No se trata solo de conectar cables y leer números, sino de entender cómo diseñar un sistema modular, organizado y ampliable. Ese enfoque es mucho más valioso que hacer una sola lectura aislada en un monitor serie.

    Cuando el sistema funcione, habréis construido una pequeña red de sensores con almacenamiento centralizado, algo muy cercano a muchos proyectos reales de automatización, IoT y monitorización.




  • 5 – Lectura de sensores con Python y almacenamiento automático en MariaDB

    5 – Lectura de sensores con Python y almacenamiento automático en MariaDB

    En esta práctica vas a convertir tu Raspberry Pi en un pequeño nodo autónomo de captura de datos dentro del proyecto RaspyAlarma.

    Tu Raspberry deberá ser capaz de:

    1. leer los datos de un sensor con Python,
    2. guardar esas lecturas en una base de datos MariaDB,
    3. ejecutar ese proceso automáticamente cada cierto tiempo mediante cron,
    4. y dejar preparada la información para una futura sincronización con un servidor central.

    Todos los alumnos deberán usar la misma estructura de base de datos, ya que más adelante será necesario unificar los datos de todos los sensores y de todas las Raspberry del proyecto.


    Objetivo general

    El objetivo de esta práctica es crear un sistema automático en el que una Raspberry:

    • lea un sensor,
    • almacene la lectura en una base de datos local,
    • identifique de qué Raspberry procede el dato,
    • indique si la lectura representa o no una situación de alerta,
    • y marque si ese dato ya fue enviado o no al servidor central.

    Estructura obligatoria de la base de datos

    Todos los alumnos deberán usar una base de datos llamada exactamente:

    raspialarm

    Y una tabla llamada exactamente:

    lecturas_sensores

    Estructura obligatoria de la tabla

    La tabla deberá contener estos campos:

    • id
    • raspberry_id
    • nombresensor
    • lectura1
    • lectura2
    • lectura3
    • fecha_hora
    • alumnoEncargado
    • descripcionSensor
    • estado_alerta
    • enviado_central

    Significado de los campos

    raspberry_id

    Este campo servirá para identificar qué Raspberry ha generado la lectura.

    Ejemplos válidos:

    • RPI01
    • RPI_AULA_01
    • RPI_PASILLO
    • RPI_HABITACION_3

    Este identificador debe ser fijo para cada Raspberry y no debe cambiar entre ejecuciones.


    estado_alerta

    Este campo servirá para indicar el estado de la lectura.

    Valores recomendados:

    • normal
    • aviso
    • critico

    No todos los sensores tendrán alertas complejas al principio, pero el campo debe existir ya desde esta fase.

    Ejemplo:

    • una lectura dentro de lo normal → normal
    • una lectura elevada pero no crítica → aviso
    • una lectura peligrosa o fuera de umbral → critico

    enviado_central

    Este campo servirá para saber si esa lectura ya ha sido enviada al servidor central del proyecto.

    Valores recomendados:

    • 0 → todavía no se ha enviado
    • 1 → ya se ha enviado

    En esta práctica, normalmente todos los registros nuevos se guardarán inicialmente con valor 0.


    Parte 1. Crear la base de datos

    Paso 1. Comprobar que MariaDB está activa

    Abre una terminal y ejecuta:

    sudo systemctl status mariadb

    Si no estuviera arrancada:

    sudo systemctl start mariadb

    Y para dejarla activada al iniciar el sistema:

    sudo systemctl enable mariadb

    Paso 2. Entrar en MariaDB

    sudo mariadb

    Paso 3. Crear la base de datos

    Dentro de MariaDB:

    CREATE DATABASE raspialarm;
    SHOW DATABASES;
    USE raspialarm;

    Parte 2. Crear la tabla común del proyecto

    Ejecuta esta instrucción SQL exactamente como aparece:

    CREATE TABLE lecturas_sensores (
    id INT AUTO_INCREMENT PRIMARY KEY,
    raspberry_id VARCHAR(50) NOT NULL,
    nombresensor VARCHAR(100) NOT NULL,
    lectura1 DECIMAL(10,2),
    lectura2 DECIMAL(10,2),
    lectura3 DECIMAL(10,2),
    fecha_hora DATETIME NOT NULL,
    alumnoEncargado VARCHAR(100) NOT NULL,
    descripcionSensor VARCHAR(255),
    estado_alerta VARCHAR(20) NOT NULL DEFAULT 'normal',
    enviado_central TINYINT(1) NOT NULL DEFAULT 0
    );

    Paso 4. Comprobar la estructura de la tabla

    DESCRIBE lecturas_sensores;

    También puedes verla completa con:

    SHOW CREATE TABLE lecturas_sensores;

    Parte 3. Crear un usuario específico para la aplicación

    No debes usar root dentro del script Python.

    Ejecuta:

    CREATE USER 'raspiuser'@'localhost' IDENTIFIED BY 'raspi1234';
    GRANT ALL PRIVILEGES ON raspialarm.* TO 'raspiuser'@'localhost';
    FLUSH PRIVILEGES;
    EXIT;

    Parte 4. Crear la estructura de carpetas

    Ejecuta:

    mkdir -p /home/pi/raspialarma/sensores
    mkdir -p /home/pi/raspialarma/logs
    mkdir -p /home/pi/raspialarma/docs

    Comprueba el resultado:

    ls -R /home/pi/raspialarma

    Parte 5. Instalar el conector de Python para MariaDB

    Ejecuta:

    sudo apt update
    sudo apt install python3-pip python3-mysqldb -y

    Parte 6. Crear primero un script de prueba con datos fijos

    Antes de leer el sensor real, primero debes comprobar que:

    • Python puede conectarse a MariaDB,
    • el INSERT funciona,
    • y las nuevas columnas también se guardan correctamente.

    Edita el archivo:

    nano /home/pi/raspialarma/sensores/captura_sensor.py

    Pega este contenido:

    #!/usr/bin/env python3
    
    import MySQLdb
    from datetime import datetime
    
    def escribir_log(mensaje):
        with open("/home/pi/raspialarma/logs/raspialarma.log", "a") as log:
            log.write(f"{datetime.now()} - {mensaje}\n")
    
    try:
        conexion = MySQLdb.connect(
            host="localhost",
            user="raspiuser",
            passwd="raspi1234",
            db="raspialarm"
        )
    
        cursor = conexion.cursor()
    
        raspberry_id = "RPI01"
        nombresensor = "SENSOR_PRUEBA"
        lectura1 = 25.50
        lectura2 = 60.00
        lectura3 = 0.00
        fecha_hora = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        alumnoEncargado = "TU_NOMBRE"
        descripcionSensor = "Prueba inicial de inserción en MariaDB"
        estado_alerta = "normal"
        enviado_central = 0
    
        sql = """
            INSERT INTO lecturas_sensores
            (raspberry_id, nombresensor, lectura1, lectura2, lectura3, fecha_hora,
             alumnoEncargado, descripcionSensor, estado_alerta, enviado_central)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
        """
    
        valores = (
            raspberry_id,
            nombresensor,
            lectura1,
            lectura2,
            lectura3,
            fecha_hora,
            alumnoEncargado,
            descripcionSensor,
            estado_alerta,
            enviado_central
        )
    
        cursor.execute(sql, valores)
        conexion.commit()
    
        mensaje = "Lectura guardada correctamente."
        print(mensaje)
        escribir_log(mensaje)
    
    except Exception as e:
        error = f"Error: {e}"
        print(error)
        escribir_log(error)
    
    finally:
        try:
            cursor.close()
            conexion.close()
        except:
            pass

    El INSERT debe hacerse desde Python

    En esta práctica no se insertarán datos desde PHP ni desde formularios web.
    La inserción de datos se realizará directamente desde un script Python.

    El script debe ejecutarse automáticamente

    El script Python deberá ejecutarse cada cierto tiempo utilizando cron.

    Debes documentar lo que significa cada lectura

    Cada sensor es diferente. Por tanto, deberás dejar claro en tu documentación qué representa:

    • lectura1
    • lectura2
    • lectura3

    Por ejemplo, en un DHT11 podrían significar:

    • lectura1 = temperatura
    • lectura2 = humedad
    • lectura3 = no usada

    Posible estructura de significado de las lecturas

    Para que todos trabajen de forma homogénea, puedes pedirles una tabla como esta en su documentación:

    Tipo de sensorlectura1lectura2lectura3
    DHT11 / DHT22TemperaturaHumedadNo usada
    HC-SR04DistanciaNo usadaNo usada
    PIRMovimiento detectado (0/1)No usadaNo usada
    MQ-2Nivel de gas/humoUmbralNo usada
    LDRIntensidad de luzNo usadaNo usada

    Paso 2. Personalizar el script

    Debes modificar al menos estos valores:

    raspberry_id = "RPI01"
    alumnoEncargado = "TU_NOMBRE"

    Sustitúyelos por valores reales.

    Ejemplo:

    raspberry_id = "RPI_AULA_03"
    alumnoEncargado = "Antonio"

    Paso 3. Dar permisos de ejecución

    chmod +x /home/pi/raspialarma/sensores/captura_sensor.py

    Paso 4. Ejecutar el script manualmente

    python3 /home/pi/raspialarma/sensores/captura_sensor.py

    Si todo ha ido bien, verás:

    Lectura guardada correctamente.

    Parte 7. Comprobar que el INSERT ha funcionado

    Entra en MariaDB:

    mariadb -u raspiuser -p

    Contraseña:

    raspi1234

    Dentro de MariaDB:

    USE raspialarm;
    SELECT * FROM lecturas_sensores;

    Comprueba que aparecen correctamente:

    • raspberry_id
    • estado_alerta
    • enviado_central

    Parte 8. Sustituir los datos fijos por la lectura real del sensor

    Cuando el script de prueba funcione, sustituye los valores fijos por los datos reales del sensor.

    La tabla seguirá siendo la misma.
    Solo cambiarán los valores que insertas.


    Parte 9. Cómo decidir el estado de alerta

    Debes programar una lógica sencilla para determinar el valor de estado_alerta.

    Ejemplo general

    if lectura1 < 30:
    estado_alerta = "normal"
    elif lectura1 < 50:
    estado_alerta = "aviso"
    else:
    estado_alerta = "critico"

    Esto es solo un ejemplo. Cada sensor deberá definir sus propios umbrales.


    Ejemplos según sensor

    Sensor de temperatura

    • temperatura normal → normal
    • temperatura alta → aviso
    • temperatura muy alta → critico

    Sensor de gas

    • valor bajo → normal
    • valor elevado → aviso
    • valor peligroso → critico

    Sensor PIR

    • sin movimiento → normal
    • movimiento detectado → puede mantenerse en aviso o critico, según el criterio del proyecto

    Sensor de distancia

    • distancia segura → normal
    • objeto cercano → aviso
    • objeto muy cercano → critico

    Parte 10. Valor inicial de enviado_central

    Todos los registros nuevos deberán guardarse inicialmente así:

    enviado_central = 0

    Esto significa que todavía no han sido enviados al servidor central.

    Más adelante, cuando construyas el sistema de sincronización, los registros enviados se actualizarán a:

    enviado_central = 1

    Parte 11. Ejemplo de script con lógica de alerta

    #!/usr/bin/env python3
    
    import MySQLdb
    from datetime import datetime
    
    def escribir_log(mensaje):
        with open("/home/pi/raspialarma/logs/raspialarma.log", "a") as log:
            log.write(f"{datetime.now()} - {mensaje}\n")
    
    try:
        conexion = MySQLdb.connect(
            host="localhost",
            user="raspiuser",
            passwd="raspi1234",
            db="raspialarm"
        )
    
        cursor = conexion.cursor()
    
        raspberry_id = "RPI01"
        nombresensor = "SENSOR_PRUEBA"
        lectura1 = 42.00
        lectura2 = 0.00
        lectura3 = 0.00
        fecha_hora = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        alumnoEncargado = "TU_NOMBRE"
        descripcionSensor = "Prueba con niveles de alerta"
        enviado_central = 0
    
        if lectura1 < 30:
            estado_alerta = "normal"
        elif lectura1 < 50:
            estado_alerta = "aviso"
        else:
            estado_alerta = "critico"
    
        sql = """
            INSERT INTO lecturas_sensores
            (raspberry_id, nombresensor, lectura1, lectura2, lectura3, fecha_hora,
             alumnoEncargado, descripcionSensor, estado_alerta, enviado_central)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
        """
    
        valores = (
            raspberry_id,
            nombresensor,
            lectura1,
            lectura2,
            lectura3,
            fecha_hora,
            alumnoEncargado,
            descripcionSensor,
            estado_alerta,
            enviado_central
        )
    
        cursor.execute(sql, valores)
        conexion.commit()
    
        mensaje = f"Lectura guardada correctamente con estado {estado_alerta}"
        print(mensaje)
        escribir_log(mensaje)
    
    except Exception as e:
        error = f"Error: {e}"
        print(error)
        escribir_log(error)
    
    finally:
        try:
            cursor.close()
            conexion.close()
        except:
            pass

    Parte 12. Automatizar la ejecución con cron

    Edita el crontab:

    crontab -e

    Añade una de estas opciones.

    Cada minuto

    * * * * * /usr/bin/python3 /home/pi/raspialarma/sensores/captura_sensor.py >> /home/pi/raspialarma/logs/cron.log 2>&1

    Cada 5 minutos

    */5 * * * * /usr/bin/python3 /home/pi/raspialarma/sensores/captura_sensor.py >> /home/pi/raspialarma/logs/cron.log 2>&1

    Cada 10 minutos

    */10 * * * * /usr/bin/python3 /home/pi/raspialarma/sensores/captura_sensor.py >> /home/pi/raspialarma/logs/cron.log 2>&1

    Parte 13. Consultas útiles para revisar la información

    Ver todas las lecturas

    SELECT * FROM lecturas_sensores;

    Ver las últimas lecturas

    SELECT * FROM lecturas_sensores ORDER BY fecha_hora DESC;

    Ver solo lecturas de una Raspberry

    SELECT * FROM lecturas_sensores
    WHERE raspberry_id = 'RPI01';

    Ver solo lecturas en estado crítico

    SELECT * FROM lecturas_sensores
    WHERE estado_alerta = 'critico';

    Ver solo lecturas pendientes de enviar al servidor central

    SELECT * FROM lecturas_sensores
    WHERE enviado_central = 0;

    Contar cuántas lecturas no se han enviado todavía

    SELECT COUNT(*) AS pendientes
    FROM lecturas_sensores
    WHERE enviado_central = 0;