====== 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]]**