Erik de Ruiter
Published © CC BY-SA

Mouse-O-Leum (Multiple Live Catch) Mouse Trap

Very reliable (multiple mice-live catch) mouse trap. Mice *cannot* escape if they trigger the IR beam! **NEW*: IoT version with phone app!

AdvancedFull instructions provided15 hours6,260
Mouse-O-Leum (Multiple Live Catch) Mouse Trap

Things used in this project

Hardware components

Servo Towerpro SG92R
×2
See list in text
×1

Software apps and online services

Blynk
Blynk
monitoring and remote control is handled via the Blynk platform. See text for guide

Hand tools and fabrication machines

CNC machine would be very helpful...

Story

Read more

Custom parts and enclosures

MouseOleum CAD files

DXF and Vectric software files included

MouseOleum Strip-Board files

These files can be used as 1:1 templates for cutting the strips and printing a label for the top. Also an OVERVIEW file to print out as component reference

MouseOleum LIBRARIES

These are the Libraries you need to paste to your Arduino Libraries folder. ***!!! NOTE: if you have compilation problems, be sure you have NO duplicate libraries...!

Schematics

MouseOleum schematic

Code

MouseOleum_Arduino

C/C++
This is the sketch for the Arduino, for the WIFI 8266 board use the other sketch!!
/* 

MouseOleum mousetrap
Version 2.2

Project URL: hackster.io/edr1924

Erik de Ruiter - The Netherlands
2019-06-23 16:30 *** IoT VERSION using the Blynk platform ***


 




  !intentionally left blank because you need to read this:


  when uploading to the arduino or 8266 board, 

  *******************************************************
  *******************************************************
  **                                                   **
  **  YOU NEED TO REMOVE THE 8266 BOARD FROM THE PCB ! **
  **                                                   **
  *******************************************************
  *******************************************************

 ! NOTE ON FLASHING THE ESP8266 Board when using a FDTI programmer
 ! DISCONNECT the Rx and Tx connections before flashing or remove
 ! the 8266 board from the PCB
 !
 ! Else the Rx and Tx connection to the Arduino will inhibit flashing

  












*/

//===================================================================
// define libraries we need
//===================================================================
#include <Arduino.h>  // only needed when using non-Arduino IDE
#include <PWMServo.h> // see comments at the end of the sketch

//===================================================================
// define ports:
//===================================================================
// Arduino pin 2 (Rx) <-> 8266 board pin 10 (Tx)
// Arduino pin 3 (Tx) <-> 8266 board pin 9 (Rx)
#define IR_MOUSE_PRESENT 4        // IR1 sensor
#define IR_MOUSE_EATING 6         // IR2 sensor
#define BUTTON_FRONTDOOR_MANUAL 7 // momentary button
#define BUTTON_TRAPDOOR_MANUAL 8  // momentary button
// ! Pins 9 and 10 are MANDATORY for the servo connections!
// Arduino pin 9 -> frontdoorServo
// Arduino pin 10 -> trapdoorServo
#define LED_MOUSE_CAUGHT 13

//===================================================================
// servo timings
//===================================================================
// !BE CAREFUL* with these values or you *will* damage the servo permanently
// define SERVO Min-Max LIMITS (0-180 degrees) values
#define SERVO_FRONTDOOR_MIN_VALUE 400
#define SERVO_FRONTDOOR_MAX_VALUE 2300
#define SERVO_TRAPDOOR_MIN_VALUE 400
#define SERVO_TRAPDOOR_MAX_VALUE 2300
// define servo UPPER and LOWER position for both trapdoors
// you have to experimentally figure these values out...
#define SERVO_FRONTDOOR_UPPER_VALUE 94 // WORK:84/DEMO:94
#define SERVO_FRONTDOOR_LOWER_VALUE 30 // WORK:25
#define SERVO_TRAPDOOR_UPPER_VALUE 131 // WORK:131
#define SERVO_TRAPDOOR_LOWER_VALUE 7   // WORK:15/DEMO:7
// Create Servo objects
PWMServo frontDoorServo;
PWMServo trapDoorServo;

//===================================================================
// define variables
//===================================================================
int trapdoorCounter = 0;
int mouseCounter = 0;
// led blink
int prevLedState = 0;
;
int ledState = 0;
int ledBlinkCounter = 0;
// buttons
int buttonFrontdoorState = 0;
int buttonTrapdoorState = 0;
bool manualTrapdoor = false;
bool manualFrontdoor = false;
// IR sensor check
int sensorIr1CheckState = 0;
int sensorIr2CheckState = 0;
bool fatalIr1Error = false;
bool fatalIr2Error = false;
// serial communication
const char startOfNumberDelimiter = '<';
const char endOfNumberDelimiter = '>';
static long receivedNumber = 0;
bool remoteCatch = false;
bool remoteDisable = false;
// timers
unsigned long timerMousePresent = 0;
unsigned long timerTrapdoorOpenDelay = 0;
unsigned long timerLedBlink = 0;
unsigned long timerIr1Check = 0;
unsigned long timerIr2Check = 0;
unsigned long timerDebounceButtonFrontdoor = 0;
unsigned long timerDebounceButtonTrapdoor = 0;

//===================================================================
// TIMER DELAY SETTINGS
//===================================================================
// !delays are in milli-seconds:
//
// The IR2 sensor (at the back of the trap) can only trigger the
// trapdoors when first the IR1 sensor (at the front of the trap)
// has been triggered. After this the IR2 sensort must be triggered
// within the time limit set below.
// This is to preven a snail, bug or moth etc. triggering the trap.
#define MOUSEPRESENT_DELAY 10000L
// The servo need some time to fully open, do not lower this value
#define TRAPDOOR_DELAY_BEFORE_CLOSING_AGAIN 300
// after closing the trapdoor and verify that the mouse did not jump out,
// we open the front door after this time has passed:
#define TRAPDOOR_DELAY_AFTER_CLOSING 2000
// the LED is used to give a visual indication of how many mice are in
// the trap. They hide under the hay in the trap so you can't see them...
// so the LED will blink x times, delay and blink x times etc.
#define LED_ON_TIME 400
#define LED_OFF_TIME 300
#define LED_DELAY 1500
// this is used to determine if the IR1 and 2 sensors are free of blockades
// and/or the electronic circuit is working OK. If one of the IR1 or IR2
// sensors give a HIGH output for more than the time set below:
// 1. The frontdoor will open; 2. the LED's will blink in SOS fashion
// 3. a notification is sent to the app; 4. The system will HALT and wait for
// a (remote) reset
// !delay in minutes:
#define IR_SENSOR_OBSTRUCTION_TIME_LIMIT 10 //minute

// define functions for the C++ compiler
// this is not used/needed for the Arduino IDE. I use the must better Visual
// Studio Code editor with the PlaformIO addon which need this declarations:
void state_machine(void);
void servoFrontdoorOpen(void);
void servoFrontdoorClose(void);
void servoTrapdoorClose(void);
void servoTrapdoorOpen(void);
void printState(void);
void blink_led(void);
void checkIrSensors(void);
void check_buttons(void);
void processSerialInput(void);

// I use serial ommunication between the Arduino Uno and the Adafruit
// 8266 Huzzah Wifi board. To prevent erratic behaviour I use 3 digit
// numbers to sent the messages. In the sketch, the text messages below
// represent the value defined here. Value definition is needed here
// because I want to use a three digit code.

// define serial communication message variables:
enum MESSAGE_ID
{
  message_MOUSE_PRESENT = 210,
  message_MOUSE_CAUGHT = 215,
  message_MOUSE_LEAVING = 230,
  message_SYSTEM_READY = 245,
  message_MANUAL_MODE = 250,
  message_REMOTE_DISABLE = 255,
  message_REMOTE_CATCH = 260,
  message_REOPEN_TRAPDOOR = 265,
  message_IR1_ERROR = 270,
  message_IR2_ERROR = 285,
  message_OBSTACLE_ERROR = 290,
};

// define NOTIFY messages
enum REMOTE_COMMANDS_ID
{
  command_CATCH = 101,
  command_DISABLE = 102,
  command_ENABLE = 103,
};

// led blink Finite State Machine states
enum LEDSTATES
{
  BEGIN,
  LED_ON,
  LED_ON_DELAY,
  LED_OFF,
  LED_OFF_DELAY,
  REPEAT_BLINK,
  BLINK_PAUSE
};

// states of the main Final State Machine
enum STATES
{
  START,
  ARMED,
  READY,
  CATCH,
  CLOSE_TD,
  VERIFY,
  OPEN_FD,
  MANUAL,
  DISABLE,
  HALT,
};
STATES state;

//*****************************************************************************
//   ######  ######## ######## ##     ## ########
//  ##    ## ##          ##    ##     ## ##     ##
//  ##       ##          ##    ##     ## ##     ##
//   ######  ######      ##    ##     ## ########
//        ## ##          ##    ##     ## ##
//  ##    ## ##          ##    ##     ## ##
//   ######  ########    ##     #######  ##
//*****************************************************************************
void setup()
{
  // define Arduino ports
  pinMode(IR_MOUSE_PRESENT, INPUT);
  pinMode(IR_MOUSE_EATING, INPUT);
  pinMode(LED_MOUSE_CAUGHT, OUTPUT);
  pinMode(BUTTON_FRONTDOOR_MANUAL, INPUT_PULLUP);
  pinMode(BUTTON_TRAPDOOR_MANUAL, INPUT_PULLUP);

  Serial.begin(9600);

  // startup 'ok' signalling
  digitalWrite(LED_MOUSE_CAUGHT, HIGH);
  delay(1000);
  digitalWrite(LED_MOUSE_CAUGHT, LOW);
  delay(500);
  digitalWrite(LED_MOUSE_CAUGHT, HIGH);
  delay(1000);
  digitalWrite(LED_MOUSE_CAUGHT, LOW);
  delay(500);
  digitalWrite(LED_MOUSE_CAUGHT, HIGH);
  delay(1000);
  digitalWrite(LED_MOUSE_CAUGHT, LOW);

  // !DO NOT use other pins for the servo's!
  // >> see the connection-table in the comments section <<
  // attach the servo on pin 9 to the servo object
  frontDoorServo.attach(SERVO_PIN_A,
                        SERVO_FRONTDOOR_MIN_VALUE,
                        SERVO_FRONTDOOR_MAX_VALUE);
  // attach the servo on pin 10 to the servo object
  trapDoorServo.attach(SERVO_PIN_B,
                       SERVO_TRAPDOOR_MIN_VALUE,
                       SERVO_TRAPDOOR_MAX_VALUE);
}

//*****************************************************************************
//  ##        #######   #######  ########
//  ##       ##     ## ##     ## ##     ##
//  ##       ##     ## ##     ## ##     ##
//  ##       ##     ## ##     ## ########
//  ##       ##     ## ##     ## ##
//  ##       ##     ## ##     ## ##
//  ########  #######   #######  ##
//*****************************************************************************
void loop()
{
  // check constantly for serial input
  if (Serial.available())
  {
    // if serial input is available, process it
    processSerialInput();
  }

  // execute main Finite State Machine (FSM)
  state_machine();

  // check the 2 buttons
  check_buttons();

  // blink the red LED's to indicate if and how many mice are caught
  blink_led();

  // check if the IR sensors are obstructed, longer than the set
  // time limit in <IR_SENSOR_OBSTRUCTION_TIME_LIMIT> variable
  // due to a bug or electronic malfunction.
  checkIrSensors();
}

//*****************************************************************************
//   ######  ########    ###    ######## ########  ######
//  ##    ##    ##      ## ##      ##    ##       ##    ##
//  ##          ##     ##   ##     ##    ##       ##
//   ######     ##    ##     ##    ##    ######    ######
//        ##    ##    #########    ##    ##             ##
//  ##    ##    ##    ##     ##    ##    ##       ##    ##
//   ######     ##    ##     ##    ##    ########  ######
//*****************************************************************************
void state_machine()
{
  switch (state)
  {

    //---------------------------------------------------------------------------
    //           888                     888
    //           888                     888
    //           888                     888
    //  .d8888b  888888  8888b.  888d888 888888
    //  88K      888        "88b 888P"   888
    //  "Y8888b. 888    .d888888 888     888
    //       X88 Y88b.  888  888 888     Y88b.
    //   88888P'  "Y888 "Y888888 888      "Y888
    //---------------------------------------------------------------------------
  case START:
    // put doors in idle position
    servoFrontdoorOpen();
    servoTrapdoorClose();
    // reset variables
    mouseCounter = 0;
    fatalIr1Error = false;
    fatalIr2Error = false;
    // delay before sending 'ready' notification to Blynk terminal widget
    delay(500);
    // !sent notification
    Serial.print(startOfNumberDelimiter);
    Serial.print(message_SYSTEM_READY);
    Serial.print(endOfNumberDelimiter);
    Serial.println();
    // mouse caught LED's off;
    digitalWrite(LED_MOUSE_CAUGHT, LOW);
    // goto idle state, looking out for mice
    state = ARMED;
    break;

    //---------------------------------------------------------------------------
    //                                               888
    //                                               888
    //                                               888
    //   8888b.  888d888 88888b.d88b.   .d88b.   .d88888
    //      "88b 888P"   888 "888 "88b d8P  Y8b d88" 888
    //  .d888888 888     888  888  888 88888888 888  888
    //  888  888 888     888  888  888 Y8b.     Y88b 888
    //  "Y888888 888     888  888  888  "Y8888   "Y88888
    //---------------------------------------------------------------------------
    // this is the idle STATE:    1. waiting for a mouse to enter the trap
    // 2. monitoring the buttons  3. checking the IR sensors for blockades.
    // 4. monitoring remote CATCH or DISABLE commands
    //---------------------------------------------------------------------------
  case ARMED:
    // ======================================
    // monitor the IR1 sensor (mouse present)
    // ======================================
    if (digitalRead(IR_MOUSE_PRESENT) == HIGH)
    {
      // a mouse has entered the trap!

      // Set the timeout timer. Within the MOUSE_PRESENT_DELAY time, the mouse
      // need to trigger the IR2 sensor (mouse eating), else we will
      // reset and wait again.
      // This is done to prevent flies/moths etc. triggering the trap.
      timerMousePresent = millis();
      // !sent notification
      Serial.print(startOfNumberDelimiter);
      Serial.print(message_MOUSE_PRESENT);
      Serial.print(endOfNumberDelimiter);
      Serial.println();
      // go to the next state: monitoring the IR2 (mouse eating) sensor...
      state = READY;
    }

    // =================================
    // check for manual opening of doors
    // =================================
    if (manualFrontdoor == true || manualTrapdoor == true)
    {
      // one of the door buttons has been used:
      // !sent notification
      Serial.print(startOfNumberDelimiter);
      Serial.print(message_MANUAL_MODE);
      Serial.print(endOfNumberDelimiter);
      Serial.println();
      // state change
      state = MANUAL;
    }

    // ============================================
    // check for remote commands from the Blynk app
    // ============================================
    if (remoteCatch == true)
    {
      // the CATCH button of the Blynk app has been pushed!
      remoteCatch = false;
      // !sent notification
      Serial.print(startOfNumberDelimiter);
      Serial.print(message_REMOTE_CATCH);
      Serial.print(endOfNumberDelimiter);
      Serial.println();
      // activate the trapdoor!
      state = CATCH;
    }
    if (remoteDisable == true)
    {
      // the DISABLE button of the Blynk app has been pushed:
      // !sent notification
      Serial.print(startOfNumberDelimiter);
      Serial.print(message_REMOTE_DISABLE);
      Serial.print(endOfNumberDelimiter);
      Serial.println();
      // go to the disbale state, shutting down the trap
      state = DISABLE;
    }

    // ======================================
    // check for IR sensors obstruction error
    // ======================================
    //
    // if a moth, snail etc blocks one of the IR sensors, keeping
    // the pin state HIGH for a set time, notify and halt the system.
    //
    if (fatalIr2Error == true)
    {
      // !sent notification
      Serial.print(startOfNumberDelimiter);
      Serial.print(message_IR2_ERROR);
      Serial.print(endOfNumberDelimiter);
      Serial.println();
      // stop the machine
      state = HALT;
    }
    break;

    //---------------------------------------------------------------------------
    //                                888
    //                                888
    //                                888
    //  888d888 .d88b.   8888b.   .d88888 888  888
    //  888P"  d8P  Y8b     "88b d88" 888 888  888
    //  888    88888888 .d888888 888  888 888  888
    //  888    Y8b.     888  888 Y88b 888 Y88b 888
    //  888     "Y8888  "Y888888  "Y88888  "Y88888
    //                                         888
    //                                    Y8b d88P
    //                                     "Y88P"
    //---------------------------------------------------------------------------
    // in this STATE, we wait for the mouse to take the next step and trigger
    // the IR2 sensor. After a set time, if this not happens, we exit this state
    //---------------------------------------------------------------------------
  case READY:
    // check if the mouse is present or has left. If present or the set timeout
    // time has not passed, then keep checking the IR2 sensor (mouse eating)
    if (digitalRead(IR_MOUSE_PRESENT) == HIGH)
    {
      timerMousePresent = millis();
    }

    if (millis() - timerMousePresent < MOUSEPRESENT_DELAY)
    {
      // no timeout or mouse still present so we now check the
      // IR2 (mouse eating) sensor.
      if (digitalRead(IR_MOUSE_EATING) == HIGH)
      {
        // the mouse HAS triggered the IR2 (mouse eating) sensor!
        // so we immediately go to the next state.
        // The mouse will have a bad day it will not forget...
        state = CATCH;
      }
    }
    else
    {
      // The <timerMousePresent> timer has exceeded <MOUSEPRESENT_DELAY> so
      // the mouse has left. We now return to <ARMED> and wait for the next
      // mouse to enter the trap...
      // !sent notification
      Serial.print(startOfNumberDelimiter);
      Serial.print(message_MOUSE_LEAVING);
      Serial.print(endOfNumberDelimiter);
      Serial.println();
      // go to
      state = ARMED;
    }

    // ======================================
    // check for IR sensors obstruction error
    // ======================================
    //
    // if a moth, snail etc blocks one of the IR sensors, keeping
    // the pin state HIGH for a set time, notify and halt the system.
    //
    if (fatalIr1Error == true)
    {
      // !sent notification
      Serial.print(startOfNumberDelimiter);
      Serial.print(message_IR1_ERROR);
      Serial.print(endOfNumberDelimiter);
      Serial.println();
      // stop the machine
      state = HALT;
    }
    break;

    //---------------------------------------------------------------------------
    //                    888            888
    //                    888            888
    //                    888            888
    //   .d8888b  8888b.  888888 .d8888b 88888b.
    //  d88P"        "88b 888   d88P"    888 "88b
    //  888      .d888888 888   888      888  888
    //  Y88b.    888  888 Y88b. Y88b.    888  888
    //   "Y8888P "Y888888  "Y888 "Y8888P 888  888
    //---------------------------------------------------------------------------
    // this state will immediately close the frontdoor and open the trapdoor
    // to catch the mouse.
    //---------------------------------------------------------------------------
  case CATCH:
    // immediately CLOSE the frontdoor and OPEN the trapdoor!
    servoFrontdoorClose();
    servoTrapdoorOpen();
    // the trapdoor is open, so start a timer to check later when we need
    // to close the trap.
    timerTrapdoorOpenDelay = millis();
    // now go to the next state
    state = CLOSE_TD;
    break;

    //---------------------------------------------------------------------------
    //           888                                   888        888
    //           888                                   888        888
    //           888                                   888        888
    //   .d8888b 888  .d88b.  .d8888b   .d88b.         888888 .d88888
    //  d88P"    888 d88""88b 88K      d8P  Y8b        888   d88" 888
    //  888      888 888  888 "Y8888b. 88888888        888   888  888
    //  Y88b.    888 Y88..88P      X88 Y8b.            Y88b. Y88b 888
    //   "Y8888P 888  "Y88P"   88888P'  "Y8888 88888888 "Y888 "Y88888
    //---------------------------------------------------------------------------
    // here we wait for the timer before closing the trapdoor. after closing
    // we wait for a set time.
    //---------------------------------------------------------------------------
  case CLOSE_TD:
    // wait before closing the trapdoor, the servo needs time to open the door
    // but we need to close it again ASAP. 300mS should be enough but you have
    // to experiment and set the value in the variable
    // <TRAPDOOR_DELAY_BEFORE_CLOSING_AGAIN>
    if (millis() - timerTrapdoorOpenDelay > TRAPDOOR_DELAY_BEFORE_CLOSING_AGAIN)
    {
      //delay has passed so close the trapdoor again!
      servoTrapdoorClose();
    }
    // now we have to wait a while before we go to the next step and verify
    // wait at least 500ms here before going to the next step. The trapdoor
    // need time to close again!
    if (millis() - timerTrapdoorOpenDelay > TRAPDOOR_DELAY_AFTER_CLOSING)
    {
      // next step:
      state = VERIFY;
    }
    break;

    //---------------------------------------------------------------------------
    //                            d8b  .d888
    //                            Y8P d88P"
    //                                888
    //  888  888  .d88b.  888d888 888 888888 888  888
    //  888  888 d8P  Y8b 888P"   888 888    888  888
    //  Y88  88P 88888888 888     888 888    888  888
    //   Y8bd8P  Y8b.     888     888 888    Y88b 888
    //    Y88P    "Y8888  888     888 888     "Y88888
    //                                            888
    //                                       Y8b d88P
    //                                        "Y88P"
    //---------------------------------------------------------------------------
    // now we check if a mouse has managed to jump up. if so re-open the trapdoor
    //---------------------------------------------------------------------------
  case VERIFY:
    // After 50 mice caught I've never seen mice jumping on the closing trapdoor
    // or clinging onto one of the side panels when the trapdoor opened. LOL
    // But to be sure we now check if both IR sensors give the 'all clear'
    if (digitalRead(IR_MOUSE_PRESENT) == HIGH ||
        digitalRead(IR_MOUSE_EATING) == HIGH)
    {
      // IR sensors detect a mouse so try opening the trapdoor again but
      // for a maximum of 3 times.
      trapdoorCounter += 1;
      // check the counter value first:
      if (trapdoorCounter > 3)
      {
        // !sent notification
        Serial.print(startOfNumberDelimiter);
        Serial.print(message_OBSTACLE_ERROR);
        Serial.print(endOfNumberDelimiter);
        Serial.println();
        // the door has been opened for 3 times already, we have a problem
        // so we notify and shutdown the system in the next state
        state = HALT;
      }
      else
      {
        // !sent notification
        Serial.print(startOfNumberDelimiter);
        Serial.print(message_REOPEN_TRAPDOOR);
        Serial.print(endOfNumberDelimiter);
        Serial.println();
        // counter value within limit so reopen the trapdoor:
        state = CATCH;
      }
    }

    // Both IR sensors give the 'all clear' so we can go on:
    else
    {
      // reset the counter
      trapdoorCounter = 0;
      // next state: open de frontdoor...
      state = OPEN_FD;
    }
    break;

    //---------------------------------------------------------------------------
    //                                       .d888      888
    //                                      d88P"       888
    //                                      888         888
    //   .d88b.  88888b.  88888b.           888888  .d88888
    //  d88""88b 888 "88b 888 "88b          888    d88" 888
    //  888  888 888  888 888  888          888    888  888
    //  Y88..88P 888 d88P 888  888          888    Y88b 888
    //   "Y88P"  88888P"  888  888 88888888 888     "Y88888
    //           888
    //           888
    //           888
    //---------------------------------------------------------------------------
    // the frontdoor can be opened now
    //---------------------------------------------------------------------------
  case OPEN_FD:
    // open the frontdoor
    servoFrontdoorOpen();
    // increase the mouse caught counter by one
    // now also, the LED will blink according to the number of mice caught.
    // this seperate state machine will act upon the value of <mouseCounter>.
    mouseCounter += 1;
    // !sent notification
    Serial.print(startOfNumberDelimiter);
    Serial.print(message_MOUSE_CAUGHT);
    Serial.print(endOfNumberDelimiter);
    Serial.println();
    // return to the idle state, ready for the next mouse
    state = ARMED;
    break;

    //---------------------------------------------------------------------------
    //                                                    888
    //                                                    888
    //                                                    888
    //  88888b.d88b.   8888b.  88888b.  888  888  8888b.  888
    //  888 "888 "88b     "88b 888 "88b 888  888     "88b 888
    //  888  888  888 .d888888 888  888 888  888 .d888888 888
    //  888  888  888 888  888 888  888 Y88b 888 888  888 888
    //  888  888  888 "Y888888 888  888  "Y88888 "Y888888 888
    //---------------------------------------------------------------------------
    // this option is made to make applying bate (peanutbutter!) or cleaning
    // more easy.   Notice that when opened, the system is idle!
    //---------------------------------------------------------------------------
  case MANUAL:
    // rapidly flash the LED's to indicate system shutdown
    // ! because of the delays used here, you have to press the buttons
    // for a short while before they will respond.
    digitalWrite(LED_MOUSE_CAUGHT, HIGH);
    delay(50);
    digitalWrite(LED_MOUSE_CAUGHT, LOW);
    delay(50);

    // open frontdoor manually
    if (manualFrontdoor == true)
    {
      servoFrontdoorClose();
    }
    else
    {
      servoFrontdoorOpen();
    }

    // open Trapdoor manually
    if (manualTrapdoor == true)
    {
      servoTrapdoorOpen();
    }
    else
    {
      servoTrapdoorClose();
    }

    // ONLY when both doors are in the idle position, the system
    // can be activated again.
    if (manualFrontdoor == false && manualTrapdoor == false)
    {
      state = START;
    }
    break;

    //---------------------------------------------------------------------------
    //       888 d8b                   888      888
    //       888 Y8P                   888      888
    //       888                       888      888
    //   .d88888 888 .d8888b   8888b.  88888b.  888  .d88b.
    //  d88" 888 888 88K          "88b 888 "88b 888 d8P  Y8b
    //  888  888 888 "Y8888b. .d888888 888  888 888 88888888
    //  Y88b 888 888      X88 888  888 888 d88P 888 Y8b.
    //   "Y88888 888  88888P' "Y888888 88888P"  888  "Y8888
    //---------------------------------------------------------------------------
    // the trap can be remotely disabled using the blynl app. this is useful
    // if you do not want to deal with caught mice for a while...
    //---------------------------------------------------------------------------
  case DISABLE:
    // a remote disabling of the trap is requested
    // so close the front door
    servoFrontdoorClose();
    // and rapidly blink the LED's on as an indication
    // !because of the delay used here, RE-enabling the trap wil have a short delay
    digitalWrite(LED_MOUSE_CAUGHT, HIGH);
    delay(50);
    digitalWrite(LED_MOUSE_CAUGHT, LOW);
    delay(50);
    // check if an exit of this state is requested
    if (remoteDisable == false)
    {
      // restart the system
      state = START;
    }
    break;

    //---------------------------------------------------------------------------
    //  888               888 888
    //  888               888 888
    //  888               888 888
    //  88888b.   8888b.  888 888888
    //  888 "88b     "88b 888 888
    //  888  888 .d888888 888 888
    //  888  888 888  888 888 Y88b.
    //  888  888 "Y888888 888  "Y888
    //---------------------------------------------------------------------------
    // this state is permanent due to an fatal error. The trap can be reset
    // via the Blynk app but if the error persists the halt state
    // will happen again.
    //---------------------------------------------------------------------------
  case HALT:
    // we CAN use delay here because the system is out of service anyway.
    // blink LED, SOS signal...
    digitalWrite(LED_MOUSE_CAUGHT, HIGH);
    delay(150);
    digitalWrite(LED_MOUSE_CAUGHT, LOW);
    delay(90);
    digitalWrite(LED_MOUSE_CAUGHT, HIGH);
    delay(150);
    digitalWrite(LED_MOUSE_CAUGHT, LOW);
    delay(90);
    digitalWrite(LED_MOUSE_CAUGHT, HIGH);
    delay(150);
    digitalWrite(LED_MOUSE_CAUGHT, LOW);
    delay(90);

    digitalWrite(LED_MOUSE_CAUGHT, HIGH);
    delay(350);
    digitalWrite(LED_MOUSE_CAUGHT, LOW);
    delay(90);
    digitalWrite(LED_MOUSE_CAUGHT, HIGH);
    delay(350);
    digitalWrite(LED_MOUSE_CAUGHT, LOW);
    delay(90);
    digitalWrite(LED_MOUSE_CAUGHT, HIGH);
    delay(350);
    digitalWrite(LED_MOUSE_CAUGHT, LOW);
    delay(90);

    digitalWrite(LED_MOUSE_CAUGHT, HIGH);
    delay(150);
    digitalWrite(LED_MOUSE_CAUGHT, LOW);
    delay(90);
    digitalWrite(LED_MOUSE_CAUGHT, HIGH);
    delay(150);
    digitalWrite(LED_MOUSE_CAUGHT, LOW);
    delay(90);
    digitalWrite(LED_MOUSE_CAUGHT, HIGH);
    delay(150);
    digitalWrite(LED_MOUSE_CAUGHT, LOW);
    delay(1000);
    // open all the door(s) permanently and stop the system because we have an
    // obstacle between the IR sensors or a malfunctioning IR sensor.
    servoTrapdoorClose();
    servoFrontdoorOpen();
    break;
  }
}

//*****************************************************************************
//  ######## ##     ## ##    ##  ######  ######## ####  #######  ##    ##
//  ##       ##     ## ###   ## ##    ##    ##     ##  ##     ## ###   ##
//  ##       ##     ## ####  ## ##          ##     ##  ##     ## ####  ##
//  ######   ##     ## ## ## ## ##          ##     ##  ##     ## ## ## ##
//  ##       ##     ## ##  #### ##          ##     ##  ##     ## ##  ####
//  ##       ##     ## ##   ### ##    ##    ##     ##  ##     ## ##   ###
//  ##        #######  ##    ##  ######     ##    ####  #######  ##    ##
//*****************************************************************************

// frontdoor to upper position
void servoFrontdoorOpen()
{
  frontDoorServo.write(SERVO_FRONTDOOR_UPPER_VALUE);
}
// frontdoor to lower position
void servoFrontdoorClose()
{
  frontDoorServo.write(SERVO_FRONTDOOR_LOWER_VALUE);
}

void servoTrapdoorOpen()
{
  // trapdoor to lower position
  trapDoorServo.write(SERVO_TRAPDOOR_LOWER_VALUE);
}
void servoTrapdoorClose()
{
  // trapdoor to upper position
  trapDoorServo.write(SERVO_TRAPDOOR_UPPER_VALUE);
}

//  888      8888888888 8888888b.       888      888 d8b          888
//  888      888        888  "Y88b      888      888 Y8P          888
//  888      888        888    888      888      888              888
//  888      8888888    888    888      88888b.  888 888 88888b.  888  888
//  888      888        888    888      888 "88b 888 888 888 "88b 888 .88P
//  888      888        888    888      888  888 888 888 888  888 888888K
//  888      888        888  .d88P      888 d88P 888 888 888  888 888 "88b
//  88888888 8888888888 8888888P"       88888P"  888 888 888  888 888  888
//
void blink_led()
{
  switch (ledState)
  {
  case BEGIN:
    if (mouseCounter > 0)
    {
      // init counter
      ledBlinkCounter = 0;
      ledState = LED_ON;
    }
    break;

  case LED_ON:
    digitalWrite(LED_MOUSE_CAUGHT, HIGH);
    // start LED on timer
    timerLedBlink = millis();
    // increase blink counter
    ledBlinkCounter += 1;
    // go to next state
    ledState = LED_ON_DELAY;
    break;

  case LED_ON_DELAY:
    // check if the LED_ON time has passed
    if (millis() - timerLedBlink > LED_ON_TIME)
    {
      // goto next state, else ignore and exit...
      ledState = LED_OFF;
    }
    break;

  case LED_OFF:
    digitalWrite(LED_MOUSE_CAUGHT, LOW);
    timerLedBlink = millis();
    ledState = LED_OFF_DELAY;
    break;

  case LED_OFF_DELAY:
    if (millis() - timerLedBlink > LED_OFF_TIME)
    {
      ledState = REPEAT_BLINK;
    }
    break;

  case REPEAT_BLINK:
    if (ledBlinkCounter < mouseCounter)
    {
      ledState = LED_ON;
    }
    else
    {
      timerLedBlink = millis();
      ledState = BLINK_PAUSE;
    }
    break;

  case BLINK_PAUSE:
    if (millis() - timerLedBlink > LED_DELAY)
    {
      ledState = START;
    }
    break;
  }
}

//   .d8888b.
//  d88P  Y88b
//  Y88b.
//   "Y888b.    .d88b.  88888b.  .d8888b   .d88b.  888d888 .d8888b
//      "Y88b. d8P  Y8b 888 "88b 88K      d88""88b 888P"   88K
//        "888 88888888 888  888 "Y8888b. 888  888 888     "Y8888b.
//  Y88b  d88P Y8b.     888  888      X88 Y88..88P 888          X88
//   "Y8888P"   "Y8888  888  888  88888P'  "Y88P"  888      88888P'
//
//
void checkIrSensors()
{
  // we need to check if the IR sensors are not obstructed for a long time
  // due to a bug or wire / electronic malfunction.

  //###########################################################################
  // check IR1 sensor
  //###########################################################################
  switch (sensorIr1CheckState)
  {
  case 0:
    // read the millis() value and set the timer
    timerIr1Check = millis();
    // next step
    sensorIr1CheckState = 1;
    break;
  case 1:
    // using the MODULO calculation we check every minute
    // only every 60 seconds the value of out calculation will be zero
    // the modulo calculation is very simple but in many cases very useful!
    if ((millis() - timerIr1Check) % 60000 == 0)
    {
      //one minute has passed so next step
      sensorIr1CheckState = 2;
    }
    break;
  case 2:
    // now we check if the sensor is obstructed (HIGH)
    if (digitalRead(IR_MOUSE_PRESENT) == HIGH)
    {
      // sensor is HIGH so just a mouse present or an obstruction/malfunction
      // go to step 3 to check if the time limit of the obstruction has passed
      sensorIr1CheckState = 3;
    }
    else
    {
      // every time the IR sensor is LOW, no mouse or obstruction,
      // we reset the time and start over.
      sensorIr1CheckState = 0;
    }
    break;
  case 3:
    // here we check if the time limit of the HIGH state of the sensor is
    // exceeded (time in minutes):
    if ((millis() - timerIr1Check) / 60000 >= IR_SENSOR_OBSTRUCTION_TIME_LIMIT)
    {
      fatalIr1Error = true;
    }
    else
    {
      // time not exceeded so do not reset the timer but continue monitoring
      sensorIr1CheckState = 1;
    }
    break;
  }
  //###########################################################################
  // check IR2 sensor
  //###########################################################################
  switch (sensorIr2CheckState)
  {
  case 0:
    timerIr2Check = millis();
    sensorIr2CheckState = 1;
    break;
  case 1:
    // using the MODULO calculation we check every minute
    if ((millis() - timerIr2Check) % 60000 == 0)
    {
      sensorIr2CheckState = 2;
    }
    break;
  case 2:
    if (digitalRead(IR_MOUSE_EATING) == HIGH)
    {
      sensorIr2CheckState = 3;
    }
...

This file has been truncated, please download it to see its full contents.

MouseOleum_8266_board

C/C++
This is the software for the Adafruit Huzzah 8266 breakout board to get the WIFI / IoT functionality. This is NOT the sketch for the Mousetrap itself. See the other sketch.
//
// MouseOleum mousetrap - IoT addon sketch
// Version 2.0
//                                                                                                  
// Erik de Ruiter - The Netherlands
// 2019-06-22 14:00
/*








  !intentionally left blank because you need to read this:


  when uploading to the arduino or 8266 board, 

  *******************************************************
  *******************************************************
  **                                                   **
  **  YOU NEED TO REMOVE THE 8266 BOARD FROM THE PCB ! **
  **                                                   **
  *******************************************************
  *******************************************************

 ! NOTE ON FLASHING THE ESP8266 Board when using a FDTI programmer
 ! DISCONNECT the Rx and Tx connections before flashing or remove
 ! the 8266 board from the PCB
 !
 ! Else the Rx and Tx connection to the Arduino will inhibit flashing

  












*/

// ! BE VERY CAREFUL !
// ! The Adafruit HUZZA 8266 breakout board has a level shifter on the
// ! Rx port so it's safe to connect to the Arduino.
// ! CHECK if you use another board that it has level shifters on each INPUT port!
// ! OUTPUT to the Arduino is 3.3 volts so that's OK because the
// ! Arduino will interpret that as 'HIGH'

// 2019-04-04 18:31

// !enable Blynk app serial communication, this must be the first line in the code
#define BLYNK_PRINT Serial
// define libraries
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <TimeLib.h>
// Blynk real time clock widget
#include <WidgetRTC.h>

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
// code for working trap: 
char auth[] = "your code";

// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "your ssid";
char pass[] = "your ssid code";

// initialize the Blynk timer
BlynkTimer timer;
// define the widgets used in the phone app
WidgetLED ledError(V12);
WidgetLED ledHeartbeat(V11);
WidgetTerminal terminal(V10);
// this widget sends the Blynk server time data to the sketch
// so we do not need a hardware RTC
WidgetRTC rtc;

#define LED_CONNECTED_OK 2
#define RESET_ARDUINO 14

int mousePresentCounter = 0;
int mouseCaughtCounter = 0;
bool led = true;
bool errorCondition = false;
bool disabledCondition = false;
bool mousePresentNotification = false;
const char startOfNumberDelimiter = '<';
const char endOfNumberDelimiter = '>';
static long receivedNumber = 0;

// define NOTIFY messages
// These 3 digit numbers have a purpose!
enum MESSAGE_ID
{
  message_MOUSE_PRESENT = 210,
  message_MOUSE_CAUGHT = 215,
  message_MOUSE_LEAVING = 230,
  message_SYSTEM_READY = 245,
  message_MANUAL_MODE = 250,
  message_REMOTE_DISABLE = 255,
  message_REMOTE_CATCH = 260,
  message_REOPEN_TRAPDOOR = 265,
  message_IR1_ERROR = 270,
  message_IR2_ERROR = 285,
  message_OBSTACLE_ERROR = 290,
};

// define NOTIFY messages
enum REMOTE_COMMANDS_ID
{
  command_CATCH = 101,
  command_DISABLE = 102,
  command_ENABLE = 103,
};

// define the used functions to keep the compiler happy
void sendData(void);
void heartbeat(void);
void sendTime(void);

//   ######  ######## ######## ##     ## ########
//  ##    ## ##          ##    ##     ## ##     ##
//  ##       ##          ##    ##     ## ##     ##
//   ######  ######      ##    ##     ## ########
//        ## ##          ##    ##     ## ##
//  ##    ## ##          ##    ##     ## ##
//   ######  ########    ##     #######  ##
void setup()
{
  Serial.begin(9600);

  // define inputs and outputs
  // 8266 board LED's are active LOW
  pinMode(LED_CONNECTED_OK, OUTPUT);
  pinMode(RESET_ARDUINO, OUTPUT);
  // initialize/turn LED's off
  digitalWrite(LED_CONNECTED_OK, HIGH);
  digitalWrite(RESET_ARDUINO, HIGH);

  // uncomment next line when NOT using your own Blynk server
  // Blynk.begin(auth, ssid, pass);
  
  // when using your own Blynk server (so easy and cheap, HIGHLY RECOMMENDED!)
  // ! notice: use COMMAS between digits, not dots
  Blynk.begin(auth, ssid, pass, IPAddress(192, 168, 178, 51), 8080);
  // start the RTC clock
  rtc.begin();

  if (Blynk.connected())
  {
    // turn on ESP8266 blue LED
    digitalWrite(LED_CONNECTED_OK, LOW);
  }

  // Setup a function to be called every x time
  timer.setInterval(1100L, sendData);
  // Setup a function to be called every x time
  timer.setInterval(1000L, heartbeat);
  // turn App error LED off
  ledError.off();
}

//  ##        #######   #######  ########
//  ##       ##     ## ##     ## ##     ##
//  ##       ##     ## ##     ## ##     ##
//  ##       ##     ## ##     ## ########
//  ##       ##     ## ##     ## ##
//  ##       ##     ## ##     ## ##
//  ########  #######   #######  ##
void loop()
{
  Blynk.run(); // Blynk heartbeat, needs to run continuously
  timer.run(); // Initiates BlynkTimer

  if (Serial.available())
  {
    byte c = Serial.read();
    switch (c)
    {
    case startOfNumberDelimiter:
      receivedNumber = 0;
      break;

    case '0' ... '9':
      receivedNumber *= 10;
      receivedNumber += c - '0';
      break;

    case endOfNumberDelimiter:
      // ********************************************
      // now we can evaluate the command received:
      switch (receivedNumber)
      {
      case message_MOUSE_PRESENT:
        // sent notification to Blynk app
        Blynk.notify("Mouse present!");
        // sent status to Blynk terminal widget
        sendTime();
        terminal.println("Mouse present!");
        terminal.flush();
        // increase counter
        // every mousepresent trigger is 5 seconds of mouse presence
        mousePresentCounter += 1;
        break;

      case message_MOUSE_LEAVING:
        // sent status to Blynk terminal widget
        sendTime();
        terminal.println("Mouse has left the trap");
        terminal.flush();
        break;

      case message_MOUSE_CAUGHT:
        // sent notification to Blynk app
        Blynk.notify("Mouse caught!");
        // sent status to Blynk terminal widget
        sendTime();
        terminal.println("Mouse caught!");
        terminal.flush();
        // increase counter
        mouseCaughtCounter += 1;
        break;

      case message_SYSTEM_READY:
        // reset error condition
        errorCondition = false;
        disabledCondition = false;
        // sent status to Blynk terminal widget
        terminal.clear();
        sendTime();
        terminal.println("MouseOleum is ready");
        terminal.flush();
        // reset error state Blynk widget
        ledError.off();
        // reset counters
        mousePresentCounter = 0;
        mouseCaughtCounter = 0;
        break;

      case message_MANUAL_MODE:
        // sent status to Blynk terminal widget
        sendTime();
        terminal.println("Manual mode, system halted");
        terminal.flush();
        break;

      case message_REMOTE_DISABLE:
        // set disabled condition
        disabledCondition = true;
        // sent status to Blynk terminal widget
        sendTime();
        terminal.println("MouseOleum is DISABLED");
        terminal.flush();
        break;

      case message_REOPEN_TRAPDOOR:
        // sent status to Blynk terminal widget
        sendTime();
        terminal.println("Trapdoor re-opened!");
        terminal.flush();
        break;

      case message_IR1_ERROR:
        // set error condition
        errorCondition = true;
        // sent notification to Blynk app
        //Blynk.notify("IR1 ERROR!");
        // sent status to Blynk terminal widget
        sendTime();
        terminal.println("ERROR IR1, system disabled");
        terminal.flush();
        // turn Blynk error LED widget on
        ledError.on();
        break;

      case message_IR2_ERROR:
        // set error condition
        errorCondition = true;
        // sent notification to Blynk app
        //Blynk.notify("IR2 ERROR!");
        // sent status to Blynk terminal widget
        sendTime();
        terminal.println("ERROR IR2, system disabled");
        terminal.flush();
        // turn Blynk error LED widget on
        ledError.on();
        break;

      case message_OBSTACLE_ERROR:
        // set error condition
        errorCondition = true;
        // sent notification to Blynk app
        //Blynk.notify("OBSTACLE ERROR!");
        // sent status to Blynk terminal widget
        sendTime();
        terminal.println("OBSTACLE, system disabled");
        terminal.flush();
        // turn Blynk error LED widget on
        ledError.on();
        break;
      } //switch (receivednumber)
    }   //switch (c)
  }     //if (Serial.available())
} //loop

//  ######## ##     ## ##    ##  ######  ######## #### #######  ##    ##  ######
//  ##       ##     ## ###   ## ##    ##    ##     ## ##     ## ###   ## ##    ##
//  ##       ##     ## ####  ## ##          ##     ## ##     ## ####  ## ##
//  ######   ##     ## ## ## ## ##          ##     ## ##     ## ## ## ##  ######
//  ##       ##     ## ##  #### ##          ##     ## ##     ## ##  ####       ##
//  ##       ##     ## ##   ### ##    ##    ##     ## ##     ## ##   ### ##    ##
//  ##        #######  ##    ##  ######     ##    #### #######  ##    ##  ######

// This function sends Arduino's up time every second to Virtual Pin (5).
// In the app, Widget's reading frequency should be set to PUSH. This means
// that you define how often to send data to Blynk App.

void sendData()
{
  // You can send any value at any time.
  // Please don't send more that 10 values per second.
  Blynk.virtualWrite(V0, mousePresentCounter);
  Blynk.virtualWrite(V1, mouseCaughtCounter);
}
void heartbeat()
{
  // change LED color
  if (errorCondition == true)
  {
    //change LED label and color
    Blynk.setProperty(V11, "label", "       ERROR!");
    Blynk.setProperty(V11, "color", "#FF0000"); // Send formatted HEX colour to vRGB.
  }
  else if (disabledCondition == true)
  {
    //change LED label and color
    Blynk.setProperty(V11, "label", "    DISABLED");
    Blynk.setProperty(V11, "color", "#FF8C00"); // Send formatted HEX colour to vRGB.
  }
  else
  {
    //change LED label and color
    Blynk.setProperty(V11, "label", "    SYSTEM OK");
    Blynk.setProperty(V11, "color", "#00FF00"); // Send formatted HEX colour to vRGB.
  }

  // switch led on/off
  if (led == true)
  {
    ledHeartbeat.on();
  }
  else
  {
    ledHeartbeat.off();
  }
  led = !led;
}

// This function will be called every time a button Widget
// in Blynk app writes is activated
// reset mouse present counter
BLYNK_WRITE(V2)
{
  if (param.asInt() == 0)
  {
    mousePresentCounter = 0;
  }
}

// This function will be called every time a button Widget
// in Blynk app writes is activated
// reset mice caught counter
BLYNK_WRITE(V3)
{
  if (param.asInt() == 0)
  {
    mouseCaughtCounter = 0;
  }
}

// This function will be called every time a button Widget
// in Blynk app writes is activated
// - disable the trap
BLYNK_WRITE(V8)
{
  if (param.asInt() == 1)
  {
    Serial.print(startOfNumberDelimiter);
    Serial.print(command_DISABLE); // send the number
    Serial.print(endOfNumberDelimiter);
    Serial.println();
  }
  if (param.asInt() == 0)
  {
    Serial.print(startOfNumberDelimiter);
    Serial.print(command_ENABLE); // send the number
    Serial.print(endOfNumberDelimiter);
    Serial.println();
  }
}

// This function will be called every time a button Widget
// in Blynk app writes is activated
// - manually catch a mouse
BLYNK_WRITE(V9)
{
  if (param.asInt() == 0)
  {
    Serial.print(startOfNumberDelimiter);
    Serial.print(command_CATCH); // send the number
    Serial.print(endOfNumberDelimiter);
    Serial.println();
  }
}

// Digital clock display of the time
void sendTime()
{
  char currentTime[9];
  // You can call hour(), minute(), ... at any time
  // Please see Time library examples for details
  sprintf(currentTime, "%02d:%02d:%02d ", hour(), minute(), second());
  // Send time to the App
  terminal.print(currentTime);
  terminal.flush();
}

/*
Comments

#define BLYNK_GREEN     "#23C48E"
#define BLYNK_BLUE      "#04C0F8"
#define BLYNK_YELLOW    "#ED9D00"
#define BLYNK_RED       "#D3435C"
#define BLYNK_DARK_BLUE "#5F7CD8"


Example of sending numbers by Serial
Author: Nick Gammon
Date: 31 March 2012

   Serial.print (startOfNumberDelimiter);    
   Serial.print (101); // send the number
   Serial.print (endOfNumberDelimiter);  
   Serial.println ();



// Example of sending numbers by Serial
// Author: Nick Gammon
// Date: 31 March 2012

const char startOfNumberDelimiter = '<';
const char endOfNumberDelimiter   = '>';

void setup ()
 { 
 srand (42);
 Serial.begin (115200);
 } // end of setup
 
void loop ()
 {
 Serial.println ("Starting ...");
 for (int i = 0; i < 10; i++)
   {
   Serial.print (startOfNumberDelimiter);    
   Serial.print (rand ());    // send the number
   Serial.print (endOfNumberDelimiter);  
   Serial.println ();
   }  // end of for

 delay (5000);
 }  // end of loop
*/

Credits

Erik de Ruiter

Erik de Ruiter

6 projects • 72 followers

Comments

Add projectSign up / Login