Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Ralph Yamamoto
Created March 30, 2021

Optimization of HVAC Performance

Adjust and monitor the performance of home HVAC system to improve energy efficiency.

487

Things used in this project

Hardware components

QuickFeather Development Kit
QuickLogic Corp. QuickFeather Development Kit
×1
Adafruit HUZZAH32 – ESP32 Feather Board
Adafruit HUZZAH32 – ESP32 Feather Board
×1
M5StickC PLUS ESP32-PICO Mini IoT Development Kit
M5Stack M5StickC PLUS ESP32-PICO Mini IoT Development Kit
×3
BME280 Temperature Humidity Sensor I2C Breakout
×4
Adafruit - Lithium Ion Polymer Battery - 3.7v 1200mAh
×1
Zulkit Junction Box ABS Plastic Black 3.9 x 2.68 x 1.97 inch
×1
Raspberry Pi 4 Model B
Raspberry Pi 4 Model B
×1

Software apps and online services

SensiML Analytics Toolkit
SensiML Analytics Toolkit
QuickLogic Corp. QuickLogic-Corp/qorc-sdk
Eclipse Mosquitto MQTT Broker
Node-RED
Espressif ESP-IDF
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

ANYCUBIC i3 Mega S FDM 3D Printer

Story

Read more

Schematics

HVAC-Optimizer Schematic

Code

HuzzahESP32_HVAC_MQTT_Client.ino

C/C++
Code to Publish Recognition and BME280 data to MQTT Dashboard and to Subscribe to Reset for QuickFeather (to reset Classification limit)
#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include "arduino_secrets.h"
#include <Wire.h>
#include "Adafruit_Sensor.h"
#include <Adafruit_BME280.h>
#include <ArduinoJson.h>

// LED Pin
const int ledPin = 13;
const int resetPin = 27;

// Set your Static IP address
IPAddress local_IP(10, 0, 0, 205);
// Set your Gateway IP address
IPAddress gateway(10, 0, 0, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(8, 8, 8, 8);   //optional
IPAddress secondaryDNS(8, 8, 4, 4); //optional

WiFiClient wifiClient;
PubSubClient client(wifiClient);
long lastMsg = 0;

const byte numChars = 100;
char receivedChars[numChars];   // an array to store the received data
boolean newData = false;
StaticJsonDocument<200> jsonBuffer;

Adafruit_BME280 bme;

float tempBME = 0.0;
float humBME = 0.0;

int recogResult;  // numerical classification

// capture the UART JSON data
void recvWithEndMarker() {
    static byte ndx = 0;
    char endMarker = '\n';
    char rc;
    
    while (Serial1.available() > 0 && newData == false) {
        rc = Serial1.read();

        if (rc != endMarker) {
            receivedChars[ndx] = rc;
            ndx++;
            if (ndx >= numChars) {
                ndx = numChars - 1;
            }
        }
        else {
            receivedChars[ndx] = '\0'; // terminate the string
            ndx = 0;
            newData = true;
        }
    }
}

// parse the JSON to extract the recognition result
void showNewData() {
    if (newData == true) {
//        Serial.print("This just in ... ");
//        Serial.println(receivedChars);
        deserializeJson(jsonBuffer, receivedChars);
        recogResult = jsonBuffer["Classification"];
//        Serial.print("Recognition result: ");
//        Serial.println(recogResult);
        newData = false;
    }
}


void setup_wifi() {
    delay(10);
    // We start by connecting to a WiFi network
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);

  if(!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
    Serial.println("Static IP Failed to configure");
  }
    
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
    }
    randomSeed(micros());
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
//    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP32Client2")) {
//      Serial.println("connected");
      // Subscribe
      client.subscribe("hvac/output");
      client.subscribe("hvac/reset");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}


void callback(char* topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;
  
  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();

  // Feel free to add more if statements to control more GPIOs with MQTT

  // If a message is received on the topic hvac/output, you check if the message is either "on" or "off". 
  // Changes the output state according to the message
  if (String(topic) == "hvac/output") {
    Serial.print("Changing output to ");
    if(messageTemp == "on"){
      Serial.println("on");
      digitalWrite(ledPin, HIGH);
    }
    else if(messageTemp == "off"){
      Serial.println("off");
      digitalWrite(ledPin, LOW);
    }
  }

  // If a message is received on the topic hvac/reset, you check if the message is either "true" or "false". 
  // Changes the output state according to the message
  if (String(topic) == "hvac/reset") { 
    if(messageTemp == "true"){
      Serial.print("Resetting QuickFeather");
      digitalWrite(resetPin, LOW);  // assert Reset
      delay(100);      
      digitalWrite(resetPin, HIGH); // release Reset
    }
    else if(messageTemp == "false"){
      Serial.print("Releasing Reset");
      digitalWrite(resetPin, HIGH);
    }
  }
  
}

void setup() {
  Serial.begin(115200);
//  while(!Serial);   // for debug
  Serial.setTimeout(500);// Set time out for 

  Serial1.begin(460800, SERIAL_8N1, 17, 16); // TX, RX swapped
//  while(!Serial1);   // for debug

    if (!bme.begin(0x76)){  
        Serial.println("Could not find a valid BME280 sensor, check wiring!");
//        while (1);
    }
  
  setup_wifi(); // connect to wifi router
  
// configure MQTT and connect
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);
  reconnect();

  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);  // LED off

  pinMode(resetPin, OUTPUT);
  digitalWrite(resetPin, HIGH); // not Reset
  
}


void loop() {
    if (!client.connected()) {
      reconnect();
    }
    client.loop();
  
    long now = millis();
    if (now - lastMsg > 30000) {
      lastMsg = now;
    
      tempBME = bme.readTemperature()*1.8 + 32 - 5;
      humBME = bme.readHumidity() + 7;
  
      // Convert the value to a char array
      char tempString[8];
      dtostrf(tempBME, 1, 2, tempString);
      Serial.print("Temperature: ");
      Serial.println(tempString);
      client.publish("hvac/temperature", tempString);
  
      // Convert the value to a char array
      char humString[8];
      dtostrf(humBME, 1, 2, humString);
      Serial.print("Humidity: ");
      Serial.println(humString);
      client.publish("hvac/humidity", humString);

      // Convert the value to a char array
//        char batString[8] = "4.124";  // Replace when resistor divider in place
        char batString[8];
        dtostrf(analogRead(A13)/564.0, 1, 2, batString);  // A13 for HuzzahESP32
        Serial.print("Battery: ");
        Serial.println(batString);
        client.publish("hvac/battery", batString);

        // Publish recognition result
        char recogBuf[8];
        char classBuf[8];
        String recogString;
        String classString;
        switch(recogResult){
          case 0:
            recogString = "Unknown";
            classString = "0";
            break;
          case 1:
            recogString = "Fan";
            classString = "1";
            break;
          case 2:
            recogString = "Furnace";
            classString = "2";
            break;
          case 3:
            recogString = "Off";
            classString = "3";
            break;
          default:
            recogString = "Unknown";
            classString = "0";
            break;
        }
        recogString.toCharArray(recogBuf,8);
        Serial.print("Recognition string: ");
        Serial.println(recogBuf); 
        client.publish("hvac/mode", recogBuf);

        classString.toCharArray(classBuf,8);
        Serial.print("Classification string: ");
        Serial.println(classBuf); 
        client.publish("hvac/classification", classBuf);
      
    }

// Read recognition result from UART
    recvWithEndMarker();
    showNewData();
        
//    delay(1000);
}

M5StickCPlus_BME280_MQTT_Floor2_Static.ino

C/C++
Code for Floor2 Remote Temperature sensor. Other remote sensors are the same with name and address changes.
/**
 * M5StickCPlus_BME280_MQTT.ino
 *
 *  Created on: 02.01.2021
 *  
 * the arduino_secrets.h file:
 * #define SECRET_SSID ""    // network name
 * #define SECRET_PASS ""    // network password
 * #define SECRET_MQTT_USER "public" // broker username
 * #define SECRET_MQTT_PASS "public" // broker password 
 *  
 *
 */

#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include "arduino_secrets.h"
#include <M5StickCPlus.h>
#include <Wire.h>
#include "Adafruit_Sensor.h"
#include <Adafruit_BME280.h>
#include "AXP192.h"

#define USE_SERIAL Serial

// LED Pin
const int ledPin = 10;

// Add your MQTT Broker IP address, example:
//const char* mqtt_server = "192.168.1.144";
const char* mqtt_server = "10.0.0.234";

// Set your Static IP address
IPAddress local_IP(10, 0, 0, 203);
// Set your Gateway IP address
IPAddress gateway(10, 0, 0, 1);

IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(8, 8, 8, 8);   //optional
IPAddress secondaryDNS(8, 8, 4, 4); //optional

WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

Adafruit_BME280 bme;

float tempBME = 0.0;
float humBME = 0.0;

void setup() {

    M5.begin();
    Wire.begin(0,26);
    M5.Lcd.setRotation(3);
    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(10, 10, 2);
    M5.Lcd.println("TEMPERATURE Floor2");
    pinMode(M5_BUTTON_HOME, INPUT);

    USE_SERIAL.begin(115200);
//    while(!USE_SERIAL);

    USE_SERIAL.println();
    USE_SERIAL.println();
    USE_SERIAL.println();

    
    if (!bme.begin(0x76)){  
        USE_SERIAL.println("Could not find a valid BME280 sensor, check wiring!");
        while (1);
    }    

    setup_wifi();
    client.setServer(mqtt_server, 1883);
    client.setCallback(callback);
  
    pinMode(ledPin, OUTPUT);
    digitalWrite(ledPin, HIGH);
 
}

void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(SECRET_SSID);

  if(!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
    Serial.println("Static IP Failed to configure");
  }

    WiFi.begin(SECRET_SSID, SECRET_PASS);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* message, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.print(topic);
  Serial.print(". Message: ");
  String messageTemp;
  
  for (int i = 0; i < length; i++) {
    Serial.print((char)message[i]);
    messageTemp += (char)message[i];
  }
  Serial.println();

  // Feel free to add more if statements to control more GPIOs with MQTT

  // If a message is received on the topic floor2/output, you check if the message is either "on" or "off". 
  // Changes the output state according to the message
  if (String(topic) == "floor2/output") {
    Serial.print("Changing output to ");
    if(messageTemp == "on"){
      Serial.println("on");
      digitalWrite(ledPin, LOW);
    }
    else if(messageTemp == "off"){
      Serial.println("off");
      digitalWrite(ledPin, HIGH);
    }
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP32Client2")) {
      Serial.println("connected");
      // Subscribe
      client.subscribe("floor2/output");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void loop() {
    if (!client.connected()) {
      reconnect();
    }
    client.loop();
  
    long now = millis();
    if (now - lastMsg > 30000) {
      lastMsg = now;
    
      tempBME = bme.readTemperature()*1.8 + 32 - 5;
      humBME = bme.readHumidity() + 7;
      M5.Lcd.setCursor(13, 40, 2);
      M5.Lcd.printf("Temperature: %2.1f F", tempBME);
      M5.Lcd.setCursor(15, 60, 2);
      M5.Lcd.printf("Humidity:    %2.1f %%", humBME);
      M5.Lcd.setCursor(15, 80, 2);
      M5.Lcd.printf("Battery:    %.3f V", M5.Axp.GetBatVoltage());
  
      // Convert the value to a char array
      char tempString[8];
      dtostrf(tempBME, 1, 2, tempString);
      Serial.print("Temperature: ");
      Serial.println(tempString);
      client.publish("floor2/temperature", tempString);
  
      // Convert the value to a char array
      char humString[8];
      dtostrf(humBME, 1, 2, humString);
      Serial.print("Humidity: ");
      Serial.println(humString);
      client.publish("floor2/humidity", humString);

      // Convert the value to a char array
      char batString[8];
      dtostrf(M5.Axp.GetBatVoltage(), 1, 2, batString);
      Serial.print("Battery: ");
      Serial.println(batString);
      client.publish("floor2/battery", batString);
      
    }
        
    delay(1000);
}

Credits

Ralph Yamamoto

Ralph Yamamoto

11 projects • 21 followers

Comments