Ford to Kenwood radio adapter HOWTO

Updates:
[2018-04-27] - Small updates

Last time I have changed a standard Ford 6000CD radio to a new 1din Kenwood one because of poor sound quality and lack of USB port in the first one (driving with a bunch of cd's and switching during it is not very comfortable).

After that I had a non working steering wheel remote because of incompatibility with Kenwood radio remote control.

So I decided to make an adapter that will be connected between Ford remote steering wheel and my radio. Of course you can find a lot of adapters in the market, but they are not so cheap (about 36 € and up) and as you see you can do it yourself for a much more acceptable price (3-4 €).

So lets get started.

My howto is made for a Ford steering wheel remote and Kenwood (mine is a KMM-122Y model from 2017) radios with a remote wire like on a pictures below. It could be helpful for other resistance based steering wheel controls. In that case you have to change a ADC voltage reading part.

Figure 1. Ford steering wheel control Figure 2. Kenwood remote cable

What stuff we will need to make it:

First part - analog signal from Ford steering wheel control

Ford steering wheel controls are based on a resistance switch (buttons switch on different resistances) as you can see on Figure 3.

Figure 3. Steering wheel control schematic

What we need to do is to make a voltage divider with a Ford steering remote as one of the resistances, put some voltage on it and read values on a microcontroller ADC.

Figure 4. Voltage divider schematic

In a voltage divider (Figure 4) I used a 1.47 kΩ resistor because it should give pretty nice results with the remote resistances and a 5V signal.

In the place of resistor R5 should be connected Ford steering wheel control.

In a Table 1 I put measurements of resistances in my steering wheel control and related voltages on a voltage divider.

Table 1. Ford steering wheel control resistances and voltages
Function Resistance [Ω] Voltage on a divider [V]
NC 5040 3.87
SEL 1050 2.08
SEEK- 562 1.38
SEEK+ 301 0.85
VOL+ 147.8 0.46
VOL- 54.4 0.18

Whole adapter schematic is shown on Figure 5. As you can see it's quite simple.

Figure 5. Adapter schematic

In the pace of J3 there may be a reset button.

Steering wheel control pins in the FAKRA connector are shown on Figure 6 (pins 6 and 8 on B part).

Figure 6. Ford FAKRA connector

On the socket side (black one on the bottom right side) you have to connect wires as shown on Figure 7. This connections were only for testing purpose, on a final product you should make more solid connection that would not fall out of socket when driving.

Figure 7. Steering wheel remote resistance connector

The second part is the digital signal to Kenwood Radio

It turns out that Kenwood radio remote protocol is a NEC protocol, same as in IR remotes. Only inverted and with no carrier. Thanks to that it is not a resistance based one, we don't need any digital potentiometer to generate resistances as it is in some radios.

NEC protocol transmission data info:

How the transmission works:

  1. Transmissions starts with a 9ms burst and 4.5ms space
  2. 8 bits of address data
  3. 8 bits of inverted address data
  4. 8 bits of command data
  5. 8 bits of inverted command data
  6. Transmission ends with a single 562.5µs burst

Figure 8. NEC protocol message format

Now we need to generate in microcontroller a digital NEC signal related to a pressed key. This signal through a microcontroller digital output (PB1) will control a Q1 NPN transistor. Transistor will switch signal on a Kenwood remote line.

Kenwood address code: 0xb9

Command codes:

Table 2. Kenwood command codes
Remote Command Remote Command Remote Command
0 0x00 8 0x08 volume + 0x14
1 0x01 9 0x09 volume - 0x15
2 0x02 track - 0x0a mute 0x16
3 0x03 track + 0x0b tuner 0x1c
4 0x04 rev 0x0c tape 0x1d
5 0x05 ff 0x0d cd 0x1e
6 0x06 play/pause 0x0e cd-md-ch 0x1f
7 0x07 source 0x13 dnpp 0x5e

Data fomat example for signal volume+ :

Table 3. Example data format
Original data
Addr ~Addr Comm ~Comm
0xB9 0x46 0x14 0xEB
10111001 01000110 00010100 11101011
LSB
0x9D 0x62 0x28 0xD7
10011101 01100010 00101000 11010111

Final frame:

[START] 10011101 01100010 00101000 11010111 [END]

This code you need to send (using a NEC protocol with start and end signals) to a Q1 transistor.

And that's all. As you see, you can make such adapter about 10 times cheaper then a market one (without sockets, case and cables).

Maybe I will update this howto, there can be added repeat commands in the microcontroller code.

Below you can get a C code for microcontroller (Atmega8).

pilot1.c


/* pilot1.c
 * Ford SWC to Kenwood radio (NEC protocol) adapter
 * Author: Michal Babik <michalb1981@o2.pl>
 */

// Steering wheel remote resistances with a half values to use
// in adc as a compare values
//
// REMOTE KEY   RESISTANCE  ADC VAL     KEY VAL
// NC           5040.0      198         0
//                          152
// SEL          1050.0      106         1
//                          88
// SEEK -       562.0       70          2
//                          56.5
// SEEK +       301.0       43          3
//                          33
// VOL +        147.8       23          4
//                          16
// VOL -        54.4        8           5
//                          4
#define F_CPU 1000000UL

#include <avr/io.h> 
#include <util/delay.h> 
#include <inttypes.h> 

#define sbi(x,y) x |= _BV(y) //set bit - using bitwise OR operator
#define cbi(x,y) x &= ~(_BV(y)) //clear bit - using bitwise AND operator
#define tbi(x,y) x ^= _BV(y) //toggle bit - using bitwise XOR operator
#define is_high(x,y) (x & _BV(y) == _BV(y)) //check if the y'th bit of
//register 'x' is high ... test if its AND with 1 is 1
/* _BV(a) is a macro which returns the value corresponding to 2 to the 
 * power 'a'. Thus _BV(PX3) would be 0x08 or 0b00001000 */

#define NEC_TIME 562.5f // base time in us
#define NEC_LINE_HI 1   // pin value to set line high
#define NEC_LINE_LO 0   // pin value to set line low
#define NEC_PORT PORTB  // kenwood radio remote pin port
#define NEC_PIN PB1     // kenwood radio remote pin
#define NEC_DDR DDRB    // kenwood radio remote direction

#define ADC_SAMPLES 50   // number of samples to read pressed key
// ----------------------------------------------------------------------------
uint8_t adc_read (void);
uint8_t check_pressed_adc_value (uint8_t adc_v);
void couple_more_samples (uint8_t *k_pressed);
void nec_set_pin (uint8_t hilo);
void nec_base (uint8_t hm, uint8_t lm);
void nec_start (void);
void nec_finish (void);
void nec_one (void);
void nec_zero (void);
void nec_8bit (uint8_t *bits);
void nec_data (uint8_t addr, uint8_t dta);
// ----------------------------------------------------------------------------
int
main (void)
{
    /*
    uint8_t k_codes[] = {0x00,    // 0
                         0x01,    // 1
                         0x02,    // 2
                         0x03,    // 3
                         0x04,    // 4
                         0x05,    // 5
                         0x06,    // 6
                         0x07,    // 7
                         0x08,    // 8
                         0x09,    // 9
                         0x0a,    // track -
                         0x0b,    // track +
                         0x0c,    // rev
                         0x0d,    // ff
                         0x0e,    // play / pause
                         0x13,    // source
                         0x14,    // volume +
                         0x15,    // volume -
                         0x16,    // mute
                         0x1c,    // tuner
                         0x1d,    // tape
                         0x1e,    // cd
                         0x1f,    // cd-md-ch
                         0x5e     // dnpp
                         };
    */

    uint8_t ken_code     = 0xb9;
    uint8_t key_codes[]  = {0x00, 0x13, 0x0a, 0x0b, 0x14, 0x15};
    uint8_t key_pressed  = 0;
    uint8_t prev_pressed = 0;

    NEC_DDR = _BV (NEC_PIN); // Communication pin as output
    cbi (NEC_PORT, NEC_PIN); // set nec pin low

    ADMUX= (1 << REFS0) | (1 << ADLAR);  //AVCC, left align
    // only 8 highest adc bits will be used, there is no need for a 10 bit precision

    ADCSRA= (1 << ADEN) | (1 << ADPS2);  //ad enable, prescaler 16 (62.5kHz)

    while (1) {
        key_pressed = check_pressed_adc_value (adc_read ());
        if (key_pressed != prev_pressed) {
            if ((key_pressed > 0) && (prev_pressed == 0)) {
                couple_more_samples (&key_pressed);
                nec_start ();
                nec_data (ken_code, key_codes[key_pressed]);
                nec_finish ();
            }
            prev_pressed = key_pressed;
        }
    }
	return 0;
}
// ----------------------------------------------------------------------------
uint8_t
adc_read (void)
{
    //Start Single conversion
    ADCSRA |= (1 << ADSC);
    //Wait for conversion to complete
    while (!(ADCSRA & (1 << ADIF)));
    //Clear ADIF by writing one to it
    ADCSRA |= (1 << ADIF);
    return ADCH; // only 8 highest bits
}
// ----------------------------------------------------------------------------
uint8_t
check_pressed_adc_value (uint8_t adc_v)
{
    uint8_t key_val = 0; // nothing (0)
    if (adc_v < 152) {
        key_val++; // SEL (1)
        if (adc_v < 88) {
            key_val++; // SEEK - (2)
            if (adc_v < 57) {
                key_val++; // SEEK + (3)
                if (adc_v < 33) {
                    key_val++; // VOL + (4)
                    if (adc_v < 16 && adc_v > 4) {
                        key_val++; // VOL - (5)
                    }
                }
            }
        }
    }
    return key_val;
}
// ----------------------------------------------------------------------------
void
couple_more_samples (uint8_t *k_pressed)
{
    // take couple more samples and create a small histogram of
    // of values, find which key has most counts
    uint8_t key_rep[] = {0, 0, 0, 0, 0, 0};
    key_rep[*k_pressed]++;
    for (uint8_t i=0; i<ADC_SAMPLES; ++i) {
        key_rep[check_pressed_adc_value (adc_read ())]++;
    }
    uint8_t max_v = 0;
    for (uint8_t j=1; j<6; ++j) {
        if (key_rep[j] > max_v) {
            max_v = key_rep[j];
            *k_pressed = j;
        }
    }
}
// ----------------------------------------------------------------------------
void
nec_set_pin (uint8_t hilo)
{
    if (hilo) sbi (NEC_PORT, NEC_PIN); // set high
    else      cbi (NEC_PORT, NEC_PIN); // set low
}
// ----------------------------------------------------------------------------
void
nec_base (uint8_t hm, uint8_t lm)
{
    nec_set_pin (NEC_LINE_HI);
    for (uint8_t i=0; i<hm; ++i) 
        _delay_us (NEC_TIME);
    nec_set_pin (NEC_LINE_LO);
    for (uint8_t i=0; i<lm; ++i) 
        _delay_us (NEC_TIME);
}
// ----------------------------------------------------------------------------
void
nec_start (void)
{
    nec_base (16, 8); // 9ms - 1, 4.5ms - 0
}
// ----------------------------------------------------------------------------
void
nec_finish (void)
{
    nec_base (1, 0); // 562.5us - 1, 0 - 0
}
// ----------------------------------------------------------------------------
void
nec_one (void)
{
    nec_base (1, 3); // 562.5us - 1, 1.6875us - 0
}
// ----------------------------------------------------------------------------
void
nec_zero (void)
{
    nec_base (1, 1); // 562.5us - 1, 562.5us - 0
}
// ----------------------------------------------------------------------------
void
nec_8bit (uint8_t *bits)
{
    for (uint8_t i=0; i<8; ++i) {
        if (*bits & 0x01)
            nec_one ();
        else
            nec_zero ();
        *bits = *bits >> 1;
    }
}
// ----------------------------------------------------------------------------
void
nec_data (uint8_t addr, uint8_t dta)
{
    uint8_t naddr = ~addr;
    uint8_t ndta  = ~dta;
    nec_8bit (&addr);
    nec_8bit (&naddr);
    nec_8bit (&dta);
    nec_8bit (&ndta);
}
// ----------------------------------------------------------------------------

References:

  1. https://techdocs.altium.com/display/FPGA/NEC+Infrared+Transmission+Protocol
  2. https://exploreembedded.com/wiki/NEC_IR_Remote_Control_Interface_with_8051
  3. https://forum.arduino.cc/index.php?topic=358121.0
  4. http://www.panuworld.net/minidisc/fordsony/index.htm
  5. http://www.angelfire.com/nd/maza/kenwood.html
  6. http://www.kinotechnik.edis.at/pages/atmel/kw-kangoo-e.html


Return to main page