Arduino Uno Meat Curing Chamber Code Issues

Hello,

I am currently converting a small mini fridge into a meat curing chamber. The mini fridge has a glass door and I am installing LED strip lighting within, and also a small computer fan to bring in fresh air.

I am using an Arduino uno, a PIR sensor, and 2 relays.

I am mounting the PIR sensor so that whenever I wave my hand in front of the door, I would like the LED strip lighting within, to turn on. The computer fan will be used to bring in fresh air, which I only need to run for 2 minutes…once a day.

The issue I am having, is trying to get the PIR sensor to continuously function, while running a computer fan for 2 minutes out of a full day, and then delaying it the rest.

Any help would be greatly appreciated.

I have some of the delay’s set low at the moment for testing purposes.

Current Code:

//the time we give the sensor to calibrate (10-60 secs according to the datasheet)
int calibrationTime = 30;

//the time when the sensor outputs a low impulse
long unsigned int lowIn;

//the amount of milliseconds the sensor has to be low
//before we assume all motion has stopped
long unsigned int pause = 5000;

boolean lockLow = true;
boolean takeLowTime;

int pir1 = 13; //the digital pin connected to the PIR sensor’s output
int relay1 = 12; //the digital pin connect to the relay output

int fanON = 7; //the digital pin connected to relay to turn on the fan

void setup() {

Serial.begin(9600);
pinMode(pir1, INPUT);
pinMode(relay1, OUTPUT);
digitalWrite(pir1, LOW);
digitalWrite(relay1, HIGH);
pinMode(fanON, OUTPUT);
digitalWrite(fanON, LOW);

//give the sensor some time to calibrate
Serial.print(“calibrating sensor “);
for(int i = 0; i < calibrationTime; i++){
Serial.print(”.”);
delay(1000);
}
Serial.println(" done");
Serial.println(“SENSOR ACTIVE”);
delay(50);
}

void loop() {

while(digitalRead(pir1) == HIGH){
delay(1500);
digitalWrite(relay1, LOW); //the led visualizes the sensors output pin state
if(lockLow){
//makes sure we wait for a transition to LOW before any further output is made:
lockLow = false;
Serial.println("—");
Serial.print(“motion detected at “);
Serial.print(millis()/1000);
Serial.println(” sec”);
delay(30000);
}
takeLowTime = true;
}

while(digitalRead(pir1) == LOW){
digitalWrite(relay1, HIGH); //the led visualizes the sensors output pin state

if(takeLowTime){
lowIn = millis(); //save the time of the transition from high to LOW
takeLowTime = false; //make sure this is only done at the start of a LOW phase
}
//if the sensor is low for more than the given pause,
//we assume that no more motion is going to happen
if(!lockLow && millis() - lowIn > pause){
//makes sure this block of code is only executed again after
//a new motion sequence has been detected
lockLow = true;
Serial.print(“motion ended at “); //output
Serial.print((millis() - pause)/1000);
Serial.println(” sec”);
delay(50);

{
if(digitalRead(fanON) == LOW)
delay(10000);
digitalWrite(fanON, HIGH);

if(digitalRead(fanON) == HIGH)
delay(20000);
digitalWrite(fanON, LOW);

}

}
}
}

There are some tips that come to my mind when reading the sketch and seeing the photo. I will write down as many tips that I can think off, and you decide if you find some of them useful. To make it work in a straightforward way, you have to abandon the way your sketch is organized at this moment.

Hardware:
The breadboard is a problem. They have often bad contacts. It may be good now, but tomorrow it might have bad contacts.
The hot glue does not attach so every surface in the same way. When it comes loose, the relay module might touch other parts.
I would like to see a low voltage part and a high voltage part. Can you reorganize it in the same box ? The relay modules turned 180° with their high voltage side at the bottom in a high voltage part and all the low voltage things in the upper part.
Be careful with the power wires. Make them the right length and when they come loose, they should not touch something.

Sketch:
For a PIR sensor, you have better control when detecting the moment that the PIR sensor turns on and ignore when it turns off. For that the StateChangeDetection is used: https://www.arduino.cc/en/Tutorial/StateChangeDetection.
When you want to turn on something and still detect a button, then you have to upgrade your sketch to use millis(). Start with the BlinkWithDelay example: https://www.arduino.cc/en/tutorial/BlinkWithoutDelay. The millis() function can be used with intervals up to 50 days, so one day is no problem.
You do a digitalWrite() for the pin1 input pin. That is not needed, use the pinMode() with INPUT or INPUT_PULLUP and that's all.
The Arduino loop() runs over and over again. You can make a sketch run smooth by letting it run as many times as possible. That means you should not wait for something with a while-statement and try to use delay() only for very short delays.

A good way for code in the loop() function is this:

void loop()
{
  // -------------------------------------------------------------------
  // Gather all the information from sensors and buttons.
  // -------------------------------------------------------------------
  int pir = digitalRead( pirPin);

  
  // -------------------------------------------------------------------
  // Process the data
  // -------------------------------------------------------------------
  if( pir != lastPirState)
  {
    ...


  // -------------------------------------------------------------------
  // Set outputs
  // -------------------------------------------------------------------
  ...
}

The third part to write output pins is often included in the second part while processing the data.

To make it easier, you can use the BlinkWithoutDelay for the fan and still use delay() for the ledstrip. The great thing about the BlinkWithoutDelay is that it will catch up after that delay() has finished. So it will continue to run exactly once every 24 hours regardsless how often the ledstrip is activated.
When you want to do more things, then you could also use millis() to activated the ledstrip for some time. But that is for later.

An example to turn on the fan for a short time and wait a long time is my millis_different_on_and_off_times.ino.
An example to turn on the ledstrip for some time just once is my millis_single_delay.ino.

Also I autoformatted your code and put it into a code block. I believe you have some issues in your second while loop with respect to code that you assume is part of an if block but is not. And possibly code you assume is not part of an if block but is.

//the time we give the sensor to calibrate (10-60 secs according to the datasheet)
int calibrationTime = 30;

//the time when the sensor outputs a low impulse
long unsigned int lowIn;

//the amount of milliseconds the sensor has to be low
//before we assume all motion has stopped
long unsigned int pause = 5000;

boolean lockLow = true;
boolean takeLowTime;

int pir1 = 13;    //the digital pin connected to the PIR sensor's output
int relay1 = 12;  //the digital pin connect to the relay output

int fanON = 7;    //the digital pin connected to relay to turn on the fan

void setup() {

  Serial.begin(9600);
  pinMode(pir1, INPUT);
  pinMode(relay1, OUTPUT);
  digitalWrite(pir1, LOW);
  digitalWrite(relay1, HIGH);
  pinMode(fanON, OUTPUT);
  digitalWrite(fanON, LOW);


  //give the sensor some time to calibrate
  Serial.print("calibrating sensor ");
  for (int i = 0; i < calibrationTime; i++) {
    Serial.print(".");
    delay(1000);
  }
  Serial.println(" done");
  Serial.println("SENSOR ACTIVE");
  delay(50);
}


void loop() {



  while (digitalRead(pir1) == HIGH) {
    delay(1500);
    digitalWrite(relay1, LOW);   //the led visualizes the sensors output pin state
    if (lockLow) {
      //makes sure we wait for a transition to LOW before any further output is made:
      lockLow = false;
      Serial.println("---");
      Serial.print("motion detected at ");
      Serial.print(millis() / 1000);
      Serial.println(" sec");
      delay(30000);
    }
    takeLowTime = true;
  }


  while (digitalRead(pir1) == LOW) {
    digitalWrite(relay1, HIGH);  //the led visualizes the sensors output pin state

    if (takeLowTime) {
      lowIn = millis();          //save the time of the transition from high to LOW
      takeLowTime = false;       //make sure this is only done at the start of a LOW phase
    }
    //if the sensor is low for more than the given pause,
    //we assume that no more motion is going to happen
    if (!lockLow && millis() - lowIn > pause) {
      //makes sure this block of code is only executed again after
      //a new motion sequence has been detected
      lockLow = true;
      Serial.print("motion ended at ");      //output
      Serial.print((millis() - pause) / 1000);
      Serial.println(" sec");
      delay(50);

      { // this is an independent code block???
        if (digitalRead(fanON) == LOW)
          delay(10000);
        digitalWrite(fanON, HIGH); // should this be part of previous if block???

        if (digitalRead(fanON) == HIGH)
          delay(20000);
        digitalWrite(fanON, LOW);  // should this be part of previous if block???
      }
    } // end if
  } // end while
} // end loop()

Thanks for all the information in the replies, much appreciated.

I’m going to watch a few more YouTube videos explaining the Millis function. I understand what it does, but I’m having issues making it work how I want it to. I tried to use your Millis on/off sketch but couldn’t transpose it properly to get it to work with my relay on pin 7, instead of blinking the built in LED.

Is there a way to set the millis back to 0 after it runs for one day and do I even need to do that? If I need this program to keep executing the fan to run for 2 minutes for say…up to 3 months. Will that unsigned long fill up after 50 days if I’m not resetting the millis?

Thanks again,

When you use millis() as in the BlinkWithoutDelay example, then it will run fine. It will rollover from 0xFFFFFFFF to 0x00000000 after 50 days, but even during the rollover it will work fine. The led in the BlinkWithoutDelay example will be exactly the same time on and off continuously without any glitch (guaranteed).

Never try to reset millis(). Never try to calculate a millis value in the future. Never use a 'long', but always 'unsigned long' for a millis value.

For a full day minus 2 minutes, you could make a constant value.
The 'UL' after the number tells the compiler that they are 'unsigned long' numbers.

const unsigned long intervalRelayOn = 2UL * 60UL * 1000UL;   // 2 minutes
const unsigned long intervalRelayOff = (24UL * 60UL * 60UL * 1000UL) - intertervalRelayOn;   // one day minus 2 minutes

Some explanation is in the lower part: GitHub - Koepel/Fun_with_millis: Small Arduino examples using the millis() function..
You don't have to understand everything. When you can accept that the BlinkWithoutDelay is how millis() is used, then perhaps you can also understand when three millis() timers are used. That is my first example "millis_basic_demo.ino". The sketch sends text to the serial monitor. There are three different and seperated parts, doing their own thing and sending text.

You can try this. It uses millis() to control the PIR/relay and fan “simultaneously” and independently.

Compiles, not tested. Also:

  • I may not have interpreted your logic correctly so YMMV
  • I removed the serial print debug messages. You can add back in whatever you like
  • I used short fan times for your tested (15-sec every minute); you can comment these out and remove other comments to restore 2-min/day timing
//smaller values for bench-testing
#define TIME_FAN_IDLE       60000ul         //mS    fan run period  (60-sec intervals for dev/testing)
#define TIME_FAN_RUN        15000ul         //mS    fan run-time    (15-sec run time for dev/testing)
//#define TIME_FAN_IDLE       86280000ul      //mS    fan run period (86400 sec/day - 120-sec * 1000 mS/sec)
//#define TIME_FAN_RUN        120000ul        //mS    fan run time  (2 minutes)
//
//fan on/off logic levels
#define PINLVL_FAN_ON       HIGH            //change to suit your circuit if needed
#define PINLVL_FAN_OFF      LOW

#define PINLVL_RLY_ON       LOW             //change to suit your circuit if needed
#define PINLVL_RLY_OFF      HIGH

//fan state machine names
#define ST_FAN_OFF          0
#define ST_FAN_ON           1

//PIR state machine names
#define ST_NOMOTION         0
#define ST_CHECKMOTION      1
#define ST_HOLD_PIR         2
#define ST_WAIT_PIR_LOW     3

//PIR timing constants
#define TIME_MIN_PIR_HIGH   1500ul          //mS    minimum time for PIR to "see" something for qual
#define TIME_HOLD_PIR       30000ul         //mS    time relay is activated after PIR high time qual
#define TIME_MIN_PIR_LOW    5000ul          //mS    time PIR must see nothing (low) before state machine reset

//the time we give the sensor to calibrate (10-60 secs according to the datasheet)
const unsigned long calibrationTime = 30000ul;

const byte pir1 = 13;    //the digital pin connected to the PIR sensor's output
const byte relay1 = 12;  //the digital pin connect to the relay output
const byte fanON = 7;    //the digital pin connected to relay to turn on the fan

void setup() 
{
    Serial.begin(9600);
    
    pinMode( pir1, INPUT );
    digitalWrite(pir1, LOW);
    //
    pinMode(relay1, OUTPUT);
    digitalWrite(relay1, HIGH);    
    //
    pinMode(fanON, OUTPUT);
    digitalWrite(fanON, LOW);
    
    //give the sensor some time to calibrate
    Serial.print("calibrating sensor ");
    for (int i = 0; i < calibrationTime; i++) 
    {
        Serial.print(".");
        delay(1000);
        
    }//for
    
    Serial.println(" done");
    Serial.println("SENSOR ACTIVE");
    delay(50);
    
}//setup

void loop() 
{
    doPIRSensor();
    doFan();
        
}//loop()

void doPIRSensor( void )
{
    static byte
        statePIR=ST_NOMOTION;
    static unsigned long
        timePIR;
    unsigned long
        timeNow;

    timeNow = millis();
    switch( statePIR )
    {
        case    ST_NOMOTION:
            //does PIR show motion?
            if( digitalRead( pir1 ) == HIGH )
            {
                //yes; get the time and move to check state
                timePIR = timeNow;
                statePIR = ST_CHECKMOTION;                
                
            }//if
                
        break;

        case    ST_CHECKMOTION:
            //check if motion there for at least TIME_MIN_PIR_HIGH mS
            //if not, return to NOMOTION state
            if( digitalRead( pir1 ) == LOW )
                statePIR = ST_NOMOTION;
            else
            {
                if( (timeNow - timePIR) >= TIME_MIN_PIR_HIGH )
                {
                    //when motion qualified, turn on relay and set hold timer and state
                    digitalWrite( relay1, PINLVL_RLY_ON );
                    timePIR = timeNow;
                    statePIR = ST_HOLD_PIR;
                    
                }//if
                
            }//else
            
        break;

        case    ST_HOLD_PIR:
            //hold relay on for TIME_HOLD_PIR mS
            if( timeNow - timePIR <= TIME_HOLD_PIR )
                return;

            //at end of time period, turn off relay
            digitalWrite( relay1, PINLVL_RLY_OFF );
            //and get time and wait for sensor to show no activity
            timePIR = timeNow;
            statePIR = ST_WAIT_PIR_LOW;
            
        break;

        case    ST_WAIT_PIR_LOW:
            //if PIR shows high during this time, reset the low timer
            if( digitalRead( pir1 ) == HIGH )
                timePIR = timeNow;
            else
            {
                //have we been low for at least TIME_MIN_PIR_LOW mS?
                //if so, return to NOMOTION state
                if( (timeNow - timePIR) >= TIME_MIN_PIR_LOW )
                    statePIR = ST_NOMOTION;
                    
            }//else
        
        break;    
        
    }//switch
        
}//doPIRSensor

void doFan( void )
{
    static byte
        stateFan = ST_FAN_OFF;
    static unsigned long
        timeFanIdle=0,
        timeFanRun=0;
    unsigned long
        timeNow;

    timeNow = millis();
    switch( stateFan )
    {
        case    ST_FAN_OFF:
            if( (timeNow - timeFanIdle) >= TIME_FAN_IDLE )
            {
                digitalWrite( fanON, PINLVL_FAN_ON );
                timeFanIdle = timeNow;                  //measures period before next fan on
                timeFanRun = timeNow;                   //measure fan run time
                stateFan = ST_FAN_ON;
                
            }//if
            
        break;

        case    ST_FAN_ON:
            if( (timeNow - timeFanRun) >= TIME_FAN_RUN )
            {
                digitalWrite( fanON, PINLVL_FAN_OFF );
                stateFan = ST_FAN_OFF;
                
            }//if
            
        break;
        
    }//switch
        
}//doFan

Hey Blackfin,

Thank you for the sketch, for some reason when testing with lower numbers like below, it operates correct with the times...The fan is on for 10 seconds and then it turns off for 10 seconds.

#define TIME_FAN_IDLE 20000ul //mS fan run period
#define TIME_FAN_RUN 10000ul //mS fan run-time

Once I put in the values it will be functioning at, run for 2 minutes and off the rest of the day....it doesn't shut off after 2 minutes....it keeps running. Any idea why that would be?

#define TIME_FAN_IDLE 86280000ul //mS fan run period
#define TIME_FAN_RUN 120000ul //mS fan run-time

Thanks.

jhuraj:
Hey Blackfin,

Thank you for the sketch, for some reason when testing with lower numbers like below, it operates correct with the times…The fan is on for 10 seconds and then it turns off for 10 seconds.

#define TIME_FAN_IDLE 20000ul //mS fan run period
#define TIME_FAN_RUN 10000ul //mS fan run-time

Once I put in the values it will be functioning at, run for 2 minutes and off the rest of the day…it doesn’t shut off after 2 minutes…it keeps running. Any idea why that would be?

#define TIME_FAN_IDLE 86280000ul //mS fan run period
#define TIME_FAN_RUN 120000ul //mS fan run-time

Thanks.

Can I assume you found and corrected the bug:

const unsigned long calibrationTime = 30000ul;

to something like:

const byte calibrationTime = 30;

With the error, the calibration cycle basically never finishes. With that corrected, the code works on my 2560.

Hello, yeah I found that and deleted it....and the PIR seems to still function. Is a calibration necessary on the PIR's?

Perhaps I will copy and paste the original sketch and just try adjusting the 30000ul to 30ul and see if that fixes the fan timing issue??

Maybe I deleted something I shouldn't have.

Ok, so what I have found with more testing and using the below is:

#define TIME_FAN_IDLE 20000ul //mS fan run period
#define TIME_FAN_RUN 10000ul //mS fan run-time

During initial start up, the program running through the code for the first time it will do the following:

-Fan will run for the full idle time 20 seconds.

Once the code runs through the seconds loops, and all other loops going forward:

-The fan will run for the difference of (20000ul - 10000ul), so for 10 seconds.

-It will then be off for the fan "Run" time, of 10 seconds.

So it keeps cycling, 10 seconds on, 10 seconds off. I could make this work, other than the start up issue when it goes through the first initial loop. If I were to plug in the numbers I want it to run at, they would have to be...

#define TIME_FAN_IDLE 86400000ul //mS fan run period
#define TIME_FAN_RUN 86280000ul //mS fan run-time

On start up, the fan would run for a full day before it went into it's second loop and timed properly. Not entirely sure how to fix that.

Thank you.

Please clarify what logic level (high or low) turns on the fan. From what you describe, it looks like LOW does.

If so, change this:

//fan on/off logic levels
#define PINLVL_FAN_ON       HIGH            //change to suit your circuit if needed
#define PINLVL_FAN_OFF      LOW

so that _ON is LOW and _OFF is HIGH.

As well, for completeness, consider changing the pin initialization in setup() to:

   .
    .
    .
    digitalWrite(fanON, PINLVL_FAN_OFF);
    .
    .
    .

Yup, that seemed to do the trick.

Thank you for all the help throughout the process, and basically building the entire program for me...

I have a lot of learning ahead of me, but at least this project can now be tested with some real product!

Thanks everyone for all the help!