I'm doing an Arduino based project using the PWM hooked up to a 0.5W 8Ω speaker in order to make various sounds, I want to implement a digital volume control and I thought about using another PWM output and an Opto-Isolator....would this be possible?

And....if it is possible - could anyone recommend a suitable candidate?

Cheers Chiphackers - Let me know if this is too vague :)

Update - I've got the speaker connected directly to the PWM output without amplification - I'm using a couple of tactile switches to turn the volume up and down

Here's the circuit (In Theory) - alt text

Here's some code too, I've got the PWM set to high speed and I'm using it to ramp the volume of the tones up and down -

// Configure PWM on pins 3 and 11 to run at maximum speed, rather than the default //

pinMode(3,OUTPUT); // speaker on pin 3

cli(); // disable interrupts while registers are configured

bitSet(TCCR2A, WGM20);
bitSet(TCCR2A, WGM21); // set Timer2 to fast PWM mode (doubles PWM frequency)
bitSet(TCCR2B, CS20);
bitClear(TCCR2B, CS21);
bitClear(TCCR2B, CS22);

sei(); // enable interrupts now that registers have been set

////////// Function for playing tones -

void playTone(long tone, int duration) 
prevMicros = micros();

  while (micros() - prevMicros < duration) 
    analogWrite(3, VOLUME);
    analogWrite(3, 0);
  • \$\begingroup\$ I was thinking about putting the sound output from the Arduino's PWM pin through the Opto-Isolator and out to the speaker, then using another PWM pin to vary the brightness of the Opto-Isolator's emitter - Should I just draw a circuit? (a theoretical one) \$\endgroup\$ – Jim Jul 19 '10 at 15:20
  • 1
    \$\begingroup\$ Gah! Not enough rep to edit. Could you kindly use the 'code' button (square '0101'), or insert 4 spaces before every line in your code? (Does the same thing.) That way, we'll get pretty colors, indenting, and correct line breaks. \$\endgroup\$ – Kevin Vermeer Jul 20 '10 at 12:59
  • \$\begingroup\$ Got it! couldn't work out the code thing - got confused when i couldn't see any tags, doh! \$\endgroup\$ – Jim Jul 20 '10 at 13:57

Disclaimer: This is in response to the posted circuit, not an answer to the question of control with a PWM. I just didn't have room in the comment.

Any special reason you need the optoisolator? As your circuit currently appears, I don't think that your D5 connection to pin 5 of your optoisolator phototransistor will supply the kind of current you want. In addition, synchronization between the two PWMs will likely cause you pain (What if the D5 PWM is always off when the D3 PWM is on and vice versa? No sound! You need to average your signal, and feed it into a buffer. You'll also likely want some hefty capacitance on your V+ line to the micro and buffer so that your supply voltage stays smooth.. However, the connection to ground and the connection to D5 totally eliminate the purpose of the optoisolator, which is to allow the speaker circuit to be at a different voltage than the input. This might be useful if you had the speaker at a remote location from the Arduino, and want to run a low-current differential signal on a twisted pair out to the speaker, which would be powered from a different power supply. As is, you might as well just connect pin 6 to D3, and just use the transistor, completely ignoring the "Opto" part of the circuit.

See the output circuit for the AVR335 appnote linked in my other answer for a circuit which effectively drives a speaker with a PWM. The filters smooth the output signal to something better approximating the input (With a rolling average), so that you get a smooth wave rather than a rough digital square wave. You can remove the unary gain amplifier, that's just to remove any feedback from the microphone (Which you don't have).

You really want some filtering and amplification on the output -It'll sound absolutely terrible if you don't. You know how those talking greeting cards sound? They have filtered outputs. Your speaker will sound worse than that if you just connect it to the PWM. I was previously assuming that you were using the phototransistor to isolate your output and transfer it to an amplifier/filter circuit running on a different power supply, but a straight connection is going to sound really bad.

  • \$\begingroup\$ Wow cheers for all the comments and help, I think I get what you mean now, I've probably over thought it! I'm only interested in basic synthesis & tones so I hooked it up without filtering and it sounds fine, amplitude control with the analogWrite() sounds nice and smooth too (I've increased the hardware timer to make it faster and smoother) \$\endgroup\$ – Jim Jul 20 '10 at 9:24
  • \$\begingroup\$ The only reason I'm trying to use an Opto-Isolator is because i can buy them from the local electronics shop - whereas they don't have any digital potentiometer ICs.....you've probably got a much simpler suggestion tho (I'm just not sure what to use) \$\endgroup\$ – Jim Jul 20 '10 at 9:53
  • \$\begingroup\$ Just thought - is this something i could be doing with a transistor? If I put the sound output through the transistor to the speaker, then just use PWM to stimulate the base? (have I been over thinking it?) \$\endgroup\$ – Jim Jul 20 '10 at 9:57
  • \$\begingroup\$ got some hi freq transistors, gunna try them instead of using an opto-isolator...makes more sense, cheers 4 the help \$\endgroup\$ – Jim Jul 20 '10 at 14:50
  • 1
    \$\begingroup\$ @Jim : You can continue to use your optoisolator - If it's like the one in your schematic, you should be able to just connect the PWM output to Pin 6 instead of Pin 1 on the 4N35 for a quick test. Also, your PWM isn't really high frequency. It's pretty quick as far as outputs from an Arduino go, but not even close in the realm of analog circuits: save your money on this one. Just make sure you're not using a MOSFET with a high gate capacitance, and you'll be fine. \$\endgroup\$ – Kevin Vermeer Jul 20 '10 at 16:40

I haven't done a whole lot with audio, but wouldn't a simpler way of reducing the volume be an digitally-controlled potentiometer in-series with the the PWM signal driving the speaker(i.e. you just want to reduce the signal voltage to reduce the volume, right?)

  • \$\begingroup\$ Yeah I think so - I was just seeing if i could do it with an Opto-isolator instead - cuz it's available locally, I'd have to get the digital potentiometer delivered - may end up ordering one anyway \$\endgroup\$ – Jim Jul 19 '10 at 17:37
  • \$\begingroup\$ The simplest way of reducing the volume is to reduce the amplitude of the PWM, and not add any components. \$\endgroup\$ – Kevin Vermeer Jul 20 '10 at 0:49
  • \$\begingroup\$ I know - I'm just already using that function for making the tones fade in and out \$\endgroup\$ – Jim Jul 20 '10 at 9:35

Wait - You want a digital volume control that is controlled by a PWM output? As in, you send the Arduino a command over the UART, or flip some switches to a binary value, and tell it to set the volume to 11? Then, your optoisolator applies a voltage/current somewhere in your output circuit? We'd need a lot more info on what kind of output amplifier you're using. It would probably be much easier to simply modify your PWM. If you increase the maximum value, or decrease the input value, you can turn the volume down entirely in software.

If you must, you can still use the analogWrite() function to control your PWM. Try something like(pseudocode):

  //File scope variable to set the volume.  
  static uint8_t volume = 0;   

 void set_volume(uint8_t new_volume) {  
    if (new_volume < 8)   // Would result in a volume of 0 
       volume = new_volume;  

 /** Divide by 2^volume:   
  *  0 = loudest,  
  *  1 = half as loud,    
  *  2 = 1/4 as loud,  
  *  3 = 1/8 as loud, etc.   
 inline uint8_t adjust_volume(uint8_t signal) {  
    return (uint8_t) signal >> volume;  

 //Identical ISRs for each of the tactile switches  
 ISR(PCINT0_vect) {      
   setVolume(read(PORTC))  //Or however you get your digital volume signal  

void main(void)  {  
   char *sound_buffer;  


If you want to do a volume control with an optoisolated input, then yes, that's quite possible. Assuming your input device is a pot, your options are a logical, frequency (rather than pulse width) modulated signal, which you can get from a voltage to frequency converter like the LM231 (there are many other options), or from an op-amp circuit with an RC oscillator. Feed the output into any digital optoisolator, and you should be able to read the frequency on a digital input pin, and modify your speaker output PWM maximum value or modulate your input values accordingly.

Alternatively, you can use a photovoltaic output optoisolator, which outputs an analog voltage (or current) based on the input current. Be careful of linearity issues when working with these circuits. You don't really want a linear volume control (You want something closer to logarithmic, or you can go all-out and implement an equal loudness contour,) however, you don't want a digital switch. The Vishay IL300 is an example of a linear phototransistor.

EDIT: I mentioned the AVR335 app note in a recent question about programmable greeting cards with audio. That would be a good resource for your question as well.

  • \$\begingroup\$ I'm already using the PWM to control the volume of the sounds - to create envelopes. If I control the overall volume with software I'll loose the envelope resolution, so I'm trying to have a separate way to control the volume \$\endgroup\$ – Jim Jul 19 '10 at 15:10
  • \$\begingroup\$ I can cope with linear, I'll just add a fixed value parallel resistor to curve it - shouldn't be a problem \$\endgroup\$ – Jim Jul 19 '10 at 15:13
  • \$\begingroup\$ You'll "lose the envelope resolution"? What does that mean? \$\endgroup\$ – endolith Jul 19 '10 at 15:38
  • \$\begingroup\$ I'm using analogWrite() to set the volume of tones/oscillations and there's 255 steps which i'm ramping up and down like an amplitude envelope - so I'm concerned about keeping the envelopes smooth \$\endgroup\$ – Jim Jul 19 '10 at 15:49
  • \$\begingroup\$ :( sorry I'm not that good at explaining things with words - normally have to show, point and draw a load of Pictonary like diagrams \$\endgroup\$ – Jim Jul 19 '10 at 15:57

Why don't you just drive the speaker with a normal transistor and alter the PWM to control the volume? You have control over the waveform that the transistor (and thus speaker) gets... no need for additional components to control it.

Also, If you're hell-bent on using the opto, drive the LED with the PWM outputs. Connect the Emitter of the transistor to ground, the collector to the speaker, and the other side of the speaker to your V+ source (more V+ = louder sound).

  • \$\begingroup\$ I'm not hell bent on using an Opto Isolator, more like looking for an alternative to a digital potentiometer....The transistor is the best option for sure - thanks to everyone for the help \$\endgroup\$ – Jim Jul 20 '10 at 22:22

Ok.....so I've realized that an opto-isolator is not the best approach for this particular application!

And.....as Andrew Kohlsmith and reemrevnivek pointed out - I could pulse width modulate the base of a transistor instead....

So I got some silicon npn epitaxial transistors (BD139) from the electronics store and gave them a go. It actually worked better than i thought - the volume control is nice and fine :)

Here's the circuit I used in the end -

Da Circuit

Thanks for everyones help and suggestions!

  • 1
    \$\begingroup\$ Very nice! Just one suggestion: You'll get a lot more volume if you put D11 into a transistor as well. Right now, you're limited to the current capability of the PWM pin, about 20mA. You also risk damaging the Arduino. 8ohms is almost a short circuit; is your micro getting hot? This transistor must be a PNP, because the speaker and Q1 will cause the voltage at the positive terminal of the speaker to be V+ if your transistor turns on. Note, though, that this means that your on/off pulses will be inverted (The PNP is off when you apply V+, and on when you pull it to ground.) \$\endgroup\$ – Kevin Vermeer Jul 20 '10 at 22:49
  • \$\begingroup\$ No it's not hot (thankfully!) If I've got enough volume as it is - do you think I could put a resistor between D11 and the speaker to protect the Arduino? \$\endgroup\$ – Jim Jul 20 '10 at 23:06
  • 1
    \$\begingroup\$ This is close to what I suggested... Instead of the speaker going to the PWM, the speaker should go to +V. then use the PWM that was going to the speaker to the resistor instead. Only one PWM needed, and that is the PWM that you control both the waveform (type of noise) and amplitude of the waveform (volume). What you have done is probably simpler for your software, but if you're interested in "upping your game" there's the method. \$\endgroup\$ – akohlsmith Jul 21 '10 at 0:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.