Hi,
Has someone already integrated this kind of sensors?
I’m looking for a smaller form factor carbon monoxide sensors then the mq-7.
Thanks
Hi,
Has someone already integrated this kind of sensors?
I’m looking for a smaller form factor carbon monoxide sensors then the mq-7.
Thanks
Push this question up
If you have the MQ-7 or the MQ-2 up and running on an ESP32 for ESPHome, would you mind sharing that code?
And a follow-up Q; If I need to calibrate these components, do you might have some advice for me as to where to find such code too?
MQ sensors are VERY bad ones. Forget it.
What sensor do you recommend to an ESP32 and ESPHome?
Why are MQ7 “so bad”? I mean - in what way? Accuracy? I just installed one in my basement where i have my pellets stove for central heating. I don’t need accuracy there, i just need to know if i’ll “die or stay alive”… ![]()
I successfully integrated the MICS 5524 sensor. As it gives an analog output, we can use the Analog To Digital Sensor, it’s just a bit tricky to figure out the conversion from analog voltage to CO level.
This relation is not straight up given in the datasheet, but there is a diagram that shows the change in resistance relative to the CO-level. There will probably be significant deviations from this graph from sensor to sensor, but this is all we have to get an estimate.
Datasheet shows line in log-log scale, so that means that y = a * x^k, and in our case CO = a * (Rs/R0)^k.
To determine our constants we will take two measurements in the diagram:
CO = 10 ppm @ Rs/R0 = 0.5
CO = 1000 ppm @ Rs/R0 = 0.01
This gives us the following solution:
a = 6.3
k = -1.1
And thus CO = 6.3 * (Rs/R0)^-1.1
Rs is the measured sensing resistance and R0 is its resistance in normal air (at apparently 4.5 ppm CO). The datasheet states that it is somewhere between 100 kΩ and 1500 kΩ, so I assumed R0 = 700 kΩ.
On the usual PCB-Modules, the sensor is typically set up to form a voltage divider with a 10 kΩ resistor to ground. This means that the voltage on the Aout pin is determined by:
Aout = 5V * (10 kΩ) / (Rs + 10 kΩ).
Now, to determine our CO level with our handy formula, we need to calculate the current resistance of Rs, so we rearrange to get this:
Rs = (5V * (10 kΩ) / Aout) - 10 kΩ
Now we can just combine everything into one final formula, to get the CO level of the measured Aout voltage:
CO = 6.3 * (((5V * (10 kΩ) / Aout) - 10 kΩ)/(700 kΩ))^-1.1
Anyways, you’re probably just interested in the configuration, so here it is.
Warning: This sensor is not suitable to get accurate measurements, this configuration will only indicate a rough estimate! Individual sensors may vary significantly in offset and sensitivity, a low reading can not be trusted!
Note: The normal ESP32 can’t read voltages below 0.075V, which results in a minimum measuered CO level of 7 ppm.
sensor:
- platform: adc
pin: GPIOXX
name: "CO"
update_interval: 30s
attenuation: auto
unit_of_measurement: "ppm"
accuracy_decimals: 0
filters:
- lambda: return 6.3f * pow(((5.0f * 10.0f / x) - 10.0f) / 700.0f, -1.1f);
I can confirm that it reacts to alcohol, but I don’t have the means to test and verify that it actually produces semi-accurate CO level readings, so use with caution!
Hi guys, thanks @JensKolb for all your pre-work! I typed in your formula into: Desmos | Graphing Calculator and found your formula did not align with the Grid on the datasheet, so I did a bit of trial and error on the above website to find a formula that is a bit closer to the chart from the MiCS 5524 datasheet.:
3.5 * (Rs)-0.85 or in code terms:
sensor:
- platform: adc
pin: GPIO32
id: volt
update_interval: 1s
attenuation: auto
internal: False
name: "Volt"
unit_of_measurement: "V"
accuracy_decimals: 5
- platform: template
id: RsR0
name: Rs/R0
internal: False
update_interval: 1s
#unit_of_measurement: "ppm"
accuracy_decimals: 5
lambda: |-
return (((5.0f * 10.0f / id(volt).state) - 10.0f) / 700.0F);
- platform: template
id: CO
name: Carbon Monoxide
internal: False
update_interval: 1s
unit_of_measurement: "ppm"
accuracy_decimals: 0
lambda: |-
float co = 3.5f * pow(id(RsR0).state, -0.85f);
if(co < 7) co = 0.0;
if(co > 10000.0) co = 10000.0;
return co;
If somebody have the time to align the other gases, this is how to set up the graph, you just have to play around with the 3.5 and -0.85 to change the graph, it actually is pretty easy now:
Thanks for the heads-up! I quietly switched x and y of the formula (CO and Rs/R0) for simpler transformation.
To recreate the graph in the datasheet, the exact formula would be
(RS/R0) = 3.53553*CO-0.849485.
But we need it the other way around: CO = a*(RS/R0)^k, and that’s what I tried to calculate.
However, I apparently did an error, because the correct values would be a=4.42214, k=-1.17718
I am testing this two sensors MICS-4514 and MICS-5524 and managed to make them work ,this is my config for now
esphome:
name: gas-sensor
friendly_name: "Multi-Gas Detector"
on_boot:
priority: 600
then:
- output.turn_on: heater_control_4514
- output.set_level:
id: heater_control_4514
level: 1.0 # 100% duty cycle for MICS-4514 heater
- output.turn_on: enable_5524
- logger.log: "MICS-4514 and MICS-5524 enabled at boot"
esp32:
board: esp-wrover-kit
framework:
type: esp-idf
sdkconfig_options:
CONFIG_ADC_CAL_EFUSE_TP_ENABLE: y
CONFIG_ADC_CAL_EFUSE_VREF_ENABLE: y
CONFIG_ADC_CAL_LUT_ENABLE: y
ota:
platform: esphome
password: !secret ota_password
web_server:
version: 3
logger:
level: INFO
baud_rate: 0 # Disables UART logging (optional)
wifi:
networks:
- ssid: !secret wifi3_ssid
password: !secret wifi3_password
- ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "Gas Sensor Fallback"
password: "xxxxxxxx"
api:
encryption:
key: !secret api_key
services:
- service: restart
then:
- logger.log: "Restart requested via Home Assistant"
- delay: 1s
i2c:
sda: GPIO21
scl: GPIO22
scan: true
id: i2c_bus
ads1115:
- address: 0x48
id: ads
continuous_mode: true
output:
- platform: ledc
pin: GPIO2
id: heater_control_4514
frequency: 100Hz # Suitable for MICS-4514 heater control
- platform: gpio
pin: GPIO4
id: enable_5524
binary_sensor:
- platform: status
name: "Sensor Status"
button:
- platform: template
name: "Start Calibration"
id: start_calibration
on_press:
then:
- output.turn_on: heater_control_4514
- output.set_level:
id: heater_control_4514
level: 1.0
- output.turn_on: enable_5524
- delay: 180s # 3-minute warm-up
- lambda: |-
float rs_red = id(mics_rs_red_4514).state;
float rs_ndx = id(mics_rs_ndx_4514).state;
float rs_5524 = id(mics_rs_5524).state;
if (!isnan(rs_red) && rs_red > 1000 && rs_red < 50000 &&
!isnan(rs_ndx) && rs_ndx > 1000 && rs_ndx < 50000 &&
!isnan(rs_5524) && rs_5524 > 1000 && rs_5524 < 50000) {
id(mics_ro_red_4514) = rs_red;
id(mics_ro_ndx_4514) = rs_ndx;
id(mics_ro_5524) = rs_5524;
ESP_LOGI("calib", "New baselines: RED_4514=%.0f Ohms, NDX_4514=%.0f Ohms, 5524=%.0f Ohms", rs_red, rs_ndx, rs_5524);
} else {
ESP_LOGE("calib", "Invalid Rs: RED_4514=%.0f Ohms, NDX_4514=%.0f Ohms, 5524=%.0f Ohms", rs_red, rs_ndx, rs_5524);
}
time:
- platform: homeassistant
id: hass_time
globals:
- id: invalid_reading_minutes
type: int
restore_value: no
initial_value: '0'
- id: mics_ro_red_4514
type: float
restore_value: true
initial_value: '10000.0'
- id: mics_ro_ndx_4514
type: float
restore_value: true
initial_value: '10000.0'
- id: mics_ro_5524
type: float
restore_value: true
initial_value: '10000.0'
interval:
- interval: 1min
then:
- lambda: |-
if (isnan(id(mics_rs_red_4514).state) || isnan(id(mics_rs_ndx_4514).state) || isnan(id(mics_rs_5524).state)) {
id(invalid_reading_minutes) += 1;
ESP_LOGW("check", "Rs is invalid for %d minute(s)", id(invalid_reading_minutes));
} else {
id(invalid_reading_minutes) = 0;
}
if (id(invalid_reading_minutes) >= 5) {
ESP_LOGE("check", "Rs invalid for 5+ minutes. Restarting...");
id(invalid_reading_minutes) = 0;
App.reboot();
}
bme68x_bsec2_i2c:
address: 0x77
model: bme680
operating_age: 28d
sample_rate: LP
supply_voltage: 3.3V
sensor:
- platform: ads1115
ads1115_id: ads
multiplexer: A0_GND
gain: 4.096 # Suitable for 0–4.096V; use 6.144 if Vout exceeds 4.096V
update_interval: 5s
id: mics_vout_red_4514
name: "MICS-4514 RED Vout"
unit_of_measurement: "V"
accuracy_decimals: 4
filters:
- median:
window_size: 5
send_every: 2
- sliding_window_moving_average:
window_size: 10
send_every: 5
- platform: ads1115
ads1115_id: ads
multiplexer: A1_GND
gain: 4.096
update_interval: 5s
id: mics_vout_ndx_4514
name: "MICS-4514 NDX Vout"
unit_of_measurement: "V"
accuracy_decimals: 4
filters:
- median:
window_size: 5
send_every: 2
- sliding_window_moving_average:
window_size: 10
send_every: 5
- platform: ads1115
ads1115_id: ads
multiplexer: A2_GND
gain: 4.096
update_interval: 5s
id: mics_vout_5524
name: "MICS-5524 Vout"
unit_of_measurement: "V"
accuracy_decimals: 4
filters:
- median:
window_size: 5
send_every: 2
- sliding_window_moving_average:
window_size: 10
send_every: 5
- platform: bme68x_bsec2
temperature:
name: "BME68x Temperature"
pressure:
name: "BME68x Pressure"
humidity:
name: "BME68x Humidity"
iaq:
name: "BME68x IAQ"
id: iaq
co2_equivalent:
name: "BME68x CO2 Equivalent"
breath_voc_equivalent:
name: "BME68x Breath VOC Equivalent"
- platform: wifi_signal
name: "WiFi Signal"
update_interval: 60s
- platform: uptime
name: "Uptime"
- platform: template
name: "MICS-4514 RED Rs"
id: mics_rs_red_4514
unit_of_measurement: "Ohms"
accuracy_decimals: 0
update_interval: 10s
lambda: |-
float vout = id(mics_vout_red_4514).state;
if (vout > 0.1 && vout < 4.9) {
return 820.0 * (5.0 / vout - 1);
}
ESP_LOGE("sensor", "Invalid MICS-4514 RED Vout: %.2fV", vout);
return NAN;
- platform: template
name: "MICS-4514 NDX Rs"
id: mics_rs_ndx_4514
unit_of_measurement: "Ohms"
accuracy_decimals: 0
update_interval: 10s
lambda: |-
float vout = id(mics_vout_ndx_4514).state;
if (vout > 0.1 && vout < 4.9) {
return 820.0 * (5.0 / vout - 1);
}
ESP_LOGE("sensor", "Invalid MICS-4514 NDX Vout: %.2fV", vout);
return NAN;
- platform: template
name: "MICS-5524 Rs"
id: mics_rs_5524
unit_of_measurement: "Ohms"
accuracy_decimals: 0
update_interval: 10s
lambda: |-
float vout = id(mics_vout_5524).state;
if (vout > 0.1 && vout < 4.9) {
return 820.0 * (5.0 / vout - 1);
}
ESP_LOGE("sensor", "Invalid MICS-5524 Vout: %.2fV", vout);
return NAN;
- platform: template
name: "MICS-4514 RED Rs/Ro Ratio"
id: rs_ro_ratio_red_4514
accuracy_decimals: 4
update_interval: 10s
lambda: |-
float rs = id(mics_rs_red_4514).state;
float ro = id(mics_ro_red_4514);
if (isnan(rs) || isnan(ro) || ro <= 0) {
return NAN;
}
return rs / ro;
- platform: template
name: "MICS-4514 NDX Rs/Ro Ratio"
id: rs_ro_ratio_ndx_4514
accuracy_decimals: 4
update_interval: 10s
lambda: |-
float rs = id(mics_rs_ndx_4514).state;
float ro = id(mics_ro_ndx_4514);
if (isnan(rs) || isnan(ro) || ro <= 0) {
return NAN;
}
return rs / ro;
- platform: template
name: "MICS-5524 Rs/Ro Ratio"
id: rs_ro_ratio_5524
accuracy_decimals: 4
update_interval: 10s
lambda: |-
float rs = id(mics_rs_5524).state;
float ro = id(mics_ro_5524);
if (isnan(rs) || isnan(ro) || ro <= 0) {
return NAN;
}
return rs / ro;
- platform: template
name: "MICS-4514 Carbon Monoxide (CO)"
id: co_ppm_4514
unit_of_measurement: "ppm"
accuracy_decimals: 1
update_interval: 10s
lambda: |-
float ratio = id(rs_ro_ratio_red_4514).state;
return isnan(ratio) ? NAN : 0.08 * pow(ratio, -2.95) * exp(1.77 * log(ratio));
- platform: template
name: "MICS-4514 Methane (CH4)"
id: ch4_ppm_4514
unit_of_measurement: "ppm"
accuracy_decimals: 1
update_interval: 10s
lambda: |-
float ratio = id(rs_ro_ratio_red_4514).state;
return isnan(ratio) ? NAN : 0.85 * pow(ratio, -2.61) * exp(1.62 * log(ratio));
- platform: template
name: "MICS-4514 Ethanol (C2H5OH)"
id: ethanol_ppm_4514
unit_of_measurement: "ppm"
accuracy_decimals: 1
update_interval: 10s
lambda: |-
float ratio = id(rs_ro_ratio_red_4514).state;
return isnan(ratio) ? NAN : 0.10 * pow(ratio, -3.14) * exp(2.43 * log(ratio));
- platform: template
name: "MICS-4514 Hydrogen (H2)"
id: h2_ppm_4514
unit_of_measurement: "ppm"
accuracy_decimals: 1
update_interval: 10s
lambda: |-
float ratio = id(rs_ro_ratio_red_4514).state;
return isnan(ratio) ? NAN : 0.04 * pow(ratio, -1.8) * exp(0.9 * log(ratio));
- platform: template
name: "MICS-4514 Ammonia (NH3)"
id: nh3_ppm_4514
unit_of_measurement: "ppm"
accuracy_decimals: 1
update_interval: 10s
lambda: |-
float ratio = id(rs_ro_ratio_red_4514).state;
return isnan(ratio) ? NAN : 0.10 * pow(ratio, -1.67) * exp(0.87 * log(ratio));
- platform: template
name: "MICS-4514 Nitrogen Dioxide (NO2)"
id: no2_ppm_4514
unit_of_measurement: "ppm"
accuracy_decimals: 2
update_interval: 10s
lambda: |-
float ratio = id(rs_ro_ratio_ndx_4514).state;
return isnan(ratio) ? NAN : 1.8 * pow(ratio, 1.18) * exp(-0.35 * log(ratio));
- platform: template
name: "MICS-5524 Carbon Monoxide (CO)"
id: co_ppm_5524
unit_of_measurement: "ppm"
accuracy_decimals: 1
update_interval: 10s
lambda: |-
float ratio = id(rs_ro_ratio_5524).state;
return isnan(ratio) ? NAN : 0.25 * pow(ratio, -2.95) * exp(1.77 * log(ratio));
- platform: template
name: "MICS-5524 Methane (CH4)"
id: ch4_ppm_5524
unit_of_measurement: "ppm"
accuracy_decimals: 1
update_interval: 10s
lambda: |-
float ratio = id(rs_ro_ratio_5524).state;
return isnan(ratio) ? NAN : 2.4 * pow(ratio, -2.61) * exp(1.62 * log(ratio));
- platform: template
name: "MICS-5524 Ethanol (C2H5OH)"
id: ethanol_ppm_5524
unit_of_measurement: "ppm"
accuracy_decimals: 1
update_interval: 10s
lambda: |-
float ratio = id(rs_ro_ratio_5524).state;
return isnan(ratio) ? NAN : 0.15 * pow(ratio, -3.14) * exp(2.43 * log(ratio));
- platform: template
name: "MICS-5524 Hydrogen (H2)"
id: h2_ppm_5524
unit_of_measurement: "ppm"
accuracy_decimals: 1
update_interval: 10s
lambda: |-
float ratio = id(rs_ro_ratio_5524).state;
return isnan(ratio) ? NAN : 0.10 * pow(ratio, -1.8) * exp(0.9 * log(ratio));
- platform: template
name: "MICS-5524 Ammonia (NH3)"
id: nh3_ppm_5524
unit_of_measurement: "ppm"
accuracy_decimals: 1
update_interval: 10s
lambda: |-
float ratio = id(rs_ro_ratio_5524).state;
return isnan(ratio) ? NAN : 0.17 * pow(ratio, -1.67) * exp(0.87 * log(ratio));
- platform: template
name: "MICS-5524 Nitrogen Dioxide (NO2)"
id: no2_ppm_5524
unit_of_measurement: "ppm"
accuracy_decimals: 2
update_interval: 10s
lambda: |-
float ratio = id(rs_ro_ratio_5524).state;
return isnan(ratio) ? NAN : 0.13 * pow(ratio, 1.18) * exp(-0.35 * log(ratio));
Hardware Setup