Spinneret refresh webpage
Hello all,
I wonder why I always need to refresh the webpage to open the user LED on Spinerret ? I use the demo code on http://spinneret.servebeer.com:5000/client/sget.htm
When I click on LED ON the URL changes but the LED does not light. I have to refresh the page and then the LED turn on.
I wonder why I always need to refresh the webpage to open the user LED on Spinerret ? I use the demo code on http://spinneret.servebeer.com:5000/client/sget.htm
When I click on LED ON the URL changes but the LED does not light. I have to refresh the page and then the LED turn on.
Comments
The referenced page shows how to invoke a client get and has nothing to do with LEDs.
Please post your source code.
There is all the files I use in my program.
I use Safari or Google Chrome.
The globalCache is set to 0... can you tell me what dose it make?
And can you tell me why I see all the Header code on the webpage?
This Demo work fine! But now if I want to access this Demo outside of my Home Network on internet what is the next step I have to do?
Thank you very much for your help!
You must configure your router - assuming you have a router.
See the port forwarding guide which can be found on the Spinneret tutorial
http://forums.parallax.com/attachment.php?attachmentid=82190&d=1308163657
The second time I click on LED ON the page load from cache.. whatever globalCache is 1 or 0 do you know why ?
check the PIR LED
{{───────────────────────────────────────────────── 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 } 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 = 01 '$300 = 768 UDP_PROTOCOL = 10 '$200 = 512 TCP_CONNECT = $04 '$100 = 256 DELAY = $05 MAX_TRIES = $05 #0, DONE, PUT, CD DAT mac byte $00, $08, $DC, $16, $F2, $73 subnet byte 255, 255 ,255, 0 gateway byte 24,122,231,1 ip byte 169,254,150,62 port word 5555 remoteIp byte 24,122,231,195 remotePort word 80 uport word 5050 emailIp byte 0, 0, 0, 0 emailPort word 25 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 00 openState byte 00 listenState byte 00 establishedState byte 00 closingState byte 00 closeWaitState byte 00 lastEstblState byte 00 lastEstblStateDebug byte 00 udpListen byte 00 tcpMask byte 11 udpMask byte 00 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 $0 dynamicContentPtr long @txdata VAR long StackSpace[20] OBJ pst : "Parallax Serial Terminal" Socket : "W5100_Indirect_Driver" SDCard : "S35390A_SD-MMC_FATEngineWrapper" Request : "Request" Response : "Response" str : "StringMethods" rtc : "S35390A_RTCEngine" 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) 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 repeat 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 PRI GotoMain Main 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 qstr := Request.Post(id, string("led")) if (strcomp( string("on"), qstr )) LedStatus(1) else LedStatus(0) return [COLOR=#ff0000]PRI Led(id) | qstr[/COLOR] [COLOR=#ff0000] '' Get the query string value[/COLOR] [COLOR=#ff0000] qstr := Request.Get(id, string("led"))[/COLOR] [COLOR=#ff0000] if (strcomp( string("on"), qstr ))[/COLOR] [COLOR=#ff0000] LedStatus(1)[/COLOR] [COLOR=#ff0000] else[/COLOR] [COLOR=#ff0000] LedStatus(0)[/COLOR] [COLOR=#ff0000] return[/COLOR] [COLOR=#ff0000]PRI LedStatus(state)[/COLOR] [COLOR=#ff0000] dira[23]~~[/COLOR] [COLOR=#ff0000] outa[23] := state[/COLOR] [COLOR=#ff0000] return[/COLOR] 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 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 test 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) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '' Socekt helpers '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PRI GetTcpSocketMask(id) return id & tcpMask PRI DecodeId(value) | tmp if(01 & value) return 0 if(10 & value) return 1 if(00 & value) return 2 if(00 & 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 InitializeSocketForEmail(id) Socket.Initialize(id, TCP_PROTOCOL, port, emailPort, @emailIp) 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. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }}
But how can I turn off the cache directly on the spinneret to provide those problem whit all user...?
Maybe i forgot something because all the Header appear on the top of the web browser... and if i delete the Heather it dose nothing.
In the 14.9 Cache-Control i see "no-cache" it could be a solution ? were can i put the "no-cache" to make it work?
OBJ rtc : "S35390A_RTCEngine.spin" CON MATCH_PATTERN = $03 TO_STRING = $02 TO_INTEGER = $01 '#1, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday '#1, January, February, March, April, May, June, July, August, September, October, November, December DAT strParams long $0 txdata long $0 conLen long $FFFF_FFFF getTime byte $1 hashTable long $0[30] headerMap long $0[30] extensions byte "htm",0, "xml",0, "xsl",0, "shp",0, "txt",0, { } "pdf",0, "zip",0, "jpg",0, "png",0, "gif",0, { } "css",0, "js", 0, "ico",0, "wmv",0, "wav",0, { } "oct",10 hearerline byte "text/html",0, "text/xml",0, "text/xml",0, "text/html",0, "text/plain; charset=utf-8",0,{ } "application/pdf",0, "application/zip",0, "image/jpeg",0, "image/png",0, "image/gif",0, { } "text/css",0, "application/javascript",0, "image/x-icon",0, "video/x-ms-wmv",0, "audio/x-wav",0, { } "application/octet-stream",0 header1 byte "HTTP/1.1 ",0 server byte "Server: Spinneret/2.1", 13, 10, 0 [COLOR=#ff0000] cache byte "Cache-Control: no-cache", 13, 10, 0[/COLOR] contenttype byte "Content-Type: ",0 contentlen byte "Content-Length: 5",0 expires byte "Expires: Wed, 01 Feb 2000 01:00:00 GMT", 13, 10, 0 newline byte 13, 10, 0 ok byte "200 OK" ,13, 10, 0 notfound byte "404 Not Found", 13, 10, 0 genError byte "<html><head><title>Error</title></head><body><h1>Error</h1></body></html>",0 tempNum byte "0000",0 PUB Constructor(stringMethods, txbuff) strParams := stringMethods txdata := txbuff InitializeHashIndex rtc.RTCEngineStart(29, 28, -1) PUB WriteExpireHeader(value) getTime := value PUB SetContentLength(value) conLen := value PRI FillExpireHeader(cachePage) | ptr, offset, temp 'ToString(integerToConvert, destinationPointer) 'Expires: Wed, 01 Feb 2000 01:00:00 GMT ptr := @expires + 9 rtc.readTime offset := -1 if(cachePage) offset := -1 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 + offset, ptr) ptr += 5 FillTimeHelper(rtc.clockHour , ptr) ptr += 3 FillTimeHelper(rtc.clockMinute , ptr) ptr += 3 FillTimeHelper(rtc.clockSecond, ptr) return @expires PRI FillTimeHelper(number, ptr) | offset offset := 0 if(number < 10) offset := 1 ToString(@number, @tempNum) bytemove(ptr+offset, @tempNum, strsize(@tempNum)) PUB BuildHeader(extension, statusCode, cachePage) | ptr, idx if(getTime) FillExpireHeader(cachePage) ptr := txdata 'Header Line 1 bytemove(ptr, @header1, strsize(@header1)) ptr += strsize(@header1) 'Status code if(statusCode == 404) bytemove(ptr, @notfound, strsize(@notfound)) ptr += strsize(@notfound) else bytemove(ptr, @ok, strsize(@ok)) ptr += strsize(@ok) 'Content-Type: bytemove(ptr, @contenttype, strsize(@contenttype)) ptr += strsize(@contenttype) idx := GetContentType(extension) bytemove(ptr, idx, strsize(idx)) ptr += strsize(idx) 'Newline bytemove(ptr, @newline, strsize(@newline)) ptr += strsize(@newline) 'Server: bytemove(ptr, @server, strsize(@server)) ptr += strsize(@server) 'Cache bytemove(ptr, @cache, strsize(@cache)) ptr += strsize(@cache) 'Content-Length: if(conLen > -1) bytemove(ptr, @contentlen, strsize(@contentlen)) ptr += strsize(@contentlen) ptr += strsize(ToString(@conLen, ptr)) bytemove(ptr, @newline, strsize(@newline)) ptr += strsize(@newline) 'Expires: bytemove(ptr, @expires, strsize(@expires)) ptr += strsize(@expires) 'Newline - Newline bytemove(ptr, @newline, strsize(@newline)) ptr += strsize(@newline) if(statusCode <> 200) bytemove(ptr, @genError, strsize(@genError)) ptr += strsize(@genError) return ptr - txdata PUB GetContentType(ptr) | i, hashcode hashcode := CreateExtensionHash(ptr) repeat i from 0 to 30 if HashTable[i] == hashcode return HeaderMap[i] return 0 PUB CreateExtensionHash(ptr) | char, t1, i t1 := i := 0 repeat until byte[ptr] == 0 char := byte[ptr++] t1 |= char << (8 * i++) return t1 PUB InitializeHashIndex | exPtr, headPtr, idx, char, t1, i exPtr := @extensions headPtr := @hearerline idx := 0 'Create file extension hash repeat until byte[exPtr] == 10 or idx > 29 i := 0 HashTable[idx] := 0 repeat until byte[exPtr] == 0 char := byte[exPtr++] HashTable[idx] |= char << (8 * i++) idx++ exPtr++ 'Map the content-type string to the hash table by index i := 0 HeaderMap[i++] := @hearerline repeat idx-1 headPtr += strsize(headPtr) + 1 HeaderMap[i++] := headPtr PRI ToString(integerToConvert, destinationPointer) long[strParams][0] := $00 long[strParams][1] := $FFFF_FFFF long[strParams][2] := integerToConvert long[strParams][3] := destinationPointer long[strParams][4] := $00 long[strParams][5] := $00 long[strParams][0] := TO_STRING repeat until long[strParams][0] == $00 return destinationPointer {{ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ 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. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }}
As far as getting around the cache, there may be a solution using an iframe tag where the Spinneret is referenced from another page as the iframe contents ... This also has an advantage of not affecting the browser history.
I'll have to think about it to implement something with the Spinneret, but I have done something similar in the past allowing for bi-directional dynamic data flow between two pages.
Yes this could be cool !
ServerCode.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd"> <script type="text/javascript"> var Seconds = -1 function Send2Client(){ var msg = document.getElementById('Server2Client').value; // Get value to send to Client // window.parent.document.getElementById("message").innerHTML = msg; // Send value to Client via a parent call // document.getElementById('Server2Client').value = ""; // Clear local value } function StartCounter(){ if(Seconds == -1){ // Check if counter has already started setInterval(function(){ incrementCounter() },1000); // if not start the counter and increment every second } } function incrementCounter(){ // increment counter and send to Client Seconds++; window.parent.document.getElementById("counter").innerHTML = Seconds+" seconds"; } function pageupdate(_interval){ // set the page update interval ; Mainly this is for the setInterval(function(){ // DEMO ... normally the server would poll the data when document.getElementById("ClientMessage").innerHTML = window.parent.document.getElementById('Client2Server').value; },_interval); // it wanted to see the Client content. But in this example } // takes place every 1/2 second </script> <html> <body onload=pageupdate(500) bgcolor="#E6E6FA"> <!-- load HTML and start update interval --> This is the Server Side:<br><br> Message to send to Client: <input type="text" id="Server2Client" name="Sendmsg" /><br> <!-- submit value to Client --> <button type="button" onclick=Send2Client()>Send</button><br> <button type="button" onclick=StartCounter()>Start Counter</button><br><br> <!-- start DEMO counter --> Message received from Client: <div id="ClientMessage"><!-- Client messsage will end up here --></div> <!-- Show message received from Client --> </body> </html>
ClientCode.html
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd"> <script type="text/javascript"> function GetValueTest(){ msg = "message from Server [" + document.getElementById("message").innerHTML + "]\n\r"; msg += "counter value from Server [" + document.getElementById("counter").innerHTML + "]"; alert(msg); } </script> <html> <!-- --------------------------------------------------------------------------------------------------------------------------- --> This is the Client Side:<br> <div id="message">Waiting for message from Server...<!-- Message from Server will end up here --></div><br> <div id="counter">Start Server Counter...<!-- Counter updates from Server will end up here --></div><br> Message to send to Server: <input type="text" id="Client2Server" /><br> <!-- This is interactive as you type, the Server 'sees' it based on the polling interval set on the server side i.e. when the server wants to see what you are typing --> <!-- Note: The javascript from the Server is what updates the 'message', 'counter', and reads from 'Client2Server' --> <!-- --------------------------------------------------------------------------------------------------------------------------- --> <!-- Create an iframe window so YOU can 'see' the server ... --> <!-- Note: normally the iframe would be hidden and only the server could control the outgoing data, but for this proof of concept DEMO the ifram is made visible --> <!-- <iframe src="ServerCode.html" id="frame1" scrolling="no" height=300 style="display:none;" ></iframe><br><br><br> --> <iframe src="ServerCode.html" id="frame1" scrolling="no" height=300 ></iframe><br><br><br> <!-- This just shows that we can get a variable update from the Server Side iframe rather than just HTML parent headers on the Client side--> <button type="button" onclick=GetValueTest()>Show data from Server as a javascript variable on the Client</button><br> </html>
Thank you !
I'm probably missing the point. As far as I can tell there are two pages executing JavaScript on the client. Data is passed on the DOM.
At some point you'll probably want to make an HTTP request which is invoked by the browser. The same caching rules will apply.
I would use AJAX over an iframe. To get around the cache, insert the current time to the URL querystring.
<html> <head> <title>GETing AJAX</title> <script language="javascript" type="application/javascript"> //Gets the browser specific XmlHttpRequest Object function getXmlHttpRequestObject() { if (window.XMLHttpRequest) { return new XMLHttpRequest(); } else if (window.ActiveXObject) { return new ActiveXObject("Microsoft.XMLHTTP"); } else { alert("Your Browser does not support AJAX!\nIt's about time to upgrade don't you think?"); } } //XmlHttpRequest object var req = getXmlHttpRequestObject(); var htmlTarget; function getRequest(resource, elementId) { // handle the case where a querystring is not detected var char = "&"; if(resource.indexOf("?", 0) == -1) { char = "?"; } if (req.readyState == 4 || req.readyState == 0) { req.open("GET", resource + char + 'ms=' + new Date().getTime(), true); req.onreadystatechange = handleResponse; htmlTarget = elementId; req.send(null); return false; } } function handleResponse() { if (req.readyState == 4) { var str = req.responseText; //alert(str); var placeholder = document.getElementById(htmlTarget); placeholder.innerHTML = str; } } </script> </head> <body style="background-color:beige;"> <div style="width:600px;margin:auto;background-color:white;padding:10px;"> <h1 style="text-align:center;color:blue;">LED</h1> <div style="text-align:center;"><a href="index.htm">Hello World</a> | <a href="led.htm">LED</a></div> <h2>Query String</h2> <p> <a href="led.htm" onClick="return getRequest('aled.htm?led=on', 'placeholder');">LED On </a> :: <a href="led.htm" onClick="return getRequest('aled.htm?led=off', 'placeholder');">LED Off</a> </p> <p>LED: <span id="placeholder">State</span></p> </div> </body> </html>
...an HTTP request happens within an iframe and does not effect the browser history or add or append data to the the parent browser URL tag. You can make the HTTP request dynamic by placing the iframe within a div and change the content with an innerHTML command in javascript.
First time poster and new spinneret/propeller user...I have tried to modify your code that is posted on http://spinneret.servebeer.com:5000/gstart/get.htm , and I do not get my local web page any more.
I ran through all Mike Gs instructions by the numbers, had it working until I added a servo and what I thought was the html adding a button for the servo (on/off)...
my code looks like :
PRI Dispatcher2(id)
''do some processing before sending the response
if(strcomp(Request.GetName(id), string("servo")))
Demo (id)
StaticFileHandler(id)
return
PRI Demo(id) | qstr
'' Get the query string value
qstr := Request.Get(id, string("servo"))
if (strcomp( string("on"), qstr ))
ServoStatus(1)
else
ServoStatus(0)
return
PRI ServoStatus(state)
cognew(MoveMotor(26),@StackSpace4) 'Start a new cog and run the MoveMotor method on it that outputs pulses on Pin 7
'The new cog that is started above continuously reads the "position" variable as it's changed by the example Spin code below
repeat
position:=100 'Start sending 1ms servo signal high pulses (100 * 10us = 1ms)
waitcnt(clkfreq+cnt) 'Wait for 1 second (1ms high pulses continue to be generated by the other cog)
position:=138 'Start sending 1.38ms servo signal high pulses (Center position)
waitcnt(clkfreq+cnt) 'Wait for 1 second (1.38ms high pulses continue to be generated by the other cog)
position:=50 'Start sending 0.5ms servo signal high pulses (Clockwise position)
waitcnt(clkfreq+cnt) 'Wait for 1 second (0.5ms high pulses continue to be generated by the other cog)
position:=225 'Start sending 2.25ms servo signal high pulses (Counterclockwise position)
waitcnt(clkfreq+cnt)
return
PUB MoveMotor(Pin) 'This method outputs a continuous stream of servo signal pulses on "Pin"
dira[Pin]~~ 'Set the direction of "Pin" to be an output
repeat 'Send out a continous train of pulses
outa[Pin]~~ 'Set "Pin" High
waitcnt((clkfreq/100_000)*position+cnt) 'Wait for the specifed position (units = 10 microseconds)
outa[Pin]~ 'Set "Pin" Low
waitcnt(clkfreq/100+cnt) 'Wait 10ms between pulses
...the rest of the code is using exactly what Mike had; I did add the requisite StackSpace for the new cogs, the variables, etc....the code loaded on to the Spinneret, I kept the index.htm page as is, then I modified the led.htm web page to look like:
<html>
<head>
<title>GETing to the Server</title>
</head>
<body>
<div style="width:600px;margin:auto;">
<h1 style="text-align:center;color:blue;">LED</h1>
<div style="text-align:center;"><a href="index.htm">Hello World</a> | <a href="led.htm">LED</a></div>
<p><a href="led.htm?led=on">LED On</a> :: <a href="led.htm?led=off">LED Off</a></p>
<p>LED: <span id="placeholder"></span></p>
<p><a href="led.htm?servo=on">SERVO On</a> :: <a href="led.htm?SERVO=off">SERVO Off</a></p>
<p>LED: <span id="placeholder"></span></p>
</div>
</div>
</body>
</html>
All the networking pieces worked fine, got the web page, turned on the led via the web server, then I did the above mods and all is no go. I get this message:
The connection was reset
The connection to the server was reset while the page was loading.
Please help!