Jump to content
43oh

Pee-Light project


Recommended Posts

Well, my first project is nothing spectacular. I call it the Pee-Light. When I walk into my bathroom in the middle of the night, an LED light in the ceiling gives me just enough light to see what I'm doing and not pee on the toilet seat. It's not enough to blind me when it comes on and I still have my night vision when I'm stumbling back to bed.

 

On the negative side

It probably lies somewhere between a project and "using a commercial product as intended". 90% of the hardware and maybe 75% of the software is an Olimex MSP430-PIR which itself was based on a reference design from TI.

 

On the positive side

It acheived my goal of finding my way around the MSP430. I didn't just copy and add to the software. I made sure I shifted code into my project bit by bit and understood it all.

I used the USB stick from my EZ430-F2013 kit to program it.

It's permanently and properly installed in my house and gets used at least once a day. (I've got a new baby so I'm up a lot.)

 

The existing PIR sensor flashed an LED when it sensed movement. My extension to it was to use a light-dependent resistor to check whether it's dark and a simple transistor to allow a 9v battery to power an LED, The LED fitting I had lying around was designed for 12v and is sealed, but actually gives just the right amount of light when run from 9v.

 

Some photos

post-3215-13513552698_thumb.jpg

post-3215-135135526991_thumb.jpg

post-3215-135135527003_thumb.jpg

 

Code

// *****************************************************************************
// Fred's PIR software
// The majority of this code is from:
// Code for application report SLAA283 - "Ultra-low Power Motion Detection using the MSP430F2013"
// ******************************************************************************
#include  

// P1.7 LDR power
// P1.6 LDR in


// Hardware notes
// P1.0 - LED
// P1.4 - onboard switch
// P2.7 - PIR_VCC
// A4   - PIR (uses P1.1 and P1.2)
// A3	- LDR (uses P1.6 and P1.7)
// LDR between 1.5 (LDR power) and 1.6 (in)
// 200k between 1.6 (in) and 0v
// Threshold 1M ohm input < 50k

// Work in progress
// P1.5 - LDR power on
// P1.6 - LDR in (dark = less than Vss/5 == 50k)

// P1
#define LED_OUT         BIT0              // Bit location for LED
#define BUTTON_IN       BIT4
#define LDR_PWR         BIT5
#define LDR_IN          BIT6

// P2
#define LIGHT_OUT       BIT6
#define SENSOR_PWR      BIT7              // Bit location for power to sensor

#define PIR_THRESHOLD   50                // Threshold for motion
#define LDR_THRESHOLD   50000
#define LIGHT_ON_TIME   20				// x about .5s
#define PIR_IGNORE      3

static unsigned int result_old = 0;         // Storage for last PIR conversion
static char readingLDR = 1;
static int lightTimer = 0;
static int pirIgnoreTimer = 0;
char testMode = 0;                        // LED control


// Definition as used before defined
void adcStartReadPIR(void);
void adcStartReadLDR(void);

void main(void)
{
//WDTCTL = WDTPW + WDTHOLD;

   // Use WD to wait about 10s
   WDTCTL = WDTPW+WDTTMSEL+WDTCNTCL+WDTSSEL; // ACLK/32768, int timer: ~10s
   BCSCTL1 = CALBC1_1MHZ;                    // Set DCO to 1MHz
   DCOCTL = CALDCO_1MHZ;
   BCSCTL1 |= DIVA_2;                        // ACLK = VLO/4
   BCSCTL3 |= LFXT1S_2;

P1OUT = LDR_PWR | BUTTON_IN;
P1DIR = LDR_PWR | LED_OUT;
   P1SEL = 0x00;

P1REN |= BUTTON_IN;                            // P1.4 pullup
 	P1IE |= BUTTON_IN;                             // P1.4 interrupt enabled
 	P1IES |= BUTTON_IN;                            // P1.4 Hi/lo edge
 	P1IFG &= ~BUTTON_IN;                           // P1.4 IFG cleared

   P2OUT = SENSOR_PWR;
   P2DIR = LIGHT_OUT | SENSOR_PWR;
   P2SEL = 0x00;

SD16CTL = SD16VMIDON + SD16REFON + SD16SSEL_1; // 1.2V ref, SMCLK
SD16AE = SD16AE1 | SD16AE2                     // P1.1 & P1.2:  A4+/- SD16_A inputs
            | SD16AE6;                            // P1.6:         A3+ SD16_A inputs
   SD16CTL &= ~SD16VMIDON;                        // VMID off: used to settle ref cap

   //adcStartReadPIR();
   // Wait for PIR sensor to settle: 1st WDT+ interval
   P1SEL |= LED_OUT;                         // Turn LED on with ACLK (for low Icc)
   while(!(IFG1 & WDTIFG));                  // ~5.4s delay: PIR sensor settling
   P1SEL &= ~LED_OUT;                        // Turn LED off with ACLK (for low Icc)

   // Reconfig WDT+ for normal operation: interval of ~341msec
   WDTCTL = WDTPW+WDTTMSEL+WDTCNTCL+WDTSSEL+WDTIS1;// ACLK/512, int timer: 341msec
   BCSCTL1 |= DIVA_3;                        // ACLK = VLO/8
   IE1 |= WDTIE;                             // Enable WDT interrupt

   _BIS_SR(LPM0_bits + GIE);
}

void adcStartReadPIR(void)
{
   readingLDR = 0;
   SD16INCTL0 = SD16GAIN_4 + SD16INCH_4;       // PGA = 4x, Diff inputs A4- & A4+
   SD16CCTL0 =  SD16SNGL + SD16IE;             // Single conversion, 256OSR, Int enable
   SD16CTL |= SD16REFON;                   // If no, turn on SD16_A ref
   SD16CCTL0 |= SD16SC;                        // Set bit to start new conversion
}

void adcStartReadLDR(void)
{
   readingLDR = 1;
   SD16INCTL0 = SD16GAIN_1 + SD16INCH_3;       // PGA = 1x, Diff inputs A3- & A3+
   SD16CCTL0 =  SD16UNI + SD16SNGL + SD16IE;   // Single conversion, 256OSR, Int enable
   SD16CTL |= SD16REFON;                   // If no, turn on SD16_A ref
   SD16CCTL0 |= SD16SC;                        // Set bit to start new conversion
}

void checkPIR(void)
{
   // See if we've had movement
   volatile unsigned int result_new;
   volatile unsigned int change;

   SD16CTL &= ~SD16REFON;                    // Turn off SD16_A ref
   result_new = SD16MEM0;                    // Save result (clears IFG)

   if (result_new > result_old)              // Get difference between samples
       change = result_new - result_old;
   else
       change = result_old - result_new;

   result_old = SD16MEM0;                    // Save last conversion

   // Ignore the first few readings after the light turns off
   if (pirIgnoreTimer < PIR_IGNORE)
   {
       pirIgnoreTimer++;
       return;
   }

   // If motion detected...
   if (change > PIR_THRESHOLD)
   {

       // LED now only used if it's light
       // Even this small light level affected LDR measurement
       P1OUT |= LED_OUT;

       // Light already on?
       if (P2OUT & LIGHT_OUT)
       {
           // Reset counter whether it's dark or not
           lightTimer = 0;
       }
       else
       {
           // Is it dark?
           adcStartReadLDR();
       }
   }
   else
   {
       P1OUT &= ~LED_OUT;
       //adcStartReadPIR();
   }
}

void checkLDR(void)
{
   unsigned int val = SD16MEM0;

   if (testMode)
   {
   	// Just LED = light
   	if (val < LDR_THRESHOLD)
        P1OUT &= ~LED_OUT;
   	else
        P1OUT |= LED_OUT;

       P2OUT &= ~LIGHT_OUT;
     __bis_SR_register_on_exit(SCG1+SCG0);     // Return to LPM3 after reti
   	return;
   }

   if (val < LDR_THRESHOLD)
   {
       // Switch light on
       P2OUT |= LIGHT_OUT;
       lightTimer = 0;
   }
  	else
  	{
  		// Just show LED so we know it's working
       P1OUT |= LED_OUT;
  	}
}

#pragma vector = SD16_VECTOR
__interrupt void SD16ISR(void)
{
if (readingLDR)
       checkLDR();
   else
       checkPIR();

    __bis_SR_register_on_exit(SCG1+SCG0);     // Return to LPM3 after reti

}

/******************************************************
// Watchdog Timer interrupt service routine
******************************************************/
#pragma vector=WDT_VECTOR
__interrupt void watchdog_timer(void)
{
// Test mode just measures light level
if (testMode)
{
	adcStartReadLDR();
    __bic_SR_register_on_exit(SCG1+SCG0);   // Keep DCO & SMCLK on after reti
}

   // Light on?
   if (P2OUT & LIGHT_OUT)
   {
       // Tick, tick, tick
       if (++lightTimer >= LIGHT_ON_TIME)
       {
           P2OUT &= ~LIGHT_OUT;
           pirIgnoreTimer = 0;
       }
   }

   if (P1OUT & LED_OUT)                 // Has motion already been detected?
       P1OUT &= ~LED_OUT;                  // If yes, turn off LED, measure on next loop
   else
       adcStartReadPIR();

   __bic_SR_register_on_exit(SCG1+SCG0);   // Keep DCO & SMCLK on after reti

}

// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
testMode ^= 0x01;                      	// Toggle test mode
P1IFG &= ~BUTTON_IN;                   	// P1.4 IFG cleared
}

 

Schematic

Still need to do this, despite it not being very much.

Link to post
Share on other sites

You can use red LED to preserve your rhodopsin.

http://en.wikipedia.org/wiki/Night_vision

In biological night vision, molecules of rhodopsin in the rods of the eye undergo a change in shape as they absorb light. Rhodopsin is the chemical that allows night-vision, and is extremely sensitive to light. Exposed to a spectrum of light, the pigment immediately bleaches, and it takes about 30 minutes to regenerate fully, but most of the adaptation occurs within the first five or ten minutes in the dark. Rhodopsin in the human rods is less sensitive to the longer red wavelengths of light, so many people use red light to help preserve night vision as it only slowly depletes the eye's rhodopsin stores in the rods and instead is viewed by the cones.

And you can make LED PWM controlled for a stroboscopic effect. This add a whole new dimension to the peeing experience.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...