Steering Wheel Controls
This was tricky to setup. For my car, Toyota, they use a particular resistance for each button pressed. I ended up using a ESP32 and a voltage divider to accomplish this. I had a total of 5 buttons, Volume Up, Volume Down, Next Track, Previous Track and Mode.
Below are the values measured for each button pressed. The mode button and the combo switch were both using a separate wire and a Ground, leaving me with a total of 3 wires to work with.
Combo
Float = 100k
Volume+ = 1k
Volume- = 3.1k
NextTrack = 1.8ohms
PrevTrack = 330ohms
MODE Switch
Float = 200k
Press = 100k
Below is a wiring diagram of the Combo and Mode switches.
With these values, I calculated that using a Resistor value of 1k gives me significant voltage steps between all buttons being pressed for the COMBO switch. The easiest was the MODE switch as there were only 2 resistance values, just wire the mode switch directly to any ADC GPIO and check the voltages produced when pressed vs floating and that’s the mode switch done. So for example, a good voltage step for the mode switch would be 1v when pressed and 3v when floating, or vice versa. Note that you need to use a dedicated ADC on the ESP for this to work. From memory there are around 4 or 5 ADC inputs on the ESP32.
Obviously, the COMBO switch was a little trickier. For this I used a 1K resistor which gave me voltage reading with steps far apart that I could read the different buttons being pressed. For example,
if (id(adc_sensor).state < 1) {
id(next_trk).toggle();
}
else if ((id(adc_sensor).state >= 2) and (id(adc_sensor).state <= 3)) {
id(previous_trk).toggle();
}
else if ((id(adc_sensor).state > 5) and (id(adc_sensor).state <= 6)) {
id(vol_up).toggle();
}
else if ((id(adc_sensor).state > 8) and (id(adc_sensor).state <= 9)) {
id(vol_down).toggle();
}
With the following Lambda, When the ADC reads a voltage below 1v, it will activate the Next Track output on the ESP32, when the ADC reads a voltage between 2 and 3v, it will activate the output for Previous Track, when the ADC reads a voltage between 5 and 6v, it will activate the output for Volume Up and so on. This allows for some inconsistencies in the ADC readings to still be able to activate the correct function for the button pressed. The Voltage Divider wiring diagram is below.
3V3 >---|____|---.---(O---.---O O---|___|---. Volume- 3100
1k | | ___ ___ |
| }---O O---|___|---{ Volume+ 1000
| | ___ ___ |
ADC <------------' }---O O---|___|---{ PrevTrack 330
| ___ ___ |
`---O O---|___|---{ NextTrack 2
|
0V <----------------(O---------------------'
Next, I used ESPHome to flash the ESP as this was the easier for me. The code is below.
sensor:
- platform: adc
pin: 34
name: "VCC Voltage"
id: adc_sensor
update_interval: 0.3s
attenuation: auto
filters:
- multiply: 3.3
on_value:
then:
lambda: |-
if (id(adc_sensor).state < 1) {
id(next_trk).toggle();
}
else if ((id(adc_sensor).state >= 2) and (id(adc_sensor).state <= 3)) {
id(previous_trk).toggle();
}
else if ((id(adc_sensor).state > 5) and (id(adc_sensor).state <= 6)) {
id(vol_up).toggle();
}
else if ((id(adc_sensor).state > 8) and (id(adc_sensor).state <= 9)) {
id(vol_down).toggle();
}
switch:
- platform: gpio
pin: GPIO17
id: vol_up
name: "volume_up"
inverted: true
on_turn_on:
then:
- delay: 200ms
- switch.turn_off: vol_up
- platform: gpio
pin: GPIO27
id: vol_down
name: "volume_down"
inverted: true
on_turn_on:
then:
- delay: 200ms
- switch.turn_off: vol_down
- platform: gpio
pin: GPIO21
id: next_trk
name: "next_track"
inverted: true
on_turn_on:
then:
- delay: 200ms
- switch.turn_off: next_trk
- platform: gpio
pin: GPIO22
id: previous_trk
name: "previous_track"
inverted: true
on_turn_on:
then:
- delay: 200ms
- switch.turn_off: previous_trk
We now have our ADC inputs mapped to the appropriate outputs. This means when we push the Volume Up button, it should activate the Volume Up output on GPIO 17 for example. This will give us a short to GROUND as it is a PULL-DOWN output on GPIO 17.
Now, we wire the outputs to the Raspberry PI. Choose 5 GPIO inputs on the Raspberry PI to use. I had some issues with certain GPIOs just refusing to work, so if you set everything up and 1 or 2 buttons don’t function, just try different GPIOs.
Once wired, you need to define the buttons and GPIOs in the Android kernel. By default, the LineageOS image has the ability to enable volume controls on GPIOs 20 and 26.
Now, we can tell LineageOS what each button does, using Key Codes. See here for a full list of the available key codes. Edit your /boot/config.txt and add your required key codes for the correct GPIO pins. Here is the GPIOs and the key codes I used.
# Keys
dtoverlay=gpio-key,gpio=26,keycode=115,label="VOLUME_UP"
dtoverlay=gpio-key,gpio=20,keycode=114,label="VOLUME_DOWN"
dtoverlay=gpio-key,gpio=21,keycode=165,label="MEDIA_PREVIOUS"
dtoverlay=gpio-key,gpio=13,keycode=163,label="MEDIA_NEXT"
dtoverlay=gpio-key,gpio=4,keycode=580,label="APP_SWITCH"
With this, all volume keys work, as well as next and previous track. My “Mode” switch on the steering wheel works as the app switcher, but this could do whatever you like, just adjust the key code accordingly.