// Amplifier Controller // //NOTE: The ATtiny84 must be set to use the 8-MHz internal clock // This requires "Burn Bootloader" to be run first // Receives simple NEC IR commands to control an audio amplifier. // IR data is received on INT0 from a standard 38-kHz IR detector. // Start: 9ms low pulse // Space: 4.5ms high pulse // Address: 13.5ms (8 data bits) // Inverted Address: 13.5ms (8 data bits) // Command: 13.5ms (8 data bits) // Inverted Command: 13.5ms (8 data bits) // Data bit = 0: 562us low pulse followed by 562.5us high pulse // Data bit = 1: 562us low pulse followed by 1.6875ms high pulse // // Commands include: // On/Off // Input Select // Volume Up // Volume Down // // Volume is controlled using MCP41100 Digital Potentiometers // // Written by Boomer48: January 2021 // Digital Pot stuff #define Pot_Data 0 #define Pot_Clock 1 #define Pot_CS 2 #define Amp_Enable 3 #define Pot_Cmd 0x13 // command to write data to pot // The MPC41100 is 100k full scale. Signal input is on "B" input // and ground is on "A" input so zero is max volume #define Num_Steps 24 byte Pot_Steps[Num_Steps] = {255, 253, 250, 247, 243, 238, 230, 219, 205, 187, 165, 140, 115, 90, 68, 50, 36, 25, 17, 12, 8, 5, 2, 0}; byte Pot_Index = 0; #define Default_Volume 5 // IR stuff #define IR_Input 4 #define Sync_Time 3000 #define One_Time 1000 #define Zero_Time 400 volatile byte Bit_Count = 0; volatile byte Byte_Count = 0; volatile byte Byte_Ready = false; volatile byte IR_Bytes[4]; unsigned long Start_Time = 0; unsigned long Pulse_Width; // Audio input selection stuff #define Audio_Enable1 10 // external #define Audio_Enable2 9 // external #define Audio_Enable3 7 // external #define Audio_Enable4 6 // Built in MP3 player #define MP3_Power 5 // low level turns on power for built in MP3 byte Audio_Source = 0; void setup() { pinMode(Pot_Data, OUTPUT); pinMode(Pot_Clock, OUTPUT); pinMode(Pot_CS, OUTPUT); pinMode(Amp_Enable, OUTPUT); pinMode(Audio_Enable1, OUTPUT); pinMode(Audio_Enable2, OUTPUT); pinMode(Audio_Enable3, OUTPUT); pinMode(Audio_Enable4, OUTPUT); pinMode(MP3_Power, OUTPUT); digitalWrite(Pot_Data, LOW); digitalWrite(Pot_Clock, LOW); digitalWrite(Pot_CS, HIGH); digitalWrite(Audio_Enable1, LOW); digitalWrite(Audio_Enable2, LOW); digitalWrite(Audio_Enable3, LOW); digitalWrite(Audio_Enable4, LOW); digitalWrite(MP3_Power, HIGH); Update_Pot(); digitalWrite(Amp_Enable, LOW); // wait for power on to enable amplifier digitalWrite(IR_Input, HIGH); // turn on internal pullup bitClear(GIMSK, INT0); // disable INT0 external interrupt bitSet(GIMSK, PCIE0); // enable interrupt-on-change bitSet(PCMSK0, IR_Input); delay(1000); } void loop() { if (Byte_Ready == true) { Byte_Ready = false; Byte_Count++; if (Byte_Count == 4) { // got all four data bytes Byte_Count = 0; if ((IR_Bytes[0] == 0x00) && (IR_Bytes[1] == 0xFF)) { switch (IR_Bytes[2]) { case 0x16: // volume down if (Pot_Index > 0) { Pot_Index--; Update_Pot(); } break; case 0x19: // volume up if (Pot_Index < (Num_Steps - 1)) { Pot_Index++; Update_Pot(); } break; case 0x44: // source select if (digitalRead(Amp_Enable) == HIGH) { Pot_Index = 0; Update_Pot(); digitalWrite(Amp_Enable, LOW); Audio_Source = (Audio_Source + 1) % 4; digitalWrite(Audio_Enable1, LOW); digitalWrite(Audio_Enable2, LOW); digitalWrite(Audio_Enable3, LOW); digitalWrite(Audio_Enable4, LOW); digitalWrite(MP3_Power, HIGH); if (Audio_Source == 0) digitalWrite(Audio_Enable1, HIGH); else if (Audio_Source == 1) digitalWrite(Audio_Enable2, HIGH); else if (Audio_Source == 2) digitalWrite(Audio_Enable3, HIGH); else { digitalWrite(MP3_Power, LOW); digitalWrite(Audio_Enable4, HIGH); } digitalWrite(Amp_Enable, HIGH); Pot_Index = Default_Volume; Update_Pot(); } break; case 0x45: // power on/off if (digitalRead(Amp_Enable) == LOW) { digitalWrite(Amp_Enable, HIGH); digitalWrite(Audio_Enable1, HIGH); Audio_Source = 0; Pot_Index = Default_Volume; Update_Pot(); } else { Pot_Index = 0; Update_Pot(); digitalWrite(Amp_Enable, LOW); digitalWrite(Audio_Enable1, LOW); digitalWrite(Audio_Enable2, LOW); digitalWrite(Audio_Enable3, LOW); digitalWrite(Audio_Enable4, LOW); digitalWrite(MP3_Power, HIGH); } break; } } } } } // end loop void Update_Pot() { byte i = 16; digitalWrite(Pot_CS, LOW); while (i > 0) { i--; if (i >= 8) digitalWrite(Pot_Data, bitRead(Pot_Cmd, i - 8)); // MSB sent first else digitalWrite(Pot_Data, bitRead(Pot_Steps[Pot_Index], i)); // MSB sent first digitalWrite(Pot_Clock, HIGH); digitalWrite(Pot_Clock, LOW); } digitalWrite(Pot_CS, HIGH); } // Interrupt-on-change handler ISR (PCINT0_vect) { //when the pin goes HIGH record the pulse start time if (digitalRead(IR_Input) == HIGH) { Start_Time = micros(); } else // pin went low if (Start_Time != 0) { //calculate the pulse width Pulse_Width = micros() - Start_Time; //clear the timer Start_Time = 0; if (Pulse_Width > Sync_Time) { // sync bit Byte_Ready = false; Bit_Count = 0; Byte_Count = 0; } else if (Pulse_Width > One_Time) { // data bit = 1 bitSet(IR_Bytes[Byte_Count], Bit_Count); Bit_Count++; } else if (Pulse_Width > Zero_Time) { // data bit = 0 bitClear(IR_Bytes[Byte_Count], Bit_Count); Bit_Count++; } else Bit_Count = 0; // error if (Bit_Count == 8) { Byte_Ready = true; Bit_Count = 0; } } }