Program memory usage: int vs word vs byte vs uint16_t

Variable T6 in program below is declared as int T6, even though its value varies only between 0 and 150.

 T6 = temp1Value;

..where temp1Value is a float.
Even though I only need the integer value.

Program memory usage: 13492 bytes when declaring int T6.

When changing int T6 to unsigned T6 prog mem = 13516 bytes
When changing to uint16_t T6 prog mem = 13516 bytes
When changing to word T6 prog mem = 13516 bytes
When changing to unsigned int T6 prog mem = 13516 bytes
When changing to byte T6 prog mem = 13518 bytes

I thought that when entering a float into an int that the effective value would be an integer?

Why does the use of int as a declaration result in lower memroy usage than any of the other declarations in this case?

#include <Wire.h>
#include <LCD.h> // will force using NewLiquidCrystal library
#include <LiquidCrystal_I2C.h>
#include <Adafruit_INA219.h>
Adafruit_INA219 ina219; //declare an instance of INA219
LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
// const int RELAIS = 10;
#define RELAIS 10 // PB2 set relais/LED output on pin 10 overtemp detection
#define BEEP 11   // PB3 set piezo beep output on pin 11
#define fan 9     // ventilator output pin
unsigned long now1;
unsigned long now2;
unsigned long interval;
// unsigned long periods[] = {500, 1500};
const word interval1 = 500;  // delay time 1 in milliseconds, high temp alarm
const word interval2 = 750; // delay time 2 in milliseconds, overtemp alarm
const word interval3 = 1000; // delay time 3 in milliseconds, LCD refresh
byte T1 = 70; // max normal operating temperature darlington & heatsink
const byte T1a = T1 - 2;  // hysteresis set variable high temperature low end at -2C
const byte T1b = T1; // hysteresis reset
const byte T2 = 85; // overtemperature darlington or heatsink
const byte T3 = 65; // kick-in temp for fan, fanspeed = 25%
const byte T4 = 85; // temp for max fan speed
const byte T5 = 5; // hysteresis value, used to substract from kick-in temperature
byte fanValue; // output value to cooling fan
int T6; // intermediate temp value for fan speed usage
const byte fanMin = 125; // minimum fan speed
const byte fanMax = 254; // maximum fanspeed
const byte v1 = 2; // max voltage when short circuit occurs
const byte C1 = 2; // min current when short circuit occurs
const int displaydelay = 500; // display update delay in millis
boolean OC = false; // overcurrent status
boolean warning; // warning status (temperature and/or current)
const byte x = 8; // loop value: loop amount = x+2 because min and max are removed
boolean message1;
boolean message2;
// unsigned long now;
float voltageDisplay;
float currentDisplay;
unsigned long temp1Value; // temp1 darlington cumulated
unsigned long temp2Value; // temp2 heatsink cumulated
unsigned long voltageValue; // spanning cumulated
unsigned long currentValue; // stroom cumulated
unsigned long cumulVoltage;
unsigned long cumulCurrent;
byte cumul;

void setup()
{
  //initialise libraries and ancillaries
  Serial.begin(9600);
  delay(100);
  Serial.println(__FILE__);
  analogReference(EXTERNAL);  // externe referntiespanning: 5,000V
  ina219.begin();
  lcd.begin(16, 2);
  pinMode (RELAIS, OUTPUT);
  pinMode (BEEP, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(fan, OUTPUT); //set fan as ventilator output
  digitalWrite(RELAIS, LOW);
  digitalWrite(BEEP, LOW);
  // LCD Backlight ON
  lcd.backlight();
  lcd.setBacklight(HIGH);

  //initialise display on startup
  lcd.home (); // go home on LCD
  lcd.print(F("    Labo PSU"));
  digitalWrite(LED_BUILTIN, HIGH);
  delay(100);
  lcd.setCursor (0, 1);
  lcd.print(F("    Erik /V10a    "));
  digitalWrite(LED_BUILTIN, LOW);
  delay(10);
  for (int i = 0; i < 4; i++)
  {
    lcd.noBacklight();
    digitalWrite(LED_BUILTIN, HIGH);
    delay(100);
    lcd.backlight();
    digitalWrite(LED_BUILTIN, LOW);
    delay(100);
  }
  // now = millis();
  lcd.clear();
}

void printToLCD(float value, byte width, byte dp) {
  char out[10];
  dtostrf(value, width, dp, out);
  lcd.print(out);
}

void loop()
{

  //initialise variables and reset loop totals to 0
  unsigned long temp1Total = 0; // temp1 darlington
  unsigned long temp2Total = 0; // temp2 heatsink
  unsigned long voltageTotal = 0;
  unsigned long currentTotal = 0;
  unsigned int signalMax0 = 0; // max value from analog 0
  unsigned long signalMin0 = 150000; // min value from analog 0, temp1 Darlington
  unsigned int signalMax1 = 0; // max value from analog 1
  unsigned long signalMin1 = 150000; // min value from analog 1, temp2 heatsink
  unsigned int signalMax2 = 0; // max value from analog 2
  unsigned long signalMin2 = 450000; // min value from analog 2, output voltage


  //start loop x + 2 times
  // x + 2 measurements of each analog input and accumulation;
  // test conditions for emergency action where and when necessary
  for (int j1 = 0; j1 < (x + 2); j1++)
  {
    //read values from analog inputs and accumulate
    temp1Value = (analogRead(0) * 50000 / 1023); // darlington temp read m°C LM35 = 10mV/°C, 5V = 500000 m°C
    delayMicroseconds(50);
    // temp1Value = (analogRead(0) * 50000 / 1023);
    // delayMicroseconds(1000);
    temp2Value = (analogRead(1) * 50000 / 1023); // heatsink temp read m°C LM35 = 10mV/°C
    delayMicroseconds(50);
    // temp2Value = (analogRead(1) * 50000 / 1023);
    // delayMicroseconds(1000);
    // voltageValue = (analogRead(2) * 45000 / 1024); // mV
    // voltageValue = (analogRead(2) * 45000 / 1024); // mV
    // delayMicroseconds(1000);
    // voltageValue = (analogRead(2) * 45000 / 1023); // mV
    // delayMicroseconds(10);
    voltageValue = (analogRead(2) * 45000 / 1023); // mV
    // Serial.print(F(" Darl. temp. = "));
    // Serial.print(temp1Value);
    // Serial.print(F("  heatsink temp. = "));
    // Serial.print(temp2Value);
    currentValue = ina219.getCurrent_mA(); //mA
    if (temp1Value < signalMin0)signalMin0 = temp1Value;
    if (temp1Value > signalMax0)signalMax0 = temp1Value;
    if (temp2Value < signalMin1)signalMin1 = temp2Value;
    if (temp2Value > signalMax1)signalMax1 = temp2Value;
    if (voltageValue < signalMin2)signalMin2 = voltageValue;
    if (voltageValue > signalMax2)signalMax2 = voltageValue;
    temp1Total += temp1Value; // temp1 darlington accum m°C*(x+2)
    temp2Total += temp2Value; // temp2 cooling fins accum m°C*(x+2)
    voltageTotal += voltageValue; // spanning mV*(x+2)
    currentTotal += currentValue; // stroom mA*(x+2)

    //test conditions for overtemp and or overcurrent warnings
    if (voltageValue / 1000 <= v1 && currentValue / 1000 >= C1) // shortcircuit
    {
      OC = true;
      lcd.setCursor (0, 1);
      lcd.print(F("kortsluiting    "));
      digitalWrite(LED_BUILTIN, HIGH);
    }
    else                                 // no shortcircuit;
    {
      OC = false;
      if (warning == false)
      {
      }
    }
  }

  //remove minimum and maximum outliers
  temp1Total -= signalMin0; // Darlington temp m°C
  temp1Total -= signalMax0;
  temp2Total -= signalMin1; // heatsink temp m°C
  temp2Total -= signalMax1;
  // Serial.print(F("   raw voltageTotal = "));
  // Serial.print(voltageTotal);
  voltageTotal -= signalMin2; // output voltage mV
  voltageTotal -= signalMax2;
  // Serial.print(F("   signalMin2 = "));
  // Serial.print(signalMin2);
  // Serial.print(F("   signalMax2 = "));
  // Serial.print(signalMax2);
  // Serial.print(F("   voltageTotal = "));
  // Serial.print(voltageTotal);
  // Serial.print(F("  Darl. min. = "));
  // Serial.print(signalMin0);
  // Serial.print(F("  Darl. max. = "));
  // Serial.print(signalMax0);
  // Serial.print(F("  heats. min. = "));
  // Serial.print(signalMin1);
  // Serial.print(F("  heats. max. = "));
  // Serial.print(signalMax1);
  // Serial.print(F(" Darl.Temp.Ttl = "));
  // Serial.print(temp1Total);
  // Serial.print(F(" Heatsink.Temp.Ttl = "));
  // Serial.println(temp2Total);

  //calculate average temperatures, voltage and current
  temp1Value = temp1Total / x / 100; // temp darlington °C
  temp2Value = temp2Total / x / 100; // temp heatsink °C
  voltageDisplay = voltageTotal / 1000.0 / x; // x = number of actually used measurements
  currentDisplay = currentTotal / 1000.0 / (x + 2);  // x+2 = number of actually used measurements
  // Serial.print(F("   voltageDisplay = "));
  // Serial.println(voltageDisplay);

  cumulVoltage += voltageTotal;
  cumulCurrent += currentTotal;
  cumul = cumul + 1;

  //fan cooling ventilator control section, output on pin 9
  bool fanOn = false;
  if (temp1Value > T4) //then it's already at max speed...
  {
    T6 = 100;
  }
  if (temp1Value > T3) // min temp1Value for fan kick-in
  {
    fanOn = true;
    T6 = temp1Value;
  }
  else if (temp1Value < (T3 - T5)) // min temp1Value - hysteresis for fan kick-out
  {
    fanOn = false;
    analogWrite(fan, 0);
  }
  if (fanOn)
  { // calculate fanspeed if temp1Value > T3
    if (temp1Value > T3)
    {
      T6 = temp1Value;
      fanValue = map(T6, T3, T4, fanMin, fanMax);
      analogWrite(fan, fanValue);
    }
    else {
      analogWrite(fan, fanMin);    // fanspeed if T3 - T5 < temp1Value < T3
    }
  }

  if (millis() - now1 >= interval) // execute every interval1 milliseconds (1000)
  {
    //test for overtemp conditions
    if ((temp1Value >= T1 && temp1Value < T2 && OC == false) || (temp2Value >= T1 && temp2Value < T2 && OC == false))  // warning high temp darlington or heatsink, no shortcircuit
    {
      // Serial.println(F("  high temp conditions"));
      // Serial.print(F("  darl = "));
      // Serial.print(temp1Value);
      // Serial.print(F("  HS. = "));
      // Serial.println(temp2Value);
      T1 = T1a; // hysteresis set to value 0°C: T2
      // T2b = T2a; // hysteresis set to value 2C: T2 -2°C
      warning = true;
      lcd.setCursor (0, 1); // go to start of 2nd line

      if (message1 == true)   // alarm, after second passage after interval3
      {
        message1 = false;
        interval = interval1;     // faster interval
        lcd.print(F("HIGH TEMP WARN!!"));
        digitalWrite(LED_BUILTIN, HIGH);
      }
      else                    // alarm reset, or first passage after high temp detect
      {
        message1 = true;
        interval = interval3;     // LCD regular refresh
        digitalWrite(LED_BUILTIN, LOW);
        lcd.print(F("I="));
        printToLCD(currentDisplay, 5, 2);
        lcd.write(F("A"));
        lcd.print(F(" T2="));
        printToLCD(temp2Value, 3, 0); // heatsink temp on line 1
        lcd.print(F("C"));
      }
    }

    if ((temp1Value >= T2 && OC == false) || (temp2Value >= T2 && OC == false))  // alarm overtemperature darlington or heatsink, no shortcircuit
    {
      // Serial.println(F("  overtemp conditions"));
      // Serial.print(F("  darl = "));
      // Serial.print(temp1Value);
      // Serial.print(F("  HS = "));
      // Serial.println(temp2Value);
      // T2b = T2a; // hysteresis st -2°C
      lcd.setCursor (0, 1); // go to start of 2nd line
      warning = true;
      digitalWrite(RELAIS, HIGH);         // overtemperature, relay opens
      lcd.setCursor (0, 1); // go to start of 2nd line

      if (message2 == true)   // alarm, after second passage after interval3
      {
        message2 = false;
        interval = interval2;     // faster interval
        lcd.print("OVERTEMP WARNING");
        digitalWrite(LED_BUILTIN, HIGH);
        digitalWrite(RELAIS, HIGH);         // overtemperature, relay opens
        digitalWrite(BEEP, HIGH);           // overtemperature, beep sounds
        tone(BEEP, 4000);
      }
      else                    // alarm reset, or first passage after high temp detect
      {
        message2 = true;
        interval = interval3;     // LCD regular refresh
        digitalWrite(LED_BUILTIN, LOW);
        digitalWrite(BEEP, LOW);
        lcd.print(F("I="));
        printToLCD(currentDisplay, 5, 2);
        lcd.print(F("A"));
        lcd.print(F(" T2="));
        printToLCD(temp2Value, 3, 0); // heatsink temp on line 2
        lcd.print(F("C"));
      }
    }
    now1 = millis();
  }

  if (millis() - now2 >= interval3)  // execute every interval1 milliseconds (500)
  {
    voltageDisplay = cumulVoltage / cumul / 1000.0 / x;
    currentDisplay = cumulCurrent / cumul / 1000.0 / (x + 2);
    // Serial.print(F("   voltageDisplay = "));
    // Serial.print(voltageDisplay);
    // Serial.print(F("  cumul = "));
    // Serial.println(cumul);
    cumulVoltage = 0;
    cumulCurrent = 0;
    cumul = 0;

    lcd.home ();
    lcd.print(F("U="));
    printToLCD(voltageDisplay, 5, 2);
    lcd.print(F("V"));
    lcd.print(F(" T1="));
    printToLCD(temp1Value, 3, 0); // Darlington temp on line 1
    lcd.print(F("C"));
    lcd.setCursor (0, 1); // go to start of 2nd line

    if (warning == false && OC == false)
    {
      digitalWrite(LED_BUILTIN, LOW);
      lcd.print(F("I="));
      printToLCD(currentDisplay, 5, 2);
      lcd.print(F("A"));
      lcd.print(" T2=");
      printToLCD(temp2Value, 3, 0); // heatsink temp on line 2
      lcd.print(F("C"));
      //}
      now2 = millis();
    }
  }

  if (temp1Value < T1 && temp2Value < T1 && OC == false)   // temperatures back to normal, no short circuit, protection relay and beep reset
  {
    lcd.setCursor (0, 1);
    noTone(BEEP);  // reset buzzer
    digitalWrite(RELAIS, LOW);
    digitalWrite(BEEP, LOW);           // overtemperature, beep sounds
    digitalWrite(LED_BUILTIN, LOW);
    warning = false;
    T1 = T1b; // reset T1 hysteresis to 0°C
  }
}

It is, assuming that the float is in the range of the integer.

AVR does not have instructions to do 16 bit calculations, so calls library functions to perform the calculation. There are different functions depending on the operands, so multiply (signed, unsigned) might be a different function to multiply (unsigned, unsigned).

I think that by changing types, you have caused different functions to be included.

if you have

float pi = 3.14;
int p = pi;

yes, p will hold pi "truncated", so it will be 3

so you get 2 bytes more. is that RAM or FLASH memory used ? which platform ?

It is program memory (I guess flash?). This is on an ATmega168P.

Operations on bytes are conducted as int, so may be you have some additional cost for the byte to int promotions or loose opportunities for the compiler optimization.

You would need to look at the binary code to see what those are…

Really?

And "..Operations on bytes are conducted as int.." ...so basically you are saying that declaration of variables as 'byte' still assigns an 'int'? Hard to believe?

No

I am saying that if you have a byte b and you do b = b + 3; then to perform the addition b is first promoted to an int (signed) the addition is performed and the result is truncated back into b.

If b had been an int, then no promotion / truncation would be necessary.

Not saying this is what’s happening here but that’s the type of things that could come into play.

Changing a type to unsigned also might impact what the optimizer is doing, hence the look at the binary.

1 Like

Are you sure about that? I just tried and I get

  t1 = t1 + 3;
 52e:	20 91 af 01 	lds	r18, 0x01AF	; 0x8001af <t1>
 532:	2d 5f       	subi	r18, 0xFD	; 253
 534:	20 93 af 01 	sts	0x01AF, r18	; 0x8001af <t1>

That’s what the standard says - the optimizer might play a role in what you got (see the subtraction of 253 instead of adding 3)

Try to run this

void setup() {
  Serial.begin(115200);
  byte b;
  Serial.println(sizeof b);
  Serial.println(sizeof(b+3));
}

void loop() {}

b will be 1 byte
b+3 will be 2 bytes (on uno, 4 on ESP32)

(Will be the same if you use a byte instead of 3 or
Serial.println(sizeof(b+(byte) 3));

1 Like

I think that is the key takeaway here. While there are general rules defined by the standard, the compiler may optimize the code in unexpected (but legal ways).

In the case of the assignment to a byte, promoting to an int and then back to a byte is numerically equivalent, so the compiler can avoid those steps.

The bottom line is that it becomes difficult to compare like for like, and the best way to see what the compiler is doing is to look at the compiler output, rather than guess at what it should be doing.

1 Like

yes, especially on a native 8 bit platform as the MCU knows how to perform those operations.

yep, that was just an example of why changing a type could lead to, but I agree, hence the

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.