LDR as digital control for analog effects

Status
Not open for further replies.
I recently got myself some NSL-32s ( they are light sensitive resistors with an LED in the same packet ).

The idea is that you can program a preset brightness for the LED resulting in a certain resistance or control the brightness via an encoder. I can use this in a ton of different ways to improve the functionality of my effects.

The NSL-32 has a huge range of resistance going into the M-ohms so I can dial in virtually any resistance I need. But the problem is because the range is so huge it is difficult for me to be precise.

I need a way to calibrate the NSL-32 and lock in the brightness of my LED to a specific range, say 1K when my encoder is fully closed and 500K when fully open.

I know I can limit the brightness of my LED using the resistor from the negative leg to ground and that will certainly help lock down one half of my problem.

At the moment I am using an Arduino to adjust the brightness using pwm. with a 220-ohm resistor on my LED I get about 45-ohm on the NSL-32 encoder fully open and when the encoder is fully closed it jumps around from 1M-17M-overload and stays at OL when I pull it back a bit I can get the meter to hover at .470K (470-ohms) but even then it jumps around a little bit.

Does anyone have any experience with these LDRs? I would love to stability use them up to 1M.

Here is the code I am using it's really straightforward.

/*
Analog input, analog output, serial output

Reads an analog input pin, maps the result to a range from 0 to 255 and uses
the result to set the pulse width modulation (PWM) of an output pin.
Also prints the results to the Serial Monitor.

The circuit:
- potentiometer connected to analog pin 0.
Center pin of the potentiometer goes to the analog pin.
side pins of the potentiometer go to +5V and ground
- LED connected from digital pin 9 to ground

created 29 Dec. 2008
modified 9 Apr 2012
by Tom Igoe

This example code is in the public domain.

Arduino - AnalogInOutSerial
*/

// These constants won't change. They're used to give names to the pins used:
const int analogInPin = A0; // Analog input pin that the potentiometer is attached to
const int analogOutPin = 13; // Analog output pin that the LED is attached to

int sensorValue = 0; // value read from the pot
int outputValue = 0; // value output to the PWM (analog out)

void setup() {
// initialize serial communications at 9600 bps:
Serial.begin(9600);
}

void loop() {
// read the analog in value:
sensorValue = analogRead(analogInPin);
// map it to the range of the analog out:
outputValue = map(sensorValue, 0, 1023, 0, 255);
// change the analog out value:
analogWrite(analogOutPin, outputValue);

// print the results to the Serial Monitor:
Serial.print("sensor = ");
Serial.print(sensorValue);
Serial.print("\t output = ");
Serial.println(outputValue);

// wait 2 milliseconds before the next loop for the analog-to-digital
// converter to settle after the last reading:
delay(2);
}
 
I have done some experimenting with LDR's for controlling elements in vacuum tube amplifiers. There are two main issues.

Are you integrating the PWM you are using to drive the LED?

That's the easy one. The "R" in an LDR is usually a piece of Cadmium Sulphide or Cadmium Selenide. It is slow to react to a change in light and asymmetrical in its time response. It can take a few milliseconds for an LDR to react to an abrupt increase in light, but hundreds of milliseconds to recover when the light is removed. It would seem that a directly modulated PWM light source running at a rate above the audio range would be suitable, and it is under some circumstances, like the plate load in s tube based guitar amp. It however does strange things when stuck in a feedback loop of an opamp that feeds an A/D converter. I'm guessing that some of that digital noise wound up in the audio which caused aliased beat notes in the down stream converter. Experimentation on a case by case basis is needed, but a simple RC should be OK.

The second problem isn't so simple, but still might not be an issue, depending on the application. No two LDR's are the same......not even close. Take two out of the same bag, feed each LED the same current, then measure the resistance of the sensor, an order of magnitude difference between the two is common. The resistance will vary with the temperature and the applied voltage.

You can characterize each LDR for it's current VS resistance curves and this may be good enough. I have tried using two LDR's with their LED's in series. One photocell went to the circuit being controlled, the other was measured by the Arduino in a feedback loop. This seems to work too, but there are still some gremlins that I don't fully understand yet......

I am using cheap surplus LDR's at vacuum tube voltages.....that alone could be the source of the gremlins.

I use these, and they have been seen on sale for as little as $.30 each.

Electronic Goldmine - Audio Opto Coupler
 
A while ago I considered both LDR optocouplers and digipots for a project I was pondering. Along the way, I realized there was a third option, namely, using PWM along with an old-fashioned analogue CMOS switch (such as the 4066) to switch a resistor in and out of circuit.

The idea was that if you switch, say, a 1k resistor in between points A & B with a 50% duty cycle, the average current is half what you'd get if the resistor was permanently connected; in other words, the average current is the same as you'd get if you had a 2k resistor instead of a 1k resistor...

Every approach I looked at had serious limitations, though. The LDR is slow and nonlinear, but least likely to fry if used with valves and their large signal voltages. Digipots are usually limited to very small signal voltages, and many are only available in SMD packaging, which is a non-starter for me. CMOS analog switches are old and obsolete and may not be easy to find, and they too are restricted to relatively low signal voltages.

In the end I wasn't happy with any of the solutions I could find, so I realized the real solution was to re-think my application so it did not need a computer-adjustable variable resistance! In my case, I wanted to control the frequency of an external sinewave audio oscillator; once I realized it wasn't easy to get a computer to control resistance, I found an alternative solution, which was to use a chip like this one: http://www.analog.com/media/en/technical-documentation/data-sheets/AD9833.pdf

Depending on what you plan to use your variable resistor for (is it to control gain?), you might find this interesting: https://www.maximintegrated.com/en/app-notes/index.mvp/id/429

If you really must have a variable resistor, and speed isn't an issue, there is always the brutally clunky approach that was once used in remote-controllable Hi-Fi audio gear: a potentiometer physically rotated by an RC servo!

-Gnobuddy
 
Peavey sells an amp simulator called Revalver. The older version that I have has a mode where you can open up an amp schematic and start changing parts. You can do things like change the plate load resistor or coupling cap values and hear the results. You can even swap different tube types........so the BDBO (Big Dumb Blonde One) got this brilliant idea.......a simulator is cool, but why not a real hardware version?

So, for now a bunch of small relays bridge resistors in parallel for coarse tuning with an LDR for fine tuning. Cap values are also relay paralleled. Tube swapping is still handled with a box of tubes and an oven mitt. I haven't worked on, or even played this thing in a few years though......someday

Digipots are usually limited to very small signal voltages

There is (or was) a digipot or a CMOS switch that worked up to 30 volts, maybe more, I don't remember. It created some really rude distortion when you pushed it a bit beyond it's limitations and the internal protection diodes turned on though. I had created a tone stack in a tube amp using these, with a suitable attenuator in front of it, it worked OK, you just couldn't try to drive the grids out of the first 12AX7 with a killer pedal board.

One of the high gain Soldano amps uses LDR's as high voltage switches. The LED's are either dark, or full brightness. This setup is used to swap in one or more extra gain stages, then things like 1 meg grid stoppers kill off all that gain...........
 
Last edited:
Thanks for everyone's input!

Intersting. I'm after something similar for an analog application. Did you get them in .au?

Yes I got them from element14

http://au.element14.com/search?st=nsl 32

Are you integrating the PWM you are using to drive the LED?

Yes, The PWM gives me a voltage of 0-5V

A while ago I considered both LDR optocouplers and digipots for a project I was pondering. Along the way, I realized there was a third option, namely, using PWM along with an old-fashioned analogue CMOS switch (such as the 4066) to switch a resistor in and out of circuit.

The idea was that if you switch, say, a 1k resistor in between points A & B with a 50% duty cycle, the average current is half what you'd get if the resistor was permanently connected; in other words, the average current is the same as you'd get if you had a 2k resistor instead of a 1k resistor...

Every approach I looked at had serious limitations, though. The LDR is slow and nonlinear, but least likely to fry if used with valves and their large signal voltages. Digipots are usually limited to very small signal voltages, and many are only available in SMD packaging, which is a non-starter for me. CMOS analog switches are old and obsolete and may not be easy to find, and they too are restricted to relatively low signal voltages.

In the end I wasn't happy with any of the solutions I could find, so I realized the real solution was to re-think my application so it did not need a computer-adjustable variable resistance! In my case, I wanted to control the frequency of an external sinewave audio oscillator; once I realized it wasn't easy to get a computer to control resistance, I found an alternative solution, which was to use a chip like this one: http://www.analog.com/media/en/technical-documentation/data-sheets/AD9833.pdf

Depending on what you plan to use your variable resistor for (is it to control gain?), you might find this interesting: https://www.maximintegrated.com/en/app-notes/index.mvp/id/429

If you really must have a variable resistor, and speed isn't an issue, there is always the brutally clunky approach that was once used in remote-controllable Hi-Fi audio gear: a potentiometer physically rotated by an RC servo!

-Gnobuddy

Before I got the LDRs I looked at a bunch of different options DIGI-Pots has a limit to the amount of voltage you can push through them --usually quite small- whereas these LDRs can take up to 60V.

I solved my problem!

first of all, I was using a 220-ohm resistor which gives me 22mA of current at 5V and 4mA at 1V resulting in a range of about 40-200 Ohms ( not impressive )

I swapped out the resistor on the LED to a 100K giving me 0.01mA at 1V and 0.05mA at 5V taking my range up into the K-ohms ( I can't remember the exact values but the datasheet has a graph and I remember it being pretty spot on)

Also, my code ranges from 0-255. 0 is a problem because that turns my LED off that's why my resistance read as an overload. So I changed it to range from 4-255 and things were way more stable. By stable I mean when applying a voltage the resistance drifts within 1ohm ( not bad at all ).

I was playing around with a gain circuit a couple of days ago and I pulled out the resistor in the feedback loop and replaced it with the LDR. Using the code above the LDR reacted quickly as I changed the value of the POT and it sounded pretty stable. I changed the code to an LFO I found online ( I'll post that below ). --BTW, If anyone can point me to a lesson that can show me how to create waveforms using PWM and change the Freq. That would be appreciated.-- All in all, it was a pretty good tremolo and a solid proof of concept.


Code:
// 3 phase PWM sine
// (c) 2016 C. Masenas
// Modified from original DDS from: 
// KHM 2009 /  Martin Nawrath

// table of 256 sine values / one sine period / stored in flash memory
PROGMEM const unsigned char sine256[]  = {
  127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,239,240,
  242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223,
  221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81,78,
  76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31,
  33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124

};
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
         
int testPin = 7;
int enablePin = 6 ;

volatile  float freq=1;
const float refclk=122.549  ;     //  16 MHz/510/256

// variables used inside interrupt service declared as voilatile
volatile unsigned long sigma;   // phase accumulator
volatile unsigned long delta;  // phase increment
byte phase0, phase1, phase2 ;

void setup()
{
  Serial.begin(9600);        // connect to the serial port
  Serial.println("DDS Test");

  pinMode(enablePin, OUTPUT);      // sets the digital pin as output
  pinMode(testPin, OUTPUT);      // sets the digital pin as output
  pinMode(12, OUTPUT);     // pin9= PWM  output / frequency output
  pinMode(10, OUTPUT);     // pin10= PWM  output / frequency output
  pinMode(11, OUTPUT);     // pin11= PWM  output / frequency output

  Setup_timer2();
  Setup_timer1();
  digitalWrite(enablePin, HIGH);

// the waveform index is the highest 8 bits of sigma
// choose refclk as freq to increment the lsb of the 8 highest bits
//    for every call to the ISR of timer2 overflow
// the lsb of the 8 highest bits is 1<<24 (1LL<<24 for long integer literal)
  delta = (1LL<<24)*freq/refclk ;  
}
void loop(){
  
  changeFreq(3);
  
              
 }

void changeFreq(float _freq){
  cbi (TIMSK2,TOIE2);              // disable timer2 overflow detect
  freq = _freq;
  delta=(1LL<<24)*freq/refclk;  // update phase increment
  sbi (TIMSK2,TOIE2);              // enable timer2 overflow detect
} 

//******************************************************************
// timer2 setup
// set prscaler to 1,  fast PWM
void Setup_timer2() {

// Timer2 Clock Prescaler to : 1
  sbi (TCCR2B, CS20);  // set
  cbi (TCCR2B, CS21);  // clear
  cbi (TCCR2B, CS22);

  // Timer2 PWM Mode 
  cbi (TCCR2A, COM2A0);  // clear OC2A on Compare Match, PWM pin 11
  sbi (TCCR2A, COM2A1);

  // set to fast PWM
  sbi (TCCR2A, WGM20);  // Mode 1, phase correct PWM
  cbi (TCCR2A, WGM21);
  cbi (TCCR2B, WGM22);

  sbi (TIMSK2,TOIE2);              // enable overflow detect
  
}
// timer1 setup  (sets pins 9 and 10)
// set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clock
void Setup_timer1() {

// Timer1 Clock Prescaler to : 1
  sbi (TCCR1B, CS10);
  cbi (TCCR1B, CS11);
  cbi (TCCR1B, CS12);

  // Timer1 PWM Mode set to Phase Correct PWM
  cbi (TCCR1A, COM1A0);  // clear OC1A on Compare Match, PWM pin 9
  sbi (TCCR1A, COM1A1);
  cbi (TCCR1A, COM1B0);  // clear OC1B on Compare Match, PWM pin 10
  sbi (TCCR1A, COM1B1);

  sbi (TCCR1A, WGM10);  // Mode 1  / phase correct PWM
  cbi (TCCR1A, WGM11);
  cbi (TCCR1B, WGM12);
  cbi (TCCR1B, WGM13);
}

//******************************************************************
// Timer2 Interrupt Service at 31372,550 KHz = 32uSec
// this is the timebase REFCLOCK for the DDS generator
// runtime : 8 microseconds ( inclusive push and pop)
// OC2A - pin 11
// OC1B - pin 10
// OC1A - pin 9
// https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM
ISR(TIMER2_OVF_vect) {

  sbi(PORTD,testPin);          

  sigma=sigma+delta; // soft DDS, phase accu with 32 bits
  phase0=sigma >> 24;     // use upper 8 bits for phase accu as frequency information
                         // read value fron ROM sine table and send to PWM DAC
  phase1 = phase0 +85 ;
  phase2 = phase0 +170 ;

  OCR2A=pgm_read_byte_near(sine256 + phase0);  // pwm pin 11
  OCR1B=pgm_read_byte_near(sine256 + phase1);  // pwm pin 10
  OCR1A=pgm_read_byte_near(sine256 + phase2);  // pwm pin 9

  cbi(PORTD,testPin);            
  
}
 
BTW, If anyone can point me to a lesson that can show me how to create waveforms using PWM and change the Freq. That would be appreciated.

Jesu, I've not done that for, ahem, longer than I'd like to admit.

You've already got a sine table, stepping through that and putting the value out the PWM gives you a sine wave. Change how fast you step through will change the frequency. Simple!

The fun part starts when your waveform period (1/frequency) is not an integer multiple of your minimum timer frequency.

If it's a divisible fraction of your sine table, you can fake it by only using every "nth" entry (e.g. every second), again assuming your table is divisible by N

Beyond that point you need to get into Z transforms and aliasing and it's really been far, far too long for me to add anything useful.

I do recall that the maths is ugly.😱
 
I would just convert everything over to use one of the Teensy boards.

They are Arduino compatible, run an ARM Cortex M4 processor with DSP extensions, and come with a cool audio library that's drag and drop easy. You can do all sorts of audio and music synthesizer stuff......LFO, just drag the waveform object into the GUI and connect its output to a PWM or DAC output pin, compile using the Arduino GUI, load to the board, done.

There is a tutorial and a kit of parts to support it, but I built my own on perf board, then added more pots......and more......and an encoder or two.......that was my first Teensy synth.

Products

Teensy Audio Library, high quality sound processing in Arduino sketches on Teensy 3.1

There is a 44/16 audio shield for HiFi quality output if needed. Usually not needed for an LFO.

PJRC Store


Want to build a 4 oscillator 1V/OCT synthesizer......just took me a little longer. I did the whole thing in the GUI with some C code I wrote to interface and multiplex all the pots. I did use the audio shield for stereo output. It was my second Teensy synth. Number 3 is about half done. It has a mix of digital Teensy stuff and some analog goodies like a ladder filter or three for sweet vintage sounds.

YouTube

The PJRC forums have a few threads where these things were used for musical instruments, MIDI controllers, and guitar FX boxes. It's worth a look even if only to get ideas. See the "audio projects" section, but some audio related stuff spills over to other sections, especially MIDI controllers.

PJRC (Teensy) Forum
 
Status
Not open for further replies.