Can anyone help me trim this down? (8,168 of 8,192 used)

My setup, in case it’s somehow relevant:

  • ATTiny84 (dip package)
  • 8x2 LCD (HD44780)
  • 1 DS18B20 sensor
  • 2 normal tactile breadboard buttons
  • 1 40A SSR

The code below works as expected; it does what I want it to do. More specifically, it reads the sensor, provides PID control for the SSR, and prints the setpoint & sensor temperatures to the LCD. I’d like to add just a little more functionality. However, I’m having space issues.

I pruned out everything I could think of. I used to have the code for the LCD broken out in void displayLCD(), but moving those lines of code into loop() saved me a few bytes. I even trimmed some code from the getTemp() routine for the sensor regarding error codes. Utimately, I ran out of ideas at 8,168 bytes of the available 8,192. Right now, I can’t even add a degree symbol to the screen without exceeding the maximum. More importantly, I’d really like to add a few lines of code to use adaptive tunings for the PID, if I can.

I’m hoping that some of you more seasoned vets will take a look and point out some of my novice mistakes. Or, if not a mistake, at least point out some more efficient ways to code this.

#include <PID_v1.h>
#include <OneWire.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>

const int DS18S20_Pin=1;
const int ssrPin=0;
const int upPin=9;
const int downPin=10;
const int Setpoint_address=0;
const int WindowSize = 5000;
double Setpoint, Input, Output;
unsigned long EEPROMTime, windowStartTime, markTime, displayTime;

LiquidCrystal lcd(8,7,6,5, 4, 3, 2);
OneWire ds(DS18S20_Pin);
PID myPID(&Input, &Output, &Setpoint,120,0.05,1, DIRECT);


void setup()
{
  lcd.begin(8,2);
  pinMode(ssrPin,OUTPUT);
  pinMode(upPin,INPUT_PULLUP);
  pinMode(downPin,INPUT_PULLUP);
  Setpoint= EEPROM.read(Setpoint_address);
  myPID.SetOutputLimits(0, WindowSize);
  myPID.SetMode(AUTOMATIC);
}


void loop()
{
  //read the sensor/////////////////////////////////////////////////////////
  Input = getTemp();


  //print to the LCD////////////////////////////////////////////////////////
  lcd.setCursor(0,0);
  if (Setpoint<100) lcd.print(" "); 
  lcd.print(Setpoint);
  //    lcd.print(char(223));  
  //    lcd.print("F");
  lcd.setCursor(0,1);
  if (Input<100) lcd.print(" "); 
  lcd.print(Input);
  //    lcd.print(char(223));  
  //    lcd.print("F");


  //compute output and switch the SSR//////////////////////////////////////
  myPID.Compute();
  if ((millis()-markTime)>WindowSize) markTime=millis();
  if ((millis()-markTime)<Output) digitalWrite(ssrPin,HIGH);
  else digitalWrite(ssrPin,LOW);


  //check buttons for Setpoint changes//////////////////////////////////////
  if (digitalRead(upPin)==LOW) Setpoint++; delay(75);
  if (digitalRead(downPin)==LOW) Setpoint--;  delay(75);

  //check every second to see if the Setpoint has been stored////////////
  if ((EEPROM.read(Setpoint_address)!=Setpoint)&&(millis()-EEPROMTime)>1000)
  { 
    EEPROM.write(Setpoint_address,Setpoint);
    EEPROMTime=millis();
  }


}


float getTemp()
{
  byte data[12];
  byte addr[8];
  if ( !ds.search(addr)) 
  {
    ds.reset_search();
    return -1000;
  }
  ds.reset();
  ds.select(addr);
  ds.write(0x44,1);
  byte present = ds.reset();
  ds.select(addr);  
  ds.write(0xBE); 
  for (int i = 0; i < 9; i++) 
  { 
    data[i] = ds.read();
  }
  ds.reset_search();
  byte MSB = data[1];
  byte LSB = data[0];
  float tempRead = ((MSB << 8) | LSB);
  float TemperatureSum = tempRead / 16;
  TemperatureSum= TemperatureSum*9/5+32;
  return TemperatureSum;
}

The big fish is the floating-point stuff. Unfortunately, removing it requires reworking the PID code.

How good are you at fixed-point (fractional) math?

Completely illiterate.

const int DS18S20_Pin=1;
const int ssrPin=0;
const int upPin=9;
const int downPin=10;
const int Setpoint_address=0;

At least some of these could be bytes.

  for (int i = 0; i < 9; i++) 
  { 
    data[i] = ds.read();
  }

The index is an int?

  float tempRead = ((MSB << 8) | LSB);

Shifting and oring two bytes does not produce a float.

There doesn't seem to be a huge amount of code there, so I guess you're using some hefty libraries. Do any of them provide functionality that you don't need that could be conditionally compiled out?

PaulS:

const int DS18S20_Pin=1;

const int ssrPin=0;
const int upPin=9;
const int downPin=10;
const int Setpoint_address=0;



At least some of these could be bytes.

I tried changing all of them as const int, int, and byte. Byte actually used more space that const int.

PaulS:

  for (int i = 0; i < 9; i++) 

{
    data[i] = ds.read();
  }



The index is an int?

I don’t know. That’s what the DS18B20 example has; I don’t know enough to try anything else.
Do you have a suggestion?

PaulS:

  float tempRead = ((MSB << 8) | LSB);

Shifting and oring two bytes does not produce a float.

Again, straight from the example code. I’m receptive to any suggestions.

All in all, I really can’t complain. I’m still new enough at this that I’m shocked at what can be accomplished with such a small investment. The chip was about $2.50, the display about $4, the sensor was $1, and the power supply was about $4. By the time I add buttons, wires, some capacitors and an enclosure of some sort, I’ll be I still come in under $15.

I’ve got a similar setup on a Mega328, but that’s got all the room in the world in comparison. I’m just curious how much one can get out of the Tiny.

PeterH:
There doesn't seem to be a huge amount of code there, so I guess you're using some hefty libraries. Do any of them provide functionality that you don't need that could be conditionally compiled out?

I'm not sure what you mean by "conditionally compiled out".

I did consider removing the EEPROM write to save the setpoint, just designate a value for the setpoint to be every time I reset and change the value each time. That only brought the code down to 7,980.

8*1024-7980 = 212 bytes free. What's the goal? How many bytes free do you estimate you will need for the additional functionality?

PaulS:

const int DS18S20_Pin=1;

const int ssrPin=0;
const int upPin=9;
const int downPin=10;
const int Setpoint_address=0;



At least some of these could be bytes.

Sorry for off topic, but I've been told (and then, I verified) that const int's, like #define's, doesn't use any memory. So... why changing const int to const byte would save memory?

Here is some code that I use to read the temperature that does not use a library.
It is much smaller than using the 1 wire library but only works with a single sensor on the bus.
It also does not use any floating point so that saves space as well.

void OneWireReset(int Pin) // reset.  Should improve to act as a presence pulse
{
     digitalWrite(Pin, LOW);
     pinMode(Pin, OUTPUT); // bring low for 500 us
     delayMicroseconds(500);
     pinMode(Pin, INPUT);
     delayMicroseconds(500);
}

void OneWireOutByte(int Pin, byte d) // output byte d (least sig bit first).
{
   byte n;

   for(n=8; n!=0; n--)
   {
      if ((d & 0x01) == 1)  // test least sig bit
      {
         digitalWrite(Pin, LOW);
         pinMode(Pin, OUTPUT);
         delayMicroseconds(5);
         pinMode(Pin, INPUT);
         delayMicroseconds(60);
      }
      else
      {
         digitalWrite(Pin, LOW);
         pinMode(Pin, OUTPUT);
         delayMicroseconds(60);
         pinMode(Pin, INPUT);
      }

      d=d>>1; // now the next bit is in the least sig bit position.
   }
   
}

byte OneWireInByte(int Pin) // read byte, least sig byte first
{
    byte d, n, b;

    for (n=0; n<8; n++)
    {
        digitalWrite(Pin, LOW);
        pinMode(Pin, OUTPUT);
        delayMicroseconds(5);
        pinMode(Pin, INPUT);
        delayMicroseconds(5);
        b = digitalRead(Pin);
        delayMicroseconds(50);
        d = (d >> 1) | (b<<7); // shift d to right and insert b in most sig bit position
    }
    return(d);
}


/*
 * Returns temp in C times 100.
 * i.e. 12.34 degress C is returned as 1234
 */
int getCurrentTemp()
{  
  int HighByte, LowByte, TReading, Tc_100, sign;

  OneWireReset(TEMP_PIN);
  OneWireOutByte(TEMP_PIN, 0xcc); // SKIP ROM (broadcast to all devices)
  OneWireOutByte(TEMP_PIN, 0x44); // CONVERT (perform temperature conversion, strong pullup for one sec)

  OneWireReset(TEMP_PIN);
  OneWireOutByte(TEMP_PIN, 0xcc); // SKIP ROM (broadcast to all devices)
  OneWireOutByte(TEMP_PIN, 0xbe); // READ SRATCHPAD

  LowByte = OneWireInByte(TEMP_PIN);
  HighByte = OneWireInByte(TEMP_PIN);
  TReading = (HighByte << 8) + LowByte;
#if 0
  sign = TReading & 0x8000;  // test most sig bit
  if (sign) // negative
  {
    TReading = (TReading ^ 0xffff) + 1; // 2's comp
  }
#endif
  Tc_100 = (6 * TReading) + TReading / 4;    // multiply by (100 * 0.0625) or 6.25

  return(Tc_100);

	
}

It uses integers so you have to do a small calculation to get things ready to print.
So here is how you use it.

	Tc_100 = getCurrentTemp();
	if(Tc_100 < 0)
	{
		Tc_100 = -Tc_100;
		sign = '-';
	}
	else
	{
		sign = '+';
	}

	if(units == 'F')
		Tc_100 = ((Tc_100 * 9 ) / 5) + 3200;

	T_whole = Tc_100 / 100;  // separate off the whole and fractional portions
	T_fract = Tc_100 % 100;

Tc_100, T_whole, and T_fract are all declared as int
units and sign are declared as char

— bill

Do you have to search for the 1-wire device address? Do you not know it? The code for searching probably takes quite a bit of space.

I'm not sure about the code size but it is fairly long function.

If there is only one sensor on the bus you can issue a SKIP ROM command and "broadcast"
to any device. That is what the code I provided does so there is no need to mess with searching
or addresses.
(updated previous code example to be a little more informative)

If using the OneWire library you can do the same by using the skip() function.
It would be a quick and easy test on this code to simply eliminate this:

  byte addr[8];
  if ( !ds.search(addr)) 
  {
    ds.reset_search();
    return -1000;
  }

And then change this:

  ds.select(addr);

to this:

  ds.skip();

in the getTemp() function.

--- bill

Very impressive!

Thanks to you guys for your tips. Just using the skip brought me down to 7740.

Eliminating the library altogether brought it down to 7474, but introduces some flaky behavior. The sensor will stay, mostly, at 79.91 (accurate), but will jump to an absurd number very briefly then immediately back to 79.91. I'm guessing that I need to slow my sample rate of the sensor down to allow it time to reset?