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>