Cloud, DevOps e IoT en español

Error "was not declared in this scope" con un struct en Arduino: la causa y 4 soluciones

15 de octubre de 2026 · Steven Carvajal · Tutoriales

Código de este artículo en GitHub →

Tienes un struct definido arriba del sketch, una función que lo recibe como parámetro, y el compilador responde con alguno de estos errores:

error: 'Sensor' was not declared in this scope
error: variable or field 'leerSensor' declared void
error: 'Sensor' does not name a type

El tipo está declarado. El problema no es tu código, sino algo que el IDE de Arduino hace con los archivos .ino antes de compilar.

La causa: los prototipos automáticos

En C++, una función tiene que estar declarada antes de usarse. Para que no tengas que escribir esas declaraciones (los prototipos), el IDE de Arduino las genera solo: lee el .ino, encuentra todas tus funciones y escribe sus prototipos en un punto del archivo, justo antes de la primera definición de función que encuentra.

Si una función usa un tipo tuyo y ese tipo se define después de ese punto, el prototipo generado lo usa antes de que exista. De ahí el error.

Este sketch falla:

#include <Arduino.h>

void setup() {        // primera función: aquí se insertan los prototipos
  Serial.begin(115200);
}

struct Sensor {       // el tipo aparece después
  uint8_t pin;
  float valor;
};

void leerSensor(Sensor &s) {   // su prototipo quedó arriba, antes de Sensor
  s.valor = analogRead(s.pin);
}

void loop() {}

El caso difícil: una macro que es una función

Este me costó más encontrarlo. En el núcleo de Arduino para ESP32, SET_LOOP_TASK_STACK_SIZE() (la macro para dar más pila a loop()) se expande a una definición de función. Si la pones antes de un #include que define tus tipos, el IDE la toma como la primera función e inserta ahí todos los prototipos:

#include <WiFi.h>
SET_LOOP_TASK_STACK_SIZE(16 * 1024);   // MAL: se expande a una función
#include "tipos.h"                      // define struct Nodo

void procesar(Nodo &n) { /* ... */ }    // error: 'Nodo' was not declared

Visualmente no hay ninguna función antes del #include, por eso es tan confuso.

Solución 1: los tipos en un archivo .h

La más limpia. Crea una pestaña nueva en el IDE llamada tipos.h, mueve ahí tus struct y enum, e inclúyela al principio del sketch:

// tipos.h
#pragma once
#include <Arduino.h>

struct Sensor {
  uint8_t pin;
  float valor;
};
// sketch.ino
#include "tipos.h"

void leerSensor(Sensor &s) {
  s.valor = analogRead(s.pin);
}

Solución 2: todos los tipos antes de la primera función

Si prefieres un solo archivo, define los tipos arriba del todo, antes de setup() y de cualquier otra función.

Solución 3: las macros que son funciones, después de los #include

#include <WiFi.h>
#include "tipos.h"
SET_LOOP_TASK_STACK_SIZE(16 * 1024);   // BIEN: después de todos los #include

Solución 4: escribe tú el prototipo

Si declaras el prototipo a mano después del tipo, el IDE no genera el suyo para esa función:

struct Sensor { uint8_t pin; float valor; };
void leerSensor(Sensor &s);   // prototipo propio

Cómo confirmar que es esto

Activa la salida detallada al compilar (Archivo → Preferencias → Mostrar salida detallada durante: compilación). El IDE muestra la ruta del .cpp que genera a partir de tu sketch; ábrelo y verás dónde quedaron los prototipos.

Si el proyecto crece, deja de pelear con esto: pasa la lógica a archivos .cpp y .h normales, donde el IDE no genera nada y C++ funciona como siempre. Es lo que hago en el firmware del ESP32 de la guía del ESP32.

Este error es uno de los 13 errores reales al construir domótica con ESP32 y AWS IoT.

Preguntas frecuentes

¿Pasa también con PlatformIO?

No con archivos .cpp: PlatformIO no genera prototipos y tienes que declararlos tú. Si usas un .ino en PlatformIO, aplica la misma conversión que el IDE de Arduino.

¿Y con enum o typedef?

Igual. Cualquier tipo propio usado en la firma de una función (parámetros o tipo de retorno) puede provocar el error.

¿Por qué a veces funciona y a veces no?

Depende de dónde quede la primera función. Mover una función de sitio, añadir una macro o reordenar los #include cambia el punto de inserción, y el mismo código pasa de compilar a fallar.

Sigue leyendo

Guía completa de esp32 y firmware: Qué es el ESP32 y por qué domina los proyectos IoT: guía para elegir y empezar