Code to big!

I finally made it and now are my code is to big…
Any tips on how I can reduce the code to make it “smaller”? I had to remove som comments so I could post it.
Is it possible to store the code on the SD card when I logg data on it?
The SD card got 4gb memory so it got enough space I think.

#include <SPI.h>
#include <Ethernet.h>
#include "DHT.h"
#include <MemoryFree.h>
#include <SD.h>
const int chipSelect = 4;

// DHT Sensor Setup
#define DHTPIN1 2     // We have connected the DHT to Digital Pin 2
#define DHTPIN2 3     // what pin we're connected to
//#define DHTPIN3 5     // what pin we're connected to
//#define DHTPIN4 6     // what pin we're connected to

#define DHTTYPE DHT11 // This is the type of DHT Sensor (Change it to DHT11 if you're using that model)

DHT dhtbasement(DHTPIN1, DHTTYPE);   // Initialize DHT object
DHT dhttest1(DHTPIN2, DHTTYPE);     // Initialize DHT object
//DHT dhttest2(DHTPIN3, DHTTYPE);     // Initialize DHT object
//DHT dhttest3(DHTPIN4, DHTTYPE);     // Initialize DHT object

// Local Network Settings
byte mac[] = {
 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 }; 

// GroveStreams Settings
String gsApiKey = "bf521e52-3d35-3798-89f3-d918cd438196";   
String gsComponentName = "Temperatur_och_luftfuktighet";        

char gsDomain[] = "grovestreams.com";   
String gsComponentTemplateId = "temp"; 
                                       

String gsStreamId1 = "s1";   //Outside Temp C.
String gsStreamId2 = "s2";   //Temp1 C.
String gsStreamId3 = "s3";   //Outside Temp C.
String gsStreamId4 = "s4";


const unsigned long updateFrequency = 20000UL; 


// Variable Setup
String myIPAddress;  
String myMac;        
                   

unsigned long lastSuccessfulUploadTime = 0; //Don't change. Used to determine if samples need to be uploaded.
boolean lastConnected = false;              //Don't change. Used for Internet Connection Reset logic
int failedCounter = 0;                      //Don't change. Used for Internet Connection Reset logic

// Initialize Arduino Ethernet Client
EthernetClient client;


void setup()
{
 // Start Serial for debugging on the Serial Monitor
 Serial.begin(9600);

 // Start Ethernet on Arduino
 startEthernet();

  Serial.print("Initializing SD card...");

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");
  


}

void loop()
{

 // Print Update Response to Serial Monitor
 if (client.available())
 {
   char c = client.read();
   Serial.print(c);
 }

 // Disconnect from GroveStreams
 if (!client.connected() && lastConnected)
 {
   Serial.println(F("...disconnected"));
   Serial.println();
   showMem(); //from desert home

   client.stop();
 }

 // Update sensor data to GroveStreams
 if(!client.connected() && (millis() - lastSuccessfulUploadTime > updateFrequency))
 {
   String temps = getTemperatures();
  
   updateGroveStreams(temps);
 }

 // Check if Arduino Ethernet needs to be restarted
 if (failedCounter > 3 ) {
  
   //Too many failures. Restart Ethernet.
   startEthernet();
 }

 lastConnected = client.connected();
}

void updateGroveStreams(String temps)
{
 unsigned long connectAttemptTime = millis();

 if (client.connect(gsDomain, 80))
 {        

   //Assemble the url used to pass the temperature readings to GroveStreams.
   // The Arduino String class can be memory intensive. char arrays should be used instead. But,
   // to make this example simple to understand we have chosen to use the String class.
   //We are passing temperature readings into two types of GroveStreams streams, Random and Interval streams.

   String url = "PUT /api/feed?compTmplId=" + gsComponentTemplateId;
   url +="&compId=" + myMac;
   url += "&compName=" + gsComponentName;
   url += "&api_key=" + gsApiKey;
   url += temps;
   url += " HTTP/1.1";
  
   //Serial.println(url);
    
   client.println(url);
   client.println("Host: " + String(gsDomain));
   client.println(F("Connection: close"));
   client.println("X-Forwarded-For: "+ myIPAddress); //Include this line if you have more than one device uploading behind
                                                     // your outward facing router (avoids the GS 10 second upload rule)
   client.println(F("Content-Type: application/json"));
   client.println();

   if (client.available())
   {
     //Read the response and display in the the console
     char c = client.read();
     Serial.print(c);
   }

   if (client.connected())
   {
     lastSuccessfulUploadTime = connectAttemptTime;
     failedCounter = 0;
   }
   else
   {
     //Connection failed. Increase failed counter
     failedCounter++;

     Serial.println("Connection to GroveStreams failed ("+String(failedCounter, DEC)+")");  
     Serial.println();
   }

 }
 else
 {
    //Connection failed. Increase failed counter
   failedCounter++;

   Serial.println("Connection to GroveStreams Failed ("+String(failedCounter, DEC)+")");  
   Serial.println();
 }

{
  // make a string for assembling the data to log:
  String dataString = "";
  short t, h;
    h = dhtbasement.readHumidity();
     t = dhtbasement.readTemperature();

      int humid = h;
    dataString += "Humidity";
    dataString += ", ";
    dataString += String(humid);
    dataString += "%";
    int temp = t;
    dataString += ", Temprature";
    dataString += String(temp);
    dataString += "*C";
    dataString += " at";
    dataString += " ";

    
    // open the file. note that only one file can be open at a time,
    // so you have to close this one before opening another.
    File dataFile = SD.open("datalog.txt", FILE_WRITE);

    // if the file is available, write to it:
    if (dataFile) {
      dataFile.println(dataString);
      dataFile.close();
    delay(1000);
    }  
}



}

// from desert home
void showMem()
{
 char Dbuf [100];
 
 strcpy_P(Dbuf,PSTR("Mem = "));
 Serial.print(Dbuf);
 Serial.println(freeMemory());
} 

void startEthernet()
{
 //Start or restart the Ethernet connection.
 client.stop();

 Serial.println(F("Connecting Arduino to network..."));
 Serial.println();  

 //Wait for the connection to finish stopping
 delay(2000);

 //Connect to the network and obtain an IP address using DHCP
 if (Ethernet.begin(mac) == 0)
 {
   Serial.println(F("DHCP Failed, reset Arduino to try again"));
   Serial.println();
 }
 else
 {

   Serial.println(F("Arduino connected to network using DHCP"));
   Serial.println();

   //Set the mac and ip variables so that they can be used during sensor uploads later
   myMac = getMacReadable();
   Serial.println("MAC: " + myMac);

   myIPAddress = getIpReadable(Ethernet.localIP());
   Serial.println("IP address: " + myIPAddress);
 }

}

String getMacReadable()
{
 //Convert the mac address to a readable string
 char macstr[20];
 snprintf(macstr, 100, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
 return String(macstr);
}

String getIpReadable(IPAddress p)
{
 //Convert the ip address to a readable string
 String ip;
 for (int i =0; i < 3; i++)
 {
   ip += String(p, DEC);
   ip += ".";
 }
 ip +=String(p[3], DEC);
 return ip;
}

String getTemperatures()
{
 //Get the temperature analog reading and convert it to a string
 float t, h, t1, h1, t2, h2, t3, h3;


     h = dhtbasement.readHumidity();
     t = dhtbasement.readTemperature();
     h1 = dhttest1.readHumidity();
     t1 = dhttest1.readTemperature();
     

 char temp[15] = {0}; 
 dtostrf(h, 12, 3, temp);
 String xh(temp);
 xh.trim();
 
 char temp2[15] = {0};
 dtostrf(t, 12, 3, temp);
 String xt(temp);
 xt.trim();
  
 char temp3[15] = {0};
 dtostrf(t1, 12, 3, temp);
 String xt1(temp);
 xt1.trim();
 
 char temp4[15] = {0};
 dtostrf(h1, 12, 3, temp);
 String xh1(temp);
 xh1.trim();

 /*
 
 char temp5[15] = {0}; 
 dtostrf(t2, 12, 3, temp);
 String xt2(temp);
 xt2.trim();
 
 char temp6[15] = {0};
 dtostrf(h2, 12, 3, temp);
 String xh2(temp);
 xh2.trim();
 
 char temp7[15] = {0};
 dtostrf(t3, 12, 3, temp);
 String xt3(temp);
 xt3.trim();

 char temp8[15] = {0}; 
 dtostrf(h3, 12, 3, temp); 
 String xh3(temp);
 xh3.trim();
*/
 String temps;
 temps = "&" + gsStreamId1 + "=" + xh;   //Temp C
 temps += "&" + gsStreamId2 + "=" + xt;  //Temp F
 temps += "&" + gsStreamId3 + "=" + xt1;  //Temp F
 temps += "&" + gsStreamId4 + "=" + xh1;  //Temp F


 return temps;
}

Regards,

Patrik.P

String gsApiKey = "bf521e52-3d35-3798-89f3-d918cd438196";   
String gsComponentName = "Temperatur_och_luftfuktighet";

When you have memory problems, Strings are the first thing that need to go.

But, it isn't clear what problems you are having. Are you exceeding the code size for the Arduino, or the SRAM size?

PaulS: String gsApiKey = "bf521e52-3d35-3798-89f3-d918cd438196";   String gsComponentName = "Temperatur_och_luftfuktighet";

When you have memory problems, Strings are the first thing that need to go.

But, it isn't clear what problems you are having. Are you exceeding the code size for the Arduino, or the SRAM size?

This is the error message I got:

Arduino:1.6.5 (Windows 8.1), Card:"Arduino Uno"

Sketch uses 33 738 bytes (104%) of program storage space. Maximum is 32 256 bytes.

Global variables use 1 643 bytes (80%) of dynamic memory, leaving 405 bytes for local variables. Maximum is 2 048 bytes.

processing.app.debug.RunnerException: Sketch too big ; see http://www.arduino.cc/en/Guide/Troubleshooting#size

  at processing.app.debug.Compiler.size(Compiler.java:335)

  at processing.app.debug.Compiler.build(Compiler.java:118)

  at processing.app.Sketch.build(Sketch.java:1162)

  at processing.app.Sketch.build(Sketch.java:1137)

  at processing.app.Editor$BuildHandler.run(Editor.java:2029)

  at java.lang.Thread.run(Thread.java:745)

Sketch too big; see http://www.arduino.cc/en/Guide/Troubleshooting#size

  This report would have more information with
  "Show verbose output during compilation"
  enabled in File > Preferences.

I really don't know what to do....

thank you for the fast reply!

You could probably eliminate a few dozen "Serial.println" statements without effect to your program.

I don't understand a lot of what you are doing with that code. For instance:

char Dbuf [100];

 strcpy_P(Dbuf,PSTR("Mem = "));
 Serial.print(Dbuf);

Allocating space for 100 characters, and then putting "Mem = " in flash memory and then copying it to SRAM and then printing it is hardly more memory or code efficient than:

Serial.print(F("Mem = "));
char temp[15] = {0}; 
 dtostrf(h, 12, 3, temp);
 String xh(temp);
 xh.trim();

 char temp2[15] = {0};
 dtostrf(t, 12, 3, temp);
 String xt(temp);
 xt.trim();
  
 char temp3[15] = {0};
 dtostrf(t1, 12, 3, temp);
 String xt1(temp);
 xt1.trim();

 char temp4[15] = {0};
 dtostrf(h1, 12, 3, temp);
 String xh1(temp);
 xh1.trim();

How many buffers do you need? After you've copied and wrapped the data in a (useless) String instance, why can't you reuse that buffer?

I suspect that if you get rid of the String class, and learn to use C strings, your code will shrink to fit.

104 percent is too much for code, but also 80% is too much for ram, since you use String objects which need a lot memory.

Your sketch seems to be a combination a few other sketches. It is possible to reduce memory by doing things in the same way, so less library functions will be used. I think you can remove the functions to convert the mac and ip to a String. The mac is known and I think there is a function to print the IP address.

Some output messages can be shorter. For example "Connection to GroveStream failed" can be "ERR50". The text "DHCP failed, reset the Arduino..." and "Arduino connected to network..." can be "DHCP fail" and "DHCP ok". Perhaps there are still one or two strings that can be 'F()' or PSTR().

When the String object is not used at all, it will fit. But you would have to rewrite the whole sketch.

The easy fix would be to get a bigger Arduino. The Mega 2560 has got 256 KB memory so your code will fit there.

Well I finally got it down to 32 518 bytes (100%) of program storage space. Maximum is 32 256 bytes.
I need to remove 262 bytes but I don’t have any more to remove…?

#include <SPI.h>
#include <Ethernet.h>
#include "DHT.h"
#include <MemoryFree.h>
#include <SD.h>
const int chipSelect = 4;
#define DHTPIN1 2     
#define DHTPIN2 3
#define DHTTYPE DHT11
DHT dhtbasement(DHTPIN1, DHTTYPE);
DHT dhttest1(DHTPIN2, DHTTYPE);
byte mac[] = {
 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 }; 
// GroveStreams Settings
String gsApiKey = "bf521e52-3d35-3798-89f3-d918cd438196";   
String gsComponentName = "Temperatur_och_luftfuktighet";        
char gsDomain[] = "grovestreams.com";   
String gsComponentTemplateId = "temp"; 
String gsStreamId1 = "s1";   //Outside Temp C.
String gsStreamId2 = "s2";   //Temp1 C.
String gsStreamId3 = "s3";   //Outside Temp C.
String gsStreamId4 = "s4";
const unsigned long updateFrequency = 20000UL; 
String myIPAddress;  
String myMac;
unsigned long lastSuccessfulUploadTime = 0; 
boolean lastConnected = false;
int failedCounter = 0;
EthernetClient client;
void setup(){
startEthernet();
if (!SD.begin(chipSelect)) {
    return;} }
void loop()
{
 if (client.available())
 {
   char c = client.read();
   Serial.print(c);
 }
 // Disconnect from GroveStreams
 if (!client.connected() && lastConnected)
 {

   client.stop();
 }

 // Update sensor data to GroveStreams
 if(!client.connected() && (millis() - lastSuccessfulUploadTime > updateFrequency))
 {
   String temps = getTemperatures();
  
   updateGroveStreams(temps);
 }

 // Check if Arduino Ethernet needs to be restarted
 if (failedCounter > 3 ) {
  
   //Too many failures. Restart Ethernet.
   startEthernet();
 }

 lastConnected = client.connected();
}

void updateGroveStreams(String temps)
{
 unsigned long connectAttemptTime = millis();

 if (client.connect(gsDomain, 80))
 {        
   String url = "PUT /api/feed?compTmplId=" + gsComponentTemplateId;
   url +="&compId=" + myMac;
   url += "&compName=" + gsComponentName;
   url += "&api_key=" + gsApiKey;
   url += temps;
   url += " HTTP/1.1"; 
   client.println(url);
   client.println("Host: " + String(gsDomain));
   client.println(F("Connection: close"));
   client.println("X-Forwarded-For: "+ myIPAddress);
   client.println(F("Content-Type: application/json"));
   client.println();

   if (client.available())
   {
     //Read the response and display in the the console
     char c = client.read();
     Serial.print(c);
   }

   if (client.connected())
   {
     lastSuccessfulUploadTime = connectAttemptTime;
     failedCounter = 0;
   }
   else
   {
     
     failedCounter++;
   }

 }
 else
 {
   failedCounter++;
}

{
  // make a string for assembling the data to log:
  String dataString = "";
  short t, h;
    h = dhtbasement.readHumidity();
     t = dhtbasement.readTemperature();

      int humid = h;
    dataString += "Humidity";
    dataString += ", ";
    dataString += String(humid);
    dataString += "%";
    int temp = t;
    dataString += ", Temprature";
    dataString += String(temp);
    dataString += "*C";
    dataString += " at";
    dataString += " ";
    File dataFile = SD.open("datalog.txt", FILE_WRITE);
    if (dataFile) {
      dataFile.println(dataString);
      dataFile.close();
    delay(1000);
    }  
}

}

// from desert home
void showMem()
{
 char Dbuf [100];
 
 strcpy_P(Dbuf,PSTR("Mem = "));
 Serial.print(Dbuf);
 Serial.println(freeMemory());
} 

void startEthernet()
{
 //Start or restart the Ethernet connection.
 client.stop();  
 delay(2000);
 if (Ethernet.begin(mac) == 0);
 else
 {
   myMac = getMacReadable();
   Serial.println("MAC: " + myMac);
   myIPAddress = getIpReadable(Ethernet.localIP());
 }
}

String getMacReadable()
{
 //Convert the mac address to a readable string
 char macstr[20];
 snprintf(macstr, 100, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
 return String(macstr);
}

String getIpReadable(IPAddress p)
{
 //Convert the ip address to a readable string
 String ip;
 for (int i =0; i < 3; i++)
 {
   ip += String(p, DEC);
   ip += ".";
 }
 ip +=String(p[3], DEC);
 return ip;
}
String getTemperatures()
{
 float t, h, t1, h1, t2, h2, t3, h3;
     h = dhtbasement.readHumidity();
     t = dhtbasement.readTemperature();
     h1 = dhttest1.readHumidity();
     t1 = dhttest1.readTemperature();
 char temp[15] = {0}; 
 dtostrf(h, 12, 3, temp);
 String xh(temp);
 xh.trim();
 
 char temp2[15] = {0};
 dtostrf(t, 12, 3, temp);
 String xt(temp);
 xt.trim();
  
 char temp3[15] = {0};
 dtostrf(t1, 12, 3, temp);
 String xt1(temp);
 xt1.trim();
 
 char temp4[15] = {0};
 dtostrf(h1, 12, 3, temp);
 String xh1(temp);
 xh1.trim();

 String temps;
 temps = "&" + gsStreamId1 + "=" + xh;   //Temp C
 temps += "&" + gsStreamId2 + "=" + xt;  //Temp F
 temps += "&" + gsStreamId3 + "=" + xt1;  //Temp F
 temps += "&" + gsStreamId4 + "=" + xh1;  //Temp F
return temps;
}

but I don't have any more to remove..?

Yes, you do. The entire String class, for starters.

PaulS: Yes, you do. The entire String class, for starters.

I'm sorry I have worked with this al day so I'm a bit tierd, the code got verified now.

Thanks alot guys for the help!

patrik93: I finally made it and now are my code is to big... Any tips on how I can reduce the code to make it "smaller"?

  1. Avoid using the "String" class and use C-strings (nullterminated strings) only (use one string buffer for as many string operations as you can, don't create dozens of strings)
  2. Avoid using "float" and use integer math only. (you will need to throw out your DHT library and need some optimized routines)

Removing the last 262 bytes is probably futile anyway. All that means is that you are down to the maximujm size, which implies that you can upload the programme but it offers you no guarantee that you can run it, and it probably won't for long. So your real objective is to keep it down to about 30,000

Getting rid of the String stuff is vital but I believe that doing so will simply put off the inevitable. What you need to do is come to terms with the fact that, if you want to do any serious datalogging, a Uno is not up to the job and you fix that by getting as Mega.

Further, you are likely to want to add something next week, Arduinos are rather like that, and that is the time when you will realise what a fool's game it is trying to squeeze it all into a Uno.

I don't understand why there is all that String in there anyway, you already seem to be using dtostrf( and maybe there is some redundancy going on. In all cases where you are defining variables as Strings, I believe it is common to use char, but I don't know if that helps much, and you real problem is probably that you have the wrong Arduino.