JeeLabs Precision RTC Module

Posted on
Page
of 2
/ 2
Next
  • I have a precision RTC module from JeeLabs knocking about that I want to use for some date time stuff.
    http://jeelabs.net/projects/hardware/wiki/Precision_RTC_Plug

    I'm guessing a module has to be written, so can someone point me in the right direction.

  • I'd take a look at http://www.espruino.com/Writing+Modules - but IMO it's worth just trying to communicate with it (using http://www.espruino.com/I2C I guess) first before you start trying to write a module.

    Having said all that, Espruino has an RTC built-in - it just counts in seconds, but @mgg1010 has written a Date module which should get put online this week sometime. Normally it runs of the in-built oscillator which isn't too accurate, but it's dead easy to solder a watch crystal on: http://www.espruino.com/Clocks

  • I've also found an online web page which has the current date on it here - http://currentmillis.com/api/millis-since-unix-epoch.php - I'm using this with the Wiznet Ethernet adaptor to set the time.

  • I had seen the post about clocks. As I had the module it I thought I may as well use it as I don't have a crystal to solder onto the Espruino.

    I setup the I2C

    I2C1.setup({scl:B6,sda:B7}); 
    

    Then tried

    I2C1.readFrom(0x68,1)
    

    The interpreter returns

    INTERNAL ERROR: Timeout on I2C Read Receive Mode
    INTERNAL ERROR: Timeout on I2C Read Receive
    INTERNAL ERROR: Timeout on I2C Read STOP
    

    What am I doing wrong?

  • Have you got 2x pullup resistors on each I2C line? Also, it's possible that you have the address in 8 bit format (in which case you'll have to shift 0x68 right by one bit).

  • Nice thing about these modules is that they have a tiny battery, you can't really go without it if you need a time and date (instead of a counter).

  • Yes have pullup resistors on the I2C lines.

    I think its my understanding thats lacking here. Let me see if what I am assuming is correct.

    So the wiki for the RTC module indicates that the DS3231 default I2C address is 0x68. There is no reason this should be different and I have no other devices on the bus.

    The DS3231 registers run from BIT0 to BIT7.

    So if I want to read 'seconds' for instance am I right in understanding that I need to calculate the exact location by adding the I2C address (0x68) + the register address (00h) and then read 4 Bits?
    I assume this then returns those 4 bits that then need to be converted into an integer.


    1 Attachment

  • The register address is usually something different to the I2C address. I could be wrong here, but I think you need:

    i2c.writeTo(0x68, 0/* register addr*/);
    var data = i2c.readFrom(0x68, 4/* bytes */);
    
  • Thanks Gordon, I'm one step close. I'm now getting data back.

    I2C1.setup({scl:B6,sda:B7});
    I2C1.writeTo(0x68, 0/* register addr*/);
    var data = I2C1.readFrom(0x68, 4/* bytes */);
    
  • @graf - you can if you have ethernet, because you can get the time from the internet on device startup. The combination of extra clock crystal and my date module seems to work for me :)

  • @mgg1010 that's assuming you always have a working internet connection at startup and you want to expose your device to the internet. Both assumptions are not an option if you ask me, but I guess that depends on the project. Thanks for the module BTW :)

  • @mgg1010 - for some applications there is no internet however if there is then I agree that the other method with the crystal would be better.

    Okay back to this little module:

    The data I get back if I ask for 2 bytes starting at address 0 seems to come back okay to give me the seconds and minutes into an array, however for some reason it appears that minutes are counted in 90 seconds intervals rather than 60.

    i.e. the seconds count up to 89 then the minute is incremented by one and the seconds restart from 0.

    Why is this?

  • @graf - OK !

  • Are the seconds stored in BCD (Binary coded decimal)?

    5*16 + 9 = 89
    
  • Well I think I may have it working!
    Only reading the time off it at the moment. Need to figure out how to set the date and the time.

    The following code appears to be reading the time okay.

    I2C1.setup({scl:B6,sda:B7});
    var rtcTime = '';
    
    
    function time() {
      I2C1.writeTo(0x68/* I2C slave address*/, 0/* register addr*/);
      var data = I2C1.readFrom(0x68, 3/* bytes */);
      var seconds = (data[0] >> 1).toString();
      var minutes = (data[1] >> 1).toString();
      var hours = (data[2] >> 1).toString();
      
      if (hours < 10) {
          hours = ("0" + hours);
      }
    
      if (minutes < 10) {
          minutes = ("0" + minutes);
      }
      
      if (seconds < 10) {
          seconds = ("0" + seconds);
      }
      
      rtcTime = (hours+":"+minutes+":"+seconds);
      return;
    }
    
    setInterval(time,500);
    

    I just need to leave it running a while to see if its keeping the right time.

  • Nope, that's not working very accurately.
    Back to the drawing board...

  • You don't seem to be decoding the BCD in the code above... What about:

    var seconds = ("0"+data[0].toString(16)).substr(-2);
    var minutes = ("0"+data[1].toString(16)).substr(-2);
    var hours = ("0"+data[2].toString(16)).substr(-2);
    rtcTime = (hours+":"+minutes+":"+seconds);
    
  • And for setting time you'll have to encode to BCD:

    parseInt(mins, 16);
    // etc
    
  • Thats better!

    I2C1.setup({scl:B6,sda:B7});
    var rtcTime = '';
    
    function time() {
      I2C1.writeTo(0x68/* I2C slave address*/, 0/* register addr*/);
      var data = I2C1.readFrom(0x68, 3/* bytes */);
      var seconds = ("0"+data[0].toString(16)).substr(-2);
      var minutes = ("0"+data[1].toString(16)).substr(-2);
      var hours = ("0"+data[2].toString(16)).substr(-2);
      rtcTime = (hours+":"+minutes+":"+seconds);
      print(text,data);  
      print(text,rtcTime);
      return;
    }
    
    setInterval(time, 10000);
    
  • Starting to get to grips with this now.
    It's probably not very elegant but the following seems to be working from what I can see. Any comments greatly appreciated good or bad.

    I2C1.setup({scl:B6,sda:B7});
    var rtcTime = '';
    var rtcDate = '';
    var rtcDateTime = '';
    var dstStatus = '';
    var dstShift = 1;
    i2c_address = 0x68;
    
    function readDateTime() {
      I2C1.writeTo(i2c_address, 0x00/* address*/);
      var data = I2C1.readFrom(i2c_address, 7/* bytes */); //read number of bytes from address
      var seconds = bcd2dec(data[0]);
      var minutes = (bcd2dec(data[1]));
      var hours = (data[2]);
      var dow = (bcd2dec(data[3]));
      var date = (bcd2dec(data[4]));
      var month = (bcd2dec(data[5]));
      var year = (bcd2dec(data[6]));
    
      if ((isDST(date,month,dow)) === true) {
        hours = bcd2dec((hours)+(dstShift));
        dstStatus = true;
      }
      else {
          hours = bcd2dec(hours);
        dstStatus = false;
      }
    
      rtcDate = date+"/"+month+"/"+year;
      rtcTime = hours+":"+minutes+":"+seconds;
      rtcDateTime = rtcDate+" "+rtcTime;
      return;
    }
    
    function setDate(date,month,year) {
      I2C1.writeTo(i2c_address,[0x04, (dec2bcd(date))]);
      I2C1.writeTo(i2c_address,[0x05, (dec2bcd(month))]);
      I2C1.writeTo(i2c_address,[0x06, (dec2bcd(year))]);
    }
    
    function setTime(hour,minute) {
      I2C1.writeTo(i2c_address,[0x00, 0]);
      I2C1.writeTo(i2c_address,[0x01, (dec2bcd(minute))]);
      if (dstStatus === true) {
        I2C1.writeTo(i2c_address,[0x02, (dec2bcd(hour-1))]);
      }
      else
        I2C1.writeTo(i2c_address,[0x02, (dec2bcd(hour))]);
    }
    
    function setDay(val) {
      switch (val.toString) {
        case "Monday":
          I2C1.writeTo(i2c_address,[0x03, (dec2bcd(1))]);
          break;
        case "Tuesday":
          I2C1.writeTo(i2c_address,[0x03, (dec2bcd(2))]);
          break;
        case "Wednesday":
          I2C1.writeTo(i2c_address,[0x03, (dec2bcd(3))]);
          break;
        case "Thursday":
          I2C1.writeTo(i2c_address,[0x03, (dec2bcd(4))]);
          break;
        case "Friday":
          I2C1.writeTo(i2c_address,[0x03, (dec2bcd(5))]);
          break;
        case "Saturday":
          I2C1.writeTo(i2c_address,[0x03, (dec2bcd(6))]);
          break;
        case "Sunday":
          I2C1.writeTo(i2c_address,[0x03, (dec2bcd(7))]);
          break;
        default:
          print(text,"Not a valid day");
        return;
      }
    }
    
    //DST
    function isDST(date,month,dow)
        {
            //January, february, and december are out.
            if (month < 3 || month > 11) {
    		return false;
    		}
            //April to October are in
            if (month > 3 && month < 11) {
    		return true;
    		}
    		var previousSunday = day - dow;
            //In march, we are DST if our previous sunday was on or after the 8th.
            if (month == 3) {
    		return previousSunday >= 8;
    		}
            //In november we must be before the first sunday to be dst.
            //That means the previous sunday must be before the 1st.
            return previousSunday <= 0;
        }
    
    
    // Convert Decimal value to BCD
    function dec2bcd(val) {
      return parseInt(val, 16);
    }
    
    // Convert BCD value to decimal
    function bcd2dec(val) {
      return ("0"+val.toString(16)).substr(-2);
    }
    
    setInterval(readDateTime, 500);
    
  • Looks good! Just a suggestion but instead of:

    switch (val.toString) {
        case "Monday":
          I2C1.writeTo(i2c_address,[0x03, (dec2bcd(1))]);
          break;
        case "Tuesday":
          I2C1.writeTo(i2c_address,[0x03, (dec2bcd(2))]);
          break;
        case "Wednesday":
          I2C1.writeTo(i2c_address,[0x03, (dec2bcd(3))]);
          break;
        case "Thursday":
          I2C1.writeTo(i2c_address,[0x03, (dec2bcd(4))]);
          break;
        case "Friday":
          I2C1.writeTo(i2c_address,[0x03, (dec2bcd(5))]);
          break;
        case "Saturday":
          I2C1.writeTo(i2c_address,[0x03, (dec2bcd(6))]);
          break;
        case "Sunday":
          I2C1.writeTo(i2c_address,[0x03, (dec2bcd(7))]);
          break;
        default:
          print(text,"Not a valid day");
        return;
      }
    

    What about:

     var days = ["Monday","Tuesday",...];
     var idx = days.indexOf(val);
      if (idx<0) {
        print("Not a valid day");
      } else {
       I2C1.writeTo(i2c_address,[0x03, (dec2bcd(1+idx))]);
     }
    }
    

    It's maybe a bit more compact...

  • Thanks Gordon I will give that a go!

    As you can tell I'm a bit of a programming novice but with a bit of perseverance I'm getting there.

    Its still pretty basic but my next task is to migrate the code into a module before adding more features.

  • I'm not sure I have got the grasp of these modules.

    How does something like this look?

    /* Copyright (c) 2014 Your Name. See the file LICENSE for copying permission. */
    /*
    Quick description of my module...
    */
    
    //private
    var C = {
      dstStatus : ,          // description
      rtcDate: ,     // description
      rtcTime: ,  // description
    };
    
    //private functions
    // Convert Decimal value to BCD
    function dec2bcd(val) {
      return parseInt(val, 16);
    }
    
    // Convert BCD value to decimal
    function bcd2dec(val) {
      return ("0"+val.toString(16)).substr(-2);
    }
    
    //DST
    function isDST(date,month,dow)
        {
            //January, february, and december are out.
            if (month < 3 || month > 11) {
        return false;
        }
            //April to October are in
            if (month > 3 && month < 11) {
        return true;
        }
        var previousSunday = day - dow;
            //In march, we are DST if our previous sunday was on or after the 8th.
            if (month == 3) {
        return previousSunday >= 8;
        }
            //In november we must be before the first sunday to be dst.
            //That means the previous sunday must be before the 1st.
            return previousSunday <= 0;
        }
    
    //public
    DS3231.prototype.C = {
      i2c_address = 0x68,
      rtcTime : ,          // description
      rtcDate : ,     // description
      rtcDay : ,  // description
      dstShift :
    };
    
    //public functions
    /* Put most of my comments outside the functions... */
    DS3231.prototype.setDate = function(date,month,year) {
      I2C1.writeTo(i2c_address,[0x04, (dec2bcd(date))]);
      I2C1.writeTo(i2c_address,[0x05, (dec2bcd(month))]);
      I2C1.writeTo(i2c_address,[0x06, (dec2bcd(year))]);
    }
    
    DS3231.prototype.setTime = function(hour,minute) {
      I2C1.writeTo(i2c_address,[0x00, 0]);
      I2C1.writeTo(i2c_address,[0x01, (dec2bcd(minute))]);
      if (dstStatus === true) {
        I2C1.writeTo(i2c_address,[0x02, (dec2bcd(hour-1))]);
      }
      else
        I2C1.writeTo(i2c_address,[0x02, (dec2bcd(hour))]);
    }
    
    DS3231.prototype.setDay = function (val) {
      var days = ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"];
      var idx = days.indexOf(val);
      if (idx<0) {
        print("Not a valid day");
      }
      else {
        I2C1.writeTo(i2c_address,[0x03, (dec2bcd(1+idx))]);
     }
    }
    
    DS3231.prototype.setDstShift = function (hours) {
      dstShift = hours;
    }
    
    DS3231.prototype.readDateTime = function () {
      I2C1.writeTo(i2c_address, 0x00/* address*/);
      var data = I2C1.readFrom(i2c_address, 7/* bytes */); //read number of bytes from address
      var seconds = bcd2dec(data[0]);
      var minutes = (bcd2dec(data[1]));
      var hours = (data[2]);
      var dow = (bcd2dec(data[3]));
      var date = (bcd2dec(data[4]));
      var month = (bcd2dec(data[5]));
      var year = (bcd2dec(data[6]));
    
      if ((isDST(date,month,dow)) === true) {
        hours = bcd2dec((hours)+(dstShift));
        dstStatus = true;
      }
      else {
          hours = bcd2dec(hours);
        dstStatus = false;
      }
    
      rtcDate = date+"/"+month+"/"+year;
      rtcTime = hours+":"+minutes+":"+seconds;
      rtcDateTime = rtcDate+" "+rtcTime;
      rtcDay = dow;
      return rtcDateTime;
    };
    
  • Looks good - there are a few bits you need though:

    • i2c_address could be in C.i2c_address as it's just a constant
    • You need to fill in the values in the C structures, but I guess this is kind of a work in progress?
    • To define a constructor function up the top: function DS3231(_i2c) { this.i2c = _i2c; }
    • Replace every occurrance of I2C1 with this.i2c
    • You need to export it at the end. With Espruino I generally export a function called connect, which takes the device name that you attach it to. For example: exports.connect = function(i2c) { return new DS3231(i2c); }

    Then to use it, you just do var rtc = require("DS3231").connect(I2C1)

    To test it, I'd just put exports = {}; right at the top, and var rtc = exports.connect(I2C1) at the bottom instead of the require. Then, you can develop it all on the right-hand side of the Web IDE.

  • Thanks for beating me to it :)

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

JeeLabs Precision RTC Module

Posted by Avatar for StuntMonkeh @StuntMonkeh

Actions