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.