Gas station without pumps

2013 December 29

Range of conductivity measurements

Filed under: Circuits course,freshman design seminar — gasstationwithoutpumps @ 21:02
Tags: , , , ,

I was curious what the range of voltage measurements would be for different salt concentrations, and how linear the measurements would be.  Measurements were made with a modified program, so that I could measure the applied voltage at the drive electrode as well as the voltage at the sense electrode. Conductance was computed as 0.01S * V_resist / (V_applied – V_resist).

solution V applied V resist conductance
distilled  3298.1mV  1.93±0.08mv  5.86µS
tap water  3257.4mV  424.80±0.14mV  1.50mS
0.1M NaCl  3074.0mV  2265.31±0.15mV  28.01mS
1.0M NaCl  3012.4mV  2842.13±0.09mV 166.9mS

The first version of the program seemed to have some problems with fluctuation in the period of the square wave, probably from using the same pin for both digital out and analog in.  I hooked up another pin to monitor the digital output and got a more consistent 65kHz square wave.

Repeating the measurements got me

solution V applied V resist conductance
distilled 3297.71±0.12mV 9.73±0.18mV 29.59µS
tap water 3258.75±0.07mV 415.10±0.25mV 1.46mS
0.1M NaCl 3065.05±0.08mV 2271.82±0.23mV 28.64mS
1.0M NaCl 3000.60±0.24mV 2850.30±0.24mV 189.6mS

The small differences are probably from cross contamination as I moved the electrodes from one bath to another. I wiped them between measurements, but did not get them completely clean and dry.

The 0.1M and 1.0M solutions do not show a ten-fold ratio of conductances, only about 6–6.6 depending which set of measurements one takes. Either I did the dilution to make the 0.1M solution wrong (quite possible—I’m clumsy at even trivial wet-lab stuff) or my assumption that conductance should be linear with concentration is way off.  It looks like the device should be able to measure from about 1E-4M to 3M NaCl.  There is enough resolution in the ADC measurements to go down to 1E-5M (if the electrodes could avoid contaminating that) and up to saturated salt (about 6.2M).  It would be important to have a series of test solutions to calibrate the unit, if the linearity assumption is wrong.

Here is a plot of the ionic concentration vs time for the second set of readings, based on the assumption of linearity and the correctness of the 1MNaCl reading:

Concentration is calculated as V_resist/(V_apply-V_resist) /18.96

Concentration is calculated as V_resist/(V_applied–V_resist) /18.96

Here is the source code for the KL25Z (using the mbed.org compiler).  Pins A0 and A2 were connected to the drive electrode, pin A1 to the sense electrode.
The sense electrode electrode was connected via 200Ω resistors to Gnd and 3.3V.

#include "mbed.h"

Serial USB_io(USBTX, USBRX);  // defaults to 9600 8N1 (reset in main to 115200 baud)
Timer since_start;

AnalogIn Vapply(PTB2);
AnalogIn Vsense(PTB1);
DigitalInOut square_out(PTB0);   // PTB0=arduino A0
//PTB0, PTB1, PTD6, and PTD7 I/O have both high drive and normal drive capability selected by the associated PTx_PCRn[DSE] control bit.

#define WARMUP (20000)    // number of cycles of toggling output before collecting data
#define COLLECT (10000)   // number of cycles of data to sum for each output
#define Vdd (3.3)      // High voltage at output
int main()
{
    USB_io.baud(115200);
    USB_io.printf("\nusec\tV_out\tV_appl\nN\tN\tN\n");

    //DEFAULT configuration of analog input
    ADC0->CFG1 = ADC_CFG1_ADLPC_MASK    // Low-Power Configuration
               | ADC_CFG1_ADIV(3)       // Clock Divide Select: (Input Clock)/8
               | ADC_CFG1_ADLSMP_MASK   // Long Sample Time
               | ADC_CFG1_MODE(3)       // (16)bits Resolution
               | ADC_CFG1_ADICLK(1);    // Input Clock: (Bus Clock)/2

    ADC0->CFG2 = ADC_CFG2_MUXSEL_MASK   // ADxxb channels are selected
               | ADC_CFG2_ADACKEN_MASK  // Asynchronous Clock Output Enable
               | ADC_CFG2_ADHSC_MASK    // High-Speed Configuration
               | ADC_CFG2_ADLSTS(0);    // Long Sample Time Select

    ADC0->SC2 = ADC_SC2_REFSEL(0);      // Default Voltage Reference

    ADC0->SC3 = ADC_SC3_AVGE_MASK       // Hardware Average Enable
                | ADC_SC3_AVGS(0);        // 4 Samples Averaged

    // FAST analog input
//    SIM->SCGC6 |= SIM_SCGC6_ADC0_MASK;   // enable ADC0 clock
//   SIM->SCGC5 |= 1 << (SIM_SCGC5_PORTB_SHIFT);  // enable PORTB clock

    ADC0->SC1[1] = ADC_SC1_ADCH(ADC0_SE9);  // PTB1

    ADC0->CFG1 =
                ADC_CFG1_MODE(3)       // (16)bits Resolution
               | ADC_CFG1_ADLSMP_MASK   // Long Sample Time
               | ADC_CFG1_ADICLK(0);    // Input Clock: (Bus Clock)

    ADC0->CFG2 = ADC_CFG2_MUXSEL_MASK   // ADxxb channels are selected
               | ADC_CFG2_ADACKEN_MASK  // Asynchronous Clock Output Enable
               | ADC_CFG2_ADHSC_MASK    // High-Speed Configuration
               | ADC_CFG2_ADLSTS(0);    // longest "long" Sample Time Select

//             | ADC_CFG2_ADLSTS(3);    // shortest "long" Sample Time Select

    ADC0->SC2 = ADC_SC2_REFSEL(0);      // Default Voltage Reference
    ADC0->SC3 = 0;        // No hardware averaging

    // PTB1 and PTB2 set as analog inputs
//    PORTB->PCR[1] = 0;
//    PORTB->PCR[2] = 0;

    // set PORTB pin 0 to high drive here
    PORTB->PCR[0]  |= PORT_PCR_DSE_MASK;

    since_start.start();
    while(1)
    {    wait_us(100);
         square_out.output();
         int32_t sum_in=0;
         int32_t sum_out=0;
         for (int i=0; i
         {
             square_out=1;
             wait_us(1);
            ADC0->SC1[0] = ADC_SC1_ADCH(ADC0_SE9);  // PTB1
             // Wait Conversion Complete
             while ((ADC0->SC1[0] & ADC_SC1_COCO_MASK) != ADC_SC1_COCO_MASK);
             int32_t in_read=ADC0->R[0];

             ADC0->SC1[0] = ADC_SC1_ADCH(ADC0_SE12);  // PTB2
             // Wait Conversion Complete
             while ((ADC0->SC1[0] & ADC_SC1_COCO_MASK) != ADC_SC1_COCO_MASK);
             int32_t out_read=ADC0->R[0];

             square_out=0;
             wait_us(1);
             ADC0->SC1[0] = ADC_SC1_ADCH(ADC0_SE9);  // PTB1
             // Wait Conversion Complete
             while ((ADC0->SC1[0] & ADC_SC1_COCO_MASK) != ADC_SC1_COCO_MASK);
             sum_in +=  in_read - ADC0->R[0];

             ADC0->SC1[0] = ADC_SC1_ADCH(ADC0_SE12);  // PTB2
             // Wait Conversion Complete
             while ((ADC0->SC1[0] & ADC_SC1_COCO_MASK) != ADC_SC1_COCO_MASK);
             sum_out += out_read - ADC0->R[0];
        }
        sum_in=0;
        sum_out=0;
        for (int i=0;i<COLLECT; i++)
        {
             square_out=1;
             wait_us(1);
            ADC0->SC1[0] = ADC_SC1_ADCH(ADC0_SE9);  // PTB1
             // Wait Conversion Complete
             while ((ADC0->SC1[0] & ADC_SC1_COCO_MASK) != ADC_SC1_COCO_MASK);
             int32_t in_read=ADC0->R[0];

             ADC0->SC1[0] = ADC_SC1_ADCH(ADC0_SE12);  // PTB2
             // Wait Conversion Complete
             while ((ADC0->SC1[0] & ADC_SC1_COCO_MASK) != ADC_SC1_COCO_MASK);
             int32_t out_read=ADC0->R[0];

             square_out=0;
             wait_us(1);
             ADC0->SC1[0] = ADC_SC1_ADCH(ADC0_SE9);  // PTB1
             // Wait Conversion Complete
             while ((ADC0->SC1[0] & ADC_SC1_COCO_MASK) != ADC_SC1_COCO_MASK);
             sum_in +=  in_read - ADC0->R[0];

             ADC0->SC1[0] = ADC_SC1_ADCH(ADC0_SE12);  // PTB2
             // Wait Conversion Complete
             while ((ADC0->SC1[0] & ADC_SC1_COCO_MASK) != ADC_SC1_COCO_MASK);
             sum_out += out_read - ADC0->R[0];
        }
        square_out.input(); // hiZ state when not driving pulses

        USB_io.printf("%10d\t%7.5f\t%7.5f\t\n", since_start.read_us(), sum_in*(Vdd/COLLECT/(1<<16)),sum_out*(Vdd/COLLECT/(1<<16))); // scale output to volts
    }
 }

2013 December 25

Using KL25Z for measuring salinity

Filed under: Circuits course,freshman design seminar — gasstationwithoutpumps @ 20:09
Tags: , , , , ,

Continuing the series of posts on measuring salinity with a couple of resistors and microprocessor

  1. Towards automatic measurement of conductivity of saline solution describes another possible freshman design project: a conductivity meter using the KL25Z board.
  2. More on automatic measurement of conductivity of saline solution looks at waveforms for square waves generated using PWM on the KL25Z board.  In this post I found that 100kHz square waves would work well.
  3. Still more on automatic measurement of conductivity of saline solution looks at waveforms for bursts of square waves generated by an Arduino board.  The bursts are limited to about 4kHz, but that may be good enough for a conductivity meter.

Today I started over on using the KL25Z board.  Since I wasn’t interested in precise frequencies, I didn’t use the PWM output this time, but used the same trick I used on the Arduino board: flipping the output bit, reading a sample, and repeating in a burst.

I record the sum of the differences between the high and low readings, and report the average at the end of each burst.  By using 40,000 cycles of warmup in each burst (discarded), then averaging over the next 10,000 cycles, I get a voltage reading that has a standard deviation of about 0.1mV on a reading of 2.843V, which is about 14–15 bits of accuracy.  The voltage reading is not constant, though, but drifts downward.

Voltage difference at undriven electrode as a function of time.  The two sudden steps were probably the result of my jostling the table by putting down my teacup too hard.

(click to embiggen) Voltage difference at undriven electrode as a function of time. The two sudden steps were probably the result of my jostling the table by putting down my teacup too hard.

I don’t have an explanation of the gradual drift in the voltage. I don’t think that this is a change in the salinity of the solution (which should be unchanged or increasing slowly due to evaporation). but a change in the characteristics of the electrodes. More likely, it is a change in the characteristics of the electrodes.  The sudden shifts when the table was jostled may be due to electrodes shifting their position in the cup or the release of a bubble.  Releasing a bubble should increase the surface area of the electrode and hence increase the conductivity and the voltage difference at the undriven electrode.  The gradual downward shift could be due to building up tiny hydrogen bubbles (too small to see) on the negative electrode.  The changes in voltage observed here are less than 0.1%, which is fairly respectable for a homebrew instrument.

Here is the (undocumented, throw-away) code that I wrote today to test out the ideas of an automatic salinity measurement system using a KL25Z:

#include "mbed.h"

DigitalInOut square_out(PTB0);   // PTB0=arduino A0
//PTB0, PTB1, PTD6, and PTD7 I/O have both high drive and normal drive capability selected by the associated PTx_PCRn[DSE] control bit.

AnalogIn IN(PTB1);  // PTB1=Arduino A1

Serial USB_io(USBTX, USBRX);  // defaults to 9600 8N1 (reset in main to 115200 baud)
Timer since_start;

#define WARMUP (40000)    // number of cycles of toggling output before collecting data
#define COLLECT (10000)   // number of cycles of data to sum for each output
#define Vdd (3.3)      // High voltage at output
int main()
{
    USB_io.baud(115200);
    USB_io.printf("\nusec\tvolts\nN\tN\n");

    //DEFAULT configuration of analog input
    ADC0->CFG1 = ADC_CFG1_ADLPC_MASK    // Low-Power Configuration
               | ADC_CFG1_ADIV(3)       // Clock Divide Select: (Input Clock)/8
               | ADC_CFG1_ADLSMP_MASK   // Long Sample Time
               | ADC_CFG1_MODE(3)       // (16)bits Resolution
               | ADC_CFG1_ADICLK(1);    // Input Clock: (Bus Clock)/2

    ADC0->CFG2 = ADC_CFG2_MUXSEL_MASK   // ADxxb channels are selected
               | ADC_CFG2_ADACKEN_MASK  // Asynchronous Clock Output Enable
               | ADC_CFG2_ADHSC_MASK    // High-Speed Configuration
               | ADC_CFG2_ADLSTS(0);    // Long Sample Time Select

    ADC0->SC2 = ADC_SC2_REFSEL(0);      // Default Voltage Reference

    ADC0->SC3 = ADC_SC3_AVGE_MASK       // Hardware Average Enable
                | ADC_SC3_AVGS(0);        // 4 Samples Averaged

    // FAST analog input
    ADC0->CFG1 =
                ADC_CFG1_MODE(3)       // (16)bits Resolution
               | ADC_CFG1_ADLSMP_MASK   // Long Sample Time
               | ADC_CFG1_ADICLK(0);    // Input Clock: (Bus Clock)

    ADC0->CFG2 = ADC_CFG2_MUXSEL_MASK   // ADxxb channels are selected
               | ADC_CFG2_ADACKEN_MASK  // Asynchronous Clock Output Enable
               | ADC_CFG2_ADHSC_MASK    // High-Speed Configuration
               | ADC_CFG2_ADLSTS(0);    // longest "long" Sample Time Select

//             | ADC_CFG2_ADLSTS(3);    // shortest "long" Sample Time Select

    ADC0->SC2 = ADC_SC2_REFSEL(0);      // Default Voltage Reference
    ADC0->SC3 = 0;        // No hardware averaging

    // set PORTB pin 0 to high drive here
    PORTB->PCR[0]  |= PORT_PCR_DSE_MASK;

    since_start.start();
    while(1)
    {
         square_out.output();
         for (int i=0; i
         {
             square_out=1;
             wait_us(1);
             volatile uint16_t rise_read=IN.read_u16();
             square_out=0;
             wait_us(1);
             volatile uint16_t fall_read=IN.read_u16();
        }
        int32_t sum=0;
        for (int i=0;i<COLLECT; i++)
        {
             square_out=1;
             wait_us(1);
             int32_t rise_read=IN.read_u16();
             square_out=0;
             wait_us(1);
             sum += rise_read - IN.read_u16();
        }
        square_out.input(); // hiZ state when not driving pulses

        USB_io.printf("%10d\t%7.5f\n", since_start.read_us(), sum*(Vdd/COLLECT/(1<<16))); // scale output to volts
    }
 }

There is still a lot that needs to be done to make this a finished project, but I’ve convinced myself that it is doable as freshman design project, which is all I really needed to do.

Still more on automatic measurement of conductivity of saline solution

In More on automatic measurement of conductivity of saline solution, I suggested using a simple voltage divider and a microcontroller to make conductivity measurements with polarizable electrodes:

Simplified circuit for conductivity tester.

Simplified circuit for conductivity tester.

I found that putting in a 100kHz square wave worked well:

At 100kHz, both the voltage and current waveforms look like pretty good square waves.

At 100kHz, both the voltage waveforms (input and output) look like pretty good square waves.

I have not yet figured out a good way on the KL25Z to provide the 100kHz signal, sample the outputs at fixed points, and communicate the result out the USB port.  Using PWM for the output was handy for just generating the output (once I fixed mbed’s off-by-one bug in their pwmout_api.c file), but that makes connecting up the analog reads more difficult.  I think that I may be better off not using PWM, but using a timer interrupt to read the analog value, change the output, and do the subtraction.  It would be fairly easy to arrange that (though I’ll probably have to figure out all the registers for the sample-and-hold and the analog-to-digital converter, as the mbed AnalogIn routine is unlikely to have the settings I want to use).   The hard part remains the interface to the host computer, as mbed does not include a simple serial interface and serial monitor like the Arduino IDE. [Correction 2013 Dec 25: my son points out that the mbed development kit has a perfectly usable serial USB interface—I had overlooked the inheritance from “Stream”, which has all the functions I thought were missing. I should be able to use the Arduino serial monitor with the Freedom KL25Z board, as long as the serial interface is set up right.]

Because I’m more familiar with the Arduino environment, and because I already have Arduino Data Logger code for the host end of the interface, I started by making a simple loop that toggles the output and reads the value after each change in output.  After repeating this several times (40 or 100), I take the last difference as the output and report that to the data logger.  I couldn’t get the frequency up where I really want it (100kHz), because the Arduino analog-to-digital converter is slow, but I was able to run at about 4kHz, which would be adequate.

Because there needs to be time for the serial communication, I did bursts of pulses with pauses between bursts.  The bursts were alternating as fast as the analog inputs were read for a fixed number of cycles, and the start of the bursts was controlled by the Arduino data logger software. Although the ends of the bursts looked the same on the oscilloscope, with the same peak-to-peak voltage, I got different readings from the Arduino depending on the spacing between the bursts. I’m not sure what is causing the discrepancy.

A difference at the beginnings of the bursts I would understand as the space between the bursts put a DC voltage across the electrodes which gradually charged them up, so that the first few pulses actually end up going outside the range of the ADC:

The bottom of the grid is 0v, and the first pulse goes up to 5.442v.  The pulses are at about 4kHz, but the bursts start 50msec apart.

The bottom of the grid is 0v, and the first pulse goes up to 5.442v. The pulses are at about 4kHz, but the bursts start 50msec apart.

The differences at the ends of the bursts as I change the spacing between bursts are probably also due to the charging, though I don’t see that clearly on the oscilloscope. I really don’t like the idea of having a DC bias across the electrodes, as we get electrolysis, with hydrogen bubbles forming on the more negative electrode. No oxygen bubbles form, probably because any oxygen released is reacting with the stainless steel to form metal oxides. If I increase the voltage and current, I get a lot of hydrogen bubbles on the negative electrode, some rusty looking precipitate coming off the positive electrode (probably an iron oxide), and a white coating building up on the positive electrode (probably a chromium oxide).

By putting a 4.7µF capacitor between the Arduino output and the electrode, I can reduce DC bias on the electrodes and get a more consistent signal from the Arduino, almost independent of the spacing between the bursts:

By using a 25msec spacing between the beginnings of bursts, I can get both the end of the burst and the beginning of the burst on the oscilloscope at once.   Using a 4.7µF capacitor between the square wave output and the electrodes results in sharp peaks across the resistor, but a more consistent reading from the Arduino ADC.

By using a 25msec spacing between the beginnings of bursts, I can get both the end of the burst and the beginning of the burst on the oscilloscope at once.
Using a 4.7µF capacitor between the square wave output and the electrodes results in sharp peaks across the resistor, but a more consistent reading from the Arduino ADC.

The voltage across the electrodes still does not average to 0v, as the pair of resistors provides a bias voltage halfway between the rails, but the pulse does not really swing rail to rail, but from 0.28v to 4.28v.  I think that the low-pass filter for setting the bias voltage that I suggested in More on automatic measurement of conductivity of saline solution may be a good idea after all, to make sure that there is no residual DC bias.

I can use the differential inputs of the Bitscope DP01 to look at the voltage across the electrodes and across the resistor to get voltage and current traces for the electrodes:

The central horizontal line is 0V for both traces here.  The green trace is the voltage at the undriven electrode (@ 2v/division) and the yellow trace is the voltage between the electrodes (@0.2v/division).

The central horizontal line is 0V for both traces here. The green trace is the voltage at the undriven electrode (@ 2v/division) and so corresponds to the current, and the yellow trace is the voltage between the electrodes (@0.2v/division).

Note that the voltage on the undriven electrode does run a little below 0V, outside the range of the Arduino ADC.  The voltage ratio of 0.248v/4.16v, together with the 100Ω Thévenin equivalent resistance results in a 5.47Ω resistance between the electrodes.  (Note: this is no longer a 1M NaCl solution—there has been evaporation, plus contamination from iron oxides, and the electrodes are not covered to the depth defined by the plastic spacer.)

I don’t know whether the conductivity meter is a good project for the freshman design seminar or not—I don’t expect the students to have the circuit skills or the programming skills to be able to do a design like this without a lot of coaching.  Even figuring out that they need to eliminate DC bias to eliminate electrolysis may be too much for them, though I do expect all to have had at least high-school chemistry. It is probably worth doing a demo of putting a large current through electrodes in salt solution, to show both the hydrogen bubbles and the formation of the oxides.  I could probably coach freshmen through the design, if they were interested in doing it, so I’ll leave it on the feasible list.

The square-wave analysis is not really suitable for a circuits course, so I think I’ll stick with sine-wave excitation for that course.

2013 December 21

More on automatic measurement of conductivity of saline solution

I decided to look a little more at automatic measurement of conductivity of a saline solution again, continuing the ideas in Towards automatic measurement of conductivity of a saline solution.

The idea is a simple one: make a voltage divider consisting of the pair of electrodes on one leg and a known resistor on the other, put a square wave in, and measure the output voltage.  By making the measurements synchronous with the square wave, we should be able to eliminate the DC component and look just at the high-frequency response. The output should be proportional to \frac{R}{Z+R}, where Z is the impedance of the electrodes (which varies with frequency) and R is the known resistance.

See Better measurement of conductivity of saline solution for the variation in the magnitude of impedance with frequency for polarizable stainless steel electrodes and Making Ag/AgCl electrodes for non-polarizable silver/silver-chloride electrodes. For polarizable electrodes, we probably want to stay over 10kHz, which shouldn’t be a problem with the Freedom KL25Z board, but could be pushing the Arduino A/D converter.  I think that the Arduino scales down the 16MHz clock to 125kHz for the A/D conversion (it needs to be ≤200kHz), and at the auto-triggered rate of 13.5 clock cycles per sample, that is only 9,259Hz sampling rate.  With single samples, at 25 clocks per sample, we’re down to 5kHz sampling. (The Freedom KL25Z board looks like it could do over 300kHz at 16 bits.)

I suspect that the thickness of the oxide coating on 316L stainless steel varies a lot with conditions (thicker in oxidizing conditions, thinner in reducing conditions), so stainless steel may not be the best choice of material for a conductivity probe, even though it will contaminate solutions less than many other probe materials.  One commercial sensor (Omega CDE-45P) uses titanium for its electrodes “for greater chemical resistance.”

The commercial sensor uses a 4-electrode design, with two sense electrodes close to, but not touching, the drive electrodes.  They claim that this allows maintaining a constant drive level despite fouling of the electrodes.  I can see how that would be useful in a continuous monitoring application, where the characteristics of the electrodes would change over time.  I’m curious how well it works—something else for me to play with.

To avoid electrolysis at the electrodes, we want there to be no DC component to the drive signal, so the sense electrode should be biased to half way between the extremes of the square wave.  The crudest way to do this would be just to put a voltage divider between the power rails, and use the Thévenin equivalent resistance as R. A much fancier way would be do a low-pass filter of the drive signal and use that to bias a patch-clamp amplifier—something like this:

Possible circuit for the front end of a 2-electrode conductivity tester. U2 is a unity gain amplifier that monitors the drive electrode and feeds the RC low-pass filter, which sets the DC bias voltage for the sense electrode through the current-to-voltage amplifier made with U1.

Possible circuit for the front end of a 2-electrode conductivity tester. U2 is a unity gain amplifier that monitors the drive electrode and feeds the RC low-pass filter, which sets the DC bias voltage for the sense electrode through the current-to-voltage amplifier made with U1.

A design like this should work so long as the drive signal remains between the rails of the op amp power supply. The output is proportional to the current through the electrodes, which is I=\pm \frac{V_{hi}-V_{lo}}{2(Z+R_{3})}, so V_{out} = \pm \frac{V_{hi}-V_{lo}}{2}\frac{R_2}{Z+R_{3}} + \frac{V_{hi}+V_{lo}}{2}.

The resistor R3 keeps the current small enough that the output of the of the current-to-voltage converter stays between the rails.  Both R2 and R3 could be increased to reduce the current, as long as R3 ≥ R2.  If R2 is set too low, then the signal for low-conductivity solutions will be too small to measure reliably, while if  R2 and R3 are set too high, then high-conductivity solutions will be difficult to measure, being too close to the full-scale range of the analog-to-digital converter.

Of course, if we know the high and low voltages of the input signal, we can get rid of the low-pass filter and amplifiers:

Simplified circuit for conductivity tester.

Simplified circuit for conductivity tester.

With the simplified circuit, the two resistors form a voltage divider, so that we have a Thévenin equivalent that is half the resistance to the voltage midway between the high and low voltage. This makes the output $V_{out} = \pm \frac{V_{hi}-V_{lo}}{2}\frac{R/2}{Z+R/2}+ \frac{V_{hi}+V_{lo}}{2}$.  This circuit is simple enough for the freshman design seminar, so I should check out how well it works.

The KL25 chip has regular-drive and high-drive digital outputs, rated for 5mA and 18mA respectively (with a 3.3v power supply).  PTB0, PTB1, PTD6, and PTD7 I/O have both high drive and normal drive capability selected by the associated PTx_PCRn[DSE] control bit. All other GPIOs are normal drive only.

I started out playing with the PWM outputs on the KL25Z board, because the interface for making nice square waves is so simple.  I found that the MBED pwmout_api.c file has some serious errors in it: the periods are off by 1 count and the default prescaling they chose does not allow integer numbers of µsec.  I fixed the off-by-one errors on period and changed the prescaling to be 48MHz/16=3MHz for the timer.  This allows integer arithmetic for setting in µsec and msec, but limits the period to 216/3 µsec = 21845µsec (instead of ~87.381 msec).

I have not yet figured out a good way to do analog-to-digital sampling triggered by both edges of the PWM output. I may end up using interrupts on the rising edge (timer interrupt, setting TOIE=1) and on the falling edge (channel interrupt, setting CHnIE=1).

Before that, I should probably make up some salt solutions and check that the waveforms look ok with the electrodes.  Let’s see—1M NaCl would be 58.44 g/l, or 29.22 g for 500ml, and 0.1M would be 2.922 g for 500ml. I made some 1M NaCl (probably a little lower concentration, since I used commercial table salt, which generally has a few other thing mixed in with the NaCl).

I set up the simpler 2-resistor circuit, and wrote a little program for the Freedom KL25Z board that produced square waves, using the touch sensor to get 2 virtual buttons to increase or decrease the frequency.  I examined the waveforms with the Bitscope USB oscilloscope (using the differential probe, though that wasn’t really necessary here).

At 1kHz (1000µs period), the input square wave  (yellow) shows a little rounding and the output across the resistor (green) shows a bit of sharpening, both due to the capacitance of the polarizable electrodes. (click to embiggen)

At 1kHz (1000µs period), the input square wave (yellow) shows a little rounding and the output across the resistor (green) shows a bit of sharpening, both due to the capacitance of the polarizable electrodes. (click to embiggen)

At 100kHz, both the voltage and current waveforms look like pretty good square waves.

At 100kHz, both the voltage and current waveforms look like pretty good square waves.

I made a table of measurements using the Bitscope cursors (a rather tedious method, but my son hasn’t made the Data Logger code for the Freedom  KL25Z yet, and I’m too lazy to do it myself).  The Bitscope showed considerable distortion of the 500kHz signal (not evident on a higher-bandwidth scope, so probabably due to the limitations of the BitScope amplifier—see my post on the Bitscope differential probe).

period µs freq kHz Vin
Vout
2 500 2.74–2.948 2.52–2.633
5 200 2.812 2.56–2.605
10 100 2.763 2.54–2.60
20 50 2.763 2.50–2.60
50 20 2.773 2.48–2.624
100 10 2.763 2.42–2.674
200 5 2.77 2.36–2.732
500 2 2.72–2.763 2.24–2.849
1000 1 2.70–2.782 2.02–3.00
2000 0.500 2.62–2.854 1.78–3.211
5000 0.200 2.56–2.941 1.34–3.642
10000 0.100 2.48–3.006 1.06–3.793
20000 0.050 2.40–3.072 0.74–4.069

Once the period gets longer than about 200µs, exactly when you look at the signals begins to really matter, as the voltage and current waveforms deviate more from a square wave. Below are the waveforms at 50Hz, which is as slow as I measured:

50Hz square wave input (yellow) does not look very square on the oscilloscope.  The green output waveform (which is the current through the electrodes) shows the effect of the capacitance of the polarizable electrodes.

50Hz square wave input (yellow) does not look very square on the oscilloscope. The green output waveform (which is the current through the electrodes) shows the effect of the capacitance of the polarizable electrodes. (click to embiggen)

Using the results for the 100kHz square wave, I get that the resistance of the 1M NaCL solution with the 316L stainless steel electrodes is about 4.69Ω, similar to the 5.5Ω I estimated from sine-wave measurements last August (with a different 1M stock solution, not as carefully made). I can’t get this into any sort of standard unit, except by calibration with known reference solutions, since the value measured depends on the geometry of hte electrodes.  The same is true of commercial conductivity testers also.

2013 September 22

Electronic sensors for water quality

Filed under: freshman design seminar — gasstationwithoutpumps @ 16:19
Tags: , , , , ,

I just read an article in AEE – Advances in Engineering Education—A Journal of Engineering Education Applications, vol. 3 #2, 2012, SENSE IT: Teaching STEM principles to middle and high school students through the design, construction and deployment of water quality sensors:

This paper describes the structure and impact of an NSF-funded ITEST project designed to enrich science, technology, engineering, and mathematics (STEM) education using educational modules that teach students to construct, program, and test a series of sensors used to monitor water quality.

The four sensors that they used for the middle school and high school students (thermistor for temperature, LED and photoresistor for turbidity, pressure gauge for depth, and electrodes for conductivity) would be suitable for the freshman design seminar. I already use the thermistor lab as the first lab in the circuits course, and I do a more sophisticated version of the conductivity and pressure sensor labs (measuring impedance with polarizable and non-polarizable electrodes and using a pressure sensor that is just a strain gauge, so that they need to build the amplifier for it).  Details of the curriculum and the sensors themselves can be found at http://senseit.org

The application to water quality measurements is reasonable, and for the freshman seminar it might be worth a field trip down to San Lorenzo River or Cowell beach to test out their designs.

We would use Arduinos or KL25Z boards, rather than Lego NXT bricks, and waterproofing their designs could be a part of the engineering. Using a drybox and a connector should not be too difficult. The robotics club has used IP68 Sealcon strain reliefs from www.productsforautomation.com for cables and Buccaneer mini IP68 connectors for disconnectable connections, both successfully. Some parts could be potted in epoxy, like the cameras for the underwater robot.

My main concern is that making the instruments be standalone might be too challenging, but just duplicating what the high school students do might not be challenging enough. I also don’t want to have to teach half the circuits course for the freshman design seminar, so I’d like to keep the necessary electronics to a minimum.

Next Page »