Go Down

Topic: 1/7 variables does not update on LCD screen (Read 237 times) previous topic - next topic

blckft

Hello. I am building an automated terrarium. So far I have it set up how I want, such that certain actions happen when certain conditions are met. As part of my projects I have an LCD display running continuously with screens switching every 3-seconds while the program updates every n-minutes (5s in the posted code).

I am running into issues when the LCD has to display the light sensor reading. All my other variables update real time as the code runs, but the light variable does not change at all on the LCD. However, it updates on the Serial Monitor, so I know the connection is correct and the sensor works.

I suspect the issue lies in how/where I am initializing the rangeLight variable, however, I don't know how to fix it.

Does anyone have any insight?

Thank you in advance.

Code:

Code: [Select]
//Terrarium Sketch
// Updated: 15/sept/2018

// Include Libraries
#include <dht.h> //humidity and temperature sensor
#include <virtuabotixRTC.h> //real clock
#include <LiquidCrystal.h> // lcd

//Initialize and Define Pins
virtuabotixRTC myRTC(7, 6, 5);

dht DHT;
#define DHT11_PIN 22

const int sensorMin = 120;
const int sensorMax = 800;
int lightsensorPin = A0;


int soilsensorPin = A1; 
int soilsensorValue = 0; 
int soilpercent = 0;


int fanPin = 24;
int relay1 = 31; //humidifier
int relay2 = 33; //grow lamp
int relay3 = 35; //heating pad

int redPin = 9;
int greenPin = 10;
int bluePin = 11;

LiquidCrystal lcd(51, 49, 47, 45, 43, 41);

int convertTosoilpercent(int value)
{
  int soilpercentValue = 0;
  soilpercentValue = map(value, 0, 582, 0, 100);
  return soilpercentValue;
}

int rangeLight;

unsigned long startMillis;
unsigned long currentMillis;
const unsigned long period = 5000; 

long previousLCDMillis = 0;    // for LCD screen update
long lcdInterval = 3000;

// screen to show
int screen = 0;   
int screenMax = 3;
bool screenChanged = true;   // initially we have a new screen,  by definition
// defines of the screens to show
#define DATE           0
#define HUMIDITY       1
#define TEMPERATURE    2
#define LIGHT          3

//////////////////////////////////////
//
// LCD display functions
//
void showDate()
{
 lcd.clear();
 lcd.print("Date: ");
 lcd.print(myRTC.dayofmonth); //You can switch between day and month if you're using American system
 lcd.print("/");
 lcd.print(myRTC.month);
 lcd.print("/");
 lcd.print(myRTC.year);
 lcd.setCursor(0,1);
 lcd.print("Time: ");
 lcd.print(myRTC.hours);
 lcd.print(":");
 lcd.print(myRTC.minutes);
 lcd.print(":");
 lcd.print(myRTC.seconds);     
}

void showHumidity()
{
 lcd.clear();
 lcd.print("Soil Hum: ");
 lcd.print(soilpercent);
 lcd.print("%");
 lcd.setCursor(0,1);
 lcd.print("Air Hum: ");
 lcd.print(DHT.humidity);
}

void showTemperature()
{
 lcd.clear();
 lcd.print("Temp F = ");
 lcd.print((DHT.temperature*1.8)+32);
 lcd.setCursor(0,1);
 lcd.print("Temp C = ");
 lcd.print(DHT.temperature);
}

void showLight()
{
 lcd.clear();

 lcd.print("Light = ");
 switch (rangeLight)
    {
        // your hand is on the sensor
        case 0:
            lcd.print("Dark");
            break;
        // your hand is close to the sensor
        case 1:
            lcd.print("Dim");
            break;
        // your hand is a few inches from the sensor
        case 2:
            lcd.print("Medium");
            break;
        // your hand is nowhere near the sensor
        case 3:
            lcd.print("Bright");
            break;
    }
}



void setup() {
 
  Serial.begin(9600); // Initializes Serial Monitor

  lcd.begin(16,2); // Initializes LCD


  pinMode(fanPin,OUTPUT);
 
  pinMode(relay1,OUTPUT);
  pinMode(relay2,OUTPUT);
  pinMode(relay3,OUTPUT);
 
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);


//Start with all relays off
  digitalWrite(relay1,LOW);
  digitalWrite(relay2,LOW);
  digitalWrite(relay3,LOW);


}

void loop() {
// Read Values

currentMillis = millis();
if (currentMillis - startMillis >= period){
int chk = DHT.read11(DHT11_PIN);

myRTC.updateTime();


int analogLight;
int rangeLight;
analogLight = analogRead(lightsensorPin);
rangeLight = map(analogLight,sensorMin,sensorMax,0,3);


soilsensorValue = analogRead(soilsensorPin);
soilpercent = convertTosoilpercent(soilsensorValue);


// If Humidity is high OR Temp is high (>80F): turn fans on
if (DHT.humidity >= 80 || DHT.temperature > 30){
  digitalWrite(fanPin,HIGH);
} else {
  digitalWrite(fanPin,LOW);
}

// If Temp is low (<68F): turn on heating pad
if (DHT.temperature < 20) {
  digitalWrite(relay3,HIGH);
} else {
  digitalWrite(relay3,LOW);
}


// If Time of Day is between 8am and 3pm AND the light sensor detects dim or below: turn relay 2 (the lamp) on
  while (myRTC.hours > 8 && myRTC.hours < 15){
    if (rangeLight < 2){
        //(myRTC.hours > 8 && myRTC.hours < 15 && rangeLight < 2)
    digitalWrite(relay2,HIGH);
    } else {
    digitalWrite(relay2,LOW);
}
  }

//If the humidity is low: turn on humidifier
if (DHT.humidity < 40) {
  digitalWrite(relay1,HIGH);
} else {
  digitalWrite(relay1,LOW);
}

//Reflect Soil Moisture with LED: if high = green, if med = blue, if low = red
if (soilsensorValue > 465){
    digitalWrite(greenPin,HIGH);  // green
} else {
    digitalWrite(greenPin,LOW); }
if (soilsensorValue > 257 && soilsensorValue < 464){
  digitalWrite(bluePin,HIGH);  // blue
} else
 {
    digitalWrite(bluePin,LOW); }
if (soilsensorValue < 256){
    digitalWrite(redPin,HIGH);  // red
} else {digitalWrite(redPin,LOW); }


// Print to serial monitor
 Serial.print("\nCurrent Date / Time: ");
 Serial.print(myRTC.dayofmonth); //You can switch between day and month if you're using American system
 Serial.print("/");
 Serial.print(myRTC.month);
 Serial.print("/");
 Serial.print(myRTC.year);
 Serial.print(" ");
 Serial.print(myRTC.hours);
 Serial.print(":");
 Serial.print(myRTC.minutes);
 Serial.print(":");
 Serial.println(myRTC.seconds);
 Serial.print("Soil Analog Value: ");
 Serial.print(soilsensorValue);
 Serial.print("\nSoil Humidity: ");
 Serial.print(soilpercent);
 Serial.print("%");
 Serial.print("\nTemperature F = ");
 Serial.println((DHT.temperature*1.8)+32);
 Serial.print("Temperature C = ");
 Serial.println(DHT.temperature);
 Serial.print("Humidity = ");
 Serial.println(DHT.humidity);
 Serial.print("Light = ");
  switch (rangeLight)
    {
        // your hand is on the sensor
        case 0:
            Serial.println("Dark");
            break;
        // your hand is close to the sensor
        case 1:
            Serial.println("Dim");
            break;
        // your hand is a few inches from the sensor
        case 2:
            Serial.println("Medium");
            break;
        // your hand is nowhere near the sensor
        case 3:
            Serial.println("Bright");
            break;
    }

startMillis = currentMillis;
   
}
 
unsigned long currentLCDMillis = millis();

  if(currentLCDMillis - previousLCDMillis > lcdInterval)  // save the last time you changed the display
  {
    previousLCDMillis = currentLCDMillis;
    screen++;
    if (screen > screenMax) screen = 0;  // all screens done? => start over
    screenChanged = true;
  }

  // DISPLAY CURRENT SCREEN
  if (screenChanged)   //-- only update the screen if the screen is changed.
  {
    screenChanged = false; // reset for next iteration
    switch(screen)
    {
    case DATE:
      showDate();
      break;
    case HUMIDITY:
      showHumidity();
      break;
    case TEMPERATURE:
      showTemperature();
      break;
    case LIGHT:
      showLight();
      break;
    default:
      break;
    }
  }
}

J-M-L

#1
Sep 16, 2018, 12:29 am Last Edit: Sep 16, 2018, 12:31 am by J-M-L
Your issue is that you declare rangeLight twice, once as a global and once locally in the loop. Due to variables scope rules  the loop() modifies only the local one, the global one never changes


Also note that When you do rangeLight = map(analogLight,sensorMin,sensorMax,0,3); you are not constraining rangeLight between 0 and 3 if analogLight gets lower than sensorMin it will get negative and if analogLight gets above sensorMax it will be above 3. Note that map also will give you 3 only if your analogLight is exactly sensorMax

Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums
Pas de messages privés SVP

blckft

Thank you so much for your reply! I made both light sensor variables [(int analogLight;) & (int rangeLight;)] global variables and it works!

I'm so hyped!

Also note that When you do rangeLight = map(analogLight,sensorMin,sensorMax,0,3); you are not constraining rangeLight between 0 and 3 if analogLight gets lower than sensorMin it will get negative and if analogLight gets above sensorMax it will be above 3. Note that map also will give you 3 only if your analogLight is exactly sensorMax
Ohh, that explains why the sensor isn't very sensitive! Do you have a better way to handle this? Since I don't want to lose the "cases" for the switch variable, I'm afraid to start messing with the code based on my very limited knowledge of C++

Again thanks so much!

blh64

you need to constrain the value of 'analogLight' to be within range and luckily, there is a function with that exact name!
Code: [Select]

analogLight = constrain( analogLight, sensorMin, sensorMax );
rangeLight = map(analogLight,sensorMin,sensorMax,0,3);

blckft

you need to constrain the value of 'analogLight' to be within range and luckily, there is a function with that exact name!
Code: [Select]

analogLight = constrain( analogLight, sensorMin, sensorMax );
rangeLight = map(analogLight,sensorMin,sensorMax,0,3);


Thanks so much! The light sensor is waaaayyyyy more responsive now!


-----
On a "feel free to completely ignore this question" note:

If (based on my original code) I want the following to happen:

Run void loop() every n-minutes, but if the humidifier turns on: only run it for m-minutes

Can this be achieved by doing something as follows:

Code: [Select]
//If the humidity is low: turn on humidifier
if (DHT.humidity < 40) {
  currentHumMillis = millis();
if (currentHumMillis - startHumMillis >= Humperiod){
  digitalWrite(relay1,HIGH);
  startHumMillis = currentHumMillis;
}
} else {
  digitalWrite(relay1,LOW);
}


where the humidifier millis are defined with all the other variables & the above code replaces the same section in the original code:

Code: [Select]
unsigned long startHumMillis;
unsigned long currentHumMillis;
const unsigned long Humperiod = 1000; //(m-minutes)


Thanks again for all your help! (and sorry for all the newbie questions)

blh64

You can't really run loop() at a given timescale. It will re-run as soon as it finishes.  What you can do, it see how long it has been since the last time you did something and either exit loop() early or test or ???  just like you are doing for the humidifier.  Take a look at the the thread https://forum.arduino.cc/index.php?topic=503368.0 or this: https://forum.arduino.cc/index.php?topic=223286.0

blckft

Oh I'm sorry, I'm running millis() for my main loop too (the whole code is posted on my original post, but I'll repost it again)

As I have coded (posted below):

The void loop() runs & 'refreshes' every 5 seconds with the millis() function. But I want the humidifier to only turn on for 1 second if the turning on conditions are met. Will my code be able to do that as posted? Right now, I can see the relay turn on because the turn on condition is met, but it stays on even when the 1 second condition passes. What am I missing or doing wrong?

Thanks again, and sorry for the confusion :)

Code: [Select]
//Terrarium Sketch
// Updated: 16/sept/2018

// Include Libraries
#include <dht.h> //humidity and temperature sensor
#include <virtuabotixRTC.h> //real clock
#include <LiquidCrystal.h> // lcd

//Initialize and Define Pins
virtuabotixRTC myRTC(7, 6, 5);

dht DHT;
#define DHT11_PIN 22

const int sensorMin = 120;
const int sensorMax = 800;
int lightsensorPin = A0;


int soilsensorPin = A1; 
int soilsensorValue = 0; 
int soilpercent = 0;


int fanPin = 24;
int relay1 = 31; //humidifier
int relay2 = 33; //grow lamp
int relay3 = 35; //heating pad

int redPin = 9;
int greenPin = 10;
int bluePin = 11;

LiquidCrystal lcd(51, 49, 47, 45, 43, 41);

int convertTosoilpercent(int value)
{
  int soilpercentValue = 0;
  soilpercentValue = map(value, 0, 582, 0, 100);
  return soilpercentValue;
}


int analogLight;
int rangeLight;

unsigned long startMillis;
unsigned long currentMillis;
const unsigned long period = 5000; //5 mins = 300000 ms

unsigned long startHumMillis;
unsigned long currentHumMillis;
const unsigned long Humperiod = 1000; //1 min = 60000 ms
 

long previousLCDMillis = 0;    // for LCD screen update
long lcdInterval = 3000;

// screen to show
int screen = 0;   
int screenMax = 3;
bool screenChanged = true;   // initially we have a new screen,  by definition
// defines of the screens to show
#define DATE           0
#define HUMIDITY       1
#define TEMPERATURE    2
#define LIGHT          3

//////////////////////////////////////
//
// LCD display functions
//
void showDate()
{
 lcd.clear();
 lcd.print("Date: ");
 lcd.print(myRTC.dayofmonth); //You can switch between day and month if you're using American system
 lcd.print("/");
 lcd.print(myRTC.month);
 lcd.print("/");
 lcd.print(myRTC.year);
 lcd.setCursor(0,1);
 lcd.print("Time: ");
 lcd.print(myRTC.hours);
 lcd.print(":");
 lcd.print(myRTC.minutes);
 lcd.print(":");
 lcd.print(myRTC.seconds);     
}

void showHumidity()
{
 lcd.clear();
 lcd.print("Soil Hum: ");
 lcd.print(soilpercent);
 lcd.print("%");
 lcd.setCursor(0,1);
 lcd.print("Air Hum: ");
 lcd.print(DHT.humidity);
}

void showTemperature()
{
 lcd.clear();
 lcd.print("Temp F = ");
 lcd.print((DHT.temperature*1.8)+32);
 lcd.setCursor(0,1);
 lcd.print("Temp C = ");
 lcd.print(DHT.temperature);
}

void showLight()
{
 lcd.clear();

 lcd.print("Light = ");
 switch (rangeLight)
    {
        // your hand is on the sensor
        case 0:
            lcd.print("Dark");
            break;
        // your hand is close to the sensor
        case 1:
            lcd.print("Dim");
            break;
        // your hand is a few inches from the sensor
        case 2:
            lcd.print("Medium");
            break;
        // your hand is nowhere near the sensor
        case 3:
            lcd.print("Bright");
            break;
    }
}



void setup() {
 
  Serial.begin(9600); // Initializes Serial Monitor

  lcd.begin(16,2); // Initializes LCD


  pinMode(fanPin,OUTPUT);
 
  pinMode(relay1,OUTPUT);
  pinMode(relay2,OUTPUT);
  pinMode(relay3,OUTPUT);
 
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);


//Start with all relays off
  digitalWrite(relay1,LOW);
  digitalWrite(relay2,LOW);
  digitalWrite(relay3,LOW);


}

void loop() {
// Read Values

currentMillis = millis();
if (currentMillis - startMillis >= period){
int chk = DHT.read11(DHT11_PIN);

myRTC.updateTime();

analogLight = analogRead(lightsensorPin);
analogLight = constrain( analogLight, sensorMin, sensorMax );
rangeLight = map(analogLight,sensorMin,sensorMax,0,3);

soilsensorValue = analogRead(soilsensorPin);
soilpercent = convertTosoilpercent(soilsensorValue);


// If Humidity is high OR Temp is high (>80F): turn fans on
if (DHT.humidity >= 80 || DHT.temperature > 30){
  digitalWrite(fanPin,HIGH);
} else {
  digitalWrite(fanPin,LOW);
}

// If Temp is low (<68F): turn on heating pad
if (DHT.temperature < 20) {
  digitalWrite(relay3,HIGH);
} else {
  digitalWrite(relay3,LOW);
}


// If Time of Day is between 8am and 3pm AND the light sensor detects dim or below: turn relay 2 (the lamp) on
  while (myRTC.hours > 8 && myRTC.hours < 15){
    if (rangeLight < 2){
        //(myRTC.hours > 8 && myRTC.hours < 15 && rangeLight < 2)
    digitalWrite(relay2,HIGH);
    } else {
    digitalWrite(relay2,LOW);
}
  }

//If the humidity is low: turn on humidifier
if (DHT.humidity < 40) {
  currentHumMillis = millis();
  if (currentHumMillis - startHumMillis >= Humperiod){
    digitalWrite(relay1,HIGH);
    startHumMillis = currentHumMillis;
}
} else {
  digitalWrite(relay1,LOW);
}

//Reflect Soil Moisture with LED: if high = green, if med = blue, if low = red
if (soilsensorValue > 465){
    digitalWrite(greenPin,HIGH);  // green
} else {
    digitalWrite(greenPin,LOW); }
if (soilsensorValue > 257 && soilsensorValue < 464){
  digitalWrite(bluePin,HIGH);  // blue
} else
 {
    digitalWrite(bluePin,LOW); }
if (soilsensorValue < 256){
    digitalWrite(redPin,HIGH);  // red
} else {digitalWrite(redPin,LOW); }


// Print to serial monitor
 Serial.print("\nCurrent Date / Time: ");
 Serial.print(myRTC.dayofmonth); //You can switch between day and month if you're using American system
 Serial.print("/");
 Serial.print(myRTC.month);
 Serial.print("/");
 Serial.print(myRTC.year);
 Serial.print(" ");
 Serial.print(myRTC.hours);
 Serial.print(":");
 Serial.print(myRTC.minutes);
 Serial.print(":");
 Serial.println(myRTC.seconds);
 Serial.print("Soil Analog Value: ");
 Serial.print(soilsensorValue);
 Serial.print("\nSoil Humidity: ");
 Serial.print(soilpercent);
 Serial.print("%");
 Serial.print("\nTemperature F = ");
 Serial.println((DHT.temperature*1.8)+32);
 Serial.print("Temperature C = ");
 Serial.println(DHT.temperature);
 Serial.print("Humidity = ");
 Serial.println(DHT.humidity);
 Serial.print("Light = ");
  switch (rangeLight)
    {
        // your hand is on the sensor
        case 0:
            Serial.println("Dark");
            break;
        // your hand is close to the sensor
        case 1:
            Serial.println("Dim");
            break;
        // your hand is a few inches from the sensor
        case 2:
            Serial.println("Medium");
            break;
        // your hand is nowhere near the sensor
        case 3:
            Serial.println("Bright");
            break;
    }

startMillis = currentMillis;
   
}
 
unsigned long currentLCDMillis = millis();

  if(currentLCDMillis - previousLCDMillis > lcdInterval)  // save the last time you changed the display
  {
    previousLCDMillis = currentLCDMillis;
    screen++;
    if (screen > screenMax) screen = 0;  // all screens done? => start over
    screenChanged = true;
  }

  // DISPLAY CURRENT SCREEN
  if (screenChanged)   //-- only update the screen if the screen is changed.
  {
    screenChanged = false; // reset for next iteration
    switch(screen)
    {
    case DATE:
      showDate();
      break;
    case HUMIDITY:
      showHumidity();
      break;
    case TEMPERATURE:
      showTemperature();
      break;
    case LIGHT:
      showLight();
      break;
    default:
      break;
    }
  }
}

J-M-L

You are at a point where you need a proper state machine or your code will turn into spaghetti...

 
Please do not PM me for help,  others will benefit as well if you post your question publicly on the forums
Pas de messages privés SVP

blckft

Sounds like I need to accept the code for what it is and have everthing run as a unit.

Thanks again for all your help! I really appreciate it!

cattledog

Quote
But I want the humidifier to only turn on for 1 second if the turning on conditions are met. Will my code be able to do that as posted? Right now, I can see the relay turn on because the turn on condition is met, but it stays on even when the 1 second condition passes. What am I missing or doing wrong?
You can not embed a millis() timer with a one second period within a millis() timer with a 5 second period. You only get to look at the one second timer every five seconds.

I think that your intent is to run the humidifier for one second and then leave it off for four seconds to work with the lag of the humidity sensor readings.

A state machine approach may be best as suggested, but I think that you can simply leave the humidity reading, the setting of startHumMillis, and the humidifier turn on code where and as it is, and bring the turn off period control outside of the main 5 second timing loop.

Just move the turn off code to the very end of loop where it is not inside any other timed blocks.

Code: [Select]
if(millis() - startHumMillis >= Humperiod)
  {
    digitalWrite(relay1, LOW);//turn off humidifier
  }

Go Up