Now that we have a gamepad connected over bluetooth, and we are able to detect it programmatically, the obvious next step would be to able to handle the input events from the gamepad. Since evdev makes devices available through character devices in the /dev/input directory, we can easily read and parse the events from the corresponding input device file using a C# FileStream, and create corresponding events using the custom event raising and handling delegate model in C#.
In order to get a list of input device files available, I can run ls -lF in the /dev/input directory. This is what the output looks like on my PINE64.
drwxr-xr-x 2 root root 80 Feb 11 2016 by-path/
crw-rw---- 1 root input 13, 64 Feb 11 2016 event0
crw-rw---- 1 root input 13, 65 Feb 11 2016 event1
crw-rw---- 1 root input 13, 66 Feb 11 2016 event2
crw-rw---- 1 root input 13, 67 Feb 11 2016 event3
crw-rw---- 1 root input 13, 68 Feb 11 2016 event4
crw-rw---- 1 root input 13, 69 Dec 19 10:48 event5
crw-rw-r-- 1 root input 13, 0 Dec 19 10:48 js0
crw-rw---- 1 root input 13, 63 Feb 11 2016 mice
crw-rw---- 1 root input 13, 32 Feb 11 2016 mouse0
If you are not running as root and you intend to be able to access the input device, you will need to add the user account to the input group. This can be done using usermod -aG input where is the current logged in user. Based on the output from my previous posts, the Xbox Wireless Controller will be accessible using /dev/input/input5.
The input event reader
Each evdev input event is represented as a struct which is defined in the uapi/linux/input.h header file.
The structure contains the following elements which can be read using a file stream in C#
time – a time value of 16 bytes. We will not actually be reading this with our code.
type – the event type, a short value of 2 bytes. Valid values can be found in the uapi/linux/input-event-codes.h header file.
code – the input event code, a short value of 2 bytes. Valid values can also be found in the uapi/linux/input-event-codes.h header file. The codes are determined based on the event type. For instance, an EV_KEY event will have an event code from one of the defined KEY_* values.
value – a value for the event, an integer value of 4 bytes. Dealing with a gamepad, for key events, this will be 0 and 1 corresponding to the up state and down state respectively, and for abs events, this would be a numeric value within the supported range.
Let’s create the aptly named EvdevReader class which will be used to open the file stream and read incoming input events. The constructor accepts an input Device as a parameter, which we created in the previous post for detecting the gamepad. The file stream is initialised in the constructor, and the class also implements IDisposable so that cleanup code for closing and releasing the stream can be called by the garbage collector.
public class EvdevReader : IDisposable
{
private bool disposed;
private bool open;
private Device device;
private FileStream stream;
public EvdevReader(Device device)
{
this.device = device;
stream = new FileStream(device.EvdevPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
open = true;
}
Next is the Read method. A 24-byte buffer is created in order to read the input events as they come in. Since the open flag essentially indicates that the stream is open, the loop will keep running while input events (every 24 bytes) are read. The time value is skipped because we have no use for it. The type, code and value values are then retrieved and then passed as arguments to the DispatchEvent which we will look at next.
public void Read()
{
byte[] buffer = new byte[24];
try
{
while (open)
{
stream.Read(buffer, 0, buffer.Length);
// start after byte 16 to skip timeval since we don't actually need it
int offset = 16;
short type = BitConverter.ToInt16(new byte[] { buffer[offset], buffer[++offset] }, 0);
short code = BitConverter.ToInt16(new byte[] { buffer[++offset], buffer[++offset] }, 0);
int value = BitConverter.ToInt32(
new byte[] { buffer[++offset], buffer[++offset], buffer[++offset], buffer[++offset] }, 0);
// Dispatch corresponding gamepad event to any subscribed event handlers
DispatchEvent(type, code, value);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
Since we’re only interested in gamepad events, we check that in the DispatchEvent method and then call the appropriate methods based on the event type. A subset of the event types and EV_ABS codes have been mapped as enums in the GamepadEventArgs class which will be used to store details about each event that is raised. Although only EV_ABS and EV_KEY events are being handled, you can extend the code to handle more event types depending on the particular input device.
private void DispatchEvent(short type, short code, int value)
{
// We only want to deal with gamepad events for now
if (device.IsGamepad && device is GenericGamepad)
{
GenericGamepad gamepad = device as GenericGamepad;
GamepadEventArgs.EventType eventType = (GamepadEventArgs.EventType) type;
switch (eventType)
{
case GamepadEventArgs.EventType.Syn:
break;
case GamepadEventArgs.EventType.Abs:
gamepad.DispatchAbsEvent(code, value);
break;
case GamepadEventArgs.EventType.Key:
// key down and key up events
gamepad.DispatchKeyEvent(code, value);
break;
case GamepadEventArgs.EventType.Msc:
break;
}
}
}
With EvdevReader reading input events from /dev/input, we can essentially raise custom events making use of the type, code and values and also handle them as may be required. I created the GenericGamepad class to do just this. Since every gamepad is expected to have buttons (or keys), we can dispatch button up and button down events for EV_KEY events. An input event value of 0 means that the button is up, while a value of 1 means that the button is pressed down. We already have an InputEventCode enum which corresponds to the buttons that may be available on a gamepad. I also created a Button enum with more meaningful names. The idea behind this is to create a helpful key map for input events that are being received from the device, which will be used in GamepadEventArgs.
public void DispatchKeyEvent(short code, int value)
{
InputEventCode keyCode = (InputEventCode) code;
GamepadEventArgs e = new GamepadEventArgs()
{
Type = GamepadEventArgs.EventType.Key,
Button = IsInputEventKeyCodeSupported(keyCode) ? this[keyCode] : Button.None,
InputEventCode = keyCode,
Value = value // not exactly necessary for button down / up events.
};
if (value == 0)
OnButtonUp(e);
else
OnButtonDown(e);
}
The DispatchKeyEvent method is quite simple. The input event code is checked using the IsInputEventKeyCodeSupported method to determine if the gamepad actually supports that key code. If it’s supported, the corresponding Button value is assigned to the event arguments, and the corresponding OnButtonUp or OnButtonDown event delegate is called based on the input event value (0 or 1).
public delegate void ButtonDownEventHandler(object sender, GamepadEventArgs e);
public delegate void ButtonUpEventHandler(object sender, GamepadEventArgs e);
public event ButtonDownEventHandler ButtonDown;
public event ButtonUpEventHandler ButtonUp;
public virtual void OnButtonDown(GamepadEventArgs e)
{
ButtonDownEventHandler handler = ButtonDown;
if (handler != null)
{
ButtonDown(this, e);
}
}
public virtual void OnButtonUp(GamepadEventArgs e)
{
ButtonUpEventHandler handler = ButtonUp;
if (handler != null)
{
ButtonUp(this, e);
}
}
Implementations of the GenericGamepad class are expected to initialise the buttons that are supported and a key map. The code from the XboxWirelessController implementation that I created shows how this can be achieved.
protected override void InitialiseButtons()
{
Buttons = new HashSet
While EV_KEY events are fairly straightforward, EV_ABS events are a bit more involved, and may vary from gamepad to gamepad. I made the `DispatchAbsEvent` method a virtual method in GenericGamepad, meaning any class which extends the base class has to override the method. The XboxWirelessController class contains an implementation with some comments showing what the corresponding buttons and expected values are. EV_ABS events are raised by the dpad, the analog sticks and the triggers on the Xbox Wireless Controller.
public override void DispatchAbsEvent(short code, int value)
{
/*
* Digital DPAD
* ABS_HAT0X -1 Left
* ABS_HAT0X 1 Right
* ABS_HAT0Y -1 Up
* ABS_HAT0Y 1 Down
*
* Triggers
* ABS_BRAKE Left trigger
* ABS_GAS Right trigger
*
* Thumbsticks
* ABS_X, ABS_Y Left thumbstick
* ABS_Z, ABS_RZ Right thumbstick
*/
GamepadEventArgs.AbsoluteAxes axis = (GamepadEventArgs.AbsoluteAxes) code;
GamepadEventArgs e = new GamepadEventArgs() { Type = GamepadEventArgs.EventType.Abs, AbsoluteAxis = axis, Value = value };
switch (axis)
{
case GamepadEventArgs.AbsoluteAxes.AbsHat0X:
if (value == -1)
e.Button = Button.DpadLeft;
else if (value == 1)
e.Button = Button.DpadRight;
break;
case GamepadEventArgs.AbsoluteAxes.AbsHat0Y:
if (value == -1)
e.Button = Button.DpadUp;
else if (value == 1)
e.Button = Button.DpadDown;
break;
case GamepadEventArgs.AbsoluteAxes.AbsBrake:
e.Button = Button.LeftTrigger;
break;
case GamepadEventArgs.AbsoluteAxes.AbsGas:
e.Button = Button.RightTrigger;
break;
case GamepadEventArgs.AbsoluteAxes.AbsX:
case GamepadEventArgs.AbsoluteAxes.AbsY:
e.Button = Button.LeftThumbstick;
break;
case GamepadEventArgs.AbsoluteAxes.AbsZ:
case GamepadEventArgs.AbsoluteAxes.AbsRZ:
e.Button = Button.RightThumbstick;
break;
}
if (axis == GamepadEventArgs.AbsoluteAxes.AbsHat0X ||
axis == GamepadEventArgs.AbsoluteAxes.AbsHat0Y ||
axis == GamepadEventArgs.AbsoluteAxes.AbsBrake ||
axis == GamepadEventArgs.AbsoluteAxes.AbsGas)
{
// button down / up events should suffice for the dpad and triggers
if (value == 0)
OnButtonUp(e);
else
OnButtonDown(e);
}
else
{
OnThumbstickMove(e);
}
}
Finally, we can test all of this in addition to detecting the gamepad with a sample console application. This will try to detect a gamepad, assign event handlers if found and then output the event args stdout whenever an input event occurs.
With this, it is very easy to build a program that can accept input system-wide and respond accordingly, and if you’re feeling adventurous, extend the code to support a plethora of input devices. You can obtain the full code listing for all the classes and enums named in this post from https://gitlab.com/akinwale/NaniteIo/tree/master/GhostMainframe.
Since I managed to get the Xbox Wireless Controller connected to the PINE64 using bluetooth, I had to come up with a way to detect that a gamepad is connected, which is neater than having to create a configuration file containing the path to the evdev file, or if I felt like being lazy, hardcode the path in the program. evdev (short form of event device) is the generic input event interface used by the Linux kernel, making input device events available and readable using files in the /dev/input directory.
The /proc/bus/input/devices file contains information about the input devices currently connected to the system. We will have to read this file in order to determine whether or not a gamepad is connected to the system. Here is what the /proc/bus/input/devices file on my PINE64 looks like.
This gives us quite a decent amount of information to make use of when dealing with input devices. The I, N, P, S, U and H prefixes simply represent the first letter in the corresponding name value while B means bitmap, representing a bitmask for that particular property. The EV bitmap represents the type of events supported by a particular device, while the KEY bitmap represents the keys and buttons that the device has. So now that we have all this information, how do we detect if a particular input device is a gamepad? Referring to section 4.3 of the Linux Gamepad Specification in the kernel documentation, which is titled Detection, gamepads which follow the protocol are expected to have BTN_GAMEPAD mapped. BTN_GAMEPAD is a constant defined in the uapi/linux/input-event-codes.h kernel header file with a value of 0x130 (decimal 304). This means we will have to check if the bit corresponding to BTN_GAMEPAD (bit 304 counting from the rightmost bit to the left) is set to 1.
Moving on to the code, the first thing we should do is create a simple class that represents an input device. We’ll call it Device and it should have a name and a property to store the evdev path. The IsGamepad property is completely optional if you intend to deal with only gamepads.
public class Device
{
public string Name { get; set; }
public string EvdevPath { get; set; }
public bool IsGamepad { get; set; }
public Device()
{
}
}
Next, we’ll create an InputDevices class, with a static method which we will call DetectGamepad. The method will parse the /proc/bus/input/devices file, retrieve the name and evdev path and check the KEY bitmap for each device found. When a device that has the BTN_GAMEPAD mapped key is found, the method will return that particular device. If no input device with the mapped key is found, null will be returned.
public class InputDevices
{
const string InputDevicesFile = "/proc/bus/input/devices";
///
/// Detects the first gamepad device connected to the system.
///
/// the first gamepad found in /proc/bus/input/devices, or null if no gamepad was found
public static Device DetectGamepad()
{
Device gamepad = null;
using (StreamReader reader = new StreamReader(new FileStream(
InputDevicesFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
Device device = new Device();
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
We make use of StreamReader to open the /proc/bus/input/devices file as readonly, and then try to read each line until we reach the end of the file (or stream).
// Get the device name
if (line.StartsWith("N", StringComparison.InvariantCulture))
{
device.Name = line.Substring(line.IndexOf('"') + 1, line.Length - line.IndexOf('"') - 2);
}
Obtaining the name of the device is fairly easy. The line starts with the N prefix, so we check this, and then we retrieve a substring containing the device name by stripping the double quotes surrounding the name.
// Get event* handler (for /dev/input/event*)
if (line.StartsWith("H", StringComparison.InvariantCulture)) // handlers
{
string[] handlers = line.Substring(line.IndexOf('=') + 1).Trim().Split(' ');
foreach (string handler in handlers)
{
if (handler.StartsWith("event", StringComparison.InvariantCulture))
{
device.EvdevPath = string.Format("/dev/input/{0}", handler);
}
}
}
Next we obtain the evdev path by checking the handlers on the line prefixed with H. The handler values are separated by spaces, so we split the string based on the space and check for the handler that starts with event. Once this has been obtained, we combine this with the the /dev/input/ prefix to get the full evdev path. In an upcoming post, we will look at how to monitor the evdev file for events using C#.
Finally, we need to detect the keys defined in the KEY bitmap. Let’s take the value for the Xbox Wireless Controller KEY bitmap as an example. 7fff000000000000 0 100040000000 0 0
This is a bitmask made up of hexadecimal values which we will convert to binary strings in order to check which bits are set. There are 5 groups of values, with each group expected to be 64 bits each. Just as a quick refresher, computers deal in binary, so 1 = 0001, 2 = 0010, 3 = 0011 and so forth. Each hexadecimal character value corresponds to 4 bits, and the individual 0-value groups can be padded with zeroes to make up 64 bits. For the KEY bitmap above, the total expected number of bits is 320. A bit value of 1 indicates that a value has been mapped. The bit position (counting from the rightmost bit to the left) corresponds to the values defined in the uapi/linux/input-event-codes.h header file referred to earlier.
With all of that making sense, we can write the code to parse the bitmap.
// Find the KEY bitmap and parse (if present)
if (line.StartsWith("B", StringComparison.InvariantCulture) && line.Contains("KEY"))
{
StringBuilder sb = new StringBuilder();
string key = line.Substring(line.IndexOf('=') + 1);
string[] groups = key.Trim().Split(' ');
foreach (string grp in groups)
{
if (grp == "0")
{
sb.Append(string.Empty.PadLeft(64, '0'));
}
else
{
StringBuilder grpBin = new StringBuilder();
foreach (char c in grp)
{
grpBin.Append(Convert.ToString(Convert.ToInt32(c.ToString(), 16), 2).PadLeft(4, '0'));
}
sb.Append(grpBin.ToString().PadLeft(64, '0'));
}
}
The bitmap value is split into groups using the spaces as the delimiter. If a group is equal to 0, then we pad the binary string with up to 64 zeroes, otherwise, we convert each hexadecimal value in the group to a binary string. Each hexadecimal value should be represented as 4 bits, so we pad to the left with zeroes if the conversion results in a string which is less than 4 bits. If a particular group’s binary string is less than 64 bits, we pad to the left with zeroes to make sure it’s up to 64.
string bin = sb.ToString();
List keybits = new List();
// Count the bits with j starting from right to left
for (int i = bin.Length - 1, j = 0; i > -1; i--, j++)
{
if (bin[i] == '1')
{
keybits.Add(j);
}
}
Now that we have the binary string, we count from the rightmost bit to the left and store the bits that are set to 1 (value mapped) in the keybits list. Next, we check if the value for BTN_GAMEPAD (304) exists in the list. If it does, then the input device is a gamepad, and we can break from the loop and return a reference to the input device. If a particular line is empty, which is checked using line.Trim().Length == 0, that signifies that the next device is about to be parsed from the file, so we create a new device instance at that point.
// BtnSouth / BtnGamepad key indicates that the device is a gamepad
if (keybits.Contains((int) InputEventCode.BtnGamepad))
{
device.IsGamepad = true;
gamepad = device;
break;
}
// Next device
if (line.Trim().Length == 0)
{
device = new Device();
}
}
}
}
return gamepad;
}
}
InputEventCode is an enum based on a subset of values defined in the uapi/linux/input-event-codes.h kernel header file.
We can test the DetectGamepad method with a simple program to output the device name and evdev path to the console if found. Add the following code to the Main method of a console application to try it out.
With the /proc/bus/input/devices file, the device name and handlers can be determined, as well as supported event types and additional capabilities. If you’re feeling adventurous, you can extend the code to detect multiple gamepad devices if connected, or to identify all supported keys / buttons for a particular gamepad, or decode the other bitmaps for a particular device. In my next post, I’ll cover how to read the evdev file in order to detect the input device events and then handle them as may be required.
Although the goal of the autonomous robot project is to have a robot that can navigate without human input, it’s not a bad idea to be able to manually override and control the robot one way or another which could come in useful in cases where it gets stuck due to being trapped in between large physical obstacles, or perhaps a bug in the code. My PINE64 order came with the stock WiFi / Bluetooth 4.0 module which is still available from the PINE store, and I also got the Xbox Wireless Controller with bluetooth support ($39.99). Any bluetooth controller should be able to get the job done, but I particularly like the look of the Xbox Wireless controller. However, it was a little difficult getting the controller to actually pair due to certain quirks with the controller implementation. I finally got it to work after spending almost an entire day on the issue and accidentally wiping my primary Ubuntu install with dd (which shouldn’t have happened, but I suppose someone shouldn’t do dangerous things when they’re tired).
In this post, I will cover how to setup the PINE64’s stock Bluetooth module, how I got the XBox Wireless controller to successfully connect via bluetooth, and run some input tests using the evtest tool. I am currently running the longsleep xenial image with kernel version 3.10.105. In a future post, we’ll write C# code to handle system-wide input events for Ghost Mainframe (which is what I’ll be calling the autonomous robot project going forward until I can think of a better name).
How to setup the stock Bluetooth module on the PINE A64+
The first thing we need to do is install the firmware which will enable the bluetooth module to work properly and load the necessary drivers. This is fairly straightforward and can be done using these steps.
git clone https://github.com/lwfinger/rtl8723bs_bt
cd rtl8723bs_bt
make
sudo make install
After make install completes, copy rtk_hciattach to your preferred bin path, either /usr/bin or /usr/local/bin.
You will also need to install the bluetooth service and the bluez stack by running sudo apt install bluetooth bluez. Once this is complete, you can test to make sure it works using the following comamnds:
Run sudo rfkill list and you should get output similar to what’s shown below.
0: sunxi-bt: Bluetooth
Soft blocked: no
Hard blocked: no
1: phy0: Wireless LAN
Soft blocked: no
Hard blocked: no
2: phy1: Wireless LAN
Soft blocked: no
Hard blocked: no
3: hci0: Bluetooth
Soft blocked: no
Hard blocked: no
If Soft blocked under Bluetooth is yes, unblock it using sudo rfkill unblock 3. This only needs to be done once. You can run the list command again to make sure that it actually got unblocked.
You can bring up the Bluetooth interface using sudo hciconfig hci0 up. Note that the hciconfig command is not available in BlueZ 5.47. Since the BlueZ version available from the xenial repository is 5.37, this is not a problem. If you don’t have hciconfig in your path, you will need to turn on the bluetooth module using bluetoothctl. Simply run power on in the bluetoothctl prompt.
Finally, you can add the following lines to /etc/rc.local if you wish to automatically power on the PINE Bluetooth module on every boot.
bluetoothctl is a command line tool that you can use to power on/off the bluetooth module, scan, discover and connect to nearby bluetooth devices. Simply run sudo bluetoothctl to get the to the bluetooth control prompt. If you are at the prompt and it’s not accepting your keyboard input, that could indicate that the bluetooth service is not running. Exit (using Ctrl+C if input is not being accepted) and then run sudo service start bluetooth.
Pairing the XBox Wireless Controller with the PINE64
This is where things got hairy. You can skip to the solution if you don’t want to read about how I arrived there.
The Story I had been following the xpad kernel driver project for a while which is how I had initially found out that there was a new version of the Xbox controller that could be connected using bluetooth. Following the instructions in this issue and this comment didn’t seem to work. Instead, I ended up with an AuthenticationCancelled error.
My initial foray into finding solutions for the problem led me to build BlueZ 5.47 from source and install, but that didn’t change anything. There were also some posts about having to update the controller’s firmware which can only be done with a Windows 10 (Why, Microsoft?!) store app called Xbox Accessories. I created a VirtualBox VM running Windows 10 Fall Creator’s edition but the app did not recognise the controller when connected over USB. This led me into a deep hole where I was trying to obtain various older Windows drivers in order to get the controller to work. None of them worked so I eventually decided to just boot Windows on my notebook, as opposed to within the VM since I guessed that the VirtualBox USB implementation was probably causing detection problems (spoiler: I was right).
I tried to boot Windows from a preinstalled SSD, which resulted in a blue screen, then I tried to burn the VM’s VHD file to a different drive connected over USB, which somehow ended up destroying all the partitions on my internal SSD (I am very sure I used /dev/sdb as the of parameter for dd, not /dev/sda, but I don’t even care anymore). Finally, I created a Windows bootable flash drive and attempted to install on the external SSD only to discover that Windows cannot be installed to a drive connected over USB or Firewire. RAGE! Since my internal SSD was wiped anyway, I finally got Windows installed with the Xbox Accessories app up and running only to discover that the controller already had the latest firmware installed. That was pointless!
I almost gave up until I decided to re-read the btmon logs which led to me noticing the error, Result: Failure - unknown options (0x0003). This finally led me in the right direction as I found this mailing list thread which points to a couple of patches I had to apply to the kernel source. After rebuilding the kernel from source and modules, the bluetoothctl connect command works, although the pair command still fails. The connect command however forces the controller to pair, and that’s what’s important.
The Solution Apply the changes in this commit (you may need to change the USB device ID – obtained using lsusb – to match your controller; mine is 0x02ea) to the kernel source and then rebuild and deploy the kernel, headers and modules following these instructions. You can also make use of the helper scripts in that repository. Following redeployment, you can reboot the board so that the new kernel and modules are loaded. You can also add the disable_ertm line to your /etc/rc.local so that it is done at boot time. Your /etc/rc.local file should have lines like so (including changes applied above):
Finally, you should get output like what’s showing using bluetoothctl (input commands are preceded with the bluetooth prompt). You will notice that although the pair command still fails with Failed to pair: org.bluez.Error.AuthenticationCanceled, the connect command actually works.
[NEW] Controller AA:AA:AA:AA:AA:AA pine64 [default]
[bluetooth]# power on
Changing power on succeeded
[CHG] Controller AA:AA:AA:AA:AA:AA Powered: yes
[bluetooth]# agent on
Agent registered
[bluetooth]# default-agent
Default agent request successful
[bluetooth]# scan on
Discovery started
[CHG] Controller AA:AA:AA:AA:AA:AA Discovering: yes
[NEW] Device XX:XX:XX:XX:XX:XX Xbox Wireless Controller
[bluetooth]# pair XX:XX:XX:XX:XX:XX
Attempting to pair with XX:XX:XX:XX:XX:XX
[CHG] Device XX:XX:XX:XX:XX:XX Connected: yes
[CHG] Device XX:XX:XX:XX:XX:XX Connected: no
Failed to pair: org.bluez.Error.AuthenticationCanceled
[bluetooth]# connect XX:XX:XX:XX:XX:XX
Attempting to connect to XX:XX:XX:XX:XX:XX
[CHG] Device XX:XX:XX:XX:XX:XX Connected: yes
[CHG] Device XX:XX:XX:XX:XX:XX Modalias: usb:v045Ep02E0d0903
[CHG] Device XX:XX:XX:XX:XX:XX Modalias: usb:v045Ep02FDd0903
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 00001124-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 00001200-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX Paired: yes
Connection successful
[Xbox Wireless Controller]#
evtest: Testing the Xbox Wireless Controller input
You can exit from bluetoothctl after the connection has been successfully established. Install evtest using sudo apt install evtest and then run it using sudo evtest. With the output like so, enter the number corresponding to your controller.
No device specified, trying to scan all of /dev/input/event*
Available devices:
/dev/input/event0: sunxi-keyboard
/dev/input/event1: axp81x-supplyer
/dev/input/event2: sunxi-ths
/dev/input/event3: sunxi_ir_recv
/dev/input/event4: MCE IR Keyboard/Mouse (sunxi-rc-recv)
/dev/input/event5: Xbox Wireless Controller
Select the device event number [0-5]:
I got the following output after I entered 5:
Input driver version is 1.0.1
Input device ID: bus 0x5 vendor 0x45e product 0x2fd version 0x903
Input device name: "Xbox Wireless Controller"
Supported events:
Event type 0 (EV_SYN)
Event type 1 (EV_KEY)
Event code 158 (KEY_BACK)
Event code 172 (KEY_HOMEPAGE)
Event code 304 (BTN_SOUTH)
Event code 305 (BTN_EAST)
Event code 306 (BTN_C)
Event code 307 (BTN_NORTH)
Event code 308 (BTN_WEST)
Event code 309 (BTN_Z)
Event code 310 (BTN_TL)
Event code 311 (BTN_TR)
Event code 312 (BTN_TL2)
Event code 313 (BTN_TR2)
Event code 314 (BTN_SELECT)
Event code 315 (BTN_START)
Event code 316 (BTN_MODE)
Event code 317 (BTN_THUMBL)
Event code 318 (BTN_THUMBR)
Event type 3 (EV_ABS)
Event code 0 (ABS_X)
Value 33687
Min 0
Max 65535
Fuzz 255
Flat 4095
Event code 1 (ABS_Y)
Value 32135
Min 0
Max 65535
Fuzz 255
Flat 4095
Event code 2 (ABS_Z)
Value 31698
Min 0
Max 65535
Fuzz 255
Flat 4095
Event code 5 (ABS_RZ)
Value 37479
Min 0
Max 65535
Fuzz 255
Flat 4095
Event code 9 (ABS_GAS)
Value 0
Min 0
Max 1023
Fuzz 3
Flat 63
Event code 10 (ABS_BRAKE)
Value 0
Min 0
Max 1023
Fuzz 3
Flat 63
Event code 16 (ABS_HAT0X)
Value 0
Min -1
Max 1
Event code 17 (ABS_HAT0Y)
Value 0
Min -1
Max 1
Event code 40 (ABS_MISC)
Value 135
Min 0
Max 255
Flat 15
Event type 4 (EV_MSC)
Event code 4 (MSC_SCAN)
Properties:
Testing ... (interrupt to exit)
Here’s the output when I press a few buttons and move the analog sticks.
Event: time 1512754351.668219, type 3 (EV_ABS), code 5 (ABS_RZ), value 33800
Event: time 1512754351.668219, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754351.668219, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754351.668219, -------------- SYN_REPORT ------------
Event: time 1512754351.690797, type 3 (EV_ABS), code 2 (ABS_Z), value 33131
Event: time 1512754351.690797, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754351.690797, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754351.690797, -------------- SYN_REPORT ------------
Event: time 1512754351.885663, type 3 (EV_ABS), code 5 (ABS_RZ), value 33845
Event: time 1512754351.885663, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754351.885663, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754351.885663, -------------- SYN_REPORT ------------
Event: time 1512754351.900684, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754351.900684, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754351.900684, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90004
Event: time 1512754351.900684, type 1 (EV_KEY), code 307 (BTN_NORTH), value 1
Event: time 1512754351.900684, -------------- SYN_REPORT ------------
Event: time 1512754351.908171, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754351.908171, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754351.908171, -------------- SYN_REPORT ------------
Event: time 1512754351.923171, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754351.923171, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754351.923171, -------------- SYN_REPORT ------------
Event: time 1512754352.005668, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754352.005668, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754352.005668, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90004
Event: time 1512754352.005668, type 1 (EV_KEY), code 307 (BTN_NORTH), value 0
Event: time 1512754352.005668, -------------- SYN_REPORT ------------
Event: time 1512754352.020838, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754352.020838, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754352.020838, -------------- SYN_REPORT ------------
Event: time 1512754352.440712, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754352.440712, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754352.440712, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90005
Event: time 1512754352.440712, type 1 (EV_KEY), code 308 (BTN_WEST), value 1
Event: time 1512754352.440712, -------------- SYN_REPORT ------------
Event: time 1512754352.448189, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754352.448189, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754352.448189, -------------- SYN_REPORT ------------
Event: time 1512754352.455711, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754352.455711, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754352.455711, -------------- SYN_REPORT ------------
Event: time 1512754352.553169, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754352.553169, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754352.553169, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90005
Event: time 1512754352.553169, type 1 (EV_KEY), code 308 (BTN_WEST), value 0
Event: time 1512754352.553169, -------------- SYN_REPORT ------------
Event: time 1512754352.560690, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754352.560690, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754352.560690, -------------- SYN_REPORT ------------
Event: time 1512754352.568174, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754352.568174, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754352.568174, -------------- SYN_REPORT ------------
Event: time 1512754352.800698, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754352.800698, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754352.800698, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90002
Event: time 1512754352.800698, type 1 (EV_KEY), code 305 (BTN_EAST), value 1
Event: time 1512754352.800698, -------------- SYN_REPORT ------------
Event: time 1512754352.808215, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754352.808215, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754352.808215, -------------- SYN_REPORT ------------
Event: time 1512754352.815704, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754352.815704, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754352.815704, -------------- SYN_REPORT ------------
Event: time 1512754352.928169, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754352.928169, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754352.928169, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90002
Event: time 1512754352.928169, type 1 (EV_KEY), code 305 (BTN_EAST), value 0
Event: time 1512754352.928169, -------------- SYN_REPORT ------------
Event: time 1512754352.935711, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754352.935711, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754352.935711, -------------- SYN_REPORT ------------
Event: time 1512754352.943412, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754352.943412, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754352.943412, -------------- SYN_REPORT ------------
Event: time 1512754353.070685, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754353.070685, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754353.070685, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90001
Event: time 1512754353.070685, type 1 (EV_KEY), code 304 (BTN_SOUTH), value 1
Event: time 1512754353.070685, -------------- SYN_REPORT ------------
Event: time 1512754353.078184, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754353.078184, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754353.078184, -------------- SYN_REPORT ------------
Event: time 1512754353.100726, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754353.100726, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754353.100726, -------------- SYN_REPORT ------------
Event: time 1512754353.175707, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754353.175707, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754353.175707, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90001
Event: time 1512754353.175707, type 1 (EV_KEY), code 304 (BTN_SOUTH), value 0
Event: time 1512754353.175707, -------------- SYN_REPORT ------------
Event: time 1512754353.993252, type 3 (EV_ABS), code 0 (ABS_X), value 33813
Event: time 1512754353.993252, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754353.993252, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754353.993252, -------------- SYN_REPORT ------------
Event: time 1512754354.000727, type 3 (EV_ABS), code 0 (ABS_X), value 33813
Event: time 1512754354.000727, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.000727, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.000727, -------------- SYN_REPORT ------------
Event: time 1512754354.015690, type 3 (EV_ABS), code 0 (ABS_X), value 32976
Event: time 1512754354.015690, type 3 (EV_ABS), code 1 (ABS_Y), value 31899
Event: time 1512754354.015690, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.015690, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.015690, -------------- SYN_REPORT ------------
Event: time 1512754354.023188, type 3 (EV_ABS), code 0 (ABS_X), value 26432
Event: time 1512754354.023188, type 3 (EV_ABS), code 1 (ABS_Y), value 31113
Event: time 1512754354.023188, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.023188, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.023188, -------------- SYN_REPORT ------------
Event: time 1512754354.030693, type 3 (EV_ABS), code 0 (ABS_X), value 25351
Event: time 1512754354.030693, type 3 (EV_ABS), code 1 (ABS_Y), value 29971
Event: time 1512754354.030693, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.030693, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.030693, -------------- SYN_REPORT ------------
Event: time 1512754354.038192, type 3 (EV_ABS), code 0 (ABS_X), value 14885
Event: time 1512754354.038192, type 3 (EV_ABS), code 1 (ABS_Y), value 21912
Event: time 1512754354.038192, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.038192, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.038192, -------------- SYN_REPORT ------------
Event: time 1512754354.045979, type 3 (EV_ABS), code 0 (ABS_X), value 7364
Event: time 1512754354.045979, type 3 (EV_ABS), code 1 (ABS_Y), value 20983
Event: time 1512754354.045979, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.045979, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.045979, -------------- SYN_REPORT ------------
Event: time 1512754354.053239, type 3 (EV_ABS), code 0 (ABS_X), value 0
Event: time 1512754354.053239, type 3 (EV_ABS), code 1 (ABS_Y), value 12813
Event: time 1512754354.053239, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.053239, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.053239, -------------- SYN_REPORT ------------
Event: time 1512754354.135693, type 3 (EV_ABS), code 1 (ABS_Y), value 12813
Event: time 1512754354.135693, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.135693, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.135693, -------------- SYN_REPORT ------------
Event: time 1512754354.150659, type 3 (EV_ABS), code 1 (ABS_Y), value 12864
Event: time 1512754354.150659, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.150659, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.150659, -------------- SYN_REPORT ------------
Event: time 1512754354.158189, type 3 (EV_ABS), code 0 (ABS_X), value 12576
Event: time 1512754354.158189, type 3 (EV_ABS), code 1 (ABS_Y), value 19757
Event: time 1512754354.158189, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.158189, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.158189, -------------- SYN_REPORT ------------
Event: time 1512754354.165722, type 3 (EV_ABS), code 0 (ABS_X), value 42285
Event: time 1512754354.165722, type 3 (EV_ABS), code 1 (ABS_Y), value 35206
Event: time 1512754354.165722, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.165722, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.165722, -------------- SYN_REPORT ------------
Event: time 1512754354.173187, type 3 (EV_ABS), code 0 (ABS_X), value 30551
Event: time 1512754354.173187, type 3 (EV_ABS), code 1 (ABS_Y), value 34260
Event: time 1512754354.173187, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.173187, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.173187, -------------- SYN_REPORT ------------
Event: time 1512754354.180700, type 3 (EV_ABS), code 0 (ABS_X), value 31150
Event: time 1512754354.180700, type 3 (EV_ABS), code 1 (ABS_Y), value 34035
Event: time 1512754354.180700, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.180700, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.180700, -------------- SYN_REPORT ------------
Event: time 1512754354.188192, type 3 (EV_ABS), code 0 (ABS_X), value 31685
Event: time 1512754354.188192, type 3 (EV_ABS), code 1 (ABS_Y), value 33443
Event: time 1512754354.188192, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.188192, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.188192, -------------- SYN_REPORT ------------
Event: time 1512754354.195760, type 3 (EV_ABS), code 0 (ABS_X), value 31898
Event: time 1512754354.195760, type 3 (EV_ABS), code 1 (ABS_Y), value 33299
Event: time 1512754354.195760, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.195760, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.195760, -------------- SYN_REPORT ------------
Event: time 1512754354.203188, type 3 (EV_ABS), code 0 (ABS_X), value 32461
Event: time 1512754354.203188, type 3 (EV_ABS), code 1 (ABS_Y), value 33109
Event: time 1512754354.203188, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.203188, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.203188, -------------- SYN_REPORT ------------
Event: time 1512754354.210698, type 3 (EV_ABS), code 0 (ABS_X), value 32597
Event: time 1512754354.210698, type 3 (EV_ABS), code 1 (ABS_Y), value 32921
Event: time 1512754354.210698, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.210698, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.210698, -------------- SYN_REPORT ------------
Event: time 1512754354.218189, type 3 (EV_ABS), code 0 (ABS_X), value 32774
Event: time 1512754354.218189, type 3 (EV_ABS), code 1 (ABS_Y), value 32751
Event: time 1512754354.218189, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.218189, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.218189, -------------- SYN_REPORT ------------
Event: time 1512754354.225724, type 3 (EV_ABS), code 0 (ABS_X), value 32950
Event: time 1512754354.225724, type 3 (EV_ABS), code 1 (ABS_Y), value 32606
Event: time 1512754354.225724, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.225724, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.225724, -------------- SYN_REPORT ------------
Event: time 1512754354.233180, type 3 (EV_ABS), code 0 (ABS_X), value 33108
Event: time 1512754354.233180, type 3 (EV_ABS), code 1 (ABS_Y), value 32545
Event: time 1512754354.233180, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.233180, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.233180, -------------- SYN_REPORT ------------
Event: time 1512754354.240703, type 3 (EV_ABS), code 0 (ABS_X), value 33242
Event: time 1512754354.240703, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.240703, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.240703, -------------- SYN_REPORT ------------
Event: time 1512754354.248196, type 3 (EV_ABS), code 0 (ABS_X), value 33299
Event: time 1512754354.248196, type 3 (EV_ABS), code 1 (ABS_Y), value 32384
Event: time 1512754354.248196, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.248196, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.248196, -------------- SYN_REPORT ------------
Event: time 1512754354.263189, type 3 (EV_ABS), code 0 (ABS_X), value 33449
Event: time 1512754354.263189, type 3 (EV_ABS), code 1 (ABS_Y), value 32322
Event: time 1512754354.263189, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.263189, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.263189, -------------- SYN_REPORT ------------
Event: time 1512754354.285686, type 3 (EV_ABS), code 0 (ABS_X), value 33584
Event: time 1512754354.285686, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.285686, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.285686, -------------- SYN_REPORT ------------
Event: time 1512754354.293188, type 3 (EV_ABS), code 1 (ABS_Y), value 32182
Event: time 1512754354.293188, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.293188, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.293188, -------------- SYN_REPORT ------------
Event: time 1512754354.338389, type 3 (EV_ABS), code 0 (ABS_X), value 33641
Event: time 1512754354.338389, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754354.338389, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754354.338389, -------------- SYN_REPORT ------------
Event: time 1512754357.218305, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754357.218305, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754357.218305, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90007
Event: time 1512754357.218305, type 1 (EV_KEY), code 310 (BTN_TL), value 1
Event: time 1512754357.218305, -------------- SYN_REPORT ------------
Event: time 1512754357.225771, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754357.225771, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754357.225771, -------------- SYN_REPORT ------------
Event: time 1512754357.233210, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754357.233210, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754357.233210, -------------- SYN_REPORT ------------
Event: time 1512754357.375728, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754357.375728, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754357.375728, type 4 (EV_MSC), code 4 (MSC_SCAN), value 90007
Event: time 1512754357.375728, type 1 (EV_KEY), code 310 (BTN_TL), value 0
Event: time 1512754357.375728, -------------- SYN_REPORT ------------
Event: time 1512754357.383257, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754357.383257, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754357.383257, -------------- SYN_REPORT ------------
Event: time 1512754357.390734, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754357.390734, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754357.390734, -------------- SYN_REPORT ------------
Event: time 1512754358.373255, type 3 (EV_ABS), code 2 (ABS_Z), value 33131
Event: time 1512754358.373255, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754358.373255, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754358.373255, -------------- SYN_REPORT ------------
Event: time 1512754358.388206, type 3 (EV_ABS), code 2 (ABS_Z), value 33131
Event: time 1512754358.388206, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754358.388206, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754358.388206, -------------- SYN_REPORT ------------
Event: time 1512754358.410732, type 3 (EV_ABS), code 2 (ABS_Z), value 33131
Event: time 1512754358.410732, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754358.410732, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754358.410732, -------------- SYN_REPORT ------------
Event: time 1512754358.463257, type 3 (EV_ABS), code 2 (ABS_Z), value 33088
Event: time 1512754358.463257, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754358.463257, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754358.463257, -------------- SYN_REPORT ------------
Event: time 1512754358.598420, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754358.598420, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754358.598420, type 4 (EV_MSC), code 4 (MSC_SCAN), value 9000c
Event: time 1512754358.598420, type 1 (EV_KEY), code 315 (BTN_START), value 1
Event: time 1512754358.598420, -------------- SYN_REPORT ------------
Event: time 1512754358.605933, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754358.605933, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754358.605933, -------------- SYN_REPORT ------------
Event: time 1512754358.613284, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754358.613284, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754358.613284, -------------- SYN_REPORT ------------
Event: time 1512754358.643259, type 3 (EV_ABS), code 5 (ABS_RZ), value 33893
Event: time 1512754358.643259, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754358.643259, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754358.643259, -------------- SYN_REPORT ------------
Event: time 1512754358.763268, type 3 (EV_ABS), code 16 (ABS_HAT0X), value 0
Event: time 1512754358.763268, type 3 (EV_ABS), code 17 (ABS_HAT0Y), value 0
Event: time 1512754358.763268, type 4 (EV_MSC), code 4 (MSC_SCAN), value 9000c
Event: time 1512754358.763268, type 1 (EV_KEY), code 315 (BTN_START), value 0
Event: time 1512754358.763268, -------------- SYN_REPORT ------------
Conclusion
While it wasn’t a pleasant experience, I’m glad I was able to get the XBox Wireless Controller to actually work with the PINE64. In my followup post, I will write out (or probably create a diagram) of the controller button mappings through evtest and then write C# code to handle the input directly from the /dev/input/event* file and generate input events accordingly.