-
• #2
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
-
• #3
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.
-
• #4
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?
-
• #5
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).
-
• #6
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).
-
• #7
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
-
• #8
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 */);
-
• #9
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 */);
-
• #12
@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?
-
• #14
Are the seconds stored in BCD (Binary coded decimal)?
5*16 + 9 = 89
-
• #15
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.
-
• #16
Nope, that's not working very accurately.
Back to the drawing board... -
• #17
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);
-
• #18
And for setting time you'll have to encode to BCD:
parseInt(mins, 16); // etc
-
• #19
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);
-
• #20
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);
-
• #21
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...
-
• #22
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.
-
• #23
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; };
-
• #24
Looks good - there are a few bits you need though:
i2c_address
could be inC.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
withthis.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, andvar 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. -
• #25
Thanks for beating me to it :)
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.