ESP32 Web Server with MPU-6050 Accelerometer and Gyroscope (3D object representation)

In this project we’ll build a web server with the ESP32 to display readings from the MPU-6050 accelerometer and gyroscope sensor. We’ll also create a 3D representation of the sensor orientation on the web browser. The readings are updated automatically using Server-Sent Events and the 3D representation is handled using a JavaScript library called three.js. The ESP32 board will be programmed using the Arduino core.

ESP32 Web Server MPU-6050 Accelerometer Gyroscope 3D object representation Arduino

To build the web server we’ll use the ESPAsyncWebServer library that provides an easy way to build an asynchronous web server and handle Server-Sent Events.

To learn more about Server-Sent Events, read: ESP32 Web Server using Server-Sent Events (Update Sensor Readings Automatically).

Watch the Video Demonstration

Watch the following video for a preview of the project we’ll build.

Project Overview

Before going straight to the project, it’s important to outline what our web server will do, so that it is easier to understand.

  • The web server displays the gyroscope values of the X, Y an Z axis;
  • The gyroscope values are updated on the web server every 10 milliseconds;
  • It displays the accelerometer values (X, Y, Z). These values are updated every 200 milliseconds;
  • The MPU-6050 sensor module also measures temperature, so we’ll also display the temperature value. The temperature is updated every second (1000 milliseconds);
  • All the readings are updated using Server-Sent Events;
  • There is a 3D representation of the sensor. The orientation of the 3D object changes accordingly to the sensor orientation. The current position of the sensor is calculated using the gyroscope values;
  • The 3D object is created using a JavaScript library called three.js;
  • There are four buttons to adjust the position of the 3D object:
    • RESET POSITION: sets angular position to zero on all axis;
    • X: sets the X angular position to zero;
    • Y: sets the Y angular position to zero;
    • Z: sets the Z angular position to zero;
MPU-6050 Accelerometer Gyroscope Web Server ESP32 How it Works

ESP32 Filesystem

To keep our project organized and make it easier to understand, we’ll create four different files to build the web server:

  • the Arduino code that handles the web server;
  • HTML file: to define the content of the web page;
  • CSS file: to style the web page;
  • JavaScript file: to program the behavior of the web page (handle web server responses, events and creating the 3D object).

The HTML, CSS and JavaScript files will be uploaded to the ESP32 SPIFFS (filesystem). To upload files to the ESP32 filesystem, we’ll use the SPIFFS Uploader Plugin.

If you’re using PlatformIO + VS Code, read this article to learn how to upload files to the ESP32 filesystem:

MPU-6050 Gyroscope and Accelerometer

The MPU-6050 is a module with a 3-axis accelerometer and a 3-axis gyroscope.

MPU6050 Module Accelerometer Gyroscope Temperature Sensor

The gyroscope measures rotational velocity (rad/s) – this is the change of the angular position over time along the X, Y and Z axis (roll, pitch and yaw). This allows us to determine the orientation of an object.

Roll Pitch Yaw Angles gyroscope

The accelerometer measures acceleration (rate of change of the velocity of an object). It senses static foces like gravity (9.8m/s2) or dynamic forces like vibrations or movement. The MPU-6050 measures acceleration over the X, Y an Z axis. Ideally, in a static object the acceleration over the Z axis is equal to the gravitational force, and it should be zero on the X and Y axis.

Using the values from the accelerometer, it is possible to calculate the roll and pitch angles using trigonometry, but it is not possible to calculate the yaw.

We can combine the information from both sensors to get accurate information about the sensor orientation.

Learn more about the MPU-6050 sensor: ESP32 with MPU-6050 Accelerometer, Gyroscope and Temperature Sensor.

Schematic Diagram – ESP32 with MPU-6050

For this project you need the following parts:

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Wire the ESP32 to the MPU-6050 sensor as shown in the following schematic diagram: connect the SCL pin to GPIO 22 and the SDA pin to GPIO 21.

MPU6050 Accelerometer Gyroscope Wiring to ESP32 Schematic Diagram Circuit

Preparing Arduino IDE

We’ll program the ESP32 board using Arduino IDE. So, make sure you have the ESP32 add-on installed. Follow the next tutorial:

If you prefer using VSCode + PlatformIO, follow the next tutorial instead:

Installing MPU-6050 Libraries

There are different ways to get readings from the sensor. In this tutorial, we’ll use the Adafruit MPU6050 library. To use this library you also need to install the Adafruit Unified Sensor library and the Adafruit Bus IO Library.

Open your Arduino IDE and go to Sketch > Include Library > Manage Libraries. The Library Manager should open.

Type “adafruit mpu6050” on the search box and install the library.

Install Adafruit MPU6050 Library Arduino IDE

Then, search for “Adafruit Unified Sensor”. Scroll all the way down to find the library and install it.

Install Adafruit Unified Library Arduino IDE

Finally, search for “Adafruit Bus IO” and install it.

Install Adafruit Bus IO Library Arduino IDE

Installing Async Web Server Libraries

To build the web server we’ll use the ESPAsyncWebServer library. This library needs the AsyncTCP library to work properly. Click the links below to download the libraries.

These libraries aren’t available to install through the Arduino Library Manager, so you need to copy the library files to the Arduino Installation Libraries folder. Alternatively, in your Arduino IDE, you can go to Sketch Include Library > Add .zip Library and select the libraries you’ve just downloaded.

Installing Arduino_JSON library

In this example, we’ll send the sensor readings to the browser in JSON format. To make it easier to handle JSON variables, we’ll use the Arduino_JSON library.

You can install this library in the Arduino IDE Library Manager. Just go to Sketch Include Library > Manage Libraries and search for the library name as follows: Arduino_JSON.

Install Arduino_JSON Library Arduino IDE

If you’re using VS Code with PlatformIO, copy the following lines to the platformio.ini file to include all the necessary libraries.

lib_deps =   adafruit/Adafruit MPU6050 @ ^2.0.3
    adafruit/Adafruit Unified Sensor @ ^1.1.4
    me-no-dev/ESP Async WebServer @ ^1.2.3
    arduino-libraries/Arduino_JSON @ 0.1.0

Filesystem Uploader Plugin

To follow this tutorial you should have the ESP32 Filesystem Uploader plugin installed in your Arduino IDE. If you don’t, follow the next tutorial to install it:

If you’re using VS Code + PlatformIO, follow the next tutorial to learn how to upload files to the ESP32 filesystem:

Organizing Your Files

To build the web server you need four different files. The Arduino sketch, the HTML file, the CSS file and the JavaScript file. The HTML, CSS and JavaScript files should be saved inside a folder called data inside the Arduino sketch folder, as shown below:

Organizing Your ESP Project Files HTML CSS JavaScript Arduino files

You can download all project files:

Creating the HTML File

Create an index.html file with the following content or download all the project files.

<!--
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-mpu-6050-web-server/

  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-->

<!DOCTYPE HTML><html>
<head>
  <title>ESP Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,">
  <link rel="stylesheet" type="text/css" href="style.css">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/107/three.min.js"></script>
</head>
<body>
  <div class="topnav">
    <h1><i class="far fa-compass"></i> MPU6050 <i class="far fa-compass"></i></h1>
  </div>
  <div class="content">
    <div class="cards">
      <div class="card">
        <p class="card-title">GYROSCOPE</p>
        <p><span class="reading">X: <span id="gyroX"></span> rad</span></p>
        <p><span class="reading">Y: <span id="gyroY"></span> rad</span></p>
        <p><span class="reading">Z: <span id="gyroZ"></span> rad</span></p>
      </div>
      <div class="card">
        <p class="card-title">ACCELEROMETER</p>
        <p><span class="reading">X: <span id="accX"></span> ms<sup>2</sup></span></p>
        <p><span class="reading">Y: <span id="accY"></span> ms<sup>2</sup></span></p>
        <p><span class="reading">Z: <span id="accZ"></span> ms<sup>2</sup></span></p>
      </div>
      <div class="card">
        <p class="card-title">TEMPERATURE</p>
        <p><span class="reading"><span id="temp"></span> &deg;C</span></p>
        <p class="card-title">3D ANIMATION</p>
        <button id="reset" onclick="resetPosition(this)">RESET POSITION</button>
        <button id="resetX" onclick="resetPosition(this)">X</button>
        <button id="resetY" onclick="resetPosition(this)">Y</button>
        <button id="resetZ" onclick="resetPosition(this)">Z</button>
      </div>
    </div>
    <div class="cube-content">
      <div id="3Dcube"></div>
    </div>
  </div>
<script src="script.js"></script>
</body>
</html>

View raw code

Head

The <head> and </head> tags mark the start and end of the head. The head is where you insert data about the HTML document that is not directly visible to the end user, but adds functionalities to the web page – this is called metadata.

The next line gives a title to the web page. In this case, it is set to ESP Web Server. You can change it if you want. The title is exactly what it sounds like: the title of your document, which shows up in your web browser’s title bar.

<title>ESP Web Server</title>

The following meta tag makes your web page responsive. A responsive web design will automatically adjust for different screen sizes and viewports.

<meta name="viewport" content="width=device-width, initial-scale=1">

We use the following meta tag because we won’t serve a favicon for our web page in this project.

<link rel="icon" href="data:,">

The styles to style the web page are on a separated file called style.css file. So, we must reference the CSS file on the HTML file as follows.

<link rel="stylesheet" type="text/css" href="style.css">

Include the Font Awesome website styles to include icons in the web page like the gyroscope icon.

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">

Finally, we need to include the three.js library to create the 3D representation of the sensor.

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/107/three.min.js"></script>

Body

The <body> and </body> tags mark the start and end of the body. Everything that goes inside those tags is the visible page content.

Top Bar

There’s a top bar with a heading in the web page. It is a heading 1 and it is placed inside a <div> tag with the class name topnav. Placing your HTML elements between <div> tags, makes them easy to style using CSS.

<div class="topnav">
  <h1><i class="far fa-compass"></i> MPU6050 <i class="far fa-compass"></i></h1>
</div>

Content Grid

All the other content is placed inside a <div> tag called content.

<div class="content">

We use CSS grid layout to display the readings on different aligned boxes (card). Each box corresponds to a grid cell. Grid cells need to be inside a grid container, so the boxes need to be placed inside another <div> tag. This new tag has the classname cards.

<div class="cards">

To learn more about CSS grid layout, we recommend this article: A Complete Guide to Grid. Here’s the card for the gyroscope readings:

<div class="card">
  <p class="card-title">GYROSCOPE</p>
  <p><span class="reading">X: <span id="gyroX"></span> rad/s</span></p>
  <p><span class="reading">Y: <span id="gyroY"></span> rad/s</span></p>
  <p><span class="reading">Z: <span id="gyroZ"></span> rad/s</span></p>
</div>

The card has a title with the name of the card:

<p class="card-title">GYROSCOPE</p>

And three paragraphs to display the gyroscope values on the X, Y and Z axis.

<p><span class="reading">X: <span id="gyroX"></span> rad/s</span></p>
<p><span class="reading">Y: <span id="gyroY"></span> rad/s</span></p>
<p><span class="reading">Z: <span id="gyroZ"></span> rad/s</span></p>

In each paragraph there’s a <span> tag with a unique id. This is needed so that we can insert the readings on the right place later using JavaScript. Here’s the ids used:

  • gyroX for the gyroscope X reading;
  • gyroY for the gyroscope Y reading;
  • gyroZ for the gyroscope Z reading.

The card to display the accelerometer readings is similar, but with different unique ids for each reading:

<div class="card">
  <p class="card-title">ACCELEROMETER</p>
  <p><span class="reading">X: <span id="accX"></span> ms<sup>2</sup></span></p>
  <p><span class="reading">Y: <span id="accY"></span> ms<sup>2</sup></span></p>
  <p><span class="reading">Z: <span id="accZ"></span> ms<sup>2</sup></span></p>
</div>

Here’s the ids for the accelerometer readings:

  • accX for the accelerometer X reading;
  • accY for the accelerometer Y reading;
  • accZ for the accelerometer Z reading.

Finally, the following lines display the card for the temperature and the reset buttons.

<div class="card">
  <p class="card-title">TEMPERATURE</p>
  <p><span class="reading"><span id="temp"></span> &deg;C</span></p>
  <p class="card-title">3D ANIMATION</p>
  <button id="reset" onclick="resetPosition(this)">RESET POSITION</button>
  <button id="resetX" onclick="resetPosition(this)">X</button>
  <button id="resetY" onclick="resetPosition(this)">Y</button>
   <button id="resetZ" onclick="resetPosition(this)">Z</button>
 </div>

The unique id for the temperature reading is temp.

Then there are four different buttons that when clicked will call the resetPosition() JavaScript function later on. This function will be responsible for sending a request to the ESP32 informing that we want to reset the position, whether its on all the axis or on an individual axis. Each button has a unique id, so that we know which button was clicked:

  • reset: to reset the position in all axis;
  • resetX: to reset the position on the X axis;
  • resetY: to reset the position on the Y axis;
  • resetZ: to reset the position on the Z axis.

3D Representation

We need to create a section to display the 3D representation.

<div class="cube-content">
  <div id="3Dcube"></div>
</div>

The 3D object will be rendered on the <div> with the 3Dcube id.

Reference the JavaScript File

Finally, because we’ll use an external JavaScript file with all the functions to handle the HTML elements and create the 3D animation, we need to reference that file (script.js) as follows:

<script src="script.js"></script>

Creating the CSS File

Create a file called style.css with the following content or download all the project files.

This file is responsible for styling the web page.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-mpu-6050-web-server/

  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/

html {
  font-family: Arial;
  display: inline-block;
  text-align: center;
}
p {
  font-size: 1.2rem;
}
body {
  margin: 0;
}
.topnav {
  overflow: hidden;
  background-color: #003366;
  color: #FFD43B;
  font-size: 1rem;
}
.content {
  padding: 20px;
}
.card {
  background-color: white;
  box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
}
.card-title {
  color:#003366;
  font-weight: bold;
}
.cards {
  max-width: 800px;
  margin: 0 auto;
  display: grid; grid-gap: 2rem;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.reading {
  font-size: 1.2rem;
}
.cube-content{
  width: 100%;
  background-color: white;
  height: 300px; margin: auto;
  padding-top:2%;
}
#reset{
  border: none;
  color: #FEFCFB;
  background-color: #003366;
  padding: 10px;
  text-align: center;
  display: inline-block;
  font-size: 14px; width: 150px;
  border-radius: 4px;
}
#resetX, #resetY, #resetZ{
  border: none;
  color: #FEFCFB;
  background-color: #003366;
  padding-top: 10px;
  padding-bottom: 10px;
  text-align: center;
  display: inline-block;
  font-size: 14px;
  width: 20px;
  border-radius: 4px;
}

View raw code

We won’t explain how the CSS for this project works because it is not relevant for the goal of this project.

Creating the JavaScript File

Create a file called script.js with the following content or download all the project files.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-mpu-6050-web-server/

  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/

let scene, camera, rendered, cube;

function parentWidth(elem) {
  return elem.parentElement.clientWidth;
}

function parentHeight(elem) {
  return elem.parentElement.clientHeight;
}

function init3D(){
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0xffffff);

  camera = new THREE.PerspectiveCamera(75, parentWidth(document.getElementById("3Dcube")) / parentHeight(document.getElementById("3Dcube")), 0.1, 1000);

  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(parentWidth(document.getElementById("3Dcube")), parentHeight(document.getElementById("3Dcube")));

  document.getElementById('3Dcube').appendChild(renderer.domElement);

  // Create a geometry
  const geometry = new THREE.BoxGeometry(5, 1, 4);

  // Materials of each face
  var cubeMaterials = [
    new THREE.MeshBasicMaterial({color:0x03045e}),
    new THREE.MeshBasicMaterial({color:0x023e8a}),
    new THREE.MeshBasicMaterial({color:0x0077b6}),
    new THREE.MeshBasicMaterial({color:0x03045e}),
    new THREE.MeshBasicMaterial({color:0x023e8a}),
    new THREE.MeshBasicMaterial({color:0x0077b6}),
  ];

  const material = new THREE.MeshFaceMaterial(cubeMaterials);

  cube = new THREE.Mesh(geometry, material);
  scene.add(cube);
  camera.position.z = 5;
  renderer.render(scene, camera);
}

// Resize the 3D object when the browser window changes size
function onWindowResize(){
  camera.aspect = parentWidth(document.getElementById("3Dcube")) / parentHeight(document.getElementById("3Dcube"));
  //camera.aspect = window.innerWidth /  window.innerHeight;
  camera.updateProjectionMatrix();
  //renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setSize(parentWidth(document.getElementById("3Dcube")), parentHeight(document.getElementById("3Dcube")));

}

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

// Create the 3D representation
init3D();

// Create events for the sensor readings
if (!!window.EventSource) {
  var source = new EventSource('/events');

  source.addEventListener('open', function(e) {
    console.log("Events Connected");
  }, false);

  source.addEventListener('error', function(e) {
    if (e.target.readyState != EventSource.OPEN) {
      console.log("Events Disconnected");
    }
  }, false);

  source.addEventListener('gyro_readings', function(e) {
    //console.log("gyro_readings", e.data);
    var obj = JSON.parse(e.data);
    document.getElementById("gyroX").innerHTML = obj.gyroX;
    document.getElementById("gyroY").innerHTML = obj.gyroY;
    document.getElementById("gyroZ").innerHTML = obj.gyroZ;

    // Change cube rotation after receiving the readinds
    cube.rotation.x = obj.gyroY;
    cube.rotation.z = obj.gyroX;
    cube.rotation.y = obj.gyroZ;
    renderer.render(scene, camera);
  }, false);

  source.addEventListener('temperature_reading', function(e) {
    console.log("temperature_reading", e.data);
    document.getElementById("temp").innerHTML = e.data;
  }, false);

  source.addEventListener('accelerometer_readings', function(e) {
    console.log("accelerometer_readings", e.data);
    var obj = JSON.parse(e.data);
    document.getElementById("accX").innerHTML = obj.accX;
    document.getElementById("accY").innerHTML = obj.accY;
    document.getElementById("accZ").innerHTML = obj.accZ;
  }, false);
}

function resetPosition(element){
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/"+element.id, true);
  console.log(element.id);
  xhr.send();
}

View raw code

Creating a 3D Object

The init3D() function creates the 3D object. To actually be able to display anything with three.js, we need three things: scene, camera and renderer, so that we can render the scene with camera.

function init3D(){
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0xffffff);

  camera = new THREE.PerspectiveCamera(75, parentWidth(document.getElementById("3Dcube")) / parentHeight(document.getElementById("3Dcube")), 0.1, 1000);

  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(parentWidth(document.getElementById("3Dcube")), parentHeight(document.getElementById("3Dcube")));
   
  document.getElementById('3Dcube').appendChild(renderer.domElement);

To create the 3D object, we need a BoxGeometry. In the box geometry you can set the dimensions of your object. We created the object with the right proportions to resemble the MPU-6050 shape.

const geometry = new THREE.BoxGeometry(5, 1, 4);

Besides the geometry, we also need a material to color the object. There are different ways to color the object. We’ve chosen three different colors for the faces.

// Materials of each face
var cubeMaterials = [ 
  new THREE.MeshBasicMaterial({color:0x03045e}),
  new THREE.MeshBasicMaterial({color:0x023e8a}), 
  new THREE.MeshBasicMaterial({color:0x0077b6}),
  new THREE.MeshBasicMaterial({color:0x03045e}),
  new THREE.MeshBasicMaterial({color:0x023e8a}), 
  new THREE.MeshBasicMaterial({color:0x0077b6}),
]; 

const material = new THREE.MeshFaceMaterial(cubeMaterials);

Finally, create the 3D object, add it to the scene and adjust the camera.

cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
renderer.render(scene, camera);

We recommend taking a look at this quick three.js tutorial to better understand how it works: Getting Started with three.js – Creating a Scene.

To be able to resize the object when the web browser window changes size, we need to call the onWindowResize() function when the event resize occurs.

// Resize the 3D object when the browser window changes size
function onWindowResize(){
  camera.aspect = parentWidth(document.getElementById("3Dcube")) / parentHeight(document.getElementById("3Dcube"));
  //camera.aspect = window.innerWidth /  window.innerHeight;
  camera.updateProjectionMatrix();
  //renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setSize(parentWidth(document.getElementById("3Dcube")), parentHeight(document.getElementById("3Dcube")));
}

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

Call the init3D() function to actual create the 3D representation.

init3D();

Events (SSE)

The ESP32 sends new sensor readings periodically as events to the client (browser). We need to handle what happens when the client receives those events.

In this example, we want to place the readings on the corresponding HTML elements and change the 3D object orientation accordingly.

Create a new EventSource object and specify the URL of the page sending the updates. In our case, it /events.

if (!!window.EventSource) {
  var source = new EventSource('/events');

Once you’ve instantiated an event source, you can start listening for messages from the server with addEventListener().

These are the default event listeners, as shown here in the AsyncWebServer documentation.

source.addEventListener('open', function(e) {
  console.log("Events Connected");
}, false);

source.addEventListener('error', function(e) {
  if (e.target.readyState != EventSource.OPEN) {
    console.log("Events Disconnected");
  }
}, false);

When new gyroscope readings are available, the ESP32 sends an event gyro_readings to the client. We need to add an event listener for that specific event.

source.addEventListener('gyro_readings', function(e) {

The gyroscope readings are a String in JSON format. For example:

{
  "gyroX" : "0.09",
  "gyroY" : "0.05",
  "gyroZ": "0.04"
}

JavaScript has a built-in function to convert a string, written in JSON format, into native JavaScript objects: JSON.parse().

var obj = JSON.parse(e.data);

The obj variable contains the sensor readings in native JavaScript format. Then, we can access the readings as follows:

  • gyroscope X reading: obj.gyroX;
  • gyroscope Y reading: obj.gyroY;
  • gyroscope Z reading: obj.gyroZ;

The following lines put the received data into the corresponding HTML elements on the web page.

document.getElementById("gyroX").innerHTML = obj.gyroX;
document.getElementById("gyroY").innerHTML = obj.gyroY;
document.getElementById("gyroZ").innerHTML = obj.gyroZ;

Finally, we need to change the cube rotation according to the received readings, as follows:

cube.rotation.x = obj.gyroY;
cube.rotation.z = obj.gyroX;
cube.rotation.y = obj.gyroZ;
renderer.render(scene, camera);

Note: in our case, the axis are switched as shown previously (rotation X –> gyroY, rotation Z –> gyroX, rotation Y –> gyroZ). You might need to change this depending on your sensor orientation.

For the accelerometer_readings and temperature events, we simply display the data on the HTML page.

source.addEventListener('temperature_reading', function(e) {
  console.log("temperature_reading", e.data);
  document.getElementById("temp").innerHTML = e.data;
}, false);

source.addEventListener('accelerometer_readings', function(e) {
  console.log("accelerometer_readings", e.data);
  var obj = JSON.parse(e.data);
  document.getElementById("accX").innerHTML = obj.accX;
  document.getElementById("accY").innerHTML = obj.accY;
  document.getElementById("accZ").innerHTML = obj.accZ;
}, false);

Finally, we need to create the resetPosition() function. This function will be called by the reset buttons.

function resetPosition(element){
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/"+element.id, true);
  console.log(element.id);
  xhr.send();
}

This function simply sends an HTTP request to the server on a different URL depending on the button that was pressed (element.id).

xhr.open("GET", "/"+element.id, true);
  • RESET POSITION button –> request: /reset
  • X button –> request: /resetX
  • Y button –> request: /resetY
  • Z button –> request: /resetZ

Arduino Sketch

Finally, let’s configure the server (ESP32). Copy the following code to the Arduino IDE or download all the project files.

/*********
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-mpu-6050-web-server/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/

#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Arduino_JSON.h>
#include "SPIFFS.h"

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

// Create an Event Source on /events
AsyncEventSource events("/events");

// Json Variable to Hold Sensor Readings
JSONVar readings;

// Timer variables
unsigned long lastTime = 0;  
unsigned long lastTimeTemperature = 0;
unsigned long lastTimeAcc = 0;
unsigned long gyroDelay = 10;
unsigned long temperatureDelay = 1000;
unsigned long accelerometerDelay = 200;

// Create a sensor object
Adafruit_MPU6050 mpu;

sensors_event_t a, g, temp;

float gyroX, gyroY, gyroZ;
float accX, accY, accZ;
float temperature;

//Gyroscope sensor deviation
float gyroXerror = 0.07;
float gyroYerror = 0.03;
float gyroZerror = 0.01;

// Init MPU6050
void initMPU(){
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("MPU6050 Found!");
}

void initSPIFFS() {
  if (!SPIFFS.begin()) {
    Serial.println("An error has occurred while mounting SPIFFS");
  }
  Serial.println("SPIFFS mounted successfully");
}

// Initialize WiFi
void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");
  Serial.print("Connecting to WiFi...");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(1000);
  }
  Serial.println("");
  Serial.println(WiFi.localIP());
}

String getGyroReadings(){
  mpu.getEvent(&a, &g, &temp);

  float gyroX_temp = g.gyro.x;
  if(abs(gyroX_temp) > gyroXerror)  {
    gyroX += gyroX_temp/50.00;
  }
  
  float gyroY_temp = g.gyro.y;
  if(abs(gyroY_temp) > gyroYerror) {
    gyroY += gyroY_temp/70.00;
  }

  float gyroZ_temp = g.gyro.z;
  if(abs(gyroZ_temp) > gyroZerror) {
    gyroZ += gyroZ_temp/90.00;
  }

  readings["gyroX"] = String(gyroX);
  readings["gyroY"] = String(gyroY);
  readings["gyroZ"] = String(gyroZ);

  String jsonString = JSON.stringify(readings);
  return jsonString;
}

String getAccReadings() {
  mpu.getEvent(&a, &g, &temp);
  // Get current acceleration values
  accX = a.acceleration.x;
  accY = a.acceleration.y;
  accZ = a.acceleration.z;
  readings["accX"] = String(accX);
  readings["accY"] = String(accY);
  readings["accZ"] = String(accZ);
  String accString = JSON.stringify (readings);
  return accString;
}

String getTemperature(){
  mpu.getEvent(&a, &g, &temp);
  temperature = temp.temperature;
  return String(temperature);
}

void setup() {
  Serial.begin(115200);
  initWiFi();
  initSPIFFS();
  initMPU();

  // Handle Web Server
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html", "text/html");
  });

  server.serveStatic("/", SPIFFS, "/");

  server.on("/reset", HTTP_GET, [](AsyncWebServerRequest *request){
    gyroX=0;
    gyroY=0;
    gyroZ=0;
    request->send(200, "text/plain", "OK");
  });

  server.on("/resetX", HTTP_GET, [](AsyncWebServerRequest *request){
    gyroX=0;
    request->send(200, "text/plain", "OK");
  });

  server.on("/resetY", HTTP_GET, [](AsyncWebServerRequest *request){
    gyroY=0;
    request->send(200, "text/plain", "OK");
  });

  server.on("/resetZ", HTTP_GET, [](AsyncWebServerRequest *request){
    gyroZ=0;
    request->send(200, "text/plain", "OK");
  });

  // Handle Web Server Events
  events.onConnect([](AsyncEventSourceClient *client){
    if(client->lastId()){
      Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
    }
    // send event with message "hello!", id current millis
    // and set reconnect delay to 1 second
    client->send("hello!", NULL, millis(), 10000);
  });
  server.addHandler(&events);

  server.begin();
}

void loop() {
  if ((millis() - lastTime) > gyroDelay) {
    // Send Events to the Web Server with the Sensor Readings
    events.send(getGyroReadings().c_str(),"gyro_readings",millis());
    lastTime = millis();
  }
  if ((millis() - lastTimeAcc) > accelerometerDelay) {
    // Send Events to the Web Server with the Sensor Readings
    events.send(getAccReadings().c_str(),"accelerometer_readings",millis());
    lastTimeAcc = millis();
  }
  if ((millis() - lastTimeTemperature) > temperatureDelay) {
    // Send Events to the Web Server with the Sensor Readings
    events.send(getTemperature().c_str(),"temperature_reading",millis());
    lastTimeTemperature = millis();
  }
}

View raw code

Before uploading the code, make sure you insert your network credentials on the following variables:

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

How the Code Works

Continue reading to learn how the code works or proceed to the next section.

Libraries

First, import all the required libraries for this project:

#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Arduino_JSON.h>
#include "SPIFFS.h"

Network Credentials

Insert your network credentials in the following variables:

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

AsyncWebServer and AsyncEventSource

Create an AsyncWebServer object on port 80.

AsyncWebServer server(80);

The following line creates a new event source on /events.

AsyncEventSource events("/events");

Declaring Variables

The readings variable is a JSON variable to hold the sensor readings in JSON format.

JSONVar readings;

In this project, we’ll send the gyroscope readings every 10 milliseconds, the accelerometer readings every 200 milliseconds, and the temperature readings every second. So, we need to create auxiliary timer variables for each reading. You can change the delay times if you want.

// Timer variables
unsigned long lastTime = 0;
unsigned long lastTimeTemperature = 0;
unsigned long lastTimeAcc = 0;
unsigned long gyroDelay = 10;
unsigned long temperatureDelay = 1000;
unsigned long accelerometerDelay = 200;

MPU-6050

Create an Adafruit_MPU5060 object called mpu, create events for the sensor readings and variables to hold the readings.

// Create a sensor object
Adafruit_MPU6050 mpu;

sensors_event_t a, g, temp;

float gyroX, gyroY, gyroZ;
float accX, accY, accZ;
float temperature;

Gyroscope Offset

Adjust he gyroscope sensor offset on all axis.

//Gyroscope sensor deviation
float gyroXerror = 0.07;
float gyroYerror = 0.03;
float gyroZerror = 0.01;

To get the sensor offset, go to File > Examples > Adafruit MPU6050 > basic_readings. With the sensor in a static position, check the gyroscope X, Y, and Z values. Then, add those values to the gyroXerror, gyroYerror and gyroZerror variables.

Initialize MPU-6050

The initMPU() function initializes te MPU-6050 sensor.

// Init MPU6050
void initMPU(){
  if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }
  Serial.println("MPU6050 Found!");
}

Initialize SPIFFS

The initSPIFFS() function initializes the ESP32 filesystem so that we’re able to get access to the files saved on SPIFFS (index.html, style.css and script.js).

void initSPIFFS() {
  if (!SPIFFS.begin()) {
    Serial.println("An error has occurred while mounting SPIFFS");
  }
  Serial.println("SPIFFS mounted successfully");
}

Initialize Wi-Fi

The initWiFi() function connects the ESP32 to your local network.

// Initialize WiFi
void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

Get Gyroscope Readings

The getGyroReadings() function gets new gyroscope readings and returns the current angular orientation on the X, Y an Z axis as a JSON string.

The gyroscope returns the current angular velocity. The angular velocity is measured in rad/s. To determine the current position of an object, we need to multiply the angular velocity by the elapsed time (10 milliseconds) and add it to the previous position.

current angle (rad) = last angle (rad) + angular velocity (rad/s) * time(s)

The gyroX_temp variable temporarily holds the current gyroscope X value.

float gyroX_temp = g.gyro.x;

To prevent small oscillations of the sensor (see Gyroscope Offset), we first check if the values from the sensor are greater than the offset.

if(abs(gyroX_temp) > gyroXerror) {

If the current value is greater than the offset value, we consider that we have a valid reading. So, we can apply the previous formula to get the current sensor’s angular position (gyroX).

gyroX += gyroX_temp / 50.0;

Note: theoretically, we should multiply the current angular velocity by the elapsed time (10 milliseconds = 0.01 seconds (gyroDelay)) – or divide by 100. However, after some experiments, we found out that the sensor responds better if we divide by 50.0 instead. Your sensor may be different and you may need to adjust the value.

We follow a similar procedure to get the Y and Z values.

float gyroX_temp = g.gyro.x;
if(abs(gyroX_temp) > gyroXerror) {
  gyroX += gyroX_temp/50.00;
}

float gyroY_temp = g.gyro.y;
if(abs(gyroY_temp) > gyroYerror) {
  gyroY += gyroY_temp/70.00;
}

float gyroZ_temp = g.gyro.z;
if(abs(gyroZ_temp) > gyroZerror) {
  gyroZ += gyroZ_temp/90.00;
}

Finally, we concatenate the readings in a JSON variable (readings) and return a JSON string (jsonString).

readings["gyroX"] = String(gyroX);
readings["gyroY"] = String(gyroY);
readings["gyroZ"] = String(gyroZ);

String jsonString = JSON.stringify(readings);
return jsonString;

Get Accelerometer Readings

The getAccReadings() function returns the accelerometer readings.

String getAccReadings(){
  mpu.getEvent(&a, &g, &temp);
  // Get current acceleration values
  accX = a.acceleration.x;
  accY = a.acceleration.y;
  accZ = a.acceleration.z;
  readings["accX"] = String(accX);
  readings["accY"] = String(accY);
  readings["accZ"] = String(accZ);
  String accString = JSON.stringify (readings);
  return accString;
}

Get Temperature Readings

The getTemperature() function returns the current temperature reading.

String getTemperature(){
  mpu.getEvent(&a, &g, &temp);
  temperature = temp.temperature;
  return String(temperature);
}

setup()

In the setup(), initialize the Serial Monitor, Wi-Fi, SPIFFS and the MPU sensor.

void setup() {
  Serial.begin(115200);
  initWiFi();
  initSPIFFS();
  initMPU();

Handle Requests

When the ESP32 receives a request on the root URL, we want to send a response with the HTML file (index.html) content that is stored in SPIFFS.

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(SPIFFS, "/index.html", "text/html");
});

The first argument of the send() function is the filesystem where the files are saved, in this case it is saved in SPIFFS. The second argument is the path where the file is located. The third argument refers to the content type (HTML text).

In your HTML file, you reference the style.css and script.js files. So, when the HTML file loads on your browser, it will make a request for those CSS and JavaScript files. These are static files saved on the same directory (SPIFFS). So, we can simply add the following line to serve static files in a directory when requested by the root URL. It serves the CSS and JavaScript files automatically.

server.serveStatic("/", SPIFFS, "/");

We also need to handle what happens when the reset buttons are pressed. When you press the RESET POSITION button, the ESP32 receives a request on the /reset path. When that happens, we simply set the gyroX, gyroY and gyroZ variables to zero to restore the sensor initial position.

server.on("/reset", HTTP_GET, [](AsyncWebServerRequest *request){
  gyroX=0;
  gyroY=0;
  gyroZ=0;
  request->send(200, "text/plain", "OK");
});

We send an “OK” response to indicate the request succeeded.

We follow a similar procedure for the other requests (X, Y and Z buttons).

server.on("/resetX", HTTP_GET, [](AsyncWebServerRequest *request){
  gyroX=0;
  request->send(200, "text/plain", "OK");
});

server.on("/resetY", HTTP_GET, [](AsyncWebServerRequest *request){
  gyroY=0;
  request->send(200, "text/plain", "OK");
});

server.on("/resetZ", HTTP_GET, [](AsyncWebServerRequest *request){
  gyroZ=0;
  request->send(200, "text/plain", "OK");
});

The following lines setup the event source on the server.

// Handle Web Server Events
events.onConnect([](AsyncEventSourceClient *client){
  if(client->lastId()){
    Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
  }
  // send event with message "hello!", id current millis
  // and set reconnect delay to 1 second
  client->send("hello!", NULL, millis(), 10000);
});
server.addHandler(&events);

Finally, start the server.

server.begin();

loop() – Send Events

In the loop(), we’ll send events to the client with the new sensor readings.

The following lines send the gyroscope readings on the gyro_readings event every 10 milliseconds (gyroDelay).

if ((millis() - lastTime) > gyroDelay) {
  // Send Events to the Web Server with the Sensor Readings
  events.send(getGyroReadings().c_str(),"gyro_readings",millis());
  lastTime = millis();
}

Use the send() method on the events object and pass as argument the content you want to send and the name of the event. In this case, we want to send the JSON string returned by the getGyroReadings() function. The send() method accepts a variable of type char, so we need to use the c_str() method to convert the variable. The name of the events is gyro_readings.

We follow a similar procedure for the accelerometer readings, but we use a different event (accelerometer_readings) and a different delay time (accelerometerDelay):

if ((millis() - lastTimeAcc) > accelerometerDelay) {
  // Send Events to the Web Server with the Sensor Readings
  events.send(getAccReadings().c_str(),"accelerometer_readings",millis());
  lastTimeAcc = millis();
}

And finally, for the temperature readings:

if ((millis() - lastTimeTemperature) > temperatureDelay) {
  // Send Events to the Web Server with the Sensor Readings
  events.send(getTemperature().c_str(),"temperature_reading",millis());
  lastTimeTemperature = millis();
}

Uploading Code and Files

After inserting your network credentials, save the code. Go to Sketch > Show Sketch Folder, and create a folder called data.

Arduino IDE Open Sketch Folder to create data folder

Inside that folder you should save the HTML, CSS and JavaScript files.

Then, upload the code to your ESP32 board. Make sure you have the right board and COM port selected. Also, make sure you’ve added your networks credentials to the code.

Upload Arduino code

After uploading the code, you need to upload the files. Go to Tools ESP32 Data Sketch Upload and wait for the files to be uploaded.

ESP32 Sketch Data Upload SPIFFS Arduino IDE

When everything is successfully uploaded, open the Serial Monitor at a baud rate of 115200. Press the ESP32 EN/RST button, and it should print the ESP32 IP address.

ESP32 IP Address printed in the Arduino IDE Serial Monitor

Demonstration

Open your browser and type the ESP32 IP address. You should get access to the web page that shows the sensor readings.

Move the sensor and see the readings changing as well as the 3D object on the browser.

ESP32 ESP8266 NodeMCU Web Server Demonstration

Note: the sensor drifts a bit on the X axis, despite some adjustments in the code. Many of our readers commented that that’s normal for this kind of MCUs. To reduce the drifting, some readers suggested using a complementary filter or a kalman filter.

Wrapping Up

The MPU-6050 is an accelerometer, gyroscope and temperature sensor on a single module. In this tutorial you’ve learned how to build a web server with the ESP32 to display sensor readings from the MPU-6050 sensor. We used server-sent events to send the readings to the client.

Using the three.js JavaScript library we’ve built a 3D representation of the sensor to show its angular position from the gyroscope readings. The system is not perfect, but it gives an idea of the sensor orientation. If someone more knowledgeable about this topic can share some tips for the sensor calibration it would be greatly appreciated.

We hope you’ve found this tutorial useful.

Learn more about the ESP32 with our resources:



Build Web Server projects with the ESP32 and ESP8266 boards to control outputs and monitor sensors remotely. Learn HTML, CSS, JavaScript and client-server communication protocols DOWNLOAD »

Build Web Server projects with the ESP32 and ESP8266 boards to control outputs and monitor sensors remotely. Learn HTML, CSS, JavaScript and client-server communication protocols DOWNLOAD »


Enjoyed this project? Stay updated by subscribing our newsletter!

44 thoughts on “ESP32 Web Server with MPU-6050 Accelerometer and Gyroscope (3D object representation)”

  1. Hi.
    Very interesting project,thank You again. How could You know
    this is just what I was growing interested in 😉
    For me the Z–axis drifts about one rad/sec in negative direction.
    Can it be some timing problem,considering there is a lot of processing going on?

    -theFinn

    Reply
        • Hi.
          You may need to adjust the offset values, as well as the values on these lines:
          float gyroX_temp = g.gyro.x;
          if(abs(gyroX_temp) > gyroXerror) {
          gyroX += gyroX_temp/50.00;
          }

          float gyroY_temp = g.gyro.y;
          if(abs(gyroY_temp) > gyroYerror) {
          gyroY += gyroY_temp/70.00;
          }

          float gyroZ_temp = g.gyro.z;
          if(abs(gyroZ_temp) > gyroZerror) {
          gyroZ += gyroZ_temp/90.00;
          }
          Regards,
          Sara

          Reply
  2. Hi All,
    this tuto is, as always, great, very clear.
    I was linking this with VR sensors like Oculus.
    If we know accelerations, we are able to calculate x,y,z positions.
    Have you already tried ? how accurate it may be ?
    Probably more accurate with esp32 than Arduino.
    It may lead to interesting stuff 🙂
    Regards,
    Bruno

    Reply
  3. Hi,

    This is indeed a real meaty project with a number of very intersting aspects.

    However I am into MicroPython and not Arduino.

    As with your other projects are you doing a MicroPython version of this?

    Regards, Sandy

    Reply
  4. Great article but I solved the drift problem by using the acceleration results to position the box on the screen,

    document.getElementById(“accX”).innerHTML = obj.accX;
    document.getElementById(“accY”).innerHTML = obj.accY;
    document.getElementById(“accZ”).innerHTML = obj.accZ;

    // Change cube rotation after receiving the readings
    yy = obj.accY;
    xx = obj.accX;
    var zz = obj.accZ;
    var xxx = (xx - xxs) * 1.57 / 10;
    var yyy = (yy - yys) * 1.57 / 10;
    zz = 0;

    console.log("xxx:" + xxx);
    console.log("yyy:" + yyy);

    cube.rotation.x = yyy;
    cube.rotation.z = xxx;
    cube.rotation.y = zz;
    renderer.render(scene, camera);

    and adding a couple of store variables in Reset

    function resetPosition(element) {

    //Store current position
    xxs = xx;
    yys = yy;

    Reply
  5. You should find gyrox/y/z biases and subtract them directly after reading to minimize gyro drift. They can be + or –

    Your best project of many great ones, with many things i had wanted to know about presentation.

    Reply
  6. Hi!

    Nice project but… No way to make it run under Platformio with ESP8266 Nodemcu V3.

    There’s no way for the libraries to load nor get the dependencies.
    Any idea?

    Reply
    • Hi.
      This project is not compatible with the ESP8266. It lags a lot! I think the ESP8266 doesn’t have enough processing power for this project as it sends events every 10 milliseconds.
      Regards,
      Sara

      Reply
  7. I’m very satisfied , good result and test of different technolgy , SPIFF, Gyro z,y,z , json, html . Many compilments to the Random Nerd Tutorial group. Gianni

    Reply
  8. Hi i check my device with other sketch and work well but when i load this sketch this error appears in the serial monitor “Failed to find MPU6050 chip”.i use i2c-scaner program and showed me it is well .i have this problem with basic_reading program also. i think this problem occurred from i2c address in the program . please help me for solving this problem.

    Reply
  9. Hi Sara!
    when i use sketch program with below i2c address i do’nt have any problem and all is ok
    “const int MPU_addr=0x68; // I2C address of the MPU-6050″
    but when i upload program with ” #include <Adafruit_MPU6050.h>” and dont define i2c directly in program lines this error appears in the serial monitor “Failed to find MPU6050 chip” if it is possible please give exact cod for solving this problem. thanks in advance for your help .

    Reply
    • Hello, sorry about my english. I also have the same problem as you
      Adafruit MPU6050 test!
      Failed to find MPU6050 chip
      How did you manage to solve it? Thanks

      Reply
  10. Hello,

    This is a very great and interesting project. I just wanted to know how could I get Gyroscope readings in Degrees and not degrees per second?

    Thank you.

    Reply
    • You need to take at least two measurements in deg/s and then multiply by the elapsed time. you’ll have the measurement in degrees.
      Regards,
      Sara

      Reply
  11. Hi Sara, awesome project!.. I cant make it fully work, got this in the arduino serial:

    Connecting to WiFi….
    192.168.100.38
    E (1219) SPIFFS: mount failed, -10025
    An error has occurred while mounting SPIFFS
    SPIFFS mounted successfully
    MPU6050 Found!

    But when I paste the ip address(192.168.100.38) on the chrome web browser, I got : HTTP ERROR 404
    Where may I start to troubleshoot this fail?. Thanks!

    Reply
  12. Hi! Good project. I’m trying to transmit the data via Wifi, but unlikely the web page doesn’t appear over the browser. However the serial monitor gives messages that wifi and Spiffs are working. Indipendently the library for MPU6050 works too, having loaded the code on a ESP32. The Ip address 192.168.1.174 however seems wrong. What error I did?
    Thank you very much for the assistance.

    Reply
    • Hi.
      What do you see on the web browser?
      Make sure that you uploaded the files to the filesystem—that’s probably your issue.
      Regards,
      Sara

      Reply
  13. Hello,

    Awesome project! I just wanted to know how could I get the tilt angle in (degrees) and not the rotational velocity (radians/sec).

    Thank you.

    Reply
  14. I like this project and have had other web based ESP32 ones of yours working. However, this one runs ok on the ESP32 and connects to the Net, but I get no web page when I use the IP address it returns. I was wondering if this is due to the fact that our network is only within the Uni for devices only and has no external link to the WWW? I noticed it has a URL within the code.
    https://cdnjs.cloudflare.com/ajax/libs/three.js/107/three.min.js
    Perhaps it cannot be run locally.

    Reply
    • Hi James.
      Double-check you uploaded the needed HTML, CSS and JavaScript files using the filesystem uploader plugin.
      Regards,
      Sara

      Reply
  15. Hello,

    Great project, but the x and y axis are drifting. Is there any solution for this? I adjusted the offset values, as well as the values on these lines but the axis are still drifting:

    float gyroX_temp = g.gyro.x;
    if(abs(gyroX_temp) > gyroXerror) {
    gyroX += gyroX_temp/50.00;
    }

    float gyroY_temp = g.gyro.y;
    if(abs(gyroY_temp) > gyroYerror) {
    gyroY += gyroY_temp/70.00;
    }

    float gyroZ_temp = g.gyro.z;
    if(abs(gyroZ_temp) > gyroZerror) {
    gyroZ += gyroZ_temp/90.00;

    Reply
  16. Hey guys is there any way to make this work for an sparkfun MPU-9250 (and if yes which parts of the code do i have to replace)?

    Reply
    • Hi Chris.
      First, you need to know how to get measurements with your sensor.
      If it is a Sparkfun product, usually they have a tutorial for their parts on their website.
      Then, you just need to change the sensor initialization and the getGyroReadings() function.
      Everything else should work as it is.
      Regards,
      Sara

      Reply

Leave a Reply to sirous Cancel reply

Download our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.