phpoc_man
Published © GPL3+

PHPoC - Take Picture - Upload to Google Drive

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

IntermediateFull instructions provided1,681
PHPoC - Take Picture - Upload to Google Drive

Things used in this project

Hardware components

PHPoC Blue
PHPoC Blue
×1
Grove - Serial Camera Kit
Seeed Studio Grove - Serial Camera Kit
×1
Button
×1

Story

Read more

Schematics

phpoc_hardware_m7ot9xyCM8.PNG

Code

index.php

PHP
Login Page
<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

PHP
main code that runs in the system loop
<?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", $envu, strlen($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", $envu, strlen($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(0, 20, "in_pu");
while(!camera_init(0, CT_JPEG, PR_160x120, JR_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(0, 20);

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(0, 20);

	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

PHP
HTTPS library
<?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($field, 0, $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($header, 0, $pos);

	switch(strtoupper($field_name))
	{
		case "STATUS-LINE":
			return $status_line;
		case "HTTP-VERSION":
			return substr($status_line, 0, 8);
		case "STATUS-CODE":
			return substr($status_line, 9, 3);
		case "REASON-PHRASE":
			return substr($status_line, 13);
	}

	$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($field, 0, $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_host, RR_AAAA, 1000);
			else
				dns_send_query($sn_http_host, RR_A, 1000);

			$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($uri, 0, $pos));
		$uri = substr($uri, $pos + 3);
	}

	$pos = strpos($uri, "/");

	if($pos === false)
	{
		$sn_http_host = $uri;
		$sn_http_path = "/";
	}
	else
	{
		$sn_http_host = substr($uri, 0, $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_host, 0, $pos);
	}

	$sn_http_req_body_len = $body_len;

	if(inet_pton($sn_http_host) === false)
	{
		if($sn_http_ip6)
			dns_send_query($sn_http_host, RR_AAAA, 500);
		else
			dns_send_query($sn_http_host, RR_A, 500);

		$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, $rbuf, 2);

			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), 0, 2, true);

			//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

PHP
Grove Camera Library
<?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, $wbuf, 1);
	}
}

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($resp, 6, $time_out) == 6)
	{
		if (bin2int($resp, 0, 1) == CAM_CMD_PREFIX
			&& bin2int($resp, 1, 1) == (CAM_CMD_ACK )
			&& bin2int($resp, 2, 1) == $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_id, 115200);

	$cmd = array(CAM_CMD_PREFIX, CAM_CMD_SYNC, 0x00, 0x00, 0x00, 0x00);
	$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(100, CAM_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($cmd, 6);

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

	$retry_cnt = 0;

	while (1)
	{
		clear_rx_buf();
		send_cmd($cmd, 6);

		if (camera_wait_for_ACK(100, CAM_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_PREFIX, CAM_CMD_PACKAGESIZE , 0x08, PIC_PKT_LEN & 0xff, (PIC_PKT_LEN>>8) & 0xff ,0);

	$retry_cnt = 0;

	while (1)
	{
		clear_rx_buf();
		send_cmd($cmd, 6);

		if (camera_wait_for_ACK(100, CAM_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_PREFIX, CAM_CMD_RESET , 0x00, 0x00, 0x00, 0x00 );
	$resp = "";

	$retry_cnt = 0;

	while (1)
	{
		clear_rx_buf();
		send_cmd($cmd, 6);

		if (camera_wait_for_ACK(100, CAM_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_PREFIX, CAM_CMD_SNAPSHOT , 0x00, 0x00, 0x00 ,0x00);
	$resp = "";

	$retry_cnt = 0;

	while (1)
	{
		clear_rx_buf();
		send_cmd($cmd, 6);

		if (camera_wait_for_ACK(100, CAM_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_PREFIX, CAM_CMD_GETPICTURE , 0x01, 0x00, 0x00 ,0x00);
	$resp = "";
	$retry_cnt = 0;

	while (1)
	{
		clear_rx_buf();
		$retry_cnt++;
		send_cmd($cmd, 6);

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

		if (camera_wait_for_ACK(100, CAM_CMD_GETPICTURE))
		{
			if (read_bytes($resp, 6, 1000) != 6)
			{
				continue;
			}
			if (bin2int($resp, 0, 1) == CAM_CMD_PREFIX
				&& bin2int($resp, 1, 1) == (CAM_CMD_DATA )
				&& bin2int($resp, 2, 1) == 0x01)
			{
				$pic_total_len = (bin2int($resp, 3, 1)) | (bin2int($resp, 4, 1) << 8) | (bin2int($resp, 5, 1) << 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_PREFIX, CAM_CMD_ACK , 0x00, 0x00, 0x00, 0x00 );

	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($cmd, 6);
			$cnt = read_bytes($packet, $len, 1200);

			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($packet, 4, $cnt-6);

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

	return true;
}
?>

Credits

phpoc_man

phpoc_man

62 projects • 407 followers

Comments