risorse | daikin altherma su emoncms

Daikin Altherma su EmonCMS

Attenzione: ho posto la massima cura ed attenzione nel redigere questi appunti; declino tuttavia ogni responsabilità per eventuali imprecisioni, errori od omissioni, così come declino ogni responsabilità per eventuali danni a cose, proprietà o persone derivanti dall’uso di questi contenuti.

Introduzione

Il sistema di monitoraggio dell'impianto fotovoltaico basato su EmonCMS di cui ho già scritto qui fa uso di una pompa di calore Daikin Altherma 3 H HT. Poiché in rete esistono diversi progetti in grado di recuperarne i parametri di funzionamento – alcuni anche di modificarli –, sarebbe interessante integrare anche questa fonte di dati nel sistema attuale (cfr. repository GitHub solarmon).

Riferimenti

Il lavoro è iniziato con l'analisi di alcune implementazioni esistenti:

I primi due progetti sfruttano il CAN bus utilizzato dalle unità interne della pompa di calore per comunicare tra loro. Il protocollo impiegato è proprietario e le specifiche non sono di dominio pubblico; in rete non si trova nulla di ufficiale a riguardo. Zanac è stato il principale attore coinvolto nella decodifica del protocollo (lui stesso ha raccolto il testimone di un utente Daikin tedesco). Il suo repository risulta però inattivo da qualche anno; anche quello di Spanni26 è fermo da un paio d'anni, ma risulta comunque più aggiornato. Entrambi supportano la connessione al CAN bus che la pompa di calore rende accessibile al connettore J13 della scheda principale secondo due modalità: Ethernet/CAN con la scheda PiCAN2 o USB/OBD attraverso un adattatore basato sull'integrato ELM327.

Il terzo progetto sfrutta una connessione seriale interna. Il protocollo utilizzato su questo canale, anch'esso proprietario e decodificato dallo sviluppatore stesso, sembra essere in uso esclusivamente sulle pompe di calore modello Altherma. Secondo il giudizio di Spanni26 questa implementazione, essendo basata su una linea seriale a 9600 Baud, risulta meno efficiente del bus CAN che invece lavora a 20Kbps.

Gli ultimi due progetti sfruttano un terzo canale di comunicazione, denominato P1/P2. Si tratta di una linea seriale non standard che fa uso di un protocollo che varia da modello a modello di pompa. L'interfacciamento con la scheda Raspberry Pi richiede lo sviluppo di un adattatore hardware piuttosto complicato.

La scelta per la prima prova di fattibilità cade sul CAN bus, considerando che è già a disposizione un adattatore USB/OBD “Ford modified ELM327”:

L'adattatore UDB/OBD che sarà oggetto dei primi esperimenti.

CAN bus

L'utente EmilianoM del forum cercaenergia, un collaboratore di Zanac, spiega nel dettaglio come hanno proceduto al reverse-engineering del protocollo nei tre articoli:

I tre articoli rappresentano un estratto del lungo thread dal titolo “Interfacciamento HPSU compact via can-bus” pubblicato sul forum cercaenergia che traccia i progressi di Zanac nel tempo, passo dopo passo, vicoli ciechi inclusi.

Queste fonti forniscono i parametri di funzionamento del CAN bus Daikin:

Collegamento alla Raspberry Pi

Quando l'adattatore USB/OBD viene collegato alla scheda Raspberry Pi, il sistema operativo crea un nuovo dispositivo USB nella gerarchia /dev, tipicamente /dev/ttyUSB0 se non ci sono altre apparecchiature connesse. I comandi lsusb, dmesg udevadm possono essere d'aiuto nell'identificare l'esatto dispositivo associato all'adattatore in caso di dubbi. L'appendice A descrive un metodo per assegnare un nome predefinito a scelta al dispositivo per facilitarne l'utilizzo.

Una volta connesso, è possibile interrogare l'adattatore attraverso i tanti comandi AT supportati dall'integrato ELM327 contenuto al suo interno (qui una copia locale delle specifiche del chip). La comunicazione può avvenire attraverso un qualunque terminale, una volta nota la velocità della seriale. L'ELM327 implementa una normale seriale 8N1 a velocità variabile; quelle supportate sono:

L'adattatore a disposizione era configurato sulla velocità massima:

pi@emonpi:/ $ screen /dev/ttyUSB0 500000

L'invio del comando di azzeramento ATZ ha avuto il seguente effetto:

>LM327 1.4

La versione non è quella ottimale, stando a quanto riportato nel forum:

Se la scatola che contiene l'elm327 ha la scritta “v1.5a” evitatela... cercate di comprare una con “v 2.1”. I cloni con versione 2.1 a quanto sembra implementano tutte le specifiche elm327!

Fonte: Zanac.

In realtà si dimostrerà sufficientemente avanzata per questa applicazione.

Se la velocità della porta seriale dell'adattatore non è nota a priori occorre procedere per tentativi fino ad ottenere una risposta sensata al comando ATZ.

Cablaggio verso la pompa di calore

Il collegamento elettrico tra l'adattatore USB/OBD e il connettore J13 della scheda madre della pompa di calore si realizza con tre conduttori:

Pin J13Pin OBD
CAN-H6
CAN-L14
CAN-GND5
CAN-VCCN.C.

Nota: l'adattatore non necessita della tensione della batteria veicolo per funzionare. Inoltre, secondo quanto riportato nel forum cercaenergia, la tensione sul pin CAN VCC del connettore J13 risulta essere, a vuoto, di circa 23V quindi potenzialmente dannosa per i circuiti dell'adattatore che sono verosimilmente tarati per un voltaggio di 12V essendo pensato per un uso prettamente automobilistico.

Verificare che l'interruttore HS-CAN/MS-CAN dell'adattatore sia in posizione HS-CAN.

Nota: HS-CAN è la linea CAN ad alta velocità (detta anche “high-speed CAN” o “CAN C”) che insiste sui pin 6/14 della presa OBD; la linea MS-CAN (“mid-speed CAN” o “CAN B”) è invece presente sui pin 3/11. Non è chiaro se collegare i cavi provenienti dalla pompa di calore ai pin 3/11 del connettore OBD spostando l'interruttore in posizione MS-CAN sia altrettanto efficace quanto la configurazione qui proposta.

Monitoraggio del traffico CAN

Per poter monitorare con successo il traffico è necessario configurare opportunamente l'integrato ELM327. Conviene innanzitutto attivare l'echo locale e l'emissione di un carattere LF dopo ogni CR per rendere le risposte più leggibili sul terminale. La sequenza di comandi da inviare è:

AT Z
OKL17 v1.4

>

Stato del terminale dopo l'invio dei comandi AT Z, AT E1 e AT L1.

A questo punto è necessario impostare l'ELM327 per l'interfacciamento a un CAN bus a 20Kbps con identificativi a 11 bit e pacchetti da 7 bit. Per farlo si può sfruttare uno dei due protocolli definiti dall'utente supportati dall'integrato:

Entrambi i protocolli necessitano dell'impostazione della corretta velocità. Mentre per il protocollo C il cambio di velocità necessita di una modifica del Programmable Parameter (nel seguito: registro) 2F, le proprietà del protocollo B possono anche essere cambiate al volo con un opportuno comando.

La velocità del CAN bus è determinata dal denominatore della frazione:

500kbaud/DIVISOR

Per ottenere una velocità di 20Kbps il divisore deve essere posto a 25 (19 in esadecimale). Questo è quindi il valore che va scritto nel registro 2D per il protocollo B, nel registro 2F per il protocollo C. La sequenza di comandi per rendere effettiva questa impostazione è:

Il primo comando aggiorna il contenuto del registro xx sovrascrivendo il vecchio valore con il nuovo divisore (19 esadecimale, 25 decimale); il secondo attiva il registro il cui contenuto verrebbe altrimenti ignorato dal firmware dell'adattatore; il terzo rende subito effettiva la nuova impostazione che in caso contrario lo diventerebbe alla successiva riaccensione.

Le altre caratteristiche del protocollo sono definite dalle maschere di bit contenute nei registri 2C per il protocollo B e 2E per il protocollo 2C:

Each bit of this byte controls an option, as follows:

b7Transmit ID Length0: 29 bit ID, 1: 11 bit ID
b6Data Length0: fixed 8 byte, 1: variable DLC
b5Receive ID Length0: as set by b7, 1: both 11 and 29 bit
b4baud rate multiplier0: ×1, 1: ×8/7
b3reserved for futureleave set at 0

b2, b1, and b0 determine the data formatting options:

b2b1b0Data Format
000none
001ISO 15765-4
010SAE J1939

Fonte: specifiche ELM327.

Per selezionare gli identificativi a 11 bit e consentire la ricezione e l'invio di pacchetti da 7 byte il registro deve assumere la configurazione 11000000, ovvero C0 in esadecimale. La sequenza completa di configurazione del protocollo B diventa quindi:

La stessa sequenza configura il protocollo C dopo aver applicato le sostituzioni 2C/2E, 2D/2F.

Come già anticipato l'ELM327 consente di configurare il solo protocollo B in modo più immediato per mezzo del comando AT PB. È sufficiente far seguire al comando i valori destinati ai registri 2C e 2D. Il comando ha effetto immediato per cui non è nemmeno necessario comandare un reset dopo la sua esecuzione:

Nota: il comando AT V1 (variable data lengths on) ha lo stesso effetto del portare a 1 il bit n. 6 dei registri 2C/2E ed anzi è prioritario rispetto allo stato di quei bit. Per compattezza qui si è preferito mostrare come agire direttamente sui registri.

La configurazione completa dell'ELM327 per il monitoraggio passivo del traffico che transita sul CAN bus via terminale è quindi composta dalla sequenza:

L'ultimo comando ha l'effetto di selezionare il protocollo B.

Per avere una visione d'insieme dei pacchetti che si scambiano i nodi della rete conviene attivare la visualizzazione degli identificativi (comando AT H1) e della dimensione dell'area dati dei singoli pacchetti (AT D1); fatto ciò si può avviare il monitoraggio passivo con il comando AT MA, che si può interrompere inviando un carattere qualunque all'integrato:

>AT H1
OK

>AT D1
OK

>AT MA
10A 7 31 00 FA 06 95 00 00
180 7 22 0A FA 06 95 00 00
10A 7 61 00 FA 01 1A 00 00
300 7 22 0A FA 01 1A 00 00
10A 7 61 00 FA 13 58 00 00
300 7 22 0A FA 13 58 00 00
10A 7 31 00 0C 00 00 00 00
180 7 22 0A 0C 00 60 00 00
...
10A 7 31 00 FA C2 FA 00 00
180 7 22 0A FA C2 FA 0B BC
STOPPED.

Stato del terminale trascorso qualche secondo di monitoraggio.

La comparsa di alcune sequenze esadecimali ben formattate conferma che l'ELM327 è stato configurato correttamente (vedere l'appendice B per la decodifica di questi messaggi). Resta ora da verificare che i nodi della rete CAN rispondono alle richieste inviate dal terminale.

Interrogazione dei nodi del CAN bus

Le pompe di calore Daikin presentano svariate decine di parametri operativi. Zanac ne ha decodificati all'incirca un centinaio e, per ognuno di loro, ha determinato la corretta sequenza di byte da trasmettere sul CAN bus per ottenerne il valore corrente. Questa conoscenza è codificata in un file CSV nel repository di Zanac (cfr. file etc/pyHPSU/commands_hpsu.csv), mentre Spanni26 ha optato per il formato JSON (cfr. file etc/pyHPSU/commands_hpsu.json).

È bene non prendere per oro colato quanto riportato in questi cataloghi: alcuni utenti segnalano diverse imprecisioni e discrepanze che non sempre sono state recepite con tempestività, vedere ad esempio il ticket #41 del repository di Spanni26 “command list cleanup”.

Per richiedere il valore di un certo parametro è dapprima necessario individuarlo in uno dei due cataloghi; per esempio, per ottenere il valore corrente della pressione dell'acqua, nel JSON predisposto da Spanni26 si trova:

    "water_pressure" : {
        "name" : "water_pressure",
        "command" : "31 00 1C 00 00 00 00",
        "id" : "190",
        "divisor" : "1000",
        "writable" : "false",
        "unit" : "bar",
        "type" : "float"
    },

Estratto del file commands_hpsu.json di Spanni26.

A fronte della definizione sopra riportata, i comandi da inviare sul CAN bus sono due:

Nel caso del parametro water_pressure:

>AT SH 190
OK

>31 00 1C 00 00 00 00
32 10 1C 04 DA 00 00
>

Questa è la conferma che pure le interrogazioni vengono recepite e correttamente servite.

Nota: può darsi che di tanto in tanto si ottenga un NO DATA come risposta: è il segnale che il timeout di ricezione dell'ELM327 è scaduto. Il valore predefinito di questo parametro è 205ms, ma può essere modificato con il comando AT ST xx. Occorre fare attenzione perché il comando interferisce con la gestione adattiva dei timeout dell'integrato (cfr. comandi AT ATx), fare riferimento alla documentazione ufficiale per ulteriori dettagli.

Prove di durata

Monitoraggio passivo del CAN bus

Ho approntato uno script Python per catturare e decodificare i messaggi che transitano sul CAN bus (cfr. file daikin_altherma_sniffer.py nel repository Solarmon). Dopo un paio d'ore di funzionamento, il risultato è stato:

Il parametro t_ext, che misura la temperatura esterna, transita regolarmente ogni 10s; gli altri parametri si presentano con un pattern temporale tanto stabile quanto inaspettato: due passaggi a distanza di poco più di un minuto intervallati da un periodo di silenzio di circa sei minuti. Questi sono i primi rilievi registrati:

Nota: per minimizzare il numero di caratteri trasferiti dal CAN bus alla seriale, l'ELM327 è stato configurato senza echo (AT E0) e con l'emissione dei LF e degli spazi inibita (comandi AT L0 e AT S0).

Monitoraggio attivo del CAN bus

Ho successivamente rifattorizzato e irrobustito il codice dello sniffer al punto precedente e trasformato in un monitor che interroga con frequenza regolare il CAN bus su una quarantina di parametri (cfr. file daikin_altherma_monitor.py sempre nel repository Solarmon). Una sessione di acquisizione con periodo di 30 secondi, della durata di otto ore circa, ha raccolto 951 campioni, di cui 23 incompleti (2.4%). L'assenza dei valori è riconducibile a sporadici casi di risposte NO DATA.

Non si evidenzia alcun schema specifico nella distribuzione o nella forma dei campioni parziali. Per il momento il tasso di mancate letture è ritenuto accettabile.

Relativamente alla frequenza di campionamento, Zanac dice:

[…] ho già fatto esperimenti di bombing, le richieste prioritarie non intasano il bus, e l'hpsu continuava a lavorare e mandare dati sul display! Con 37 richieste ogni 2 minuti siamo centinaia di volte sotto il bombing che ho testato.

Fonte: Zanac.

E ancora:

20000 baud / 108 bit per messaggio di richiesta = 185 richieste AL SECONDO... Dimezziamole per includere anche le risposte, più di 90 richieste / risposte AL SECONDO!!!!

Fonte: Zanac.

Appendici

A – Assegnazione di un nome arbitrario a un dispositivo USB

Per assegnare un nome specifico ad un dispositivo USB è sufficiente ricavare idVendor e idProduct dell'apparecchiatura e usare queste informazioni per identificarla all'atto della connessione. I due attributi sono ricavabili attraverso il comando lsusb, dmesg o udevadm, da lanciare dopo aver collegato il dispositivo:

pi@emonpi:/ $ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 004: ID 10c4:ea60 Cygnal Integrated Products, Inc. CP2102/CP2109 UART Bridge Controller [CP210x family]
Bus 001 Device 003: ID 148f:7601 Ralink Technology, Corp. MT7601U Wireless Adapter
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Il dispositivo di interesse in questo caso è il secondo in lista:

Si procede quindi alla creazione del file di sistema /etc/udev/rules.d/10-usb-serial.rules:

pi@emonpi:/ $ sudo nano /etc/udev/rules.d/10-usb-serial.rules

Il file contiene le regole d'associazione dispositivo/nome simbolico (una per riga):

# USB/OBD Adapter
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SYMLINK+="ttyUSB_CAN"

Per rendere immediatamente effettiva la nuova configurazione dare il comando:

pi@emonpi:/ $ sudo udevadm trigger

L'effetto è quello desiderato:

pi@emonpi:/ $ ls -al /dev/ttyUSB*
crw-rw---- 1 root dialout 188, 0 Dec 20 21:54 /dev/ttyUSB0
lrwxrwxrwx 1 root root         7 Dec 20 21:53 /dev/ttyUSB_CAN -> ttyUSB0

B – Protocollo CAN bus Daikin

Nota: le informazioni riportate di seguito sono il risultato di un'integrazione di quanto trovato in rete, in particolare (ma non solo) nell'articolo già citato Hack Daikin HPSU Compact con Raspberry Pi, Python e PiCAN2 – II, in Rotex HPSU configuration di Crycode-de e nella pagina https://www.juerg5524.ch.

Riprendiamo in esame alcuni dei pacchetti catturati durante il monitoraggio passivo:

10A 7 31 00 FA 06 95 00 00
180 7 22 0A FA 06 95 00 00
10A 7 61 00 FA 01 1A 00 00
300 7 22 0A FA 01 1A 00 00
10A 7 31 00 0C 00 00 00 00
180 7 22 0A 0C 00 60 00 00

Le prime tre cifre esadecimali rappresentano l'identificativo del messaggio CAN; in questo caso corrispondono all'indirizzo del nodo della rete che ha originato il messaggio secondo la tabella:

MessageSource
0x10ADisplay
0x180Caldaia
0x30xModulo di controllo
0x400Termostato remoto
0x500Modulo di riscaldamento
0x60xModulo di miscelazione
0x680Software remoto

Una possibile interpretazione degli identificativi dei messaggi.

Il 7 che sistematicamente segue l'identificativo del messaggio, pubblicato in virtù del comando AT D1, rappresenta il numero di byte che costituiscono il corpo del messaggio e conferma quanto letto sui forum: su questo CAN bus transitano pacchetti da 7 byte.

I due byte successivi contengono l'identificativo del nodo destinatario del messaggio ed il tipo di messaggio stesso; l'indirizzo del destinatario si ottiene con la formula:

target address = (B0 & 0xF0) << 3 | (B1 & 0x0F)

Il tipo di messaggio è invece codificato nei 4 bit meno significativi del primo byte:

B0 & 0x0FMessage Type
0x00Set
0x01Request
0x02Response

Il terzo byte contiene l'identificativo del parametro oggetto dell'interrogazione; se valorizzato a FA allora l'identificativo è un intero a 16 bit ed è memorizzato nei due byte successivi, con il byte più significativo per primo. Segue infine il valore assunto dal parametro, che è memorizzato nel byte o nei due byte successivi a seconda che il parametro sia definito su 8 o 16 bit.

Alla luce di queste osservazioni, i 6 messaggi d'esempio sono così interpretabili:

MessageSourceTargetTypeParameterValue
10A 3100FA069500000x10A0x180Request0x0695N/A
180 220AFA069500000x1800x10AResponse0x06950x0000
10A 6100FA011A00000x10A0x300Request0x011AN/A
300 220AFA011A00000x3000x10AResponse0x011A0x0000
10A 31000C000000000x10A0x180Request0x0CN/A
180 220A0C006000000x1800x10AResponse0x0C0x0060

Resta la questione di determinare l'indirizzo con il quale deve presentarsi il software di monitoraggio; quasi tutte le implementazioni tendono a usare l'indirizzo del nodo destinatario aumentato di 16 (0x10 esadecimale): per interrogare il nodo 180 si invia un messaggio con ID 190, per interrogare il nodo 300 si invia un messaggio con ID 310 e così via. Il comando per impostare l'ID del messaggio è AT SH xxx; tale identificativo verrà adottato per tutti i messaggi che verranno inviati da qui in avanti.

Sebbene inizialmente nutrissi qualche dubbio sulla necessità di allineare l'ID del messaggio all'identificativo del nodo destinatario — del resto il display emette sempre messaggi con ID 0x10A sia quando interroga la caldaia, sia quando interroga il modulo di controllo — si verifica facilmente che è proprio così.

Riferimenti

Il file note-daikin.md del repository Solarmon contiene la cronistoria completa del processo che ha prodotto questa analisi.

Pagina modificata il 06/01/2023