ESP8266 wifi controlled Air Conditioner

Hi all

So an interesting project.

My kids sleep with the AC on, so by morning the room is an ice box as well costing me higher electricity usage.

So I built a small thermostat using an ESP8266, I would set the AC on temperature to around 26 degrees and the off temperature to around 19 degrees, the unit would wake up by every 15 mins and carry out the appropriate action by sending the correct IR code to control the AC.

It worked OK but every time time I wanted to change temperatures I had to re-program which was annoying and was not reliable enough, so I then thought about a system where I could set the on off temperatures controlled through my phone.

I found a project online which has the 2 set points but switches a relay on and off at the set points.

My question is can someone with more experience check the code, to see how easy it would be to implement IR code sending at the trip points rather than the relay on off.

Alm help is greatfully appreciated.

Cheers Alan

#include <EEPROM.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <FS.h>
#include <ESP8266mDNS.h>
#include <user_interface.h>
#include <OneWire.h>

//gpio pin  //nodeMCU pin
//ds18b20 5  //d1
const int relay = 4;  //d2
const int button_toggle = 14;  //d5
const int button_manual = 12;  //d6
const int led_manual = 13;  //d7
const int led_connected = 15;  //d8

#define TRIGGER_LOW  (0)
#define TRIGGER_HIGH  (1)
#define TRIGGER_INSIDE  (2)
#define TRIGGER_OUTSIDE  (3)
#define TRIGGER_HIGH_LOW  (4)
#define TRIGGER_LOW_HIGH  (5)
#define TRIGGER_CUSTOM1  (6)
#define TRIGGER_CUSTOM2  (7)
#define TRIGGER_CUSTOM3  (8)

typedef struct
{
  float temperature;
  float trigger_high;
  float trigger_low;
  uint8_t trigger_type;
  bool fahrenheit;
  bool manual;
  bool on_off;
  bool unprotected;
}
settings_t;

settings_t settings = { 99, 30, 20, TRIGGER_LOW, false, false, false, false };

const int temperature_sample_period = 1000; // in milliseconds.
uint32_t temperature_sample_timer = 0;

const int led_connected_blink_timer_period = 1000; // in milliseconds.
uint32_t led_connected_blink_timer = 0;

bool button_manual_being_pressed = false;
bool button_toggle_being_pressed = false;
bool send_settings_to_webpage = true;

char ssid[51];
char pass[51];
char APName[] = "Wi-Fi Thermostat";
String beginPageText;
unsigned long lastSettingsChange = 0;
byte wifiState = 0;
bool ap_connected = false;
#define connected 1
#define ssidIncorrect 2
#define passIncorrect 3

ESP8266WebServer server(80);

OneWire  ds(5);  // on pin 5 (a 4.7k resistor is necessary)

void settings_print(void)
{
  // Wi-Fi settings.
  Serial.println("ssid: " + String(ssid));
  Serial.println("pass: " + String(pass));
  Serial.println("unprotected: " + String(settings.unprotected));
  // Thermostat settings.
  Serial.println("fahrenheit: " + String(settings.fahrenheit));
  Serial.println("high trigger: " + String(settings.trigger_high));
  Serial.println("low trigger: " + String(settings.trigger_low));
  Serial.println("trigger type: " + String(settings.trigger_type));
  Serial.println("manual: " + String(settings.manual));
}

bool button_pressed(uint8_t button)
{
  if (digitalRead(button)==0)
  {
    // Crude debounce.
    delay(25);
    if (digitalRead(button)==0) return true;
  }
  return false;
}

void setup(void)
{
  pinMode(relay,OUTPUT);
  pinMode(button_toggle,INPUT_PULLUP);
  pinMode(button_manual,INPUT_PULLUP);
  pinMode(led_manual,OUTPUT);
  pinMode(led_connected,OUTPUT);
  Serial.begin(9600);
  EEPROM.begin(512);
  // Uncomment to erase EEPROM, then recomment for normal operation.
  //eeprom_clear();
  eeprom_read();
  settings_print();
  ds1820_start_conversion();
  startWiFi();
  SPIFFS.begin();
  setupWebserver();
  send_settings_to_webpage = true;
  Serial.println("HTTP server started");
}

void loop(void)
{
  // Wi-Fi connected led. Connected: LED on, not connected: LED blinks.
  if (wifiState==connected || ap_connected==true) digitalWrite(led_connected,HIGH);
  else if (led_connected_blink_timer<=millis())
  {
    led_connected_blink_timer = millis() + led_connected_blink_timer_period;
    digitalWrite(led_connected,!digitalRead(led_connected));
  }

  // Read temperature.
  if (temperature_sample_timer<=millis())
  {
    temperature_sample_timer = millis() + temperature_sample_period;
    settings.temperature = ds1820_get_temperature();
    ds1820_start_conversion();
    //Serial.print(settings.temperature);
    //Serial.print(char(176)); // Print degree symbol.
    //Serial.println(settings.fahrenheit==true?"F":"C");
  }
  
  server.handleClient();

  // Do the thermostat thing.
  if (settings.manual==false)
  {
    // Automatic mode.
    if (settings.trigger_type==TRIGGER_CUSTOM1) // Custom 1
    {
      // Add your custom 1 code here.
    }
    else if (settings.trigger_type==TRIGGER_CUSTOM2) // Custom 2
    {
      // Add your custom 2 code here.
    }
    if (settings.trigger_type==TRIGGER_CUSTOM3) // Custom 3
    {
      // Add your custom 3 code here.
    }
    else if (settings.trigger_type==TRIGGER_HIGH_LOW) // Switch with hysteresis
    {
      // Switch on when T>T_high, switch off when T<T_low
      if (settings.temperature>settings.trigger_high) settings.on_off = true; // Output on.
      else if (settings.temperature<settings.trigger_low) settings.on_off = false; // Output off.
    }
    else if (settings.trigger_type==TRIGGER_LOW_HIGH) // Switch with hysteresis
    {
      // Switch on when T<T_low, switch off when T>T_high
      if (settings.temperature<settings.trigger_low) settings.on_off = true; // Output on.
      else if (settings.temperature>settings.trigger_high) settings.on_off = false; // Output off.
    }
    else if ((settings.trigger_type==TRIGGER_LOW && settings.temperature<settings.trigger_low) || // Switch on when T<T_low, switch off when T>T_low
        (settings.trigger_type==TRIGGER_HIGH && settings.temperature>settings.trigger_high) || // Switch on when T>T_high, switch off when T<T_high
        (settings.trigger_type==TRIGGER_INSIDE && (settings.temperature>settings.trigger_low && settings.temperature<settings.trigger_high)) || // Switch on when T_low<T<T_high
        (settings.trigger_type==TRIGGER_OUTSIDE && (settings.temperature<settings.trigger_low || settings.temperature>settings.trigger_high))) // Switch on when T<T_low or T>T_high
    {
      settings.on_off = true; // Output on.
    }
    else settings.on_off = false; // Output off.
  }
  else
  {
    // Manual mode.
    if (button_pressed(button_toggle)==true)
    {
      // Button pressed and was not being pressed?
      if (button_toggle_being_pressed==false)
      {
        settings.on_off = !settings.on_off;
        button_toggle_being_pressed = true;
        send_settings_to_webpage = true;
      }
    }
    else button_toggle_being_pressed = false; // Button released.
  }

  // Read button Manual
  if (button_pressed(button_manual)==true)
  {
    // Button pressed and was not being pressed?
    if (button_manual_being_pressed==false)
    {
      settings.manual = !settings.manual;
      button_manual_being_pressed = true;
      send_settings_to_webpage = true;
    }
  }
  else button_manual_being_pressed = false; // Button released.

  digitalWrite(relay,settings.on_off); // Update output.
  digitalWrite(led_manual,settings.manual); // Update Manual LED.
}

Is that all the code? I would say either some code is missing or this isn't the final version. What you posted won't let you control or monitor anything with your phone.

I suggest you have a look at an application called "Blynk". It lets you build a phone app, designing your own control screen by dragging & dropping without having to write any code for the phone. You just write the Arduino code and the Blynk library takes care of all the communication with the phone app, reading the control settings and updating the readings.

Whatever code you use, changing it from activating a relay directly to sending ir codes should be perfectly possible and not too difficult. The only downside to the idea is that the Arduino can't tell if the if codes were received and acted on by the device under control.

"It worked OK but every time time I wanted to change temperatures I had to re-program which was annoying and was not reliable enough, so I then thought about a system where I could set the on off temperatures controlled through my phone."

You might consider using the ESP as a web server that supplies a web page with text boxes so you can send the desired temperature values back to the ESP board.

In answer to Paul, you are correct this is not all the code, there is webserver code, sensor code etc, but this is the main code that would need to be adjusted to send the IR code rather than activate the relay, that's why I just posted that for ideas on how to modify it.

Yes I'm well familiar with Blynk, but this code has everything I need and would like to modify it if possible.

Zoomkat, that's the point this code does have a webserver, I just did not paste all the code for it, I feel this code will do everything I need, just need to know how to change the output from a relay to an IR pulse.

Cheers

You'd replace instructions to click a relay with instructions to execute a function to knock out the appropriate code via IR. Do you have that remote control code info?
The AnalysIR guy always used to find these a/c inquiries.

ESP32 has BT which is super-simple to send an "on/off" string - no HC-0x, no webservers, html, etc.

Hi all

Thanks for your replies and support, I decided to go back and look at the project and it probably was overkill code wise to start modifying too much.

So with Pauls suggestion I decided on Blynk, I wrote some code and cobbled some bits together, in theory it should work, however when I set the values on my phone using the Blynk interface, the eeprom settings are never saved, so everytime I cycle the battery I lose my min and max settings.

Any idea on how to fix this.

Cheers

#define BLYNK_PRINT Serial
#include <SPI.h>
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <SimpleTimer.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <EEPROM.h>
#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Panasonic.h>
#include <Wire.h>

char auth[] = "QZo8vxA_AubM1sm_FVfLp0q3TDDDkhsP";
char ssid[] = "Ozric Network";
char pass[] = "Hawkwind69!";
bool level = 0;
int count = 1;
int maxTemp = 30;
int minTemp = 18;
float temperature = 0;

const uint16_t kIrLed = D7; // IR LED PIN
IRPanasonicAc ac(kIrLed);

#define ONE_WIRE_BUS D5     // Temp Sensor PIN
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

SimpleTimer timer;

void readSensor()
{
  sensors.requestTemperatures();
  temperature = sensors.getTempCByIndex(0);  // Reads Temperature
}

void reportToBlynk()

{
  Blynk.virtualWrite(V1, temperature);
  Blynk.virtualWrite(V2, minTemp);
  Blynk.virtualWrite(V3, maxTemp);
}

BLYNK_WRITE(V2)

{
  minTemp = param.asInt();
  Serial.print ("Temp Set ");
  Serial.println ( minTemp);
  EEPROM.write(2, minTemp);          //Stores the value of minTemperature to EEPROM
  EEPROM.commit();
}

BLYNK_WRITE(V3)

{
  maxTemp = param.asInt();
  Serial.print ("Temp Set ");
  Serial.println ( maxTemp);
  EEPROM.write(3, maxTemp);          //Stores the value of maxTemp to EEPROM
  EEPROM.commit();
}

void OutputIR()
{

  if ((temperature > maxTemp) && (count = 1)) {
    ac.setModel(kPanasonicRkr);
    ac.on();
    ac.send();
    delay(500);         //  Checks to see if Tempertaure is too high, if yes send IR code to turn on AC
    count = 0;
  }
  if ((temperature < minTemp) && (count = 0)) {

    ac.setModel(kPanasonicJke);
    ac.off();
    ac.send();
    delay(500);        //  Checks to see if Tempertaure is too low, if yes send IR code to turn off AC
    count = 1;
  }

}

void setup()
{
  ac.begin();
  Wire.begin();
  EEPROM.begin(512);
  pinMode(D7, OUTPUT);
  Serial.begin(9600);

  delay(1000);
  // Set up Blynk
  Blynk.begin(auth, ssid, pass);
  // Set up temp sensor
  sensors.begin();

  //Read default parameters from EEProm
  minTemp = EEPROM.read(2);
  maxTemp = EEPROM.read(3);

  Serial.println(minTemp);
  Serial.println(maxTemp);
  Serial.println(temperature);
  timer.setInterval(1000L, reportToBlynk);
}

void loop()
{
  readSensor();
  OutputIR();
  Blynk.run();
  timer.run();
}

It's been a while since I used Blynk myself, but I seem to remember there is no need to store values like your min & max temperatures in eeprom. They are stored on the Blynk server and as soon as the connection is made between the arduino and the server, the values will in your sketch will be updated through BLYNK_WRITE().

If that isn't happening, try adding this code:

BLYNK_CONNECTED() {
    Blynk.syncAll();
}

Using eeprom can be important in Blynk projects where the connection can be intermittent but you need the Arduino to continue to work regardless. In a normal home situation like your project, there is little chance of being unable to connect to the server and receive the values.

Try removing these lines:

Blynk.virtualWrite(V2, minTemp);
  Blynk.virtualWrite(V3, maxTemp);

Hi Paul

Your a star, the syncAll worked a treat.

Thank you for your help.

Much appreciated :wink:

You should post your corrected code in case it's helpful to others in the future. However, I would suggest redacting your auth codes & passwords this time (and modifying post #5).