How do I write a "Switch-Case" construct?

What you are attempting to do here is a good approach.
A couple things:

As I looked at the various modbus components in the ESPHome docs, I believe each of its ‘functions’ is determined by which address your component uses. I presume (haven’t confirmed by looking it up) that address 0x3201 returns the charge state you’d like to display in text form. Other modbus components will use different addresses and thus will be working with different data from the modbus.

That remains the core objective of this thread. A typical programmer’s approach is to first try what “ought to work” and when it fails, start logging variables to be sure they contain what you expect them to. That’s the idea behind your logging statement there.
Returning x at the end is just basically having the lambda do nothing (since the component calling the lambda already had x, which it passes to the lambda. So, ignore x, and ignore what the component displays for now, and focus on what values are turning up in the log because of line 77.

Tell me the error the compiler is issuing about line 77 and we can fix it so you can get log output that may be helpful in the next step, which is to finally get that switch statement working.

On second glance, I think what might be wrong with the ESP_LOGD line is that you used ‘%f’ to represent the uint_16 value you’re passing it. That usually won’t work. Try ‘%x’ instead of ‘%f’.
(’%f’ is for floating point values. ‘%x’ will print human-readable hex digits from the integer value you’re handing it).

Ah Sorry, in your HA configuration. Just return the number to HA (the num sensor) and the translation is done in HA.

Sorry, I haven’t made myself clear. I know the Modbus Regiser Map and the address (0x3201) that I am looking at is defined here: LS1024b Modbus Controller. What I am searching for is a list/understanding of the modbus_controller class. For if I understand correctly, this:

 = modbus_controller::word_from_hex_str(x, 0)

… means that “something” (that something will not become clear until later…) is returned from the function word_from_hex_str() which is defined in the class modbus_controller. The double colons ( :: ) are the scope resolution operator, telling me that word_from_hex_str() is not a local (to self) function. Thus,
There must be a modbus_controller.cpp and modbus_controller.h somewhere? Do you know where? Is it something to do with PlatformIO Libraries?

Regards, M.

I don’t know where this function is defined, but from its name and the returned value type, I can tell that it expects a string as input and returns a binary ‘word’ (e.g. an int) that you can do math on.
One does not normally do math on strings (arrays of printable characters), so this converts the module-supplied string ‘x’ into an actual binary value that we’ll use in the switch statement and selectors.
Perhaps someone more familiar with modbus components is more familiar with this function.

For this case, all we need to know about it is that it gives us the value needed to make decisions about when selecting a string to return as the displayed value of the text_sensor.

I found it defined here, and it’s pretty clearly just turning strings (e.g. “4F”) into the binary value of 0x4F stored in an integer word, starting with the 0th character of the string.

So…
Did you get the log to produce something useful?

Yep, you were right. I’d got the string format wrong! Should have been using %d.

sensor:
  - platform: modbus_controller
    modbus_controller_id: ls1024b
    id: charging_mode
    name: "Charging Mode"
    address: 0x3201
    unit_of_measurement: ""
    register_type: read
    value_type: U_WORD
    accuracy_decimals: 0

  #   # D3D2 = 0x00 = "No Charge"
  #   # D3D2 = 0x01 = "Float"
  #   # D3D2 = 0x02 = "Boost"
  #   # D3D2 = 0x03 = "Equalizing"
   
    lambda: |-
      //uint16_t local_var = modbus_controller::word_from_hex_str(x, 0);
      uint16_t local_var = x;
      ESP_LOGD("custom", "local_var is: %d", local_var);
      local_var = local_var >> 2;
      ESP_LOGW("custom", "local_var right shift 2 is: %d", local_var);
      local_var = local_var & 0x03;
      ESP_LOGI("custom", "local_var & 0x03 is: %d", local_var);
      return local_var;

Gives me this in the log…

[11:22:09][V][modbus_controller:159]: Updating modbus component
[11:22:09][V][modbus_controller:125]: Range : 3201 Size: 1 (4) skip: 0
[11:22:09][V][modbus_controller:035]: Sending next modbus command to device 1 register 0x3201 count 1
[11:22:09][V][modbus:205]: Modbus write: 01.04.32.01.00.01.6E.B2 (8)
[11:22:09][V][modbus_controller:487]: Command sent 4 0x3201 1
[11:22:09][V][modbus:058]: Modbus received Byte  1 (0X1)
[11:22:09][V][modbus:058]: Modbus received Byte  4 (0X4)
[11:22:09][V][modbus:058]: Modbus received Byte  2 (0X2)
[11:22:09][V][modbus:058]: Modbus received Byte  0 (0X0)
[11:22:09][V][modbus:058]: Modbus received Byte  11 (0Xb)
[11:22:09][V][modbus:058]: Modbus received Byte  248 (0Xf8)
[11:22:09][V][modbus:058]: Modbus received Byte  247 (0Xf7)
[11:22:09][V][modbus_controller:055]: Modbus response queued
[11:22:09][V][modbus_controller:062]: Process modbus response for address 0x3201 size: 2
[11:22:09][V][modbus_controller:098]: data for register address : 0x3201 : 
[11:22:09][D][custom:077]: local_var is: 11
[11:22:09][W][custom:079]: local_var right shift 2 is: 2
[11:22:09][I][custom:081]: local_var & 0x03 is: 2
[11:22:09][V][modbus_controller.sensor:021]: Value overwritten by lambda
[11:22:09][D][modbus_controller.sensor:025]: Sensor new state: 2.00
[11:22:09][V][sensor:074]: 'Charging Mode': Received new state 2.000000
[11:22:09][D][sensor:124]: 'Charging Mode': Sending state 2.00000  with 0 decimals of accuracy

… moving on to do similar to text_sensor:

Regards, M.

1 Like

The lambda and all that we’ve been discussing is supposed to be used inside a text_sensor, not a regular sensor component. I’m not clear why you’d want to be using a lambda to modify the returned value of a regular sensor at all, given what your original question said you were seeking to do.

I look forward to hearing how it works out once you try it in a text_sensor component.

Because, I’m reasonably confident that my sensor: yaml is getting the correct data (0x01,0x04,0x02,0x00,0x01,0x78,0xf0). So by sticking with sensor: I could practice my lambda code in the knowledge that if it didn’t work, it was my lambda coding and not the sensor yaml.
Now, I’m moving on to a text_sensor.
Look out for more question… :wink:

Done…
Thank you all for your help. I’m a happy camper now :sweat_smile:

text_sensor:
  - platform: modbus_controller
    modbus_controller_id: ls1024b
    name: "Charging Mode"
    id: chg_mode
    address: 0x3201
    register_type: read
    raw_encode: HEXBYTES
    
    lambda: |-
      uint16_t local_var = modbus_controller::word_from_hex_str(x, 0);
      //ESP_LOGI("mprowe", "The value of local_var is: %d", local_var);
      local_var = local_var >> 2;
      //ESP_LOGI("mprowe", "local_var right shift 2 is: %d", local_var);
      local_var = local_var & 0x03;
      //ESP_LOGI("mprowe", "local_var & 0x03 is: %d", local_var);
      switch (local_var) {
        case 0: return std::string("Not Charging");
        case 1: return std::string("Float Charging");
        case 2: return std::string("Boost Charging");
        case 3: return std::string("Equalizing");
        default: return std::string("Unknown");
      }

Dashboard Card
image

Thanks once again, M.

2 Likes

There ya go. Congrats.
Yeah the reaso to use a ‘text_sensor’ is that you want to display a string, and that’s what ‘text_sensors’ do. It’s why they exist.
The sensors that display values, that’s what the ‘sensor’ does.