A remote control preamp in the works

Status
This old topic is closed. If you want to reopen this topic, contact a moderator using the "Report Post" button.
I'm just starting on a new preamp - the analog circuitry will be based on Rod Elliott's P88 and my Bugle 2 phono preamp, and the control circuitry will use an Arduino with a pile of relays, motorized pot, infra-red, bluetooth, and a rotary encoder. In addition to these elements, I will also be integrating a Mini-DSP for room correction, and an oDAC to provide music from my server.

All the signal flows will be in the analog domain once they are inside the box. The oDAC takes USB in, and the MiniDSP has built-in ADC and DAC.

The idea is ultimately to be able to control the whole system from my phone or a tablet over bluetooth, but also to support an IR remote and direct manipulation at the front panel.

It's going to take some time - the analog piece is pretty straightforward, but I want to write my own software and assemble all the hardware myself from a variety of sources.

As I go I'll try to share the software as it develops, and to document the process in case anyone here is in need of a soporific.

I don't imagine any of this is really new, but it might be interesting to someone wanting to implement some parts of the system.

Also, I'll probably be asking for help when my head gets lost in the spaghetti :confused:
 
Assembling the bits...

Here are some of the components: I'm still waiting for the microcontroller and the motorized pot...

This is a standard 20x2 display of the kind that you can buy for $5 or so (depending on where...China is cheapest). It interfaces directly to the Arduino using six pins, and will display the source and volume, and any other information I can think of. The Arduino LCD library makes talking to this very simple.

Display.jpg

This is an H-bridge board I found for about $6 - it will control a couple of motors using two pins plus an enable pin per motor. I will be using it to drive the DC motor on the back of a motorized potentiometer. I'm getting a pot with extra gangs, so I can use one as a sense channel. I'll read the voltage across one gang so I know where the wiper is. That way I can tell when the user manually manipulates the pot. (That's the theory - I haven't tested it yet).

H-Bridge.jpg

If I have enough pins, I'd like to use these power relays to control off-board power - that way I can use the preamp to turn other equipment on and off. The relays are rated at 10A at 125V or 250V.

Power-Relay.jpg

This is really the heart of the control system - eight DPDT signal relays. I'll use five for sources (phono, CD, DAC, tuner, tape/aux); one for tape source/monitor, one to switch the DSP in or out of circuit, and one for system muting.

The relays are controlled by one wire each. I'll probably use a shift register connected to the 8 control wires - that means I can get away with only three connections to the arduino (enable, clock, data). There are standard Arduino functions for writing data to the shift register, so I'm hoping this will be straightforward.

Signal-Relays.jpg
 
The build plan

I think the software will be the most complex element, although I'll probably have to be careful with layout and power supplies to keep digital noise out of the analog world. Here's what I'm thinking.

  1. Start writing the software. Test what I can on a breadboard.
  2. Build the analog system - +-15V power supply, preamp, volume control, one set of RCA inputs. Test. Listen to some music.
  3. Build the digital power supplies. I'll need 5V for the Arduino and relay switching and display. I think the pot / H-bridge needs 24V, but I'll know more when it arrives and I can test it. I'll keep the digital power supplies separate from the analog side.
  4. Integrate the Arduino (Leonardo is the current plan) with a display and rotary encoder. Prove I can read the encoder, update internal variables, and display status. At this stage the analog and digital systems do not interact.
  5. Add the signal relay board. This probably requires a small PCB / stripboard for the shift register. Get the source relay switching working from the rotary encoder. Build out the source control system and add a bushel of RCA plugs. Make sure there's no sound degradation.
  6. Add the wiring for the mute, DSP and tape/monitor relays. Add the new controls into the Arduino (rotary encoder-driven).
  7. Develop the volume control code and hardware. This is a bit more complex: i need to measure the state of the volume control, update the screen accordingly. This is the first phase, before I have other ways to alter volume (IR and bluetooth).
  8. Add IR capability and train from an Apple remote. Nothing too complicated: volume up/down, source up/down, mute, DSP in/out will be a good start.
  9. Develop volume control driver software for the H-bridge to implement volume messages from the IR remote.
  10. Add the bluetooth module. It's basically a serial port. I need to figure out how to drive it from my phone in an elegant way. It will do the same things the infrared remote will do, but it might have a nicer UI.
It seems like a lot of work, but I think if I break it down, and test it piece by piece, it should be possible. I'm sure there's more I'll think of as I go through it...
 
Arduino pin assignments

The Arduino Leonardo has 20 sense pins. I need all of them, I think, even using a shift register to drive the signal relays. And I guess I don't have enough pins to run the external relays.

Strictly speaking I don't need a separate mute relay - it will be enough to turn off all the inputs - but it seems cleaner somehow to have a relay at the output. We'll see how well it works.

Here's my pin assignment table:

Code:
// preamp_01.h
// Port definitions and constants

#define BT_RX         0                 // Bluetooth / serial receive data
#define BT_TX         1                 // Bluetooth / serial transmit data        
#define IR            2                 // The Infrared sensor
#define LCD_PORTS     3, 4, 5, 6, 7, 8  // two control and four data
#define VOL_SENSE    A0                 // analog in to read pot position
#define VOL_ADVANCE  10                 // H-bridge up
#define VOL_REDUCE   11                 // H-bridge down
#define VOL_ENABLE   12                 // H-bridge motor enable
#define ENC_SWITCH   13                 // the encoder push-button switch
#define ENC_1        A1                 // two pins to read the encoder
#define ENC_2        A2                 // 
#define RELAY_DS     A3                 // shift register data send
#define RELAY_CLK    A4                 // shift register clock
#define RELAY_STO    A5                 // shift register latch (store) data

Pins 0 and 1 are already set up to work with the serial port, so I will keep them so assigned, and they will be the in and out for the serial bluetooth module.

The IR module only needs a single pin.

The LCD display has two control pins and four data pins. They are driven by the LCD library provided with the Arduino - all I need to do is hook them up.

VOL_SENSE is an analog input, generating a value from 0 to 1023 from an analog voltage from 0 to 5V using the internal 10 bit DAC. I'll hook up one gang of the pot between 5V and 0 and measure the position of the wiper to figure out what my current volume setting is.

The next three pins are to drive the H-bridge that turns the pot motor clockwise or anti-clockwise. Two set the direction, and one is used to enable or disable the motor. It can be fed from a pulse-width modulated output, so I can set a suitable rotational speed.

The rotary encoder has three active pins. Two provide overlapping square waves from which I can tell which way the encoder is turning, and one is a momentary push switch that I can use to change a setting.

Finally, three pins to control the shift register that will control the relays.

My understanding is that the analog pins can also be used as digital, so the last six are used to sense the rotary encoder and drive the relays via the shift register.
 
Hi,

This is my first post here and I am very interested in your project. I've previously built a couple of small electrical projects and played around with an RPi but never used an Arduino. I am particularly interested in the part where you mentioned interacting with your phone via bluetooth - can I ask what type of phone you have and how you plan to do this?
 
Hi,

This is my first post here and I am very interested in your project. I've previously built a couple of small electrical projects and played around with an RPi but never used an Arduino. I am particularly interested in the part where you mentioned interacting with your phone via bluetooth - can I ask what type of phone you have and how you plan to do this?

Here's the theory: I don't yet have the bluetooth gadget, so I haven't tested it.

The Arduino has built-in serial communications - you can read and write characters to and from the serial port. Normally this is used for communicating to a PC via a USB or other connection. But this Bluetooth device plugs onto the serial connection, and it sets up a serial connection between the Arduino and anything that is paired to it. Then you can send information back and forth using the serial protocol.

So my plan is to create a small application for my iPhone that responds to key pressed by sending single characters to the preamp via bluetooth. The preamp will listen to the serial connection, and depending on what characters come through, it will take the appropriate action.

In a second version, I may try to make the communications bi-directional - writing status information back to the phone using the same mechanism. But I don't know yet how easy or hard this will be. I may prototype the connection using my Mac, which has a bluetooth capability, and which can easily send serial data.

This is a link to the same kind of bluetooth module (Ebay).

And a picture:
mjcDOErMVwtCrFIUiwLg6IQ.jpg


Does that make sense?

tim
 
Figuring out IR Control

I have a Vishay IR receiver TSOP4838 for infrared communications, and I want to use Apple remotes to control my preamp.

I set up a quick breadboard to test and figure out the codes:

IR Test copy.jpg

It was initially a bit confusing, because two different remotes gave different codes for the same functions. I eventually found that there were three bits from the long string of bits that were consistent and that correlated with the function being sent, so by masking out the rest, and switching in the code based just on those three bits, I was able to set up a simple command loop.

Here's the test code:

Code:
void IRCommand() 
{
  if (irrecv.decode(&results))  // if a command has been received
  {
    if (results.value == 4294967295)  // if it's a repeat command
      {
         lcd.setCursor(3, 3);
         lcd.print("Repeat...");
         irrecv.resume();   // go get another comand
         return;
      }
  // otherwise, this is the main part of the function
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Command received: ");
    lcd.setCursor(0, 1);
    lcd.print(results.value, HEX);    // print it out in hexadecimal
    lcd.setCursor(0, 2);
    lcd.print((results.value & 0x00007000) / 0x1000, HEX); // the bits we want
    lcd.print(" ");
    
    switch ((results.value & 0x00007000) / 0x1000)
    {                            // these work for the two Apple remotes I have...
      case 5:
         lcd.print("Volume Up");
         break;
      case 3:
         lcd.print("Volume Down");
         break;
      case 1:
         lcd.print("Mode -1");
         break;
      case 6:
         lcd.print("Mode +1");
         break;
      case 2:
         lcd.print("Mode Set");
         break;
      case 4:
         lcd.print("Standby");
         break;
      default:
         break;
    }
    
    irrecv.resume();  // go and listen for the next command
  }
}

First part gets the value, and checks to see if it's the code for "repeat what I said last". If so, we print it. This code is the same for a range of different remotes.

Then we print the value out in HEX so I can tell what's coming through.

Next I mask and divide, so the relevant three bits are the only bits left, and I run a switch based on the remaining bits. This will be replaced in the final code with actions to increase or decrease the volume, change a mode (tape monitor, DSP enable/disable; mute/unmute).
 
Thinking about the layout

There's quite a lot of stuff. Here's what I'm thinking of doing. I am looking at an enclosure that's 15" wide, and 11.5" deep - or perhaps 12" to make things a bit easier to fit in.

This doesn't allow me to fit in the mains transformer, but I quite like having that in a separate container and location. I think it makes sense to put the regulators in the main enclosure, because I can keep the analog an digital power supplies completely separated from the diode bridges onwards. That, I hope, will reduce the likelihood of digital noise reaching the analog circuitry.

The phono preamp will be my recently built Bugle2, which has its own power supply. I think that's justified given the sensitivity / high gain of the stage.

The DAC is an oDAC, which is powered from the USB that feeds its data.

The MiniDSP can be powered from USB, but I'll probably give it a 5V supply from the same source as the Arduino.

Here's a scale drawing, although I don't yet have the pot, and the regulators have not been built.

"mode" is the rotary encoder; "gain" is the motorized pot, and "balance" is an analog pot that I plan to set and forget. It will not have digital control.

Any thoughts about the layout? This is intended to keep low level signals short, and to separate analog from digital as much as possible.

Panels-02.png
 
Laying out the digital hardware

The microcontroller is an Arduino Leonardo (still waiting for it to arrive), and I just need a bit of glue to connect all the devices.

It seems as though the easy way to connect everything is to use a proto shield - I have one from SeeedStudio.
2013-09-02 14.18.59.jpg

Here's a rough indication of the layout, created with DIYLC.
Protoshield.png

The main purpose of the shield is to give each peripheral it's own plug and socket, using 0.1" Molex connectors. While I hate connecting wires to the little metal bits, I like the flexibility and tidiness. I bought a 10' roll of ribbon cable which will let me make a lifetime's worth of little peripheral connectors.

I still need to check the polarity of a few things - the good news is that if I accidentally reverse data connections, I can fix that in software (with a few exceptions like the serial port, and where I need analog read capability or interrupt capability.
 
Analog progress

I've populated the preamp board and built a power supply for it...

2013-09-08 12.33.18.jpg

The power supply is a simple linear regulated supply with a CRC smoother before the LM7815 / 7915. I'm using a toroidal transformer from Antek.

2013-09-08 12.32.20.jpg
It's really a bit ridiculous to be building all this digital stuff to go with a simple, clean preamp like this, but the nice thing about DIY is the ability to indulge oneself.

I'm still thinking about which output caps to use...not really sure how much of a difference it makes. I'm also still waiting for the op amps to arrive from Mouser.

I'm still waiting for the pot to arrive as well - four channel Alps. Until that comes I can't really test much.
 
Digital system progress

The wiring of the control board has begun - my first attempt at using a protoboard like this. There are multiple challenges - figuring out where to put stuff, lots of solder connections, checking and re-checking.

As power is coming through the Arduino, I am concerned that a wiring error could blow up the regulator or do various other kinds of overload damage, as the Arduino has very little built-in protection.

Here's the proto-board, top and bottom:
2013-09-08 12.33.47.jpg

2013-09-08 12.33.57.jpg

And here's what it looks like attached to the Arduino.

2013-09-08 21.02.13.jpg

I'm using 2.54mm connectors - the process of making them is pretty nasty:mad:. For the first few I used solder and pliers, but I found a crimping tool at Fry's for $18, and it seemed like a good investment. The connectors are still very small and easy to misalign, so I'm wasting a few. My hope is that using these connectors will make it easier to assemble, test and maintain, but they will only help if the connections are reliable, which seems not so easy to do.
 
Looks good Tim!
For communication, it is advised to send data packets, not only characters. Or a bunch of characters. So you define a comminucation protocol for yourself, something line that:
[Header] [Command] [Value] [Checksum][End]

If you are controlling the volume, you could send back the current position to your app.
If you make a simple application with buttons, you can set those buttons to send the commands to the preamp. And a feedback graphic or text to show the current valuest, let it be the volume or the input channel (if you have analog input too).

This is a long work you have started. But this will be very unique. Keep going.
 
Looks good Tim!
For communication, it is advised to send data packets, not only characters. Or a bunch of characters. So you define a comminucation protocol for yourself, something line that:
[Header] [Command] [Value] [Checksum][End]

Thanks for the encouragement. It does look like a long project. But I am hoping that if I test as I go, one function at a time, it should be possible to get it all working.

I agree - a protocol for the Bluetooth communication will be more reliable. I may start with the simplest thing I can get working, and then make it more robust - after all, that's just software. Right now I'm not sure of the best way to create the phone app - I don't want to have to learn Objective C, and I haven't found a suitable app framework I can use on IOS. I may be able to code it as a web app with a server on my desktop computer - not sure of the range of the Bluetooth connection.

I am also hoping to have bi-directional communication eventually. It's great to have immediate feedback on the screen.
 
I am just a noob as well in mobile programming, but as I heard QT is a nice framework, and using that You can make apps for different targets, like iOS and Android.

It's many years since I even thought about QT - I didn't realize it had come so far.

I'll look into the port to iOS - it looks as though it can be programmed in Javascript / ECMAscript as well, which might be a good way to build something simple.

Qt (framework) - Wikipedia, the free encyclopedia
 
Code: the main program - initialization

I thought I'd start to look at the code to drive the control system. This will be developed incrementally, and I'm trying to keep things structured so I can test each piece separately.

Like any Arduino sketch, there's a setup() function, and a loop() function.

I also have to set up some global variables. Because I'm controlling hardware, it's useful to keep track of the global state of various things, and let each function fiddle with state as needed. I know it's not the greatest programming practice - very little information hiding - but it's going to have to do for now.

Here's the initialization code that sets up the global variables:

Code:
#include "globals.h"
#include <LiquidCrystal.h>
#include <IRremote.h>
#include <Encoder.h>
#include <EEPROM.h>

// Global Variables

boolean Standby = false;               // 0 means all is off and quiet; 1 is operating
LiquidCrystal Lcd(LCD_PORTS);          // LCD in 4-pin mode
IRrecv Irrecv(IR_PORT);                // Single pin to read IR data
decode_results Results;                // Output from IR receiver
Encoder Enc(ENC_1, ENC_2);             // Create an encoder object
long EncPos = 2;                       // Accumulator for encoder state change recognition, init to DSP
int Volume;                            // Value read from potentiometer via VOL_SENSE
byte Input = 1;                        // Default to CD input for first boot-up
byte CommandMode = 1;                  // IR and Rotary Controller (and maybe Bluetooth) "focus"
boolean CommandActive = false;         // true if we're processing a command
unsigned long CommandActiveTime;       // Time of start of new command active state
boolean Monitor = false;               // Default to source, not monitor
boolean Dsp = true;                    // DSP is active by default
boolean Mute = true;                   // MUTE is active at startup
byte RelayState = 0b10000000;          // Initialize to default state of the 8 relays
static char * Relays[] = {
  "Phono   ", "CD      ", "DAC     ", "Tuner   ", "Tape    ", "Monitor ", "DSP     ", "MUTE ALL"};
int source = 0;

First I load the different library header files, then I create a set of variables as defined above.

The system has a standby mode, during which the display sleeps, and the system is muted. The microprocessor will still be running.

The display, IR receiver, and encoder are all using libraries, so I just have to initialize them using the definitions in my global constants list that I showed in post 4 above.

The system has two operating modes: command mode and listen mode - in command mode there's something going on - usually the rotary encoder has been moved. Command mode ends with a timeout or when the button is pressed to implement a command like changing the input or muting / unmuting.

RelayState is used to feed the shift register with values for the 8 relays. The Relays variable describes the functions of the 8 relays, and the potential modes / operating conditions for the preamp.

As a naming convention, the global variables begin with an uppercase letter. Constants are all caps, and local variables and functions begin with lower case.

I'll cover the setup() and loop() functions in a separate post.
 
Code: setup()

The setup function runs once at the beginning of system operation. It sets up all the initial conditions so we have a clean start.

Code:
void setup() 
{
  // Initialize relay ports and set all to their default states (using initialized value of RelayState)
  updateShiftRegister(RelayState);                  // Initialize all relays to default state
  // Mute is 1 (enabled); all others are zero

  // Initialize display ports and print splash screen
  displaySplash(1000);                              // Show splash screen for 1 second

  // Initialize rotary encoder ports
  encoderInit();

  // Initialize IR port
    IRInit();

   // Initialize bluetooth
   
   Serial.begin(9600);     // Bluetooth dongle attached to the serial port

  // Initialize volume control input and motor controls

  pinMode(VOL_SENSE, INPUT);
  Volume = volumeGet();

  // Recall source, DSP settings and set signal path
  if (EEPROM.read(ACTIVE_INPUT == 255)) 
  {                                  // First time: set default values into EEPROM
    EEPROM.write(ACTIVE_INPUT, Input);
    EEPROM.write(ACTIVE_DSP, Dsp);
    EEPROM.write(ACTIVE_MONITOR, Monitor);
  }
  // Now get values (either as saved or new values just written)
  Input   = EEPROM.read(ACTIVE_INPUT);
  Dsp     = EEPROM.read(ACTIVE_DSP);
  Monitor = EEPROM.read(ACTIVE_MONITOR);

  inputSet(Input);                  // set the selected input to be active (connected)
  DSPSet(Dsp);                       // set the DSP on or off
  monitorSet(Monitor);               // set the tape monitor on or off

  // Unmute the output
  muteSet(Mute = true);           // set the mute variable and update the register

  // Display source, volume, DSP flag
  Lcd.clear();
  updateDisplay();
}
The basic elements are obvious from the comments. We set up the shift register to initialize all the relays. We don't want any sources turned on or any sound to be produced while we're going through the setup.

We put up a splash screen and waste some time so I can have the satisfaction of seeing a custom message at startup, then we initialize the rotary encoder, IR and bluetooth functions.

Volume is a bit different - we read the position of the volume control - we want the displayed volume to reflect the actual volume.

The EEPROM stores state information between runs, so nothing important is lost at power down (so long as you remember to go to standby).

Reading the EEPROM is free, but the hardware is designed for a limited number of write cycles, so we don't initialize it except for the very first time we run. The EEPROM is storing which input is active, the state of the tape monitor function, and whether or not the MiniDSP is in-circuit.

After we've read all that, we set the relays appropriately, unmute, clear and reset the display. I am writing separate functions for different elements of the system. I'll describe them later.
 
Code: loop()

The loop code is very simple, and I'm not sure how well it's going to work.

The idea is that we just sequentially check each source of commands, and if there is one, we process it. This could possibly break if someone interleaves inputs from different sources - for example moving the rotary encoder and then sending a signal from the remote or bluetooth.

I think I can make it work if each function leaves the system in a clean state that the next function can work from. We'll see.

Here's the loop code:

Code:
void loop() 
{
  // Operating assumption is that everything is in a valid state.
  // The job of the loop is to recognize commands that change system state,
  // and to implement the change. 
  // Each input source tests for an input, interprets it, calls the
  // appropriate implementation function, and updates the display.
  // Each subroutine blocks until it's finished processing a command, if there's one to handle.

  if (Standby)
    Standby = !encoderSwitch();            // check to see if the encoder switch has been pressed
  else
  {  // Process commands from each source in turn

    encoderCommand();                      // Used for source selection, and monitor / DSP / Mute / Standby.

    volumeUpdate();                        // Check for manual alteration of the volume knob.
    // We store the volume and display it for the user.
    IRCommand();                           // Listen for a command: volume, source, other controls.
    bluetoothCommand();                    // Bluetooth interface: same commands as IR.
  }
}

First we check for standby mode. Only pushing the button on the rotary encoder will pop us out of standby. If we're not in standby, we just call a function for each command source: rotary encoder, volume control, infrared, and bluetooth.

Each of these functions is going to do one atomic thing, and put everything back into a clean state ready for what happens next.
 
Status
This old topic is closed. If you want to reopen this topic, contact a moderator using the "Report Post" button.