randtekk
Published © GPL3+

Sleepy Snail-Mail Concierge

Monitor your mailbox, indicate when you have physical mail. Extensive use of Power Down Sleep mode extends battery life to about 40 days

IntermediateShowcase (no instructions)1,813
Sleepy Snail-Mail Concierge

Things used in this project

Hardware components

Photo resistor
Photo resistor
×1
Arduino Nano R3
Arduino Nano R3
×1
Vishay Phototransistor BPW85B
×1
Resistor 1M ohm
Resistor 1M ohm
×3
Resistor 100k ohm
Resistor 100k ohm
×1
Buck Converter Module, adjustable, LM2596 Based
×1
5 mm LED: Red
5 mm LED: Red
×2
Battery Holder, 18650 x 2
Battery Holder, 18650 x 2
×1
LED Flip Light for LEDs and reflector
×1
Mailbox, black plastic
×1
Perf Board, 2.5 x 3.5 inches
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Schematics

Schematic

Code

Mailbox Monitor Code

Arduino
Code for Mailbox Monitoring system
#include <LowPower.h>// Library for sleep routines
#include <stdio.h>

int sensePin = A0; // Photo Resistor Sensor Pin
int lightPin = 2;  // LED Illuminator Control Pin
int doorPin = 3;   // Photo Transistor Door Interrupt Pin
int ledPin = 13;   // Status LED Pin
int voltPin = A7;  // Battery Voltage Sense Pin

long lastCheckMillis = 0; // time of last mail check in millis
long testTimeout; // In test mode, returns to normal mode when millis() exceeds this value
const bool ON = LOW; // For controlling Illuminator LEDs
const bool OFF = HIGH;// For controlling Illuminator LEDs
volatile bool boxWasOpened = false;// Set to true when mailbox needs checked for mail

int testMode = 0;// Select from mormal run mode (0 value) or a test mode (1 = voltage, 2 = Light sensor, illuminator off, 3 = Light sensor, Illuminator on)
bool mailInBox = false; // set to true if mail is detected in box


void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(lightPin, OUTPUT);
  pinMode(doorPin, INPUT);
  digitalWrite(lightPin, ON);
  attachInterrupt(digitalPinToInterrupt(doorPin), doorInterrupt, FALLING); // Interrupt for door sensing

  Serial.begin(115200);
  Light(OFF); // Start with illuminator off
}
void doorInterrupt(void) {
  boxWasOpened = true;//  Ready for mail checking next time through main loop
  mailInBox = false;
}

int readAverage(int myPin) { // Read an average value on any analog pin, 10 iterations
  int i = 0;
  int iterations = 10;
  long readVals = 0;
  for (i = 1; i <= iterations; i++)
  {
    readVals += analogRead(myPin);
  }
  return (readVals / iterations);
}

void Light(bool myStatus) { // Control Illuminator LEDs
  digitalWrite(lightPin, myStatus);
}

void LED(bool myStatus) {  // Control Status LEDs
  digitalWrite(ledPin, !myStatus);
}

bool CheckMail(bool haveMail) { // Check for mail in mailbox after door close interrupt
  long senseVal; // Photo Transistor value read
  bool retVal = false ; // Return value for function
  Light(ON);
  delay(300); // Allow time for illuminator to stabilize
  lastCheckMillis = millis();

  senseVal = readAverage(sensePin);
  Serial.println(senseVal);
  if (senseVal >= 35) { // Mail detected at this light level
    retVal = true;
  }
  Light(OFF);
  Serial.println(retVal);
  return retVal;
}

void wakeUp()
{
  // Just a handler for the pin interrupt.
}



int testCheck(void) { // check for and initiate test modes

  if ((digitalRead(doorPin) == HIGH) && readAverage(sensePin) < 100) {
    delay(1000); // Only enter test mode if Photoresistor is blocked, door is opened

    if ((digitalRead(doorPin) == HIGH) && readAverage(sensePin) < 100) {
      testMode ++; // Increment test mode when command received
      if (testMode > 3) {
        testMode = 0;
        Light(OFF);
        boxWasOpened = true;
      }
      Serial.print("Test mode = ");
      Serial.println(testMode);
      for (int x = 1; x < 11; x++) { // Indicate test mode selected on the status LEDs before starting test display
        flashFast(testMode);
        delay (500);

      }
      testTimeout = millis() + 60000; //exit test mode after 60 seconds
      delay(1000);

    }
    else {

    }
  }
}

void sleepNow() { // Sleep mode used for NO mail present

  if ((digitalRead(doorPin) == LOW) && analogRead(sensePin) <= 25) { // OK for sleep mode


    attachInterrupt(0, wakeUp, LOW); //set wakeup interrupt
    Serial.println("Going to sleep now.");
    delay(200);
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); // Set sleep mode NO timeout
    // Disable external pin interrupt on wake up pin.

    detachInterrupt(0);
    Serial.println("Now I'm up!");
  }
  else {
    Serial.println("Not tired yet");
  }
}
void sleep2() { // Sleep mode used for mail IS present
  attachInterrupt(0, wakeUp, LOW);
  Serial.println("Going into sleep2 now.");
  delay(200);
  LowPower.powerDown(SLEEP_2S, ADC_OFF, BOD_OFF); // Set sleep mode with 2 second timeout
  // Disable external pin interrupt on wake up pin.

  detachInterrupt(0);
  Serial.println("NOw I'm up!");

}

void flash(int iterations) { // Flash status LEDs number of times requested in <iterations>
  for (int x = 1; x <= iterations; x++ ) {

    //digitalWrite(ledPin, HIGH);
    LED(ON);
    delay(150);
    //digitalWrite(ledPin, LOW);
    LED(OFF);
    delay(200);
  }
}

void flashFast(int iterations) { // double Speed Flash status LEDs number of times requested in <iterations>
  for (int x = 1; x <= iterations; x++ ) {

    LED(ON);
    delay(150);
    LED(OFF);
    delay(100);
  }
}

void showSenseRead(int myVall) { // Display Sensor reading on status LEDs
  int intDigit;
  String myChar;
  char valStr[5];

  sprintf(valStr, "%d", myVall);// Convert reading to a character string
  Serial.print("Sensor Value ");
  Serial.println(valStr);

  if (myVall == 0) {// Quickly flash 15 time if reading is zero
    flashFast(15);
  }
  else if ((myVall > 0) && (myVall < 10)) {
    flash(myVall);
    delay(300);
  }
  else if (myVall > 9 && myVall < 100) {
    int xVal;
    xVal = myVall / 10;
    flash(xVal);
    delay(300);
    xVal = myVall % 10;
    flash(xVal);
    delay (300);
  }
  else if (myVall > 99 && myVall < 1000) {
    int xVal;
    xVal = myVall / 100;
    flash(xVal);
    delay(300);
    xVal = (myVall - xVal * 100) / 10;
    flash(xVal);
    delay(300);
    xVal = myVall % 10;
    flash(xVal);
    delay (300);
  }
}

void showVolts(float myVolts) {// Display battery voltage on status LEDs
  int intDigit;
  if (myVolts == 0.00) {// Send 0 to force voltage reading
    myVolts = readVolts();
  }
  myVolts *= 1.00;
  String myChar;
  char voltStr[6];

  dtostrf(myVolts, 4, 2, voltStr);// Convert voltage to a character string

  Serial.print("ShowVolts VOltage ");
  Serial.println(voltStr);
  // delay(10);
  dtostrf(myVolts, 4, 2, voltStr);// Convert voltage to a character string
  myChar = voltStr[0];

  intDigit = String(myChar).toInt();
  // Serial.print("Corrent Digit ");
  //Serial.println(intDigit);
  flash(intDigit);
  delay(300);

  myChar = voltStr[2];

  intDigit = String(myChar).toInt();

  //Serial.print("Corrent Digit ");
  //Serial.println(intDigit);

  if (intDigit > 0 ) {
    flash(intDigit);
    delay( 300);
  }
  else {
    delay(400);
  }
  //  my
  myChar = voltStr[3];

  intDigit = String(myChar).toInt();

  //Serial.print("Corrent Digit ");
  // Serial.println(intDigit);

  if (intDigit > 0 ) {
    flash(intDigit);
    delay( 300);
  }
  else {
    delay(400);
  }

}

float readVolts(void) {// Get current battery voltage and return it
  float myRead;
  float myVolts;
  myRead = float(readAverage(voltPin));
  float myConv = 4.63 / 1023.00;
  myVolts = (myRead * 2 * myConv * 1.0811);
  Serial.println(myVolts);
  return (myVolts);
}
void loop() {
  static bool lowVolts = false;
  float curVolts;
  curVolts = readVolts();
  if (curVolts <= 6.5) {
    lowVolts = true; // If battery is running low, LEDs will flasw 3 times, not one for mail indication

  }
  testCheck();  // Check for test mode request
  if ((millis() > testTimeout) && testMode > 0) {// Return to normal mode after 1 minute of test mode
    testMode = 0;
    Light(OFF);
    boxWasOpened = true;// want to ensure check
  }
  if (testMode == 0) {// Normal mail display mode

    if (digitalRead(doorPin) == HIGH) {// Stop mail indication when door is opened
      digitalWrite(ledPin, LOW);
      mailInBox = false;// will be set to true once door is closed
    }
    if (readAverage(sensePin >= 25)) {// Can be due to EITHER door being opened

      boxWasOpened = true;
      while (readAverage(sensePin) >= 25) {
        //do nothing until door is closed again
      }

    }
    if (boxWasOpened && digitalRead(doorPin) == LOW) {
      delay(200);
      if (boxWasOpened && digitalRead(doorPin) == LOW) {
        //delay(2000);
        mailInBox  = CheckMail(mailInBox);// Actual mail check
        boxWasOpened = false;
      }

    }

    Serial.println();
    Serial.println(mailInBox);
    Serial.println();
    if (mailInBox == true) {// Mail Present in Box
      LED(ON);
      Serial.println("LED ON");
      delay(100);
      LED(OFF);

      if (lowVolts) {// Blink 3 times instead of one for low voltage
        delay(200);
        LED(ON);
        delay(100);
        LED(OFF);
        delay(200);
        LED(ON);
        delay(100);
        LED(OFF);



      }

      Serial.println(readAverage(sensePin));
      //delay(2350);
      sleep2();

    }
    else {
      LED(OFF);

      delay(200);
      sleepNow();// No mail, go to hard sleep 
    }
  }
  else if (testMode == 1) {  //  Test mode 1 display
    // curVolts = readVolts();
    int myVall;
    myVall = readAverage(sensePin);

    showVolts(curVolts);
    //flash(5);
    delay (2000);
  }

  else if (testMode > 1) {  // Test modes 2, 3 display
    Light (testMode == 2);// light on if testmode is 3
    int myVall;
    myVall = readAverage(sensePin);
    Serial.print("Light Value ");
    Serial.println(myVall);
    showSenseRead(myVall);
    delay(2000);
  }
}

Credits

randtekk
5 projects • 20 followers

Comments