Changes between Initial Version and Version 1 of Tutorial4AddInputFrontend


Ignore:
Timestamp:
Nov 22, 2017, 10:44:04 AM (8 years ago)
Author:
fma
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Tutorial4AddInputFrontend

    v1 v1  
     1= Tutorial 4: Add input frontend support =
     2
     3''Files for this tutorial are available in [https://framagit.org/fma38/Py4bot/tree/master/py4bot/examples/tutorial_4 py4bot/examples/tutorial_4/].''
     4
     5Here, we will see how to create a custom input frontend class for a new gamepad. This tutorial assumes you are running a linux OS.
     6
     7Again, we create a new empty dir in our home dir:
     8
     9{{{
     10$ mkdir tutorial_4
     11$ cd tutorial_4
     12}}}
     13
     14As example, we are going to add support for the gamepad of the Freebox Revolution v6 ADSL box, widely used in France:
     15
     16[[Image(gamepad_free.jpg, 300px)]]
     17
     18(Note that this model has been replaced by another one)
     19
     20First, we need to know how this gamepad is detected by the kernel. For that, we just launch, from a console, the command:
     21
     22{{{
     23$ sudo journalctl -f
     24}}}
     25
     26and plug the gamepad. Here is what the kernel outputs:
     27
     28{{{
     29[3687970.593351] usb 2-1.7: new low-speed USB device number 15 using ehci-pci
     30[3687970.706423] usb 2-1.7: New USB device found, idVendor=0079, idProduct=0006
     31[3687970.706426] usb 2-1.7: New USB device strings: Mfr=1, Product=2, SerialNumber=0
     32[3687970.706428] usb 2-1.7: Product: Generic   USB  Joystick 
     33[3687970.706430] usb 2-1.7: Manufacturer: DragonRise Inc. 
     34[3687974.416506] input: DragonRise Inc.   Generic   USB  Joystick   as /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.7/2-1.7:1.0/0003:0079:0006.0004/input/input15
     35[3687974.416759] dragonrise 0003:0079:0006.0004: input,hidraw3: USB HID v1.10 Joystick [DragonRise Inc.   Generic   USB  Joystick  ] on usb-0000:00:1d.0-1.7/input0
     36[3687974.416780] dragonrise 0003:0079:0006.0004: Force Feedback for DragonRise Inc. game controllers by Richard Walmsley <richwalm@gmail.com>
     37}}}
     38
     39'''Py4bot''' comes with '''{{{py4bot-gui-evcal.py}}}''', a graphical tool to retreive an usb input device mapping. To use it, we need to give it the device path in '''{{{/dev}}}'''. Let's see what is our gamepad path:
     40
     41{{{
     42$ ls -1 /dev/input/by-id/
     43usb-1bcf_USB_Optical_Mouse-event-mouse
     44usb-1bcf_USB_Optical_Mouse-mouse
     45usb-DragonRise_Inc._Generic_USB_Joystick-event-joystick
     46usb-DragonRise_Inc._Generic_USB_Joystick-joystick
     47usb-Microsoft_Microsoft®_Digital_Media_Keyboard_3000-event-kbd
     48usb-Microsoft_Microsoft®_Digital_Media_Keyboard_3000-if01-event-kbd
     49}}}
     50
     51Here, the path to use is '''usb-DragonRise_Inc._Generic_USB_Joystick-event-joystick''':
     52
     53{{{
     54$ py4bot-gui-evcal.py /dev/input/by-id/usb-DragonRise_Inc._Generic_USB_Joystick-event-joystick
     55
     56{('EV_ABS', 3L): [(('ABS_X', 0L),
     57                   AbsInfo(value=127, min=0, max=255, fuzz=0, flat=15, resolution=0)),
     58                  (('ABS_Y', 1L),
     59                   AbsInfo(value=127, min=0, max=255, fuzz=0, flat=15, resolution=0)),
     60                  (('ABS_Z', 2L),
     61                   AbsInfo(value=131, min=0, max=255, fuzz=0, flat=15, resolution=0)),
     62                  (('ABS_RX', 3L),
     63                   AbsInfo(value=127, min=0, max=255, fuzz=0, flat=15, resolution=0)),
     64                  (('ABS_RZ', 5L),
     65                   AbsInfo(value=127, min=0, max=255, fuzz=0, flat=15, resolution=0)),
     66                  (('ABS_HAT0X', 16L),
     67                   AbsInfo(value=0, min=-1, max=1, fuzz=0, flat=0, resolution=0)),
     68                  (('ABS_HAT0Y', 17L),
     69                   AbsInfo(value=0, min=-1, max=1, fuzz=0, flat=0, resolution=0))],
     70 ('EV_FF', 21L): [(['FF_EFFECT_MIN', 'FF_RUMBLE'], 80L),
     71                  ('FF_PERIODIC', 81L),
     72                  (['FF_SQUARE', 'FF_WAVEFORM_MIN'], 88L),
     73                  ('FF_TRIANGLE', 89L),
     74                  ('FF_SINE', 90L),
     75                  (['FF_GAIN', 'FF_MAX_EFFECTS'], 96L)],
     76 ('EV_KEY', 1L): [(['BTN_JOYSTICK', 'BTN_TRIGGER'], 288L),
     77                  ('BTN_THUMB', 289L),
     78                  ('BTN_THUMB2', 290L),
     79                  ('BTN_TOP', 291L),
     80                  ('BTN_TOP2', 292L),
     81                  ('BTN_PINKIE', 293L),
     82                  ('BTN_BASE', 294L),
     83                  ('BTN_BASE2', 295L),
     84                  ('BTN_BASE3', 296L),
     85                  ('BTN_BASE4', 297L),
     86                  ('BTN_BASE5', 298L),
     87                  ('BTN_BASE6', 299L)],
     88 ('EV_MSC', 4L): [('MSC_SCAN', 4L)],
     89 ('EV_SYN', 0L): [('SYN_REPORT', 0L),
     90                  ('SYN_CONFIG', 1L),
     91                  ('SYN_DROPPED', 3L),
     92                  ('?', 4L),
     93                  ('?', 21L)]}
     94}}}
     95
     96The script automatically retreives the mapping, and creates a nice GUI:
     97
     98[[Image(py4bot-gui-evcal_1.png, 300px)]]
     99
     100Here, we can play with buttons. For analog axis to work, we need to press the ''Analog'' button; the red light should turn on.
     101
     102When playing with analog axis, we can see that a few of them are inverted. '''Py4bot''' expects than values increase when you push axis front/right. If it is not the case, we need to click the '''Invert''' checkbox.
     103
     104Note that on this specific gamepad, the '''{{{ABS_Z}}}''' axis is a combination of '''{{{ABS_X}}}''' and '''{{{ABS_RZ}}}'''. Don't know what is the purpose... I suggest we don't use it.
     105
     106Anyway, we should now have the following config:
     107
     108[[Image(py4bot-gui-evcal_2.png, 300px)]]
     109
     110Then, we just quit the script by closing the window. In the console, we see the final config:
     111
     112{{{
     113BUTTON_MAPPING = {
     114    0x120:   0,  # BTN_JOYSTICK
     115    0x121:   1,  # BTN_THUMB
     116    0x122:   2,  # BTN_THUMB2
     117    0x123:   3,  # BTN_TOP
     118    0x124:   4,  # BTN_TOP2
     119    0x125:   5,  # BTN_PINKIE
     120    0x126:   6,  # BTN_BASE
     121    0x127:   7,  # BTN_BASE2
     122    0x128:   8,  # BTN_BASE3
     123    0x129:   9,  # BTN_BASE4
     124    0x12a:  10,  # BTN_BASE5
     125    0x12b:  11,  # BTN_BASE6
     126}
     127
     128AXIS_MAPPING = {
     129    0x00:  0,  # ABS_X
     130    0x01:  1,  # ABS_Y
     131    0x02:  2,  # ABS_Z
     132    0x03:  3,  # ABS_RX
     133    0x05:  4,  # ABS_RZ
     134    0x10:  5,  # ABS_HAT0X
     135    0x11:  6,  # ABS_HAT0Y
     136}
     137
     138DEFAULT_CALIBRATION = {
     139     0: (  +0, +255),  # ABS_X
     140     1: (+255,   +0),  # ABS_Y
     141     2: (  +0, +255),  # ABS_Z
     142     3: (  +0, +255),  # ABS_RX
     143     4: (+255,   +0),  # ABS_RZ
     144     5: (  -1,   +1),  # ABS_HAT0X
     145     6: (  +1,   -1),  # ABS_HAT0Y
     146}
     147}}}
     148
     149Let's now create a file named '''{{{dragonRise.py}}}'''. At the top of the file, we add a few imports:
     150
     151{{{
     152#!python
     153
     154from py4bot.services.logger import Logger
     155from py4bot.common.intervalMapper import IntervalMapper
     156from py4bot.inputs.frontends.frontendUsb import FrontendUsb
     157}}}
     158
     159Then, we copy/paste the output of the script:
     160
     161{{{
     162#!python
     163
     164# Button mapping
     165BUTTON_MAPPING = {
     166    0x120:   0,  # BTN_JOYSTICK
     167    0x121:   1,  # BTN_THUMB
     168    0x122:   2,  # BTN_THUMB2
     169    0x123:   3,  # BTN_TOP
     170    0x124:   4,  # BTN_TOP2
     171    0x125:   5,  # BTN_PINKIE
     172    0x126:   6,  # BTN_BASE
     173    0x127:   7,  # BTN_BASE2
     174    0x128:   8,  # BTN_BASE3
     175    0x129:   9,  # BTN_BASE4
     176    0x12a:  10,  # BTN_BASE5
     177    0x12b:  11,  # BTN_BASE6
     178}
     179
     180# Axis mapping
     181AXIS_MAPPING = {
     182    0x00:  0,  # ABS_X
     183    0x01:  1,  # ABS_Y
     184    0x02:  2,  # ABS_Z
     185    0x03:  3,  # ABS_RX
     186    0x05:  4,  # ABS_RZ
     187    0x10:  5,  # ABS_HAT0X
     188    0x11:  6,  # ABS_HAT0Y
     189}
     190
     191# Axis calibration
     192DEFAULT_CALIBRATION = {
     193    0: (  +0, +255),  # ABS_X
     194    1: (+255,   +0),  # ABS_Y
     195    2: (  +0, +255),  # ABS_Z
     196    3: (  +0, +255),  # ABS_RX
     197    4: (+255,   +0),  # ABS_RZ
     198    5: (  -1,   +1),  # ABS_HAT0X
     199    6: (  +1,   -1),  # ABS_HAT0Y
     200}
     201
     202}}}
     203
     204Finally, we create the class itself:
     205
     206{{{
     207#!python
     208
     209class DragonRise(FrontendUsb):
     210    """ Freebox Revolution v6 gamepad implementation (first model)
     211    """
     212    def __init__(self, path, calibration=DEFAULT_AXIS_CALIBRATION):
     213        """ Init DragonRise object
     214        """
     215        super(DragonRise, self).__init__("DragonRise", path)
     216
     217        self._intervalMapper = {}
     218        for axis in calibration.keys():
     219            self._intervalMapper[axis] = IntervalMapper(calibration[axis])
     220
     221    def _keyToButton(self, eventCode):
     222        code = BUTTON_MAPPING[eventCode]
     223
     224        return code
     225
     226    def _absToAnalog(self, eventCode, eventValue):
     227        axis = AXIS_MAPPING[eventCode]
     228        value = self._intervalMapper[axis](eventValue)
     229
     230        return axis, value
     231}}}
     232
     233That's it, our new gamepad support is added. We can then use it as fronted for our '''{{{Gamepad}}}''' class, instead of '''{{{Thrustmaster}}}''' (see [[Tutorial1FirstRobot|tutorial 1]]).
     234
     235Note that this class is already in '''Py4bot'''.
     236
     237In the [[Tutorial5AdvancedRemoteControlUsage|next tutorial]], we will go deeper in controllers usage.