3
\$\begingroup\$

I want to build a little LED indicator for my bathroom door. The basic idea is that when a person sits or stands next to the toilet, a little transmitter signals it wirelessly to the LED diode which is mounted on the front side of the door. The LED turns on so the next person who wants to come in knows that the toiled is occupied. When the guest comes out of the bathroom, the transmitter yet again signals that the LED should turn off. I came up with a plan to connect the LED to a little battery. The circuit has to work as a On/Off switch. Next to the toiled will be a circuit with a photoresistor (?) which can help in detecting someone next to the toilet. The change in its current/voltage triggers the little transmitter which sends a simple pulse (or another simple signal), which the LED circuit will be able to receive, and then changes its state.

But I have a few questions. Is using the photoresistor a good plan? Maybe there is a better solution of which I have no idea? What kind of signal should the transmitter produce? What would be best to use as a switch circuit on the LED side?

I would be happy to read some suggestions that may help me with this project.

\$\endgroup\$
  • \$\begingroup\$ Use a toilet door lock. What if the person isn't stood by the toilet sensor i.e. using the bath? \$\endgroup\$ – Andy aka May 28 '14 at 10:18
  • \$\begingroup\$ Great idea :) Although I would rather use the LED. Maybe I could use a motion detector for bath detection combined with the IR/photoresistor for the toilet? \$\endgroup\$ – Bart May 28 '14 at 10:37
  • \$\begingroup\$ A door lock doesn't need batteries, won't suffer from condensation, can be viewed in direct sunlight and is fool-proof and with the correct tools can be overridden. If there's a solution that's obvious then not going for it ought to be justified because people here like giving answers that are potentially useful to several folk and without understanding the justification you may not get the best answers. \$\endgroup\$ – Andy aka May 28 '14 at 10:42
  • 1
    \$\begingroup\$ Presumably the person inside the bathroom will need to turn on the light. There are light switches available with a little mains-powered LED indicator inside, which you should be able to wire up so that as the light in the bathroom is turned on, so is the LED. Problem solved! Assuming the switch is outside the bathroom, of course. \$\endgroup\$ – Indrek May 28 '14 at 11:01
  • 1
    \$\begingroup\$ In commercial applications where occupied indication is needed (trains or airplanes for example) they use a system that gets the occupancy information from the locking mechanism. So when someone locks the door the circuit is closed and a light turns on. You can do sometihing like this even if you use a rudimentary sliding lock. It will be directly on the door, and it won't need any fancy electronics, only a power source, a resistor and a LED. The part about standing next to the toilet, well IR would be ok, you can take an motion detecting room freshener and hack it to send the signal. \$\endgroup\$ – WalyKu May 28 '14 at 12:48
1
\$\begingroup\$

I built something along these lines. It's a wireless door sensor with a magnetic sensor on one side and an LED on the receiver. Conveniently, both the door sensor and the receiver are identical in schematic, board, and firmware, save for a single jumper which denotes which role the receiver/sensor will play.

This design uses a reed sensor, which is a magnetic sensor that detects the presence of a magnet within a couple inches. I attach a magnet to the bottom of the door and attach the sensor board to the bottom of the door frame. This works on toilet stalls or the bathroom door itself.

In order to save power, the receiver turns on for 1/10th of a second every second. That means the receiver will have a 1 second delay, but it consumes 1/10th of the power. You can easily adjust these numbers based on how little power you want the receiver to use and how much of a delay you are willing to tolerate.

The remote uses hardware pin change interrupts, so it only uses power when broadcasting. That means you can run it off a single coin cell battery for months.

Here's the schematic:

Schematic

This schematic allows for both a CR2032 coin cell battery or a 2-pin JST battery connector (used for AA battery packs and LiPo's). It uses the nRF24L01 wireless IC, which is easily available.

The source code and EAGLE files are available at https://github.com/samuelclay/doormonitor Here's the firmware source code. Libraries and dependencies are linked above, but this is the main routine.

#include <avr/sleep.h> 
#include <avr/interrupt.h> 
#include <avr/wdt.h>

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <pinchange.h>

void sleepNow(void);
void wakeup();
void setupWatchdog(uint8_t prescalar);

#if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__)
    RF24 radio(3,7);
    const int role_pin = 10;
    const int sensor_pin = 2;
    const int led_pin = 1;
    // const int sensor_pin = 8;
    // const int led_pin = 9;
#else
    RF24 radio(9, 10);
    const int role_pin = 6;
    const int sensor_pin = 2;
    const int led_pin = 4;
#endif 

const uint64_t pipe = 0xA8E8F0F0F1LL;

typedef enum { wdt_16ms = 0, wdt_32ms, wdt_64ms, wdt_128ms, wdt_250ms, 
               wdt_500ms, wdt_1s, wdt_2s, wdt_4s, wdt_8s } wdt_prescalar_e;

typedef enum { 
    role_remote = 1, 
    role_led = 2
} role_e;
role_e role;
const char* role_friendly_name[] = { "invalid", "Remote", "LED Board"};

uint8_t led_state = 0;
uint8_t sensor_state = 0;
volatile int awakems = 0;
int send_tries = 0;
bool send_ok = false;

void setup(void) {
    // set up the role pin
    pinMode(role_pin, INPUT);
    digitalWrite(role_pin, HIGH);
    delay(20); // Just to get a solid reading on the role pin

    // read the address pin, establish our role
    role = digitalRead(role_pin) ? role_remote : role_led;
    digitalWrite(role_pin, LOW);

    Serial.begin(9600);

    radio.begin();
    radio.setChannel(38);
    radio.setDataRate(RF24_250KBPS);
    radio.setAutoAck(pipe, true);
    radio.setRetries(15, 15);

    if (role == role_remote) {
        radio.openWritingPipe(pipe);
        radio.stopListening();
    } else {
        radio.openReadingPipe(1,pipe);
        radio.startListening();
    }

//    radio.printDetails();

    if (role == role_remote) {
        pinMode(sensor_pin,INPUT);
        digitalWrite(sensor_pin,HIGH);
    }

    if (role == role_led) {
        setupWatchdog(wdt_2s);
    }

    pinMode(led_pin,OUTPUT);
    led_state = LOW;
    digitalWrite(led_pin, led_state);
    int i = role == role_led ? 4 : 2;
    int pause = role == role_led ? 100 : 300;
    while (i--) {
        delay(pause);
        digitalWrite(led_pin, HIGH);
        delay(pause);
        digitalWrite(led_pin, LOW);
    }
}

void loop(void) {
    bool different = false;

    if (role == role_remote) {
        // Get the current state of buttons, and
        // Test if the current state is different from the last state we sent
        uint8_t state = !digitalRead(sensor_pin);
        Serial.write("Sensor state: ");
        Serial.write(state ? "ON" : "off");
        Serial.write('\n');
        if (state != sensor_state) {
            different = true;
            send_tries = 1000;
            sensor_state = state;
            led_state = sensor_state;
        }

        // Send the state of the buttons to the LED board
        if (send_tries && (different || !send_ok)) {
            Serial.write("Now sending...");
            digitalWrite(led_pin, led_state);
            radio.powerUp();
            delay(10);
            send_ok = radio.write( &sensor_state, sizeof(uint8_t) );
            if (send_ok) {
                Serial.write("ok\n\r");
            } else {
                char tries_left_char[1];
                send_tries--;
                itoa(send_tries, tries_left_char, 10);
                Serial.write("failed (");
                Serial.write((char *)tries_left_char);
                Serial.write(" tries left)\n\r");
                digitalWrite(led_pin, LOW);
                delay(25);        
                digitalWrite(led_pin, led_state);            
            }
            radio.powerDown();
            awakems = 0;
        }

        awakems += 1;
        if (awakems > 10) {
            sleepNow();
            awakems = 0;
        }
    }

    if (role == role_led) {
         // digitalWrite(led_pin, HIGH);

        if (radio.available()) {
            // Dump the payloads until we've gotten everything
            bool done = false;
            awakems = 0;
            while (!done) {
                done = radio.read( &sensor_state, sizeof(uint8_t) );
            }
            Serial.write("Got buttons: ");
            Serial.write(sensor_state ? "ON\n\r" : "off\n\r");
            led_state = sensor_state;
            digitalWrite(led_pin, led_state);
            awakems = -10000;
        }

        uint8_t incomingByte;
        // send data only when you receive data:
        if (Serial.available() > 0) {
            // read the incoming byte:
            incomingByte = Serial.read();

            // say what you got with both the ASCII and decimal representations
            Serial.print("I received: ");
            Serial.write(incomingByte);
            Serial.print(" : ");
            Serial.println(incomingByte, DEC);

            awakems = 0;
        }

        awakems += 1;
        if (awakems > 4000) {
            // digitalWrite(led_pin, LOW);
            sleepNow();
            awakems = 0;
        }
    }
}
#define BODS 7                   //BOD Sleep bit in MCUCR
#define BODSE 2                  //BOD Sleep enable bit in MCUCR
uint8_t mcucr1, mcucr2;

void setupWatchdog(uint8_t prescalar) {
  prescalar = min(9,prescalar);
  uint8_t wdtcsr = prescalar & 7;
  if ( prescalar & 8 )
    wdtcsr |= _BV(WDP3);

  MCUSR &= ~_BV(WDRF);
  WDTCSR = _BV(WDCE) | _BV(WDE);
  WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE);
}

void sleepNow(void) {
    Serial.write("Sleeping...\n\r");
    if (role == role_led) {
        radio.stopListening();
    }
    radio.powerDown();

    if (role == role_remote) {
        attachPcInterrupt(sensor_pin, wakeup, CHANGE);
    }
    ACSR |= _BV(ACD);                         //disable the analog comparator
    ADCSRA &= ~_BV(ADEN);                     //disable ADC
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();

    //turn off the brown-out detector.
    //must have an ATtiny45 or ATtiny85 rev C or later for software to be able to disable the BOD.
    //current while sleeping will be <0.5uA if BOD is disabled, <25uA if not.
    cli();
    mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE);  //turn off the brown-out detector
    mcucr2 = mcucr1 & ~_BV(BODSE);
    MCUCR = mcucr1;
    MCUCR = mcucr2;
    sei();                         //ensure interrupts enabled so we can wake up again
    sleep_cpu();                   //go to sleep
    cli();                         //wake up here, disable interrupts
    if (role == role_remote) {
        detachPcInterrupt(sensor_pin);
    }
    sleep_disable();               
    sei();                         //enable interrupts again (but INT0 is disabled from above)
    Serial.print("Wakeup...\n\r");
    if (role == role_led) {
      radio.startListening();
    }
    delay(5);
}

void wakeup() {
    awakems = 0;
}

ISR(WDT_vect) {
    awakems = 0;
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Wow, thank you. Seems like a very interesting project. I'll definitely look into it. I guess it will do the job! \$\endgroup\$ – Bart May 29 '14 at 6:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.