Hoewel de MCBA-ketelbesturing al weer wat ouder is, wilde ik toch weten wat er op de bus gebeurt. Met behulp van het programma Recom, een “GMI1414” interface en wat geduld vond ik het uit.
De eerste stappen in de juiste richting vond u al op de postings doe-het-zelf: Remeha-ketel en Arduino en I²C en een Remeha-ketel. Nu bestelde ik de “Remeha INTERFACE (RECOM-MCBA GMI1414)” en ondertussen kwam ik wat te weten over deze ketelsturing.
Het is namelijk Philips. In elk geval doorverkocht door Gasmodul, Honeywell en Bosch. De interface bevat – zoals hier ook al werd opgemerkt – een MAX202 TTL-naar-RS232-transceiver en een LPC764 microcontroller.
Op basis van de datasheet van beide chips is eenvoudig terug te vinden welke pinnen je moet uitlezen om zowel het seriële signaal als het I²C-signaal van de ketel te bekijken:
Aansluiten op een logic analyzer – en daar gaan we. De communicatie met dit type – oudere – ketels verloopt via 4800 baud en verder geheel standaard. Ik heb me niet bekommerd om DSR/DTR en RTS/CTS – volgens mij doet niemand dat.
Initieel zendt Recom seriëel de volgende serie (alle getallen in hex):
07 42 A0 00 05 40 D2
De eerste twee bytes en de laatste byte zijn opdrachten voor de GMI1414: de eerste byte is de lengte van het bericht; dan volgt de opdracht (42 is “lees als master”, in latere communicatie vind ik nog de opdrachten 43, dat lijkt “schrijf als master” en 40, lijkt te zijn “lees als slave”). De laatste byte is de 2-complement-checksum van het gehele bericht – je telt alle bytes op, modulo 256 en met de checksum erbij kom je dan weer op nul uit.
De A0, 00 en 05 zijn I²C-bus-opdrachten. A0 is het te lezen address – de verwarring 7-bit versus 8-bit I²C-adressering laat ik even voor wat het is, gewoon verwarring dus, zoek zelf maar even na dat A0 / 2 = 0x50 en omdat de LSB in I²C-land de lees/schrijfbit is, kun je het te gebruiken adres zowel schrijven als A0 (dan is in principe A0 lezen en A1 is schrijven, maar dat noteert niemand zo), of als 0x50R / 0x50W. De 00 is het register op dit adres, want zo werkt dat: je schrijft 00 naar de slave met adres 0x50 en daarna lees je de waarde(n) van dit register terug.
Op de I²C-bus stuurt de GMI1414 vervolgens als busmaster inderdaad de volgende serie:
50W00 50R
De ketel antwoordt met precies 5 bytes (kijk, dat is de 05 uit onze opdracht):
AA 02 24 01 00
… waarna de interface via 4800 baud terugmeldt:
08 00 AA 02 24 01 00 27
Ook hier weer: 08 bytes lengte; 00 is zoiets als “geen opdracht” of “niks”; AA 02 24 01 00 is de door de ketel verstrekte data en 27 de 2-complement-checksum.
Het uitlezen van de ketelconfiguratie doet Recom door op dezelfde manier steeds 8 bytes te lezen van de slave op 0x50 (of 0xA0) uit registers (hex) 40, 48, 50, 58, 60, 68, 70, en 78. Dus:
Recom: 07 42 A0 40 08 40 8F (seriëel) I2C: 50W40, 50R - de ketel zegt "37 0D 3C 59 6E 2F 00 0F" ... wat seriëel 0B 00 37 0D 3C 59 6E 2F 00 0F 70 wordt Recom: 07 42 A0 48 08 40 87 (seriëel) I2C: 50W48, 50R - enzovoorts.
Met behulp van de xml-files in de configuratiedirectory van Recom kunnen we achterhalen wat de verschillende bytes betekenen, maar daar is nog wel een hobbel te nemen. De bestanden staan namelijk vol kruisverwijzingen. Quinta_P1.xml bevat bijvoorbeeld teksten als name.nr=”1050″ description.nr=”923″ unit.nr=”312″, die je op basis van de file language-nl.xml moet omzetten: text id=”1050″ value=”Start modul. dT”, text id=”923″ value=”Als de dT boven deze waarde komt, terugmoduleren” en 312 staat gewoon voor gewoon “°C”.
Om dat allemaal te fixen, schreef ik een Python-programma, dat alle result.nr, name.nr en unit.nr omzet in de gegevens uit language-nl.xml. (N.B. zelf word ik altijd vrolijk van recursieve functies, dat is m.i. ook de beste methode om een XML-bestand te bewerken – waarbij aangetekend dat “language-nl.xml” zodanig plat is opgezet dat ik het daar maar even oversloeg). (Terzijde: ik vind Python prachtig om even gauw iets in te programmeren, maar het gegeven dat je halverwege je code gewoon een functie kunt definiëren vind ik vunzig. Maar ja, code opschonen voor een losse blogpost is me dan ook weer wat veel van het goede).
#!/usr/bin/python3 from xml.dom import minidom from xml.dom import Node xml=minidom.parse("/home/username/config/language-nl.xml") textlijst={} for n in xml.childNodes[0].childNodes: if(n.localName == 'text'): textlijst[n.attributes.item(0).value]=n.attributes.item(1).value xml.unlink() xml=minidom.parse("/home/username/config/Quinta_P1.xml") def replaceresultnr(node): if ( (node.nodeType == Node.ELEMENT_NODE) and (node.hasAttributes())): for attrnum in range(node.attributes.length): if (node.attributes.item(attrnum).name in ["result.nr", "name.nr", "unit.nr", "description.nr"] ): node.attributes.item(attrnum).value = textlijst[node.attributes.item(attrnum).value] for subnode in node.childNodes: replaceresultnr(subnode) replaceresultnr(xml.documentElement) print(xml.toxml())
(to be continued)