Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * Arduino as a replacement for the Klipsch Promedia (Ultra) 5.1 Control Pod
- *
- * I HAVE ONLY TESTED THIS ON THE ULTRA, BUT IT SHOULD BE COMPATIBLE
- * WITH THE STANDARD SYSTEM TOO, ACCORDING TO THIS:
- * http://www.thompdale.com/bash_amplifier/5-1/Control_pods/5-1_control_pod.htm
- *
- * Sketch: KlipschPM51UltraControlPod.ino
- * Date: 2012-06-05
- *
- * Author: Christopher Zurcher
- * Email: zurcher+klipschpod@gmail.com
- *
- * Credits:
- * Dale Thompson and Evan Shultz for the schematics:
- * http://www.thompdale.com/bash_amplifier/5-1/5-1_bash_amp.htm
- *
- * Ken Shirriff for the IR Receiver library:
- * http://www.arcfn.com/2009/08/multi-protocol-infrared-remote-library.html
- *
- * Arduino Playground for the Timer1 Library
- * http://arduino.cc/playground/Code/Timer1
- *
- */
- #include <Wire.h> //I2C library
- #include <TimerOne.h>
- #include <IRremote.h> //IRremote uses Timer2
- /////////////
- //CONSTANTS//
- /////////////
- #define ATT_MAX VOL_MAX-VOL_MIN //Attenuation
- #define ATT_MIN 0
- #define VOL_MAX 80 //Goes to 80, as the factory box does
- #define VOL_MIN 1 //PT2258 attenuates down to -79db
- #define OFFSET_MAX 15 //As per factory box
- #define OFFSET_MIN -15 //Factory box was limited to -9 because only 2 characters
- #define OUT_PWR A0 //Naming pins
- #define OUT_HP A1
- #define SWITCH_0 A2
- #define SWITCH_1 A3
- #define STATE_OFF 0 //Power States
- #define STATE_PWR 1
- #define STATE_HP 2
- #define MODE_OFF 0 //Modes for if we are changing any of the offsets
- #define MODE_REAR 13
- #define MODE_CENTER 14
- #define MODE_SUB 15
- #define SEVEN_SEG_FIRST_PIN 2 //First pin of 7-seg pins (left pins 0 and 1 open for Serial)
- #define SEVEN_SEG_STROBE_OFFSET 9 //Beginning of character select pins
- ///////////
- //GLOBALS//
- ///////////
- int deviceAddress = (0x88 >> 1); //I2C address of the volume control chip (PT2258)
- //Shifted right because the Wire library wants a 7-bit address
- unsigned long offsetTime = 0; //Values used to time-out the display of offset volumes
- unsigned long offsetDelay = 3500; //In milliseconds
- //Initialize at minimum volume; never liked the factory setting of 40
- int volume = VOL_MIN; //"Current" volume
- int volPWR = VOL_MIN; //Stores PWR state volume when in HP mode
- int volHP = VOL_MIN; //Stores HP state volume when in PWR mode
- int offsetRear = 0;
- int offsetCenter = 0;
- int offsetSub = 0;
- int offsetMode = MODE_OFF;
- int state = STATE_OFF; //Initialize system as OFF
- boolean switch0 = false;
- boolean switch1 = false;
- boolean sevenSegOn = true;
- int RECV_PIN = 13; //Input pin from IR receiver
- IRrecv irrecv(RECV_PIN);
- decode_results results;
- byte sevenSegPosition = 0;
- byte sevenSegChars[4] = {10,10,10,10}; //Initialize as blank
- byte sevenSegDigits[17][7] = {{ 1,1,1,1,1,1,0 }, // = 0
- { 0,1,1,0,0,0,0 }, // = 1
- { 1,1,0,1,1,0,1 }, // = 2
- { 1,1,1,1,0,0,1 }, // = 3
- { 0,1,1,0,0,1,1 }, // = 4
- { 1,0,1,1,0,1,1 }, // = 5
- { 1,0,1,1,1,1,1 }, // = 6
- { 1,1,1,0,0,0,0 }, // = 7
- { 1,1,1,1,1,1,1 }, // = 8
- { 1,1,1,0,0,1,1 }, // = 9
- { 0,0,0,0,0,0,0 }, // = 10 Blank
- { 0,1,1,0,1,1,1 }, // = 11 H (for headphones)
- { 1,1,0,0,1,1,1 }, // = 12 P
- { 0,0,0,0,1,0,1 }, // = 13 r //Rear
- { 1,0,0,1,1,1,0 }, // = 14 C //Center
- { 1,0,1,1,0,1,1 }, // = 15 S //Sub
- { 0,0,0,0,0,0,1 }}; // = 16 - //Negative
- //////////////
- //INTERRUPTS//
- //////////////
- //Timer1 ISR
- void updateSevenSeg()
- {//Serial.println("updateSevenSeg()");
- digitalWrite(SEVEN_SEG_STROBE_OFFSET+(sevenSegPosition++),HIGH); //Turn off previous char
- sevenSegPosition &= 0x03; //Limit character select to 0-3
- sevenSegWrite(sevenSegOn?sevenSegChars[sevenSegPosition]:10); //Load the next char
- digitalWrite(SEVEN_SEG_STROBE_OFFSET+(sevenSegPosition),LOW); //Turn on next char
- }
- /////////
- //SETUP//
- /////////
- //This runs when you first plug in the system to power, not every time you "power-on"
- // because the Control pod is run on a constant 5V standby power
- void setup()
- {
- //Serial.begin(9600); //For debugging
- irrecv.enableIRIn(); // Start the IR Remote receiver
- //Setup pins for PWR_MODE and HP_MODE control
- pinMode(OUT_PWR, OUTPUT);
- pinMode(OUT_HP, OUTPUT);
- digitalWrite(OUT_PWR, HIGH); //Active Low
- digitalWrite(OUT_HP, HIGH); //Active Low
- //Setup pins for the unused switches
- pinMode(SWITCH_0, INPUT);
- pinMode(SWITCH_1, INPUT);
- digitalWrite(SWITCH_0, HIGH); //Internal Pull-Up
- digitalWrite(SWITCH_1, HIGH); //Internal Pull-Up
- //Setup pins for SevenSeg display
- for(int i = SEVEN_SEG_FIRST_PIN; i < SEVEN_SEG_FIRST_PIN+11 ; i++)
- {
- pinMode(i, OUTPUT);
- digitalWrite(i, HIGH); //Off, for now
- }
- //Digital Pin RECV_PIN is the input coming from the IR receiver module
- pinMode(RECV_PIN, INPUT);
- digitalWrite(RECV_PIN, LOW); //No Internal Pull-Up
- delay(200); //Allow power-up of PT2258
- Wire.begin(); //Initialise the connection
- mute();
- setVolumeHP(); //Set volumes to 0
- Timer1.initialize(2500); //Set a timer length of 2500 microseconds
- // (2.5 milliseconds) for sevenSeg character strobe
- Timer1.attachInterrupt(updateSevenSeg); //Attach sevenSeg update as Timer1 ISR
- }
- ////////////////////
- //LOOP a.k.a. MAIN//
- ////////////////////
- void loop()
- {//Serial.println("loop()");
- //-----------IR REMOTE CODES----------------------------------
- if ((state == STATE_OFF) && irrecv.decode(&results))
- {
- sevenSegOn = true; //Re-enable the display, in case it was left off at power-down
- switch(results.value) //Switch on received remote code
- {
- case 0xA90: //Power
- statePWR();
- break;
- case 0xDD0: //Recall
- stateHP();
- break;
- default:
- break;
- }
- delay(250);
- irrecv.resume(); // Reset to receive the next value
- }
- else if ((state != STATE_OFF) && irrecv.decode(&results)) //Check if new remote code
- {
- switch(results.value) //Do stuff based on what button was pushed
- {
- case 0xA90: //Power
- stateOFF();
- break;
- case 0xDD0: //Recall
- if(state == STATE_PWR)
- {
- stateHP();
- }
- else if(state == STATE_HP)
- {
- statePWR();
- }
- break;
- case 0x290: //Mute
- volume = VOL_MIN;
- setVolume();
- break;
- case 0x5D0: //Display
- sevenSegOn = !sevenSegOn; //Hide/show display
- break;
- case 0x490: //Volume +
- volume++;
- setVolume();
- break;
- case 0xC90: //Volume -
- volume--;
- setVolume();
- break;
- case 0x090: //Channel +
- volume = ((volume+5)/5)*5; //Up to nearest 5
- setVolume();
- break;
- case 0x890: //Channel -
- volume = (volume%5)?(volume-(volume%5)):(volume-5); //Down to nearest 5
- setVolume();
- break;
- case 0x010: //NumPad 1
- offsetModeOn(MODE_REAR, offsetRear+1); //Increase offset by 1
- break;
- case 0xC10: //NumPad 4
- if (offsetMode == MODE_REAR) //If already in this offset mode
- offsetModeOff(); //Return to regular volume display
- else
- offsetModeOn(MODE_REAR, offsetRear); //Show current offset
- break;
- case 0x610: //NumPad 7
- offsetModeOn(MODE_REAR, offsetRear-1); //Decrease offset by 1
- break;
- case 0x810: //NumPad 2
- offsetModeOn(MODE_CENTER, offsetCenter+1); //Increase offset by 1
- break;
- case 0x210: //NumPad 5
- if (offsetMode == MODE_CENTER) //If already in this offset mode
- offsetModeOff(); //Return to regular volume display
- else
- offsetModeOn(MODE_CENTER, offsetCenter); //Show current offset
- break;
- case 0xE10: //NumPad 8
- offsetModeOn(MODE_CENTER, offsetCenter-1); //Decrease offset by 1
- break;
- case 0x410: //NumPad 3
- offsetModeOn(MODE_SUB, offsetSub+1); //Increase offset by 1
- break;
- case 0xA10: //NumPad 6
- if (offsetMode == MODE_SUB) //If already in this offset mode
- offsetModeOff(); //Return to regular volume display
- else
- offsetModeOn(MODE_SUB, offsetSub); //Show current offset
- break;
- case 0x110: //NumPad 9
- offsetModeOn(MODE_SUB, offsetSub-1); //Decrease offset by 1
- break;
- default:
- break;
- }
- delay(250); //So you don't accidentally turn it up instantly to 80
- irrecv.resume(); // Reset to receive the next value
- }
- //This switches the display back to main volume after offsetDelay time has passed
- if(offsetMode)
- {
- if((millis() - offsetTime) > offsetDelay)
- {
- sevenSegChars[0] = 10;
- sevenSegChars[1] = 10;
- sevenSegChars[2] = volume/10;
- sevenSegChars[3] = volume%10;
- offsetMode = MODE_OFF;
- }
- }
- }//END LOOP
- //Helps with serial output: allows for leading zeroes
- //Only used for debugging
- //Code from here: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1279500031
- // (Reply #1, by "bootsector")
- void printHex(int num)
- {
- char tmp[16];
- char format[128];
- sprintf(format, "0x%%.%dX", 2);
- sprintf(tmp, format, num);
- Serial.println(tmp);
- }
- /////////////
- //Functions//
- /////////////
- void sevenSegWrite(byte digit)
- {//Serial.print("sevenSegWrite(byte digit)");
- for (byte segCount = 0; segCount < 7; segCount++)
- {
- digitalWrite(segCount+SEVEN_SEG_FIRST_PIN, !sevenSegDigits[digit][segCount]);
- //The "!" is because the segments are active low, but they are stored here in the code
- //in the array sevenSegDigits[][] the other way around for easier readability
- }
- return;
- }
- void mute()
- {//Serial.println("mute()");
- Wire.beginTransmission(deviceAddress);
- Wire.write(0xF9); //MUTE ON
- Wire.endTransmission();
- return;
- }
- void unmute()
- {//Serial.println("unmute()");
- Wire.beginTransmission(deviceAddress);
- Wire.write(0xF8); //MUTE OFF
- Wire.endTransmission();
- return;
- }
- void stateHP()
- {//Serial.println("stateHP()");
- state = STATE_HP;
- mute();
- offsetModeOff(); //In case user switches to HP before offsetMode times out
- digitalWrite(OUT_PWR, HIGH); //Disable speaker amps
- delayMicroseconds(15); //Empirical
- digitalWrite(OUT_HP, LOW); //Enable headphone amp
- for(int i=0; i < 8; i++) //Blink the "HP" label while we wait for the power-amp to drain
- {
- sevenSegChars[2] = (i%2)?11:10;
- sevenSegChars[3] = (i%2)?12:10;
- delay(400); //400*8 for a total of 3.2 seconds, same delay as original
- }
- sevenSegChars[2] = volume/10;
- sevenSegChars[3] = volume%10;
- volume = volHP; //Restore headphone state volume
- setVolume(); //set volume to stored HP volume level
- unmute();
- return;
- }
- void statePWR()
- {//Serial.println("statePWR()");
- state = STATE_PWR;
- mute();
- sevenSegChars[2] = volume/10;
- sevenSegChars[3] = volume%10;
- digitalWrite(OUT_HP, HIGH); //Disable headphone amp
- volume = volPWR; //Restore power state volume
- setVolume(); //set volume to stored PWR volume level
- unmute();
- delay(300); //wait for things to settle
- digitalWrite(OUT_PWR, LOW); //Enable speaker amps
- return;
- }
- void stateOFF()
- {//Serial.println("stateOFF()");
- state = STATE_OFF;
- mute();
- offsetMode = MODE_OFF; //In case user switches off before offsetMode times out
- for(int i=0; i<=3; i++)
- { //Blank out Display
- sevenSegChars[i] = 10;
- }
- digitalWrite(OUT_HP, HIGH); //Disable headphone amp
- digitalWrite(OUT_PWR, HIGH); //Disable speaker amps
- volPWR = VOL_MIN; //These 2 lines set your volumes back to minimum on power-down.
- volHP = VOL_MIN; //I like this because you don't power-on only to accidentally go deaf
- // if your roommates left it set super loud when they turned it off.
- //If you like your volume to remember where you left it, comments these out.
- delay(5000); //Don't let system turn back on for 5 seconds
- return;
- }
- void offsetModeOff()
- {//Serial.println("offsetModeOff()");
- offsetTime -= offsetDelay;
- sevenSegChars[0] = 10;
- sevenSegChars[1] = 10;
- sevenSegChars[2] = volume/10;
- sevenSegChars[3] = volume%10;
- return;
- }
- void offsetModeOn(int mode, int offset)
- {//Serial.println("offsetModeOn()");
- if(state != STATE_PWR)
- { //Don't do anything if not in powered-speaker mode
- return;
- }
- offsetMode = mode; //Set global mode
- offset = constrain(offset, OFFSET_MIN, OFFSET_MAX);
- switch(mode)
- {
- case MODE_REAR:
- offsetRear = offset;
- setVolume(); //Apply whatever offset change has occurred
- sevenSegChars[0] = MODE_REAR; //r
- break;
- case MODE_CENTER:
- offsetCenter = offset;
- setVolume();
- sevenSegChars[0] = MODE_CENTER; //C
- break;
- case MODE_SUB:
- offsetSub = offset;
- setVolume();
- sevenSegChars[0] = MODE_SUB; //S
- break;
- default:
- break;
- }
- sevenSegChars[1] = (offset < 0)?16:10; //- For negative numbers
- sevenSegChars[2] = abs(offset)/10;
- sevenSegChars[3] = abs(offset)%10;
- offsetTime = millis(); //Mark when offsetMode was most recently triggered
- return;
- }
- void setVolume()
- {//Serial.println("setVolume()");
- volume = constrain(volume, VOL_MIN, VOL_MAX); //Make sure volume is within limits
- if(state == STATE_PWR)
- {
- if(offsetMode) //Clear out the offset labeling
- {
- offsetModeOff();
- }
- volPWR = volume; //Stored so it can be retrieved after returning from HP mode
- setVolumePWR();
- }
- else
- {
- volHP = volume;
- setVolumeHP();
- }
- //Update Display
- sevenSegChars[2] = volume/10;
- sevenSegChars[3] = volume%10;
- return;
- }
- void setVolumePWR()
- {//Serial.println("setVolumePWR()");
- int att = VOL_MAX - volume; //Need to send attenuation value, not volume, to PT2258
- int tempAtt;
- int frontTens;
- int frontOnes;
- int rearTens;
- int rearOnes;
- Wire.beginTransmission(deviceAddress);
- frontTens = att/10;
- frontOnes = att%10;
- Wire.write( 0x80 | frontTens ); //FR -10db
- Wire.write( 0x90 | frontOnes ); // -1db
- Wire.write( 0x40 | frontTens ); //FL
- Wire.write( 0x50 | frontOnes );
- tempAtt = constrain(att-offsetRear, ATT_MIN, ATT_MAX);
- rearTens = tempAtt/10;
- rearOnes = tempAtt%10;
- Wire.write( 0x00 | rearTens ); //RR
- Wire.write( 0x10 | rearOnes );
- Wire.write( 0x20 | rearTens ); //RL
- Wire.write( 0x30 | rearOnes );
- tempAtt = constrain(att-offsetCenter, ATT_MIN, ATT_MAX);
- Wire.write( 0x60 | (tempAtt/10) ); //FC
- Wire.write( 0x70 | (tempAtt%10) );
- tempAtt = constrain(att-offsetSub, ATT_MIN, ATT_MAX);
- Wire.write( 0xA0 | (tempAtt/10) ); //LFE
- Wire.write( 0xB0 | (tempAtt%10) );
- Wire.endTransmission();
- return;
- }
- void setVolumeHP()
- {//Serial.println("setVolumeHP()");
- int att = VOL_MAX - volume;
- int tens = att/10;
- int ones = att%10;
- Wire.beginTransmission(deviceAddress);
- Wire.write( 0x80 | tens ); //FR -10db
- Wire.write( 0x90 | ones ); // -1db
- Wire.write( 0x40 | tens ); //FL
- Wire.write( 0x50 | ones );
- Wire.write( 0x00 | tens ); //RR
- Wire.write( 0x10 | ones );
- Wire.write( 0x20 | tens ); //RL
- Wire.write( 0x30 | ones );
- Wire.write( 0x60 | tens ); //FC
- Wire.write( 0x70 | ones );
- Wire.write( 0xA7 ); //LFE set to max attenuation
- Wire.write( 0xB9 ); //...as done in original controller
- Wire.endTransmission();
- return;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement