From 84a17780dda32c74edb2c12ce310a99c89ce4aad Mon Sep 17 00:00:00 2001 From: Pavel Fedin Date: Sat, 10 Aug 2024 15:12:21 +0300 Subject: [PATCH 1/3] Tools: Separate simulators Move simulators under Simulators/ for clarity and easier merging from 8266 fork Signed-off-by: Pavel Fedin --- Tools/Makefile | 12 ++++-------- Tools/Simulators/Makefile | 17 +++++++++++++++++ Tools/Simulators/README.md | 2 ++ Tools/{ => Simulators}/faikin-s21.c | 0 Tools/{faikin.c => Simulators/faikin-x50.c} | 0 5 files changed, 23 insertions(+), 8 deletions(-) create mode 100644 Tools/Simulators/Makefile create mode 100644 Tools/Simulators/README.md rename Tools/{ => Simulators}/faikin-s21.c (100%) rename Tools/{faikin.c => Simulators/faikin-x50.c} (100%) diff --git a/Tools/Makefile b/Tools/Makefile index 3381cf41..83add923 100644 --- a/Tools/Makefile +++ b/Tools/Makefile @@ -15,13 +15,15 @@ SQLLIB=$(shell mariadb_config --libs) SQLVER=$(shell mariadb_config --version | sed 'sx\..*xx') endif -TOOLS := faikin faikin-s21 ifdef SQLINC -TOOLS += faikinlog faikingraph +TOOLS := faikinlog faikingraph else +TOOLS := $(warning Warning - mariadb/mysql not installed, needed if you want to build tools) endif +all: tools + tools: $(TOOLS) ifeq ($(shell uname),Darwin) @@ -42,12 +44,6 @@ AJL/ajl.o: AJL/ajl.c CCOPTS=${SQLINC} -I. -I/usr/local/ssl/include -D_GNU_SOURCE -g -Wall -funsigned-char -lm OPTS=-L/usr/local/ssl/lib ${SQLLIB} ${CCOPTS} -faikin: faikin.c - gcc -O -o $@ $< -lpopt ${INCLUDES} ${LIBS} - -faikin-s21: faikin-s21.c ../ESP32/main/daikin_s21.h ../ESP32/main/faikin_enums.h - gcc -O0 -g -o $@ $< -lpopt -lm ${INCLUDES} ${LIBS} - faikinlog: faikinlog.c SQLlib/sqllib.o AJL/ajl.o ../ESP32/main/acextras.m ../ESP32/main/acfields.m ../ESP32/main/accontrols.m cc -O -o $@ $< -lpopt -lmosquitto -ISQLlib SQLlib/sqllib.o -IAJL AJL/ajl.o ${INCLUDES} ${OPTS} diff --git a/Tools/Simulators/Makefile b/Tools/Simulators/Makefile new file mode 100644 index 00000000..867a965d --- /dev/null +++ b/Tools/Simulators/Makefile @@ -0,0 +1,17 @@ +ifeq ($(shell uname),Darwin) +INCLUDES := -I/usr/local/include/ +LIBS := -L/usr/local/Cellar/popt/1.18/lib/ +else +LIBS := +INCLUDES := +endif + +ESP_DIR := ../../ESP + +faikin-x50: faikin-x50.c + gcc -O -o $@ $< -lpopt -I${ESP_DIR} ${INCLUDES} ${LIBS} + +faikin-s21: faikin-s21.c ${ESP_DIR}/main/daikin_s21.h ${ESP_DIR}/main/faikin_enums.h + gcc -O -g -o $@ $< -lpopt -I${ESP_DIR} ${INCLUDES} ${LIBS} + +all: faikin-x50 faikin-s21 diff --git a/Tools/Simulators/README.md b/Tools/Simulators/README.md new file mode 100644 index 00000000..3a29a7ac --- /dev/null +++ b/Tools/Simulators/README.md @@ -0,0 +1,2 @@ +This directory contains air conditioner simulators, which can be used to test Faikin without need to have +an actual air conditioner. \ No newline at end of file diff --git a/Tools/faikin-s21.c b/Tools/Simulators/faikin-s21.c similarity index 100% rename from Tools/faikin-s21.c rename to Tools/Simulators/faikin-s21.c diff --git a/Tools/faikin.c b/Tools/Simulators/faikin-x50.c similarity index 100% rename from Tools/faikin.c rename to Tools/Simulators/faikin-x50.c From 84c42b824b8ace19efbe4476df798578e735e9e0 Mon Sep 17 00:00:00 2001 From: Pavel Fedin Date: Sat, 10 Aug 2024 15:23:29 +0300 Subject: [PATCH 2/3] Simulators/faikin-s21: Remove unused headers We don't use them in the code, and they are missing on Windows, preventing the build Signed-off-by: Pavel Fedin --- Tools/Simulators/faikin-s21.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Tools/Simulators/faikin-s21.c b/Tools/Simulators/faikin-s21.c index a21dd2e1..c02d29cc 100644 --- a/Tools/Simulators/faikin-s21.c +++ b/Tools/Simulators/faikin-s21.c @@ -11,8 +11,6 @@ #include #include #include -#include -#include #include "main/daikin_s21.h" From 447ab1f8ad53c2b23679df95c95728f1caf33eda Mon Sep 17 00:00:00 2001 From: Pavel Fedin Date: Thu, 8 Aug 2024 01:06:09 +0300 Subject: [PATCH 3/3] Simulators/faikin-s21: Complete support for protocol version 1 Reverse-engineered from original Daikin BRP069B41 online controller, which is now able to work with this simulator. See #408 for some documented findings Signed-off-by: Pavel Fedin --- Tools/Simulators/faikin-s21.c | 94 +++++++++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 16 deletions(-) diff --git a/Tools/Simulators/faikin-s21.c b/Tools/Simulators/faikin-s21.c index c02d29cc..9cf70a02 100644 --- a/Tools/Simulators/faikin-s21.c +++ b/Tools/Simulators/faikin-s21.c @@ -85,22 +85,28 @@ static void s21_ack(int p) serial_write(p, &response, 1); } -static void s21_reply(int p, unsigned char *response, const unsigned char *cmd, int payload_len) +static void s21_nonstd_reply(int p, unsigned char *response, int body_len) { - int pkt_len = S21_MIN_PKT_LEN + payload_len; + int pkt_len = 3 + body_len; // 3 bytes for STX, checksum, ETX int l; s21_ack(p); // Send ACK before the reply - response[S21_STX_OFFSET] = STX; - response[S21_CMD0_OFFSET] = cmd[S21_CMD0_OFFSET] + 1; - response[S21_CMD1_OFFSET] = cmd[S21_CMD1_OFFSET]; - response[S21_PAYLOAD_OFFSET + payload_len] = s21_checksum(response, pkt_len); - response[S21_PAYLOAD_OFFSET + payload_len + 1] = ETX; + response[S21_STX_OFFSET] = STX; // +2 below accounts for this + response[S21_CMD0_OFFSET + body_len] = s21_checksum(response, pkt_len); + response[S21_CMD0_OFFSET + body_len + 1] = ETX; serial_write(p, response, pkt_len); } +static void s21_reply(int p, unsigned char *response, const unsigned char *cmd, int payload_len) +{ + response[S21_CMD0_OFFSET] = cmd[S21_CMD0_OFFSET] + 1; + response[S21_CMD1_OFFSET] = cmd[S21_CMD1_OFFSET]; + + s21_nonstd_reply(p, response, 2 + payload_len); // Body is two cmd bytes plus payload +} + static void send_temp(int p, unsigned char *response, const unsigned char *cmd, int value) { char buf[5]; @@ -283,12 +289,40 @@ main(int argc, const char *argv[]) s21_reply(p, response, buf, S21_PAYLOAD_LEN); break; + case '2': + // BRP069B41 sends this as first command. If NAK is received, it keeps retrying + // and doesn't send anything else. Suggestion - query AC features + // The response values here are kindly provided by a user in reverse engineering + // thread: https://github.com/revk/ESP32-Faikin/issues/408#issuecomment-2278296452 + // Correspond to A/C models CTXM60RVMA, CTXM35RVMA + // It was experimentally found that with different values, given by FTXF20D, the + // controller falls into error 252 and refuses to accept A/C commands over HTTP. + if (debug) + printf(" -> unknown ('F2')\n"); + response[3] = 0x3D; // FTXF20D: 0x34; + response[4] = 0x3B; // FTXF20D: 0x3A; + response[5] = 0x00; + response[6] = 0x80; + + s21_reply(p, response, buf, S21_PAYLOAD_LEN); + break; + case '3': + if (debug) + printf(" -> powerful ('F3') %d\n", powerful); + response[3] = 0x30; // No idea what this is, taken from my FTXF20D + response[4] = 0xFE; + response[5] = 0xFE; + response[6] = powerful ? 2 : 0; + + s21_reply(p, response, buf, S21_PAYLOAD_LEN); + break; case '4': + // Also taken from CTXM60RVMA, CTXM35RVMA. Not researched yet. if (debug) printf(" -> unknown ('F4')\n"); - response[3] = 0x30; // No idea what this is, taken from my FTXF20D + response[3] = 0x30; response[4] = 0x00; - response[5] = 0xA0; + response[5] = 0x80; // FTXF20D: 0xA0; response[6] = 0x30; s21_reply(p, response, buf, S21_PAYLOAD_LEN); @@ -305,8 +339,8 @@ main(int argc, const char *argv[]) break; case '6': if (debug) - printf(" -> powerful %d\n", powerful); - response[3] = powerful ? '2' : '0'; + printf(" -> powerful ('F6') %d\n", powerful); + response[3] = powerful ? 2 : 0; response[4] = 0; response[5] = 0; response[6] = 0; @@ -321,14 +355,28 @@ main(int argc, const char *argv[]) response[5] = 0; response[6] = 0; + s21_reply(p, response, buf, S21_PAYLOAD_LEN); + break; + case '8': + if (debug) + printf(" -> Protocol version = 1.0\n"); + // 'F8' - this is found out to be protocol version. + // My FTXF20D replies with '0020' (assuming reading in reverse like everything else). + // If we say that, BRP069B41 then asks for F9 (we know it's different form of home/outside sensor) + // then proceeds requiring more commands, majority of english alphabet. I got tired implementing + // all of them and tried to downgrade the response to '0000'. This caused the controller sending + // 'MM' command (see below), and then it goes online with our emulated A/C. + // '0010' gives the same results + response[3] = 0x30; + response[4] = 0x31; // FTXF20D: 0x32; + response[5] = 0x30; + response[6] = 0x30; + s21_reply(p, response, buf, S21_PAYLOAD_LEN); break; /* * I also tried the following commands on my FTXF20D and got * responses as listed. It is currently unknown what they report. - * 'F2' 06 02 47 32 34 3A 00 80 67 03 - * 'F3' 06 02 47 33 30 30 30 00 0A 03 - * 'F8' 06 02 47 38 30 32 30 30 41 03 * 'F9' 06 02 47 39 B4 FF FF 30 62 03 * 'RI' 06 02 53 49 35 36 32 2B 64 03 - the same data as for 'RH', probably firmware bug */ @@ -337,9 +385,23 @@ main(int argc, const char *argv[]) s21_nak(p, buf); continue; } - } else if (buf[1] == 'R') { + } else if (buf[S21_CMD0_OFFSET] == 'M') { + if (debug) + printf(" -> unknown ('F4')\n"); + // This is sent by BRP069B41 for protocol version 1 (see F8 description above) + // I experimentally found out that this command doesn't have a second + // byte, and the A/C always responds with this. Note non-standard + // response form. + response[S21_CMD0_OFFSET] = 'M'; + response[2] = 'F'; + response[3] = 'F'; + response[4] = 'F'; + response[5] = 'F'; + + s21_nonstd_reply(p, response, 5); + } else if (buf[S21_CMD0_OFFSET] == 'R') { // Query temperature sensors - switch (buf[2]) { + switch (buf[S21_CMD1_OFFSET]) { case 'H': send_temp(p, response, buf, home); break;