Global Ints & text_sensors - ChatGPT & I need human hep!

Even with the help of AI, I am going around in circles with this!

Can you YAML/C++ experts have a look at this code and hopefully correct or suggest an alternative approach?
I am reluctant to go down the Home Assistant API sensor route if it can be avoided. I am hoping to do as much as I can in the ESP32.

This is the “active ingredients” of my yaml (full disclosure available on request :grinning:):

<<: !include tinypico_mundane_base_code.yaml

globals:
  - id: my_ts
    type: int
    restore_value: true
    initial_value: '0'

text_sensor:
# See: https://esphome.io/cookbook/lambda_magic.html
  - platform: custom
    lambda: |-
      auto my_custom_sensor = new UartReadLineSensor(id(myUART23));
      App.register_component(my_custom_sensor);
      return {my_custom_sensor};
    text_sensors:    
      - id: "odb_tx"
        name: "OBD TX"
        #This works. But I want an incrementing conter, not static text.
        filters:
          prepend: "A psudo timestamp "
        #filters:
        #  - lambda: |-
        #      *globals.my_ts++; // Increment the global counter
        #      return std::to_string(*globals.my_ts) + " " + x;

  - platform: custom
    lambda: |-
      auto my_custom_sensor = new UartReadLineSensor(id(myUART18));
      App.register_component(my_custom_sensor);
      return {my_custom_sensor};
    text_sensors:  
      -  id: "obd_rx"
         name: "OBD RX"
         #filters:
         # - lambda: |-
         #     *globals.my_ts++; // Increment the global counter
         #     return std::to_string(*globals.my_ts) + " " + x;

What I hope you can see, is that I am trying to create a global counter that is read/writable by both text_sensors and than can be pre/append to the returned value from my_custom_sensor().
The rem’d out filter section is where I would like some help. I get compilation errors on both the increment my_ts and the return lines:

Compiling .pioenvs/tinypico/src/main.cpp.o
<unicode string>: In lambda function:
<unicode string>:40:15: error: expected primary-expression before '.' token
<unicode string>:41:37: error: expected primary-expression before '.' token
*** [.pioenvs/tinypico/src/main.cpp.o] Error 1

Regards, Martin
PS - Be as rude as you like about having to resort to ChatGPT’s help :blush:

Please take your ChatGPT code and stick it where it hurts.

Please be constructive. Code from anywhere will probable have flaws.
I am not the brightest bunny around here. I’ll not learn without your help.
And discobot told that you were a friendly bunch.

Best regards, M.

You basically have your answer as-is in the documentation:

What Chris said… Globals are simply accessed by referencing id(global_name), and indeed the linked doc has an example of incrementing one.

This has got some examples of converting ints to strings and concatenation. Possibly it’s not optimal but it works.

Just make sure your are referencing the globals with the right syntax (per above)

Hi all,
Thanks for all your help but I’m not making any progress. Can I clarify what I think you are telling me?
Is the linked doc, this one? Automations and Templates — ESPHome
And the example of incrementing a global variable, is in the Automation section:

If this is correct, how can I incorporate an automation within my text_sensors?
For completeness, this is my work in progress:

<<: !include tinypico_mundane_base_code.yaml

globals:
  - id: my_ts
    type: int
    restore_value: true
    initial_value: '0'

text_sensor:
  # An Automation here? to increment the global var
  # So that it can be passed to the UartReadLine()
  # in the following two text_sensors

  - platform: custom
    # See: https://esphome.io/cookbook/lambda_magic.html
    # File is: /homeassistant/esphome/uart_read_line_sensor.h
    lambda: |-
      auto my_custom_sensor = new UartReadLineSensor(id(myUART23), id(my_ts));
      App.register_component(my_custom_sensor);
      return {my_custom_sensor};
    text_sensors:    
      - id: odb_tx
        name: "OBD TX"

  - platform: custom
  #  #/homeassistant/esphome/uart_read_line_sensor.h
    lambda: |-
      auto my_custom_sensor = new UartReadLineSensor(id(myUART18), id(my_ts) );
      App.register_component(my_custom_sensor);
      return {my_custom_sensor};
    text_sensors:  
      - id: obd_rx
        name: "OBD RX"

Regards, M.

Lambdas are the same wherever they are.
Your chatgpt code is likely completely bogus, but that’s another story…

2 Likes
id(my_global_int) += 1;

^^^That is how you increment a global in a lamda (from the example).

I use ChatGPT quite a lot to help me write lambdas, and I find it very useful. But it is rubbish at writing a whole app. You have to ask it to work on very specific bits of logic, and keep adding to working code, and then often you still need to iterate a fair bit to get something small working.

Sometimes I’ll then ask people who actually know how to code to look over my working solution.

Often you’re better off starting with forum searches and then trying AI if you’re not getting good hits.

Thank you for repeating the increment code. However, I think that there must be additional subtleties in play for me?

I have changed my yaml so that it passes the value of my_ts+=1; & my_ts+=2; in the calls to UartReadLineSensor():

<<: !include tinypico_mundane_base_code.yaml

globals:
  - id: my_ts
    type: int
    restore_value: true
    initial_value: '0'

text_sensor:

  - platform: custom
    # See: https://esphome.io/cookbook/lambda_magic.html
    # File is: /homeassistant/esphome/uart_read_line_sensor.h
    lambda: |-
      auto my_custom_sensor = new UartReadLineSensor(id(myUART23), id(my_ts) += 1);
      App.register_component(my_custom_sensor);
      return {my_custom_sensor};
    text_sensors:    
      - id: odb_tx
        name: "OBD TX"

  - platform: custom
  #  #/homeassistant/esphome/uart_read_line_sensor.h
    lambda: |-
      auto my_custom_sensor = new UartReadLineSensor(id(myUART18), id(my_ts) += 2 );
      App.register_component(my_custom_sensor);
      return {my_custom_sensor};
    text_sensors:  
      - id: obd_rx
        name: "OBD RX"

And this is the mod’ed C++ function (from: Lambda Magic — ESPHome) with an ESP_LOGD() to print the local copy of ‘my_ts’.

#include "esphome.h"

class UartReadLineSensor : public Component, public UARTDevice, public TextSensor {
 private:
   int my_ts_local;
 public:
  UartReadLineSensor(UARTComponent *parent, int my_ts) : UARTDevice(parent), my_ts_local(my_ts) { }

  void setup() override {
    // nothing to do here
  }

  int readline(int readch, char *buffer, int len)
  {
    static int pos = 0;
    int rpos;
    ////////////////////////////////////////////////
    ESP_LOGD("UartReadLine", "In setup, my_ts_local = %u", my_ts_local); 
    ///////////////////////////////////////////////

    if (readch > 0) {
      switch (readch) {
        case '\n': // Ignore new-lines
          break;
        case '\r': // Return on CR
          rpos = pos;
          pos = 0;  // Reset position index ready for next time
          return rpos;
        default:
          if (pos < len-1) {
            buffer[pos++] = readch;
            buffer[pos] = 0;
          }
      }
    }
    //ESP_LOGD("my custom component", "readch = %u", readch);      
    // No end of line has been found, so return -1.
    return -1;
  }

  void loop() override {
    const int max_line_length = 80;
    static char buffer[max_line_length];
    while (available()) {
      if(readline(read(), buffer, max_line_length) > 0) {
        publish_state(buffer);
      }
    }
  }
};

And these are the debug messages:

[12:06:23][D][Reporting from readline():017]: my_ts_local = 1
[12:06:23][D][Reporting from readline():017]: my_ts_local = 3
[12:06:25][D][text_sensor:064]: 'OBD TX': Sending state '>.'

From this evidence, I conclude (probably incorrectly!) that when I send the first character (a ‘full stop’) down the test circuit, the first debug line reports my_ts_local is 1. This is what I would expect as the parent lambda executed id(my_ts) +=1;
Again my understanding is that this compound postfix assignment should read the value at the address of my_ts, add 1, and write back to the address my_ts?
Which it seems to have done.
The next debug line also seems to be as expected. The lambda in the second text_sensor, incremented my_ts by 2 and passed it to the readline() function, so that debug line reports 3. Lastly, for the obd_tx text_sensors: (‘tx’ as viewed from the odb tester), it reports sending the text “>.” to HA. However, repeat the exercise for a second command, and my_ts must be back at zero again, because the debug lines report 1 & 3 again?

Conclusion: Although the local copy of my_ts within the text_sensor: has retained its value between text_sensors:, it is not writing back to the globals:? So that the next time the text_sensor code is called, it pulls the unaltered value (zero) from the globals:.

As anyone who has taken the time to read down this far, will testify, I am probably suffering the classic confusion of an event-driven language. To step around this confusion, added to by the multi-layered python/yaml/C++ environment, is it possible to simplify my task?

Why does the documentation seem to suggest that I have to have two or more text_sensors: as children to a single text_sensor:? Can I not just have two independent text_sensor: (I’ve tried - believe me! With no sucess)

Or can anyone supply a sample proof that writing to a globals: from within a text_sensor, actually works.

I really am appreciative of all the help you folk are offering.
Regards, M.

Regards, M.