All of the buildings, all of those cars
were once just a dream
in somebody's head
Mercy Street - Peter Gabriel


10 minutes read

Pic 000


I’m the lucky owner of a Devialet Expert Pro 250 (which started as the D Premier many moons ago) driving a pair of Martin Logan CLX Art. (Yes, it’s as good as the many raving reviews say. If you have to chance to buy one, don’t hesitate).

But what has this to do with this blog post? Well, one of the nice things of the Devialet is its remote control and more specifically, the way they implemented the volume control. The huge knob allows you to change the volume in a very natural and satisfying way. Turn up the volume in the truest sense of the word. And this was something I really missed for my Sony TA-F501ES which are powering my small speakers in the man cave. Especially the way Sony implemented the volume up/down is extremely annoying. When you press the up/down buttons, the volume changes in steps of 0.5dB, which are very small steps. However, if you press the up/down buttons more than 3 times per second (which happens a lot because those 0.5dB steps are so small) then the volume suddenly changes in steps of 6 dB, which is really too much. Before you know it you have changed the volume by 12 or even 18 dB because you’re frantically pushing up/down.

The Sony TA-F501ES class D amplifier.
The remote control for the above amplifier.

So, the idea started to ripen to build a dedicated volume control for this amplifier and if possible avoid those 6 dB jumps in volume. For this I decided to start with an Arduino Uno and see how far I got with that platform. As it turned out, it was possible to build a functional remote with a very low power consumption where batteries should last years.

Prototype on a breadboard on the left. Finished remote on the right.

Remote control codes

First task was to find out what the codes were for volume Up/Down for this particular amplifier. And there I hit the first problem. It must be that this wasn’t a very popular amplifier since none of the databases had an entry for its remote control (an RM-A AUU012).

Luckily, it’s not hard to find out which codes an IR remote is sending with the excellent IRremote library by Ken Shirriff and Rafi Khan.

After a few minutes I already found the needed 12 bit codes:

  • Up: 0x486
  • Down: 0xC86

This is going to be a minimal remote control. No other buttons to e.g. change the input are present. It’s build for one thing and it should do it well.


Obviously, I can’t take an of the shelve Arduino Uno since this is intended to be powered by batteries. All Arduino boards contain at least a voltage regulator and several other chips that take up too much power. So I took the Arduino schematics and removed everything that was not absolutely needed and I came to the following design:

Schematics for a minimal Arduino Uno.

I did add some space to add pin headers for the I/O I don’t use, a reset switch and place for resistors to the Tx/Rx pins that so that the PCB can be reused for other projects as well (helps recycling those PCB’s you get too much from your favourite PCB manufacturer).

With a little bit of extra effort, it’s easy to dimension the PCB in such a way it will fit nicely in its future enclosure. There’s a small cut away as well for the sending IR LED and the rotary encoder is dead centre of course.

A 3D render of the PCB board.


Not being able to mill big pieces of aluminium I decided to build the body of the remote control from wood. Since I still have quite some 18 mm thick rubberwood leftovers when I build my bookshelves I decided to give that a try.

The body

Since the Devialet remote control is about as perfect as you can get, I took over its outer dimensions which is 120 by 120 mm. I used OpenSCAD to draw a template that then can be used by EstlCAM to generate the gcode for the CNC mill:

openSCAD in action rendering a 2D model of the body.
Bottom view of the milled body using rubber wood.
Bottom view of the milled body and the control knob using rubber wood.

Playing a bit with the tolerances made it so that the PCB was such a tight fit in the body that no screws or glue was necessary to keep the PCB in place.

The PCB fits very nicely inside the body.
Top view showing the rotary encoder nicely centred and the control knob next to it.

The control knob

The knob has a diameter of 61 mm. But here I’m running into a practical problem. The shaft of the rotary encoder is D shaped instead of a perfect circle. That’s impossible to mill with a 1/8" mill. And also here I wanted a tight fit so that I would not need any screws or glue to keep the knob securely attached to the shaft of the rotary encoder.

I solved this by using my CNC mill to mill a 61 mm cylinder and pocket a 20 mm cylinder 16 mm deep along its centre axis. The 20 mm pocket is then filled with a 3D printed cylinder (16 mm high) with a D shaped hole along the centre axis.

For the templates, I used openSCAD again:

Template for the rubberwood control knob.
3D model for the volume knob centre piece.

Top view rubberwood volume control knob.
Bottom view rubberwood volume control knob (showing the pocket to be filled with the 3D centre piece).

3D printed centre piece.
Trying it out on the shaft of the rotary encounter.
Using some convincing arguments to let things fit...
Knob and centre piece firmly in place.

Looks good :-)


The source code for this (platformIO) project can be found on BitBucket (see links at the end of this post).

The project uses 3 libraries:

The IRremote library is used to send out the up/down control codes via the IR LED connected to pin 3 (default pin). I found that the Sony receiver is not happy when you only send the IR code once (nothing happens in that case) and prefers to have a quick burst of codes send rapidly after each other with a very short pause in between. That’s why you see in the code that I send the up/down codes 5 times with a short 15 millisecond pause in between.

For the rotary encoder I used the Encoder library. The rotary encoder is connected to pin 5 and 6. I also connected the push switch of the rotary encoder to pin 4. This allowed me to use the volume control in 2 modes. In the default (start up) mode, the remote control will send out a maximum of 3 up/down bursts per second. This prevents having those huge, sudden 6 dB changes of volume when you turn the knob too fast. If you push the knob, this safety measure is disabled and up/down codes will be send continuously. Push the knob once again, and you return back to the safe mode.

Since the remote control runs from batteries, it’s of course important to get the lowest possible power consumption. For this I used the Low Power library and was able to obtain excellent results.

Without taking any power saving measures, the ATmega328 consumes around 16mA (at 5V). Experimenting with just putting the ATmega328 in idle:

static void lowPowerSleep(period_t period)
    LowPower.idle(period, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_ON,
                  SPI_OFF, USART0_OFF, TWI_OFF);
} /* end lowPowerSleep */

helped only a little bit and power consumption remained way too high and batteries would be depleted in a matter of days. Not good.

So time to take more drastic measures and really power down the ATmega328. But in that case, the ATmega328 needs an external interrupt to wake up again and I did not foresee that in my PCB design. Luckily it was as simple as adding a small wire from pin 2 (which I used for the wake up interrupt pin) to one of the pins the rotary encoder was connected to (pins 5 and 6). When you turn the rotary encoder, pin 5 and 6 will go LOW and that can be used to wakeup the ATmega328 as well. So I added a small wire from pin 5 to pin 2 as you can see here:

Connecting pin 2 with pin 5 to wake up the ATmega328.

I added this bit of code which gets called after 2 seconds of inactivity:

#define wakeUpPin                  2    // Use pin 2 as wake up pin

static void powerDown()
    attachInterrupt(0, wakeUp, LOW); // Allow wakeUp pin to trigger interrupt on low.

    #ifdef myDEBUG

    LowPower.powerDown(SLEEP_FOREVER, ADC_ON, BOD_ON);

} /* end powerDown */

which just attaches pin 2 to an interrupt and then powers down. So what’s the result? Well not bad at all. The quiescent current dropped to 137 μA.

A lot better. Only 137 μA quiescent current instead of 16 mA.

But I knew the ATmega328 could do a lot better than that. Luckily, there is an easy fix. I accidentally left the ADC and Brown Out Detector ON during power down.

#define wakeUpPin                  2    // Use pin 2 as wake up pin

static void powerDown()
    attachInterrupt(0, wakeUp, LOW); // Allow wakeUp pin to trigger interrupt on low.

    #ifdef myDEBUG

    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);

} /* end powerDown */

Modifying and uploading the new code dropped the quiescent current to just 0.11 μA:

Victory. Only 0.11 μA quiescent current.

With such a low quiescent current, batteries should last years.

Finishing up

From the ATmega328 data sheet we know that its operating voltage can range from as low as 1.8 V to 5.5 V. So the remote controller should be able to operate with just 2 AAA batteries (the PCB design I made has room for 3 AAA’s). Trying it out and indeed it did.

Since the body of the volume control is quite slim (only 18 mm in height) the battery holder would stick out if I mounted it on the PCB itself. So I cut away a part of the PCB big enough to let a double AA battery fit through:

Cutting away part of the PCB to make room for a AAA battery holder.
Glueing the battery compartment to the body.

To protect the rubberwood, give a satin silky smooth feeling and let the wood grain stand out a bit more, I applied a bit of oil to the rubberwood:

After applying some oil and sanding.

And that’s it. The remote control now sits proudly on my desk, in arms reach, and changing the volume experience has become a lot more pleasant. It’s not perfect because of the annoying volume jumps Sony decided to use but a lot better nevertheless.

As usual, there are still improvements that could be made. E.g. the remote control could benefit from being a bit heavier and maybe I should consider of trying to make an enclosure from concrete like I did for my rain barrel level meter.

Using IR dictates that remote control and amplifier need to see each other. I could consider using e.g. a Zigbee transceiver (which the Devialet is actually using) to work around that. But in that case, I would need to design the receiving end for the amplifier as well. And that becomes suddenly a much bigger project.

KiCAD project including openSCAD design files

source code (platformIO project)

Recent posts

See more