Diferencia para definir entradas digitales

Hola:

A la hora de ver a la gente hacer programas de Arduino, se puede hacer de varias maneras. Hay personas que usan esta forma.

int boton1 = 1;
int boton2 = 2;
int boton3 = 3;

Si les pregunta que vas a cambiar el valor, dicen no. Si les pregunta. ¿Por qué usas int si con byte tienes de 0 a 255? No tienes tanta entradas. Se quedaría el mismo resultado así abajo. Valores tipo constante porque su valor de las entradas en fija y ahorrar más ram con byte.

const byte boton1 = 1;
const byte boton2 = 2;
const byte boton3 = 3;

Otros si deja que los valores son fijos, usan esto:

#define boton1 1
#define boton2 2
#define boton3 3

¿Qué ventaja tiene uno sobre otro?

Saludos.

Edito:
Me olvidé comentar que las constantes, se escribe en mayúsculas.

const byte BOTON1 = 1;
const byte BOTON2 = 2;
const byte BOTON3 = 3;

Si te topas con una variable de una palabra, para tener buenas prácticas de programación, se escribe así con todas las letras de las variables en minúsculas.

byte boton = 4;

Si incluye varias palabra en la misma variable se escribe o expresa así: Primera letra en minúsculas y las demás palabras en mayúsculas, así entiendes una frase en la variable.

byte esteEsMiBoton = 4;

Si es una constante, una sola palabra se escribe con mayúscula así:

const byte BOTON = 4;

Si es una frase de esta forma:

cont byte ESTE_ES_MI_BOTON = 4;

Hay muchos problemas hoy en día sobre si la constante es una variable o no. Variable se llama así proque su valor varía, constante const mucho no lo llaman variable porque su valor no varía.

yo siempre uso #define precisamente porque no conlleva gasto de ram ,aunque creo que en algun post en que tambien discutio esto mismo ,se llego a la conclusion de que con el modificador const tampoco habia consumo de ram.

En Arduino no lo se, en PIC escrito en ensamblador, te aseguro que en const consume un punto de dirección de memoria más los datos en su interior. También tiene #include, se comporta en este caso, en la memoria programa, por eso no se puede modificar. Hay modelos que si, modificación del propio firmware pero tiene que estar diseñado para esto.

Hay muchas razones para que estas cosas presenten dudas continuas o permanentes.
Todos los dias quienes ayudamos en este foro corregimos estas cosas.

Para mi el gran culpable de esto es instructables, ya que veo cientos de tutoriales de códigos que usan int en lugar de #define o const byte, porque? Pues por que la gente resuelve algo y cree que debe comunicarlo y claro que esta bien, pero luego viene el lector que ASUME que eso es correcto y se populariza el mal uso.

Es como programar hoy en Windows. Antes todos pensábamos en como optimizar los códigos y ahora ves que algo termina siendo un programa de un tamaño que no te imaginabas, pero es por el uso de librerías que no siempre usas. No me meto mas en eso porque no es mi fuerte.
En Arduino pasa lo mismo. Se cree que sobra RAM, sobre FLASh pero no es hasta que no te queda mas que te preocupas de optimizar.
Es una mala práctica recurrente y si no, lean las 3 primeras páginas de General/Software/Hardware y cuenten cuantos códigos tienen int para definir pines. Descarten a los que responden siempre en este foro y se sorprenderán con un alto porcentaje.

En Documentación => Indice de temas tutoriales => Programacion General hay un hilo Ardutips hecho por @noter y @Alfaville que resumen varias de estas cosas.
El post#6 Ajustar el tamaño de las variables segun el uso es la explicación o respueta a la pregunta de @Metaconta
El post de @noter El modificador const

Yo tenía entendido que declarar variables aún con const sigue ocupando memoria RAM. De hecho, no es por nada que a algo declarado con const se le pueda extraer su puntero; mientras que declarado con #define no se puede. Por ejemplo:

const byte b = 64;
byte* p = (byte*)&b; // ¿Bug del compilador? Claro, sin el casting explícito no me deja

void setup() {
  Serial.begin(9600);
  *p = 32; // ¿Acabo de modificar una "constante"?
  Serial.println(b);

}

void loop() {
}

Si este código imprimiera "32", entonces el uso de const no es del todo "a prueba de tontos" :o

Sin embargo, esto definitivamente no me compila:

#define B 64
byte* p = (byte*)&B; // Dice que no está definido el operador unario & en macro constantes

void setup() {
  Serial.begin(9600);
  *p = 32; // ¿Acabo de modificar una "constante"?
  Serial.println(B);

}

void loop() {
}

Por esta razón es que llego a creer que #define es la manera más adecuada de declarar constantes. No ocupa del todo memoria RAM ya que en tiempo de compilación se traduce en valores "hard coded". Por ejemplo:

#define LED 2
#define TIEMPO_RETRASO 500

void setup() {
  pinMode(LED, OUTPUT);
}

void loop() {
  digitalWrite(LED, HIGH);
  delay(TIEMPO_RETRASO);
  digitalWrite(LED, LOW);
  delay(TIEMPO_RETRASO);
}

El compilador lo traduce en:

void setup() {
  pinMode(2, 1); // Si no me equivoco, OUTPUT vale 1
}

void loop() {
  digitalWrite(2, 1);
  delay(500);
  digitalWrite(2, 0);
  delay(500);
}

Dicho en otras palabras: los #define son como "piezas de rompecabezas" del código; por eso no necesitan de la RAM (y no se les puede extraer el puntero).
Es más, si algo se declara pero no se llega a usar en ninguna parte del código; dicho valor ni siquiera llega a formar parte del compilado (también aplica para vectores con PROGMEM).

Por esta razón es que:

const byte b = 64;

No le hallo mucho sentido hacerlo; aparte de que igual ocupa memoria RAM, el valor queda también en memoria flash (no es por arte de magia que, en el arranque, el CPU del micro recuerde cuál era).

Aclaro de una vez:

#define MENSAJE "Hola mundo de Arduino"

Estoy de acuerdo que se declara con #define, pero ESTO NO AHORRARÁ MEMORIA RAM SI SE USA.
Las cadenas de caracteres son otro cantar; los valores "hard coded" que introduciría son el puntero a dicha cadena, y hablar de puntero implica que los datos deben estar en RAM.
Declararlo así al final termina en lo que mencioné en el párrafo anterior: queda en RAM y en flash.

En este caso, la manera correcta de declarar cadenas de caracteres constantes, y que no queden en RAM, serían las siguientes:

const char MENSAJE[] PROGMEM = "Hola mundo de Arduino";

// También aplica para vectores de cualquier tipo.
// Si la función a utilizar no sabe manejar punteros de memoria flash, no quedaría de otra que hacer una copia en RAM; suena contradictorio al tema, pero la diferencia está en que debería ser en un único espacio y reutilizable
#define MENSAJE F("Hola mundo de Arduino")

// La macrofunción F() solo acepta literales (entrecomillados), no vectores de char ni objetos String
// Solo se puede utilizar en objetos que hereden de Print o funciones que sepan manejar el tipo de dato __FlashStringHelper

ArduMyth:
Tampoco tiene sentido para mí hacer esto y prefiero un array.

Si te sientes más cómodo programando así, no te lo voy a discutir; pero si pretendes ahorrar RAM de esta manera, estás equivocado.
Mira que lo acabo de decir: el const por sí solo no evita el consumo en RAM (y encima no es tan resistente a la modificación intencional); para eso está el #define en datos individuales, o el PROGMEM para conjuntos (vectores) de datos.

PD: los #define también sirven para ahorrarle cálculos al micro. Ejemplo:

#define GIGABYTE 1024 * 1024 * 1024
// El resultado al ser mayor que 65535, el compilador implícitamente entiende que la constante es de tipo long o unsigned long

En vez de:

const unsigned long GIGABYTE = 1024 * 1024 * 1024;
// Encima el programador debe escoger el tipo de dato más adecuado; un novato que opta ciegamente por int, después no tiene idea qué hizo mal
// Si el compilador no es lo suficientemente optimizado, este cálculo lo tiene que realizar la CPU del micro (en tiempo de ejecución)

Buenas explicaciones con ejemplos incluidos.

La const si se queda en la RAM, solo que no se modifica nunca, no te deja, ni en otros lenguajes de PC. El #Define se queda en la memoria programa, en el firmware de Arduino, nunca se borra si pierde la energía como la RAM.

De todas maneras, parece que es más bien posibilidades y gustos de usar #define o las const.

Saludos.

Tampoco volverse locos y ser estrictos cuando el tamaño del código lo requiere que antes se comenten otros consumos evitables con cosas tan simples como

Serial.print("Temperatura");

Buenas:

Muchos se pasan por alto a la hora es escribir variables, como dije arriba, si ven una variable en mayúsculas como esto.

PIN_LED_13

Automáticamente, ya sabes que es una constante (const). Tener estas buenas prácitas de programación, no solo nos podemos entender nosotros mismos, si no los que nos rodean.

Que mal hábito veo por aquí, poner en una constante esto así:

pinLed

Como dijeron arriba, en muchos tutoriales, incluido ciertos libros y no son pocos, que pone pinLed a una constante. Desde que sea así, pinLed,un buen programador (en mi caso no lo soy pero se intenta), sabe que es una variable, porque su valor varía. Así estamos todos, confundiéndonos, sobre todo al leer códigos de otros, cuando estamos iniciando a este mundo de la programación.

Incluso al guardar datos en un array, hay de tipo variables y constantes, variables minúsculas y constantes mayúsculas.

¿Es mucho esfuerzo aprender buenas prácticas de programación?

¿Da pereza cambiar la forma que tenía acostumbrado por lo que diga un forero?

:wink:

Cada uno a lo suyo. Xddddddd.

Saludos.

No debemos obsesionarnos tanto con ciertas cosas. El compilador realiza muchas optimizaciones sobre el código inicial. El utilizar #define o const va a ser traducido por el mismo código en muchas ocasiones, ya que en ambos casos se va a intentar cargar en el ensamblador como un literal. Si compilamos por ejemplo este código:

//const int HOLA=14;
#define HOLA 14
int a;
void setup()
{
int *puntero=&a;
*puntero=HOLA;
}

void loop()
{
  
}

Si comentamos el define y descomentamos el const, la compilación devuelve los mismos tamaños (programa 670 bytes, datos 11 bytes), y aunque no lo he comprobado, seguramente el ensamblador será idéntico. Incluso cambiando a const byte, se mantiene la igualdad.
Sin embargo, si intento referenciar la constante, al compilador no le queda otro remedio que establecerla en memoria (con un define esto sería imposible) y cambian las cosas (programa 670 bytes, datos 11 bytes):

const int HOLA=14;
//#define HOLA 14
int a;
void setup()
{
int *puntero=&HOLA;
*puntero=a;
}

void loop()
{
  
}

Resumiendo, pienso que las definiciones de constantes para pines y demás las deberíamos hacer como más cómodo o más bonito nos parezca. Otra cuestión son los tipos para las variables, y si deben ser locales o globales.

Buenas explicación con ejemplos. :wink:

No entiendo poner el &.

int *puntero=&HOLA;

¿Qué significa exactamente?

No lo había visto.

Vengo de un libro de novatos, ejjejejeje. :wink:

Puedes coger el hex, compararlo con un editor hexadecimal, si no cambia el tamaño, puede cambiar otras cosas.

Saludos.

noter:
Si comentamos el define y descomentamos el const, la compilación devuelve los mismos tamaños (programa 670 bytes, datos 11 bytes), y aunque no lo he comprobado, seguramente el ensamblador será idéntico. Incluso cambiando a const byte, se mantiene la igualdad.
Sin embargo, si intento referenciar la constante, al compilador no le queda otro remedio que establecerla en memoria (con un define esto sería imposible) y cambian las cosas (programa 670 bytes, datos 11 bytes):

Ya decía yo que era ridículo que const almacenara el valor en ambas memorias; pero ya veo que estaba equivocado.

De hecho estoy de acuerdo con esa "teoría" de que mi demostración en realidad estaba forzando al compilador a almacenarlo en RAM. También por el hecho de que los vectores y cadenas de caracteres siempre van a la RAM, a menos que se use F() o PROGMEM