{"id":101046,"date":"2021-01-21T14:23:51","date_gmt":"2021-01-21T14:23:51","guid":{"rendered":"https:\/\/randomnerdtutorials.com\/?p=101046"},"modified":"2025-03-14T16:28:00","modified_gmt":"2025-03-14T16:28:00","slug":"esp32-mpu-6050-web-server","status":"publish","type":"post","link":"https:\/\/randomnerdtutorials.com\/esp32-mpu-6050-web-server\/","title":{"rendered":"ESP32 Web Server with MPU-6050 Accelerometer and Gyroscope (3D object representation)"},"content":{"rendered":"\n<p>In this project we&#8217;ll build a web server with the ESP32 to display readings from the MPU-6050 accelerometer and gyroscope sensor. We&#8217;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.<\/p>\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" fetchpriority=\"high\" decoding=\"async\" width=\"1200\" height=\"675\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-Web-Server-MPU-6050-Accelerometer-Gyroscope-3D-object-Arduino.jpg?resize=1200%2C675&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 Web Server MPU-6050 Accelerometer Gyroscope 3D object representation Arduino\" class=\"wp-image-101451\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-Web-Server-MPU-6050-Accelerometer-Gyroscope-3D-object-Arduino.jpg?w=1280&amp;quality=100&amp;strip=all&amp;ssl=1 1280w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-Web-Server-MPU-6050-Accelerometer-Gyroscope-3D-object-Arduino.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-Web-Server-MPU-6050-Accelerometer-Gyroscope-3D-object-Arduino.jpg?resize=1024%2C576&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-Web-Server-MPU-6050-Accelerometer-Gyroscope-3D-object-Arduino.jpg?resize=768%2C432&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 1200px) 100vw, 1200px\" \/><\/figure><\/div>\n\n\n<p>To build the web server we\u2019ll use the&nbsp;ESPAsyncWebServer library&nbsp;that provides an easy way to build an asynchronous web server and handle Server-Sent Events.<\/p>\n\n\n\n<p class=\"rntbox rntclblue\">To learn more about Server-Sent Events, read: <a href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-sent-events-sse\/\">ESP32 Web Server using Server-Sent Events (Update Sensor Readings Automatically)<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Watch the Video Demonstration<\/h2>\n\n\n\n<p>Watch the following video for a preview of the project we&#8217;ll build.<\/p>\n\n\n<p style=\"text-align:center\"><iframe width=\"720\" height=\"405\" src=\"https:\/\/www.youtube.com\/embed\/dXcF-Uqa-gw?rel=0\" frameborder=\"0\" allowfullscreen><\/iframe><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Project Overview<\/h2>\n\n\n\n<p>Before going straight to the project, it\u2019s important to outline what our web server will do, so that it is easier to understand.<\/p>\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"1012\" height=\"738\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/ESP-MPU6050-Web-Server-Arduino.png?resize=1012%2C738&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"\" class=\"wp-image-101097\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/ESP-MPU6050-Web-Server-Arduino.png?w=1012&amp;quality=100&amp;strip=all&amp;ssl=1 1012w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/ESP-MPU6050-Web-Server-Arduino.png?resize=300%2C219&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/ESP-MPU6050-Web-Server-Arduino.png?resize=768%2C560&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 1012px) 100vw, 1012px\" \/><\/figure><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li>The web server displays the gyroscope values of the X, Y an Z axis;<\/li>\n\n\n\n<li>The gyroscope values are updated on the web server every 10 milliseconds;<\/li>\n\n\n\n<li>It displays the accelerometer values (X, Y, Z). These values are updated every 200 milliseconds;<\/li>\n\n\n\n<li>The MPU-6050 sensor module also measures temperature, so we&#8217;ll also display the temperature value. The temperature is updated every second (1000 milliseconds);<\/li>\n\n\n\n<li>All the readings are updated using Server-Sent Events;<\/li>\n\n\n\n<li>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;<\/li>\n\n\n\n<li>The 3D object is created using a JavaScript library called <a href=\"https:\/\/threejs.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">three.js<\/a>;<\/li>\n\n\n\n<li>There are four buttons to adjust the position of the 3D object:\n<ul class=\"wp-block-list\">\n<li>RESET POSITION: sets angular position to zero on all axis;<\/li>\n\n\n\n<li>X: sets the X angular position to zero;<\/li>\n\n\n\n<li>Y: sets the Y angular position to zero;<\/li>\n\n\n\n<li>Z: sets the Z angular position to zero;<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" decoding=\"async\" width=\"751\" height=\"294\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/MPU-6050-How-It-Works-ESP32.png?resize=751%2C294&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"MPU-6050 Accelerometer Gyroscope Web Server ESP32 How it Works\" class=\"wp-image-101420\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/MPU-6050-How-It-Works-ESP32.png?w=751&amp;quality=100&amp;strip=all&amp;ssl=1 751w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/MPU-6050-How-It-Works-ESP32.png?resize=300%2C117&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 751px) 100vw, 751px\" \/><\/figure><\/div>\n\n\n<h3 class=\"wp-block-heading\">ESP32 Filesystem<\/h3>\n\n\n\n<p>To keep our project organized and make it easier to understand, we&#8217;ll create four different files to build the web server:<\/p>\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"345\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/MPU6050-Web-Server-Project-Folder-Structure-SPIFFS.png?resize=640%2C345&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"\" class=\"wp-image-101104\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/MPU6050-Web-Server-Project-Folder-Structure-SPIFFS.png?w=640&amp;quality=100&amp;strip=all&amp;ssl=1 640w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/MPU6050-Web-Server-Project-Folder-Structure-SPIFFS.png?resize=300%2C162&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/figure><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li>the Arduino code that handles the web server;<\/li>\n\n\n\n<li>HTML file: to define the content of the web page;<\/li>\n\n\n\n<li>CSS file: to style the web page;<\/li>\n\n\n\n<li>JavaScript file: to program the behavior of the web page (handle web server responses, events and creating the 3D object).<\/li>\n<\/ul>\n\n\n\n<p>The HTML, CSS and JavaScript files will be uploaded to the ESP32 LittleFS filesystem. To upload files to the ESP32 filesystem, we&#8217;ll use the <a href=\"https:\/\/randomnerdtutorials.com\/arduino-ide-2-install-esp32-littlefs\/\" title=\"\">LittleFS Uploader Plugin<\/a>. Make sure you install it on your Arduino IDE:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/arduino-ide-2-install-esp32-littlefs\/\">Arduino IDE 2: Install ESP32 LittleFS Uploader (Upload Files to the Filesystem)<\/a><\/li>\n<\/ul>\n\n\n\n<p>If you&#8217;re using PlatformIO + VS Code, read this article to learn how to upload files to the ESP32 filesystem: <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-vs-code-platformio-littlefs\/\">ESP32 with VS Code and PlatformIO: Upload Files to LittleFS Filesystem<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">MPU-6050 Gyroscope and Accelerometer<\/h2>\n\n\n\n<p>The MPU-6050 is a module with a 3-axis accelerometer and a 3-axis gyroscope.<\/p>\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"422\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/MPU6050-Module-Accelerometer-Gyroscope-Temperature-Sensor.jpg?resize=750%2C422&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"MPU6050 Module Accelerometer Gyroscope Temperature Sensor\" class=\"wp-image-101201\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/MPU6050-Module-Accelerometer-Gyroscope-Temperature-Sensor.jpg?w=750&amp;quality=100&amp;strip=all&amp;ssl=1 750w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/MPU6050-Module-Accelerometer-Gyroscope-Temperature-Sensor.jpg?resize=300%2C169&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/figure><\/div>\n\n\n<p>The gyroscope measures rotational velocity (rad\/s) &#8211; 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.<\/p>\n\n\n<div class=\"wp-block-image is-resized is-style-default\">\n<figure class=\"aligncenter size-medium\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"300\" height=\"272\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/roll-pitch-yaw.png?resize=300%2C272&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Roll Pitch Yaw Angles gyroscope \" class=\"wp-image-101026\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/roll-pitch-yaw.png?resize=300%2C272&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/roll-pitch-yaw.png?resize=1024%2C928&amp;quality=100&amp;strip=all&amp;ssl=1 1024w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/roll-pitch-yaw.png?resize=768%2C696&amp;quality=100&amp;strip=all&amp;ssl=1 768w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/roll-pitch-yaw.png?w=1484&amp;quality=100&amp;strip=all&amp;ssl=1 1484w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/figure><\/div>\n\n\n<p>The accelerometer measures acceleration (rate of change of the velocity of an object). It senses static foces like gravity (9.8m\/s<sup>2<\/sup>) 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.<\/p>\n\n\n\n<p>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. <\/p>\n\n\n\n<p>We can combine the information from both sensors to get accurate information about the sensor orientation. <\/p>\n\n\n\n<p class=\"rntbox rntclblue\">Learn more about the MPU-6050 sensor: <a href=\"https:\/\/randomnerdtutorials.com\/esp32-mpu-6050-accelerometer-gyroscope-arduino\/\">ESP32 with MPU-6050 Accelerometer, Gyroscope and Temperature Sensor<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Schematic Diagram &#8211; ESP32 with MPU-6050<\/h2>\n\n\n\n<p>For this project you need the following parts:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/makeradvisor.com\/tools\/mpu-6050-3-axis-accelerometer-and-gyroscope-sensor\/\" target=\"_blank\" rel=\"noreferrer noopener\">MPU-6050 Accelerometer Gyroscope<\/a> (<a href=\"https:\/\/randomnerdtutorials.com\/esp32-mpu-6050-accelerometer-gyroscope-arduino\/\">ESP32 Guide<\/a>)<\/li>\n\n\n\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/makeradvisor.com\/tools\/esp32-dev-board-wi-fi-bluetooth\/\" target=\"_blank\">ESP32&nbsp;<\/a>(read&nbsp;<a href=\"https:\/\/makeradvisor.com\/esp32-development-boards-review-comparison\/\" target=\"_blank\" rel=\"noreferrer noopener\">Best ESP32 development boards<\/a>)<\/li>\n\n\n\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/makeradvisor.com\/tools\/mb-102-solderless-breadboard-830-points\/\" target=\"_blank\">Breadboard<\/a><\/li>\n\n\n\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/makeradvisor.com\/tools\/jumper-wires-kit-120-pieces\/\" target=\"_blank\">Jumper wires<\/a><\/li>\n<\/ul>\n\n\n<p>You can use the preceding links or go directly to <a href=\"https:\/\/makeradvisor.com\/tools\/?utm_source=rnt&utm_medium=post&utm_campaign=post\" target=\"_blank\">MakerAdvisor.com\/tools<\/a> to find all the parts for your projects at the best price!<\/p><p style=\"text-align:center;\"><a href=\"https:\/\/makeradvisor.com\/tools\/?utm_source=rnt&utm_medium=post&utm_campaign=post\" target=\"_blank\"><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2017\/10\/header-200.png?w=1200&#038;quality=100&#038;strip=all&#038;ssl=1\"><\/a><\/p>\n\n\n\n<p>Wire the ESP32 to the MPU-6050 sensor as shown in the following schematic diagram: connect the SCL pin to <span class=\"rnthl rntclblue\">GPIO 22<\/span> and the SDA pin to <span class=\"rnthl rntclgreen\">GPIO 21<\/span>.<\/p>\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"726\" height=\"687\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/MPU6050_ESP32_Wiring-Schematic-Diagram.png?resize=726%2C687&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"MPU6050 Accelerometer Gyroscope Wiring to ESP32 Schematic Diagram Circuit\" class=\"wp-image-101013\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/MPU6050_ESP32_Wiring-Schematic-Diagram.png?w=726&amp;quality=100&amp;strip=all&amp;ssl=1 726w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/MPU6050_ESP32_Wiring-Schematic-Diagram.png?resize=300%2C284&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 726px) 100vw, 726px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">Preparing Arduino IDE<\/h2>\n\n\n\n<p>We\u2019ll program the ESP32 board using Arduino IDE. So, make sure you have the ESP32 add-on installed. Follow the next tutorial:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/installing-esp32-arduino-ide-2-0\/\" title=\"\">Install the ESP32 Board in Arduino IDE<\/a><\/li>\n<\/ul>\n\n\n\n<p>If you prefer using VSCode + PlatformIO, follow the next tutorial instead:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/vs-code-platformio-ide-esp32-esp8266-arduino\/\">Getting Started with VS Code and PlatformIO IDE for ESP32 and ESP8266<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Installing Libraries<\/h3>\n\n\n\n<p>There are different ways to get readings from the sensor. In this tutorial, we&#8217;ll use the <a href=\"https:\/\/github.com\/adafruit\/Adafruit_MPU6050\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit MPU6050 library<\/a>. To use this library you also need to install the <a href=\"https:\/\/github.com\/adafruit\/Adafruit_Sensor\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit Unified Sensor library<\/a> and the <a href=\"https:\/\/github.com\/adafruit\/Adafruit_BusIO\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit Bus IO Library<\/a>.<\/p>\n\n\n\n<p>To build the web server, we&#8217;ll use the <a href=\"https:\/\/github.com\/ESP32Async\/ESPAsyncWebServer\" target=\"_blank\" rel=\"noopener\" title=\"\">ESPAsyncWebServer<\/a> and the <a href=\"https:\/\/github.com\/ESP32Async\/AsyncTCP\" target=\"_blank\" rel=\"noopener\" title=\"\">AsyncTCP<\/a> libraries. In this example, we\u2019ll send the sensor readings to the browser in JSON format. To make it easier to handle JSON variables, we\u2019ll use the\u00a0<a href=\"https:\/\/github.com\/arduino-libraries\/Arduino_JSON\" target=\"_blank\" rel=\"noreferrer noopener\">Arduino_JSON library<\/a> by Arduino.<\/p>\n\n\n\n<p>Here&#8217;s the list of libraries you need to install:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/adafruit\/Adafruit_MPU6050\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit MPU6050 library<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/adafruit\/Adafruit_Sensor\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit Unified Sensor library<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/adafruit\/Adafruit_BusIO\" target=\"_blank\" rel=\"noreferrer noopener\">Adafruit Bus IO Library<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/ESP32Async\/ESPAsyncWebServer\" target=\"_blank\" rel=\"noopener\" title=\"\">ESPAsyncWebServer<\/a> by ESP32Async<\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/ESP32Async\/AsyncTCP\" target=\"_blank\" rel=\"noopener\" title=\"\">AsyncTCP<\/a> by ESP32Async<\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/arduino-libraries\/Arduino_JSON\" target=\"_blank\" rel=\"noreferrer noopener\">Arduino_JSON library<\/a> by Arduino<\/li>\n<\/ul>\n\n\n\n<p>Open your Arduino IDE and go to <strong>Sketch <\/strong>> <strong>Include Library<\/strong> > <strong>Manage Libraries<\/strong>. The Library Manager should open. Search for the libraries&#8217; names and install them.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Filesystem Uploader Plugin<\/h3>\n\n\n\n<p>To follow this tutorial you should have the ESP32&nbsp;Filesystem Uploader plugin installed in your Arduino IDE. If you don&#8217;t, follow the next tutorial to install it:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/arduino-ide-2-install-esp32-littlefs\/\" title=\"\">Install ESP32 LittleFS Filesystem Uploader on Arduino IDE<\/a><\/li>\n<\/ul>\n\n\n\n<p>If you&#8217;re using VS Code + PlatformIO, follow the next tutorial to learn how to upload files to the ESP32 filesystem:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/esp32-vs-code-platformio-littlefs\/\">ESP32 with VS Code and PlatformIO: Upload Files to LittleFS Filesystem<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Organizing Your Files<\/h2>\n\n\n\n<p>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&nbsp;<em><strong>data<\/strong>&nbsp;<\/em>inside the Arduino sketch folder, as shown below:<\/p>\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"640\" height=\"345\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/MPU6050-Web-Server-Project-Folder-Structure-SPIFFS.png?resize=640%2C345&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Organizing Your ESP Project Files HTML CSS JavaScript Arduino files\" class=\"wp-image-101104\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/MPU6050-Web-Server-Project-Folder-Structure-SPIFFS.png?w=640&amp;quality=100&amp;strip=all&amp;ssl=1 640w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/MPU6050-Web-Server-Project-Folder-Structure-SPIFFS.png?resize=300%2C162&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 640px) 100vw, 640px\" \/><\/figure><\/div>\n\n\n<p><strong>You can download all project files:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_MPU_6050_Web_Server\/ESP32_MPU_6050_Web_Server.zip\" target=\"_blank\" rel=\"noreferrer noopener\">Download ESP_Web_Server_MPU6050.ino, index.html, style.css and script.js<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Creating the HTML File<\/h2>\n\n\n\n<p>Create an&nbsp;<em>index.html<\/em>&nbsp;file with the following content or&nbsp;<a href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_MPU_6050_Web_Server\/ESP32_MPU_6050_Web_Server.zip\" target=\"_blank\" rel=\"noreferrer noopener\">download all the project files<\/a>.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-html\">&lt;!--\r\n  Rui Santos\r\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp32-mpu-6050-web-server\/\r\n\r\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\r\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\r\n--&gt;\r\n\r\n&lt;!DOCTYPE HTML&gt;&lt;html&gt;\r\n&lt;head&gt;\r\n  &lt;title&gt;ESP Web Server&lt;\/title&gt;\r\n  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;\r\n  &lt;link rel=&quot;icon&quot; href=&quot;data:,&quot;&gt;\r\n  &lt;link rel=&quot;stylesheet&quot; type=&quot;text\/css&quot; href=&quot;style.css&quot;&gt;\r\n  &lt;link rel=&quot;stylesheet&quot; href=&quot;https:\/\/use.fontawesome.com\/releases\/v5.7.2\/css\/all.css&quot; integrity=&quot;sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr&quot; crossorigin=&quot;anonymous&quot;&gt;\r\n  &lt;script src=&quot;https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/three.js\/107\/three.min.js&quot;&gt;&lt;\/script&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n  &lt;div class=&quot;topnav&quot;&gt;\r\n    &lt;h1&gt;&lt;i class=&quot;far fa-compass&quot;&gt;&lt;\/i&gt; MPU6050 &lt;i class=&quot;far fa-compass&quot;&gt;&lt;\/i&gt;&lt;\/h1&gt;\r\n  &lt;\/div&gt;\r\n  &lt;div class=&quot;content&quot;&gt;\r\n    &lt;div class=&quot;cards&quot;&gt;\r\n      &lt;div class=&quot;card&quot;&gt;\r\n        &lt;p class=&quot;card-title&quot;&gt;GYROSCOPE&lt;\/p&gt;\r\n        &lt;p&gt;&lt;span class=&quot;reading&quot;&gt;X: &lt;span id=&quot;gyroX&quot;&gt;&lt;\/span&gt; rad&lt;\/span&gt;&lt;\/p&gt;\r\n        &lt;p&gt;&lt;span class=&quot;reading&quot;&gt;Y: &lt;span id=&quot;gyroY&quot;&gt;&lt;\/span&gt; rad&lt;\/span&gt;&lt;\/p&gt;\r\n        &lt;p&gt;&lt;span class=&quot;reading&quot;&gt;Z: &lt;span id=&quot;gyroZ&quot;&gt;&lt;\/span&gt; rad&lt;\/span&gt;&lt;\/p&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div class=&quot;card&quot;&gt;\r\n        &lt;p class=&quot;card-title&quot;&gt;ACCELEROMETER&lt;\/p&gt;\r\n        &lt;p&gt;&lt;span class=&quot;reading&quot;&gt;X: &lt;span id=&quot;accX&quot;&gt;&lt;\/span&gt; ms&lt;sup&gt;2&lt;\/sup&gt;&lt;\/span&gt;&lt;\/p&gt;\r\n        &lt;p&gt;&lt;span class=&quot;reading&quot;&gt;Y: &lt;span id=&quot;accY&quot;&gt;&lt;\/span&gt; ms&lt;sup&gt;2&lt;\/sup&gt;&lt;\/span&gt;&lt;\/p&gt;\r\n        &lt;p&gt;&lt;span class=&quot;reading&quot;&gt;Z: &lt;span id=&quot;accZ&quot;&gt;&lt;\/span&gt; ms&lt;sup&gt;2&lt;\/sup&gt;&lt;\/span&gt;&lt;\/p&gt;\r\n      &lt;\/div&gt;\r\n      &lt;div class=&quot;card&quot;&gt;\r\n        &lt;p class=&quot;card-title&quot;&gt;TEMPERATURE&lt;\/p&gt;\r\n        &lt;p&gt;&lt;span class=&quot;reading&quot;&gt;&lt;span id=&quot;temp&quot;&gt;&lt;\/span&gt; &amp;deg;C&lt;\/span&gt;&lt;\/p&gt;\r\n        &lt;p class=&quot;card-title&quot;&gt;3D ANIMATION&lt;\/p&gt;\r\n        &lt;button id=&quot;reset&quot; onclick=&quot;resetPosition(this)&quot;&gt;RESET POSITION&lt;\/button&gt;\r\n        &lt;button id=&quot;resetX&quot; onclick=&quot;resetPosition(this)&quot;&gt;X&lt;\/button&gt;\r\n        &lt;button id=&quot;resetY&quot; onclick=&quot;resetPosition(this)&quot;&gt;Y&lt;\/button&gt;\r\n        &lt;button id=&quot;resetZ&quot; onclick=&quot;resetPosition(this)&quot;&gt;Z&lt;\/button&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n    &lt;div class=&quot;cube-content&quot;&gt;\r\n      &lt;div id=&quot;3Dcube&quot;&gt;&lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n&lt;script src=&quot;script.js&quot;&gt;&lt;\/script&gt;\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_MPU_6050_Web_Server\/data\/index.html\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Head<\/h3>\n\n\n\n<p>The <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;head&gt;<\/span><\/span> and <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;\/head&gt;<\/span><\/span> 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 \u2013 this is called metadata.<\/p>\n\n\n\n<p>The next line gives a title to the web page. In this case, it is set to <strong>ESP Web Server<\/strong>. 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\u2019s title bar.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;title&gt;ESP Web Server&lt;\/title&gt;<\/code><\/pre>\n\n\n\n<p>The following meta tag makes your web page responsive. A responsive web design will automatically adjust for different screen sizes and viewports.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"&gt;<\/code><\/pre>\n\n\n\n<p>We use the following meta tag because we won\u2019t serve a favicon for our web page in this project.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;link rel=\"icon\" href=\"data:,\"&gt;<\/code><\/pre>\n\n\n\n<p>The styles to style the web page are on a separated file called <em>style.css<\/em> file. So, we must reference the CSS file on the HTML file as follows.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;link rel=\"stylesheet\" type=\"text\/css\" href=\"style.css\"&gt;<\/code><\/pre>\n\n\n\n<p>Include the Font Awesome website styles to include icons in the web page like the gyroscope icon.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.5.1\/css\/all.min.css\"&gt;<\/code><\/pre>\n\n\n\n<p>Finally, we need to include the <em>three.js<\/em> library to create the 3D representation of the sensor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/three.js\/107\/three.min.js\"&gt;&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Body<\/h3>\n\n\n\n<p>The <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;body&gt;<\/span><\/span> and <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;\/body&gt;<\/span><\/span> tags mark the start and end of the body. Everything that goes inside those tags is the visible page content.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Top Bar<\/h4>\n\n\n\n<p>There\u2019s a top bar with a heading in the web page. It is a heading 1 and it is placed inside a <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;div&gt;<\/span><\/span> tag with the class name <span class=\"rnthl rntliteral\">topnav<\/span>. Placing your HTML elements between <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;div&gt;<\/span><\/span> tags, makes them easy to style using CSS.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;div class=\"topnav\"&gt;\n  &lt;h1&gt;&lt;i class=\"far fa-compass\"&gt;&lt;\/i&gt; MPU6050 &lt;i class=\"far fa-compass\"&gt;&lt;\/i&gt;&lt;\/h1&gt;\n&lt;\/div&gt;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Content Grid<\/h4>\n\n\n\n<p>All the other content is placed inside a <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;div&gt;<\/span><\/span> tag called <span class=\"rnthl rntliteral\">content<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;div class=\"content\"&gt;<\/code><\/pre>\n\n\n\n<p>We use CSS grid layout to display the readings on different aligned boxes (<span class=\"rnthl rntliteral\">card<\/span>). 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 <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;div&gt;<\/span><\/span> tag. This new tag has the classname <span class=\"rnthl rntliteral\">cards<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;div class=\"cards\"&gt;<\/code><\/pre>\n\n\n\n<p>To learn more about CSS grid layout, we recommend this article: <a href=\"https:\/\/css-tricks.com\/snippets\/css\/complete-guide-grid\/\" target=\"_blank\" rel=\"noreferrer noopener\">A Complete Guide to Grid<\/a>. Here&#8217;s the card for the gyroscope readings:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;div class=\"card\"&gt;\n  &lt;p class=\"card-title\"&gt;GYROSCOPE&lt;\/p&gt;\n  &lt;p&gt;&lt;span class=\"reading\"&gt;X: &lt;span id=\"gyroX\"&gt;&lt;\/span&gt; rad\/s&lt;\/span&gt;&lt;\/p&gt;\n  &lt;p&gt;&lt;span class=\"reading\"&gt;Y: &lt;span id=\"gyroY\"&gt;&lt;\/span&gt; rad\/s&lt;\/span&gt;&lt;\/p&gt;\n  &lt;p&gt;&lt;span class=\"reading\"&gt;Z: &lt;span id=\"gyroZ\"&gt;&lt;\/span&gt; rad\/s&lt;\/span&gt;&lt;\/p&gt;\n&lt;\/div&gt;<\/code><\/pre>\n\n\n\n<p>The card has a title with the name of the card:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;p class=\"card-title\"&gt;GYROSCOPE&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<p>And three paragraphs to display the gyroscope values on the X, Y and Z axis.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;p&gt;&lt;span class=\"reading\"&gt;X: &lt;span id=\"gyroX\"&gt;&lt;\/span&gt; rad\/s&lt;\/span&gt;&lt;\/p&gt;\n&lt;p&gt;&lt;span class=\"reading\"&gt;Y: &lt;span id=\"gyroY\"&gt;&lt;\/span&gt; rad\/s&lt;\/span&gt;&lt;\/p&gt;\n&lt;p&gt;&lt;span class=\"reading\"&gt;Z: &lt;span id=\"gyroZ\"&gt;&lt;\/span&gt; rad\/s&lt;\/span&gt;&lt;\/p&gt;<\/code><\/pre>\n\n\n\n<p>In each paragraph there&#8217;s a <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;span&gt;<\/span><\/span> tag with a unique <span class=\"rnthl rntliteral\">id<\/span>. This is needed so that we can insert the readings on the right place later using JavaScript. Here&#8217;s the ids used:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><span class=\"rnthl rntliteral\">gyroX<\/span> for the gyroscope X reading;<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">gyroY<\/span> for the gyroscope Y reading;<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">gyroZ<\/span> for the gyroscope Z reading.<\/li>\n<\/ul>\n\n\n\n<p>The card to display the accelerometer readings is similar, but with different unique ids for each reading:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;div class=\"card\"&gt;\n  &lt;p class=\"card-title\"&gt;ACCELEROMETER&lt;\/p&gt;\n  &lt;p&gt;&lt;span class=\"reading\"&gt;X: &lt;span id=\"accX\"&gt;&lt;\/span&gt; ms&lt;sup&gt;2&lt;\/sup&gt;&lt;\/span&gt;&lt;\/p&gt;\n  &lt;p&gt;&lt;span class=\"reading\"&gt;Y: &lt;span id=\"accY\"&gt;&lt;\/span&gt; ms&lt;sup&gt;2&lt;\/sup&gt;&lt;\/span&gt;&lt;\/p&gt;\n  &lt;p&gt;&lt;span class=\"reading\"&gt;Z: &lt;span id=\"accZ\"&gt;&lt;\/span&gt; ms&lt;sup&gt;2&lt;\/sup&gt;&lt;\/span&gt;&lt;\/p&gt;\n&lt;\/div&gt;<\/code><\/pre>\n\n\n\n<p>Here&#8217;s the ids for the accelerometer readings:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><span class=\"rnthl rntliteral\">accX<\/span> for the accelerometer X reading;<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">accY<\/span> for the accelerometer Y reading;<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">accZ<\/span> for the accelerometer Z reading.<\/li>\n<\/ul>\n\n\n\n<p>Finally, the following lines display the card for the temperature and the reset buttons.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;div class=\"card\"&gt;\n  &lt;p class=\"card-title\"&gt;TEMPERATURE&lt;\/p&gt;\n  &lt;p&gt;&lt;span class=\"reading\"&gt;&lt;span id=\"temp\"&gt;&lt;\/span&gt; &amp;deg;C&lt;\/span&gt;&lt;\/p&gt;\n  &lt;p class=\"card-title\"&gt;3D ANIMATION&lt;\/p&gt;\n  &lt;button id=\"reset\" onclick=\"resetPosition(this)\"&gt;RESET POSITION&lt;\/button&gt;\n  &lt;button id=\"resetX\" onclick=\"resetPosition(this)\"&gt;X&lt;\/button&gt;\n  &lt;button id=\"resetY\" onclick=\"resetPosition(this)\"&gt;Y&lt;\/button&gt;\n   &lt;button id=\"resetZ\" onclick=\"resetPosition(this)\"&gt;Z&lt;\/button&gt;\n &lt;\/div&gt;\n<\/code><\/pre>\n\n\n\n<p>The unique id for the temperature reading is <span class=\"rnthl rntliteral\">temp<\/span>.<\/p>\n\n\n\n<p>Then there are four different buttons that when clicked will call the <span class=\"rnthl rntliteral\">resetPosition()<\/span> 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:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><span class=\"rnthl rntliteral\">reset<\/span>: to reset the position in all axis;<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">resetX<\/span>: to reset the position on the X axis;<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">resetY<\/span>: to reset the position on the Y axis;<\/li>\n\n\n\n<li><span class=\"rnthl rntliteral\">resetZ<\/span>: to reset the position on the Z axis.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">3D Representation<\/h4>\n\n\n\n<p>We need to create a section to display the 3D representation.<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;div class=\"cube-content\"&gt;\n  &lt;div id=\"3Dcube\"&gt;&lt;\/div&gt;\n&lt;\/div&gt;<\/code><\/pre>\n\n\n\n<p>The 3D object will be rendered on the <span class=\"rnthl rntliteral\"><span style=\"color: #333399;\">&lt;div&gt;<\/span><\/span> with the <span class=\"rnthl rntliteral\">3Dcube<\/span> id.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Reference the JavaScript File<\/h4>\n\n\n\n<p>Finally, because we&#8217;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 (<em>script.js<\/em>) as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code language-html\"><code>&lt;script src=\"script.js\"&gt;&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Creating the CSS File<\/h2>\n\n\n\n<p>Create a file called <em>style.css<\/em> with the following content or <a href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_MPU_6050_Web_Server\/ESP32_MPU_6050_Web_Server.zip\" target=\"_blank\" rel=\"noreferrer noopener\">download all the project files<\/a>.<\/p>\n\n\n\n<p>This file is responsible for styling the web page.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-css\">\/*\r\n  Rui Santos\r\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp32-mpu-6050-web-server\/\r\n\r\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\r\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\r\n*\/\r\n\r\nhtml {\r\n  font-family: Arial;\r\n  display: inline-block;\r\n  text-align: center;\r\n}\r\np {\r\n  font-size: 1.2rem;\r\n}\r\nbody {\r\n  margin: 0;\r\n}\r\n.topnav {\r\n  overflow: hidden;\r\n  background-color: #003366;\r\n  color: #FFD43B;\r\n  font-size: 1rem;\r\n}\r\n.content {\r\n  padding: 20px;\r\n}\r\n.card {\r\n  background-color: white;\r\n  box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);\r\n}\r\n.card-title {\r\n  color:#003366;\r\n  font-weight: bold;\r\n}\r\n.cards {\r\n  max-width: 800px;\r\n  margin: 0 auto;\r\n  display: grid; grid-gap: 2rem;\r\n  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\r\n}\r\n.reading {\r\n  font-size: 1.2rem;\r\n}\r\n.cube-content{\r\n  width: 100%;\r\n  background-color: white;\r\n  height: 300px; margin: auto;\r\n  padding-top:2%;\r\n}\r\n#reset{\r\n  border: none;\r\n  color: #FEFCFB;\r\n  background-color: #003366;\r\n  padding: 10px;\r\n  text-align: center;\r\n  display: inline-block;\r\n  font-size: 14px; width: 150px;\r\n  border-radius: 4px;\r\n}\r\n#resetX, #resetY, #resetZ{\r\n  border: none;\r\n  color: #FEFCFB;\r\n  background-color: #003366;\r\n  padding-top: 10px;\r\n  padding-bottom: 10px;\r\n  text-align: center;\r\n  display: inline-block;\r\n  font-size: 14px;\r\n  width: 20px;\r\n  border-radius: 4px;\r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_MPU_6050_Web_Server\/data\/style.css\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>We won&#8217;t explain how the CSS for this project works because it is not relevant for the goal of this project.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Creating the JavaScript File<\/h2>\n\n\n\n<p>Create a file called <em>script.js<\/em> with the following content or <a href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_MPU_6050_Web_Server\/ESP32_MPU_6050_Web_Server.zip\" target=\"_blank\" rel=\"noreferrer noopener\">download all the project files<\/a>.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-javascript\">\/*\r\n  Rui Santos\r\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp32-mpu-6050-web-server\/\r\n\r\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\r\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\r\n*\/\r\n\r\nlet scene, camera, rendered, cube;\r\n\r\nfunction parentWidth(elem) {\r\n  return elem.parentElement.clientWidth;\r\n}\r\n\r\nfunction parentHeight(elem) {\r\n  return elem.parentElement.clientHeight;\r\n}\r\n\r\nfunction init3D(){\r\n  scene = new THREE.Scene();\r\n  scene.background = new THREE.Color(0xffffff);\r\n\r\n  camera = new THREE.PerspectiveCamera(75, parentWidth(document.getElementById(&quot;3Dcube&quot;)) \/ parentHeight(document.getElementById(&quot;3Dcube&quot;)), 0.1, 1000);\r\n\r\n  renderer = new THREE.WebGLRenderer({ antialias: true });\r\n  renderer.setSize(parentWidth(document.getElementById(&quot;3Dcube&quot;)), parentHeight(document.getElementById(&quot;3Dcube&quot;)));\r\n\r\n  document.getElementById('3Dcube').appendChild(renderer.domElement);\r\n\r\n  \/\/ Create a geometry\r\n  const geometry = new THREE.BoxGeometry(5, 1, 4);\r\n\r\n  \/\/ Materials of each face\r\n  var cubeMaterials = [\r\n    new THREE.MeshBasicMaterial({color:0x03045e}),\r\n    new THREE.MeshBasicMaterial({color:0x023e8a}),\r\n    new THREE.MeshBasicMaterial({color:0x0077b6}),\r\n    new THREE.MeshBasicMaterial({color:0x03045e}),\r\n    new THREE.MeshBasicMaterial({color:0x023e8a}),\r\n    new THREE.MeshBasicMaterial({color:0x0077b6}),\r\n  ];\r\n\r\n  const material = new THREE.MeshFaceMaterial(cubeMaterials);\r\n\r\n  cube = new THREE.Mesh(geometry, material);\r\n  scene.add(cube);\r\n  camera.position.z = 5;\r\n  renderer.render(scene, camera);\r\n}\r\n\r\n\/\/ Resize the 3D object when the browser window changes size\r\nfunction onWindowResize(){\r\n  camera.aspect = parentWidth(document.getElementById(&quot;3Dcube&quot;)) \/ parentHeight(document.getElementById(&quot;3Dcube&quot;));\r\n  \/\/camera.aspect = window.innerWidth \/  window.innerHeight;\r\n  camera.updateProjectionMatrix();\r\n  \/\/renderer.setSize(window.innerWidth, window.innerHeight);\r\n  renderer.setSize(parentWidth(document.getElementById(&quot;3Dcube&quot;)), parentHeight(document.getElementById(&quot;3Dcube&quot;)));\r\n\r\n}\r\n\r\nwindow.addEventListener('resize', onWindowResize, false);\r\n\r\n\/\/ Create the 3D representation\r\ninit3D();\r\n\r\n\/\/ Create events for the sensor readings\r\nif (!!window.EventSource) {\r\n  var source = new EventSource('\/events');\r\n\r\n  source.addEventListener('open', function(e) {\r\n    console.log(&quot;Events Connected&quot;);\r\n  }, false);\r\n\r\n  source.addEventListener('error', function(e) {\r\n    if (e.target.readyState != EventSource.OPEN) {\r\n      console.log(&quot;Events Disconnected&quot;);\r\n    }\r\n  }, false);\r\n\r\n  source.addEventListener('gyro_readings', function(e) {\r\n    \/\/console.log(&quot;gyro_readings&quot;, e.data);\r\n    var obj = JSON.parse(e.data);\r\n    document.getElementById(&quot;gyroX&quot;).innerHTML = obj.gyroX;\r\n    document.getElementById(&quot;gyroY&quot;).innerHTML = obj.gyroY;\r\n    document.getElementById(&quot;gyroZ&quot;).innerHTML = obj.gyroZ;\r\n\r\n    \/\/ Change cube rotation after receiving the readinds\r\n    cube.rotation.x = obj.gyroY;\r\n    cube.rotation.z = obj.gyroX;\r\n    cube.rotation.y = obj.gyroZ;\r\n    renderer.render(scene, camera);\r\n  }, false);\r\n\r\n  source.addEventListener('temperature_reading', function(e) {\r\n    console.log(&quot;temperature_reading&quot;, e.data);\r\n    document.getElementById(&quot;temp&quot;).innerHTML = e.data;\r\n  }, false);\r\n\r\n  source.addEventListener('accelerometer_readings', function(e) {\r\n    console.log(&quot;accelerometer_readings&quot;, e.data);\r\n    var obj = JSON.parse(e.data);\r\n    document.getElementById(&quot;accX&quot;).innerHTML = obj.accX;\r\n    document.getElementById(&quot;accY&quot;).innerHTML = obj.accY;\r\n    document.getElementById(&quot;accZ&quot;).innerHTML = obj.accZ;\r\n  }, false);\r\n}\r\n\r\nfunction resetPosition(element){\r\n  var xhr = new XMLHttpRequest();\r\n  xhr.open(&quot;GET&quot;, &quot;\/&quot;+element.id, true);\r\n  console.log(element.id);\r\n  xhr.send();\r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_MPU_6050_Web_Server\/data\/script.js\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Creating a 3D Object<\/h3>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">init3D()<\/span> function creates the 3D object. To actually be able to display anything with <em>three.js<\/em>, we need three things: scene, camera and renderer, so that we can render the scene with camera.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>function init3D(){\n  scene = new THREE.Scene();\n  scene.background = new THREE.Color(0xffffff);\n\n  camera = new THREE.PerspectiveCamera(75, parentWidth(document.getElementById(\"3Dcube\")) \/ parentHeight(document.getElementById(\"3Dcube\")), 0.1, 1000);\n\n  renderer = new THREE.WebGLRenderer({ antialias: true });\n  renderer.setSize(parentWidth(document.getElementById(\"3Dcube\")), parentHeight(document.getElementById(\"3Dcube\")));\n   \n  document.getElementById('3Dcube').appendChild(renderer.domElement);<\/code><\/pre>\n\n\n\n<p>To create the 3D object, we need a <span class=\"rnthl rntliteral\">BoxGeometry<\/span>. 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.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>const geometry = new THREE.BoxGeometry(5, 1, 4);<\/code><\/pre>\n\n\n\n<p>Besides the geometry, we also need a material to color the object. There are different ways to color the object. We&#8217;ve chosen three different colors for the faces.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>\/\/ Materials of each face\nvar cubeMaterials = &#091; \n  new THREE.MeshBasicMaterial({color:0x03045e}),\n  new THREE.MeshBasicMaterial({color:0x023e8a}), \n  new THREE.MeshBasicMaterial({color:0x0077b6}),\n  new THREE.MeshBasicMaterial({color:0x03045e}),\n  new THREE.MeshBasicMaterial({color:0x023e8a}), \n  new THREE.MeshBasicMaterial({color:0x0077b6}),\n]; \n\nconst material = new THREE.MeshFaceMaterial(cubeMaterials);<\/code><\/pre>\n\n\n\n<p>Finally, create the 3D object, add it to the scene and adjust the camera.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>cube = new THREE.Mesh(geometry, material);\nscene.add(cube);\ncamera.position.z = 5;\nrenderer.render(scene, camera);<\/code><\/pre>\n\n\n\n<p>We recommend taking a look at this quick <em>three.js<\/em> tutorial to better understand how it works: <a href=\"https:\/\/threejs.org\/docs\/index.html#manual\/en\/introduction\/Creating-a-scene\" target=\"_blank\" rel=\"noreferrer noopener\">Getting Started with three.js &#8211; Creating a Scene<\/a>.<\/p>\n\n\n\n<p>To be able to resize the object when the web browser window changes size, we need to call the <span class=\"rnthl rntliteral\">onWindowResize()<\/span> function when the event <span class=\"rnthl rntliteral\">resize<\/span> occurs.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>\/\/ Resize the 3D object when the browser window changes size\nfunction onWindowResize(){\n  camera.aspect = parentWidth(document.getElementById(\"3Dcube\")) \/ parentHeight(document.getElementById(\"3Dcube\"));\n  \/\/camera.aspect = window.innerWidth \/  window.innerHeight;\n  camera.updateProjectionMatrix();\n  \/\/renderer.setSize(window.innerWidth, window.innerHeight);\n  renderer.setSize(parentWidth(document.getElementById(\"3Dcube\")), parentHeight(document.getElementById(\"3Dcube\")));\n}\n\nwindow.addEventListener('resize', onWindowResize, false);\n<\/code><\/pre>\n\n\n\n<p>Call the <span class=\"rnthl rntliteral\">init3D()<\/span> function to actual create the 3D representation.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>init3D();<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Events (SSE)<\/h3>\n\n\n\n<p>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. <\/p>\n\n\n\n<p>In this example, we want to place the readings on the corresponding HTML elements and change the 3D object orientation accordingly.<\/p>\n\n\n\n<p>Create a new <span class=\"rnthl rntliteral\">EventSource<\/span> object and specify the URL of the page sending the updates. In our case, it <span class=\"rnthl rntliteral\">\/events<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>if (!!window.EventSource) {\n  var source = new EventSource('\/events');<\/code><\/pre>\n\n\n\n<p>Once you\u2019ve instantiated an event source, you can start listening for messages from the server with <span class=\"rnthl rntliteral\">addEventListener()<\/span>.<\/p>\n\n\n\n<p>These are the default event listeners, as shown here in the AsyncWebServer&nbsp;<a href=\"https:\/\/github.com\/me-no-dev\/ESPAsyncWebServer#setup-event-source-in-the-browser\" target=\"_blank\" rel=\"noreferrer noopener\">documentation<\/a>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>source.addEventListener('open', function(e) {\n  console.log(\"Events Connected\");\n}, false);\n\nsource.addEventListener('error', function(e) {\n  if (e.target.readyState != EventSource.OPEN) {\n    console.log(\"Events Disconnected\");\n  }\n}, false);<\/code><\/pre>\n\n\n\n<p>When new gyroscope readings are available, the ESP32 sends an event <span class=\"rnthl rntliteral\">gyro_readings<\/span> to the client. We need to add an event listener for that specific event.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>source.addEventListener('gyro_readings', function(e) {<\/code><\/pre>\n\n\n\n<p>The gyroscope readings are a String in JSON format. For example:<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>{\n  \"gyroX\" : \"0.09\",\n  \"gyroY\" : \"0.05\",\n  \"gyroZ\": \"0.04\"\n}<\/code><\/pre>\n\n\n\n<p>JavaScript has a built-in function to convert a string, written in JSON format, into native JavaScript objects: <span class=\"rnthl rntliteral\">JSON.parse()<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>var obj = JSON.parse(e.data);<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">obj<\/span> variable contains the sensor readings in native JavaScript format. Then, we can access the readings as follows:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>gyroscope X reading: <span class=\"rnthl rntliteral\">obj.gyroX<\/span>;<\/li>\n\n\n\n<li>gyroscope Y reading: <span class=\"rnthl rntliteral\">obj.gyroY<\/span>;<\/li>\n\n\n\n<li>gyroscope Z reading: <span class=\"rnthl rntliteral\">obj.gyroZ<\/span>;<\/li>\n<\/ul>\n\n\n\n<p>The following lines put the received data into the corresponding HTML elements on the web page.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>document.getElementById(\"gyroX\").innerHTML = obj.gyroX;\ndocument.getElementById(\"gyroY\").innerHTML = obj.gyroY;\ndocument.getElementById(\"gyroZ\").innerHTML = obj.gyroZ;<\/code><\/pre>\n\n\n\n<p>Finally, we need to change the cube rotation according to the received readings, as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>cube.rotation.x = obj.gyroY;\ncube.rotation.z = obj.gyroX;\ncube.rotation.y = obj.gyroZ;\nrenderer.render(scene, camera);<\/code><\/pre>\n\n\n\n<p><strong>Note:<\/strong> in our case, the axis are switched as shown previously (rotation X &#8211;&gt; gyroY, rotation Z &#8211;&gt; gyroX, rotation Y &#8211;&gt; gyroZ). You might need to change this depending on your sensor orientation.<\/p>\n\n\n\n<p>For the <span class=\"rnthl rntliteral\">accelerometer_readings<\/span> and <span class=\"rnthl rntliteral\">temperature<\/span> events, we simply display the data on the HTML page.<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>source.addEventListener('temperature_reading', function(e) {\n  console.log(\"temperature_reading\", e.data);\n  document.getElementById(\"temp\").innerHTML = e.data;\n}, false);\n\nsource.addEventListener('accelerometer_readings', function(e) {\n  console.log(\"accelerometer_readings\", e.data);\n  var obj = JSON.parse(e.data);\n  document.getElementById(\"accX\").innerHTML = obj.accX;\n  document.getElementById(\"accY\").innerHTML = obj.accY;\n  document.getElementById(\"accZ\").innerHTML = obj.accZ;\n}, false);<\/code><\/pre>\n\n\n\n<p>Finally, we need to create the <span class=\"rnthl rntliteral\">resetPosition()<\/span> function. This function will be called by the reset buttons. <\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>function resetPosition(element){\n  var xhr = new XMLHttpRequest();\n  xhr.open(\"GET\", \"\/\"+element.id, true);\n  console.log(element.id);\n  xhr.send();\n}<\/code><\/pre>\n\n\n\n<p>This function simply sends an HTTP request to the server on a different URL depending on the button that was pressed (<span class=\"rnthl rntliteral\">element.id<\/span>).<\/p>\n\n\n\n<pre class=\"wp-block-code language-javascript\"><code>xhr.open(\"GET\", \"\/\"+element.id, true);<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>RESET POSITION button &#8211;&gt; request: <span class=\"rnthl rntliteral\">\/reset<\/span><\/li>\n\n\n\n<li>X button &#8211;&gt; request: <span class=\"rnthl rntliteral\">\/resetX<\/span><\/li>\n\n\n\n<li>Y button &#8211;&gt; request: <span class=\"rnthl rntliteral\">\/resetY<\/span><\/li>\n\n\n\n<li>Z button &#8211;&gt; request: <span class=\"rnthl rntliteral\">\/resetZ<\/span><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Arduino Sketch<\/h2>\n\n\n\n<p>Finally, let&#8217;s configure the server (ESP32). Copy the following code to the Arduino IDE or <a href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_MPU_6050_Web_Server\/ESP32_MPU_6050_Web_Server.zip\" target=\"_blank\" rel=\"noreferrer noopener\">download all the project files<\/a>.<\/p>\n\n\n<pre style=\"max-height: 40em; margin-bottom: 20px;\"><code class=\"language-c\">\/*********\r\n  Rui Santos &amp; Sara Santos - Random Nerd Tutorials\r\n  Complete project details at https:\/\/RandomNerdTutorials.com\/esp32-mpu-6050-web-server\/\r\n  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.\r\n  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\r\n*********\/\r\n#include &lt;Arduino.h&gt;\r\n#include &lt;WiFi.h&gt;\r\n#include &lt;AsyncTCP.h&gt;\r\n#include &lt;ESPAsyncWebServer.h&gt;\r\n#include &lt;Adafruit_MPU6050.h&gt;\r\n#include &lt;Adafruit_Sensor.h&gt;\r\n#include &lt;Arduino_JSON.h&gt;\r\n#include &quot;LittleFS.h&quot;\r\n\r\n\/\/ Replace with your network credentials\r\nconst char* ssid = &quot;REPLACE_WITH_YOUR_SSID&quot;;\r\nconst char* password = &quot;REPLACE_WITH_YOUR_PASSWORD&quot;;\r\n\r\n\/\/ Create AsyncWebServer object on port 80\r\nAsyncWebServer server(80);\r\n\r\n\/\/ Create an Event Source on \/events\r\nAsyncEventSource events(&quot;\/events&quot;);\r\n\r\n\/\/ Json Variable to Hold Sensor Readings\r\nJSONVar readings;\r\n\r\n\/\/ Timer variables\r\nunsigned long lastTime = 0;  \r\nunsigned long lastTimeTemperature = 0;\r\nunsigned long lastTimeAcc = 0;\r\nunsigned long gyroDelay = 10;\r\nunsigned long temperatureDelay = 1000;\r\nunsigned long accelerometerDelay = 200;\r\n\r\n\/\/ Create a sensor object\r\nAdafruit_MPU6050 mpu;\r\n\r\nsensors_event_t a, g, temp;\r\n\r\nfloat gyroX, gyroY, gyroZ;\r\nfloat accX, accY, accZ;\r\nfloat temperature;\r\n\r\n\/\/Gyroscope sensor deviation\r\nfloat gyroXerror = 0.07;\r\nfloat gyroYerror = 0.03;\r\nfloat gyroZerror = 0.01;\r\n\r\n\/\/ Init MPU6050\r\nvoid initMPU(){\r\n  if (!mpu.begin()) {\r\n    Serial.println(&quot;Failed to find MPU6050 chip&quot;);\r\n    while (1) {\r\n      delay(10);\r\n    }\r\n  }\r\n  Serial.println(&quot;MPU6050 Found!&quot;);\r\n}\r\n\r\nvoid initLittleFS() {\r\n  if (!LittleFS.begin()) {\r\n    Serial.println(&quot;An error has occurred while mounting LittleFS&quot;);\r\n  }\r\n  Serial.println(&quot;LittleFS mounted successfully&quot;);\r\n}\r\n\r\n\/\/ Initialize WiFi\r\nvoid initWiFi() {\r\n  WiFi.mode(WIFI_STA);\r\n  WiFi.begin(ssid, password);\r\n  Serial.println(&quot;&quot;);\r\n  Serial.print(&quot;Connecting to WiFi...&quot;);\r\n  while (WiFi.status() != WL_CONNECTED) {\r\n    Serial.print(&quot;.&quot;);\r\n    delay(1000);\r\n  }\r\n  Serial.println(&quot;&quot;);\r\n  Serial.println(WiFi.localIP());\r\n}\r\n\r\nString getGyroReadings(){\r\n  mpu.getEvent(&amp;a, &amp;g, &amp;temp);\r\n\r\n  float gyroX_temp = g.gyro.x;\r\n  if(abs(gyroX_temp) &gt; gyroXerror)  {\r\n    gyroX += gyroX_temp\/50.00;\r\n  }\r\n  \r\n  float gyroY_temp = g.gyro.y;\r\n  if(abs(gyroY_temp) &gt; gyroYerror) {\r\n    gyroY += gyroY_temp\/70.00;\r\n  }\r\n\r\n  float gyroZ_temp = g.gyro.z;\r\n  if(abs(gyroZ_temp) &gt; gyroZerror) {\r\n    gyroZ += gyroZ_temp\/90.00;\r\n  }\r\n\r\n  readings[&quot;gyroX&quot;] = String(gyroX);\r\n  readings[&quot;gyroY&quot;] = String(gyroY);\r\n  readings[&quot;gyroZ&quot;] = String(gyroZ);\r\n\r\n  String jsonString = JSON.stringify(readings);\r\n  return jsonString;\r\n}\r\n\r\nString getAccReadings() {\r\n  mpu.getEvent(&amp;a, &amp;g, &amp;temp);\r\n  \/\/ Get current acceleration values\r\n  accX = a.acceleration.x;\r\n  accY = a.acceleration.y;\r\n  accZ = a.acceleration.z;\r\n  readings[&quot;accX&quot;] = String(accX);\r\n  readings[&quot;accY&quot;] = String(accY);\r\n  readings[&quot;accZ&quot;] = String(accZ);\r\n  String accString = JSON.stringify (readings);\r\n  return accString;\r\n}\r\n\r\nString getTemperature(){\r\n  mpu.getEvent(&amp;a, &amp;g, &amp;temp);\r\n  temperature = temp.temperature;\r\n  return String(temperature);\r\n}\r\n\r\nvoid setup() {\r\n  Serial.begin(115200);\r\n  initWiFi();\r\n  initLittleFS();\r\n  initMPU();\r\n\r\n  \/\/ Handle Web Server\r\n  server.on(&quot;\/&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    request-&gt;send(LittleFS, &quot;\/index.html&quot;, &quot;text\/html&quot;);\r\n  });\r\n\r\n  server.serveStatic(&quot;\/&quot;, LittleFS, &quot;\/&quot;);\r\n\r\n  server.on(&quot;\/reset&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    gyroX=0;\r\n    gyroY=0;\r\n    gyroZ=0;\r\n    request-&gt;send(200, &quot;text\/plain&quot;, &quot;OK&quot;);\r\n  });\r\n\r\n  server.on(&quot;\/resetX&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    gyroX=0;\r\n    request-&gt;send(200, &quot;text\/plain&quot;, &quot;OK&quot;);\r\n  });\r\n\r\n  server.on(&quot;\/resetY&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    gyroY=0;\r\n    request-&gt;send(200, &quot;text\/plain&quot;, &quot;OK&quot;);\r\n  });\r\n\r\n  server.on(&quot;\/resetZ&quot;, HTTP_GET, [](AsyncWebServerRequest *request){\r\n    gyroZ=0;\r\n    request-&gt;send(200, &quot;text\/plain&quot;, &quot;OK&quot;);\r\n  });\r\n\r\n  \/\/ Handle Web Server Events\r\n  events.onConnect([](AsyncEventSourceClient *client){\r\n    if(client-&gt;lastId()){\r\n      Serial.printf(&quot;Client reconnected! Last message ID that it got is: %u\\n&quot;, client-&gt;lastId());\r\n    }\r\n    \/\/ send event with message &quot;hello!&quot;, id current millis\r\n    \/\/ and set reconnect delay to 1 second\r\n    client-&gt;send(&quot;hello!&quot;, NULL, millis(), 10000);\r\n  });\r\n  server.addHandler(&amp;events);\r\n\r\n  server.begin();\r\n}\r\n\r\nvoid loop() {\r\n  if ((millis() - lastTime) &gt; gyroDelay) {\r\n    \/\/ Send Events to the Web Server with the Sensor Readings\r\n    events.send(getGyroReadings().c_str(),&quot;gyro_readings&quot;,millis());\r\n    lastTime = millis();\r\n  }\r\n  if ((millis() - lastTimeAcc) &gt; accelerometerDelay) {\r\n    \/\/ Send Events to the Web Server with the Sensor Readings\r\n    events.send(getAccReadings().c_str(),&quot;accelerometer_readings&quot;,millis());\r\n    lastTimeAcc = millis();\r\n  }\r\n  if ((millis() - lastTimeTemperature) &gt; temperatureDelay) {\r\n    \/\/ Send Events to the Web Server with the Sensor Readings\r\n    events.send(getTemperature().c_str(),&quot;temperature_reading&quot;,millis());\r\n    lastTimeTemperature = millis();\r\n  }\r\n}\r\n<\/code><\/pre>\n\t<p style=\"text-align:center\"><a class=\"rntwhite\" href=\"https:\/\/github.com\/RuiSantosdotme\/Random-Nerd-Tutorials\/raw\/master\/Projects\/ESP32\/ESP32_MPU_6050_Web_Server\/ESP32_MPU_6050_Web_Server.ino\" target=\"_blank\">View raw code<\/a><\/p>\n\n\n\n<p>Before uploading the code, make sure you insert your network credentials on the following variables:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Replace with your network credentials\nconst char* ssid = \"REPLACE_WITH_YOUR_SSID\";\nconst char* password = \"REPLACE_WITH_YOUR_PASSWORD\";<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">How the Code Works<\/h2>\n\n\n\n<p>Continue reading to learn how the code works or proceed to the next section.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Libraries<\/h3>\n\n\n\n<p>First, import all the required libraries for this project:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>#include &lt;Arduino.h&gt;\n#include &lt;WiFi.h&gt;\n#include &lt;AsyncTCP.h&gt;\n#include &lt;ESPAsyncWebServer.h&gt;\n#include &lt;Adafruit_MPU6050.h&gt;\n#include &lt;Adafruit_Sensor.h&gt;\n#include &lt;Arduino_JSON.h&gt;\n#include \"LittleFS.h\"<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Network Credentials<\/h3>\n\n\n\n<p>Insert your network credentials in the following variables:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Replace with your network credentials\nconst char* ssid = \"REPLACE_WITH_YOUR_SSID\";\nconst char* password = \"REPLACE_WITH_YOUR_PASSWORD\";<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">AsyncWebServer and AsyncEventSource<\/h3>\n\n\n\n<p>Create an <span class=\"rnthl rntliteral\">AsyncWebServer<\/span> object on port 80.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>AsyncWebServer server(80);<\/code><\/pre>\n\n\n\n<p>The following line creates a new event source on <span class=\"rnthl rntliteral\">\/events<\/span>.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>AsyncEventSource events(\"\/events\");<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Declaring Variables<\/h3>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">readings<\/span> variable is a JSON variable to hold the sensor readings in JSON format.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>JSONVar readings;<\/code><\/pre>\n\n\n\n<p>In this project, we&#8217;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.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Timer variables\nunsigned long lastTime = 0;\nunsigned long lastTimeTemperature = 0;\nunsigned long lastTimeAcc = 0;\nunsigned long gyroDelay = 10;\nunsigned long temperatureDelay = 1000;\nunsigned long accelerometerDelay = 200;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"mpu\">MPU-6050<\/h3>\n\n\n\n<p>Create an <span class=\"rnthl rntliteral\">Adafruit_MPU5060<\/span> object called <span class=\"rnthl rntliteral\">mpu<\/span>, create events for the sensor readings and variables to hold the readings.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Create a sensor object\nAdafruit_MPU6050 mpu;\n\nsensors_event_t a, g, temp;\n\nfloat gyroX, gyroY, gyroZ;\nfloat accX, accY, accZ;\nfloat temperature;<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">Gyroscope Offset<\/h4>\n\n\n\n<p>Adjust he gyroscope sensor offset on all axis.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/Gyroscope sensor deviation\nfloat gyroXerror = 0.07;\nfloat gyroYerror = 0.03;\nfloat gyroZerror = 0.01;<\/code><\/pre>\n\n\n\n<p>To get the sensor offset, go to <strong>File <\/strong>&gt; <strong>Examples <\/strong>&gt; <strong>Adafruit MPU6050<\/strong> &gt; <strong>basic_readings<\/strong>. With the sensor in a static position, check the gyroscope X, Y, and Z values. Then, add those values to the <span class=\"rnthl rntliteral\">gyroXerror<\/span>, <span class=\"rnthl rntliteral\">gyroYerror<\/span> and <span class=\"rnthl rntliteral\">gyroZerror<\/span> variables.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Initialize MPU-6050<\/h4>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">initMPU()<\/span> function initializes te MPU-6050 sensor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Init MPU6050\nvoid initMPU(){\n  if (!mpu.begin()) {\n    Serial.println(\"Failed to find MPU6050 chip\");\n    while (1) {\n      delay(10);\n    }\n  }\n  Serial.println(\"MPU6050 Found!\");\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Initialize LittleFS<\/h3>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">initLittleFS()<\/span> function initializes the ESP32 filesystem so that we&#8217;re able to get access to the files saved on LittleFS (<em>index.html<\/em>, <em>style.css<\/em> and <em>script.js<\/em>).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void initLittleFS() {\n  if (!LittleFS.begin()) {\n    Serial.println(\"An error has occurred while mounting LittleFS\");\n  }\n  Serial.println(\"LittleFSmounted successfully\");\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Initialize Wi-Fi<\/h3>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">initWiFi()<\/span> function connects the ESP32 to your local network.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Initialize WiFi\nvoid initWiFi() {\n  WiFi.mode(WIFI_STA);\n  WiFi.begin(ssid, password);\n  Serial.print(\"Connecting to WiFi ..\");\n  while (WiFi.status() != WL_CONNECTED) {\n    Serial.print('.');\n    delay(1000);\n  }\n  Serial.println(WiFi.localIP());\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Get Gyroscope Readings<\/h3>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">getGyroReadings()<\/span> function gets new gyroscope readings and returns the current angular orientation on the X, Y an Z axis as a JSON string.<\/p>\n\n\n\n<p>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. <\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>current angle (rad) = last angle (rad) + angular velocity (rad\/s) * time(s)<\/code><\/pre>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">gyroX_temp<\/span> variable temporarily holds the current gyroscope X value.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>float gyroX_temp = g.gyro.x;<\/code><\/pre>\n\n\n\n<p>To prevent small oscillations of the sensor (see <a href=\"#mpu\">Gyroscope Offset<\/a>), we first check if the values from the sensor are greater than the offset.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if(abs(gyroX_temp) &gt; gyroXerror) {<\/code><\/pre>\n\n\n\n<p>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&#8217;s angular position (<span class=\"rnthl rntliteral\">gyroX<\/span>).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>gyroX += gyroX_temp \/ 50.0;<\/code><\/pre>\n\n\n\n<p class=\"rntbox rntclblue\"><strong>Note: <\/strong> theoretically, we should multiply the current angular velocity by the elapsed time (10 milliseconds = 0.01 seconds (<span class=\"rnthl rntliteral\">gyroDelay<\/span>)) &#8211; 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.<\/p>\n\n\n\n<p>We follow a similar procedure to get the Y and Z values.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>float gyroX_temp = g.gyro.x;\nif(abs(gyroX_temp) &gt; gyroXerror) {\n  gyroX += gyroX_temp\/50.00;\n}\n\nfloat gyroY_temp = g.gyro.y;\nif(abs(gyroY_temp) &gt; gyroYerror) {\n  gyroY += gyroY_temp\/70.00;\n}\n\nfloat gyroZ_temp = g.gyro.z;\nif(abs(gyroZ_temp) &gt; gyroZerror) {\n  gyroZ += gyroZ_temp\/90.00;\n}<\/code><\/pre>\n\n\n\n<p>Finally, we concatenate the readings in a JSON variable (<span class=\"rnthl rntliteral\">readings<\/span>) and return a JSON string (<span class=\"rnthl rntliteral\">jsonString<\/span>).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>readings&#091;\"gyroX\"] = String(gyroX);\nreadings&#091;\"gyroY\"] = String(gyroY);\nreadings&#091;\"gyroZ\"] = String(gyroZ);\n\nString jsonString = JSON.stringify(readings);\nreturn jsonString;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Get Accelerometer Readings<\/h3>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">getAccReadings()<\/span> function returns the accelerometer readings.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>String getAccReadings(){\n  mpu.getEvent(&amp;a, &amp;g, &amp;temp);\n  \/\/ Get current acceleration values\n  accX = a.acceleration.x;\n  accY = a.acceleration.y;\n  accZ = a.acceleration.z;\n  readings&#091;\"accX\"] = String(accX);\n  readings&#091;\"accY\"] = String(accY);\n  readings&#091;\"accZ\"] = String(accZ);\n  String accString = JSON.stringify (readings);\n  return accString;\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Get Temperature Readings<\/h3>\n\n\n\n<p>The <span class=\"rnthl rntliteral\">getTemperature()<\/span> function returns the current temperature reading.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>String getTemperature(){\n  mpu.getEvent(&amp;a, &amp;g, &amp;temp);\n  temperature = temp.temperature;\n  return String(temperature);\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">setup()<\/h3>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">setup()<\/span>, initialize the Serial Monitor, Wi-Fi, LittleFS and the MPU sensor.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>void setup() {\n  Serial.begin(115200);\n  initWiFi();\n  initLittleFS();\n  initMPU();<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Handle Requests<\/h3>\n\n\n\n<p>When the ESP32 receives a request on the root URL, we want to send a response with the HTML file (<em>index.html<\/em>) content that is stored in LittleFS.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.on(\"\/\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  request-&gt;send(LittleFS, \"\/index.html\", \"text\/html\");\n});<\/code><\/pre>\n\n\n\n<p>The first argument of the <span class=\"rnthl rntliteral\">send()<\/span> 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).<\/p>\n\n\n\n<p>In your HTML file, you reference the <em>style.css<\/em> and <em>script.js<\/em> 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.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.serveStatic(\"\/\", LittleFS, \"\/\");<\/code><\/pre>\n\n\n\n<p>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 <span class=\"rnthl rntliteral\">\/reset<\/span> path. When that happens, we simply set the <span class=\"rnthl rntliteral\">gyroX<\/span>, <span class=\"rnthl rntliteral\">gyroY<\/span> and <span class=\"rnthl rntliteral\">gyroZ<\/span> variables to zero to restore the sensor initial position.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.on(\"\/reset\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  gyroX=0;\n  gyroY=0;\n  gyroZ=0;\n  request-&gt;send(200, \"text\/plain\", \"OK\");\n});<\/code><\/pre>\n\n\n\n<p>We send an &#8220;<span class=\"rnthl rntliteral\">OK<\/span>&#8221; response to indicate the request succeeded.<\/p>\n\n\n\n<p>We follow a similar procedure for the other requests (X, Y and Z buttons).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.on(\"\/resetX\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  gyroX=0;\n  request-&gt;send(200, \"text\/plain\", \"OK\");\n});\n\nserver.on(\"\/resetY\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  gyroY=0;\n  request-&gt;send(200, \"text\/plain\", \"OK\");\n});\n\nserver.on(\"\/resetZ\", HTTP_GET, &#091;](AsyncWebServerRequest *request){\n  gyroZ=0;\n  request-&gt;send(200, \"text\/plain\", \"OK\");\n});<\/code><\/pre>\n\n\n\n<p>The following lines setup the event source on the server.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>\/\/ Handle Web Server Events\nevents.onConnect(&#091;](AsyncEventSourceClient *client){\n  if(client-&gt;lastId()){\n    Serial.printf(\"Client reconnected! Last message ID that it got is: %u\\n\", client-&gt;lastId());\n  }\n  \/\/ send event with message \"hello!\", id current millis\n  \/\/ and set reconnect delay to 1 second\n  client-&gt;send(\"hello!\", NULL, millis(), 10000);\n});\nserver.addHandler(&amp;events);<\/code><\/pre>\n\n\n\n<p>Finally, start the server.<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>server.begin();<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">loop() &#8211; Send Events<\/h3>\n\n\n\n<p>In the <span class=\"rnthl rntliteral\">loop()<\/span>, we&#8217;ll send events to the client with the new sensor readings.<\/p>\n\n\n\n<p>The following lines send the gyroscope readings on the <span class=\"rnthl rntliteral\">gyro_readings<\/span> event every 10 milliseconds (<span class=\"rnthl rntliteral\">gyroDelay<\/span>).<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if ((millis() - lastTime) &gt; gyroDelay) {\n  \/\/ Send Events to the Web Server with the Sensor Readings\n  events.send(getGyroReadings().c_str(),\"gyro_readings\",millis());\n  lastTime = millis();\n}<\/code><\/pre>\n\n\n\n<p>Use the <span class=\"rnthl rntliteral\">send()<\/span> method on the <span class=\"rnthl rntliteral\">events<\/span> 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 <span class=\"rnthl rntliteral\">getGyroReadings()<\/span> function. The <span class=\"rnthl rntliteral\">send()<\/span> method accepts a variable of type char, so we need to use the <span class=\"rnthl rntliteral\">c_str()<\/span> method to convert the variable. The name of the events is <span class=\"rnthl rntliteral\">gyro_readings<\/span>.<\/p>\n\n\n\n<p>We follow a similar procedure for the accelerometer readings, but we use a different event (<span class=\"rnthl rntliteral\">accelerometer_readings<\/span>) and a different delay time (<span class=\"rnthl rntliteral\">accelerometerDelay<\/span>):<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if ((millis() - lastTimeAcc) &gt; accelerometerDelay) {\n  \/\/ Send Events to the Web Server with the Sensor Readings\n  events.send(getAccReadings().c_str(),\"accelerometer_readings\",millis());\n  lastTimeAcc = millis();\n}<\/code><\/pre>\n\n\n\n<p>And finally, for the temperature readings:<\/p>\n\n\n\n<pre class=\"wp-block-code language-c\"><code>if ((millis() - lastTimeTemperature) &gt; temperatureDelay) {\n  \/\/ Send Events to the Web Server with the Sensor Readings\n  events.send(getTemperature().c_str(),\"temperature_reading\",millis());\n  lastTimeTemperature = millis();\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Uploading Code and Files<\/h2>\n\n\n\n<p>After inserting your network credentials, save the code. Go to&nbsp;<strong>Sketch<\/strong>&nbsp;&gt;&nbsp;<strong>Show Sketch Folder<\/strong>, and create a folder called&nbsp;<strong>data<\/strong>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"351\" height=\"271\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/arduino-ide-show-sketch-folder.png?resize=351%2C271&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Arduino IDE Open Sketch Folder to create data folder\" class=\"wp-image-158892\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/arduino-ide-show-sketch-folder.png?w=351&amp;quality=100&amp;strip=all&amp;ssl=1 351w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/arduino-ide-show-sketch-folder.png?resize=300%2C232&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 351px) 100vw, 351px\" \/><\/figure><\/div>\n\n\n<p><strong>Inside that folder you should save the HTML, CSS and JavaScript files.<\/strong><\/p>\n\n\n\n<p>Then, upload the code to your ESP32 board. Make sure you have the right board and COM port selected. Also, make sure you\u2019ve added your networks credentials to the code.<\/p>\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"36\" height=\"39\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/05\/arduino-ide-2-upload-button.png?resize=36%2C39&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Arduino IDE 2 Upload Button\" class=\"wp-image-146269\"\/><\/figure><\/div>\n\n\n<p>After uploading the code, you need to upload the files. In the Arduino IDE, press [<strong>Ctrl<\/strong>] + [<strong>Shift<\/strong>] + [<strong>P<\/strong>] on Windows or [<strong>\u2318<\/strong>] + [<strong>Shift<\/strong>] + [<strong>P<\/strong>] on MacOS to open the command palette. Search for the&nbsp;<strong>Upload LittleFS to Pico\/ESP8266\/ESP32<\/strong>&nbsp;command and click on it.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"744\" height=\"401\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/upload-files-little-fs-esp32-arduino-ide.png?resize=744%2C401&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"Upload LittleFS to Pico ESP8266 ESP32 Arduino IDE\" class=\"wp-image-158893\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/upload-files-little-fs-esp32-arduino-ide.png?w=744&amp;quality=100&amp;strip=all&amp;ssl=1 744w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2024\/06\/upload-files-little-fs-esp32-arduino-ide.png?resize=300%2C162&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 744px) 100vw, 744px\" \/><\/figure><\/div>\n\n\n<p>If you don\u2019t have this option is because you didn\u2019t install the filesystem uploader plugin.<a href=\"https:\/\/randomnerdtutorials.com\/arduino-ide-2-install-esp32-littlefs\/\">&nbsp;Check this tutorial<\/a>.<\/p>\n\n\n\n<p class=\"rntbox rntcred\"><strong>Important:&nbsp;<\/strong>make sure the Serial Monitor is closed before uploading to the filesystem. Otherwise, the upload will fail.<\/p>\n\n\n\n<p>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.<\/p>\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"639\" height=\"445\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/MPU6050-Web-Server-Serial-Monitor-IP-Address.png?resize=639%2C445&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 IP Address printed in the Arduino IDE Serial Monitor\" class=\"wp-image-101099\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/MPU6050-Web-Server-Serial-Monitor-IP-Address.png?w=639&amp;quality=100&amp;strip=all&amp;ssl=1 639w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/MPU6050-Web-Server-Serial-Monitor-IP-Address.png?resize=300%2C209&amp;quality=100&amp;strip=all&amp;ssl=1 300w\" sizes=\"(max-width: 639px) 100vw, 639px\" \/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">Demonstration<\/h2>\n\n\n\n<p>Open your browser and type the ESP32 IP address. You should get access to the web page that shows the sensor readings.<\/p>\n\n\n\n<p>Move the sensor and see the readings changing as well as the 3D object on the browser.<\/p>\n\n\n<div class=\"wp-block-image is-style-default\">\n<figure class=\"aligncenter size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1012\" height=\"738\" src=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/ESP-MPU6050-Web-Server-Arduino.png?resize=1012%2C738&#038;quality=100&#038;strip=all&#038;ssl=1\" alt=\"ESP32 ESP8266 NodeMCU Web Server Demonstration\" class=\"wp-image-101097\" srcset=\"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/ESP-MPU6050-Web-Server-Arduino.png?w=1012&amp;quality=100&amp;strip=all&amp;ssl=1 1012w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/ESP-MPU6050-Web-Server-Arduino.png?resize=300%2C219&amp;quality=100&amp;strip=all&amp;ssl=1 300w, https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2020\/12\/ESP-MPU6050-Web-Server-Arduino.png?resize=768%2C560&amp;quality=100&amp;strip=all&amp;ssl=1 768w\" sizes=\"(max-width: 1012px) 100vw, 1012px\" \/><\/figure><\/div>\n\n\n<p><strong>Note: <\/strong>the sensor drifts a bit on the X axis, despite some adjustments in the code. Many of our readers commented that that&#8217;s normal for this kind of MCUs. To reduce the drifting, some readers suggested using a complementary filter or a kalman filter. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>The MPU-6050 is an accelerometer, gyroscope and temperature sensor on a single module. In this tutorial you&#8217;ve learned how to build a web server with the ESP32 to display sensor readings from the MPU-6050 sensor. We used <a href=\"https:\/\/randomnerdtutorials.com\/esp32-web-server-sent-events-sse\/\">server-sent events<\/a> to send the readings to the client.<\/p>\n\n\n\n<p>Using the <a href=\"https:\/\/threejs.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">three.js<\/a> JavaScript library we&#8217;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.<\/p>\n\n\n\n<p>We hope you&#8217;ve found this tutorial useful.<\/p>\n\n\n\n<p>Learn more about the ESP32 with our resources:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/randomnerdtutorials.com\/build-web-servers-esp32-esp8266-ebook\/\">Build Web Servers with the ESP32 and ESP8266 eBook<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/learn-esp32-with-arduino-ide\/\">Learn ESP32 with Arduino IDE<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/randomnerdtutorials.com\/projects-esp32\/\">More ESP32 Projects and Guides\u2026<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In this project we&#8217;ll build a web server with the ESP32 to display readings from the MPU-6050 accelerometer and gyroscope sensor. We&#8217;ll also create a 3D representation of the sensor &#8230; <\/p>\n<p class=\"read-more-container\"><a title=\"ESP32 Web Server with MPU-6050 Accelerometer and Gyroscope (3D object representation)\" class=\"read-more button\" href=\"https:\/\/randomnerdtutorials.com\/esp32-mpu-6050-web-server\/#more-101046\" aria-label=\"Read more about ESP32 Web Server with MPU-6050 Accelerometer and Gyroscope (3D object representation)\">CONTINUE READING \u00bb<\/a><\/p>\n","protected":false},"author":5,"featured_media":101451,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[276,281,277,299,264],"tags":[],"class_list":["post-101046","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-esp32","category-esp32-project","category-esp32-arduino-ide","category-0-esp32","category-project"],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/randomnerdtutorials.com\/wp-content\/uploads\/2021\/01\/ESP32-Web-Server-MPU-6050-Accelerometer-Gyroscope-3D-object-Arduino.jpg?fit=1280%2C720&quality=100&strip=all&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/101046","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/comments?post=101046"}],"version-history":[{"count":4,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/101046\/revisions"}],"predecessor-version":[{"id":167943,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/posts\/101046\/revisions\/167943"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media\/101451"}],"wp:attachment":[{"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/media?parent=101046"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/categories?post=101046"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/randomnerdtutorials.com\/wp-json\/wp\/v2\/tags?post=101046"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}