Is using #define to declare constants in arduino c a bad idea?

While reading on how to conserve RAM, one of the things I read is using #define to define constants. While reading some random stuff online, most people are of the opinion that #define should never be used and that Const are better used. I'm actually particular about conserving RAM. What are the advantages and disadvantages that comes with using #define over Const in defining constants in arduino.

consts will typically be type-safe.

I’m not going to second-guess the compiler about RAM savings.

don't be afraid of const variables.
Most of the your const byte - or whatever constants - will be optimized by the compiler anyway.
No need to put constants into a #define "to save RAM".

make two short sketches - and compare.
Stay with const or constexpr for the sake of "type-safe".

btw: you are now fighting "RAM issues" for weeks now. Isn't it time to upgrade the controller with something new with more SRAM?

#define is a pre-processor directive. the pre-preprocessor replaces all instance of the #define symbol with the string it is defined as.

for example, "#define N 12" replaces all instances of N with 12 in the code. it allows all place in the code where the value of N is used to changed by making a change to a single line of code.

RAM is not affected by the use of the #define symbol. the C compiler itself is unaware of what the preprocessor has done.

the use of "const int" will avoid any ambiguity about the intended data type of the constant which may avoid unintended errors

TheMemberFormerlyKnownAsAWOL:
consts will typically be type-safe.

I'm not going to second-guess the compiler about RAM savings.

I've been using #define for a while and it seems to work. So far I haven't had any issues with it. Does it in anyway confuse the compiler?

#define is soooo 1970s.

noiasca:
btw: you are now fighting "RAM issues" for weeks now. Isn't it time to upgrade the controller with something new with more SRAM?

You got me on that one!
And Gosh, you're damn right. May be it's time to pick up my bluepill again.

If you’re down to picking over the odd byte or nybble here or there because of a numeric literal consuming a byte of RAM rather than being incorporated into a machine instruction, you’re definitely into new processor territory, or you’re obsessing over the wrong part of the code.

search for Arduino Pro Mega - these a makerfriendly AVR Mega, they come in several variants, but most of them are nearly the size of a Arduino UNO. Some come without an USB and spare additional 3mm of PCB size.

And again: do you test with #define and const:

/*
  Blink
  Der Sketch verwendet 924 Bytes (2%) des Programmspeicherplatzes. Das Maximum sind 32256 Bytes.
  Globale Variablen verwenden 9 Bytes (0%) des dynamischen Speichers, 2039 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.
*/

#define ledPin 13
#define timeOn 1000
#define timeOff 1000

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(ledPin, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(timeOn);                       // wait for a second
  digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW
  delay(timeOff);                       // wait for a second
}

vs.

/*
  Blink

  Der Sketch verwendet 924 Bytes (2%) des Programmspeicherplatzes. Das Maximum sind 32256 Bytes.
  Globale Variablen verwenden 9 Bytes (0%) des dynamischen Speichers, 2039 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.


*/

const byte ledPin = 13;
const uint16_t timeOn = 1000;
const uint16_t timeOff = 1000;

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(ledPin, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(timeOn);                       // wait for a second
  digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW
  delay(timeOff);                       // wait for a second
}

If you compile the example like it comes from the IDE - including the hardcoded times for on and off - it will use the same amount of PROGMEM and SRAM.

You see? There is no change in program size, nor static memory usage ("globals").
The compiler will optimize the code if you help him and declare your variables as const.

You just get a problem, if you forget to declare them const and the compiler isn't able to guess that it is constant:

int ledPin = 13;   // if you forget to use const ...
int timeOn = 1000;
int timeOff = 1000;


// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(ledPin, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(timeOn);                       // wait for a second
  digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW
  delay(timeOff);                       // wait for a second
}

Der Sketch verwendet 946 Bytes (2%) des Programmspeicherplatzes. Das Maximum sind 32256 Bytes.
Globale Variablen verwenden 9 Bytes (0%) des dynamischen Speichers, 2039 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.

@noiasca, You're absolutely correct, I did that with my code and I confirmed that the two are exactly the same in terms of sram and progmem. I'm in this situation because i did things upside down. I had the hardware all wired up before giving considerable thoughts to the software they will run as well as the space they will occupy. At this point, changing hardware may cause considerable loss of time and resources I may never get.
Coping with these challenges has taught me invaluable lessons I couldn't have learnt in any other way.
Anyways, I have my SRAM usage trimmed down to only 58 percent. I have removed all use case of strings except one global string which I don't know how to deal with. Everything works for now. Working for the future is another thing though

Again, I would even use that much SRAM, if not for the ArduinoJson 5 i'm using to send data to the server. I heard version 6 is more RAM friendly. I have tried several times to use it but for some unknown reason, the version 6 hasn't worked with my program.

Sketch uses 25884 bytes (80%) of program storage space. Maximum is 32256 bytes.
Global variables use 1205 bytes (58%) of dynamic memory, leaving 843 bytes for local variables. Maximum is 2048 bytes.

I tried to check the free SRAM while the code was running. I noticed the lowest point was when the code was running the ArduinoJson 5 strings. At those point, the program sometimes have only 250 bytes of SRAM free.

If you can’t replace your last String object, consider to use .reserve() with the maximum size of your buffer you really need. This can help you to prevent heap fragmentation.

regarding JSON:

if you just need to send a JSON, have you ever tried to build the JSON on your own? How long does the JSON get (in bytes?) what is you destination (which variable/array is accepted by your client?)

Yes I called _buffer.reserve(50) in my setup method.

noiasca:
if you just need to send a JSON, have you ever tried to build the JSON on your own? How long does the JSON get (in bytes?) what is you destination (which variable/array is accepted by your client?)

This is the tricky part and I wish I can build the JSON on my own.
This is the method that uses the JSON string.

SoftwareSerial gsm(9,10);// gsm module
void sendHttp(){

  if (gsm.available())
  Serial.write(gsm.read());// ensure that bearer is on
  if(isConnected()==false){
   activateBearerProfile(); 
  }
   gsm.print(F("AT+HTTPTERM\r\n"));
  _buffer=_readSerial(2000); 
  Serial.println(_buffer);
  gsm.print(F("AT+HTTPINIT\r\n"));
  _buffer=_readSerial(TIMEOUT); 
  Serial.println(_buffer);
    if (_buffer.indexOf(F("OK")) != -1) {
      gsm.print(F("AT+HTTPSSL=1\r"));
       _buffer=_readSerial(TIMEOUT);
       Serial.println(_buffer);
       if (_buffer.indexOf(F("OK")) != -1) {   
        gsm.println(F("AT+HTTPPARA=\"CID\",1"));
        _buffer = _readSerial(2000);
        Serial.println(_buffer);
          if (_buffer.indexOf(F("OK")) != -1) {
                 
  StaticJsonBuffer<140> jsonBuffer;
  JsonObject& object = jsonBuffer.createObject();
  checkMem();
  object.set("Site",site);
  object.set("Voltage One",mainsVoltage);
  object.set("Mains Freq",frequency);
  object.set("Voltage Two",inverterVoltage);
  object.set("Time",timeStamp); //timestamp in iso8601 format
  
  object.printTo(Serial);
  Serial.println(" ");  
  String sendtoserver;
  object.prettyPrintTo(sendtoserver);
   _readSerial(TIMEOUT);
  gsm.println(F("AT+HTTPPARA=\"URL\",\"https://domain.mywebsite.com\"")); //Server address
  _buffer = _readSerial(TIMEOUT);
  Serial.println(_buffer);
   gsm.println(F("AT+HTTPPARA=\"CONTENT\",\"application/json\""));
   _buffer = _readSerial(TIMEOUT);
  Serial.println(_buffer);
     if (_buffer.indexOf(F("OK")) != -1) {
  gsm.println("AT+HTTPDATA=" + String(sendtoserver.length()) + ",100000");
  Serial.println(sendtoserver);//*************************************************************
  delay(2000);
  _buffer = _readSerial(TIMEOUT);
  Serial.println(_buffer);
 checkMem();
  gsm.println(sendtoserver);
  _readSerial(TIMEOUT);
                  }
            }
       }
    }
}

If building my own JSON or using version 6 of JSON can be a game changer, Pls I like to know how. The JSON string is under 140 bytes

It should be pretty simple to build your own JSON format C string using sprintf or strcat. Then you can lose the JSON library and String objects altogether.

wildbill:
It should be pretty simple to build your own JSON format C string using sprintf or strcat. Then you can lose the JSON library and String objects altogether.

Some cheering news then. Please can you give me some head start. I must confess I have absolutely no idea on how to go about it neither have I seen it done before. I've always thought JSON is one sacred stuff from the blues.

Maybe start by posting a sample JSON string created by the library and details of where the data comes from

UKHeliBob:
Maybe start by posting a sample JSON string created by the library and details of where the data comes from

{"Site":"Gwaytruio","Mains Volt":231,"Mains Freq":50.02,"Inv Volt":231,"Time":"2020-11-11T15:22:41"}

AT+HTTPPARA="CONTENT","application/json"

OK

{
  "Site": "Gwaytruio",
  "Mains Volt": 231,
  "Mains Freq": 50.02,
  "Inv Volt": 231,
  "Time": "2020-11-11T15:22:41"
}
AT+HTTPDATA=122,100000

DOWNLOAD

The two arrays in curly bracket are the things the arduinoJson generates and sends to the server with the code I posted earlier. The four variables are all globals. The data is coming from different sensors.

Bringing a neat circularity to this topic, “Site”, Mains Volt", “Mains Freq” etc, look to me to be ideal candidates for the preprocessor’s stringify operator.

Here's a start with sprintf:

// {"Site":"Gwaytruio","Mains Volt":231,"Mains Freq":50.02,"Inv Volt":231,"Time":"2020-11-11T15:22:41"}
char buf[128];
char site[]="Gwaytruio";
int MainsVoltage =231;
float Freq=50.02;

void setup()
{
char MainsFreq[8];  
Serial.begin(115200);
dtostrf(Freq,5,2,MainsFreq);
sprintf(buf,"{\"site\":\"%s\",\"Mains volt\":%d,\"Mains Freq\":%s}",site,MainsVoltage,MainsFreq);
Serial.println(buf);
}

void loop()
{
}

Wao, thank you to everyone. I was able to follow @wildbill lead and now I could do away with the ArduinoJson library altogether and everything works perfectly giving the SRAM sufficient head room. It's only now I realized that the JSON thing is just formatting.

Big thanks once again

All my worries are nearly gone.