Last active
August 29, 2015 14:17
-
-
Save BobBurns/c5caa2fe70b059637382 to your computer and use it in GitHub Desktop.
php websocket device server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/php | |
<?php | |
/* websocket/device server program for raspberry pi | |
* this program only deals with packets that are 125 bytes max | |
* currently only one client can connect at a time. Maybe not a bad thing. | |
* run as super user (for gpio access) | |
* check out https://gist.github.com/BobBurns/6f83930912da4094f014 for the client html code | |
* much of the juicy bits adapted from ghedipunk/PHP-Websockets | |
* | |
* commands to send to client: | |
* 0xA0 0x00 device A power state off | |
* 0xA0 0x01 power state on | |
* 0xA0 0x02 power state sleep | |
* 0xA1 0xXX 0xXX sensor 1 value | |
* 0xA2 0xXX 0xXX sensor 2 value | |
* 0xA3 0xXX 0xXX sensor 3 value | |
* 0xA4 0xXX 0xXX sensor 4 value | |
* | |
* 0xB0 0x00 device B power state off | |
* 0xB0 0x01 power state on | |
* 0xB0 0x02 power state sleep | |
* 0xB1 0xXX 0xXX sensor 1 value | |
* 0xB2 0xXX 0xXX sensor 2 value | |
* 0xB3 0xXX 0xXX sensor 3 value | |
* 0xB4 0xXX 0xXX sensor 4 value | |
* commands sent to server | |
* 0x00 disconnect | |
* 0x10 power off device A | |
* 0x11 power on device A | |
* 0x12 sleep device A | |
* 0x13 0xXX 0xXX inc by value bus 1 device A | |
* 0x14 0xXX 0xXX dec by value bus 1 | |
* 0x15 0xXX 0xXX set value bus 1 | |
* 0x16 0xXX 0xXX inc by value bus 2 | |
* 0x17 0xXX 0xXX dec by value bus 2 | |
* 0x18 0xXX 0xXX set value bus 2 | |
* | |
* 0x20 power off device B | |
* 0x21 power on device B | |
* 0x22 sleep device B | |
* 0x23 0xXX 0xXX inc by value bus 1 device B | |
* 0x24 0xXX 0xXX dec by value bus 1 | |
* 0x25 0xXX 0xXX set value bus 1 | |
* 0x26 0xXX 0xXX inc by value bus 2 | |
* 0x27 0xXX 0xXX dec by value bus 2 | |
* 0x28 0xXX 0xXX set value bus 2 | |
*/ | |
$errno = 0; $errstr = ''; | |
$resource = ''; | |
$on = false; | |
$maxbuf = 4096; | |
// open gpio pin 23 to input 24 to output | |
// this program has led on gpio pin 24 and a button on pin 23 | |
system ( "echo 23 > /sys/class/gpio/export" ); | |
system ( "echo in > /sys/class/gpio/gpio23/direction" ); | |
system ( "echo 24 > /sys/class/gpio/export" ); | |
system ( "echo out > /sys/class/gpio/gpio24/direction" ); | |
$server = @stream_socket_server("tcp://0.0.0.0:2342", $errno, $errstr); | |
if ($server === false) { | |
fwrite(STDERR, "Error: $errno: $errstr\n"); | |
exit(1); | |
} | |
echo "listening for connection...\n"; | |
$result = array(); | |
for (;;) { | |
$conn = @stream_socket_accept($server, -1, $peer); | |
if ($conn !== false) { | |
// got connection now do handshake | |
wshandshake($conn); | |
$oventemp = 0x20; | |
$humidity = 50; | |
$on = false; | |
// main loop | |
for (;;) { | |
stream_set_blocking($conn, 0); | |
// get device values | |
$button = array(); | |
exec ( "cat /sys/class/gpio/gpio23/value" , $button ); | |
if ($button[0] == 1) { | |
printf("button pressed"); | |
if ($on === false) $on = true; | |
else if ($on === true) $on = false; | |
} | |
if ($on === true) | |
system( "echo 1 > /sys/class/gpio/gpio24/value" ); | |
else | |
system( "echo 0 > /sys/class/gpio/gpio24/value" ); | |
if ($on === true) $message = 0x01; | |
else $message = 0x00; | |
// send some values | |
$cmd = 0xA0; // powerstate device A | |
$dataframe = setupState($cmd, $message); | |
fwrite($conn, $dataframe); | |
$oventemp += rand(-2,2); | |
if ($oventemp > 255) $oventemp = 255; | |
if ($oventemp < 0) $oventemp = 0; | |
$cmd = 0xA1; // sensor 1 device A | |
$dataframe = setupValue($cmd, 0x00, $oventemp); | |
fwrite($conn, $dataframe); | |
$cmd = 0xA2; // sensor 2 device A | |
$dataframe = setupValue($cmd, 0x00, $humidity); | |
fwrite($conn, $dataframe); | |
// echo 'message successful '; | |
// check for incomming | |
$recbuf = fread($conn, $maxbuf); | |
if ($recbuf != false) { | |
$result = getIncomming($recbuf); | |
if ($result[0] == 0) { | |
echo "Bye!"; | |
break; | |
} | |
switch ($result[0]) { // check sent command and do something with it | |
case 0x10: | |
// power off | |
break; | |
case 0x11: | |
// power on | |
break; | |
case 0x12: | |
// sleep device A | |
break; | |
case 0x13: | |
$humidity += $result[2]; | |
break; | |
case 0x14: | |
$humidity -= $result[2]; | |
break; | |
case 0x15: | |
// etc | |
break; | |
} | |
} | |
usleep(500000); | |
} | |
fclose($conn); | |
} | |
} | |
function wshandshake($conn) { | |
global $resource; | |
$MAXLEN = 4096; | |
$magicGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | |
$headers = array(); | |
if (!($buf = fread($conn, $MAXLEN))) printf("error: fread\n"); | |
$lines = explode("\n", $buf); | |
print_r($lines); | |
foreach ($lines as $line) { | |
if (strpos($line, ":") !== false) { | |
$header = explode(":", $line, 2); | |
$headers[strtolower(trim($header[0]))] = trim($header[1]); | |
} elseif (stripos($line,"get ") !== false) { | |
preg_match("/GET (.*) HTTP/i", $line, $request); | |
$headers['get'] = trim($request[1]); | |
} | |
} | |
if (isset($headers['get'])) | |
$resource = $headers['get']; | |
if (!isset($headers['sec-websocket-version']) || strtolower($headers['sec-websocket-version']) != 13) { | |
$handshakeResponse = "HTTP/1.1 426 Upgrade Required\r\nSec-WebSocketVersion: 13"; | |
fwrite($conn, $handshakeResponse); | |
return false; | |
} | |
$webSocketKeyHash = sha1($headers['sec-websocket-key'] . $magicGUID); | |
$rawToken = ""; | |
for ($i = 0; $i < 20; $i++) { | |
$rawToken .= chr(hexdec(substr($webSocketKeyHash,$i*2, 2))); | |
} | |
$handshakeToken = base64_encode($rawToken); | |
$handshakeResponse = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $handshakeToken\r\n\r\n"; | |
fwrite($conn, $handshakeResponse); | |
return true; | |
} | |
function setupState($cmd, $state) { | |
$opcode = 0x82; | |
$len = 0x02; | |
return chr($opcode) . chr($len) . chr($cmd) . chr ($state); | |
} | |
function setupValue($cmd, $valH, $valL) { | |
$opcode = 0x82; | |
$len = 0x03; | |
return chr($opcode) . chr($len) . chr($cmd) . chr($valH) . chr($valL); | |
} | |
function setupPacket($message) { | |
$opcode = 0x81; // to send text | |
$length = strlen($message); | |
print_r($length); | |
$extendedLength = ""; | |
if ($length < 126) { | |
$paylen = dechex($length); | |
} | |
return chr($opcode) . chr($paylen) . $message; | |
} | |
function getIncomming($inbuf) { | |
$decoded = array(); | |
$arrayBuffer = unpack('C*', $inbuf); | |
$opcode = $arrayBuffer[1] & 0x0F; | |
$paylen = $arrayBuffer[2] & 0x7F; | |
$maskbit = $arrayBuffer[2] & 0x80; | |
if ($opcode == 0x08) { | |
$decoded[0] = 0; | |
return $decoded; | |
} | |
if ($maskbit > 0) { | |
$mask = array($arrayBuffer[3], $arrayBuffer[4], $arrayBuffer[5], $arrayBuffer[6]); | |
for ($i = 0; $i < $paylen; $i++) { | |
$decoded[$i] = $arrayBuffer[7+$i] ^ $mask[$i % 4]; | |
} | |
} | |
return $decoded; | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment