Controla un Rover Arduino con Firmata y Mando Xbox One - Guía Completa
Componentes y materiales
| Mando Xbox One + Adaptador inalámbrico para Windows | 1 |
| Módulo Bluetooth (BlueSMiRF Silver, HC-05 u HC-06) | 1 |
| Arduino UNO | 1 |
| Kit rover: chasis, 2 ruedas, 2 motores DC, placa controladora L298N Dual H-Bridge | 1 |
| Porta-pilas 9V (para Arduino) | 1 |
| Porta-pilas 6xAA (para motores) | 1 |
| Portátil con Windows 10, Bluetooth, Visual Studio 2015+ | 1 |
Aplicaciones y servicios
- Windows 10 IoT Core (app intermedia entre Firmata y mando Xbox)
Sobre el proyecto
Hace unos meses compré un kit rover barato controlado por Arduino Uno. Muy completo: chasis, ruedas, motores, UNO R3, L298N, sensor ultrasónico, servo, shield de sensores. Una ganga.
El kit está pensado para operación autónoma, pero mi idea era controlarlo manualmente con mando Xbox One usando protocolo Firmata. ¡Y funciona genial!
Montaje del kit
El manual venía en chino pero es lógico. Solo hay una base, así que puse componentes arriba y abajo (controlador de motores abajo). Dos porta-pilas separados: uno 9V para Arduino, otro 6xAA para motores (necesario para que arranquen).
Conexión motores - L298N
El L298N controla velocidad y dirección de dos motores DC con señales del Arduino.
Pinout L298N:
- Motor 1: + y -
- Alimentación motores: 9V (porta-pilas 6xAA)
- GND común: une GND porta-pilas 6xAA + GND Arduino
- 5V out: no usado (Arduino tiene su propia alimentación)
- ENA → Arduino D10 (PWM) - velocidad motor 1
- IN1 → Arduino D9 - dirección motor 1
- IN2 → Arduino D8 - dirección motor 1
- IN3 → Arduino D7 - dirección motor 2
- IN4 → Arduino D6 - dirección motor 2
- ENB → Arduino D5 (PWM) - velocidad motor 2
- Motor 2: + y -
- Jumper 5V/12V: NO quitar (alimentación ≤12V)
Conexión Arduino
Conectar GND y 5V de la fuente al Arduino. Conectar 6 líneas (ENA, IN1-4, ENB) a pines D10-D5.
Lógica de control:
- Pines dirección (ej. D7/D8): ambos LOW = motor parado. Uno HIGH + otro LOW = gira un sentido. Invertidos = sentido contrario.
- Pines PWM (D5, D10): valor 0-255 = velocidad (0=parado, 255=máximo). Son pines marcados con
~en el UNO.
Conexión Bluetooth
Módulo HC-06: RX↔TX (cruzados), VCC a 5V Arduino, GND a GND Arduino.
Sketch Firmata en Arduino
Solo subir StandardFirmata (Ejemplos → Firmata → StandardFirmata). Desconectar RX/TX antes de subir.
Nota: bajé el baudrate hardcodeado a 9600 por calidad de mi módulo Bluetooth.
Prueba rápida sin programar: Windows Remote Arduino Experience
Pasos: 1) Conectar a HC-06 emparejado. 2) PWM → pin 5 = 128. 3) Digital → pin 6 HIGH. ¡Motor gira! Prueba velocidades y direcciones (pins 5/10 PWM, 6/7/8/9 dirección).
App UWP como intermediaria (Xbox → Firmata)
App UWP simple: dos botones y un TextBlock. Función: 1) Conectar Bluetooth + Firmata. 2) Leer mando Xbox y convertir a comandos rover.
Control "tanque" con dos sticks: cada stick controla un motor (adelante/atrás = adelante/atrás motor correspondiente).
XAML (MainPage.xaml)
<StackPanel Orientation="Vertical">
<Button Name="btnStart" Click="btnStart_Click" FontSize="50" Width="400" Content="Start" />
<Button Name="btnController" Content="Controller" FontSize="50" Width="400" IsEnabled="False" Click="btnController_Click" />
<TextBlock Name="tbRead" FontSize="20" Foreground="Gray" />
</StackPanel>
C# Code-Behind (MainPage.xaml.cs)
public sealed partial class MainPage : Page {
private BluetoothSerial _bluetooth;
private RemoteDevice _arduino;
private UwpFirmata _firmata = null;
private Gamepad _Gamepad = null;
private double _SpeedLimit = 0.75; // Máx 75% PWM
private double _ActionPoint = 0.15; // Zona muerta stick
private byte _LeftForward = 6, _LeftBackward = 7, _LeftValue = 5;
private byte _RightForward = 9, _RightBackward = 8, _RightValue = 10;
private Dictionary<byte, PinState> _CurrentPinStates = new Dictionary<byte, PinState>();
private Dictionary<byte, ushort> _CurrentSpeedValues = new Dictionary<byte, ushort>();
public MainPage() { this.InitializeComponent(); }
private void btnStart_Click(object sender, RoutedEventArgs e) {
btnStart.IsEnabled = false;
Gamepad.GamepadAdded += Gamepad_GamepadAdded;
Gamepad.GamepadRemoved += Gamepad_GamepadRemoved;
_bluetooth = new BluetoothSerial("HC-06");
_bluetooth.ConnectionLost += BluetoothConnectionLost;
_bluetooth.ConnectionFailed += BluetoothConnectionFailed;
_bluetooth.ConnectionEstablished += OnConnectionEstablished;
_firmata = new UwpFirmata();
_arduino = new RemoteDevice(_firmata);
_firmata.begin(_bluetooth);
_bluetooth.begin(9600, SerialConfig.SERIAL_8N1);
_arduino.DeviceReady += ArduinoDeviceReady;
}
// ... handlers de conexión Bluetooth, gamepad, etc. ...
private async void btnController_Click(object sender, RoutedEventArgs e) {
DisableEnableButtons(false);
while (true) {
await Task.Delay(TimeSpan.FromMilliseconds(3));
if (_Gamepad == null) continue;
var reading = _Gamepad.GetCurrentReading();
// STICK IZQUIERDO → Motor izquierdo
if (Math.Abs(reading.LeftThumbstickY) < _ActionPoint) {
ArduinoDigitalWrite(_LeftForward, PinState.LOW);
ArduinoDigitalWrite(_LeftBackward, PinState.LOW);
} else if (reading.LeftThumbstickY >= _ActionPoint) {
ArduinoDigitalWrite(_LeftForward, PinState.HIGH);
ArduinoDigitalWrite(_LeftBackward, PinState.LOW);
ArduinoAnalogWrite(_LeftValue, (ushort)(255 * Math.Abs(reading.LeftThumbstickY) * _SpeedLimit));
} else {
ArduinoDigitalWrite(_LeftForward, PinState.LOW);
ArduinoDigitalWrite(_LeftBackward, PinState.HIGH);
ArduinoAnalogWrite(_LeftValue, (ushort)(255 * Math.Abs(reading.LeftThumbstickY) * _SpeedLimit));
}
// STICK DERECHO → Motor derecho (lógica idéntica)
if (Math.Abs(reading.RightThumbstickY) < _ActionPoint) {
ArduinoDigitalWrite(_RightForward, PinState.LOW);
ArduinoDigitalWrite(_RightBackward, PinState.LOW);
} else if (reading.RightThumbstickY >= _ActionPoint) {
ArduinoDigitalWrite(_RightForward, PinState.HIGH);
ArduinoDigitalWrite(_RightBackward, PinState.LOW);
ArduinoAnalogWrite(_RightValue, (ushort)(255 * Math.Abs(reading.RightThumbstickY) * _SpeedLimit));
} else {
ArduinoDigitalWrite(_RightForward, PinState.LOW);
ArduinoDigitalWrite(_RightBackward, PinState.HIGH);
ArduinoAnalogWrite(_RightValue, (ushort)(255 * Math.Abs(reading.RightThumbstickY) * _SpeedLimit));
}
}
}
// Evita enviar comandos duplicados (mejora rendimiento)
private void ArduinoDigitalWrite(byte port, PinState state) {
if (!_CurrentPinStates.ContainsKey(port) || _CurrentPinStates[port] != state) {
_CurrentPinStates[port] = state;
_arduino.digitalWrite(port, state);
}
}
private void ArduinoAnalogWrite(byte port, ushort value) {
if (!_CurrentSpeedValues.ContainsKey(port) || _CurrentSpeedValues[port] != value) {
_CurrentSpeedValues[port] = value;
_arduino.analogWrite(port, value);
}
}
}
Nota: En Firmata, escribir PWM = escribir valor analógico en pin digital (no hay método PWM específico). Los métodos ArduinoDigitalWrite/AnalogWrite evitan saturar la comunicación ignorando valores repetidos.
La luz intermitente no es solo estética: cuando tus creaciones se mueven, la seguridad es lo primero. El cacharro es tonto, tú no.
Proceso de manufactura
- Sistema de Asistencia RFID Fiable con Arduino y Python
- Mando universal Android con Arduino, 1Sheeld y control IR
- Construye un Voltímetro Inteligente con Arduino y Smartphone – Proyecto DIY Fácil
- Control Remoto IoT de Brazo Robótico Personalizado con Arduino MKR1000 y ThingSpeak
- Medición precisa de frecuencia y ciclo de trabajo con Arduino UNO
- Medición de Distancia en Tiempo Real con Sonar Arduino y Visualización en Processing
- Control de brillo LED sin interrupciones con Bolt IoT y Arduino UNO
- Brazo Robótico Inteligente Controlado por Arduino: Tutorial DIY Fácil
- Controla tu TV con Alexa mediante Arduino IoT Cloud: Guía Paso a Paso
- Construye una Radio FM Compacta con Arduino Nano y RDA5807M
- Control Inalámbrico de Iluminación Doméstica desde Smartphone con 1Sheeld