18B20 Data logging code getting too large for UNO

Hi All,

Thanks for a fantastic forum (I have searched extensively to get from nowhere to this far) all happy and working except one problem that I need some expert advice on.

I can explain the requirement I have in detail if necessary, but in summary I am building a UNO & ethernet shield data logger for 20+ DS18B20s. All the functionality I need is almost in the code and working.

The next stage is to add to the code some basic monitoring LEDs, push-buttons to alter the Sample interval and make the data displayed on the web page a bit tidier

However my problem is that I am running out of room for my code. As I approach 28k bytes, weird things start happening at run time.

Could anyone give me some suggestions how I can reduce the size of the code whilst keeping the functionality I currently have? Interestingly the code and this message also exceeds the max size for forum posts, so I have attached as instead. I must be doing something wrong :frowning:

thanks

You must, there is no attachment.

28K flash image should not be a problem. You may be diagnosing one bug as another.

Be aware that large code doesn't find as many helpers with time for it, but still have hope.

Weird - I’m sure I attached… here it is

#include <OneWire.h>
#include <DallasTemperature.h>
#include <SD.h>
#include <Time.h>
#include <SPI.h>
#include <Ethernet.h>

//SD CARD CONFIG
const int chipSelect = 4;                                  //On the Ethernet Shield, CS is pin 4

//ONEWIRE CONFIG
#define ONE_WIRE_BUS 8                                    // One wire on pin 8
#define TEMPERATURE_PRECISION 10
#define NUMBER_OF_SENSORS 5                               // number of sensors to size the array correctly - MAX will be 25 

OneWire oneWire(ONE_WIRE_BUS);                            // Tell the one wire library where the bus is. 
DallasTemperature sensors(&oneWire);                      // Tell Dallas where the sensors are
DeviceAddress thermometer[NUMBER_OF_SENSORS];             // array to hold device addresses

//SAMPLE TIME CONFIG
unsigned long writeTime = 0;                              // Timestamp for sample writes                
const int interval = 30;                                  // SET SAMPLE FREQUENCY HERE - in seconds. 

//ETHERNET CONFIG
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };                   // Old Shield - anything will do here as long as it doesn’t clash
IPAddress ip(192,168,0,16);                              // The IP I want to use
EthernetServer server(80);                      

//LETS GET GOING
void setup(void)
{
  // start serial port
  Serial.begin(9600);

  // Start up the Sensors
  sensors.begin();

  // locate devices on the bus put in array, print, and set precision
  Serial.print(sensors.getDeviceCount(), DEC);          // Get devices - CHECK with NUMBER_OF_SENSORS
  Serial.println(" devices...");                        
  oneWire.reset_search();                               // Must be called before search() - Not sure why

  //  
  for (byte i=0; i < NUMBER_OF_SENSORS; i++)            // assigns the address at each index into array 
  {
    if (!oneWire.search(thermometer[i])) {
      Serial.println("Unable to find address");        // Missing Sensor or Array too large .
      return;                                          // STOP - were not going further WILL NEED LED BLINK FOR PROD
    }
    Serial.print("Device Address: ");
    printAddress(thermometer[i]);                      // While were here lets print the address
    Serial.println();
    sensors.setResolution(thermometer[i],              // While were here#2 lets set the precision for the sensors
    TEMPERATURE_PRECISION); 
  }

  // Start up the SD Card
  Serial.print("Initializing SD card...");
  pinMode(10, OUTPUT);                               // pin 10 has to be set to OUPUT (Arduino rules) 

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    return;                                         // STOP - were not going further WILL NEED LED BLINK FOR PROD
  }
  Serial.println("card initialized.");

  // start up  the Ethernet connection and the server:
  Serial.print("Initializing Network card...");
  Ethernet.begin(mac, ip);
  server.begin();
  delay(1000);
  Serial.print(" IP Address =");
  Serial.println(Ethernet.localIP());  
}

// FUNCTION to print a device address
void printAddress(DeviceAddress deviceAddress)
{
  Serial.print("Address:");
  for (uint8_t i = 0; i < 8; i++)
  {
    if (deviceAddress[i] < 16) Serial.print("0");        // zero pad the address if necessary
    Serial.print(deviceAddress[i], HEX);
  }
  Serial.print(" ");
}

// FUNCTION to print the temperature
void printTemperature(DeviceAddress deviceAddress)
{
  float tempC = sensors.getTempC(deviceAddress);
  Serial.print("Temp:");
  Serial.print(tempC);
  Serial.print(" ");
}

// FUNCTION print a sample to Serial
void printData(DeviceAddress deviceAddress, int eolCheck)
{
  printAddress(deviceAddress);
  printTemperature(deviceAddress);
  if (eolCheck == (NUMBER_OF_SENSORS-1)) {           // check if this is the last sensor, then print a timestamp & newline
    Serial.print(writeTime);
    Serial.println();
  }
}

// FUNCTION to write a sample to SD card
void writeData(DeviceAddress deviceAddress, int eolCheck)
{
  File dataFile = SD.open("datalog.txt", FILE_WRITE);   // open a file
  if (dataFile) {                                       // if the file is available, write to it:
    writeAddress(deviceAddress, dataFile);
    writeTemperature( deviceAddress, dataFile);
    if (eolCheck == (NUMBER_OF_SENSORS-1)) {            // check if this is the last sensor, then print a timestamp & newline
      dataFile.print(writeTime);
      dataFile.println();
    }
    dataFile.close();
  }  
  else {
    Serial.println("write error");                      // if the file isn't open, pop up an error:
  }
} 

// FUNCTION to write the temperature to SD card
void writeTemperature(DeviceAddress deviceAddress, File dataFile)
{
  float tempC = sensors.getTempC(deviceAddress);
  dataFile.print("Temp:");
  dataFile.print(tempC);
  dataFile.print(" ");
}

// FUNCTION to write a device address to SD card
void writeAddress(DeviceAddress deviceAddress, File dataFile)
{
  dataFile.print("Address:");
  for (byte i = 0; i < 8; i++)
  {
    if (deviceAddress[i] < 16) dataFile.print("0");       // zero pad the address if necessary
    dataFile.print(deviceAddress[i], HEX);
  }
  dataFile.print(" ");
}

// FUNCTION to publish sample to web client
void publishData()
{
  EthernetClient client = server.available();            // listen for incoming clients
  if (client) {
    boolean currentLineIsBlank = true;                   // an http request ends with a blank line
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (c == '\n' && currentLineIsBlank) {           // if newline AND blank line the http request has ended,
          client.println();
          // send web page
          client.println("<!DOCTYPE html>");
          client.println("<html>");
          client.println("<head>");
          client.println("<title>Arduino Temp Data Acquisition </title>");
          client.print("<meta http-equiv=\"refresh\" content=\"10\">");
          client.println("</head>");
          client.println("<body>");
          for (byte i=0; i < NUMBER_OF_SENSORS; i++) {   
            client.print (i);
            client.print(" ");
            /*     for (uint8_t j = 0; j < 8; j++)
             {
             // zero pad the address if necessary
             if (deviceAddress[j] < 16) Serial.print("0");
             client.print(deviceAddress[i], HEX);
             }
             */
            client.print(" ");
            client.println (sensors.getTempC(thermometer[i]));
            client.print(" | ");
          }
          client.println (writeTime);
          client.println("</body>");
          client.println("</html>");
          break;
        }
        if (c == '\n') {
          currentLineIsBlank = true;                      // you're starting a new line
        } 
        else if (c != '\r') {
          currentLineIsBlank = false;                     // you've gotten a character on the current line
        }
      }
    }
    delay(1);                                            // give the web browser time to receive the data
    client.stop();                                       // close the connection:
  }
}

void loop(void)
{ 
  unsigned long currentTime = now();                     // get the time since power up
  if (currentTime - writeTime >= interval) {             // is it time to sample yet?
    sensors.requestTemperatures();                       // call all sensors to give  temperature 
    writeTime = currentTime;                             // save the this iterations sample  time  
    for (byte i=0; i < NUMBER_OF_SENSORS; i++) {           
      printData(thermometer[i], i);                      // print the device information COMEMENT OUT FOR PROD
      writeData(thermometer[i], i);                      // write the device information 
    }
  }
  publishData();                                        // put  outside the sample loop so that it is more responsive.
                                                        // it will use time and temp from the previous Sample
}

I had just finished a reply in the other thread on the same subject and when I went to post, the thread was gone along with all that typing.

Looking at your code, it is likely you are using too much RAM, when stack meets heap is bad.

You have 4K flash space left and maybe 1K of printed strings.

Serial.print( "This text gets copied to RAM at startup." );

Serial.print( F( "This text stays in and prints directly from flash." ));

If weird things start happenings as you said, you are most likely running out of RAM..

chaintong:

#include <OneWire.h>

#include <DallasTemperature.h>
#include <SD.h>
#include <Time.h>
#include <SPI.h>
#include <Ethernet.h>

and

As I approach 28k bytes, weird things start happening at run time.

It all seems well on the way to a typical datalogging exercise, and too big for a Uno. The weird things are in fact normal.

Essentially, Unos are not up to the job and you will be a lot better off getting a Mega now than after you have finished flogging a dead horse.

The code I saw has loads of RAM taken up in labels to print. It should be able to work once fixed.

With a different approach, even more space can be saved. Also the default SPI buffer can be reduced.

However the project itself seems to be infected with a case of featuritis which almost always requires more hardware.

There are ATtiny dataloggers since years ago but they don't do internet. An UNO is 4x a tiny85.

Thanks all, I will have a go with Serial.printF, I will also remove a lot of the serial.print to the USB line since it wont be needed (I only included it as ‘debug’).

However I think I have seen the writing on the wall. In short the UNO was a great purchase to get me going, but I failed to appreciate how small the UNO actually is !! A Mega is clearly the way forward for my future needs.

The features are all part of the requirements, which for ‘interest’ and completeness I have included below, so others can realise the limit of scope for the UNO.

The systems will be installed, dismantled and moved multiple times, by fitters. On each installation, the system will be ethernet wired. I will not install it or commission it, there will be no serial USB connection on commissioning. The fitters will hook up the temp sensors, noting the addresses and placement, when finished they will plug in the Ethernet and power , then walk away.

The sensors will be picked by the fitters from a bin of DS18B20s, so I don’t know the addresses before hand.

Once operational the systems needs to be checked real-time via Ethernet. After 1 week of monitoring the system will be dismantled, moved to the next site, and the case (containing the ardunio & Ethernet card and obviously the SD card) returned for data download from the SD card.

BTW I quickly googled reducing the SPI buffer size as also suggested but, that doesn’t look really the way forward – am I wrong here?

Buffer reduction is a possible step, it allows SPI to be used on smaller AVR's. I have an MP3 board where some of the example code uses a 34 byte SPI buffer, just big enough to burst mode. That does require some tricky code to service on time and keep the music steady.

If you only ever make few of these devices then definitely going to a larger AVR will be easier and quicker.

If you are making a product that many many will be made, consider breaking the code up to run on 2+ stand-alone 328P's or your UNO and a co-controller. One can run the HTML and the other can feed to and read from that one. If you don't want to mess with mounting chips, 328P SMT Micro boards can be gotten for $5 or less. OTOH there are clone Megas for around $10 though I can't vouch for component quality or life or that one bought one month will be the same as another the next.

I've fixed code for two people who went to MEGA2560 needlessly, but the difference is lunch money.

chaintong:
In short the UNO was a great purchase to get me going, but I failed to appreciate how small the UNO actually is

You are not the first to do this, and you can be sure that switching to a Mega is the way to go, even though adding some LEDs and buttons will not be a significant extra burden. What probably will be a significant is the extra feature you haven't thought about yet but probably will think about next week. Arduinos are rather like that. Further, deleteting all the serial stuff will not win you many prizes. You can test that right now by doing just that, and checking the memory used after compiling.