Scott Paul
Published © LGPL

JeePioneer Radio Control

Control your Chrysler car's aftermarket radio with only cruise control buttons, thanks to Arduino

IntermediateFull instructions provided3,805
JeePioneer Radio Control

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
IR LED
×1

Hand tools and fabrication machines

Jeep

Story

Read more

Schematics

JeePioneer_Schematic.png

Code

JeePioneer

C/C++
Here is the Arduino code (.ino)
/*******************************
* Chrysler/Jeep Cruise Control to Pioneer Radio Control Adapter
* July 14 2015
* Aruido Uno tested, built on breadboard later
* Created by: Speedlimit88.com - Mcgavinz26
* Version 1_0 Working in Jeep with UNO and breadboard
*******************************/
/*
INTRODUCTION:
This program was tested on a 2005 Jeep Liberty with only cruise control buttons on the steering wheel.
The radio was a Pioneer DEH-X6600BT.  The infrared(IR) LED commands have been the same on many Pioneer
radios for a while.  This should work with most other Pioneers. Cruise control can still work if it is 
turned on, but the radio commands will also happen.  Don't be an idiot.  By trying to reproduce this 
device you are automatically saying, "If I do something stupid like trying to work radio controller and 
accidentally doing something bad with the cruise control while driving, then I take full responsibility 
for my own actions ... and I am an idiot."

TESTING NOTES:
Loose wire test results in car with wire twisted, not soldered, grounded to cigarette lighter
Analog read of pin 6, violet, under steering wheel
With the engine off, the analog reading was very stable (analog read +/-1 bit tolerance)
With the engine on, the tolerance was not so stable (analog read +/-15)
But even though it was unstable, the average seemed similar to what was read engine off.
Sketch uses 5,256 bytes (16%) of program storage space. Maximum is 32,256 bytes.
Global variables use 373 bytes (18%) of dynamic memory, leaving 1,675 bytes for local variables. 
*/

int RESpin = A4; //analog voltage read in from the steering wheel
int IRpin = 7;   //This pin is for infrared control output to mute/unmute a radio

unsigned long ButtonV = 1023;      //This is where the button pressed value is stored as it is read
unsigned long TotalButtonV = 0;    //This is the button pressed total value as it is added up for averaging
int NoPressV = 880;                //when no buttons pressed, voltage above at least this nubmer (925 was actual reading)
unsigned long ButPresCounter = 0;  //counts through the while loop while a button is pressed
int ButWhileDelay = 1100;          //counts in while loop before average is calculated
unsigned long AveButtonV = 0;      //the average value of the button voltage when pressed
int AnalogTolerance = 20;          //this the tolerance in the analog values read in (bits)
int ButtonDebounceCount = 0;       //counter to help debounce button presses, avoid double press
int VoltBitsOffset = 10;           //Use this to tweak the voltage offset if necessary on all buttons

//Button mapping: Name of button on steering wheel and its 10-bit analog voltage value
int RES_ACCEL     = 823;
int CANCEL        = 365;
int DECEL         = 561;
int SET           = 686;
int SET_CANCEL    = 320;
int SET_RES_ACCEL = 628;
int SET_DECEL     = 463;

//Connect a Pioneer radio command to a button name from above plus an offset if necessary
int VolumeUp = RES_ACCEL + VoltBitsOffset;
int PrestFwd = CANCEL + VoltBitsOffset;
int VolumeDn = DECEL + VoltBitsOffset;
int SourceSl = SET_RES_ACCEL + VoltBitsOffset;
int TrackFwd = SET_CANCEL + VoltBitsOffset;
int TrackBck = SET_DECEL + VoltBitsOffset;

//Create a high and a low range for each radio control used
int VolumeUp_ = VolumeUp + AnalogTolerance;
int _VolumeUp = VolumeUp - AnalogTolerance;
int PrestFwd_ = PrestFwd + AnalogTolerance;
int _PrestFwd = PrestFwd - AnalogTolerance;
int VolumeDn_ = VolumeDn + AnalogTolerance;
int _VolumeDn = VolumeDn - AnalogTolerance;
int SourceSl_ = SourceSl + AnalogTolerance;
int _SourceSl = SourceSl - AnalogTolerance;
int TrackFwd_ = TrackFwd + AnalogTolerance;
int _TrackFwd = TrackFwd - AnalogTolerance;
int TrackBck_ = TrackBck + AnalogTolerance;
int _TrackBck = TrackBck - AnalogTolerance;
int _SET = SET + VoltBitsOffset - 30;     //if the SET button is press, these values are used to stop averaging because
int SET_ = SET + VoltBitsOffset + 30;     // the math gets screwed up coming in and out of combo button presses

boolean SETisPressed = 0;   //this will equal 1 if the SET steering wheel button is pressed
int HoldButtonDelay = 500;  //time in milliseconds that the button is delayed after first press
int _AveButtonLast = 0;     //this is needed to know if the same button is being pressed as last time
int AveButtonLast_ = 0;

//These commands are used when pulsing the infrared LED
int TheFirstPulse = 335;
int TheFirstDelay = 4200;
int A_Dot = 500;
int A_Dash = 1525;
int IRPulses = 21;
int x = 0;

void setup(){
  pinMode(IRpin, OUTPUT); 
  digitalWrite(IRpin, HIGH); 
  Serial.begin(9600);   //only used in development for debugging
}

void loop(){
  ////Read the button voltage
  ButtonV = analogRead(RESpin);  
  
  //if only the SET button is pressed, then make use of that to avoid doing the button average calculation
  if (ButtonV > _SET && ButtonV < SET_) SETisPressed = 1;
  else SETisPressed = 0;
  
  //Button average calculation:
  while (ButtonV < NoPressV && SETisPressed == 0){    //while the voltage is low enough to indicate its pressed
    ButtonV = analogRead(RESpin);      //start adding up the voltages read
    TotalButtonV += ButtonV;
    ButPresCounter++;                  //count the times through this loop
    if (ButPresCounter > ButWhileDelay){
      AveButtonV = TotalButtonV/ButPresCounter;   //Average the voltage
      ButtonV = 1023;                  //this will kick you out of the while loop
    }
    if (ButtonV > _SET && ButtonV < SET_) SETisPressed = 1;
    else SETisPressed = 0; //this will also kick you out of the loop, helpful for multi buttons pressed
  }
  
  //this tracks how long a button is held down to avoid unintended double taps
  if (AveButtonV > _AveButtonLast && AveButtonV < AveButtonLast_){
      ButtonDebounceCount++;
    }
  else ButtonDebounceCount = 0;
  
  //if the same button was pressed as the last time a button was pressed, delay for some time
  if (ButtonDebounceCount == 1) delay(HoldButtonDelay);
  
  ///////Call an IR light command/////////////////////////////////////////
  if (AveButtonV > _VolumeUp && AveButtonV < VolumeUp_) {
    Serial.println("Volume UP");
    PioneerVolumeUp();
  }
  
  if (AveButtonV > _PrestFwd && AveButtonV < PrestFwd_) {
    Serial.println("Preset Forward");
    PioneerPrestFwd();
  }
  
  if (AveButtonV > _VolumeDn && AveButtonV < VolumeDn_) {
    Serial.println("Volume DOWN");
    PioneerVolumeDn();
  }
  
  if (AveButtonV > _SourceSl && AveButtonV < SourceSl_) {
    Serial.println("Source");
    PioneerSource();
  }
  
  if (AveButtonV > _TrackFwd && AveButtonV < TrackFwd_) {
    Serial.println("Track Forward");
    PioneerTrackFwd();
  }
   
  if (AveButtonV > _TrackBck && AveButtonV < TrackBck_) {
    Serial.println("Track Back");
    PioneerTrackBack();
  }

  //put a high and low tolerance on the analog average of the button pressed
  AveButtonLast_ = AveButtonV + AnalogTolerance;
  _AveButtonLast = AveButtonV - AnalogTolerance;
  ///Reset Counters//
  ButPresCounter = 0;
  TotalButtonV = 0;
  AveButtonV = 1023;
}

//This will pulse the IR LED a specific way that is common at the beginning of all the IR commands
//Look for it used later in all of the Pioneer commands below
void TheFirstIRsignal(){
  for (x = 0; x < TheFirstPulse; x++) {
    digitalWrite(IRpin, LOW);
    delayMicroseconds(8);
    digitalWrite(IRpin, HIGH);
    delayMicroseconds(9);
  } 
  delayMicroseconds(TheFirstDelay);
  for (x = 0; x < IRPulses; x++) {
    digitalWrite(IRpin, LOW);
    delayMicroseconds(8);
    digitalWrite(IRpin, HIGH);
    delayMicroseconds(9);
  } 
 return;
}

//This will delay the IR LED signal a short time and then send a short IR signal
void Dot(){
  delayMicroseconds(A_Dot);
  for (x = 0; x < IRPulses; x++) {
    digitalWrite(IRpin, LOW);
    delayMicroseconds(8);
    digitalWrite(IRpin, HIGH);
    delayMicroseconds(9);
  }   
 return;
}

//This will delay the IR LED signala longer time and then send a short IR signal
void Dash(){
  delayMicroseconds(A_Dash);
  for (x = 0; x < IRPulses; x++) {
    digitalWrite(IRpin, LOW);
    delayMicroseconds(8);
    digitalWrite(IRpin, HIGH);
    delayMicroseconds(9);
  }   
 return;
}

void PioneerVolumeUp(){
  TheFirstIRsignal();
  Dash();
  Dot();
  Dash();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dot();
  Dot();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dash();
  Dash();
  Dash();
  return;
}
  
void PioneerVolumeDn(){
  TheFirstIRsignal();
  Dash();
  Dot();
  Dash();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dash();
  Dot();
  Dash();
  Dot();
  Dot();
  Dot();
  Dot();
  Dot();
  Dot();
  Dash();
  Dot();
  Dash();
  Dash();
  Dash();
  Dash();
  return;
}

void PioneerTrackFwd(){
  TheFirstIRsignal();
  Dash();
  Dot();
  Dash();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dash();
  Dot();
  Dot();
  Dot();
  Dot();
  Dash();
  Dot();
  Dot();
  Dot();
  Dash();
  Dash();
  Dash();
  Dash();
  Dot();
  Dash();
  return;
}

void PioneerPrestFwd(){
  TheFirstIRsignal();
  Dash();
  Dot();
  Dash();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dot();
  Dot();
  Dot();
  Dot();
  Dot();
  Dot();
  Dash();
  Dot();
  Dash();
  Dash();
  Dash();
  Dash();
  Dash();
  Dash();
  Dot();
  Dash();
  return;
}

void PioneerMute(){ 
  TheFirstIRsignal();
  Dash();
  Dot();
  Dash();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dot();
  Dot();
  Dash();
  Dash();
  Dot();
  Dot();
  Dot();
  Dot();
  Dash();
  Dash();
  Dot();
  Dot();
  Dash();
  Dash();
  Dash();
  Dash();  
  return;
}

void PioneerSource(){ 
  TheFirstIRsignal();
  Dash();
  Dot();
  Dash();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dot();
  Dash();
  Dot();
  Dash();
  Dash();
  Dot();
  Dot();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dot();
  Dash();
  Dash();
  Dash();  
  return; 
}

void PioneerTrackBack(){ 
  TheFirstIRsignal();
  Dash();
  Dot();
  Dash();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dot();
  Dash();
  Dot();
  Dot();
  Dot();
  Dot();
  Dash();
  Dot();
  Dash();
  Dot();
  Dash();
  Dash();
  Dash();
  Dash();
  Dot();
  Dash();
  return;
}

Credits

Scott Paul

Scott Paul

2 projects • 3 followers
Hope I live to see the day Detroit gets a real Robocop. Dick Jones isn't doing anything to fix it.

Comments