phpoc_man
Published © GPL3+

Real-Time Monitoring Dice's State on Webpage

This project shows how to monitor a dice's state on webpage using PHPoC and 3D motion sensor.

BeginnerFull instructions provided1,514

Things used in this project

Hardware components

PHPoC Blue
PHPoC Blue
×1
PHPoC mikroBUS Expansion Board
PHPoC mikroBUS Expansion Board
×1
MikroE MM7150 3D motion sensor
×1
Batery
×1
Handmade Dice Box
×1

Story

Read more

Schematics

Components

Code

Main task (task0.php)

PHP
This task responds for reading data from MM7150 and send it via websocket to Client.
You can get library for MM7150 here: http://www.phpoc.com/forum/viewtopic.php?f=42&t=223
<?php

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

include_once "/lib/sd_340.php";
include_once "/lib/vd_MM7150_pin_map.php";
include_once "/lib/vn_hid_i2c.php";
include_once "/lib/vd_MM7150.php";

function unsign2sign($val)
{
    if($val&0x8000)
        $val = ($val&0x7fff) - 0x8000;
    
    return $val;
}
// Init user interface

i2c_setup(0, MM7150_ADDR);
uio_setup(0, INT_PIN, "in");
uio_setup(1, WAKEUP_PIN, "out");
uio_out(1, WAKEUP_PIN, HIGH);    
ws_setup(0, "inclinometer", "csv.phpoc");
//List of sensor in use

$sensor_list = array(
                ACCEL_SENSOR_TYPE, 
                GYRO_SENSOR_TYPE, 
                CMP_SENSOR_TYPE, 
                ORI_SENSOR_TYPE, 
                INCL_SENSOR_TYPE);

// Init MM7150. Retrieve HID & report descriptors, and all device features
mm7150_init($sensor_list);

//$sensor_type = ACCEL_SENSOR_TYPE;
//$sensor_type = GYRO_SENSOR_TYPE;
//$sensor_type = CMP_SENSOR_TYPE;
//$sensor_type = ORI_SENSOR_TYPE;
$sensor_type = INCL_SENSOR_TYPE;

mm7150_enable_sensor($sensor_type);

$mult = hid_i2c_get_exponent($sensor_type);

$data = "";    

hid_i2c_request_data($data, $sensor_type);
    
$x_val = unsign2sign(bin2int($data, 0, 2)) * $mult;
$y_val = unsign2sign(bin2int($data, 2, 2)) * $mult;
$z_val = unsign2sign(bin2int($data, 4, 2)) * $mult;
    
while(1)
{    
    /*
    //Polling Driven
    hid_i2c_request_data($data, $sensor_type);
    
    $x_val = unsign2sign(bin2int($data, 0, 2)) * $mult;
    $y_val = unsign2sign(bin2int($data, 2, 2)) * $mult;
    $z_val = unsign2sign(bin2int($data, 4, 2)) * $mult;
    
    echo "pitch: ", $x_val , ",   ";
    echo "roll : ", $y_val , ",   ";
    echo "yaw  : ", $z_val , "\r\n";
    
    */
    
    // Event Driven
    if(uio_in(0, INT_PIN) == INT_ASSERTED)
    {                    
        $data = "";        
        
        $in_sens_type = hid_i2c_read_data($data);
        
        if($in_sens_type == $sensor_type)
        {
            $x_val = unsign2sign(bin2int($data, 0, 2)) * $mult;
            $y_val = unsign2sign(bin2int($data, 2, 2)) * $mult;
            $z_val = unsign2sign(bin2int($data, 4, 2)) * $mult;
            
            echo "pitch: ", $x_val , ",   ";
            echo "roll : ", $y_val , ",   ";
            echo "yaw  : ", $z_val , "\r\n";
            
            if(ws_state(0) == TCP_CONNECTED)
            {
                $wbuf = "[$x_val, $y_val, $z_val]";
                ws_write(0, $wbuf);
            }
        }
    }
    //Client may request data for the first time to get offset.
    if(ws_state(0) == TCP_CONNECTED)
    {
        $rbuf = "";
        $rlen = ws_read_line(0, $rbuf);

        if($rlen)
        {
            $wbuf = "[$x_val, $y_val, $z_val]";
            ws_write(0, $wbuf);
        }
    }
}

?>

Web Interface (index.php)

PHP
This code responds for receive real-time data from PHPoC via web socket and update state of dice.
I use three js library https://threejs.org/ to draw 3D dice and rotate it
<!DOCTYPE html>
<html lang="en">
    <head>
        <title>three.js webgl - geometry - cube</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <style>
            body { text-align: center;} 
            #myCanvas {
                margin-right: auto;
                margin-left: auto;
                width: 400px; 
                height: 400px; 
                position: relative;
                border: 1px solid #000;
                background-color: #000000;
            }
        </style>
        <script>
            var pitch = 0, roll = 0, yaw = 0;
            var pitch_offset = 0, roll_offset = 0, yaw_offset = 0;            
            var ws = null;
            var first_data = true;
            
            function ws_onopen()
            {
                document.getElementById("ws_state").innerHTML = "OPEN";
                document.getElementById("wc_conn").innerHTML = "Disconnect";
                first_data = true;
                //Send request to get data 
                ws.send("0\r\n"); 
            }
            function ws_onclose()
            {
                document.getElementById("ws_state").innerHTML = "CLOSED";
                document.getElementById("wc_conn").innerHTML = "Connect";
                ws.onopen = null;
                ws.onclose = null;
                ws.onmessage = null;
                ws = null;
            }
            function ws_onmessage(e_msg)
            {
                var arr = JSON.parse(e_msg.data);
                if(first_data)
                {
                    first_data = false;
                    pitch_offset = arr[0];
                    roll_offset  = arr[1];
                    yaw_offset   = arr[2];
                }
                pitch = (arr[0] - pitch_offset) * Math.PI/180;
                roll  = (arr[1] - roll_offset) * Math.PI/180;
                yaw   = (arr[2] - yaw_offset) * Math.PI/180;
                console.log("pitch: "+ arr[0] +", roll: "+ arr[1] +", yaw: "+ arr[2]); 
            }
            function wc_onclick()
            {
                if(ws == null)
                {
                    ws = new WebSocket("ws://<?echo _SERVER("HTTP_HOST")?>/inclinometer", "csv.phpoc");
                    document.getElementById("ws_state").innerHTML = "CONNECTING";
                    
                    ws.onopen = ws_onopen;
                    ws.onclose = ws_onclose;
                    ws.onmessage = ws_onmessage; 
                }
                else
                    ws.close();
            }
        </script>
    </head>
    <body>
        <canvas id="myCanvas"></canvas>
        <script src="https://threejs.org/build/three.js"></script>

        <script>

            var camera, scene, renderer;
            var mesh;

            init();
            animate();

            function init() {

                camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
                camera.position.y = 40;
                camera.position.z = 60;
                camera.lookAt(new THREE.Vector3( 0, -2, -5 ));

                scene = new THREE.Scene();
                            
                var geometry = new THREE.BoxBufferGeometry(20, 20, 20, 2, 2, 2);
                
                //var material = new THREE.MeshBasicMaterial( { map: texture } );
                var materials = [
                       new THREE.MeshBasicMaterial({
                           map: new THREE.TextureLoader().load('dice_1.jpg')
                       }),
                       new THREE.MeshBasicMaterial({
                           map: new THREE.TextureLoader().load('dice_2.jpg')
                       }),
                       new THREE.MeshBasicMaterial({
                           map: new THREE.TextureLoader().load('dice_3.jpg')
                       }),
                       new THREE.MeshBasicMaterial({
                           map: new THREE.TextureLoader().load('dice_4.jpg')
                       }),
                       new THREE.MeshBasicMaterial({
                           map: new THREE.TextureLoader().load('dice_5.jpg')
                       }),
                       new THREE.MeshBasicMaterial({
                           map: new THREE.TextureLoader().load('dice_6.jpg')
                       })
                    ];

                mesh = new THREE.Mesh( geometry, new THREE.MeshFaceMaterial(materials ));
                mesh.position.y = 10;
                scene.add( mesh );
                
                var grid = new THREE.GridHelper( 50, 20, 0xFFFFFF, 0XFFFFFF);
                //grid.rotateOnAxis( new THREE.Vector3( 1, 0, 0 ), 90 * ( Math.PI/180 ) );
                grid.position.y = 0;
                grid.material.opacity = 0.25;
                grid.material.transparent = true;
                scene.add( grid );

                var canvas = document.getElementById("myCanvas");
                renderer = new THREE.WebGLRenderer({ canvas: canvas });
                
                renderer.setPixelRatio( window.devicePixelRatio );
                renderer.setSize( window.innerWidth/2, window.innerHeight/2 );
                document.body.appendChild( renderer.domElement );


                window.addEventListener( 'resize', onWindowResize, false );

            }

            function onWindowResize() {

                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();

                renderer.setSize( window.innerWidth/2, window.innerHeight/2 );

            }

            function animate() {

                requestAnimationFrame( animate );

                mesh.rotation.x = roll;
                mesh.rotation.y = yaw;
                mesh.rotation.z = pitch;

                renderer.render( scene, camera );
            }

        </script>
        <p>WebSocket : <span id="ws_state">null</span><br></p>
        <button id="wc_conn" type="button" onclick="wc_onclick();">Connect</button>
    </body>
</html>

Credits

phpoc_man

phpoc_man

62 projects • 406 followers

Comments