Make infrared remote control with M5Stack series, get temperature of Swichbot thermo-hygrometer and control air conditioner automatically
Switchbot Digital Thermo-Hygrometer Smart Home Appliances-High precision swiss sensor, temperature and humidity control with smartphone graph record with Alarm Alexa, GoogleHome, IFTTT Compatible.
I will make a sketch to automatically control the air conditioner
#include "M5Atom.h"
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_MitsubishiHeavy.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
int scanTime = 3; //In seconds
uint16_t scanInterval = 600000; // 10分(In ms)
BLEScan* pBLEScan;
BLEUUID serviceUUID = BLEUUID("cba20d00-224d-11e6-9fb8-0002a5d5c51b");
BLEUUID serviceDataUUID = BLEUUID("00000d00-0000-1000-8000-00805f9b34fb");
const uint16_t kIrLed = 12; // Atom lite内蔵LED
const uint16_t acLed = 26; // IR Unit
IRsend irsend(kIrLed);
IRMitsubishiHeavy152Ac mitsuAc(acLed);
const char *ssid = "";
const char *password = "";
bool autoControl = false;
bool ready = false;
void printState() {
// Display the settings.
Serial.println("Panasonic A/C remote is in the following state:");
Serial.printf(" %s\n", mitsuAc.toString().c_str());
// Display the encoded IR sequence.
unsigned char* ir_code = mitsuAc.getRaw();
Serial.print("IR Code: 0x");
for (uint8_t i = 0; i < kPanasonicAcStateLength; i++)
Serial.printf("%02X", ir_code[i]);
Serial.println();
}
void sendAc(bool on, int temp) {
mitsuAc.setPower(on);
mitsuAc.setFan(kMitsubishiHeavy152FanAuto );
mitsuAc.setMode(kMitsubishiHeavyCool);
mitsuAc.setTemp(temp);
mitsuAc.setSwingVertical(kMitsubishiHeavy152SwingVLow );
mitsuAc.setSwingHorizontal(kMitsubishiHeavy152SwingHAuto );
mitsuAc.send();
}
float temperature = 26.0;
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
if(!advertisedDevice.haveServiceUUID()) return;
if(!advertisedDevice.getServiceUUID().equals(serviceUUID)) return;
printf("SwitchBot Meter!\n");
if(!advertisedDevice.haveServiceData()) return;
std::string s = advertisedDevice.getServiceData();
if(!advertisedDevice.getServiceDataUUID().equals(serviceDataUUID)) return;
const char* servicedata = s.c_str();
int battery = servicedata[2] & 0b01111111;
bool isTemperatureAboveFreezing = servicedata[4] & 0b10000000;
temperature = ( servicedata[3] & 0b00001111 ) / 10.0 + ( servicedata[4] & 0b01111111 );
if(!isTemperatureAboveFreezing){
temperature = -temperature;
}
int humidity = servicedata[5] & 0b01111111;
printf("battery: %d\n", battery);
printf("temperature: %.1f\n", temperature);
printf("humidity: %d\n", humidity);
printf("\n");
}
};
void Task1(void *pvParameters) {
while(autoControl) {
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
pBLEScan->clearResults();
ready = true; // loop内で
delay(scanInterval);
}
vTaskDelete(NULL);
}
void setup()
{
Serial.begin(115200);
M5.begin(true, false, true);
irsend.begin();
mitsuAc.begin();
M5.begin();
BLEDevice::init("");
pBLEScan = BLEDevice::getScan(); //create new scan
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(), true);
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
pBLEScan->setInterval(10000);
pBLEScan->setWindow(9999); // less or equal setInterval value
}
void loop()
{
M5.update();
if (M5.Btn.wasPressed())
{
Serial.println("Btn.wasPressed() == TRUE");
autoControl = true;
xTaskCreatePinnedToCore(Task1,"Task1", 4096, NULL, 3, NULL, 1);
}
if (ready) {
if (27 < temperature) {
M5.dis.drawpix(0, 0x0000f0);
sendAc(true, 24);
printState();
M5.dis.drawpix(0, 0x000000);
ready = false;
}
else if (temperature < 25) {
M5.dis.drawpix(0, 0x0000f0);
sendAc(true, 28);
printState();
M5.dis.drawpix(0, 0x000000);
ready = false;
}
}
delay(1);
}
In this example, if the temperature of the Mitsubishi air conditioner is higher than 28 degrees, increase the cooling to 24 degrees, when the temperature drops below 25 degrees, the cooling is controlled to 28 degrees.Change this to your liking.I will also post the log of automatic control.
Serial port information ↓
Auto AC ON
SwitchBot Meter!
temperature: 27.4
Panasonic A/C Power: On, Mode: 1 (Cool), Temp: 24C
27.3
27.2
27.1
26.9
(...)
25.0
24.9
Panasonic A/C Power: On, Mode: 1 (Cool), Temp: 28C
24.8
24.9
25.1
25.2
Compatible with AlexaIf you install EspAlexa, you can operate it with Alexa.Please check this out for details.However, if you use BLE and EspAlexa at the same time, the sketch will be too big.We will respond by changing the partition settings.
#include <WiFi.h>
#include "M5Atom.h"
#include <Espalexa.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_MitsubishiHeavy.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
int scanTime = 3; //In seconds
uint16_t scanInterval = 600000; // 10分(In ms)
BLEScan* pBLEScan;
BLEUUID serviceUUID = BLEUUID("cba20d00-224d-11e6-9fb8-0002a5d5c51b");
BLEUUID serviceDataUUID = BLEUUID("00000d00-0000-1000-8000-00805f9b34fb");
const uint16_t kIrLed = 12; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
const uint16_t acLed = 26; // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRsend irsend(kIrLed); // Set the GPIO to be used to sending the message.
IRMitsubishiHeavy152Ac mitsuAc(acLed);
// Change this!!
const char *ssid = "";
const char *password = "";
// prototypes
bool connectWifi();
//callback functions
void terebiChanged(EspalexaDevice *dev);
void airconChanged(EspalexaDevice *dev);
void zidouChanged(EspalexaDevice *dev);
bool wifiConnected = false;
Espalexa espalexa;
bool autoControl = false;
bool ready = false;
void printState() {
// Display the settings.
Serial.println("Panasonic A/C remote is in the following state:");
Serial.printf(" %s\n", mitsuAc.toString().c_str());
// Display the encoded IR sequence.
unsigned char* ir_code = mitsuAc.getRaw();
Serial.print("IR Code: 0x");
for (uint8_t i = 0; i < kPanasonicAcStateLength; i++)
Serial.printf("%02X", ir_code[i]);
Serial.println();
}
void sendAc(bool on, int temp) {
mitsuAc.setPower(on);
mitsuAc.setFan(kMitsubishiHeavy152FanAuto );
mitsuAc.setMode(kMitsubishiHeavyCool);
mitsuAc.setTemp(temp);
mitsuAc.setSwingVertical(kMitsubishiHeavy152SwingVLow );
mitsuAc.setSwingHorizontal(kMitsubishiHeavy152SwingHAuto );
mitsuAc.send();
}
int state = 0;
float temperature = 26.0;
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
if(!advertisedDevice.haveServiceUUID()) return;
if(!advertisedDevice.getServiceUUID().equals(serviceUUID)) return;
printf("SwitchBot Meter!\n");
if(!advertisedDevice.haveServiceData()) return;
std::string s = advertisedDevice.getServiceData();
if(!advertisedDevice.getServiceDataUUID().equals(serviceDataUUID)) return;
const char* servicedata = s.c_str();
int battery = servicedata[2] & 0b01111111;
bool isTemperatureAboveFreezing = servicedata[4] & 0b10000000;
temperature = ( servicedata[3] & 0b00001111 ) / 10.0 + ( servicedata[4] & 0b01111111 );
if(!isTemperatureAboveFreezing){
temperature = -temperature;
}
int humidity = servicedata[5] & 0b01111111;
printf("battery: %d\n", battery);
printf("temperature: %.1f\n", temperature);
printf("humidity: %d\n", humidity);
printf("\n");
}
};
//our callback functions
void terebiChanged(EspalexaDevice *d) {
if (d == nullptr)
return; //this is good practice, but not required
Serial.print("A changed to ");
Serial.println("ON");
M5.dis.drawpix(0, 0x00f000);
irsend.sendNEC(0x15748B7);
delay(500);
M5.dis.drawpix(0, 0x000000);
}
void zidouChanged(EspalexaDevice *d) {
if (d == nullptr) return;
M5.dis.drawpix(0, 0x00f000);
if (d->getValue()){
Serial.println("Auto AC ON");
autoControl = true;
xTaskCreatePinnedToCore(Task1,"Task1", 4096, NULL, 3, NULL, 1);
}
else {
Serial.println("Auto AC OFF");
autoControl = false;
ready = false;
}
delay(500);
M5.dis.drawpix(0, 0x000000);
}
void airconChanged(EspalexaDevice *d)
{
if (d == nullptr)
return;
M5.dis.drawpix(0, 0x00f000);
sendAc(false, 26);
delay(500);
M5.dis.drawpix(0, 0x000000);
}
// connect to wifi – returns true if successful or false if not
bool connectWifi()
{
bool state = true;
int i = 0;
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
Serial.println("Connecting to WiFi");
// Wait for connection
Serial.print("Connecting...");
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
if (i > 20)
{
state = false;
break;
}
i++;
}
Serial.println("");
if (state)
{
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
else
{
Serial.println("Connection failed.");
}
return state;
}
void Task1(void *pvParameters) {
while(autoControl) {
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
pBLEScan->clearResults();
ready = true;
delay(scanInterval);
}
vTaskDelete(NULL);
}
void setup()
{
Serial.begin(115200);
M5.begin(true, false, true);
irsend.begin();
mitsuAc.begin();
M5.begin();
// Initialise wifi connection
wifiConnected = connectWifi();
if (!wifiConnected)
{
while (1)
{
Serial.println("Cannot connect to WiFi. Please check data and reset the ESP.");
delay(2500);
}
}
// Define your devices here.
// espalexa.addDevice("TEREBI", terebiChanged, EspalexaDeviceType::onoff); //non-dimmable device
// espalexa.addDevice("AIRCON", airconChanged, EspalexaDeviceType::onoff); //Dimmable device, optional 4th parameter is beginning state (here fully on)
espalexa.addDevice("ZIDOU", zidouChanged, EspalexaDeviceType::onoff); //Dimmable device, optional 4th parameter is beginning state (here fully on)
espalexa.begin();
BLEDevice::init("");
pBLEScan = BLEDevice::getScan(); //create new scan
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(), true);
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
pBLEScan->setInterval(10000);
pBLEScan->setWindow(9999); // less or equal setInterval value
}
void loop() {
espalexa.loop();
M5.update();
if (M5.Btn.wasPressed())
{
Serial.println("Btn.wasPressed() == TRUE");
// irsend.sendNEC(0x15748B7);
M5.dis.drawpix(0, 0x00f000);
sendAc(true, 25);
printState();
// autoControl = true;
// xTaskCreatePinnedToCore(Task1,"Task1", 4096, NULL, 3, NULL, 1);
delay(500);
M5.dis.drawpix(0, 0x000000);
}
if (ready) {
if (27 < temperature) {
M5.dis.drawpix(0, 0x0000f0);
sendAc(true, 24);
printState();
M5.dis.drawpix(0, 0x000000);
ready = false;
}
else if (temperature < 25) {
M5.dis.drawpix(0, 0x0000f0);
sendAc(true, 28);
printState();
M5.dis.drawpix(0, 0x000000);
ready = false;
}
}
delay(1);
}
When combined with Alexa it looks like this in the video, the Atom lite built-in infrared LED (G12) is used to display the TV.
@coppercele
Comments