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.
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;
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 LittleFS filesystem. To upload files to the ESP32 filesystem, we’ll use the LittleFS Uploader Plugin. Make sure you install it on your Arduino IDE:
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.
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.
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:
- MPU-6050 Accelerometer Gyroscope (ESP32 Guide)
- ESP32 (read Best ESP32 development boards)
- Breadboard
- Jumper wires
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.
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.
Then, search for “Adafruit Unified Sensor”. Scroll all the way down to find the library and install it.
Finally, search for “Adafruit Bus IO” and install it.
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.
If you’re using VS Code with PlatformIO, copy the following lines to the platformio.ini file to include all the necessary libraries, set the serial monitor baud rate to 115200 and set the default filesystem to LittleFS.
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
monitor_speed = 115200
board_build.filesystem = littlefs
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:
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> °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>
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://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
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> °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;
}
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();
}
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 & Sara Santos - Random Nerd Tutorials
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 "LittleFS.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 initLittleFS() {
if (!LittleFS.begin()) {
Serial.println("An error has occurred while mounting LittleFS");
}
Serial.println("LittleFS 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();
initLittleFS();
initMPU();
// Handle Web Server
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(LittleFS, "/index.html", "text/html");
});
server.serveStatic("/", LittleFS, "/");
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();
}
}
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 "LittleFS.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 LittleFS
The initLittleFS() function initializes the ESP32 filesystem so that we’re able to get access to the files saved on LittleFS (index.html, style.css and script.js).
void initLittleFS() {
if (!LittleFS.begin()) {
Serial.println("An error has occurred while mounting LittleFS");
}
Serial.println("LittleFSmounted 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, LittleFS and the MPU sensor.
void setup() {
Serial.begin(115200);
initWiFi();
initLittleFS();
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 LittleFS.
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(LittleFS, "/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 LittleFS. 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 (LittleFS). 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("/", LittleFS, "/");
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.
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.
After uploading the code, you need to upload the files. In the Arduino IDE, press [Ctrl] + [Shift] + [P] on Windows or [⌘] + [Shift] + [P] on MacOS to open the command palette. Search for the Upload LittleFS to Pico/ESP8266/ESP32 command and click on it.
If you don’t have this option is because you didn’t install the filesystem uploader plugin. Check this tutorial.
Important: make sure the Serial Monitor is closed before uploading to the filesystem. Otherwise, the upload will fail.
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.
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.
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:
Parabéns! Excelente tutorial.
perhaps a mercury switch to keep x at zero when flat?
That’s a good idea.
Thanks for sharing.
Regards,
Sara
why in my serial upload just “?????” like that?
Hi.
Did you change the Serial Monitor baud rate to 115200?
Regards.
Sara
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
Hi.
I think it is related with the sensor itself.
It happens to us on the X-axis.
Regards,
Sara
all my axis drift
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
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
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
Hi.
Unfortunately, that’s that on our to-do list.
Regards,
Sara
Ooops…..
Blank browser screen.
All appeared to work perfectly until then.
???
All the rest of your projects have been perfect.
Reloaded it all, and it works!
Peter
Wow ,can’t wait to test it,awesome work!
It would be fantastic if you could display the data on a TFT display – say 2,8″…
Fantastic, I have build it and it works, I hope you can solve the drifting of the X-axis.
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;
Hi Alistair, I attempted your suggestions to correct drift, however unsuccessful. If I could get past this glitch, it’d be near perfect for a remote level project that I’m attempting. I wonder if you could post the entire sketch (or we could communicate directly).
Much appreciated
Mike
Ont, Canada
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.
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?
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
Hi Sara!
Well, I finally made it work with the ESP8266 🙂 Just had to replace some libraries like Wifi.h with ESP8266Wifi and a couple more for that specific device.
As you say, it’s a big laggy but anyway it was cool to see it work!
Thanks for your answer 🙂
That’s great!
Hi Manuel! Can I contact with you to learn a about this project with ESP8266. My mail is [email protected]. Hope you agree! Thank you so much.
Hello Manuel! Can you please tell me what other libraries did you replace to be able to work with esp8266.
Thank you.
You have the code…because I’m trying to do it with esp8266 and I can’t…can you send it please?
fcsimoes83 at gmail.com
Hi, this project can work on ESP8266? Hope you do it!
It works with the ESP8266, but it doesn’t work well.
The web server lags a lot.
Regards,
Sara
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
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.
Hi.
What’s the I2C address of your sensor?
Pass the address as an argument to the begin() method.
Regards,
Sara
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 .
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
Hi, have you solve the problem? If you have, can you show me how pls. Tks
Hi, I’m having problem “Failed to find MPU6050 chip”.
Have you solve the problem? If you have, can you show me how please.
I am looking forward to hearing from you
Hi.
It might be that the sensor is not properly connected to the right GPIOs, or that the sensor has a different I2C address.
Run an I2C scanner sketch to make sure of the address: https://raw.githubusercontent.com/RuiSantosdotme/Random-Nerd-Tutorials/master/Projects/LCD_I2C/I2C_Scanner.ino
I hope this helps.
Regards,
Sara
Hi,
Im having the same problem. If you can slove it, please show me.
Regards
Hi. Whould be possible to send all data and see them over a mobile or tablet?
Many thanks.
Francesco
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.
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
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!
Hi, how did you solve that?
Yes, I did.
I had forgottten to upload the data folder, to the SPIFFS ESP32 file system.
Here is how:
https://github.com/me-no-dev/arduino-esp32fs-plugin#installation
I am sorry…my mistake.
Its working good now, thanks a lot!
Excellent tutorial ! and it’s very interesting,Thanks!
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.
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
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.
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.
Hi James.
Double-check you uploaded the needed HTML, CSS and JavaScript files using the filesystem uploader plugin.
Regards,
Sara
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;
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)?
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
Did you ever figure this out?
Hi, I took an alternative approach eventually ,by successfully utilizing MQTT and NodeRed (and the modules mentioned obv).
Hi. I like you mqtt idea. can you share your code? thank you.
Hi Sara … can I save the result from server to CSV file
Hello, I have an ESP32 lite device. I couldn’t find the GPIO22 and GPIO 21 pin connections. I’ve been looking for the I2C pins but couldn’t find it. Failed to find MPU6050 is displayed on the serial port screen. I would be very happy if you could help me with this subject.
Hi.
I’m not familiar with that device? Does it have another name?
Regards,
Sara
Hi
A great project I have been experimenting with the ESP32 and GY521. I have noticed that with enabled has an effect on the Z axis readings. Others have noted that with a 90 degree rotation the reading is only around the 75 degrees. If I run with out the wifi enabled then I can calibrate the offsets so that the sensor will read 90 degrees, however with wfi enable the it returns 75 degrees with the same calibration settings. Calibrating with wifi enabled makes no difference.
I could try moving the sensor some distance away from the ESP32 to see if this makes a difference. Have you or anyone else seen this. My short term solution is to multiply by 1.2
Hi,
I am using this board LOLIN32 w/ battery holder (link attached)
When I flash your code: the compilation passes but I get an error when the code is flashing to the board.
it says: A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet header
amazon.com/Wal-front-Bluetooth-Transceiver-Development/dp/B07HGS5JBP?SubscriptionId=AKIAJQUMBT74S4ZKCBTA&tag=makeradvisor-20&linkCode=xm2&camp=2025&creative=165953&creativeASIN=B07HGS5JBP
Hi
After uploading the code, when you start seeing a lot of dots on the debugging window, press and hold for about a second the BOOT button.
Regards,
Sara
When i pasted the IP address to firefox I see a blank page and when I copy it to Edge (Microsoft) I get a 500 error.
Hi.
Make sure that you’ve uploaded all the provided files to the board filesystem.
See the section “Uploading Code and Files”.
Regards,
Sara
Hi everyone,
I’m really glad, that this project is very complete and explained in detail!
Thank you so much!
Hello,
Can anyone make a file that doesn’t show X motion?
Dear Sara, library wire.h is missing in the sketch. Is it necessary?
Hi.
What do you mean?
Are you getting any errors?
Regards,
Sara
Dear Sara, Thank you for the good post.
The Z-axis is moving in the direction of “-0.01rad”, is there any solution yet?
Has the dirfting issue be resolved?
Hi thanks for this wonderful project, i have a question,
is there a any possibilty to add accelometer reading saving function like a button after we click this button automaticly saving 3 second reading to txt file?
thanks for advance
Hi sara , is it possible to change from radian to degree? and how? i really need it for my project assignment, it would be nice if you can help me
Hi.
Yes. You just need to add the calculation to convert from radians to degress. https://www.varsitytutors.com/hotmath/hotmath_help/topics/radian-to-degree-measure
Regards,
Sara
Thanks , but where should i put the calculation? i really just a newb for this
Hi.
Right after getting the final values.
After this:
gyroX += gyroX_temp/50.00;
This:
gyroY += gyroY_temp/70.00;
And this:
gyroZ += gyroZ_temp/90.00;
I hope this helps.
Regards,
Sara
How can I save the sensor reading?
How would you like to save the readings?
SD card, SPIFFS, database?
SPIFFS or database where I can store it for post processing
hola, me ayudan?
el progranma se me queda dando puntos uno tras otro cundo trata de conectarse a la wifi?como lo soluciono?
estoy tratando de conectarme a un punto de acceso de un movil.Puede ser?
Ayuda!
Yes.
You need to set your phone as an hotspot.
Make sure you are inserting the right hotspot username and password.
Hi, very interesting and nice project. Can I ask what should I do if my Web Page isn’t loading? i get the error “IP ADRESS refused to connect.”
Hola; una pregunta! Por qué para acceder a la servidor web necesito estar conectado a internet?? No se supone q es un servidor local. Alguien que me pueda responder esa pregunta!
Hi.
In this case you need access to the internet to access the javascript libraries to build the shape.
Regards,
Sara
Hi
But is it theoretically possible to build this example as in the tutorial “ESP32 Access Point”? So you could use the ESP32 as an Accespoint instead of the web server?
Hi.
Yes. If you load all the necessary javascript libraries to the esp32.
Regards,
Sara
Hi
I tried first time with a set of esp32 and mpu6050, the project work nicely done.
But after that i tried with another set of esp32 and mpu6050(different esp32 and mpu), but when i run the code it said mpu6050 chip not found. How can i solve it? Tks.
Hi.
Check the connections and the I2C address of your sensor.
Regards,
Sara
I solve it, but i change another mpu modules(3rd one), the second ones i think it doesnt support adafruit_mpu6050.h, only support mpu6050_light.h
Hi,
Im having problem “Failed to find MPU6050 chip”. Have you slove this problem? If you can, please show me how to slove it.
Regards
I have implemented this project with PlatformIO in VScode and it is working fine, except for this message “ERROR: Too many messages queued”. After some minutes, it reboots.
I changed gyroDelay to 50ms, the messages continue, but it stopped rebooting.
Is there any advice to return gyroDelay to 10ms and keep it from rebooting?
Moreover, what about some advice to get rid of these messages?
Same here
Same,
I try to change quque size from 32 to 256 in AsyncTCP.cpp and change size of: unsigned long gyroDelay = 50; with no results.
My Esp32 devkit v1 has a 30 pins
How to get the sensor readings on mobile also how to connect ESP32 remotly with PC/mobile
hello, I want to use esp32 for controlling antenna rotator using webpage; by calculating the position of a satellite and give the position in terms of steps for the stepper motors on the antenna rotator,,,,,, I want to use sgp4 to calculate to position of the satellite that I want to track , can you help me on this?
I CANT GET THE IP ADDRESS WHY
Hi.
Maybe your network credentials are not correct.
Or the board can’t get access to Wi-Fi signal.
Regards,
Sara
I have an ESP32 WROOM 32 and MPU6050.
I’ve succesfully compiled and uploaded the sketch and webserver files.
Everything works BUT…weird movements 🙁
Every time I reset, the object’s position continues to tilt and rotate without even moving the circuit. I tried to calibrate using the adafruit example too, but it keeps spinning every time. What can I do?
Thanks
P.S. I’ve also changed the mpu6050 (i own 3 of them) but with no luck
Solved with this:
float gyroXerror = 1.0;
float gyroYerror = 0.02;
float gyroZerror = 1.0;
It’s a bit glitchy but it works
Dear Sara and Rui,
Thank you for this nice program, this is my experience. The sketch works great My esp 32 connects to wifi, I receive the address, 6050 is found and SPIFFS mounted (as my serial wrote).
But once I open the web address nothing happens. My Arduino IDE is the 2.0.3 and while I have found the “show sketch folder” under “sketch menu”, with the data folder and the 3 other files (index script and style), I couldn’ t find the “Esp32 sketch data upload” under the tools menu, it doens’t exist! Where I was wrong?
Hi.
You need to install the ESP32 Uploader Plugin on ARduino IDE. You have to use the legacy version 1.8.X.
It doesn’t work on version 2.0.
See this tutorial: https://randomnerdtutorials.com/install-esp32-filesystem-uploader-arduino-ide/
Regards,
Sara
Just back here for tell you that i have obtained much better results with a BNO055. It cost a little more than mpu but has a builtin algorithm called Fusion that gives you directly stable pitch/roll/yaw values and prevent headaches caused by drift/derive errors and complex filters integrations.
Thanks for the project. It helped me a lot during tests.
Hi.
Great.
Thanks for sharing your feedback.
Regards,
Sara
Hi,
When i connect esp32 and mpu6050 with battery the data are random. What may be the issue?
Hi.
Double-check the wiring.
Regards,
Sara
actually it works with USB, not working with battery.
Hi, Monisha Gowri S
I am designing a circuit that combines esp32 with mpu6050 using battery 5V to feed the whole circuit. Can I refer to the project you did? I am a newbie, I have many problems with the stability of battery power and connection.
I’m looking forward hearing from you.
Regards./
Hello Sara,
I tried your code with Arduino IDE and Platformio.
Here is the result after uploading:
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1184
load:0x40078000,len:13192
load:0x40080400,len:3028
entry 0x400805e4
Connecting to WiFi….
192.168.1.18
SPIFFS mounted successfully
MPU6050 Found!
I tried to connect to 192.168.1.18 with different browsers.
The result was :the site is forbidden or nothing.
Do you have idea what to try?
Regards
Good afternoon.
First of all, thank you very much for this great article as always.
I have loaded it into an ESP32 wroom and it works perfectly with the drift in my case of the z axis but the rest very well.
I have tried to load the same sw on Xiao ESP32C3 and at the compilation moment it gives me the following error
Error compiling for the XIAO_ESP32C3 card.
Does anyone know the cause?
Thank you very much
Hello, good, because when you change all the configuration on the website in the gyroscope and accelerometer values, it gives me a value of 2 in all the fields, do you know how I can solve it?
Hey great tutorial! Love all your guys work, and thinks it great how informative it is for beginners. Learned from this a few months back, but needed to get the reference for accreditation, and in the middle of googling it, found the tutorial taken word for word on another site – just with slightly different colors and pictures – but no credit or anything given. Here’s the link to the guy’s site if you guys haven’t been made aware of this yet.
(REMOVED URL)
Thanks again for all you guys do for the community!!
Hi.
Yes. I know.
Not only this post, but almost all of our posts are copied into their website.
Unfortunately, there is nothing we can do about it.
It’s very frustrating and so unfair because they get half of the traffic by just copying all our articles and just changing a few words.
Regards,
Sara
R
Hi. great work on this project. How can I edit the web page so it just plots a graph of the x, y, z, acceleration values and one of the temperature? thanks.
Thank you for all the hard work! I have learned a ton here!
Not sure if this has been covered.
Does anyone know how I could Freeze the Web Page and throw an alert up if the Gyro was moved by some many degrees? Kind of like a Movement monitor for security. I would assume it could be accomplished in the JS? Any Ideas or help would be very much appreciated.
Regards
Brian
have found multiple error trying to build it on arduino ide 2.3.2
/Arduino/libraries/ESPAsyncWebServer/src/WebAuthentication.cpp: In function ‘bool getMD5(uint8_t*, uint16_t, char*)’:
/Arduino/libraries/ESPAsyncWebServer/src/WebAuthentication.cpp:80:3: error: ‘mbedtls_md5_starts_ret’ was not declared in this scope; did you mean ‘mbedtls_md5_starts’?
80 | mbedtls_md5_starts_ret(&_ctx);
have tried downloading the .zip for ESPAsyncWebServer or using the ESPAsyncWebServer that appear on library manager
Are you using Arduino IDE or PlatformIO?
What’s the ESP32 boards version you have installed?
Regards,
Sara
Arduino IDE , and as Boards was using https://espressif.github.io/arduino-esp32/package_esp32_index.json, but have seen your post about boards and now i’m using :https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json,
Now is compiling and uploading right, thanks
working on a ESP32 Wroom-32 board
Hello, I encountered the same error as you. How did you solve it specifically? Forgive me for not understanding your reply.Hope you can help me.
hello there first of all great program but I have a question is there a way I could change the box into another shape as I’m currently working on a project which requires me to simulate the motion of a rocket in a similar way as done by you
Yes.
You can do that. You need to know THREE.js to do that.
Regards,
Sara
A fatal error occurred: Could not open COM4, the port doesn’t exist
SPIFFS Upload failed!
Hi.
Close all serial monitor connections before uploading the SPIFFS image.
Additionally, you may need to press the ESP32 boot button when you start seeing a lof of dots on the debuggin window.
Regards,
Sara
hola, me sale un problema con el SPIFSS, y cuando coloco la dirección ip en mi navegador, no me aparece nada, estoy usando la version 2.2.3.
Saludos
Suitable for spifts above arduino 2.0:https://github.com/espx-cz/arduino-spiffs-upload
Remember to click “upload” after completing “spifts upload”
Hello, I have the following problem, I get the IP adress from the serial monitor, but when I go to that IP adress in my browser I get a “You can’t access this web site” message. What should I do? or What could be the problem?
Hi.
Did you upload the files to the filesystem?
If you open the javascript console, which errors do you get?
Regards,
Sara
Thanks, I already solved the problem, my pc was’t conected to the same router that provided the wifi network for the esp32
hello ,
i want convert this project into Access point method ,im try to do but isnt work it,can you share me code for this
Hi,
Is the 3D animation cube not working anymore? I cant seems to move it even though theres values in my gyroscope
“Connecting to WiFi……192……..LittleFS mounted successfully…MPU6050 Found!”
then when I type the ip address, “This site can’t be reached”
hellpppppppp
Make sure you are on the same network that the ESP32.
Also make sure that you actually uploaded the codes to the board, which is probably your problem.
Regards.
Sara
I’ve already check my WLAN device list and the esp is already connected, but still the site can’t be reached
Can I run this using MPU9250/6500?
Hey,
Really cool tutorial!
I was wondering if you knew how I could change the 3D object to display a CAD file that I have created to move with the gyroscope readings for a different application?
Nice tutorial, works great thanks!
Any solutions for the drift yet?