Hace unas semanas empecé a trabajar en un proyecto con un coche de radio control y se me ocurrió la manera de manejarlo con el control del PS4, el DualShock 4 (DS4).
Para establecer la conexión con el DS4 se tiene que enviar el VendorId y ProductId, una vez establecida la conexión este envía "reportes" los cuales son tramas de 64 bytes que contienen información del estado de los botones y del DS4.
La tabla 1 muestra la descripción del reporte que consta de 64 bytes.
Para configurar el led o activar los vibradores se envía una trama de 11 bytes.
La tabla 2 muestra la descripción del reporte a enviar de 11 bytes.
Lo primero es instalar la biblioteca de hidapi, terminado la instalación se tienen que declarar unas udev rules para el funcionamiento sin acceso root.
Abrir o crear el archivo
El siguiente código es un ejemplo que lee los botones y jostick del DS4.
[1] [Christopher Rosell]. (2013). ds4drv. Retrieved from https://github.com/chrippa/ds4drv
[2] [Wikipedia]. (2004). HID. Retrieved from https://es.wikipedia.org/wiki/HID
[3] [Christopher Rosell]. (2013). Bluetooth dongle compatibility. Retrieved from https://github.com/chrippa/ds4drv/wiki/Bluetooth-dongle-compatibility
[4] [Alan Ott]. (2010). Hidapi. Retrieved from https://github.com/signal11/hidapi
¿Por qué C/C++?
Existen varias bibliotecas para manejar el DS4 por ejemplo, el ds4drv1 pero dado que el proyecto en el que estoy trabajando está escrito en C/C++ necesito desarrollar la biblioteca.¿Cómo funciona el DS4?
El DS4 es un dispositivo HID2 este se puede conectar por medio de USB o Bluetooth (ver compatibilidad3)Para establecer la conexión con el DS4 se tiene que enviar el VendorId y ProductId, una vez establecida la conexión este envía "reportes" los cuales son tramas de 64 bytes que contienen información del estado de los botones y del DS4.
La tabla 1 muestra la descripción del reporte que consta de 64 bytes.
byte index | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
[0] | Report ID (USB) | |||||||||||||||||||||||||||||||||||||||
[1] | X axis (0 = left) | |||||||||||||||||||||||||||||||||||||||
[2] | Y axis (0 = up) | |||||||||||||||||||||||||||||||||||||||
[3] | Right Stick axis X | |||||||||||||||||||||||||||||||||||||||
[4] | Right Stick axis Y | |||||||||||||||||||||||||||||||||||||||
[5] | D-PAD : hat format (0x08 is released)
| |||||||||||||||||||||||||||||||||||||||
[6] | (8) | (4) | (2) | (1) | ||||||||||||||||||||||||||||||||||||
[7] | Counter (counts up by 1 per report) | T-PAD click (2) | (1) | |||||||||||||||||||||||||||||||||||||
[8] | Trigger (0 = released/unpressed, 0xFF = fully pressed) | |||||||||||||||||||||||||||||||||||||||
[9] | Trigger | |||||||||||||||||||||||||||||||||||||||
[10] | Unknown, seems to count downwards, non-random pattern | |||||||||||||||||||||||||||||||||||||||
[11] | Unknown, seems to count upwards by 3, but by 2 when [10] underflows | |||||||||||||||||||||||||||||||||||||||
[12] | Battery Level | |||||||||||||||||||||||||||||||||||||||
[13] | Unknown | |||||||||||||||||||||||||||||||||||||||
[14 - 15] | Accel Z (signed): orientation acceleration measures | |||||||||||||||||||||||||||||||||||||||
[16 - 17] | Accel Y | |||||||||||||||||||||||||||||||||||||||
[18 - 19] | Accel X | |||||||||||||||||||||||||||||||||||||||
[20 - 21] | Gyro X: orientation measures | |||||||||||||||||||||||||||||||||||||||
[22 - 23] | Gyro Y | |||||||||||||||||||||||||||||||||||||||
[24 - 25] | Gyro Z | |||||||||||||||||||||||||||||||||||||||
[26 - 29] | Unknown | |||||||||||||||||||||||||||||||||||||||
[30] | EXT/HeadSet/Earset: bitmask
| |||||||||||||||||||||||||||||||||||||||
[31 - 32] | Unknown: speculation: theses 5 next (reserved) nibbles for future additional products | |||||||||||||||||||||||||||||||||||||||
[33] | Unknown: speculation: could be bitmaps for control commands like volume, etc. | T-PAD event active:
(seen only)
| ||||||||||||||||||||||||||||||||||||||
[34] | T-PAD: auto incrementing number to track last update? | |||||||||||||||||||||||||||||||||||||||
[35] | 0 if finger №1 is down. | T-PAD: tracking numbers, unique to each finger (№1) down, so for each lift and repress, it gets a newly incremented figure. | ||||||||||||||||||||||||||||||||||||||
[36 - 38] | T-PAD: each finger (№1) location/positional data: static upon finger lifting, to maintain state.
To decode, each coordinated (x & y) is using 12 bits, you need to mask/split and swap the middle byte : e.g: 0x8a 4|0 28 → 0x08a 284 → x= 138 y= 644 | |||||||||||||||||||||||||||||||||||||||
[39] | 0 if finger №2 is down. | T-PAD: tracking numbers, unique to each finger (№2) down. | ||||||||||||||||||||||||||||||||||||||
[40 - 42] | T-PAD: each finger (№2) location. | |||||||||||||||||||||||||||||||||||||||
[44 - 47] | T-PAD: the previous touches (№1) track and location | |||||||||||||||||||||||||||||||||||||||
[48 - 51] | T-PAD: the previous touches (№2) track and location | |||||||||||||||||||||||||||||||||||||||
[52 - 63] | TODO |
Para configurar el led o activar los vibradores se envía una trama de 11 bytes.
La tabla 2 muestra la descripción del reporte a enviar de 11 bytes.
byte index | bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 |
---|---|---|---|---|---|---|---|---|
[0] | 0x05 | |||||||
[1] | 0x00 | |||||||
[2] | 0x04 | |||||||
[3] | 0xf0 disables the rumble motors, 0xf3 enables them | |||||||
[4] | Rumble (right / weak) | |||||||
[5] | Rumble (left / strong) | |||||||
[6] | RGB color (Red) | |||||||
[7] | RGB color (Green) | |||||||
[8] | RGB color (Blue) | |||||||
[9] | Unknown | |||||||
[10] | Unknown |
Codificado
Para el ejemplo se desarrolló sobre el sistema operativo GNU/Linux Ubuntu 16.04 y utilizando la biblioteca de hidapi4.Lo primero es instalar la biblioteca de hidapi, terminado la instalación se tienen que declarar unas udev rules para el funcionamiento sin acceso root.
Abrir o crear el archivo
/etc/udev/rules.d/61-dualshock.rulesSe declara lo siguiente:
SUBSYSTEM=="input", GROUP="input", MODE="0666" SUBSYSTEM=="usb", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="0268", MODE:="666", GROUP="plugdev" KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev" SUBSYSTEM=="input", GROUP="input", MODE="0666" SUBSYSTEM=="usb", ATTRS{idVendor}=="054c", ATTRS{idProduct}=="05c4", MODE:="666", GROUP="plugdev" KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0664", GROUP="plugdev"Recargar udev rules
sudo udevadm control --reload-rules
El siguiente código es un ejemplo que lee los botones y jostick del DS4.
//============================================================================ // _ _ _ _ // /_\ ___| | ____ _| |_| | // //_\\/ __| |/ / _` | __| | // / _ \__ \ < (_| | |_| | // \_/ \_/___/_|\_\__,_|\__|_| // // Name : Ds4.h // Author : Marco Antonio Cruz // Version : 0.01v // Description : Macros de DualShock 4. // Referencias : http://www.psdevwiki.com/ps4/DS4-USB // Gcc flags : -lhidapi-hidraw -lhidapi-libusb //============================================================================ #ifndef DS4_H_ #define DS4_H_ #define DS4_VENDOR_ID 1356 #define DS4_PRODUCT_ID 1476 /** * Mascara de botones */ #define BTN_NORTH 0x00 #define BTN_NORTH_EAST 0x01 #define BTN_EAST 0x02 #define BTN_SOUTH_EAST 0x03 #define BTN_SOUTH 0x04 #define BTN_SOUTH_WEST 0x05 #define BTN_WEST 0x06 #define BTN_NORTH_WEST 0x07 #define BTN_TRIANGLE 0x80 #define BTN_CIRCLE 0x40 #define BTN_CROSS 0x20 #define BTN_SQUARE 0x10 #endif /* DS4_H_ */
//============================================================================ // _ _ _ _ // /_\ ___| | ____ _| |_| | // //_\\/ __| |/ / _` | __| | // / _ \__ \ < (_| | |_| | // \_/ \_/___/_|\_\__,_|\__|_| // // Name : Ds4.cxx // Author : Marco Antonio Cruz // Version : 0.01v // Description : Ejemplo de uso de DualShock 4. // Referencias : http://www.psdevwiki.com/ps4/DS4-USB //============================================================================ #include <iostream> #include <stdio.h> #include <unistd.h> #include <hidapi.h> #include "ds4.h" using namespace std; /** * Lectura del stick izquierdo. * Buffer Data format * byte index bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 * [1] Axi x * [2] Axi y */ void readLeftStick(unsigned char buf[]) { wprintf(L"Left Axi x: %x, y: %x\n", buf[1], buf[2]); } /** * Lectura del stick derecho. * byte index bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 * [3] Axi x * [4] Axi y */ void readRightStick(unsigned char buf[]) { wprintf(L"Right: Axi x: %x, y: %x\n", buf[3], buf[4]); } /** * Lectura de botones: triangulo, circulo, equis y cuadrado. * Buffer Data format * byte index bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 * [5] & () X [] x x x x * * & : Triangulo * (): Circulo * X : Equis * []: Cuadrado * x : No importa */ void readButtons(unsigned char buf[]) { if (buf[5] & BTN_TRIANGLE) { wprintf(L"Triangle\n"); } if (buf[5] & BTN_CIRCLE) { wprintf(L"Circle\n"); } if (buf[5] & BTN_CROSS) { wprintf(L"Cross\n"); } if (buf[5] & BTN_SQUARE) { wprintf(L"Square\n"); } } /** * Lectura del pad. * Buffer Data format * byte index bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 * [5] x x x x 1 0 0 0 * * - 1000 * ↑← 0111 * ← 0110 * ↓← 0101 * ↓ 0100 * ↓→ 0011 * → 0010 * ↑→ 0001 * ↑ 0000 */ void readPad(unsigned char buf[]) { switch (buf[5] & 0xF) { case BTN_NORTH: wprintf(L"North\n"); break; case BTN_NORTH_EAST: wprintf(L"North east\n"); break; case BTN_EAST: wprintf(L"East\n"); break; case BTN_SOUTH_EAST: wprintf(L"South east\n"); break; case BTN_SOUTH: wprintf(L"South\n"); break; case BTN_SOUTH_WEST: wprintf(L"South west\n"); break; case BTN_WEST: wprintf(L"Old west\n"); break; case BTN_NORTH_WEST: wprintf(L"North west\n"); break; default: break; } } /** * Lectura de datos del DS4. */ void read(hid_device *handle) { unsigned char buf[64]; int res = hid_read_timeout(handle, buf, 64, 200); if (res == -1 || res == 0) { wprintf(L"error reading data\n"); return; } readPad(buf); readButtons(buf); readLeftStick(buf); readRightStick(buf); } /** * Envia informacion al DS4. * Buffer Data format 11 * byte index bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 * [0] 0x05 * [1] 0x00 * [2] 0x04 * [3] 0xf0 disables the rumble motors, 0xf3 enables them * [4] Rumble (right / weak) * [5] Rumble (left / strong) * [6] RGB color (Red) * [7] RGB color (Green) * [8] RGB color (Blue) * [9] Unknown * [10] Unknown */ void write(hid_device *handle) { const unsigned char rojo = 0xff; const unsigned char verde = 0x00; const unsigned char azul = 0x0f; const unsigned char strongRumble = 0x0F; const unsigned char weakRumble = 0xF0; unsigned char sendBuf[] = { 5, 255, 4, 0, weakRumble, strongRumble, rojo, verde, azul, 0, 0 }; hid_write(handle, sendBuf, 11); } int main(int argc, char* argv[]) { int res; unsigned char buf[64]; wchar_t wstr[64]; hid_device *handle; if ((res = hid_init()) != 0) { wprintf(L"unable hid init\n"); return -1; } /**Envio vendorId y productId para establecer conexion**/ handle = hid_open(DS4_VENDOR_ID, DS4_PRODUCT_ID, NULL); if (handle == NULL) { printf("unable to open device\n"); return -1; } /**Imprimo informacion del HID**/ res = hid_get_manufacturer_string(handle, wstr, 64); wprintf(L"Manufacturer String: %s\n", wstr); res = hid_get_product_string(handle, wstr, 64); wprintf(L"Product String: %s\n", wstr); res = hid_get_serial_number_string(handle, wstr, 64); wprintf(L"Serial Number String: (%d) %s\n", wstr[0], wstr); res = hid_get_indexed_string(handle, 1, wstr, 64); wprintf(L"Indexed String 1: %s\n", wstr); res = hid_read(handle, buf, 64); /**Activo el led y los vibradores**/ write(handle); /**Bucle para leer los botones**/ while (true) { /**Leo los botones**/ read(handle); } res = hid_exit(); return 0; }
[1] [Christopher Rosell]. (2013). ds4drv. Retrieved from https://github.com/chrippa/ds4drv
[2] [Wikipedia]. (2004). HID. Retrieved from https://es.wikipedia.org/wiki/HID
[3] [Christopher Rosell]. (2013). Bluetooth dongle compatibility. Retrieved from https://github.com/chrippa/ds4drv/wiki/Bluetooth-dongle-compatibility
[4] [Alan Ott]. (2010). Hidapi. Retrieved from https://github.com/signal11/hidapi
Comentarios
Publicar un comentario