PHPoC takes picture from camera and upload it to Google Drive via Google Drive API. Login process is via OAuth 2.0

Click image for larger version  Name:	PHPoC_Google_Drive.PNG Views:	0 Size:	179.7 KB ID:	1500



Hardware Components
  • PHPoC Blue or PHPoC Black or P4M
  • Grove Camera
  • Button
Click image for larger version  Name:	phpoc_hardware.PNG Views:	0 Size:	408.2 KB ID:	1501




Demonstration

The below demonstration is for Arduino, but it is the same for PHPoC.





How It Works


1. Login to Google Account via OAuth 2.0 for IoT devices to obtain access_token.

Login process is described in this project on Hackster.

2. When the button is pressed, PHPoC gets picture from camera, and then upload to Google Drive using access_token via Google Drive API.

Google Drive API for uploading file is described in Google document.




How To
  • Create Google Project from Google Developer Portal and obtain GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET
  • Replace GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in task0.php code
  • Upload all files to PHPoC Blue. See instruction
  • Access Login Page on PHPoC :http://ip_address and Login to Your Google Account
  • Press Button to take Picture
  • Check your Google Drive after two second, you will see the taken picture in your Drive.



Code

index.php : Login page
PHP Code:
<html>
<head>
<title>PHPoC / <?echo system("uname -i")?></title>
<meta content="initial-scale=1.0, maximum-scale=1.0, minimum-scale=0.5, width=device-width, user-scalable=yes" name="viewport">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto" type="text/css">
<style>
body { text-align:center; }
.center {
    margin: auto;
    position: absolute;
    -webkit-backface-visibility: hidden;
    left:0;
    right:0;
    text-align: center;
    top: 20%;
}
.hearder {
    width: 100%;
    max-width:400px;
    color: #008B8B;
    padding: 5px;
    border-bottom: solid;
    margin-bottom: 5px;

    font-size: 200%;
    display: inline-block;
}
.wc_text, .loader {
    display: inline-block;
    width: 100%;
    max-width:300px;
    line-height: 150%;
}
.code {
    font-family: "Courier New", Courier, monospace;
    font-size: 150%;
    font-weight: bold;
    color: #A52A2A;
}
.success {font-weight: bold; color: #A52A2A;}

/*loading icon*/
.lds-roller {
  display: inline-block;
  position: relative;
  width: 64px;
  height: 64px;
}
.lds-roller div {
  animation: lds-roller 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
  transform-origin: 32px 32px;
}
.lds-roller div:after {
  content: " ";
  display: block;
  position: absolute;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #A52A2A;
  margin: -3px 0 0 -3px;
}
.lds-roller div:nth-child(1) {animation-delay: -0.036s;}
.lds-roller div:nth-child(1):after {top: 50px;left: 50px;}
.lds-roller div:nth-child(2) {animation-delay: -0.072s;}
.lds-roller div:nth-child(2):after {top: 54px;left: 45px;}
.lds-roller div:nth-child(3) {animation-delay: -0.108s;}
.lds-roller div:nth-child(3):after {top: 57px;left: 39px;}
.lds-roller div:nth-child(4) {animation-delay: -0.144s;}
.lds-roller div:nth-child(4):after {top: 58px;left: 32px;}
.lds-roller div:nth-child(5) {animation-delay: -0.18s;}
.lds-roller div:nth-child(5):after {top: 57px;left: 25px;}
.lds-roller div:nth-child(6) {animation-delay: -0.216s;}
.lds-roller div:nth-child(6):after {top: 54px;left: 19px;}
.lds-roller div:nth-child(7) {animation-delay: -0.252s;}
.lds-roller div:nth-child(7):after {top: 50px;left: 14px;}
.lds-roller div:nth-child(8) {animation-delay: -0.288s;}
.lds-roller div:nth-child(8):after {top: 45px;left: 10px;}
@keyframes lds-roller {0% {transform: rotate(0deg);}100% {transform: rotate(360deg);}}
</style>
<script>
var ws;

function init()
{
    ws = new WebSocket("ws://<?echo _SERVER("HTTP_HOST")?>/login", "text.phpoc");

    ws.onopen = ws_onopen;
    ws.onclose = ws_onclose;
    ws.onmessage = ws_onmessage;
}

function ws_onopen()
{
    if(ws && (ws.readyState == 1))
        ws.send('google\r\n');
}
function ws_onclose()
{
    alert('CANNOT connect to device. Please reload webpage');
    ws.onopen = null;
    ws.onclose = null;
    ws.onmessage = null;
    ws = null;
}

function ws_onmessage(e_msg)
{
    e_msg = e_msg || window.event; // MessageEvent

    var obj = JSON.parse(e_msg.data);
    var wc_text = document.getElementById('wc_text');

    if(obj.action == 'LOGIN')
    {
        wc_text.innerHTML  = 'Next, visit <a href="' + obj.verification_url + '" target="_blank">' + obj.verification_url + '</a> and enter this code:<br>';
        wc_text.innerHTML += '<span class="code">' + obj.user_code + '</span>';
    }
    else
    if(obj.action == 'SUCCESS')
    {
        document.getElementById('loader').style.display = 'none';
        wc_text.innerHTML  = '<span class="success">Success!</span><br>';
        wc_text.innerHTML += 'You are now logged in from PHPoC';
    }
}

window.onload = init;
</script>

</head>
<body>
<div class="center">
    <div class="hearder">
        <div style="font-size: 150%">
        <span style="color:#4285F4">G</span>
        <span style="color:#EA4335;">o</span>
        <span style="color:#FBBC05;">o</span>
        <span style="color:#4285F4;">g</span>
        <span style="color:#34A853;">l</span>
        <span style="color:#EA4335;">e</span>
        </div>
        Login for PHPoC
    </div>
    <br><br>
    <div class="wc_text" id="wc_text"></div><br><br>
    <div class="loader" id="loader">
        <div class="lds-roller"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div>
    </div>
</div>

</body>
</html>


task0.php : main code that runs in the system loop
PHP Code:
<?php

if(_SERVER("REQUEST_METHOD"))
    exit; 
// avoid php execution via http request

include_once "/lib/sn_tcp_ws.php";
include_once 
"/lib/sn_http_b2_2.php";
include_once 
"/lib/sn_json_b1.php";
include_once 
"/lib/sd_340.php";
include_once 
"/lib/sc_envu.php";
include_once 
"/lib/vd_uart_camera.php";

// Replace your GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET here
define("GOOGLE_CLIENT_ID",     "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps  .googleusercontent.com");
define("GOOGLE_CLIENT_SECRET""xxxxxxxxxxxxxxxxxxxxxxxx");

function 
http($method$url$body)
{
    
http_req_header("Accept: */*");
    
http_req_header("Content-Type: application/x-www-form-urlencoded");

    
$resp_head http_request($method$url$body);

    
$resp_body "";

    if(
$resp_head !== "")
        
$rlen http_read_sync($resp_body);

    
http_close();

    
//echo "HTTP RESPONSE HEADER:\r\n$resp_head";
    //echo "HTTP RESPONSE BODY:\r\n$resp_body\r\n\r\n\r\n";

    
return $resp_body;
}

function 
upload_file($access_token)
{
    if(
$picture_len camera_get_picture())
    {
        
$file_name "PHPoC_" date("YmdHis");
        
$boundary "foo_bar_baz";
        
$metadata  "--$boundary\r\n";
        
$metadata .= "Content-Type: application/json; charset=UTF-8\r\n\r\n";
        
$metadata .= "{"title": "$file_name"}\r\n\r\n";
        
$jpeg_boundary  "--$boundary\r\n";
        
$jpeg_boundary .= "Content-Type: image/jpeg\r\n\r\n";
        
$end_boundary "\r\n--$boundary--";

        
$body_len strlen($metadata) + strlen($jpeg_boundary) + $picture_len strlen($end_boundary);

        
http_req_header("Accept: */*");
        
http_req_header("Content-Type: multipart/related; boundary=$boundary");
        
http_req_header("Authorization: Bearer $access_token");

        
$url "https://www.googleapis.com/upload/drive/v2/files?uploadType=multipart";

        
http_start("POST"$url$body_len);

        while(
1)
        {
            
$resp_head http_loop();

            if(
$resp_head === HTTP_STATE_REQ_BODY)
            {
                
http_req_body($metadata);
                
http_req_body($jpeg_boundary);

                
$packet_num camera_packet_num();
                for(
$i 0$i $packet_num$i++)
                {
                    
$packet camera_get_packet($i);
                    
http_req_body($packet);
                }

                
$packet camera_get_packet($i);

                
http_req_body($end_boundary);
            }
            else
            if(!
is_string($resp_head))
                
usleep(1000);
            else
                break;
        }

        
$resp_body "";

        if(
$resp_head !== "")
            
$rlen http_read_sync($resp_body);

        
http_close();

        
//echo "HTTP RESPONSE HEADER:\r\n$resp_head";
        
echo "$resp_body";

        return 
$resp_body;
    }
}

function 
google_oauth_login()
{
    global 
$access_token$refresh_token$expires_at;

    
// Step 1: Request device and user codes
    
$url "https://accounts.google.com/o/oauth2/device/code";
    
$body  "client_id=" GOOGLE_CLIENT_ID;
    
$body .= "&scope=https://www.googleapis.com/auth/drive.file";

    
$response http("POST"$url$body);

    if(
$response)
    {
        
// Step 2: Handle the authorization server response
        
$device_code      json_text_value(json_search($response"device_code"));;
        
$user_code        json_text_value(json_search($response"user_code"));
        
$verification_url json_text_value(json_search($response"verification_url"));
        
$expires_in       json_text_value(json_search($response"expires_in"));
        
$interval         json_text_value(json_search($response"interval"));

        
// Step 3: Display the user code
        
$msg '{"provider": "google",';
        
$msg .= '"action": "LOGIN",';
        
$msg .= '"verification_url": "' $verification_url '",';
        
$msg .= '"user_code": "' $user_code '"';
        
$msg .= '}';

        
ws_write(1$msg);
        echo 
"Next, visit $verification_url on your desktop or smartphone and enter this code: $user_code\r\n";

        
// Step 5: Poll authorization server
        
$url "https://www.googleapis.com/oauth2/v4/token";
        
$body  "client_id=" GOOGLE_CLIENT_ID;
        
$body .= "&client_secret=" GOOGLE_CLIENT_SECRET;
        
$body .= "&code=$device_code";
        
$body .= "&grant_type=http://oauth.net/grant_type/device/1.0";

        
$loop_count = (int)$expires_in / (int)$interval;

        for(
$i 0$i <= $loop_count$i++)
        {
            
//if(ws_state(1) != TCP_CONNECTED)
                //break;

            
sleep((int)$interval);

            if((
$response http("POST"$url$body)))
            {
                
$access_token  json_text_value(json_search($response"access_token"));
                
$refresh_token json_text_value(json_search($response"refresh_token"));
                
$expires_in    json_text_value(json_search($response"expires_in"));

                if(
$access_token)
                {
                    echo 
"access_token:"$access_token,     "\r\n";
                    
// Step 6: Upload Image to Google Drive
                    //echo upload_file($access_token);

                    
$expires_at time() + (int)$expires_in 60;

                    
$envu =  envu_read("envu");
                    
envu_update($envu"access_token"$access_token);
                    
envu_update($envu"expires_at""$expires_at");
                    
envu_update($envu"refresh_token"$refresh_token);

                    
envu_write("envu"$envustrlen($envu), 0);

                    
$msg '{"provider": "google",';
                    
$msg .= '"action": "SUCCESS"}';
                    
ws_write(1$msg);

                    return 
$access_token;
                }
            }
        }
    }

    return 
"";
}

function 
google_oauth_exchange_token()
{
    global 
$access_token$refresh_token$expires_at;

    
$url "https://www.googleapis.com/oauth2/v4/token";
    
$body  "client_id=" GOOGLE_CLIENT_ID;
    
$body .= "&client_secret=" GOOGLE_CLIENT_SECRET;
    
$body .= "&refresh_token=$refresh_token";
    
$body .= "&grant_type=refresh_token";

    
$response_1 "";
    
$response_2 "";
    
$rlen http("POST"$url$body$response_1$response_2);

    if(
$rlen <= MAX_STRING_LEN)
    {
        
$access_token json_text_value(json_search($response_1"access_token"));
        
$expires_in json_text_value(json_search($response_1"expires_in"));
        
$expires_at time() + (int)$expires_in 60;

        
envu_update($envu"access_token"$access_token);
        
envu_update($envu"expires_at""$expires_at");
        
envu_write("envu"$envustrlen($envu), 0);

        return 
$access_token;
        echo 
"get NEW access token\r\n";
    }
    else
        echo 
"exceed MAX_STRING_LEN\r\n";

    return 
"";
}


ws_setup(1"login""text.phpoc");
uio_setup(020"in_pu");
while(!
camera_init(0CT_JPEGPR_160x120JR_640x480))
    ;

$envu =  envu_read("envu");
$access_token envu_find($envu"access_token");
$refresh_token envu_find($envu"refresh_token");
$expires_at envu_find($envu"expires_at");

$button_pre_state uio_in(020);

while(
1)
{
    if(
ws_state(1) == TCP_CONNECTED)
    {
        
$rbuf "";
        
$rlen ws_read_line(1$rbuf);

        if(
$rlen)
        {
            
$provider rtrim($rbuf"\r\n");

            if(
$provider == "google")
                
google_oauth_login();
        }
    }

    
$in_value uio_in(020);

    if(
$button_pre_state != $in_value && $in_value == 1)
    {
        if(
$access_token != "")
        {
            if((int)
$expires_at time())
            {
                
$access_token "";

                if(
$refresh_token)
                    
google_oauth_exchange_token();
                else
                    echo 
"NO refresh_token!!!!!!!!\r\n";
            }

            if(
$access_token)
                
upload_file($access_token);
        }
    }

    
$button_pre_state $in_value;
}
?>

sn_http_b2_2.php : HTTPS library
PHP Code:
<?php

// $psp_id sn_http.php date 20181004

include_once "/lib/sn_dns.php";

define("HTTP_STATE_CLOSED",     0);
define("HTTP_STATE_RR_A",       1);
define("HTTP_STATE_CONNECT",    2);
define("HTTP_STATE_HEADER",     3);
define("HTTP_STATE_BODY",       4);
define("HTTP_STATE_CHUNK_CRLF"5);
define("HTTP_STATE_CHUNK_LEN",  6);
define("HTTP_STATE_CHUNK",      7);

define("HTTP_STATE_REQ_BODY",  8);

define("HTTP_VERSION""HTTP/1.1");
define("HTTP_RECV_TIMEOUT"10); // receiving header & read_sync timeout

$sn_http_state 0;
$sn_http_tcp_pid 0;
$sn_http_ip6 false;
$sn_http_protocol "";
$sn_http_method "";
$sn_http_host "";
$sn_http_port 0;
$sn_http_path "";
$sn_http_req_header "";
$sn_http_req_body_len "";
$sn_http_chunk_len 0;
$sn_http_query_count 0;
$sn_http_next_tick 0;

function 
sn_http_get_tick()
{
    while((
$pid pid_open("/mmap/st9"O_NODIE)) == -EBUSY)
        
usleep(500);

    if(!
pid_ioctl($pid"get state"))
        
pid_ioctl($pid"start");

    
$tick pid_ioctl($pid"get count");
    
pid_close($pid);

    return 
$tick;
}

function 
sn_http_cleanup()
{
    global 
$sn_http_state$sn_http_tcp_pid;
    global 
$sn_http_req_header;

    if(
$sn_http_tcp_pid)
    {
        if(
pid_ioctl($sn_http_tcp_pid"get state") != TCP_CLOSED)
            
pid_ioctl($sn_http_tcp_pid"close");
    }

    
$sn_http_req_header "";
    
$sn_http_chunk_len 0;

    
$sn_http_state HTTP_STATE_CLOSED;
}

function 
http_setup($udp_id$tcp_id$dns_server ""$ip6 false)
{
    global 
$sn_http_tcp_pid$sn_http_ip6;

    
$sn_http_tcp_pid pid_open("/mmap/tcp$tcp_id");
    
$sn_http_ip6 $ip6;

    
dns_setup($udp_id$dns_server$ip6);
}

// internal function header() is for http response
// http_req_header() is for http request
function http_req_header($field)
{
    global 
$sn_http_state;
    global 
$sn_http_req_header;

    if(
$sn_http_state != HTTP_STATE_CLOSED)
    {
        echo 
"http_req_header: session busy\r\n";
        return;
    }

    
// don't use explode(). field value may contain ':'
    
$pos strpos($field":");

    if(
$pos === false)
    {
        echo 
"http_req_header: invalid header field $field\r\n";
        return;
    }

    
$field_name ltrim(rtrim(substr($field0$pos)));

    if(!
$field_name)
    {
        echo 
"http_req_header: invalid header field $field\r\n";
        return;
    }

    if(
strlen($field) > ($pos 1))
        
$field_value ltrim(rtrim(substr($field$pos 1)));
    else
        
$field_value "";

    
$sn_http_req_header .= ($field_name ":");

    if(
$field_value)
         
$sn_http_req_header .= (" " $field_value);

    
$sn_http_req_header .= "\r\n";
}

function 
http_auth($auth_id$auth_pwd$method "Basic")
{
    if(
$method != "Basic")
    {
        echo 
"http_auth: unsupported authorization method "$method"\r\n";
        return;
    }

    
$enc_id_pwd system("base64 enc %1""$auth_id:$auth_pwd");

    
http_req_header("Authorization: $method $enc_id_pwd");
}

function 
http_find_header($header$field_name)
{
    
$pos strpos($header"\r\n");

    if(
$pos === false)
        return 
"";

    
$status_line substr($header0$pos);

    switch(
strtoupper($field_name))
    {
        case 
"STATUS-LINE":
            return 
$status_line;
        case 
"HTTP-VERSION":
            return 
substr($status_line08);
        case 
"STATUS-CODE":
            return 
substr($status_line93);
        case 
"REASON-PHRASE":
            return 
substr($status_line13);
    }

    
$header_array explode("\r\n"$header);
    
$header_count count($header_array) - 1;

    for(
$i 0$i $header_count$i++)
    {
        
$field $header_array[$i];
        
$pos strpos($field":");

        if(
$pos === false)
            continue;

        if(
strtoupper(substr($field0$pos)) == strtoupper($field_name))
            return 
ltrim(rtrim(substr($field$pos 1)));
    }

    return 
false;
}

function 
sn_http_connect($addr$port)
{
    global 
$sn_http_state$sn_http_tcp_pid;
    global 
$sn_http_protocol;

    if(!
$sn_http_tcp_pid)
        
$sn_http_tcp_pid pid_open("/mmap/tcp0");

    if(
$sn_http_protocol == "https")
    {
        
pid_ioctl($sn_http_tcp_pid"set api ssl");
        
pid_ioctl($sn_http_tcp_pid"set ssl method tls1_client");
    }

    echo 
"sn_http: connect to $addr:$port...";

    
pid_bind($sn_http_tcp_pid""0);
    
pid_connect($sn_http_tcp_pid$addr$port);

    
$sn_http_state HTTP_STATE_CONNECT;
}

function 
sn_http_loop_rr()
{
    global 
$sn_http_ip6;
    global 
$sn_http_host$sn_http_port;
    global 
$sn_http_query_count;

    
$rr dns_loop();

    if(
$rr === false)
        return 
false;

    if(
$rr == "")
    {
        if(
$sn_http_query_count)
        {
            echo 
"sn_http: retry lookup $sn_http_host\r\n";

            if(
$sn_http_ip6)
                
dns_send_query($sn_http_hostRR_AAAA1000);
            else
                
dns_send_query($sn_http_hostRR_A1000);

            
$sn_http_query_count--;
            return 
false;
        }
        else
        {
            echo 
"sn_http: lookup failed\r\n";
            return 
"";
        }
    }

    
sn_http_connect($rr$sn_http_port);

    return 
false;
}

function 
http_loop()
{
    global 
$sn_http_state$sn_http_tcp_pid$sn_http_ip6$sn_http_protocol;
    global 
$sn_http_method$sn_http_host$sn_http_port$sn_http_path;
    global 
$sn_http_req_header$sn_http_req_body_len;
    global 
$sn_http_next_tick;

    if(
$sn_http_state == HTTP_STATE_CLOSED)
        return 
"";

    switch(
$sn_http_state)
    {
        case 
HTTP_STATE_RR_A:
            if(
sn_http_loop_rr() === "")
            {
                
sn_http_cleanup();
                return 
"";
            }
            break;

        case 
HTTP_STATE_CONNECT:
            
$state pid_ioctl($sn_http_tcp_pid"get state");

            if(
$state == TCP_CLOSED)
            {
                echo 
"failed\r\n";
                
sn_http_cleanup();
                return 
"";
            }
            else
            {
                
$connected false;

                if((
$sn_http_protocol == "https") && ($state == SSL_CONNECTED))
                    
$connected true;

                if((
$sn_http_protocol == "http") && ($state == TCP_CONNECTED))
                    
$connected true;

                if(
$connected)
                {
                    echo 
"ok\r\n";
                    
$sn_http_next_tick sn_http_get_tick() + HTTP_RECV_TIMEOUT 1000;

                    
pid_send($sn_http_tcp_pid$sn_http_method " ");
                    
pid_send($sn_http_tcp_pid$sn_http_path " " HTTP_VERSION "\r\n");
                    
pid_send($sn_http_tcp_pid"Host: $sn_http_host\r\n");
                    
pid_send($sn_http_tcp_pid"Connection: close\r\n");
                    if(
$sn_http_method == "POST")
                        
pid_send($sn_http_tcp_pid"Content-Length: $sn_http_req_body_len\r\n");
                    if(
$sn_http_req_header)
                        
pid_send($sn_http_tcp_pid$sn_http_req_header);
                    
pid_send($sn_http_tcp_pid"\r\n");

                    
$sn_http_state HTTP_STATE_REQ_BODY;
                }
            }
            break;

        case 
HTTP_STATE_REQ_BODY:
            if(
$sn_http_req_body_len <= 0)
                
$sn_http_state HTTP_STATE_HEADER;

            return 
$sn_http_state;

        case 
HTTP_STATE_HEADER:
            
$len pid_ioctl($sn_http_tcp_pid"get rxlen \r\n\r\n");

            if(
$len)
            {
                
$resp_head "";
                
pid_recv($sn_http_tcp_pid$resp_head$len);

                if(
http_find_header($resp_head"Transfer-Encoding") === "chunked")
                    
$sn_http_state HTTP_STATE_CHUNK_LEN;
                else
                    
$sn_http_state HTTP_STATE_BODY;

                return 
$resp_head;
            }

            
$state pid_ioctl($sn_http_tcp_pid"get state");

            if((
$state != TCP_CONNECTED) && ($state != SSL_CONNECTED))
            {
                echo 
"sn_http: connection closed\r\n";
                
sn_http_cleanup();
                return 
"";
            }

            if(
sn_http_get_tick() >= $sn_http_next_tick)
            {
                echo 
"sn_http: request timeout\r\n";
                
sn_http_cleanup();
                return 
"";
            }

            break;
    }

    return 
false;
}

function 
http_start($method$uri$body_len 0)
{
    global 
$sn_http_state$sn_http_ip6;
    global 
$sn_http_protocol$sn_http_method;
    global 
$sn_http_host$sn_http_port$sn_http_path;
    global 
$sn_http_req_body_len;
    global 
$sn_http_query_count;

    if(
$sn_http_state != HTTP_STATE_CLOSED)
    {
        echo 
"http_start: session busy\r\n";
        return;
    }

    
$sn_http_method strtoupper($method);

    
$pos strpos($uri"://");

    if(
$pos === false)
        
$sn_http_protocol "http";
    else
    {
        
$sn_http_protocol strtolower(substr($uri0$pos));
        
$uri substr($uri$pos 3);
    }

    
$pos strpos($uri"/");

    if(
$pos === false)
    {
        
$sn_http_host $uri;
        
$sn_http_path "/";
    }
    else
    {
        
$sn_http_host substr($uri0$pos);
        
$sn_http_path substr($uri$pos);
    }

    
$pos strpos($sn_http_host":");

    if(
$pos === false)
    {
        if(
$sn_http_protocol == "https")
            
$sn_http_port 443;
        else
            
$sn_http_port 80;
    }
    else
    {
        
$sn_http_port = (int)substr($sn_http_host$pos 1);
        
$sn_http_host substr($sn_http_host0$pos);
    }

    
$sn_http_req_body_len $body_len;

    if(
inet_pton($sn_http_host) === false)
    {
        if(
$sn_http_ip6)
            
dns_send_query($sn_http_hostRR_AAAA500);
        else
            
dns_send_query($sn_http_hostRR_A500);

        
$sn_http_query_count 2;
        
$sn_http_state HTTP_STATE_RR_A;
    }
    else
        
sn_http_connect($sn_http_host$sn_http_port);
}

function 
http_req_body($body)
{
    global 
$sn_http_tcp_pid$sn_http_req_body_len;

    
$sn_http_req_body_len -= strlen($body);
    
pid_send($sn_http_tcp_pid$body);
}

function 
http_request($method$uri$body "")
{
    
$body_len =  strlen($body);
    
http_start($method$uri$body_len);

    while(
1)
    {
        
$resp_head http_loop();

        if(
$resp_head === HTTP_STATE_REQ_BODY)
        {
            
http_req_body($body);
        }
        else
        if(!
is_string($resp_head))
            
usleep(1000);
        else
            return 
$resp_head;
    }
}

function 
http_state()
{
    global 
$sn_http_state;

    return 
$sn_http_state;
}

function 
sn_http_rxlen()
{
    global 
$sn_http_state$sn_http_tcp_pid;
    global 
$sn_http_chunk_len;

    if(
$sn_http_state == HTTP_STATE_BODY)
        return 
pid_ioctl($sn_http_tcp_pid"get rxlen");

    if(
$sn_http_state == HTTP_STATE_CHUNK_CRLF)
    {
        if(
pid_ioctl($sn_http_tcp_pid"get rxlen") > 2)
        {
            
$rbuf "";
            
pid_recv($sn_http_tcp_pid$rbuf2);

            if(
$rbuf != "\r\n")
                echo 
"sn_http: invalid chunk trailer\r\n";

            
//echo "sn_http: trailing CRLF\r\n";

            
$sn_http_state HTTP_STATE_CHUNK_LEN;
        }
        else
            return 
0;
    }

    if(
$sn_http_state == HTTP_STATE_CHUNK_LEN)
    {
        if(
$rlen pid_ioctl($sn_http_tcp_pid"get rxlen \r\n"))
        {
            
$rbuf "";
            
pid_recv($sn_http_tcp_pid$rbuf$rlen);

            
$sn_http_chunk_len bin2int(hex2bin($rbuf), 02true);

            
//echo "sn_http: chunk_len $sn_http_chunk_len\r\n";

            
if($sn_http_chunk_len)
                
$sn_http_state HTTP_STATE_CHUNK;
            else
                
$sn_http_state HTTP_STATE_BODY;
        }
        else
            return 
0;
    }

    if(
$sn_http_state == HTTP_STATE_CHUNK)
    {
        if(
$sn_http_chunk_len)
        {
            
$rlen pid_ioctl($sn_http_tcp_pid"get rxlen");

            if(
$rlen $sn_http_chunk_len)
                return 
$sn_http_chunk_len;
            else
                return 
$rlen;
        }
        else
        {
            
$sn_http_state HTTP_STATE_CHUNK_CRLF;
            return 
0;
        }
    }

    return 
0;
}

function 
http_read(&$rbuf$rlen MAX_STRING_LEN)
{
    global 
$sn_http_state$sn_http_tcp_pid;
    global 
$sn_http_chunk_len;

    if(
$sn_http_state HTTP_STATE_BODY)
        return 
0;

    
$state pid_ioctl($sn_http_tcp_pid"get state");

    if((
$state != TCP_CONNECTED) && ($state != SSL_CONNECTED))
    {
        if(!
pid_ioctl($sn_http_tcp_pid"get rxlen"))
        {
            
sn_http_cleanup();
            return 
0;
        }
    }

    
$rxlen sn_http_rxlen();

    if(
$rxlen)
    {
        if(
$rlen $rxlen)
            
$rlen $rxlen;

        if(
$sn_http_state == HTTP_STATE_CHUNK)
        {
            
$sn_http_chunk_len -= $rlen;

            if(
$sn_http_chunk_len 0)
            {
                echo 
"http_read: chunk underflow $sn_http_chunk_len\r\n";
                
$sn_http_chunk_len 0;
            }
        }

        return 
pid_recv($sn_http_tcp_pid$rbuf$rlen);
    }
    else
        return 
0;
}

function 
http_read_sync(&$rbuf$rlen MAX_STRING_LEN)
{
    global 
$sn_http_state;

    if(
$sn_http_state HTTP_STATE_BODY)
        return 
0;

    
$rbuf "";
    
$frag "";

    
$timeout_tick sn_http_get_tick() + HTTP_RECV_TIMEOUT 1000;

    while(
$rlen)
    {
        
$len http_read($frag$rlen);

        if(
$len)
        {
            
$rbuf .= $frag;
            
$rlen -= $len;
        }
        else
            
usleep(1000);

        if(
$sn_http_state == HTTP_STATE_CLOSED)
            break;

        if(
sn_http_get_tick() >= $timeout_tick)
        {
            echo 
"http_read_sync: timeout\r\n";
            break;
        }
    }

    return 
strlen($rbuf);
}

function 
http_close()
{
    
sn_http_cleanup();
}

?>

vd_uart_camera.php : Grove Camera Library
PHP Code:
<?php

// camera command
define("CAM_CMD_PREFIX",      0xAA);
define("CAM_CMD_SYNC",        0x0D);
define("CAM_CMD_ACK",         0x0E);
define("CAM_CMD_NAK",         0x0F);
define("CAM_CMD_INITIAL",     0x01);
define("CAM_CMD_DATA",        0x0A);
define("CAM_CMD_RESET",       0x08);
define("CAM_CMD_POWEROFF",    0x09);
define("CAM_CMD_BAUDRATE",    0x07);
define("CAM_CMD_PACKAGESIZE"0x06);
define("CAM_CMD_SNAPSHOT",    0x05);
define("CAM_CMD_GETPICTURE",  0x04);
define("CAM_CMD_LIGHTFREQ",   0x13);

//Color Type
define("CT_GRAYSCALE_2"0x01);
define("CT_GRAYSCALE_4"0x02);
define("CT_GRAYSCALE_8"0x03);
define("CT_COLOR_12"0x05);
define("CT_COLOR_16"0x06);
define("CT_JPEG"0x07);

//Preview Resolution
define("PR_80x60"0x01);
define("PR_160x120"0x03);

// JPEG Resolution
define("JR_80x64",   0x01);
define("JR_160x128"0x03);
define("JR_320x240"0x05);
define("JR_640x480"0x07);

define("PIC_PKT_LEN"1000); //data length of each read, dont set this too big because ram is limited

// server to client
define("_CMD_CAMERA_DATA_START"0x14);
define("_CMD_CAMERA_DATA",       0x15);
define("_CMD_CAMERA_DATA_STOP",  0x16);

$camera_uart_id 0;
$camera_packet_num 0;
$camera_last_packet_len 0;


function 
set_uart($id)
{
    global 
$camera_uart_id;

    
$camera_uart_id $id;
}

function 
clear_rx_buf()
{
    global 
$camera_uart_id;

    
$rbuf "";
    
$len uart_read($camera_uart_id$rbuf);

    
//log
    
if($len)
    {
        echo 
"clear:<<"bin2hex($rbuf), "\r\n";
    }

}

function 
send_cmd($cmd$CAM_CMD_len)
{
    global 
$camera_uart_id;

    for (
$i 0$i $CAM_CMD_len$i++)
    {
        
$wbuf int2bin(($cmd[$i]&0xff),1);
        
uart_write($camera_uart_id$wbuf1);
    }
}

function 
read_bytes(&$dest$len$timeout)
{
    global 
$camera_uart_id;

    
$pid_st0 pid_open("/mmap/st0");
    
pid_ioctl($pid_st0"set mode free");
    
pid_ioctl($pid_st0"set dir up");
    
pid_ioctl($pid_st0"set div ms");
    
pid_ioctl($pid_st0"start");
    
$tick pid_ioctl($pid_st0"get count");

    
$rlen 0;

    while(
$tick $timeout)
    {
        if((
$rlen uart_readn($camera_uart_id$dest$len)) == $len)
        {
            break;
        }
        
$tick pid_ioctl($pid_st0"get count");
    }

    
pid_close($pid_st0);
    return 
$rlen;
}

function 
camera_wait_for_ACK($time_out$command)
{
    
$resp "";
    if (
read_bytes($resp6$time_out) == 6)
    {
        if (
bin2int($resp01) == CAM_CMD_PREFIX
            
&& bin2int($resp11) == (CAM_CMD_ACK )
            && 
bin2int($resp21) == $command )
        {
            
//echo "camera: received ACK\r\n";
            
return true;
        }
    }
    
//echo "camera: not receive ACK\r\n";
    
return false;
}

function 
camera_init($uart_id$color_type$preview_resolution$jpeg_resolution)
{
    global 
$camera_uart_id;

    
$camera_uart_id $uart_id;

    
uart_setup($uart_id115200);

    
$cmd = array(CAM_CMD_PREFIXCAM_CMD_SYNC0x000x000x000x00);
    
$resp "";

    echo 
"camera: CAM_CMD_SYNC...\r\n";

    
$retry_cnt 0;

    while (
1)
    {
        
clear_rx_buf();
        
send_cmd($cmd,6);
        if (
camera_wait_for_ACK(100CAM_CMD_SYNC))
            break;

        
$retry_cnt++;

        if(
$retry_cnt 20)
            return 
false;
            
//system("reboot php 100");

        
usleep(10000);
    }
    
$cmd[1] = CAM_CMD_ACK ;
    
$cmd[2] = CAM_CMD_SYNC;
    
send_cmd($cmd6);

    
//initial
    
echo "camera: CAM_CMD_INITIAL...\r\n";
    
$cmd = array( CAM_CMD_PREFIXCAM_CMD_INITIAL 0x00$color_type$preview_resolution$jpeg_resolution );
    
$resp "";

    
$retry_cnt 0;

    while (
1)
    {
        
clear_rx_buf();
        
send_cmd($cmd6);

        if (
camera_wait_for_ACK(100CAM_CMD_INITIAL))
            break;

        
$retry_cnt++;

        if(
$retry_cnt 20)
            return 
false;
            
//system("reboot php 100");
    
}
    
// set packet size
    //echo "camera: set CAM_CMD_PACKAGESIZE\r\n";
    
$cmd = array( CAM_CMD_PREFIXCAM_CMD_PACKAGESIZE 0x08PIC_PKT_LEN 0xff, (PIC_PKT_LEN>>8) & 0xff ,0);

    
$retry_cnt 0;

    while (
1)
    {
        
clear_rx_buf();
        
send_cmd($cmd6);

        if (
camera_wait_for_ACK(100CAM_CMD_PACKAGESIZE))
            break;

        
$retry_cnt++;

        if(
$retry_cnt 20)
            return 
false;
            
//system("reboot php 100");
    
}

    return 
true;
}

function 
camera_reset($reset 0x01)
{
    
//initial
    
$cmd = array( CAM_CMD_PREFIXCAM_CMD_RESET 0x000x000x000x00 );
    
$resp "";

    
$retry_cnt 0;

    while (
1)
    {
        
clear_rx_buf();
        
send_cmd($cmd6);

        if (
camera_wait_for_ACK(100CAM_CMD_RESET))
            break;

        
$retry_cnt++;

        if(
$retry_cnt 20)
            
system("reboot php 100");
    }
}

function 
camera_capture()
{
    
//snapshot.
    //echo "camera: CAM_CMD_SNAPSHOT...\r\n";
    
$cmd = array( CAM_CMD_PREFIXCAM_CMD_SNAPSHOT 0x000x000x00 ,0x00);
    
$resp "";

    
$retry_cnt 0;

    while (
1)
    {
        
clear_rx_buf();
        
send_cmd($cmd6);

        if (
camera_wait_for_ACK(100CAM_CMD_SNAPSHOT))
            break;

        
$retry_cnt++;

        if(
$retry_cnt 20)
            
system("reboot php 100");
    }
}

function 
camera_get_picture()
{
    global 
$camera_packet_num;
    global 
$camera_last_packet_len;

    
// send get picture command and get total size
    
$cmd = array( CAM_CMD_PREFIXCAM_CMD_GETPICTURE 0x010x000x00 ,0x00);
    
$resp "";
    
$retry_cnt 0;

    while (
1)
    {
        
clear_rx_buf();
        
$retry_cnt++;
        
send_cmd($cmd6);

        if(
$retry_cnt 5)
            return 
false;
            
//system("reboot php 100");

        
if (camera_wait_for_ACK(100CAM_CMD_GETPICTURE))
        {
            if (
read_bytes($resp61000) != 6)
            {
                continue;
            }
            if (
bin2int($resp01) == CAM_CMD_PREFIX
                
&& bin2int($resp11) == (CAM_CMD_DATA )
                && 
bin2int($resp21) == 0x01)
            {
                
$pic_total_len = (bin2int($resp31)) | (bin2int($resp41) << 8) | (bin2int($resp51) << 16);
                
//echo "\r\npic_total_len: ", $pic_total_len, "\r\n";
                
break;
            }
        }
    }

    
// get data
    
$camera_packet_num = ($pic_total_len) / (PIC_PKT_LEN 6);
    
$camera_last_packet_len PIC_PKT_LEN;

    
$mod $pic_total_len % (PIC_PKT_LEN-6);

    if (
$mod != 0)
    {
        
$camera_packet_num += 1;
        
$camera_last_packet_len $mod 6;
    }

    echo 
"pic_total_len: $pic_total_len\r\n";
    return 
$pic_total_len;
}

function 
camera_packet_num()
{
    global 
$camera_packet_num;
    return 
$camera_packet_num;
}

function 
camera_get_packet($packet_id)
{
    global 
$camera_packet_num;
    global 
$camera_last_packet_len;

    
$packet "";
    
$cmd = array( CAM_CMD_PREFIXCAM_CMD_ACK 0x000x000x000x00 );

    if(
$packet_id $camera_packet_num)
    {
        
$cmd[4] = $packet_id 0xff;
        
$cmd[5] = ($packet_id >> 8) & 0xff;

        
$retry_cnt 0;
        
$retry true;

        if(
$packet_id < ($camera_packet_num-1))
            
$len PIC_PKT_LEN ;
        else
        {
            
$len $camera_last_packet_len;
        }

        while(
$retry)
        {
            
usleep(6000);
            
clear_rx_buf();
            
$retry_cnt++;
            
send_cmd($cmd6);
            
$cnt read_bytes($packet$len1200);

            if(
$cnt)
            {
                
//checksum funtion
                /*
                $sum = 0;

                for ($y = 0; $y < $cnt - 2; $y++)
                {
                    $sum += bin2int($packet, $y, 1);
                }

                $sum &= 0xff;

                if ($sum != bin2int($packet, ($cnt-2), 1))
                {
                    echo "checksum error";
                    if ($retry_cnt < 100) $retry = true;
                    else
                    {
                        break;
                    }
                }
                else */
                
{
                    
$retry false;
                }
            }

            if(
$retry_cnt 5)
                return 
false;
                
//system("reboot php 100");
        
}

        if(
$retry) break;

        
$data substr($packet4$cnt-6);

        return 
$data;
    }
    else
    if(
$packet_id == $camera_packet_num)
    {
        
$cmd[4] = 0xf0;
        
$cmd[5] = 0xf0;
        
send_cmd($cmd6);
    }
    else
        return 
false;

    return 
true;
}
?>