/* * "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.04 */ #include #include #include #include #include #include #include /* * IR key scans for sony remotes */ #define KEYSCAN_POWER 149 #ifdef SEVEN_THRU_ZERO #define KEYSCAN_PORT1 134 // keypad 7 #define KEYSCAN_PORT2 135 // keypad 8 #define KEYSCAN_PORT3 136 // keypad 9 #define KEYSCAN_PORT4 137 // keypad 0 #else #define KEYSCAN_PORT1 131 // keypad 4 #define KEYSCAN_PORT2 132 // keypad 5 #define KEYSCAN_PORT3 133 // keypad 6 #define KEYSCAN_PORT4 134 // keypad 7 #endif #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 250 #define PWM_SLOW_SPEED 180 #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 RELAY_INPUT1_SEL_PIN 2 // low or high #define RELAY_INPUT2_SEL_PIN 3 // low or high #define MUTE_PIN 11 // mute relay (toggled) #define IR_PIN 8 // IR receiver (sensor pin wired direct) #define PWM_MOTOR_PIN_CCW 9 // motor pot (h-bridge logic input) counter-clockwise #define PWM_MOTOR_PIN_CW 10 // motor pot (h-bridge logic input) clockwise #define LED_PIN 13 // we don't normally use that /* * 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 /*********************************** * start of main C code * **********************************/ void setup() { delay(100); // Let everything wake up init_system(); } void loop() { handle_IR_keys_normal_mode(); } void do_relay_Input_selection() { #ifdef NOT if (input_selector == 1) { digitalWrite(RELAY_INPUT2_SEL_PIN, LOW); digitalWrite(RELAY_INPUT1_SEL_PIN, LOW); } else if (input_selector == 2) { digitalWrite(RELAY_INPUT2_SEL_PIN, LOW); digitalWrite(RELAY_INPUT1_SEL_PIN, HIGH); } else if (input_selector == 3) { digitalWrite(RELAY_INPUT2_SEL_PIN, HIGH); digitalWrite(RELAY_INPUT1_SEL_PIN, LOW); } else if (input_selector == 4) { digitalWrite(RELAY_INPUT2_SEL_PIN, HIGH); digitalWrite(RELAY_INPUT1_SEL_PIN, HIGH); } #endif // NOT } 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() { /* * 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) { case KEYSCAN_ARROW_ENTER: if (power == 0) break; // power was in the 'off' or 'standby' state 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 break; case KEYSCAN_ARROW_RIGHT: if (power == 0) break; // power was in the 'off' or 'standby' state 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 break; // volume DOWN case KEYSCAN_ARROW_LEFT: if (power == 0) break; // power was in the 'off' or 'standby' state if (mute == 1) break; 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 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 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 break; // volume FAST_DOWN case KEYSCAN_ARROW_DOWN: if (power == 0) break; // power was in the 'off' or 'standby' state if (mute == 1) break; 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 break; /* * input selectors */ #if 0 case KEYSCAN_PORT1: if (power == 0) return; // not allowed if power is off 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 break; #endif #if 0 case KEYSCAN_PORT2: if (power == 0) return; // not allowed if power is off 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 break; #endif #if 0 case KEYSCAN_PORT3: if (power == 0) return; // not allowed if power is off 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 break; #endif #if 0 case KEYSCAN_PORT4: if (power == 0) return; // not allowed if power is off 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 break; #endif /* * 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); break; } // 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