Sunday, 26 July 2015

Photodiodes as illuminometers

Continuing with the theme of experimenting with simple, home-brewed sensors, I was wondering about how to produce a wide-range illuminometer (or a Luxmeter) with the speed of silicon and the broad range of the human eye.

By and large, electronic detectors are incapable of making meaningful measurements of physical quantities over many orders of magnitude - they tend to either be insensitive at low levels or easily swamped at high levels ... or they break when you try to measure something far too big (I think that everyone has burned out a multimeter or two this way).

One of the guiding principles of electronics is to attempt to design a measuring device that uses a linear characteristic. This is fine for many instruments - and it works for a photodiode luxmeter, provided that you know that the range of measurement is within the compass of your device.

But, what about using a logarithmic characteristic - this will extend the measurement range of the device, but at the cost of having to perform mathematical work on the raw numbers.

As we saw in my previous post on thermistors as thermometers, the use of software simplifies what would have been a pretty horrible piece of electronic design, especially where the equation relating some measured value with the quantity we want to measure is well known.

Unfortunately, no one seems to share their thoughts on using the open-circuit, photovoltaic mode for photodiodes - they prefer to use transimpedance amplifiers, and their additional circuitry.

So, to the simplest passive circuit imaginable...
Vout is connected to an analog input of the microcontroller. The circuit approximates an open-circuit since the analog input of most modern sensor chips (including microcontrollers) is an extremely high impedance CMOS (or similar) device.

According to my understanding, the relevant equation is:


Not an easy one, but certainly not as nasty as the Steinhart-Hart equation for thermistors.

V0 (dark voltage) can be easily determined by putting a piece of foil over the photodiode; a and d, two arbitrary constants, can be left (for now) as 1 and zero respectively - they can be used to calibrate the photodiode later.

More problematical are k and c, two more arbitrary constants. It is possible to determine these experimentally, but a bit of creative guesswork with trial and error will do almost as well (at least for now).

In actual fact, for the sake of ease of tweaking, there is another constant that I have used, which allows for the slope of the equation to be altered after adding in any offset. This trick only works because there is no negative illuminance possible for this system and the illuminance value should pass through the origin. The new equation is therefore:


The relevant line in the code is the assignment to the variable, luxmeterLux, near the end of the code.

As previously, the code is presented as a header file luxmeter.h, and the constants #defined in the preamble. I am keen for anyone with a better understanding of the physics (or the maths) to poke holes or make improvements in the mathematics of this code snippet.

Please see the notes below the code for a couple of caveats for more advanced users ...





// luxmeter.h



// ####################################################################

// ####################################################################

// ##

// ##  luxmeter.ino (luxmeter.h)

// ##  version 1.00.00

// ##  date    26-Jul-2015

// ##  author  Alysson Rowan (AlyssonR)

// ##

// ##  Copyright (C) 2015, Alysson Rowan

// ##   alyssonrowan @ gmail.com

// ##

// ####################################################################

// ####################################################################

// ##

// ##  This program is provided on an as-is basis without warranty.

// ##  No claim is made as toward operability or fitness for purpose

// ##  whatsoever. No liability can be accepted for any loss or damages

// ##  howsoever caused in respect of this software.

// ##

// ##  This software is made freely available under the terms of the

// ##  GNU General Public License V2. In short, you are allowed to do

// ##  anything with this program except sell it and hide the

// ##  source code.

// ##

// ##  In addition, this program is "postcard ware" – if you find it

// ##  useful then please send me an e-mail and tell me about your

// ##  application.

// ##

// ##  Your comments and suggestions are much appreciated.

// ##

// ####################################################################

// ####################################################################



// ***************************\           /****************************

// ***************************** WARNING ******************************

// ***************************/           \****************************

//

// The function analogLuxmeterSacaled returns a value in units AIU

// Arbitrary Illimination Units – which may approximate Lux.

//

// This is because I have limited resources for calibrating a luxmeter

// of this (or any other) type. Linearity and scaling is not guaranteed

// (or even terribly likely).





// The following constants were determined mainly by trial and error ...



#define luxmeterPort A0          // analog input pin assignment

#define luxmeterVdrk 0.02        // Dark voltage on photodiode

#define luxmeterCorr 1.0         // Calibration scaling factor

#define luxmeterOffs -0.292      // Calibration offset

#define luxmeterSlope .1573      // a slope correction



// Photovoltaic constants coefficients

// – these magic numbers are for an S1226 (Hammamatsu) photodiode

//   your mileage may vary!



#define luxmeterConA 23.791    // coefficient a

#define luxmeterConB -1.0     // coefficient b



// set up analog luxmeter

void analogLuxmeterEnable()

    {

    analogReference(DEFAULT);

    pinMode(luxmeterPort, OUTPUT);

    digitalWrite(luxmeterPort, LOW);

    pinMode(luxmeterPort, INPUT);

    analogRead(luxmeterPort);

    }



// read analog luxmeter

float analogLuxmeterRead()

    {

    int luxmeterRaw;

    float luxmeterScaled;



    luxmeterRaw = analogRead(luxmeterPort);

    // integer value in range 0 – 1023



    luxmeterScaled = luxmeterRaw * 0.00488759;

    // magic number converts integer reading to millivolts

    return luxmeterScaled;

    }



// convert millivolts to Lux (or something similar)

float analogLuxmeterConvert( float luxmeterScaled)

    {

    float luxmeterLux;

    luxmeterLux = (luxmeterCorr * (exp(luxmeterConA * (luxmeterScaled - luxmeterVdrk) + luxmeterConB)) + luxmeterOffs) * luxmeterSlope;

    return luxmeterLux;

    }

The caveats (cautions) ... which could also provide interesting further development ...

The value V0, or the voltage generated by the photodiode when in the dark, is temperature dependent. Indeed, this fact was used in  several machines to measure temperature before accurate thermoresistive (thermistor) devices were readily available. A matching photodiode with a foil cover could be used to provide temperature compensation for this sensor.

Photometer photodiodes can be damaged by prolonged exposure to excessive light levels - please don't invest in an expensive device unless you are certain that it is what you need. Mine were taken from scrap equipment, and are therefore, effectively, disposable .

Take care to ensure that the photodiode is correctly oriented - reverse polarity could damage your analog input.

No comments:

Post a Comment

Comments and suggestions are warmly welcomed.