Muffsy Stereo Relay Input Selector

Open Source, versatile audio relay input selector controlled by an ESP32

Similar projects worth following
My take on the stereo relay input selector.

* The input selector is controlled by an ESP32 with full pin break-out for maximum flexibility

This project uses Panasonic TQ-2 relays and separates power and signal grounds to prevent any switching noise, clicks or pops in the audio channels.

This project is fully open source. Use it for any purpose and change it into whatever you like. I would appreciate some credit if you decide to fork the project, but it's not required.

This project is fully built and tested. There's no complete programming of the ESP32 offered as of yet.

Eagle project files and gerbers are available in the files section.


The complete DIY kit is for sale on Tindie:

The code, gerbers and Eagle files all reside here:

Instructions on


  • Four stereo inputs and one mute circuit, switched with Panasonic TQ-2 relays
  • One stereo output
  • Controlled with ESP32
  • Space for the ESP32S/NodeMCU, with break-out for all pins
  • IO pins go high when activating a relay, can be used for indicator LEDs
  • Power/GND for ESP32 and relays separate from signal power/GND
  • Fully open source, no strings attached

Schematics, Downloads and other resources

  • Source code, Eagle project files and Gerbers are available on Github:

    Eagle project files, Gerbers and source code found on these project pages are outdated, but kept for reference. Go to Github for the latest revisions.

ESP32 Module

The ESP32 module used for this project is the one called ESP32s NodeMCU. It's got 2x19 pins, no pin labels on the top, and a white bottom side with the text "NodeMCU ESP-32S v1.1"

The ESP32 development kit has a CP2102 USB to UART bridge, drivers are available from Silicon Labs:

Information on how to program the ESP32 using the Arduino IDE is available from Espressif. The board name in the Arduino IDE is "NodeMCU-32S", as it says on the backside of the board:

Espressif's "IoT Development Framework" IDF/SDK is available here, along with a lot more information:

Just the Gerber files for the new version with mute:

x-zip-compressed - 312.58 kB - 03/01/2019 at 08:48


Smaller version, added output for solid state relay, and mute. Zip file includes Gerbers for PCB manufacturing. More info here:

application/x-zip-compressed - 323.03 kB - 02/03/2019 at 20:47


Smaller version, added output for solid state relay. Zip file includes Gerbers for PCB manufacturing. More info here:

x-zip-compressed - 618.43 kB - 11/02/2018 at 09:08


Muffsy Relay Input Selector - Eagle project files

x-zip-compressed - 39.45 kB - 02/26/2018 at 07:19


Muffsy Relay Input Selector without ESP32 - Eagle project files

x-zip-compressed - 46.23 kB - 04/15/2018 at 15:28


View all 8 files

View all 8 components

  • An even better idea - Mute circuit

    skrodahl01/15/2019 at 06:25 0 comments

    The design files are available in the files section.

    To avoid pops when turning the preamp circuitry on and off, a mute circuit is really useful. So I sacrificed one input and converted the fifth relay to mute. 

    The functionality of this board is now:

    • Four inputs, one input, one mute relay
    • All relays (except the SSR control, which technically isn't a relay) have flyback diodes to prevent clicks and pops
    • The relays (TQ2-5V) are "make before break", which also prevents clicks and pops when changing to another input
    • Separate power and signal GND
    • Mute, goes between the preamp output and the power amp input.
    • Control for Solid State Relay (SSR) 
    • Break-out for all ESP32 pins (even those that are in use for relays)
    • Control inputs with rotational switch 
    • Control inputs, SSR and mute with ESP32

    Here's a quick and dirty drawing in MS Paint to show what I mean:

    Picture of the actual board:

    Below is the design in Eagle, the big red rectangles are the two separate ground planes. A Post-IT note is 76x76 mm by the way.

    Here's the final build.

    Arduino Code, Four Inputs and Mute

    /* Muffsy Relay Input Selector
     *  Control relays using IR and rotary encoder
     *  Control external power to amp using IR and push button
      * powerState:
      *   0: Boot
      *     powerOn()
      *       startup procedure
      *       read NVRAM (relayCount)
      *       set relays to off (previousRelay = relayCount)
      *       set power amp to off, SSR = LOW
      *   1: Powered ON
      *       turn on power button LED
      *       set power amp to on, SSR = HIGH
      *       trigger relayOn(): previousRelay = relayCount + 1
      *     rotaryEncoder()
      *       increases or decreases relayCount depending on rotational direction
      *       pushbutton: Power ON/OFF
      *       does only Power ON if powerState == 2
      *     irRemote()
      *       input up/down
      *       input direct (buttons 1-5)
      *       power on/off
      *       does only Power ON if powerState == 2
      *     relayOn()
      *       activates relays based on the relayCount
      *       handles relayCount too high or low
      *     powerControl()
      *       read power button, set powerState accordingly
      *    2: Powered OFF
      *     turn off all relays
      *     set power amp to off (SSR = LOW)
      *     powerControl()
      *       read power button, set powerState == 1 if pushed
      *     irRemote()
      *       read power button, set powerState == 1 if pushed
     // Libraries
     #include <IRremote.h> // IR Remote Library
     #include <EEPROM.h> // EEPROM Library
     //  Size: 1 (relayCount)
     #define EEPROM_SIZE 1
     // Variables, pin definitions
     // Onboard LED/Power LED
     #define LED 2
     // IR Receiver pin and setup
     #define IR_Recv 13
     IRrecv irrecv(IR_Recv);
     decode_results results;
     // Power button
     #define poweronButton 14
     // Pins for the rotary encoder:
     #define rotaryA 35
     #define rotaryB 34
     // Relays
     #define R1 23
     #define R2 22
     #define R3 21
     #define R4 19
     #define R5 18
     //Solid State Relay
     #define SSR 17
     // Rotary Encoder variables
     int counter = 0; 
     int previous = 0;
     int aState;
     int aPreviousState;  
     // Relay Array
     int relays[] = {23, 22, 21, 19, 18};
     // Relay variables
     int relayCount;
     int previousRelay;
     int relayNumber;
     // Power/Mute variables
     int powerState;
     int buttonState = 1;       // the current reading from the input pin
     int lastButtonState = 1;   // the previous reading from the input pin
     unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
     unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers
     int mute = 0; // Mute on/off (1/0)
     // Setup
     void setup() { 
       // Power button
       pinMode (poweronButton,INPUT_PULLUP);
       // Onboard LED
       pinMode (LED,OUTPUT);
       // Rotary Encoder
       pinMode (rotaryA,INPUT);
       pinMode (rotaryB,INPUT);
       // Reads the initial state of the rotaryA
       aPreviousState = digitalRead(rotaryA); 
       // Relays
       pinMode (R1,OUTPUT);
       pinMode (R2,OUTPUT);
       pinMode (R3,OUTPUT);
       pinMode (R4,OUTPUT);
       pinMode (R5,OUTPUT);
       pinMode (SSR,OUTPUT);
       // Relay...
    Read more »

  • Full Functionality - Rotational Encoder and Remote Control

    skrodahl12/06/2018 at 23:36 1 comment

    Here's my test setup:

    More about this below. But first, getting there was not all that easy:

    IRremote.h and the ESP32

    IRremote.h does not want to play nice with ANY library saving anything to the NVS memory. That's sort of a letdown if you'd like to use an infrared remote AND save preferences or states to the ESP32's internal storage.

    Here's what happens (continuously, like every 0.2 of a second), showing only one instance of the error:

    ets Jun  8 2016 00:22:57
    rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    configsip: 0, SPIWP:0xee
    mode:DIO, clock div:1
    entry 0x400802e4
    Guru Meditation Error: Core  1 panic'ed (Cache disabled but cached memory region accessed)
    Core 1 register dump:
    PC      : 0x400d0d78  PS      : 0x00060034  A0      : 0x40081664  A1      : 0x3ffc0be0  
    A2      : 0x00000001  A3      : 0x00000002  A4      : 0x000000ff  A5      : 0x40086d14  
    A6      : 0xf0000040  A7      : 0x00290000  A8      : 0x80081188  A9      : 0x3ff5f024  
    A10     : 0x3ffc1044  A11     : 0x20000000  A12     : 0x00000400  A13     : 0x3ffb1e60  
    A14     : 0x00000020  A15     : 0xffffffff  SAR     : 0x00000015  EXCCAUSE: 0x00000007  
    EXCVADDR: 0x00000000  LBEG    : 0x400012e5  LEND    : 0x40001309  LCOUNT  : 0x800d3245  
    Core 1 was running in ISR context:
    EPC1    : 0x4008a377  EPC2    : 0x00000000  EPC3    : 0x00000000  EPC4    : 0x400d0d78
    Backtrace: 0x400d0d78:0x3ffc0be0 0x40081661:0x3ffc0c00 0x4008a374:0x00000000

    I tried using EEPROM.h and Preferences.h. It still behaves the same, so I guess the problem must lie with IRremote.h.

    The Solution

    Sure enough, here's somebody who's encountered the same problem. Not only that, he's also solved it:

    His solution lets you turn off the IR Receiver when you do an EEPROM.commit, and turn it on again afterwards.

    A small problem though, the instructions were a little off. Here's how to make IRremote.h work on the ESP32:

    Change the File IRremote.h

    On Windows, you'll find the file in Documents\Arduino\Libraries\IRremote\IRremote.h. Search for the following line:

    void  enableIRIn ( ) ;

    Replace it with:

    void  enableIRIn (bool enable) ;

    Don't forget to do the changes described here as well:

    At the end of the file IRremote.h, find this piece of code:

    const int sendPin = SEND_PIN;
    } ;

    Replace the first line (#else) with:

    #elif defined(SEND_PIN)

    Replace the File esp32.cpp

    On Windows, you'll find the file in Documents\Arduino\Libraries\IRremote\esp32.cpp. Replace everything in the file with this code:

    #ifdef ESP32
    // This file contains functions specific to the ESP32.
    #include "IRremote.h"
    #include "IRremoteInt.h"
    // "Idiot check"
    #error Must undef USE_DEFAULT_ENABLE_IR_IN
    hw_timer_t *timer;
    void IRTimer(); // defined in IRremote.cpp, masqueraded as ISR(TIMER_INTR_NAME)
    // initialization
    void  IRrecv::enableIRIn (bool enable)
        // Interrupt Service Routine - Fires every 50uS
        // ESP32 has a proper API to setup timers, no weird chip macros needed
        // simply...
    Read more »

  • Example Code - IR Controlled SSR

    skrodahl11/13/2018 at 20:36 0 comments

    The new, smaller board can control a solid state relay (or turn on/off 5V to anything else) on port IO17. Here's some example code using an IR remote to control that port. 

    This code can be changed into controlling the input relays as well, of course.

    Pressing one button on your remote turns the SSR on, another button turns it off. Read below to decide which buttons they are.The on-board LED lights up when the SSR is turned on.

    Getting the code to compile

    Note that IRremote.h won't compile unless you change it according to this forum post:

    Change the end of IRremote.h (probably in your Documents\Arduino\libraries\IRremote folder) from:

    const int sendPin = SEND_PIN;
    } ;


    #elif defined(SEND_PIN)
    const int sendPin = SEND_PIN;
    } ;

    Note that it's only the first row that's changed, the rest is there to make sure you find the right section of the code.

    IR Receiver 

    I've used a TSOP4838 (not sure if others will work), and connected pin 1 to IO13 on the ESP32. The IR receiver's pin 2 connects to GND and pin 3 to +3.3V.

    Open the Serial Monitor in the Arduino IDE, and it will print the IR codes it receives. Use them directly in the program below, the lines are commented with "Button chosen for "ON" / "OFF". Recompile and transfer it to the ESP32. 

    Here's me pressing "1", "2" and "3" on my remote control. "1" and "2" are already configured as "ON" and "OFF":

    The first output, "7770223", is the "1"-button on my remote control. So I added it to the row that says "Button chosen for "ON"". The next number shown, "7774303", is the "2"-button on my remote. It was added to the row that says "Button chosen for "OFF"". I even pressed "3" on my remote, "7766143", but did nothing with that value.

    Example Code

    * Muffsy Relay Input Selector
    * Turn a solid state relay ON or OFF using IR
    * Pin 1 on TSOP4838 IR receiver is connected to IO13 on the Relay Board
    * (Pins 2 and 3, GND and 3.3V respectively)
    * 2018-11-13
    * Creative Commons Attribution 4.0 International
    * IRremote.h can be installed directly in the Arduino IDE,
    * using "Tools -> Manage library". Search for IRremote, 
    * select the one by Ken Shiriff
    * The ESP32 board on the Muffsy Relay Input Selector is called 
    * "NodeMCU-32S" in the Arduino IDE
    #include <IRremote.h>
    int IR_Recv = 13; // IR Receiver, IO13
    int SSR = 17; // Solid State Relay, IO17
    int LED = 2;  // Onboard LED, Pin IO2
    int SSRState; // Declare integer variable for SSRState
    IRrecv irrecv(IR_Recv);
    decode_results results;
    void setup() {
      Serial.begin(9600);  // Starts serial communication
      irrecv.enableIRIn(); // Starts the receiver
      pinMode(SSR, OUTPUT); // Sets the SSR pin as output
      pinMode(LED, OUTPUT); // Sets the onboard LED pin as output
      SSRState = 0; // Start with the SSR set to "OFF"
    void loop() {
      // Turn SSR and LED "OFF" if SSRState is 0, "ON" if SSRState is 1
      if (SSRState == 0) {
        digitalWrite(SSR, LOW);
        digitalWrite(LED, LOW);
      else {
        digitalWrite(SSR, HIGH);
        digitalWrite(LED, HIGH);
      // Decodes the infrared input
      if (irrecv.decode(&results)) {
        long int decCode = results.value;
        // Switch case to use the selected remote control button
        switch (results.value) {
          case 7770223: // Button chosen for "ON"
              digitalWrite(SSR, HIGH);
              digitalWrite(LED, HIGH);
              Serial.println("Turned SSR ON");
              // Set SSRState to 1 ("ON")
              SSRState = 1;
          case 7774303: // Button chosen for "OFF"
    Read more »

  • Smaller version, New Software

    skrodahl11/02/2018 at 09:22 0 comments

    I found that I needed a smaller board, so I've shaved of about an inch of the PCB's width. It's now about the size of a standard Post-It.

     I've also added another 5V controllable output on IO17, since I need to use a solid state relay to power on and off a power amp. 

    The Eagle design files and Gerber files are available in the Files section as

    Improved software for the ESP32 is available on github

    The ESP connects to your wireless networks and presents a web page with buttons to select inputs. It also has an HTTP GET API if you want to make your own web page for it.

  • Combating the 250V peak when turning off the relay

    skrodahl05/25/2018 at 08:01 0 comments

    I just found this video that shows very clearly why the diodes on the relays are needed. Worst case, you'll get a 250V peak without them. This can harm your electronics, and will create very audible pops.

    The Muffsy Relay Input Selector combats this by using the flyback diodes, of course, and separating the power and signal grounds.

  • Lo Tech Muffsy Stereo Relay Input Selector - Without ESP32

    skrodahl04/15/2018 at 15:22 0 comments

    A version that excludes the ESP32 and adds LEDs has been requested. So I made one. Eagle project files and Gerbers are available in the files section.

    Here it is (just imagine the drum roll first):

  • Wifi problem gives great cable for rotary switch - The Muffsy Input Switch is now fully tested

    skrodahl04/06/2018 at 13:27 0 comments

    This project log discusses using the relay board without the ESP32. There's now a board designed specifically for this:

    One problem with projects like these is that the wiring has a tendency to end up as a heap of spaghetti.

    So I've been wondering about how I should do the rotary switch in particular (in a setup without the ESP32 module). This is the simplest setup, and you don't have to write any code at all.

    Coinciding with this, wireless coverage isn't that great in my new house. So I bought a Google Wifi bundle, and look what came with it:

    Yup, a very flat network cable. Great timing, hey? Let's get snipping!

    I kept the white leads on each side, and cut the white ones in the middle. The switch is 2P5T, I'll only use one of the poles. Yeah, and the ESP32 module will be removed.

    Here's the board without the ESP32 and the transistors, cable fully connected:

    There are crimp connectors on the rotary switch, since it's fairly sensitive to heat (such as from a soldering iron). That's it, apart from connecting power to the relays, connect inputs and output, and cut the flat cable to the correct length.

    Finally, with power connected. The rotary switch works perfectly, turn it to slowly and it will break one channel before connecting the other one. So no two channels mixed at any time.

    There you have it. A stereo relay input switch that can be controlled by either an ESP32-module or a rotary switch. Fully tested and all.

  • Simple sketch - Switch between Relay1 and 2, every 4 seconds

    skrodahl03/31/2018 at 15:53 0 comments

    This sketch will:

    • Define all the relays, and the ESP32's internal LED
    • Turn on Relay1, blink internal LED once
    • Wait four seconds
    • Turn off Relay1, turn on Relay2, blink internal LED twice
    • Wait four seconds, turn off Relay2 and start over
    • It also writes to the serial monitor when a relay is turned on or off, and uses two different ways of blinking the LED

    This shows how to turn on and off relays. Remember, you can connect an LED between the relay1-5 IO pins together with a 330 ohms resistor to GND. This LED will light up when the corresponding relay is active.

    The ESP32 has the capability to store the last selected relay, so it can be selected directly if the device is turned off and on again. (not shown here)

    Instead of just looping through the relays you can use push buttons, a potentiometer or a rotary encoder (and lots of other things that can hook up to the ESP32) to decide which relay is turned on.

    #define LED   2       // Internal LED
    #define relay1 23     // relay1 on IO23
    #define relay2 22     // relay2 on IO22
    #define relay3 21     // relay3 on IO21
    #define relay4 19     // relay4 on IO19
    #define relay5 18     // relay5 on IO18
    void setup()
        pinMode(relay1, OUTPUT);
        pinMode(relay2, OUTPUT);
        pinMode(relay3, OUTPUT);
        pinMode(relay4, OUTPUT);
        pinMode(relay5, OUTPUT);
        pinMode(LED, OUTPUT);
    void loop()
        // Turn on relay1
        Serial.println("Relay #1 - ON");
        digitalWrite(relay1, HIGH);
        // Blink internal LED once
        digitalWrite(LED, HIGH);
        digitalWrite(LED, LOW);
        // Wait 4 seconds
        // Turn off relay1
        // Turn on relay2
        Serial.println("Relay #1 - OFF");
        digitalWrite(relay1, LOW);
        Serial.println("Relay #2 - ON");
        digitalWrite(relay2, HIGH);
        // Blink internal LED twice
        for (int counter=0; counter<2; counter = counter+1){
          digitalWrite(LED, HIGH);
          digitalWrite(LED, LOW);
        // Wait 4 seconds
        // Turn off Relay2
        Serial.println("Relay #2 - OFF");
        digitalWrite(relay2, LOW);

  • Eagle Project Files and Gerbers

    skrodahl02/26/2018 at 15:13 0 comments

    Eagle project files and Gerbers are now available in the files section.

  • First Test of Audio Switching - Success

    skrodahl02/25/2018 at 21:30 0 comments

    The ESP32S is now mounted on pin headers:

    Testing with an oscilloscope and the QA401 audio analyzer reveals that there's no switching noise, nor any clicks or pops injected in the audio channels. And, it does of course switch the left and right stereo channels. :) 

View all 15 project logs

Enjoy this project?



Samy33 wrote 11/04/2021 at 14:30 point

Great job  skrodahl! Cangratulations! Furthermore, 2 outputs for a simple H-Bridge for motorized potentiometer would be very useful. Up and down from remote controller, volume buttons. Is hard to make the modifications in code? I am not a programmer but I like to do some projects.

  Are you sure? yes | no

jtomasrl wrote 05/12/2020 at 01:20 point

Excellent project. Can I use this input selector to go from a TOSLINK input (I2S) to a I2S decoder?

  Are you sure? yes | no

Maarten wrote 02/26/2019 at 21:12 point

Great project, thanks skrodahl! PCB and components are on the way. Has anyone found a nice enclosure to mount this in?

  Are you sure? yes | no

kottj wrote 02/02/2019 at 19:08 point

Great project! When can we  expect project files for  a version with mute?

  Are you sure? yes | no

skrodahl wrote 02/03/2019 at 20:49 point

Sorry for taking so long time to post the project files. Eagle sch/brd, Gerbers and the cam-file for creating the Gerbers are included:

  Are you sure? yes | no

Pepe Aracil wrote 12/08/2018 at 08:27 point


Can I add your board to the list of supported hardware in the virkey project?

  Are you sure? yes | no

skrodahl wrote 01/09/2019 at 23:00 point

Absolutely, if you think it fits in there. It's open source and freely available. 

  Are you sure? yes | no wrote 03/12/2018 at 10:24 point

Hey, do you plan to offer this as a kit like with the MC Phono Amp? I would be very interested.

  Are you sure? yes | no

skrodahl wrote 03/20/2018 at 15:28 point


Due to the cost of both the ESP32 module and the relays, it would be a very expensive kit indeed. Also, it lacks a power supply.

The answer is no on this one. But you do have all the design files to have your own boards produced, and a Mouser shopping cart for easy ordering of the components.

  Are you sure? yes | no

Zoltan Sisko wrote 02/25/2018 at 22:57 point

The emitter and collector of the transistors are reversed on the schematic diagram.

  Are you sure? yes | no

skrodahl wrote 02/26/2018 at 07:14 point

In what way? I am connecting two grounds. The circuit is shorted when the base goes high, regardless which sides are connected to the base and the collector.

I have changed it though, so it looks the way it "should". New schematics, Eagle project files and gerbers are in place.

  Are you sure? yes | no

Zeev Glozman wrote 02/06/2018 at 16:28 point

Hey i just wanted, to share this while its a completely differnet application, its an interesting read.

  Are you sure? yes | no

skrodahl wrote 02/02/2018 at 15:25 point

The following steps have been taken to avoid pops and noise:

 * Using "make before break" relays
 * There are diodes between the relay rails
 * The relay power/GND is separated from signal/GND (which means this things needs its own separate PSU to work as intended)

I hope that will make this thing silent. I won't know before I build it though. PCBs are on their way on the slow boat, so it'll be a week or four...

  Are you sure? yes | no

skrodahl wrote 03/02/2018 at 10:18 point

UPDATE: The relay board has now been fully tested using an oscilloscope and the QA401 audio analyzer.

There are no clicks, pops or noise of any kind entering the audio channels.

  Are you sure? yes | no

K.C. Lee wrote 02/02/2018 at 12:47 point

I had to use relay for muxing audio because I didn't want to wait for my PCB.  It worked but there are pops when they switches.  Video shows it.

Relay contacts bounce during switching and inject noise into the audio. That's why I didn't use them for my design.    Analog MUX on the other hand are quiet.

If you still want relays, you could mute the output during switching.  Also looks for something with less bounce.

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates