/* * "MVCmaster(tm), the motorized volume control edition" ;) * * An open-source arduino controller-based motorized volume control, for home stereo use * * Author: Bryan Levin (linux-works) * * Revision: v1.06 (17-feb-2010) * Revision: v1.07 (17-feb-2010) * Revision: v1.09 (18-feb-2010) * Revision: v1.10 (19-feb-2010) */ #include #include #include #include #include #include #include #define USE_INPUT_SEL 1 #define PWM_DC 255 /* * IR key scans for sony remotes */ #define KEYSCAN_POWER 149 #define KEYSCAN_DISPLAY 1364 //#define KEYSCAN_PORT1 134 // keypad 7 //#define KEYSCAN_PORT2 135 // keypad 8 //#define KEYSCAN_PORT3 136 // keypad 9 //#define KEYSCAN_PORT4 137 // keypad 0 #define KEYSCAN_PORT1 128 // keypad 1 #define KEYSCAN_PORT2 129 // keypad 2 //#define KEYSCAN_PORT1 131 // keypad 4 //#define KEYSCAN_PORT2 132 // keypad 5 //#define KEYSCAN_PORT3 133 // keypad 6 //#define KEYSCAN_PORT4 134 // keypad 7 #define KEYSCAN_ARROW_UP 1401 #define KEYSCAN_ARROW_DOWN 1402 #define KEYSCAN_ARROW_LEFT 1403 #define KEYSCAN_ARROW_RIGHT 1404 #define KEYSCAN_ARROW_ENTER 1291 #define KEYSCAN_DEBUG1 128 // keypad 1 #define KEYSCAN_DEBUG2 129 // keypad 2 #define KEYSCAN_DEBUG3 130 // keypad 3 /* * eeprom locations (slot numbers in eeprom address space) */ #define EEPROM_MAGIC 0 #define EEPROM_POWER 1 #define EEPROM_MUTE 2 #define EEPROM_INPUT_SEL 3 // IR decoding #define start_bit 2000 // Start bit threshold (Microseconds) #define bin_1 1000 // Binary 1 threshold (Microseconds) #define bin_0 400 // Binary 0 threshold (Microseconds) #define PULSE_DUR 1000 #define PULSE_2200 2200 #define SONY_IR_PROTO_PULSE_TRAIN_LEN 11 // misc timing constants #define MOTOR_KEYSCAN_DELAY 400 #define MOTOR_KEYSCAN_SHORT_DELAY 200 #define PWM_SLOW_SPEED 255 // 160 #define PWM_FAST_SPEED 255 #define PWM_OFF 0 #define KEYPRESS_DEBOUNCE_LONG_DELAY 500 /* * arduino (not atmel!) pins for physical computing i/o */ #define PIN0_RESERVED 0 // pin0 reserved for tx/rx data #define PIN1_RESERVED 1 // pin1 reserved for tx/rx data #define LED_PWM 5 // pwm dimming of our mode_button led #define LED_RED_ON 6 #define LED_BLUE_ON 7 #define IR_PIN 8 // IR receiver (sensor pin wired direct) #define KNOB_LED_PWM_PIN 1 // dummy, for now #define MODE_BUTTON 10 // multipurpose SPST N.O. push button #define PWM_MOTOR_PIN_CCW 11 // motor pot (h-bridge logic input) counter-clockwise #define PWM_MOTOR_PIN_CW 12 // motor pot (h-bridge logic input) clockwise //#define LED_PIN 13 // we don't normally use that #define LED_PIN 9 // we don't normally use that #define MUTE_PIN 14 // mute relay (toggled) #define RELAY_INPUT1_SEL_PIN 15 // low or high #define RELAY_INPUT2_SEL_PIN 16 // low or high /* * globals */ //byte ms,ls; int key; byte power = 1; byte mute = 0; byte input_selector = 1; unsigned long last_button_press_ts = 0; // reset after each user button press byte display_button_led = 1; // 1=show the led; 0=turn led entirely off (dark mode) /*********************************** * start of main C code * **********************************/ void setup() { init_system(); delay(1000); // wait 1 second on power up, ignoring the IR and front buttons //analogWrite(LED_PWM, PWM_DC); // half bright the mode button LED digitalWrite(LED_PWM, HIGH); display_button_led = 1; // its on } void loop() { handle_IR_keys_normal_mode(); } void do_toggle_Input_selection() { // toggle between inputs 1 and 2 if (input_selector == 1) { input_selector = 2; } else { input_selector = 1; } change_input_selector(1); // change AND write new val to EEPROM delay(1000 /*KEYPRESS_DEBOUNCE_LONG_DELAY*/); // debounce } void do_relay_Input_selection() { if (input_selector == 1) { digitalWrite(RELAY_INPUT2_SEL_PIN, LOW); digitalWrite(RELAY_INPUT1_SEL_PIN, LOW); digitalWrite(LED_RED_ON, HIGH); // high turns OFF the led! digitalWrite(LED_BLUE_ON, LOW); // high turns OFF the led! } else if (input_selector == 2) { digitalWrite(RELAY_INPUT2_SEL_PIN, LOW); digitalWrite(RELAY_INPUT1_SEL_PIN, HIGH); digitalWrite(LED_RED_ON, LOW); // high turns OFF the led! digitalWrite(LED_BLUE_ON, HIGH); // high turns OFF the led! } else if (input_selector == 3) { digitalWrite(RELAY_INPUT2_SEL_PIN, HIGH); digitalWrite(RELAY_INPUT1_SEL_PIN, LOW); digitalWrite(LED_RED_ON, HIGH); // high turns OFF the led! digitalWrite(LED_BLUE_ON, HIGH); // high turns OFF the led! } else if (input_selector == 4) { digitalWrite(RELAY_INPUT2_SEL_PIN, HIGH); digitalWrite(RELAY_INPUT1_SEL_PIN, HIGH); digitalWrite(LED_RED_ON, HIGH); // high turns OFF the led! digitalWrite(LED_BLUE_ON, HIGH); // high turns OFF the led! } } void change_input_selector(byte write_to_eeprom_flag) { if (write_to_eeprom_flag != 0) { // save the new input selector in EEPROM EEwrite(EEPROM_INPUT_SEL, input_selector); } // engage the right relays based on this new input-selector do_relay_Input_selection(); } void turn_off_motors() { analogWrite(PWM_MOTOR_PIN_CW, PWM_OFF); // turn off both PWM pins at start of routine analogWrite(PWM_MOTOR_PIN_CCW, PWM_OFF); // (same) } void handle_IR_keys_normal_mode() { byte front_button_val; /* * scan the 'keyboard' (front panel buttons) * note, there is no debounce since I'm assuming the user installed a cheap .01uF cap for debounce */ front_button_val = digitalRead(MODE_BUTTON); if (front_button_val == LOW) { do_toggle_Input_selection(); } /* * we got a valid IR start pulse! fetch the keycode, now. */ key = get_IR_key(); if ( (key == 0) || (key == -1) ) { turn_off_motors(); return; } switch (key) { #ifdef USE_MUTE case KEYSCAN_ARROW_ENTER: if (power == 0) break; // power was in the 'off' or 'standby' state #ifdef DEBUG // debug! for (int ii=0; ii<5; ii++) { digitalWrite(LED_PIN, 0); // temporarily turn off LED delay(500); digitalWrite(LED_PIN, 1); // temporarily turn off LED delay(500); } #endif digitalWrite(LED_PIN, 0); // temporarily turn off LED turn_off_motors(); if (mute == 1) { mute = 0; // (un)mute relay } else { mute = 1; // mute relay } digitalWrite(MUTE_PIN, mute); EEwrite(EEPROM_MUTE, mute); delay(KEYPRESS_DEBOUNCE_LONG_DELAY); // debounce if (display_button_led == 1) { digitalWrite(LED_PIN, 1); // restore led } break; #endif case KEYSCAN_ARROW_RIGHT: if (power == 0) break; // power was in the 'off' or 'standby' state digitalWrite(LED_PIN, 0); // temporarily turn off LED if (mute == 1) { mute = 0; digitalWrite(MUTE_PIN, mute); EEwrite(EEPROM_MUTE, mute); } analogWrite(PWM_MOTOR_PIN_CCW, PWM_OFF); // turn off opposite PWM pin first! analogWrite(PWM_MOTOR_PIN_CW, PWM_SLOW_SPEED); // PWM pin to control pot motor (clockwise) delay(MOTOR_KEYSCAN_SHORT_DELAY); // debounce if (display_button_led == 1) { digitalWrite(LED_PIN, 1); // restore led } break; // volume DOWN case KEYSCAN_ARROW_LEFT: if (power == 0) break; // power was in the 'off' or 'standby' state if (mute == 1) break; digitalWrite(LED_PIN, 0); // temporarily turn off LED analogWrite(PWM_MOTOR_PIN_CW, PWM_OFF); // turn off opposite PWM pin first! analogWrite(PWM_MOTOR_PIN_CCW, PWM_SLOW_SPEED); // PWM pin to control pot motor (counter-clockwise) delay(MOTOR_KEYSCAN_SHORT_DELAY); // debounce if (display_button_led == 1) { digitalWrite(LED_PIN, 1); // restore led } break; /* * fast-up and fast-down via up-arrow and down-arrow keys */ // volume FAST_UP case KEYSCAN_ARROW_UP: if (power == 0) break; // power was in the 'off' or 'standby' state digitalWrite(LED_PIN, 0); // temporarily turn off LED if (mute == 1) { mute = 0; digitalWrite(MUTE_PIN, mute); EEwrite(EEPROM_MUTE, mute); } analogWrite(PWM_MOTOR_PIN_CCW, PWM_OFF); // turn off opposite PWM pin first! analogWrite(PWM_MOTOR_PIN_CW, PWM_FAST_SPEED); // PWM pin to control pot motor (clockwise) delay(MOTOR_KEYSCAN_DELAY); // debounce if (display_button_led == 1) { digitalWrite(LED_PIN, 1); // restore led } break; // volume FAST_DOWN case KEYSCAN_ARROW_DOWN: if (power == 0) break; // power was in the 'off' or 'standby' state if (mute == 1) break; digitalWrite(LED_PIN, 0); // temporarily turn off LED analogWrite(PWM_MOTOR_PIN_CW, PWM_OFF); // turn off opposite PWM pin first! analogWrite(PWM_MOTOR_PIN_CCW, PWM_FAST_SPEED); // PWM pin to control pot motor (counter-clockwise) delay(MOTOR_KEYSCAN_DELAY); // debounce if (display_button_led == 1) { digitalWrite(LED_PIN, 1); // restore led } break; /* * input selectors */ case KEYSCAN_PORT1: if (power == 0) return; // not allowed if power is off digitalWrite(LED_PIN, 0); // temporarily turn off LED turn_off_motors(); input_selector = 1; change_input_selector(1); last_button_press_ts = millis(); // save timestamp of last buttonpress delay(KEYPRESS_DEBOUNCE_LONG_DELAY); //Debounce switch if (display_button_led == 1) { digitalWrite(LED_PIN, 1); // restore led } // digitalWrite(LED_PIN, 1); // restore led break; case KEYSCAN_PORT2: if (power == 0) return; // not allowed if power is off digitalWrite(LED_PIN, 0); // temporarily turn off LED turn_off_motors(); input_selector = 2; change_input_selector(1); last_button_press_ts = millis(); // save timestamp of last buttonpress delay(KEYPRESS_DEBOUNCE_LONG_DELAY); //Debounce switch if (display_button_led == 1) { digitalWrite(LED_PIN, 1); // restore led } break; case KEYSCAN_DISPLAY: if (power == 0) return; // not allowed if power is off digitalWrite(LED_PIN, 0); // temporarily turn off LED if (display_button_led == 1) { digitalWrite(LED_RED_ON, HIGH); // high turns OFF the led! digitalWrite(LED_BLUE_ON, HIGH); // high turns OFF the led! //analogWrite(KNOB_LED_PWM_PIN, 0); // turn brightness to lowest on the knob led //digitalWrite(LED_PWM, LOW); digitalWrite(LED_PIN, LOW); display_button_led = 0; } else { display_button_led = 1; digitalWrite(LED_PIN, HIGH); // restore led do_relay_Input_selection(); // turn led back on again (handle the correct led color) //analogWrite(KNOB_LED_PWM_PIN, PWM_DC); // turn brightness to middle on the knob led //digitalWrite(LED_PWM, HIGH); } last_button_press_ts = millis(); // save timestamp of last buttonpress delay(KEYPRESS_DEBOUNCE_LONG_DELAY); //Debounce switch break; #if 0 case KEYSCAN_PORT3: if (power == 0) return; // not allowed if power is off digitalWrite(LED_PIN, 0); // temporarily turn off LED turn_off_motors(); input_selector = 3; change_input_selector(1); last_button_press_ts = millis(); // save timestamp of last buttonpress delay(KEYPRESS_DEBOUNCE_LONG_DELAY); //Debounce switch digitalWrite(LED_PIN, 1); // restore led break; case KEYSCAN_PORT4: if (power == 0) return; // not allowed if power is off digitalWrite(LED_PIN, 0); // temporarily turn off LED turn_off_motors(); input_selector = 4; change_input_selector(1); last_button_press_ts = millis(); // save timestamp of last buttonpress delay(KEYPRESS_DEBOUNCE_LONG_DELAY); //Debounce switch digitalWrite(LED_PIN, 1); // restore led break; #endif #if 0 case KEYSCAN_POWER: // debug! for (int ii=0; ii<10; ii++) { digitalWrite(LED_PIN, 0); // temporarily turn off LED delay(200); digitalWrite(LED_PIN, 1); // temporarily turn off LED delay(200); } digitalWrite(LED_PIN, 0); // temporarily turn off LED break; #endif /* * power button */ #ifdef ALLOW_POWER_BUTTON case KEYSCAN_POWER: if (power == 0) { // power was in the 'off' or 'standby' state power = 1; // we're now power=on mute = 0; // disable mute on power-up events // power was ON when user pressed the power button } else { power = 0; // we're now power=off mute = 0; // disable mute on power-up events } EEwrite(EEPROM_POWER, power); EEwrite(EEPROM_MUTE, mute); EEwrite(EEPROM_INPUT_SEL, input_selector); // we didn't change this, but we want to save it //digitalWrite(LED_PIN, power); digitalWrite(MUTE_PIN, mute); last_button_press_ts = millis(); // save timestamp of last buttonpress delay(KEYPRESS_DEBOUNCE_LONG_DELAY); digitalWrite(LED_PIN, power); // restore led break; #endif // power button } // switch } int get_IR_key() { int data[SONY_IR_PROTO_PULSE_TRAIN_LEN+1]; byte i; int result = 0; int seed = 1; if (pulseIn(IR_PIN, LOW, PULSE_DUR) < PULSE_2200) { return -1; } for (i=0; i bin_1) { // is it a 1? data[i] = 1; } else { if (data[i] > bin_0) { // is it a 0? data[i] = 0; } else { data[i] = 2; // Flag the data as invalid; I don't know what it is! } } } for (i=0; i 1) { return -1; // Return -1 on invalid data } } for (i=0; i