Shop OBEX P1 Docs P2 Docs Learn Events
web server help needed — Parallax Forums

web server help needed

michaelmonsomichaelmonso Posts: 19
edited 2011-08-22 10:33 in General Discussion
trying to interface spinneret with manual gate operator open/close/lock push buttons. I have it set up through a low voltage relay connecting and operating one button push button (open) currently. im using the following demo code which would light an led but is actually set up to close a relay and bridge the push button control. i need code to say once on is selected to open the gate (light led) for 2 seconds and then shut off so that it would open the gate but not hold the gate open. i just want the webserver to toggle the gate on for 2 seconds and return to low without having to hit off

{{
─────────────────────────────────────────────────
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, $EF, $22
subnet byte 255, 255 ,255, 0
gateway byte 162, 228, 0, 1
ip byte 162, 228, 0, 120
port word 5000
remoteIp byte
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]
tempBuff 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 $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
SDCard.changeDirectory(@approot)
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)

'pst.str(string(13,"Dispatcher(id)",13))

''Do some processing before sending the response
if(strcomp(Request.GetFileName(id), string("led.htm")) AND Request.GetDepth(id) == 1)
if(Led(id))
return

if(strcomp(Request.GetFileName(id), string("post.htm")) AND Request.GetDepth(id) == 1)
Post(id)

if(strcomp(Request.GetFileName(id), string("aled.htm")) AND Request.GetDepth(id) == 1)
SendLedResposne(id)
return

'' The RESTful command is in the root location
if(strcomp(Request.GetPathNode(id, 0), string("led")))
RestLed(id)
return

'' HTML time
if(strcomp(Request.GetPathNode(id, 0), string("time")))
GetTime(id)
return

'' XML time
if(strcomp(Request.GetPathNode(id, 0), string("xmltime")))
XmlTime(id)
return

StaticFileHandler(id)
return


PRI XmlTime(id) | headerOffset
dynamicContentPtr := @tempBuff
FillHttpDate

PushDynamicContent(@xmlencode)
PushDynamicContent(string("<root application='spinneret'><time><![CDATA["))
PushDynamicContent(@httpDate)
PushDynamicContent(string("]]></time></root>"))

'' Write the header and XML body
headerOffset := Response.BuildHeader(string("xml"), 200, false)
Socket.txTCP(id, @txdata, headerOffset)
StringSend(id, @tempBuff)

bytefill(@tempBuff, 0, TEMP_BUFFER)

return


PRI GetTime(id) | ptr, headerOffset
dynamicContentPtr := @tempBuff

'' Place the date in memory
FillHttpDate

PushDynamicContent(string("<p>"))
PushDynamicContent(@httpDate)
PushDynamicContent(string("</p>"))

'' Write the header and HTML body
headerOffset := Response.BuildHeader(string("htm"), 200, false)
Socket.txTCP(id, @txdata, headerOffset)
StringSend(id, @tempBuff)

bytefill(@tempBuff, 0, TEMP_BUFFER)

return


PRI RestLed(id) | pin, state, temp, headerLen
'' URL => led\pin\on|off

'' Get the pin and state
pin := Request.GetPathNode(id, 1)
state := Request.GetPathNode(id, 2)

'' Convert the ASCII pin number to an integer
bytemove(@numBuff, pin, strsize(pin))
temp := str.ToInteger(@numBuff)

if (strcomp(string("on"), state ))
PinState(temp, 1)
else
PinState(temp, 0)

bytefill(@numBuff, 0, strsize(pin))

'' Build and send the header
'' Send the value of led= on or off
headerLen := Response.BuildHeader(string("htm"), 200, globalCache)
Socket.txTCP(id, @txdata, headerLen)
Socket.txTCP(id, state, strsize(state))

return


PRI PinState(pin, state)
dira[pin]~~
outa[pin] := state
return


PRI SendLedResposne(id) | headerLen, qstr

'' Get the query string value
qstr := Request.Get(id, string("led"))

'' Exit if there is no querystring
if(qstr == 0)
return

'' Turn the LED on if the led= value is "on"
if (strcomp(string("on"), qstr ))
LedStatus(1)
else
LedStatus(0)

'' Build and send the header
'' Send the value of led= on or off
headerLen := Response.BuildHeader(Request.GetExtension(id), 200, globalCache)
Socket.txTCP(id, @txdata, headerLen)
Socket.txTCP(id, qstr, strsize(qstr))

return


PRI Led(id) | qstr
'' Get the query string value
qstr := Request.Get(id, string("led"))

if(qstr == 0)
return false

if (strcomp(string("on"), qstr ))
LedStatus(1)
else
LedStatus(0)
return true


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





PRI LedStatus(state)
dira[26]~~
outa[26] := 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



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 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(%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 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 GetPath(id) | i
bytefill(@filepath, 0 ,33)
dynamicContentPtr := @filepath
PushDynamicContent(@approot)

i := 0
repeat Request.GetDepth(id)-1
PushDynamicContent(Request.GetPathNode(id, i++))
PushDynamicContent(@approot)


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]
numBuff byte $0[10]
xmlencode byte "<?xml version='1.0' encoding='utf-8'?>",0
filepath byte $0[33]
'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. │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
}}

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2011-08-21 12:33
    attachment.php?attachmentid=78421&d=1297987572

    Posting Propeller code via cut and paste is very problematic. You lose all the crucial indenting and sometimes lose some subscripts due to them looking like forum formatting codes.
  • michaelmonsomichaelmonso Posts: 19
    edited 2011-08-22 10:31
  • michaelmonsomichaelmonso Posts: 19
    edited 2011-08-22 10:33
    {{
    &#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472; 
    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        
    &#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472; 
    }}
    
    {
    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, $EF, $22
      subnet                byte    255, 255 ,255, 0
      gateway               byte    162, 228, 0, 1 
      ip                    byte    162, 228, 0, 120
      port                  word    5000
      remoteIp              byte     {65.98.8.151}
      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    %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
    
                     
    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)
            
            StaticFileHandler(id)
            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)
                
            return
        
    PRI LedStatus(state)
            dira[26]~~
            outa[26] := 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
    
    
      
    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(%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 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"                 
    
    {{
    &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
    &#9474;                                                   TERMS OF USE: MIT License                                                  &#9474;                                                            
    &#9500;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9508;
    &#9474;Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation    &#9474; 
    &#9474;files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,    &#9474;
    &#9474;modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software&#9474;
    &#9474;is furnished to do so, subject to the following conditions:                                                                   &#9474;
    &#9474;                                                                                                                              &#9474;
    &#9474;The above copyright notice and this permission notice shall be included in all copies or substantial ions of the Software.&#9474;
    &#9474;                                                                                                                              &#9474;
    &#9474;THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE          &#9474;
    &#9474;WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR         &#9474;
    &#9474;COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,   &#9474;
    &#9474;ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                         &#9474;
    &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
    }}
    
Sign In or Register to comment.