Saturday, April 28, 2012

5.1 amplifier system redesign

I always wanted a 5.1 amplifier system for my computer, but never bothered to go out and buy one. One day I got a dead 5.1 amplifier system that appeared to be ok. When I opened it up, everything seemed to be in order. No signs of overheating, voltages were good, unit turned on normally. Everything worked, but no sound. System was based on three integrated circuits and a ordinary amplifier with preamplifier.


Time to diagnose what was wrong, starting with the amplifier and preamplifier. I took a ordinary cable with 3.5 jack from old headphones, pealed off one end and exposed GND, L channel and R channel. Then I connected this cable to my old MP3 player, turned the volume down to almost mute and pressed play. Next I connected the headphones GND with my amplifiers GND and start poking around with R and L channel.  Before this I inspected the amplifier and preamplifier and looked for where the audio comes in. I also discovered that preamplifier had a MUTE option, so I had to connect this wire (MUTE) to +5V to enable sound. When I touched audio in pins on preamplifier with one of the headphones channel outputs, amplifier came to life and started playing. I tested all 6 channels (FRONT LEFT, FRONT RIGHT, CENTER, SUB, REAR LEFT, REAR RIGHT) and every one of them was ok. Sound was good, no distortion and good quality.  From this I could assume that amplifier and preamplifier is ok.
 
Next I measured the MUTE wire from main microprocessor and found that it gives out +5V to enable amplifier. So this was not the case. Only 3 more options were left. Faulty micro controller, or two audio chips.

This system used BT2323 chip as audio selector and mixer, BT2258 as volume controller and BT1610 as the system controller. After some time searching on the net, I found nothing. Then I remembered some post long ago about volume controller IC PT2258. No problem here. Found data sheet, examples and even some source code for AVR. Then I compared these two chips and found out that they were the same. Same pins, same pin out (looked on the board and traced wires).  Also tried with PT2323 and it was a hit. Same pin out same pins everything. Complete data sheets (with I2C commands) of these two ICs are included in my download. Sadly no luck for BT1610 or PT1610. Looks like it was a custom micro controller.

I assumed that the BT1610 was OK, because unit turned on normally. So it should be the fault in these two chips. BT2323 and BT2258. I searched eBay and found that they were very cheap. I immediately ordered 10 pcs of each chip (PT2258 and PT2323), put everything away and forgot about it for a month. Then the chips arrived.

PT2258 was easy to replace because it was in DIP package. PT2323 was a problem though. When I tried to remove it with a hot air station, all the cooper connections went with it. Really don't know if it was my fault or the PCB was really bad. Probably both. Well this didn't stop me and I successfully replaced PT2323, but it took me 2 hours of hand soldering little wires to the chip. After inspection and no problems found I hooked the system up and turned it on. Again no sound... This could only mean BT1610 has a fault. And it was true. I hooked up I2C line from BT1610 to Arduino I2C sniffer and found out I2C communication was dead. Thought to myself what a waste of time and money for trying to fix this. The system was beyond repair, because you couldn't get BT1610. Ok, for spare parts it is then. Once again I put everything away and forget about it for a week or so.   

One day I was walking true some stores with hi-fi audio and saw that Genius and Logitech 5.1 systems start from 90€ and I really wanted one for a long time. But then again I would have to modify them to work with my setup I have in my room. Well, will be in my room anyways.... still working on it (I have 3 computers, 4 monitors and stereo audio system that I command over Internet - and I really do mean command, not just mains power relay switching. More of this setup in my next posts, for now it is just a proof of concept). It would be a waste spending money on something that needs to be modified completely. Then I remembered I already have a 5.1 system that needs a "little" more work done and it will be completely for my needs and my desires. So I started working...

I went to get that busted 5.1 amplifier and started thinking. I inspected all of circuitry and decided on what to do next. System had one stereo input and one 6-channel input, 4 buttons, encoder, 7-segment led display and IR receiver. And this is what I wanted to do:

  • PT2323 supports 4 stereo inputs and one 6-channel (add 3 stereo inputs to my design)
  • replace 7-segment led display with cool 2x16 red LCD module with black background
  • replace IR sensor that is for 38kHz to 36kHz for a Sony remote (I did not have original remote)
  • reuse encoder and 4 buttons (Mute->On/Off, 2.1/5.1->MUTE, BASS- -> MENU, BASS+ ->OK )
  • add a relay for amplifier power (you can still hear that annoying buzz in speakers when on stand-by)
  • add serial communication for controlling over Internet
  • 3 wires for controlling amplifier coming out (MUTE, SDA, SCL)
  • 3 wires for power coming out (+5V, +12V, GND)
  • 6 wires for amplifier power coming out (-12V|GND|+12V  - RELAY-  BACK TO AMPLIFIER)
  • 20 wires coming out for LCD and FRONT PANEL:
    • LCD (GND, +5V, lcdV, back light, R, E, D4, D5, D6, D7)
    • Front panel (Button 1, 2, 3, 4, encoder 1, 2, leds, IR, GND, +5V)
  • 7 wires coming out for 3 additional stereo inputs
    • L2, R2, L3, R3, L4, R4, GND
  • prepare all the wires coming out and soldered them to boards




Before I continue I will say that all this could be mounted inside the amplifier, but I like it this way. You can see all the effort went into this, otherwise you are looking in an ordinary amplifier bought in a store. It may be stupid for someone but for me... it keeps me warm inside knowing that this is my work.

Before I started doing anything else, I connected PT2323 and PT2258 SDA, SCL and GND to Arduino, and send some data over I2C to try out if it works. I used wire library for Arduino. It is already included in Arduino IDE and all commands were listed in data sheets. A quick test was a success and amplifier was working. Now I can continue with my design. 


Now that everything seemed ok, I put it all back together. I started thinking on which micro controller to use. I wanted to go with MSP430F2274, but it is not available in DIP package and I still haven't finished my UV lamp for creating circuits. So I decided to go with a DIP package soldered on breadboard. I needed 20 available I/O pins. And as luck would have it, ATMEGA328P for Arduino Uno has them, in DIP package and also I had one left. One thing I will miss though, MSP430 series have nice low power modes and put the chip to sleep. Arduino is a little bit different. But nevertheless, I don't need sleep mode because this is not battery powered application and power is not an issue. So I started drawing the schematic with consideration of which pin will do what.


UPDATE: Missing 100nF decoupling capacitor between reset and gnd line!

Now that the schematic was done, it is time to create it on a breadboard. You can download all three schematics at the end of this post in PDF format. For the preamplifier and amplifier schematic you will have to search the net. There are tons of schematics out there.


Now that the breadboard was done, I wired it up and finished with the hardware part. I also hot-glued all the wires and holes to be air tight as much as possible. All together took me about 48 hours for the hardware part (testing, inspecting, repairing and redesign).



Now I had an 5.1 amplifier system that still didn't work. Off to programming software. First of all I went on the net and found all the libraries I needed. This is library for I2C, internal eeprom, lcd, ir, encoder and interrupts. The I2C, LCD and internal EEPROM libraries are already included in Arduino IDE. The rest I found on the Internet. IR library from Ken Shirriff, that I used before, PinChangeInt library for interrupts and AdaEncoder library for rotary encoder. So special thanks to all the authors of these libraries that made my life easier. Here is how they are included:
#include <Wire.h>                        // I2C library    
#include <LiquidCrystal.h>               // LCD library   
#include <IRremote.h>                    // IR library                   
#include <PinChangeInt.h>                // Interrupt library    
#include <AdaEncoder.h>                  // Encoder library
#include <EEPROM.h>                      // Eeprom library  
Now I won't go into details on how these libraries work and how are they imported to Arduino IDE. All I can say is that they are easy to use and very well written.

A week of intense programming and my software was finished. Here is a short description of how it works:
  • on power on read default values from eeprom and store them to global variables
  • setup micro controller
  • set default status (on or stand-by)
  • loop():
    • check serial communication if anything received
    • check IR if anything received
    • check buttons and execute commands
    • update lcd if needed
  
This is a basic overview of how the software works, but it can also do a number of different things like:
  • enable or disable remote
  • enable or disable buttons
  • change default settings and store them in eeprom
  • lcd mode (always on, on for a period of time)
  • different info displayed on lcd for a period of time (on timeout)
  • software button debounce
  • delay between IR receive
  • full control with serial communication
  • anything audio related  (channel corrections, soft volume transition, enhance, boost...) 

Code:
// 5.1 amplifier system redesign
// by Neoxy <http://www.neoxy-yx.blogspot.com>

// Special thanks to all the authors of included libraries

// Created 20 April 2012

/* Circuit:
 * LCD RS pin to digital pin 10
 * LCD Enable pin to digital pin 9
 * LCD D4 pin to digital pin 8
 * LCD D5 pin to digital pin 7
 * LCD D6 pin to digital pin 6
 * LCD D7 pin to digital pin 5
 
 * I2C SCL to analog pin 5
 * I2C SDA to analog pin 4
 
 * TX to digital pin 0
 * RX to digital pin 1
 
 * IR to digital pin 11
 
 * Relay to digital pin 4
 
 * Mute to analog 3
 
 * LED light and backlight to analog 2
 
 * Volume encoder I pin to digital pin 2  
 * Volume encoder II pin to digital pin 3
 * Button 1 to analog pin 0
 * Button 2 to analog pin 1
 * Button 3 to digital pin 12 
 * Button 4 to digital pin 13
 
 */

//******************************************************************************
// GLOBALS, FLAGS, INCLUDES, INTTERUPT FUNCTIONS  
//******************************************************************************

// includes
#include <Wire.h>                        // I2C library    
#include <LiquidCrystal.h>               // LCD library   
#include <IRremote.h>                    // IR library                   
#include <PinChangeInt.h>                // Interrupt library    
#include <AdaEncoder.h>                  // Encoder library
#include <EEPROM.h>                      // Eeprom library    

// delay defines (these are delays that don't slow down processor)
#define IR_WAIT       3500               // ir wait counts  
#define LCD_WAIT      100000             // lcd wait counts
#define INT_WAIT      3000               // interrupt debounce wait counts
#define TEMP_LCD_WAIT 50000              // temp lcd show wait counts 
#define volDELAY      50                 // this one is used for normal delay(); 

// IR mapped buttons
#define rMUTE  0x68b5f    // IR remote button MUTE (emulate button MUTE)
#define rON    0xa8b5f    // IR remote button ON (emulate button ON)
#define rCH    0xc8b5f    // IR remote button CHANNEL++
#define rMODE  0x28b5f    // IR remote button MODE++
#define rBASSP 0x9ebd0    // IR remote button BASS++
#define rBASSM 0x98bd0    // IR remote button BASS-- 
#define rREARP 0x8cb5f    // IR remote button REAR++
#define rREARM 0xcb5f     // IR remote button REAR-- 
#define rCENP  0x2cb5f    // IR remote button CENTER++
#define rCENM  0xccb5f    // IR remote button CENTER--
#define rVOLP  0x490      // IR remote button VOLUME++ (emulate ENCODER++)
#define rVOLM  0xc90      // IR remote button VOLUME-- (emulate ENCODER--)
#define rLCD   0x2ab5f    // IR remote button LCD ON/OFF
#define rENH   0x18bd0    // IR remote button ENHANCE ACTIVE/DISABLED
#define rBOOST 0x70b5f    // IR remote button BOOST ACTIVE/DISABLED
#define rMENU  0xd8b5f    // IR remote button MENU (emulate button MENU)
#define rOK    0xb0b5f    // IR remote button OK (emulate button OK)

// IR globals
int RECV_PIN = 11;        // Receive pin
IRrecv irrecv(RECV_PIN);  // Set receive
decode_results results;   // Received IR codes   

// Serial communication globals and flags
int bufferX[16] = {'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'};  // global buffer for received chars
int xyz = 0;                      // BufferX size count    
int kontrolaCMD = 0;              // Control flag for BufferX read
char *code = "AMP1";              // Serial starting code for our Amplifier

// global varaiables for LCD
LiquidCrystal lcd(10, 9, 8, 7, 6, 5);   // LCD pins
byte lcd_flag = 0;                      // flag for lcd update        

// These global values will be red from EEPROM 
byte volume = 0;                  // volume level 0-79
byte FL = 128;                    // front left volume level correct
byte FR = 128;                    // front right volume level correct
byte CE = 128;                    // center volume level correct
byte SU = 128;                    // sub left volume level correct
byte RL = 128;                    // rear left volume level correct    
byte RR = 128;                    // rear right volume level correct
byte CH = 0;                    // Selected channel 
byte enhance = 1;               // enhance select 
byte amp = 0;                   // +6dB boost select
byte mute = 0;                  // mute select
byte unit = 0;                  // unit on/stand-by state
byte mode = 0;                  // speaker mode

byte external_buttons = 1;              // enable external buttons
byte external_remote = 1;               // enable remote
byte enable_lcd = 1;                    // enable LCD (lcd on/off)
//----------------------------------------------------------------------

// other global values and flags
byte int1 = 0;                 // interrupt flag for button 1 (on/off)
byte int2 = 0;                 // interrupt flag for button 2 (mute)
byte int3 = 0;                 // interrupt flag for button 3 (menu)     
byte int4 = 0;                 // interrupt flag for button 4 (ok)
byte button_menus = 0;         // global variable for  main menu
int set_menu = 0;              // global variable for sub_menu 
int temp_menu = 0;             // global temp menu varaiable (for quick status)
int set_corrections_menu = 0;       // global variable for sub_menu of corrections
byte read_value = 0;                // read encoder flag value in menu
byte do_this = 0;                   // select menu flag
byte do_correct = 0;                // select correct flag
int temp1 = external_buttons;               // flag for enable buttons
int temp2 = external_remote;                // flag for enable remote
int temp3 = enable_lcd;                     // flag for enable lcd
int temp4 = unit;                           // flag for unit on/off

unsigned long counter_lcd = LCD_WAIT;       // delay counter for lcd change 
unsigned int ir_counter = IR_WAIT;          // delay counter for ir receive
unsigned int int_counter = INT_WAIT;        // delay counter for interrupt (debounce)
unsigned long temp_lcd_counter = TEMP_LCD_WAIT; // delay counter for temp LCD status show
byte debounce = 1;                          // debounce flag (when 1 debounce over, we can read value again)  

int encoder_value = 0;          // value for encoder
int8_t clicks = 0;              // encoder clicks   
char id = 0;                    // encoder ID       

//------------------------------------------------------------
// button interrupt actions (set flag to 1)
void but1func()         // set interrupt flag on button 1
{
  if(external_buttons == 1) int1 = 1;  // if buttons enabled, set flag (ON/OFF)
}

void but2func()         // set interrupt flag on button 2 (MUTE)
{
  if(external_buttons == 1) int2 = 1;
}

void but3func()         // set interrupt flag on button 3 (MENU)
{
  if(external_buttons == 1) int3 = 1;
}

void but4func()         // set interrupt flag on button 4 (OK)
{
  if(external_buttons == 1) int4 = 1;
}
//------------------------------------------------------------

//******************************************************************************
// SETUP AND POWER ON DEFAULTS
//******************************************************************************
void setup()
{
  if(EEPROM.read(16) != 23)    // very small chance that unknown eeprom value would be 23 :) (lucky gues I would say) 
  {                            // this will be done only once, this is to store default values
     EEPROM.write(16,23);       // on first power up, so we don't get unknown invalid eeprom values
     write_to_eeprom();
  }
    
  read_from_eeprom();   // load values from eeprom 

  pinMode(4, OUTPUT);     // as output (relay for amplifier power)
  pinMode(A3, OUTPUT);    // as output (mute)
  pinMode(A2, OUTPUT);    // as output (led lights) 

  digitalWrite(4, LOW);    // set 0  (relay for amplifier power) 
  digitalWrite(A3, LOW);   // set 0  (mute)
  digitalWrite(A2, LOW);   // set 0  (led lights)

  // attach interrupt, pull-up, on FALLING (from HIGH to LOW) 
  pinMode(A0, INPUT); 
  digitalWrite(A0, HIGH);            // set A0 input intterupt (ON/OFF)
  PCintPort::attachInterrupt(A0, &but1func, FALLING);
  pinMode(A1, INPUT); 
  digitalWrite(A1, HIGH);            // set A1 input intterupt (MUTE)
  PCintPort::attachInterrupt(A1, &but2func, FALLING);
  pinMode(12, INPUT); 
  digitalWrite(12, HIGH);            // set 12 input intterupt (MENU)
  PCintPort::attachInterrupt(12, &but3func, FALLING);
  pinMode(13, INPUT); 
  digitalWrite(13, HIGH);            // set 13 input intterupt (OK)
  PCintPort::attachInterrupt(13, &but4func, FALLING);

  AdaEncoder::addEncoder('a',2 ,3);  // encoder on pins 2 and 3

  delay(1000);          // wait for voltage stabilisation  
  Wire.begin();         // join I2C
  Serial.begin(9600);   // start serial communication
  irrecv.enableIRIn();  // start receiver
  lcd.begin(16, 2);     // start LCD
  lcd.noCursor();       // turn off cursor  
  pt2323(1);            // Stereo input 1 (so we don't leave our PT chip floating)
  pt2258(79, 0);        // -79dB All CH (mute)

  // From here the program begins
  set_unit();            // turn on/off with default settings
}

//******************************************************************************
// MAIN LOOP with timeout operation
//******************************************************************************
void loop()
{
  checkSerial();              // check for serial commands    

  if(external_remote == 1)    // if remote enabled 
  {
    checkIR();                // check for IR commands if enabled 
    if(ir_counter == IR_WAIT)   // just before timeout 
    {
      irrecv.resume();          // listen for new commands
      ir_counter++;             // count +1 to wait in next statement
    }  
    else if(ir_counter == IR_WAIT+1)    // here IR waits for a counter reset and values are checked 
    {
      // Do nothing
    }  
    else
    {
      ir_counter++;             // count to timeout   
    }
  }

  if(external_remote == 1 || external_buttons == 1) checkButtons();     // check for button commands if at least one enabled 

  if(temp_menu >= 3)        // if temporary LCD info is needed
  {
    button_menus = temp_menu;  // display info we need (just for some time)
    temp_menu = 0;          // reset value 
  }

  if(enable_lcd == 1 && lcd_flag == 1 && unit == 1)  // update lcd if unit is on, LCD is enabled and if an update is needed
  {   
    lcd_led_enable(1);        // turn on LCD
    update_lcd();             // update LCD info if enabled and lcd_flag set 
    lcd_flag = 0;             // reset flag 
  }
  if(enable_lcd == 0 && lcd_flag == 1 && unit == 1)    // show lcd for a shord period of time, if unit is on and if update is needed
  { 
    lcd_led_enable(1);        // turn on LCD and LEDs
    counter_lcd = 0;          // reset counter to start again  
    update_lcd();             // update LCD info 
    lcd_flag = 0;             // reset flag for LCD update
  }

  if(counter_lcd == LCD_WAIT && enable_lcd == 0)    // lcd timeout 
  {
    lcd_led_enable(0);                              // turn off display                
    counter_lcd++;                                  // count +1 to go in next statement where we will wait
  }
  else if(counter_lcd == LCD_WAIT+1)               // here counter will be stuck until called again (set to 0)     
  {
    // do nothing
  }
  else counter_lcd++;                               // counter_lcd++


  if(int_counter == INT_WAIT)       // on timeout enable reading of buttons (for debounce)   
  {
    debounce = 1;      // debounce over
    int_counter++;     // go in next statement where we will wait
  }
  else if(int_counter == INT_WAIT + 1)  // here counter is stuck until called again (set to 0)
  {
    // do nothing
  }
  else 
  {
    int_counter++;
  }

  if(temp_lcd_counter == TEMP_LCD_WAIT)       // on timeout show regular INFO on LCD, until then show temp INFO  
  {
    button_menus = 0;         // go in main menu (reset) from temp_menu
    temp_lcd_counter++;       // count +1 
    lcd_flag = 1;             // update LCD info
  }
  else if(temp_lcd_counter == TEMP_LCD_WAIT + 1)  // here we wait for another call
  {
    // do nothing
  }
  else 
  {
    temp_lcd_counter++;
  }
}

//******************************************************************************
// PT2323 AND PT2258 COMMAND SET 
//******************************************************************************
//------------------------------------------------------------------------------
// 2CH to 6CH translator and input selector command set
//------------------------------------------------------------------------------
void pt2323(byte command)
{
  Wire.beginTransmission(74);   // transmit to device 0x94(hex) -> 148(dec)(addressing is 7-bit) -> 148/2
  switch(command)
  {
  case 0:
    Wire.send(0xc7);   // 6-ch input
    break;
  case 4:
    Wire.send(0xc8);   // stereo 4 input
    break;
  case 3:
    Wire.send(0xc9);   // stereo 3 input
    break;
  case 2:
    Wire.send(0xca);   // stereo 2 input
    break;
  case 1:
    Wire.send(0xcb);   // stereo 1 input
    break;
  case 5:
    Wire.send(0xd0);   // enhance active
    break;
  case 6:
    Wire.send(0xd1);   // enhance disable
    break;
  case 7:
    Wire.send(0x90);   // 0dB setup
    break;
  case 8:
    Wire.send(0x91);   // +6dB setup
    break;
  case 9:
    Wire.send(0xf0);   // FL mute disabled
    break;
  case 10:
    Wire.send(0xf1);   // FL mute
    break;
  case 11:
    Wire.send(0xf2);   // FR mute disabled
    break;
  case 12:
    Wire.send(0xf3);   // FR mute
    break;
  case 13:
    Wire.send(0xf4);   // CE mute disabled
    break;
  case 14:
    Wire.send(0xf5);   // CE mute
    break;
  case 15:
    Wire.send(0xf6);   // SU mute disabled
    break;
  case 16:
    Wire.send(0xf7);   // SU mute
    break;
  case 17:
    Wire.send(0xf8);   // SL mute disabled
    break;
  case 18:
    Wire.send(0xf9);   // SL mute
    break;
  case 19:
    Wire.send(0xfa);   // SR mute disabled
    break;
  case 20:
    Wire.send(0xfb);   // SR mute
    break;
  case 21:
    Wire.send(0xfe);   // All CH mute disabled
    break;
  default:
    Wire.send(0xff);   // All CH mute
    break; 
  }
  Wire.endTransmission();     // stop transmitting  
}

//------------------------------------------------------------------------------
// Volume controller IC command set
//------------------------------------------------------------------------------
void pt2258(byte command, byte ch)  // send volume level commands
{
  byte x10;
  byte x1; 

  if(command >= 10)
  {
    x10 = command/10;        // set decade step
    x1 = command % 10;       // set step
  }
  else                       // set step       
  {
    x1 = command;
    x10 = 0;
  }

  switch(ch)                 // which channel to command 
  {
  case 0:    // all channels
    x1 = x1 + 0xe0;
    x10 = x10 + 0xd0;
    break;

  case 1:    // channel 1
    x1 = x1 + 0x90;
    x10 = x10 + 0x80;
    break;

  case 2:    // channel 2
    x1 = x1 + 0x50;
    x10 = x10 + 0x40;
    break;

  case 3:    // channel 3
    x1 = x1 + 0x10;
    x10 = x10 + 0x00;
    break;

  case 4:    // channel 4
    x1 = x1 + 0x30;
    x10 = x10 + 0x20;
    break;

  case 5:    // channel 5
    x1 = x1 + 0x70;
    x10 = x10 + 0x60;
    break;

  case 6:    // channel 6
    x1 = x1 + 0xb0;
    x10 = x10 + 0xa0;
    break;

  default:   // mute functions    
    if(command == 0) x10, x1 = 0xf8;   // mute off
    else x10, x1 = 0xf9;               // mute on       
    break;
  }

  for(int i = 0; i <= 2; i++)  // repeat 2x (had some unknown issues when transmitted only once)
  {  
    Wire.beginTransmission(68); // transmit to device 0x88(hex) -> 136(dec)(addressing is 7-bit) -> 136/2
    Wire.send(x10);             // send decade step         
    Wire.send(x1);              // send step
    Wire.endTransmission();     // stop transmitting   
  }  
}

//******************************************************************************
// AUDIO SETTINGS
//******************************************************************************
//------------------------------------------------------------------------------
// Set volume in steps (soft volume change)
//------------------------------------------------------------------------------
void set_volume(byte begin_from, byte to_level)
{ 
  // calculate corrects 
  int var1 = 0;    // FL
  int var2 = 0;    // FR
  int var3 = 0;    // CE
  int var4 = 0;    // SU
  int var5 = 0;    // RR
  int var6 = 0;    // RL

  var1 = FL-128;
  var2 = FR-128;
  var3 = CE-128;
  var4 = SU-128;
  var5 = RR-128;
  var6 = RL-128;

  // set volume + corrects
  int y;

  if(begin_from < to_level)          // increse volume
  {
    for(y = begin_from; y < to_level; y++)
    {  
      if((0 <= (79 - (y + var1))) && (79 >= (79 - (y + var1))))  pt2258(79 - (y + var1), 1);  // send value if in 0-79 range
      if((0 <= (79 - (y + var2))) && (79 >= (79 - (y + var2))))  pt2258(79 - (y + var2), 2);  
      if((0 <= (79 - (y + var3))) && (79 >= (79 - (y + var3))))  pt2258(79 - (y + var3), 3);
      if((0 <= (79 - (y + var4))) && (79 >= (79 - (y + var4))))  pt2258(79 - (y + var4), 4);
      if((0 <= (79 - (y + var5))) && (79 >= (79 - (y + var5))))  pt2258(79 - (y + var5), 5);
      if((0 <= (79 - (y + var6))) && (79 >= (79 - (y + var6))))  pt2258(79 - (y + var6), 6);

      delay(volDELAY);  // delay for a soft transition of volume
    }
  } 
  else if(begin_from > to_level)      // decrease volume
  {
    for(y = begin_from; y > to_level; y--)
    {
      if((0 <= (79 - (y + var1))) && (79 >= (79 - (y + var1))))  pt2258(79 - (y + var1), 1);  // send value if in 0-79 range
      if((0 <= (79 - (y + var2))) && (79 >= (79 - (y + var2))))  pt2258(79 - (y + var2), 2);  
      if((0 <= (79 - (y + var3))) && (79 >= (79 - (y + var3))))  pt2258(79 - (y + var3), 3);
      if((0 <= (79 - (y + var4))) && (79 >= (79 - (y + var4))))  pt2258(79 - (y + var4), 4);
      if((0 <= (79 - (y + var5))) && (79 >= (79 - (y + var5))))  pt2258(79 - (y + var5), 5);
      if((0 <= (79 - (y + var6))) && (79 >= (79 - (y + var6))))  pt2258(79 - (y + var6), 6);

      delay(volDELAY);
    }
  }
  else          // same volume just corrects, here we don't need soft transition
  {
    var1 = var1 + volume;
    if(var1 < 0) var1 = 0;
    if(var1 > 79) var1 = 79;
    var2 = var2 + volume;
    if(var2 < 0) var2 = 0;
    if(var2 > 79) var2 = 79;
    var3 = var3 + volume;
    if(var3 < 0) var3 = 0;
    if(var3 > 79) var3 = 79;
    var4 = var4 + volume;
    if(var4 < 0) var4 = 0;
    if(var4 > 79) var4 = 79;
    var5 = var5 + volume;
    if(var5 < 0) var5 = 0;
    if(var5 > 79) var5 = 79;
    var6 = var6 + volume;
    if(var6 < 0) var6 = 0;
    if(var6 > 79) var6 = 79;

    // send new values for each channel
    pt2258(79 - var1, 1);
    pt2258(79 - var2, 2);
    pt2258(79 - var3, 3);
    pt2258(79 - var4, 4);
    pt2258(79 - var5, 5);
    pt2258(79 - var6, 6);
  }
  if(to_level >= 79) to_level = 79;    // do not exceed 79
  if(to_level <= 0)  to_level = 0;     // do not go in -
  volume = to_level;
  if(volume == 0) pt2258(79,0);        // mute all channels  (otherwise you could hear volume of + correct)
}

//------------------------------------------------------------------------------
// Set speaker mode
//------------------------------------------------------------------------------
void set_mode()    // set speaker mode
{
  switch(mode)
  {
  case 1:                      // 2.1 mode
    pt2323(14);  // disable CE 
    pt2323(18);  // disable RL
    pt2323(20);  // disable RR
    break;
  case 2:                      // 3.1 mode
    pt2323(13);  // enable CE
    pt2323(18);  // disable RL
    pt2323(20);  // disable RR
    break;
  case 3:                      // 4.1 mode
    pt2323(14);  // disable CE
    pt2323(17);  // enable RL
    pt2323(19);  // enable RR
    break;
  default:                     // 5.1 mode
    pt2323(13);  // disable CE
    pt2323(17);  // enable RL
    pt2323(19);  // enable RR 
    break;
  }
}

//------------------------------------------------------------------------------
// Set enhance
//------------------------------------------------------------------------------
void set_enhance()
{
  if(enhance == 1) pt2323(5); // enhance enabled
  else pt2323(6);
}

//------------------------------------------------------------------------------
// Set +6dB setup
//------------------------------------------------------------------------------    
void set_amp()
{
  if(amp == 1) pt2323(8);   // boost enabled
  else pt2323(7);
}

//------------------------------------------------------------------------------
// Select channel
//------------------------------------------------------------------------------
void set_ch()
{
  pt2323(CH);   // set channel
}

//------------------------------------------------------------------------------
// Set mute
//------------------------------------------------------------------------------
void set_mute()
{
  if(mute == 0)
  { 
    amplifier_enable(1);         // enable amplifier
  }
  else
  {
    amplifier_enable(0);         // disable amplifier
  }
}

//------------------------------------------------------------------------------
// Set unit (power)
//------------------------------------------------------------------------------
void set_unit()
{
  if(unit == 1) // power on
  {
    default_flags();           // load default flags on power up 
    lcd_led_enable(1);         // enable lcd and leds
    lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
    lcd.print("5.1  Amplifier");
    lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
    lcd.print("   by Neoxy   ");
    delay(2500);               // here we can afford a delay like this
    lcd.clear();
    lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
    lcd.print("  Turning ON  ");

    set_ch();                      // set channel
    set_mode();                    // set mode      
    set_enhance();                 // set enhance mode 
    set_amp();                     // set +6dB setup
    set_volume(0, volume);         // set volume from level 0 to level volume
    set_mute();                    // set mute
    delay(1500);

    lcd_flag = 1;     // update lcd  
  }
  else
  {
    amplifier_enable(0);       // turn off amplifier 
    lcd.clear();
    lcd_led_enable(1);        // enable lcd and leds
    lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
    lcd.print("   Stand-by   ");
    delay(2000); // here we can afford a delay like this
    lcd.clear();    
    lcd_led_enable(0);         // turn off LCD and LED lights
    lcd_flag = 0;              // do not update lcd
  }  
}

//------------------------------------------------------------------------------
// Enable or disable amplifier
//------------------------------------------------------------------------------
void amplifier_enable(byte cmd)    // enable or disable amplifier
{
  if(cmd == 1)                           // turn on amplifier 
  {
    digitalWrite(4, HIGH);   // relay on
    delay(200);                // small delay like this does not influence operation much
    digitalWrite(A3, HIGH);   // mute off
  }
  else                                   // turn off amplifier
  {
    digitalWrite(A3, LOW);   // mute on
    delay(50);
    digitalWrite(4, LOW);   // relay off
  } 
}

//------------------------------------------------------------------------------
// Enable or disable LCD and LED lights
//------------------------------------------------------------------------------
void lcd_led_enable(byte cmd)      // enable or disable lcd
{
  if(cmd == 1)                           // turn on or off LCD and leds 
  {
    digitalWrite(A2, HIGH);    // leds on
    lcd.display();             // turn on display
  }
  else
  {
    digitalWrite(A2, LOW);    // leds off
    lcd.noDisplay();          // turn off display
  }
}

//******************************************************************************
// SERIAL COMMUNICATION
//******************************************************************************
//------------------------------------------------------------------------------
// Check Serial Communication, verify code and execute command
//------------------------------------------------------------------------------
void checkSerial()              // check serial commands
{
  byte temp;    // temp value
  byte c;       // received char 
  if(Serial.available() > 0)     // If something received
  {
    while(Serial.available() != 0 && kontrolaCMD == 0) // read incomming bytes if flag = 0
    {
      c = Serial.read();   // read receaved byte from serial buffer
      if(c == 13 || c == 10 || xyz >= 16 ) kontrolaCMD = 1;  // if end of line received or temp buffer size exceeded escape, set flag to 1  
      else                    
      { 
        bufferX[xyz] = c;      // store in our temp buffer
        xyz++;                 // increment bufferX position
      }    
    } 

    if(kontrolaCMD==1)        // If EOL detected or buffer size exceeded  
    {
      if(compareStr(0,code) == 1)    // compare if code is a match (our case "AMP1")
      {
        if(compareStr(4, "00") == 1)    // next code 00 (stand-by, on)
        {
          lcd_flag = 1;    // here we want a lcd update
          temp = toNum(6);  

          if(temp <= 1)    // only default values
          {
            if(unit != temp)
            {
              unit = temp;
              set_unit();
            }
          }
          Serial.print(code);          // send back OK when done
          Serial.println("OK");
        }
        else if(compareStr(4, "01") == 1)   // code 01 (set channel)
        {
          lcd_flag = 1;    // here we want a lcd update
          temp = toNum(6);
          if(temp <= 4)
          {
            if(temp != CH)
            {
              CH = temp;
              set_ch();
            }
          }
          Serial.print(code);
          Serial.println("OK");
        }
        else if(compareStr(4, "02") == 1)   // code 02 (set mode)
        {
          lcd_flag = 1;  // here we want a lcd update
          temp = toNum(6);
          if(temp <= 3)
          {
            if(temp != mode)
            {
              mode = temp;
              set_mode();
            }
          }
          Serial.print(code);
          Serial.println("OK");
        }
        else if(compareStr(4, "03") == 1)   // code 03 (set correction), here we don't need a lcd update (lcd_flag not set) 
        {
          temp = toNum(8);
          if(temp <= 168 && temp >= 88)
          {
            if(compareStr(6, "FL"))              // no problem here if same value is set
            {
              FL = temp;
            }
            else if(compareStr(6, "FR"))
            {
              FR = temp;
            }
            else if(compareStr(6, "CE"))
            {
              CE = temp;
            }
            else if(compareStr(6, "SU"))
            {
              SU = temp;
            }
            else if(compareStr(6, "RL"))
            {
              RL = temp;
            }
            else if(compareStr(6, "RR"))
            {
              RR = temp;
            }
            set_volume(volume,volume);
          }
          Serial.print(code);
          Serial.println("OK");
        }
        else if(compareStr(4, "04") == 1)  // code 04 (set enhance)
        {
          lcd_flag = 1; 
          if(toNum(6) <= 1)
          {
            enhance = toNum(6);
            set_enhance();
          }
          Serial.print(code);
          Serial.println("OK");
        }
        else if(compareStr(4, "05") == 1)  // code 05 (set amp)
        {
          lcd_flag = 1;
          if(toNum(6) <= 1)
          {
            amp = toNum(6);
            set_amp();
          }
          Serial.print(code);
          Serial.println("OK");
        }
        else if(compareStr(4, "06") == 1)  // code 06 (set volume)
        {
          lcd_flag = 1;
          if(toNum(6) <= 79)
          {
            set_volume(volume, toNum(6)); // from current volume to new volume
          }
          Serial.print(code);
          Serial.println("OK"); 
        }
        else if(compareStr(4, "07") == 1)  // set mute
        {
          lcd_flag = 1;
          if(toNum(6) <= 1)
          {
            mute = toNum(6);
            set_mute();
          }
          Serial.print(code);
          Serial.println("OK");
        }
        else if(compareStr(4, "08") == 1)  // set buttons 
        {
          if(toNum(6) <= 1)
          {
            external_buttons = toNum(6);
          }
          Serial.print(code);
          Serial.println("OK");
        }
        else if(compareStr(4, "09") == 1)  // set remote
        {
          if(toNum(6) <= 1)
          {
            external_remote = toNum(6);
          }
          Serial.print(code);
          Serial.println("OK");
        }
        else if(compareStr(4, "10") == 1)  // set lcd enable
        {
          lcd_flag = 1;
          if(toNum(6) <= 1)
          {
            enable_lcd = toNum(6);
          }
          Serial.print(code);
          Serial.println("OK");           
        }
        else if(compareStr(4, "11") == 1)  // store in flash
        {
          write_to_eeprom();
          Serial.print(code);
          Serial.println("OK");
        }
        else if(compareStr(4, "20") == 1)  // return main status
        {
          Serial.print(code);
          Serial.print("P");
          Serial.print(unit, DEC);
          Serial.print("C");
          Serial.print(CH, DEC);
          Serial.print("M");
          Serial.print(mode, DEC);
          Serial.print("E");
          Serial.print(enhance, DEC);
          Serial.print("A");
          Serial.print(amp, DEC);
          Serial.print("V");
          if(volume<10) Serial.print("0");
          Serial.print(volume, DEC);
          Serial.print("MU");
          Serial.print(mute, DEC);
          Serial.print("B");
          Serial.print(external_buttons, DEC);
          Serial.print("R");
          Serial.print(external_remote, DEC);
          Serial.print("L");
          Serial.println(enable_lcd, DEC);
        }
        else if(compareStr(4, "21") == 1)  // return correction settings
        {
          Serial.print(code);
          Serial.print("L");
          if(FL<10) Serial.print("0");
          if(FL<100) Serial.print("0");
          Serial.print(FL, DEC); 
          Serial.print("R");
          if(FR<10) Serial.print("0");
          if(FR<100) Serial.print("0");
          Serial.print(FR, DEC);
          Serial.print("S");
          if(SU<10) Serial.print("0");
          if(SU<100) Serial.print("0");
          Serial.print(SU, DEC);
          Serial.print("C");
          if(CE<10) Serial.print("0");
          if(CE<100) Serial.print("0");
          Serial.print(CE, DEC);
          Serial.print("L");
          if(RL<10) Serial.print("0");
          if(RL<100) Serial.print("0");
          Serial.print(RL, DEC);
          Serial.print("R");
          if(RR<10) Serial.print("0");
          if(RR<100) Serial.print("0");
          Serial.println(RR, DEC);
        }
        else
        {
          // do nothing, inccorect code
        } 
      }
      bufXClear();            // Clear BufferX
      xyz = 0;                // reset bufferX counter
      kontrolaCMD = 0;        // Read again flag
    }
  }
}

//------------------------------------------------------------------------------
// Convert  1, 2 or 3 chars to number (Location in bufferX at startLocation)
//------------------------------------------------------------------------------
int toNum(byte startLocation)
{
  char numbers[10] = {
    '0','1','2','3','4','5','6','7','8','9'  };  // array of chars (numbers)
  byte flag_nmb = 0;    // flag for number found 

  int x = 0;

  for(int i = 0; i < 10; i++)          // check first loction for number
  {
    if(bufferX[startLocation] == numbers[i]) x = x + (i*100), flag_nmb = 1;
  }

  if(flag_nmb == 0) return 0;  // number not found
  flag_nmb = 0;                // reset flag

  for(int i = 0; i < 10; i++)         // check second location for number
  {
    if(bufferX[startLocation+1]== numbers[i]) x = x + (i*10), flag_nmb = 1; 
  }

  if(flag_nmb == 0) // number on second location not found
  {
    if(x != 0 )return x/100;    // we don't want to divide by 0 (end of the world and stuff.. :)
    else return 0;
  }

  flag_nmb = 0; // reset flag

  for(int i = 0; i < 10; i++)  // check third location for number
  {
    if(bufferX[startLocation+2]== numbers[i]) x = x + i, flag_nmb = 1; 
  }

  if(flag_nmb == 0)  // number on third location not found
  {
    if(x != 0 ) return x/10;   // we don't want to divide by 0 (end of the world and stuff.. :)
    else return 0;
  }

  if(x >= 256) return 0;
  return x;    // if all 3 numbers found 
}

//------------------------------------------------------------------------------
// Clear temp Serial buffer
//------------------------------------------------------------------------------
void bufXClear()          // clear serial buffer
{
  for(int x = 0; x < 16; x++)   // clear bufferX
  {
    bufferX[x] = '\0';            // \0 in ASCII end of string
  }
}

//------------------------------------------------------------------------------
// Commpare bufferX from location startPos with another string
//------------------------------------------------------------------------------
byte compareStr(int startPos, char *nekaj)
{
  int comp = 1;
  while(*nekaj)
  {
    if(bufferX[startPos] != *nekaj++) comp = 0;
    startPos++;
  }
  return comp;
}

//******************************************************************************
// EXTERNAL BUTTONS
//******************************************************************************
//------------------------------------------------------------------------------
// External buttons command execute, move between menus
//------------------------------------------------------------------------------
void checkButtons()             // check button interrupts
{
  if(read_button1() == 1)        // read on/off (allways)
  {
    if(unit == 1)
    {
      unit = 0;
      set_unit();
    }
    else
    {
      unit = 1;
      set_unit();
    }
  }

  if(read_button2() == 1)          // read mute
  {
    if(mute == 1)
    {
      mute = 0;
      set_mute();
    }
    else
    {
      mute = 1;
      set_mute();
    }
    lcd_flag = 1; // update lcd
  }

  int tempvalue = 0;                  // temp value    
  byte butn_menu = read_button3();    // read button menu and work with this value  
  byte butn_ok = read_button4();      // read button ok and work with this value
  check_encoder();                    // read encoder and work with this value

  if(butn_menu == 1) lcd_flag = 1;      // if button pressed update lcd
  if(butn_ok == 1) lcd_flag = 1;        // if button pressed update lcd
  if(encoder_value != 0) lcd_flag = 1;  // if encoder turned update lcd

  if(butn_ok == 1 && do_this == 0 && button_menus == 1)    // toggle do_this with OK button (depends on where in menu are we)
  {
    do_this = 1;
  }  
  else if(butn_ok == 1 && do_this == 1 && button_menus == 1)
  {
    do_this = 0;
    read_value = 1;             // here flag is set to change menu with encoder, and not change value 
  }
  else 
  {
    // do nothing
  }

  if(butn_ok == 1 && do_correct == 0  && button_menus == 2)  // toggle do_correct with OK button (depends on where in menu are we)
  {
    do_correct = 1;
  }
  else if(butn_ok == 1 && do_correct == 1  && button_menus == 2)
  {
    do_correct = 0;
    read_value = 1;              // here flag is set to change menu with encoder, and not change value
  }
  else 
  {
    // do nothing
  }


  if(butn_menu == 1)          // read menu if button pressed, move between menus (depends on where we are), also set default flags
  {  
    if(button_menus == 0) button_menus = 1, read_value = 1, set_menu = 0,  do_correct = 0, do_this = 0;
    else if(button_menus == 2) button_menus = 1, set_menu = 2, do_correct = 0, do_this = 0;
    else button_menus = 0, read_value = 1, set_menu = 0,  do_correct = 0, do_this = 0;
  }

  // Programmed menues
  switch(button_menus)
  {
  case 0:      // default - status  only change volume, show status
    do_correct = 0;
    do_this = 0;
    temp1 = external_buttons;                     // flag for enable buttons, latter used for APPLY and STORE, other changes take effect immediatly
    temp2 = external_remote;                      // flag for enable remote
    temp3 = enable_lcd;                           // flag for enable lcd
    temp4 = unit;                                 // flag for unit on/off
    if(encoder_value != 0)        // if encoder is not 0
    {
      if(volume + encoder_value > 79) set_volume(volume, 79);
      else if(volume + encoder_value < 0 ) set_volume(volume, 0);
      else set_volume(volume, (volume + encoder_value));
      set_menu = 0;            // in main menu we change volume with encoder 
      read_value = 1;     
    }
    break;

  case 1:      // go in menu selection (if menu pressed)
    do_correct = 0;
    if(read_value == 1)       // we are in menu and we move between him with encoder
    { 
      set_menu = set_menu + encoder_value;
      if(set_menu < 0) set_menu = 13;
      if(set_menu > 13) set_menu = 0;
    }
    switch(set_menu)  // jump between menus
    {
    case 0:    // set CH
      if(do_this == 1) // if OK pressed, encoder is used to change value and not move between menu
      {
        read_value = 0;                          // dont'leave this menu, set value
        if(CH + encoder_value < 0) CH = 4;
        else if(CH + encoder_value > 4) CH = 0;
        else CH = CH + encoder_value;
        if(encoder_value != 0) set_ch();         // if we have a value on encoder, execute command
      }
      else read_value = 1;                       // move between menu, encoder used for this 
      break;

    case 1:    // set mode
      if(do_this == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        if(mode + encoder_value < 0) mode = 3;
        else if(mode + encoder_value > 3) mode = 0;
        else mode = mode + encoder_value;
        if(encoder_value != 0) set_mode();
      }
      else read_value = 1;
      break;

    case 2:    // set correct
      if(do_this == 1)
      {
        read_value = 1;
        button_menus = 2;  // go in corrections menu  
      }
      else 
      {  
        read_value = 1;
        set_corrections_menu = 0;
      }
      break;

    case 3:    // set enhence
      if(do_this == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        if(enhance + encoder_value <= 0) enhance = 0;
        if(enhance + encoder_value >= 1) enhance = 1;
        if(encoder_value != 0) set_enhance();
      }
      else read_value = 1;
      break;

    case 4:    // set boost
      if(do_this == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        if(amp + encoder_value <= 0) amp = 0;
        if(amp + encoder_value >= 1) amp = 1;
        if(encoder_value != 0) set_amp();
      }
      else read_value = 1;
      break;

    case 5:    // set mute
      if(do_this == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        if(mute + encoder_value <= 0) mute = 0;
        if(mute + encoder_value >= 1) mute = 1;
        if(encoder_value != 0) set_mute();
      }
      else read_value = 1;
      break;

    case 6:    // set volume
      if(do_this == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        tempvalue = volume + encoder_value;
        if(tempvalue < 0) tempvalue = 0;
        if(tempvalue > 79) tempvalue = 79;
        if(encoder_value != 0) set_volume(volume, tempvalue);
      }
      else read_value = 1;
      break;

    case 7:    // set buttons
      if(do_this == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        if(external_buttons + encoder_value <= 0 && encoder_value != 0) temp1 = 0;  // value stored in temp value, will be set on APPLY or STORE
        if(external_buttons + encoder_value >= 1 && encoder_value != 0) temp1 = 1;
      }
      else read_value = 1;
      break;

    case 8:    // set remote
      if(do_this == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        if(external_remote + encoder_value <= 0 && encoder_value != 0) temp2 = 0;  // value stored in temp value, will be set on APPLY or STORE
        if(external_remote + encoder_value >= 1 && encoder_value != 0) temp2 = 1;
      }
      else read_value = 1;
      break;

    case 9:    // set lcd
      if(do_this == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        if(enable_lcd + encoder_value <= 0 && encoder_value != 0) temp3 = 0;    // value stored in temp value, will be set on APPLY or STORE
        if(enable_lcd + encoder_value >= 1 && encoder_value != 0) temp3 = 1;
      }
      else read_value = 1;
      break;

    case 10:    // set on/off
      if(do_this == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        if(unit + encoder_value <= 0 && encoder_value != 0) temp4 = 0;   // value stored in temp value, will be set on APPLY or STORE
        if(unit + encoder_value >= 1 && encoder_value != 0) temp4 = 1;
      }
      else read_value = 1;
      break;

    case 11:    // set apply
      if(do_this == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        external_buttons = temp1;    // set globals with temp values, we don't need to set unit ON/OFF because we don't want to turn our unit off
        external_remote = temp2;
        enable_lcd = temp3; 

        button_menus = 0;    // return in main menu
      }
      break;

    case 12:    // set store
      if(do_this == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        external_buttons = temp1;    // set globals with temp values
        external_remote = temp2;
        enable_lcd = temp3;
        unit = temp4;

        button_menus = 0;
        write_to_eeprom();  // store in eeprom
        
        unit = 1;  // set flag for unit ON, because we don't want to turn off unit, just store the value as default
      }
      break;

    default:   // exit
      if(do_this == 1)
      {
        button_menus = 0;    // go in main menu
      }
      break;
    }
    break;

  case 2:
    do_this = 0;
    if(read_value == 1)       // we are in sub menu for correction
    { 
      set_corrections_menu = set_corrections_menu + encoder_value;    // move between menu with encoder
      if(set_corrections_menu < 0) set_corrections_menu = 6;
      if(set_corrections_menu > 6) set_corrections_menu = 0;
    }
    switch(set_corrections_menu)      // go in sub menu
    {
    case 0:    // set FL
      if(do_correct == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        if(FL + encoder_value < 88) FL = 88;
        else if(FL + encoder_value > 168) FL = 168;
        else FL = FL + encoder_value;
      }
      else read_value = 1;
      break;

    case 1:    // set FR
      if(do_correct == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        if(FR + encoder_value < 88) FR = 88;
        else if(FR + encoder_value > 168) FR = 168;
        else FR = FR + encoder_value;
      }
      else read_value = 1;
      break;

    case 2:    // set SU
      if(do_correct == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        if(SU + encoder_value < 88) SU = 88;
        else if(SU + encoder_value > 168) SU = 168;
        else SU = SU + encoder_value;
      }
      else read_value = 1;
      break;

    case 3:    // set CE
      if(do_correct == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        if(CE + encoder_value < 88) CE = 88;
        else if(CE + encoder_value > 168) CE = 168;
        else CE = CE + encoder_value;
      }
      else read_value = 1;
      break;

    case 4:    // set RR
      if(do_correct == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        if(RR + encoder_value < 88) RR = 88;
        else if(RR + encoder_value > 168) RR = 168;
        else RR = RR + encoder_value;
      }
      else read_value = 1;
      break;

    case 5:    // set RL
      if(do_correct == 1)
      {
        read_value = 0;  // dont'leave this menu, set value
        if(RL + encoder_value < 88) RL = 88;
        else if(RL + encoder_value > 168) RL = 168;
        else RL = RL + encoder_value;
      }
      else read_value = 1;
      break;

    default:   // go back
      if(do_correct == 1)
      { 
        set_menu = 2;    // set defaults for previous menu  
        do_correct = 0;
        read_value = 1;
        button_menus = 1; 
      }
      else read_value = 1;
      break;
    }
    if(do_correct == 1 && encoder_value != 0) set_volume(volume, volume);  // if a value was on encoder execute command
    break;

  default:    // should not go here
    break;
  }
  encoder_value = 0;        // reset value 
}

//------------------------------------------------------------------------------
// Check encoder clicks, and buttons
//------------------------------------------------------------------------------
void check_encoder()      // check encoder status
{
  encoder *thisEncoder;
  if(external_buttons == 1 && unit == 1) // if buttons enabled and unit is on
  {
    thisEncoder=AdaEncoder::genie(&clicks, &id);    // check clicks
    if(clicks != 0)    
    {
      if(clicks > 1) clicks = 1;      // move only with 1 in either direction
      if(clicks < -1) clicks = -1;
      encoder_value = clicks;         // store in global value
    }
  }
  clicks = 0;    // reset clicks
}

//------------------------------------------------------------------------------
byte read_button1()  // on/off
{
  if(int1 == 1 && debounce == 1)  // read if interrupt flag set and debounce over
  {
    debounce = 0;          // reset debounce
    int_counter = 0;       // reset debounce counter
    int1 = 0; 
    return 1;
  }
  int1 = 0;   // reset flag (in case debounce was not timed out and interrupt flag was set)
  return 0;
}

//------------------------------------------------------------------------------
byte read_button2()   // mute 
{
  if(int2 == 1 && unit == 1 && debounce == 1) // read only if unit is on, on interrupt and debounce over 
  {
    debounce = 0;
    int_counter = 0;
    int2 = 0;
    return 1;
  }
  int2 = 0;  // reset flag (in case debounce was not timed out and interrupt flag was set)
  return 0;
}

//------------------------------------------------------------------------------
byte read_button3()    // menu
{
  if(int3 == 1 && unit == 1 && debounce == 1)
  {
    debounce = 0;
    int_counter = 0;
    int3 = 0;
    return 1;
  }
  int3 = 0;
  return 0;
}

//------------------------------------------------------------------------------
byte read_button4()    // ok
{
  if(int4 == 1 && unit == 1 && debounce == 1)
  {
    debounce = 0;
    int_counter = 0;
    int4 = 0;
    return 1;
  }
  int4 = 0;
  return 0;
}

//******************************************************************************
// REMOTE 
//******************************************************************************
//------------------------------------------------------------------------------
// Remote commands execute
//------------------------------------------------------------------------------
void checkIR()                  // check IR commands
{
  if((irrecv.decode(&results) != 0))  // if something received
  {
    //Serial.println(results.value, HEX);  // here you can uncomment this to listen for values of unknown remote
    if(((unit == 0 && results.value == rON) || unit == 1) && (IR_WAIT+1) == ir_counter) // check if unit is on, if unit is off (just power button), and if timedout
    {
      ir_counter = 0;         // reset timeout counter
      switch(results.value)
      {
      case rON:       // on/off
        int1 = 1;          // emulate button pressed (set interrupt flag)
        break;

      case rMUTE:     // mute
        int2 = 1;
        break;

      case rCH:     // ch
        CH++;
        if (CH == 5) CH = 0;  // do not exceed 4, set back to 0
        set_ch();
        lcd_flag = 1;    // here we want lcd to update
        break;

      case rMODE:     // mode
        mode++;
        if (mode == 4) mode = 0;
        set_mode();
        lcd_flag = 1;
        break;

      case rBASSP:     // bass+
        temp_menu = 3;                // temp lcd info will be shown
        temp_lcd_counter = 0;         // temp lcd counter reset   
        if (SU >= 168) SU = 168;
        else SU++;
        set_volume(volume,volume);
        lcd_flag = 1;
        break;

      case rBASSM:     // bass-
        temp_menu = 3;
        temp_lcd_counter = 0;
        if (SU <= 88) SU = 88;
        else SU--;
        set_volume(volume,volume);
        lcd_flag = 1;
        break;

      case rREARP:     // rear+
        temp_menu = 5;
        temp_lcd_counter = 0;
        if (RR >= 168) RR = 168;
        else RR++;
        if (RL >= 168) RL = 168;
        else RL++;
        set_volume(volume,volume);
        lcd_flag = 1;
        break;

      case rREARM:     // rear-
        temp_menu = 5;
        temp_lcd_counter = 0;
        if (RR <= 88) RR = 88;
        else RR--;
        if (RL <= 88) RL = 88;
        else RL--;
        set_volume(volume,volume);
        lcd_flag = 1;
        break;

      case rCENP:     // center+
        temp_menu = 6;
        temp_lcd_counter = 0;
        if (CE >= 168) CE = 168;
        else CE++;
        set_volume(volume,volume);
        lcd_flag = 1;
        break;

      case rCENM:     // center-
        temp_menu = 6;
        temp_lcd_counter = 0;
        if (CE <= 88) CE = 88;
        else CE--;
        set_volume(volume,volume);
        lcd_flag = 1;
        break;

      case rVOLP:     // vol+
        encoder_value++; 
        lcd_flag = 1;
        break;
      case rVOLM:     // vol-
        encoder_value--;
        lcd_flag = 1;
        break;

      case rLCD:     // lcd on/off
        temp_menu = 4;
        temp_lcd_counter = 0;
        enable_lcd++;
        if (enable_lcd == 2) enable_lcd = 0;
        lcd_flag = 1;
        break;

      case rENH:     // enhance
        enhance++;
        if (enhance == 2) enhance = 0;
        set_enhance();
        lcd_flag = 1;
        break;    

      case rBOOST:     // boost
        amp++;
        if (amp == 2) amp = 0;
        set_amp();
        lcd_flag = 1;
        break;

      case rMENU:     // menu
        int3 = 1;
        break;

      case rOK:     // ok
        int4 = 1;
        break;

      default:   // incorect code
        break;
      }
    }
    irrecv.resume();      // receive next value on IR
    results.value = 0;    // reset results value to 0
  }
}

//******************************************************************************
// LCD UPDATE
//******************************************************************************
//------------------------------------------------------------------------------
// Update LCD
//------------------------------------------------------------------------------
void update_lcd()
{
  switch(button_menus)    // where we are in menu
  {
  case 0:      // status and volume
    lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
    switch(CH)
    {
    case 1:
      lcd.print("CH-1 ");
      break;
    case 2:
      lcd.print("CH-2 ");
      break;
    case 3:
      lcd.print("CH-3 ");
      break;
    case 4:
      lcd.print("CH-4 ");
      break;
    default:
      lcd.print("6-CH ");
      break;
    }
    switch(mode)
    {
    case 1:
      lcd.print(" 2.1 MODE");
      break;
    case 2:
      lcd.print(" 3.1 MODE");
      break;
    case 3:
      lcd.print(" 4.1 MODE");
      break;
    case 4:
    default:
      lcd.print(" 5.1 MODE");
      break;
    }
    lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
    switch(enhance)
    {
    case 1:
      lcd.print("E");
      break;
    default:
      lcd.print(" ");
      break;
    }
    switch(amp)
    {
    case 1:
      lcd.print("B  ");
      break;
    default:
      lcd.print("   ");
      break;
    }
    lcd.print(" VOL ");
    if(volume < 10) lcd.print(" ");
    lcd.print(volume, DEC);
    lcd.print("  ");
    switch(mute)
    {
    case 1:
      lcd.print("M");
      break;
    default:
      lcd.print(" ");
      break;
    }
    break;

  case 1:      // main menu (move between menu)
    switch(set_menu)
    {
    case 0:  // ch
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print(" SET  CHANNEL ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_this == 1) lcd.print("S");  // S stands for selected and waits for a change of value
      else lcd.print(" ");
      switch(CH)
      {
      case 1:
        lcd.print("    CH-1     ");
        break;
      case 2:
        lcd.print("    CH-2     ");
        break;
      case 3:
        lcd.print("    CH-3     ");
        break;
      case 4:
        lcd.print("    CH-4     ");
        break;
      default:
        lcd.print("    6-CH     ");
        break;
      }
      break;

    case 1:  // mode
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print("   SET MODE   ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_this == 1) lcd.print("S");
      else lcd.print(" ");
      switch(mode)
      {
      case 1:
        lcd.print("  2.1  MODE  ");
        break;
      case 2:
        lcd.print("  3.1  MODE  ");
        break;
      case 3:
        lcd.print("  4.1  MODE  ");
        break;
      default:
        lcd.print("  5.1  MODE  ");
        break;
      }
      break;

    case 2:  // correct
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print("  CH CORRECT  ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      lcd.print("              ");
      break;

    case 3:  // enhence
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print(" SET  ENHANCE ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_this == 1) lcd.print("S");
      else lcd.print(" ");
      switch(enhance)
      {
      case 1:
        lcd.print("   ACTIVE    ");
        break;
      default:
        lcd.print("  DISABLED   ");
        break;
      }
      break;

    case 4:  // boost
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print("  SET BOOST   ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_this == 1) lcd.print("S");
      else lcd.print(" ");
      switch(amp)
      {
      case 1:
        lcd.print("   ACTIVE    ");
        break;
      default:
        lcd.print("  DISABLED   ");
        break;
      }
      break;

    case 5:  // mute
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print("   SET MUTE   ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_this == 1) lcd.print("S");
      else lcd.print(" ");
      switch(mute)
      {
      case 1:
        lcd.print("   ACTIVE    ");
        break;
      default:
        lcd.print("  DISABLED   ");
        break;
      }
      break;

    case 6:  // volume
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print("  SET VOLUME  ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_this == 1) lcd.print("S");
      else lcd.print(" ");
      lcd.print("  VOL  ");
      if(volume < 10) lcd.print(" "); 
      lcd.print(volume, DEC);
      lcd.print("    ");
      break;

    case 7:  // buttons
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print("  SET BUTTONS ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_this == 1) lcd.print("S");
      else lcd.print(" ");
      switch(temp1)        // here we use temp values, globals need to be set
      {
      case 1:
        lcd.print("   ACTIVE    ");
        break;
      default:
        lcd.print("  DISABLED   ");
        break;
      }
      break;

    case 8:  // remote
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print("  SET REMOTE  ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_this == 1) lcd.print("S");
      else lcd.print(" ");
      switch(temp2)
      {
      case 1:
        lcd.print("   ACTIVE    ");
        break;
      default:
        lcd.print("  DISABLED   ");
        break;
      }
      break;

    case 9:  // lcd
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print("   LCD MODE   ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_this == 1) lcd.print("S");
      else lcd.print(" ");
      switch(temp3)
      {
      case 1:
        lcd.print("     ON      ");
        break;
      default:
        lcd.print("   TIMEOUT   ");
        break;
      }
      break;

    case 10:  // on-off
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print("  SET ON/OFF  ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_this == 1) lcd.print("S");
      else lcd.print(" ");
      switch(temp4)
      {
      case 1:
        lcd.print("     ON      ");
        break;
      default:
        lcd.print("     OFF     ");
        break;
      }
      break;

    case 11:  // apply
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print("     APPLY    ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      lcd.print("              ");
      break;

    case 12:  // store
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print("     STORE    ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      lcd.print("              ");
      break;

    default:   // back
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print("     BACK     ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      lcd.print("              ");
      break;
    }
    break;

  case 2:      // correct menu, move beetween correct menu
    switch(set_corrections_menu)
    {
    case 0:    // FL
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print(" SET  FRONT L ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_correct == 1) lcd.print("S");
      else lcd.print(" ");
      lcd.print("    ");
      if(FL-128 < 10 && FL-128> -10) lcd.print(" ");
      if(FL>128) lcd.print("+");
      lcd.print(FL-128);
      lcd.print("      ");
      break;

    case 1:    // FR
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print(" SET  FRONT R ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_correct == 1) lcd.print("S");
      else lcd.print(" ");
      lcd.print("    ");
      if(FR-128 < 10 && FR-128> -10) lcd.print(" ");
      if(FR>128) lcd.print("+");
      lcd.print(FR-128);
      lcd.print("      ");
      break;

    case 2:    // SU
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print(" SET SUBWOOFER ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_correct == 1) lcd.print("S");
      else lcd.print(" ");
      lcd.print("    ");
      if(SU-128 < 10 && SU-128> -10) lcd.print(" ");
      if(SU>128) lcd.print("+");
      lcd.print(SU-128);
      lcd.print("      ");
      break;

    case 3:    // CE
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print("  SET CENTER  ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_correct == 1) lcd.print("S");
      else lcd.print(" ");
      lcd.print("    ");
      if(CE-128 < 10 && CE-128> -10) lcd.print(" ");
      if(CE>128) lcd.print("+");
      lcd.print(CE-128);
      lcd.print("      ");
      break;

    case 4:    // RR
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print(" SET  REAR  R ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_correct == 1) lcd.print("S");
      else lcd.print(" ");
      lcd.print("    ");
      if(RR-128 < 10 && RR-128> -10) lcd.print(" ");
      if(RR>128) lcd.print("+");
      lcd.print(RR-128);
      lcd.print("      ");
      break;

    case 5:    // RL
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print(" SET  REAR  L ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      if(do_correct == 1) lcd.print("S");
      else lcd.print(" ");
      lcd.print("    ");
      if(RL-128 < 10 && RL-128> -10) lcd.print(" ");
      if(RL>128) lcd.print("+");
      lcd.print(RL-128);
      lcd.print("      ");
      break;

    default:   // BACK
      lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
      lcd.print("     BACK     ");
      lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
      lcd.print("              ");
      break;
    }
    break;

  case 3:        // remote bass info (this is a temp LCD INFO)
    lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
    lcd.print("  SUBWOOFER   ");
    lcd.setCursor(1, 1);       // cursor at 2nd character, 2 line 
    lcd.print("     ");
    if(SU-128 < 10 && SU-128> -10) lcd.print(" ");
    if(SU>128) lcd.print("+");
    lcd.print(SU-128);
    lcd.print("      ");  
    break;

  case 4:        // remote lcd on/off info(this is a temp LCD INFO)
    lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
    lcd.print("   LCD MODE   ");
    lcd.setCursor(1, 1);       // cursor at 2nd character, 1 line 
    if(enable_lcd == 1)lcd.print("      ON      ");
    else lcd.print("    TIMEOUT   "); 
    break;

  case 5:        // remote rear info (this is a temp LCD INFO)
    lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
    lcd.print("  REAR L & R  ");
    lcd.setCursor(1, 1);       // cursor at 2nd character, 1 line 
    lcd.print("  ");
    if(RR-128 > -10 && RR-128 < 10) lcd.print(" ");
    if(RR > 128) lcd.print("+");
    lcd.print(RR-128);
    lcd.print("    ");
    if(RL-128 > -10 && RL-128 < 10) lcd.print(" ");
    if(RL > 128) lcd.print("+");
    lcd.print(RL-128);
    lcd.print("  ");  
    break;

  case 6:        // remote cen info (this is a temp LCD INFO)
    lcd.setCursor(1, 0);       // cursor at 2nd character, 1 line 
    lcd.print("    CENTER    ");
    lcd.setCursor(1, 1);       // cursor at 2nd character, 1 line 
    lcd.print("     ");
    if(CE-128 > -10 && CE-128 < 10) lcd.print(" ");
    if(CE > 128) lcd.print("+");
    lcd.print(CE-128);
    lcd.print("      ");
    break;

  default:     // should not go here
    break;
  }
}

//******************************************************************************
// EEPROM READ AND WRITE
//******************************************************************************
//------------------------------------------------------------------------------
// Store settings in eeprom
//------------------------------------------------------------------------------
void write_to_eeprom()          
{
  EEPROM.write(0, volume);      // store volume settings in address location 0 (1 byte for location)
  EEPROM.write(1, FL);          // ...        
  EEPROM.write(2, FR);                  
  EEPROM.write(3, CE);                  
  EEPROM.write(4, SU);                  
  EEPROM.write(5, RL);                      
  EEPROM.write(6, RR);                  
  EEPROM.write(7, CH);                   
  EEPROM.write(8, enhance);              
  EEPROM.write(9, amp);                 
  EEPROM.write(10, mute);               
  EEPROM.write(11, unit);               
  EEPROM.write(12, mode);               

  EEPROM.write(13, external_buttons);   
  EEPROM.write(14, external_remote);    
  EEPROM.write(15, enable_lcd);         
}

//------------------------------------------------------------------------------
// Read from eeprom and store to our global variables
//------------------------------------------------------------------------------
void read_from_eeprom()
{
  volume = EEPROM.read(0);
  FL = EEPROM.read(1);
  FR = EEPROM.read(2);
  CE = EEPROM.read(3);
  SU = EEPROM.read(4);
  RL = EEPROM.read(5);    
  RR = EEPROM.read(6);
  CH = EEPROM.read(7); 
  enhance = EEPROM.read(8); 
  amp = EEPROM.read(9);
  mute = EEPROM.read(10);
  unit = EEPROM.read(11);
  mode = EEPROM.read(12);

  external_buttons = EEPROM.read(13);
  external_remote = EEPROM.read(14);
  enable_lcd = EEPROM.read(15);
}

//******************************************************************************
// FLAGS
//******************************************************************************
//------------------------------------------------------------------------------
// Default settings of global flags
//------------------------------------------------------------------------------
void default_flags()
{
  int1 = 0;                 // interrupt flag for button 1
  int2 = 0;                 // interrupt flag for button 2
  int3 = 0;                 // interrupt flag for button 3     
  int4 = 0;                 // interrupt flag for button 4 
  button_menus = 0;         // global variable for menu
  set_menu = 0;              // global variable for sub_menu 
  temp_menu = 0;             // global temp menu varaiable (for quick status)
  set_corrections_menu = 0;      // global variable for sub_menu of corrections
  read_value = 0;                // read encoder flag value in menu
  do_this = 0;                   // select menu flag
  do_correct = 0;                // select correct flag
  temp1 = -1;                     // flag for enable buttons
  temp2 = -1;                     // flag for enable remote
  temp3 = -1;                     // flag for enable lcd
  temp4 = -1;                     // flag for unit on/off     
  encoder_value = 0;              // value for encoder
  clicks = 0;                     // value for encoder clicks
  lcd_flag = 0;                   // lcd_flag
}
I used my own software timeout feature with a simple counter, it is used for:
  • check IR (delay between IR receive)
  • delay for reading buttons (for debounce)
  • LCD (to turn off after some time if active)
  • temporary LCD info (shows it, then goes to normal after some time)

I went with my method, all though I could use a function millis(). The problem here is that millis() overflow after approximately 50 days. And here a problem could occur, resulting as an unstable system (subtracting unsigned smaller value with a larger one). If someone has more experience working with this function please fell free to leave a comment. Well my counters get stuck at some point and they wait until called again (set to 0). With this I have full control, and never have to worry about overflow. These count delays are defined at the beginning. A reminder that this is not an accurate delay, but it is good enough to do my bidding. Of to uploading software to my board.



My breadboard has a external connector, from which you can connect your Arduino Uno and program the unit. You just connect TX, RX, Reset, GND, and +5V to your Arduino Uno with no chip in. Be aware that you also must disconnect jumper on the breadboard that supplies +5V from the amplifier. Reason why I'm using USB power is that, that sometimes Arduino Uno doesn't want to reset Atmega328p on upload if powered externally. Thus resulting in error message "not in sync".  When uploading is finished, disconnect Arduino Uno and put the jumper back on. Now you can connect TX and RX for external control.


When upload was complete I hooked the system up and gave it a try. Everything was working, except one little hardware bug I had. I forgot to connect 100nF decoupling capacitor to reset and GND line. This was showing as an unexpected system reset when there was an EMF disturbance. When capacitor was added, this problem disappeared.

I have also tested my software in any possible way and it is very responsive, does not hang and is well commented.  When compiled it uses 24116 bytes of memory, so there is still room for new features. Below you can see the video of what it can do.


First of all, I apologise about poor video quality and editing.. it was my first time. Audio on the video is poor because of poor microphone in my camera, otherwise audio is excellent quality. Volume on computers were set to 50%, that's why amplifier was laud at volume 60, otherwise it gets pretty laud at volume 50. I also had to censor some parts of the video, because my static IP was showed. And that's just for me to know :)
About controlling over Internet... I used Ethernet shield and Arduino Uno. System is communicating with TV and my Amplifier over serial connection, computer and other stuff are just normal digital outputs for now. Like I said, this is still a prototype and I'm still working on it. More of this will be published in my future posts.

Serial communication is the same as in my Serial LCD project with MSP430. Command must start with a code (in my case "AMP1") for system to accept it, other code and values must follow and a end of line (\n) to tell the system transmission is over. Here is a table of serial commands:


Serial commands:
code: AMP1
code
00
x
\n
x = 0 - 1
Turn unit ON/OFF
code
01
x
\n
x = 0 - 4
Select audio in
code
02
x
\n
x = 0 - 3
Select mode
code
03
FL
xxx
\n
x = 88 – 168
Front Left correct

FR
xxx
\n
x = 88 – 168
Front Right correct
SU
xxx
\n
x = 88 – 168
Subwoofer correct
CE
xxx
\n
x = 88 – 168
Center correct
RR
xxx
\n
x = 88 – 168
Rear Right correct
RL
xxx
\n
x = 88 – 168
Rear Left correct
code
04
x
\n
x = 0 – 1
Set enhance
code
05
x
\n
x = 0 – 1
Set boost
code
06
xx
\n
x = 0 – 79
Set volume
code
07
x
\n
x = 0 – 1
Set mute
code
08
x
\n
x = 0 – 1
Set external buttons
code
09
x
\n
x = 0 – 1
Set external remote
code
10
x
\n
x = 0 – 1
LCD settings
code
11
\n

Store in eeprom
code
20
\n

Return status
code
21
\n

Return correction settings
Response:
code
OK
\n
Respond with OK
code
PxCxMxExAxVxxMUxBxRxLx
\n
Return status
code
LxxxRxxxSxxxCxxxLxxxRxxx
\n
Return corrections

You can find table of commands with descriptions in my download.

There is still room for improvement, and new features... but for now I don't need them:
  • send strings on LCD via serial communication
  • work in stealth mode (LCD and LEDs completely off)
  • IR receiver sends IR values over serial communication for other applications
  • configure count delays in system menu
  • configure IR hex values over serial communication 
  • ...

You can download entire project here.

All comments, opinions and suggestions are welcome.

Next posts will be about computer remote control and controlling over Internet. Until next time... happy hacking