Weird instance problem developing a library

I'm making my [second] attempt to create a useful library using the Arduino IDE (0021), and I've run into a weird issue, and I'm wondering if anyone can give me any insight. Compilation seems to go OK, but depending on where I create my library object's instance, the ATmega328 I'm uploading to will crash - code in the setup() function does not run, but the chip can be reprogrammed.

Here's the code I'm using:

DS1307.h

#ifndef DS1307_H_
#define DS1307_H_

#include "WProgram.h"

// DS1307 I2C slave address
#define DS1307_I2C_ADDRESS B1101000
// DS1307 register addresses
#define DS1307_REG_SEC   0
#define DS1307_REG_MIN   1
#define DS1307_REG_HR    2
#define DS1307_REG_DOW   3 // day of week
#define DS1307_REG_DATE  4 // day of month
#define DS1307_REG_MONTH 5
#define DS1307_REG_YEAR  6
#define DS1307_REG_CTL   7


class DS1307 {
  public:
    // storage for date and time
    byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
    // methods for accessing the chip
    DS1307(void);
    void getDateTime(void);
  private:
    byte decToBcd(byte);
    byte bcdToDec(byte);
};

#endif // DS1307_H_

DS1307.cpp

#include "WProgram.h"
#include <Wire.h>
#include "DS1307.h"


// constructor
DS1307::DS1307(void) {
  Wire.begin();
  getDateTime();
} // DS1307()


// Convert normal decimal numbers to binary coded decimal
byte DS1307::decToBcd(byte val) {
  return ( (val/10*16) + (val%10) );
} // decToBcd()


// Convert binary coded decimal to normal decimal numbers
byte DS1307::bcdToDec(byte val) {
  return ( (val/16*10) + (val%16) );
} // bcdToDec()


// Gets the date and time from the ds1307 and
// stores it into the class' public variables.
void DS1307::getDateTime(void) {
  // Reset the register pointer
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(DS1307_REG_SEC);
  Wire.endTransmission();
  // now retrieve the DT
  Wire.requestFrom(DS1307_I2C_ADDRESS, 7);
  // A few of these need masks because certain bits are control bits
  second     = bcdToDec(Wire.receive() & 0x7f);
  minute     = bcdToDec(Wire.receive());
  hour       = bcdToDec(Wire.receive() & 0x3f);  // Need to change this if 12 hour am/pm
  dayOfWeek  = bcdToDec(Wire.receive());
  dayOfMonth = bcdToDec(Wire.receive());
  month      = bcdToDec(Wire.receive());
  year       = bcdToDec(Wire.receive());
  Wire.endTransmission();
} // getDateTime()

Demo_DS1307.pde

#include <Wire.h>
#include <DS1307.h>

#define VERSTR "Demo_DS1307 v0.02 by Emo Mosley on 1-6-2011"
#define PIN_HB 8

// VT100 escape sequences
#define ESCAPE 27
#define VT_CLEAR "\e[2J"
#define VT_RIGHT "\e[1C"
#define VT_ROW2 "\e[2H"
#define VT_EEOL "\e[K"

// instance of DS1307 RTC
// declaring the instance here causes the ATmega to lock up:
//DS1307 rtc;

// display refresh timing
#define DISP_POLL 250
unsigned long display_poll_dt;
char str[20];


void setup(void) {
  pinMode(PIN_HB, OUTPUT);     
  Serial.begin(19200);
  Serial.print(VT_CLEAR);
  Serial.print(VERSTR);
  digitalWrite(PIN_HB, HIGH);   // turn on the heartbeat LED on
  //rtc.getDateTime();
} // setup()


void loop(void) {
  // instance of DS1307 RTC
  // when the instance is declared here, the program works as expected,
  // but isn't this a BAD place for it?
  DS1307 rtc;

  // check display refresh timer
  if(millis() >= display_poll_dt) {
    // update the display timer
    display_poll_dt += DISP_POLL;
    // grab the current time
    rtc.getDateTime();
    // format the time into a readable format
    sprintf(str, "%2d:%02d:%02d\r\n", rtc.hour, rtc.minute, rtc.second);
    // display the time out to the serial port
    Serial.print(VT_ROW2);
    Serial.print(VT_EEOL);
    Serial.print(str);
  } // if
} // loop()

In most code I see using a library, the object is instantiated globally in the pde file shortly after its #include statement. In some cases, this happens within the include file (but I can't get it to compile that way for some reason). The only way I could get it to run is by declaring the instance at the top of the loop() function! I must be doing something wrong, but I lack the experience with OOP to troubleshoot what should be a simple thing to do.

Does anyone have any ideas?
Many thanks.
-Emo

Hello,

Here is my quick guess...
Remove getDateTime() from constructor. I'm not sure but shouldn't you 1st call something like setDateTime() and only then getDatetime() in loop or time to time? Probably what you need at setup() is setDataTime(...) at least once then call getDateTime().

Check this example: Hobby Robotics » An I2C Bus Example Using the DS1307 Real-Time Clock

And yes that's not a very nice place for instance.

flipo

Flipo,

Thanks for the reply - ironically, that link you posted is where I got the code to talk to the RTC chip using the Wire library! My pale attempt is to take that basic code and turn it into a useful library, which is where I'm running into problems.

I'll try out your recommendation - I can see where trying to use an object's method within its constructor might be a bit "rude" to the program, so I'll try moving it out of there. Still, though, the program runs perfectly fine when the instance is within loop(). I won't be able to get back to it until later tonight, but I'll post my findings.

Weirdness! :slight_smile:
-Emo

You'll notice that most libraries have a begin method - Serial.begin(), Server.begin(), Wire.begin(), etc.

There's a very good reason for that. Will your constructor be called before or after the Wire constructor? What do you mean you don't know. You better know if you are going to call Wire.begin() in your constructor.

Since you don't, and can't, know whether your constructor or the Wire constructor is going to be called first, you should not rely on a constructor having been called. All the stuff that normally would go in a constructor really should be put in the begin() method, so that when your begin method is called, you KNOW that all the other constructors have been called, so you CAN use Wire.begin().

flipo,
Guess what? I removed that getDateTime() call from the constructor and now it's running great on the AVR. Thank you for that quick insight.
:slight_smile:

PaulS,
Thank you for the advice - I'm still learning OOP along with learning AVR programming, so it's good for me to learn about how to organize the code properly. What it sounds like what I need to do is to create a begin() method and move the Wire.begin() inside of it, yes? It makes sense. The funny thing is that my constructor will be empty! I've seen other examples of empty constructors out there, so I know it's OK.

I appreciate everyone's help! Things are looking up for the project!
-Emo

What it sounds like what I need to do is to create a begin() method and move the Wire.begin() inside of it, yes?

Yes.

The funny thing is that my constructor will be empty! I've seen other examples of empty constructors out there, so I know it's OK.

There may be nothing that you need to explicitly do in the constructor. If you look at the machine code that is generated, though, the constructor is far from a trivial function.