====== Using OsmAnd with a Remote Controller ======
{{.:osmand:osmand-remote-controller.jpg?380 |OsmAnd paierd with a Bluetooth remote controller}}
I'm an early joiner of the **[[https://www.openstreetmap.org|OpenStreetMap]]** project, so I'm a big fan of the **[[https://play.google.com/store/apps/details?id=net.osmand|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!**
===== The Mocute Universal Wireless Remote Controller =====
[{{ .:osmand:mocute-remote-controller.jpg?280|The Mocute Remote Controller}}]
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**.
| {{:img:pros_icon.png?nolink&36|}} | **Low price** (5-10 €).\\ **Very small** size. |
| {{:img:cons_icon.png?nolink&36|}} | **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
===== Android ScanCodes and KeyCodes =====
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.
===== Android Key Layouts =====
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
...
===== Mocute default key mapping =====
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).
[{{.:osmand:mocute-gamepad-keys.jpg?360|Key mapping in GAME mode}}]
==== The joystick in GAME mode ====
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 **[[https://play.google.com/store/apps/details?id=ru.elron.gamepadtester|Gamepad tester]]**. You can see **key events** (ScanCodes and KeyCodes) with apps like **[[https://play.google.com/store/apps/details?id=aws.apps.keyeventdisplay|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 |
===== Connecting the external input device to OsmAnd =====
**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 |
===== Remapping the controller keys (root required) =====
The **GAME mode** is somewhat working out-of-the-box, but with **map scroll** only. We want the following:
- **Swap the X with the Y axis** and **invert the X**. This is because we want to use the controller in vertical position.
- Associate the **X** button to **zoom in**, and **Y** button to **zoom out**.
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!
===== Key Mapping Test =====
Using the **[[https://play.google.com/store/apps/details?id=aws.apps.keyeventdisplay|KeyEvent Display]]** Android app, you can see what **ScanCode** and what associated **Android key code** is generated by each key pressed.
Using the **[[https://play.google.com/store/apps/details?id=com.flossga.android.whichbutton|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 **[[https://play.google.com/store/apps/details?id=ru.elron.gamepadtester|Gamepad tester]]** Android app, you can see what **joystick axis** are associated with a gamepad, and the analogic values reported for each **joystick position**.
===== Other remote controllers =====
* **[[https://daytona-global.com/products/?page=Products_Product&mfrID=13&categID=28&subcategID=80&productID=87352|Daytona Smartphone controller]]** - Keypresses sent to the Android device: Enter, Power, Escape, Home, Joystick.
* **[[https://carpe-iter.com/carpe-iter-control/|Carpe Iter Control]]** - Seven programmable keys, with two functions each.
===== Web References =====
* **[[https://github.com/osmandapp/Osmand/issues/7295|OsmAnd issue #7295: External input device add extra map keys]]**
* **{{mocute-032_s23-auto_manual_en.pdf|MOCUTE 032 S23 User Manual}}** (original link: [[https://www.kjell.com/globalassets/mediaassets/540960_96667_manual_en.pdf|540960_96667_manual_en.pdf]])
* **[[https://stackoverflow.com/questions/34571538/how-to-find-the-scancode-of-an-unknown-key-in-android|How to find the scancode of an “unknown key” in Android?]]**
* **[[https://forum.xda-developers.com/showthread.php?t=2413172|How to map gamepad/joystick axes to control the UI?]]**
* **[[https://developer.android.com/reference/android/view/KeyEvent.html|KeyEvent - Android Developers]]**
* **[[https://source.android.com/devices/input/key-layout-files|Key Layout Files]]**
* **[[https://play.google.com/store/apps/details?id=com.flossga.android.whichbutton|Key Tester]]** Android app
* **[[https://play.google.com/store/apps/details?id=aws.apps.keyeventdisplay|KeyEvent Display]]** Android app
* **[[https://play.google.com/store/apps/details?id=ru.elron.gamepadtester|Gamepad tester]]** Android app
* **[[https://stackoverflow.com/questions/11768356/need-table-of-key-codes-for-android-and-presenter|Need table of key codes for android and presenter]]**