Posts with «conductivity» label

Using KL25Z for measuring salinity

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.

(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.


Filed under: Circuits course, freshman design seminar Tagged: Arduino, bioengineering, circuits, conductivity, electrodes, KL25Z

Using KL25Z for measuring salinity

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.

(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.


Filed under: Circuits course, freshman design seminar Tagged: Arduino, bioengineering, circuits, conductivity, electrodes, KL25Z

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.

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

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 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.

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 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.


Filed under: Circuits course, freshman design seminar Tagged: Arduino, bioengineering, circuits, conductivity, electrodes, KL25Z, voltage divider

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.

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

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 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.

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 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.


Filed under: Circuits course, freshman design seminar Tagged: Arduino, bioengineering, circuits, conductivity, electrodes, KL25Z, voltage divider