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