/* * This is a repair of the "source selection" circuit of the Sansui AU-X517R audio amplifier, * using an Arduino Nano. * * In the second approach, I am using a circle method, because the first approach failed in * handling movement of the selector button more than one position. * Now, I mimic exactly the behaviour of the rotary switch, dividing the circle in 360 pieces * and perform what is expected from the switch in each piece. * * The original circuit utilizes an ALPS (Japan) module, containing three sets of rotary switches. * Each set has a total of 12 pins, where one pin is used as a common and is connected * in a rotary fashion to each of the other 11 pins. * All three sets are rotated together via a motor. The two sets are wired to 6 sources * (PHONO, TUNER, CD, LINE, TAPE1, TAPE2) and each of these sets is associated with * the Left and Right channel, respectively. * * The third set is wired to a resistor ladder. The output of the resistor ladder is used * by the original microcontroller to identify the position of the switch, via an ADC. * Subsequently, the controller activates the motor to bring the switch to the desired location. * * The intermediate pins of the audio switches (2, 4, 6, 8, 10, 12) are wired to audio ground. * The intermediate pins of the resistor ladder (2, 4, 6, 8, 10, 12) are left unconnected. * * Due to a broken plastic gear of the original circuit and since the module is no longer available * in the market (https://www.aliexpress.com/item/bella-Japanese-original-band-switch-ALPS-Alps-motor-shaft-length-30MM-6-speed-rotary-switch/1363271059.html), * I will replace it with an Arduino board which mimics the behaviour of the original * circuit. In detail: * * Two digital inputs are connected to the motor control circuit (LB1641). Their input table is: * * IN1 IN2 Action * ------------------ * 0 0 Brake * 1 0 Forward drive * 0 1 Reverse drive * 1 1 Brake * * Based on the above instruction, the new circuit will cycle through a set of pins which provide a GND * to the external resistor ladder. When the desired position is reached as defined by the setting of the * control to brake, the new position will have been identified and a relay will activate the audio input * from the desired audio source. * * Please note that we do not use the analog voltage provided by the Selector button to control our movement. * This happens because the system can be remotely operated via an infrared remote control. * Therefore, we only use the position of the Selector for the initial startup and * we use the motor control pins for any subsequent movement. * */ // Digital output pins activating the relays // D4, D5, D6, D7, D8 and D9 const int audioControlPins[] = {4, 5, 6, 7, 8, 9}; // Pins connected to the switch position control resistor ladder. // The first pin is the first resistor divider and the output of the ladder. // We can even use the A/D converter of the Arduino // to read the analog voltage of this pin. const int resistorLadderPins[] = {A5, A4, A3, A2, A1, A0}; // Number of audio sources. This must be the number of // elements in the above arrays const int NUM_SOURCES = 6; // The two pins which controlled the movement of the motor // and are now becoming our control input. // D10 going high is forward and D11 going high is reverse const int motorControlPins[] = {10, 11}; // Pin connected to the selector resistor ladder const int selectorAnalogPin = A6; // Variable to set the current index in the above arrays int currentSourceIndex; // Variable to set the previous source index int previousSourceIndex; // From 0 to 359 int positionInCircle; // One of the 6 circle segments. From 0 to 5 int circleSegment; // The time required for the simulated motor to travel one of the 360 degrees // in milliseconds const int ROTATION_SPEED = 5; // Digital output state that activates an audio relay const bool AUDIO_ACTIVE = HIGH; //------------------------------------------------------------------------------ void setup() { // As soon as possible... initAudioControlPins(); // It also disables all sources // Set the two control pins as Input. // The circuit uses a diode in each pin to identify a 0, // because the chip send 2V as HIGH. That is why we need the // pull-up resistor, to keep the keep at 5V HIGH on idle. pinMode(motorControlPins[0], INPUT_PULLUP); pinMode(motorControlPins[1], INPUT_PULLUP); // Sets all switch position control ladder pins initially as INPUT // and simulate a high-Z state initResistorLadderPins(); // The pin which we use for analog reading pinMode(selectorAnalogPin, INPUT); // We use debugging below, so it is time to activate the serial port Serial.begin(57600); while (!Serial) { ; // wait for serial port to connect. Needed for native USB } // Read selector voltage to identify the current position of the switch // We will set this position as the current position of the switch currentSourceIndex = selectorPosition(); // Simulate the switch position control ladder to be equal to the // selector control ladder setControlIndex(currentSourceIndex); // Let the internal microcontoller to run its own A/D // and understand the position of the switch. // This is absolutely essential. // ** For some reason, without this delay, the next command did not activate the arduino pin. // ** Needs further investigation why this happens! How can the setting of an analog pin // inhibit the setting of a digital pin? Is the 2mA current too low and messes with the setting // of the analog pins? *** // Must be higher than 15 msec. Set 200 to be safe // In the normal loop we do not face such problem, probably because the rotation that we // implement takes a lot more time until the next source is activated. delay(200); enableSource(currentSourceIndex); // From our point of view, we are stable and previousSourceIndex points // to our current position. previousSourceIndex = currentSourceIndex; // The circle is divided in 6 zones, each of 60 degrees, from 0 to 59 // From 0 to 7 we have the half of the unused contact (1 of the 12 of the rotary switch) // From 8 to 21, we have a gap // From 22 to 37 we have the valid contact // From 38 to 51 we have a gap // From 52 to 59 we have the half of the next unused contact // We start considering that we are in the middle of the valid contact between 22 and 37 positionInCircle = (currentSourceIndex * 60) + 29; circleSegment = currentSourceIndex; } //------------------------------------------------------------------------------ void loop() { bool motorMoving = false; bool input_1 = digitalRead(motorControlPins[0]); bool input_2 = digitalRead(motorControlPins[1]); if (input_1 == HIGH && input_2 == LOW) { motorMoving = true; disableAllSources(); ++positionInCircle; if (positionInCircle >= 360) positionInCircle = 0; circleSegment = positionInCircle / 60; delay(ROTATION_SPEED); //printPosition(); } else if (input_1 == LOW && input_2 == HIGH) { motorMoving = true; disableAllSources(); --positionInCircle; if (positionInCircle < 0) positionInCircle = 359; circleSegment = positionInCircle / 60; delay(ROTATION_SPEED); //printPosition(); } else { motorMoving = false; circleSegment = positionInCircle / 60; currentSourceIndex = circleSegment; if (previousSourceIndex != currentSourceIndex) { // enable only if we have a change in source enableSource(currentSourceIndex); previousSourceIndex = currentSourceIndex; } } int indexOffset = circleSegment * 60; if (positionInCircle >= (indexOffset + 0) && positionInCircle <= (indexOffset + 7)) { // unused contact setHighZ(); } else if (positionInCircle >= (indexOffset + 8) && positionInCircle <= (indexOffset + 21)) { // gap setHighZ(); } else if (positionInCircle >= (indexOffset + 22) && positionInCircle <= (indexOffset + 37)) { // valid contact setControlIndex(circleSegment); } else if (positionInCircle >= (indexOffset + 38) && positionInCircle <= (indexOffset + 51)) { // gap setHighZ(); } else if (positionInCircle >= (indexOffset + 52) && positionInCircle <= (indexOffset + 59)) { // unused contact setHighZ(); } } //------------------------------------------------------------------------------ // Initializes the audio control pins. // Sets the pins as OUTPUT with initial state Inactive (= LOW) void initAudioControlPins() { for (int index = 0; index < NUM_SOURCES; ++index) { // Note the notation NOT(AUDIO_ACTIVE) digitalWrite(audioControlPins[index], !AUDIO_ACTIVE); pinMode(audioControlPins[index], OUTPUT); } } //------------------------------------------------------------------------------ // Enables an audio source by enabling the digital output // which controls a relay. // The input is an index to the audioControlPins array void enableSource(int index) { digitalWrite(audioControlPins[index], AUDIO_ACTIVE); Serial.print("Enabling source: "); Serial.print(index); Serial.print(", "); printSourceName(index); Serial.println(); } //------------------------------------------------------------------------------ // Disables all sources. We need to make // sure that our actions are not connecting two or more sources to each other void disableAllSources() { for (int index = 0; index < NUM_SOURCES; ++index) { digitalWrite(audioControlPins[index], !AUDIO_ACTIVE); } } //------------------------------------------------------------------------------ // Initialize all resistor ladder pins to high-Z state void initResistorLadderPins() { for (int index = 0; index < NUM_SOURCES; ++index) { // Avoid the internal pullup resistor digitalWrite(resistorLadderPins[index], LOW); // Set as inputs to create a high-Z state pinMode(resistorLadderPins[index], INPUT); } } //------------------------------------------------------------------------------ // Set all resistor ladder pins to high-Z state // Same as initResistorLadderPins, but without setting the port to LOW. // It is set once so there is no need to spend time doing that over and over. void setHighZ() { for (int index = 0; index < NUM_SOURCES; ++index) { // Set as inputs to create a high-Z state pinMode(resistorLadderPins[index], INPUT); } } //------------------------------------------------------------------------------ // Sets a specific resistor ladder pin to LOW // and subsequently source current through it. // This is achieved by setting it to output and write a 0 to it. // Since the port register has already been initialized to 0 // we do not need to re-write the 0. Just set pin as output. // This action simulates the position control of the rotary switch to be at // a specific location. The output of the switch control resistor ladder // will provide a voltage depending on the index, as follows: // // 0V TAPE2 // 0.8V TAPE1 // 1.5V LINE // 2.3V CD // 3V TUNER // 3.6V PHONO // // We can monitor this if we like by doing an analogRead(resistorLadderPins[0]) void setControlIndex(int index) { pinMode(resistorLadderPins[index], OUTPUT); } //------------------------------------------------------------------------------ // Checks analog input of the Selector button and // the output of the resistor ladder. // Used for testing void checkAnalogPorts() { int analogInput = analogRead(resistorLadderPins[0]); float voltageIn = float (analogInput * 5)/1024; Serial.print ("Voltage input: "); Serial.print(voltageIn); Serial.print("V"); int analogOutput = analogRead(selectorAnalogPin); float voltageOut = float (analogOutput * 5)/1024; Serial.print (" Voltage output: "); Serial.print(voltageOut); Serial.println("V"); } //------------------------------------------------------------------------------ // Reads the analog Selector pin to identify the current position // of the selector ressistor ladder. // NOTE: This is a separate resistor ladder from the switch control ladder // and is used to signal the 47C440 controller about the position of // the selector button. // The function is used only upon startup int selectorPosition() { int index; int analogIn = analogRead(selectorAnalogPin); float voltage = (float) (analogIn * 5)/1024; Serial.print("Selector voltage: "); Serial.print(voltage); Serial.println("V"); // Check the ladder voltage if (voltage <= 0.37) { index = 0; // 0V -> TAPE2 } else if (voltage > 0.37 && voltage <= 1.07) { index = 1; // 0.8V -> TAPE1 } else if (voltage > 1.07 && voltage <= 1.79) { index = 2; // 1.4V -> LINE } else if (voltage > 1.79 && voltage <= 2.52) { index = 3; // 2.2V -> CD } else if (voltage > 2.52 && voltage <= 3.19) { index = 4; // 2.9V -> TUNER } else { index = 5; // 3.5V -> PHONO } Serial.print("Current Selector Position (0-5): "); Serial.print(index); Serial.print(", Source: "); printSourceName(index); Serial.println(); return index; } //------------------------------------------------------------------------------ // Prints the name of the source input, based on the index void printSourceName(int index) { switch (index) { case 0: Serial.print("TAPE2"); break; case 1: Serial.print("TAPE1"); break; case 2: Serial.print("LINE"); break; case 3: Serial.print("CD"); break; case 4: Serial.print("TUNER"); break; case 5: Serial.print("PHONO"); break; default: Serial.print("Unknown"); } } //------------------------------------------------------------------------------ // Prints the position in the circle void printPosition() { Serial.print("Position in circle "); Serial.print(positionInCircle); Serial.print(" Index "); Serial.println(circleSegment); }