index.html
1 <!DOCTYPE html> 2 <!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]--> 3 <!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]--> 4 <!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]--> 5 <!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]--> 6 <head> 7 <meta charset="utf-8"> 8 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> 9 <title>Adafruit BNO055 Absolute Orientation Sensor</title> 10 <meta name="description" content=""> 11 <meta name="viewport" content="width=device-width, initial-scale=1"> 12 13 <link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet"> 14 <style> 15 body { 16 padding-top: 50px; 17 padding-bottom: 20px; 18 } 19 </style> 20 21 <!--[if lt IE 9]> 22 <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> 23 <script>window.html5 || document.write('<script src="js/vendor/html5shiv.js"><\/script>')</script> 24 <![endif]--> 25 </head> 26 <body> 27 <!--[if lt IE 7]> 28 <p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p> 29 <![endif]--> 30 <div class="container"> 31 <div class="row"> 32 <div class="col-sm-12"> 33 <h1 class="text-center">Adafruit BNO055 Absolute Orientation Sensor Demo</h1> 34 <h3 id="connecting">Connecting...</h3> 35 <div class="col-sm-12" id="renderer"> 36 </div> 37 </div> 38 </div> 39 <div class="row" id="controls"> 40 <div class="col-sm-4"> 41 <h3>Orientation (degrees):</h3> 42 <h4>Heading = <span id="heading">0</span></h4> 43 <h4>Roll = <span id="roll">0</span></h4> 44 <h4>Pitch = <span id="pitch">0</span></h4> 45 </div> 46 <div class="col-sm-4"> 47 <h3>Calibration:</h3> 48 <h4>(0=uncalibrated, 3=fully calibrated)</h4> 49 <h4>System = <span id="calSys">0</span></h4> 50 <h4>Gyro = <span id="calGyro">0</span></h4> 51 <h4>Accelerometer = <span id="calAccel">0</span></h4> 52 <h4>Magnetometer = <span id="calMag">0</span></h4> 53 </div> 54 <div class="col-sm-4"> 55 <h3>Actions:</h3> 56 <form> 57 <div class="form-group"> 58 <label for="model">Model: 59 <select class="form-control" id="model"> 60 </select> 61 </div> 62 <div class="form-group"> 63 <button type="button" class="btn btn-primary" id="straighten">Straighten</button> 64 </div> 65 <div class="form-group"> 66 <button type="button" class="btn btn-primary" id="saveCalibration">Save Calibration</button> 67 </div> 68 <div class="form-group"> 69 <button type="button" class="btn btn-primary" id="loadCalibration">Load Calibration</button> 70 </div> 71 </form> 72 </div> 73 </div> 74 </div> 75 <script src="{{ url_for('static', filename='js/jquery-2.1.4.min.js') }}"></script> 76 <script src="{{ url_for('static', filename='js/three.min.js') }}"></script> 77 <script src="{{ url_for('static', filename='js/DDSLoader.js') }}"></script> 78 <script src="{{ url_for('static', filename='js/MTLLoader.js') }}"></script> 79 <script src="{{ url_for('static', filename='js/OBJMTLLoader.js') }}"></script> 80 <script src="{{ url_for('static', filename='js/OBJLoader.js') }}"></script> 81 <script src="{{ url_for('static', filename='js/STLLoader.js') }}"></script> 82 <script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script> 83 <script> 84 $(document).ready(function() { 85 // Configuration 86 // Set size of the WebGL renderer scene. 87 var sceneWidth = 640; 88 var sceneHeight = 480; 89 // Define list of 3D models. Each item should have a name property that 90 // will be rendered in the drop down, and a load function that is called 91 // with the model instance and should add a model property with a Three.js 92 // scene graph object that will be rendered. 93 var models = [ 94 { 95 name: 'Bunny', 96 load: function(model) { 97 objMTLLoader.load( 98 '{{ url_for('static', filename='bunny.obj') }}', 99 '{{ url_for('static', filename='bunny.mtl') }}', 100 function(object) { 101 var geom = object.children[1].geometry; 102 // Rebuild geometry normals because they aren't loaded properly. 103 geom.computeFaceNormals(); 104 geom.computeVertexNormals(); 105 // Build bunny mesh from geometry and material. 106 model.model = new THREE.Mesh(geom, material); 107 // Move the bunny so it's roughly in the center of the screen. 108 model.model.position.y = -4; 109 } 110 ); 111 } 112 }, 113 { 114 name: 'Cat Statue', 115 load: function(model) { 116 stlLoader.load( 117 '{{ url_for('static', filename='cat-top.stl') }}', 118 function(geometry) { 119 // Regenerate normals because they aren't loaded properly. 120 geometry.computeFaceNormals(); 121 geometry.computeVertexNormals(); 122 // Load the model and build mesh. 123 model.model = new THREE.Mesh(geometry, material); 124 // Rotate, scale, and move so the cat is facing out the screen. 125 model.model.rotation.x = -90 * (Math.PI / 180.0); 126 model.model.scale.set(0.15, 0.15, 0.15); 127 model.model.position.y = -4; 128 } 129 ); 130 } 131 }, 132 { 133 name: 'XYZ Axes', 134 load: function(model) { 135 // Build some cylinders and rotate them to form a cross of the XYZ axes. 136 model.model = new THREE.Group(); 137 var xAxis = new THREE.Mesh(new THREE.CylinderGeometry(0.25, 0.25, 7, 32, 32), 138 material); 139 xAxis.rotation.z = 90.0*(Math.PI/180.0); 140 model.model.add(xAxis); 141 var yAxis = new THREE.Mesh(new THREE.CylinderGeometry(0.25, 0.25, 7, 32, 32), 142 material); 143 model.model.add(yAxis); 144 var zAxis = new THREE.Mesh(new THREE.CylinderGeometry(0.25, 0.25, 7, 32, 32), 145 material); 146 zAxis.rotation.x = 90.0*(Math.PI/180.0); 147 model.model.add(zAxis); 148 } 149 } 150 ]; 151 152 // Global state. 153 var bnoData = null; 154 var offset = null; 155 var orientation = null; 156 var objMTLLoader = new THREE.OBJMTLLoader(); 157 var stlLoader = new THREE.STLLoader(); 158 var currentModel = null; 159 160 // Start with main controls hidden until connected. 161 $('#controls').hide(); 162 163 // Setup Three.js scene and camera. 164 var scene = new THREE.Scene(); 165 var camera = new THREE.PerspectiveCamera(75, sceneWidth / sceneHeight, 0.1, 1000); 166 // Start with the camera moved back a bit to look directly at the origin. 167 camera.position.z = 10; 168 169 // Setup Three.js WebGL renderer and add it to the page. 170 var renderer = new THREE.WebGLRenderer(); 171 renderer.setSize(sceneWidth, sceneHeight); 172 renderer.setClearColor(0xff0000, 0); 173 $('#renderer').append(renderer.domElement); 174 $('#renderer canvas').addClass('center-block'); // Center the renderer. 175 176 // Create white material for the models. 177 var material = new THREE.MeshPhongMaterial({ color: 0xffffff }); 178 179 // Setup 3 point lighting with a red and blue point light in upper left 180 // and right corners, plus a bit of backlight from the rear forward. 181 var pointLight1 = new THREE.PointLight(0xffbbbb, 0.6); 182 pointLight1.position.set(40, 15, 40); 183 scene.add(pointLight1); 184 var pointLight2 = new THREE.PointLight(0xbbbbff, 0.6); 185 pointLight2.position.set(-40, 15, 40); 186 scene.add(pointLight2); 187 var backLight = new THREE.DirectionalLight(0xffff, 0.3); 188 backLight.position.set(0, -0.25, -1); 189 scene.add(backLight); 190 191 // Create a couple groups to apply rotations to the 3D model at different 192 // stages. The outer group called offset is set to the reverse rotation 193 // of the current BNO orientation when the 'Straighten' button is clicked. 194 // This will force the model to center itself staring directly out of 195 // the screen. The inner group called orientation will be rotated with 196 // the current BNO sensor orientation and cause the model to rotate. 197 offset = new THREE.Group(); 198 orientation = new THREE.Group(); 199 offset.add(orientation); 200 scene.add(offset); 201 202 // Main rendering function. 203 function render() { 204 requestAnimationFrame(render); 205 // Switch to the first model once it's loaded. 206 if (currentModel === null) { 207 if (models[0].hasOwnProperty('model')) { 208 currentModel = 0; 209 orientation.add(models[0].model); 210 } 211 } 212 // Update the orientation with the last BNO sensor reading quaternion. 213 if (bnoData !== null) { 214 orientation.quaternion.set(bnoData.quatX, bnoData.quatY, bnoData.quatZ, bnoData.quatW); 215 } 216 renderer.render(scene, camera); 217 } 218 render(); 219 220 // Populate drop-down of 3D models and load all the models.. 221 $.each(models, function(index, model) { 222 // Populate drop-down. 223 $('#model').append($("<option />").val(index).text(model.name)); 224 // Kick off loading the model. 225 model.load(model); 226 }); 227 228 // Model list changed event. 229 $('#model').change(function() { 230 // Remove the old model. 231 orientation.remove(models[currentModel].model); 232 // Update the current model and add it to the scene. 233 currentModel = $('#model')[0].selectedIndex; 234 orientation.add(models[currentModel].model); 235 }); 236 237 // Straighten button click handler. 238 $('#straighten').click(function() { 239 // Get the current orientation of the BNO sensor and compute its 240 // conjugate or reverse rotation and apply it to the offset group. 241 // This will reset the 3D model so that it faces directly forward based 242 // on the current BNO sensor orientation. 243 var currentQuat = new THREE.Quaternion(bnoData.quatX, bnoData.quatY, bnoData.quatZ, bnoData.quatW); 244 offset.quaternion.copy(currentQuat.conjugate()); 245 }); 246 247 // Save calibration click handler calls the /save_calibration API. 248 $('#saveCalibration').click(function() { 249 $.post("{{ url_for('save_calibration') }}"); 250 }); 251 $('#saveCalibration').hide(); 252 253 // Load calibration click handler calls the /load_calibration API. 254 $('#loadCalibration').click(function() { 255 $.post("{{ url_for('load_calibration') }}"); 256 }); 257 $('#loadCalibration').hide(); 258 259 // Function called when a new sensor reading is received. 260 function updateSensorData(data) { 261 // Save the reading then update the UI. 262 bnoData = data; 263 $('#heading').text(data.heading); 264 $('#roll').text(data.roll); 265 $('#pitch').text(data.pitch); 266 $('#calSys').text(data.calSys); 267 $('#calGyro').text(data.calGyro); 268 $('#calAccel').text(data.calAccel); 269 $('#calMag').text(data.calMag); 270 } 271 272 // Create server sent event connection to receive BNO sensor data. 273 var server = new EventSource('/bno'); 274 // Add server sent event handlers. 275 server.onmessage = function(e) { 276 // Update BNO sensor values. 277 updateSensorData(JSON.parse(e.data)); 278 }; 279 server.onopen = function(e) { 280 // Hide connecting status and show controls when connection is made. 281 $('#connecting').hide(); 282 $('#controls').show(); 283 }; 284 server.onerror = function(e) { 285 // Hide controls and show connecting status if connection closes. 286 $('#controls').hide(); 287 $('#connecting').show(); 288 }; 289 }); 290 </script> 291 </body> 292 </html>