marco-hoyer / zcan

API proxy for Zehnder ComfoAir Q series devices with CAN bus interface
31 stars 7 forks source link

Controlling the comfoair-q using CAN #1

Open djwlindenaar opened 6 years ago

djwlindenaar commented 6 years ago

Hi, I'm very happy with the code you've put up, since I'm trying to connect my comfoair-q 450 to my home automation using CAN. I've already figured out some more can messages ( have to double-check before I will upload), but the basis you've provided is great. How did you figure these out? Did you have something to sniff? If so could you share that data?

Especially I'm still trying to figure out how to control anything on the system. When I send the command to change the bypass setting, nothing happens. Is there something like a handshake needed or..?

I'd love to get some help and once I get things working contribute back to the project as well.

Best regards Daniel

djwlindenaar commented 6 years ago

@marco-hoyer do you have any logs available? Especially I'm wondering where there are some initialization messages between the comfoair-q unit and the comfoconnect. I'm suspecting that, since my unit is not responding to any commands given over CAN...

marco-hoyer commented 6 years ago

Hi @djwlindenaar, I started sniffing the CAN traffic going over the wire regularly and searched for some patterns and tried to find out how they encoded the values comparing what I saw to what was displayed on the device itself. Then I had a Zehnder ComfoSense C67 device for a while which helped finding some more things. I mostly bought it to reverse engineer how to send commands and thats where I failed so far. Even when I sent the exact same sequences I was not able to make the device do anything. The code is there but mostly for testing atm. I have returned the ComfoSense device and bought a ComfoConnect KNX C bridge and will try to find some time to make another try to find out how it works. I'm totally open for ideas and would be glad if you would contribute something.

marco-hoyer commented 6 years ago

I will see if I can find some logs from the traffic I recorded.

marco-hoyer commented 6 years ago

zcan show will log all so far unknown messages (makes it easier to look at) zcan show --all will log all messages

marco-hoyer commented 6 years ago

I updated the readme with some information about how to execute the code. If you have any questions don't hesitate to ask.

djwlindenaar commented 6 years ago

Thanks for that, I had to figure it out from reading the code :)

I've been able to use the mapping.py for a different software that will output a json file with all the latest sensor data. I'm using the USBtin as well, but I'm using the SocketCAN interface in linux, which allows me to use standard candump and cansend methods to inject packets.

When I send the sequence for changing the ventilation level:

def write_ventilation_level(iterator):
    messages = [
        bytes("T1F0{}505180084150101000000".format(iterator), encoding="ASCII"),
        bytes("T1F0{}505180100201C00000300".format(iterator), encoding="ASCII"),
        bytes("T1F0{}14410".format(int(iterator) - 1), encoding="ASCII")
    ]
CanBus().write_messages(messages)

Nothing happens. It seems to me the 'iterator' is actually the ID of the device sending the request, which are listed nicely in the protocol.md of michaelarnauts/comfoconnect:

productId type description
1 ComfoAirQ The ComfoAirQ ventilation unit.
2 ComfoSense ComfoSense C
3 ComfoSwitch ComfoSwitch C
4 OptionBox  
5 ZehnderGateway ComfoConnect LAN C
6 ComfoCool ComfoCool Q600
7 KNXGateway ComfoConnect KNX C
8 Service Tool  
9 PT Tool Production test tool
10 DVT Tool Design verification test tool

You have listed in mapping.py the reference to T1F055051 as well as T1F075051, which makes sense considering you had both the ComfoConnect LAN C and the ComfoConnect KNX C.

Now it seems to me for the ComfoAirQ to accept commands, first a kind of handshake or identification has gone on. I've tried the following which seems to trigger something of that sort. Normally I get about once per second a kind of ping: slcan0 10000001 [0]

Now when I inject the same kind of ping like this: slcan0 10000005 [0]

I get back:

  slcan0  10000001   [0]  remote request
  slcan0  10000001   [4]  01 01 00 00

So it seems this triggers something in the ComfoAirQ. But I'm not sure what to send next, hence a log would be nice :). Especially if you could get one right from bootup of the ComfoAirQ, so we can be sure any identification/initialization sequence is there. I'd appreciate it!

Best regards Daniel

marco-hoyer commented 6 years ago

The idea of the iterator potentially being an id is an interesting one. I usually saw the ComfoSense sending 1,3,5 and 7 as iterator value for subsequent commands. It always repeated but I couldn't make sense of it so far. I recorded messages for setting the ventilation level. You see the value regularly alternating without any visible reference.

3 to 1
28.02.2018 01:44:31 INFO: type:T id:1F015051 length:8 data:[0, 132, 21, 1, 1, 0, 0, 0] (raw: b'T1F01505180084150101000000\r')
28.02.2018 01:44:31 INFO: type:T id:1F015051 length:8 data:[1, 0, 32, 28, 0, 0, 1, 0] (raw: b'T1F01505180100201C00000100\r')
28.02.2018 01:44:31 INFO: type:T id:1F015051 length:3 data:[130, 0, 0] (raw: b'T1F0150513820000\r')
28.02.2018 01:44:31 INFO: type:T id:1F001441 length:0 data:[] (raw: b'T1F0014410\r')

1 to 3
28.02.2018 01:45:14 INFO: type:T id:1F035051 length:8 data:[0, 132, 21, 1, 1, 0, 0, 0] (raw: b'T1F03505180084150101000000\r')
28.02.2018 01:45:14 INFO: type:T id:1F035051 length:8 data:[1, 0, 32, 28, 0, 0, 3, 0] (raw: b'T1F03505180100201C00000300\r')
28.02.2018 01:45:14 INFO: type:T id:1F035051 length:3 data:[130, 0, 0] (raw: b'T1F0350513820000\r')
28.02.2018 01:45:14 INFO: type:T id:1F021441 length:0 data:[] (raw: b'T1F0214410\r')

3 to 1
28.02.2018 01:45:31 INFO: type:T id:1F055051 length:8 data:[0, 132, 21, 1, 1, 0, 0, 0] (raw: b'T1F05505180084150101000000\r')
28.02.2018 01:45:31 INFO: type:T id:1F055051 length:8 data:[1, 0, 32, 28, 0, 0, 1, 0] (raw: b'T1F05505180100201C00000100\r')
28.02.2018 01:45:31 INFO: type:T id:1F055051 length:3 data:[130, 0, 0] (raw: b'T1F0550513820000\r')
28.02.2018 01:45:31 INFO: type:T id:1F041441 length:0 data:[] (raw: b'T1F0414410\r')

2 to 3
28.02.2018 01:52:19 INFO: type:T id:1F035051 length:8 data:[0, 132, 21, 1, 1, 0, 0, 0] (raw: b'T1F03505180084150101000000\r')
28.02.2018 01:52:19 INFO: type:T id:1F035051 length:8 data:[1, 0, 32, 28, 0, 0, 3, 0] (raw: b'T1F03505180100201C00000300\r')
28.02.2018 01:52:19 INFO: type:T id:1F035051 length:3 data:[130, 0, 0] (raw: b'T1F0350513820000\r')
28.02.2018 01:52:19 INFO: type:T id:1F021441 length:0 data:[] (raw: b'T1F0214410\r')

? to 1
28.02.2018 01:57:16 INFO: type:T id:1F075051 length:8 data:[0, 132, 21, 1, 1, 0, 0, 0] (raw: b'T1F07505180084150101000000\r')
28.02.2018 01:57:16 INFO: type:T id:1F075051 length:8 data:[1, 0, 32, 28, 0, 0, 1, 0] (raw: b'T1F07505180100201C00000100\r')
28.02.2018 01:57:16 INFO: type:T id:1F075051 length:3 data:[130, 0, 0] (raw: b'T1F0750513820000\r')
28.02.2018 01:57:16 INFO: type:T id:1F061441 length:0 data:[] (raw: b'T1F0614410\r')

1 to 3
28.02.2018 01:57:40 INFO: type:T id:1F015051 length:8 data:[0, 132, 21, 1, 1, 0, 0, 0] (raw: b'T1F01505180084150101000000\r')
28.02.2018 01:57:40 INFO: type:T id:1F015051 length:8 data:[1, 0, 32, 28, 0, 0, 3, 0] (raw: b'T1F01505180100201C00000300\r')
28.02.2018 01:57:40 INFO: type:T id:1F015051 length:3 data:[130, 0, 0] (raw: b'T1F0150513820000\r')
28.02.2018 01:57:40 INFO: type:T id:1F001441 length:0 data:[] (raw: b'T1F0014410\r')

3 to 1
28.02.2018 01:57:56 INFO: type:T id:1F035051 length:8 data:[0, 132, 21, 1, 1, 0, 0, 0] (raw: b'T1F03505180084150101000000\r')
28.02.2018 01:57:56 INFO: type:T id:1F035051 length:8 data:[1, 0, 32, 28, 0, 0, 1, 0] (raw: b'T1F03505180100201C00000100\r')
28.02.2018 01:57:56 INFO: type:T id:1F035051 length:3 data:[130, 0, 0] (raw: b'T1F0350513820000\r')
28.02.2018 01:57:56 INFO: type:T id:1F021441 length:0 data:[] (raw: b'T1F0214410\r')

1 to 3
28.02.2018 01:58:12 INFO: type:T id:1F055051 length:8 data:[0, 132, 21, 1, 1, 0, 0, 0] (raw: b'T1F05505180084150101000000\r')
28.02.2018 01:58:12 INFO: type:T id:1F055051 length:8 data:[1, 0, 32, 28, 0, 0, 3, 0] (raw: b'T1F05505180100201C00000300\r')
28.02.2018 01:58:12 INFO: type:T id:1F055051 length:3 data:[130, 0, 0] (raw: b'T1F0550513820000\r')
28.02.2018 01:58:12 INFO: type:T id:1F041441 length:0 data:[] (raw: b'T1F0414410\r')

3 to 1
28.02.2018 02:00:24 INFO: type:T id:1F015051 length:8 data:[0, 132, 21, 1, 1, 0, 0, 0] (raw: b'T1F01505180084150101000000\r')
28.02.2018 02:00:24 INFO: type:T id:1F015051 length:8 data:[1, 0, 32, 28, 0, 0, 1, 0] (raw: b'T1F01505180100201C00000100\r')
28.02.2018 02:00:24 INFO: type:T id:1F015051 length:3 data:[130, 0, 0] (raw: b'T1F0150513820000\r')
28.02.2018 02:00:24 INFO: type:T id:1F001441 length:0 data:[] (raw: b'T1F0014410\r')

1 to 3
28.02.2018 02:00:52 INFO: type:T id:1F035051 length:8 data:[0, 132, 21, 1, 1, 0, 0, 0] (raw: b'T1F03505180084150101000000\r')
28.02.2018 02:00:52 INFO: type:T id:1F035051 length:8 data:[1, 0, 32, 28, 0, 0, 3, 0] (raw: b'T1F03505180100201C00000300\r')
28.02.2018 02:00:52 INFO: type:T id:1F035051 length:3 data:[130, 0, 0] (raw: b'T1F0350513820000\r')
28.02.2018 02:00:52 INFO: type:T id:1F021441 length:0 data:[] (raw: b'T1F0214410\r')

3 to 1
28.02.2018 02:01:05 INFO: type:T id:1F055051 length:8 data:[0, 132, 21, 1, 1, 0, 0, 0] (raw: b'T1F05505180084150101000000\r')
28.02.2018 02:01:05 INFO: type:T id:1F055051 length:8 data:[1, 0, 32, 28, 0, 0, 1, 0] (raw: b'T1F05505180100201C00000100\r')
28.02.2018 02:01:05 INFO: type:T id:1F055051 length:3 data:[130, 0, 0] (raw: b'T1F0550513820000\r')
28.02.2018 02:01:05 INFO: type:T id:1F041441 length:0 data:[] (raw: b'T1F0414410\r')

1 to 3
28.02.2018 02:01:20 INFO: type:T id:1F075051 length:8 data:[0, 132, 21, 1, 1, 0, 0, 0] (raw: b'T1F07505180084150101000000\r')
28.02.2018 02:01:20 INFO: type:T id:1F075051 length:8 data:[1, 0, 32, 28, 0, 0, 3, 0] (raw: b'T1F07505180100201C00000300\r')
28.02.2018 02:01:20 INFO: type:T id:1F075051 length:3 data:[130, 0, 0] (raw: b'T1F0750513820000\r')
28.02.2018 02:01:20 INFO: type:T id:1F061441 length:0 data:[] (raw: b'T1F0614410\r')

3 to 1
28.02.2018 02:01:35 INFO: type:T id:1F015051 length:8 data:[0, 132, 21, 1, 1, 0, 0, 0] (raw: b'T1F01505180084150101000000\r')
28.02.2018 02:01:35 INFO: type:T id:1F015051 length:8 data:[1, 0, 32, 28, 0, 0, 1, 0] (raw: b'T1F01505180100201C00000100\r')
28.02.2018 02:01:35 INFO: type:T id:1F015051 length:3 data:[130, 0, 0] (raw: b'T1F0150513820000\r')
28.02.2018 02:01:35 INFO: type:T id:1F001441 length:0 data:[] (raw: b'T1F0014410\r')

1 to 3
28.02.2018 02:01:50 INFO: type:T id:1F035051 length:8 data:[0, 132, 21, 1, 1, 0, 0, 0] (raw: b'T1F03505180084150101000000\r')
28.02.2018 02:01:50 INFO: type:T id:1F035051 length:8 data:[1, 0, 32, 28, 0, 0, 3, 0] (raw: b'T1F03505180100201C00000300\r')
28.02.2018 02:01:50 INFO: type:T id:1F035051 length:3 data:[130, 0, 0] (raw: b'T1F0350513820000\r')
28.02.2018 02:01:50 INFO: type:T id:1F021441 length:0 data:[] (raw: b'T1F0214410\r')
djwlindenaar commented 6 years ago

hmmm, then it may be something else after all.

Anyway, something funky happened yesterday evening, where the ComfoAirQ suddenly decided to change the codings... now it sends:

  slcan0  10000028   [0] 
  slcan0  001E8068   [2]  27 0A

Somehow the last byte of the can_id has changed (and so my code does not function anymore...)

Did something like this ever happen to you?

Best regards Daniel

djwlindenaar commented 6 years ago

@marco-hoyer, did you have a chance to track down those logs? It'd be really helpful!

By now the data changed again and I'm now getting 19 and 59 as last bytes of the can ID.

gytisgreitai commented 5 years ago

@djwlindenaar did you have any progress with this?

djwlindenaar commented 5 years ago

Yes I have :)

I've been reverse engineering the firmware for the comfoair Q and that has given some insights. Although it is quite limited, since of course all symbols were stripped from the firmware. Basically I found out that this ComfoConnect Lan product is not much more than a LAN2CAN bridge with some session management. Most of the commands for the ComfoConnect translate directly to CAN messages.

So the CAN messages starting with 0x1Fxxxxxx are the commands. And indeed, @marco-hoyer was right, there's a sequence number which allows to link responses to commands. The format is:

    def CanID(self):
        addr = 0x0
        addr |= self.SrcAddr << 0
        addr |= self.DstAddr << 6
        addr |= self.AnotherCounter <<12
        addr |= self.MultiMsg<<14
        addr |= self.A8000   <<15
        addr |= self.A10000  <<16
        addr |= self.SeqNr   <<17
        addr |= 0x1F         <<24

I've not yet figured out the two bits called A8000 and A10000. The first two addresses are the addresses of devices on the bus. This is by default determined for each device sold by Zehnder, but I found out the when you send a message with the same SrcAddr as the ComfoAirQ, it will pick a new random address. Probably to avoid collision? or maybe this allows multiple units on a single can bus. The address at bit 12/13 is not completely clear to me, it seems to be another kind of counter or someting... Maybe it is used when several separate answers are sent in response to one command to keep them apart? Unclear...

Then the content of the message is structured like this: Byte 1: Cmd Byte 2: CmdUnit Byte 3...: Data

CmdUnit is the submodule which Cmd is targeted to:

cmdMapping = {
        "NODE": 0x1,
        "COMFOBUS": 0x2,
        "ERROR": 0x3,
        "SCHEDULE": 0x15,
        "VALVE": 0x16,
        "FAN": 0x17,
        "POWERSENSOR": 0x18,
        "PREHEATER": 0x19,
        "HMI": 0x1A,
        "RFCOMMUNICATION": 0x1B,
        "FILTER": 0x1C,
        "TEMPHUMCONTROL": 0x1D,
        "VENTILATIONCONFIG": 0x1E,
        "NODECONFIGURATION": 0x20,
        "TEMPERATURESENSOR": 0x21,
        "HUMIDITYSENSOR": 0x22,
        "PRESSURESENSOR": 0x23,
        "PERIPHERALS": 0x24,
        "ANALOGINPUT": 0x25,
        "COOKERHOOD": 0x26,
        "POSTHEATER": 0x27,
        "COMFOFOND": 0x28,
        "COOLER": 0x15,
        "CC_TEMPERATURESENSOR": 0x16,
        "IOSENSOR": 0x15,
        }

Then the Cmd meaning depends on this subunit. F.e. for the Schedule unit (0x15) we have:

cmdSchedules = {
        "GETSCHEDULEENTRY": 0x80,
        "ENABLESCHEDULEENTRY": 0x81,
        "DISABLESCHEDULEENTRY": 0x82,
        "GETTIMERENTRY": 0x83,
        "ENABLETIMERENTRY": 0x84,
        "DISABLETIMERENTRY": 0x85,
        "GETSCHEDULE": 0x86,
        "GETTIMERS": 0x87,
        }

Then the data is defined by the Cmd and CmdUnit. For example for timers, I found this list:

1 Preset
2 Bypass
3 Temperature Profile
4 Standby
5 ComfoCool off
6 Exhaust Fan off
7 Supply Fan off
8 Preset Manual
9 Hood
A H23?

Now we can assemble a command to turn off the inlet fan for one hour:

cnet.write_CN_Msg(0x11, cnet.ComfoAddr, 1, 0, 1, [0x84,0x15,0x07,0x01,0x00,0x00,0x00,0x00,0x10,0x0E,0x00,0x00,0x01,0x00, 0x00, 0x00])

which decodes as:

0x84: EnableTimerEntry
0x15: Schedule unit
0x07: InletOff
0x01: ??
0x00000000: ??
0x00000E10: 3600 s = 1 hour
0x00000001: ?? I guess enable the timer?

Or to disable the timer and return to normal operation

cnet.write_CN_Msg(0x11, cnet.ComfoAddr, 1, 0, 1, [0x85,0x15,0x07,0x01])```

There's still a lot to document, which I'm not so fond of, so always loses from figuring out the next item...

I have a little python code which is far from complete/finished/usable, but I could put it up if you like.

Best regards Daniel

gytisgreitai commented 5 years ago

Ok so you can actually send commands and they are accepted by the unit? That is great to hear. And yes please, if possible I would like to get the code.

I still need to mount my Q350 and order can-to-usb adapter, so that will probably take a month until i can play with it...

gytisgreitai commented 5 years ago

Hm but aren't commands the same as described here? https://github.com/michaelarnauts/comfoconnect/blob/master/PROTOCOL.md ?

So if you would like to set fan speed to 3 you would send: 8415 0101 0000 0000 0100 0000 03 | Switch to fan speed 3 which would look like this: cnet.write_CN_Msg(0x11, cnet.ComfoAddr, 1, 0, 1, [0x84,0x15,0x01,0x01, 0x00,0x00, 0x00,0x00, 0x01,0x00, 0x00,0x00, 0x03])

Or am I missing something?

djwlindenaar commented 5 years ago

Yes I can send commands and they are indeed accepted.

They indeed closely resemble the commands to the comfoconnect Lan device. Some padding is required, but other than that it's the same.

gytisgreitai commented 5 years ago

That is really great, fantastic job. Already ordered can-to-usb adapter, and waiting for the code :)

gytisgreitai commented 5 years ago

Hey @djwlindenaar so today my can adapter arrived. I now can read the messages on CAN bus, but with other library :) maybe you have more mappings than defined in this library? I'm interested in extract air temperature

gytisgreitai commented 5 years ago

ok, I managed to map all temperature and humidity values, afaik some mappings in mapping.py are incorrect, e.g. 00454041

What is strange is that some values arrive with different ids, e.g. outdoor air temperature seems to be mapped to both 00450041 and 00370041.

I also don't see any correlation between ids and comfoconnect protocol, e.g. exhaust fan flow is hex 001DC041 but described as pdid 119 in comfoconnect. But maybe there shouldn't be any?

And that chinese CAN adapter is crap... almost zero protocol documentation, some hakish way to make it work on linux, and I get a lot of garbage in between CAN packets, at least if the reverse engineered protocol spec is correct, so probably I'll have to go with usbtin :/

djwlindenaar commented 5 years ago

I just forked this repository into djwlindenaar/zcan. And committed my scripts. Have a look at those. It contains some hints as to what you're referring to... Especially have a look at mapping2.py and how it's used in e.g. testcan.py.

This stuff is still very hackish and geared specifically for my personal use, but we probably should invest a bit to get it to a more useful and shareable state...

I use the usbtin and it's stable/great.

BR Daniel

decontamin4t0R commented 5 years ago

COB-ID 001DC041 is related to pdid 119: (dec) 119 << 2 = (hex) 1DC

djwlindenaar commented 5 years ago

Actually 119(dec)<<14 = 001DC000

gytisgreitai commented 5 years ago

Cool, thanks for the code. mapping2.py looks really extensive. Readonly mode with the chinese adapter works fine. Now I somehow must figure out how write to the ComfAir :)

Now looking at the example code in sendmsg.py if I would like to set venilation mode to Auto I would send: write_CN_Msg(0x11, cnet.ComfoAddr, 1, 0, 1, [0x85,0x15,0x08,0x01]) and if my ComfoAddr from captured frames is at address 1, data length is less that 8 bytes then the CanID would be (extended, 4 bytes) : 0X1F011051 ?

Or am I missing something?

Edit: yes, all good. I'm able to set this to auto by using the chinese provided windows software, so something is wrong with my linux implementation:)

djwlindenaar commented 5 years ago

Looks about right.

I believe the 0x85 command really means 'disable timer'. And that results in auto mode if all timers are disabled.

I think it should be possible to manipulate the configuration through the pdo's but haven't yet figured that out...

gytisgreitai commented 5 years ago

yeah, got that short one working. Now comes the splitting:) Can you elaborate more on the data split pattern? why are you adding changing first byte of the data array? protocol spec?

And which one is the correct one to change ventilation level? in sendmsg.py I see '0x84,0x15,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x00,0x03,0x00, 0x00, 0x00' and '0x84,0x15,0x02,0x01,0x00,0x00,0x00,0x00,0x10,0x3E,0x00,0x00,0x01,0x00, 0x00, 0x00' which both are different from comfoconnect protocol spec (e.g. '8415 0101 0000 0000 0100 0000 01')

djwlindenaar commented 5 years ago

The first one should work. The 03 is the level. The 00,1C,00,00 is the time (0x00001C00 s).

djwlindenaar commented 5 years ago

The second one set the bypass...

marco-hoyer commented 5 years ago

Wow, I noticed recently that I had an expired email address in my GitHub profile. I didn't realise what happened here and now I'm super exited about your progress. Still trying to successfully send some commands... :-).

I started work on a Golang implementation of the existing code with your improvements and some more features. Will share a link once it is uploaded. I recently found out that socat is an awesome utility to forward serial connections via TCP which massively simplifies development and testing for me.

marco-hoyer commented 5 years ago

Say I want to set the ventilation level to 1. My ComfoAir also has id 1 and I have chosen 42 as source id and 3 as sequence number. This brought me to CAN ID: 0x1f07506a and payload 0x8415010100000000001C000001000000

Sending this data distributed amongst 3 datagrams like this:

data 8415010100000000003C000001000000
len 16

chunk1 84150101000000
message1 T1F07506A80084150101000000

chunk2 00003C00000100
message2 T1F07506A80100003C00000100

chunk3 0000
message3 T1F07506A38a0000

Does this make sense to you? I don't see anything happening at the device.

When I sniff messages sent from my ComfoConnect KNX C device changing ventilation level looks like this:

for level 3

id | dlc | data
1F035057 8 0084150101000000
1F035057 7 8100FFFFFFFF03
1F0215C1 0

for level 1

id | dlc | data
1F055057 8 0084150101000000
1F055057 7 8100FFFFFFFF01
1F0415C1 0
djwlindenaar commented 5 years ago

Hi @marco-hoyer, good to have you back :)

I'd have to check, but that'll be next weekend. I noticed that sometimes less padding is needed, but it also does not hurt to have a too long message. Too short hurts, though.

I'm wondering whether 42 as source ID might be too high. I'd need to check about the number of bits available for ID or whether there may be a filter on the ID...

Maybe you should try using the same ID as the comfoconnect..?

marco-hoyer commented 5 years ago

I tried with ID 11 and a variation of the messages I sniffed from the ComfoConnect KNX c box which worked for 3 consecutive calls and then stopped working again. Maybe because of the sequence number not being altered. Did you understand the mechanics behind this sequence number? Can I choose any? Do I need to change it from one command to another?

The messages I sent:

1F075051 8 0084150101000000\r
1F075051 7 8100FFFFFFFF03\r

(where 03 at the very end is the ventilation level)

djwlindenaar commented 5 years ago

I haven't checked, but the sequence number is certainly part of the reassembly process for multiple message split. Also it is used for preparing the actual change in the system as well as the reply to acknowledge the communication. You may be running into a race condition by not changing the sequence number..?

djwlindenaar commented 5 years ago

Oh yes, the sequence is only 2 bits, so 0..3 is valid

marco-hoyer commented 5 years ago

@djwlindenaar thanks, that helps a lot

marco-hoyer commented 5 years ago

I again dig a bit deeper into what the ComfoConnect KNX C box is doing on the CAN bus when attached the first time and got the following:

2019/04/26 18:09:31 {id:10000001 binaryId:268435457 pdu:0 length:0 ComfoAirHeader:{src:1 dst:0 A3000:0 multiMessage:0 A8000:0 A10000:0 SequenceNumber:0} data: pingDeviceId:1}
2019/04/26 18:09:33 {id:10080017 binaryId:268959767 pdu:32 length:4 ComfoAirHeader:{src:23 dst:0 A3000:0 multiMessage:0 A8000:0 A10000:0 SequenceNumber:0} data:8B5AEF50 pingDeviceId:0}
2019/04/26 18:09:33 {id:10000001 binaryId:268435457 pdu:0 length:0 ComfoAirHeader:{src:1 dst:0 A3000:0 multiMessage:0 A8000:0 A10000:0 SequenceNumber:0} data: pingDeviceId:1}
2019/04/26 18:09:35 {id:10000017 binaryId:268435479 pdu:0 length:4 ComfoAirHeader:{src:23 dst:0 A3000:0 multiMessage:0 A8000:0 A10000:0 SequenceNumber:0} data:07FF0132 pingDeviceId:23}
2019/04/26 18:09:35 {id:10000001 binaryId:268435457 pdu:0 length:4 ComfoAirHeader:{src:1 dst:0 A3000:0 multiMessage:0 A8000:0 A10000:0 SequenceNumber:0} data:01010000 pingDeviceId:1}
2019/04/26 18:09:36 {id:10000017 binaryId:268435479 pdu:0 length:4 ComfoAirHeader:{src:23 dst:0 A3000:0 multiMessage:0 A8000:0 A10000:0 SequenceNumber:0} data:07010000 pingDeviceId:23}
2019/04/26 18:09:37 {id:10000001 binaryId:268435457 pdu:0 length:0 ComfoAirHeader:{src:1 dst:0 A3000:0 multiMessage:0 A8000:0 A10000:0 SequenceNumber:0} data: pingDeviceId:1}
2019/04/26 18:09:38 {id:10000017 binaryId:268435479 pdu:0 length:0 ComfoAirHeader:{src:23 dst:0 A3000:0 multiMessage:0 A8000:0 A10000:0 SequenceNumber:0} data: pingDeviceId:23}
2019/04/26 18:09:38 {id:1F011057 binaryId:520163415 pdu:1028 length:5 ComfoAirHeader:{src:23 dst:1 A3000:1 multiMessage:0 A8000:0 A10000:1 SequenceNumber:0} data:0101011007 pingDeviceId:0}
2019/04/26 18:09:38 {id:1F0015C1 binaryId:520099265 pdu:1024 length:4 ComfoAirHeader:{src:1 dst:23 A3000:1 multiMessage:0 A8000:0 A10000:0 SequenceNumber:0} data:00781040 pingDeviceId:0}
2019/04/26 18:09:38 {id:1F031057 binaryId:520294487 pdu:1036 length:8 ComfoAirHeader:{src:23 dst:1 A3000:1 multiMessage:0 A8000:0 A10000:1 SequenceNumber:1} data:02011D01630A0B0C pingDeviceId:0}
2019/04/26 18:09:38 {id:1F0255C1 binaryId:520246721 pdu:1033 length:8 ComfoAirHeader:{src:1 dst:23 A3000:1 multiMessage:1 A8000:0 A10000:0 SequenceNumber:1} data:00E600F4000A00BC pingDeviceId:0}
2019/04/26 18:09:38 {id:1F0255C1 binaryId:520246721 pdu:1033 length:8 ComfoAirHeader:{src:1 dst:23 A3000:1 multiMessage:1 A8000:0 A10000:0 SequenceNumber:1} data:0100E9000A00B100 pingDeviceId:0}
2019/04/26 18:09:38 {id:1F0255C1 binaryId:520246721 pdu:1033 length:5 ComfoAirHeader:{src:1 dst:23 A3000:1 multiMessage:1 A8000:0 A10000:0 SequenceNumber:1} data:82BE000A00 pingDeviceId:0}
2019/04/26 18:09:38 {id:1F051057 binaryId:520425559 pdu:1044 length:4 ComfoAirHeader:{src:23 dst:1 A3000:1 multiMessage:0 A8000:0 A10000:1 SequenceNumber:2} data:85150801 pingDeviceId:0}
2019/04/26 18:09:38 {id:1F0415C1 binaryId:520361409 pdu:1040 length:0 ComfoAirHeader:{src:1 dst:23 A3000:1 multiMessage:0 A8000:0 A10000:0 SequenceNumber:2} data: pingDeviceId:0}
2019/04/26 18:09:38 {id:000C4041 binaryId:802881 pdu:49 length:1 ComfoAirHeader:{src:1 dst:1 A3000:0 multiMessage:1 A8000:0 A10000:0 SequenceNumber:2} data:01 pingDeviceId:0}
2019/04/26 18:09:38 {id:0010C041 binaryId:1097793 pdu:67 length:1 ComfoAirHeader:{src:1 dst:1 A3000:0 multiMessage:1 A8000:1 A10000:0 SequenceNumber:0} data:02 pingDeviceId:0}
2019/04/26 18:09:38 {id:000D0041 binaryId:852033 pdu:52 length:1 ComfoAirHeader:{src:1 dst:1 A3000:0 multiMessage:0 A8000:0 A10000:1 SequenceNumber:2} data:FF pingDeviceId:0}
2019/04/26 18:09:38 {id:000D4041 binaryId:868417 pdu:53 length:1 ComfoAirHeader:{src:1 dst:1 A3000:0 multiMessage:1 A8000:0 A10000:1 SequenceNumber:2} data:FF pingDeviceId:0}
2019/04/26 18:09:38 {id:000E0041 binaryId:917569 pdu:56 length:1 ComfoAirHeader:{src:1 dst:1 A3000:0 multiMessage:0 A8000:0 A10000:0 SequenceNumber:3} data:FF pingDeviceId:0}
2019/04/26 18:09:38 {id:1F075057 binaryId:520573015 pdu:1053 length:8 ComfoAirHeader:{src:23 dst:1 A3000:1 multiMessage:1 A8000:0 A10000:1 SequenceNumber:3} data:0084150101000000 pingDeviceId:0}
2019/04/26 18:09:38 {id:1F075057 binaryId:520573015 pdu:1053 length:7 ComfoAirHeader:{src:23 dst:1 A3000:1 multiMessage:1 A8000:0 A10000:1 SequenceNumber:3} data:8100201C000000 pingDeviceId:0}
2019/04/26 18:09:38 {id:1F0615C1 binaryId:520492481 pdu:1048 length:0 ComfoAirHeader:{src:1 dst:23 A3000:1 multiMessage:0 A8000:0 A10000:0 SequenceNumber:3} data: pingDeviceId:0}
2019/04/26 18:09:38 {id:1F011057 binaryId:520163415 pdu:1028 length:5 ComfoAirHeader:{src:23 dst:1 A3000:1 multiMessage:0 A8000:0 A10000:1 SequenceNumber:0} data:011D011008 pingDeviceId:0}
2019/04/26 18:09:38 {id:1F0015C1 binaryId:520099265 pdu:1024 length:1 ComfoAirHeader:{src:1 dst:23 A3000:1 multiMessage:0 A8000:0 A10000:0 SequenceNumber:0} data:00 pingDeviceId:0}
2019/04/26 18:09:38 {id:1F031057 binaryId:520294487 pdu:1036 length:7 ComfoAirHeader:{src:23 dst:1 A3000:1 multiMessage:0 A8000:0 A10000:1 SequenceNumber:1} data:02011E01120C0B pingDeviceId:0}
2019/04/26 18:09:38 {id:1F0215C1 binaryId:520230337 pdu:1032 length:3 ComfoAirHeader:{src:1 dst:23 A3000:1 multiMessage:0 A8000:0 A10000:0 SequenceNumber:1} data:1E0000 pingDeviceId:0}
2019/04/26 18:09:38 {id:1F051057 binaryId:520425559 pdu:1044 length:5 ComfoAirHeader:{src:23 dst:1 A3000:1 multiMessage:0 A8000:0 A10000:1 SequenceNumber:2} data:011E01100D pingDeviceId:0}
2019/04/26 18:09:38 {id:1F0415C1 binaryId:520361409 pdu:1040 length:1 ComfoAirHeader:{src:1 dst:23 A3000:1 multiMessage:0 A8000:0 A10000:0 SequenceNumber:2} data:00 pingDeviceId:0}
2019/04/26 18:09:38 {id:1F071057 binaryId:520556631 pdu:1052 length:5 ComfoAirHeader:{src:23 dst:1 A3000:1 multiMessage:0 A8000:0 A10000:1 SequenceNumber:3} data:011E011007 pingDeviceId:0}
2019/04/26 18:09:38 {id:1F0615C1 binaryId:520492481 pdu:1048 length:1 ComfoAirHeader:{src:1 dst:23 A3000:1 multiMessage:0 A8000:0 A10000:0 SequenceNumber:3} data:00 pingDeviceId:0}
2019/04/26 18:09:39 {id:10000001 binaryId:268435457 pdu:0 length:0 ComfoAirHeader:{src:1 dst:0 A3000:0 multiMessage:0 A8000:0 A10000:0 SequenceNumber:0} data: pingDeviceId:1}
2019/04/26 18:09:40 {id:1F015057 binaryId:520179799 pdu:1029 length:8 ComfoAirHeader:{src:23 dst:1 A3000:1 multiMessage:1 A8000:0 A10000:1 SequenceNumber:0} data:0084150101000000 pingDeviceId:0}
2019/04/26 18:09:40 {id:1F015057 binaryId:520179799 pdu:1029 length:7 ComfoAirHeader:{src:23 dst:1 A3000:1 multiMessage:1 A8000:0 A10000:1 SequenceNumber:0} data:8100201C000001 pingDeviceId:0}
2019/04/26 18:09:40 {id:1F0015C1 binaryId:520099265 pdu:1024 length:0 ComfoAirHeader:{src:1 dst:23 A3000:1 multiMessage:0 A8000:0 A10000:0 SequenceNumber:0} data: pingDeviceId:0}
2019/04/26 18:09:40 {id:10000017 binaryId:268435479 pdu:0 length:0 ComfoAirHeader:{src:23 dst:0 A3000:0 multiMessage:0 A8000:0 A10000:0 SequenceNumber:0} data: pingDeviceId:23}
2019/04/26 18:09:41 {id:10000001 binaryId:268435457 pdu:0 length:0 ComfoAirHeader:{src:1 dst:0 A3000:0 multiMessage:0 A8000:0 A10000:0 SequenceNumber:0} data: pingDeviceId:1}
2019/04/26 18:09:42 {id:10000017 binaryId:268435479 pdu:0 length:0 ComfoAirHeader:{src:23 dst:0 A3000:0 multiMessage:0 A8000:0 A10000:0 SequenceNumber:0} data: pingDeviceId:23}

While the ComfoAir unit sends pings like 10000001 I found a ping message like 10000023 after attaching the KNX box. Seems like the box picked 0x23 as it's own ID.

Then there are some interesting messages which look to me like some sort of registration from the newly attached box.

For example there is a message from id 23 to id 0 (probably a broadcast?!) with payload 8B5AEF50 every time the box is rebooted.

djwlindenaar commented 5 years ago

Nice, I'll have a look at the decompiled code and try to figure out what it is doing.

djwlindenaar commented 5 years ago

I would also be very interested to see the dialogue in case settings are changed through the app. E.g. is it possible to change the settings for comfort temperature or the flow rate for each ventilation level in the app?

marco-hoyer commented 5 years ago

Hey, I don't have the ComfoConnect LAN C which allows one to use the App but a KNX Bus interface called ComfoConnect KNX C. I can configure this device to listen to specific KNX messages to control the ventilation unit. You can find a manual with details about what this device can do here: https://www.zehnder-systems.de/download/44688/133748/47371.pdf.

I will try to capture CAN bus messages for all available actions there within the next days.

djwlindenaar commented 5 years ago

Ah I guess that has similar capabilities. However the app might allow for some more settings

I had a look at the 1000017 messages and it registers the ID in the comfoair q probably used to check whether certain messages should be sent later on. The 4 bytes payload is stored in memory, but I need to dig further to check what they are used for. At least each ID has a memory location for 4 bytes.

The ID can go up to 0x3F, so 64 locations are available of 4 bytes each and the registration is stored as a bitmap in an int64

gytisgreitai commented 5 years ago

I've pushed my sample code that I use to control comfoair from openhab via mqtt https://github.com/gytisgreitai/zehnder-can-mqtt . Kind of works, just it seems that sometimes commands don't go through :) For now a simple for loop with repeat and sleep helps to mitigate it, but not always. Thanks to djwlindenaar for a lot of help :)

marco-hoyer commented 5 years ago

@gytisgreitai that looks nice. I will have a deeper look next days. Your problem with commands not being accepted matches my experiences. I think there is some mechanics one needs to understand. @djwlindenaar did you see any reaction to the heartbeat messages every device on the bus seems to send? So maybe we need to send a heartbeat for our source id regularly? (I've seen one every 2 seconds)

marco-hoyer commented 5 years ago

Plus there is this registration message with 4 bytes payload we discussed earlier...

marco-hoyer commented 5 years ago

I actually tried a lot but cannot send any command successfully. An example for enabling auto mode below.

command: 0x85150801 source: 0x34 destination: 0x1 unknownAddress: 0x1 multiMessage: 0x0 A8000: 0x0 A10000: 0x1 sequenceNumber: 0x1

makes up the address: 0x1F031074

putting everything together with the command looks like this: 0x1F031074485150801

I altered the sequenceNumber but never got it working. Any idea from your side? Could you maybe compare this to what you do?

decontamin4t0R commented 5 years ago

@marco-hoyer after experimenting a bit with the commands, I was able to change the ventilation level and auto/manual mode. It seems like you were still in automatic mode when sending the messages. For example, my ventilation currently has level 2 when being in auto. When I send manual mode using

1F015057#0084150801000000
1F015057#81000100000001

and then change my level to 3 using

1F015057#0084150101000000
1F015057#8100FFFFFFFF03

and afterwards enable automatic mode with your command

1F011074#85150801 (just seq number is 0x0 and not 0x1)

it switches back to 2, indicating a change to automatic. You can confirm this by checking the values sent for pdid 49, 56 and 65, see https://github.com/michaelarnauts/comfoconnect/blob/master/PROTOCOL.md for details.

decontamin4t0R commented 5 years ago

bitfield 15 is if an error occured. Then the data will be one result byte Some of those errors: 41 unknown not enough bytes 40 not enough space in resbuf? 12 unknown unit 13 unknown subunit 11 unknown command 15 incompatible type 32 not gettable or settable 30 value not in range

Using 1F011074#0120011003 you receive 4210\x00, the code for installer, and with 1F011074#0320010a02 you set the ventilation into installer (as if you had entered the code)

djwlindenaar commented 5 years ago

Nice find! How did you figure out these commands?

decontamin4t0R commented 5 years ago

Combining the results of the firmware and the Android app, together with some testing. Which stuff would be preferred to investigate?

djwlindenaar commented 5 years ago

Cool!

djwlindenaar commented 5 years ago

I'd be interested in changing various setting over CAN. Like the ventilation setpoints per level and target temperature, to name a few

decontamin4t0R commented 5 years ago

Atleast the target temperature is something I am able to provide: First, the PDOs: 67: The current ComfoCool Profile (Normal, Cool, Warm) (Ill add 212 as the target_temperature - but thats nothing new) The commands to change the target temperatures: (all sent to CAN-ID 1F011074, but that should not really matter) Basic Structure: 03 1D 01 var temp temp = INT16, low byte first var (min value, max value (both enforced), steps of 1°C (not enforced)):

I think you should still hold onto the int values, not sure what happens when you set a non-int and then try to go there in the menu...) The current comfortprofile can be set using the 3 timer subunit, e.g. (00) 84 15 03 01 00 00 00 (81) 00 ff ff ff ff 01 (01 = Cool profile, 00 would be normal, 02 = warm)

Ill check out the setpoints per level.

EDIT

Ive had some luck with the setpoints. so to change em: 03 1E 01 var target target = uint16 with the target (m³/h? I have set it to m³/h, as viewable with PDO-ID 224 kg/h (1), l/s (2), m³/h(3)), low byte first var = 03 is away? 04 low, 05 medium, 06 high

Some additional info on the bytes: 03 is SETPROPERTY 1E is VENTILATIONCONFIG 01 is subUnitIdx, as there is only one of it, its 1.

5m³/h steps, min values and max values might vary on unit or detected max airflow?

decontamin4t0R commented 4 years ago

I am updating the documentation of michaelarnauts repo to include the CAN-Protocol and my findings https://github.com/michaelarnauts/comfoconnect/pull/6

michaelarnauts commented 4 years ago

Cool! I didn't know there were others trying to decode the messaging of the ComfoAirQ!