here I describe a very simple method to implement a water level sensor.
The idea is to charge a capacitor and discharge it through the liquid itself, if the electrodes are immersed in it.
This can be done with an I/O pin, three external passive components and few firmware instructions.
Referring to schematics in figure 1, a PIC16F818 is used to control a tank for ultrasonic cleaning: set temperature, set timer and water level control, output readout by some leds. Just the water level control circuit is shown, using RB7 I/O as sensor input and outputting a led indication on RB6.
Here the assembly code:
00442 ;======== input / output routines ===========================
00443 ; switch reading (port B)
00444 ; ******** level sensor read (RB7) and led low level output (RB6) ******
00445 ; Time and Temp leds toggle and output (port B)
00446 ; port A output
00447 ; switch debounce routine
0086 00449 read_switches
.................... omissis ..............................................
00471 ;************ Reading of water level
0096 3014 00472 movlw 20 ; init timeoutcounter
0097 00CA 00473 movwf CNT2
0098 3000 00474 movlw B_TRIS_lout ; set RB0,..,RB7 as outputs (not affecting Z flag)
0099 1683 00475 bsf STATUS,RP0 ; bank 1
009A 0086 00476 movwf TRISB
009B 1283 00477 bcf STATUS,RP0 ; bank 0
009C 1786 00478 bsf PORTB,7 ; charge level capacitor
009D 0BCA 00479 decfsz CNT2,f ; charge timeout RB7: 20x3=60 Tcyc
009E 289D 00480 goto $-1
009F 3080 00481 movlw B_TRIS_levrd ; set RB0,..,RB6 as outputs (not affecting Z flag)
00A0 1683 00482 bsf STATUS,RP0 ; bank 1
00A1 0086 00483 movwf TRISB
00A2 1781 00484 bsf OPTION_REG,NOT_RBPU ; disable pull-ups
00A3 1283 00485 bcf STATUS,RP0 ; bank 0
00A4 0806 00486 movf PORTB,w ; lettura port B reading
00A5 3980 00487 andlw h'80' ; mask bit 7
00A6 1903 00488 btfsc STATUS,Z ; if RB7 = 1 then continue
00A7 28B0 00489 goto levshort ; else if RB7 = 0 then skip to RB6=1 (capacitor discharged by a short circuit)
00A8 3064 00490 movlw 100 ; RB7 = 1 wait for capacitor discharge
00A9 00CA 00491 movwf CNT2 ; init timeout counter
00AA 0BCA 00492 decfsz CNT2,f ; timeout to read RB7: 100x3=300 Tcyc
00AB 28AA 00493 goto $-1
00AC 0806 00494 movf PORTB,w ; port B reading
00AD 3980 00495 andlw h'80' ; mask bit 7
00AE 1306 00496 bcf PORTB,6 ; preset RB6=0
00AF 1D03 00497 btfss STATUS,Z ; if RB7 = 0 then skip with RB6=0 (capacitor discharged by water)
00B0 00498 levshort
00B0 1706 00499 bsf PORTB,6 ; else if RB7 = 1 then set RB6 (capacitor charged: no water)
00B1 1386 00500 bcf PORTB,7 ; keep output RB7=0 (read-modify-write instruction)
00B2 3000 00503 movlw B_TRIS_lout ; set RB0,..RB3 as outputs (not affecting Z flag)
00B3 1683 00504 bsf STATUS,RP0 ; bank 1
00B4 0086 00505 movwf TRISB
00B5 1381 00507 bcf OPTION_REG,NOT_RBPU ; enable pull-ups
00B6 1283 00509 bcf STATUS,RP0 ; bank 0
00B7 0008 00510 return
.................... omissis ..............................................
Two words about the phisical implementation of the sensor itself: since the tank was an AISI 315 medical grade stainless steel, it was used as ground reference, while the sensor was a M3 stainless steel screw fixed to the tank wall and insulated by the very same plastic insulators that are commonly used to mount TO220 devices on heatsinks with mica or SilPad. A mounting sketch is shown in figure 2.
Of course, you can shape the sensor in many other ways, depending on your application. This is just an hint.
In figure 3 another way to do the job, with three sensors, the same of figure 2: one is mounted at the bottom of the tank (tank empty) and the remaining two are mounted on the side of the tank, some 2 cm spaced in height (tank full and tank overfull).
The sensors (LEV0, LEV1 and LEV2) are read by RE0, RE1 and RE2 I/Os of PIC16F877.
This time, the hardware is made with two capacitors and one resistor for each sensor input. The capacitors forms three 9:10 voltage dividers, connected to ground through the water resistance. C6, C7 and C8 are connected toghether to RC2 I/O of PIC16F877, that provides to discharge them.
The whole system has been tailored to work with a broad range of water EC (EC = Electro Conductivity, from 10 uS/cm up to 10 mS/cm).
The reading sequence is as follows:
- initially all capacitors are kept discharged by RE0,..,RE2 and RC2 all outputting 0 level;
- RE0,..,RE2 are raised to high level (Vcc) for 6 us;
- RE0,..,RE2 are switched to inputs;
- Common RC2 to C6, C7 and C8 is raised to high level (Vcc) for 2 us, letting capacitors start to be charged through C9, C10 and C11 and water resistance;
- First read of PORT E: RE0,..,RE2 inputs are swapped in POE4,..,POE6 register bits;
Please note: readings should be high level unless a sensor is short circuited or liquid EC is too high (more than 10 mS/cm);
- wait for 2001 us, to allow C6,..,C8 to be charged through C9,..,C11 and water resistance;
- second read of PORT E: RE0,..,RE2 inputs are stored in POE0,..,POE2 register bits. A low level indicates the presence of water, even with very low EC (about 10 uS/cm: R.O. water);
- discharge capacitors, by setting RE0,..,RE2 and RC2 outputs at low level;
- perform other read operations (read RC1 input and store in POE3 register bit);
- debounce inputs, update level indicator leds LevLD0..2 and returns, leaving decisions to other parts of program.
Here the assembly code:
; Some I/O definitions:
; PORTC bit assignement, TRIS I/O direction controls and defines
00027 ; PORTC: COVER input, LCD control lines D/C and R/W,
00028 ; Lev0..3 capacitors charge output, serial communications
00029 ; RC0 = RTS (RS232 control line O),
00030 ; RC1 = COVER (I),
00031 ; RC1 = R/W (LCD control line O),
00032 ; RC2/CCP1 = Lev0..2 capacitors charge output (O),
00033 ; RC2/CCP1 = LevLD0..2, CovLD led disable output (O),
00034 ; RC2/CCP1 = D/C (LCD control line O),
00035 ; RC3/SCL = I2C communication (I),
00036 ; RC4/SDA = I2C communication (I),
00037 ; RC5/SDO = CTS (RS232 control line I),
00038 ; RC6/TX = RS232 communication (O),
00039 ; RC7/RX = RS232 communication (I).
000000B8 00040 C_TRIS_init equ h'B8' ; control direction (PORTC)
000000B8 00041 C_TRIS_i2c equ h'B8' ; data direction for I2C (PORTC)
000000BA 00042 C_TRIS_rdCv equ h'BA' ; read Cover input (PORTC)
000000E7 00044 PORTC_init equ h'E7' ; SCL and SDA init mask
00000001 00045 Cover equ 1 ; cover bit
00000002 00046 Cov_led_dis equ 2 ; covLD disable output bit
00000002 00047 Lev_charge equ 2 ; Level capacitors charge bit
00000003 00048 SCL equ 3 ; i2c SCL
00000004 00049 SDA equ 4 ; i2c SDA
00000005 00050 CTS equ 5 ; Clear To Send
00000020 00051 CTS_msk equ h'20' ; Clear To Send read mask
00052 #define Cover_bit PORTC,Cover
00053 #define Cov_led_dis_bit PORTC,Cov_led_dis
00054 #define Lev_charge_bit PORTC,Lev_charge
; PORTE bit assignement, TRIS I/O direction controls and defines
00090 ; PORTE: Level inputsLCD control lines, outputs
00091 ; RE0 = LEV0 (I/O), RE1 = LEV1 (I/O), RE2 = LEV2 (I/O),
00000000 00092 E_TRIS_init equ h'00' ; control direction (PORTE)
00000007 00093 E_TRIS_levr equ h'07' ; read levels direction (PORTE)
00075 ; inputs reading (Cover bit and level sensors Lev0, Lev1, Lev2) *
108D 00077 rd_inputs ; level sensor and cover bit inputs reading
108D 0858 00078 movf POE,w ; debounce
108E 00D7 00079 movwf OLD_POE
108F 30FF 00080 movlw h'ff'
1090 0089 00081 movwf PORTE ; 6uS: LEV0, LEV1, LEV2 output high
1091 3009 00082 movlw PORTE
1092 0084 00083 movwf FSR
1093 3007 00084 movlw E_TRIS_levr ; set RE0, RE1, RE2 inputs
1094 1507 00085 bsf Lev_charge_bit ; 2 uS charge input capacitors
1095 1683 00086 bsf STATUS,RP0 ; bank 1
1096 0089 00087 movwf TRISE
1097 0E00 00088 swapf INDF,w ; 1uS: read PORTE
1098 1283 00089 bcf STATUS,RP0 ; bank 0
1099 00D8 00090 movwf POE ; copy: POE4 <- RE0, POE5 <- RE1, POE6 <- RE2
109A 30FA 00091 movlw 250 ; delay 250x8 + 1 = 2001uS
109B 00C5 00092 movwf cont
109C 0000 00093 nop
109D 0000 00094 nop
109E 0000 00095 nop
109F 0000 00096 nop
10A0 0000 00097 nop
10A1 0BC5 00098 decfsz cont,f
10A2 289C 00099 goto $-6
10A3 0809 00100 movf PORTE,w ; read PORTE
10A4 04D8 00101 iorwf POE,f ; copy: POE0 <- RE0, POE1 <- RE1, POE2 <- RE2
10A5 3000 00102 movlw E_TRIS_init ; set RE0, RE1, RE2 outputs (high)
10A6 0189 00103 clrf PORTE ; LEV0, LEV1, LEV2 output low
10A7 1107 00104 bcf Lev_charge_bit ; discharge input capacitors
10A8 1683 00105 bsf STATUS,RP0 ; bank 1
10A9 0089 00106 movwf TRISE
10AA 30BA 00107 movlw C_TRIS_rdCv ; set RC2 input
10AB 0087 00108 movwf TRISC
10AC 1283 00109 bcf STATUS,RP0 ; bank 0
10AD 1887 00110 btfsc Cover_bit ; read Cover bit in POE3
10AE 15D8 00111 bsf POE,3
10AF 30B8 00112 movlw C_TRIS_i2c ; set RC2 output
10B0 1683 00113 bsf STATUS,RP0 ; bank 1
10B1 0087 00114 movwf TRISC
10B2 1283 00115 bcf STATUS,RP0 ; bank 0
10B3 0857 00116 movf OLD_POE,w ; debounce: update levels only if POE = OLD_POE
10B4 0658 00117 xorwf POE,w
10B5 1D03 00118 btfss STATUS,Z
10B6 0008 00119 return
10B7 085A 00120 movf Levls,w ; debounce
10B8 00D9 00121 movwf OLD_Levls
10B9 307F 00122 movlw h'7f' ; invert levels
10BA 0658 00123 xorwf POE,w
10BB 00DA 00124 movwf Levls
10BC 0E5A 00125 swapf Levls,w ; update level indicator leds LevLD0..2
10BD 065A 00126 xorwf Levls,w
10BE 39F0 00127 andlw h'f0'
10BF 00D5 00128 movwf POB
10C0 0008 00129 return
Advantages of this system:
- extreme simplicity in making sensors,
- small size of sensors,
- very simple hardware,
- very simple firmware.
Disadvantages of this system:
- electric circuit is connected to water, in particular water is at ground potential: this could impair other sensor readout, such as pH sensors,
- the same could concern electric safety issues.
Some other further possible developements:
By measuring the time of discharge or by convert the input voltage (into an analog input with an embedded ADC) after a given discharge time, one could think to perform:
- EC readout and measurement, but beware to water polarization phenomena: usually this measure is performed using an AC signal,
- continous level monitoring: making sensor made of two stainless steel bars, from bottom to top of the tank, one can imagine to have a sort of potentiometer where the cursor is water level. But the readout is also EC dependent.
This is just an hint, a starting point, that everyone can elaborate and adapt to his own application.
The two examples are part of real devices that were in production in the firm I was worrking in at that time.