Help using the read_write "onChange" function to control a relay based on DHT readings

Hi there,

I am trying to control a heater and a humidifier using a couple solid state relays and a DHT22.

I need some assistance with the "read_write" section of the code. I am trying to turn on the relay when the humidity (DHT22) reads less than the new number that is entered through the dashboard (in the onChange function). I am struggling with how to write the if statement in this section based on the new value that is entered. I want it to turn on/off based on the DHT reading, but also when a new value is entered through the cloud.

Thank you so much! (some of this code is irrelevant because of the scale, but I figured everyone would want the full code).

Below is my code:

/* 
  Sketch generated by the Arduino IoT Cloud Thing "Untitled 3"
  https://create.arduino.cc/cloud/things/6088350c-964d-4523-af55-120d2ea9d56b 

  Arduino IoT Cloud Variables description

  The following variables are automatically generated and updated when changes are made to the Thing

  float weightCloudVariable;
  CloudRelativeHumidity humidity;
  bool relay;
  CloudTemperature temperature;

  Variables which are marked as READ/WRITE in the Cloud Thing will also have functions
  which are called when their values are changed from the Dashboard.
  These functions are generated with the Thing and added at the end of this sketch.
*/

#include "thingProperties.h"
#include <DHT.h>
#include <DHT_U.h>

//for ssr
#define relayPin 7

//declare DHT22
#define DHTPIN 2
#define DHTTYPE DHT22
DHT dht(DHTPIN,DHTTYPE);

//Declare softwareSerial
auto & scaleSerial = Serial1;

//Timing Variables
float previousWeight = 0;
float weightThreshold = 0.001;
unsigned long previousMillis = 0;
const long interval = 1000;





void setup() {

  // Initialize serial and wait for port to open:
  Serial.begin(9600);


   //Initialize software serial
  dht.begin();

  //initialize software serial
  scaleSerial.begin(9600);

  //pin mode for ssr
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, HIGH);
  

  // Defined in thingProperties.h
  initProperties();

  // Connect to Arduino IoT Cloud
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  
  /*
     The following function allows you to obtain more information
     related to the state of network and IoT Cloud connection and errors
     the higher number the more granular information you’ll get.
     The default is 0 (only errors).
     Maximum is 4
 */
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();

  
    
}

void loop() {
  ArduinoCloud.update();


  humidity = dht.readHumidity();
  Serial.print(" Humidity: ");
  Serial.println(humidity);
  
  temperature = dht.readTemperature();
  Serial.print("Temperature: ");
  Serial.print(temperature);

  delay(9600);

 // onRelayChange();

  onHumidityChange();

  if (onHumidityChange()<humidity))
  {
    digitalWrite(relayPin, HIGH);
  }
  else {
    digitalWrite(relayPin, LOW);
  }

  
// Scale Code
  //Get the current time
unsigned long currentMillis = millis();

  // Check if it's time to send data
  if (currentMillis - previousMillis >= interval) {
    // Save the current time as the last time we sent data
  previousMillis = currentMillis;

    // Read and process the scale data
    if (scaleSerial.available()) {
      String weightData = "";
// Read data from the scale
      while (scaleSerial.available()) {
        char c = scaleSerial.read();
        weightData += c;
      }

      float currentWeight = extractWeight(weightData);

        if(abs(currentWeight - previousWeight) > weightThreshold){
        previousWeight = currentWeight;

      Serial.println(currentWeight);
         Serial.println(weightData);
      }

       // Print the full raw data from the scale (for debugging purposes)
      Serial.println("Raw Data: " + weightData);

      // Extract only the numeric part of the data (the weight)
      float weightValue = extractWeight(weightData);

      // Print the extracted weight value
    Serial.println("Weight Value: " + String(weightValue));

      //Send the extracted weight value to the cloud
      weightCloudVariable = currentWeight; 
    }
  } 
  }

float extractWeight(String data){
  //data.trim();
  String numericPart = "";

  for (int i = 0; i < data.length(); i++) {

    if (isDigit(data[i]) || data[i] == '.'){
      numericPart += data[i];
    }
  }
    return numericPart.toFloat();
  
}


/*
  Since Relay is READ_WRITE variable, onRelayChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onRelayChange()  {
  //if(relay){
    //digitalWrite(relayPin, HIGH);
  //}
  //else {
    //digitalWrite(relayPin, LOW);
    
  }
  

  

/*
  Since Humidity is READ_WRITE variable, onHumidityChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onHumidityChange()  {


  if ???
 
  }


/*
  Since Temperature is READ_WRITE variable, onTemperatureChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onTemperatureChange()  {
  // Add your code here to act upon Temperature change
}

/*
  Since WeightCloudVariable is READ_WRITE variable, onWeightCloudVariableChange() is
  executed every time a new value is received from IoT Cloud.
*/



The concept behind "onChange" is often called state change detection:

You can do the same stuff on analog sensors: You compare the value to what it was before, and if it is different, act, and save the new value for next time.

I assume you know how to write the code for is it higher/lower which goes in the main loop after the readHumidity statement . As far as the change, use the OnChange event that is created by the cloud, that is what it is for.
I see you started an if statement, in the on change, but all you need is the turn on statement.

This is a little vague. What do you mean by "is it higher/lower"? For the turn on statement do you mean "digitalWrite(relayPin, HIGH)"?

Higher as in 20 is higher than 18.
IDK, I didn't look at details, just concepts but that sounds right

in loop() i see

  • why is onHumidtyChange() called twice
  • why is it used to compare to the humidity value when onHumidity returns a void instead of a value

shouldn't the test above take affect regardless of when some humidity target value is changed?


looks like you commented out some code and lost some braces, so besides the

beiing incomplete, the code won't compile

also looks like there's an extra parenthesis in


one other thing to consider is hysterisis, instead of turning something on/off at on value, turning it on at the value -X and turning in off at the value+X

1 Like

I have this here because this was my first idea, but I meant to comment it out. I called the onHumidityChange() so it runs every time. However, I was unsure if that was the right approach. I wanted to compare the humidity value with the changed value from the OnHumidity function but I realized I can't do that because its calling a void, which is where my question lies of how I can do that. for example, comparing the DHT read to the new value so the relay will turn on.

I like this idea, but again how do I implement this for the change value. Or how do I call for the changed value?

Should I have the full if statement in the OnChange function and also call the Onchange function in the loop?

Inside of the ArduinoCloud.update(); command, it would call the broken onHumiditychange() function, and then in the next line, you overwrite it with a new humidity value from the DHT.

What value do you want to act on? The one you updated from the cloud? Or the one from the DHT sensor?

1 Like

I can't try your code because I do not have the matching thingProperties.h file.

You might consider trying this untested replacement for your broken onHumidityChange() function:

/*
  Since Humidity is READ_WRITE variable, onHumidityChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onHumidityChange()  {
  if (humidity > 17) {
    Serial.print("Humidity is above 17");
  } else {
    Serial.print("Humidity is 17 or lower");
  }
}

I think that should print out one message or the other when the dashboard number changes.

Do both the dashboard variable and the one being written over by this dht.readHumidity() line have the same name?

Well, I guess both. I want it to act on the value from the cloud, but also actively reading the sensor in case it drops below. In that case it would be acting from the sensor.

If they are both the same name, if you change it on the dashboard, then the assignment in the program will change it back about 10 seconds later after the delay(9600).

Maybe you want another variable like humidityThreshold that you could control from the web, and use on the device to compare with the humidity reading?

look this over

const byte PinLed = LED_BUILTIN;
enum { Off = HIGH, On = LOW };

// ---------------------------------------------------------
int threshold  = 200;
int Hysterisis = 2;
int state      = Off;

// -------------------------------------
void controlSomething ()
{
    int sensor = analogRead (A0);       // pot
    Serial.println (sensor);

    if (On == state)  {
        if (threshold + Hysterisis < sensor)
            digitalWrite (PinLed, state = Off);
    }
    else if (threshold - Hysterisis > sensor)
        digitalWrite (PinLed, state = On);
    
}

// ---------------------------------------------------------
void monitorUserInterface ()
{
    if (Serial.available ())  {
        char buf [90];
        int  n = Serial.readBytesUntil ('\n', buf, sizeof(buf)-1);
        buf [n] = '\0';
        threshold = atoi (buf);
    }
}

// ---------------------------------------------------------
void
loop (void)
{
    controlSomething ();
    monitorUserInterface ();
    delay (1000);
}

// ---------------------------------------------------------
void
setup (void)
{
    Serial.begin (9600);
    pinMode (PinLed, OUTPUT);
    digitalWrite (PinLed, state = Off);
}