Solarfocus ecomanager-touch via Modbus TCP

Recently I connected a Solarfocus Biomass Boiler “therminator II” with openHAB. These products use the “Solarfocus ecomanager-touch” controller, which has a 7” touch display. With this tutorial I share my working configuration and experiences on the Modbus TCP connection.

openHAB environment
Tested OH versions 2.5.12 - Release Build, 3.0.1 - Release Build
Needed Bindings Modbus
Needed Transformations Javascript Transformation, Map Transformation

1. Check software version and MODBUS TCP option on Solarfocus controller

The Modbus TCP Feature requires a minimum software version. If you don’t have the Modbus TCP Status menu, ask your dealer to activate this feature on your device.

Product Software version
Wood Boiler therminator II touch from V 19.072
Wood Boiler pelletelegance, octoplus, pellettop touch from V 19.050
Control module ecomanager-touch from V 19.050
Air Source Heat Pump vampair

Make sure your Solarfocus is connected to a network. The IP settings can be managed in the IP VNC menu.

2. Textual configurations

solarfocus.things

Bridge modbus:tcp:solarfocus [ host="192.168.0.85", port=502, id=1 ] {

    // Input registers: Functioncode FC04 (0x04)
    // +++++ NOTE: readTransform="JS(conv_divide10.js)" working - readTransform="JS:conv_divide10.js" not working +++++

    Bridge poller sf_heizkreis [ start=1100, length=8, refresh=5000, type="input" ] {
        Thing data SF1100 [ readStart="1100", readValueType="int16", readTransform="JS(conv_divide10.js)" ]   // Vorlauftemperatur
        Thing data SF1103 [ readStart="1103", readValueType="uint16", readTransform="JS(conv_binary2.js)" ]   // Begrenzungsthermostat offen/geschlossen
        Thing data SF1105 [ readStart="1105", readValueType="uint16", readTransform="JS(conv_binary1.js)" ]   // Heizkreispumpe Ein/Aus
        Thing data SF1106 [ readStart="1106", readValueType="uint16" ]   // Mischerstellung
        Thing data SF1107 [ readStart="1107", readValueType="uint16" ]   // Status Heizkreis
    }

    Bridge poller sf_puffer [ start=1900, length=5, refresh=10000, type="input" ] {
        Thing data SF1900 [ readStart="1900", readValueType="int16", readTransform="JS(conv_divide10.js)" ]   // Puffertemperatur oben
        Thing data SF1901 [ readStart="1901", readValueType="int16", readTransform="JS(conv_divide10.js)" ]   // Puffertemperatur unten
        Thing data SF1902 [ readStart="1902", readValueType="int16", readTransform="JS(conv_divide10.js)" ]   // Puffertemperatur X35
        Thing data SF1903 [ readStart="1903", readValueType="int16", readTransform="JS(conv_binary1.js)" ]   // Puffer Ladepumpe
        Thing data SF1904 [ readStart="1904", readValueType="uint16" ]   // Pufferstatus
    }

    Bridge poller sf_boiler [ start=500, length=2, refresh=10000, type="input" ] {
        Thing data SF500 [ readStart="500", readValueType="int16", readTransform="JS(conv_divide10.js)" ]   // Boiler Temperatur
        Thing data SF501 [ readStart="501", readValueType="uint16" ]   // Boiler Status
    }

    Bridge poller sf_kessel [ start=2400, length=10, refresh=5000, type="input" ] {
        Thing data SF2400 [ readStart="2400", readValueType="int16", readTransform="JS(conv_divide10.js)" ]   // Kesseltemperatur
        Thing data SF2401 [ readStart="2401", readValueType="uint16" ]   // Statuszeile Kessel
        Thing data SF2404 [ readStart="2404", readValueType="int16" ]   // Nachrichtennummer
        Thing data SF2405 [ readStart="2405", readValueType="int16", readTransform="JS(conv_binary3.js)" ]   // Türkontakt offen/geschlossen
        Thing data SF2406 [ readStart="2406", readValueType="int16" ]   // Kesselreinigung %
        Thing data SF2407 [ readStart="2407", readValueType="int16" ]   // Ascheboxfüllstand
        Thing data SF2408 [ readStart="2408", readValueType="int16", readTransform="JS(conv_divide10.js)" ]   // Außentemperatur
        Thing data SF2409 [ readStart="2409", readValueType="int16" ]   // Kesselbetriebsart
    }


    // Holding registers: Read with Functioncode FC3 (0x03); Write with Functioncode  FC16  (0x10)
    // +++++ NOTE: writeMultipleEvenWithSingleRegisterOrCoil="true" (FC16); By default, or when 'false, FC06 ("Write single holding register")

    Bridge poller sf_holdingreg1 [ start=32600, length=8, refresh=20000, type="holding" ] {
        Thing data SF32600  [ readStart="32600", readValueType="int16", readTransform="JS(conv_divide10.js)", writeStart="32600", writeValueType="int16", writeTransform="JS(conv_multiply10.js)", writeType="holding", writeMultipleEvenWithSingleRegisterOrCoil="true" ]   // Vorlaufsolltemperatur
        Thing data SF32603  [ readStart="32603", readValueType="int16", writeStart="32603", writeValueType="int16", writeType="holding", writeMultipleEvenWithSingleRegisterOrCoil="true" ]   // Heizkreisbetriebsart
        Thing data SF32605  [ readStart="32605", readValueType="int16", readTransform="JS(conv_divide10.js)", writeStart="32605", writeValueType="int16", writeTransform="JS(conv_multiply10.js)", writeType="holding", writeMultipleEvenWithSingleRegisterOrCoil="true" ]   // Raumtemperatur Soll
        Thing data SF32606  [ readStart="32606", readValueType="int16", readTransform="JS(conv_divide10.js)", writeStart="32606", writeValueType="int16", writeTransform="JS(conv_multiply10.js)", writeType="holding", writeMultipleEvenWithSingleRegisterOrCoil="true" ]   // Raumtemperatur Ist extern
        Thing data SF32607  [ readStart="32607", readValueType="int16", writeStart="32607", writeValueType="int16", writeType="holding", writeMultipleEvenWithSingleRegisterOrCoil="true" ]   // Raumfeuchte Ist extern
    }

    Bridge poller sf_holdingreg2 [ start=32000, length=3, refresh=30000, type="holding" ] {
        Thing data SF32000  [ readStart="32000", readValueType="int16", readTransform="JS(conv_divide10.js)", writeStart="32000", writeValueType="int16", writeTransform="JS(conv_multiply10.js)", writeType="holding", writeMultipleEvenWithSingleRegisterOrCoil="true" ]   // Boiler – Solltemperatur
        Thing data SF32001  [ readStart="32001", readValueType="int16", readTransform="JS(conv_binary1.js)", writeStart="32001", writeValueType="int16", writeTransform="JS(conv_binary6.js)", writeType="holding", writeMultipleEvenWithSingleRegisterOrCoil="true" ]   // Boiler – Einmalladung
        Thing data SF32002  [ readStart="32002", readValueType="int16", writeStart="32002", writeValueType="int16", writeType="holding", writeMultipleEvenWithSingleRegisterOrCoil="true" ]   // Boiler – Freigabeart
    }
}

solarfocus.items

Group gSolarfocus   "Group Solarfocus"     <text>
Group gSFPuffer     "Group Buffer tank"         <text>
Group gSFHeizkreis  "Group Heating circuit"      <text>
Group gSFBoiler     "Group Hot water"         <text>

// ########## Heating circuit ##########
Number  MODBUS_SF1100      "Flow pipe temperature [%.1f °C]"  <flowpipe>  (gSolarfocus, gSFHeizkreis)  { channel="modbus:data:solarfocus:sf_heizkreis:SF1100:number" }   // Vorlauftemperatur
Contact MODBUS_SF1103      "Limit thermostat []"  <lock>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_heizkreis:SF1103:contact" }   // Begrenzungsthermostat offen/geschlossen
Switch  MODBUS_SF1105      "Heating circuit pump []"  <pump>  (gSolarfocus, gBhCount, gSwCount)  { channel="modbus:data:solarfocus:sf_heizkreis:SF1105:switch" }   // Heizkreispumpe Ein/Aus
Number  MODBUS_SF1106      "Mixer position [%d %%]"  <text>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_heizkreis:SF1106:number" }   // Mischerstellung
Number  MODBUS_SF1107      "Status Heating circuit [MAP(sf-heizkreis1.map):%s]"  <radiator>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_heizkreis:SF1107:number" }   // Status Heizkreis


// ########## Buffer tank ##########
Number  MODBUS_SF1900      "Buffer temperature top [%.1f °C]"  <temperature>  (gSolarfocus, gSFPuffer)  { channel="modbus:data:solarfocus:sf_puffer:SF1900:number" }   // Puffertemperatur oben
Number  MODBUS_SF1901      "Buffer temperature bottom [%.1f °C]"  <temperature>  (gSolarfocus, gSFPuffer)  { channel="modbus:data:solarfocus:sf_puffer:SF1901:number" }   // Puffertemperatur unten
Number  MODBUS_SF1902      "Buffer temperature X35 [%.1f °C]"  <temperature>  (gSolarfocus, gSFPuffer)  { channel="modbus:data:solarfocus:sf_puffer:SF1902:number" }   // Puffertemperatur X35
Switch  MODBUS_SF1903      "Buffer charging pump []"  <pump>  (gSolarfocus, gBhCount, gSwCount)  { channel="modbus:data:solarfocus:sf_puffer:SF1903:switch" }   // Puffer Ladepumpe
Number  MODBUS_SF1904      "Status Buffer [MAP(sf-puffer.map):%s]"  <text>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_puffer:SF1904:number" }   // Pufferstatus


// ########## Hot water ##########
Number  MODBUS_SF500      "Hot water temperature [%.1f °C]"  <faucet>  (gSolarfocus, gSFBoiler)  { channel="modbus:data:solarfocus:sf_boiler:SF500:number" }   // Boiler Temperatur
Number  MODBUS_SF501      "Status Hot water [MAP(sf-boiler.map):%s]"  <text>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_boiler:SF501:number" }   // Boiler Status


// ########## Biomass Boiler ##########
Number  MODBUS_SF2400      "Boiler temperature [%.1f °C]"  <fire>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_kessel:SF2400:number" }   // Kesseltemperatur
Number  MODBUS_SF2401      "Status Boiler [MAP(sf-kessel1.map):%s]"  <text>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_kessel:SF2401:number" }   // Statuszeile Kessel
Number  MODBUS_SF2404      "Message [MAP(sf-meldungen1.map):%s]"  <error>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_kessel:SF2404:number" }   // Nachrichtennummer
Contact MODBUS_SF2405      "Front door []"  <frontdoor>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_kessel:SF2405:contact" }   // Türkontakt offen/geschlossen
Number  MODBUS_SF2406      "Boiler cleaning [%d %%]"  <settings>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_kessel:SF2406:number" }   // Kesselreinigung %
Number  MODBUS_SF2407      "Ash box level [%d %%]"  <settings>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_kessel:SF2407:number" }   // Ascheboxfüllstand
Number  MODBUS_SF2408      "Temperature outside [%.1f °C]"  <temperature>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_kessel:SF2408:number" }   // Außentemperatur
Number  MODBUS_SF2409      "Boiler operating mode [MAP(sf-kessel2.map):%s]"  <text>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_kessel:SF2409:number" }   // Kesselbetriebsart


// ########## Holding registers heating circuit ##########
Number  MODBUS_SF32600      "Flow pipe set-point temperature [%.1f °C]"  <flowpipe>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_holdingreg1:SF32600:number" }   // Vorlaufsolltemperatur
Number  MODBUS_SF32603      "Heating circuit operating mode [MAP(sf-heizkreis2.map):%s]"  <radiator>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_holdingreg1:SF32603:number" }   // Heizkreisbetriebsart
Number  MODBUS_SF32605      "Room set-point temperature [%.1f °C]"  <temperature>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_holdingreg1:SF32605:number" }   // Vorlaufsolltemperatur
Number  MODBUS_SF32606      "Room temperatur external [%.1f °C]"  <temperature>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_holdingreg1:SF32606B:number" }   // Raumtemperatur Ist extern
Number  MODBUS_SF32607      "Room humidity external [%.0f %%]"  <humidity>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_holdingreg1:SF32607B:number" }   // Raumfeuchte Ist extern


// ########## Holding registers hot water ##########
Number  MODBUS_SF32000      "Hot water set-point temperature [%.1f °C]"  <temperature>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_holdingreg2:SF32000:number" }   // Boiler – Solltemperatur
Switch  MODBUS_SF32001      "Hot water one-time charge []"  <switch>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_holdingreg2:SF32001:switch" }   // Boiler – Einmalladung
Number  MODBUS_SF32002      "Hot water operating mode [MAP(sf-boiler2.map):%s]"  <text>  (gSolarfocus)  { channel="modbus:data:solarfocus:sf_holdingreg2:SF32002:number" }   // Boiler – Freigabeart

3. Transformation files

Following you can see the content of the used transformation files. Please note: Most of the map files don’t show all lines. The complete information can be found in the specification document provided by Solarfocus (see References). Please excuse the German language I used here. Please ask Solarfocus for specifications in other languages.

sf-heizkreis1.map

200=Heizkreis ist ausgeschaltet
201=Dauerheizbetrieb
202=Trinkwasserspeichervorrang ist aktiv
203=Dauerabsenkbetrieb
204=Außenfühler Unterbrechung oder Kurzschluss
205…
NULL=undefined
UNDEF=UNDEF
-=no data
=?

sf-puffer.map

200=Puffer ist nicht freigeschaltet
201=Bereitschaft
202=Puffer wird beladen
203=Frostschutzbetrieb
204=Kaminkehrer
205…
NULL=undefined
UNDEF=UNDEF
-=no data
=?

sf-boiler.map

200=Trinkwasserspeicher ist nicht freigeschaltet
201=Bereitschaft
202=Trinkwasserspeicher wird beladen
203=Frostschutzbetrieb
204=Kaminkehrer
205…
NULL=undefined
UNDEF=UNDEF
-=no data
=?

sf-kessel1.map

200=Bereitschaft
201=Zündphase
202=Pelletsbetrieb
203=Kesselsolltemperatur erreicht, Nachlauf
204=Nachlauf
205…
NULL=undefined
UNDEF=UNDEF
-=no data
=?

sf-meldungen1.map

200=OK
201=(1) Bus Unterbrechung
202=(2) Netzsicherung defekt
203=(3) Triac-Sicherung defekt
204=(4) Fehler 24 V DC Versorgung
205…
NULL=undefined
UNDEF=UNDEF
-=no data
=?

sf-kessel2.map

0=Stückholz
1=Stückholz Automatik
2=Stückholz + Pellets
3=Stückholz Automatik + Pellets
4=Pellets
5=Hackgut
NULL=undefined
UNDEF=UNDEF
-=no data
=?

sf-heizkreis2.map

0=Dauerbetrieb
1=Absenkbetrieb
2=Automatik
3=Heizkreis aus (Frostwache)
NULL=undefined
UNDEF=UNDEF
-=no data
=?

sf-boiler2.map

0=Immer Aus
1=Immer Ein
2=Montag – Sonntag
3=Blockweise (Mo-Fr, Sa-So)
4=Tagweise
NULL=undefined
UNDEF=UNDEF
-=no data
=?

Onoff.map

OFF=Off □
ON=On ■
OPEN=open ▯
CLOSED=closed ▮
NULL=undefined
UNDEF=UNDEF
-=no data
=?

conv_divide10.js

(function(inputData) {
    // on read: the polled number as string
    // on write: openHAB command as string
    var DIVIDE_BY = 10;
    return parseFloat(inputData) / DIVIDE_BY;
})(input)

conv_multiply10.js

(function(inputData) {
    // on read: the polled number as string
    // on write: openHAB command as string
    var MULTIPLY_BY = 10;
    return Math.round(parseFloat(inputData, 10) * MULTIPLY_BY);
})(input)

conv_binary1.js

(function(inputData) {
    var out = inputData ;      // allow UNDEF to pass through
    if (inputData == '1' || inputData == 'ON' || inputData == 'OPEN') {
        out = 'ON' ;  // change to OFF or OPEN depending on your Item type
    } else if (inputData == '0' || inputData == 'OFF' || inputData == 'CLOSED') {
        out = 'OFF' ;   // in Anführungszeichen!
    }
    return out ;      // return a string
})(input)

conv_binary3.js

(function(inputData) {
    var out = inputData ;      // allow UNDEF to pass through
    if (inputData == '1' || inputData == 'ON' || inputData == 'OPEN') {
        out = 'OPEN' ;  // change to OFF or OPEN depending on your Item type
    } else if (inputData == '0' || inputData == 'OFF' || inputData == 'CLOSED') {
        out = 'CLOSED' ;   // in Anführungszeichen!
    }
    return out ;      // return a string
})(input)

conv_binary6.js

(function(inputData) {
    var out = ""   //inputData ;      // allow UNDEF to pass through
    if (inputData == 'ON' || inputData == '1' || inputData == 'CLOSED') {
        out = '1' ;  // change to OFF or OPEN depending on your Item type
    } else if (inputData == 'OFF' || inputData == '0' || inputData == 'OPEN') {
        out = '0' ;   // in Anführungszeichen!
    }
    return out ;      // return a string
})(input)

solarfocus.sitemap

sitemap solarfocus label="Solarfocus Biomass Boiler" {
    Frame label="Heating circuit" {
        Default item=MODBUS_SF1100         // Vorlauftemperatur
        Default item=MODBUS_SFHZKURVBERVL  // Heizkurve berechn. Vorlauf
        Default item=MODBUS_SF1106         // Mischerstellung
        Text    item=MODBUS_SF1105  label="Heating circuit pump [MAP(onoff.map):%s]" valuecolor=[==OFF="#A4A4A4", ==ON="#A900DB"] labelcolor=[==ON="green"] 
        Text    item=MODBUS_SF1103  label="Limit thermostat [MAP(onoff.map):%s]" valuecolor=[==CLOSED="#A4A4A4", ==OPEN="#A900DB"] labelcolor=[==OPEN="fuchsia"]  //visibility=[MODBUS_SF1103 == OPEN]
        Default item=MODBUS_SF1107         // Status Heizkreis
    }
    Frame label="Buffer tank" {
        Default item=MODBUS_SF1900   valuecolor=[>=82="red", <40="#1d8dfd"]       // Puffertemperatur oben
        Default item=MODBUS_SF1901   valuecolor=[>=82="red", <40="#1d8dfd"]       // Puffertemperatur unten
        Default item=MODBUS_SF1902   valuecolor=[>=82="red", <40="#1d8dfd"]       // Puffertemperatur X35
        Text    item=MODBUS_SF1903  label="Buffer charging pump [MAP(onoff.map):%s]" valuecolor=[==OFF="#A4A4A4", ==ON="#A900DB"] labelcolor=[==ON="green"] 
        Default item=MODBUS_SF1904         // Pufferstatus
    }
    Frame label="Hot water" {
        Default item=MODBUS_SF500   valuecolor=[>=65="red", <40="#1d8dfd"]         // Boiler Temperatur
        Default item=MODBUS_SF501         // Boiler Status
    }
    Frame label="Biomass Boiler" {
        Default item=MODBUS_SF2400   valuecolor=[>=82="red", <60="#1d8dfd"]         // Kesseltemperatur
        Default item=MODBUS_SF2409         // Kesselbetriebsart
        Text    item=MODBUS_SF2405  label="Front door [MAP(onoff.map):%s]" valuecolor=[==CLOSED="#A4A4A4", ==OPEN="#A900DB"] labelcolor=[==OPEN="green"] 
        Default item=MODBUS_SF2406   valuecolor=[>=95="red", >=90="orange"]                // Kesselreinigung %
        Default item=MODBUS_SF2407   valuecolor=[>=95="red", >=90="orange"]                // Ascheboxfüllstand
        Default item=MODBUS_SF2408   valuecolor=[>=0="red", <0="#1d8dfd"]       // Außentemperatur
        Default item=MODBUS_SF2404   valuecolor=[==0="green", ==200="green", >=1="red"]          // Nachrichtennummer
        Default item=MODBUS_SF2401         // Statuszeile Kessel
    }
    Frame label="Control" {
        Setpoint item=MODBUS_SF32603       minValue=0 maxValue=3 step=1   // Heizkreisbetriebsart
        Setpoint item=MODBUS_SF32600       minValue=22 maxValue=70 step=1   // Vorlaufsolltemperatur
        Setpoint item=MODBUS_SF32605       minValue=5 maxValue=45 step=1   // Raumtemperatur Soll
        Setpoint item=MODBUS_SF32002       minValue=0 maxValue=4 step=1   // Boiler – Freigabeart
        Setpoint item=MODBUS_SF32000       minValue=20 maxValue=65 step=0.5   // Boiler – Solltemperatur
        Default  item=MODBUS_SF32001             // Boiler – Einmalladung
    }
    Frame label="External Room sensor" {
        Default item=MODBUS_SF32606         // Raumtemperatur Ist extern
        Default item=MODBUS_SF32607      //Raumfeuchte Ist extern
    }
}

4. The result (basicui)

5. References

2 Likes

Hi Alfred,

many thanks for sharing your solution. I got a SolarFocus PelletElegance (Firmware Version 19.120) and my setup to read temperatures from the heating system is similar, but I have a couple of issues:

  1. The status of Heating Circuit 1 (register 1107) doesn’t read the values 0-28 like described in the documentation, but has always the same value as the temperature as the temperature of HK2 (register 1150). Very strange, but I checked the configuration a 1000 times and cannot find the error.

Thing definition looks as follows:

    Bridge poller Heizkreis1 [ start=1100, length=8 , refresh=60000, type="input" ] {
        Thing data HK1_Vorlauftemperatur         "Heizkreis 1 Vorlauftemperatur" @ "Heizung" [ readStart="1100", readValueType="int16", readTransform="JS(divide10.js)" ]
        Thing data HK1_Pumpe                     "Heizkreis 1 Pumpe"             @ "Heizung" [ readStart="1105", readValueType="uint16" ]
        Thing data HK1_Mischer                   "Heizkreis 1 Mischer"           @ "Heizung" [ readStart="1106", readValueType="uint16" ]
        Thing data HK1_Status                    "Heizkreis 1 Status"            @ "Heizung" [ readStart="1107", readValueType="uint16" ]
    }

    // Heizkreis 2 - Heizkörper
    Bridge poller Heizkreis2 [ start=1150, length=8, refresh=60000, type="input" ] {
        Thing data HK2_Vorlauftemperatur         "Heizkreis 2 Vorlauftemperatur" @ "Heizung" [ readStart="1150", readValueType="int16", readTransform="JS(divide10.js)" ]
        Thing data HK2_Pumpe                     "Heizkreis 2 Pumpe"             @ "Heizung" [ readStart="1155", readValueType="uint16" ]
        Thing data HK2_Mischer                   "Heizkreis 2 Mischer"           @ "Heizung" [ readStart="1156", readValueType="uint16" ]
        Thing data HK2_Status                    "Heizkreis 2 Status"            @ "Heizung" [ readStart="1157", readValueType="uint16" ]
    }

Items:

// Heizkreis 1 - Fußbodenheizung
Number:Temperature    Solarfocus_HK1_Vorlauftemperatur         "Heizkreis 1 Vorlauftemperatur [%.1f °C]"    <temperature>    (gChart)    {channel="modbus:data:SolarFocusTCP:Heizkreis1:HK1_Vorlauftemperatur:number"}
Number                Solarfocus_HK1_Pumpe                     "Heizkreis 1 Pumpe"                          <pump>                       {channel="modbus:data:SolarFocusTCP:Heizkreis1:HK1_Pumpe:number"}
String                Solarfocus_HK1_Status                    "Heizkreis 1 Status"                                                      {channel="modbus:data:SolarFocusTCP:Heizkreis1:HK1_Status:string"}	// [profile="transform:MAP", function="solarfocus_heizkreisstatus.map"]}
Number                Solarfocus_HK1_Mischer                   "Heizkreis 1 Mischer [%.0f %%]"                                           {channel="modbus:data:SolarFocusTCP:Heizkreis1:HK1_Mischer:number"}

// Heizkreis 2 - Heizkörper
Number:Temperature    Solarfocus_HK2_Vorlauftemperatur         "Heizkreis 2 Vorlauftemperatur [%.1f °C]"    <temperature>    (gChart)    {channel="modbus:data:SolarFocusTCP:Heizkreis2:HK2_Vorlauftemperatur:number"}
Switch                Solarfocus_HK2_Pumpe                     "Heizkreis 2 Pumpe"                          <pump>                       {channel="modbus:data:SolarFocusTCP:Heizkreis2:HK2_Pumpe:switch"}
String                Solarfocus_HK2_Status                    "Heizkreis 2 Status"                                                      {channel="modbus:data:SolarFocusTCP:Heizkreis2:HK2_Status:string" [profile="transform:MAP", function="solarfocus_heizkreisstatus.map"]}
Number                Solarfocus_HK2_Mischer                   "Heizkreis 2 Mischer [%.0f %%]"                                           {channel="modbus:data:SolarFocusTCP:Heizkreis2:HK2_Mischer:number"}

Both items always update together:

2021-05-04 17:55:08.295 [vent.ItemStateChangedEvent] - Solarfocus_HK1_Status changed from 264 to 275
2021-05-04 17:55:25.853 [vent.ItemStateChangedEvent] - Solarfocus_HK2_Vorlauftemperatur changed from 26.4 °C to 27.5 °C

I always thought that this may be a bug in the firmware, but today the system got the yearly maintenance and also the firmware updated. Do you have an idea what the error might be?

  1. The value in register 1105 and 1155 (Status Heizkreispumpe) doesn’t read 0/1 but a value between 0-100 (Percent?)

  2. I cannot get the holding registers to work to control the heating circuits or push the room temperatures. I also tried your example but got an error:

2021-05-04 17:45:51.753 [ERROR] [rt.modbus.internal.ModbusManagerImpl] - Last try 3 failed when executing request (ModbusPollerThingHandler.ModbusPollerReadRequest@1e5ccbe[slaveId=1,functionCode=READ_MULTIPLE_REGISTERS,start=32600,length=8,maxTries=3]). Aborting. Error was: net.wimpi.modbus.ModbusSlaveException Error Code = 2 [operation ID ee123a91-f586-4383-a1f6-8526ab4e11ae]
2021-05-04 17:45:51.754 [ERROR] [ernal.handler.ModbusDataThingHandler] - Thing modbus:data:SolarFocusTCP:sf_holdingreg1:SF32600 'Modbus Data' had ModbusSlaveErrorResponseExceptionImpl error on read: ModbusSlaveErrorResponseException(error=2)
2021-05-04 17:45:51.755 [ERROR] [ernal.handler.ModbusDataThingHandler] - Thing modbus:data:SolarFocusTCP:sf_holdingreg1:SF32603 'Modbus Data' had ModbusSlaveErrorResponseExceptionImpl error on read: ModbusSlaveErrorResponseException(error=2)
2021-05-04 17:45:51.756 [ERROR] [ernal.handler.ModbusDataThingHandler] - Thing modbus:data:SolarFocusTCP:sf_holdingreg1:SF32605 'Modbus Data' had ModbusSlaveErrorResponseExceptionImpl error on read: ModbusSlaveErrorResponseException(error=2)
2021-05-04 17:45:51.757 [ERROR] [ernal.handler.ModbusDataThingHandler] - Thing modbus:data:SolarFocusTCP:sf_holdingreg1:SF32606 'Modbus Data' had ModbusSlaveErrorResponseExceptionImpl error on read: ModbusSlaveErrorResponseException(error=2)
2021-05-04 17:45:51.758 [ERROR] [ernal.handler.ModbusDataThingHandler] - Thing modbus:data:SolarFocusTCP:sf_holdingreg1:SF32607 'Modbus Data' had ModbusSlaveErrorResponseExceptionImpl error on read: ModbusSlaveErrorResponseException(error=2)

Any suggestions?

Many thanks,
Roland

Hello Roland,

Unfortunately I’m not an expert for Modbus.

Question 1, 2
Currently I don’t have the solution on hand. I think it makes sense to check the Modbus raw data with another application like “CAS Modbus Viewer” for Windows or “Modbus Viewer” for Android. If all register data are as expected, then the next step would be to further investigate on your openhab configuration step by step.

Question 3
Is the error when writing or even when reading the register data? Have you tried a pure read task? E.g.

Thing data SF32603B [ readStart="32603", readValueType="int16" ]   // Heizkreisbetriebsart; nur lesend

I hope this will provide some starting points.

Alfred

2 Likes

Hi Alfred,

many thanks for your reply. In the meantime I was able to solve the first two issues myself by poking around with the registers. It seems that the PellelElegance uses different registers for some of the values. I successfully read both heating circuits with this configuration:

    Bridge poller Heizkreis1 [ start=1100, length=8 , refresh=60000, type="input" ] {
        Thing data HK1_Vorlauftemperatur         "Heizkreis 1 Vorlauftemperatur" @ "Heizung" [ readStart="1100", readValueType="int16", readTransform="JS(divide10.js)" ]
        Thing data HK1_Pumpe                     "Heizkreis 1 Pumpe"             @ "Heizung" [ readStart="1104", readValueType="uint16", readTransform="JS(binary_switch.js)" ]
        Thing data HK1_Mischer                   "Heizkreis 1 Mischer"           @ "Heizung" [ readStart="1105", readValueType="uint16" ]
        Thing data HK1_Status                    "Heizkreis 1 Status"            @ "Heizung" [ readStart="1106", readValueType="uint16" ]
    }

    // Heizkreis 2 - Heizkörper
    Bridge poller Heizkreis2 [ start=1150, length=8, refresh=60000, type="input" ] {
        Thing data HK2_Vorlauftemperatur         "Heizkreis 2 Vorlauftemperatur" @ "Heizung" [ readStart="1150", readValueType="int16", readTransform="JS(divide10.js)" ]
        Thing data HK2_Pumpe                     "Heizkreis 2 Pumpe"             @ "Heizung" [ readStart="1154", readValueType="uint16", readTransform="JS(binary_switch.js)" ]
        Thing data HK2_Mischer                   "Heizkreis 2 Mischer"           @ "Heizung" [ readStart="1155", readValueType="uint16" ]
        Thing data HK2_Status                    "Heizkreis 2 Status"            @ "Heizung" [ readStart="1156", readValueType="uint16" ]
    }

As you can see pump, mixer and status have an offset of -1 against the register addresses given in the documentation. But I verified the values I read against the values displayed on the heatings touchpanel and they match. I really wonder why Solarfocus should be implementing different registers for different models rather than using the same control device with the same software for all their heating devices … but who knows …

Unfortunately I was not successful with the holding registers. When I try the “read only” configuration you suggested, I get this:

2021-05-13 10:56:29.979 [WARN ] [rt.modbus.internal.ModbusManagerImpl] - Try 1 out of 3 failed when executing request (ModbusPollerThingHandler.ModbusPollerReadRequest@108d329[slaveId=1,functionCode=READ_MULTIPLE_REGISTERS,start=32600,length=8,maxTries=3]). Will try again soon. Error was: net.wimpi.modbus.ModbusSlaveException Error Code = 2 [operation ID 43577607-065c-4540-9769-effcf3a042cc]
2021-05-13 10:56:30.089 [WARN ] [rt.modbus.internal.ModbusManagerImpl] - Try 2 out of 3 failed when executing request (ModbusPollerThingHandler.ModbusPollerReadRequest@108d329[slaveId=1,functionCode=READ_MULTIPLE_REGISTERS,start=32600,length=8,maxTries=3]). Will try again soon. Error was: net.wimpi.modbus.ModbusSlaveException Error Code = 2 [operation ID 43577607-065c-4540-9769-effcf3a042cc]
2021-05-13 10:56:30.199 [ERROR] [rt.modbus.internal.ModbusManagerImpl] - Last try 3 failed when executing request (ModbusPollerThingHandler.ModbusPollerReadRequest@108d329[slaveId=1,functionCode=READ_MULTIPLE_REGISTERS,start=32600,length=8,maxTries=3]). Aborting. Error was: net.wimpi.modbus.ModbusSlaveException Error Code = 2 [operation ID 43577607-065c-4540-9769-effcf3a042cc]
2021-05-13 10:56:30.200 [ERROR] [ernal.handler.ModbusDataThingHandler] - Thing modbus:data:SolarFocusTCP:sf_holdingreg1:SF32603B 'Modbus Data' had ModbusSlaveErrorResponseExceptionImpl error on read: ModbusSlaveErrorResponseException(error=2)

==> /var/log/openhab2/events.log <==

2021-05-13 10:56:30.202 [hingStatusInfoChangedEvent] - 'modbus:data:SolarFocusTCP:sf_holdingreg1:SF32603B' changed from ONLINE to OFFLINE (COMMUNICATION_ERROR): Error (ModbusSlaveErrorResponseExceptionImpl) with read. Request: ModbusPollerThingHandler.ModbusPollerReadRequest@108d329[slaveId=1,functionCode=READ_MULTIPLE_REGISTERS,start=32600,length=8,maxTries=3]. Description: ModbusSlaveErrorResponseException(error=2). Message: Slave responsed with error=2
2021-05-13 10:56:30.203 [hingStatusInfoChangedEvent] - 'modbus:poller:SolarFocusTCP:sf_holdingreg1' changed from ONLINE to OFFLINE (COMMUNICATION_ERROR): Error with read: org.openhab.io.transport.modbus.internal.ModbusSlaveErrorResponseExceptionImpl: Slave responsed with error=2
2021-05-13 10:56:30.204 [hingStatusInfoChangedEvent] - 'modbus:data:SolarFocusTCP:sf_holdingreg1:SF32603B' changed from OFFLINE (COMMUNICATION_ERROR): Error (ModbusSlaveErrorResponseExceptionImpl) with read. Request: ModbusPollerThingHandler.ModbusPollerReadRequest@108d329[slaveId=1,functionCode=READ_MULTIPLE_REGISTERS,start=32600,length=8,maxTries=3]. Description: ModbusSlaveErrorResponseException(error=2). Message: Slave responsed with error=2 to ONLINE

I was trying around with the CAS Modbus Scanner (many thanks for the hint!) but wasn’t able to get this running either. When trying to read registers that even work in OpenHAB, I always get errors: “0x01- ILLEGAL FUNCTION”:

Maybe I misconfigured it. Do you have any suggestions?

What’s you firmware version on your Ecomanager-Touch? Mine has V19.120 (was updated last week during the yearly maintenance).

Many thanks,
Roland

It’s a perennial problem for Modbus users. Some manufacturers talk about register numbers, which start at 1. Some about register addresses, which start at 0. Register number 1 = address 0.

The binding works in 0-based addresses.

That was my first thought, but it’s not consistent across all registers. In the documentation there are some registers marked as beeing unused or available only on certain models. Until these registers the numbers match but the subsequent registers have an offset on my device.

Example:
Heating Circuits:

Function            |  Documented Register    | Actual Register
Feeding Temperature |  1100                   | 1100
Room Temperature    |  1101                   | 1101
Room Humidity       |  1102                   | 1102
Some valve          |  1103                   | 1103
Unused              |  1104                   |
Circuit Pump        |  1105                   | 1104
Mixer               |  1106                   | 1105
Circuit Status      |  1107                   | 1106

So either the documentation is wrong (at least for some of their devices), or my firmware is buggy.

Another way you can observe an apparent +/-1 shift is involving 32-bit values.
Let’s say you have a 32-bit integer; but Modbus only deals in 16-bit registers.
That’s okay, we break our value into halves A and B and put into two registers N and N+1.
There are no rules about whether A goes into N or N+1

An unsuspecting user can come along and read N+1 as a 16-bit value, and even see a value that agrees with the 32-bit version (supposedly at N) if it is less than 65,000

All that does not seem likely here, though. 32-bit use is common enough, but the giveaway is you only see every other address in the maker’s tables.

How you can you tell if 32-bit values are involved? You can’t, except by trial and error, or relying on manufacturers docs.

Hi Roland,

The Firmware version of my Ecomanager-Touch is V 20.100. The last update on my therminator II biomass boiler was done in March this year.

I double-checked all used registers and can proof for my environment (therminator II) that the registers in openHAB fully match with the documentation from solarfocus. It’s strange that your environment is dealing with a different logic.

Let’s do another try with the holding registers and the Scanner. With this configuration I’m able to successfully read the registers 32600 to 32607:

The Result (you can use “Show Filters” and “Change View” to show the offset values):

Alfred

Hi Alfred,

I poked around a lot in the past few weeks, but wasn’t able to access the Holding Registers. No matter from which device or ModBus-Tester I try, I always get a “ILLEGAL DATA ADDRESS”.

These are my settings in CAS Modbus Scanner:

And this is the result:

I get the impression that these registers are not available in my firmware. I will reach out to Solarfocus to find out if it’s a bug in my particular firmware version. The firmware should have been updated with the most recent version during the last service in April. The plumber had an USB stick with him and applied an update to the EcoManager touch, but maybe it wasn’t the current version. He doesn’t seem to be very familiar with this “computer stuff” anyway.

Many thanks for your help anyway. I will update this thread if I find out anything.

Thanks,
Roland