Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

infrared Liquid level detector-SharpGP2D12

Status
Not open for further replies.
The ping pong balls idea is very clever.

I ruled out bubblewrap as it would get wet and maybe stick to the side as the level dropped. Styrofoam would work but tends to break up. It's also a little light.

I've got stuff here that's sold for signs, it's 4mm thick closed cell foam on the inside with thin hard plastic outer surfaces. It's stiff and heavy enough not to get hung up as the level drops but still floats reliably and won't bend or break up like styrofoam might.

But the ping pong balls idea seems best, you can even add them to a closed tank where a flat floater needs a tank with no top.

It still seems disappointing to need a floater when the sensor is ALMOST reliable just sensing the water...
 
I put a level eye over the sensor which i had bolted to the side of the LCD...and it was not 100%level.....I put the level on the table i was using to take readings and it was not level either....(i still have to implement Noggins ADC filter in the code and retest it)

Well im going to try to get the project to work without the floater....
but if push comes to shove hehe then....ill be buying some ping pong balls
 
Last edited:
structures/union lovers

I was advised by Noggin:
// Whenever you read your adc, just call Update_Filter( &adc_filter, adc_value ).
//Wherever you perform a calculation on the ADC value, use adc_filter.output instead.
I added the code in RED Highlight below...I just want to know if im using it in the correct place...

Code:
#include <pic.h>
#include "pic.h"
#include "delay.h"
#include "math.h" 
#include <stdio.h>
#include <stdlib.h>	//

#define FALSE 0
#define TRUE  1
struct filter_type {
   unsigned int accumulator;
   unsigned int output;
   unsigned char shift_value;
   unsigned char initialized;
} ;

struct filter_type adc_filter;

void userMenu(char pos); //  
void FloatToStr(float , char[]); 
void DelayMs(unsigned char);
void lcd_cmd(unsigned char);
void lcd_data(unsigned char); 
void lcd_clear(void);
void lcd_puts(const char[]);
void lcd_goto_L1(void);
void lcd_goto_L2(void);
void lcd_cursor(unsigned char);
void lcd_init(void);
void init(void);
char WaitForInput(char expire);  

unsigned char user_input(void);
void home_screen(void);
void EnterHeight(void);
void EnterScreen(void);
void ShowDigits(unsigned char val);
void calc_distance(void);
void main(void);




unsigned char cm2LCD;
unsigned char posneg;
unsigned char LLHigh, LLLow;
unsigned int LiquidLevel;


      

#define LCD_RS RC0		//LCD RS pin
#define LCD_EN RC1		//LCD EN pin
#define LCD_STROBE()	LCD_EN = 1; asm("nop"); asm("nop"); LCD_EN = 0

unsigned char cm10;		//
unsigned char cm;		//
unsigned int math;		// used for voltage calculations


unsigned char NumDec;
unsigned char NumSep[2];
   
unsigned char i,j,k;

unsigned char height=50;
unsigned char range=10;
unsigned char area;


char input_sw;

  
char mnuPOS;

unsigned char MyVal;
unsigned char MyValLCD[2];
unsigned char MyMaxVal;
unsigned char MyMinVal;
unsigned long bres;		// for bresenham 2-second timer system

unsigned char ;

#define HOME_SW RC2				//HOME switch	
#define INCREASE_SW RC3			//INCREASE switch
#define DECREASE_SW RC4			//DECREASE switch
#define ENTERSETTINGS_SW RA4	//ENTERSETTINGS switch



///////////////////////CONVERT FLOAT TO STRING///////////////////
// This function was taken from the CAVR library. It was modified slightly
// to suit our design.
void FloatToStr(float n, char str[])
{
float scale;
unsigned char d,f;
f=0;i=0;
if (n<0.0) {n=-n; str[f]='-'; f++;}; 
n=n+0.005;
scale=1.0;
while (n>=scale) {scale=scale*10.0; ++i;};
if (i==0) {str[f]='0'; f++;}
else
while (i--)
	  {
      scale=floor(0.5+scale/10.0);
      d=(unsigned char) (n/scale);
      str[f]=d+'0';
      n=n-scale*d;
	  f++;
      };

str[f]='.';
f++;
for (j=0;j<=1;j++) //2 decimal points
      {
      n=n*10.0;
      d=(unsigned char) n;
      str[f]=d+'0';
      n=n-d;
	  f++;
	  };
str[f]='\0';
}
///////////////////END CONVERT FLOAT TO STRING///////////////////

/////////////////////////////DELAY///////////////////////////////
void DelayMs(unsigned char cnt)
{
#if	XTAL_FREQ <= 2MHZ
	do {
		DelayUs(996);
	} while(--cnt);
#endif

#if    XTAL_FREQ > 2MHZ	
	unsigned char	p;
	do {
		p = 4;
		do { 
			DelayUs(250);
		} while(--p);  
	} while(--cnt);
#endif
}

void DelayS(unsigned char cnt)
{
	for (j=0; j<(cnt*10); j++)
		DelayMs(100);
}
///////////////////////////DELAY END/////////////////////////////

//////////////////////////////LCD SETUP//////////////////////////
/* send a command to the LCD */
void lcd_cmd(unsigned char c)
{
	DelayMs(2); //wait for LCD to be ready shorter delay 
	LCD_RS = 0;	 //write instruction
	PORTB = (c & 0xF0); //load upper nibble on LCD data lines
	LCD_STROBE(); //send instruction to LCD
	PORTB = ((c << 4) & 0xF0); //load upper nibble on LCD data lines
	LCD_STROBE(); //send instruction to LCD   	
}

/* send data to the LCD */
void lcd_data(unsigned char c)
{
	DelayMs(2); //wait for LCD to be ready shorter delay 
	PORTB = 0x00;
	LCD_RS = 1; //write data
    PORTB |= (c & 0xF0); //load upper nibble on LCD data lines     
	LCD_STROBE(); //send instruction to LCD
	PORTB &= 0x00; //load upper nibble on LCD data lines
	PORTB |= ( (c << 4) & 0xF0); 
	LCD_STROBE(); //send instruction to LCD
}

/*Clear the LCD*/
void lcd_clear(void)
{
	lcd_cmd(0x01); //command to clear LCD
}

/*write a string of chars to the LCD*/
void lcd_puts(const char s[])
{
	j = -1;
	while(s[++j]!=('\0')) // send characters until null character reached
		lcd_data(s[j]);
}

/*go to beginning of line 1*/
void lcd_goto_L1(void)
{
	lcd_cmd(0b10000000); // command to go to line 1
}

/*go to beginning of line 2*/
void lcd_goto_L2(void)
{
	lcd_cmd(0b11000000); // command to go to line 2
}

/*move cursor "x" positions to the right*/
void lcd_cursor(unsigned char x)
{
	lcd_cmd(((x)&0x7F)|0x80); 
}

/*initialise the LCD - put into 4 bit mode*/
void lcd_init(void)
{
	LCD_RS = 0;	
	LCD_EN = 0;
	DelayMs(20); //wait for LCD startup
	lcd_cmd(0x02);
 
	lcd_cmd(0x28);	// 4-bit mode
	lcd_cmd(0x08);	// display off
	lcd_cmd(0x01);	// clear display
	lcd_cmd(0x0C);	// disp. on, cursor off, cursor blink off
	lcd_cmd(0x06);	// entry mode
	lcd_cmd(0x80);  // initialise DDRAM address to zero
}
//////////////////////////LCD SETUP END//////////////////////////
    

void init(void)
{   
	
	// setup the PIC 16f690
	OSCCON = 0x72;      	// internal osc, 8MHz


	PORTA = 0;
	TRISA = 0b10010010; 	// RA7 high imp, RA3 is serial out, RA4 button input 


	
 	PORTB = 0;          	// PORTB not used
    WPUB = 1;				// PORTB pullups ON		
    RABPU = 0;


    /* Init ADC */
    ADCON0 = 0b10000101;	// bit 7 right justify,analogue channel select bits bits5-2  0001=AN1,ADC ON, RA1 is ADC input
	ADCON1 = 0b00100000;	//bits6-4  fosc/32
    ADON=1;	                // turn on the A2D conversion module

	
	ANSEL=0x02;            //set RA1 as analog input for GP2 sensor
	ANSELH=0x00;

    T1CON = 0b00010001;     // TMR1 is ON, 1:2 prescale, =1MHz
	T2CON = 0b00000101;     // TMR2 is ON, 1:4 prescale, =1MHz



	MyVal = 0; //initializn these variables here
	MyMinVal = 0;
	MyMaxVal = 99;
  
	TRISB=0x00; 
	TRISC=0xFC;
    
	lcd_init(); //call LCD initialisation

}
//EDITED this a bit. now if expire input is set it will leave loop with a 0
//instead of rechecking this will be useful for display your measured data and 
//waiting for user to exit.
char WaitForInput(char expire){
char done;
char temp;
done = 0;
temp = 0;
while(!done){
    if(!ENTERSETTINGS_SW){
		while(ENTERSETTINGS_SW);
        temp = 1;
        done = 0xff;
    }

    if(!HOME_SW){
		while(HOME_SW);
        temp = 2;
        done = 0xff;
    }

    if(!INCREASE_SW){
		while(INCREASE_SW);
        temp = 3;
        done = 0xff;
    }

    if(!DECREASE_SW){
		while(DECREASE_SW);
        temp = 4;
        done = 0xff;
    }
	if(expire == 1) break;
}//end of while
    DelayMs(150);    //debounce
    return temp;
}//




void Init_Filter( struct filter_type *filter, unsigned char shift )
{
   filter->initialized = FALSE;
   filter->shift_value = shift;
}

void Update_Filter( struct filter_type *filter, unsigned int new_value )
{
   if (filter->initialized != TRUE)
   {
      filter->output = new_value;
      filter->accumulator = new_value << filter->shift_value;
      filter->initialized = TRUE;
   }
   else
   {
      filter->accumulator -= filter->output;
      filter->accumulator += new_value;
      filter->output = filter->accumulator >> filter->shift_value;
   }
}







void userMenu(char pos){
    unsigned int delaytime = 100000; // 100ms CHANGE THIS FOR YOUR BELOW DELAY 
	lcd_clear();
	lcd_goto_L1();

	switch(pos){
		case 0:
			lcd_puts("    HEIGHT    ");
			break;		
		case 1:
			lcd_puts("    RANGE     ");
			break;		
		case 2:
			lcd_puts(" SURFACE AREA ");
			break;							//
		case 3: 
			lcd_puts("   MEASURED   ");
			 while(WaitForInput(1) != 2){		//Wait for user to press enter to leave loop

 				// wait for 2 seconds, uses TMR1 free running at 1Mhz
   				while(!TMR1IF)  		// wait for TMR1 overflow
				TMR1IF = 0; 			// clear overflow flag

				bres += 65536;			// add 65536uS to bres value
				if(bres >= delaytime )		// if reached 2 seconds!
	 			{
    				bres -= delaytime ;	// subtract 2 seconds, keep error 
		  	
					// read the ADC voltage RA1 (Sharp GP2 sensor)
					[COLOR="DarkOrchid"][B]//	Whenever you read your adc, just call Update_Filter( &adc_filter, adc_value ).[/B][/COLOR]
					GODONE=1;					// initiate conversion on the channel 0
					while(GODONE) continue;  	// Wait convertion done
					[COLOR="Red"][B]Update_Filter( &adc_filter, adc_value );[/B][/COLOR]
					calc_distance();			// convert ADC value to distance

					lcd_goto_L2();				//Only change line 2
//
					if(posneg == 'p')
						lcd_data('+');
					else
						lcd_data('-');
//
					lcd_data(LLHigh);
					lcd_data(LLLow);
	    			lcd_puts(" [cm] ");			//comment this out if you want
			   } 
		} 
		lcd_goto_L1();
		lcd_puts(" Loading Home ");
		lcd_goto_L2();
		lcd_puts("              ");
		DelayS(1);
		break;
	}

	if(pos == 3) return;
	lcd_goto_L2();
	lcd_puts("Press Up/Down"); //home screen message (line 2)
}

// New Menu System
void EnterHeight(void){
	lcd_clear();
	lcd_goto_L1();
	lcd_puts(" ENTER HEIGHT ");
	lcd_goto_L2();
	lcd_puts("Press Up/Down"); //home screen message (line 2)
}

void EnterScreen(void){
	lcd_clear();
	lcd_goto_L1();
	lcd_puts(" [cm] ");
}

void ShowDigits(unsigned char val){

    MyValLCD[0] = val /10;    //returns the quotient (if temp = 35 the result is 3)
    MyValLCD[1] = val % 10; 	//Returns remainder   (if temp = 35 the result is 5)

	MyValLCD[0] += 0x30;	//to ASCII
	MyValLCD[1] += 0x30;	//to ASCII

	EnterScreen();
	lcd_goto_L2();
	lcd_data(MyValLCD[0]);	//to LCD
	lcd_data(MyValLCD[1]);  //to LCD
}

  
void calc_distance(void)
{
	unsigned int tmp;
unsigned int mathKeep;		// used for voltage calculations backup
	// from the transeiver datasheet the analog voltage is
	// the inverse of distance, so distance can be calculated
	// d = (1 / volts) then just scaled to suit the transeiver

	// load ADC value in 16bit math var

[COLOR="DarkOrchid"][B]//Wherever you perform a calculation on the ADC value, use adc_filter.output instead.[/B][/COLOR]
	
//	math = ADRESH;
   [COLOR="Red"][B] math=adc_filter.output;[/B][/COLOR]
	math = (math * 256);
//	math += ADRESL;
	[COLOR="Red"][B]math +=adc_filter.output;[/B][/COLOR]


	// now invert it; (1 / volts) use (6050 / volts) for scaling
	math = (6050 / math);
	if(math >= 2) math -=2;		// fix linear error (-2)
	if(math > 99) math = 99;	// max limit at 99cm

	//Create a copy of math for more use
	mathKeep = math;
    //LiquidLevel=height-;	

	// convert from 0-99 to 2 decimal digits, 0-99cm
	cm10=0;
	while(math >= 10)
	{
		cm10++;
		math -= 10;
	}
	cm = math; 

	math = mathKeep;	//Now our original data is back and can be used.
	//This will check if negative
	LiquidLevel=height-math;	

//
	posneg = 'p';
        if((LiquidLevel < 0) || (LiquidLevel > 0xFF00)){
		LiquidLevel = -LiquidLevel ;
		posneg = 'n';
	}
//
	//if below zero which will be in the 0xFFFF range i just chose any 0xFFxx number :D
      //LiquidLevel is higher than 09 so spilt the variable LiquidLevel into 2 //
      LLHigh = ( LiquidLevel / 10 ) + '0'; // 
      LLLow = ( LiquidLevel % 10 ) + '0';  // 
      


}
 //
unsigned char user_input(void)		//This will return what we want
{
	char done = 0;
 
	MyVal = 0;			//Start on 0
	while(done == 0){
   	  input_sw = WaitForInput(0);
		
      switch(input_sw){
		case 1:
			done = 0xff; 			//This tells us the user finished entering
			lcd_goto_L1();
			lcd_puts("      OK       "); //home screen message (line 1)
			break;
      	case 3:
			if(MyVal < MyMaxVal)
      			MyVal++;
			EnterScreen();
			ShowDigits(MyVal);
			break;
      	case 4:
			if(MyVal > MyMinVal)
      			MyVal--;
			EnterScreen();
			ShowDigits(MyVal);
        	break;
		default: 
			break;
      }
	  
	}
	DelayMs(250);
	DelayMs(250);
	return MyVal;
}

void home_screen(void){
	mnuPOS = 0;
	lcd_clear();
	lcd_goto_L1();
	lcd_puts("INFRARED LIQUID"); //home screen message (line 1)
	lcd_goto_L2();
	lcd_puts("LEVEL DETECTOR"); //home screen message (line 2)
  
	input_sw = 0;			//Reset the value

	while(input_sw != 1)  	//Wait until enter is pressed
		input_sw = WaitForInput(0);

	userMenu(0);
	DelayMs(2);				//shorter delay 
    height = user_input();	//The HEIGHT var will have the myVal

	userMenu(1);
	DelayMs(2);				//shorter delay 
    range = user_input();	//The HEIGHT var will have the myVal

	userMenu(2);
	DelayMs(2);				//shorter delay 
    area = user_input();	//The HEIGHT var will have the myVal
//
	userMenu(3);
	DelayMs(2);				//shorter delay 
	input_sw = 0;			//Reset the value


}
  
//*********************************************************
/*  Junebug_Serial4.c    RomanBlack.com 19th July 2009.

    uses "zero error 1 second timer"
	system to generate a 2 second interval, then every
	2 seconds it reads an analog voltage from a
	Sharp GP2 distance sensor and converts it to decimal
	distance, then sends that data to a LCD

	Code for MikroC, config handled by MikroC compiler;
	_INTI02_OSC_1H
	_WDT_OFF_2H
	-MCLRE_ON_3H
	_LVP_OFF_4L 
	_PWRT_ON_2L
*/
//*********************************************************

void main(void)
{  
    init();	// initialise I/O ports, LCD
    while(1){
 	home_screen();


        
              
 
   } 
}



Im a bit unsure about:


Code:
[COLOR="DarkOrchid"][B]//Wherever you perform a calculation on the ADC value, use adc_filter.output instead.[/B][/COLOR]
	
//	math = ADRESH;
   [COLOR="Red"][B] math=adc_filter.output;[/B][/COLOR]
	math = (math * 256);
//	math += ADRESL;
	[COLOR="Red"][B]math +=adc_filter.output;[/B][/COLOR]


Can adc_filter.output be broken up into a high & low value then assigned to math?
OR do i assign adc_filter output directly to math as is??
 
Use the old code to format ADRESH and ADRESL into the 16bit var math,
then call Noggin's filter it with math as the second argument, it will average the new_value into the filter accumulator.

Then when you want the filtered value you read the 16bit variable; adc_filter.output
 
Use the old code to format ADRESH and ADRESL into the 16bit var math,
then call Noggin's filter it with math as the second argument, it will average the new_value into the filter accumulator.

Then when you want the filtered value you read the 16bit variable; adc_filter.output

Just checking if i followed your instructions correctly(below in RED highlight)???...i made the changes in the calc_distance function below....



Code:
#include <pic.h>
#include "pic.h"
#include "delay.h"
#include "math.h" 
#include <stdio.h>
#include <stdlib.h>	//

#define FALSE 0
#define TRUE  1
struct filter_type {
   unsigned int accumulator;
   unsigned int output;
   unsigned char shift_value;
   unsigned char initialized;
} ;

struct filter_type adc_filter;

void userMenu(char pos); //  
void FloatToStr(float , char[]); 
void DelayMs(unsigned char);
void lcd_cmd(unsigned char);
void lcd_data(unsigned char); 
void lcd_clear(void);
void lcd_puts(const char[]);
void lcd_goto_L1(void);
void lcd_goto_L2(void);
void lcd_cursor(unsigned char);
void lcd_init(void);
void init(void);
char WaitForInput(char expire);  

unsigned char user_input(void);
void home_screen(void);
void EnterHeight(void);
void EnterScreen(void);
void ShowDigits(unsigned char val);
void calc_distance(void);
void main(void);




unsigned char cm2LCD;
unsigned char posneg;
unsigned char LLHigh, LLLow;
unsigned int LiquidLevel;


      

#define LCD_RS RC0		//LCD RS pin
#define LCD_EN RC1		//LCD EN pin
#define LCD_STROBE()	LCD_EN = 1; asm("nop"); asm("nop"); LCD_EN = 0

unsigned char cm10;		//
unsigned char cm;		//
unsigned int math;		// used for voltage calculations


unsigned char NumDec;
unsigned char NumSep[2];
   
unsigned char i,j,k;
//char temp[8];
//[/b]
unsigned char height=50;
unsigned char range;
unsigned char area;
unsigned char SensorPos=10;
//[/b] New Vars
char input_sw;

  
char mnuPOS;

unsigned char MyVal;
unsigned char MyValLCD[2];
unsigned char MyMaxVal;
unsigned char MyMinVal;
unsigned long bres;		// for bresenham 2-second timer system

unsigned char ;

#define HOME_SW RC2				//HOME switch	
#define INCREASE_SW RC3			//INCREASE switch
#define DECREASE_SW RC4			//DECREASE switch
#define ENTERSETTINGS_SW RA4	//ENTERSETTINGS switch



///////////////////////CONVERT FLOAT TO STRING///////////////////
// This function was taken from the CAVR library. It was modified slightly
// to suit our design.
void FloatToStr(float n, char str[])
{
float scale;
unsigned char d,f;
f=0;i=0;
if (n<0.0) {n=-n; str[f]='-'; f++;}; 
n=n+0.005;
scale=1.0;
while (n>=scale) {scale=scale*10.0; ++i;};
if (i==0) {str[f]='0'; f++;}
else
while (i--)
	  {
      scale=floor(0.5+scale/10.0);
      d=(unsigned char) (n/scale);
      str[f]=d+'0';
      n=n-scale*d;
	  f++;
      };

str[f]='.';
f++;
for (j=0;j<=1;j++) //2 decimal points
      {
      n=n*10.0;
      d=(unsigned char) n;
      str[f]=d+'0';
      n=n-d;
	  f++;
	  };
str[f]='\0';
}
///////////////////END CONVERT FLOAT TO STRING///////////////////

/////////////////////////////DELAY///////////////////////////////
void DelayMs(unsigned char cnt)
{
#if	XTAL_FREQ <= 2MHZ
	do {
		DelayUs(996);
	} while(--cnt);
#endif

#if    XTAL_FREQ > 2MHZ	
	unsigned char	p;
	do {
		p = 4;
		do { 
			DelayUs(250);
		} while(--p);  
	} while(--cnt);
#endif
}

void DelayS(unsigned char cnt)
{
	for (j=0; j<(cnt*10); j++)
		DelayMs(100);
}
///////////////////////////DELAY END/////////////////////////////

//////////////////////////////LCD SETUP//////////////////////////
/* send a command to the LCD */
void lcd_cmd(unsigned char c)
{
	DelayMs(2); //wait for LCD to be ready shorter delay 
	LCD_RS = 0;	 //write instruction
	PORTB = (c & 0xF0); //load upper nibble on LCD data lines
	LCD_STROBE(); //send instruction to LCD
	PORTB = ((c << 4) & 0xF0); //load upper nibble on LCD data lines
	LCD_STROBE(); //send instruction to LCD   	
}

/* send data to the LCD */
void lcd_data(unsigned char c)
{
	DelayMs(2); //wait for LCD to be ready shorter delay 
	PORTB = 0x00;
	LCD_RS = 1; //write data
    PORTB |= (c & 0xF0); //load upper nibble on LCD data lines     
	LCD_STROBE(); //send instruction to LCD
	PORTB &= 0x00; //load upper nibble on LCD data lines
	PORTB |= ( (c << 4) & 0xF0); 
	LCD_STROBE(); //send instruction to LCD
}

/*Clear the LCD*/
void lcd_clear(void)
{
	lcd_cmd(0x01); //command to clear LCD
}

/*write a string of chars to the LCD*/
void lcd_puts(const char s[])
{
	j = -1;
	while(s[++j]!=('\0')) // send characters until null character reached
		lcd_data(s[j]);
}

/*go to beginning of line 1*/
void lcd_goto_L1(void)
{
	lcd_cmd(0b10000000); // command to go to line 1
}

/*go to beginning of line 2*/
void lcd_goto_L2(void)
{
	lcd_cmd(0b11000000); // command to go to line 2
}

/*move cursor "x" positions to the right*/
void lcd_cursor(unsigned char x)
{
	lcd_cmd(((x)&0x7F)|0x80); 
}

/*initialise the LCD - put into 4 bit mode*/
void lcd_init(void)
{
	LCD_RS = 0;	
	LCD_EN = 0;
	DelayMs(20); //wait for LCD startup
	lcd_cmd(0x02);
 
	lcd_cmd(0x28);	// 4-bit mode
	lcd_cmd(0x08);	// display off
	lcd_cmd(0x01);	// clear display
	lcd_cmd(0x0C);	// disp. on, cursor off, cursor blink off
	lcd_cmd(0x06);	// entry mode
	lcd_cmd(0x80);  // initialise DDRAM address to zero
}
//////////////////////////LCD SETUP END//////////////////////////
    

void init(void)
{   
	
	// setup the PIC 16f690
	OSCCON = 0x72;      	// internal osc, 8MHz


	PORTA = 0;
	TRISA = 0b10010010; 	// RA7 high imp, RA3 is serial out, RA4 button input 


	
 	PORTB = 0;          	// PORTB not used
    WPUB = 1;				// PORTB pullups ON		
    RABPU = 0;


    /* Init ADC */
    ADCON0 = 0b10000101;	// bit 7 right justify,analogue channel select bits bits5-2  0001=AN1,ADC ON, RA1 is ADC input
	ADCON1 = 0b00100000;	//bits6-4  fosc/32
    ADON=1;	                // turn on the A2D conversion module

	
	ANSEL=0x02;            //set RA1 as analog input for GP2 sensor
	ANSELH=0x00;

    T1CON = 0b00010001;     // TMR1 is ON, 1:2 prescale, =1MHz
	T2CON = 0b00000101;     // TMR2 is ON, 1:4 prescale, =1MHz



	MyVal = 0; //initializn these variables here
	MyMinVal = 0;
	MyMaxVal = 99;
  
	TRISB=0x00; 
	TRISC=0xFC;
    
	lcd_init(); //call LCD initialisation

}
//EDITED this a bit. now if expire input is set it will leave loop with a 0
//instead of rechecking this will be useful for display your measured data and 
//waiting for user to exit.
char WaitForInput(char expire){
char done;
char temp;
done = 0;
temp = 0;
while(!done){
    if(!ENTERSETTINGS_SW){
		while(ENTERSETTINGS_SW);
        temp = 1;
        done = 0xff;
    }

    if(!HOME_SW){
		while(HOME_SW);
        temp = 2;
        done = 0xff;
    }

    if(!INCREASE_SW){
		while(INCREASE_SW);
        temp = 3;
        done = 0xff;
    }

    if(!DECREASE_SW){
		while(DECREASE_SW);
        temp = 4;
        done = 0xff;
    }
	if(expire == 1) break;
}//end of while
    DelayMs(150);    //debounce
    return temp;
}//




void Init_Filter( struct filter_type *filter, unsigned char shift )
{
   filter->initialized = FALSE;
   filter->shift_value = shift;
}

void Update_Filter( struct filter_type *filter, unsigned int new_value )
{
   if (filter->initialized != TRUE)
   {
      filter->output = new_value;
      filter->accumulator = new_value << filter->shift_value;
      filter->initialized = TRUE;
   }
   else
   {
      filter->accumulator -= filter->output;
      filter->accumulator += new_value;
      filter->output = filter->accumulator >> filter->shift_value;
   }
}







void userMenu(char pos){
    unsigned int delaytime = 100000; // 100ms CHANGE THIS FOR YOUR BELOW DELAY 
	lcd_clear();
	lcd_goto_L1();

	switch(pos){
		case 0:
			lcd_puts("    HEIGHT    ");
			break;		
		case 1:
			lcd_puts("    RANGE     ");
			break;		
		case 2:
			lcd_puts(" SURFACE AREA ");
			break;							//
		case 3: 
			lcd_puts("   MEASURED   ");
			 while(WaitForInput(1) != 2){		//Wait for user to press enter to leave loop

 				// wait for 2 seconds, uses TMR1 free running at 1Mhz
   				while(!TMR1IF)  		// wait for TMR1 overflow
				TMR1IF = 0; 			// clear overflow flag

				bres += 65536;			// add 65536uS to bres value
				if(bres >= delaytime )		// if reached 2 seconds!
	 			{
    				bres -= delaytime ;	// subtract 2 seconds, keep error 
		  	
					// read the ADC voltage RA1 (Sharp GP2 sensor)
					GODONE=1;					// initiate conversion on the channel 0
					while(GODONE) continue;  	// Wait convertion done
					calc_distance();			// convert ADC value to distance

					lcd_goto_L2();				//Only change line 2
//
					if(posneg == 'p')
						lcd_data('+');
					else
						lcd_data('-');
//
					lcd_data(LLHigh);
					lcd_data(LLLow);
	    			lcd_puts(" [cm] ");			//comment this out if you want
			   } 
		} 
		lcd_goto_L1();
		lcd_puts(" Loading Home ");
		lcd_goto_L2();
		lcd_puts("              ");
		DelayS(1);
		break;
	}

	if(pos == 3) return;
	lcd_goto_L2();
	lcd_puts("Press Up/Down"); //home screen message (line 2)
}

// New Menu System
void EnterHeight(void){
	lcd_clear();
	lcd_goto_L1();
	lcd_puts(" ENTER HEIGHT ");
	lcd_goto_L2();
	lcd_puts("Press Up/Down"); //home screen message (line 2)
}

void EnterScreen(void){
	lcd_clear();
	lcd_goto_L1();
	lcd_puts(" [cm] ");
}

void ShowDigits(unsigned char val){

    MyValLCD[0] = val /10;    //returns the quotient (if temp = 35 the result is 3)
    MyValLCD[1] = val % 10; 	//Returns remainder   (if temp = 35 the result is 5)

	MyValLCD[0] += 0x30;	//to ASCII
	MyValLCD[1] += 0x30;	//to ASCII

	EnterScreen();
	lcd_goto_L2();
	lcd_data(MyValLCD[0]);	//to LCD
	lcd_data(MyValLCD[1]);  //to LCD
}

  
void calc_distance(void)
{
	unsigned int tmp;
        unsigned int mathKeep;		// used for voltage calculations backup
	// from the transeiver datasheet the analog voltage is
	// the inverse of distance, so distance can be calculated
	// d = (1 / volts) then just scaled to suit the transeiver

	// load ADC value in 16bit math var
	[B][COLOR="Red"]//Use the old code to format ADRESH and ADRESL into the 16bit var math[/COLOR][/B],
	math = ADRESH;
	math = (math * 256);
	math += ADRESL;

	[B][COLOR="Red"]//then call Noggin's filter it with math as the second argument, 
	//it will average the new_value into the filter accumulator.[/COLOR][/B]
	Update_Filter( &adc_filter, math );

[B][COLOR="Red"]//Then when you want the filtered value you read the 16bit variable; adc_filter.output [/COLOR][/B]
[B][COLOR="DarkOrchid"]from here on...i use adc_filter.output in place of math[/COLOR][/B]
	// now invert it; (1 / volts) use (6050 / volts) for scaling
	adc_filter.output = (6050 / adc_filter.output);
	if(adc_filter.output >= 2) adc_filter.output -=2;		// fix linear error (-2)
	if(adc_filter.output > 99) adc_filter.output = 99;	// max limit at 99cm

	//Create a copy of adc_filter.output for more use
    mathKeep = adc_filter.output;
   

	// convert from 0-99 to 2 decimal digits, 0-99cm
	cm10=0;
	while(adc_filter.output >= 10)
	{
		cm10++;
		adc_filter.output -= 10;
	}
	cm = adc_filter.output;

	adc_filter.output = mathKeep;	//Now our original data is back and can be used.
	
    //This will check if negative
	LiquidLevel=height-adc_filter.output;	

//
	posneg = 'p';
        if((LiquidLevel < 0) || (LiquidLevel > 0xFF00)){
		LiquidLevel = -LiquidLevel ;
		posneg = 'n';
	}
//
	//if below zero which will be in the 0xFFFF range i just chose any 0xFFxx number :D
      //LiquidLevel is higher than 09 so spilt the variable LiquidLevel into 2 //
      LLHigh = ( LiquidLevel / 10 ) + '0'; // 
      LLLow = ( LiquidLevel % 10 ) + '0';  // 
      
 

}
 //
unsigned char user_input(void)		//This will return what we want
{
	char done = 0;
 
	MyVal = 0;			//Start on 0
	while(done == 0){
   	  input_sw = WaitForInput(0);
		
      switch(input_sw){
		case 1:
			done = 0xff; 			//This tells us the user finished entering
			lcd_goto_L1();
			lcd_puts("      OK       "); //home screen message (line 1)
			break;
      	case 3:
			if(MyVal < MyMaxVal)
      			MyVal++;
			EnterScreen();
			ShowDigits(MyVal);
			break;
      	case 4:
			if(MyVal > MyMinVal)
      			MyVal--;
			EnterScreen();
			ShowDigits(MyVal);
        	break;
		default: 
			break;
      }
	  
	}
	DelayMs(250);
	DelayMs(250);
	return MyVal;
}

void home_screen(void){
	mnuPOS = 0;
	lcd_clear();
	lcd_goto_L1();
	lcd_puts("INFRARED LIQUID"); //home screen message (line 1)
	lcd_goto_L2();
	lcd_puts("LEVEL DETECTOR"); //home screen message (line 2)
  
	input_sw = 0;			//Reset the value

	while(input_sw != 1)  	//Wait until enter is pressed
		input_sw = WaitForInput(0);

	userMenu(0);
	DelayMs(2);				//shorter delay 
    height = user_input();	//The HEIGHT var will have the myVal

	userMenu(1);
	DelayMs(2);				//shorter delay 
    range = user_input();	//The HEIGHT var will have the myVal

	userMenu(2);
	DelayMs(2);				//shorter delay 
    area = user_input();	//The HEIGHT var will have the myVal
//
	userMenu(3);
	DelayMs(2);				//shorter delay 
	input_sw = 0;			//Reset the value

//Waits for user to press ENTER to show home screen
/*
"enter height"
call enter/settings (which is now user input function in new code)
height=MyVal

"enter range"
call user_input
range=MyVal

"enter surface area"
call user_input
area=MyVal
*/
}
  
//*********************************************************
/*  Junebug_Serial4.c    RomanBlack.com 19th July 2009.

    uses "zero error 1 second timer"
	system to generate a 2 second interval, then every
	2 seconds it reads an analog voltage from a
	Sharp GP2 distance sensor and converts it to decimal
	distance, then sends that data to a LCD

	Code for MikroC, config handled by MikroC compiler;
	_INTI02_OSC_1H
	_WDT_OFF_2H
	-MCLRE_ON_3H
	_LVP_OFF_4L 
	_PWRT_ON_2L
*/
//*********************************************************

void main(void)
{  
    init();	// initialise I/O ports, LCD
    while(1){
 	home_screen();

 /*// wait for 2 seconds, uses TMR1 free running at 1Mhz
   	while(!TMR1IF)  // wait for TMR1 overflow
	TMR1IF = 0; 	// clear overflow flag

	bres += 65536;			// add 65536uS to bres value
	if(bres >= 2000000)		// if reached 2 seconds!
	 {
    	bres -= 2000000;	// subtract 2 seconds, keep error
		  	
		// read the ADC voltage RA1 (Sharp GP2 sensor)
		GODONE=1;	// initiate conversion on the channel 0
		while(GODONE) continue;  // Wait convertion done
		calc_distance();		// convert ADC value to distance
	//	LiquidLevel=height-;
	//	lcd_clear();                // Using the new menu you can erase or
	//	lcd_data(cm10 + '0');
	//	lcd_data(cm + '0');
	 //   lcd_goto_L2(); 
	   // lcd_puts("[cm] ");			//comment this out if you want
*/
 
      
        
               
 
   } 
}
 
Yeah that looks about right. One problem I can see is that compilers for microcontrollers are aften clumsy handling strunctures and it might be slow (and use more code) to do all the work on adc_filter.output rather than on math. You could check your compiler output to see if that is the case.

I probably would have done;
math = adc_filter.output
then just used the orig code that calculated in math.

but then I wouldn't have used noggin's version of that filter. ;)
 
Cool....this method is ready to test.....:).
Thanx Mr RB....:rolleyes:
I will fill you'll in on the results when i implement it....

Im multitasking, learning for exams.....& trying to get the code working
 
You could check your compiler output to see if that is the case.
i use HI-TECH universal toolsuite
HI-TECH ANSI C Compiler
Clean: Deleting intermediary and output files.
Clean: Deleted file "C:\cFinalDesign\cFilterAv.p1".
Clean: Deleted file "C:\cFinalDesign\cFilterAv.cof".
Clean: Deleted file "C:\cFinalDesign\cFilterAv.hex".
Clean: Deleted file "C:\cFinalDesign\cFilterAv.sym".
Clean: Deleted file "C:\cFinalDesign\cFilterAv.map".
Clean: Deleted file "C:\cFinalDesign\cFilterAv.hxl".
Clean: Deleted file "C:\cFinalDesign\startup.lst".
Clean: Deleted file "C:\cFinalDesign\startup.rlf".
Clean Warning: File "C:\cFinalDesign\doprnt.p1" doesn't exist.
Clean Warning: File "C:\cFinalDesign\doprnt.pre" doesn't exist.
Clean: Deleted file "C:\cFinalDesign\cFilterAv.obj".
Clean: Deleted file "C:\cFinalDesign\cFilterAv.lst".
Clean: Deleted file "C:\cFinalDesign\cFilterAv.rlf".
Clean: Deleted file "C:\cFinalDesign\cFilterAv.sdb".
Clean: Done.
Build C:\cFinalDesign\cFilterAv for device 16F690
Using driver C:\Program Files\HI-TECH Software\PICC\PRO\9.65\bin\picc.exe

Executing: "C:\Program Files\HI-TECH Software\PICC\PRO\9.65\bin\picc.exe" --pass1 C:\cFinalDesign\cFilterAv.c -q --chip=16F690 -P --runtime=default --opt=default -D__DEBUG=1 -g --asmlist "--errformat=Error [%n] %f; %l.%c %s" "--msgformat=Advisory[%n] %s" "--warnformat=Warning [%n] %f; %l.%c %s"
Executing: "C:\Program Files\HI-TECH Software\PICC\PRO\9.65\bin\picc.exe" -ocFilterAv.cof -mcFilterAv.map --summary=default --output=default cFilterAv.p1 --chip=16F690 -P --runtime=default --opt=default -D__DEBUG=1 -g --asmlist "--errformat=Error [%n] %f; %l.%c %s" "--msgformat=Advisory[%n] %s" "--warnformat=Warning [%n] %f; %l.%c %s"
HI-TECH C PRO for the PIC10/12/16 MCU family (Lite) V9.65
Copyright (C) 1984-2009 HI-TECH SOFTWARE
(1273) Omniscient Code Generation not available in Lite mode (warning)
Warning [765] C:\cFinalDesign\cFilterAv.c; 474. degenerate unsigned comparison

Memory Summary:
Program space used 755h ( 1877) of 1000h words ( 45.8%)
Data space used 3Dh ( 61) of 100h bytes ( 23.8%)
EEPROM space used 0h ( 0) of 100h bytes ( 0.0%)
Configuration bits used 0h ( 0) of 1h word ( 0.0%)
ID Location space used 0h ( 0) of 4h bytes ( 0.0%)

Running this compiler in PRO mode, with Omniscient Code Generation enabled,
produces code which is typically 52% smaller than in Lite mode.
The HI-TECH C PRO compiler output for this code could be 976 words smaller.
See **broken link removed** for more information.

Loaded C:\cFinalDesign\cFilterAv.cof.

********** Build successful! **********
 
OK so i finally tested the "infrared liquid level detector" out with the last code that i posted on this thread....(with the addition of Noggins Filter)

i used a clear glass vessel. NO FLOATER. The sensor was Perpendicular to the surface of the liquid(Used a level Eye to verify this)

Tested 3 different liquids:
1. water,
2. handy andy(thick white liquid),
3. sunlight liquid(thick green liquid)

for all 3 tests....

if i enter the height as 30.....

& starts taking readings whilst varying the level of the liquid...
on LCD: it shows 5, 10 ,30 & it stops at the HEIGHT the user entered.


if i enter the height as 40.....

& sensor starts taking readings whilst varying the level of the liquid......
on LCD: it shows 5, 10 ,30 so on & it stops at the HEIGHT the user entered ie. 40.


The liquid level eventually shows as the height the user entered....How do i fix this in the code???
 
Code:
void Init_Filter( struct filter_type *filter, unsigned char shift )
{
   filter->initialized = FALSE;
   filter->shift_value = shift;
}

void Update_Filter( struct filter_type *filter, unsigned int new_value )
{
   if (filter->initialized != TRUE)
   {
      filter->output = new_value;
      filter->accumulator = new_value << filter->shift_value;
      filter->initialized = TRUE;
   }
   else
   {
      filter->accumulator -= filter->output;
      filter->accumulator += new_value;
      filter->output = filter->accumulator >> filter->shift_value;
   }
}





Code:
void calc_distance(void)
{
	unsigned int tmp;
        unsigned int mathKeep;		// used for voltage calculations backup
	// from the transeiver datasheet the analog voltage is
	// the inverse of distance, so distance can be calculated
	// d = (1 / volts) then just scaled to suit the transeiver

	// load ADC value in 16bit math var
	[COLOR="Red"]//Use the old code to format ADRESH and ADRESL into the 16bit var math,[/COLOR]
	math = ADRESH;
	math = (math * 256);
	math += ADRESL;

	[COLOR="Red"]//then call Noggin's filter it with math as the second argument, 
	//it will average the new_value into the filter accumulator.[/COLOR]
	Update_Filter( &adc_filter, math );
[COLOR="Red"]
//Then when you want the filtered value you read the 16bit variable; adc_filter.output 
from here on...i use adc_filter.output in place of math[/COLOR]
	// now invert it; (1 / volts) use (6050 / volts) for scaling
	adc_filter.output = (6050 / adc_filter.output);
	if(adc_filter.output >= 2) adc_filter.output -=2;		// fix linear error (-2)
	if(adc_filter.output > 99) adc_filter.output = 99;	// max limit at 99cm

	//Create a copy of adc_filter.output for more use
    mathKeep = adc_filter.output;
   

	// convert from 0-99 to 2 decimal digits, 0-99cm
	cm10=0;
	while(adc_filter.output >= 10)
	{
		cm10++;
		adc_filter.output -= 10;
	}
	cm = adc_filter.output;

	adc_filter.output = mathKeep;	//Now our original data is back and can be used.
	
    //This will check if negative
	LiquidLevel=height-adc_filter.output;	

//
	posneg = 'p';
        if((LiquidLevel < 0) || (LiquidLevel > 0xFF00)){
		LiquidLevel = -LiquidLevel ;
		posneg = 'n';
	}
//
	//if below zero which will be in the 0xFFFF range i just chose any 0xFFxx number :D
      //LiquidLevel is higher than 09 so spilt the variable LiquidLevel into 2 //
      LLHigh = ( LiquidLevel / 10 ) + '0'; // 
      LLLow = ( LiquidLevel % 10 ) + '0';  // 
      
 

}
 //



The detector measures levels of the liquid as i vary it, however the final measurement for the liquid AT ANY LEVEL goes to the height the user input & this is wrong...Im stumped... the only changes i made to the code was in the calc_distance function....& the addition of Noggins filter. Anyone knows why this is happening?
 
Just came to SAY THANK U THANK U THANK U 2 ALL that helped me:):):)...........This Wednesday is my Final Oral Exam on the project. Electro tech RULEZ!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ;)
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top