Global Variable ignored in template lambda?

I have a magnometer that every time my ESP looses power it’s readings change slightly. It’s a minor annoyance to have to go in and find out it’s min/max readings so I can get it to accurately measure my gas usage.

So I’ve setup some code that on boot sets it to an “uncalibrated” state and waits until it measures enough of a change in the reading to set itself into a “calibrated” state.

I put a couple of debug statements and found that even though the calibrated variable is never set to true, in the template area it treats it as if it was true. Am I missing something in how I should set up my lambda in the platform: template area? Here is my code:

substitutions:
  name: "gas-meter"

esphome:
  name: "${name}"
  friendly_name: Gas Meter

  #on_boot:
  #- priority: 800.0
  #  then:
  #    - lambda: |-
  #        id(gas_max_mid) = (id(gas_max)-id(gas_min))*0.75+id(gas_min);
  #        id(gas_min_mid) = (id(gas_max)-id(gas_min))*0.25+id(gas_min);

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API
api:
  encryption:
    key: ""

ota:


wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: ""
    password: ""

captive_portal:

globals:
   - id: gas_counter_total
     type: long
     restore_value: no
     initial_value: '0'
   - id: gas_counter
     type: long
     restore_value: no
     initial_value: '0'
   - id: gas_high
     type: bool
     restore_value: no
     initial_value: 'false'
   - id: gas_max
     type: float
     restore_value: no
     initial_value: '-1000.0'
   - id: gas_min
     type: float
     restore_value: no
     initial_value: '1000.0'
   - id: gas_max_mid
     type: float
     restore_value: no
     initial_value: '-2000.0'
   - id: gas_min_mid
     type: float
     restore_value: no
     initial_value: '2000.0'
   - id: calibrated
     type: bool
     restore_value: no
     initial_value: 'false'



interval:
  - interval: 0.1s
    then:
      - lambda: |-
          # If the current reading is greater than the current max/min, set max to current reading and if device is "calibrated" update the max and min mid values
          if (id(gasx).state > id(gas_max)) {
            id(gas_max) = id(gasx).state;
            if (id(calibrated)) {
              id(gas_max_mid) = (id(gas_max)-id(gas_min))*0.75+id(gas_min);
              id(gas_min_mid) = (id(gas_max)-id(gas_min))*0.25+id(gas_min);
            }
          } else if (id(gasx).state < id(gas_min)) {
            id(gas_min) = id(gasx).state;
            if (id(calibrated)) {
              id(gas_max_mid) = (id(gas_max)-id(gas_min))*0.75+id(gas_min);
              id(gas_min_mid) = (id(gas_max)-id(gas_min))*0.25+id(gas_min);
            }
          }
          # If the absolute value of the max-min is greater than 10, but less than 1000 set calibrated to true and update min/max mid values
          if ((fabs(id(gas_max)-id(gas_min)) > 10) && (fabs(id(gas_max)-id(gas_min)) < 1000.0) && (!id(calibrated))) {
            ESP_LOGD("custom", "Just set to true");
            id(calibrated) = true;
            id(gas_max_mid) = (id(gas_max)-id(gas_min))*0.75+id(gas_min);
            id(gas_min_mid) = (id(gas_max)-id(gas_min))*0.25+id(gas_min); 
          }
          # If calibrated, evaluate current state to see if gas is being used
          if (id(calibrated)) {
            if (id(gasx).state > id(gas_max_mid) && !id(gas_high)) {
              id(gas_counter_total) += 1;
              id(gas_counter) += 1;	
              id(gas_high) = true;
            } else if (id(gasx).state < id(gas_min_mid) && id(gas_high)) {
              id(gas_high) = false;
            }   
          }

i2c:
  sda: GPIO21 
  scl: GPIO22
  #scan: true
  frequency: 10kHz


sensor:
  - platform: qmc5883l
    address: 0x0D
    field_strength_x:
      name: "Gas Meter Field Strength X"
      id: gasx
      internal: true
    field_strength_y:
      name: "Gas Meter Field Strength Y"
      id: gasy
      internal: true
    field_strength_z:
      name: "Gas Meter Field Strength Z"
      id: gasz
      internal: true
    heading:
      name: "Gas Meter Heading"
      internal: true
    range: 200uT
    oversampling: 512x
    update_interval: 0.1s

#8 counts per cubic foot, multiplied by 2 to get per minute based on update interval, only evaluate if device is "calibrated"
  - platform: template    
    name: "Gas Rate"
    lambda: |-
      if (id(calibrated)) {
        int temp = id(gas_counter);
        id(gas_counter) -= temp;
        float temp2 = temp;
        float temp3 = (temp2/8)*2; 
        return temp3;
      } else {
        return 0;
      }
    update_interval: 30s
    unit_of_measurement: ft³/min
    device_class: 'gas'

  - platform: template    
    name: "Gas Total"
    lambda: |-
      if (id(calibrated)) {
        float temp = id(gas_counter_total);
        return temp/8;
      } else {
        return 0;
      }
    update_interval: 1s
    unit_of_measurement: 'ft³'
    state_class: 'total_increasing'
    device_class: 'gas'

# Reset calibration state
button:
  - platform: template
    name: "${name} Calibrate"
    id: Calibrate
    icon: "mdi:chart-histogram"
    on_press:
      then:
        lambda: |-
          id(gas_max) = -1000.0;
          id(gas_min) = 1000.0;
          id(gas_max_mid) = 500.0;
          id(gas_min_mid) = -500.0;
          id(calibrated) = false;

Are you sure that your gasx sensor isn’t evaluating to something that makes the logic test that sets your global to true to pass?

Post the logs from startup, that may provide a clue.

It might be helping. I added in a some more debug lines. Most notably, in the interval area, I added a line telling me if it thought calibrated was true, and as I make a connection to it, it already thinks it’s calibrated…

Okay, I think I know the issue. I’m checking to see if my split is greater than 10, but less than 1000. My logic is that since I’ve set the initial min/max to 1000/-1000, a split of 2000, then it will first set it’s limits to within a few decimal places of each other with a new sensor reading

So it boots, gas_max is -1000 gas_min is 1000. The first read gasx is 14. This is greater than gas_max so now gas_max is 14 and gas_min is 1000.

Since I wrote my checks for gas_min and gas_max as an IF…ELSE statement gas min isn’t updated and it sees if I’m in my calibration range. The difference between gas_min (1000) and gas_max (14) is indeed greater than 10 and less than 1000. And it says I’m calibrated.

The next scan gasx is say 13.8, now my gas_min is set and my reporting is 100% wrong. I just need to clean up my logic. A simple fix would be to make the gas_min and gas_max two if statements instead of an if…else statement, then on boot, my min and max would set to the same number and be less than 10.

I’ll do some testing, I think I’m looking in the right direction now.

1 Like