Hay una diferencia enorme entre "esta placa puede correr IA" y "esta placa corre IA a una velocidad que sirve para algo". Este tutorial se mete en la segunda pregunta.

Vas a poner a funcionar un modelo de lenguaje completo dentro de un Arduino UNO Q: un chatbot que responde en tu navegador, sin cuenta de pago, sin API externa y sin que ninguna palabra salga de tu red. Después vamos a medir cuán rápido es realmente, y a explicar por qué rinde lo que rinde. que es la parte que casi nunca se cuenta.

Qué vas a saber hacer al final:

  • Levantar el ejemplo "Edge AI Assistant" de App Lab y conversar con un modelo local.
  • Elegir entre Gemma 3 1B y Qwen 3.5 0.8B con criterio, no al azar.
  • Resolver el error "Insufficient disk space" que aparece aunque el modelo pese 700 MB.
  • Medir tokens por segundo con tu propio script y entender qué limita esa cifra.
  • Integrar el modelo en una app Python tuya.

Qué necesitas

  • Un Arduino UNO Q. Conviene la variante de 4 GB, porque los modelos de lenguaje son hambrientos de memoria; la de 2 GB alcanza para los primeros intentos.
  • Un cable USB-C para alimentación y datos. Nada más de hardware.
  • Arduino App Lab en el computador (este tutorial usa la versión 0.10.0) y una cuenta Arduino.
  • Conexión a internet una sola vez, para bajar el modelo. Después la IA funciona completamente offline.

Qué significa "local" acá, y qué pasa con la NPU

Los dos modelos que ofrece App Lab corren sobre llama.cpp, el mismo motor de código abierto sobre el que está construido Ollama. "Local" significa entonces algo muy concreto: el modelo es un archivo que vive en la placa y las respuestas se generan ahí mismo, sin que nada viaje a la nube.

Queda la pregunta que Qualcomm usa para promocionar el chip. El procesador del UNO Q trae una NPU (Neural Processing Unit), una unidad de cálculo especializada en operaciones de redes neuronales. ¿La usa el chatbot?

En la selección de modelos de App Lab solo aparecen modelos de llama.cpp. se reconocen por el prefijo llamacpp: que la consola imprime al arrancar, por ejemplo llamacpp:Qwen3.5-0.8B-Q4_0. Y llama.cpp calcula clásicamente en la CPU. Lo comprobamos más abajo con datos.

NPU en una frase: mientras la CPU hace de todo un poco, la NPU está especializada en el tipo de cálculo del que están hechas las redes neuronales. la misma idea que una tarjeta gráfica en el entrenamiento de IA, pero diminuta y de bajo consumo.

Paso 1: abrir el ejemplo "Edge AI Assistant"

No hay que escribir una línea de código: el ejemplo viene listo en App Lab.

  1. En la vista de ejemplos, sección Inspirations, escribe edge en el buscador y abre Edge AI Assistant.

Arduino App Lab 0.10.0 con la pestaña Inspirations y el ejemplo Edge AI Assistant encontrado en el buscador

  1. El ejemplo usa dos bricks: Web UI para la interfaz y Large Language Model para la IA. Como los ejemplos integrados son de solo lectura, hay que duplicarlo con el botón Copy and edit app, arriba a la derecha.

El ejemplo Edge AI Assistant abierto en App Lab mostrando los bricks Web UI y Large Language Model, con el botón Copy and edit app arriba a la derecha

  1. Ponle un nombre a la copia y confirma con Create new.

Diálogo Create new app en Arduino App Lab al duplicar el ejemplo Edge AI Assistant

  1. Aprieta Run arriba a la derecha y espera a que la app arranque.
  2. Abre la interfaz en el navegador. Lo más simple es por mDNS usando el nombre de la placa: http://<nombre-de-la-placa>.local:7000. Si el .local no resuelve, saca la IP: en el terminal de la placa escribe hostname -I, toma la primera dirección y abre http://<ip>:7000.

Paso 2: elegir y descargar un modelo

Sin modelo no hay chatbot, y los modelos hay que bajarlos una vez antes de que corran localmente. En el primer Run la app avisa que falta el modelo.

Aviso 'AI model not available' en App Lab: la app necesita que se descargue un modelo antes de arrancar

Se ofrecen dos modelos deliberadamente chicos (los 4 GB de RAM ponen el techo), ambos desde Hugging Face:

  • Gemma 3 1B (722 MB). el modelo compacto de Google, con alrededor de mil millones de parámetros.
  • Qwen 3.5 0.8B (507 MB). todavía más chico, de Alibaba: más rápido y más frugal, pero más flojo con el lenguaje. Viene preseleccionado en el ejemplo.

Selección de modelos en el brick LLM de App Lab, con Gemma 3 1B (722 MB) y Qwen 3.5 0.8B

En la pestaña AI models descargas el que quieras. Para este tutorial usamos Gemma 3 1B: entrega un español más redondo. Si lo que buscas es velocidad máxima y no te molesta un lenguaje más torpe, el Qwen chico vale la pena. La descarga demora algunos minutos según tu conexión; después no necesitas internet nunca más.

El tropiezo: "Insufficient disk space" con un modelo de 700 MB

Es el error que frena a la mayoría, y no tiene nada que ver con el tamaño del modelo: es la partición. Míralo con df -h:

Código
/dev/mmcblk0p68  9.8G  9.3G  776K 100%  /
/dev/mmcblk0p69   18G  705M   17G   5%  /home/arduino

La partición del sistema (/) tiene apenas 9,8 GB y está llena, mientras que la partición grande de datos, /home/arduino, tiene 17 GB casi vacíos al lado. App Lab arranca cada brick como un contenedor Docker, y Docker guarda sus imágenes y los modelos descargados en /var/lib/docker — o sea, en la partición chica.

Alivio rápido (borra contenedores e imágenes sin usar):

Bash
docker system prune -af

Solución definitiva: mover Docker a la partición grande. Son cuatro pasos, mejor por SSH o en el terminal de la placa dentro de App Lab.

1. Detener Docker y copiar los datos:

Bash
sudo systemctl stop docker docker.socket
sudo rsync -aP /var/lib/docker/ /home/arduino/docker/

El -a de rsync no es decorativo: preserva permisos, dueños, enlaces simbólicos y marcas de tiempo. Docker con el driver overlay2 depende de esa estructura exacta, así que una copia con cp -r común te puede dejar imágenes corruptas. El -P solo agrega barra de progreso y reanudación.

2. Ajustar el archivo de configuración. Ábrelo con el editor nano:

Bash
sudo nano /etc/docker/daemon.json

De fábrica, en el UNO Q ahí solo está la configuración de logs. Agrega una única línea, "data-root", de modo que el archivo completo quede exactamente así (la línea de data-root es la nueva):

JSON
{
    "log-driver": "json-file",
    "log-opts": {
        "max-size": "10m",
        "max-file": "2"
    },
    "data-root": "/home/arduino/docker"
}

Cuidado con el JSON: después de la llave } que cierra log-opts tiene que ir una coma, y después de la línea nueva de data-root no. En nano guardas con Ctrl + O, confirmas con Enter y cierras con Ctrl + X. Si en tu archivo ya hay más contenido, agrega solo la línea de data-root y deja el resto tal cual.

3. Arrancar Docker y verificar:

Bash
sudo systemctl start docker
docker info | grep "Docker Root Dir"

Si la salida ahora dice /home/arduino/docker, el traslado resultó.

4. Recién ahí, limpiar. Si el paso 3 salió bien y docker image ls sigue mostrando tus imágenes, libera el espacio viejo:

Bash
sudo rm -rf /var/lib/docker

Ese orden importa más de lo que parece: si borras /var/lib/docker antes de confirmar que el daemon está leyendo la ruta nueva, te quedas sin las imágenes y sin la copia, y hay que volver a descargar todo. Verifica primero, borra después.

Paso 3: conversar con la IA

En la interfaz web te recibe una vista de chat con algunos prompts de ejemplo y un campo de texto libre. Escribes tu pregunta, tus mensajes aparecen a la derecha y las respuestas de la IA a la izquierda, en streaming: palabra por palabra, mientras el modelo las genera. Así ves de inmediato que algo está pasando, en vez de esperar un bloque terminado.

Interfaz de chat del Edge AI Assistant en el navegador, con preguntas a la derecha y respuestas del modelo local a la izquierda

Dos controles útiles: los botones de sugerencia agregan bloques ya redactados a tu pregunta y ayudan a armar mejores prompts, y Reset chat borra el historial y arranca una conversación nueva. Eso último importa porque el asistente tiene memoria corta: recuerda los últimos mensajes, así que puedes repreguntar sin repetir todo el contexto.

La parte honesta: ¿qué tan bueno es esto?

Un modelo de mil millones de parámetros en una placa de 60 euros no es GPT-4. La pregunta interesante es si alcanza igual para algo útil.

Primero: ¿usa la NPU?

Una mirada a htop durante una respuesta es concluyente: los cuatro núcleos del procesador al tope, cerca del 99%.

Vista dividida de chat y htop: los cuatro núcleos del Arduino UNO Q trabajando al 99% durante una respuesta

Y el propio servidor del modelo lo delata: arranca con el parámetro --device none y desde un paquete puramente de CPU (/opt/pkg-cpu/bin/llama-server). Queda claro: el UNO Q calcula el modelo de lenguaje completamente en la CPU. Es el mismo camino que Ollama en la Raspberry Pi.

Y es justo decirlo: que corra en CPU no es un defecto ni prueba que la NPU sea inútil. Qualcomm promociona la aceleración de IA del chip sobre todo para procesamiento de imágenes. detección de objetos, clasificación. , no para modelos de lenguaje. Los LLM son simplemente la carga equivocada para esa unidad.

Las cifras medidas

Medición hecha contra la API del servidor del modelo, con Gemma 3 1B:

Medición Valor (Gemma 3 1B, CPU)
Unidad de cálculo los 4 núcleos de CPU al ~99%, NPU sin usar (--device none)
Generación de texto ~5,5 tokens/s (estable entre corridas)
Procesamiento del prompt ~9,7 tokens/s
Tiempo hasta la primera palabra ~2 s (prompt corto)
Respuesta de unas tres frases ~14 s
RAM: reposo → modelo cargado ~720 MB → ~930 MB (de 3,6 GB)
Consumo bajo carga de IA ~1,4 W (en reposo 0,4 W)

En palabras: con unos 5,5 tokens por segundo, el asistente escribe notoriamente más lento de lo que tú lees. Una respuesta breve está lista en unos 14 segundos; una larga se pasa del minuto. Para probar y para preguntas simples alcanza; para cualquier cosa con urgencia, no.

Por qué 5,5 tokens/s y no más

Acá está la explicación que casi nunca aparece, y que sirve para cualquier modelo en cualquier placa. La generación de texto en un LLM es un problema limitado por ancho de banda de memoria, no por potencia de cálculo. Para producir cada token, el procesador tiene que leer los pesos completos del modelo desde la RAM. Gemma 3 1B en cuantización Q4_0 pesa unos 720 MB, así que la cota superior de velocidad es aproximadamente:

Código
tokens/s ≈ ancho de banda útil de memoria ÷ tamaño del modelo en RAM

De ahí salen tres conclusiones prácticas:

  1. Agregar núcleos casi no ayuda una vez que el bus de memoria está saturado. Por eso ves los cuatro núcleos al 99% y aun así el número no sube.
  2. El tamaño del modelo manda. Qwen 3.5 0.8B pesa 507 MB contra los 722 MB de Gemma: ese solo hecho lo hace alrededor de 1,4 veces más rápido, antes de considerar cualquier otra diferencia. Si necesitas velocidad, baja de modelo antes que de cuantización.
  3. Q4_0 significa pesos de 4 bits. Es una compresión con pérdida: reduce el modelo a un cuarto de su tamaño en 16 bits y, en el mismo movimiento, multiplica la velocidad. El precio es algo de calidad en las respuestas. que en modelos tan chicos ya es limitada.

Y el dato que sí es récord: el consumo

Aquí el UNO Q tiene un argumento real. Incluso con los cuatro núcleos al tope, la placa consume alrededor de 1,4 W; en reposo, 0,4 W. Eso da una eficiencia de aproximadamente 4 tokens por segundo por watt, una cifra que placas mucho más rápidas no alcanzan, porque el consumo sube más rápido que el rendimiento.

Traducido a algo concreto: dejarla funcionando 24/7 cuesta cerca de 1 kWh al mes. A tarifa residencial chilena eso son unos $150 CLP mensuales. Un asistente offline permanente por el precio de un café al año es un caso de uso que sí existe.

¿Y la calidad de las respuestas?

Exactamente lo que se puede esperar de un modelo tan chico: el idioma fluye sorprendentemente bien, pero los datos bailan. Ante "¿qué es una Raspberry Pi?" respondió una explicación limpia y correcta; en cambio inventó de cero la respuesta sobre el UNO Q ("una placa Uno modificada") y dijo que tenía "dos" núcleos. Las dos cosas son falsas.

Sirve como generador de ideas, como ayuda para redactar o como consulta offline para preguntas simples. Como fuente de conocimiento confiable, no. La memoria RAM nunca es el problema. con ~930 MB sobra espacio hasta en la placa de 4 GB; el cuello de botella es únicamente el cálculo.

Mide tú mismo tus tokens/s

Por debajo, el modelo corre en un contenedor propio que expone una API compatible con OpenAI (en este caso en http://llamacpp-models-runner:9999/v1). Eso significa que puedes hablarle con cualquier cliente de OpenAI o directamente con curl — muy práctico para colgarle tus propios scripts o un benchmark:

Bash
curl -s http://llamacpp-models-runner:9999/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"llamacpp:gemma-3-1b-it-Q4_0","messages":[{"role":"user","content":"Cuenta del 1 al 50"}],"stream":false}' \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['usage'])"

El objeto usage que devuelve trae los tokens de entrada y de salida. Divide los tokens de salida por el tiempo que demoró el comando y tienes tu propia cifra de tokens/s, sin depender de la de nadie. Repítelo con el otro modelo y vas a ver la diferencia de tamaño reflejada casi linealmente.

La IA dentro de tu propia app

Si quieres ir más allá del chatbot terminado, controlar el modelo desde Python toma sorprendentemente poco código. El brick llm expone la clase LargeLanguageModel:

Python
from arduino.app_bricks.llm import LargeLanguageModel
from arduino.app_utils import App

# Elegir el modelo explícitamente (tiene que estar descargado antes).
# La consola imprime el ID exacto al arrancar, acá Gemma 3 1B:
llm = LargeLanguageModel(model="llamacpp:gemma-3-1b-it-Q4_0")
llm.with_memory(10)   # recordar los últimos 10 mensajes como contexto

def frage_stellen():
    prompt = "Erklaere mir in zwei Saetzen, was eine NPU ist."
    for chunk in llm.chat_stream(prompt):
        print(chunk, end="", flush=True)
    print()

App.run(user_loop=frage_stellen)

El patrón es siempre el mismo: elegir modelo, opcionalmente darle memoria, y recoger la respuesta token a token con chat_stream(). El código fuente completo y comentado del chatbot está en el repositorio app-bricks-examples de Arduino.

Variantes y mejoras

1. Asistente de comandos para tu taller. Como la API es compatible con OpenAI, puedes apuntarle cualquier script que ya tengas escrito para ChatGPT cambiando una sola URL. Un uso concreto: un pequeño servicio que reciba mensajes de texto, los clasifique con el modelo local ("encender", "apagar", "estado") y mande la orden al lado microcontrolador del UNO Q. Toda la interpretación del lenguaje ocurre sin internet.

2. Resumidor de logs offline. Alimenta al modelo con las últimas líneas de un log de sensores y pídele un resumen en una frase. Con 5,5 tokens/s no sirve para conversar, pero para producir una línea cada quince minutos sobra. y es un caso donde la lentitud simplemente no importa.

3. Compara contra otras placas con el mismo método. El script de curl de más arriba funciona igual contra un servidor Ollama en una Raspberry Pi 5 (cambiando el host y el puerto por 11434). Corre el mismo prompt en ambas, anota tokens/s y mide el consumo con un medidor USB: vas a ver que la Pi 5 gana claramente en velocidad y pierde igual de claramente en eficiencia por watt. Ese contraste, medido por ti y no leído en un blog, es la mejor forma de decidir cuál placa te conviene.

Límites, y cuándo conviene otra placa

  • Solo modelos chicos. Los 4 GB de RAM son un techo duro. Acá corren modelos del orden de mil millones de parámetros o menos (Gemma 3 1B, Qwen 3.5 0.8B), no los modelos grandes de los asistentes en la nube.
  • No hay asistente de voz. Entrada y salida por voz Arduino solo las ofrece en el modelo mayor VENTUNO Q, no en el UNO Q. Acá es chat de texto.
  • Cuándo preferir una Raspberry Pi. Si necesitas más RAM, modelos más grandes o ya tienes un entorno Ollama funcionando, una Raspberry Pi 5 (o derechamente una máquina con GPU) te va a servir mejor: sus núcleos son bastante más rápidos calculando. El UNO Q gana en consumo mínimo y en la simplicidad de un clic dentro de App Lab.

Personalización para Chile

Para este proyecto necesitas exactamente dos cosas, y las dos están con stock local en MechatronicStore:

  • Arduino UNO Q QRB2210 compatible con Linux Debian (SKU C-222). $91.990 CLP
  • Cable USB Tipo C a USB Tipo A 1mt (SKU B-101). $2.190 CLP

Total: $94.180 CLP aproximado. El tutorial original habla de unos 60 euros por la placa (~$62.000 CLP), pero ese valor no considera envío internacional, IVA de importación ni las semanas de espera.

Equivalencias y advertencias:

  • Cualquier cable USB-C de datos sirve; el que no sirve es un cable de "solo carga", que alimenta la placa pero no la deja aparecer en App Lab. Es una causa de fallo silenciosa y frecuente.
  • Si el ejercicio de comparación te tienta, MechatronicStore también tiene Raspberry Pi 5 y disipadores para ella, pero ojo: no son parte de este tutorial. El UNO Q funciona solo, y la Pi es otro proyecto con su propia fuente y su propia refrigeración.
  • Para dejar el asistente funcionando permanente no necesitas nada extra: con 1,4 W la placa se alimenta sin problema desde cualquier cargador USB que ya tengas.

Recursos

Contenido inspirado en el tutorial de Philipp Schweizer en raspberry.tips. Versión chilena con componentes en stock local en MechatronicStore.