(AKA - Another Terrarium powered by Blynk) Peltier Heater/Cooler control via Sensirion SHT10, MCP23017 I/O expander, Wemos D1 Min & BLYNK

This took me far more time to write than it should have, but this code works within the limitations of only having digitalRead available. It’s not pretty but it’s functional. I’m going to roll most of this sketch and a few other bits into my existing project sketch and see how it goes…

// Experimental fan failure detection code for use with the MCP23017 port expander IC.




#include <SimpleTimer.h>
#include <Wire.h>
#include <Centipede.h>

int fanState1;// returns either HIGH or LOW
int fanState2;// returns either HIGH or LOW
int previousfanState1;// count of all HIGH's returned from the digitalRead(). Count is reset to 0 whenever a LOW is returned.
int previousfanState2;// count of all HIGH's returned from the digitalRead(). Count is reset to 0 whenever a LOW is returned.

SimpleTimer timer;
Centipede CS; // create Centipede object

void checkFanState() {

  fanState1 = CS.digitalRead(9);// digitalRead() will toggle between 0 and 1, depending on the reading frequency of the function & the RPM of the fan.
  fanState2 = CS.digitalRead(10);// 0 or LOW returned = Fans are ON. A continuously returned 1 or HIGH indicates that the fan is NOT rotating.
  previousfanState1 = previousfanState1 + fanState1;// creates an counter that increments by 1 for each digitalRead() that returns HIGH.
  previousfanState2 = previousfanState2 + fanState2;// creates an counter that increments by 1 for each digitalRead() that returns HIGH.
  

  if (fanState1 < 1 ) {// if a LOW is returned....
    previousfanState1 = 0;// reset the HIGH count to 0
    
    Serial.print("FAN1 ON");// Normal Fan operation
    Serial.println();
  }
  if (previousfanState1 > 10){// if a HIGH is returned more than 10 times in a row...we're assuming there's a problem.
    Serial.print("FAN1 MALFUNCTION");
    Serial.println();
  }
  else{
    Serial.println(previousfanState1);
  }
  if (fanState2 < 1 ) {// if a LOW is returned....
    previousfanState2 = 0;// reset the HIGH count to 0
    
    Serial.print("FAN2 ON");// Normal Fan operation
    Serial.println();
  }
  if (previousfanState2 > 10){// if a HIGH is returned more than 10 times in a row...we're assuming there's a problem.
    Serial.print("FAN2 MALFUNCTION");
    Serial.println();
  }
  else{
    Serial.println(previousfanState2);
    
  }
}

void setup() {

  Serial.begin(9600);
  Wire.begin(); // start I2C
  CS.initialize(); // set all register to default
  CS.pinMode(9, INPUT); //  tach signal 1
  CS.pinMode(10, INPUT); // tach signal 2
  CS.pinMode(8, OUTPUT); //SET PIN TO OUTPUT
  CS.digitalWrite(8, HIGH); // turns fans ON

  timer.setInterval(1000L, checkFanState);


}

void loop() {

  timer.run();

}



if the hardware are arduino uno, esp8266 sp01, DHT11…can you help me to make sketch and tutorial of blynk for running this project ?

Sorry, I don’t really have the time to re-write the entire project to fit the hardware you have. If you’d like to recreate this project for yourself, it would be far easier to use the same hardware that I used and then modify the software to include the DHT11. I started this project using the DHT22, so I do have some code available if needed.
I’m not familiar with the ESP8266 sp01, but it may be compatible with my sketch “as is”, since you’d only need 3 I/O pins to make it all work. 1 I/O pin for the DHT11 and the SDA and SCL pins for I2C communication with the MCP23017 port expander & the PCA8685 LED driver IC.

hello sir its amazing project thank u very much for sharing it with us .
i tried to compile the code ofc after download all the lib that u listed in the code but am gettung this error
i would be very thankful if u could guide me to it

Arduino: 1.8.2 (Windows 10), Board: "WeMos D1 R2 & mini, 80 MHz, 921600, 4M (3M SPIFFS)"

C:\Users\ALEN\Documents\Arduino\libraries\Sensirion-master\Sensirion.cpp:52:15: error: conflicting declaration 'const float D1'

   const float D1  = -40.1;          // for deg C @ 5V

               ^

In file included from C:\Users\ALEN\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.3.0\cores\esp8266/Arduino.h:283:0,

                 from C:\Users\ALEN\Documents\Arduino\libraries\Sensirion-master\Sensirion.cpp:21:

C:\Users\ALEN\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.3.0\variants\d1_mini/pins_arduino.h:38:22: error: 'D1' has a previous declaration as 'const uint8_t D1'

 static const uint8_t D1   = 5;

                      ^

exit status 1
Error compiling for board WeMos D1 R2 & mini.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

am useing wemos d1 mini
thank u in advance

dananmo

A little editing of the Sensirion .cpp file is all that’s needed. Since the Wemos D1 Mini operates at 3.3V, and the line of code that’s giving you trouble is meant for a 5V device, It can be commented out without causing any problems.

Look for this section of code in the .cpp file and comment out the offending line like I’ve done below-

// Temperature & humidity equation constants
  //const float D1  = -40.1;          // for deg C @ 5V
  const float D2h =   0.01;         // for deg C, 14-bit precision
  const float D2l =   0.04;         // for deg C, 12-bit precision

Once that’s done the code should compile normally. Please let me know if you have any questions.

This is a great project I like the cabinets for the lizards I got the Arduino code to compile just fine no problems just can not get the second part to work

thank u very much sir after the changes u told me to do now it compiling with no error .
thanks again

Here’s the latest QR code for the project

and the latest version of the software. All the listed features, including fan failure detection, are included and functional. I had to break the code down into two separate chunks as it all can’t fit into one post.

/* Updated 5/20/2017

  TERRARIUM HVAC & LIGHTING CONTROL powered by BLYNK.



  Humidity & Temperature levels are sensed by an Adafruit SHT10 soil Humidity/Temp sensor, and logic is used to determine
  if measured Humidity and/or Temperature parameters require adjustment.
  All control commands are sent via I2C to an MCP23017 I/O port expander or PCA9685 led controller IC to save precious I/O pins on the ESP8266.
  All relay control signals are held HIGH before being driven LOW to activate 4 "active LOW' relays.
  Relay #1 controls the Humidifier's A/C line directly.
  Relays #2 & 3 are used to toggle the direction of 12V current through a 60watt peltier device.
  Relays #2 & 3 are wired in such a way that both the Peltiers positive & negative wires are on the ground side of the circuit when the relays are at rest.
  a Heating or Cooling command energizes one of the two relays, passing +12V through the Peltier device in one direction for heaating and the other for cooling.
  Led lighting has Sunrise/Sunset dimming capabilities controlled by a PCA9685 IC. A Slider Widget determines the Maximum "Brightness", another slider widget sets the Fade Duration
  while a Timer Input Widget sets the length of the photoperiod.
  Relay #4 is used to switch ON/OFF a conventional 120V light bulb or "Basking Lamp", following the same schedule as the leds.

  BLYNK DASHBOARD SETUP-

  "MEASURED HUMIDITY"    GAUGE  ON (V0)  output set 0 to 100,  LABEL /pin./ %
  "MEASURED TEMP (F)"    GAUGE  ON (V1)  output set 50 to 100, LABEL /pin./ *F
  "DESIRED HUMIDITY"     GAUGE  ON (V5)  output set 5 to 100,  LABEL /pin./ %
  "DESIRED TEMPERATURE"  GAUGE  ON (V3)  output set 50 to 100, LABEL /pin./ *F
   HUMIDITY setting      STEP   ON (V4)  output set 5 to 100,  STEP-1.0,  SEND STEP- NO, LOOP VALUES= ON, ICONS +/-
   TEMPERATURE setting   STEP   ON (V2)  output set 50 to 100, STEP-1.0,  SEND STEP- NO, LOOP VALUES= ON, ICONS +/-
   TIMER  INPUT  Ch 1           ON (V13)                       START/STOP= YES, DAYOFWEEK= NO, SUNRISE/SUSET= YES, TIMEZONE SELECTION= NO.
   TIMER  INPUT  Ch 2           ON (V20)                       START/STOP= YES, DAYOFWEEK= NO, SUNRISE/SUSET= YES, TIMEZONE SELECTION= NO.
   LED MAX LEVEL Ch 1    SLIDER ON (V12) output set 0 to 100,  SEND ON RELEASE= ON.
   LED MAX LEVEL Ch 2    SLIDER ON (V18)  output set 0 to 100, SEND ON RELEASE= ON.
   LED FADE TIME Ch 1    SLIDER ON (V14)  output set 0 to 180, SEND ON RELEASE= ON.
   LED FADE TIME Ch 2    SLIDER ON (V19)  output set 0 to 180, SEND ON RELEASE= ON.
   LED CURRENT LEVEL CH 1 LABELED VALUE Display ON (V15), set to PUSH
   LED CURRENT LEVEL CH 2 LABELED VALUE Display ON (V17), set to PUSH
   TIME of DAY            LABELED VALUE Display ON (V16), set to PUSH
   
   LED INDICATOR WIDGETS-        (V6) COLOR= blue           LABEL- "HUM"
                                 (V7) COLOR= red            LABEL- "H"
                                 (V8) COLOR= blue           LABEL- "C"
                                 (V9) COLOR= red            LABEL- "M"
                                 (V10)COLOR= geeen          LABEL- "OK"
                                 (V11)COLOR= green          LABEL- "OK"
                         


  CODE FEATURES COMPLETED-

  Auto save Humidity & Temperaature settings to EEPROM - DONE
  WiFiManager autoconnect AP and BLYNK TOKEN (needed for login to Blynk server)- DONE
  RTC- DONE
  TIMER INPUT- For led & conventional lighting On/OFF period control- DONE
  Read Teperature/Humidity data using a Sensirion SHT10 sensor. - DONE
  Humidity/Temp Gauges & Settings step widgets- DONE
  Add ESP8266 OTA code to sketch - DONE
  Temperature and Humidity Relay control functions via MCP23017 port expander.- DONE
  Relay control code for external A/C light - DONE
  PWM LED lighting Sunrise/Sunset dimming control logic for two independent channels (PCA9685 Based)- DONE
  Fan failure detection & notification - DONE.
  
  
*/

#include <FS.h>                   // https://github.com/tzapu/WiFiManager This Library needs to be included FIRST!
#define BLYNK_PRINT Serial        // comment this out to save space
#include <BlynkSimpleEsp8266.h>
#include <SimpleTimer.h>
#include <Wire.h>
#include <Centipede.h>            // Library used to control the MCP23017 port expander chip. download available here - http://macetech.com/Centipede.zip
#include <ArduinoOTA.h>

//included libraries for WiFiManager - AutoConnectWithFSParameters
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager
#include <ArduinoJson.h>          //https://github.com/bblanchon/ArduinoJson
#include <EEPROM.h>

//added for RTC Widget (BLYNK app)
#include <TimeLib.h>
#include <WidgetRTC.h>

//added from Sensirion library for use with the SHT10 Humidity/Temp sensor- https://github.com/spease/Sensirion
#include <Sensirion.h> // comment out the line of code below found in the sensirion.cpp file to prevent compiler issues when using the code with the Wemos D1 Mini.
                       // "const float D1  = -40.1;          // for deg C @ 5V"
   
//Library used to control the PCA9685 PWM Controller IC https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library
#include <Adafruit_PWMServoDriver.h>

// ALL other libraries can be found here- https://github.com/blynkkk/blynk-library/releases/tag/v0.4.6



//LED status indicator widgets
WidgetLED led1(V6);  //led assigned to virtual pin V6  used for HUMIDIFIER ON/OFF indication
WidgetLED led2(V7);  //led assigned to virtual pin V7  used for HEATER ON/OFF indication
WidgetLED led3(V8);  //led assigned to virtual pin V8  used for COOLER ON/OFF indication
WidgetLED led4(V9);  //led assigned to virtual pin V9  used for SHT1X sensor malfunction indication
WidgetLED led5(V10); //led assigned to virtual pin V10 used for TEMP IN RANGE indication
WidgetLED led6(V11); //led assigned to virtual pin V11 used for HUMIDITY IN RANGE indication

// Sensirion sensor constants and variables.
const uint8_t dataPin =  D0;              // SHT serial data on pin D0 of the WEMOS D1 Mini.
const uint8_t sclkPin =  D3;              // SHT serial clock on pin D3 of the WEMOS D1 Mini.
const uint32_t TRHSTEP   = 5000UL;        // Sensor query period
uint16_t rawData;
byte measActive = false;
byte measType = TEMP;
unsigned long trhMillis = 0;              // Time interval tracking


Sensirion sht = Sensirion(dataPin, sclkPin);
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
WidgetRTC rtc;// REAL TIME CLOCK added to BLYNK app dashboard
Centipede CS; // From the Centipede library, it's functions are used to interact with the MCP23017 I2C I/O expander chip.
SimpleTimer timer;// From the SimpleTimer Library. it creates a timer object.


bool isFirstConnect = true;
// Humidity and Temperature variables
unsigned int desiredHum; // variable used to store the setpoint value for the desired humidity, once the measured humidity is lower than this value - the humDiff, start humidification.
int PreviousdesiredHum;// variable used to compare curent desired Humidity setting with the stored EEProm value.
int humDiff = 10; //  a negative differential that's used to prevent constant humidifier relay cycling near the desired setpoint.
float humidity;// the humidity reading from the SHT10 sensor
int desiredTemp;  //variable used to store the setpoint value for the desired temperture, once the measured temperture is more/less than this value (+/- the tempDiff), heating or cooling will be activated.
int PreviousdesiredTemp;// variable used to compare curent desired Temperature setting with the stored EEProm value.
int tempDiff = 1;          // the +/- temperature differential.
float temp;// the temperature reading from the SHT10 sensor

// FAN failure detection variables
int fanState1;// returns either HIGH or LOW
int fanState2;// returns either HIGH or LOW
int previousfanState1;// count of all HIGH's returned from the digitalRead(). Count is reset to 0 whenever a LOW is returned.
int previousfanState2;// count of all HIGH's returned from the digitalRead(). Count is reset to 0 whenever a LOW is returned.

////Led lighting & timing variables.
long nowseconds;            // time  now  in seconds
long startseconds0;         // start time in seconds for pwmLED0
long stopseconds0;          // stop  time in seconds for pwmLED0
long startseconds1;         // start time in seconds for pwmLED1
long stopseconds1;          // stop  time in seconds for pwmLED1

int fadetime0;
int fadetime1;
long fadetimemillis0;       // fadetime in millis() for pwmLED0
long fadetimemillis1;       // fadetime in millis() for pwmLED1

int minPWM = 1;// variable for min PWM value. Needs to be >= 1.
int maxPWM0; // variable for max PWM value attached to BLYNK Virtual pin.
int maxPWM1; // variable for max PWM value attached to BLYNK Virtual pin.

int currentFadePosition0;// don't change this!
int currentFadePosition1;// don't change this!

byte fadeIncrement = 1; //How smooth to fade? a setting of 1 uses all 12 bits (4096 steps).

unsigned long previousFadeMillis0;// millis() timing Variable, just for fading
unsigned long previousFadeMillis1;// millis() timing Variable, just for fading
int desiredledLevel0;
int desiredledLevel1;
int LedLevelNow0;
int LedLevelNow1;
long stepWaitTime0;  //How long to watch the clock before incrementing each step. (time in milliseconds)
long stepWaitTime1;  //How long to watch the clock before incrementing each step. (time in milliseconds)


// divide your desired dimming time duration(in millis) by the maxPWM variable value / fadeIncrement variable value to
// get the stepWaitTime variable value needed.
// EXAMPLE: (maxPWM = 4095/fadeIncrement = 1) to dim over 1 hour is == 3600000/4095/1 == stepWaitTime of 879 millis.

char blynk_token[34] = "YOUR_BLYNK_TOKEN";//added from WiFiManager - AutoConnectWithFSParameters

#define TestLED 2 // on board LED pin assignment

char Date[16];
char Time[16];

//flag for saving data
bool shouldSaveConfig = false;

//callback notifying the need to save config
void saveConfigCallback () {
  Serial.println("Should save config");
  shouldSaveConfig = true;
}

// All Relay Outputs correspond to PORT A pins on a MCP23017 chip at I2C address = 0x20;.
// All 4 Relay controlled loads are switched ON when MCP23017 outputs go LOW.
// Humidifier_Relay #1 = GPA3 // the pin number of the Humidifier relay. Centipede control pin #3 on the MCP23017 chip.
// Heater Relay     #2 = GPA2 // the pin number of the Heater relay.     Centipede control pin #2 on the MCP23017 chip.
// Cooler Relay     #3 = GPA1 // the pin number of the Cooler relay.     Centipede control pin #1 on the MCP23017 chip.
// Lighting Relay   #4 = GPA0 // the pin number of the Lighting relay.   Centipede control pin #0 on the MCP23017 chip.

// Additional control using PORT B pins on the MCP23017 chip.
// FAN CONTROL Activation Signal = GPB0 // Centipede control pin #8 on the MCP23017 chip.
// FAN CONTROL Input Tach Signal = GPB1 // Centipede control pin #9 on the MCP23017 chip.
// FAN CONTROL Input Tach Signal = GPB2 // Centipede control pin #10 on the MCP23017 chip.




// This function runs the SHT10 sensor and sends Humidity and Temperature values to gauge widgets in the BLYNK app.
// Humidity readings on (V0)
// Temperature readings on (V1)
void sendSensor() {
  // Read values from the SHT10 sensor
  unsigned long curMillis = millis();          // Get current time in millis

  // non-blocking calls to SHT10 sensor
  if (curMillis - trhMillis >= TRHSTEP) {      // Time for new measurements?
    measActive = true;
    measType = TEMP;
    sht.meas(TEMP, &rawData, NONBLOCK);        // Start temp measurement
    trhMillis = curMillis;
  }
  if (measActive && sht.measRdy()) {           // Note: no error checking
    if (measType == TEMP) {                    // Process temp or humi?
      measType = HUMI;
      temp = sht.calcTemp(rawData);            // Convert raw sensor data
      sht.meas(HUMI, &rawData, NONBLOCK);      // Start humidity measurement
    } else {
      measActive = false;
      humidity = sht.calcHumi(rawData, temp); // Convert raw sensor data
      logData();
    }
  }
  Blynk.virtualWrite(V0, humidity); // guage in BLYNK app showing "Actual Humidity" as reported by the SH10 sensor.
  Blynk.virtualWrite(V1, temp); // guage in BLYNK app showing "Actual Temp" as reported by the SHR10 sensor.

}
void logData() {
  Serial.print("Temperature = ");
  Serial.print(temp);
  Serial.println(" F ");
  Serial.print("Humidity = ");
  Serial.print(humidity);
  Serial.println(" % ");

}

///// Humidity & Temperature step settings & Gauge Widget functions. Used to set and provide an eye appealing Desired Humidity & Temperature gauge readout.

BLYNK_WRITE(V2) {// step widget to set the Desired Temp in BLYNK app.
  desiredTemp = param.asInt();
  Blynk.virtualWrite(V3, desiredTemp);// Gauge widget showing desired Temp In BLYNK app.

}
BLYNK_WRITE(V4) { // step widget to set the desired Humidity in BLYNK app.
  desiredHum = param.asInt();
  Blynk.virtualWrite(V5, desiredHum);// Gauge widget showing desired Humidity In BLYNK app.

}
// LED lighting functions...
void setLed() {
  if (currentFadePosition0 <= 1) {
    stepWaitTime0 = (fadetimemillis0 / maxPWM0);
    pwm.setPWM(0, 0, 0);
  }
  else {
    stepWaitTime0 = (fadetimemillis0 / maxPWM0);
    pwm.setPWM(0, 0, currentFadePosition0);
  }
  if (currentFadePosition1 <= 1) {
    stepWaitTime1 = (fadetimemillis1 / maxPWM1);
    pwm.setPWM(1, 0, 0);
  }
  else {
    stepWaitTime1 = (fadetimemillis1 / maxPWM1);
    pwm.setPWM(1, 0, currentFadePosition1);
  }
  Serial.print("desiredledLevel0 = ");
  Serial.println(desiredledLevel0);
  Serial.print("desiredledLevel1 = ");
  Serial.println(desiredledLevel1);

  Serial.print("stepWaitTime0 = ");
  Serial.println(stepWaitTime0);
  Serial.print("stepWaitTime1 = ");
  Serial.println(stepWaitTime1);


  Serial.print("currentFadePosition0 = ");
  Serial.println(currentFadePosition0);
  Serial.print("currentFadePosition1 = ");
  Serial.println(currentFadePosition1);
}
// Led fading function for pwmLED0, LED channel 1.
void ledFade0(unsigned long thisMillis0) {

  if (nowseconds < startseconds0) {
    currentFadePosition0 = minPWM;
  }
  if (nowseconds > startseconds0 && nowseconds < stopseconds0) {
    // is it time to start the Sunrise?
    // if not, nothing happens
    if (thisMillis0 - previousFadeMillis0 >= stepWaitTime0) {
      currentFadePosition0 = currentFadePosition0 + fadeIncrement;
      if (currentFadePosition0 >= maxPWM0) {
        // At max limit stop the fade
        currentFadePosition0 = maxPWM0;
      }
      // put actionable () here.
      
      // reset millis for the next iteration (fade timer only)
      previousFadeMillis0 = thisMillis0;
    }
  }
  if (nowseconds > stopseconds0) {
    // is it time to start the Sunset yet?
    // if not, nothing happens
    if (thisMillis0 - previousFadeMillis0 >= stepWaitTime0) {
      currentFadePosition0 = currentFadePosition0 - fadeIncrement;
      if (currentFadePosition0 <= minPWM) {
        // At min limit stop the fade
        currentFadePosition0 = minPWM;
      }
      // put actionable () here
      
      // reset millis for the next iteration (fade timer only)
      previousFadeMillis0 = thisMillis0;
    }
  }
}

// Led fading function for pwmLED1, LED channnel 2.
void ledFade1(unsigned long thisMillis1) {

  if (nowseconds < startseconds1) {
    currentFadePosition1 = minPWM;
  }
  if (nowseconds > startseconds1 && nowseconds < stopseconds1) {
    // is it time to start the Sunrise?
    // if not, nothing happens
    if (thisMillis1 - previousFadeMillis1 >= stepWaitTime1) {
      currentFadePosition1 = currentFadePosition1 + fadeIncrement;
      if (currentFadePosition1 >= maxPWM1) {
        // At max limit stop the fade
        currentFadePosition1 = maxPWM1;
      }
      // put actionable () here.
      // reset millis for the next iteration (fade timer only)
      previousFadeMillis1 = thisMillis1;
    }
  }
  if (nowseconds > stopseconds1) {
    // is it time to start the Sunset yet?
    // if not, nothing happens
    if (thisMillis1 - previousFadeMillis1 >= stepWaitTime1) {
      currentFadePosition1 = currentFadePosition1 - fadeIncrement;
      if (currentFadePosition1 <= minPWM) {
        // At min limit stop the fade
        currentFadePosition1 = minPWM;
      }
      // put actionable () here
      // reset millis for the next iteration (fade timer only)
      previousFadeMillis1 = thisMillis1;
    }
  }
}

// Digital clock display of the time
void clockDisplay()
{
  // You can call hour(), minute(), ... at any time
  // Please see Time library examples for details

  String currentDate = String(month()) + " " + day() + " " + year();
   // You can call hour(), minute(), ... at any time
  // Please see Time library examples for details
  if (minute() < 10 && second() < 10) {
    String currentTime = String(hour()) + ":0" + minute() + ":0" + second();// adds leading 0 to minutes and seconds.
    Blynk.virtualWrite(V16, currentTime);
  }
  if (minute() < 10 && second() > 9 ) {
    String currentTime = String(hour()) + ":0" + minute() + ":" + second();// adds leading 0 to minutes only.
    Blynk.virtualWrite(V16, currentTime);
  }
  if (minute() > 9 && second() < 10) {
    String currentTime = String(hour()) + ":" + minute() + ":0" + second();// adds leading 0 to seconds only.
    Blynk.virtualWrite(V16, currentTime);
  }
  if (minute() >= 10 && second() >= 10) {
    String currentTime = String(hour()) + ":" + minute() + ":" + second();// no leading 0's added.
    Blynk.virtualWrite(V16, currentTime);
  }
  nowseconds = ((hour() * 3600) + (minute() * 60) + second());
  Serial.print("Nowseconds =");
  Serial.println(nowseconds);
  // Send curent time of day in seconds to the App.
 
  Blynk.virtualWrite(V15, LedLevelNow0);// shows current led intensity pecentage 0 - 100%.
  LedLevelNow0 = map(currentFadePosition0, 1, 4095, 0, 100);

  Blynk.virtualWrite(V17, LedLevelNow1);// shows current led intensity pecentage 0 - 100%.
  LedLevelNow1 = map(currentFadePosition1, 1, 4095, 0, 100);

}
BLYNK_WRITE(V12) {// slider widget to set the maximum led level from the Blynk App.
  desiredledLevel0 = param.asInt();
  maxPWM0 = map(desiredledLevel0, 0, 100, minPWM, 4095);
}
BLYNK_WRITE(V18) {// slider widget to set the maximum led level from the Blynk App.
  desiredledLevel1 = param.asInt();
  maxPWM1 = map(desiredledLevel1, 0, 100, minPWM, 4095);
}
BLYNK_WRITE(V14) {// slider widget to set the led fade duration up tp 3 hours.
  fadetime0 = param.asInt();
  fadetimemillis0  = map(fadetime0, 0, 180, 1, 10800000);// 3 hour fade duration is max
  
}
BLYNK_WRITE(V19) {// slider widget to set the led fade duration up tp 3 hours.
  fadetime1 = param.asInt();
  fadetimemillis1  = map(fadetime1, 0, 180, 1, 10800000);// 3 hour fade duration is max
  
}
//Humidifier control functions- Switches Relay#1 ON/OFF to control A/C powered Humidifier.
void determineHumidifierOnOff()
{
  /// logic used to determine whether or not relative humidity should be raised.
  if (humidity > 1 && humidity < desiredHum - humDiff )// This function looks at the humidity level and subtracts the differential (humDiff) and switches the humidifier on if the prpoer conditions are met.  humDiff = 5 points under desired Humidity level.
  { // HUMIDIFIER ON
    CS.digitalWrite(3, LOW);//Humidifier Relay ON. Pin# assigment corresponds to MCP23017 I/O PORT A register #'s
    Serial.println("HUMIDIFIER ON");
    led1.on();//HUMIDIFIER INDICATOR ON
    led4.off();//MALFUNCTION INDICATOR OFF
    led6.off();//DESIRED RANGE INDICATION OFF
  }

  if (humidity < 1) // this function stops the humidifier if a non valid reading is received from the SHT1X sensor.
  { // HUMIDIFIER OFF
    CS.digitalWrite(3, HIGH);//Humidifier Relay OFF. Pin# assigment corresponds to MCP23017 I/O PORT A register #'s
    Serial.println("SHT10 Sensor Malfunction- HUMIDIFIER operation terminated");
    led1.off();////HUMIDIFIER INDICATOR OFF
    led4.on();//MALFUNCTION INDICATOR ON
    led6.off();//DESIRED RANGE INDICATION OFF
    Blynk.notify("TERRA HVAC Controller: SENSOR Malfuction");
  }

  if (humidity >= desiredHum)// this function stops the humidifier if the measured humidity is in the desired range.
  { // HUMIDIFIER OFF
    CS.digitalWrite(3, HIGH);//Hunidifier Relay OFF. Pin# assigment corresponds to MCP23017 I/O PORT A register #'s
    Serial.println("HUMIDIFIER OFF");
    led1.off();//HUMIDIFIER INDICATOR OFF
    led4.off();//MALFUNCTION INDICATOR OFF
    led6.on();//DESIRED RANGE INDICATION ON
  }

  if (humidity >= desiredHum - humDiff)
  {
   led6.on();//DESIRED RANGE INDICATION ON

  }
}

// Temperature Control function used to switch Heating/Cooling on/off by toggling the direction of current
// through a 60 watt Peltier device. The fans used to provide heating or cooling airflow are activated through a separate
// circuit, anytime current is flowing through the Peltier device.

void determineHeatorCool()
{

  /// logic used to determine whether or not heating or cooling is needed. Relay #2 = HEATER, Relay #3 = COOLER.

  if (temp < 6)// an unplugged sensor reads a constant 5 degrees.
  { // HEATER/COOLER OFF This function stops Heating/Cooling if a non valid value is received from the SHT10 sensor.
    CS.digitalWrite(2, HIGH);//Heater Relay OFF. Pin# assigment corresponds to MCP23017 I/O PORT A&B register #'s
    CS.digitalWrite(1, HIGH);//Cooler Relay OFF
    CS.digitalWrite(8, LOW);//FAN CONTROLLER OFF
    Serial.println("SHT10 Sensor Malfunction- HEATING/COOLING operation terminated");
    led2.off();//HEATING INDICATOR OFF
    led3.off();//COOLING INDICATOR OFF
    led4.on();//MALFUNCTION INDICATOR ON
    led5.off();//TEMP IN RANGE OFF
    Blynk.notify("TERRA HVAC Controller: SENSOR Malfuction");
  }

  if (temp > 10 && temp < desiredTemp - tempDiff)
  { // HEATER ON / COOLER OFF
    CS.digitalWrite(2, LOW);//Heater Relay ON. Pin# assigment corresponds to MCP23017 I/O PORT A&B register #'s
    CS.digitalWrite(1, HIGH);//Cooler Relay OFF
    CS.digitalWrite(8, HIGH);//FAN CONTROLLLER ON
    Serial.println("HEATING");
    led2.on();//HEATING INDICATOR ON
    led3.off();//COOLING INDICATOR OFF
    led4.off();//MALFUNCTION INDICATOR OFF
    led5.off();//TEMP IN RANGE OFF

    checkFanState();// Function checks for fan failure and send a notification to the Blynk App.
  }

  if (temp > desiredTemp + tempDiff )
  { // HEATER OFF/ COOLER ON
    CS.digitalWrite(2, HIGH);//Heater Relay OFF. Pin# assigment corresponds to MCP23017 I/O PORT A register #'s
    CS.digitalWrite(1, LOW);//Cooler Relay  ON
    CS.digitalWrite(8, HIGH);//FAN CONTROLLER ON
    Serial.println("COOLING");
    led2.off();//HEATING INDICATOR OFF
    led3.on();//COOLING INDICATOR ON
    led4.off();//MALFUNCTION INDICATOR OFF
    led5.off();//TEMP IN RANGE OFF

    checkFanState();// Function checks for fan failure and send a notification to the Blynk App.
  }
  if (temp > desiredTemp - tempDiff  && temp < desiredTemp + tempDiff)
  { // HEATER OFF/ COOLER OFF.... TEMP IN DESIRED RANGE.
    CS.digitalWrite(2, HIGH);//Heater Relay OFF. Pin# assigment corresponds to MCP23017 I/O PORT A&B register #'s
    CS.digitalWrite(1, HIGH);//Cooler Relay OFF
    CS.digitalWrite(8, LOW);//FAN CONTROLLER OFF
    Serial.println("TEMP = DESIRED RANGE");
    led2.off();//HEATING INDICATOR OFF
    led3.off();//COOLING INDICATOR OFF
    led4.off();//MALFUNCTION INDICATOR OFF
    led5.on();//TEMP IN RANGE ON
  }
}
void LightsOn()// relay #4  tied to (GPA I/Opin 0).
{
  CS.digitalWrite(0, LOW);//LIGHT Relay ON
}
void LightsOff()// relay #4 tied to (GPA I/Opin 0).
{
  CS.digitalWrite(0, HIGH);//LIGHT Relay OFF
}

void checkFanState() {

  fanState1 = CS.digitalRead(9);// digitalRead() will toggle between 0 and 1, depending on the reading frequency of the function & the RPM of the fan.
  fanState2 = CS.digitalRead(10);// 0 or LOW returned = Fans are ON. A continuously returned 1 or HIGH indicates that the fan is NOT rotating.
  previousfanState1 = previousfanState1 + fanState1;// creates an counter that increments by 1 for each digitalRead() that returns HIGH.
  previousfanState2 = previousfanState2 + fanState2;// creates an counter that increments by 1 for each digitalRead() that returns HIGH.
  

  if (fanState1 < 1 ) {// if a LOW is returned....
    previousfanState1 = 0;// reset the HIGH count to 0
    
    Serial.print("FAN1 ON");// Normal Fan operation
    Serial.println();
  }
  if (previousfanState1 > 10){// if a HIGH is returned more than 10 times in a row...we're assuming there's a problem.
    Serial.print("FAN1 MALFUNCTION");
    Serial.println();
    Blynk.notify("FAN1 MALFUNCTION");

  }
  else{
    Serial.println(previousfanState1);
  }
  if (fanState2 < 1 ) {// if a LOW is returned....
    previousfanState2 = 0;// reset the HIGH count to 0
    
    Serial.print("FAN2 ON");// Normal Fan operation
    Serial.println();
  }
  if (previousfanState2 > 10){// if a HIGH is returned more than 10 times in a row...we're assuming there's a problem.
    Serial.print("FAN2 MALFUNCTION");
    Serial.println();
    Blynk.notify("FAN2 MALFUNCTION");

  }
  else{
    Serial.println(previousfanState2);
    
  }
}

void GetPresets() {// Pre-set Humidity/Temperature values are stored in ESP8266 memory.

  if (desiredTemp != PreviousdesiredTemp) {  //update the EEPROM if desired temperature has changed.
    EEPROM.write(1, desiredTemp);
    EEPROM.commit();
    Serial.print(F("New desired temperature saved: "));
    Serial.println(desiredTemp);
    PreviousdesiredTemp = desiredTemp;
  }
  desiredTemp = EEPROM.read(1);
  if ((desiredTemp < 50) || (desiredTemp > 100)) {
    desiredTemp = 75;
    Serial.println(F("No saved temperature setting."));
  }
  if (desiredHum != PreviousdesiredHum) {  //update the EEPROM if desired temperature has changed.
    EEPROM.write(2, desiredHum);
    EEPROM.commit();
    Serial.print(F("New desired humidity level saved: "));
    Serial.println(desiredHum);
    PreviousdesiredHum = desiredHum;
  }
  desiredHum = EEPROM.read(2);
  if ((desiredHum < 0) || (desiredHum > 100)) {
    desiredHum = 50;
    Serial.println(F("No saved humidity setting."));
  }
}
void activetoday() {       // check if schedule should run today
  if (year() != 1970) {
    Blynk.syncVirtual(V13); // sync timeinput widget
  }
}

BLYNK_WRITE(V13) {
  sprintf(Date, "%02d/%02d/%04d",  day(), month(), year());
  sprintf(Time, "%02d:%02d:%02d", hour(), minute(), second());

  TimeInputParam t(param);

  Serial.print("Checked schedule at: ");
  Serial.println(Time);
  int dayadjustment = -1;
  if (weekday() == 1) {
    dayadjustment =  6; // needed for Sunday, Time library is day 1 and Blynk is day 7
  }
  if (t.isWeekdaySelected((weekday() + dayadjustment))) { //Time library starts week on Sunday, Blynk on Monday
    Serial.println("Schedule ACTIVE today");
    if (t.hasStartTime()) // Process start time
    {
      Serial.println(String("Start: ") + t.getStartHour() + ":" + t.getStartMinute());
    }
    if (t.hasStopTime()) // Process stop time
    {
      Serial.println(String("Stop : ") + t.getStopHour() + ":" + t.getStopMinute());
    }
    // Display timezone details, for information purposes only
    Serial.println(String("Time zone: ") + t.getTZ()); // Timezone is already added to start/stop time
    Serial.println(String("Time zone offset: ") + t.getTZ_Offset()); // Get timezone offset (in seconds)

    for (int i = 1; i <= 7; i++) {  // Process weekdays (1. Mon, 2. Tue, 3. Wed, ...)
      if (t.isWeekdaySelected(i)) {
        Serial.println(String("Day ") + i + " is selected");
      }
    }
    nowseconds = ((hour() * 3600) + (minute() * 60) + second());
    startseconds0 = (t.getStartHour() * 3600) + (t.getStartMinute() * 60);
    //Serial.println(startsecondswd);  // used for debugging
    if (nowseconds >= startseconds0) {
      Serial.print("LIGHTS ON");
      Serial.println(String(" ") + t.getStartHour() + ":" + t.getStartMinute());
      if (nowseconds <= startseconds0 + 90) {  // 90s on 60s timer ensures 1 trigger command is sent
        digitalWrite(TestLED, LOW); // set LED ON
        // code here to switch the leds & Light ON
        LightsOn();
      }
    }
    else {
      Serial.println("LIGHTS OFF");
      // nothing more to do here, waiting for motor to be turned on later today
    }
    stopseconds0 = (t.getStopHour() * 3600) + (t.getStopMinute() * 60);
    //Serial.println(stopsecondswd);  // used for debugging
    if (nowseconds >= stopseconds0) {
      Serial.print("LEDs STOPPED at");
      Serial.println(String(" ") + t.getStopHour() + ":" + t.getStopMinute());
      if (nowseconds <= stopseconds0 + 90) { // 90s on 60s timer ensures 1 trigger command is sent
        digitalWrite(TestLED, HIGH); // set LED OFF
        // code here to switch the leds & Light OFF
        LightsOff();
      }
    }
    else {
      if (nowseconds >= startseconds0) { // only show if motor has already started today
        Serial.println("LIGHTS ON");
        // nothing more to do here, waiting for motor to be turned off later today
      }
    }
  }
  else {
    Serial.println("LIGHTS OFF");
    // nothing to do today, check again in 1 minutes time
  }
  Serial.println();
}
BLYNK_WRITE(V20) {

  TimeInputParam t(param);
  Serial.print("Checked schedule at: ");
  Serial.println(Time);
  int dayadjustment = -1;
  if (weekday() == 1) {
    dayadjustment =  6; // needed for Sunday, Time library is day 1 and Blynk is day 7
  }
  if (t.isWeekdaySelected((weekday() + dayadjustment))) { //Time library starts week on Sunday, Blynk on Monday
    Serial.println("Schedule ACTIVE today");
    nowseconds = ((hour() * 3600) + (minute() * 60) + second());
    startseconds1 = (t.getStartHour() * 3600) + (t.getStartMinute() * 60);
    if (nowseconds >= startseconds1) {
      if (nowseconds <= startseconds1 + 90) {  // 90s on 60s timer ensures 1 trigger command is sent

        // code here
      }
    }
    else {
      Serial.println("Relay not on");// nothing more to do here, waiting for relay to be turned on later today
    }
    stopseconds1 = (t.getStopHour() * 3600) + (t.getStopMinute() * 60);
    if (nowseconds >= stopseconds1) {
      // 90s on 60s timer ensures 1 trigger command is sent
      if (nowseconds <= stopseconds1 + 90) {

        // code here
      }
    }
    else {
      if (nowseconds >= startseconds1) { // only show if motor has already started today
        Serial.println("Relay is still ON");
        // nothing more to do here, waiting for motor to be turned off later today
      }
    }
  }
  else {
    Serial.println("Schedule INACTIVE today");
    // nothing to do today, check again in 1 minutes time
  }
  Serial.println();
}


void reconnectBlynk() {
  if (!Blynk.connected()) {
    if (Blynk.connect()) {
      BLYNK_LOG("Reconnected");
    } else {
      BLYNK_LOG("Not reconnected");
    }
  }
}


1 Like

Here’s the setup() and loop() portion of the code-

void setup()
{
  // put your setup code here, to run once:
  ArduinoOTA.begin();// Arduino "Over the Air Update" initializer ()
  pinMode(TestLED, OUTPUT);
  digitalWrite(TestLED, HIGH); // set LED OFF
  Serial.begin(115200);// Start serial communication....
  Wire.begin(); // start the I2C communication protocol

  CS.initialize(); // initalize the MCP23017 I/O expander chip.
  CS.pinMode(3, OUTPUT); //SET PIN TO OUTPUT. Pin# assigment corresponds to MCP23017 I/O PORT A&B register #'s
  CS.pinMode(2, OUTPUT); //SET PIN TO OUTPUT
  CS.pinMode(1, OUTPUT); //SET PIN TO OUTPUT
  CS.pinMode(0, OUTPUT); //SET PIN TO OUTPUT
  CS.pinMode(8, OUTPUT); //SET PIN TO OUTPUT
  CS.pinMode(9, INPUT);  //SET PIN TO INPUT, will be used for fan failure detection.
  CS.pinMode(10, INPUT); //SET PIN TO INPUT, will be used for fan failure detection.

  CS.digitalWrite(3, HIGH); //(#1) HUMIDIFIER RELAY set to OFF when HIGH. Pin# assigment corresponds to MCP23017 I/O PORT A&B register #'s
  CS.digitalWrite(2, HIGH); //(#2) HEATER RELAY     set to OFF when HIGH
  CS.digitalWrite(1, HIGH); //(#3) COOLER RELAY     set to OFF when HIGH
  CS.digitalWrite(0, HIGH); //(#4) LIGHT RELAY      set to OFF when HIGH
  CS.digitalWrite(8, LOW); //      FAN CONTROL      set to OFF when LOW  (used with custom PcB only)

  //Load any saved settings from the EEPROM
  EEPROM.begin(20);
  Serial.println(F("STARTING UP : LOADING SETTINGS FROM MEMORY"));
  Serial.println(F(""));
  delay(1000);




  //The following code is borrowed from WiFiManager
  //clean FS, for testing
  //SPIFFS.format();

  //read configuration from FS json
  Serial.println("mounting FS...");

  if (SPIFFS.begin()) {
    Serial.println("mounted file system");
    if (SPIFFS.exists("/config.json")) {
      //file exists, reading and loading
      Serial.println("reading config file");
      File configFile = SPIFFS.open("/config.json", "r");
      if (configFile) {
        Serial.println("opened config file");
        size_t size = configFile.size();
        // Allocate a buffer to store contents of the file.
        std::unique_ptr<char[]> buf(new char[size]);

        configFile.readBytes(buf.get(), size);
        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject(buf.get());
        json.printTo(Serial);
        if (json.success()) {
          Serial.println("\nparsed json");
          strcpy(blynk_token, json["blynk_token"]);

        } else {
          Serial.println("failed to load json config");
        }
      }
    }
  } else {
    Serial.println("failed to mount FS");
  }
  //end read
  // The extra parameters to be configured (can be either global or just in the setup)
  // After connecting, parameter.getValue() will get you the configured value
  // id/name placeholder/prompt default length
  WiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 34);
  Serial.println(blynk_token);
  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;

  //set config save notify callback
  wifiManager.setSaveConfigCallback(saveConfigCallback);

  //add all your parameters here
  wifiManager.addParameter(&custom_blynk_token);

  //reset settings - for testing
  //wifiManager.resetSettings();

  //set minimu quality of signal so it ignores AP's under that quality
  //defaults to 8%
  wifiManager.setMinimumSignalQuality();

  //sets timeout until configuration portal gets turned off
  //useful to make it all retry or go to sleep
  //in seconds
  wifiManager.setTimeout(600);

  //fetches ssid and pass and tries to connect
  //if it does not connect it starts an access point with the specified name
  //here  "TERRA-HVAC-AP"
  //and goes into a blocking loop awaiting configuration
  if (!wifiManager.autoConnect("TERRA-HVAC AP")) {
    Serial.println("failed to connect and hit timeout");
    delay(3000);
    //reset and try again, or maybe put it to deep sleep
    ESP.reset();
    delay(5000);
  }

  //if you get here you have connected to the WiFi
  Serial.println("TERRA-HVAC connected :)");

  //read updated parameters
  strcpy(blynk_token, custom_blynk_token.getValue());

  //save the custom parameters to FS
  if (shouldSaveConfig) {
    Serial.println("saving config");
    DynamicJsonBuffer jsonBuffer;
    JsonObject& json = jsonBuffer.createObject();
    json["blynk_token"] = blynk_token;

    File configFile = SPIFFS.open("/config.json", "w");
    if (!configFile) {
      Serial.println("failed to open config file for writing");
    }

    json.printTo(Serial);
    json.printTo(configFile);
    configFile.close();
    //end save
  }

  Serial.println("local ip");
  Serial.println(WiFi.localIP());
  Blynk.config(blynk_token);
  Blynk.connect();
  rtc.begin();
  Blynk.syncAll();
  pwm.begin();// initialize the PCA9685 led controller IC.
  pwm.setPWMFreq(1524);  // This is the maximum PWM frequency
  pwm.setPWM(0, 0, currentFadePosition0);
  pwm.setPWM(1, 0, currentFadePosition1);
  Blynk.notify("TERRA HVAC Controller: ONLINE");


  if (!blynk_token)
  {
    Serial.println("Failed to connect to Blynk server");
    wifiManager.resetSettings();// reset all settings and spool up the "TERRA HVAC" AP again.
    
    delay(1000);
  }
  // Simple Timer functions-
  timer.setInterval(1000L, setLed);           // adjust the led lighting every second
  timer.setInterval(1000L, clockDisplay);     // send clock data once per second 
  timer.setInterval(5000L, GetPresets);       // check for updated presets every 5 seconds
  timer.setInterval(1000L, sendSensor);       // App data is updated every second, but sendSensor() queries SHT10 sensor every 5 seconds. 
  timer.setInterval(5000L, determineHeatorCool);// runs heat/cool fuction evey 5 seconds
  timer.setInterval(5000L, determineHumidifierOnOff );// runs humidifier function evey 5 seconds
  timer.setInterval(60000L, activetoday);  // check every minute if lighting schedule should run today
  timer.setInterval(30000L, reconnectBlynk);  // check every 30s if still connected to Blynk Cloud server

}

void loop()
{  
  // LED lighting channel 1
  // get the current time, for this time around loop
  // all millis() timer checks will use this time stamp
  unsigned long currentMillis0 = millis();
  ledFade0(currentMillis0);

  // LED lighting channel 2
  // get the current time, for this time around loop
  // all millis() timer checks will use this time stamp
  unsigned long currentMillis1 = millis();
  ledFade1(currentMillis1);
 
  ArduinoOTA.handle();// Arduino "Over the Air Update" handler ()
  timer.run(); // Initiates SimpleTimer
  if (Blynk.connected()) {
    Blynk.run();

  }
}

1 Like

@William, with such a large sketch, you may be better to split the functions in to their own header files.

Thanks for the advice. I’ve tried doing that in the past, but I haven’t quite figured out how to do it correctly. I get compiler “issues” when I try. I’ll spend a little more time to learn how to do it properly, now that this code functions as intended. At least that’s what I’m telling myself right now LOL.:grin:

1 Like

9 posts were merged into an existing topic: Arduino IDE, PlatformIO, Tabs and other programming options

15 posts were split to a new topic: Problems scanning a large QR code from another project

no problem user error

Hello William, when you order the PCB did it come with the components already soldered or did you receive only the PCB board without any component?

Regards.

Hello, a have a problem with json version 6 … a litle help for me :blush:

This is the error:
error: DynamicJsonBuffer is a class from ArduinoJson 5. Please see arduinojson.org/upgrade to learn how to upgrade your program to ArduinoJson version 6
DynamicJsonBuffer jsonBuffer;
^
exit status 1
DynamicJsonBuffer is a class from ArduinoJson 5. Please see arduinojson.org/upgrade to learn how to upgrade your program to ArduinoJson version 6
Can sombody convert the versoin 5 to version 6 to me?

It’s much easier to simply downgrade to V5.xx in the Arduino IDE while you compile this code, then update back to the latest v6.xx version when you’ve done (if needed).

Pete.

1 Like

OK, How to downgrade the Arduino to use V5?? I dont know much for this action…

In the IDE…
Sketch/Include Library/Manage Libraries
Allow the list to load then type “ArduinoJson” in the filter bar.
Choose Select Version and pick 5.13.4 from the dropdown then press the Install button.

When restoring the later version you can either pick it from the dropdown or just hit the Upgrade button to have the latest version installed.

Pete.

1 Like

You are great men… Thanks for help. Be happy and strong… Thanks again and chears from Bulgaria.

1 Like