We like to prevent our cat from going out too late at night , so we tend to close our cat flap when it’s getting dark. When we’re not home before it gets dark, or are busy, sometimes we forget to lock the flap and the cat disappears for hours on end leaving us concerned about his well-being. The flap

I decided to automate the flap so that it opens and closes on a timer. It’s not possible to buy something that locks the flap at a specific time, so I used a PIC18F2550 microcontroller and a DS1306 real-time clock module to control the flap. There’s a surplus Nokia 5110 graphic LCD to display the time when required, and three buttons to allow the user (me) to set the clock and open and close times. The PIC, RTC and LCD communicate with SPI.

Power Saving

Since this project is running on batteries (it’s not feasible to connect a wall-wart power supply to the door), the application must be energy efficient. The PIC sleeps for the majority of the time, and is only woken by interrupts when the alarm is activated or a button is pressed. On wake, it turns on power to the LCD display (which is also low-power), gets the current time from the RTC and updates the display. After about 10 seconds of inaction, the display is turned off and the PIC goes back to sleep.

The schematic of the circuit

/images/cat.jpg The majority of the power is used to move the servo motor, so I used an N-FET to only apply power to the servo when it needs to be moved. When this is the case, the FET is turned on by the microcontroller, which then sends the relevant PWM signal to the motor to move it to the correct position and then powers the motor off. This regime allows four AA batteries to power the whole lot for several weeks (so far).

Mechanical

The guts of the insides

I used a small RC servo to move the lock on our existing cat flat, and used a piece of aluminium to link the two parts together. Our cat flap was originally designed to be opened with a magnet on the cat’s collar but the solenoid had burned out and wasn’t working. I used the frame that the solenoid was mounted in to hold the servo. I fastened an M3x30 screw into the back of the lock dial with a small metal plate and embedded it in epoxy. The beauty of this design is that the mechanics are completely enclosed within the existing body of the door. The project box fastened to the door beside the flap reflects that this unit is soldered on veroboard in a very inefficient manner, and also contains 4xAA batteries for power. I’ve managed to design a 5x5cm PCB for manufacture without too much effort, and the use of surface mount parts would probably allow the board to fit into the original cat flap housing too.

The Mechanism

Operating Modes

The flap can operate in three different modes. The first is automatic, where the flap is locked and unlocked on a pre-programmed time schedule. The second is in-only, where the flap will close at a pre-programmed time, but will not reopen – this is useful for ensuring that the cat is in the house so that he acn go to the vets. The final operating mode is manual, where the timer is not used and the flap is opened by pressing the open and close buttons.

Firmware

The code was written in Microchip C with MPLABX, and is shown below. I wrote a module to control the LCD, which is based on the code shown on Sparkfun for these LCDs. The module is available for download on github . I will probably create a module to handle the communications to the RTC, but for now it’s just scattered through the main codebase from when I hacked it together.

#include <p18f2550.h>
#include <delays.h>
#include <timers.h>
#include <spi.h>
#include "glcd.h"
#include <stdio.h>
#include <string.h>
#include <portb.h>
#include <reset.h>
#include <EEP.h>


#pragma config FOSC = INTOSC_HS //internal oscillator
#pragma config WDT = OFF //no watchdog
#pragma config VREGEN = OFF //disable USB regulator
#pragma config LPT1OSC = ON //low power timer 1
#pragma config PLLDIV = 1
#pragma config BOR = OFF
#pragma config PBADEN = OFF
#pragma config LVP = OFF //Single supply ICSP disabled



//Pins Outputs
#define _servoSignal LATAbits.LATA0
#define _servoEnable LATAbits.LATA1
#define _RTCSelect LATAbits.LATA2  
#define _LCDSelect LATAbits.LATA3
#define _LCDReset LATAbits.LATA4
#define _LCDData LATAbits.LATA5
#define _LCDEnable LATCbits.LATC2  //turn on power to the LCD via a transistor

//constants
const char RUNMODE = 0x0;
const char SETOPENTIME = 0x1;
const char SETCLOSETIME = 0x2;
const char SETTIMEMODE = 0x3;
const char SETOPENPOSMODE = 0x4;
const char SETCLOSEPOSMODE = 0x5;

const char DOOROPEN = 0x1;
const char DOORCLOSED = 0x0;
const int SLEEPDELAY = 30; //arbitrarily chosen

const char MODEBUTTON = 1 << 4;
const char SETHBUTTON = 1 << 5;
const char SETMBUTTON = 0x08; //1 << 3;

//Variables
int i, openPos, closePos, sleepCounter, interruptFired;
char doorState; //whether the door is open or closed.
char SPIRecv[16];
char opMode;  //0 for standard running, 1 for setAlm, 2 for setTime, 4 for setPos
typedef struct {
	int hours;
	int minutes;
	int seconds;
} time;

time currentTime, openTime, closeTime;  //Hold the open and close times for the door.

//Prototypes
void moveServo(int position);
void updateDisplay(void);
time getTimeFromRTC(unsigned char);
void setTimeToRTC(time myTime, char location); //1 for alarm,0 for clock
void handleAlarm(void);
void saveSettings(void);
void loadSettings(void);
void handleButton(char button);
int BCDToDecimal(unsigned char bcdByte);
unsigned char  DecimalToBCD(int decimalByte);
void writeToEEPROM(void);
void readFromEEPROM(void);
void setNextAlarm(void);


//Initialise EEPROM *-****************************************
#pragma romdata EEPROMSECT = 0xf00000
rom unsigned char eedata[]={0x47,0x35,0x61,0x45,0x02,0x03};

#pragma code
#pragma interrupt handleInterrupt
/***********************************************************************
 * void handleInterrupt()
 * Test the status of the different interrupt flags, and then set interruptFired
 * to the relevant value for handling elsewhere in the main loop.
 * We're not doing any work here so that things like SPI writes are not messed up.
*/
void handleInterrupt(){
        SPI_Clear_Intr_Status_Bit; //Do this here too.
	if (INTCONbits.RBIF ==1) {
		INTCONbits.RBIF = 0; //Reset the interrupt flag.
                interruptFired=0x1; //A button was pressed
	} else if (INTCON3bits.INT2IF ==1) {
		INTCON3bits.INT2IF =0;
                interruptFired =0x2; //Alarm was fired on INT2
        }
	
	SPI_Clear_Intr_Status_Bit; //Set the SPI flag to 0 to tell the code that SPI writing is complete.

}

/****************************************************************************************
void handleAlarm(char open_or_close)

Process the values of the thrown alarm call. 

NOTE:
This function makes the assumption that Alarm interrupts will only be thrown by the RTC. If 
there's noise or some other issue that causes the PIC to interrupt, you'll get the same behaviour
 as you were expecting from the alarm (i.e. servo state changing)

/****************************************************************************************/
void handleAlarm()
{
	time currentAlarm;
	currentTime = getTimeFromRTC(0x0);
	currentAlarm = getTimeFromRTC(0x07);
	//Check to see whether this is an open or close alarm, and then do the right thing with the door setting. 
	//This should also clear the alarm interrupt flag on the RTC
	if ((currentAlarm.hours == openTime.hours) && (currentAlarm.minutes == openTime.minutes)) {
            //This is an openAlarm;
            doorState = DOOROPEN;
            LCDCharacter('O', 1);
            moveServo(openPos);
	} else if ((currentAlarm.hours == closeTime.hours) && (currentAlarm.minutes == closeTime.minutes)) {
            //this is a closeAlarm
            doorState = DOORCLOSED;
            LCDCharacter('C', 1);
            moveServo(closePos);
	} else {
		LCDCharacter('E', 1);
	}
	setNextAlarm(); //Make sure that the next alarm is properly programmed.
}
/****************************************************************************************
void handleButton(char button)

Process the values of the currently pressed button.
Will call the relevant handlers or update the relevant globals within this function.

/****************************************************************************************/
void handleButton(char button){
char updateRequired; //set to 0x1 if RTC needed, set to 0x2 if Open or Close, 0x6 for position (not implemented)
//time currentAlarm; //This is the alarm that is set in the RTC right now;
	if(button & MODEBUTTON) {
		sleepCounter = 0;  //Make sure that the display doesn't sleep in the next few seconds.
		opMode += 1;
		if(opMode > SETCLOSEPOSMODE) {opMode =0;}
		LCDClear(); //This is not in the best place, but it's the easiest way to ensure that the display shows only text for the current mode.
	} else if (button & SETHBUTTON) {
		sleepCounter = 0;
		if(opMode==SETOPENTIME) {
			openTime.hours++;
			if(openTime.hours>23)openTime.hours=0;
                        updateRequired = updateRequired & 0x2; //Need to write this stuff to EEPROM
		} else if (opMode==SETCLOSETIME) {
			closeTime.hours++;
			if(closeTime.hours>23) closeTime.hours=0;
                        updateRequired = updateRequired & 0x2; //Need to write this stuff to EEPROM
		} else if (opMode == SETTIMEMODE){
			currentTime.hours++;
			if(currentTime.hours>23) currentTime.hours=0;
			setTimeToRTC(currentTime, 0x80);
		} else if (opMode == RUNMODE) {
                    if(!(doorState & DOOROPEN)){
                        moveServo(openPos);
                        doorState = DOOROPEN;
                    }
                } else if (opMode == SETOPENPOSMODE) {
                    openPos++;
                    if(openPos>24) openPos=24;
                    moveServo(openPos);
                } else if (opMode == SETCLOSEPOSMODE) {
                    closePos++;
                    if(closePos>24) closePos=24;
                    moveServo(closePos);
                }
	} else if (button & SETMBUTTON) {
		sleepCounter = 0;
		if(opMode==SETOPENTIME) {
			openTime.minutes++;
			if(openTime.minutes>59)openTime.minutes=0;
			updateRequired = updateRequired & 0x2; //write alarm time to the eeprom
		} else if (opMode==SETCLOSETIME) {
			closeTime.minutes++;
			if(closeTime.minutes>59) closeTime.minutes=0;
			updateRequired = updateRequired & 0x2;
		} else if (opMode == SETTIMEMODE){
			currentTime.minutes++;
			if(currentTime.minutes>59) currentTime.minutes=0;
			setTimeToRTC(currentTime, 0x80);
		}  else if (opMode == RUNMODE) {
                    if(!(doorState & DOORCLOSED)){
                        moveServo(closePos);
                        doorState = DOORCLOSED;
                    }
                }  else if (opMode == SETOPENPOSMODE) {
                    openPos--;
                    if(openPos<1) openPos=1;
                    moveServo(openPos);
                } else if (opMode == SETCLOSEPOSMODE) {
                    closePos--;
                    if(closePos<1) closePos=1;
                    moveServo(closePos);
                }

	}  
	updateDisplay();		


	if (updateRequired & 0x02) {
		writeToEEPROM(); // make sure that the correct alarm times are writen to EEPROM
		//Also need to figure out if the alarm that is set just now is still the correct one.
		setNextAlarm();
	}	
}
/*****************************************************************************
void setNextAlarm()
Work out which alarm (open or close) is next and make sure that it's set into the RTC
Input: None
Output: None - works on data read and written to the RTC and some of the globals that are already washing around...
Logic: If the trap is open (its state is stored in a global, of course), the next alarm should be the close one. else if it's open set the open one. 
	The default fallback is the closeTime alarm.

/*****************************************************************************/
void setNextAlarm(void)
{
	if (doorState == DOORCLOSED) {
		//next alarm needs to be openTime
		setTimeToRTC(openTime, 0x87);// Alarm1 is written to at 0x8b, Alarm 0 at 0x87
	} else {
		//next alarm needs to be closeTime
		setTimeToRTC(closeTime, 0x87);
	}
}
/****************************************************************************
moveServo(position)
Min position value = 1, max = 24
Move the servo to the relevant position by turning on the transistor, setting the PWM and 
allowing the device to move to the relevant position. Servo is then disabled to save power
*****************************************************************************/
void moveServo(int position)
{
	int timer;
        char interrupts;
        interrupts = INTCONbits.GIE;
	_servoEnable = 1;
	for(timer=0; timer<100; timer++)
	{
		_servoSignal = 1; // send timed pulse
		Delay100TCYx(position);
		_servoSignal = 0;
		Delay100TCYx(25-position);
                Delay100TCYx(27);
	}	
	_servoEnable = 0;	
        INTCONbits.GIE = interrupts;
}
/***************************************************************************
 void writeToEEPROM()
Writes the openTime and closeTime globals to EEPROM locations
 * Add openPos and closePos values too so that these are stored over reset
Added to handle writing stuff to EEPROM for saving alarm times and stuff
******************************************************************************/
 void writeToEEPROM(void)
{
 
	Write_b_eep (0x00, (DecimalToBCD(openTime.hours)+0x40));
		Busy_eep ();
	Write_b_eep (0x01, (DecimalToBCD(openTime.minutes)));
		Busy_eep ();
	Write_b_eep (0x02, (DecimalToBCD(closeTime.hours)+0x40));
		Busy_eep ();
	Write_b_eep (0x03, (DecimalToBCD(closeTime.minutes)));
		Busy_eep ();
	Write_b_eep (0x04, openPos);
		Busy_eep ();
        Write_b_eep (0x05, closePos);
		Busy_eep ();
}


/********************************************************************************
void readFromEEPROM(unsigned int addr)
 Reads the alarm times for open and close from EEPROM.

Input: None (reads all EEPROM values associated with these alarms)
Output: None (sets the globals inside the function)

Alarms live in 8 bytes of memory on the DS1306 RTC: 0x09 - 0x0e
They are BCD format in the order Seconds, Mins, Hours, Day, Second1, Min1, Hour1, Day1
The MSB is a mask that tells the RTC whether the entry is to be used for the alarm 
(i.e. if the mins mask is set = 1, every time the RTC mins matches the alm mins, the interrupt will be triggered).
For this application, the openTime and closeTime housr and minutes will be stored - nothing else.
/*********************************************************************************/
void readFromEEPROM(void)
 {
	openTime.hours = BCDToDecimal(Read_b_eep(0x00)-0x40);
		Busy_eep ();
	openTime.minutes = BCDToDecimal(Read_b_eep(0x01));
		Busy_eep ();
	closeTime.hours = BCDToDecimal(Read_b_eep(0x02)-0x40);
		Busy_eep ();
	closeTime.minutes = BCDToDecimal(Read_b_eep(0x03));
		Busy_eep ();
        openPos = Read_b_eep(0x04);
		Busy_eep ();
        closePos = Read_b_eep(0x05);
                Busy_eep();
}

/*************************************************************************************/


/**************************************************************************************
void updateDisplay(void)
Update the LCD display based on the settings current mode
Run mode:
display whether the trap is open or closed, the current time and the
time of the next event.
SETALMMODE:
display the time of the open and close events, and allow user to select one for change
SETTIMEMODE:
Display the current time and allow the clock to be updated to reflect this
SETPOSMODE:
Show open and close positions for servo. Allow value to be changed selected.

GLOBAL INPUTS:
opMode - the current mode of the application
cursor position - the variable that's going to be changed when the increment button is pushed.

***************************************************************************************/
void updateDisplay(void) 
{	

//	char myTime[];	
	char testings[10];
	//_servoEnable = ~_servoEnable;
	if(opMode == RUNMODE){
 		currentTime = getTimeFromRTC(0x0);
		sprintf(testings, (const rom far char *)"%02i:%02i:%02i", currentTime.hours, currentTime.minutes, currentTime.seconds); 
		gotoXY(0,1);	
		LCDString(testings, 8);
                gotoXY(0,2);
                sprintf(testings, "Door %01i", doorState);
                LCDString(testings, 6);
                gotoXY(1,5);
                LCDCharacter('O',0);
                gotoXY(40,5);
                LCDCharacter('M',0);
                gotoXY(75,5);
                LCDCharacter('C', 0);

	} else if(opMode == SETOPENTIME) {
		gotoXY(0,0);
		sprintf(testings, (const rom far char *)"Open At:");
		LCDString( testings , 8);
		sprintf(testings, (const rom far char *)"%02i:%02i", openTime.hours, openTime.minutes); 
		gotoXY(0,1);	
		LCDString(testings,5);
                 gotoXY(1,5);
                LCDCharacter('h',0);
                gotoXY(40,5);
                LCDCharacter('M',0);
                gotoXY(75,5);
                LCDCharacter('m', 0);
	
	} else if(opMode == SETCLOSETIME) {
		gotoXY(0,0);
		sprintf(testings, (const rom far char *)"Close At:");
		LCDString( testings , 9);
		sprintf(testings, (const rom far char *)"%02i:%02i", closeTime.hours, closeTime.minutes); 
		gotoXY(0,1);	
		LCDString(testings,5);
                 gotoXY(1,5);
                LCDCharacter('h',0);
                gotoXY(40,5);
                LCDCharacter('M',0);
                gotoXY(75,5);
                LCDCharacter('m', 0);
	
	} else if(opMode == SETTIMEMODE) {
		gotoXY(0,0);
		sprintf(testings, (const rom far char *)"Set Time:");
		LCDString( testings , 9);
		sprintf(testings, (const rom far char *)"%02i:%02i:%02i", currentTime.hours, currentTime.minutes, currentTime.seconds); 
		gotoXY(0,1);	
		LCDString(testings, 8);
                gotoXY(1,5);
                LCDCharacter('h',0);
                gotoXY(40,5);
                LCDCharacter('M',0);
                gotoXY(75,5);
                LCDCharacter('m', 0);
	
	} else if(opMode == SETOPENPOSMODE) {
		gotoXY(0,0);		
		sprintf(testings, (const rom far char *)"Set Open:");
		LCDString( testings , 9);
		gotoXY(0,1);
		sprintf(testings, (const rom far char *)"Open: %02i", openPos);
		LCDString(testings, 8);
		gotoXY(1,5);
                LCDCharacter('+',0);
                gotoXY(40,5);
                LCDCharacter('M',0);
                gotoXY(75,5);
                LCDCharacter('-', 0);
	} else if(opMode == SETCLOSEPOSMODE) {
		gotoXY(0,0);
		sprintf(testings, (const rom far char *)"Set Close:");
		LCDString( testings , 10);
		gotoXY(0,1);
		sprintf(testings, (const rom far char *)"Close: %02i", closePos);
		LCDString(testings, 9);
                gotoXY(1,5);
                LCDCharacter('+',0);
                gotoXY(40,5);
                LCDCharacter('M',0);
                gotoXY(75,5);
                LCDCharacter('-', 0);
		
	}
}

int BCDToDecimal(unsigned char bcdByte)
{
	return(((bcdByte & 0xF0) >> 4)*10) + (bcdByte & 0x0F);
}

unsigned char  DecimalToBCD(int decimalByte)
{
	return(((decimalByte/10)<<4)|(decimalByte % 10));
}

/***************************************************************************************
time getTimeFromRTC(location)
Send the relevant SPI commands to have the RTC spit back either the time or the alarm 
and return the value as a type 'time', which has members hours, minutes, and seconds.
location should be 0x00 for time, 0x0b for alarm1, which asserts high for 62ms pin:INT1.
WARNING:
If you select a writeable address to read from, the 16 following addresses will be written as 0x00
This would be bad news for you!!

***************************************************************************************/
time getTimeFromRTC(unsigned char location){
	char data[4];	
	time myCurrentTime;
	OpenSPI(SLV_SSOFF & SPI_FOSC_4, MODE_01, SMPEND);
	_RTCSelect = 1;
	Delay1KTCYx(10);
	WriteSPI(location);  //Select to read the setting locations
	getsSPI(data,4);  //Should read back the time or alarm registers (depending on location value fed in)
	Delay1KTCYx(1);	
	_RTCSelect = 0;
	CloseSPI();
	//Need to convert BCD to int.
	myCurrentTime.hours = BCDToDecimal(data[2]); //(24h mode)
	myCurrentTime.minutes = BCDToDecimal(data[1]);
	myCurrentTime.seconds = BCDToDecimal(data[0]);

	return myCurrentTime;
}

/**********************************************************************************
setTimeToRTC(time myTime, char location)
Set time or alarm value to the RTC
location 0x80 for the clock
	0x87 for alarm0
	0x8b for alarm1
/**********************************************************************************/
void setTimeToRTC(time myTime, char location)
{
	OpenSPI(SLV_SSOFF & SPI_FOSC_4, MODE_01, SMPEND);
	_RTCSelect = 1;
	Delay1KTCYx(10);
	WriteSPI(location); //select seconds for write
	//WriteSPI(DecimalToBCD(myTime.seconds)); //Always zero out seconds
        WriteSPI(0x0);
	WriteSPI(DecimalToBCD(myTime.minutes)); 
	WriteSPI(DecimalToBCD(myTime.hours)); // 24h clock
        if (location == 0x80){
            WriteSPI(0x01); //day
            WriteSPI(0x01); //date
            WriteSPI(0x01); //month
            WriteSPI(0x01); //year
        } else {
            WriteSPI(0x80); //For alarm to call every day, bit 7 must be set. Since we're not showing days or date, set this always
        }
        Delay1KTCYx(10);
	_RTCSelect=0;
	CloseSPI();
}

void main(void)	
{
    char data;
    char testings[10];
    unsigned char config0 = 0x00;
    TRISA=0;
    TRISC=0;
    TRISB=0x3c;  //Some of PortB is an input  
    PORTA=0;
    PORTC=0;
    _LCDEnable=1; // Turn on power to the LCD.
    Delay10KTCYx(20);
    LCDInit();
    LCDClear();
    opMode = RUNMODE;
    doorState = DOORCLOSED;
    
    readFromEEPROM(); //get the servo position values from eeprom
    moveServo(closePos);
    
    // Set up and turn on the interrupts.
    INTCON = 0; // disable all interrupts
    INTCONbits.RBIF=0; //Clear PortB interrupt flag.
    INTCONbits.RBIE=0; //Turn off PortB on change to prevent it calling too fast
    INTCON3bits.INT2IF=0; //Clear external interrupt 2 flag
    INTCON2bits.INTEDG2=0; //Trigger on the falling edge of INT2
    INTCON3bits.INT2IE=1; //Turn on external interrupt 2
    INTCONbits.GIE=1;  //Turn on global interrupts

    Delay10KTCYx(2);

//Set up the RTC

    OpenSPI(SLV_SSOFF & SPI_FOSC_4, MODE_01, SMPEND);
        _RTCSelect = 1;
        Delay1KTCYx(10);
        WriteSPI(0x8f); //Select Control Register on DS1306 RTC
        WriteSPI(0x05); //Turn on 1Hz, AIE0, turn off WP bit
        _RTCSelect = 0;
    CloseSPI();
    Delay1KTCYx(10);

/**************************************************************
The right thing to do here is to set the clock to 12:00 if it doens't have a
proper time in it. Otherwise, don't set it since we don't want to delete our settings every time the
PIC resets.
/* Read memory location in user RAM on RTC. Theory is that if it's not got an
 * expected value, the chip has lost power and should be reset.
 * If the value is set, we should assume that the clock is still set and not reset it.
 *
**************************************************************/

    OpenSPI(SLV_SSOFF & SPI_FOSC_4, MODE_01, SMPEND);
        _RTCSelect = 1;
        Delay1KTCYx(10);
        WriteSPI(0x20); //Select Read from RAM on DS1306 RTC
        getsSPI(data,1); //get data to from RAM
        _RTCSelect = 0;
    CloseSPI();
    Delay1KTCYx(10);

    if (!(data & 0xaa)){ //i.e. if clock's not set, then set the current time
        currentTime.hours = 12;
        currentTime.minutes = 00;
        currentTime.seconds = 00;
        setTimeToRTC(currentTime, 0x80);
        Delay1KTCYx(2);

        //Write to RAM so we know that the clock has been reset.
        OpenSPI(SLV_SSOFF & SPI_FOSC_4, MODE_01, SMPEND);
            _RTCSelect = 1;
            Delay1KTCYx(10);
            WriteSPI(0xa0); //Select write to RAM
            WriteSPI(0xaa); //write known data into RAM
            _RTCSelect = 0;
        CloseSPI();
        Delay1KTCYx(10);
    }


    readFromEEPROM();  //Get the alarm times from the EEPROM;
	

    setNextAlarm(); //Put the next alarm into memory
    Delay1KTCYx(2);
    sleepCounter = 0;
    //OpenPORTB(PORTB_CHANGE_INT_ON);
    while(1){
        if(interruptFired == 0x1){
            INTCONbits.RBIE=0; //Turn off PortB interrupt on change since it's only needed when the PIC is asleep.
            // We put the LCD to off when we Sleep() in main(), so need to power it on again.
             _LCDEnable=1;
             Delay10KTCYx(20);
            /*if(isWU())*/LCDInit();
            sleepCounter = 0; //Make sure that there's no sleep for a while.
            interruptFired = 0;
         } else if (interruptFired == 0x2) {
             handleAlarm();
             INTCON3bits.INT2IF=0;  //Clear interrupt flag
             interruptFired = 0;
         }
		
        updateDisplay();
        handleButton(PORTB);
        sleepCounter++;
        Delay1KTCYx(20);
        if(sleepCounter > SLEEPDELAY){
            opMode=RUNMODE; //Drop back to the run mode.
            PORTA=0; //power saving
            PORTC=0;
            OpenPORTB(PORTB_CHANGE_INT_ON);
            INTCONbits.GIE=0;
            INTCONbits.RBIF=0; //Clear PortB interrupt flag.
            INTCONbits.RBIE=1; //Turn on PortB interrupt on change.
            INTCONbits.PEIE=0; //Turn off peripheral interrupts
            INTCONbits.INT0IE=0;
            INTCON3bits.INT2IE=1; //Turn on external interrupt 0	for the alarmy wakey wakey
            INTCONbits.GIE=1;
                        _LCDData = 0; //Power saving to make sure that LCD control pins are not floating
                        _LCDSelect=0;
            OSCCONbits.IDLEN = 0;
            _LCDEnable=0; //Turns off the LCD - will need to LCDInit() when woken up.
            Sleep();
        }
	}
} </code>