Arduino Nano 33 BLE Rev2 - Air Mouse
01 Overview
The BLE Air-Mouse Motion Controller (Nano 33 BLE Rev2) is a ireless Human Interface Device (HID) developed using the Arduino Nano 33 BLE Rev2.The boards utilizes the board build in gyroscope and accelerometer to detect hand movement and orientation changes, translating them into cursor movements on a paired computer through Bluetooth Low Energy (BLE).
The project demonstrates the integration of sensor fusion techniques, motion tracking, and BLE Human Interface Device (HID) communication, providing an intuitive and portable alternative input device suitable for presentations, accessibility applications, and interactive systems.
Project Use Case
Presentations and accessibility applications where users need a wireless, intuitive input device for controlling cursor movements and interacting with digital content.
02 Hardware and Software Components
Gather everything below before you start. This is your checklist to prepare for this device.
Hardware Components
| Component | Description |
|---|---|
| Arduino Nano 33 BLE Rev2 | A compact microcontroller board based on the nRF52840 with built-in Bluetooth Low Energy (BLE), accelerometer, gyroscope, and magnetometer. It serves as the main controller of the project. |
| Tactile Push Button Switch with Round Cap | A switch used to provide user input, such as activating gesture mode to conduct actions. |
| Host PC or phone | A computer or smartphone used to receive BLE signals from the Arduino, program the board, or test the device. |
| Battery Holder/case plastic | A plastic enclosure designed to securely hold lithium-ion batteries and provide convenient electrical connections. |
| Li-Ion Battery | A rechargeable battery that supplies portable power to the system. Common examples include 18650 cells. |
| Micro-USB Cable | Used for programming the Arduino, serial communication, and powering the board during development. |
| DC-DC Step Down Power Supply Module | Also called a buck converter, it reduces a higher input voltage to a lower regulated voltage suitable for powering electronic components. |
Software Tools
| Software | Version / Details |
|---|---|
| Arduino IDE | 2.3.10 |
| File | Description |
|---|---|
| BLE_Airmouse.ino | The main Arduino sketch for the BLE Air Mouse project. |
03 Application Discussion
The Arduino Nano 33 BLE Rev2 serves as the central processing unit of the system. It features an nRF52840 microcontroller with integrated Bluetooth Low Energy (BLE) capability, making it suitable for wireless HID applications.
Inertial Measurement Unit
The onboard Inertial Measurement Unit contains an accelerometer, gyroscope and magnetometer to measure the device's acceleration, rotation, and direction.
LM2596 DC-DC Step Down Power Supply Module
The LM2596 is a versatile DC-DC step-down converter that reduces a higher input voltage to a lower regulated voltage, suitable for powering electronic components.
04 Hardware Setup
Wire the components to the board using the tables below.
Air Mouse Board
Air Mouse Wiring
Arduino GPIO
| Pin / Signal | Connects To |
|---|---|
| D2 | Tactile Push Button Switch with Round Cap → GND |
| D3 | Resistor → LED → GND |
| D4 | Resistor → LED → GND |
Components Pin
| Component Pin | Board Pin | Description |
|---|---|---|
| LM2596 OUT+ | VIN | Input voltage to the power supply module |
| LM2596 OUT- | GND | Ground connection for the power supply module |
| Tactile Push Button Switch with Round Cap → GND | D2 | Conduct an input signal |
| Resistor → LED → GND | D3 | Provide current limiting for the LED and act as a indicator for Bluetooth connection |
| Resistor → LED → GND | D4 | Provide current limiting for the LED and act as a indicator for the status of the Gesture Mode |
Assembly Instructions
- Install Arduino Nano 33 BLE Rev2.
- Add Tactile Push Button Switch and connect to the digital pin.
- Use micro-usb cable to upload firmware.
- Connect the Resistor & LED to the digital pins of the Arduino.
- Attach LM2596 with its power supply and connect the OUT+ to the VIN and OUT- to GND.
- Pair device.
05 Software Setup
Follow these steps in order. Do not skip any step.
Step 1 — Set Up Development Environment
- Run Arduino IDE
- Install the Arduino Mbed OS Nano Boards package
- Install the required libraries via the Library Manager
Step 2 — Board Settings
Use exactly these settings in your IDE. Wrong settings will cause upload failures.
| Setting | Value |
|---|---|
| Board | Arduino Mbed OS Nano Boards: Arduino Nano 33 BLE |
| Port | Arduino Nano 33 BLE Rev2 |
Step 3 — Install Libraries
Install the following libraries via the Library Manager:
- Arduino_BMI270_BMM150 v1.2.3
- Mbed BLE Mouse v1.3.0
- Madgwick 1.2.0
Step 4 — Upload the Code
- Connect to the Arduino via micro-USB cable.
- Double press the reset button, if the orange LED is blinking it is in bootloader mode.
- Copy the code and paste to the Arduino IDE, Make sure it is in the correct port.
- Click Upload.
06 Code
Copy the code and prepare to upload to the device.
#include <Arduino_BMI270_BMM150.h>
#include <HIDMouse.h>
#include <MadgwickAHRS.h>
#define MOUSE_BUTTON_LEFT 1
#define MOUSE_BUTTON_RIGHT 2
#define MOUSE_BUTTON_MIDDLE 4
HIDMouse bleMouse;
Madgwick filter;
const float DEADZONE = 3.0;
const float SENSITIVITY = 0.20;
const int MAX_SPEED = 35;
float
gx, gy, gz,
ax, ay, az,
pitch, roll, yaw;
float gx_offset=0;
float gy_offset=0;
int Movx = 0;
int Movy = 0;
const float SENSOR_RATE = 104.0;
float centerPitch;
float centerRoll;
const float NOD_ANGLE = 18.0;
const float NOD_RETURN_ANGLE = 7.0;
const float NOD_DIRECTION = -1.0;
const float ROLL_ANGLE = 18.0;
const float ROLL_RETURN_ANGLE = 7.0;
const float ROLL_DIRECTION = 1.0;
const unsigned long GESTURE_COOLDOWN = 600;
enum GestureState
{
GESTURE_NONE,
CHIN_DOWN,
CHIN_UP,
ROLL_RIGHT,
ROLL_LEFT
};
GestureState activeGesture = GESTURE_NONE;
bool nodClickLocked = false;
bool gestureStarted = false;
bool leftlock = false;
unsigned long lastNodTime = 0;
float angleDifference(float angle, float reference)
{
float difference = angle - reference;
while (difference > 180.0) difference -= 360.0;
while (difference < -180.0) difference += 360.0;
return difference;
}
void setup()
{
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
pinMode(2, INPUT_PULLUP);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
if (!IMU.begin())
{
Serial.println("IMU Failed");
while (1);
}
filter.begin(SENSOR_RATE);
bleMouse.setDeviceName("Air Mouse");
bleMouse.begin();
for(int i=0;i<500;i++)
{
IMU.readGyroscope(gx,gy,gz);
gx_offset+=gx;
gy_offset+=gy;
delay(2);
}
gx_offset/=500;
gy_offset/=500;
}
void detectGesture()
{
digitalWrite(4, HIGH);
if (nodClickLocked)
return;
float pitchChange = angleDifference(pitch, centerPitch) * NOD_DIRECTION;
float rollChange = angleDifference(roll, centerRoll) * ROLL_DIRECTION;
if (activeGesture == GESTURE_NONE)
{
if (millis() - lastNodTime < GESTURE_COOLDOWN)
return;
if (pitchChange > NOD_ANGLE)
{
activeGesture = CHIN_DOWN;
Serial.println("Chin down started");
}
else if (pitchChange < -NOD_ANGLE)
{
activeGesture = CHIN_UP;
Serial.println("Chin up started");
}
else if (rollChange > ROLL_ANGLE)
{
activeGesture = ROLL_RIGHT;
Serial.println("Roll right started");
}
else if (rollChange < -ROLL_ANGLE)
{
activeGesture = ROLL_LEFT;
Serial.println("Roll left started");
}
return;
}
uint8_t mouseButton = 0;
bool gestureComplete = false;
if (activeGesture == CHIN_DOWN &&
fabsf(pitchChange) < NOD_RETURN_ANGLE)
{
mouseButton = MOUSE_BUTTON_LEFT;
gestureComplete = true;
Serial.println("Chin down: Left Click");
}
else if (activeGesture == CHIN_UP && fabsf(pitchChange) < NOD_RETURN_ANGLE)
{
mouseButton = MOUSE_BUTTON_RIGHT;
gestureComplete = true;
Serial.println("Chin up: Right Click");
}
else if (activeGesture == ROLL_RIGHT && fabsf(rollChange) < ROLL_RETURN_ANGLE)
{
mouseButton = MOUSE_BUTTON_MIDDLE;
gestureComplete = true;
Serial.println("Roll right: Middle");
}
else if (activeGesture == ROLL_LEFT && fabsf(rollChange) < ROLL_RETURN_ANGLE)
{
if (!leftlock)
{
bleMouse.press(MOUSE_BUTTON_LEFT);
leftlock = true;
Serial.println("Left Locked");
}
else
{
bleMouse.release(MOUSE_BUTTON_LEFT);
leftlock = false;
Serial.println("Left Unlocked");
}
activeGesture = GESTURE_NONE;
nodClickLocked = true;
lastNodTime = millis();
}
if (gestureComplete)
{
bleMouse.press(mouseButton);
delay(20);
bleMouse.release(mouseButton);
activeGesture = GESTURE_NONE;
nodClickLocked = true;
lastNodTime = millis();
}
}
void loop()
{
if (!bleMouse.isConnected())
{
digitalWrite(LED_BUILTIN, LOW);
digitalWrite(3, LOW);
bleMouse.release(MOUSE_BUTTON_LEFT);
leftlock = false;
gestureStarted = false;
activeGesture = GESTURE_NONE;
nodClickLocked = false;
delay(5);
return;
}
else
{
digitalWrite(3, HIGH);
}
digitalWrite(LED_BUILTIN, HIGH);
IMU.readGyroscope(gx, gy, gz);
IMU.readAcceleration(ax, ay, az);
filter.updateIMU(gx, gy, gz, ax, ay, az);
pitch = filter.getPitch();
roll = filter.getRoll();
yaw = filter.getYaw();
Movx = 0;
Movy = 0;
if (digitalRead(2) == LOW)
{
if (!gestureStarted)
{
centerPitch = pitch;
centerRoll = roll;
activeGesture = GESTURE_NONE;
nodClickLocked = false;
gestureStarted = true;
Serial.println("Gesture mode started");
}
detectGesture();
}
else
{
digitalWrite(4, LOW);
gestureStarted = false;
activeGesture = GESTURE_NONE;
nodClickLocked = false;
gx -= gx_offset;
gy -= gy_offset;
if (fabsf(gx) > DEADZONE)
{
Movx = gx * SENSITIVITY;
}
if (fabsf(gy) > DEADZONE)
{
Movy = gy * SENSITIVITY;
}
Movx = constrain(Movx, -MAX_SPEED, MAX_SPEED);
Movy = constrain(Movy, -MAX_SPEED, MAX_SPEED);
}
if (Movx != 0 || Movy != 0)
{
bleMouse.move(Movx, -Movy);
}
delay(5);
}
07 Code Breakdown
Here is what each part of the code does. Read this after uploading.
Libraries
| Library | Purpose |
|---|---|
| Arduino_BMI270_BMM150 | The Arduino_BMI270_BMM150 library is used to access the built-in gyroscope, accelerometer, and magnetometer of the Arduino Nano 33 BLE Rev2 for motion sensing, orientation tracking, and gesture recognition. |
| Mbed BLE Mouse | The Mbed BLE Mouse library is used to simulate mouse movements and clicks over a BLE connection. |
| Madgwick | The Madgwick library is used for sensor fusion to estimate orientation from IMU data. |
Key Functions
angleDifference
Used to calculate the difference between new angles and center angles.
setup
Checks the hardware and initializes the offset of the gyroscope, for smoother movement.
detectGesture
Detected gestures based on the orientation data from the IMU, and conduct actions accordingly.
loop
The main execution loop that continuously reads sensor data and updates the system state.
General Program Workflow
- default state: detecting Bluetooth connection
- Connection occurred LED turns on
- Read gyroscope data then sent to cursor movement
- Hold Tactile button switch to activate gesture mode
- Detect orientation direction and lock
- Detect orientation return to previous state
- Use the initial direction detection to conduct actions accordingly
- No more orientation detection data until gesture mode reactivated
- Release Tactile button switch to exit gesture mode
- Return to cursor movement
08 Testing and Calibration
After uploading, verify each of the following to confirm the system is working correctly.
Cursor movement drifting
It tends to use the earliest detected gyroscope data, causing the cursor to drift over time. To fix go to your Bluetooth remove the Air Mouse, lay down the device on a flat surface, then press reset button on the Arduino then reconnect.
Bluetooth Connected but no response
The library Mbed BLE Mouse has issues on it's Bluetooth service, simply go to your Bluetooth then select remove device on the Air Mouse then reconnect.
09 System Demonstration
The images below show the working system. Use these to verify your output matches what is expected.
Air Mouse Code
If you connect the Arduino to your computer via micro-usb please set the following.
- Open serial monitor and set baud rate to 115200
- Connect the device to the Bluetooth either the computer or other external device
- Press and hold the push button, expected serial output: "Gesture mode started"
Once you have received it expect of the following actions depending on whether your roll or pitch and they can only appear once or until the push button is release:
- Nod down: "Chin down started"
- Nod up: "Chin up started"
- Roll right: "Roll right started"
- Roll left: "Roll left started"
Only one will appear the other text will not on which you will need to return the original orientation to complete its action and conduct the following depending on its initial orientation expect the following serial output:
-
Chin down: Left Click
-
Chin up: Right Click
-
Roll right: Middle (It is a toggle)
- The left roll will conduct an act similar on holding the left mouse button:
- Left Locked
-
Left Unlocked
Air Mouse Device
Video Demonstration
10 Conclusion
The BLE Air-Mouse Motion Controller project successfully demonstrates the integration of motion sensing, gesture recognition, and Bluetooth Low Energy (BLE) communication using the Arduino Nano 33 BLE Rev2. The system provides an intuitive and portable alternative input device suitable for various applications, including presentations and accessibility solutions.
Possible Improvements and Future Enhancements
- Have a BLE HID Joystick Service due to the lack of its library.
- Utilize the Magnetometer for the inclusion of direction base on north, east, west and, south.
- Improve or create a better BLE service implementation.
11 References
- https://github.com/
- https://www.arduino.cc/
- https://openai.com/index/previewing-gpt-5-6-sol/
12 Project Authors
- Russel Jon C. Recapente
- Cristel Jade B. Oribe
