Gas station without pumps

2017 August 6

Beacon detector board

I’m planning to sit in on CMPE 118/L (Mechatronics) this fall, and so I started looking over some of the material for the course from previous years.  One exercise involved designing a “beacon detector” that signals when it sees an infrared signal modulated with a 2kHz square wave. The exercise calls for an all-analog solution (phototransistor, transimpedance amplifier, active filter, rectifier, peak detector, …), which I plan to do when the time comes, but I got intrigued by the idea of doing an almost purely digital design.  That was the motivation for the Goertzel filter blog post.

I decided to take the digital design further and make a beacon detector that not only detects the 2kHz IR beacon, but also indicates what direction it is in.  To do this, I wanted 8 phototransistors around a circle, with one every 45°.  One can get a crude estimate of the angle from just which detector gets the strongest signal, but with wide-angle sensors one should be able to get finer estimates by looking at the ratio of the signals from two adjacent channels.

Because I was deliberately going for minimal analog design as an exercise, I used just a phototransistor and a pullup resistor for each channel, and a Teensy LC board for all the processing.  I chose SFH325FA side-look phototransistors, because they provide a nice, cubical package that I thought would make them easier to align.  They are surface-mount components, but with a 2.54mm pitch for the two terminals, they aren’t much harder to solder than through-hole parts.

For testing, I soldered one of the SFH325FA phototransistors to a pair of male header pins. This allowed me to experiment with different pullup resistor sizes in different lighting conditions and at different distances from an IR emitter.

My experimentation with different pullup resistors indicated that I could not operate in full sunlight, no matter what size pullup resistor I used—when the pullup was small enough that the phototransistor had sufficient voltage across it in full sunlight, the signal from the IR beacon was too small to be useful.  With AC-coupled amplifiers, I could have made it work, but I was committed to nearly pure digital solution.  If I had sunlight nearby, but the phototransistor itself was shaded, then I could go up to 22kΩ for the pullup without problems. The limiting factor was then that a very close beacon could saturate the detector, making it hard to determine power ratios.  I ended up choosing 22kΩ as my pullup, as I wanted to detect beacons from up to 2m away.

With a 22kΩ pullup and a strong signal, I can get a very clean “shark’s fin” waveform, because of the capacitance of the phototransistor acting with the resistor as a low-pass filter.  This low-pass filtering helps remove aliasing artifacts from the sampling by the analog-to-digital filter.

Behind each phototransistor I placed an LED, to indicate which channel had the maximum signal.  I charlieplexed the LEDs, so that I needed only 4 I/O pins for the 8 LEDs (I could encode up to 12 LEDs with 4 charlieplex wires). I used WP710A10SGC green LEDs for the indicators, because I had a lot of them around, but it would have been better to use an amber LED with a wide-angle, diffuse case, as the green indicators are only clearly visible over a fairly narrow angle.  The current-limiting resistors I used would keep the current low enough even with a low-forward-voltage red LED.

I also added an RGB LED to indicate the overall state—I used a common-anode one, because my son had a lot of them with diffuse cases, which are best for an indicator that needs to be seen from any angle.  The RGB LEDs were part of an unidentified lot from China, so I don’t have a part number or specs for them.  I hooked them up to PWM pins, so that I could adjust the brightness and color as needed.

I also designed in a connector for SPI connection, so that the board could be used a slave peripheral of another microcontroller.  I used Eagle on my old laptop to design the PCB (I know that in Need to find new PC board software and PCB CAD tools I said that I would be trying to switch to KiCAD or Diptrace, but I was lazy and stuck with what I already knew how to use).

I sent the design to Seeedstudio to make the PCB—like many of the Chinese firms that cater to hobbyists, they had a sale in July of boards up to 10cm×10cm for only $5.  Of course, it cost me another $21 for shipping with DHL, because I was too impatient to wait for Chinese air mail (and not trusting enough of the reliability). I chose Seeedstudio because they had the best user interface to their design-rule check, and at least as good pricing as anyone else.

The boards arrived quite quickly—only 8 days from order to delivery. Here is what they look like:

The back of the board has some documentation to remind me what is connected where.

The front of the board has relatively little documentation.

The boards are 8cm×8cm, fitting comfortably within both the cheap manufacturing limit and the Eagle free-version size limit.  I made them this big so that the Teensy LC board would fit without interfering with mounting holes (for M2 and M3 screws) and a central hole for attaching the board to a servo arm.

Getting a good right angle for the surface-mount phototransistors took some practice:

My first solder joint for the SFH325FA phototransistor tilted the transistor substantially, because the solder underneath formed a wedge. I reworked it a couple of times and finally got something sort of acceptable.

By the fifth phototransistor, I had worked out a technique that seemed to hold the phototransistor cleanly at a right angle. There is probably not much solder underneath the phototransistor, but the large wedge behind the phototransistor should provide sufficient mechanical strength.

Here are pictures of the populated board:

The back of the board does not look very different after being populated. I put one M3 screw in the bottom M3 screw hole, in order to see how compatible the screw hole design is with the screw head.

The top of the board, when populated shows how close channels 0 and 7 come to the Teensy board.

Here the board is on (powered through the USB cable), detecting an IR beacon about a meter away in the direction of channel 4. The green LED by the channel indicates the direction, and the green RGB LED indicates a strong signal.

I put the header pins on the pullup resistors, so that I could record the signal seen by the analog-to-digital converters.  Unfortunately, the signal is much noisier than I expected:

The large spikes are at 15kHz, and probably correspond to noise injected by the ADC sampling.

I turned off the beacon and looked just at the noise spikes, using the 10× probe on the oscilloscope to get better bandwidth.  I sampled at 100MHz and averaged 1000 traces to get a clean view of the signal (the triggering was set far enough from the background level that only fairly large spikes were captured, but close enough that the 1000 frames were gathered as quickly as possible).

The pulses are very short (2–3 µs), so almost certainly correspond to the charging of the sampling capacitor. After looking at this waveform, I changed my sample time on the ADC to 2µs (it had been 1µs) to reduce the noise in what the ADC reports.

This post has gotten more than long enough, though I still have a couple of things that should be added: a schematic diagram of the board and a plot of the reported angle vs actual angle.

The schematics from Eagle are really ugly, so I’ve been thinking about redrawing them with SchemeIt—perhaps that can be a subsequent post.

The reported angle vs. actual angle plot is going to require building a jig that allow me to set the angle precisely.  I tried jury-rigging something out of Lego today, but the result had too much slop—the board could not be reliably set level with the IR emitter at a fixed angle.  The OED-EL-1L2 5mm IR emitter does fit in the middle hole of a Lego Technic brick, so I using Lego to align the IR emitter was attractive. I may have to make something out of wood and MDF (medium-density fiberboard) for a more solid test jig.  The M3 screw holes will allow me to attach the board firmly to MDF with standoffs, and I can drill holes in Lego bricks to make a stand for the IR emitter.

 

Advertisements

2014 June 10

Digi-key end-of-life notification for IR emitter

Filed under: Circuits course — gasstationwithoutpumps @ 11:12
Tags: , , ,

One of the things I like about dealing with Digi-key as a supplier for electronics parts is that they treat me like an important customer, even though I order only small numbers of parts.  For example, they automatically send out notifications whenever a manufacturer informs them that a part is being discontinued—like the notice I got today:

Part Life Notification

Dear Valued Digi-Key Customer,

You have purchased the following part number from Digi-Key within the last two years. The manufacturer has announced an update to the part status.

Part Affected
Manufacturer OSRAM OPTO SEMICONDUCTORS INC
Description LED IR EMITTER 950NM
Manufacturer Part Number SFH 4512
Digi-Key Part Number 475-2943-ND
Customer Reference Number IR EMITTER
Status End Of Life
Last Time Buy Date 12/01/2014
Substitutes Please click here

It used to be that only big industrial customers got that sort of service—now even hobbyists, students, and professors can be kept informed about the parts they use. Digi-key doesn’t have to spend a lot to do individual notifications (it’s all automated), but it probably took them a fair amount of time to set up the system to do so, and this sort of attention to customer service is one of the things that has made me a loyal (though small) customer.

I’ll drop the IR emitter from next year’s class part list (we didn’t do much with it this year anyway).

I’ll also change the red LED from LED red diffuse 3mm 625nm WP710A10ID to WP3A8HD whose peak emission is at 700nm (where the molar extinction coefficient for oxyhemoglobin is only 290 cm-1/M, rather than ~683 cm-1/M for the 627nm peak of the WP710A10ID we used this year). Ideal would be a 686nm peak, where oxyhemoglobin is most transparent (272.8 cm-1/M), but there’s not much available between 700nm and 660nm (some more expensive ones at 697nm, which makes little difference from 700nm), and 700nm is better than 660nm.

2013 July 27

Failed attempt at pulse oximeter

In Optical pulse monitor with little electronics  and Digital filters for pulse monitor, I developed an optical pulse monitor using an IR emitter, a phototransistor, 2 resistors, and an Arduino.  On Thursday, I decided to try to extend this to a pulse oximeter, by adding a red LED (and current-limiting resistor) as well.  Because excluding ambient light is so important, I decided to build a mount for everything out of a block of wood:

Short piece of 2x2 wood, with a 3/4" diameter hoe drilled with a Forstner bit part way through the block.  Two 1/8" holes drilled for 3mm LEDs on top, and one for a 3mm phototransistor on the bottom (lined up with the red LED).  Wiring channels were cut with the same 1/8" drill bit, and opened up a with a round riffler.  Electrical tape holds the LEDs and phototransistor in place (removed here to expose the diodes).

Short piece of 2×2 wood, with a 3/4″ diameter hole drilled with a Forstner bit partway through the block. Two 1/8″ holes drilled for 3mm LEDs on top, and one for a 3mm phototransistor on the bottom (lined up with the red LED). Wiring channels were cut with the same 1/8″ drill bit, and opened up a with a round riffler. Electrical tape holds the LEDs and phototransistor in place (removed here to expose the diodes).

My first test with the new setup was disappointing.  The signal from the IR LED swamped out the signal from the red LED, being at least 4 times as large. The RC discharge curves for the phototransistor for the IR signal was slow enough that I would have had to go to a very low sampling rate to see the red LED signal without interference from the discharge from the IR pulse.  I could reduce the signal for the IR LED to only twice the red output by increasing the IR current-limiting resistor to 1.5kΩ, and reduce the RC time constant of the phototransistor by reducing the pulldown resistor for it to 100kΩ The reduction in the output of the IR LED and decreased sensitivity of the phototransistor made about a 17-fold reduction in the amplitude of the IR signal, and the red signal was about a thirtieth of what I’d previously been getting for the IR signal.  Since the variation in amplitude that made up my real signal was about 10 counts before, it is substantially less than 1 count now, and is  too small to be detected even with the digital filters that I used.

I could probably solve this problem of a small signal by switching from the Arduino to the KL25Z, since going from a 10-bit ADC to a 16-bit ADC would allow a 64 times larger signal-to-noise ratio (that is, +36dB), getting me back to enough signal to be detectable even with the reductions..  I’ve ordered headers from Digi-Key for the KL25Z, so next week I’ll be able to test this.

I did do something very stupid yesterday, though in a misguided attempt to fix the problem.  I had another red LED (WP710A10ID) that was listed on the spec sheet as being much brighter than the one I’d been using (WP3A8HD), so I soldered it in.  The LED was clearly much brighter, but when I put my finger in the sensor, I got almost no red signal!  What went wrong?

A moment’s thought explained the problem to me (I just wish I had done that thinking BEFORE soldering in the LED).  Why was the new LED brighter for the same current?  It wasn’t that the LED was more efficient at generating photons, but that the wavelength of the light was shorter, and so the eye was more sensitive to it.

Spectrum of the WP3A8HD red LED that I first used.  It has a peak at 700nm and dominant wavelength at 660nm.  I believe that the "dominant wavelength" refers to the peak of the spectrum multiplied by the sensitivity of the human eye.

Spectrum of the WP3A8HD red LED that I first used. It has a peak at 700nm and dominant wavelength at 660nm. I believe that the “dominant wavelength” refers to the peak of the spectrum multiplied by the sensitivity of the human eye.  Spectrum copied from Kingbright preliminary specification for WP3A8HD.

Spectrum of the WP710A10ID brighter red LED.  The peak is at 627nm and the "dominant wavelength" is 617nm.  The extra brightness is coming from this shorter wavelength, where the human eye is more sensitive.

Spectrum of the WP710A10ID brighter red LED that didn’t work for me. The peak is at 627nm and the “dominant wavelength” is 617nm. The extra brightness is coming from this shorter wavelength, where the human eye is more sensitive. Image copied from the Kingbright spec sheet.

CIE_1931 luminosity curve, representing a stndardized sensitivity of the human eye under high-light conditions (scotopic vision).  Copied from Wikipedia: http://upload.wikimedia.org/wikipedia/commons/thumb/7/72/CIE_1931_Luminosity.png/640px-CIE_1931_Luminosity.png

1931 CIE luminosity curve, representing a standardized sensitivity of the human eye with bright lighting (photopic vision). The peak is at 555nm. Note that there are better estimates of human eye sensitivity now available (see the discussion of newer ones in the Wikipedia article on the Luminosity function).
Image copied from Wikipedia.

The new LED is brighter, because the human eye is more sensitive to its shorter wavelength, but the optimum sensitivity of the phototransistor is at longer wavelengths, so the phototransistor is less sensitive to the new LED than to the old one.

Typical spectral sensitivity of a silicon photodiode or phototransistor.  This curve does not take into account any absorption losses in the packaging of the part, which can substantially change the response.  Unfortunately, Kingbright does not publish a spectral sensitivity curve for their WP3DP3B phototransistor.  Image copied from https://upload.wikimedia.org/wikipedia/commons/4/41/Response_silicon_photodiode.svg

Typical spectral sensitivity of a silicon photodiode or phototransistor. This curve does not take into account any absorption losses in the packaging of the part, which can substantially change the response. Note that the peak sensitivity is in the infrared, around 950nm, not in the green around 555nm as with the human eye. Unfortunately, Kingbright does not publish a spectral sensitivity curve for their WP3DP3B phototransistor, so this image is a generic one copied from https://upload.wikimedia.org/wikipedia/commons/4/41/Response_silicon_photodiode.svg

This sensitivity is much better matched to the IR emitter (WP710A10F3C) than to either of the red LEDs:

Spectrum for the WP710A10F3C IR emitter, copied from the Kingbright spec sheet.  The peak is at 940nm with a 50nm bandwidth.  There is no "dominant wavelength", because essentially all the emissions are outside the range of the human eye.

Spectrum for the WP710A10F3C IR emitter, copied from the Kingbright spec sheet. The peak is at 940nm with a 50nm bandwidth. There is no “dominant wavelength”, because essentially all the emissions are outside the range of the human eye.

Furthermore, blood and flesh is more opaque at the shorter wavelength, so I had more light absorbed and less sensitivity in the detector, making for a much smaller signal.

Scott Prahl's estimate of oxyhemoglobin and deoxyhemoglobin molar extinction coefficients, copied from http://omlc.ogi.edu/spectra/hemoglobin/summary.gif The higher the curve here the less light is transmitted.  Note that 700nm has very low absorption, but 627nm has much higher absorption.

Scott Prahl’s estimate of oxyhemoglobin and deoxyhemoglobin molar extinction coefficients, copied from http://omlc.ogi.edu/spectra/hemoglobin/summary.gif
Tabulated values are available at http://omlc.ogi.edu/spectra/hemoglobin/summary.html and general discussion at http://omlc.ogi.edu/spectra/hemoglobin/
The higher the curve here the less light is transmitted. Note that 700nm has very low absorption (290), but 627nm has over twice as high an absorption (683).  Also notice that in the infrared

I had to go back to the red LED (WP3A8HD) that I started with. Here is an example of the waveform I get with that LED, dropping the sampling rate to 10Hz:

The green waveform is the voltage driving the red LED and through a 100Ω resistor.  The red LED is on for the 1/30th of second that the output is low, then the IR LED is on (through a 1.5kΩ resistor) for 1/30th of a second, then both are off.  THe yellow trace shows the voltage at the phototransistor emitter with a 680kΩ pulldown. This signal seems to have too little amplitude for the variation to be detected with the Arduino.

The green waveform is the voltage driving the red LED and through a 100Ω resistor. The red LED is on for the 1/30th of second that the output is low, then the IR LED is on (through a 1.5kΩ resistor) for 1/30th of a second, then both are off. THe yellow trace shows the voltage at the phototransistor emitter with a 680kΩ pulldown.
This signal seems to have too little amplitude for the variation to be detected with the Arduino (the scale is 1v/division with 0v at the bottom of the grid).

I can try increasing the signal by using 2 or more red LEDs (though the amount of current needed gets large), or I could turn down the IR signal to match the red signal and use an amplifier to get a big enough signal for the Arduino to read.  Sometimes it seems like a 4.7kΩ resistor on the IR emitter matches the output, and sometimes there is still much more IR signal received, depending on which finger I use and how I hold it in the device.

I was thinking of playing with some amplification, but I could only get a gain of about 8, and even then I’d be risking saturation of the amplifier.  I think I’ll wait until the headers come and I can try the KL25Z board—the gain of 64 from the higher resolution ADC is likely to be more useful.  If that isn’t enough, I can try adding gain also.  I could also eliminate the “off-state” and just amplify the difference between IR illumination and red illumination.  I wonder if that will let me detect the pulse, though.

2013 July 24

Digital filters for pulse monitor

In Optical pulse monitor with little electronics, I talked a bit about an optical pulse monitor using the Arduino and just 4 components (2 resistors, an IR emitter, and a phototransistor).  Yesterday, I had gotten as far as getting good values for resistors, doing synchronous decoding, and using a very simple low-pass IIR filter to clean up the noise.  The final result still had problems with the baseline shifting (probably due to slight movements of my finger in the sensor):

With digital low-pass filtering, the pulse signal is much cleaner, but the sharp downward transition at the start of each pulse has been rounded off by the filter.

(click to embiggen) Yesterday’s plot with digital low-pass filtering, using y(t) = (x(t) + 7 y(t-1) )/8.  There is not much noise, but the baseline wobbles up and down a lot, making the signal hard to process automatically.

Today I decided to brush off my digital filter knowledge, which I haven’t used much lately, and see if I could design a filter using only small integer arithmetic on the Arduino, to clean up the signal more. I decided to use a sampling rate fs = 30Hz on the Arduino, to avoid getting any beating due to 60Hz pickup (not that I’ve seen much with my current setup). The 30Hz choice was made because I do two measurements (IR on and IR off) for each sample, so my actual measurements are at 60Hz, and should be in the same place in any noise waveform that is picked up. (Europeans with 50Hz line frequency would want to use 25Hz as their sampling frequency.)

With the 680kΩ resistor that I selected yesterday, the 30Hz sampling leaves plenty of time for the signal to charge and discharge:

The grid line in the center is at 3v.  The green trace is the signal to on the positive side of the IR LED, so the LED is on when the trace is low (with 32mA current through the pullup resistor).  The yellow trace is the voltage at the Arduino input pin: high when light is visible, low when it is dark.  This recording was made with my middle finger between the LED and the phototransistor.

The grid line in the center is at 3v. The green trace is the signal to on the positive side of the IR LED, so the LED is on when the trace is low (with 32mA current through the pullup resistor). The yellow trace is the voltage at the Arduino input pin: high when light is visible, low when it is dark. This recording was made with my middle finger between the LED and the phototransistor.

I decided I wanted to replace the low-pass filter with a passband filter, centered near 1Hz (60 beats per minute), but with a range of about 0.4Hz (24 bpm) to 4Hz (240bpm). I don’t need the passband to be particularly flat, so I decided to go with a simple 2-pole, 2-zero filter (called a biquad filter). This filter has the transfer function

H(z) = \frac{b_{0} + b_{1}z^{-1} + b_{2}z^{-2}}{1+a_{1}z^{-1}+a_{2}z^{-2}}

To get the gain of the filter at a frequency f, you just compute \left| H( e^{i \omega} ) \right|, where \omega = 2 \pi f / f_{s}.  Note that the z values that correspond to sinusoids are along the unit circle, from DC at e^{0} = 1 up to the Nyquist frequency f_{s}/2 at e^{\pi} = -1.

The filter is implemented as a simple recurrence relation between the input x and the output y:

y(t) = b_{0} x(t) + b_{1}x(t-1) + b_{2}x(t-2) - a_{1}y(t-1) - a_{2}y(t-2)

This is known as the “direct” implementation.  It takes a bit more memory than the “canonical” implementation, but has some nice properties when used with small-word arithmetic—the intermediate values never get any further from 0 than the output and input values, so there is no overflow to worry about in intermediate computations.

I tried using an online web tool to design the filter http://www-users.cs.york.ac.uk/~fisher/mkfilter/, and I got some results but not everything on the page is working.  One can’t very well complain to Tony Fisher about the maintenance, since he died in 2000. I tried using the tool at http://digitalfilter.com/enindex.html to look at filter gain, but it has an awkward x-axis (linear instead of logarithmic frequency) and was a bit annoying to use.  So I looked at results from Tony Fisher’s program, then used my own gnuplot script to look at the response for filter parameters I was interested in.

The filter program gave me one obvious result (that I should not have needed a program to realize): the two zeros need to be at DC and the Nyquist frequency—that is at ±1.  That means that the numerator of the transfer function is just 1-z^{-2}, and b0=1, b1=0, and b2=–1.  The other two parameters it gave me were a2=0.4327386423 and a1=–1.3802466192.  Of course, I don’t want to use floating-point arithmetic, but small integer arithmetic, so that the only division I do is by powers of 2 (which the compiler turns into a quick shift operation).

I somewhat arbitrarily selected 32 as my power of 2 to divide by, so that my transfer function is now

H(z) = 32 \frac{1 - z^{-2}}{32+A_{1}z^{-1}+A_{2}z^{-2}}

and my recurrence relation is

y(t) = \left(32 \left( x(t) - x(t-2) \right) - A_{1} y(t-1) - A_{2} y(t-2) \right)/32

with A1 and A2 restricted to be integers.  Rounding the numbers from Fisher’s program suggested A1=-44 and A2=14, but that centered the filter at a bit higher frequency than I liked, so I tweaked the parameters and drew plots to see what the gain function looked like.  I made one serious mistake initially—I neglected to check that the two poles were both inside the unit circle (they were real-valued poles, so the check was just applying the quadratic formula).  My first design (not the one from Fisher’s program) had one pole outside the unit circle—it looked fine on the plot, but when I implemented it, the values grew until the word size was exceeded, then oscillated all over the place.  When I realized what was wrong, I checked the stability criterion and changed the A2 value to make the pole be inside the unit circle.

I eventually ended up with A1=-48 and A2=17, which centered the filter at 1, but did not have as high an upper frequency as I had originally thought I wanted:

The gain of the filter that I ended up implementing has -3dB points at about 0.43 and 2.15 Hz.

(click to embiggen) The gain of the filter that I ended up implementing has -3dB points at about 0.43 and 2.15 Hz.

Here is the gnuplot script I used to generate the plot—it is not fully automatic (the xtics, for example, are manually set). Click it to expand.

fs = 30	# sampling frequency
A0=32.  # multiplier (use power of 2)
b=16.

A1=-(A0+b)
A2=b+1

peak = fs/A0	# approx frequency of peak of filter

set title sprintf("Design of biquad filter, fs=%3g Hz",fs)

set key bottom center
set ylabel "gain [dB]"
unset logscale y
set yrange [-20:30]

set xlabel "frequency [Hz]"
set logscale x
set xrange [0.01:0.5*fs]

set xtics add (0.43, 2.15)
set grid xtics

j=sqrt(-1)
biquad(zinv,b0,b1,b2,a0,a1,a2) = (b0+zinv*(b1+zinv*b2))/(a0+zinv*(a1+zinv*a2))
gain(f,b0,b1,b2,a0,a1,a2) = abs( biquad(exp(j*2*pi*f/fs),b0,b1,b2,a0,a1,a2))
phase(f,b0,b1,b2,a0,a1,a2) = imag(log( biquad(exp(j*2*pi*f/fs),b0,b1,b2,a0,a1,a2)))

plot 20*log(gain(x,A0,0,-A0,  A0,A1,A2)) \
		title sprintf("%.0f (1-z^-2)/(%.0f+ %.0f z^-1 + %.0f z^-2)", \
			A0, A0, A1, A2), \
	20*log(gain(peak,A0,0,-A0,  A0,A1,A2))-3 title "approx -3dB"

I wrote a simple Arduino program to sample the phototransistor every 1/60th of a second, alternating between IR off and IR on. After each IR-on reading, I output the time, the difference between on and off readings, and the filtered difference. (click on the code box to view it)

#include "TimerOne.h"

#define rLED 3
#define irLED 5

// #define CANONICAL   // use canonical, rather than direct implementation of IIR filter
// Direct implementation seems to avoid overflow better.
// There is probably still a bug in the canonical implementation, as it is quite unstable.

#define fs (30) // sampling frequency in Hz
#define half_period (500000L/fs)  // half the period in usec

#define multiplier  32      // power of 2 near fs
#define a1  (-48)           // -(multiplier+k)
#define a2  (17)            // k+1

volatile uint8_t first_tick;    // Is this the first tick after setup?
void setup(void)
{
    Serial.begin(115200);
//    pinMode(rLED,OUTPUT);
    pinMode(irLED,OUTPUT);
//    digitalWrite(rLED,1);  // Turn RED LED off
    digitalWrite(irLED,1); // Turn IR LED off

    Serial.print("# bandpass IIR filter\n# fs=");
    Serial.print(fs);
    Serial.print(" Hz, period=");
    Serial.print(2*half_period);
    Serial.print(" usec\n#  H(z) = ");
    Serial.print(multiplier);
    Serial.print("(1-z^-2)/(");
    Serial.print(multiplier);
    Serial.print(" + ");
    Serial.print(a1);
    Serial.print("z^-1 + ");
    Serial.print(a2);
    Serial.println("z^-2)");
#ifdef CANONICAL
    Serial.println("# using canonical implementation");
#else
    Serial.println("# using direct implementation");
#endif
    Serial.println("#  microsec raw   filtered");

    first_tick=1;
    Timer1.initialize(half_period);
    Timer1.attachInterrupt(half_period_tick,half_period);
}

#ifdef CANONICAL
// for canonical implementation
 volatile int32_t w_0, w_1, w_2;
#else
// For direct implementation
 volatile int32_t x_1,x_2, y_0,y_1,y_2;
#endif

void loop()
{
}

volatile uint8_t IR_is_on=0;    // current state of IR LED
volatile uint16_t IR_off;       // reading when IR is off (stored until next tick)

void half_period_tick(void)
{
    uint32_t timestamp=micros();

    uint16_t IR_read;
    IR_read = analogRead(0);
    if (!IR_is_on)
    {   IR_off=IR_read;
        digitalWrite(irLED,0); // Turn IR LED on
        IR_is_on = 1;
        return;
    }

    digitalWrite(irLED,1); // Turn IR LED off
    IR_is_on = 0;

    Serial.print(timestamp);
    Serial.print(" ");

    int16_t x_0 = IR_read-IR_off;
    Serial.print(x_0);
    Serial.print(" ");

 #ifdef CANONICAL
    if (first_tick)
    {  // I'm not sure how to initialize w for the first tick
       w_2 = w_1 = multiplier*x_0/ (1+a1+a2);
       first_tick = 0;
    }
 #else
    if (first_tick)
    {   x_2 = x_1 = x_0;
        first_tick = 0;
    }
#endif

#ifdef CANONICAL
    w_0 = multiplier*x_0 - a1*w_1 -a2*w_2;
    int32_t y_0 = w_0 - w_2;
    Serial.println(y_0);
    w_2=w_1;
    w_1=w_0;
#else
     y_0 = multiplier*(x_0-x_2) - a1*y_1 -a2*y_2;
     Serial.println(y_0);
     y_0 /= multiplier;
     x_2 = x_1;
     x_1 = x_0;
     y_2 = y_1;
     y_1 = y_0;
#endif
}

Here are a couple of examples of the input and output of the filtering:

    The input signals here are fairly clean, but different runs often get quite different amounts of light through the finger, depending on which finger is used and the alignment with the phototransistor. Note that the DC offset shifts over the course of each run.

(click to embiggen) The input signals here are fairly clean, but different runs often get quite different amounts of light through the finger, depending on which finger is used and the alignment with the phototransistor. Note that the DC offset shifts over the course of each run.

IR-biquad-filtered

(click to embiggen) After filtering the DC offset and the baseline shift are gone. The two very different input sequences now have almost the same range. There is a large, clean downward spike at the beginning of each pulse.

Overall, I’m pretty happy with the results of doing digital filtering here. Even a crude 2-zero, 2-pole filter using just integer arithmetic does an excellent job of cleaning up the signal.

Optical pulse monitor with little electronics

In yesterday’s blog post, I talked mainly about what my son did with his time yesterday, to mention the small amount of debugging help I gave him.  Today I’ll post about what I did with most of my time yesterday.

This year, I am hoping to lead a 2-unit freshman design seminar for bioengineering students.  (Note: I did not say “teach” here, as I’m envisioning more of a mentoring role than a specific series of exercises.)  One thing I’m doing is trying to come up with design projects that freshmen with essentially no engineering skills can do as a team.  They may have to learn something new (I certainly hope they do!), but they should only spend a total of 60 hours on the course, including class time.  Since I want to spend some of class time on lab tours, lab safety, using the library resources, and how to work in a group effectively, there is not a lot of time left for the actual design and implementation.

One of the things I found very valuable in designing the Applied Circuits course was doing all the design labs myself, sometimes several times, in order to tweak the specs and anticipate where the students would have difficulty.  I expect to do some redesign of a couple of the circuits labs this year, but that course is scheduled for Spring (and finally got official approval this week), while the (not yet approved) freshman seminar is scheduled for Winter.  So I’m now experimenting with projects that I think may be suitable for the freshman design seminar.

These students cannot individually be expected to know anything useful, high school in California being what it is.  As a group, though, I think I can expect a fair amount of knowledge of biology, chemistry, and physics, with perhaps a sprinkling of math and computer programming.  I can’t expect any electronics knowledge, and we won’t have access to a machine shop—we may get permission for the students to use a laser cutter under supervision.  We can probably get some space in an electronics lab, but maybe not in a bio lab (the dean took away the department’s only teaching lab, with a “promise” to build a bigger one—but it is unlikely to be available for the freshmen by Winter quarter—I miss our first dean of engineering, as we seem to have had a series of incompetent deans since then).

So I’m looking for projects that can essentially be built at home with minimal tools and skills, but that are interesting enough to excite students to continue to higher levels in the program.  And I want them to be design projects, not kit-building or cookbook projects, with multiple possible solutions.

So far, there have been a couple of ideas suggested, both involving a small amount of electronics and some mechanical design:

  • An optical growth meter for continuously monitoring a liquid culture of bacteria or yeast. The electronics here is just a light source (LED or laser diode with current-limiting resistor), a phototransistor,  a current-to-voltage converter for the phototransistor (one resistor), and a data logger (like the Arduino Data Logger we use for the circuits course).  The hard part is coming up with a good way to get uniform sampling of the liquid culture while it is in an incubator on a shaker table.  There are lots of possible solutions: mounting stuff around flasks, immersed sensors, bending glass tubing so that the swirling culture is pumped through the tubes, external peristaltic pumps, … .  Design challenges include how the parts of the apparatus that touch the culture will be sterilized, how to keep things from falling apart when they are shaken for a couple of days, and so forth.   I’ve not even started trying to do a design here, though I probably should, as the mechanical design is almost all unfamiliar to me, and I’d be a good example of the cluelessness that the students would bring to the project.
  • An optical pulse sensor or pulse oximeter.  This is the project I decided to work on yesterday. The goal is to shine light through a finger and get a good pulse signal.  (I tried this last summer, making a very uncomfortable ear clip and doing a little testing before rejecting the project for the circuits course.)  If I can get good pulse signals from both red and IR light sources, I should be able to take differences or ratios and get an output proportional to blood oxygenation.

I decided yesterday to try to build a pulse monitor with almost no electronics.  In particular, I wanted to try building without an op amp or other amplifier, feeding the phototransistor signal directly into an Arduino analog in.  (I may switch to using the KL25Z for this project, as the higher resolution on the analog-to-digital converter means I could use smaller signals without amplification.)

A phototransistor is essentially a light-to-current converter.  The current through the phototransistor is essentially linear in the amount of light, over a pretty wide range. The Arduino analog inputs are voltage sensors, so we need to convert the current to a voltage.  The simplest way to do this is to put a series resistor to ground, and measure the voltage across the series resistor.  The voltage we see is then the current times the resistance.  Sizing the resistor is a design task—how big a current do we get with the intensity of light through the finger, and how much voltage do we need. The voltage needed can be estimated from the resolution of the analog-to-digital converter, but the amount of light is best measured empirically.

One problem that the pulse monitor faces is huge variations in ambient light.  Ideally the phototransistor gets light only from LED light shining through the finger, but that is a bit hard to set up while breadboarding.  Distinguishing the ambient light from the light through the finger can be difficult. Yesterday, I tried to reduce that problem by using “synchronous decoding”.  That is, I turned the LED on and off, and measured the difference between the phototransistor current with the LED on and with the LED off.  Using the Arduino to control the LED as well as to read the voltage is fairly easy—these are the sorts of tasks that are starter projects on the Arduino, so should be within the capabilities of the freshmen (with some learning on their part).

I also looked at the phototransistor output with my BitScope oscilloscope, so that I could see the waveform that the Arduino was sampling two points from.  Here is an example waveform:

The x-axis is 20ms/division, and the y-axis 1v/division, with the center line at 2v. I put in a 50% duty cycle (20ms on, 20ms off).

The x-axis is 20ms/division, and the y-axis 1v/division, with the center line at 2v.
I put in a 50% duty cycle (20ms on, 20ms off).  The IR light is shining through my index finger.

For the above trace, I used a 680kΩ pulldown resistor to convert the current to voltage. I played a lot with different resistors yesterday, to get a feel for the tradeoffs.  Such a large resistor provides a large voltage swing for a small change in current, but the parasitic capacitance makes for rather slow RC charge/discharge curves.  Using larger resistors does not result in larger swings (unless the frequency of the input is reduced), because the RC time constant gets too large and the slowly changing signal does not have time to make a full swing.  I tried, as an experiment, adding a unity-gain buffer, so that the BitScope and Arduino inputs would not be loading the phototransistor.  This did not make much difference, so most of the parasitic capacitance is probably in the phototransistor itself.  One can get faster response for a fixed change in light only by decreasing the voltage swing, which would then require amplification to get a big enough signal to be read by the Arduino.  (It may be that the extra 6 bits of resolution on the KL25Z board would allow a resistor as low as 20kΩ and much faster response.)

Note that ambient light results in a DC shift of the waveform without a change in shape, until it gets bright enough that the current is more than 5v/680kΩ (about 7µA), at which point the signal gets clipped.  Unfortunately, ordinary room lighting is enough to saturate the sensor with this large a series resistor.  I was able to get fairly consistent readings by using the clothespin ear clip I made last summer, clamped open to make it the right size for my finger.  I did even better when I put the clip and my hand into a camera bag that kept out most of the ambient light.  Clearly, mechanical design for eliminating ambient light will be a big part of this design.

One might think that the 2v signal seen on the BitScope is easily big enough for pulse detection, but remember that this is not the signal we are interested in.  The peak-to-peak voltage is proportional to how transparent the finger is—we are interested in the variation of that amplitude with blood flow.  Here is an example plot of the sort of signal we are looking at:

The pulse here is quite visible, but is only about a 15–30 count change in the 300-count amplitude signal.  Noise from discretization makes the signal hard to pick out.

(click to embiggen) The pulse here is quite visible, but is only about a 15–30 count change in the 300-count amplitude signal. Noise from discretization (and other sources) makes the signal hard to pick out auotmatically.  This signal was recorded with the Arduino data logger, but only after I had modified the data logger code to turn the IR emitter on and off and report differences in the readings rather than the readings themselves. Note the sharp downward transition (increased opacity due to more blood) at the beginning of each pulse.

To get a bigger, cleaner signal, I decided to do some very crude low-pass filtering on the Arduino. I used the simplest of infinite-impulse response (IIR) filters: Y(t) = a X(t) + (1-a) Y(t-1). Because division is very slow on the Arduino, I limited myself to simple shifts for division: a= 1/2, 1/4, or 1/8. To avoid losing even more precision, I actually output X(t) + 7 Y(t-1) then divided by 8 to get Y(t). I also used a 40msec sampling period, with the IR emitter on for 20ms, then off for 20msec (the waveform shown in the oscilloscope trace above).

With digital low-pass filtering, the pulse signal is much cleaner, but the sharp downward transition at the start of each pulse has been rounded off by the filter.

(click to embiggen) With digital low-pass filtering, the pulse signal is much cleaner, but the sharp downward transition at the start of each pulse has been rounded off by the filter. This data was not captured with the Arduino Data Logger, but by cutting and pasting from the Arduino serial monitor, which involves simpler (hence more feasible for freshmen) programming of the Arduino.

I now have a very clean pulse signal, using just the Arduino, an IR emitter, a phototransistor, and two resistors. There is still a huge offset, as the signal is 200 counts out of 4600, and the offset fluctuates slowly.  To get a really good signal, I’d want to do a bandpass filter that passes 0.3Hz to 3Hz (20bpm–200bpm), but designing that digital filter would be beyond the scope of a freshman design seminar.  Even the simple IIR filter is pushing a bit here.

I’m not sure how to go from here to the pulse oximeter (using both an IR and a red LED) without fancy digital filtering.  Here is the circuit so far:

Although the 120Ω resistor allows up to 32mA, I didn't believe that the Arduino outputs could actually sink that much current—20 mA is what the spec sheet allows. Checking with the BitScope, I see a 3840mV drop across the resistor, for 32mA.  Note: I used pins D3 and D5 of the Arduino so that I could use pulse-width modulation (PWM) if I wanted to. (Schematic drawn with Digikey's SchemeIt.)

Although the 120Ω resistor allows up to 32mA, I didn’t believe that the Arduino outputs could actually sink that much current—20 mA is what the spec sheet allows. Checking with the BitScope, I see a 3840mV drop across the resistor, for 32mA. Note: I used pins D3 and D5 of the Arduino so that I could use pulse-width modulation (PWM) if I wanted to. (Schematic drawn with Digikey’s SchemeIt.)

(Update 2015-Jul-5: I just noticed that the schematic used a PNP phototransistor symbol, rather than an NPN one—I’d make my students redo their reports for a mistake that big!)

Create a free website or blog at WordPress.com.

%d bloggers like this: