This project is a wireless Air Mouse built using an ESP32 and MPU6050 sensor. It allows you to control your computer cursor by moving your hand in the air. The ESP32 reads motion data from the MPU6050 and sends it via Bluetooth as a mouse device.
The MPU6050 measures rotational movement (gyro data). The ESP32 processes this data, filters noise (deadzone + smoothing), and converts it into mouse movement. Bluetooth HID is used to send cursor movement and clicks to your computer.
#include <Arduino.h>
#include <Wire.h>
#include <BleMouse.h>
BleMouse bleMouse;
// -------- MPU6050 --------
const uint8_t IMUAddress = 0x68;
uint8_t i2cData[14];
int16_t gyroX, gyroZ;
// -------- Settings --------
int Sensitivity = 500;
float speed = 2.5;
int delayi = 5;
int deadzone = 4;
// -------- Offset --------
float offsetX = 0;
float offsetZ = 0;
// -------- Buttons --------
#define BTN_LEFT 18
#define BTN_RIGHT 19
bool lastLeft = HIGH;
bool lastRight = HIGH;
// -------- I2C Write --------
uint8_t i2cWrite(uint8_t reg, uint8_t* data, uint8_t len, bool stop) {
Wire.beginTransmission(IMUAddress);
Wire.write(reg);
Wire.write(data, len);
return Wire.endTransmission(stop);
}
// -------- I2C Write Single --------
uint8_t i2cWrite2(uint8_t reg, uint8_t data, bool stop) {
return i2cWrite(reg, &data, 1, stop);
}
// -------- I2C Read --------
uint8_t i2cRead(uint8_t reg, uint8_t* data, uint8_t nbytes) {
Wire.beginTransmission(IMUAddress);
Wire.write(reg);
if (Wire.endTransmission(false))
return 1;
Wire.requestFrom(IMUAddress, nbytes, (uint8_t)true);
for (uint8_t i = 0; i < nbytes; i++) {
if (Wire.available())
data[i] = Wire.read();
else
return 2;
}
return 0;
}
// -------- CALIBRATION --------
void calibrateGyro() {
long sumX = 0;
long sumZ = 0;
Serial.println("Calibrating... DON'T MOVE");
for (int i = 0; i < 500; i++) {
i2cRead(0x3B, i2cData, 14);
int16_t gx = ((i2cData[8] << 8) | i2cData[9]);
int16_t gz = ((i2cData[12] << 8) | i2cData[13]);
sumX += gx;
sumZ += gz;
delay(5);
}
offsetX = sumX / 500.0;
offsetZ = sumZ / 500.0;
Serial.println("Calibration done!");
}
// -------- SETUP --------
void setup() {
Serial.begin(115200);
Wire.begin();
pinMode(BTN_LEFT, INPUT_PULLUP);
pinMode(BTN_RIGHT, INPUT_PULLUP);
uint8_t data[4];
data[0] = 7;
data[1] = 0x00;
data[3] = 0x00;
while (i2cWrite(0x19, data, 4, false));
while (i2cWrite2(0x6B, 0x01, true));
while (i2cRead(0x75, data, 1));
delay(200);
calibrateGyro();
bleMouse.begin();
}
// -------- LOOP --------
void loop() {
while (i2cRead(0x3B, i2cData, 14));
gyroX = ((i2cData[8] << 8) | i2cData[9]) - offsetX;
gyroZ = ((i2cData[12] << 8) | i2cData[13]) - offsetZ;
gyroX = (gyroX / Sensitivity / 1.1) * speed;
gyroZ = (gyroZ / Sensitivity) * -speed;
if (abs(gyroX) < deadzone) gyroX = 0;
if (abs(gyroZ) < deadzone) gyroZ = 0;
static float smoothX = 0;
static float smoothZ = 0;
smoothX = smoothX * 0.8 + gyroX * 0.2;
smoothZ = smoothZ * 0.8 + gyroZ * 0.2;
gyroX = smoothX;
gyroZ = smoothZ;
if (bleMouse.isConnected()) {
bleMouse.move(gyroZ, -gyroX);
bool leftState = digitalRead(BTN_LEFT);
if (leftState == LOW && lastLeft == HIGH) {
bleMouse.press(MOUSE_LEFT);
}
if (leftState == HIGH && lastLeft == LOW) {
bleMouse.release(MOUSE_LEFT);
}
lastLeft = leftState;
bool rightState = digitalRead(BTN_RIGHT);
if (rightState == LOW && lastRight == HIGH) {
bleMouse.press(MOUSE_RIGHT);
}
if (rightState == HIGH && lastRight == LOW) {
bleMouse.release(MOUSE_RIGHT);
}
lastRight = rightState;
}
delay(delayi);
}
[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps =
t-vk/ESP32 BLE Mouse
#include <Wire.h>
#include <BleMouse.h>
BleMouse bleMouse;
// -------- MPU6050 --------
const uint8_t IMUAddress = 0x68;
uint8_t i2cData[14];
int16_t gyroX, gyroZ;
// -------- Settings --------
int Sensitivity = 500;
float speed = 2.5;
int delayi = 5;
int deadzone = 4;
// -------- Offset (drift fix) --------
float offsetX = 0;
float offsetZ = 0;
// -------- Buttons --------
#define BTN_LEFT 18
#define BTN_RIGHT 19
bool lastLeft = HIGH;
bool lastRight = HIGH;
// -------- I2C Write --------
uint8_t i2cWrite(uint8_t reg, uint8_t* data, uint8_t len, bool stop) {
Wire.beginTransmission(IMUAddress);
Wire.write(reg);
Wire.write(data, len);
return Wire.endTransmission(stop);
}
// -------- I2C Write Single --------
uint8_t i2cWrite2(uint8_t reg, uint8_t data, bool stop) {
return i2cWrite(reg, &data, 1, stop);
}
// -------- I2C Read --------
uint8_t i2cRead(uint8_t reg, uint8_t* data, uint8_t nbytes) {
Wire.beginTransmission(IMUAddress);
Wire.write(reg);
if (Wire.endTransmission(false))
return 1;
Wire.requestFrom(IMUAddress, nbytes, (uint8_t)true);
for (uint8_t i = 0; i < nbytes; i++) {
if (Wire.available())
data[i] = Wire.read();
else
return 2;
}
return 0;
}
// -------- CALIBRATION --------
void calibrateGyro() {
long sumX = 0;
long sumZ = 0;
Serial.println("Calibrating... DON'T MOVE");
for (int i = 0; i < 500; i++) {
i2cRead(0x3B, i2cData, 14);
int16_t gx = ((i2cData[8] << 8) | i2cData[9]);
int16_t gz = ((i2cData[12] << 8) | i2cData[13]);
sumX += gx;
sumZ += gz;
delay(5);
}
offsetX = sumX / 500.0;
offsetZ = sumZ / 500.0;
Serial.println("Calibration done!");
}
// -------- SETUP --------
void setup() {
Serial.begin(115200);
Wire.begin();
pinMode(BTN_LEFT, INPUT_PULLUP);
pinMode(BTN_RIGHT, INPUT_PULLUP);
uint8_t data[4];
data[0] = 7;
data[1] = 0x00;
data[3] = 0x00;
while (i2cWrite(0x19, data, 4, false));
while (i2cWrite2(0x6B, 0x01, true));
while (i2cRead(0x75, data, 1));
delay(200);
calibrateGyro();
bleMouse.begin();
}
// -------- LOOP --------
void loop() {
while (i2cRead(0x3B, i2cData, 14));
gyroX = ((i2cData[8] << 8) | i2cData[9]) - offsetX;
gyroZ = ((i2cData[12] << 8) | i2cData[13]) - offsetZ;
gyroX = (gyroX / Sensitivity / 1.1) * speed;
gyroZ = (gyroZ / Sensitivity) * -speed;
if (abs(gyroX) < deadzone) gyroX = 0;
if (abs(gyroZ) < deadzone) gyroZ = 0;
static float smoothX = 0;
static float smoothZ = 0;
smoothX = smoothX * 0.8 + gyroX * 0.2;
smoothZ = smoothZ * 0.8 + gyroZ * 0.2;
gyroX = smoothX;
gyroZ = smoothZ;
if (bleMouse.isConnected()) {
// Move mouse
bleMouse.move(gyroZ, -gyroX);
// LEFT CLICK
bool leftState = digitalRead(BTN_LEFT);
if (leftState == LOW && lastLeft == HIGH) {
bleMouse.press(MOUSE_LEFT);
}
if (leftState == HIGH && lastLeft == LOW) {
bleMouse.release(MOUSE_LEFT);
}
lastLeft = leftState;
// RIGHT CLICK
bool rightState = digitalRead(BTN_RIGHT);
if (rightState == LOW && lastRight == HIGH) {
bleMouse.press(MOUSE_RIGHT);
}
if (rightState == HIGH && lastRight == LOW) {
bleMouse.release(MOUSE_RIGHT);
}
lastRight = rightState;
}
delay(delayi);
}