Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SWI key remapping #1

Open
lbdroid opened this issue Feb 24, 2017 · 8 comments
Open

SWI key remapping #1

lbdroid opened this issue Feb 24, 2017 · 8 comments

Comments

@lbdroid
Copy link
Owner

lbdroid commented Feb 24, 2017

In development currently.

This is, lets say "inspired by" someone else's work, found here;
https://forum.xda-developers.com/android-auto/android-head-units/joying-2gb-steering-wheel-key-t3543390

So the problem with the SWI controls interface, is that it only provides a very rigid subset of the appropriate set of tasks that should be possible via the steering wheel buttons interface.

The above link is to a modified version of the package that maps steering wheel buttons to various actual functions. It is a good start, but it is also severely limited.

Limitations of the FACTORY system;
Lacks the ability to map many useful functions to steering wheel buttons. Things like HOME, BACK, OVERVIEW, etc.

Limitations of the MODIFIED version;

  1. It works by breaking the flow of the factory version, and sending a keycode over to a SHELL SCRIPT. This means overhead and processing delay,
  2. In order to preserve some factory functions that interact specifically with a bunch of factory programs, like the AMFM radio application -- think being able to map buttons to functions like switching frequencies, it was not able to free up enough available "keycodes" to map against a steering wheel with "a lot" of buttons. I think it is limited to EIGHT or fewer truly programmable buttons, whereas my steering wheel has 11 buttons. This means that I was forced to map more of the buttons to these "preserved" functions than I want. Since I always have the AM radio on, it will ALWAYS consume these functions, and my buttons are therefore useless.

So I'm going to change the approach of this quite dramatically compared to the current modified version and do it like this;

-- No more shell script. I'm going to use SystemProperties to store the functions I want mapped to the mcukeys.
-- The SystemProperties will be of the form "persist.mcu.keyname" and will have either a string or integer value as defined;
0: DEFAULT FUNCTION

0: inject key event of that specific value
-1: send this key to the shell script
<-1: this key will support -val MULTI-FUNCTIONs via MULTI-PRESS. For instance; -3: this key supports 3 functions.
{string}: of the form "package/classname" -- the activity component that should be launched with this key.

For multi-press modes, there must also match up an equal number of additional system properties that define the different functions of this key, for example;

persist.mcu.mode = -3
persist.mcu.mode.1 = 0
persist.mcu.mode.2 = 3
persist.mcu.mode.3 = "com.google.android.apps.maps/com.google.android.maps.MapsActivity"

Where single press performs DEFAULT FUNCTION, double press injects a keycode of "3" (HOME), and triple press will launch Google Maps.

The way this will work, is it will keep an integer array that stores the number of times that a key has been pressed. When a function is executed, it will reset the value in that array to 0.

When a button is pressed, it will increment the array by 1, and pass the NEW value of that cell into a THREAD, which will sleep for a fixed short time, and check if the value in the array has been updated. IF IT HAS NOT, then it will look up the function to perform from the system property, do it, and then reset the array cell to zero. If it HAS been updated, then we do nothing, because the thread started by the next button press will look after it.

It might be necessary to also store a value indicating the time that a "first press" was issued, so our thread dies before it resets the counter to zero, but it happened more than, say, 5 seconds ago, then we can reset it at that point.

I will also make it able to map more than one set of the "default" functions to a single button. For example, I would like to take the next and back buttons and make them such that ONE press is "next frequency", TWO press is "next saved channel", and THREE press is "seek next". Or something to that effect. I think maybe what I will do is use string value MCU#, so for instance....

persist.mcu.up = -3
persist.mcu.up.1 = 0
persist.mcu.up.2 = MCU4
persist.mcu.up.3 = MCU7

Where MCU4 would correspond to the 4th mcuKey function, and MCU7 would correspond to the 7th.

All of this should be possible through modification to HandlerMain.class, which fortunately, appears to have decompiled correctly, so I should be able to modify it in the Java code and then mix it into the smali code.

@lbdroid
Copy link
Owner Author

lbdroid commented Mar 1, 2017

This MCU black box thing we have stuck in the middle here is a real thorn in my side. I've tried picking up the key code from the "media" keys -- the MCU simply does not transmit them when the radio is on. I've tried programming the MCU with keys outside of the standard key range, and it ignores that (no key code).

Clearly, it is more trouble to hack the MCU than it is worth, so I'm going to take an alternative and universal approach -- https://www.adafruit.com/products/2995 -- this thing is a bluetooth low energy transceiver with a cortex M0, 10 analog inputs (among other things), and comes with sample source code for implementing HID KEYBOARD protocol. Some minor variations to that and the steering wheel will BECOME an actual keyboard, rather than feeding signals into the abyss.

Internal pull up resistor on the ATSAMD21G (the brain on those boards) is 40k ohm. This wont provide a sensible resolution for SWI (0.027V-0.405V relative to 3.3V), which means we will have to use an external pull-up resistor.

My SWI seems to have resistance in the range of 330-5600 ohm (this is with a modification though, from the factory, it only went up to 3200 ohm -- BUT!! The chinese radio was fine at reading it at that), so if we went with a pull up resistor that is around 1000-2200 ohm, we would end up having a sensible resolution. At 3.3V reference and 1000 ohm, we would end up with readings in the range of 0.82V-2.80V. At 2200 ohm, readings in the range of 0.43V-2.37V. Half way between at 1600 ohm would give readings of 0.56V-2.57V, about as close to ideal as possible, since it puts the button with lowest resistance at ~0.6V off of zero, and it puts the button with highest resistance at 0.7 volts off of reference.

@gustden
Copy link

gustden commented Mar 2, 2017

I love your work! The BT tethering works perfect, along with the dashcam. Glad my work inspired you to work on this.

I agree, not being able to free up enough "codes" is a big issue, limited by the MCU. I did play around with storing the command in an XML file and reading that periodically instead of launching a shell script. Then it expanded to me wanting to do pre/post actions, such as pausing current player, calculating the next music source based on current task list, followed by a button press or play action a couple seconds later. I would have ended up with essentially my own programming language within the XML to accommodate various tasks. I'm more of a c/unix programmer and agree my "shell" solution isn't pretty :)

@lbdroid
Copy link
Owner Author

lbdroid commented Mar 3, 2017

Thanks. I actually have an updated version of the bluetooth tethering program to upload, fixes a minor glitch in the order of some intents hitting when the phone boots up that resulted in the tethering service not enabling after the phone is rebooted. I'll make a point of adding it to THIS repository tomorrow.

I was notified by a store that my order of two of those "Feather M0 Bluefruit"'s are ready for pickup, so I'm going to have them in my hand tomorrow morning to start playing with. We can do a lot more with them than just generate proper key presses, since it can actually support multiple profiles simultaneously, and it may even be able to send key presses up the existing SWI inputs. I'll have to check how much current comes out of them though.

Hey, do you happen to know if these things will read media keys on a standard keyboard and pass them into the MCU?

@lbdroid
Copy link
Owner Author

lbdroid commented Mar 4, 2017

I have the boards and have been playing around with them a little bit. Really really easy to work with. I already have it sending key codes to an Android device. Also confirmed that the car radios do work with BT LE. This is definitely viable.

@lbdroid
Copy link
Owner Author

lbdroid commented Mar 6, 2017

So this happened;

https://github.com/lbdroid/JoyingIntelStuff/tree/master/hidcontrolkey

This is a modification of the adafruit hidcontrolkey demo, which can read and respond to input voltage on lines A1 and A2.

There are a number of variables that need to be set to correspond to the inputs.
buttonsAVal[] -- this is the list of input readings corresponding to the A1 input line. This array can be lengthened or shortened as needed, along with adjusting buttonsANum.

buttonsBVal[] -- same as buttonsAVal[], except for A2 input line.

buttonThresh -- the inputs are analog, so they can jump around a bit. If you set an input value to, for example, 250, and a threshold of 25, then it will accept any input in the range of [226,274].

buttonLongPress -- the number of 10 ms (approx) cycles that a button must be held down for to correspond to a "long press". So a value of "50" will mean about 500 ms, or half of a second. The reason it is approximate is because it works by incrementing a ticker each time it cycles the main loop, and of course, it actually takes time to run the loop, which means that the cycle time will be a tiny bit higher than 10 ms.

Now for the "requirements"....

  1. Adafruit Feather M0 Bluefruit LE (these cost $30)
  2. A couple of 1.5k resistors (about 1 cent)

And the SETUP:

  1. Take one of those resistors and connect it from the "3V" pin (which is actually 3.3V) to "A1". Take the other resistor and connect it from "3V" to "A2".
  2. Take your SWI1 wire, disconnect it from the car radio, and connect it to "A1". Take SWI2, disconnect it from car radio, connect to "A2". Take SWIG, and you should probably be able to just leave it hooked up to the car chassis/negative and call it good, alternatively, you can disconnect it and hook it up to the GND pin on the Feather M0.
  3. Download, install, setup ARDUINO IDE as per these links;
    https://learn.adafruit.com/adafruit-feather-m0-bluefruit-le/setup
    https://learn.adafruit.com/adafruit-feather-m0-bluefruit-le/using-with-arduino-ide
  4. Load sketch linked above, modify to suit, save, and upload.
  5. Install https://github.com/lbdroid/JoyingIntelStuff/blob/master/BTTether.apk to car radio, open it, and click the "launch bluetooth settings" button, then PAIR the car radio with the Bluefruit.
  6. Press SWI buttons and observe the car radio responding to them.

Note: Of course, you do need to POWER the thing. You can use a micro-USB cable. It doesn't have to be a "good" cable, just a power only cable will suffice. Alternatively, you can power it with 5V directly to the "USB" and "GND" pins.
**** IMPORTANT!!!! DO NOT apply car power voltage to the USB pin on the board. The USB input pin feeds directly into the MCP73831/2 battery charge controller, which has an absolute maximum input voltage of 7.0. Car power will cook it, so either feed it from an existing safe source of 5V (like the USB ports on the car radio), or add a safe source of 5V.

So now you're going to say... but, you've got a USB device plugged into a USB port, why don't we just use USB? Of course we COULD do that, and that would be fine, except that there would be a lot more work to do this -- in particular, we would have to write something to run on the radio that listens for commands from our USB device. While not the most challenging bit of programming, it is just that much simpler to leverage capabilities that the device already has.

There is a lot of room for this program to be improved. Above is just a "quick and dirty". For example; all of the code for handling the two input lines is dumbly duplicated when it doesn't have to be. The "counter" approach is ugly as hell and should be comparing time instead. We could also significantly decrease the required button input threshold by applying a smoothing filter to the inputs, even just plain old averaging. There is also room to consider multiple simultaneous key presses -- and I mean within the SAME input -- resistance of circuit = sum of series resistances, inverse resistance of circuit = sum of inverses of parallel resistances.

@lbdroid
Copy link
Owner Author

lbdroid commented Apr 18, 2017

I really hate chinese hardware.
While the chinese car radios are able to pair with a BTLE device, it turns out that they don't like BTLE-HID. They simply don't respond to HID commands over BTLE.

@gtxaspec
Copy link

Have you tried this with 6.0?

@lbdroid
Copy link
Owner Author

lbdroid commented May 12, 2017 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants