Automatic UV steriliser - program doesnt loop itself

Hello,
I am trying to create a UV Steriliser for rooms. I am using Arduino Uno, a relay, a PIR sensor and a quartz UV Tube with its ballast. I am trying to make a system which detects a person and turns on the UV for 15 mins once the person has moved out of its range. I had tested the following code. But it works only once. I need the system to be in a loop a work every time a person walks out of the room.

What I am trying to do is - read the PIR Sensor to detect if a person is in the room. If so, wait for him to move out and then turn on the bulb for 15 min. If someone enters again, then the bulb is turned off and the sterilisation should happen again.

All I have achieved so far is to turn on the bulb when the person leaves one time. Now the system doesn’t turn on the bulb once the process is complete or once the PIR detects motion in between.

A little guidance will be much appreciated. Thank you in advance.

int ledPin = 3;                // choose the pin for the LED
int inputPin = 2;               // choose the input pin (for PIR sensor)
int pirState = LOW;             // we start, assuming no motion detected
int val = 0;                    // variable for reading the pin status
bool needclean = false;
long cleanlength = 10000; 
void setup() {
  pinMode(ledPin, OUTPUT);      // declare LED as output
  pinMode(inputPin, INPUT);     // declare sensor as input

  Serial.begin(9600);
  //  Serial.println(cleanlength);
}

void loop() {
  val = digitalRead(inputPin);  // read input value
  if (val == HIGH) {            // check if the input is HIGH
    digitalWrite(ledPin, LOW);  // turn LED OFF
    needclean = true;
    if (pirState == LOW) {
      pirState = HIGH;
    }
  } else {
    if (pirState == HIGH) {
        pirState = LOW;
      if (needclean == true) {
        if (millis() <= cleanlength) {
          digitalWrite(ledPin, HIGH);
        }
        else {
          digitalWrite(ledPin, LOW);
          needclean = false;
         // pirState = LOW;
          //delay(10);
        }
      }
    }
  }
}

It looks to me like it will only “work” up to ten seconds after reset.
Is that correct?

See this tutorial on how to use millis(): https://forum.arduino.cc/index.php?topic=503368.0

This is great, too: Blink without delay() explained line-by-line

TheMemberFormerlyKnownAsAWOL:
It looks to me like it will only "work" up to ten seconds after reset.
Is that correct?

I have set 10 s for debugging. Later I will change that. As of now, it works for 10s and the system is idle. I want it to repeat the process which doesn't happen.

It won't ever repeat, unless you learn how to use millis() properly and rewrite the code.

Edit: actually, it will repeat when the millis() timer rolls over, around 50 days after starting the program.

jremington:
It won't ever repeat, unless you learn how to use millis() properly and rewrite the code.

Edit: actually, it will repeat when the millis() timer rolls over, around 50 days after starting the program.

I have edited code as given below. Now the device turns on after the PIR detection, but it doesnt turn off after the specified time. Could you please point out where I am going wrong?

int ledPin = 3;                // choose the pin for the LED
int inputPin = 2;               // choose the input pin (for PIR sensor)
int pirState = LOW;             // we start, assuming no motion detected
int val = 0;                    // variable for reading the pin status
bool needclean = false;
long cleanstart = 0;
long safetytimer = 0;
//long safetydelay = 100000;// 100 seconds
long safetydelay = 1000;// 5 seconds for debug
long cleanlength = 10000;
void setup() {
  pinMode(ledPin, OUTPUT);      // declare LED as output
  pinMode(inputPin, INPUT);     // declare sensor as input

  Serial.begin(9600);
  //  Serial.println(cleanlength);
}

void loop() {
  val = digitalRead(inputPin);  // read input value
  if (val == HIGH) {            // check if the input is HIGH
    digitalWrite(ledPin, LOW);  // turn LED OFF
    needclean = true;
    if (pirState == LOW) {
      pirState = HIGH;
    }
  } else {
    if (pirState == HIGH) {
      pirState = LOW;
      safetytimer =  millis();
      cleanstart = millis();

    }
  }
      if (millis() - safetytimer >= safetydelay) {
    if (needclean == true) {
      if (millis() - cleanstart >= cleanlength) {
        digitalWrite(ledPin, HIGH);
        needclean = false;
      }
        else {
          needclean = false;

          digitalWrite(ledPin, LOW);
          // pirState = LOW;
          //delay(10);
        }
      }
    }
  
  }

ALWAYS use unsigned long for variables associated with millis() and micros().

There are two times involved, and complicated logic. You forgot to add useful comments that section of the code. What time is the "turn off" time? What action is supposed to be the "turn off" action?

It sounds like this is how it should work:

  1. When a person enters the room if the UV is on then turn it off
  2. When the person leaves the UV should turn on for 15 minutes

jremington:
ALWAYS use unsigned long for variables associated with millis() and micros().

There are two times involved, and complicated logic. You forgot to add useful comments that section of the code. What time is the "turn off" time? What action is supposed to be the "turn off" action?

Sorry I haven't used useful comments before.

What the system is meant to do is: If someone enters the room, it will detect the person and once they exit the room and then start the sterilization for the time set. If someone enters in between, the UV will be turned off and the same process will continue. This is what I am trying to achieve.

int ledPin = 3;                // choose the pin for the LED
int inputPin = 2;               // choose the input pin (for PIR sensor)
int pirState = LOW;             // we start, assuming no motion detected
int val = 0;                    // variable for reading the pin status
bool needclean = false;
long cleanstart = 0;
long safetytimer = 0;
//long safetydelay = 100000;// 100 seconds
long safetydelay = 1000;// 5 seconds for debug
long cleanlength = 10000;
void setup() {
  pinMode(ledPin, OUTPUT);      // declare uv as output
  pinMode(inputPin, INPUT);     // declare sensor as input

  Serial.begin(9600);
  //  Serial.println(cleanlength);
}

void loop() {
  val = digitalRead(inputPin);  // read input value
  if (val == HIGH) {            // check if the input is HIGH
    digitalWrite(ledPin, LOW);  // motion is detected so turn UV OFF if it is on
    needclean = true;           // sterilisation is required
    if (pirState == LOW) {      
      pirState = HIGH;          // seeting it high so as to know motion detected
    }
  } else {
    if (pirState == HIGH) {
      pirState = LOW;
      safetytimer =  millis();
      cleanstart = millis();     

    }
  }
      if (millis() - safetytimer >= safetydelay) {      //checking if the time elapsed is higher than the safety delay
    if (needclean == true) {                            //checking if sterilisation is reuired ie if motion is detected
      if (millis() - cleanstart >= cleanlength) {       // loop to turn on the UV untill sterilisation time and then turn off
        digitalWrite(ledPin, HIGH);
        needclean = false;                              // resetting the sterilsation check
      }
        else {
          needclean = false;

          digitalWrite(ledPin, LOW);
          // pirState = LOW;
          //delay(10);
        }
      }
    }
  
  }

You should also look at the state change detection example since you really only want to know when a person entered the room or left the room

const int ledPin = 3;                // choose the pin for the LED
const int inputPin = 2;               // choose the input pin (for PIR sensor)
int previousPIRstate = LOW;             // we start, assuming no motion detected

bool isCleaning = false;
unsigned long cleanstart = 0;
//const unsigned long safetydelay = 100000;// 100 seconds
const unsigned long cleanlength = 10000;

void setup() {
  pinMode(ledPin, OUTPUT);      // declare LED as output
  pinMode(inputPin, INPUT);     // declare sensor as input

  Serial.begin(9600);
  //  Serial.println(cleanlength);
}

void loop() {
  int PIRstate = digitalRead(inputPin);  // read input value
  if ( previousPIRstate != PIRstate ) {
    // something has changed, either a person has entered or left
    if (PIRstate == HIGH) {
      // a person is present
      // turn off UV
      digitalWrite(ledPin, LOW);  // turn LED OFF
      isCleaning = false;
    } else {
      // a person has left so begin cleanse
      cleanstart = millis();
      digitalWrite(ledPin, HIGH);
      isCleaning = true;
    }
    delay(50);
  }
  previousPIRstate = PIRstate;

  if (isCleaning == true && millis() - cleanstart >= cleanlength) {
    digitalWrite(ledPin, LOW);
    isCleaning = false;
  }
}

Here is a version of your code that mostly works. Note that I added serial print statements to aid in debugging. It is still too complex for what it does. I will post a cleaner version later.

int ledPin = 3;                // choose the pin for the LED
int inputPin = 2;               // choose the input pin (for PIR sensor)
int pirState = LOW;             // we start, assuming no motion detected
int val = 0;                    // variable for reading the pin status
bool needclean = false;
unsigned long cleanstart = 0;
unsigned long safetytimer = 0;
unsigned long safetydelay = 1000;
unsigned long cleanlength = 10000;
void setup() {
  pinMode(ledPin, OUTPUT);      // declare LED as output
  pinMode(inputPin, INPUT);     // declare sensor as input

  Serial.begin(115200);
}

void loop() 
{
  val = digitalRead(inputPin);  
  if (val == HIGH) 
  {
    Serial.println("Motion detected");
    digitalWrite(ledPin, LOW);  // turn LED OFF
    needclean = true;
    pirState = HIGH;
  } 
  else 
  {
    if (pirState == HIGH) 
    {
      Serial.println("Motion gone");
      pirState = LOW;
      safetytimer =  millis();
      cleanstart = millis();
    }
  }
  if (millis() - safetytimer >= safetydelay) 
  {
    if (needclean == true) 
    {
      Serial.println(millis());
      if (millis() - cleanstart >= cleanlength) 
      {
        Serial.println("Clean expired");
        digitalWrite(ledPin, HIGH);
        needclean = false;
      }
    }
  }
}

Here is a version using state detection and a state machine:

const int ledPin = 3;
const int inputPin = 2;
byte prev_motion;
unsigned long cleanstart = 0;
const unsigned long cleanlength = 10000;
unsigned long safetystart = 0;
const unsigned long safetylength = 5000;

enum _state
{
  NOT_CLEANING,
  SAFETY_DELAY,
  CLEANING
};

_state state;

void setup()
{
  pinMode(ledPin, OUTPUT);
  pinMode(inputPin, INPUT);
  prev_motion = digitalRead(inputPin);
  // Set cleaning off
  state = NOT_CLEANING;
  digitalWrite(ledPin, HIGH);
  Serial.begin(9600);
}

void loop()
{
  byte motion;

  // Detect if motion is present or not
  motion = digitalRead(inputPin);
  if (motion != prev_motion)
  {
    delay(10); // debounce (if needed)
    if (motion == HIGH)
    {
      Serial.println("Motion detected");
      // Turn off cleaning
      state = NOT_CLEANING;
      digitalWrite(ledPin, HIGH);
    }
    else
    {
      Serial.println("Motion gone");
      // Safety delay
      state = SAFETY_DELAY;
      safetystart = millis();
    }
  }
  prev_motion = motion;

  switch (state)
  {
    case NOT_CLEANING:
      break;

    case SAFETY_DELAY:
      if (millis() - safetystart >= safetylength)
      {
        Serial.println("Safety delay complete");
        digitalWrite(ledPin, LOW);
        cleanstart = millis();
        state = CLEANING;
      }
      break;

    case CLEANING:
      if (millis() - cleanstart >= cleanlength)
      {
        Serial.println("Clean complete");
        digitalWrite(ledPin, HIGH);
        state = NOT_CLEANING;
      }
      break;

    default:
      Serial.println("!!!Invalid State!!!");
  }
}

Really, you shouldn't implement this without safety features. Short wave UV can seriously damage a person's eyesight. A naive visitor to the room wouldn't know that. They might just become curious about the purple light and go and stare at it.

aarg:
Really, you shouldn't implement this without safety features. Short wave UV can seriously damage a person's eyesight. A naive visitor to the room wouldn't know that. They might just become curious about the purple light and go and stare at it.

You are absolutely correct. The "safety delay" only provides a rudimentary level of safety because it assumes that if there is no motion for a set amount of time then no one is in the room.

Easy to implement a warning ‘sterilisation about to begin, please leave the room’. Or wtte.

aarg:
Really, you shouldn't implement this without safety features. Short wave UV can seriously damage a person's eyesight. A naive visitor to the room wouldn't know that. They might just become curious about the purple light and go and stare at it.

Basically the plan is to use it in a closed room. And the safety feature is just in case.

You want to use it in a room or a case?

pmagowan:
Easy to implement a warning ‘sterilisation about to begin, please leave the room’. Or wtte.

What about children?

shehzi:
Basically the plan is to use it in a closed room. And the safety feature is just in case.

All safety features are just in case. Nobody says, "hey I can crash my car today because I know it's equipped with airbags". You should put an interlock switch (and maybe flashing light) on the door - that's the approach usually taken for things like this. Think of a microwave oven.

TheMemberFormerlyKnownAsAWOL:
You want to use it in a room or a case?

Initially planned to do it on a closed room. But now due to the safety complications, setting it up inside a box.