Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Controlling the comfoair-q using CAN #1

Open
djwlindenaar opened this issue Jun 16, 2018 · 99 comments
Open

Controlling the comfoair-q using CAN #1

djwlindenaar opened this issue Jun 16, 2018 · 99 comments

Comments

@djwlindenaar
Copy link

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
Copy link
Author

@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
Copy link
Owner

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
Copy link
Owner

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

@marco-hoyer
Copy link
Owner

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

@marco-hoyer
Copy link
Owner

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

@djwlindenaar
Copy link
Author

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
Copy link
Owner

marco-hoyer commented Jun 25, 2018

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
Copy link
Author

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
Copy link
Author

@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
Copy link

@djwlindenaar did you have any progress with this?

@djwlindenaar
Copy link
Author

djwlindenaar commented Jan 6, 2019

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
Copy link

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
Copy link

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
Copy link
Author

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
Copy link

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

@gytisgreitai
Copy link

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
Copy link

gytisgreitai commented Jan 26, 2019

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
Copy link
Author

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
Copy link

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

@djwlindenaar
Copy link
Author

Actually 119(dec)<<14 = 001DC000

@gytisgreitai
Copy link

gytisgreitai commented Jan 27, 2019

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
Copy link
Author

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
Copy link

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
Copy link
Author

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

@djwlindenaar
Copy link
Author

The second one set the bypass...

@marco-hoyer
Copy link
Owner

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
Copy link
Owner

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
Copy link
Author

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
Copy link
Owner

marco-hoyer commented Apr 24, 2019

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
Copy link
Author

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..?

@marco-hoyer
Copy link
Owner

All of a sudden it does not work anymore. I can no longer receive anything. Is it some faulty hardware or maybe i somehow initialized the canbus node and that has been working till today (I had a power surge so the comfoair (and rpi) was shut off).

May it be that you have an ID conflict and your ComfoAir device has chosen a different ID after restart?

@vekexasia
Copy link

hey @marco-hoyer thanks for the reply.

My code should use 0x11 as source ID.

https://github.com/vekexasia/comfoairq-mqtt/blob/a7f8cf6db23697cbf8da69e1dce7335277584d32/src/index.ts#L78-L84

But the problem is that I don't receive anything on the bus. (Well to be honest, sending does not work as well).

@vekexasia
Copy link

Also, when the code starts i don't send anything on the bus. I just set the code to listen and eventually send a message when a command arrives. Maybe it requires a "Start" command somehow?

@vekexasia
Copy link

Ok note to self. Check for commented stuff. Apparently i had commented out from my code the S2\r and 0\r code. So the usbtin never initialized itself....
🤦

@vekexasia
Copy link

For those of you interested i also did an implementation with esp32 and a 3d enclosure here: https://github.com/vekexasia/comfoair-esp32

image

@vespadj
Copy link

vespadj commented Feb 18, 2021

Hi to all! I'm replicating https://github.com/vekexasia/comfoairq-mqtt
I connect the USB-CAN of @marco-hoyer , @gytisgreitai and @vekexasia (first prototype) like https://www.aliexpress.com/item/32966467821.html
Under Linux, I open the Port /dev/ttyUSB0, so comfoairq-mqtt at startup log me "Serial Port open!" but nothing happen to ComfoAir when I send message.

First of all, I want to know: How should be blink the LED on board of Comfoair?
On mine:

  • LED slow blinks (1 per second) when CAN cable is disconnected
  • LED fast blinks (with 2 or 3 slow blinks then fast blinks again) when CAN is connected but not ready

And How should be blink the LEDs on USB-CAN?

@vespadj
Copy link

vespadj commented Feb 21, 2021

I solved checking cable wiring. LED lights fixed.

J101:
red 12 V dc
black GND
yellow CAN_H
white CAN_L

@samezrp
Copy link

samezrp commented Mar 16, 2022

Hi vekexasia,
Would you mind helping me? I checked all cabling, all looks OK, I get following messages and there is no communication:
image
I nave AERISNext recuperator, but it should be the same as Zhender.
How can I check what is on CAN bus? This CAN Forced Reset came in when I un-commented following line in code:
CAN0.setDebuggingMode(true);

edit:
Is there anyone who in fact managed to run this esp32 stuff?
After a while I run djwlindenaar's zcan and managed to give boost command and some others, I did not managed to communicate with esp at all :/

@michaelarnauts
Copy link

In case it is usefull for somebody, here is a small function to convert CAN ID's to PDO ID's and visa-versa.

    def pdo_to_can(pdo, node_id=1):
        """Convert a PDO-ID to a CAN-ID."""
        return ((pdo << 14) + 0x40 + node_id).to_bytes(4, byteorder='big').hex()

    def can_to_pdo(can, node_id=1):
        """Convert a CAN-ID to a PDO-ID."""
        return (int(can, 16) - 0x40 - node_id) >> 14


    print("00454041 = %s" % can_to_pdo("00454041"))
    print("00458041 = %s" % can_to_pdo("00458041"))

returns

00454041 = 277
00458041 = 278

I'm documenting more PDO's here: https://github.com/michaelarnauts/aiocomfoconnect/blob/master/docs/PROTOCOL-PDO.md

@xavan83
Copy link

xavan83 commented Dec 22, 2023

Hello, the links for the aliexpress adapters are invalid. Can you indicate which one you test and how to connect these 3pins adapters to the 4pins connector of the zehnder unit? Thank you

@vekexasia
Copy link

hey @xavan83 i'd suggest you to take a look at https://github.com/vekexasia/comfoair-esp32

@xavan83
Copy link

xavan83 commented Dec 22, 2023

I am not confident about building this adapter by my self.
I prefer an "all-in-one solution"

@xavan83
Copy link

xavan83 commented Dec 31, 2023

Can you explain how to plug a 3pins adapter to a 4pins zehnder connector? Where to find the 4th "power" pin?

@vespadj
Copy link

vespadj commented Jan 13, 2024

@xavan83 ,

I solved checking cable wiring. LED lights fixed.

J101:
red 12 V dc
black GND
yellow CAN_H
white CAN_L

so:

black GND
yellow CAN_H
white CAN_L

@p5y
Copy link

p5y commented Apr 10, 2024

Hi all,

Great job on figuring out how to communicate with the ComfoAir!
It's awesome to be able to read statistics from the unit with the projects that built on the information in this thread.

I was wondering, did anyone try to do the same with the Zehnder Option Box that enables use of a lot of external sensors? I figured it would be great to replicate Option Box functionality for domotics use. One could for example use existing CO2 sensor ppm value and translate this to a 1-10V value that the Option Box normally sends to the ComfoAir. This would enable CO2 controlled ventilation demand.

I have sniffed the startup comms between Option Box and ComfoAir with a U-CAN, candlelight and CANgaroo as a start, but my reverse engineering skills are a bit rusty. Anyone interested in pursuing this together?

@p5y
Copy link

p5y commented Apr 15, 2024

So I've tried to get familiar with the protocol since my last message.
Tried to figure out what messages I captured on the bus.
Went through the available documentation and some code.

I've isolated all communication from Option Box (address 2) to ComfoAir Q (address 1).
The ventilation unit sends a lot of general status info to the option box that I think it doesn't care about, so I'm ignoring for now. Let's hope it doesn't bite me later.

This is what I think is relevant:

0.000000 Start of measurement
0.000000 1 10080001x Rx d 4 80 61 6A 6E
0.083722 1 10080002x Rx d 4 CF 9A 0F 53
2.084196 1 10040002x Rx d 0
2.085940 1 804042x Rx d 2 89 59
2.087738 1 808042x Rx d 2 0C 00
2.089529 1 80c042x Rx d 2 00 00
2.091351 1 810042x Rx d 2 00 00
2.093110 1 824042x Rx d 2 00 9D
2.094911 1 828042x Rx d 2 00 9D
2.096665 1 82c042x Rx d 2 00 9D
2.098301 1 814042x Rx d 1 00
2.099918 1 818042x Rx d 1 00
2.101539 1 81c042x Rx d 1 00
2.103159 1 820042x Rx d 1 00
2.105356 1 10000002x Rx d 4 04 01 00 00
2.107416 1 10040001x Rx d 4 E7 2B 2C 2D
2.110568 1 804042x Rx d 2 89 59
2.115225 1 808042x Rx d 2 0C 00
2.117022 1 80c042x Rx d 2 00 00
2.120263 1 810042x Rx d 2 00 00
2.124861 1 824042x Rx d 2 00 9D
2.126650 1 828042x Rx d 2 00 9D
2.162488 1 82c042x Rx d 2 00 9D
2.164122 1 814042x Rx d 1 00
2.165738 1 818042x Rx d 1 00
2.167364 1 81c042x Rx d 1 00
2.168978 1 820042x Rx d 1 00
2.171116 1 10000001x Rx d 4 01 01 01 64
2.174157 1 44042x Rx d 1 00
2.373307 1 604042x Rx d 2 00 00
2.379685 1 644042x Rx d 1 00
2.387860 1 688042x Rx d 1 C4
2.426414 1 804042x Rx d 2 89 59

There's some sort of handshake going on in the 100?000?x messages.
When the handshake is (partially?)completed, the option box sends the current status.

I've added a potentiometer to the first 1-10V input during capture, this corresponds with the 804042x messages. The capture shows a lot of messages to this address as I turn the pot over the 38sec of capture time.

Quick python script identifies the following mapping:

804042 src = 2 dest = 1 mapping = 513
808042 src = 2 dest = 1 mapping = 514
80c042 src = 2 dest = 1 mapping = 515
810042 src = 2 dest = 1 mapping = 516
824042 src = 2 dest = 1 mapping = 521
828042 src = 2 dest = 1 mapping = 522
82c042 src = 2 dest = 1 mapping = 523
814042 src = 2 dest = 1 mapping = 517
818042 src = 2 dest = 1 mapping = 518
81c042 src = 2 dest = 1 mapping = 519
820042 src = 2 dest = 1 mapping = 520
44042 src = 2 dest = 1 mapping = 17
604042 src = 2 dest = 1 mapping = 385
644042 src = 2 dest = 1 mapping = 401
688042 src = 2 dest = 1 mapping = 418

When I have time again I want to try and emulate the option box and send sensor data myself.

From the discussion above and information I could find I could not find much information on the handshake that occurs. Any more information on that that I missed maybe?

@samumar82
Copy link

Hello, I'm following this post as I have a CONFOAIR 180 as looks like the pinouts are exactely the same.
Would love to try this project on an ESP32 C3.
Actually the machine is connected via cables to the COMFOSENSE display from which I can regulate the fan, program timings etc.
There are 4 pins, I believe 12V, GND, RX and TX.
Do you think it is safe to wire like described here in parallel with the COMFOSENSE interface and try this code?
Anyone already tried on this unit?

@p5y
Copy link

p5y commented May 6, 2024

You probably have a unit with serial interface. Give this a try:
https://github.com/adorobis/hacomfoairmqtt

@avrovulcanxh607
Copy link

From the discussion above and information I could find I could not find much information on the handshake that occurs. Any more information on that that I missed maybe?

Just to add I am also trying to do this. My main goal for the moment is to emulate the Boost zero-volt contact.

So far I can get it to work by connecting up the existing option box to do the handshake, and then quickly swap over the CANBus before it notices... Once that's done my code is able to keep the connection alive and emulate the contact as expected, but obviously I want to be able to do the full handshake directly!

Just wondering if you ever got any further with it?

@decontamin4t0R
Copy link

I think that no one has publicly documented the handshake before. Maybe you could capture the handshake with candump or wireshark and upload it? We currently do not have the need for an optionbox or a LAN C, therefore I can not try it myself...

@avrovulcanxh607
Copy link

Yes this is the problem I have!

I've been using a CANBus adapter on an Arduino to do all my experiments, so I'll have a go at capturing the handshake.

I will readily admit I don't fully understand the IDs etc so any pointers are greatly appreciated.

@p5y
Copy link

p5y commented Oct 31, 2024

Does the handshake message change every powerup?
I haven't had time to investigate any further yet.

@avrovulcanxh607
Copy link

I don't think so - but the problem is I only have access to one unit for testing, so if it varies between units, systems etc it'll screw stuff up!

@p5y
Copy link

p5y commented Oct 31, 2024

There's not much to permanently screw up I think. Especially with the optionbox.
My handshake is already posted a few messages back, so you can compare against that for starters.

@avrovulcanxh607
Copy link

So far I've determined that the option box sends back data regardless of the handshake, so it seems very likely that it doesn't care about it at all. That's how I discovered the correct ID etc to keep the connection alive & trigger the boost - connected the Option box directly to my arduino & powered up seperately. It happily pings away.

It therefore seems possible that the handshake message is something like a version number...? In any case I'm very much hoping that means it won't ever change.

@gytisgreitai
Copy link

It seems that my comfoair died. It does the startup sequence, fans are on, display is not working and then it stops and green light on top keeps blinking. Is it possible to check the error codes somehow?

@djwlindenaar
Copy link
Author

Haven't figured out a way to check error codes. I guess we need someone with a LAN-C box and a sniffer on the can bus for that.
Coincidentally I had a similar issue with my unit. Display not lighting up and abnormal function.
I didn't figure out what was wrong, because during diagnosing it just worked again. This happened when I disconnected the display (with power off) and then started the unit. It just worked as normal. Then powered off again, reconnected the display and that also worked again. I guess some poor contact on the display connector, but can't be sure.

@gytisgreitai
Copy link

for me it was a faulty sensor, having the ability to read error codes would have probably saved some money in service labor cost :) But oh well, good that at leas there is some control

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests