I'm an early joiner of the OpenStreetMap project, so I'm a big fan of the OsmAnd app. It is my preferred choice for motorbike on-board navigation.
A long-standing issue was the impossiblity to operate the map while wearing gloves. To be clear: panning and zooming on the Android touchscreen using gloves is impossibile. Here we explore the possibility to use a little Bluetooth remote controller to get at least the basic pan/zoom functions; in short: it could work!
It is a low-cost (5-10 euros) Bluetooth device, sold under different brands. At present (Jan 2020) you can find it on Amazon or Aliexpress.com under the generic terms Android gamepad Bluetooth controller. It has an internal rechargeable battery which should last about 10 hours. The small size is quite convenient for using it on the handlebar of a motorbike, the biggest problem being to be not water-proof.
Low price (5-10 €). Very small size. |
|
No waterproof. Cannot remap buttons: some presets available, but not user-configurable. Auto stand-by if not operated for 15 minutes. Need to press start and wait 3 seconds to operate again. Only 10 hours operating time per charge. Non replaceable battery. |
Once paired with the Android device, you can get some information using cat /proc/bus/input/devices:
I: Bus=0005 Vendor=ffff Product=0000 Version=0000 N: Name="MOCUTE-032_S23-AUTO" P: Phys= S: Sysfs=/devices/virtual/misc/uhid/0005:FFFF:0000.0001/input/input9 U: Uniq=E6:25:A1:48:F8:E0 H: Handlers=sysrq gpufreq_ib event9 B: PROP=0 B: EV=10001f B: KEY=3f0003007f 0 0 483ffff17aff32d bf54444600000000 6fdb0000001f0001 130f938b17c007 ffff7bfad9415fff febeffdfffefffff fffffffffffffffe B: REL=143 B: ABS=100030627 B: MSC=10
Generally speaking, an Android input device generates a different hardware ScanCode when you press each different key. Using an appropriate keylayout file, each ScanCode is translated into an Android KeyCode, which is sent to the application. For example: the Mocute controller generates the ScanCode 114 when you press the A button, looking at /system/usr/keylayout/Generic.kl the scancode is translated into the mnemonic KEYCODE_VOLUME_DOWN (which in turn is the decimal value 25) and sent to the app. The app decides what to do with that key press.
Many key layouts exists into an Android device, they are stored into the /system/usr/keylayout/ directory. The most important one is the Generic.kl file, which is applied if not specific case are found. This is an excerpt of the file:
... key 10 9 key 11 0 key 12 MINUS key 13 EQUALS key 14 DEL key 15 TAB key 16 Q key 17 W key 18 E ... key 113 VOLUME_MUTE key 114 VOLUME_DOWN key 115 VOLUME_UP ...
The Mocute-032 controller has a little slide switch with two positions: GAME and KEY. Each mode generates different ScanCodes for the four buttons. The joystick operates differently: in KEY mode it emulates four digital switches (keyboard keys which generate the ScanCode), while in GAME mode it operates as an analog joystick with two axis (without generating a ScanCode).
In GAME mode, each axis position is reported as a floating point number ragining from -1.0 to 1.0, where the zero is at the central position. When the joystick is pushed near to one side, an Android KeyCode is generated. This means that beside the axes position, an appropriate KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT or KEYCODE_DPAD_RIGHT is sent to the application. The Android app receives a KeyCode, but the device does not generate any ScanCode.
You can see joystick axes values with apps like Gamepad tester. You can see key events (ScanCodes and KeyCodes) with apps like KeyEvent Display.
KEY Mode | GAME Mode | |||||
---|---|---|---|---|---|---|
ScanCode | Android key code | ScanCode | Android key code | |||
UP | 163 | 87 | KEYCODE_MEDIA_NEXT | AXIS Y 0x01 -0.8 | 19 | KEYCODE_DPAD_UP |
DOWN | 165 | 88 | KEYCODE_MEDIA_PREVIOUS | AXIS Y 0x01 1.0 | 20 | KEYCODE_DPAD_DOWN |
LEFT | 208 | 90 | KEYCODE_MEDIA_FAST_FORWARD | AXIS X 0x00 -1.0 | 21 | KEYCODE_DPAD_LEFT |
RIGHT | 168 | 89 | KEYCODE_MEDIA_REWIND | AXIS X 0x00 0.8 | 22 | KEYCODE_DPAD_RIGHT |
A | 114 | 25 | KEYCODE_VOLUME_DOWN | 304 | 96 | KEYCODE_BUTTON_A |
B | 28 | 66 | KEYCODE_ENTER | 305 | 97 | KEYCODE_BUTTON_B |
X | 115 | 24 | KEYCODE_VOLUME_UP | 307 | 99 | KEYCODE_BUTTON_X |
Y | 158 | 4 | KEYCODE_BACK | 308 | 100 | KEYCODE_BUTTON_Y |
OsmAnd 3.5.5 supports some external input devices, to use the Mocute controller we need to enable the generic keyboard external input device; from the Menu button ⇒ Configure profile ⇒ General settings ⇒ External input devices ⇒ Keyboard.
At the moment OsmAnd recognized only a few KeyCodes:
Android KeyCode | OsmAnd Action |
---|---|
KEYCODE_DPAD_RIGHT | Scroll right |
KEYCODE_DPAD_LEFT | Scroll left |
KEYCODE_DPAD_UP | Scroll up |
KEYCODE_DPAD_DOWN | Scroll down |
KEYCODE_PLUS | Zoom in |
KEYCODE_MINUS | Zoom out |
The GAME mode is somewhat working out-of-the-box, but with map scroll only. We want the following:
In theory - to remap the functions of our controller - it should be possible to provide a specific keylayout file for just this controller: Android's instructions say to simply create a file with the name Vendor_ffff_Product_0000.kl (see the vendor and product read from the /proc/bus/input/devices
pseudofile). Unfortunately this does not work, may be because ffff and 0000 are not valid ID values.
So we have to change the /system/usr/keylayout/Generic.kl file, redefining some ScanCodes and axis (the rest of the file must remain untouched):
key 304 MINUS key 305 MINUS key 307 PLUS key 308 PLUS axis 0x00 Y axis 0x01 invert X
NOTICE: I have redefined scancodes 304 and 308 as duplicates of 305 and 307. This is because the default action for scancode 304 was BUTTON_A, which opens the Actions menu in OsmAnd 3.5.5; opening this menu by accident is very annoying because you can close it only using the touchscreen!
This change - unfortunately - have the side effect to remap all the external gamepads that we will connect to our Android device!
Using the KeyEvent Display Android app, you can see what ScanCode and what associated Android key code is generated by each key pressed.
Using the Key Tester Android app, you can check what Android key code is received by the app when you press a key. So you can check if the key layout (mapping from ScanCode to Android KeyCode) is applied as expected.
Using the Gamepad tester Android app, you can see what joystick axis are associated with a gamepad, and the analogic values reported for each joystick position.