Translate

Monday, November 16, 2015

A precise capacitive water level sensor

By Giovanni Carrera rev. 161115 - http://ardupiclab.blogspot.it/

Introduction
This sensor is particularly suitable for punctual dynamic level measurements, and therefore for wave profiles.
This project is not new, but dates back to the mid-70s, when I studied with Professor Becchi, tested and designed it in the laboratories of Hydraulics of the University of Genoa. We also published the paper "A capacitive wave transducer for hydraulic measurements" [1].

I used these probes for years in towing tanks for the measurements of waves generated by the hull of the ship models. A few years ago I used this probe to measure the waves generated by a catamaran in the Lake Como, as shown in the following picture.
Operating principle
The probe can be represented as a thin insulated conducting wire with a layer of non-stick material such as Teflon, as seen in figure. A metal not isolated rod electrode serves as ground reference.
Because the not pure water is a conductor, especially at high frequencies, the system can be modeled as a cylindrical capacitor, whose capacity Cx is proportional to the part of the probe immersed in the water.
To avoid problems caused by the reversal of the meniscus, in correspondence with the change of the vertical motion of the liquid, it is necessary to use a probe with a very small diameter. For insulating liquids, the wire insulation is no longer essential but the ground electrode must be closer to the probe.
But when it comes to practice, I realize that it is not possible to seal the submerged end of the wire probe. After using special glue, specific for the Teflon, it was found that after some time the water has infiltrated into the probe. Furthermore the wire could not be too thin because it is not maintained rigidly straight-lined.
So I used an archway frame which tended a double wire, U shaped, returned at the submerged end by a small plastic pulley and a thin wire, PTFE insulated, as that utilized for electrical wiring, as shown in the following figure.

For what I remember of the many experiments that I made together with a colleague, I had noticed that by exciting the probe with positive signals, there were still residual charges on the thin film of water on the wire, this phenomenon produces a hysteresis, i.e. a small difference between the ascent and descent of the water on the insulated wire. For these reason a unipolar excitation is not suitable for dynamic measurements, even at low frequencies such as that of the waves.
In addition, also the frequency has great importance. From these experiences I have designed the electronic circuits in order to excite the probe with a bipolar signal at about 400 kHz.

The probe electronics
The probe electronics is mounted close to the end of the wire, the scheme is shown in the following figure.
U1 is a 400 kHz astable oscillator and its output is connected to the input of U2 that is configured as monostable multivibrator. The width of output pulses is proportional to the probe capacity, so the output is a PWM (Pulse Width Modulation) signal. The operational amplifier U3 has the function to smooth the signal with a weak filtering. I used a 4-wire shielded cable to connect it to the rest of the electronics. I used shielded cable with a length up to 30 m without problems.

The R4 resistor value depends on probe capacity, the indicated value (15k) is for a 30 cm probe.
The linearity of this system is very good, in my last calibration, the correlation coefficient was 0.999992 with an error of about 0.5 %.

Components used in the probe circuit
component
description
component
description
R1
26.7 kW ±1% metal film
C1, C2
1 µF,25V tantalum electrolytic capacitor
R2
15 kW ±1% metal film
C3, C4, C7, C8, C11
100 nF, 50V ceramic capacitors
R3
22 kW ±5%
C5
1 nF, 50V ceramic capacitors
R4
15 kW ±1% metal film
C6
22 pF ceramic NPO or mica capacitor
R5
220 W ±5%
C9
330 pF polystyrene or mylar capacitor
R6, R7
100 kW ±1% metal film
C10
10 nF mylar capacitor
U1, U2
Intersil ICM7555 timer IC
U3
LF13741, LM741, TL081,  op amp


This circuit was put in a cylindrical sealed case at the lower end of which is connected the probe wire with a coaxial connector (PL-259 female). On the upper end of tube there is a connector for the cable that connects the transducer to the rest of the instrumentation.
This capacitive transducer is powered by a +/-5 V supply, realized with common type regulators 7805/7905, as well as the main +/- 15V power supply that uses regulators 7815/7915.
The rest of electronic circuit is shown in the following figure.
The transistor Q1 works as analog switch that settles the PWM signal amplitude to a precise value. It is connected to a low-pass active filter (U1), followed by a summing amplifier (U2B), to remove the offset (using Rp2), then a power amplifier (U2A) with sufficient current to drive the galvanometers of the paper recorders that were used in the 70s. The cutoff frequency of the second order Sallen-Key filter is approximately 500 Hz. The output is bipolar +/-10V or less, adjusted by gain potentiometer Rp1. The potentiometers Rp1 and Rp2 are ten turns type for a better adjusting.
Some modification are now required to obtain a signal for a system like Arduino, with an unipolar input 0-5V.
The following images show the arrangement of the probe components.




Bibliography
1)      "A capacitive wave transducer for  hydraulic  measurements",  Ignazio Becchi,  Giovanni Carrera,  Hydraulik und  gewasserkunde,  Technische Universitat, Heft Nr.27 , pag.19 - Munchen 1978.

Tuesday, October 20, 2015

A bipolar analog I/O for Arduino Due

Giovanni Carrera rev. 20/10/15
The aim of this work is to convert the unipolar AD and DA converters of Arduino Due into bipolar with appropriate circuits. Bipolar analog inputs and outputs are required in those cases in which the signals can take on also negative values; they have as reference a more stable zero voltage or ground and not the half of the supply voltage or something like that.
With the use of the proposed circuits, an Arduino Due system can be easily employed in replacing an analog PID type controller, with greater potentiality and flexibility. Acquisition systems, low frequency oscilloscopes or spectrum analyzers and waveform generators are examples of additional applications.

ADC input bipolar

We begin to see how to convert a unipolar analog input into bipolar. A simple voltage divider, such as that illustrated in Figure 1, does this job well.  Using the Ohm’s law we can easily obtain the following formulas:
Assuming that the output current is zero, the current through R1 and R2 will be that shown in the formula (1). In these conditions, the output voltage Vo is that of the formula (2).
Suppose you want an input voltage V2 of ± 3.3V which should correspond to 0 and 3.3 V of output voltage. It can be shown that this statement is satisfied if you put V1 equal to 3.3 V. By the formula (2), in fact, we have that if V2 = -3.3 V, V0 = 0 only if R1 = R2  and in the case where V2 = +3.3 V, V0 = 3.3 V. So that the circuit meets our conditions if we set V1 = + 3.3V = Vref, V2 = Vi (bipolar input) and R1 = R2. In order to get closer to the theoretical model of the divider we must use an operational amplifier with high input impedance and unity gain, like the one shown in Figure 2.
This type of operational amplifier can also work with single power supply up to output voltage ​​close to zero, although starts to saturate to about 1 .5 V below the supply voltage, namely + 3.5V in our case. This non-linearity becomes a merit because we want to limit the input of the ADC. The voltage on input pins with respect to ground, as we can see on CPU datasheet, must be in the range: -0.3V to +4.0 V, otherwise a permanent damage can happen. For additional safety, a resistor of 100 was also added in series to the output, as shown in Figure 7.

DAC output became bipolar
To produce a bipolar signal is used a non-inverting operational amplifier connected in a particular way, as shown in Figure 3, i.e. with an offset Vr on the inverting input. In fact, a non-inverting amplifier has the end of R1 connected to ground and, in the case R1 = R2, has a gain G = 2.
The theory says that we can’t generate negative signals without a negative generator, so this op amp requires also a complementary negative supply.
Applying the theoretical model of the operational amplifiers is obtained the following relation:
Vo = Vi – I*R2  = Vi*(1 + K) – K*Vr
Where K = R2 /R1. Our conditions are as follows: if Vi = 0  than Vo = -Vr = -3.3 V and if Vi = + 3.3V than Vo = Vr = +3.3 V. They are satisfied only if K = 1, that is, with equal resistors that give a gain G = 2.

The Arduino Due DAC
Arduino Due does not have an analog output voltage from 0 V to Vref,  this topic has been widely discussed in my post “How to modify analog output range of Arduino Due”. The circuit of figure 5, with a gain equal to G = 3.3/2.2 = 1.5, reaches the desired output range from 0 to 3.3V.
Anti-aliasing filter
A useful thing is to put to the input a low-pass filter to prevent noise aliasing which may arise if the input signal is not band-limited. In figure 6 we can see the scheme of an active low-pass Sallen-Key filter, second order and unity gain and the formula to calculate its cut-off frequency.

In figure 7 we see the complete circuit scheme, from left to right we can see: the low-pass filter (R1, R2, C1, C2 and U2B), the bipolar to unipolar input circuit (R3, Rp1, R4 and U1A) , the differential amplifier (R8, R9, R10, R11, R6, Rp2, R7 and U1B), the unipolar to bipolar output circuit and the negative power supply (C7, C6 and U3).
The low pass filter, with the values shown in the diagram has a cut-off frequency of approximately 220 Hz. The capacitor C1 = 2*C2 is realized with two parallel capacitors, everyone with the capacity equal to C2.
The trimmer Rp1 must be adjusted to obtain on the U1A output a voltage equal to Vref/2 = 1.65V with zero input. The typical value of Arduino Due Vref is 3.3 V, but should be measured with a precise DVM.
To calibrate the trimmer Rp2, it is necessary to output to the DAC the minimum value, that is zero, and act on Rp2 until bringing to a minimum the output voltage of U1B. These operational, powered with single power supply, fail to give a zero output true, but there are very close (a few mV).
Using battery power, the simpler and more economical solution to generate the negative voltage of -5 V is to use an ICL7660 which is a DC/DC charge pump converter. It has the advantage of requiring only a pair of electrolytic capacitors, but has as a disadvantage that the output voltage is not controlled and is reduced by increasing the current. In our case, the dual op amp LM358, in addition to the advantages already described, have also low power consumption.

The practical realization
In Figure 8  you can see the prototype realized on a breadboard card.

The filters require passive components of good precision, a tolerance of ± 1% is readily available for resistors but the capacitors have tolerances around 10-20%, so they should be selected using a capacitance meter.

The tests

Test of input circuits
In addition to the theory, necessary for the development of a project, it is often required to make even the experimental tests. Static measurements carried out on the prototype, with the purpose of seeing his behaviour at the limits of the input/output range, have given very interesting results.
For test purposes, I wrote a dedicated program (see the software section), which is used also to calibrate the analog output. The supply voltages measured on my system are: Vref = 3.310, V+5 = 5.032 and V-5 = -4.975 volts.
The first tests have been quite disastrous in regard to voltage values below one volts output I saw numbers that varied greatly and randomly. From a search on the Internet, I discovered that the problem was caused by the IDE Arduino (version 1.5.3), which had bugs in their function analogRead () for Arduino Due. I recompiled my program with the latest version 'nightly' and, fortunately, things are back to normal.
Overcome the drawbacks mentioned, the results of the linear regression were very good with a correlation of 0.9999981. With the calibration results, we can convert the number N output from the converter:
Vi = N* 0.001609 – 3.3097 (3)
This is precisely the formula to be used in the program to obtain the input voltage expressed in volts. With it one can easily derive the actual input range that goes from -3.310 V (N = 0) to 3.278 V (N = 4095). To make these calculations I used Excel, but that's okay any spreadsheet or least squares program.

Tests of output circuits
With a Vref = 3.31 V, and the power supply of 5.032 V, I measured a minimum voltage output by the DAC of 0.553 V and a maximum of 2,766 V, values very close to 1/6 and 5/6 of the Vref.
The measures on my Arduino Due of the output circuits formed by the differential amplifier U1B, and the unipolar to bipolar converter U2a also gave very good results with a correlation of 0.9999998. With the calibration results, we can get the number to be sent to the DAC to obtain the output voltage Vo:
N = INT(Vo*623.0613 + 2053.7584) (4)
Obviously the formulas (3) and (4) are valid for my system and are obtained with the calibration measurements.
At the software level we must treat the signals to be sent to the DAC or to be read by the ADC as 12 bits unsigned integers, with a minimum of zero, corresponding to -Vo and a maximum of 212-1 = 4095, corresponding to +Vo. The ideal zero volts will be equal to 2048. In essence, we have to work on integers: from -2048 (-Vo) to +2047 (+ Vo) and add 2048 before sending them to DAC or subtract 2,048 readings of ADC. If we want to be more precise it is necessary to use the formulas (3) and (4) obtained by linear regression of a number of measures that, in my case, ranged from 7 for the DAC  to 10-16 for ADC.

The software
For tests on Arduino Due AD and DA converters, I wrote a simple program that sends to the DAC0 values of N according to the table: {0,512,1024,2048,3072,3584,4095}. They are generated in succession, each time we press the button SSbutton connected to pin 32. At the same time are printed on the LCD display of my system both the number sent to the DAC, both the number read from the ADC on the channel A0. In the absence of the display, with minor changes to the program, these data can be printed on the monitor of the Arduino IDE via serial communication.

Bibliography
  1. "Active Low-Pass Filter Design”, Texas Instruments application Report SLOA049B - September 2002
  2. “Atmel ARM Cortex-M3 Product Family (SAM3)”, Atmel application note 42187A−SAM−10/2013

Wednesday, September 30, 2015

Tiny Accelerometers Acquisition System

By Giovanni Carrera - http://ardupiclab.blogspot.it/

The aim of this project is to realize a small acquisition system of a triaxial accelerometer connected via serial interface to a pc or tablet, using an appropriate adapter as TTL/USB or TTL/Bluetooth.
However, this system can also be used for other sensors with the same output range.

The Circuit
I used the PIC microcontroller 16F688, with minor modifications to the firmware you can use another microcontroller with one UART, three channels ADC and two digital bit.
For the CPU clock I used a 18.432 MHz quartz, the reason is that this frequency is particularly suitable for serial baud rate generation. You can find it easily in a RS232 board for PC.
The following figure shows the schematic of this project.
A quadruple dip switch allows you to set the sample rate and the range of accelerometers. To optimize the input range of the ADC, I used the TL431 regulator as adjustable reference source with a good thermal stability. As indicated in the scheme, the trimmer must be adjusted for a 2.80 output voltage, the same output range of the sensors.  The serial out is TTL 5V level. The following picture shows the arrangement of the components on the board of my prototype.

 The card with the accelerometer module has been removed to better see the mounted components, while the photo below shows the accelerometer module mounted. I used the module DC-SS009 made by Sure Electronics, but you can use an equivalent board. It utilizes the chip MMA7260QT from Freescale Semiconductor.
The J3, 4 pin, connector is used to acquire external analog signals, in this case the accelerometer module must be removed. RS1 is a quadruple sil resistor array. For the voltage divider I used 1% tolerance resistors and a 20 turns trimpot.
For a pc connection I used a TTL to USB adapter, which also powers the system. Are particularly suitable even those contained in the same USB connector, you have to install its driver on the PC.

The software
I set prescaler of Timer#0 to 256 and TMR0= 76, so it overflows every 180 counts. With these settings and with the quartz used, you get an interrupt every 10 ms (f = 100 Hz). This is the base period to generate the sampling periods with appropriate multipliers.
Two dip switches (dip1, dip2) are used to select the sampling period multiplier in order to obtain the frequencies of 5, 10, 50 and 100 Hz. The other two dip switches set the full scale of accelerometers(1.5, 2, 4, 6 g) by acting directly on the chip MMA7260QT and are not controlled by the program.
The serial output is ASCII coded with 38400 baud rate. Every row contains the numbers (0-1023) corresponding to the accelerometers X, Y, Z.  At first, after the reset, the device transmits the selected period in milliseconds .
To get the data in physical quantity (g) it is necessary to make a calibration using the acceleration of gravity, therefore, the conversion may be done on the pc.  I compiled my program  in mikroPascal PRO for PIC (mikroElektronika, www.mikroe.com).
The source and compiled files (hex format) can be downloaded at:


Bibliography
1)      “3 Axis Acceleration Sensor Board User’s Guide”, DC-SS009, Sure Electronics, 2007.

2)      “±1.5g - 6g Three Axis Low-g Micromachined Accelerometer”, MMA7260QT, Freescale Semiconductor, 2007.


Saturday, September 19, 2015

How to use the TFT display 2.2" QVGA with Arduino, Part 2/2.

Arduino applications with ILI9341 library (Part 2)

By Giovanni Carrera
SD wirings.
On the other side there is a 4-pin connector (not welded) for driving the SD card on the module, the following table shows the connections:
TFT board
J4 Pin
function
1
SD Cs
2
SD MOSI
3
SD MISO
4
SD Sck

If you want to use the SD card, the pin SD Cs must be connected to D10,another free pin of Arduino, while the other SPI signals must be connected together with those of the TFT display because in the module they are separate.
In the examples of the library there is a program that loads bitmap images from the SD card, it has been tested successfully, after transferring the files to the SD and correct one of the names in the program. In the sketch was used pin D4 of Arduino for the chip select of SD, but this is already used for resetting the TFT, so you need to change the program and assign the Cs of SD pin D10 Arduino (pin 16 of CPU).

Example#1:  alphanumeric writing  on the TFT

I did not find any documents describing the graphic functions library Seeed ILI9341. Were extremely useful the examples of graphics and text applications provided with the library. From these sketches and the library functions, well enough commented, I have written the following sketch of the test:
/* Text writing program using ILI9341 library
   using size 2 fonts, 20 characters/line are available
   in this case 20 pixel are needed for spacing
   G. Carrera 3 gen 2013
*/

#include <stdint.h>
#include <TFTv2.h>
#include <SPI.h>

void setup()
{
  TFT_BL_ON;      // turn on the TFT backlight
  Tft.TFTinit();  // initialize the TFT library
  //write a string, x=0, y=0, size:2, color: yellow
  Tft.drawString("Prova display TFT",0,0,2,YELLOW);
  Tft.drawString("Misure analogiche:",0,40,2,WHITE);
}

void loop()
{
  // to cancel the previous values, it covers with a black rectangle:
  Tft.fillRectangle(100, 80, 100, 120,BLACK);
  int dato = analogRead(A0);// read the channel #0
  //write an integer, position: x,y, size:3, color: red
  Tft.drawString("Ch0:",10,80,3, RED);
  Tft.drawNumber(dato, 100, 80, 3, GREEN);
  dato = analogRead(A1);// read the channel #1
  Tft.drawString("Ch1:",10,120,3, RED);
  Tft.drawNumber(dato, 100, 120, 3, GREEN);
  dato = analogRead(A2);// read the channel #2
  Tft.drawString("Ch2:",10,160,3, RED);
  Tft.drawNumber(dato, 100, 160, 3, GREEN);
  delay(1000);
}
I have tried to describe in the comments the syntax of used functions. The library seems to support four font sizes. The size 1 is hard to read, using the size 2 there are 20 characters per line and must be spaced at least 20 pixels. So you can create a display with 16 rows x 20 columns. By comparing the list with the previous, we note that the 0,0 is the top left.
Since the data are updated approximately every second, you have to delete the pixels written in the previous loop. So as not to rewrite the permanent written, it was decided to cancel the measures with a filled rectangle (FillRectangle), black, large enough to cover the writing. The writings of the measures are of size 3 with a pitch of 40 pixels.

Example#2:  graphics of analog data samples.
Before writing this program I have studied the graphic functions library. In Appendix I listed the main functions.
You can not even call digital oscilloscope the proposed system, but is a good starting point and can already be useful in many applications. The limits are evident: the relative slowness of Arduino (Uno) and writing on a display interfaced with a low speed serial bus.
Note that you must delete the previous point before drawing the current one. To do this I have created two buffers, one for the measures to be graphed and one for the previously tracks, to be outlined in black. You could also reset all memory or draw a black rectangle. The following figure shows the representation of a sinusoidal signal of about 1 Hz.
/* Program TFT_oscillo
 Using the ILI9341 library
 Graphics the time series of 240 8 bit samples
 G. Carrera 18 gen 2013
 */

#include <stdint.h>
#include <TFTv2.h>
#include <SPI.h>

Unsigned long deltat = 14; //sample period in milliseconds
unsigned int data[240];// data buffer
unsigned int datap[240];// previous values buffer (to be clear)
unsigned long pms = 0;
unsigned int ch0;
unsigned int i = 0;

void setup()
{
  TFT_BL_ON;// TFT backlight on
  Tft.TFTinit();  // TFT library initialize
  // draw string, (x, y), size: 3, color: yellow
  Tft.drawString("GCar - Oscillo",0,0,2,YELLOW);
  delay(2000);
  Tft.drawString("GCar - Oscillo",0,0,2,BLACK);// black to clear
}

void loop()
{
  unsigned long cms = millis();
  // checks if elapsed deltat milliseconds
  if(cms - pms > deltat) {
    pms = cms;// updates pms
    // converts A0
    ch0 = analogRead(A0);
    data[i]= map(ch0, 0, 1023, 0, 319);
    i++;
    if (i > 239){
      //Tft.TFTinit();
      for (int i=0; i <= 239; i++){
        // graphic of data acquired
        Tft.setPixel(i+10, datap[i], BLACK);//clear the previous dot
        Tft.setPixel(i+10, data[i], CYAN);// print new dot
        datap[i]= data[i];// updates previous data
      }
      i = 0;
    } 
  }
}

APPENDIX: library  TFTv2 functions

The following was obtained by analyzing the library files and the data-sheet controller ILI9341. It describes only the main functions.

Default colors: RED, GREEN, BLUE, BLACK, YELLOW, WHITE, CYAN, BRIGHT_RED, GRAY1, Gray2.

Display resolution: 240 × 320, with X = 0 to 239 and Y = 0 to 319. The position 0,0 is the top left. The controller has a graphics memory of 240 × 320 × 18 bits = 172800 bytes. The 18 bit color damage 218 = 262,144 colors, with 6 bits for each basic color (RGB). For increased data transfer rates can be used for 16-bit pixels with 65,536 colors (R: 5-bit G: 6-bit, B: 5-bit). As seen on the type of variable color (INT16U), the library uses 16-bit.

Initialization:
TFT_BL_ON;// Turn on the LED backlight

TFT_BL_OFF; //Turn Off the backlight

Tft.TFTinit (void);// initializes the display

Alphanumeric print functions
Tft.drawChar Print a rough character position pox, Poy, of size size and color fgcolor:
Tft.drawChar (INT8U ascii, INT16U pox, INT16U Poy, INT16U size, INT16U fgcolor);

Tft.drawString Print the string string, the position pox, Poy, of size size and color fgcolor:
Tft.drawString (char * string, INT16U pox, INT16U Poy, INT16U size, INT16U fgcolor);

Tft.drawNumber Print the integer long_num position pox, Poy, of size size and color fgcolor:
Tft.drawNumber (long long_num, INT16U pox, INT16U Poy, INT16U size, INT16U fgcolor);

Tft.drawFloat Print floatNumber number with decimal decimal places, the position pox, Poy, of size size and color fgcolor:
Tft.drawFloat (float floatNumber, INT8U decimal, INT16U pox, INT16U Poy, INT16U size, INT16U fgcolor);

Graphic functions
Tft.drawHorizontalLine draw a horizontal line from the position pox, POY, long length and color color:
Tft.drawHorizontalLine (INT16U pox, INT16U Poy, INT16U length, INT16U color);

Tft.drawVerticalLine draws a vertical line from the position pox, POY, long length and color color:
Tft.drawVerticalLine (INT16U pox, INT16U Poy, INT16U length, INT16U color);

Tft.drawLine draw a horizontal line from the position x0, y0 the position x1, y1, color color:
Tft.drawLine (INT16U x0, y0 INT16U, INT16U x1, y1 INT16U, INT16U color);

Tft.drawRectangle draws a rectangle on the position pox, POY, long length, wide width and color color:
Tft.drawRectangle (INT16U pox, INT16U Poy, INT16U length, width INT16U, INT16U color);

Tft.fillRectangle draws a rectangle filled the position pox, POY, long length, wide width and color color:
Tft.fillRectangle (INT16U pox, INT16U Poy, INT16U length, width INT16U, INT16U color);

Tft.drawCircle trace a circle of center pox, Poy, of radius r color color:
Tft.drawCircle (POX int, int Poy, int r, INT16U color);

Tft.drawTraingle trace a triangle vertices poX1, poY1, poX2, poY2, poX3, poY3 and color color:
Tft.drawTraingle (poX1 int, int poY1, poX2 int, int poY2, poX3 int, int poY3, INT16U color);

Tft.setPixel track a point to position pox, Poy and color color:
Tft.setPixel (INT16U pox, INT16U Poy, INT16U color);

Tft.setXY is positioned to point pox, Poy:
Tft.setXY (INT16U pox, INT16U Poy);