{{
ybox2 - Alarm Clock widget
http://www.ladyada.net/make/ybox2
ABOUT
This simple (!?) program will get the current UTC time from a navy time server
and display it. You can set the time zone, alarm time and alarm mode using the webbrowser
If a password was set in the bootloader, it will be
required to change the settings.
}}
CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
clk_y_off = 2
clk_x_off = 0
pixelchar = 14
OBJ
websocket : "api_telnet_serial"
term : "TV_Text"
subsys : "subsys"
settings : "settings"
http : "http"
auth : "auth_digest"
tel : "api_telnet_serial"
random : "RealRandom"
num : "Numbers"
VAR
byte path_holder[128]
byte tv_mode
' Statistics
long stat_refreshes
long stat_errors
long clockstack[200]
long myip[4]
byte month, date, minute, hour, sec, alarming
DAT
productName BYTE "digital clock widget",0
productURL BYTE "http://www.ladyada.net/make/ybox2/",0
info_refresh_period long 60*60*12 ' in seconds
PUB init | i
dira[0]:=1 ' Set direction on reset pin
outa[0]:=0 ' Set state on reset pin to LOW
dira[subsys#SPKRPin]:=1
stat_refreshes:=0
stat_errors:=0
' Default to NTSC
tv_mode:=term#MODE_NTSC
settings.start
subsys.init
' If there is a TV mode preference in the EEPROM, load it up.
if settings.findKey(settings#MISC_TV_MODE)
tv_mode := settings.getByte(settings#MISC_TV_MODE)
' Start the TV Terminal
term.startWithMode(12,tv_mode)
term.setcolors(@palette)
term.str(string($0C,1))
term.str(@productName)
term.out(13)
term.str(@productURL)
term.out(13)
term.out($0c)
term.out(2)
repeat term#cols
term.out($90)
term.out($0c)
term.out(0)
subsys.StatusLoading
' Init the auth object with some randomness
random.start
auth.init(random.random)
random.stop
if settings.findKey(settings#MISC_STAGE2)
settings.removeKey(settings#MISC_STAGE2)
if settings.findKey(settings#MISC_SOUND_DISABLE) == FALSE
dira[subsys#SPKRPin]:=1
else
dira[subsys#SPKRPin]:=0
if NOT settings.findKey(settings#SERVER_PATH)
if NOT \initial_configuration
showMessage(string("Server configuration failed!"))
subsys.StatusFatalError
SadChirp
waitcnt(clkfreq*100000 + cnt)
reboot
outa[0]:=1 ' Pull ethernet reset pin high, ending the reset condition.
if not \tel.start(1,2,3,4,6,7)
showMessage(string("Unable to start networking!"))
subsys.StatusFatalError
SadChirp
waitcnt(clkfreq*10000 + cnt)
reboot
if settings.getData(settings#NET_MAC_ADDR,@clockstack,6)
term.str(string("MAC: "))
repeat i from 0 to 5
if i
term.out("-")
term.hex(byte[@clockstack][i],2)
term.out(13)
if NOT settings.getData(settings#NET_IPv4_ADDR,@clockstack,4)
term.str(string("IPv4 ADDR: DHCP..."))
repeat while NOT settings.getData(settings#NET_IPv4_ADDR,@clockstack,4)
if ina[subsys#BTTNPin]
reboot
delay_ms(500)
term.out($0A)
term.out($00)
term.str(string("IPv4 ADDR: "))
repeat i from 0 to 3
if i
term.out(".")
term.dec(byte[@clockstack][i])
term.out(13)
if settings.getData(settings#NET_IPv4_DNS,@clockstack,4)
term.str(string("DNS ADDR: "))
repeat i from 0 to 3
if i
term.out(".")
term.dec(byte[@clockstack][i])
term.out(13)
\initial_configuration
if settings.getData(settings#SERVER_IPv4_ADDR,@clockstack,4)
term.str(string("SERVER ADDR:"))
repeat i from 0 to 3
if i
term.out(".")
term.dec(byte[@clockstack][i])
term.out(":")
term.dec(settings.getWord(settings#SERVER_IPv4_PORT))
term.out(13)
if settings.getString(settings#SERVER_PATH,@clockstack,40)
term.str(string("SERVER PATH:'"))
term.str(@clockstack)
term.str(string("'",13))
if settings.getString(settings#SERVER_HOST,@clockstack,40)
term.str(string("SERVER HOST:'"))
term.str(@clockstack)
term.str(string("'",13))
cognew(ClockCog, @clockstack)
repeat
HappyChirp
i:=\httpServer
term.out("[")
term.dec(i)
term.out("]")
subsys.StatusFatalError
SadChirp
delay_ms(500)
websocket.closeAll
reboot
PRI initial_configuration
settings.setString(settings#SERVER_HOST,string("tycho.usno.navy.mil"))
settings.setData(settings#SERVER_IPv4_ADDR,string(199,211,133,239),4)
settings.setWord(settings#SERVER_IPv4_PORT,80)
settings.setString(settings#SERVER_PATH,string("/cgi-bin/timer.pl"))
return TRUE
PUB drawbigchar(charx, chary, bitmap) | i, j
term.out($A)
term.out(charx)
term.out($B)
term.out(chary)
repeat i from 0 to 6
'term.hex(BYTE[bitmap][i],2)
repeat j from 4 to 0
if(BYTE[bitmap][i] & (1 << j))
term.out(pixelchar)
else
term.out(32)
term.out($A)
term.out(charx)
term.out($B)
term.out(chary+i+1)
CON
CLOCK_SUCCESS = 860276
pub Alarm | i
dira[subsys#SPKRPin]:=1
repeat i from 0 to 30
outa[subsys#SPKRPin]:=!outa[subsys#SPKRPin]
delay_ms(1)
outa[subsys#SPKRPin]:=0
delay_ms(50)
pub fillscreen | i
term.out(1)
repeat i from 0 to 600
term.str(string(" "))
pub ClockCog | retrydelay,port,err, currtime, i
port := 20000
retrydelay := 1 ' Reset the retry delay
repeat
if (err:=\ClockUpdate(port)) == CLOCK_SUCCESS
retrydelay := 1 ' Reset the retry delay
subsys.StatusIdle
' print out the clock
term.str(string($C,7))
term.out(0)
term.out(1)
repeat
'subsys.Click
if ((settings.getByte(settings#TIMEZONE) < 0) or (settings.getByte(settings#TIMEZONE) > 49))
settings.setByte(settings#TIMEZONE, 24)
currtime := subsys.RTC + ((settings.getByte(settings#TIMEZONE) + 24) * 1800)
sec := currtime // 60
minute := (currtime/60) // 60
hour := (currtime / 3600) // 24
if alarming
if (sec & 1)
term.setcolors(@paletteALARM)
else
term.setcolors(@palette)
drawbigchar(0, clk_y_off, @num0+(hour/10)*7)
drawbigchar(6, clk_y_off, @num0+(hour//10)*7)
drawbigchar(14, clk_y_off, @num0+(minute/10)*7)
drawbigchar(20, clk_y_off, @num0+(minute//10)*7)
drawbigchar(28, clk_y_off, @num0+(sec/10)*7)
drawbigchar(34, clk_y_off, @num0+(sec//10)*7)
if (settings.getByte(settings#ALARM_ON))
term.str(string($A, 26, $B, 12, "Alarm @ "))
i := settings.getByte(settings#ALARM_HOUR)
term.dec(i/10)
term.dec(i//10)
term.str(string(":"))
i := settings.getByte(settings#ALARM_MIN)
term.dec(i/10)
term.dec(i//10)
' now check if we're ready to alarm!
if ((settings.getByte(settings#ALARM_HOUR) == hour) and (settings.getByte(settings#ALARM_MIN) == minute))
alarming := 1
else
{
if (alarming)
term.str(string($C, 7))
term.out(0)
}
alarming := 0
else
term.str(string($A, 26, $B, 12, " "))
'term.str(string("Time zone: "))
'term.dec(settings.getByte(settings#TIMEZONE))
settings.getData(settings#NET_IPv4_ADDR,@myip,4)
term.str(string($A, 1, $B, 12, "IP: "))
repeat i from 0 to 3
if i
term.out(".")
term.dec(byte[@myip][i])
repeat
if alarming
Alarm
while currtime == subsys.RTC + ((settings.getByte(settings#TIMEZONE) + 24) * 1800)
term.str(string($B,12))
term.dec(subsys.RTC) ' Print out the RTC value
term.out(" ")
tel.close
delay_s(info_refresh_period) ' 30 sec delay
else
if err>0
subsys.StatusErrorCode(err)
stat_errors++
showMessage(string("Error!"))
tel.closeall
websocket.closeall
if retrydelay < 60
retrydelay+=retrydelay
delay_s(retrydelay) ' failed to connect
if ++port > 30000
port := 20000
pub ClockUpdate(port) | timeout, addr, gotstart,in,i,header[4],value[4], buffer_idx, stringptr
if settings.getString(settings#SERVER_PATH,@path_holder,64)=<0
abort 5
addr := settings.getLong(settings#SERVER_IPv4_ADDR)
if tel.connect(@addr,settings.getWord(settings#SERVER_IPv4_PORT),port) == -1
abort 6
term.str(string($1,$A,39,$C,1," ",$C,$8,$1,$B))
term.out(0)
tel.waitConnectTimeout(2000)
ifnot tel.isEOF
stat_refreshes++
term.str(string($1,$B,12," "))
term.str(string($1,$A,39,$C,$8," ",$1))
tel.str(string("GET "))
tel.str(@path_holder)
tel.str(string(" HTTP/1.0",13,10)) ' use HTTP/1.0, since we don't support chunked encoding
if settings.getString(settings#SERVER_HOST,@path_holder,64)
tel.txmimeheader(string("Host"),@path_holder)
tel.txmimeheader(string("User-Agent"),string("PropTCP"))
tel.txmimeheader(string("Connection"),string("close"))
tel.str(@CR_LF)
repeat while \http.getNextHeader(tel.handle,@header,16,@value,16)>0
if strcomp(string("Refresh"),@header)
info_refresh_period:=atoi(@value)
if info_refresh_period < 4
info_refresh_period:=4 ' Four second minimum refresh
timeout := cnt
i:=0
repeat
if (in := tel.rxcheck) > 0
if in == 10 ' new line
buffer[buffer_idx] := 0
'term.str(@buffer)
'term.dec(buffer_idx)
if (strstrn(@buffer, string("
"), BUFFMAX) <> -1) AND (strstrn(@buffer, string("UTC"), BUFFMAX) <> -1)
' we found
'term.str(@buffer)
stringptr := @buffer + 4
term.str(stringptr)
term.out(13)
parseDateline(stringptr)
i := 1
'my_cnt := cnt
term.str(string("done!"))
tel.close
return CLOCK_SUCCESS
buffer_idx := 0 ' prepare for next string buffer
elseif buffer_idx < BUFFMAX ' make sure we dont overrrun the string buffer
buffer[buffer_idx] := in
buffer_idx++
else
ifnot tel.isConnected
if i
return CLOCK_SUCCESS
abort 4
if cnt-timeout>10*clkfreq ' 10 second timeout
abort(subsys#ERR_DISCONNECTED)
else
abort(subsys#ERR_NO_CONNECT)
return 5
PUB parseDateline(str) ' in form something like "Mon. dd, hh:mm:ss"
'term.str(str)
' Find month first
if (strstrn(str, string("Jan"), 3) == 0)
month := 1
elseif (strstrn(str, string("Feb"), 3) == 0)
month := 2
elseif (strstrn(str, string("Mar"), 3) == 0)
month := 3
elseif (strstrn(str, string("Apr"), 3) == 0)
month := 4
elseif (strstrn(str, string("May"), 3) == 0)
month := 5
elseif (strstrn(str, string("Jun"), 4) == 0)
month := 6
elseif (strstrn(str, string("Jul"), 3) == 0)
month := 7
elseif (strstrn(str, string("Aug"), 3) == 0)
month := 8
elseif (strstrn(str, string("Sep"), 3) == 0)
month := 9
elseif (strstrn(str, string("Oct"), 3) == 0)
month := 10
elseif (strstrn(str, string("Nov"), 3) == 0)
month := 11
elseif (strstrn(str, string("Dec"), 3) == 0)
month := 12
'term.str(string("Month : "))
'term.dec(month)
'term.out(13)
str += strstrn(str, string(". "), 10)+2
'term.str(str)
date := num.FromStr(str, NUM#DEC)
'term.str(string("Date : "))
'term.dec(date)
'term.out(13)
str += strstrn(str, string(", "), 10)+2
'term.str(str)
hour := num.FromStr(str, NUM#DEC)
'term.str(string("Hour : "))
'term.dec(hour)
'term.out(13)
str += strstrn(str, string(":"), 10)+1
'term.str(str)
minute := num.FromStr(str, NUM#DEC)
'term.str(string("Min : "))
'term.dec(minute)
'term.out(13)
str += strstrn(str, string(":"), 10)+1
'term.str(str)
sec := num.FromStr(str, NUM#DEC)
'term.str(string("Sec : "))
'term.dec(sec)
'term.out(13)
subsys.setRTC((hour * 3600) + (minute * 60) + sec)
'RTCoffset:=0
'RTCoffset := ((hour * 3600) + (minute * 60) + sec) - subsys.RTC
PUB strstrn(haystack, needle, len) | i, j ' finds needle string in haystack string, up to len bytes long
i := 0 ' string incrementer
j := 0 ' substring incrementer
RESULT := -1 ' our success (-1 not found, otherwise index of substr
repeat i from 0 to len
if BYTE[haystack][i] == 0 ' haystack over
quit
if (BYTE[haystack][i] == BYTE[needle][0]) ' start substr compare
j := 0
repeat while (j < strsize(needle))
if ((i+j) => len)
quit
if BYTE[haystack][i+j] <> BYTE[needle][j]
quit
j++
if j == strsize(needle)
RESULT := i ' success!
return
PUB showMessage(str)
term.str(string($1,$B,12,$C,$1))
term.str(str)
term.str(string($C,$8))
pub HappyChirp
subsys.chirpHappy
pub SadChirp
subsys.chirpSad
PRI delay_ms(Duration)
waitcnt(((clkfreq / 1_000 * Duration - 3932)) + cnt)
PRI delay_s(Duration)
repeat Duration
delay_ms(1000)
VAR
byte httpMethod[8]
byte httpPath[128]
byte httpHeader[32]
byte buffer[128]
byte buffer2[128]
DAT
HTTP_200 BYTE "HTTP/1.1 200 OK"
CR_LF BYTE 13,10,0
HTTP_303 BYTE "HTTP/1.1 303 See Other",13,10,0
HTTP_404 BYTE "HTTP/1.1 404 Not Found",13,10,0
HTTP_403 BYTE "HTTP/1.1 403 Forbidden",13,10,0
HTTP_401 BYTE "HTTP/1.1 401 Authorization Required",13,10,0
HTTP_411 BYTE "HTTP/1.1 411 Length Required",13,10,0
HTTP_501 BYTE "HTTP/1.1 501 Not Implemented",13,10,0
HTTP_HEADER_SEP BYTE ": ",0
HTTP_HEADER_CONTENT_TYPE BYTE "Content-Type",0
HTTP_HEADER_LOCATION BYTE "Location",0
HTTP_HEADER_CONTENT_DISPOS BYTE "Content-disposition",0
HTTP_HEADER_CONTENT_LENGTH BYTE "Content-Length",0
HTTP_CONTENT_TYPE_HTML BYTE "text/html; charset=utf-8",0
HTTP_CONNECTION_CLOSE BYTE "Connection: close",13,10,0
BUFFMAX BYTE 128
pri httpUnauthorized(authorized)
websocket.str(@HTTP_401)
websocket.str(@HTTP_CONNECTION_CLOSE)
auth.generateChallenge(@buffer,127,authorized)
websocket.txMimeHeader(string("WWW-Authenticate"),@buffer)
websocket.str(@CR_LF)
websocket.str(@HTTP_401)
pri httpNotFound
websocket.str(@HTTP_404)
websocket.str(@HTTP_CONNECTION_CLOSE)
websocket.str(@CR_LF)
websocket.str(@HTTP_404)
pri parseIPStr(instr,outaddr) | char, i,j
repeat j from 0 to 3
BYTE[outaddr][j]:=0
j:=0
repeat while j < 4
case BYTE[instr]
"0".."9":
BYTE[outaddr][j]:=BYTE[outaddr][j]*10+BYTE[instr]-"0"
".":
j++
other:
quit
instr++
if j==3
return TRUE
abort FALSE
PUB atoi(inptr):retVal | i,char
retVal~
' Skip leading whitespace
repeat while BYTE[inptr] AND BYTE[inptr]==" "
inptr++
repeat 10
case (char := BYTE[inptr++])
"0".."9":
retVal:=retVal*10+char-"0"
OTHER:
quit
pub addTextField(id,label,value,length)
websocket.str(string("