Modbus, sensor reading without lambda

I’m trying to read values from the device over modbus, with esphome.
From debug i have

[16:35:01][I][:057]: Lambda incoming value=10752.000000 - data array size is 2
[16:35:01][I][:058]: Sensor properties: adress = 0xB4C, offset = 0x0 value type=3
[16:35:01][I][:061]: data[0]=0x00 (42)
[16:35:01][I][:061]: data[0]=0x3FF00000 (0)
[16:35:01][I][:065]: Sensor value: = 42

How to get data from sensor?

sensor:
  - platform: modbus_controller
    modbus_controller_id: Heater
    name: "External temp"
    id: temperature_outside
    register_type: holding
    address: 0x0b4c
    device_class: temperature
    value_type: S_WORD
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    filters:
      - multiply: 0.1

result = 1075,2

is it possible to get this data without lambda?

Byte swap

10752 = 2A00

002A = 42

You haven’t posted any lambda. Post your complete yaml for best support…

now i’m using lambda like that

   lambda: |-
        int value = data[1] << 8 |  data[0];
        return value ;

I’m just wounering if it’s possible to do it without lambda

try U_WORD

I don’t believe so, why anyway?

ok go it, last thing.
I’m getting value of the sensor

  - platform: modbus_controller
    modbus_controller_id: mbd
    name: "temp comfort boiler"
    id: temperature_boil_komfort
    register_type: holding
    address: 0x0b67
    value_type: U_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    accuracy_decimals: 1
    filters:
      - multiply: 0.1
    lambda: |-
        int value = data[3] << 8 |  data[2];
        return value ; 

I’m trying to change temp with command

select:
  - platform: modbus_controller
    id: onoffsel1
    name: "Temp comfort boiler set"
    address: 0x0b67
    value_type: U_WORD
    entity_category: config
    icon: "mdi:toggle-switch"
    optionsmap:
      "45": 45
      "46": 46

But when i set value
46 - temp is set 1177,6
45 - temp is set 1152

Any idea?

It would be more productive if you post a link to your devices modbus protocol.

unfortunatley, vendor don’t wan’t to share that information, so everything is reverse engineering

So what are you getting out from modbus sensor with U-WORD without that lambda?
Maybe it’s FP32 from two registers? Or something else…
What you know about those registers after reverse engineering?

Edit:
46 is 0x002E in hex
0x2E00 is 11776 in decimal
So you have swapped hi and low byte.

what i got so far

esphome:
  name: ${device}
  platform: ESP8266
  board: d1_mini

logger:
  baud_rate: 0

status_led:
  pin: GPIO2
uart:
  id: mod_bus
  tx_pin: TX
  rx_pin: RX
  baud_rate: 9600
  stop_bits: 1
  parity: NONE

modbus:
  id: modbus1

modbus_controller:
  - id: kospel
    address: 101
    modbus_id: modbus1
    setup_priority: -11
    update_interval: 20s
    command_throttle: 1s
    allow_duplicate_commands: true

number:
  - platform: modbus_controller
    id: config_temp_boil_komf
    name: "Ustaw temp komfortowa boilera"
    address: 0x0b67
    value_type: U_WORD
    entity_category: config
    icon: "mdi:toggle-switch"
    min_value: 35
    max_value: 50
    lambda: |-
      int value = data[3] << 8 |  data[2];
      value = value / 10;
      return value ; 
    write_lambda: |-
      int int_value = static_cast<int>(x * 10);
      return (int_value << 8) | (int_value >> 8); 
  
  - platform: modbus_controller
    id: config_temp_boil_eko
    name: "Ustaw temp ekonomiczna boilera"
    address: 0x0b66
    value_type: U_WORD
    entity_category: config
    icon: "mdi:toggle-switch"
    min_value: 30
    max_value: 45
    lambda: |-
      int value = data[1] << 8 |  data[0];
      value = value / 10;
      return value ; 
    write_lambda: |-
      int int_value = static_cast<int>(x * 10);
      return (int_value << 8) | (int_value >> 8);     

sensor:
  - platform: total_daily_energy
    name: "Kospel zużycie energii dzienne"
    id: kospel_daily_power_usage
    power_id: power
    unit_of_measurement: kWh
    icon: mdi:calendar-clock

  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "Temperatura w na zewnątrz"
    id: temperature_outside
    register_type: holding
    address: 0x0b4c
    device_class: temperature
    value_type: S_WORD
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    filters:
      - multiply: 0.1
    lambda: |-
        int16_t value = data[item->offset+1] << 8 |  data[item->offset];
        return value ;  
 
  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "Temperatura boiler"
    id: temperature_boil
    register_type: holding
    address: 0x0b4a
    device_class: temperature
    value_type: S_WORD
    unit_of_measurement: "°C"
    accuracy_decimals: 1
    filters:
      - multiply: 0.1
    lambda: |-
        int value = data[item->offset+1] << 8 |  data[item->offset];
        return value ; 

  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "Temperatura Wejścia"
    id: temperature_in
    register_type: holding
    address: 0x0b48
    value_type: S_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    accuracy_decimals: 1
    filters:
      - multiply: 0.1
    lambda: |-
        int value = data[1] << 8 |  data[0];
        return value ;

  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "Temperatura Wyjścia"
    id: temperature_out
    register_type: holding
    address: 0x0b49
    value_type: S_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    accuracy_decimals: 1
    filters:
      - multiply: 0.1
    lambda: |-
        int value = data[3] << 8 |  data[2];
        return value ; 

  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "Temperatura zadana układu"
    id: temperature_factor
    register_type: holding
    address: 0x0b44
    value_type: U_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    accuracy_decimals: 1
    filters:
      - multiply: 0.1
    lambda: |-
        int value = data[1] << 8 |  data[0];
        return value ; 

  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "Temperatura boilera min"
    id: temperature_boil_min
    register_type: holding
    address: 0x0bbe
    value_type: U_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    accuracy_decimals: 1
    filters:
      - multiply: 0.1
    lambda: |-
        int value = data[2] << 8 |  data[1];
        return value ; 
  
  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "Temperatura boilera max"
    id: temperature_boil_max
    register_type: holding
    address: 0x0bbf
    value_type: U_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    accuracy_decimals: 1
    filters:
      - multiply: 0.1
    lambda: |-
        int value = data[2] << 8 |  data[1];
        return value ; 

  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "Temperatura boilera komfortowa"
    id: temperature_boil_komfort
    register_type: holding
    address: 0x0b67
    value_type: U_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    accuracy_decimals: 1
    filters:
      - multiply: 0.1
    lambda: |-
        int value = data[3] << 8 |  data[2];
        return value ; 

  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "Temperatura boilera ekonomiczna"
    id: temperature_boil_ekonomiczna
    register_type: holding
    address: 0x0b66
    value_type: U_WORD
    unit_of_measurement: "°C"
    device_class: temperature
    accuracy_decimals: 1
    filters:
      - multiply: 0.1
    lambda: |-
        int value = data[1] << 8 |  data[0];
        return value ; 

  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "Ciśnienie w układzie"
    id: presaure
    register_type: holding
    address: 0x0b4e
    value_type: S_WORD
    unit_of_measurement: "bar"
    accuracy_decimals: 1
    device_class: pressure
    filters:
      - multiply: 0.01
    lambda: |-
        int value = data[1] << 8 |  data[0];
        return value ;
 
  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "Przepływ"
    id: flow
    register_type: holding
    address: 0x0b4f
    value_type: U_WORD
    device_class: volume_flow_rate 
    unit_of_measurement: "l/min"
    accuracy_decimals: 1
    filters:
      - multiply: 0.1
    lambda: |-
        int value = data[item->offset+1] << 8 |  data[item->offset];
        return value ;        

  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "Moc"
    id: power
    register_type: holding
    address: 0x0b46
    value_type: S_WORD
    unit_of_measurement: "kW"
    device_class: power
    accuracy_decimals: 1
    filters:
      - multiply: 0.1
    lambda: |-
        int value = data[1] << 8 |  data[0];
        return value ;
    
    
  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "[WB] Wejścia binarne"
    internal: true
    id: wb
    register_type: holding
    address: 0x0b51
  
  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "[WB] Wejścia binarne rw_flagi_1"
    internal: true
    id: wbrwf1
    register_type: holding
    address: 0x0b55  
  

binary_sensor:
  - platform: template
    name: "[WB] CO_YES_NO"
    lambda: |-
        uint16_t value = (int(id(wbrwf1).state) & 0xFF00) >> 8 | (int(id(wbrwf1).state) & 0x00FF);
        return ((value >> 4) & 1);  


  - platform: template
    name: "[WB] Pompa"
    lambda: |-
        uint16_t value = (int(id(wb).state) & 0xFF00) >> 8 | (int(id(wb).state) & 0x00FF);
        return (value & 1); 

  - platform: template
    name: "[WB] Pompa cyrkulacyjna"
    lambda: |-
            uint16_t value = (int(id(wb).state) & 0xFF00) >> 8 | (int(id(wb).state) & 0x00FF);
            return ((value >> 1) & 1); 


  - platform: template
    name: "[WB] Wejscie sygnału nadrzędnego"
    lambda: |-
      uint16_t value = (int(id(wb).state) & 0xFF00) >> 8 | (int(id(wb).state) & 0x00FF);
      return ((value >> 3) & 1);  

  - platform: template
    name: "[WB] Wejście regulatora pokojowego"
    lambda: |-
      uint16_t value = (int(id(wb).state) & 0xFF00) >> 8 | (int(id(wb).state) & 0x00FF);
      return ((value >> 4) & 1);

  - platform: template
    name: "[WB] Fun"
    lambda: |-
      uint16_t value = (int(id(wb).state) & 0xFF00) >> 8 | (int(id(wb).state) & 0x00FF);
      return ((value >> 6) & 1);
     
  - platform: template
    name: "[WB] CO"
    lambda: |-
      uint16_t value = (int(id(wb).state) & 0xFF00) >> 8 | (int(id(wb).state) & 0x00FF);
      return ((value >> 7) & 1);
  
  - platform: template
    name: "[WB] CWU"
    lambda: |-
      uint16_t value = (int(id(wb).state) & 0xFF00) >> 8 | (int(id(wb).state) & 0x00FF);
      return ((value >> 8) & 1);    
        
 #Konfiguracja
text_sensor:
  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "Tryb pracy boilera"
    id: tryb_pracy_boilera
    register_type: holding
    address: 0x0b30
    raw_encode: HEXBYTES
    lambda: |-
        uint16_t value = data[1] << 8 |  data[0];
        switch (value) {
        case 0: return std::string("Ekonomiczny");
        case 1: return std::string("Przeciw zamarzanie");
        case 2: return std::string("Komfortowy");
        default: return std::string("Unknown");
        }
        return x;      
  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "Tryb pracy kotła"
    id: tryb_pracy_kotla
    register_type: holding
    address: 0x0b32
    raw_encode: HEXBYTES
    lambda: |-
        uint16_t value = data[1] << 8 |  data[0];
        switch (value) {
        case 0: return std::string("Ekonomiczny");
        case 1: return std::string("Przeciw zamarzanie");
        case 2: return std::string("Komfortowy");
        case 3: return std::string("Komfortowy minus");
        case 4: return std::string("Komfortowy plus");
        case 64: return std::string("Grzanie pokoju");
        default: return std::string("Unknown");
        }
        return x;         
  
  - platform: modbus_controller
    modbus_controller_id: kospel
    name: "[WB] Zawór TDR"
    id: tdr
    register_type: holding
    address: 0x0b51
    raw_encode: HEXBYTES
    lambda: |-
        uint16_t value = (int(id(wb).state) & 0xFF00) >> 8 | (int(id(wb).state) & 0x00FF);
        value = ((value >> 2) & 1); 
        switch (value) {
        case 0: return std::string("co");
        case 1: return std::string("cwu");
        default: return std::string("Unknown");
        }
        return x;   

  - platform: template
    name: "[WB] Zima"
    lambda: |-
      uint16_t value = (int(id(wbrwf1).state) & 0xFF00) >> 8 | (int(id(wbrwf1).state) & 0x00FF);
      
      int zima = ((value >> 5) & 1);  
      int lato = ((value >> 3) & 1);
      
      if (zima == 1 && lato == 1) {
         return std::string("Tryb zimowy");  
      } else if (lato == 1 && zima == 0) {
         return std::string("Tryb letni"); 
      } else {
         return std::string("Kocioł wyłączony");  
      }
        
               

I’m struggling with command to set winter/summer/off

  • To set winter to on, bit 5 needs to be 1, bit 3 needs to be 1
  • To set summer mode bit 5 needs to be 0, bit 3 needs to be 1
  • To Off system, mode bit 5 needs to be 0, bit 3 needs to be 0

i wrote below select command, but for me mistery is how to modify two bits at once

select:
  - platform: modbus_controller
    id: onoffsel1
    name: "Boiler mode set"
    address: 0x0b55
    value_type: U_WORD
    entity_category: config
    icon: "mdi:toggle-switch"
    optionsmap:
      "Winter": 2
      "Summer": 1
      "Off": 0
    lambda: |-
      uint16_t value = (int(id(wbrwf1).state) & 0xFF00) >> 8 | (int(id(wbrwf1).state) & 0x00FF);
      
      int winter= ((value >> 5) & 1);  
      int summer = ((value >> 3) & 1);
      int tryb = 0;
      if (winter== 1 && summer== 1) {
         return std::string("Zima");
      } else if (summer== 1 && winter== 0) {
         return std::string("summer");
      } else {
         return std::string("off");
      }  
    write_lambda: |-
    
        
      if (value == 0) {
         
      } else if (value == 1) {
       
      } else if (value == 2) {
       
      }

      return reg;  // Return the modified register value

Thank you very much for support

Give a whole byte if other bits are not changing. What are the bits 0,1,2,4,6,7?

seems that you are asking for?
[20:29:44][I][:349]: Lambda incoming value=13209.000000 - data array size is 2
[20:29:44][I][:350]: Sensor properties: adress = 0xB55, offset = 0x0 value type=1
[20:29:44][I][:353]: data[0]=---- (0)
[20:29:44][I][:353]: data[0]=---- (1072693248)
[20:29:44][I][:357]: Sensor value: = 39219

Thank you !

I didn’t understand a bit…

ok any idea how to read whole address?

I don’t follow you anymore. Read or write? Which address?

Ok i have it:)
For Off is 0x0013
For summer mode 0x001B
For winter is 0x0033
Question, How to write it using select?
13 00010011
1b 00011011
33 00110011

I have never tried select, either just like that in hex or if it really wants int,
optionsmap:
“Winter”: 19
“Summer”: 27
“Off”: 51

Thank you for your support,
I’ve created repository on github for Kospel heater over modbus.

1 Like