[Project]

Control light via motion or web


[Items]

1. PHPoC Blue
2. Relay Board (4-Port Digital Output Board)
3. mikroBUS Board
4. mikroBUS IR Gesture click made by MikroElektronika
5. light


Click image for larger version  Name:	light.jpg Views:	1 Size:	66.9 KB ID:	402

Click image for larger version  Name:	phpoc_board.jpg Views:	1 Size:	127.0 KB ID:	403

Click image for larger version  Name:	light_with_phpoc_board.jpg Views:	1 Size:	51.9 KB ID:	404


[How it works]

Once the sensor reports that gesture has occurred, PHPoC Blue reads the direction.
When gesture occurred from the left direction, the lamp will be turned on.
It reads the right direction for the light to be turned off.
When you open a webpage, you can connect PHPoC Blue though websocket automatically.
If you click the light image on the web, you can control the light to be turned on and off.


[Video clip]





[Source Code]

1. task0.php
Code:
<?php

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

include_once "vd_ir_gesture_click.php";
include_once "/lib/sd_340.php";
include_once "/lib/sd_spc.php";
include_once "/lib/sn_tcp_ws.php";

define("SLOT_INT", 8);
define("RELAY_OUT", 19);
define("RELAY_STATUS", 16);

$sid = 1;
$rwbuf = "";
$return_value = "";

uio_setup(0, SLOT_INT, "in"); // INT
uio_setup(0, RELAY_OUT, "OUT");
uio_setup(0, RELAY_STATUS, "IN");
uio_out(0, RELAY_OUT, LOW);

spc_reset();
spc_sync_baud(115200);
spc_scan();
spc_request($sid, 4, "set 0 output low");

ir_gesture_setup(0);

ws_setup(0, "gesture", "csv.phpoc");


function check_light_status()
{
    if("200,1" == spc_request(1, 4, "get 3 output"))
        return "on";
    else
        return "off";
}

while(1)
{
    $ws_state = ws_state(0);

    if($ws_state == TCP_CONNECTED)
    {    
        $return_value = check_light_status();

        ws_write(0, $return_value);

        $rlen = ws_read_line(0, $rwbuf);
                if($rlen) {
                    //$rwcmd = rtrim($rwbuf, "\r\n");
                    $rwcmd = (int)$rwbuf;

                    switch ($rwcmd)
                    {
                    case 0:
                        uio_out(0, RELAY_OUT, LOW);                    
                        break;
                    case 1:
                        uio_out(0, RELAY_OUT, HIGH);
                        break;
                    case 2:
                        $return_value = check_light_status();
                        if ( "on" == $return_value )
                            spc_request($sid, 4, "set 3 output low");
                        else
                            spc_request($sid, 4, "set 3 output high");
                        break;                    
                    default:                        
                        $gesture = "none";
                        break;
                    }
                    $return_value = check_light_status();
            }
            ws_write(0, $return_value);
    }
    else {
        if(uio_in(0, SLOT_INT) == 0)
        {
            if(is_status_register_gint())
            {
                if(is_gesture_status_register_gvalid())
                {
                    $res = read_gesture();
                    $gesture = "";
                    switch($res)
                    {
                        case DIRECTION_UP:
                            $gesture = "up";
                            break;
                        case DIRECTION_DOWN:
                            $gesture = "down";
                            break;
                        case DIRECTION_LEFT:
                            $gesture = "left";
                            spc_request($sid, 4, "set 3 output high");
                            break;
                        case DIRECTION_RIGHT:
                            $gesture = "right";
                            spc_request($sid, 4, "set 3 output low");
                            break;
                        case DIRECTION_NEAR:
                            $gesture = "near";
                            break;
                        case DIRECTION_FAR:
                            $gesture = "far";
                            break;
                        default:                        
                            $gesture = "none";
                            break;
                    }
                }
            }
        }        
    }
}

?>
2. index.php
Code:
<!DOCTYPE html>
<html>
<head>
<title>PHPoC / <?echo system("uname -i")?></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<style> body { text-align: center; } </style>
<script>
var ws;

function init()
{
    imgChange("light_on.jpg");

    var button = document.getElementById("light_canvas");

    button.addEventListener("touchstart", mouse_down);
//    button.addEventListener("touchend", mouse_up);
//    button.addEventListener("touchcancel", mouse_up);
    button.addEventListener("mousedown", mouse_down);
//    button.addEventListener("mouseup", mouse_up);
//    button.addEventListener("mouseout", mouse_up);

    connect_onclick();
}

function imgChange(imagePath) {
            var canvas    = document.getElementById("light_canvas");
            var context = canvas.getContext("2d");
            var imageObject = new Image();
            imageObject.onload = function(){
                  context.drawImage(imageObject,0,0);
            };
            imageObject.src=imagePath;
}

function connect_onclick()
{
    if(ws == null)
    {
        var ws_host_addr = "<?echo _SERVER("HTTP_HOST")?>";

        if((navigator.platform.indexOf("Win") != -1) && (ws_host_addr.charAt(0) == "["))
        {
            // network resource identifier to UNC path name conversion
            ws_host_addr = ws_host_addr.replace(/[\[\]]/g, '');
            ws_host_addr = ws_host_addr.replace(/:/g, "-");
            ws_host_addr += ".ipv6-literal.net";
        }

        ws = new WebSocket("ws://" + ws_host_addr + "/gesture", "csv.phpoc");

        document.getElementById("ws_state").innerHTML = "CONNECTING";

        ws.onopen = ws_onopen;
        ws.onclose = ws_onclose;
        ws.onmessage = ws_onmessage;
    }
    else
        ws.close();
}

function mouse_down()
{
    if(ws.readyState == 1)
       ws.send("2\r\n");

    event.preventDefault();
}

function mouse_up()
{
}

function ws_onopen()
{
    document.getElementById("ws_state").innerHTML = "<font color='blue'>CONNECTED</font>";
    document.getElementById("bt_connect").innerHTML = "Disconnect";
}
function ws_onclose()
{
    document.getElementById("ws_state").innerHTML = "<font color='gray'>CLOSED</font>";
    document.getElementById("bt_connect").innerHTML = "Connect";

    ws.onopen = null;
    ws.onclose = null;
    ws.onmessage = null;
    ws = null;

//    document.getElementById("msg").hidden = false;
}
function ws_onmessage(e_msg)
{
    e_msg = e_msg || window.event; // MessageEvent

    switch(e_msg.data)
    {
        case "on":
            // on
            imgChange("light_on.jpg");            
            break;
        case "off":
            // off
            imgChange("light_off.jpg");
            break;
        default:
            break;
    }
}

window.onload = init;
</script>
</head>

<body>
<canvas id="light_canvas" width="194" height="605"></canvas>
<br/><br/>
<h2>WebSocket <font id="ws_state" color="gray">CLOSED</font></h2>
<button id="bt_connect" type="button" onclick="connect_onclick();">Connect</button>

</body>
</html>
3. vd_ir_gesture_click.php
Code:
<?php
include_once "/lib/sd_340.php";

define("APDS9960_I2C_ADDRESS", 0x39);

define("REGISTER_ENABLE", "\x80");
define("REGISTER_ADC_INTEGRATION_TIME", "\x81");

define("REGISTER_WAIT_TIME", "\x83");

define("REGISTER_ALS_INT_LOW_THRE_LOW", "\x84");
define("REGISTER_ALS_INT_LOW_THRE_HIGH", "\x85");
define("REGISTER_ALS_INT_HIGH_THRE_LOW", "\x86");
define("REGISTER_ALS_INT_HIGH_THRE_HIGH", "\x87");

define("REGISTER_P_INT_LOW_THRESHOLD", "\x89");
define("REGISTER_P_INT_HIGH_THRESHOLD", "\x8B");

define("REGISTER_PERSISTENCE", "\x8C");
define("REGISTER_CONFIG1", "\x8D");
define("REGISTER_P_PULSE_COUNT", "\x8E");
define("REGISTER_CONTROL1", "\x8F");
define("REGISTER_CONFIG2", "\x90");

define("REGISTER_ID", "\x92");
define("REGISTER_STATUS", "\x93");

define("REGISTER_P_OFFSET_UP_RIGHT", "\x9D");
define("REGISTER_P_OFFSET_DOWN_LEFT", "\x9E");

define("REGISTER_CONFIG3", "\x9F");

define("REGISTER_G_P_ENTER_THRESHOLD", "\xA0");
define("REGISTER_G_EXIT_THRESHOLD", "\xA1");
define("REGISTER_G_CONFIG1", "\xA2");
define("REGISTER_G_CONFIG2", "\xA3");

define("REGISTER_G_UP_OFFSET", "\xA4");
define("REGISTER_G_DOWN_OFFSET", "\xA5");
define("REGISTER_G_LEFT_OFFSET", "\xA7");
define("REGISTER_G_RIGHT_OFFSET", "\xA9");

define("REGISTER_G_PULSE_COUNT_AND_LEN", "\xA6");

define("REGISTER_G_CONFIG3", "\xAA");
define("REGISTER_G_CONFIG4", "\xAB");
define("REGISTER_G_FIFO_LEVEL", "\xAE");
define("REGISTER_G_STATUS", "\xAF");

define("REGISTER_G_FIFO_UP", "\xFC");
define("REGISTER_G_FIFO_DOWN", "\xFD");
define("REGISTER_G_FIFO_LEFT", "\xFE");
define("REGISTER_G_FIFO_RIGHT", "\xFF");

define("GESTURE_THRESHOLD_OUT", 10);
define("GESTURE_SENSITIVITY_1", 50);
define("GESTURE_SENSITIVITY_2", 20);

define("DIRECTION_NONE", 0);
define("DIRECTION_LEFT", 1);
define("DIRECTION_RIGHT", 2);
define("DIRECTION_UP", 3);
define("DIRECTION_DOWN", 4);
define("DIRECTION_NEAR", 5);
define("DIRECTION_FAR", 6);
define("DIRECTION_ALL", 7);

define("PROXIMITY_NA_STATE", 0);
define("PROXIMITY_NEAR_STATE", 1);
define("PROXIMITY_FAR_STATE", 2);
define("PROXIMITY_ALL_STATE", 3);

$vd_gesture_fifo_up = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
$vd_gesture_fifo_down = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
$vd_gesture_fifo_left = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
$vd_gesture_fifo_right = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

$vd_i2c_id = 0;

$vd_total_gestures = 0;
$vd_direction = DIRECTION_NONE;

$vd_up_down_delta = 0;
$vd_left_right_delta = 0;

$vd_up_down_count = 0;
$vd_left_right_count = 0;
$vd_near_count = 0;
$vd_far_count = 0;

$vd_proximity_state = PROXIMITY_NA_STATE;

function vd_write_register($register_address, $buf)
{
    $wbuf = $register_address.$buf;
    return i2c_write(0, $wbuf);
}

function vd_read_register($register_address, &$buf, $length)
{
    global $vd_i2c_id;
    $wbuf = $register_address;
    $read = i2c_write_read($vd_i2c_id, $wbuf, $buf, $length);
    return $read;
}

define("POWER_ON", 0);
define("ALS_ENABLE", 1);
define("PROXIMITY_DETECT_ENABLE", 2);
define("WAIT_ENABLE", 3);
define("ALS_INTERRUPT_ENABLE", 4);
define("PROXIMITY_INTERRUPT_ENABLE", 5);
define("GESTURE_ENABLE", 6);
define("ALL", 7);

function vd_set_enable_register($field, $enable)
{
    $rbuf = "";
    $read = vd_read_register(REGISTER_ENABLE, $rbuf, 1);
    if($read == 0)
        return;

    $reg_val = bin2int($rbuf, 0, 1);

    if($field == ALL)
    {
        $reg_val = 0x00;
        if($enable == true)
            $reg_val = 0x7F;
    }
    else
    {
        $reg_val &= ~(1 << $field);
        if($enable == true)
            $reg_val |= (1 << $field);
    }

    return vd_write_register(REGISTER_ENABLE, int2bin($reg_val, 1));
}

define("LED_DRIVE_STRENGTH", 0);
define("PROXIMITY_GAIN_CONTROL", 1);
define("ALS_COLOR_GAIN_CONTROL", 2);

function vd_write_control_register1($field, $value)
{
    $rbuf = "";
    vd_read_register(REGISTER_CONTROL1, $rbuf, 1);

    $reg_val = bin2int($rbuf, 0, 1);

    switch($field)
    {
        case LED_DRIVE_STRENGTH:
            $reg_val &= ~(1 << 6) | (1 << 7);
            $value = ($value << 6) | $reg_val; 
            break;
        case PROXIMITY_GAIN_CONTROL:
            $reg_val &= ~(1 << 2) | (1 << 3); 
            $value = ($value << 2) | $reg_val; 
            break;
        case ALS_COLOR_GAIN_CONTROL:
            $reg_val &= ~(1 << 0) | (1 << 1); 
            $value |= $reg_val; 
            break;
    }

    return vd_write_register(REGISTER_CONTROL1, int2bin($value, 1));
}

function vd_write_als_low_int_threshold($value)
{
    $val_low = ($value & 0x00ff);
    $val_high = ($value & 0xff00) >> 8;

    vd_write_register(REGISTER_ALS_INT_LOW_THRE_LOW, int2bin($val_low, 1));
    vd_write_register(REGISTER_ALS_INT_LOW_THRE_HIGH, int2bin($val_high, 1));
}

function vd_write_als_high_int_threshold($value)
{
    $val_low = ($value & 0x00ff);
    $val_high = ($value & 0xff00) >> 8;

    vd_write_register(REGISTER_ALS_INT_HIGH_THRE_HIGH, int2bin($val_high, 1));
    vd_write_register(REGISTER_ALS_INT_HIGH_THRE_LOW, int2bin($val_low, 1));
}

define("GESTURE_WAIT_TIME", 0);
define("GESTURE_LED_DRIVE_STRENGTH", 1);
define("GESTURE_GAIN_CONTROL", 2);

function vd_write_gesture_config2_register($field, $value)
{
    $rbuf = "";
    vd_read_register(REGISTER_G_CONFIG2, $rbuf, 1);

    $reg_val = bin2int($rbuf, 0, 1);

    switch($field)
    {
        case GESTURE_GAIN_CONTROL:
            $reg_val &= ~(1 << 5) | (1 << 6);
            $value = ($value << 5) | $reg_val; 
            break;
        case GESTURE_LED_DRIVE_STRENGTH:
            $reg_val &= ~(1 << 3) | (1 << 4); 
            $value = ($value << 3) | $reg_val; 
            break;
        case GESTURE_WAIT_TIME:
            $reg_val &= ~(1 << 0) | (1 << 1) | (1 << 2);
            $value |= $reg_val; 
            break;
    }

    return vd_write_register(REGISTER_G_CONFIG2, int2bin($value, 1));
}

define("GESTURE_MODE", 0);
define("GESTURE_INTERRUPT_ENABLE", 1);

function vd_write_gesture_config4_register($field, $value)
{
    $rbuf = "";    
    vd_read_register(REGISTER_G_CONFIG4, $rbuf, 1);

    $reg_val = bin2int($rbuf, 0, 1);

    switch($field)
    {
        case GESTURE_MODE:
            $reg_val &= ~(1 << 0);
            $value = ($value << 0) | $reg_val; 
            break;
        case GESTURE_INTERRUPT_ENABLE:
            $reg_val &= ~(1 << 1);
            $value = ($value << 1) | $reg_val; 
            break;
    }

    return vd_write_register(REGISTER_G_CONFIG4, int2bin($value, 1));
}

function vd_write_gesture_led_boost($value)
{
    $rbuf = "";    
    vd_read_register(REGISTER_CONFIG2, $rbuf, 1);

    $reg_val = bin2int($rbuf, 0, 1);

    $reg_val &= ~(1 << 4) | (1 << 5);
    $reg_val = ($value << 4) | $reg_val;

    return vd_write_register(REGISTER_G_CONFIG4, int2bin($value, 1));
}

function ir_gesture_setup($i2c_id)
{
    global $vd_i2c_id;
    $vd_i2c_id = $i2c_id;
    i2c_setup($i2c_id, APDS9960_I2C_ADDRESS, "sm");

    // read chip id.
    $rbuf = "";
    $read = vd_read_register(REGISTER_ID, $rbuf, 1);
    if($read == 0)
    {
        echo "Reading ID register has failed.\r\n";
        exit;
    }

    //----------------------------------------------------------------------------------------------------------------
    // Disable all features.
    vd_set_enable_register(ALL, false);
    //----------------------------------------------------------------------------------------------------------------

    //----------------------------------------------------------------------------------------------------------------
    /// Set default values for ambient light and proximity registers
    vd_write_register(REGISTER_ADC_INTEGRATION_TIME, int2bin(219, 1)); // 219 - 103ms
    vd_write_register(REGISTER_WAIT_TIME, int2bin(246, 1)); // 246 - 27ms ((256 - 246) * 2.78ms)
    vd_write_register(REGISTER_P_PULSE_COUNT, "\x87"); // proximity pulse count register: 0x87, 16us, 8 pulses
    vd_write_register(REGISTER_P_OFFSET_UP_RIGHT, "\x00"); //0 - 0 offset
    vd_write_register(REGISTER_P_OFFSET_DOWN_LEFT, "\x00"); // 0 - 0 offset
    vd_write_register(REGISTER_CONFIG1, "\x60"); // do not use 12x wait (WTIME) factor

    //-----------------------------------------------------------------------------------
    // Control Register One(0x8F)
    // LDRIVE - bits 7:6, 0 - 100 mA, 1 - 50 mA, 2 - 25 mA, 3 - 12.5 mA
    vd_write_control_register1(LED_DRIVE_STRENGTH, 0);
    // PGAIN - bits 3:2, 0 - 1x, 1 - 2x, 2 - 4x, 3 - 8x
    vd_write_control_register1(PROXIMITY_GAIN_CONTROL, 2);
    // AGAIN - bits 1:0, 0 - 1x, 1 - 4x, 2 - 16x, 3 - 64x
    vd_write_control_register1(ALS_COLOR_GAIN_CONTROL, 1);
    //-----------------------------------------------------------------------------------

    vd_write_register(REGISTER_P_INT_LOW_THRESHOLD, "\x00"); // Low proximity threshold
    vd_write_register(REGISTER_P_INT_HIGH_THRESHOLD, int2bin(50, 1)); // High proximity threshold

    vd_write_als_low_int_threshold(0xffff); 
    vd_write_als_high_int_threshold(0);

    vd_write_register(REGISTER_PERSISTENCE, "\x22"); //0x22    // 2 consecutive prox or ALS for int.
    vd_write_register(REGISTER_CONFIG2, "\x01"); // Configuration Register Two(0x90): no saturation interrupts or LED boost
    vd_write_register(REGISTER_CONFIG3, "\x00"); // Configuration Three Register(0x9F): Enable all photodiodes, no SAI
    //----------------------------------------------------------------------------------------------------------------

    //----------------------------------------------------------------------------------------------------------------
    // Set default values for gesture sense registers
    vd_write_register(REGISTER_G_P_ENTER_THRESHOLD, int2bin(40, 1)); // Threshold for entering gesture mode
    vd_write_register(REGISTER_G_EXIT_THRESHOLD, int2bin(30, 1)); // threshold for exit gesture proximity
    vd_write_register(REGISTER_G_CONFIG1, "\x40"); // Gesture Configuration One Register(0xA2): interrupt is generated after 4 datasets are added to FIFO.

    //-----------------------------------------------------------------------------------
    // Gesture Configuration Two Register(0xA3)
    // GGAIN - bits 6:5, 0 - 1x, 1 - 2x, 2 - 4x, 3 - 8x
    vd_write_gesture_config2_register(GESTURE_GAIN_CONTROL, 2);    
    // GLDRIVE - bits 4:3, 0 - 100mA, 1 - 50mA, 2 - 25mA, 3 - 12.5mA
    vd_write_gesture_config2_register(GESTURE_LED_DRIVE_STRENGTH, 0);    
    // GWTIME - bits 2:0, 0 - 0ms, 1 - 2.8ms, 2 - 5.6ms, 3 - 8.4ms, 4 - 14.0ms, 5 - 22.4ms, 6 - 30.8ms, 7 - 39.2ms
    vd_write_gesture_config2_register(GESTURE_WAIT_TIME, 1);
    //-----------------------------------------------------------------------------------

    vd_write_register(REGISTER_G_UP_OFFSET, "\x00");
    vd_write_register(REGISTER_G_DOWN_OFFSET, "\x00");
    vd_write_register(REGISTER_G_LEFT_OFFSET, "\x00");
    vd_write_register(REGISTER_G_RIGHT_OFFSET, "\x00");

    // Gesture Pulse Count and Length Register(0xA6)
    // GPLEN : bits 7:6, 0 - 4us, 1 - 8us(default), 2 - 16us, 3 - 32us
     // GPULSE : bits 5:0, 0 - 1, 1 - 2, 2 - 3, ..... 63 - 64.
    vd_write_register(REGISTER_G_PULSE_COUNT_AND_LEN, "\xC9"); // 32us, 10 pulses
    // Gesture Configuration Three Register(0xAA)
    vd_write_register(REGISTER_G_CONFIG3, "\x00"); // All photodiodes active during gesture

    //-----------------------------------------------------------------------------------
    // Gesture Configuration Four Register(0xAB)
    // GIEN(Gesture interrupt enable) bits 1.
    vd_write_gesture_config4_register(GESTURE_INTERRUPT_ENABLE, 0); // Disable gesture interrupts
    //-----------------------------------------------------------------------------------
    //----------------------------------------------------------------------------------------------------------------

    //----------------------------------------------------------------------------------------------------------------
    // enable gesture sensor
    vd_write_register(REGISTER_WAIT_TIME, "\xff");     // ((256 - 255) * 2.78ms) = 2.78ms
    vd_write_register(REGISTER_P_PULSE_COUNT, "\x89"); // proximity pulse count register: 16us, 10 pulses
    vd_write_gesture_led_boost(3); //LED_BOOST: bits 5:4, 0 - 100%, 1 - 150%, 2 - 200%, 3 - 300%

    //-----------------------------------------------------------------------------------
    // Gesture Configuration Four Register(0xAB)
    // GIEN(Gesture interrupt enable) bits 1.
    vd_write_gesture_config4_register(GESTURE_INTERRUPT_ENABLE, 1);
    // GMODE(Gesture mode) bits 0.
    vd_write_gesture_config4_register(GESTURE_MODE, 1);
    //-----------------------------------------------------------------------------------

    //-----------------------------------------------------------------------------------
    // Enable Register(0x80)
    vd_set_enable_register(POWER_ON, true); // PON(power on) bits 0. 
    vd_set_enable_register(WAIT_ENABLE, true);// WEN(wait enable) bits 3.
    vd_set_enable_register(PROXIMITY_DETECT_ENABLE, true); // PEN(proximity detect enable) bits 2.
    vd_set_enable_register(GESTURE_ENABLE, true); // GEN(gesture enable) bits 6.
    //-----------------------------------------------------------------------------------
    //----------------------------------------------------------------------------------------------------------------
}

function read_enable_register()
{    
    $rbuf = "";    
    vd_read_register(REGISTER_ENABLE, $rbuf, 1);
    return bin2int($rbuf, 0, 1);
}

function is_gesture_status_register_gvalid()
{
    $rbuf = "";    
    vd_read_register(REGISTER_G_STATUS, $rbuf, 1);    
    $status = bin2int($rbuf, 0, 1);
    $status &= 0b00000001;
    return $status == 1 ? true : false;
}

function is_status_register_gint()
{
    $rbuf = "";    
    vd_read_register(REGISTER_STATUS, $rbuf, 1);    
    $status = bin2int($rbuf, 0, 1);
    return ($status & (1 << 2)) ? true : false;
}

function process_gesture_data()
{
    global $vd_gesture_fifo_up, $vd_gesture_fifo_down, $vd_gesture_fifo_left, $vd_gesture_fifo_right;
    global $vd_total_gestures;
    global $vd_up_down_delta;
    global $vd_left_right_delta;
    global $vd_up_down_count;
    global $vd_left_right_count;
    global $vd_near_count;
    global $vd_far_count;
    global $vd_proximity_state;

    $first_up = $first_down = $first_left = $first_right = 0;
    $last_up = $last_down = $last_left = $last_right = 0;

    /* If we have less than 4 total gestures, that's not enough */
    if($vd_total_gestures <= 4)
        return -1;

    /* Check to make sure our data isn't out of bounds */
    if(($vd_total_gestures <= 32 ) && ($vd_total_gestures > 0))
    {
        /* Find the first value in U/D/L/R above the threshold */
        for($i = 0;$i < $vd_total_gestures;$i++)
        {
            if(($vd_gesture_fifo_up[$i] > GESTURE_THRESHOLD_OUT) && ($vd_gesture_fifo_down[$i] > GESTURE_THRESHOLD_OUT) && ($vd_gesture_fifo_left[$i] > GESTURE_THRESHOLD_OUT) && ($vd_gesture_fifo_right[$i] > GESTURE_THRESHOLD_OUT))
            {
                $first_up = $vd_gesture_fifo_up[$i];
                $first_down = $vd_gesture_fifo_down[$i];
                $first_left = $vd_gesture_fifo_left[$i];
                $first_right = $vd_gesture_fifo_right[$i];
                break;
            }
        }

        /* If one of the _first values is 0, then there is no good data */
        if(($first_up == 0) || ($first_down == 0) || ($first_left == 0) || ($first_right == 0))
            return -1;
        /* Find the last value in U/D/L/R above the threshold */
        for($i = $vd_total_gestures - 1;$i >= 0;$i--)
        {
            if(($vd_gesture_fifo_up[$i] > GESTURE_THRESHOLD_OUT) && ($vd_gesture_fifo_down[$i] > GESTURE_THRESHOLD_OUT) && ($vd_gesture_fifo_left[$i] > GESTURE_THRESHOLD_OUT) && ($vd_gesture_fifo_right[$i] > GESTURE_THRESHOLD_OUT))
            {        
                $last_up = $vd_gesture_fifo_up[$i];
                $last_down = $vd_gesture_fifo_down[$i];
                $last_left = $vd_gesture_fifo_left[$i];
                $last_right = $vd_gesture_fifo_right[$i];
                break;
            }
        }
    }

    /* Calculate the first vs. last ratio of up/down and left/right */
    $up_down_ratio_first = (($first_up - $first_down) * 100) / ($first_up + $first_down);
    $left_right_ratio_first = (($first_left - $first_right) * 100) / ($first_left + $first_right);
    $up_down_ratio_last = (($last_up - $last_down) * 100) / ($last_up + $last_down);
    $left_right_ratio_last = (($last_left - $last_right) * 100) / ($last_left + $last_right);

    /* Determine the difference between the first and last ratios */
    $delta1 = $up_down_ratio_last - $up_down_ratio_first;
    $delta2 = $left_right_ratio_last - $left_right_ratio_first;

    /* Accumulate the UD and LR delta values */
    $vd_up_down_delta += $delta1;
    $vd_left_right_delta += $delta2;

    /* Determine U/D gesture */
    if($vd_up_down_delta >= GESTURE_SENSITIVITY_1)
        $vd_up_down_count = 1;
    else if($vd_up_down_delta <= -GESTURE_SENSITIVITY_1)
        $vd_up_down_count = -1;
    else
        $vd_up_down_count = 0;

    /* Determine L/R gesture */
    if($vd_left_right_delta >= GESTURE_SENSITIVITY_1)
        $vd_left_right_count = 1;
    else if($vd_left_right_delta <= -GESTURE_SENSITIVITY_1)
        $vd_left_right_count = -1;
    else
        $vd_left_right_count = 0;

    /* Determine Near/Far gesture */
    if(($vd_up_down_count == 0) && ($vd_left_right_count == 0))
    {
        if((abs($vd_up_down_delta) < GESTURE_SENSITIVITY_2 ) && (abs($vd_left_right_delta) < GESTURE_SENSITIVITY_2))
        {
            if(($vd_up_down_delta == 0) && ($vd_left_right_delta == 0))
                $vd_near_count++;
            else if(($vd_up_down_delta != 0) || ($vd_left_right_delta != 0))
                $vd_far_count++;

            if(($vd_near_count >= 10) && ($vd_far_count >= 2))
            {
                if(($vd_up_down_delta == 0) && ($vd_left_right_delta == 0))
                    $vd_proximity_state = PROXIMITY_NEAR_STATE;
                else if(($vd_up_down_delta != 0) && ($vd_left_right_delta != 0))
                    $vd_proximity_state = PROXIMITY_FAR_STATE;
                return 0;
            }
        }
    } 
    else 
    {
        if((abs($vd_up_down_delta) < GESTURE_SENSITIVITY_2) && (abs($vd_left_right_delta) < GESTURE_SENSITIVITY_2))
        {
            if(($vd_up_down_delta == 0) && ($vd_left_right_delta == 0))
                $vd_near_count++;

            if($vd_near_count >= 10)
            {
                $vd_up_down_count = 0;
                $vd_left_right_count = 0;
                $vd_up_down_delta = 0;
                $vd_left_right_delta = 0;
            }
        }
    }
    return -1;
}

function decode_gesture()
{
    global $vd_proximity_state;
    global $vd_direction;
    global $vd_up_down_count, $vd_left_right_count;
    global $vd_up_down_delta, $vd_left_right_delta;

    if($vd_proximity_state == PROXIMITY_NEAR_STATE)
    {
        $vd_direction = DIRECTION_NEAR;
        return 0;
    }
    else if($vd_proximity_state == PROXIMITY_FAR_STATE)
    {
        $vd_direction = DIRECTION_FAR;
        return 0;
    }

    /* Determine swipe direction */
    if(($vd_up_down_count  == -1) && ($vd_left_right_count == 0))
        $vd_direction = DIRECTION_UP;
    else if(($vd_up_down_count == 1) && ($vd_left_right_count == 0))
        $vd_direction = DIRECTION_DOWN;
    else if(($vd_up_down_count == 0) && ($vd_left_right_count == 1))
        $vd_direction = DIRECTION_RIGHT;
    else if(($vd_up_down_count == 0) && ($vd_left_right_count == -1))
        $vd_direction = DIRECTION_LEFT;
    else if(($vd_up_down_count == -1) && ($vd_left_right_count == 1))
    {
        if(abs($vd_up_down_delta) > abs($vd_left_right_delta))
            $vd_direction = DIRECTION_UP;
        else
            $vd_direction = DIRECTION_RIGHT;
    }
    else if(($vd_up_down_count == 1) && ($vd_left_right_count == -1))
    {
        if(abs($vd_up_down_delta) > abs($vd_left_right_delta))
            $vd_direction = DIRECTION_DOWN;
        else
            $vd_direction = DIRECTION_LEFT;
    }
    else if(($vd_up_down_count == -1) && ($vd_left_right_count == -1))
    {
        if(abs($vd_up_down_delta) > abs($vd_left_right_delta))
            $vd_direction = DIRECTION_UP;
        else
            $vd_direction = DIRECTION_LEFT;
    }
    else if(($vd_up_down_count == 1) && ($vd_left_right_count == 1))
    {
        if(abs($vd_up_down_delta) > abs($vd_left_right_delta))
            $vd_direction = DIRECTION_DOWN;
        else
            $vd_direction = DIRECTION_RIGHT;
    }
    else
        return -1;

    return 0;
}

function read_gesture()
{
    global $vd_gesture_fifo_up, $vd_gesture_fifo_down, $vd_gesture_fifo_left, $vd_gesture_fifo_right;
    global $vd_total_gestures;
    global $vd_direction;
    global $vd_up_down_delta;
    global $vd_left_right_delta;
    global $vd_up_down_count;
    global $vd_left_right_count;
    global $vd_near_count;
    global $vd_far_count;
    global $vd_proximity_state;

    if(!is_gesture_status_register_gvalid() || !(read_enable_register() & 0b01000001))
        return DIRECTION_NONE;;

    $rbuf = "";
    $fifo_count = 0;

    $index = 0;
    $vd_total_gestures = 0;
    $vd_direction = DIRECTION_NONE;

    $vd_up_down_delta = 0;
    $vd_left_right_delta = 0;

    $vd_up_down_count = 0;
    $vd_left_right_count = 0;

    $vd_near_count = 0;
    $far_counbt = 0;

    $vd_proximity_state = PROXIMITY_NA_STATE;

    while(1)
    {
        if(is_gesture_status_register_gvalid())
        {
            $rbuf = "";
            $wbuf = REGISTER_G_FIFO_LEVEL;
            $read = i2c_write_read(0, $wbuf, $rbuf, 1);
            if($read == 0)
                $fifo_count = 0;
            else
                $fifo_count = bin2int($rbuf, 0, 1);

            if($fifo_count > 0)
            {
                $rbuf = "";
                $wbuf = REGISTER_G_FIFO_UP;
                $read = i2c_write_read(0, $wbuf, $rbuf, $fifo_count * 4);

                for($i = 0;$i < $fifo_count * 4;$i += 4)
                {
                    $vd_gesture_fifo_up[$index] = bin2int($rbuf[$i], 0, 1);
                    $vd_gesture_fifo_down[$index] = bin2int($rbuf[$i + 1], 0, 1);
                    $vd_gesture_fifo_left[$index] = bin2int($rbuf[$i + 2], 0, 1);
                    $vd_gesture_fifo_right[$index] = bin2int($rbuf[$i + 3], 0, 1);

                    $index++;
                    $vd_total_gestures++;
                }
                //-------------------------------------------------
                if(!process_gesture_data())
                {
                    if(!decode_gesture())
                    {
                    }
                }
                //-------------------------------------------------
                $index = 0;
                $vd_total_gestures = 0;
            }
        }
        else
        {
            decode_gesture();
            return $vd_direction;
        }
    }
}
?>