/* * MECRO KIT - PROYECTO 18: AIR MOUSE PRO + OLED HUD
* Versión: High-Fidelity UI
* Autor: MECRO Engineering Dept.
*/
// --- CONFIGURACIÓN DE ENTORNO ---
// Comenta esta línea para subir al ESP32 Real
#define MODO_WOKWI
#include <Wire.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// --- CONFIGURACIÓN OLED ---
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// --- GESTIÓN DE LIBRERÍAS (MOCK VS REAL) ---
#ifdef MODO_WOKWI
#define MOUSE_LEFT 1
#define MOUSE_RIGHT 2
class BleMouse {
public:
BleMouse(String name, String manufacturer, int battery) {}
void begin() { Serial.println("--- SIMULACIÓN BT: LISTO ---"); }
bool isConnected() { return true; }
void move(int x, int y) { /* Solo simula movimiento */ }
void press(uint8_t b) { Serial.print("CLICK: "); Serial.println(b); }
void release(uint8_t b) { Serial.print("RELEASE: "); Serial.println(b); }
};
#else
#include <BleMouse.h>
#endif
// --- INSTANCIAS ---
BleMouse bleMouse("MECRO AirMouse", "MECRO Inc", 100);
Adafruit_MPU6050 mpu;
// --- PINOUT ---
#define PIN_LEFT_CLICK 15
#define PIN_RIGHT_CLICK 4
// --- CONFIGURACIÓN DE CONTROL ---
const float SENSITIVITY = 15.0;
const float DEADZONE = 0.1;
// Variables Globales para UI
int global_moveX = 0;
int global_moveY = 0;
bool isLeftPressed = false;
bool isRightPressed = false;
unsigned long last_screen_update = 0;
void setup() {
Serial.begin(115200);
// Pines
pinMode(PIN_LEFT_CLICK, INPUT_PULLUP);
pinMode(PIN_RIGHT_CLICK, INPUT_PULLUP);
// 1. INICIAR OLED (Vital para estatus)
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("Fallo OLED"));
for(;;);
}
display.clearDisplay();
display.setTextColor(WHITE);
// Intro MECRO (Branding es importante, Gerard)
display.setTextSize(2);
display.setCursor(10,10);
display.println("MECRO");
display.setTextSize(1);
display.setCursor(10,35);
display.println("Systems Init...");
display.display();
delay(1000);
// 2. INICIAR SENSOR
if (!mpu.begin()) {
display.clearDisplay();
display.setCursor(0,0);
display.println("ERROR: IMU 6050");
display.display();
while (1) { delay(10); }
}
mpu.setGyroRange(MPU6050_RANGE_500_DEG);
mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
// 3. INICIAR BT
bleMouse.begin();
display.clearDisplay();
}
void loop() {
if (bleMouse.isConnected()) {
// --- LECTURA SENSOR ---
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
float gyroX = g.gyro.z;
float gyroY = g.gyro.y;
if (abs(gyroX) < DEADZONE) gyroX = 0;
if (abs(gyroY) < DEADZONE) gyroY = 0;
global_moveX = -(gyroX * SENSITIVITY);
global_moveY = -(gyroY * SENSITIVITY);
if (global_moveX != 0 || global_moveY != 0) {
bleMouse.move(global_moveX, global_moveY);
}
// --- GESTIÓN BOTONES & ESTADO UI ---
// Izquierdo
if (digitalRead(PIN_LEFT_CLICK) == LOW) {
if (!isLeftPressed) {
bleMouse.press(MOUSE_LEFT);
isLeftPressed = true;
}
} else {
if (isLeftPressed) {
bleMouse.release(MOUSE_LEFT);
isLeftPressed = false;
}
}
// Derecho
if (digitalRead(PIN_RIGHT_CLICK) == LOW) {
if (!isRightPressed) {
bleMouse.press(MOUSE_RIGHT);
isRightPressed = true;
}
} else {
if (isRightPressed) {
bleMouse.release(MOUSE_RIGHT);
isRightPressed = false;
}
}
}
// --- ACTUALIZACIÓN DE PANTALLA (No Bloqueante) ---
// Solo dibujamos a 10-15 FPS para no ralentizar el mouse
if (millis() - last_screen_update > 80) {
updateHUD();
last_screen_update = millis();
}
}
void updateHUD() {
display.clearDisplay();
// 1. BARRA SUPERIOR
display.setTextSize(1);
display.setCursor(0,0);
display.print("MECRO AIR");
// Icono de estado BT (Simulado)
display.setCursor(90, 0);
if(bleMouse.isConnected()) display.print("[BT:ON]");
else display.print("[BT:--]");
// 2. DATOS DE TELEMETRÍA (Centro)
display.setCursor(0, 15);
display.print("DX: "); display.print(global_moveX);
display.setCursor(64, 15);
display.print("DY: "); display.print(global_moveY);
// Visualizador de Ejes (Barras dinámicas)
// Dibujamos una línea base
display.drawLine(64, 35, 64, 55, WHITE); // Eje Vertical
display.drawLine(40, 45, 88, 45, WHITE); // Eje Horizontal
// Punto móvil que representa tu mano
// Mapeamos el movimiento a píxeles para el HUD
int xPos = constrain(64 + global_moveX, 40, 88);
int yPos = constrain(45 + global_moveY, 35, 55);
display.fillCircle(xPos, yPos, 2, WHITE);
// 3. ESTADO DE BOTONES (Pie de página)
// Dibujar cajas para Click Izq (L) y Der (R)
// Botón Izquierdo
display.drawRect(10, 50, 20, 12, WHITE);
if(isLeftPressed) {
display.fillRect(10, 50, 20, 12, WHITE); // Relleno si presionado
display.setTextColor(BLACK, WHITE); // Texto invertido
} else {
display.setTextColor(WHITE);
}
display.setCursor(14, 52); display.print("L");
// Botón Derecho
display.setTextColor(WHITE);
display.drawRect(98, 50, 20, 12, WHITE);
if(isRightPressed) {
display.fillRect(98, 50, 20, 12, WHITE);
display.setTextColor(BLACK, WHITE);
}
display.setCursor(102, 52); display.print("R");
display.setTextColor(WHITE); // Reset color
display.display();
}