Public Lab Research note

Sync-mod for ambient background removal -- first tests

by donblair | March 23, 2016 18:01 23 Mar 18:01 | #12881 | #12881

This is a quick follow-up to the idea posted here.

The idea in that note was that by adding a pulse at a certain frequency to an emitter (i.e. turn an LED on and off at a certain rate) and filtering a detector signal (a photodiode) so that only signals of that frequency are received, we might be able to do light scattering experiments (like turbidity measurements) and avoid contamination from any light sources that don't match our particular frequency.

I'll be posting more details soon, but I attempted this with a visible red LED and a matching phototransistor:

In the video, the top green line on the oscilloscope is the detector, and the bottom green line (the nice square wave) is the emitter signal.

What I try to show in the video is: while closing and opening the box and shining a light don't affect the magnitude of the top trace significantly, it is nevertheless sensitive to any disruption in the pulsed beam of light.

Schematics and more details forthcoming!

Next step: try an 850 nm LED of the sort more commonly used in turbidity, and see how well we can distinguish liquids of different turbidities (while varying ambient light conditions).


Hi @donblair,

I came across this post while dealing with the same struggle -- ambient light rejection for turbidity measurements that are open to the environment. The youtube video and the technical note links got me started on a solution, which I'd thought I'd share since this post hasn't had a followup yet.

Luis Orozco of Analog Devices gives the basic setup for a modulated photo-detection setup in Figure 10 of the technical note "Optimizing Precision Photodiode Sensor Circuit Design": a light-source is pulse-width modulated, and a photodiode detecting that light source is connected in photovoltaic mode to a transimpedance amplifier (and a second-stage gain amplifier if needed) and then AC-coupled with a capacitor and filtered. In his very elegant solution the clock that modulates the light source also controls a chopper switch connected to a precision difference amplifier to capture just the positive peak voltage of the AC-coupled signal, which is finally smoothed with a low-pass filter before sampling with an analog-to-digital converter (ADC).

With the parts I had on hand, and a less sophisticated knowledge of electronics than Orozco, I figured that the chopper and difference amp could be replaced with a full-wave rectifier. The performance may not be quite the same but it's hard to beat four plain diodes for price and simplicity. Here's a breadboard photo and a schematic done with "jellybean" components:



So, after the transimpedance amplifier and a second gain stage (on one LM358 op amp), there's a high pass filter at roughly 1.5KHz, a full wave rectifier, and a low-pass filter at roughly 15Hz. The Arduino code at the bottom of the post uses the AVR Timer1 registers to set a (roughly) 8KHz pulse on pin 9, which drives one of two IR LEDs (the other, on pin 12, is directly driven). I connected an Arduino to the breadboarded setup as described in the schematic, and connected the output of the LM358 (MOD in the schematic) to Arduino pin A0 and the demodulated signal (DEMOD) to Arduino pin A1. A basic serial interpreter (below) allows us to run some quick experiments through the Arduino serial monitor window by entering the indicated commands in sequence, hitting the Send button after each command :

  1. Reset the Arduino, then turn on just the directly driven LED (command "d") and taking some readings of the output signals ("r"). The signal at pin A1 should read 0 (or rather, it will read at the level of the op amp's offset voltage). This shows that the device suitably rejects unmodulated ambient light.

  2. Turn off the directly driven LED ("d"), turn on the modulated LED with a duty cycle of roughly 10% ("p100"), and take some readings ("r"). This shows that the device does a pretty reasonable job of demodulating the photodiode signal. You can do this in an area with a decent amount of ambient light and experiment with covering and uncovering the test setup, and the demodulated signal (A1) shouldn't change.

  3. However, we can see some limitations of the system if we then turn on the direct LED again ("d") -- the signal at A1 should drop to 0 (after a few seconds, given the discharge curve of the 10uF capacitor in the low-pass filter). Why does this happen? The direct LED is so bright that it's basically swamping out the signal of the modulated LED, and because there's effectively then no modulated signal leaving the op amp, nothing makes it through the band-pass and rectifier -- which highlights the difference between noise rejection and signal capture.

Electric ghosts seem to love haunting analog circuits on breadboards, so I routed the board layout below and uploaded it to OSHPark for fabrication. Will send a followup comment after assembly and testing.

I'd love to know if you got further on this project -- maybe with a solution a bit less hacky than mine :)

Cheers! Chris


[PLEASE NOTE: I couldn't get the code to format properly, so there will probably be some errors (e.g. "_BV" is being rendered as "BV" in the posted comment). Will try to fix this soon.]

int dc_led_pin 12; char message[8]; int counter, pwm_index, serial_val; bool dc_led_on = false;

void setup() { pinMode(dc_led_pin, OUTPUT); Serial.begin(9600);
DDRB |= (1<<PB1); TCCR1A = BV(COM1A1); TCCR1A |= BV(WGM10) | BV(WGM11); TCCR1B = BV(CS10); pwm_index = 0; OCR1A = pwm_index; }

void loop() { SerialCheck(); delay(100); }

void SerialCheck(){ while (Serial.available() > 0) { char new_byte =; message[counter++] = new_byte; if (new_byte == -1) continue; // if no characters are in the buffer read() returns -1 else if (new_byte == '\n') { if(message[0] == 'p'){ pwm_index = serial_val % 1024 ; OCR1A = pwm_index; Serial.print("setting PWM to: "); Serial.println(pwm_index); } else if(message[0] == 'd'){ dc_led_on = !dc_led_on; Serial.println("toggling dc led"); if(dc_led_on) digitalWrite(dc_led_pin, HIGH); else digitalWrite(dc_led_pin, LOW); } else if(message[0] == 'r'){ Serial.println("reading A0, A1:"); for(int i = 0; i < 500; i++){ Serial.print(analogRead(A0)); delay(1); Serial.print(","); delay(1); Serial.println(analogRead(A1)); delay(1); } } counter = 0; serial_val = 0; memset(message, 0, sizeof(message)); break; // exit the while(1), we're done receiving } else if (new_byte > 47 && new_byte < 58) { serial_val *= 10; // shift left 1 decimal place serial_val += (new_byte - 48); // convert ASCII to integer, add, and shift left 1 decimal place } } }

Is this a question? Click here to post it to the Questions page.

Reply to this comment...

Login to comment.