Setting Spinneret time from a web page on the SD Card

Here's where I get to show how woefully tiny my knowledge is, despite weeks of messing with this thing.
What I want to do is have a webpage on the SD card that someone can enter in the date and time into the appropriate fields for each element, hit the submit button, and have it change the RTC time & date setting.
My RTC stuff is working well, and I can read the time/date info and put it in an email and send it all along just fine. But here's the latest that I've tried, unsuccessfully - obviously - to enter the time and date, and have it set the RTC:
I have created up in the VAR section my CKSetArray:
And I'm running this under the Dispatcher:
Syear, Smonth, etc., are the variable names (the input tag names) in the hrml file on my SD card.
I have, for now, commented out my rtc.writeTime line - which works well on its own. I've got the variables just outputting to the Parallax Serial Terminal for now, and I've tried pst.str, which yields blank spaces, and pst.dec, which yields all sorts of odd characters, but nothing I've tried so far yields anything close to the characters I'm entering.
ALSO... as a side note - I've discovered that my Spinneret will not find any filename on the SD Card longer than 7 characters. testlog.htm works fine, but testfile.htm will not. I get a 404. I've tried this with several filenames - the only thing in common is the 7 character limitation. Any ideas how I managed that? :-) I found this out while trying to get xmlexample.htm to work. Got a 404. Spent an hour swapping things in and out to get it to find the file, eventually had nothing but html and body tags with the text "test" in it. Still got a 404. Just renamed the file to test.htm, and it found it just fine.
Thanks!
Dave
What I want to do is have a webpage on the SD card that someone can enter in the date and time into the appropriate fields for each element, hit the submit button, and have it change the RTC time & date setting.
My RTC stuff is working well, and I can read the time/date info and put it in an email and send it all along just fine. But here's the latest that I've tried, unsuccessfully - obviously - to enter the time and date, and have it set the RTC:
I have created up in the VAR section my CKSetArray:
VAR
long StackSpace[20]
byte EEArray[10] ' For 24LC256 EEPROM data reads for sending email with data embedded.
byte CKSetArray[6] ' For RTC data setting from web interface.
And I'm running this under the Dispatcher:
PRI Dispatcher(id)
''Do some processing before sending the response
if(strcomp(Request.GetName(id), string("led")))
Led(id)
if(strcomp(Request.GetName(id), string("post")))
Post(id)
StaticFileHandler(id)
return
PRI Post(id) | qstr
'' Get the post value
CKSetArray[6] := Request.Post(id, string("Syear"))
CKSetArray[5] := Request.Post(id, string("Smonth"))
CKSetArray[4] := Request.Post(id, string("Sdate"))
CKSetArray[3] := Request.Post(id, string("Sday"))
CKSetArray[2] := Request.Post(id, string("Shour"))
CKSetArray[1] := Request.Post(id, string("Sminute"))
CKSetArray[0] := Request.Post(id, string("Ssecond"))
pst.str(CKSetArray[6])
pst.str(CKSetArray[5])
pst.str(CKSetArray[4])
pst.str(CKSetArray[3])
pst.str(CKSetArray[2])
pst.str(CKSetArray[1])
pst.str(CKSetArray[6])
'rtc.writeTime(CKSetArray[0], CKSetArray[1], CKSetArray[2], CKSetArray[3], CKSetArray[4], CKSetArray[5], CKSetArray[6]) 'SET THE RTC (second, minute, hour, day, date, month, year)
:Syear, Smonth, etc., are the variable names (the input tag names) in the hrml file on my SD card.
I have, for now, commented out my rtc.writeTime line - which works well on its own. I've got the variables just outputting to the Parallax Serial Terminal for now, and I've tried pst.str, which yields blank spaces, and pst.dec, which yields all sorts of odd characters, but nothing I've tried so far yields anything close to the characters I'm entering.
ALSO... as a side note - I've discovered that my Spinneret will not find any filename on the SD Card longer than 7 characters. testlog.htm works fine, but testfile.htm will not. I get a 404. I've tried this with several filenames - the only thing in common is the 7 character limitation. Any ideas how I managed that? :-) I found this out while trying to get xmlexample.htm to work. Got a 404. Spent an hour swapping things in and out to get it to find the file, eventually had nothing but html and body tags with the text "test" in it. Still got a 404. Just renamed the file to test.htm, and it found it just fine.
Thanks!
Dave
Comments
I verified HttpServer renders 8 character file names. This is an example straight from the online manual that resides on a Spinneret sitting on my desk. This rig has been running for over two years.
http://68.99.247.152:5000/images/pstbinup.png
Here's the html form I'm using, filename=post.htm
<html> <head> <title>POSTing to the Server</title> </head> <body> <div style="width:600px;margin:auto;"> <h1>System Administrative Functions</h1> <form id="post" name="post" method="post"> <h2>Set the system date and time:</h2> <input id="Smonth" name="Smonth" type="text" size=2><span style="font size:20pt;font-weight:bolder;">/</span><input id="" name="Sdate" type="text" size=2><span style="font size:20pt;font-weight:bolder;">/20</span><input id="Syear" name="Syear" type="text" size=2> - <input id="Sdate" name="Sday" type="hidden" value=0> <input id="Shour" name="Shour" type="text" size=2><span style="font size:20pt;font-weight:bolder;">:</span><input id="Sminute" name="Sminute" type="text" size=2><span style="font size:20pt;font-weight:bolder;">:</span><input id="Ssecond" name="Ssecond" type="text" size=2><br> <!-- LED:<input id="led" type="text" name="led" /> --> <input type="submit" name="Submit" value="Click to set time and date" /> </form> </div> </body> </html>
Thanks for helping... I'n so far over my head... but I'm still miles ahead of where I was a few weeks ago, many thanks to you.
<form id="post" name="post" method="post">
The Dispatch is looking for "post" so I assume it the file name on the disk is post.htm.
if(strcomp(Request.GetName(id), string("post"))) Post(id)
The filter above is true regardless of the HTTP action, GET or POST. You don't want that... this is betterif(strcomp(Request.GetName(id), string("post")) AND strcomp(Request.GetMethod(id), string("POST")))
I'm not sure how CKSetArray[] is declared but it must be a long. The Request.Post returns a string pointer.
long CKSetArray[7]
The entire IF block test should look like the following. I verified this functionality.
if(strcomp(Request.GetName(id), string("post")) AND strcomp(Request.GetMethod(id), string("POST"))) pst.char(13) pst.str(Request.GetFileName(id)) pst.char(13) CKSetArray[6] := Request.Post(id, string("Syear")) CKSetArray[5] := Request.Post(id, string("Smonth")) CKSetArray[4] := Request.Post(id, string("Sdate")) CKSetArray[3] := Request.Post(id, string("Sday")) CKSetArray[2] := Request.Post(id, string("Shour")) CKSetArray[1] := Request.Post(id, string("Sminute")) CKSetArray[0] := Request.Post(id, string("Ssecond")) pst.str(CKSetArray[6]) pst.char(13) pst.str(CKSetArray[5]) pst.char(13) pst.str(CKSetArray[4]) pst.char(13) pst.str(CKSetArray[3]) pst.char(13) pst.str(CKSetArray[2]) pst.char(13) pst.str(CKSetArray[1]) pst.char(13) pst.str(CKSetArray[6])
In the future, please post all your source code. Otherwise I have to guess which it is a bit of a time blackhole.
For the remaining question here, in the interest of full code disclosure, here is teh full HTTPServer.spin that I am running - but it will be followed by the parts that I am pretty sure are the only ones needed to see where I am in error.
HTTPServer.spin as I've modified it, in full:
{{ ───────────────────────────────────────────────── Copyright (c) 2011 AgaveRobotics LLC. See end of file for terms of use. File....... HTTPServer.spin Author..... Mike Gebhard Company.... Agave Robotics LLC Email...... mailto:mike.gebhard@agaverobotics.com Started.... 11/01/2010 Updated.... 07/16/2011 ───────────────────────────────────────────────── }} { About: HTTPServer is the designed for use with the Spinneret Web server manufactured by Parallax Inc. Usage: HTTPServer is the top level object. Required objects: • Parallax Serial Terminal.spin • W5100_Indirect_Driver.spin • S35390A_SD-MMC_FATEngineWrapper.spin • Request.spin • Response.spin • StringMethods.spin • S35390A_RTCEngine.spin Change Log: } CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 MAX_PACKET_SIZE = $5C0 '1472 '$800 = 2048 RxTx_BUFFER = $800 '$600 = 1536 TEMP_BUFFER = $600 '$5B4 = 1460 TCP_PROTOCOL = %0001 '$300 = 768 UDP_PROTOCOL = %0010 '$200 = 512 TCP_CONNECT = $04 '$100 = 256 DELAY = $05 MAX_TRIES = $05 #0, DONE, PUT, CD DAT mac byte $00, $08, $DC, $16, $F3, $D3 subnet byte 255, 255 ,255, 0 gateway byte 192, 168, 1, 1 ip byte 192, 168, 1, 120 port word 5000 remoteIp byte 65, 98, 8, 151 {65.98.8.151} remotePort word 80 uport word 5050 emailIp byte 8, 24, 153, 30 emailPort word 587 ''587 ''110 ''25 port2 word 5010 status byte $00, $00, $00, $00 rxdata byte $0[RxTx_BUFFER] txdata byte $0[RxTx_BUFFER] udpdata byte $0[TEMP_BUFFER] fileErrorHandle long $0 debug byte $0 lastFile byte $0[12], 0 closedState byte %0000 openState byte %0000 listenState byte %0000 establishedState byte %0000 closingState byte %0000 closeWaitState byte %0000 lastEstblState byte %0000 lastEstblStateDebug byte %0000 udpListen byte %0000 tcpMask byte %1111 udpMask byte %1000 fifoSocketDepth byte $0 fifoSocket long $00_00_00_00 debugSemId byte $00 debugCounter long $00 stringMethods long $00 closingTimeout long $00, $00, $00, $00 udpLen long $00 time byte "00/00/0000 00:00:00", 0 httpDate byte "Wed, 01 Feb 2000 01:00:00 GMT", 0 globalCache byte $1 dynamicContentPtr long @txdata tankfile byte "tankfarm.csv", 0 VAR long StackSpace[20] byte EEArray[10] ' For 24LC256 EEPROM data reads for sending email with data embedded. long CKSetArray[7] ' For RTC data setting from web interface. OBJ pst : "Parallax Serial Terminal" Socket : "W5100_Indirect_Driver" SDCard : "S35390A_SD-MMC_FATEngineWrapper" Request : "Request" Response : "Response" str : "StringMethods" rtc : "S35390A_RTCEngine" nums : "Simple_Numbers_plus" ' "Numbers" PUB Initialize | id, size, st debug := 1 SDCard.Start stringMethods := str.Start Request.Constructor(stringMethods) Response.Constructor(stringMethods, @txdata) pst.Start(115_200) pause(200) 'Mount the SD card pst.str(string("Mount SD Card - ")) SDCard.mount(fileErrorHandle) pst.str(string("OK",13)) pst.str(string("Start RTC: ")) rtc.RTCEngineStart(29, 28, -1) pause(200) 'rtc.writeTime(0, 15, 18, 0, 25, 6, 2013) ' (second, minute, hour, day, date, month, year) 'pause(200) pst.str(FillTime) 'Start the W5100 driver if(Socket.Start) pst.str(string(13, "W5100 Driver Started", 13)) pst.str(string(13, "Status Memory Lock ID : ")) pst.dec(Socket.GetLockId) pst.char(13) if(debugSemId := locknew) == -1 pst.str(string("Error, no HTTP server locks available", 13)) else pst.str(string("HTTP Server Lock ID : ")) pst.dec(debugSemId) pst.char(13) 'Set the Socket addresses SetMac(@mac) SetGateway(@gateway) SetSubnet(@subnet) SetIP(@ip) ' Initailize TCP sockets (defalut setting; TCP, Port, remote ip and remote port) repeat id from 0 to 3 InitializeSocket(id) Request.Release(id) pause(50) ' Set all TCP sockets to listen pst.char(13) repeat id from 0 to 3 Socket.Listen(id) pst.str(string("TCP Socket Listener ID : ")) pst.dec(id) pst.char(13) pause(50) pst.Str(string(13,"Started Socket Monitoring Service", 13)) cognew(StatusMonitor, @StackSpace) pause(250) pst.Str(string(13, "Initial Socket States",13)) StackDump pst.Str(string(13, "Initial Socket Queue",13)) QueueDump pst.str(string(13,"//////////////////////////////////////////////////////////////",13)) Main PUB Main | packetSize, id, i, reset, j, temp ''HTTP Service ''GetMyIp(3) ''clockandemail repeat dira[22] := 0 '' Sets 22 to input '' This block watches the buffered IO on P22 for a 0 signal, and if ina[22] == 0 '' it sends the email when there is a low pulse. It also fires off the LED dira[23]~~ '' just because... why not. outa[23] := 1 ''ina[22] 'TankLog(string("XTM-MAN,01,G.H. Berlin/Windward,ID1,Manchester NH,001,Product,06/11/2013 19:00:00,000.00,100.00")) pause(DELAY) SendTestEmail(2) outa[23] := 0 ''ina[22] repeat until fifoSocket == 0 bytefill(@rxdata, 0, RxTx_BUFFER) if(debug) pst.str(string(13, "----- Start of Request----------------------------",13)) pause(DELAY) else pause(DELAY) ' Pop the next socket handle id := DequeueSocket if(id < 0) next if(debug) pst.str(string(13,"ID: ")) pst.dec(id) pst.str(string(13, "Request Count : ")) pst.dec(debugCounter) pst.char(13) packetSize := Socket.rxTCP(id, @rxdata) reset := false if ((packetSize < 12) AND (strsize(@rxdata) < 12)) repeat i from 0 to MAX_TRIES 'pst.str(string(13,"* Retry *")) 'Wait for a few moments and try again waitcnt((clkfreq/500) + cnt) packetSize := Socket.rxTCP(id, @rxdata) if(packetSize > 12) quit if(i == MAX_TRIES) 'Clean up resource request Request.Release(id) Socket.Disconnect(id) reset := true if(debug) StackDump pst.char(13) QueueDump pst.str(string(13,"* Timeout *",13)) if(reset) next Request.InitializeRequest(id, @rxdata) if(debug) pst.char(13) HeaderLine1(id) else pause(DELAY) ' Process router Dispatcher(id) 'Clean up request resource Request.Release(id) ' This starts the close process -> 0x00 ' use close to force a close Socket.Disconnect(id) bytefill(@txdata, 0, RxTx_BUFFER) debugCounter++ GotoMain PUB SendTestEmail(id) | size, tempMask, wait, stringPointer, tankNum, tankStr, Yr, Mo, Dy, Hr, Mn, Sc tempMask := tcpMask SetTcpSocketMaskById(id, 0) wait := 200 Socket.Close(id) pause(delay) InitializeSocketForEmail(id) pause(delay) ' Connect to the mail server pst.str(string(13, "Connecting to mail server")) Socket.Connect(id) pause(wait) repeat while !Socket.Connected(id) pst.str(string(13,"Connected... Talking with mail server")) 'Send greeting StringSend(id, string("EHLO xanatos@xanatos.com", 13, 10)) pause(wait) '============================================================================== StringSend(id, string("AUTH LOGIN", 13,10)) pause(wait) ' 'Send user name StringSend(id, string("eGFuYXRvc0B4YW5hdG9zLmNvbQ==", 13, 10)) ' ' ' 'Send password StringSend(id, string("cGVhY2VhbmR0cmFucXVpbGl0eQ==", 13, 10)) '============================================================================== ' From Address StringSend(id, string("MAIL FROM: *******@*******.com", 13, 10)) pause(wait) ' To Address StringSend(id, string("RCPT TO: *******@*******.com", 13, 10)) pause(wait) ' To Address 'StringSend(id, string("RCPT TO:*******@*******.com", 13, 10)) 'pause(wait) 'Start of the email content StringSend(id, string("DATA", 13, 10)) pause(wait) 'Visible From line StringSend(id, string("From: *******@*******.com", 13, 10)) pause(wait) 'Visible To line StringSend(id, string("To: *******@*******.com", 13, 10)) pause(wait) 'Subject line StringSend(id, string("SUBJECT: Email from the Spinneret", 13, 10)) pause(wait) 'Mime-Type StringSend(id, string("Mime-Version: 1.0", 13, 10)) pause(wait) 'Content Type StringSend(id, string("Content-Type: text/plain; charset=us-ascii", 13, 10, 13, 10)) pause(wait) Mo := rtc.clockMonth Dy := rtc.clockDate Yr := rtc.clockYear Hr := rtc.clockHour Mn := rtc.clockMinute Sc := rtc.clockSecond repeat tankNum from 1 to 16 StringSend(id, string("XTM-MAN,")) StringSend(id, string("01,0,0,0,")) StringSend(id, nums.decn(tankNum, 3)) StringSend(id, string(",0,")) StringSend(id, nums.decn(Mo, 2)) StringSend(id, string("/")) StringSend(id, nums.decn(Dy, 2)) StringSend(id, string("/")) StringSend(id, nums.decn(Yr, 4)) StringSend(id, string(" ")) StringSend(id, nums.decn(Hr, 2)) StringSend(id, string(":")) StringSend(id, nums.decn(Mn, 2)) StringSend(id, string(":")) StringSend(id, nums.decn(Sc, 2)) StringSend(id, string(",")) StringSend(id, string("000.00,")) StringSend(id, string("050.00", 13, 10)) pause(wait) 'Quit conversation StringSend(id, string(".", 13, 10)) pause(wait) StringSend(id, string("QUIT", 13, 10)) pause(wait) pst.str(string(13,"Done",13)) pst.str(string(13,"Conversation Log",13)) 'Display log repeat until size := Socket.rxTCP(id, @rxdata) pst.str(@rxdata) pst.str(string(13, "Disconnect and reset socket: ")) pst.dec(id) pst.char(13) ' Reset the socket Socket.Disconnect(id) pause(delay) ' Reset the tcpMask tcpMask := tempMask InitializeSocket(id) pause(delay) return '======================================================================================= PUB clockandemail 'SET THE CLOCK '(second, minute, hour, day, date, month, year) 'rtc.writeTime(00, 10, 20, 2, 10, 6, 2013) 'SET THE RTC repeat waitcnt(clkfreq/5+cnt) rtc.readtime pst.str(string(" ")) pst.dec(rtc.clockHour) pst.str(string(":")) pst.dec(rtc.clockMinute) pst.str(string(":")) pst.dec(rtc.clockSecond) pst.str(string(" ",13 )) if rtc.clockMinute//10 == 0 waitcnt(clkfreq/5+cnt) 'wait for stuff to stop SendTestEmail(2) 'INITIALIZES THE SOCKET AND sends the email waitcnt(clkfreq/5+cnt) 'wait for stuff to stop '======================================================================================= PRI GotoMain Main PRI InitializeSocketForEmail(id) Socket.Initialize(id, TCP_PROTOCOL, port2, emailPort, @emailIp) return PRI InitializeSocket2(id) Socket.Initialize(id, TCP_PROTOCOL, port2, remotePort, @remoteIp) return PRI Dispatcher(id) ''Do some processing before sending the response if(strcomp(Request.GetName(id), string("led"))) Led(id) 'if(strcomp(Request.GetName(id), string("post"))) if(strcomp(Request.GetName(id), string("post")) AND strcomp(Request.GetMethod(id), string("POST"))) Post(id) StaticFileHandler(id) return PRI Post(id) | qstr, Yr, Mo, Dy, Hr, Mn, Sc '' Get the post value CKSetArray[6] := Request.Post(id, string("Syear")) CKSetArray[5] := Request.Post(id, string("Smonth")) CKSetArray[4] := Request.Post(id, string("Sdate")) CKSetArray[3] := Request.Post(id, string("Sday")) CKSetArray[2] := Request.Post(id, string("Shour")) CKSetArray[1] := Request.Post(id, string("Smin")) CKSetArray[0] := Request.Post(id, string("Ssec")) pst.char(13) pst.str(CKSetArray[5]) pst.str(string("/")) pst.str(CKSetArray[4]) pst.str(string("/")) pst.str(CKSetArray[6]) pst.char(13) pst.str(CKSetArray[2]) pst.str(string(":")) pst.str(CKSetArray[1]) pst.str(string(":")) pst.str(CKSetArray[0]) pst.char(13) pst.str(CKSetArray[3]) pst.char(13) rtc.writeTime(CKSetArray[0], CKSetArray[1], CKSetArray[2], CKSetArray[3], CKSetArray[4], CKSetArray[5], CKSetArray[6]) 'SET THE RTC (second, minute, hour, day, date, month, year) waitcnt(500+cnt) Mo := rtc.clockMonth Dy := rtc.clockDate Yr := rtc.clockYear Hr := rtc.clockHour Mn := rtc.clockMinute Sc := rtc.clockSecond pst.char(13) pst.str(nums.decn(Mo, 2)) pst.char(13) pst.str(nums.decn(Dy, 2)) pst.char(13) pst.str(nums.decn(Yr, 4)) pst.char(13) pst.str(nums.decn(Hr, 2)) pst.char(13) pst.str(nums.decn(Mn, 2)) pst.char(13) pst.str(nums.decn(Sc, 2)) pst.char(13) 'qstr := Request.Post(id, string("led")) 'if (strcomp(string("on"), qstr )) ' LedStatus(1) ' pst.str(string(" - on",13,10)) 'else ' LedStatus(0) ' pst.str(string(" - off",13,10)) return PRI Led(id) | qstr '' Get the query string value qstr := Request.Get(id, string("led")) if (strcomp(string("on"), qstr )) LedStatus(1) else LedStatus(0) pst.str(string(" - off",13,10)) return PRI LedStatus(state) dira[23]~~ outa[23] := state return PRI StaticFileHandler(id) | fileSize, i, headerLen, temp, j ''Serve up static files from the SDCard 'pst.str(string(13,"Static File Handler",13)) SDCard.changeDirectory(@approot) pst.char(13) 'Make sure the directory exists ifnot(ChangeDirectory(id)) 'send 404 error WriteError(id) SDCard.changeDirectory(@approot) return ' Make sure the file exists ifnot(FileExists(Request.GetFileName(id))) 'send 404 error WriteError(id) SDCard.changeDirectory(@approot) return ' Open the file for reading SDCard.openFile(Request.GetFileName(id), "r") fileSize := SDCard.getFileSize 'WriteResponseHeader(id) 'BuildHeader(extension, statusCode, expirer) headerLen := Response.BuildHeader(Request.GetExtension(id), 200, globalCache) Socket.txTCP(id, @txdata, headerLen) if fileSize < MAX_PACKET_SIZE ' send the file in one packet SDCard.readFromFile(@txdata, fileSize) Socket.txTCP(id, @txdata, fileSize) else ' send the file in a bunch of packets repeat SDCard.readFromFile(@txdata, MAX_PACKET_SIZE) Socket.txTCP(id, @txdata, MAX_PACKET_SIZE) fileSize -= MAX_PACKET_SIZE ' once the remaining fileSize is less then the max packet size, just send that remaining bit and quit the loop if fileSize < MAX_PACKET_SIZE and fileSize > 0 SDCard.readFromFile(@txdata, fileSize) Socket.txTCP(id, @txdata, fileSize) quit ' Bailout if(i++ > 1_000_000) WriteError(id) quit SDCard.closeFile SDCard.changeDirectory(@approot) return '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '' SDCard Logger '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PRI TankLog(logToAppend) '' logToAppend: Pointer to a string of text we'd like to log SDCard.changeDirectory(@approot) if(FileExists(@tankfile)) SDCard.openFile(@tankfile, "A") else SDCard.newFile(@tankfile) SDCard.writeData(logToAppend, strsize(logToAppend)) SDCard.writeData(@crlf_crlf, 2) SDCard.closeFile return PRI WriteError(id) | headerOffset '' Simple 404 error pst.str(string(13, "Write 404 Error",13 )) headerOffset := Response.BuildHeader(Request.GetExtension(id), 404, false) Socket.txTCP(id, @txdata, headerOffset) return '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '' Write data to a buffer '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PRI PushDynamicContent(content) ' Write the content to memory ' and update the pointer bytemove(dynamicContentPtr, content, strsize(content)) dynamicContentPtr := dynamicContentPtr + strsize(content) return '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '' directory and file handlers '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PRI ChangeDirectory(id) | i, found 'Handle directory structure for this Request if(Request.GetDepth(id) > 1) repeat i from 0 to Request.GetDepth(id)-2 'Return if the directory is not found ifnot(FileExists(Request.GetPathNode(id, i))) return false found := SDCard.changeDirectory(Request.GetPathNode(id, i)) return true PRI FileExists(fileToCompare) | filenamePtr 'Start file find at the top of the list SDCard.startFindFile 'Verify that the file exists repeat while filenamePtr <> 0 filenamePtr := SDCard.nextFile if(str.MatchPattern(filenamePtr, fileToCompare, 0, false ) == 0 ) return true return false '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '' Time Methods and Formats '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PRI GetTime(id) | ptr, headerOffset ptr := @udpdata FillHttpDate bytemove(ptr, string("<p>"),3) ptr += 3 bytemove(ptr, @httpDate, strsize(@httpDate)) ptr += strsize(@httpDate) bytemove(ptr, string("</p>"),4) ptr += 3 headerOffset := Response.BuildHeader(Request.GetExtension(id), 200, false) Socket.txTCP(id, @txdata, headerOffset) StringSend(id, @udpdata) bytefill(@udpdata, 0, TEMP_BUFFER) return PRI FillTime | ptr, num 'ToString(integerToConvert, destinationPointer) '00/00/0000 00:00:00 ptr := @time rtc.readTime FillTimeHelper(rtc.clockMonth, ptr) ptr += 3 FillTimeHelper(rtc.clockDate, ptr) ptr += 3 FillTimeHelper(rtc.clockYear, ptr) ptr += 5 FillTimeHelper(rtc.clockHour , ptr) ptr += 3 FillTimeHelper(rtc.clockMinute , ptr) ptr += 3 FillTimeHelper(rtc.clockSecond, ptr) return @time PRI FillHttpDate | ptr, num, temp 'ToString(integerToConvert, destinationPointer) 'Wed, 01 Feb 2000 01:00:00 GMT ptr := @httpDate rtc.readTime temp := rtc.getDayString bytemove(ptr, temp, strsize(temp)) ptr += strsize(temp) + 2 FillTimeHelper(rtc.clockDate, ptr) ptr += 3 temp := rtc.getMonthString bytemove(ptr, temp, strsize(temp)) ptr += strsize(temp) + 1 FillTimeHelper(rtc.clockYear, ptr) ptr += 5 FillTimeHelper(rtc.clockHour , ptr) ptr += 3 FillTimeHelper(rtc.clockMinute , ptr) ptr += 3 FillTimeHelper(rtc.clockSecond, ptr) return @httpDate PRI FillTimeHelper(number, ptr) | offset offset := 0 if(number < 10) offset := 1 str.ToString(@number, @tempNum) bytemove(ptr+offset, @tempNum, strsize(@tempNum)) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '' SDCard Logger '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PRI AppendLog(logToAppend) '' logToAppend: Pointer to a string of text we'd like to log SDCard.changeDirectory(@approot) if(FileExists(@logfile)) SDCard.openFile(@logfile, "A") else SDCard.newFile(@logfile) SDCard.writeData(string(13,10,"----- Start "),14) SDCard.writeData(FillTime, 19) SDCard.writeData(string(" -----"),6) SDCard.writeData(@crlf_crlf, 2) SDCard.writeData(logToAppend, strsize(logToAppend)) SDCard.writeData(@crlf_crlf, 2) SDCard.writeData(string("----- End "),10) SDCard.writeData(FillTime, 19) SDCard.writeData(string(" -----"),6) SDCard.writeData(@crlf_crlf, 2) SDCard.closeFile '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '' Memory Management '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PRI Set(DestAddress, SrcAddress, Count) bytemove(DestAddress, SrcAddress, Count) bytefill(DestAddress+Count, $0, 1) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '' Socket helpers '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PRI GetTcpSocketMask(id) return id & tcpMask PRI DecodeId(value) | tmp if(%0001 & value) return 0 if(%0010 & value) return 1 if(%0100 & value) return 2 if(%1000 & value) return 3 return -1 PRI QueueSocket(id) | tmp if(fifoSocketDepth > 4) return false tmp := |< id 'Unique check ifnot(IsUnique(tmp)) return false tmp <<= (fifoSocketDepth++) * 8 fifoSocket |= tmp return true PRI IsUnique(encodedId) | tmp tmp := encodedId & $0F repeat 4 if(encodedId & fifoSocket) return false encodedId <<= 8 return true PRI DequeueSocket | tmp if(fifoSocketDepth == 0) return -2 repeat until not lockset(debugSemId) tmp := fifoSocket & $0F fifoSocket >>= 8 fifoSocketDepth-- lockclr(debugSemId) return DecodeId(tmp) PRI ResetSocket(id) Socket.Disconnect(id) Socket.Close(id) PRI IsolateTcpSocketById(id) | tmp tmp := |< id tcpMask &= tmp PRI SetTcpSocketMaskById(id, state) | tmp '' The tcpMask contains the socket the the StatusMonitor monitors tmp := |< id if(state == 1) tcpMask |= tmp else tmp := !tmp tcpMask &= tmp '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '' W5100 Helper methods '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PRI GetCommandRegisterAddress(id) return Socket#_S0_CR + (id * $0100) PRI GetStatusRegisterAddress(id) return Socket#_S0_SR + (id * $0100) PRI SetMac(_firstOctet) Socket.WriteMACaddress(true, _firstOctet) return PRI SetGateway(_firstOctet) Socket.WriteGatewayAddress(true, _firstOctet) return PRI SetSubnet(_firstOctet) Socket.WriteSubnetMask(true, _firstOctet) return PRI SetIP(_firstOctet) Socket.WriteIPAddress(true, _firstOctet) return PRI StringSend(id, _dataPtr) Socket.txTCP(id, _dataPtr, strsize(_dataPtr)) return PRI SendChar(id, _dataPtr) Socket.txTCP(id, _dataPtr, 1) return PRI SendChars(id, _dataPtr, _length) Socket.txTCP(id, _dataPtr, _length) return PRI InitializeSocket(id) Socket.Initialize(id, TCP_PROTOCOL, port, remotePort, @remoteIp) return PRI InitializeUPDSocket(id) Socket.Initialize(id, UDP_PROTOCOL, uport, remotePort, @remoteIp) return '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '' Debug/Display Methods '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PRI QueueDump '' Display socket IDs in the queue '' ie 00000401 -> socket Zero is next to pop off pst.str(string("FIFO[")) pst.dec(fifoSocketDepth) pst.str(string("] ")) pst.hex(fifoSocket, 8) PRI StackDump | clsd, open, lstn, estb, clwt, clng, id, ulst '' This method is purely for debugging '' It displays the status of all socket registers repeat until not lockset(debugSemId) clsd := closedState open := openState lstn := listenState estb := establishedState clwt := closeWaitState clng := closingState ulst := udpListen lockclr(debugSemId) pst.char(13) repeat id from 3 to 0 pst.dec(id) pst.str(string("-")) pst.hex(status[id], 2) pst.str(string(" ")) pause(1) pst.str(string(13,"clsd open lstn estb clwt clng udps", 13)) pst.bin(clsd, 4) pst.str(string("-")) pst.bin(open, 4) pst.str(string("-")) pst.bin(lstn, 4) pst.str(string("-")) pst.bin(estb, 4) pst.str(string("-")) pst.bin(clwt, 4) pst.str(string("-")) pst.bin(clng, 4) pst.str(string("-")) pst.bin(ulst, 4) pst.char(13) PRI HeaderLine1(id) | i pst.str(Request.GetMethod(id)) pst.char($20) i := 0 repeat Request.GetDepth(id) pst.char($2F) pst.str(Request.GetPathNode(id, i++)) PRI Pause(Duration) waitcnt(((clkfreq / 1_000 * Duration - 3932) #> 381) + cnt) return PRI StatusMonitor | id, tmp, value '' StatusMonitor is the heartbeat of the project '' Here we monitor the state of the Wiznet 5100's 4 sockets repeat Socket.GetStatus32(@status[0]) ' Encode status register states repeat until not lockset(debugSemId) closedState := openState := listenState := establishedState := { } closeWaitState := closingState := 0 repeat id from 0 to 3 case(status[id]) $00: closedState |= |< id closedState &= tcpMask $13: openState |= |< id openState &= tcpMask $14: listenState |= |< id listenState &= tcpMask $17: establishedState |= |< id establishedState &= tcpMask $18,$1A,$1B: closingState |= |< id closingState &= tcpMask $1C: closeWaitState |= |< id closeWaitState &= tcpMask $1D: closingState |= |< id closingState &= tcpMask $22: udpListen |= |< id udpListen &= udpMask if(lastEstblState <> establishedState) value := establishedState repeat while value > 0 tmp := DecodeId(value) if(tmp > -1) QueueSocket(tmp) tmp := |< tmp tmp := !tmp value &= tmp lastEstblState := establishedState lockclr(debugSemId) ' Initialize a closed socket if(closedState > 0) id := DecodeId(closedState) if(id > -1) InitializeSocket(id & tcpMask) 'Start a listener on an initialized/open socket if(openState > 0) id := DecodeId(openState) if(id > -1) Socket.Listen(id & tcpMask) ' Close the socket if the status is close/wait ' response processor should close the socket with disconnect ' there could be a timeout so we have a forced close. ' TODO: CCheck for a port that gets stuck in a closing state 'if(closeWaitState > 0) 'id := DecodeId(closeWaitState) 'if(id > -1) 'Socket.Close(id & tcpMask) 'pause(100) return DAT approot byte "\", 0 defaultpage byte "index.htm", 0 logfile byte "log.txt", 0 'binFile byte "filename.bin", 0 FS byte "/", 0 fn byte "filename=", 0 doublequote byte $22, 0 crlf byte 13, 10, 0 crlf_crlf byte 13, 10, 13, 10, 0 uploadfile byte $0[12], 0 uploadfolder byte "uploads", 0 tempNum byte "0000",0 multipart byte "Content-Type: multipart/form-data; boundary=",0 boundary byte $2D, $2D boundary1 byte $0[64] 'loadme file "TogglePin0.binary" {{ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ TERMS OF USE: MIT License │ ├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation │ │files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, │ │modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software│ │is furnished to do so, subject to the following conditions: │ │ │ │The above copyright notice and this permission notice shall be included in all copies or substantial ions of the Software.│ │ │ │THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE │ │WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR │ │COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, │ │ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }}
I won't post "post.htm" because we know that's working - the numbers are unquestionably making it into the Propeller on the Spinneret because I can pst.str them to the PST window.
The question is "What does the rtc.writeTime line expect for data type?"
I have tried my CKSetArray[x] alone, with string(), and with nums.decn(CKSetArray[x], 2))... and several other things that just didn't work. Here's the code I have where the dispatcher DOES parse the values properly, then I set the time with rtc.writeTime(), then I read the individual variables - and they are NOT what I wrote. First the code - then the next window is the display of the PST output.
Code of section in question:
PRI Dispatcher(id) ''Do some processing before sending the response if(strcomp(Request.GetName(id), string("led"))) Led(id) 'if(strcomp(Request.GetName(id), string("post"))) if(strcomp(Request.GetName(id), string("post")) AND strcomp(Request.GetMethod(id), string("POST"))) Post(id) StaticFileHandler(id) return PRI Post(id) | qstr, Yr, Mo, Dy, Hr, Mn, Sc '' Get the post value CKSetArray[6] := Request.Post(id, string("Syear")) CKSetArray[5] := Request.Post(id, string("Smonth")) CKSetArray[4] := Request.Post(id, string("Sdate")) CKSetArray[3] := Request.Post(id, string("Sday")) CKSetArray[2] := Request.Post(id, string("Shour")) CKSetArray[1] := Request.Post(id, string("Smin")) CKSetArray[0] := Request.Post(id, string("Ssec")) pst.char(13) pst.str(CKSetArray[5]) pst.str(string("/")) pst.str(CKSetArray[4]) pst.str(string("/")) pst.str(CKSetArray[6]) pst.char(13) pst.str(CKSetArray[2]) pst.str(string(":")) pst.str(CKSetArray[1]) pst.str(string(":")) pst.str(CKSetArray[0]) pst.char(13) pst.str(CKSetArray[3]) pst.char(13) rtc.writeTime(CKSetArray[0], CKSetArray[1], CKSetArray[2], CKSetArray[3], CKSetArray[4], CKSetArray[5], CKSetArray[6]) 'SET THE RTC (second, minute, hour, day, date, month, year) waitcnt(500+cnt) Mo := rtc.clockMonth Dy := rtc.clockDate Yr := rtc.clockYear Hr := rtc.clockHour Mn := rtc.clockMinute Sc := rtc.clockSecond pst.char(13) pst.str(nums.decn(Mo, 2)) pst.char(13) pst.str(nums.decn(Dy, 2)) pst.char(13) pst.str(nums.decn(Yr, 4)) pst.char(13) pst.str(nums.decn(Hr, 2)) pst.char(13) pst.str(nums.decn(Mn, 2)) pst.char(13) pst.str(nums.decn(Sc, 2)) pst.char(13) return
And output of the PST window - the first section is entering time values and date values like 01/23/2045 and 12:34:56; the next one I just entered 11/11/11 and 22:22:22. Note that the output obtained after reading the RTC is the same, with a year of 2099, etc. Note ALSO that if I manually set the rtc at the initialization routine of HTTPServer.spin, comment out the setting line in the post handler, and read the values for the time and date, they DO read correctly. It's ONLY when I try to set the rtc with the values obtained from the post that they go to the values indicated:
----- Start of Request---------------------------- ID: 0 Request Count : 2 POST /post.htm 12/34/56 01:23:45 12 31 2099 23 59 59 ----- Start of Request---------------------------- ID: 0 Request Count : 3 POST /post.htm 11/11/11 22:22:22 12 31 2099 23 59 59
I'm guessing that that 12/31/2099 at 23:59:59 is some sort of value that equates to the max time/date of the Spinneret... and I'm assuming that it's due to my sending the numbers in teh wrong format. Note that the reading and writing of the values I am sending from my post.htm form is fine - only the setting of the rtc and reading those values back creates the error.
I'm continuing to try sending various data types, but I just don't yet know enough to know everything I should be sending - or that I can send... and I'm not sure yet of where to find that info. A search of "setting RTC time date Spinneret" hasn't yet yielded the nugget I seem to be missing...
Thanks again...... I hope I can find a way to pay this forward.
Dave
CKSetArray[0] := Request.Post(id, string("Ssecond"))
All you're doing is copying a pointer. The pointer is only good for the duration of the request/response. If you need to store the data - so the data persists between requests - then move the bytes to a buffer. Otherwise *poof* unexpected results.
It is much better to update the RTC using SNTP. There are examples on the forum. The newer driver handles SNTP time automatically [nudge nudge hint hint].
http://code.google.com/p/propeller-w5200-driver/source/browse/trunk/#trunk%2F%20propeller-w5200-driver%2FSpinneret
PUB writeTime(second, minute, hour, day, date, month, year) | index, information[7] '' 33 Stack Longs '' //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// '' // Changes the current real time clock time settings. Returns true on success and false on failure. '' // '' // Second - Number to set the second to between 0 - 59. '' // Minute - Number to set the minute to between 0 - 59. '' // Hour - Number to set the hour to between 0 - 23. '' // Day - Number to set the day to between 1 - 7. '' // Date - Number to set the date to between 1 - 31. '' // Month - Number to set the month to between 1 - 12. '' // Year - Number to set the year to between 2000 - 2099. '' // '' // If the real time clock was previously powered on but not initialized this method will initialize the real time clock '' // setting all registers to zero and clearing all interrupts. After doing so the time and date will be setup. '' // '' // If the real time clock previously browned out but was not re-initialized this method will re-initialize the real time '' // clock setting all registers to zero and clearing all interrupts. After doing so the time and date will be setup. '' ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
The statement below passes a pointer value to each of the writeTime parameters. The pointer is a string pointer. The string contains ASCII encoded characters which means the values are not numbers. They are codes that represent numbers.
rtc.writeTime(CKSetArray[0], CKSetArray[1], CKSetArray[2], CKSetArray[3], CKSetArray[4], CKSetArray[5], CKSetArray[6])
What ya need to do is convert the ASCII strings to numbers. Then pass the numbers to the writeTime method.
It is far easier and less code to use the SNTP object.
I have in fact seen the SNTP stuff in the OBEX and in several posts. My question about this - since I saw one post that seemed to indicate that an SNTP server can go down/disappear - is, How reliable is the SNTP setting function over extended time? Once this whole thing is running, I won't have convenient access to it, so I wouldn't want to have to fly out just to change a server address. Can the SNTP functions be relied on over time without worry?
Lastly - while I am stressing out over when I promised that this would be ready by, I'm really enjoying the learning aspects of all the many areas I'm trying to come up to speed on simultaneously. And while it may turn out that SNTP is the way to go, I still feel there is a lot to learn by completing my understanding of why my time setting routine is not working as expected. I'd like to understand exactly WHAT IS a buffer in this context, and how would I change my CKSetArray[x] values to hold the actual VALUE rather than a pointer to them... this seems to be an important understanding that I feel like I am on the verge of understanding, but I'm just not seeing over that particular hill yet.
Thanks. I hope I'm not annoying with all my requests, but I do learn well and I'm persistent as hell, which is why I have become so proficient with the Stamps. I really want to be that confident with the Propeller, but I still feel like a preschooler - and as far as I can tell, you're the most knowledgeable in these areas, and I really appreciate your willingness to guide me to understanding what I'm doing...
Dave
PS., I posted this while you were responding above... reading now. Can I buy ya a beer or something? :-)
I thought I did that when I used the format:
rtc.writeTime(nums.decn(CKSetArray[0], 2), nums.decn(CKSetArray[0], 2), ......................
Where nums is the numbers object I'm using and set in my OBJ block shown in my full code in the first post. So if converting my CKSetArray using nums isn't doing the trick - I am obviously not understanding how nums is working either... :-)
Thanks... feeling way over my depth here.... :-)
Dave
Is this a pointer to a location in memory?
So I guess what I'm looking for is how do I specify CKSetArray[0] := "The value stored at Request.Post(id, string("Ssec"))" instead of CKSetArray[0] := "The location of the value stored at Request.Post(id, string("Ssec"))"
Can you confirm if my understanding of this is correct?
Yes, SNTP is reliable.
The posted code did not contain a call to nums.decn. I'm not familiar with nums.decn so I can't help you there. You'll have to look at the object.
You're not getting it. The value is a character (byte) array. The pointer is a memory location where the byte array starts. The byte array ends with a zero - zero terminated. That's how strings work.
How is it that I can pst.str the data at CKSetArray[5] for example to the PST window, but have it not be able to put that same data into the rtc.writeTime values? You say that CKSetArray[x] contains a pointer to the data, but not the actual data itself. You say the data is a string that is zero-terminated. So my understanding at this point is that the data contained in CKSetArray[x] is a pointer to the location of the actual data I want to use, which begins at the location contained in CKSetArray[x], and ends at the first instance of that zero-terminated string... but it sounds like you are telling me that is wrong. I guess I have no idea what you are saying then - please help me get this....
Dave
PRI Post(id) | qstr, Yr, Mo, Dy, Hr, Mn, Sc '' Get the post value CKSetArray[6] := Request.Post(id, string("Syear")) CKSetArray[5] := Request.Post(id, string("Smonth")) CKSetArray[4] := Request.Post(id, string("Sdate")) CKSetArray[3] := Request.Post(id, string("Sday")) CKSetArray[2] := Request.Post(id, string("Shour")) CKSetArray[1] := Request.Post(id, string("Smin")) CKSetArray[0] := Request.Post(id, string("Ssec")) Sc := str.ToInteger(CKSetArray[0]) Mn := str.ToInteger(CKSetArray[1]) Hr := str.ToInteger(CKSetArray[2]) Dy := str.ToInteger(CKSetArray[4]) Mo := str.ToInteger(CKSetArray[5]) Yr := 2000 + str.ToInteger(CKSetArray[6]) pst.char(13) pst.str(CKSetArray[5]) pst.str(string("/")) pst.str(CKSetArray[4]) pst.str(string("/")) pst.str(CKSetArray[6]) pst.char(13) pst.str(CKSetArray[2]) pst.str(string(":")) pst.str(CKSetArray[1]) pst.str(string(":")) pst.str(CKSetArray[0]) pst.char(13) pst.str(CKSetArray[3]) pst.char(13) rtc.writeTime(Sc, Mn, Hr, 0, Dy, Mo, Yr) 'SET THE RTC (second, minute, hour, day, date, month, year) waitcnt(500+cnt)
It also echoes to the Parallax Serial Terminal properly.
I must be having some sort of contention going on with some other process in the Propeller's program, since when I set the time, it also stops incrementing. The emails sent out from the unit show the time as it was set, but a few minutes later - same time still shows.
However - it must still be keeping time somewhere, because if I reload the RAM (F10), and then have it send an email with the timestamp embedded - the time has been updating internally all along.
I am so far over my head.......
Imagine we have the decimal number 49. 49 fits comfortably in a byte data type being 49 and less than 256. To display 49 on the Parallax Terminal is easy - pst.dec(49).
What if the number 49 is not a number but a ASCII encoded representation of 49. In other words, a string and the kind of item found in the HTTP protocol when using POST. Well, the ASCII equivalent of 49 requires two bytes. One byte for the number 4 and one byte for the number 9. In memory the string 49 looks like; [52 | 57 | 0]. The zero on the end is the end of the string - zero terminated
The decimal value 49 prints as the number 1 and pst.dec(49) parses the decimal value 49. Dec first sends the value 52 then 57 out the serial port.
Code to play with...
CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 CR = 13 VAR DAT num byte $0 charArray byte $0[10] OBJ pst : "Parallax Serial Terminal" PUB Main | str bytefill(@num, 0, 11) pst.Start(115_200) pause(500) 'Assign and print num := 49 pst.str(string("Print Number....")) pst.dec(num) pst.char(13) pst.str(string("Print Char......")) pst.char(num) DisplayMemory(@num, 5, true) 'Move num to a char array and view memory pst.str(string("Print String....")) charArray[0] := 52 charArray[1] := 57 pst.str(@charArray) DisplayMemory(@charArray, 5, true) PUB DisplayMemory(addr, len, isHex) | j pst.str(string(13,"-----------------------------------------------------",13)) pst.str(string(13, " ")) repeat j from 0 to $F pst.hex(j, 2) pst.char($20) pst.str(string(13, " ")) repeat j from 0 to $F pst.str(string("-- ")) pst.char(13) repeat j from 0 to len if(j == 0) pst.hex(0, 4) pst.char($20) pst.char($20) if(isHex) pst.hex(byte[addr + j], 2) else if(byte[addr+j] < $20 OR byte[addr+j] > $7E) if(byte[addr+j] == 0) pst.char($20) else pst.hex(byte[addr+j], 2) else pst.char($20) pst.char(byte[addr+j]) pst.char($20) if((j+1) // $10 == 0) pst.char($0D) pst.hex(j+1, 4) pst.char($20) pst.char($20) pst.char(13) pst.char(13) pst.str(string("Start: ")) pst.dec(addr) pst.str(string(" Len: ")) pst.dec(len) pst.str(string(13,"-----------------------------------------------------",13,13)) PRI pause(Duration) waitcnt(((clkfreq / 1_000 * Duration - 3932) #> 381) + cnt) return
You must query the RTC when you want to know the current time - readTime. Otherwise you'll get whatever is in the buffer from the last query.An example being your statement in the posts above that "What ya need to do is convert the ASCII strings to numbers. Then pass the numbers to the writeTime method." Since (I thought) that I had already tried that using the nums.decn(x) structure from SimpleNumbers.spin, and it failed completely, it took a while for me to circle back around and try it using the str.toInteger() method on the array variables. My error originally was apparently to apply the method at the wrong point in the process.
But even now, having found how to make it work, I am still very unclear as to why it would work at one place in the assignment process, but not in the other...
But I've been on this today now for over nine hours. My brain is fried, and I'm guessing you're probably pretty tired of seeing my name in this forum today, so I'm done for tonight.
One thing I can assure you of - I've been working at learning this thing for weeks now, non stop, reading literally hundreds of posts, viewing many, many code examples, going through the OBEX, and trying, trying, and trying all sorts of bits of code. I'm not trying to get one thing working - I'm having to get literally dozens of different functions working and tied together in the framework of the Spinneret. And I'm having to do it all at once. If there's one thing I'm doing - and I know you can't see it, and I'm sure it doesn't look like it - but I am doing the work on my end. I have managed to learn an enormous amount in a relatively short period of time - at least comparatively to where I started - but it's still not even close to a beginning with regards to where I need to be with this stuff in about two weeks.
And I very much recognize the time and effort it takes to help someone learn, especially on a complex subject like Spin and the Propeller. Thank you very much - I hope I can repay the favor some day.
Dave
PS., Adding rtc.readTime before the block of rtc.clockMonth, etc., did the trick - but I can't figure out why it was incrementing the time before, without that statement in there. When I'm not under such tight time constraints, I'll go back to that and figure out what of the things I added made the change.
To add on to what Mike G. said earlier about ASCII conversion and such, I thought I'd share this chart with you.
Here is a nice "conversion" chart you can use with Mikes earlier reply about ASCII encoding. This chart shows you the equivalents of several data encoding schemes as well as their binary representation. Copy it and stuff it into a spin file as a commented section for future reference if you'd like.
{ ASCII control characters (character code 0-31) The first 32 characters in the ASCII-table are unprintable control codes and are used to control peripherals such as printers. ASCII printable characters (character code 32-127) Codes 32-127 are common for all the different variations of the ASCII table, they are called printable characters, represent letters, digits, punctuation marks, and a few miscellaneous symbols. You will find almost every character on your keyboard. Character 127 represents the command DEL. The extended ASCII codes (character code 128-255) There are several different variations of the 8-bit ASCII table. The table below is according to ISO 8859-1, also called ISO Latin-1. Codes 129-159 contain the Microsoft
Windows Latin-1 extended characters.
DEC OCT HEX BIN Symbol HTML HTML Description
Number Name
---------------------------------------------------------------------------
0 000 00 00000000 NUL Null char
1 001 01 00000001 SOH Start of Heading
2 002 02 00000010 STX Start of Text
3 003 03 00000011 ETX End of Text
4 004 04 00000100 EOT End of Transmission
DEC OCT HEX BIN Symbol HTML HTML Description
Number Name
---------------------------------------------------------------------------
5 005 05 00000101 ENQ Enquiry
6 006 06 00000110 ACK Acknowledgment
7 007 07 00000111 BEL Bell
8 010 08 00001000 BS Back Space
9 011 09 00001001 HT Horizontal Tab
10 012 0A 00001010 LF
Line Feed
11 013 0B 00001011 VT Vertical Tab
12 014 0C 00001100 FF Form Feed
13 015 0D 00001101 CR
Carriage Return
14 016 0E 00001110 SO Shift Out / X-On
15 017 0F 00001111 SI Shift In / X-Off
16 020 10 00010000 DLE Data Line Escape
17 021 11 00010001 DC1 Device Control 1 (oft. XON)
18 022 12 00010010 DC2 Device Control 2
19 023 13 00010011 DC3 Device Control 3 (oft. XOFF)
20 024 14 00010100 DC4 Device Control 4
21 025 15 00010101 NAK Negative Acknowledgement
22 026 16 00010110 SYN Synchronous Idle
23 027 17 00010111 ETB End of Transmit Block
24 030 18 00011000 CAN Cancel
25 031 19 00011001 EM End of Medium
26 032 1A 00011010 SUB Substitute
27 033 1B 00011011 ESC Escape
28 034 1C 00011100 FS File Separator
29 035 1D 00011101 GS Group Separator
30 036 1E 00011110 RS Record Separator
DEC OCT HEX BIN Symbol HTML HTML Description
Number Name
---------------------------------------------------------------------------
31 037 1F 00011111 US Unit Separator
32 040 20 00100000 Space
33 041 21 00100001 ! ! Exclamation mark
34 042 22 00100010 " " " Double quotes (or speech marks)
35 043 23 00100011 # # Number
36 044 24 00100100 $ $ Dollar
37 045 25 00100101 % % Procenttecken
38 046 26 00100110 & & & Ampersand
39 047 27 00100111 ' ' Single quote
40 050 28 00101000 ( ( Open parenthesis (or open bracket)
41 051 29 00101001 ) ) Close parenthesis (or close bracket)
42 052 2A 00101010 * * Asterisk
43 053 2B 00101011 + + Plus
44 054 2C 00101100 , , Comma
45 055 2D 00101101 - - Hyphen
46 056 2E 00101110 . . Period, dot or full stop
47 057 2F 00101111 / / Slash or divide
48 060 30 00110000 0 0 Zero
49 061 31 00110001 1 1 One
50 062 32 00110010 2 2 Two
51 063 33 00110011 3 3 Three
52 064 34 00110100 4 4 Four
53 065 35 00110101 5 5 Five
54 066 36 00110110 6 6 Six
55 067 37 00110111 7 7 Seven
56 070 38 00111000 8 8 Eight
DEC OCT HEX BIN Symbol HTML HTML Description
Number Name
---------------------------------------------------------------------------
57 071 39 00111001 9 9 Nine
58 072 3A 00111010 : : Colon
59 073 3B 00111011 ; ; Semicolon
60 074 3C 00111100 < < < Less than (or open angled bracket)
61 075 3D 00111101 = = Equals
62 076 3E 00111110 > > > Greater than (or close angled bracket)
63 077 3F 00111111 ? ? Question mark
64 100 40 01000000 @ @ At symbol
65 101 41 01000001 A A Uppercase A
66 102 42 01000010 B B Uppercase B
67 103 43 01000011 C C Uppercase C
68 104 44 01000100 D D Uppercase D
69 105 45 01000101 E E Uppercase E
70 106 46 01000110 F F Uppercase F
71 107 47 01000111 G G Uppercase G
72 110 48 01001000 H H Uppercase H
73 111 49 01001001 I I Uppercase I
74 112 4A 01001010 J J Uppercase J
75 113 4B 01001011 K K Uppercase K
76 114 4C 01001100 L L Uppercase L
77 115 4D 01001101 M M Uppercase M
78 116 4E 01001110 N N Uppercase N
79 117 4F 01001111 O O Uppercase O
80 120 50 01010000 P P Uppercase P
81 121 51 01010001 Q Q Uppercase Q
82 122 52 01010010 R R Uppercase R
DEC OCT HEX BIN Symbol HTML HTML Description
Number Name
---------------------------------------------------------------------------
83 123 53 01010011 S S Uppercase S
84 124 54 01010100 T T Uppercase T
85 125 55 01010101 U U Uppercase U
86 126 56 01010110 V V Uppercase V
87 127 57 01010111 W W Uppercase W
88 130 58 01011000 X X Uppercase X
89 131 59 01011001 Y Y Uppercase Y
90 132 5A 01011010 Z Z Uppercase Z
91 133 5B 01011011 [ [ Opening bracket
92 134 5C 01011100 \ \ Backslash
93 135 5D 01011101 ] ] Closing bracket
94 136 5E 01011110 ^ ^ Caret - circumflex
95 137 5F 01011111 _ _ Underscore
96 140 60 01100000 ` ` Grave accent
97 141 61 01100001 a a Lowercase a
98 142 62 01100010 b b Lowercase b
99 143 63 01100011 c c Lowercase c
100 144 64 01100100 d d Lowercase d
101 145 65 01100101 e e Lowercase e
102 146 66 01100110 f f Lowercase f
103 147 67 01100111 g g Lowercase g
104 150 68 01101000 h h Lowercase h
105 151 69 01101001 i i Lowercase i
106 152 6A 01101010 j j Lowercase j
107 153 6B 01101011 k k Lowercase k
108 154 6C 01101100 l l Lowercase l
DEC OCT HEX BIN Symbol HTML HTML Description
Number Name
---------------------------------------------------------------------------
109 155 6D 01101101 m m Lowercase m
110 156 6E 01101110 n n Lowercase n
111 157 6F 01101111 o o Lowercase o
112 160 70 01110000 p p Lowercase p
113 161 71 01110001 q q Lowercase q
114 162 72 01110010 r r Lowercase r
115 163 73 01110011 s s Lowercase s
116 164 74 01110100 t t Lowercase t
117 165 75 01110101 u u Lowercase u
118 166 76 01110110 v v Lowercase v
119 167 77 01110111 w w Lowercase w
120 170 78 01111000 x x Lowercase x
121 171 79 01111001 y y Lowercase y
122 172 7A 01111010 z z Lowercase z
123 173 7B 01111011 { { Opening brace
124 174 7C 01111100 | | Vertical bar
125 175 7D 01111101 } } Closing brace
126 176 7E 01111110 ~ ~ Equivalency sign - tilde
127 177 7F 01111111 Delete
128 200 80 10000000 € Euro sign
129 201 81 10000001
130 202 82 10000010 ‚ Single low-9 quotation mark
131 203 83 10000011 ƒ Latin small letter f with hook
132 204 84 10000100 „ Double low-9 quotation mark
133 205 85 10000101
…Horizontal ellipsis
134 206 86 10000110 †Dagger
DEC OCT HEX BIN Symbol HTML HTML Description
Number Name
---------------------------------------------------------------------------
135 207 87 10000111 ‡Double dagger
136 210 88 10001000 ˆ Modifier letter circumflex accent
137 211 89 10001001 ‰Per mille sign
138 212 8A 10001010 ŠLatin capital letter S with caron
139 213 8B 10001011 ‹Single left-pointing angle quotation
140 214 8C 10001100 Œ Latin capital ligature OE
141 215 8D 10001101
142 216 8E 10001110 Latin captial letter Z with caron
143 217 8F 10001111
144 220 90 10010000
145 221 91 10010001 ‘ Left single quotation mark
146 222 92 10010010 ’ Right single quotation mark
147 223 93 10010011 “ Left double quotation mark
148 224 94 10010100 ” Right double quotation mark
149 225 95 10010101 • Bullet
150 226 96 10010110 – En dash
151 227 97 10010111 — Em dash
152 230 98 10011000 ˜ Small tilde
153 231 99 10011001 ™ Trade mark sign
154 232 9A 10011010 šLatin small letter S with caron
155 233 9B 10011011 ›Single right-pointing angle quotation mark
156 234 9C 10011100 œ Latin small ligature oe
157 235 9D 10011101
158 236 9E 10011110 Latin small letter z with caron
159 237 9F 10011111 ÿ Latin capital letter Y with diaeresis
160 240 A0 10100000 * Non-breaking space
161 241 A1 10100001 ¡ ¡ ¡ Inverted exclamation mark
162 242 A2 10100010 ¢ ¢ ¢ Cent sign
163 243 A3 10100011 £ £ £ Pound sign
164 244 A4 10100100 ¤ ¤ ¤Currency sign
165 245 A5 10100101 ¥ ¥ ¥ Yen sign
166 246 A6 10100110 ¦ ¦ ¦Pipe, Broken vertical bar
167 247 A7 10100111 § § § Section sign
168 250 A8 10101000 ¨ ¨ ¨ Spacing diaeresis - umlaut
169 251 A9 10101001
© Copyright sign
170 252 AA 10101010 ª ª ª Feminine ordinal indicator
171 253 AB 10101011 « « « Left double angle quotes
172 254 AC 10101100 ¬ ¬ ¬ Not sign
173 255 AD 10101101 * * * Soft hyphen
174 256 AE 10101110
® Registered trade mark sign
175 257 AF 10101111 ¯ ¯ ¯ Spacing macron - overline
176 260 B0 10110000 ° ° ° Degree sign
177 261 B1 10110001 ± ± ±Plus-or-minus sign
178 262 B2 10110010 ² ² ² Superscript two - squared
179 263 B3 10110011 ³ ³ ³ Superscript three - cubed
180 264 B4 10110100 ´ ´ ´ Acute accent - spacing acute
181 265 B5 10110101 µ µ µ Micro sign
182 266 B6 10110110 ¶ ¶ ¶ Pilcrow sign - paragraph sign
183 267 B7 10110111 · · ·Middle dot - Georgian comma
184 270 B8 10111000 ¸ ¸ ¸ Spacing cedilla
185 271 B9 10111001 ¹ ¹ ¹ Superscript one
186 272 BA 10111010 º º º Masculine ordinal indicator
187 273 BB 10111011 » » » Right double angle quotes
DEC OCT HEX BIN Symbol HTML HTML Description
Number Name
---------------------------------------------------------------------------
188 274 BC 10111100 ¼ ¼ ¼Fraction one quarter
189 275 BD 10111101 ½ ½ ½Fraction one half
190 276 BE 10111110 ¾ ¾ ¾Fraction three quarters
191 277 BF 10111111 ¿ ¿ ¿Inverted question mark
192 300 C0 11000000 À À ÀLatin capital letter A with grave
193 301 C1 11000001 Á Á ÁLatin capital letter A with acute
194 302 C2 11000010 Â Â Â Latin capital letter A with circumflex
195 303 C3 11000011 Ã Ã ÃLatin capital letter A with tilde
196 304 C4 11000100 Ä Ä Ä Latin capital letter A with diaeresis
197 305 C5 11000101 Å Å Å Latin capital letter A with ring above
198 306 C6 11000110 Æ Æ Æ Latin capital letter AE
199 307 C7 11000111 Ç Ç ÇLatin capital letter C with cedilla
200 310 C8 11001000 È È ÈLatin capital letter E with grave
201 311 C9 11001001 É É ÉLatin capital letter E with acute
202 312 CA 11001010 Ê Ê Ê Latin capital letter E with circumflex
203 313 CB 11001011 Ë Ë Ë Latin capital letter E with diaeresis
204 314 CC 11001100 Ì Ì ÌLatin capital letter I with grave
205 315 CD 11001101 Í Í ÍLatin capital letter I with acute
206 316 CE 11001110 Î Î Î Latin capital letter I with circumflex
207 317 CF 11001111 Ï Ï Ï Latin capital letter I with diaeresis
208 320 D0 11010000 Ð Ð Ð Latin capital letter ETH
209 321 D1 11010001 Ñ Ñ ÑLatin capital letter N with tilde
210 322 D2 11010010 Ò Ò ÒLatin capital letter O with grave
211 323 D3 11010011 Ó Ó ÓLatin capital letter O with acute
212 324 D4 11010100 Ô Ô Ô Latin capital letter O with circumflex
213 325 D5 11010101 Õ Õ ÕLatin capital letter O with tilde
214 326 D6 11010110 Ö Ö Ö Latin capital letter O with diaeresis
215 327 D7 11010111 × × × Multiplication sign
216 330 D8 11011000 Ø Ø ØLatin capital letter O with slash
217 331 D9 11011001 Ù Ù ÙLatin capital letter U with grave
DEC OCT HEX BIN Symbol HTML HTML Description
Number Name
---------------------------------------------------------------------------
218 332 DA 11011010 Ú Ú ÚLatin capital letter U with acute
219 333 DB 11011011 Û Û Û Latin capital letter U with circumflex
220 334 DC 11011100 Ü Ü Ü Latin capital letter U with diaeresis
221 335 DD 11011101 Ý Ý ÝLatin capital letter Y with acute
222 336 DE 11011110 Þ Þ Þ Latin capital letter THORN
223 337 DF 11011111 ß ß ß Latin small letter sharp s - ess-zed
224 340 E0 11100000 à à àLatin small letter a with grave
225 341 E1 11100001 á á áLatin small letter a with acute
226 342 E2 11100010 â â â Latin small letter a with circumflex
227 343 E3 11100011 ã ã ãLatin small letter a with tilde
228 344 E4 11100100 ä ä ä Latin small letter a with diaeresis
229 345 E5 11100101 å å å Latin small letter a with ring above
230 346 E6 11100110 æ æ æ Latin small letter ae
231 347 E7 11100111 ç ç çLatin small letter c with cedilla
232 350 E8 11101000 è è èLatin small letter e with grave
233 351 E9 11101001 é é éLatin small letter e with acute
234 352 EA 11101010 ê ê ê Latin small letter e with circumflex
235 353 EB 11101011 ë ë ë Latin small letter e with diaeresis
236 354 EC 11101100 ì ì ìLatin small letter i with grave
237 355 ED 11101101 í í íLatin small letter i with acute
238 356 EE 11101110 î î î Latin small letter i with circumflex
239 357 EF 11101111 ï ï ï Latin small letter i with diaeresis
240 360 F0 11110000 ð ð ð Latin small letter eth
241 361 F1 11110001 ñ ñ ñLatin small letter n with tilde
242 362 F2 11110010 ò ò òLatin small letter o with grave
243 363 F3 11110011 ó ó óLatin small letter o with acute
244 364 F4 11110100 ô ô ô Latin small letter o with circumflex
245 365 F5 11110101 õ õ õLatin small letter o with tilde
246 366 F6 11110110 ö ö ö Latin small letter o with diaeresis
247 367 F7 11110111 ÷ ÷ ÷Division sign
DEC OCT HEX BIN Symbol HTML HTML Description
Number Name
---------------------------------------------------------------------------
248 370 F8 11111000 ø ø øLatin small letter o with slash
249 371 F9 11111001 ù ù ùLatin small letter u with grave
250 372 FA 11111010 ú ú úLatin small letter u with acute
251 373 FB 11111011 û û û Latin small letter u with circumflex
252 374 FC 11111100 ü ü ü Latin small letter u with diaeresis
253 375 FD 11111101 ý ý ýLatin small letter y with acute
254 376 FE 11111110 þ þ þ Latin small letter thorn
255 377 FF 11111111 ÿ ÿ ÿ Latin small letter y with diaeresis
}
The key to remember (using Mike's reply with 49 decimal as the example) is that ASCII characters are SINGLE characters. If it's an ASCII value, it's ONE character, and ALWAYS AND ONLY ONE character. EACH of these characters is represented by ONE byte of data. That's why there are only 256 ASCII characters (8 bits in 1 byte only has 256 possible combinations)
So... as Mike said, the DECIMAL value 49 requires TWO ASCII characters. The first is a 4 and the next is a 9.
If you look at the chart, the DECIMAL value 49 has a binary value of 00110001 which is 8 bits (1 byte) of data. But this binary value equals the character 1 in ASCII. Since ASCII needs 1 byte of data PER CHARACTER, we have to convert between data types to make everything correct.
To create the ASCII value of "49" we actually have to have TWO bytes of data. The first byte would be for the ASCII character 4, which is a binary value of 00110100 when you look up the ASCII value 4. The second byte is ASCII value 9 which is a binary value of 00111001 when you look up the ASCII value of 9.
The same principle applies to all ASCII values equally.
If you wanted to create the ASCII string of characters x a n a t o s you would need to use 7 bytes of data. The first would be the ASCII value x which is binary value 01111000 ... which is 8 bits (1 byte) of data... and on and on.
Now, look at the chart one more time. If you want to convert the ASCII value to the decimal equivalent, look at the chart really closely. Look at the ASCII value 4. Then look at the DECIMAL EQUIVALENT of it. You'll see the DECIMAL EQUIVALENT is 52... which is 48 more than 4. Now look at the rest of the ASCII number characters and their decimal equivalents... (hint, hint)
You can build your own conversion methods to convert from ASCII characters to the corresponding DECIMAL value, and vice versa. However, Mike has an object in HTTPServ for the spinneret called StringMethods which has methods that do this already and they work perfectly. Also, there are other objects on the OBEX that do tasks like this for you.
I hope this'll help you a bit. Hang in there!
Robert
Robert - thank YOU for this - visualization of the data works really well for me and this does that very well. I have a chart similar to this for my web stuff, but it is only for the &#xxx; style encoding - not hex and binary! This is great.
To follow along further in this, I could also look at the high-byte of the string "49", and just take the least significant 4 bits - which is 0100, or 4... and then go to the next byte, and take those LSBs, which are 1001, or 9. So I can either subtract 48, or take the 4 LSBs - if I'm only interested in turning an ASCII number into a DEC anyway - correct?
And lastly, from what I'm understanding now, and to use your example above, if I had x a n a t o s as a STRING - it would really be eight bytes - seven for the ASCII characters, and one byte of all zeros - am I correct on that count?
Now applying this to my understanding for setting my RTC, for example (and yes, I will be using SNTP, but this RTC setting thing has really helped me to understand how to get data from the user into the Spinneret/Propeller via a web page, so it remains a useful training aid for me here) - here's the code line that takes the input fields from my HTML form and places that string into my variable array (or one line of it in this case):
CKSetArray[6] := Request.Post(id, string("Syear"))
So if the user entered 13 for the year, the actual data being held in CKSetArray[6] (specified as a long in my VAR block) would actually be an ASCII "1" and an ASCII "3", plus a byte of all zeros - am I still correct there?
Then, I'm using one of the StringMethods to convert those strings to integers:
Yr := 2000 + str.ToInteger(CKSetArray[6])
And this works well now. Now StringMethods.spin is written in PASM, and after speaking with Mike this morning, I was able to understand what's happening in that PASM section a bit more (no pun intended...), but if you can bear with my long-winded post just another second - would I be correct that A routine to convert (not necessarily THE routine), would be for the program to take the string value, go to the first character (ASCII character), and either subtract 48, or alternately take the LS 4 Bits, get that number, and multiply it by 10, then go to the next number, take those LS 4 bits, add them to the result of the operation on the previous number, and if there were no subsequent numbers, then that's the answer. If there was another number, it would multiply by 10 again, get the next number, add that in, and so forth, until it finally encountered that terminal 0, correct?? Have I got this finally???? :-)
0
(That's a terminal zero for this post)
Yes.
Yes. You would use 7 bytes for the xanatos characters and then a zero-termination byte, which is simply a byte at the end of the string with a binary value of 0000_0000 (decimal value of 0)
I'm not sure about this one, but it sounds almost correct. I think the actual binary value placed in CKSetArray[6] from this would be 0011_0001 0011_0011 0000_0000 0000_0000 which basically should be fine. One way to find out would be use PST to display the value in binary for you once like this:
Yr := 2000 + str.ToInteger(CKSetArray[6]) pst.Bin(CKSetArray[6],32)
But, Mike or someone else might be able to answer this one more accurately. Sorry!
Well, there are many ways to do this. But trying to following your strategy, you could do the following:
Let's say you set up an array of BYTES (not longs) to hold a 4 digit number where each byte is actually an ASCII character, and you do so using your VAR section. You'd want to declare 5 bytes, so that your first 4 bytes hold the data and the last byte is always going to be zero (your zero termination byte.)
You'd use something like this:
VAR byte my_number[5]
Or you could use your DAT section a couple different ways, like this:
DAT my_number byte "0","0","0","0",0 'or my_number byte $0[4],0 'or my_number byte 0[5] 'or my_number byte 0[4],0
One way or another, you've just set aside a "place holder" for 4 bytes of data where each byte will contain an ASCII characters binary value. You've also set up a zero-termination byte.
So, let's suppose the number you want to place into these bytes is "1234" where each digit is actually the ASCII value of that digit.
byte 0 = 0011_0001 = ASCII 1 = DEC 49 (left most byte)
byte 1 = 0011_0010 = ASCII 2 = DEC 50
byte 2 = 0011_0011 = ASCII 3 = DEC 51
byte 3 = 0011_0100 = ASCII 4 = DEC 52
byte 4 = 0000_0000 = zero.
Since it's a 4 digit number, the first byte (byte 0) represents the ASCII value of the digit 1, which is in the the 1000's place. So... subtract 48 decimal from this ASCII value to get the DECIMAL value. Then multiply by 1000.
Do the same for byte 1... subract 48 and multiply by 100.
Do the same for byte 2... subract 48 and multiply by 10.
Do the same for byte 3... subract 48 and multiply by 1. Or not.
Add 'em all together and you've got your brand new shiny decimal value.
Note- to do it this way, you don't actually *need* a terminating zero because we don't actually do anything with it. But if you use Mikes object (which I strongly recommend) like you wrote above with
Yr := 2000 + str.ToInteger(CKSetArray[6]), you DO need the terminating zero because his method requires it. It uses it to say "oh, I've got to the end of the ASCII bytes, so I'm done now."
Thank you for that. I really needed to feel like I was understanding SOMETHING on more than just a superficial level...
And that VAR vs DAT block example of the many different but equivalent ways to set up my byte arrays (or long arrays too I would guess) was very helpful. If I had encountered those different configurations in different programs it would have taken me quite a while to realize they were all saying the same thing.
Unfortunately I can't spend much time to bask in the glory of having finally understood how to work with these items in Spin, because I am trying to figure out why the RESTful Services example isn't working on my Spinneret - when I go to aled.htm, I get redirected to led.htm immediately, and clicking the led on/off links does nothing...
This whole process has been like pulling teeth for me... but it sure feels good when I finally do get some piece of it working well - not just because of a happy accident, but because I genuinely understand it.
Thank you both for your help - very much.
Dave