Skip to content
This repository was archived by the owner on May 11, 2025. It is now read-only.

Commit 657c5b3

Browse files
authored
merge main changes
merge main changes
2 parents ce75bbc + 844a2ad commit 657c5b3

File tree

148 files changed

+21636
-1623
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

148 files changed

+21636
-1623
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@
66
src/setup.h
77
private/
88
.vscode/settings.json
9+
contrib/ldd_decoder/bin/
10+
contrib/ldd_decoder/obj/
11+
contrib/ldd_decoder/label_defines.*
12+
src/my_setup.h

.travis.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
language: python
22
python:
3-
- "3.7"
3+
- "3.9"
44

55
# Cache PlatformIO packages using Travis CI container-based infrastructure
66
sudo: false
@@ -12,13 +12,13 @@ cache:
1212
env:
1313

1414
install:
15+
- pip install --upgrade pip setuptools importlib-metadata
1516
- pip install -U platformio
16-
- pio update
17+
- pio pkg install
18+
- pio pkg update
1719

18-
#
19-
# Libraries from PlatformIO Library Registry:
20-
#
21-
- pio lib -g install 89 6246
2220

2321
script:
24-
- pio ci --board=esp32doit-devkit-v1 --board=m5stick-c --lib="." src
22+
# Don't build native, used only for unit tests
23+
- pio ci --project-conf=platformio.ini --lib="." -e nodemcuv2 -e esp32 -e m5stickc -e m5stickcplus src
24+
- pio test -e native -v

README.md

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<hr/>
44

55
<p align="center">
6-
<a href="https://travis-ci.com/raomin/ESPAltherma"><img src="https://img.shields.io/travis/com/raomin/espaltherma?style=for-the-badge" /></a>
6+
<a href="https://travis-ci.com/raomin/ESPAltherma"><img src="https://app.travis-ci.com/raomin/ESPAltherma.svg?branch=main&status=passed" /></a>
77
&nbsp;
88
<img src="https://img.shields.io/github/last-commit/raomin/ESPAltherma?style=for-the-badge" />
99
&nbsp;
@@ -16,15 +16,15 @@
1616

1717
<hr/>
1818

19-
<p><b>ESPAltherma</b> is a solution to monitor Daikin Altherma / ROTEX / HOVAL Belaria heat pump activity using just Arduino on an <b>ESP32</b> Microcontroller.</p>
19+
<p><b>ESPAltherma</b> is a solution to monitor Daikin Altherma / ROTEX / HOVAL Belaria heat pump activity using just Arduino on an <b>ESP32</b> or <b>ESP8266</b> Microcontroller.</p>
2020

2121
_If this project has any value for you, please consider [buying me a 🍺](https://www.buymeacoffee.com/raomin) or even better [sponsoring ESPAltherma](https://github.com/sponsors/raomin/)!. I don't do this for money but it feels good to get some support! Thanks :)_
2222

2323
## Features
2424

2525
<ul style="list-style-position: inside;">
2626
<li>Connects with the serial port of Altherma on port X10A.</li>
27-
<li>Needs just an ESP32, no need for extra hardware.</li>
27+
<li>Needs just an ESP32, no need for extra hardware. ESP8266 is also supported.</li>
2828
<li>Queries the Altherma for selected values at defined interval.</li>
2929
<li>Converts and formalizes all values in a JSON message sent over MQTT.</li>
3030
<li>Easily integrates with Home Assistant's MQTT auto-discovery.</li>
@@ -42,7 +42,7 @@ _If this project has any value for you, please consider [buying me a 🍺](https
4242
## Hardware
4343

4444
- A Daikin Altherma or Daikin Altherma based heat pump (ROTEX, HOVAL Belaria...)
45-
- An ESP32 *I recommend the M5StickC, it has an integrated display, a magnet, fits well next to the board and is properly isolated. But any ESP32 should work.*
45+
- An ESP32 or ESP8266 *I recommend an ESP32, more precisely the M5StickC, it has an integrated display, a magnet, fits well next to the board and is properly isolated. But any ESP32 should work. A support is added for esp8266.*
4646
- 5 pins JST EH 2.5mm connector (or 4 Dupont wires M-F)
4747

4848
## Software
@@ -60,12 +60,13 @@ _If this project has any value for you, please consider [buying me a 🍺](https
6060
2. Optional - If you are using an **M5StickC** (or M5Stack), select the corresponding environment from the status bar:
6161
Click ![end m5](doc/images/defaultenv.png) and select **env:M5StickC** on the top. The status bar should display ![end m5](doc/images/m5envv.png)
6262
For **M5StickCPlus** select **env:M5StickCPlus**
63+
If you are using an **ESP8266** select the `nodemcuv2` environement.
6364

6465
3. Edit the file `src/setup.h` as follows:
6566
- enter your wifi and mqtt settings
66-
- select your RX TX GPIO pins connected to the X10A port. *The ESP32 has 3 serial ports. The first one, Serial0 is reserved for ESP<-USB->PC communication and ESP Altherma uses the Serial0 for logging (as any other project would do). So if you open the serial monitor on your PC, you'll see some debug from ESPAltherma. ESP32 can map any GPIO to the serial ports. Do NOT use the main Serial0 GPIOs RX0/TX0.*
67+
- select your RX TX GPIO pins connected to the X10A port. *The ESP32 has 3 serial ports. The first one, Serial0 is reserved for ESP<-USB->PC communication and ESP Altherma uses the Serial0 for logging (as any other project would do). So if you open the serial monitor on your PC, you'll see some debug from ESPAltherma. ESP32 can map any GPIO to the serial ports. Do NOT use the main Serial0 GPIOs RX0/TX0.* * The ESP8266 only has 1.5 Serial ports so it uses a software based serial driver. You can choose any pins, but some will stop you from being able to use the console*
6768

68-
Try to stick to the RX2/TX2 of your board (probably GPIO16/GPIO17). **For M5StickC or M5StickCPlus, 26 and 36 will automatically be used if you selected the corresponding environment**.
69+
For ESP32 try to stick to the RX2/TX2 of your board (probably GPIO16/GPIO17). **For M5StickC or M5StickCPlus, 26 and 36 will automatically be used if you selected the corresponding environment**. For ESP8266 pins 4 & 5 (D2 & D1 on the NodeMCUv2) are known to work well.
6970

7071
- uncomment the `#include` line corresponding to your heat pump. E.g.
7172

@@ -91,11 +92,11 @@ For **M5StickCPlus** select **env:M5StickCPlus**
9192
```
9293
9394
4. Now open and edit the file you just uncommented, e.g. `include/def/ALTHERMA(HYBRID).h` (or the one under the language chosen) as follow:
94-
Uncomment each line of the values you are interested in. *Try not to get everything as it will turn into a very big mqtt message*
95+
Uncomment each line of the values you are interested in. *Try not to get everything as it will turn into a very big mqtt message*.
9596
9697
```c++
9798
...
98-
LabelDef PROGMEM labelDefs[] = {
99+
LabelDef labelDefs[] = {
99100
// {0x00,0,801,0,-1,"*Refrigerant type"},
100101
{0x60,0,304,1,-1,"Data Enable/Disable"}, //<-- This value will be queried and reported
101102
// {0x60,1,152,1,-1,"Indoor Unit Address"},
@@ -109,26 +110,22 @@ For **M5StickCPlus** select **env:M5StickCPlus**
109110
110111
A wiki page is available [here](https://github.com/raomin/ESPAltherma/wiki/Information-about-Values) where everyone can comment on the values and their definition.
111112
112-
5. You're ready to go! Connect your ESP32 and click -> Upload! Or run on the command line:
113-
114-
```bash
115-
$ pio run --environment <your environment> --target upload
116-
````
113+
5. You're ready to go! Connect your ESP32/ESP8266 and click -> Upload! Or press `F1` and select -> `PlatformIO: Upload`
117114

118115
## Step 2: Connecting to the Heat pump
119116

120117
1. Turn OFF your heat pump at the circuit breaker.
121118
2. Unscrew your pannel to access the main PCB of your unit.
122-
3. Localize the X10A connector on your the PCB. This is the serial port on the main PCB.
123-
4. Using the 5 pin connector or 4 Dupont wires, connect the ESP32 as follow. Pay attention to the orientation of the socket.
119+
3. Localize the X10A connector on your the PCB. This is the serial port on the main PCB. If your installation include a bi-zone module, the X10A port is occupied with a connector to the Bi-Zone module. You should then connect to the X12A port on the bi-zone module. Pins are identical to the X10A.
120+
4. Using the 5 pin connector or 4 Dupont wires, connect the ESP as follow. Pay attention to the orientation of the socket.
124121

125122
### Daikin Altherma 4 pin X10A Connection
126123

127124
![The X10A connector](doc/images/schematics.png)
128125

129126
| X10A | ESP32 |
130127
| ---- | ----- |
131-
| 1-5V | 5V - VIN *Can supply voltage for the ESP32 :)* |
128+
| 1-5V | 5V - VIN *Can supply voltage for the ESP :)* |
132129
| 2-TX | `RX_PIN` *Default GPIO 16. Prefer RX2 of your board.* |
133130
| 3-RX | `TX_PIN` *Default GPIO 17. Prefer TX2 of your board.* |
134131
| 4-NC | Not connected |
@@ -142,7 +139,7 @@ Some heat pumps (ROTEX) have an X10A port which connects differently:
142139

143140
![](doc/images/rotexX10A.png)
144141

145-
Some users reported that the 5V from their ROTEX was not enough to power their ESP32. In this case, use an USB charger to power the ESP32. The 5V from the X10A is then not needed. Whatever you do, **make sure you keep a wire connecting the GND of the ESP32 to the GND pin of the X10A (even if you power your ESP32 with a USB charger)!!**
142+
Some users reported that the 5V from their ROTEX was not enough to power their ESP32/ESP8266. In this case, use an USB charger to power the ESP32/ESP8266. The 5V from the X10A is then not needed. Whatever you do, **make sure you keep a wire connecting the GND of the ESP32/ESP8266 to the GND pin of the X10A (even if you power your ESP32/ESP8266 with a USB charger)!!**
146143

147144

148145
5. Cross check twice the connections and turn on your heat pump. Two new entities AlthermaSensor and AlthermaSwitch should appear in Home Assistant. AlthermaSensor holds the values as attributes.
@@ -205,18 +202,33 @@ Note: Smart Grid needs to be switched ON in the heatpump configuration menu, oth
205202

206203
# Troubleshooting
207204

208-
## Specific issues
205+
## Specific issues with M5
209206

210207
- If, when using an M5StickC (or M5Stack), the ESP32 is unresponsive, upload fails etc. Make sure that you change the ![default env on pio](doc/images/defaultenv.png) environment to ![end m5](doc/images/m5envv.png) on the status bar. Otherwise the default serial port in setup.h conflicts with the PSRAM of M5.
211208

212209
## Generic issues
213210

214211
Possible generic issues could be: improper wifi signal, unsupported protocol, unsupported GPIOs for Serial (stick to default RX2/TX2).
215212

216-
ESPAltherma generates logs on the main serial port (USB). Connect to the ESP32 and open the serial monitor on Platformio.
213+
ESPAltherma generates logs on the main serial port (USB) and on the screen of the M5. Connect to the ESP32 and open the serial monitor on Platformio.
217214

218215
ESPAltherma also generates logs on MQTT. If Wifi and MQTT is not the issue, look at the logs on the topic `espaltherma/log`. You can see them on Home Assistant through Configuration -> Integration -> MQTT -> Config -> Listen to a topic.
219216

217+
## Logs show 'Timeout on register' with value 0x15 0xea or 'Error 0x15 0xEA returned from HP'
218+
219+
`0x15 0xea` is the reply from the heatpump to say it does not understand the protocol.
220+
If you have an older Altherma heat pump (around 2010 or before) it is probably using the older S protocol.
221+
To activate it, at the end of `setup.h` change `#define PROTOCOL 'I'` to `#define PROTOCOL 'S'`
222+
Also select the `def/PROTOCOL_S_ROTEX.h` or `def/PROTOCOL_S.h` definition file.
223+
224+
## Logs show 'Time out! Check connection' 'Wrong CRC on registry...'
225+
226+
This means that the communication is wrong. Usual suspects:
227+
228+
1. Un-conected GND: whatever you do, the GND of the ESP should always be connected to the GND of the Altherma. So, if you power your ESP with a USB charger (or your computer), make sure you also connect the GND from the ESP to your GND of the Altherma.
229+
2. If not GND, then it's alway the Dupont cable. A faulty dupont cable is a VERY COMMON cause of issue. You can have a perfectly looking cable, they are not the best to do connection on the X10A connector (although much more common than an EH JST 5pin). So, change your cable. You can also use a common 2.54 female long header, plug it to the X10A connector and then your dupont cable to the long pins of the header.
230+
![pic of header](doc/images/header.png)
231+
220232
## Note on voltage
221233

222234
The serial port of X10A is TTL 5V, where the ESP32 is 3.3V. Your ESP32 might not be 5V tolerant. If you want to play it safe, you should use a level shifter to convert Daikin TX - RX ESP line from 5V to 3.3V.
@@ -319,13 +331,13 @@ When put in terms of ESPAltherma variables, the COP can be define as a sensor li
319331

320332
Not directly. It might be possible to change registry values using the serial port but I'm not aware of this. If you know, comment on [the dedicated issue](/../../issues/1).
321333

322-
However, ESPAltherma, supports an extra GPIO to control a relay that you can plug as *external On Off thermostat*. See [**Controling your Daikin Altherma heat pump**](#controling-your-daikin-altherma-heat-pump).
334+
However, ESPAltherma, supports an extra GPIO to control a relay that you can plug as *external On/Off thermostat*. See [**Controling your Daikin Altherma heat pump**](#controling-your-daikin-altherma-heat-pump).
323335

324336
If you want to configure your heat pump using an arduino, you can interact with the P1P2 serial protocol (the one of the digital thermostats) using the [nice work on P1P2Serial](https://github.com/Arnold-n/P1P2Serial) of Arnold Niessen.
325337

326338
## Where can I get more info on the protocol used?
327339

328-
It took quite some time to reverse engineering the protocol. If you're interested, I documented my findings [here](doc/Daikin%20I%20Protocol.md).
340+
It took quite some time to reverse engineer the protocol. If you're interested, I documented my findings [here](doc/Daikin%20I%20protocol.md).
329341

330342
## Is it safe? Can I break my machine?
331343

@@ -389,8 +401,13 @@ You can also [sponsor this project](https://github.com/sponsors/raomin/) (ie reg
389401

390402
## ❤ Sponsors ❤
391403

392-
<a href="https://github.com/freddydeschepper">@freddydeschepper</a>
393-
<a href="https://github.com/qwirx">@qris (Chris Wilson)</a>
404+
<a href="https://github.com/freddydeschepper">@freddydeschepper</a><br/>
405+
<a href="https://github.com/qris">@qris (Chris Wilson)</a><br/>
406+
<a href="https://github.com/mauromorello">@mauromorello (Mauro Morello)</a><br/>
407+
<a href="https://github.com/kloni">@kloni (Tom Klonikowski)</a><br/>
408+
<a href="https://github.com/tarmor1">@tarmor1</a><br/>
409+
<a href="https://github.com/EvertJob">@EvertJob (toppe)</a><br/>
410+
<a href="https://github.com/FusisCaesar">@FusisCaesar</a><br/>
394411

395412
# License
396413
ESPAltherma is licensed under ![MIT Licence](https://img.shields.io/github/license/raomin/ESPAltherma.svg?style=for-the-badge)

contrib/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
This folder contains various scripts & resources useful to hack Rotex/Daikin heat pumps.
2+
3+
Nothing fancy, quite some hard-coded values to change depending on your setup. Hope it helps anyway and saves you some time!
4+
5+
# Links
6+
7+
Official Daikin diagnostic tools (required by those scripts) can be found here: https://www.thermalsupplyinc.com/daikin-service-checkers/

contrib/hp_emulator.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""
2+
Heat pump simulator.
3+
4+
Can be done locally using com0com driver on windows: https://sourceforge.net/projects/com0com/
5+
6+
Create a virtual COM ports pair (COM6/7 in this example).
7+
Connect Daikin DChecker to COM7, this script to COM6 (simulating the HP).
8+
"""
9+
10+
import time
11+
import serial
12+
13+
def main():
14+
ser = serial.Serial('COM8', 9600, timeout=0.1,
15+
parity=serial.PARITY_EVEN, stopbits=1)
16+
while True:
17+
data = ser.read(100)
18+
if data:
19+
print(f"Received: {data}")
20+
reply = None
21+
# Reply to S protocol
22+
if data == b'\x02\x53\xaa':
23+
print("Match 53!")
24+
reply = b"\x53\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xab"
25+
elif data == b'\x02\x50\xad':
26+
print("Match 50!")
27+
# 6 only
28+
reply = b"\x50\x01\x00\x00\x00\xae"
29+
elif data == b'\x02\x54\xa9':
30+
print("Match 54!")
31+
reply = b"\x54\xb4\x0d\xa8\x17\xd0\x18\x24\x1d\x3c\x2c\x01\x00\x20\x00\x00\x00\x79"
32+
elif data == b'\x02\x55\xa8':
33+
print("Match 55!")
34+
reply = b"\x55\x01\x00\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8b"
35+
elif data == b'\x02\x56\xa7':
36+
print("Match 56!")
37+
# 6 only
38+
reply = b"\x56\x01\x00\x00\x00\xa8"
39+
else:
40+
print("Error reply...")
41+
reply = b"\x15\xea"
42+
43+
if reply:
44+
ser.write(reply)
45+
46+
else:
47+
time.sleep(0.1)
48+
49+
if __name__ == "__main__":
50+
main()
51+

contrib/ktb_decoder.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""
2+
Decoder of ktb files, used by official Daikin service Checker.
3+
ktb stands for "KishuTable".
4+
"""
5+
import glob
6+
import argparse
7+
# private byte[] DecodeType3Data(byte[] data)
8+
# {
9+
# byte[] numArray = new byte[data.Length];
10+
# for (int index = 0; index < data.Length; ++index)
11+
# {
12+
# byte num = (byte) (((int) data[index] >> 4 & 15) + ((int) data[index] << 4 & 240));
13+
# numArray[index] = ~num;
14+
# }
15+
# return numArray;
16+
# }
17+
18+
def decode_d3(src: bytes) -> bytes:
19+
""""Magic" taken from DecodeType3Data in D3Command.cs."""
20+
decoded = bytes([~((src_byte >> 4 & 15) + (src_byte << 4 & 240)) % 256 for src_byte in src])
21+
22+
return decoded
23+
24+
def parse_cli() -> argparse.Namespace:
25+
"""Parse command line options."""
26+
example_text = """
27+
python .\\bin\\ktb_decoder.py set_decoded.txt 'C:\\\\Program Files (x86)\\\\Daikin\\\\Checker4\\\\D3Model_en\\\\*.set'
28+
python .\\bin\\ktb_decoder.py ktb_decoded.txt 'C:\\\\Program Files (x86)\\\\Daikin\\\\Checker4\\\\D3Model_en\\\\*.ktb'
29+
"""
30+
parser = argparse.ArgumentParser(epilog=example_text)
31+
parser.add_argument('outfile', type=str,
32+
help='Output file')
33+
parser.add_argument('infiles', type=str,
34+
help='Input files pattern (glob supported)')
35+
args = parser.parse_args()
36+
return args
37+
38+
def main():
39+
args = parse_cli()
40+
with open(args.outfile, "wb") as output_file:
41+
for filepath in glob.iglob(args.infiles):
42+
separator = f"========== {filepath} =========="
43+
print(separator)
44+
with open(filepath, "rb") as f:
45+
f_bytes = f.read()
46+
# print(f_bytes)
47+
decoded = decode_d3(f_bytes)
48+
print(decoded.decode("utf-8", "backslashreplace"))
49+
output_file.write(separator.encode() + b"\n\n" + decoded + b"\n\n")
50+
51+
if __name__ == "__main__":
52+
main()

contrib/ldd_decoder/Program.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System;
2+
using Dchecker.Models;
3+
using System.IO;
4+
using System.Text;
5+
using System;
6+
using System.Collections;
7+
using System.Collections.Generic;
8+
9+
namespace ldd_decoder
10+
{
11+
class Program
12+
{
13+
static void Main(string[] args)
14+
{
15+
// Decode binary LDD
16+
Console.WriteLine("Decoding LDD file...");
17+
LabelDefine[] labeldefines = DataConvModel.DeserializeLabelDefine(
18+
"D:\\Dchecker3_V3602\\Dchecker3\\Storage\\DataModule\\Conv\\S\\en\\DEFAULT.ldd");
19+
20+
// Export it to CSV
21+
Console.WriteLine("Exporting CSV...");
22+
string outDir = Directory.GetCurrentDirectory();
23+
bool res = new DataConvModel(new SamplingInfo()).SaveLabelDefine(
24+
"label_defines.csv", labeldefines, false, outDir);
25+
26+
// Format corresponding ESPAltherma def include file
27+
Console.WriteLine("Generating ESPAltherma def include file...");
28+
GenerateDefInclude("label_defines.h", labeldefines, outDir);
29+
30+
Console.WriteLine("Done.");
31+
}
32+
33+
static void GenerateDefInclude(string fileName, LabelDefine[] defines, string folder = null)
34+
{
35+
StringBuilder stringBuilder = new StringBuilder(
36+
"#include \"labeldef.h\"\n// Generated def file\nLabelDef labelDefs[] = {\n");
37+
38+
foreach (LabelDefine define in defines)
39+
{
40+
// {0x50,0,217,1,-1,"Operation Mode"},
41+
stringBuilder.Append(String.Format("{{0x{0:X},{1},{2},{3},-1,\"{4}\"}},\n", Encoding.Default.GetBytes(define.DataId)[0], define.Offset, define.ConvId, define.Bytes, define.LabelStr));
42+
43+
44+
//stringBuilder.Append(Environment.NewLine);
45+
}
46+
stringBuilder.Append("};\n");
47+
using (StreamWriter streamWriter = new StreamWriter(folder + "\\" + fileName, false, Encoding.GetEncoding("utf-8")))
48+
{
49+
streamWriter.Write((object)stringBuilder);
50+
streamWriter.Close();
51+
}
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)