'**************************************************************** '* Name : RTC_Serial.BAS * '* Author : Rob Faludi with code by Holoubek, Schimmel & others* '* Version : 2.1 * '* Notes : http://www.faludi.com * '* : * '**************************************************************** DEFINE OSC 20 Include "modedefs.bas" ' Set up Debug Out DEFINE DEBUG_REG PORTC DEFINE DEBUG_BIT 4 DEFINE DEBUG_BAUD 9600 DEFINE DEBUG_MODE 0 TRISB = %11100010 'set vars for clock runMode 'RTC pins SDA var PORTD.0 ' I2C data pin SCL var PORTD.1 ' I2C clock pin SQW var PORTC.3 RTCSec var byte ' Seconds RTCMin var byte ' Minutes RTCHour var byte ' Hours RTCWDay var byte ' Weekday RTCDay var byte ' Day RTCMonth var byte ' Months RTCYear var byte ' Year RTCCtrl var byte ' Control 'conversion Variables DecimalYears var byte DecimalMonths var byte DecimalDays var byte DecimalWeekdays var byte DecimalHours var byte Decimal12Hours var Byte amPm var bit DecimalMinutes var byte DecimalSeconds var byte address var byte dataHours var byte dataMinutes var byte dataSeconds var byte currentMode var bit currentPAN var word currentAddr VAR WORD currentDestination VAR WORD nistString var byte[48] nistYear VAR BYTE nistMonth var byte nistDay var byte nistHour var byte nistMinute var byte nistSecond var byte nistDST var byte dayTimeSuccess var BIT offset VAR BYTE lastDayTimeCheck VAR BYTE serMode var WORD serMode = 84 'inverted: 16468 ttl: 84 'end vars for clock runMode 'serial var 'Define serial output pin TX var portc.6 RX var portc.7 'end serial var 'test light var testLight var PORTB.7 i var byte 'end test light var GOSUB blinkTwice GOSUB setupXbee currentDestination = $FFFF GOSUB setDestination GOSUB subgetTime GOSUB subDayTime currentMode=1 disable debug 'main loop main: if decimalMinutes == 59 && Decimalseconds == 59 then currentMode = 0 currentPAN = $C GOSUB setPAN gosub subgetTime GOSUB showTime currentMode = 1 currentPAN = $CC gosub setPAN ELSE GOSUB subgetTime GOSUB showTime Endif if lastDayTimeCheck != DecimalHours THEN GOSUB subDayTime ENDIF DEBUG "*20",DEC2 DecimalYears,DEC2 DecimalMonths,_ DEC2 DecimalDays,DEC2 DecimalHours,DEC2 DecimalMinutes,_ DEC2 DecimalSeconds,10,13 goto main 'end main loop subDayTime: debug "daytime start" 'change to XPort PAN & Address currentPAN = $7777 GOSUB setPAN currentAddr = $0 GOSUB setAddr currentDestination = $1 GOSUB setDestination 'open connection to NIST on port 13 'read time into string GOSUB getDayTime if dayTimeSuccess == 1 then debug "daytime gotten" 'parse string into time variables & set zone for daylight savings GOSUB parseDayTime 'set clock GOSUB subSetClock ELSE DEBUG "daytime failed" endif lastDayTimeCheck = DecimalHours 'change everything back to regular mode currentPAN = $CC GOSUB setPAN currentAddr = $1 GOSUB setAddr currentDestination = $FFFF GOSUB setDestination RETURN subgetTime: I2CRead SDA,SCL,$D0,$00,[RTCSec,RTCMin,RTCHour,RTCWDay,RTCDay,RTCMonth,RTCYear,RTCCtrl] gosub bcdtodec return bcdtodec: 'Convert BCD to decimal... assume variable RTCSeconds holds the time in BCD format... 'Years DecimalYears=RTCYear & $F0 DecimalYears=DecimalYears>>4 DecimalYears=DecimalYears*10 DecimalYears=DecimalYears+(RTCYear & $0F) 'Months DecimalMonths=RTCMonth & $10 DecimalMonths=DecimalMonths>>4 DecimalMonths=DecimalMonths*10 DecimalMonths=DecimalMonths+(RTCMonth & $0F) 'Days DecimalDays=RTCDay & $30 DecimalDays=DecimalDays>>4 DecimalDays=DecimalDays*10 DecimalDays=DecimalDays+(RTCDay & $0F) 'Day of Weeks DecimalWeekdays = (RTCWDay & $0F) 'Hours DecimalHours=RTCHour & $30 DecimalHours=DecimalHours>>4 DecimalHours=DecimalHours*10 DecimalHours=DecimalHours+(RTCHour & $0F) 'convert from 24 hr time to 12 if (DecimalHours >= 13) then Decimal12Hours = Decimal12Hours - 12 amPm = 1 else Decimal12Hours = Decimal12Hours amPm = 0 endif 'Minutes DecimalMinutes=RTCMin & $70 DecimalMinutes=DecimalMinutes>>4 DecimalMinutes=DecimalMinutes*10 DecimalMinutes=DecimalMinutes+(RTCMin & $0F) 'Seconds DecimalSeconds=RTCsec & $70 DecimalSeconds=DecimalSeconds>>4 DecimalSeconds=DecimalSeconds*10 DecimalSeconds=DecimalSeconds+(RTCSec & $0F) RTCHour=(nistHour + 24 -offset) DIG 1 RTCHour=RTCHour<<4 RTCHour=RTCHour+((nistHour + 24 -offset) DIG 0) return 'convert decimals to BCD to send to clock chip decimaltobcdLOW: RTCSec=nistSecond DIG 1 RTCsec=RTCSec<<4 RTCSec=RTCSec+(nistSecond DIG 0) RTCMin=nistMinute DIG 1 RTCMin=RTCMin<<4 RTCMin=RTCMin+(nistMinute DIG 0) return decimaltobcdHIGH: RTCHour=(nistHour-offset) DIG 1 RTCHour=RTCHour<<4 RTCHour=RTCHour+((nistHour-offset) DIG 0) RTCDay=nistDay DIG 1 RTCDay=RTCDay<<4 RTCDay=RTCDay+(nistDay DIG 0) RTCMonth=nistMonth DIG 1 RTCMonth=RTCMonth<<4 RTCMonth=RTCMonth+(nistMonth DIG 0) RTCYear=nistYear DIG 1 RTCYear=RTCYear<<4 RTCYear=RTCYear+(nistYear DIG 0) return showTime: SELECT CASE currentMode CASE IS == 0 GOSUB outputWeekday Serout2 TX,serMode,[", "] GOSUB outputMonth Serout2 TX,serMode,[" ", DEC2 DecimalDays,", 20",DEC2 DecimalYears] SEROUT2 TX,serMode,[" ",dec2 DecimalHours, ":",dec2 DecimalMinutes,":",_ dec2 DecimalSeconds,10,13] CASE IS == 1 Serout2 TX,serMode,["*20",DEC2 DecimalYears,DEC2 DecimalMonths,_ DEC2 DecimalDays,DEC2 DecimalHours,DEC2 DecimalMinutes,_ DEC2 DecimalSeconds,10,13] END SELECT RETURN subsetClock: 'Write command ' RTCSec = $00 ' Seconds ' RTCMin = $31 ' Minutes ' RTCHour = $12 ' Hours ' RTCWDay=$04 ' Weekday ' RTCDay=$01 ' Day ' RTCMonth=$11 ' Months ' RTCYear=$06 ' Year ' RTCCtrl=$10 ' Control preset to output 1 second 'Tick' on SQWpin I2CWrite SDA,SCL,$D0,$00,[RTCSec,RTCMin,RTCHour,RTCWDay,RTCDay,RTCMonth,RTCYear,RTCCtrl] Pause 1000 return outputMonth: select case DecimalMonths case 1 Serout2 TX,serMode,["January"] case 2 Serout2 TX,serMode,["February"] case 3 Serout2 TX,serMode,["March"] case 4 Serout2 TX,serMode,["April"] case 5 Serout2 TX,serMode,["May"] case 6 Serout2 TX,serMode,["June"] case 7 Serout2 TX,serMode,["July"] case 8 Serout2 TX,serMode,["August"] case 9 Serout2 TX,serMode,["September"] case 10 Serout2 TX,serMode,["October"] case 11 Serout2 TX,serMode,["November"] case 12 Serout2 TX,serMode,["December"] end Select RETURN outputWeekday: select case DecimalWeekdays case 1 Serout2 TX,serMode,["Sunday"] case 2 Serout2 TX,serMode,["Monday"] case 3 Serout2 TX,serMode,["Tuesday"] case 4 Serout2 TX,serMode,["Wednesday"] case 5 Serout2 TX,serMode,["Thursday"] case 6 Serout2 TX,serMode,["Friday"] case 7 Serout2 TX,serMode,["Saturday"] end Select RETURN setupXbee: configure: ' label to jump back to if configuration times out ' blink status light twice on startup GOSUB blinkTwice ' for some reason it seems to help to send an arbitrary character first ' then pause for the guard time before requesting command mode serout2 portc.6, serMode, ["X"] pause 1100 ' put the XBee in command mode serout2 portc.6, serMode, ["+++"] ' wait for a response from the XBee for 2000 ms, or start ' over at the configure label if no valid response comes SERIN2 portc.7, serMode, 2000, configure, [WAIT ("OK")] ' set the Guard Time to 50 ms (same as hex 0x32) serout2 portc.6, serMode, ["ATGT32,"] ' exit command mode serout2 portc.6, serMode, ["CN",13] ' wait for a response from the XBee for 2000 ms, or start ' over at the configure label if no valid response comes SERIN2 portc.7, serMode, 2000, configure, [WAIT ("OK")] RETURN setPAN: ' label to jump back to if configuration times out ' pause for the guard time before requesting command mode pause 65 ' put the XBee in command mode serout2 portc.6, serMode, ["+++"] ' wait for a response from the XBee for 2000 ms, or start ' over at the configure label if no valid response comes SERIN2 portc.7, serMode, 2000, setPAN, [WAIT ("OK")] ' set the PAN (personal area network) ID number serout2 portc.6, serMode, ["ATID",HEX currentPAN,","] ' exit command mode serout2 portc.6, serMode, ["CN",13] ' wait for a response from the XBee for 2000 ms, or start ' over at the configure label if no valid response comes SERIN2 portc.7, serMode, 2000, setPAN, [WAIT ("OK")] RETURN setAddr: ' pause for the guard time before requesting command mode pause 65 ' put the XBee in command mode serout2 portc.6, serMode, ["+++"] ' wait for a response from the XBee for 2000 ms, or start ' over at the configure label if no valid response comes SERIN2 portc.7, serMode, 2000, setAddr, [WAIT ("OK")] ' set the local address serout2 portc.6, serMode, ["ATMY",HEX currentAddr,","] ' exit command mode serout2 portc.6, serMode, ["CN",13] ' wait for a response from the XBee for 2000 ms, or start ' over at the configure label if no valid response comes SERIN2 portc.7, serMode, 2000, setAddr, [WAIT ("OK")] return setDestination: ' pause for the guard time before requesting command mode pause 65 ' put the XBee in command mode serout2 portc.6, serMode, ["+++"] ' wait for a response from the XBee for 2000 ms, or start ' over at the configure label if no valid response comes SERIN2 portc.7, serMode, 2000, setDestination, [WAIT ("OK")] ' set the local address serout2 portc.6, serMode, ["ATDH0,DL",HEX currentDestination,","] ' exit command mode serout2 portc.6, serMode, ["CN",13] ' wait for a response from the XBee for 2000 ms, or start ' over at the configure label if no valid response comes SERIN2 portc.7, serMode, 2000, setDestination, [WAIT ("OK")] RETURN getDayTime: 'send connection string to xport 'time.nist.gov on port 13 (more info at http://tf.nist.gov/service/its.htm) serout2 portc.6, serMode, ["C192.43.244.18/13",13] debug " sent connection request... " 'check for C and fail if you don't get it SERIN2 portc.7, serMode, 2000, dayTimeFail, [WAIT ("C")] 'get and parse the time string from NIST 'for some reason, the RTC expects these values in the 'HEX version of decimal numbers. Weird, but it works. SERIN2 portc.7, serMode, 10000, dayTimeFail, [WAIT (" "), dec2 nistYear] SERIN2 portc.7, serMode, 2000, dayTimeFail, [WAIT ("-"), dec2 nistMonth] SERIN2 portc.7, serMode, 2000, dayTimeFail, [WAIT ("-"), dec2 nistDay] SERIN2 portc.7, serMode, 2000, dayTimeFail, [WAIT (" "), dec2 nistHour] SERIN2 portc.7, serMode, 2000, dayTimeFail, [WAIT (":"), dec2 nistMinute] SERIN2 portc.7, serMode, 2000, dayTimeFail, [WAIT (":"), dec2 nistSecond] SERIN2 portc.7, serMode, 2000, dayTimeFail, [WAIT (" "), dec2 nistDST] debug " year:" debug dec nistYear debug " month:" debug dec nistMonth debug " day:" debug dec nistDay debug " hour:" debug dec nistHour debug " minute:" debug dec nistMinute debug " second:" debug dec nistSecond debug " DST:" debug dec nistDST dayTimeSuccess = 1 ' if we made it through this code then things worked dayTimeDone: ' we'll end up here if things failed RETURN dayTimeFail: ' this code will run on daytime lookup failure dayTimeSuccess = 0 GOSUB blinkTwice GOSUB blinkTwice GOSUB blinkTwice GOTO dayTimeDone RETURN parseDayTime: ' this function parses UTC into Eastern Time with Daylight Savings if needed IF nistDST > 51 || nistDST == 0 || nistDST == 1 THEN offset = 5 'standard time ELSE '(if nistDST < 50 && nistDST > 1) || nistDST==50 || nistDST==51 then offset = 4 'daylight time ENDIF IF (nistHour + 24 - offset) < 24 THEN gosub decimaltobcdLOW ' to simplify the code, prevent having to subtractively ' calculate hour, day, month and year ' at times when UTC of these is later than local ELSE gosub decimaltobcdLOW gosub decimaltobcdHIGH ENDIF RETURN blink: HIGH testLight pause 250 Low testLight Pause 250 RETURN blinkTwice: 'testlight for i = 1 to 2 HIGH testLight pause 50 Low testLight Pause 250 next i 'end testlight RETURN