crankyoldgit / IRremoteESP8266

Infrared remote library for ESP8266/ESP32: send and receive infrared signals with multiple protocols. Based on: https://github.com/shirriff/Arduino-IRremote/
GNU Lesser General Public License v2.1
2.95k stars 831 forks source link

Airwell - toggling power on every state change #2035

Open mbrevda opened 1 year ago

mbrevda commented 1 year ago

Version/revision of the library used

2.8.6

Describe the bug

Airwell commands all toggle power - even when it's already on.

To Reproduce

See sample code, triggered like

curl -X PUT -d '{"temp":23,"power":true,"mode":1,"power":true}' 192.168.1.51/state

When sending this multiple times, power will toggle on and off

Example code used

Include all relevant code snippets or links to the actual code files. Tip: How to quote your code so it is still readable in the report.

```cpp /* Copyright 2019 Motea Marius This example code will create a webserver that will provide basic control to AC units using the web application build with javascript/css. User config zone need to be updated if a different class than Collix need to be used. Javasctipt file may also require minor changes as in current version it will not allow to set fan speed if Auto mode is selected (required for Airwell). */ #include "Web-AC-control.h" #if defined(ESP8266) #include #include #include #include #endif // ESP8266 #if defined(ESP32) #include #include #include #include #endif // ESP32 #include #include #include #include #include //// ###### User configuration space for AC library classes ########## #include // replace library based on your AC unit model, check https://github.com/crankyoldgit/IRremoteESP8266 #define AUTO_MODE kAirwellAuto #define COOL_MODE kAirwellCool #define DRY_MODE kAirwellDry #define HEAT_MODE kAirwellHeat #define FAN_MODE kAirwellFan #define FAN_AUTO kAirwellFanAuto #define FAN_MIN kAirwellFanLow #define FAN_MED kAirwellFanMedium #define FAN_HI kAirwellFanAuto // ESP8266 GPIO pin to use for IR blaster. const uint16_t kIrLed = 4; // Library initialization, change it according to the imported library file. IRAirwellAc ac(kIrLed); /// ##### End user configuration ###### struct state { uint8_t temperature = 22, fan = 0, operation = 0; bool powerStatus; }; File fsUploadFile; // core state acState; // settings char deviceName[] = "AC Remote Control"; #if defined(ESP8266) ESP8266WebServer server(80); ESP8266HTTPUpdateServer httpUpdateServer; #endif // ESP8266 #if defined(ESP32) WebServer server(80); #endif // ESP32 bool handleFileRead(String path) { // send the right file to the client (if it exists) // Serial.println("handleFileRead: " + path); if (path.endsWith("/")) path += "index.html"; // If a folder is requested, send the index file String contentType = getContentType(path); // Get the MIME type String pathWithGz = path + ".gz"; if (FILESYSTEM.exists(pathWithGz) || FILESYSTEM.exists(path)) { // If the file exists, either as a compressed archive, or normal // If there's a compressed version available if (FILESYSTEM.exists(pathWithGz)) path += ".gz"; // Use the compressed verion File file = FILESYSTEM.open(path, "r"); // Open the file server.streamFile(file, contentType); // Send it to the client file.close(); // Close the file again Serial.println(String("\tSent file: ") + path); return true; } Serial.println(String("\tFile Not Found: ") + path); // If the file doesn't exist, return false return false; } String getContentType(String filename) { // convert the file extension to the MIME type if (filename.endsWith(".html")) return "text/html"; else if (filename.endsWith(".css")) return "text/css"; else if (filename.endsWith(".js")) return "application/javascript"; else if (filename.endsWith(".ico")) return "image/x-icon"; else if (filename.endsWith(".gz")) return "application/x-gzip"; return "text/plain"; } void handleFileUpload() { // upload a new file to the FILESYSTEM HTTPUpload& upload = server.upload(); if (upload.status == UPLOAD_FILE_START) { String filename = upload.filename; if (!filename.startsWith("/")) filename = "/" + filename; // Serial.print("handleFileUpload Name: "); // Serial.println(filename); // Serial.println(upload.contentLength); fsUploadFile = FILESYSTEM.open(filename, "w+"); // Open the file for writing in FILESYSTEM (create if it doesn't exist) filename = String(); } else if (upload.status == UPLOAD_FILE_WRITE) { if (fsUploadFile) fsUploadFile.write(upload.buf, upload.currentSize); // Write the received bytes to the file } else if (upload.status == UPLOAD_FILE_END) { if (fsUploadFile) { // If the file was successfully created fsUploadFile.close(); // Close the file again // Serial.print("handleFileUpload Size: "); // Serial.println(upload.totalSize); server.sendHeader("Location", "/success.html"); // Redirect the client to the success page server.send(303); } else { server.send(500, "text/plain", "500: couldn't create file"); } } } void handleNotFound() { String message = "File Not Found\n\n"; message += "URI: "; message += server.uri(); message += "\nMethod: "; message += (server.method() == HTTP_GET) ? "GET" : "POST"; message += "\nArguments: "; message += server.args(); message += "\n"; for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; } server.send(404, "text/plain", message); } void setup() { Serial.begin(115200); Serial.println(); ac.begin(); Serial.println("mounting " FILESYSTEMSTR "..."); if (!FILESYSTEM.begin()) { Serial.println("Failed to mount file system"); return; } WiFiManager wifiManager; if (!wifiManager.autoConnect(deviceName)) { delay(3000); ESP.restart(); delay(5000); } #if defined(ESP8266) httpUpdateServer.setup(&server); #endif // ESP8266 server.on("/state", HTTP_PUT, []() { DynamicJsonDocument root(1024); DeserializationError error = deserializeJson(root, server.arg("plain")); if (error) { server.send(404, "text/plain", "FAIL. " + server.arg("plain")); } else { if (root.containsKey("temp")) { acState.temperature = (uint8_t) root["temp"]; } if (root.containsKey("fan")) { acState.fan = (uint8_t) root["fan"]; } if (root.containsKey("power")) { acState.powerStatus = root["power"]; } if (root.containsKey("mode")) { acState.operation = root["mode"]; } String output; serializeJson(root, output); server.send(200, "text/plain", output); delay(200); Serial.print("received power state: "); Serial.println(acState.powerStatus ? "true" : "false"); Serial.print("current power state: "); Serial.println(acState.powerStatus ? "true" : "false"); Serial.print("received temperature: "); Serial.println(acState.temperature); Serial.print("current temperature: "); Serial.println(ac.getTemp()); Serial.print("received operation: "); Serial.println(acState.operation); Serial.print("current operation: "); Serial.println(ac.getMode()); if (acState.powerStatus) { //if (!ac.getPowerToggle()) { //Serial.println("here"); ac.setPowerToggle(true); //} // if (ac.getTemp() != acState.temperature) { // ac.setTemp(acState.temperature); // } // if (acState.operation != ac.getMode()) { // if (acState.operation == 1) { // ac.setMode(COOL_MODE); // } else if (acState.operation == 2) { // ac.setMode(HEAT_MODE); // } else if (acState.operation == 3) { // ac.setMode(AUTO_MODE); // } else if (acState.operation == 4) { // ac.setMode(DRY_MODE); // } else if (acState.operation == 5) { // ac.setMode(FAN_MODE); // } // } if (acState.operation != 3) { // if (acState.fan == 0) { // ac.convertFan(FAN_AUTO); // } else if (acState.fan == 1) { // ac.convertFan(FAN_MIN); // } else if (acState.fan == 2) { // ac.convertFan(FAN_MED); // } else if (acState.fan == 3) { // ac.convertFan(FAN_HI); // } } } else { ac.setPowerToggle(false); } ac.send(); Serial.println("state: " + ac.toString()); } }); server.on("/file-upload", HTTP_POST, // if the client posts to the upload page []() { // Send status 200 (OK) to tell the client we are ready to receive server.send(200); }, handleFileUpload); // Receive and save the file server.on("/file-upload", HTTP_GET, []() { // if the client requests the upload page String html = "
"; html += ""; html += ""; html += "
"; server.send(200, "text/html", html); }); server.on("/", []() { server.sendHeader("Location", String("ui.html"), true); server.send(302, "text/plain", ""); }); server.on("/state", HTTP_GET, []() { DynamicJsonDocument root(1024); root["mode"] = acState.operation; root["fan"] = acState.fan; root["temp"] = acState.temperature; root["power"] = acState.powerStatus; String output; serializeJson(root, output); server.send(200, "text/plain", output); }); server.on("/restart", []() { server.send(200, "text/html", "reset"); delay(100); ESP.restart(); }); server.on("/reset", []() { server.send(200, "text/html", "reset"); delay(100); FILESYSTEM.format(); ESP.restart(); }); server.serveStatic("/", FILESYSTEM, "/", "max-age=86400"); server.onNotFound(handleNotFound); server.begin(); } void loop() { server.handleClient(); } ```

Expected behavior

Mode to be set as sent

Output of raw data from IRrecvDumpV2.ino or V3 (if applicable)

Include some serial text of the raw dumps of what the device saw.

Power On:

Timestamp : 000053.825
Library   : v2.8.6

Protocol  : AIRWELL
Code      : 0x256C80002 (34 Bits)
Mesg Desc.: Power Toggle: On, Mode: 1 (Cool), Fan: 1 (Medium), Temp: 24C
uint16_t rawData[169] = {3030, 3744,  1930, 1018,  902, 1914,  1798, 1956,  1930, 1914,  882, 970,  1866, 1958,  1014, 928,  1774, 1062,  858, 1958,  1928, 930,  856, 1064,  898, 1024,  898, 1022,  856, 974,  904, 1018,  856, 1064,  898, 1002,  854, 976,  852, 1070,  852, 1068,  852, 1070,  918, 914,  916, 1004,  918, 1002,  918, 1004,  918, 1828,  1856, 1072,  2956, 3818,  1858, 1072,  918, 1918,  1768, 1984,  1926, 1918,  874, 956,  1926, 1918,  1010, 912,  1788, 1048,  876, 1962,  1882, 954,  914, 1008, 906, 1016,  904, 1018,  904, 928,  902, 1020,  900, 1022,  900, 1038,  842, 988,  842, 1078,  842, 1080,  844, 1078,  842, 1904,  1850, 1078,  2950, 3824,  1850, 1080,  842, 1994,  1758, 1994,  1850, 1994,  842, 988,  1848, 1996,  934, 986,  1758, 1080,  842, 1994,  1848, 988,  840, 1080,  842, 1080,  842, 1080,  842, 988,  842, 1080,  842, 1080,  842, 1080,  842, 988,  842, 1080,  842, 1080,  820, 1104,  840, 988,  842, 1080,  842, 1080,  842, 1080,  842, 1904,  1850, 1080,  3864};  // AIRWELL 256C80002
uint64_t data = 0x256C80002;

What brand/model IR demodulator are you using?

Athom IR

I have followed the steps in the Troubleshooting Guide & read the FAQ

Yes

Has this library/code previously worked as expected for you?

no

mbrevda commented 1 year ago

Playing around with this a bit more, I noticed that after receiving a power on, subsequent commands are perceived as power off. Perhaps the encoding/decoding is slightly off for this remote?

Power on:

Timestamp : 001387.563
Library   : v2.8.6

Protocol  : AIRWELL
Code      : 0x256C80002 (34 Bits)
Mesg Desc.: Power Toggle: On, Mode: 1 (Cool), Fan: 1 (Medium), Temp: 24C
uint16_t rawData[169] = {3052, 3722,  1950, 1022,  830, 1962,  1790, 1986,  1858, 1986,  922, 928,  1840, 1984,  944, 998,  1792, 1044,  902, 1914,  1862, 996,  900, 1022,  902, 1020,  898, 1022,  900, 930,  834, 1088,  900, 1020,  902, 1020,  902, 928,  900, 1022,  900, 1022,  900, 1022,  900, 932,  900, 1000,  920, 1002,  918, 1002,  916, 1830,  1858, 1070,  2956, 3816,  1858, 1072,  916, 1922,  1832, 1920,  1858, 1986,  850, 980,  1858, 1986,  1012, 910,  1836, 1000,  920, 1918,  1926, 910,  874, 1048,  876, 1044,  920, 1002,  918, 914,  910, 1010,  904, 1018,  878, 1042,  904, 926,  908, 1014,  904, 1016,  904, 1018,  902, 946,  882, 1040,  882, 1038,  882, 1040,  842, 1904,  1850, 1078,  2950, 3802,  1872, 1080,  842, 1976,  1778, 1994,  1850, 1994,  844, 986,  1848, 1994,  934, 988,  1756, 1080,  842, 1994,  1850, 988,  842, 1080,  842, 1080,  842, 1080,  842, 988,  842, 1080,  840, 1082,  840, 1080,  844, 986,  842, 1080,  842, 1080,  840, 1080,  842, 990,  840, 1080,  842, 1082,  842, 1078,  842, 1904,  1848, 1080,  3862};  // AIRWELL 256C80002
uint64_t data = 0x256C80002;

Temp up:

Timestamp : 001393.191
Library   : v2.8.6

Protocol  : AIRWELL
Code      : 0x56D00002 (34 Bits)
Mesg Desc.: Power Toggle: Off, Mode: 1 (Cool), Fan: 1 (Medium), Temp: 25C
uint16_t rawData[175] = {3018, 2840,  898, 1046,  876, 1050,  886, 1948,  1808, 1946,  1878, 1966,  892, 958,  1856, 1944,  1006, 962,  1786, 1946,  1878, 1070,  870, 960,  868, 1054,  866, 1054,  870, 1050,  872, 960,  894, 1026,  872, 1050,  894, 1028,  870, 938,  870, 1072,  894, 1024,  898, 1026,  898, 932,  896, 1026,  896, 1026,  896, 1024,  896, 1830,  1856, 1092,  2978, 2858,  918, 1024,  896, 1024,  898, 1898,  1788, 1988,  1858, 1986,  920, 932,  1838, 1986,  942, 1000,  1814, 1918,  1858, 1090,  894, 916,  914, 1008,  912, 1008,  914, 1008,  914, 916,  912, 1008,  914, 1008,  914, 1008,  914, 918,  914, 1008,  914, 1006,  914, 1008,  914, 892,  872, 1072,  918, 1004,  852, 1072,  916, 1828,  1880, 1048,  2980, 2878,  918, 1004,  916, 1006,  914, 1898,  1842, 1934,  1910, 1936,  078,  844, 988,  842, 1078,  844, 1078,  844, 1078,  844, 988,  844, 1078,  844, 1078,  842, 1080,  842, 988,  844, 1078,  842, 1080,  842, 1078,  844, 1902,  1850, 1080,  3864};  // AIRWELL 56D00002
uint64_t data = 0x56D00002;

Timestamp : 001393.487
Library   : v2.8.6

Protocol  : AIRWELL
Code      : 0x56D00002 (34 Bits)
Mesg Desc.: Power Toggle: Off, Mode: 1 (Cool), Fan: 1 (Medium), Temp: 25C
uint16_t rawData[175] = {3010, 2852,  864, 1076,  844, 1078,  844, 1992,  1760, 1994,  1850, 1992,  844, 986,  1852, 1992,  936, 986,  1760, 1994,  1850, 1078,  844, 986,  844, 1076,  844, 1078,  844, 1078,  844, 986,  844, 1078,  844, 1078,  844, 1078,  844, 966,  864, 1078,  844, 1078,  844, 1078,  844, 986,  844, 1078,  842, 1080,  844, 1078,  844, 1902,  1852, 1076,  2948, 2890,  862, 1078,  844, 1076,  844, 1992,  1760, 1994,  1850, 1994,  844, 964,  1872, 1994,  936, 986,  1760, 1994,  1850, 1078,  844, 986,  842, 1078,  844, 1078,  844, 1078,  844, 988,  842, 1080,  842, 1078,  844, 1080,  842, 986,  844, 1078,  844, 1080,  840, 1080,  844, 988,  842, 1078,  842, 1080,  842, 1078,  842, 1904,  1848, 1080,  2948, 2910,  842, 1080,  842, 1080,  842, 1994,  1758, 1994,  1850, 1994,  842, 988,  1848, 1994,  934, 988,  1756, 1996,  1850, 1080,  842, 988,  842, 1080,  842, 1080,  840, 1080,  842, 988,  842, 1080,  842, 1080,  840, 1080,  842, 988,  842, 1080,  818, 1132,  790, 1132,  790, 1040,  790, 1132,  790, 1132,  788, 1132,  790, 1954,  1798, 1132,  3812};  // AIRWELL 56D00002
uint64_t data = 0x56D00002;
NiKiZe commented 1 year ago

A toggle means that the AC should switch state (this is for those that implements a horrible protocol on their ACs) Other ACs has a state for power.

Not exactly following where the issue is perceived. In the server you are using. (Code which I don't really have time to try and understand) is the power toggle set or not?

You will have to make sure it doesn't set the power toggle on each command.

mbrevda commented 1 year ago

The main line is this:

ac.setPowerToggle(true);

Calling it (or setting temp or mode.) toggels the power mode, it doesn't set the AC to on. Interestingly, setting ac.setPowerToggle(false); does seem to consistently turn off the ac.

You will have to make sure it doesn't set the power toggle on each command.

But how?

NiKiZe commented 1 year ago

Then you need to collect data from your original remote to see if they have introduce a power state (and not just the toggle)

That is collecting the code for both power on and off as well as some more data, then comparing each bit to what is known to figure out exactly what is needed.

jbrandek commented 11 months ago

This happen to me also with a Whirlpool AC it does not follow what normally does a AC Power on command in other ACs will make the power goes on does not matter state, with this ACs a power on command will toggle the AC

NiKiZe commented 11 months ago

@jbrandek how are you using the library? If the protocol only supports power toggle, then the toggle needs to be sent or not based on the "known state", if the wanted state is on and "known" is off then the toggle needs to be sent, so you need to track this or use the internal states that does the tracking for you.