Lambda in Automation weird value input from sensor

Hi All,

I’m trying to implement some logic in my sketch but can’t get it working. I try to use a CO2 value to calculate a new value and use this. But it won’t work as expected. The value is not making any sense (not does the output).

I tired on_value and on_raw_value, neither work. Looks like the input from the sensor is wrong in the wrong format ? Any and all idea’s are welcome!

Goal is the use the CO level (1193.75ppm for example) to base the led color (amount of RED) on. So 1000 ppm = 0.0 and 1500 ppm is 1.0

sensor:
  - platform: scd30
    i2c_id: bus_a
    address: 0x61
    co2:
      name: "scd30 CO2"
      id: scd30_co2
      accuracy_decimals: 1
      on_value:
        then:
        - if:
            condition:
              lambda: return (x < 1000);
            then:
              - light.turn_on:
                  id: color_leds_id
                  green: 1.0
                  red: 0.0
                  blue: 0.0
                  transition_length: 2s
                  brightness: 45%
        - if:
            condition:
              lambda: return (x > 1000 and x < 1500);
            then:
              - light.turn_on:
                  id: color_leds_id
                  #red: !lambda "return x/1500;"
                  #- lambda: return x * (9.0/5.0) + 32.0;
                  #red: !lambda "return (1.0 - ((x - 1) / 500));"
                  #red: !lambda "return x / 500.0 - 2.0;"
                  red: !lambda |-
                    static float red_intense = 0.0;
                    ESP_LOGD("main", "Red Number 1 is: %d", red_intense);
                    ESP_LOGD("main", "Input Number is: %d", x);
                    ESP_LOGD("main", "Input sensor is: %d", id(scd30_co2).state);
                    red_intense = (x / 500.0 - 2.0);
                    ESP_LOGD("main", "Red Number 2 is: %d", red_intense);
                    return red_intense;
                  green: 0.1
                  blue: 0.0
                  transition_length: 3s
                  brightness: 0.5
        - if:
            condition:
              lambda: return (x > 1500);
            then:
              - light.turn_on:
                  id: color_leds_id
                  green: 0.0
                  red: 1.0
                  blue: 0.0
                  transition_length: 4s
                  brightness: 55%
    temperature:
      name: "scd30 Temperature"
      id: scd30_temperature
      accuracy_decimals: 1
    humidity:
      name: "scd30 Humidity"
      id: scd30_humidity
      accuracy_decimals: 1
    temperature_offset: 6
    update_interval: 10s

Some logging output with debug:

[13:49:49][D][scd30:153]: Got CO2=1193.75ppm temperature=21.33°C humidity=45.24%
[13:49:49][D][sensor:092]: 'scd30 CO2': Sending state 1193.75183 ppm with 1 decimals of accuracy
[13:49:49][D][main:504]: Red Number 1 is: -2147483648
[13:49:49][D][main:505]: Input Number is: -536870912
[13:49:49][D][main:506]: Input sensor is: -536870912
[13:49:49][D][main:508]: Red Number 2 is: 536870912
[13:49:49][D][light:265]: 'LED Light' Setting:
[13:49:49][D][light:278]:   Brightness: 50%
[13:49:49][D][light:287]:   Red=100%, Green=26%, Blue=0%
[13:49:49][D][light:304]:   Transition Length: 3.0s
[13:49:49][D][sensor:092]: 'scd30 Temperature': Sending state 21.32587 °C with 1 decimals of accuracy
[13:49:49][D][sensor:092]: 'scd30 Humidity': Sending state 45.24489 % with 1 decimals of accuracy

Hi

About the logging.
I may be so that you are trying log the float values but you say they are integer numbers.
My guess there is a printf hiding in the under the ESP_LOGD(…

The “%d” signal that the thing after is an integer. Try to change it to “%f” instead.
Maybe your printout going make more sense.

/Mattias

1 Like

Hi Mattias!

Thanks you for responding, you’re absolutely right. Changing to %F fixes the values in the debug.

I went on testing and now have to following working. It might be able to make it more slick in code, but time is up for now.

sensor:
  - platform: scd30
    i2c_id: bus_a
    address: 0x61
    co2:
      name: "scd30 CO2"
      id: scd30_co2
      accuracy_decimals: 1
      on_value:
        then:
        - if:
            condition:
              and:
                - lambda: return x > 300.0;
                - lambda: return x < 1000.0;
            then:
              - light.control:
                  id: color_leds_id
                  green: 0.75
                  red: 0.0
                  blue: 0.0
                  transition_length: 2.0s
                  brightness: 70%
        - if:
            condition:
              and:
                - lambda: return x > 1000.0;
                - lambda: return x < 1500.0;
            then:
              - light.control:
                  id: color_leds_id
                  red: !lambda "return x / 500.0 - 2.0;"
                  green: !lambda "return (1 - ((x - 1000) / 500));"
                  blue: 0.0
                  transition_length: 2.0s
                  brightness: 70%
        - if:
            condition:
              and:
                - lambda: return x > 1500.0;
                - lambda: return x < 10000.0;
            then:
              - light.control:
                  id: color_leds_id
                  green: 0.0
                  red: 0.75
                  blue: 0.0
                  transition_length: 2.0s
                  brightness: 70%
    temperature:
      name: "scd30 Temperature"
      id: scd30_temperature
      accuracy_decimals: 1
    humidity:
      name: "scd30 Humidity"
      id: scd30_humidity
      accuracy_decimals: 1
    temperature_offset: 5.5
    update_interval: 10s

Great that you got it to work.
/Mattias

for the sake of completeness:

Note that in the logger component (and i expect in the actual LED logic) the brightness is used to calculate logged %. So 0.5 RED with 0.7 brightness is: (0.5 / 0.7 *1)

[13:14:51][D][light:278]:   Brightness: 70%
[13:14:51][D][light:287]:   Red=71%, Green=100%, Blue=0%

That got me confused about my logic too.

Thanks for this useful post!

I think I made the smarter code you were too lazy to create :wink:
The code below will smoothly transition the light from green to yellow and then orange depending on the CO2 value.

  - platform: senseair
    co2:
      name: "CO2 sensor"
      id: co2sensor
      on_value:
        then:
          - light.turn_on:
              id: rgblicht
              state: on
              # brightness: 100% need to make this dependent on light sensor, otherwise too bright at night.
# Objective: 400ppm=Green; 900ppm=Yellow; 1200ppm=Red with transitions in between
# -->Between 400ppm and 900ppm Red should gradually increase from 0 to 1: Formula: (X-400)/(900-400) & clamp function will restrict result to be between 0 and 1.
# -->Between 900ppm and 1200ppm Green should gradually decrease from 1 to 0: Formula: 1-(X-900)/(1200-900) & clamp function will restrict result to be between 0 and 1.
              red: !lambda |-
                static float red_intense = 0.0;
                ESP_LOGD("main", "Input Number is: %f", x);
                red_intense = ((x-400.0) / 500.0);
                ESP_LOGD("main", "Red Number is: %f", red_intense);
                red_intense = clamp(red_intense,0.0,1.0);
                ESP_LOGD("main", "Red Number clamped is: %f", red_intense);
                return red_intense;
              green: !lambda |-
                static float green_intense = 0.0;
                ESP_LOGD("main", "Input Number is: %f", x);
                green_intense = (1 - (x-900.0) / 300.0);
                ESP_LOGD("main", "Green Number is: %f", green_intense);
                green_intense = clamp(green_intense,0.0,1.0);
                ESP_LOGD("main", "Green Number clamped is: %f", green_intense);
                return green_intense;
              blue: 0.0

Thanks! That is a great improvement indeed.

While I indeed prefer lazy over tired any day, I did modify my code as well after this post. :innocent: This is to support my CO2 sensor project I build for the school of my kids. You can find all the code et al here on my github: https://github.com/matthijsberg/SCO2/

1 Like

Glad to hear that.
FYI I see in the code on your github that you still use things like } else if (x > 800 and x < 1500) {
You don’t need this anymore. The clamp functionality will keep de colour values nicely between 0 and 1.
So in the example code I posted above a CO2 PPM of 300 would in principle lead to red being (300-400)/500 which is a negative value, but the clamp function will raise this value to 0.

PS: nice 3d printing skills! That’s an area I haven’t dared to venture into yet :wink:
PS2: I was considering to borrow my sensor to my kids school as well because of the COVID-19 pandemic, but my wife thinks they will not want to rely on some DIY thingy :-p

Thanks, I’ll change that in my next iteration (if ever ;-)).

The teachers are really happy with it. I tested 4 of them, with others commercial sensors too, and they where all in the same range. The sensirion sensor is really reliable. Don’t use eCO2 sensors, I found them way off, especially in higher values.
When it’s red they open a door or window, and they see it changing to green really fast again. It’s not too distracting for the kids, yet noticeable enough. I did change the upper limit to 1500 ppm since 1200 is just not doable. Also, 70% red is already really red from a distance.

I plan on building a dashboard for school in HA with all the sensors in it, but I need to add in MQTT since mDNS is disabled at school breaking the websocket integration. (can’t get the IP based fallback to work). When time is on my side again perhaps. :wink:

2 Likes

OK, if I see the director of the school I may have a word with him :wink:

I chose the senseair S8 over the SCD30 because it’s supposed to have a superior Automatic Background Calibration functionality, also so far people haven’t identified fake versions of the Senseair S8 (supposedly there are a lot of fake SCD30’s).

FYI if you read the values into home assistant (like I do) having a value every 10 seconds is perhaps a bit much to store. My ESP32 reads the value every 4 seconds (because that is the update frequency of the Senseair S8) but I only send the value to Home Assistant if the difference is higher than 4ppm:

      filters:
        - delta : 4.0 #only send the result if the parts per million difference with the last sent result is higher than this