Alacrity Wiki

SEN66 reference

This is a brief reference of the inner workings of the SEN66 sensor used in AirBox V2. It explains the physics behind the readings + the tech. PLEASE USE THE OFFICIAL MANUALS FROM SENSIRION FOR THE REAL IMPLEMENTATON.

PLEASE USE THE OFFICIAL MANUALS FROM SENSIRION FOR THE REAL IMPLEMENTATON

This page

This pages aims to better explain the inner working of the SEN66 sensor used in AirBox V2. This tiem investment might seem like a bad idea at first sight but SEN66 measures so many things that, after looking at its inner workings and basic principles, you can pretty easily infer about other sensors as well

Sensirion's website

For the most amount of detail possible, visit: https://sensirion.com/products/catalog/SEN66

Speficication

Source: sensirion.com/products/catalog/SEN66

Particulate matter

Parameter Value Unit
Mass concentration precision ±10 %
Mass concentration range 0 – 1000 µg/m³
Lifetime > 10 years
Particle size range PM1.0, PM2.5, PM4 and PM10 —

Humidity

Parameter Value Unit
Typ. relative humidity accuracy 4.5 %RH

Temperature

Parameter Value Unit
Typ. temperature accuracy 0.45 °C

CO₂

Parameter Value Unit
CO₂ output range 0 – 40000 ppm
Accuracy ±50.0 ppm

VOC

Parameter Value Unit
Sensor output 0 – 500 VOC Index

Generic

Parameter Value Unit
Supply voltage 3.15 – 3.6 V
Average supply current 90 mA
Interfaces I²C —
Size (L×W×H) 55.2 × 25.6 × 21.3 mm

Measured pollutants - importance and measuring

CO2 and the photoacoustic NDIR technology

Perhaps the most important metric for indoor air quality in normal living conditions (i.e. without any peculiar pollution sources like a 3D printer - either FDM or resin or whatever the fuck arcane stuff you can own -).

The reason why has to do with:

  1. The fact that we exhale it.
  2. The fact that most spaces ar not well ventilated.

CO2 in the air we breathe out

Humans inhale environmental air containing about 20.95% Oxygen and 0.043% CO2 (430ppm; ppm: parts per million) by volume. You can tell the amount of CO2 in the atmosphere is very limited. The accumulation of CO2 inside a room is mainly due to the CO2 we exhale. During aerobic cellular respiration (an oxidative process), the Oxygen we inhale is used as an electron acceptor inside our mithochondria to create ATP (Adenoside Triphospate), our main energy source (used throughout the whole body, including our brains and muscles).

The equation for the chemical reaction is as follows:

C6H12O6 (s) + 6 O2 (g) → 6 CO2 (g) + 6 H2O (l) + energy
ΔG = −2880 kJ per mol of C6H12O6

This equation, while technically correct, is not complete and does not describe the process fully, which goes as follows:

1. Glycolisis

The glucose (C6H12O6, 6 carbons per molecule) we get from our diets is first converted into two molecules of pyruvate (3 carbons each). 2 ATP is invested, 4 is produces -> net gain of 2 ATP. At this stage no Oxygen is consumed nor any CO2 produced.

2. Oxidation

The pyruvate resulted from the Glycolisis process is then transported into the mithochondrial matrix and broken down into acetyl-CoA by the enzyme complex pyruvate dehydrogenase.
This step releases CO2 (1 molecule per pyruvate).

3. Citric Acid Cycle

Acetyl-CoA enters a cyclic pathway of eight enzyme-catalyzed reactions in the mitochondrial matrix. The 2-carbon acetyl group is fully oxidized to 2 molecules of CO2.

4. Electron transport chain (ETC) and oxidative phosphorylation

This is where O2 finally comes in, and where the bulk of the energy is actually captured.

NADH and FADH2 drop off their electrons at protein complexes (Complex I through IV) embedded in the inner mitochondrial membrane.
As electrons pass from complex to complex, they move to progressively lower-energy states. That released energy is used to pump H+ ions from the matrix into the intermembrane space, building up a concentration and charge gradient (the proton-motive force).
At the very end of the chain, Complex IV hands the electrons off to O2, which combines with those electrons and H+ to form H2O. This is the 6 H2O in the equation,** and it's the only step that directly consumes O2**.
The H+ gradient built up across the membrane then flows back down through ATP synthase, a molecular turbine that uses that flow to physically spin and catalyze ATP production from ADP + phosphate. This coupling of a concentration gradient to ATP synthesis is called chemiosmosis.

This stage produces the vast majority of ATP: roughly 26 to 28 of the total cca. 30 ATP per glucose, versus only 4 made directly in glycolysis and the citric acid cycle combined.

The whole process is fundamentally a redox reaction (oxidation-reduction). Glucose's carbons start out relatively electron-rich (reduced) and end up as CO2, which is fully oxidized. Those electrons don't vanish, they get shuttled by NAD+/NADH and FAD/FADH2 to the electron transport chain, and ultimately end up on oxygen, reducing it to water. In a real sense, respiration is just a very carefully controlled, multi-step burning of glucose, using enzymes to break a single large energy release into many small, harvestable ones.

Why this matters

Our bodies love oxygen (as you can see) but they hate CO2. High concentrations of CO2 in the air we breathe make our blood more acidic (because, when levels are high enough, it reacts enough with water to form carbonic acid), in turn distrupting really important proceses (like protein function, enzyme function) which is probably what makes us feel dizzy, have headaches and what reduces our cognitive ability when concentrations rise.

The effects over our body at each level

That high levels of CO2 have bad effects (ranging from reduced attention to slight congitive impairment, dizziness and headaches) is a fact (https://www.mdpi.com/2073-4433/13/6/891 ; table 1 names a bunch of studies that prove it). But what are the effects at each level and what level should we be at?


CO2 Concentration Health Effects

CO2 Level Main Effect Study / Source
400-1000 ppm Baseline outdoor / ventilated air (ASHRAE ventilation adequacy threshold), noting that outdoor is supposed to be 420-430 ppm and our SEN66 is designed to have 400 as baseline for air quality. ASHRAE Standard 62.1
~950 ppm Increased headaches, loss of concentration, lower test scores in classrooms CO2Meter review of classroom air quality studies
1000 ppm Decision-making performance measurably reduced in chamber studies Chi et al. 2022 (MDPI, Atmosphere)
1400 ppm Basic decision-making cut ~25%; complex strategic thinking cut ~50% Satish et al. 2012, cited in CU Boulder / Karnauskas study
1400 ppm (in-vehicle) Increased traffic violations and driver fatigue vs. lower levels Driving simulator study, 50 participants
600 vs 2100 ppm No significant cognitive performance difference found ScienceDirect chamber study (2023)
700 vs 2500 ppm Pilot decision-making significantly better at lower level (OR 1.69) Flight simulator study, 30 commercial pilots
3000-5000 ppm Physiological response changes; self-reported fatigue increase ScienceDirect review (2023)
<5000 ppm (meta-analysis) Inconsistent, high-heterogeneity effects on cognitive task performance ScienceDirect meta-analysis (2023)
5000 ppm Regulatory 8-hour occupational exposure limit; mild respiratory stimulation possible OSHA / NIOSH / ACGIH (29 CFR 1910.1000)
5000-20,000 ppm No significant cognitive or psychomotor impairment, with or without exercise Maniscalco et al. 2022 (Indoor Air)
>12,000 ppm Noticeable increase in cerebral blood flow Maniscalco et al. 2022 (Indoor Air)
15,000 ppm (42 days) No psychomotor deficits; moderate anxiety, apathy, desire to leave increased Schaefer 1961 (submarine study)
30,000 ppm (5 days) No measurable effect on steadiness, vigilance, memory, or arithmetic performance Glatte et al. 1967
30,000 ppm Regulatory short-term exposure limit (15 minutes) NIOSH STEL
~39,500 ppm Increased respiration rate in resting subjects Sinclair et al. 1969
40,000 ppm Immediately Dangerous to Life or Health (IDLH) threshold NIOSH IDLH documentation
50,000 ppm (30 min) Signs of intoxication ACGIH 1971, cited in NIOSH IDLH review
50,000 ppm Used clinically to trigger panic attacks in panic disorder research Maniscalco et al. 2022 (Indoor Air)
70,000-100,000 ppm (few min) Unconsciousness Flury and Zernik 1931
100,000 ppm Immediately dangerous to life; loss of consciousness within minutes AIHA 1971 / Hunter 1975

NOTE: the 1000-5000 ppm range is the least settled range in this table. Several studies contradict each other on whether cognitive effects are real, dose-dependent, or caused by co-occurring pollutants (bioeffluents, VOCs) rather than CO2 itself. Everything from 5000 ppm upward is much more consistently documented, since it comes from occupational exposure and acute toxicology data rather than contested cognitive-performance chamber studies.

Thus, you must aim to have under 1000ppm as much as possible. Classrooms consistently have levels above 1400ppm which disrupts learning, attention and congitive ability.

WTF is NDIR

If you open the SEN66 sensor you'll quickly realize it's a kitbash of multiple sensors from Sensirion.
The sensor mainly responsible with CO2 (but that also measures humidity and temperature as far as I am aware) is the SCD4x (probably SCD41).
This is a photoacousting NDIR sensor, as the title of this chapter suggest we're starting with the NDIR part.

NDIR stands for Nondispersive Infrared and, as the name suggests, uses an infrared lamp to determine the concentration of a certain gas inside a chamber.
Some molecules like to absorb some wavelngths of radiation more than they aborb others. CO2 likes to absorb IR around 4.26 µm particularity much.
The NDIR sensor uses a lamp that generates a broad range of IR wavelengths at one end and a light sensor with a filter tuned specifically to eliminate basically everything except 4.26 µm.

The homogoenous medium in the chamber will then aborb that specific wavelenght of light overwhelmingly associated with CO2, making the amount of radiation at the other end of the chamber a lot smaller, according to Beer Lambert's law:
bll-2.jpg

Basically the less CO2 in the chamber there is, the more light will come out the other end of the chamber and will hit the detector after being filtered for that specific wavelength that gets abordbed by CO2.

This is the main principle on which the SCD30 works.
Our SCD4x does things a little bit differently.

The photoacoustic part

SCD4x series sensors are photoacousting NIDR, a very specific kind of NIDR. In Sensirion's words:

In contrast to transmissive NDIR sensors, photoacoustic NDIR sensors
detect the amount of energy that is absorbed by CO2 molecules. When
pulsing the infra-red emitter, CO2 molecules absorb infra-red light
periodically. This causes additional molecular vibration resulting in a
pressure wave inside the measurement chamber. The higher the CO2
concentration, the more light is absorbed, and thus the greater the
amplitude of this acoustic wave becomes. A microphone inside the gas
chamber measures this, from which the CO2 concentration can then be
calculated.

You can see more types of NDIR sensors described by them here.

If you want to find out more about these kinds of sensors you can check out this paper (made by Romanian scientists at Cambridge btw)

Why go photoacoustic at all? Size.

Remember Beer-Lambert from above. The light that makes it through the chamber is:

I = I0 × e^(-ε × c × L)

I0 = light going in, I = light coming out
ε  = how strongly CO2 absorbs at 4.26 µm
c  = CO2 concentration
L  = length of the path the light travels through the gas

Around room concentrations, a change of a few tens of ppm changes the transmitted light by only a tiny fraction, so a transmissive sensor is trying to see a small dip in a big signal. The cure is a long optical path (a bigger L), which is why transmissive sensors are relatively big: the SCD30 is 35 × 23 × 7 mm.

A photoacoustic sensor flips the problem around. It doesn't look at the light that survived, it listens to the energy that got absorbed. With no CO2 in the chamber there is (ideally) no sound at all, so it measures a signal on top of zero instead of a small difference between two big numbers. That doesn't need a long path, so the chamber can be tiny: a standalone SCD4x is 10.1 × 10.1 × 6.5 mm. That's how a decent CO2 sensor fits, together with the PM, gas and humidity parts and a fan, into the 55 × 26 × 21 mm of the SEN66.

Fun fact: the photoacoustic effect was discovered in 1880 by Alexander Graham Bell while he was working on the photophone, a device that sent sound over a beam of light. The telephone guy is also the reason the CO2 sensor in the AirBox works.

Two more details straight from the datasheet that explain how the CO2 output behaves:

Not every SEN6x has the same CO2 sensor

All SEN6x modules share the same mechanical design, but the SEN63C and SEN69C use a different CO2 sensor: the STCC4, which works by thermal conductivity. It heats the air in a tiny cavity and measures how well the gas carries that heat away. CO2 conducts heat worse than the rest of the air, so more CO2 means slightly less heat loss. It's tiny (4 × 3 × 1.2 mm) and cheap, but less accurate: ±(100 ppm + 10% of the reading).

The SEN66 is the one with the photoacoustic sensor, and its accuracy depends on the concentration:

CO2 concentration SEN66 accuracy
400 - 1000 ppm ±(50 ppm + 2.5% of reading)
1001 - 2000 ppm ±(50 ppm + 3% of reading)
2001 - 5000 ppm ±(40 ppm + 5% of reading)

Source: SEN6x datasheet, v0.92 (December 2025). So the ±50 ppm from the spec table at the top is the best case: at 1500 ppm the tolerance is about ±95 ppm. The output goes up to 40,000 ppm, but the accuracy is only specified up to 5000 ppm.

What the CO2 reading depends on

The microphone in the chamber doesn't measure "ppm". It measures how much energy the CO2 molecules in the chamber absorbed, which depends on how many CO2 molecules are in there. ppm is a ratio (CO2 molecules per million molecules of air), so to get from one to the other the sensor needs to know a few things about the air around it.

Pressure (and altitude)

At the same ppm, air at a lower pressure simply contains fewer molecules per cubic centimeter (ideal gas law: n/V = p / (R × T)). Fewer CO2 molecules in the chamber means a weaker acoustic signal. To a first approximation, an uncompensated pressure error of x% becomes a CO2 error of roughly x%.

The SEN66 assumes 1013 hPa (sea level) unless you tell it otherwise. You can tell it in two ways:

Bucharest sits at roughly 70-90 m, about 1% below sea-level pressure, so the default isn't a disaster here. Take an AirBox to a school in Brașov (around 600 m), though, and you're already ~7% off. Both settings are volatile (they're lost on every reset or power cycle), so the firmware has to send them again at every boot.

Temperature and humidity

Temperature changes the number of molecules per volume exactly like pressure does, and humidity changes the composition (and the thermal and acoustic properties) of the gas in the chamber. The SEN66 compensates for both internally using its own RH/T measurement. You don't have to do anything here, except making sure the RH/T measurement itself is right, which is harder than it sounds (see the temperature and humidity chapter below).

Calibration: ASC, FRC and why your sensor thinks fresh air is 400 ppm

Every NDIR sensor drifts slowly over the years (the IR source ages, the optics and, in this case, the microphone change). Factory calibration alone won't hold for 10 years, so the SEN66 keeps recalibrating itself with Automatic Self Calibration (ASC): it assumes that the lowest CO2 level it sees over a period of time is fresh outdoor air at 400 ppm, and slowly shifts its baseline so that this minimum reads 400 ppm.

That's where the "designed to have 400 as baseline" from the CO2 table comes from, and it has consequences:

When you can't rely on ASC, there's Forced Recalibration (FRC): you expose the sensor to air with a known CO2 concentration and tell it "this is X ppm". The procedure from the datasheet:

  1. Run a continuous measurement for at least 3 minutes in air with a stable, homogeneous CO2 level (set the altitude or pressure before that).
  2. Stop the measurement and wait at least 1400 ms.
  3. Send Perform Forced CO2 Recalibration with the reference concentration. It answers with the correction it applied (returned value - 0x8000, in ppm), or with 0xFFFF if it failed.

Unlike most settings, the FRC result is persistent (it survives power cycles). The catch is the word "known": outdoor air next to a busy road or with a few people standing around is not 400 ppm, and FRC is only as good as your reference. If you mess it up, Perform CO2 Sensor Factory Reset (SEN66 firmware 1.2 or newer) erases the FRC and ASC history and starts over.

Breathing on the sensor is the classic quick test, and it works. Just know what you're looking at: exhaled air contains about 4% CO2, roughly 38,000-40,000 ppm. That's about the IDLH value from the table above, coming out of your mouth with every breath (it's harmless there because it's on its way out, not in). It's also why an AirBox placed right next to someone's face measures that person's breath instead of the room.

From mitochondria to your sensor: why rooms fill up with CO2

The respiration chapter above isn't just biology trivia: it's literally the input of the physics problem your sensor is measuring. Let's put numbers on it.

How much CO2 does one person make?

Everything your body does is paid for by that redox reaction. Burning one mole of glucose releases about 2800 kJ as heat (ΔH, very close to the ΔG of 2880 kJ above) and produces 6 moles of CO2, so roughly every 470 kJ of energy you spend comes with one mole of CO2.

A person sitting at a desk runs at roughly 120 W. Ventilation engineers even have a unit for this: 1 "met" is about 58 W per m² of body surface, a seated person doing school or office work is ~1.2 met, and an adult has ~1.8 m² of body surface. So:

CO2 production ≈ 120 J/s / 470,000 J/mol ≈ 0.000255 mol/s
               ≈ 0.000255 mol/s × 24 L/mol (one mole of gas at room temperature)
               ≈ 0.006 L/s

Real people don't run on pure glucose, though. The ratio between the CO2 you produce and the O2 you consume is called the respiratory quotient (RQ): exactly 1.0 for carbohydrates (6 CO2 for 6 O2, look at the equation above), about 0.7 for fats and around 0.85 for a normal mixed diet. Fat produces less CO2 for the same energy, so measured values for a seated adult end up around 0.005 L/s, about 18 liters of CO2 per hour (close to a kilogram per day). School kids produce a bit less (roughly 0.003-0.004 L/s, depending on age), and anybody walking around, building robots or playing football produces a lot more, because CO2 production scales with power.

The room is a bucket with a hole in it

People add CO2 to the room (the tap); ventilation carries room air out and replaces it with outdoor air (the hole). Written as a mass balance:

V × dC/dt = G × 10^6 - Q × (C - C_out)

V     = room volume [m³]
C     = indoor CO2 [ppm], C_out = outdoor CO2 (~425 ppm)
G     = CO2 produced by everyone in the room [m³/h]
Q     = fresh (outdoor) air flowing through the room [m³/h]

Starting from fresh air, the solution is:

C(t) = C_out + (G / Q) × 10^6 × (1 - e^(-t/τ)),     with τ = V / Q

Two things fall out of this:

A classroom, worked out

25 students (~0.004 L/s each), a 60 m² classroom with a 3 m ceiling (V = 180 m³), windows closed. With the windows shut, the only fresh air comes in through leaks around windows and doors; a rough but common estimate for an older building is 0.5 air changes per hour, so Q ≈ 90 m³/h.

G      = 25 × 0.004 L/s = 0.1 L/s = 0.36 m³/h
C_ss   = 425 + (0.36 / 90) × 10^6 = 425 + 4000 ≈ 4400 ppm   (where it would end up)
τ      = 180 / 90 = 2 hours

after one 50 minute class:      C ≈ 425 + 4000 × (1 - e^(-50/120))  ≈ 1800 ppm
after two classes (100 min):    C ≈ 425 + 4000 × (1 - e^(-100/120)) ≈ 2700 ppm

That's the "classrooms consistently have levels above 1400 ppm" warning from above, explained with high school math. Now flip it around: how much fresh air do you need to stay under 1000 ppm?

Q = G / (1000 - 425) ppm = 0.36 m³/h / 0.000575 ≈ 630 m³/h
  ≈ 175 L/s ≈ 7 L/s per student ≈ 3.5 air changes per hour

You won't get 3.5 air changes per hour through a closed window. That's why "open the windows during every break" (or mechanical ventilation) is the actual fix. A few plants on the windowsill won't do anything against 25 people breathing.

This is also where the famous 1000 ppm comes from. The old ventilation standard ASHRAE 62-1989 asked for 15 cfm (about 7 L/s) of outdoor air per person in classrooms. With a seated adult producing ~0.005 L/s, that keeps CO2 about 700 ppm above outdoor levels, so with the outdoor air of the time (~300-350 ppm) you land around 1000 ppm. It was never a toxicity limit: it's a "this room is getting enough fresh air" benchmark. CO2 works as a tracer for everything else people emit (body odors and other bioeffluents, moisture, whatever germs they're carrying), which goes up together with it. That's very likely part of why the 1000-5000 ppm studies in the table above disagree with each other: some chamber studies that separated pure CO2 from human bioeffluents (e.g. Zhang et al., 2017) found the negative effects mostly when the bioeffluents were present.

How much of other people are you breathing?

Exhaled breath carries roughly 38,000 ppm more CO2 than the air that was breathed in. So the excess CO2 in a room tells you which fraction of the air around you has already been through somebody's lungs (Rudnick & Milton, 2003):

rebreathed fraction f = (C - C_out) / 38,000
Indoor CO2 Rebreathed fraction
800 ppm ~1%
1000 ppm ~1.5%
1400 ppm ~2.6%
2000 ppm ~4%
2700 ppm (our classroom after two classes) ~6%

At the end of the second class in our example, about 1 in every 17 liters you breathe was exhaled by a classmate first. This is why CO2 monitors were widely recommended for schools during COVID-19: CO2 is the cheapest proxy for "how much of everyone else's breath (and whatever is in it) am I sharing".

One caveat: an air purifier with a HEPA filter removes particles (including virus-laden aerosols) but not CO2. In a room with good filtration, CO2 overestimates the infection risk. It's still a perfect ventilation meter.

Particulate matter and laser scattering

What PM actually is

"Particulate matter" is everything solid or liquid that floats in the air: dust, soot, smoke, pollen fragments, sea salt, tiny droplets of condensed chemicals. PM is named by size:

Two things that confuse everybody:

  1. The categories are cumulative, not separate bins. PM10 includes all of PM4, which includes all of PM2.5, and so on. That's why in your data PM1 ≤ PM2.5 ≤ PM4 ≤ PM10, always.
  2. "Diameter" means aerodynamic diameter: the diameter of a sphere with a density of 1 g/cm³ that falls through air at the same speed as the particle. Real particles are fluffy soot chains, crystals and blobs; the definition describes how they behave in air (and in your lungs), not their actual shape.

PM is given in µg/m³, micrograms of particles per cubic meter of air. That's a mass. Keep it in mind, because the SEN66 can't weigh anything.

Where it comes from:

Why size matters

Your respiratory tract is a series of filters that get finer the deeper you go:

That's why PM2.5 is the headline metric: it's the fraction that gets deep. Long-term exposure is linked to heart attacks, strokes, lung cancer (IARC has classified outdoor air pollution and particulate matter as Group 1 carcinogens since 2013), COPD, asthma, diabetes and low birth weight, and no "safe" level has been found below which the effects disappear. The WHO estimates that outdoor air pollution caused about 4.2 million premature deaths worldwide in 2019, mainly through fine particulate matter.

Where you should be

Metric WHO guideline (2021) EU limit now (2008/50/EC) EU limit from 2030 (Directive 2024/2881)
PM2.5, yearly average 5 µg/m³ 25 µg/m³ 10 µg/m³
PM2.5, daily average 15 µg/m³ none 25 µg/m³ (max. 18 days/year)
PM10, yearly average 15 µg/m³ 40 µg/m³ 20 µg/m³
PM10, daily average 45 µg/m³ 50 µg/m³ (max. 35 days/year) 45 µg/m³ (max. 18 days/year)

These are outdoor (ambient air) standards. There's no binding EU limit for PM indoors, but your lungs don't care where the particles come from, so the WHO numbers are a sensible target inside too. A clean room without sources usually sits at a few µg/m³ (mostly what leaks in from outside); frying something on the stove can push PM2.5 into the hundreds for a while.

If you want to compare an AirBox against the official outdoor measurements, Romania's national air quality monitoring network (RNMCA) publishes its data at calitateaer.ro. Those stations use reference methods, which brings us to the question of how a 20 g module measures PM at all.

How the SEN66 sees particles without weighing them

The PM part of the SEN66 (Sensirion calls the building block SPS6x) is an optical particle counter (OPC):

  1. A fan pulls air through a channel (about 0.8-0.9 L/min according to Sensirion's SEN6x FAQ).
  2. A laser beam crosses the channel. It's 850 nm, near-infrared, so invisible.
  3. Every time a particle crosses the beam it scatters a short flash of light, which a photodetector picks up.
  4. The number of flashes gives you the number of particles, and the brightness of each flash gives you an estimate of its size (bigger particles scatter more light).
  5. From the size distribution, the sensor computes the volume of the particles, multiplies it by an assumed density and gets a mass in µg/m³.

Step 5 is where all the assumptions live. How much light a particle scatters depends not only on its size but also on its shape and its refractive index: a black soot particle and a transparent droplet of the same size look very different to the detector. To turn flashes into µg/m³, the sensor assumes a "typical" aerosol. Concretely, the SEN6x is calibrated against a TSI DustTrak DRX 8533 reference instrument in "Ambient Mode", which is tuned for the relatively light aerosols you find indoors, and it's verified with a potassium chloride (KCl) test aerosol. Sensirion itself says that outdoor and industrial aerosols are often much denser and recommends determining an application-specific correction factor if that's what you measure.

Even the "real" instruments don't agree perfectly. According to Sensirion's own PM specification statement, two high-end optical reference instruments of the same model can differ by more than 20% if they're configured or maintained differently. The gold standard is still gravimetric: pull air through a filter for 24 hours and weigh the filter on a microbalance. Optical sensors give you a value every second instead of one per day, which is why everybody uses them anyway.

Why it can't see the smallest particles

Particles comparable in size to the wavelength scatter light in a complicated, lumpy way (Mie scattering). Particles much smaller than the wavelength are in the Rayleigh regime, where the scattered intensity drops roughly with the sixth power of the diameter (and with the fourth power of the wavelength, which is also why the sky is blue). Shrink a particle from 0.3 µm to 0.1 µm and it scatters roughly 3^6 ≈ 700 times less light: it disappears into the detector noise. That's why every size range in the datasheet starts at 0.3 µm.

The practical consequence for a maker lab: desktop FDM 3D printers emit huge numbers of ultrafine particles, mostly well below 0.1 µm (ABS a lot more than PLA). The SEN66 is basically blind to them, so a quiet PM reading next to a running printer does not mean the air is clean. Measuring ultrafines needs a condensation particle counter (it condenses vapor onto the particles until they're big enough to see optically), which is a lab instrument, not a 20 g module.

Why PM4 and PM10 are (mostly) educated guesses

This one comes straight from Sensirion's documentation. Low-cost sensors like this one only catch a small fraction of the particles in the air flowing through them (Sensirion quotes 3-5%), so they rely heavily on statistics. And big particles are rare: at the same mass concentration, an aerosol made of 10 µm particles has 1000 times fewer particles than one made of 1 µm particles, because mass scales with volume, and volume scales with the diameter cubed.

A back-of-envelope estimate with those numbers (assuming particles with a density of 1 g/cm³):

10 µg/m³ of 1 µm particles         ≈ 19 particles/cm³
air through the sensor             ≈ 0.85 L/min ≈ 14 cm³/s   -> ~270 particles/s
counted (~4%)                      ≈ 11 particles/s ≈ 650 per minute

same mass made of 10 µm particles  -> 1000x fewer -> ~0.65 counted per minute
to collect the same ~650 counts    -> ~1000 minutes ≈ 17 hours

The relative noise of a count is about 1/√N (Poisson statistics), so you need lots of counts for a stable number. Sensirion's solution, written in the datasheet: PM4 and PM10 are calculated from the distribution profile of all measured particles, not counted directly. You can see it in the precision specs too:

Output Precision, 0-100 µg/m³ Precision, 100-1000 µg/m³
PM1, PM2.5 ±(5 µg/m³ + 5% of reading) ±10% of reading
PM4, PM10 ±25 µg/m³ ±25% of reading

Practical rule: trust PM1 and PM2.5, treat PM4 and PM10 as an estimate. Events dominated by big particles (pollen, construction dust, sanding, a Saharan dust episode) will likely be underestimated, because the algorithm extrapolates from the small particles it actually saw.

"Precision" in Sensirion's PM specs does not mean accuracy. It's the device-to-device variation: how far an individual sensor can be from the average of a batch of sensors measuring the same air. For a network like AirBox this is good news for comparisons (two nodes in the same air should agree within ±(5 µg/m³ + 5%) on PM2.5), but it says nothing about how close both of them are to a reference station.

Number concentration: the output nobody reads (but should)

Besides mass, the SEN66 can give you number concentrations (particles per cm³) for PM0.5, PM1, PM2.5, PM4 and PM10 through the Read Number Concentration Values command. That's much closer to what the sensor actually measures, before the "assume a shape and a density" step. Sensirion itself points out that µg/m³ became the standard largely because weighing filters came first, and that size distributions and number concentrations would arguably say more about health effects. Small particles weigh almost nothing but are by far the most numerous, so the number concentration often reacts to combustion and cooking much more clearly than the mass does.

Humidity makes particles grow

Many particles (sea salt, ammonium sulfate and nitrate) are hygroscopic: at high relative humidity they absorb water and swell. The OPC sees a bigger particle, assumes the "dry" density and overestimates the mass. Above roughly 70-80% RH this becomes significant, and fog or mist droplets get counted as "particles" outright. Outdoors on a Bucharest winter morning that's the normal state of things, not an edge case. Two consequences:

Keeping the optics clean: sheath flow and fan cleaning

A particle counter that sucks in dust 24/7 for 10 years should, in theory, end up coated in dust. Sensirion's answer is a patented way of guiding the air inside the sensor, which they call sheath flow, that keeps dust and dirt away from the optical components. Their dust resistance test simulates ~35 µg/m³ of indoor PM over 10 years (a rounded-up average for indoor air in Beijing); that test, together with failure-rate calculations for every component, is what the "> 10 years" lifetime in the spec table is based on.

The fan is the other part that wears out, and the SEN66 watches it for you through the device status register (see below): a fan speed warning when the speed is more than 10% off target, and a fan error when it reads 0 RPM while it should be spinning. If either shows up, the PM values are probably wrong.

There's also a Start Fan Cleaning command, which runs the fan at maximum speed for 10 seconds to blow the dust out. Older Sensirion PM sensors did this automatically once a week; on the SEN6x the automatic cleaning was removed to keep it quiet, so it's up to your firmware. It only works in idle mode, so in practice: stop the measurement, wait 1.4 s, start the fan cleaning, wait at least 10 s, start measuring again and ignore PM for the first ~30 s (the PM start-up time). The VOC algorithm survives a stop/start, so you don't lose its learning (more about that below).

There's a laser inside the SEN66. The module as a whole is a Class 1 laser product (safe), but only while it's closed and intact. The datasheet explicitly warns that a damaged or opened sensor can expose you to the invisible Class 3R laser source inside, which can cause permanent eye damage. It's 850 nm, so you won't even see it coming. If you open one up to admire the kitbash inside, do it unpowered.

VOC and NOx: a hot plate that smells

The spec table at the top lists the VOC Index, but the SEN66 has a second gas output it doesn't mention: the NOx Index. Both come from a metal-oxide (MOX) gas sensor, the same technology as Sensirion's standalone SGP41.

What VOCs are

VOC stands for volatile organic compound: carbon-based molecules that evaporate easily at room temperature. That's thousands of different compounds, from harmless to nasty:

Health-wise, "VOCs" means very little as a category: the ethanol in hand sanitizer and benzene are both VOCs, and only one of them is a known carcinogen. The US EPA has found the levels of common organic pollutants to be 2-5 times higher indoors than outdoors, simply because indoors is where the sources are.

What NOx is

NOx = NO + NO2, the nitrogen oxides. They form whenever something burns hot in air: at high temperatures the N2 and O2 of the air itself react to NO, which then oxidizes to NO2. Outdoors the main source is traffic (diesel especially); indoors it's gas stoves and gas boilers. NO2 irritates the airways, and cooking with gas has been associated with more asthma in children. The WHO guideline for NO2 is 10 µg/m³ as a yearly average and 25 µg/m³ as a daily average; the EU yearly limit drops from 40 to 20 µg/m³ in 2030. For scale: 10 µg/m³ of NO2 is only about 5 ppb.

How a metal-oxide sensor works

The sensing element is a thin film of a metal oxide (tin dioxide type, an n-type semiconductor) sitting on a microscopic hotplate heated to a few hundred °C.

  1. In clean air, oxygen molecules stick to the surface of the oxide grains and steal electrons from them (they become O2- and O- ions on the surface). This creates an electron-depleted layer around every grain, and the film has a high resistance.
  2. Reducing gases (most VOCs, but also H2, CO and ethanol) react with that adsorbed oxygen, basically burning on the hot surface. The stolen electrons are given back, the depleted layers shrink and the resistance drops.
  3. Oxidizing gases (NO2, ozone) do the opposite: they grab even more electrons, so the resistance rises.

So the same kind of material reacts to both families of gases, in opposite directions, which is how one tiny chip can give you a VOC signal and a NOx signal. In general, the temperature of the hotplate is one of the main knobs MOX sensors use to tune how strongly each kind of gas reacts. And the heat isn't there by accident: the surface reactions need it to happen fast and reversibly, and it keeps water from sticking to the surface.

The datasheet gives a nice hint about the raw signal: the raw outputs (SRAW_VOC and SRAW_NOx, in "ticks" from 0 to 65535) are proportional to the logarithm of the resistance of the sensing layer. MOX resistance follows roughly a power law of the gas concentration (R ∝ C^-β), so the logarithm turns it into something linear in log-concentration, and it squeezes a resistance range of several orders of magnitude into 16 bits.

The downsides of MOX sensors are exactly why the SEN66 doesn't give you ppb:

WTF is a VOC Index?

Since a MOX sensor can't tell you what is in the air or how much in absolute terms, Sensirion does what your nose does: it gets used to the place and tells you about changes. Walk into a house that smells of cooking and after ten minutes you stop noticing it; step outside, come back in, and you smell it again. The Gas Index Algorithm running inside the SEN66 works the same way:

The NOx Index works the same way, except that its "normal" is 1: most of the time there's no NOx source indoors, so an event is the exception. Anything above 1 is a NOx event (somebody turned on the gas stove, traffic air came in through the window).

The algorithm has a few more knobs, all adjustable through Set VOC Algorithm Tuning Parameters and Set NOx Algorithm Tuning Parameters:

Parameter VOC default NOx default What it does
Index offset 100 1 The index value that means "typical conditions"
Learning time offset 12 h 12 h How fast the "normal" level adapts
Learning time gain 12 h not used (must stay 12 h) How fast the scaling (how many points a given change is worth) adapts
Gating max duration 180 min 720 min During a high event, learning is frozen so the event doesn't become the new normal, but only for this long
Std initial 50 not used (must stay 50) Initial guess of the signal variability: lower = more sensitive while still learning, but more device-to-device variation
Gain factor 230 230 Amplifies or attenuates the index

Consequences you need to understand before putting VOC Index numbers on a public dashboard:

  1. It can't tell you that a place is constantly bad. A room with permanent off-gassing (new furniture, a resin printer that's always running) becomes the "normal" within about a day and reads ~100. A VOC Index of 100 means "usual for this place", not "good".
  2. The first hours don't count. According to the datasheet, the VOC Index needs up to 1 hour after switch-on to meet its specs, and the NOx Index up to 6 hours.
  3. A reboot erases the learning, unless you save it. Get VOC Algorithm State returns 8 bytes that you can store and hand back with Set VOC Algorithm State (in idle mode, before starting the measurement) after a power cycle. The state survives a simple stop/start, but not a reset or a power loss. In firmware: read it periodically and keep it in non-volatile memory, but not every second, because on an ESP32 that's flash and flash has a limited number of write cycles. Once an hour is plenty. (There's no equivalent command for NOx.)
  4. Compare devices on events, not on absolute numbers. The device-to-device variation is up to ±15 points (or ±15%, whichever is larger) for the VOC Index and up to ±50 points (or ±50%) for the NOx Index.

Some MOX sensors (SGP30, CCS811, ENS160, and the BME680/BME688 with Bosch's software library) output an eCO2 or "CO2 equivalent" value. That is not CO2. It's a VOC (or hydrogen) signal converted into a CO2 number under the assumption that people are the only VOC source in the room. Clean your desk with IPA and your "CO2" goes through the roof. The SEN66 measures real CO2 with its photoacoustic sensor, which is one of the main reasons it's worth its price.

If you need an actual number in ppb or µg/m³ (for building standards like RESET or WELL), Sensirion has an application note ("Compliance of Sensirion's VOC Sensors with Building Standards") describing how to derive a TVOC value from the sensor. Keep in mind that it's still an estimate based on a MOX signal, not a measurement of any specific compound.

Reading the channels together

The real power of a sensor like the SEN66 is that you can cross-check its channels against each other. Some typical patterns (patterns, not rules):

What you see Likely cause
CO2 and VOC Index rise together, slowly People in a poorly ventilated room
VOC Index jumps, CO2 flat Cleaning products, IPA, perfume, soldering, glue, paint
PM and VOC Index jump, NOx flat Cooking on an electric stove, candles
PM, VOC Index and NOx Index jump together Cooking on a gas stove
PM rises, gases flat Dust (vacuuming, sweeping, sanding) or particles coming in from outside
NOx Index rises with nobody around A gas appliance, or traffic/heating exhaust coming in from outside
Very high PM together with RH above ~80% Probably fog or humidity, check before you panic
Everything drops at once Somebody opened the windows

This is exactly the kind of reasoning that a single "air quality score" hides from you.

Temperature and humidity: the glue that holds everything together

Temperature and relative humidity look like the boring channels, but they matter twice: once for you, and once for every other measurement in the module.

Why they matter for you

Why they matter for the other sensors

That last point goes both ways, and the datasheet shows it nicely: for an error in the CO2, PM or gas sensor, it warns that the temperature and humidity values may be out of spec too, because the compensation algorithms depend on the state of those sensors. Everything in this box is connected to everything else.

How they're measured

The datasheet gives away which chip does this job: the Activate SHT Heater and Get SHT Heater Measurements commands talk about the SHT4x inside, Sensirion's 4th generation humidity and temperature sensor (the same family you can buy as a standalone breakout board).

Two things to know about the SHT inside:

The self-heating trap

This is the single most common mistake with any temperature/humidity sensor built into a device, so let's do the math once. The amount of water vapor in the air (the absolute humidity) doesn't change when the air warms up by a couple of degrees inside your box, but the saturation vapor pressure does, so the RH changes. Using Sensirion's constants for the Magnus formula (saturation vapor pressure over water):

e_s(T) = 6.112 hPa × exp( 17.62 × T / (243.12 + T) ),     T in °C

RH_ambient = RH_sensor × e_s(T_sensor) / e_s(T_ambient)

Example: the room is at 22 °C, but the air around the sensor is 3 °C warmer because of the electronics around it. The sensor says 25 °C and 45% RH:

e_s(25 °C) ≈ 31.6 hPa,     e_s(22 °C) ≈ 26.4 hPa
RH_ambient = 45% × 31.6 / 26.4 ≈ 54%
Extra heat at the sensor Sensor reads Real RH in the 22 °C room
1 °C 45% RH ~48%
2 °C 45% RH ~51%
3 °C 45% RH ~54%
5 °C 45% RH ~61%

Rule of thumb at normal indoor conditions: every 1 °C of self-heating makes the RH read about 3 points too low (and the temperature 1 °C too high, obviously).

How the SEN66 handles it

By default, the SEN66's outputs are already compensated for the module's own self-heating by an internal engine that Sensirion calls the STAR engine. But once you put it in an enclosure next to a microcontroller, a voltage regulator and a Wi-Fi radio, the thermal situation changes and you have to tell it about the extra heat.

Set Temperature Offset Parameters applies:

T_compensated = T_measured + (Slope × T_measured) + Offset

The STAR engine then recalculates the relative humidity for the compensated temperature (the same Magnus math as above), so you only correct the temperature and the RH follows.

Set Temperature Acceleration Parameters (K, P, T1, T2, idle mode only) handles the dynamic part: a sensor inside a plastic box reacts slowly to changes of the room temperature, and this filter makes the output respond faster. Sensirion's application note "SEN6x - Temperature Acceleration and Compensation Instructions" explains how to derive both parameter sets for your own device and gives starting values for typical device types.

Both settings are volatile: they go back to their defaults on every reset, so the firmware has to send them at every boot.

Finding the offset for your build

  1. Put the finished device (final enclosure, final firmware, Wi-Fi doing what it normally does) in a room with a stable temperature, out of direct sunlight and drafts.
  2. Put a reference thermometer next to it, outside the enclosure, in free air. A DS18B20 on a cable works (it's specified at ±0.5 °C between -10 and +85 °C).
  3. Wait until everything reaches thermal equilibrium: hours, not minutes, because the plastic box heats up slowly.
  4. Offset = reference - SEN66 reading (usually a negative number). Put it in slot 0.
  5. If you can, repeat at a different room temperature to see whether you also need a slope.

The SEN66's own accuracy is typically ±0.45 °C and ±4.5% RH, so a ±0.5 °C reference is good enough to remove a self-heating error of a few degrees, not to calibrate the sensor to lab precision.

Talking to the SEN66

This chapter is here to help you understand what happens on the wire. For the real implementation, use the datasheet and Sensirion's official drivers: embedded-i2c-sen66 (C), arduino-i2c-sen66 and python-i2c-sen66.

Electrical

Parameter Value
Supply voltage 3.15 - 3.6 V (3.3 V typical, 3.6 V is also the absolute maximum)
SDA / SCL Open drain, external pull-ups needed (e.g. 10 kΩ), tolerate 5 V logic
Current, idle 3.3 mA
Current, measuring (average) 90 mA typical, 110 mA max
Current, peaks 300 mA typical, 350 mA max (2 ms pulses)
Supply ripple below 100 Hz max. 30 mV (the rest of the SEN6x family allows 100 mV)
I²C address 0x6B (7-bit)
I²C speed max. 100 kHz (standard mode)
Connector ACES 51468-0064N-001 on the sensor; cable side ACES 51452-006H0H0-001 or compatible (e.g. JST GHR-06V-S)
Cable max. 50 cm, ideally under 10 cm or shielded

Pinout: 1 = VDD, 2 = GND, 3 = SDA, 4 = SCL, 5 = GND (connected internally to pin 2) or not connected, 6 = VDD (connected internally to pin 1) or not connected.

SDA and SCL tolerate 5 V logic, VDD does not. 3.6 V is the absolute maximum supply voltage, so never power it from a 5 V rail.

Two of those numbers are specific to the SEN66 and they stand out: the peak current (300-350 mA, more than double the ~140 mA typical peaks of the other SEN6x models) and the stricter low-frequency ripple limit. The SEN66 is the only model in the family with the photoacoustic CO2 sensor, so my educated guess is that both come from it: pulsing an IR emitter takes current, and a sensor that literally listens with a microphone doesn't like low-frequency supply noise. Whatever the exact reason, the consequence for your board is the same: the 3.3 V regulator has to handle the SEN66's 350 mA peaks on top of the microcontroller (Wi-Fi transmit bursts are in the hundreds of mA too), with enough bulk capacitance close to the connector that the rail doesn't sag or ripple.

And for the curious: running a SEN66 continuously costs about 90 mA × 3.3 V ≈ 0.3 W, around 7 Wh per day. That empties a typical 18650 cell in under two days, for the sensor alone. You can't simply duty-cycle it either: PM needs ~30 s to stabilize after every start, the VOC/NOx algorithms and the CO2 self-calibration are built around continuous operation, and the NOx Index needs up to 6 hours to meet its specs. The SEN66 is happiest on mains power.

How the protocol works

The CRC is a CRC-8 with polynomial 0x31 (x^8 + x^5 + x^4 + 1), initial value 0xFF, no reflection and no final XOR. Test vector: the CRC of the bytes 0xBE 0xEF is 0x92. In C:

uint8_t sensirion_crc8(const uint8_t *data, size_t len) {
    uint8_t crc = 0xFF;
    for (size_t i = 0; i < len; i++) {
        crc ^= data[i];
        for (int bit = 0; bit < 8; bit++) {
            crc = (crc & 0x80) ? (uint8_t)((crc << 1) ^ 0x31) : (uint8_t)(crc << 1);
        }
    }
    return crc;
}

The data framing (16-bit words, each followed by a CRC-8 with polynomial 0x31 and initial value 0xFF) is shared by most of Sensirion's I²C sensors: SHT3x/SHT4x, SCD4x, SGP40/SGP41, SPS30, SEN5x. Learn it once here and you can read most of their datasheets.

A minimal measurement loop

power on -> wait at least 100 ms (sensor startup)

idle mode, optional configuration:
    Set Sensor Altitude or Set Ambient Pressure   (CO2 pressure compensation)
    Set Temperature Acceleration Parameters       (idle mode only)
    Set VOC Algorithm State                       (restore saved learning, idle mode only)
    Set Temperature Offset Parameters             (also allowed while measuring)

Start Continuous Measurement (0x0021) -> first results after ~1.1 s

every second:
    Get Data Ready (0x0202)           -> wait 20 ms -> read 3 bytes
    if ready:
        Read Measured Values (0x0300) -> wait 20 ms -> read 27 bytes
    every now and then:
        Read Device Status (0xD206)
        Get VOC Algorithm State (0x6181), save it

Decoding Read Measured Values (0x0300)

The answer is 27 bytes: 9 words of 2 bytes + 1 CRC each.

# Value Type Conversion "Unknown" value
1 PM1.0 uint16 value / 10 -> µg/m³ 0xFFFF
2 PM2.5 uint16 value / 10 -> µg/m³ 0xFFFF
3 PM4.0 uint16 value / 10 -> µg/m³ 0xFFFF
4 PM10 uint16 value / 10 -> µg/m³ 0xFFFF
5 Relative humidity int16 value / 100 -> %RH 0x7FFF
6 Temperature int16 value / 200 -> °C 0x7FFF
7 VOC Index int16 value / 10 0x7FFF
8 NOx Index int16 value / 10 0x7FFF (also during the first 10-11 s after power-on or reset)
9 CO2 uint16 ppm, no scaling 0xFFFF (also during the first 5-6 s after the measurement starts)

A realistic example frame, CRCs included:

00 34 80 | 00 50 FF | 00 5B 15 | 00 60 3A | 11 94 E6 | 12 48 B2 | 03 E8 D4 | 00 0A 5A | 03 2C 57

00 34 -> 52    -> PM1.0     = 5.2 µg/m³    (CRC 0x80)
00 50 -> 80    -> PM2.5     = 8.0 µg/m³    (CRC 0xFF)
00 5B -> 91    -> PM4.0     = 9.1 µg/m³    (CRC 0x15)
00 60 -> 96    -> PM10      = 9.6 µg/m³    (CRC 0x3A)
11 94 -> 4500  -> RH        = 45.00 %      (CRC 0xE6)
12 48 -> 4680  -> T         = 23.40 °C     (CRC 0xB2)
03 E8 -> 1000  -> VOC Index = 100.0        (CRC 0xD4)
00 0A -> 10    -> NOx Index = 1.0          (CRC 0x5A)
03 2C -> 812   -> CO2       = 812 ppm      (CRC 0x57)

Notes:

Device status: ask the sensor whether it's OK

Read Device Status (0xD206) returns a 32-bit register. The bits that matter for the SEN66:

Bit Name Meaning Sticky
21 SPEED Fan speed more than 10% off target (warning; not checked during the first 10 s) No
11 PM Particulate matter sensor error Yes
9 CO2-2 CO2 sensor error (bit 12, CO2-1, belongs to the SEN63C/SEN69C) Yes
7 GAS VOC/NOx sensor error Yes
6 RH&T Humidity/temperature sensor error Yes
4 FAN Fan error (0 RPM while it should be spinning) Yes

Sticky errors stay set until you call Read And Clear Device Status (0xD210), reset or power cycle the sensor, so you won't miss a short glitch. Log this register together with your data: later, it's what tells you whether a weird value came from the air or from the sensor.

Placement and enclosure: everything above, applied

Rule Protects Why
Mount it in the room at roughly the height of people's heads (~1-1.5 m), not in a corner Everything You want the air people actually breathe, in a spot where the air mixes
Not right next to anybody's face CO2 Exhaled air is ~40,000 ppm: you'd measure one person's breath instead of the room
Not next to windows, doors, vents or radiators, not in direct sunlight Everything, especially RH/T and CO2 You'd measure incoming air or a local hot spot instead of the room
Keep the inlet and outlet free, and don't let the outlet air loop back into the inlet PM Blocked flow and recirculation distort the particle counts
Keep it thermally separated from the MCU, regulator and radio RH/T (and through RH/T, everything) Self-heating, see the Magnus math above
No fresh silicone, glue, paint or solvents inside the enclosure; let new 3D-printed or glued enclosures air out first VOC/NOx Siloxane poisoning, and a VOC Index that learns your glue as "normal"
Never let it see condensation; stick to 10-40 °C and 20-80% RH for normal operation Everything It's an indoor module: condensation can damage it and fog ruins PM data
Make sure it gets fresh air at least once a week CO2 Automatic self-calibration depends on it
Mount it softly Noise It's quiet (under 24 dB(A) at 20 cm), but a rigid mount can turn the fan's vibration into an audible hum through the enclosure

Sensirion has a dedicated application note for this, SEN6x - Mechanical Design and Assembly Guidelines, with design-in examples. Read it before designing an AirBox case around the sensor.

Using the SEN66 to understand other sensors

As promised at the top: once you understand the SEN66, most of the air quality sensors you'll come across are variations on the same few principles.

Principle In the SEN66 What it really senses Typical problems Other sensors using it
Photoacoustic NDIR CO2 The sound made by CO2 absorbing pulsed IR light Pressure/altitude, needs calibration (ASC/FRC) SCD40, SCD41, Infineon XENSIV PAS CO2
Transmissive NDIR Not in the SEN66 IR light lost on its way through the gas Bigger (needs a long path), same calibration story (often called ABC) SCD30, MH-Z19 series, SenseAir S8
Thermal conductivity Not in the SEN66 (SEN63C/SEN69C) How well the gas carries heat away Lower accuracy, needs RH/T compensation STCC4, STC31
Laser scattering (OPC) PM Flashes of scattered light Blind below ~0.3 µm, PM10 extrapolated, humidity, aerosol type SPS30, Plantower PMS5003/PMS7003, SDS011
Metal oxide (MOX) VOC, NOx Resistance of a hot oxide film Not selective, drift, humidity, poisoning, only relative SGP40, SGP41, SGP30, CCS811, ENS160, BME680/BME688, MQ series
Capacitive RH + bandgap T RH, T Capacitance of a polymer, transistor voltages Self-heating, creep, condensation SHT3x/SHT4x, BME280, HDC1080, AHT20
Electrochemical Not in the SEN66 Current from a redox reaction at an electrode Cross-sensitivity, a few years of lifetime, temperature dependence Alphasense and similar NO2/O3/CO cells (what you'd use for actual NO2 in ppb)

Summary

  • CO2 is measured for real (photoacoustic NDIR) and is basically a ventilation meter and a "how much of other people's breath am I breathing" meter. Aim for under 1000 ppm, give the sensor fresh air once a week and tell it the pressure or altitude.
  • PM is counted optically and converted to mass using assumptions. Trust PM1 and PM2.5, treat PM4 and PM10 as estimates, and remember that it's blind below 0.3 µm and that humidity inflates it.
  • VOC and NOx are relative indices from a MOX sensor: 100 (VOC) and 1 (NOx) mean "usual for this place over the last day", not "good". Save the VOC algorithm state across reboots.
  • Temperature and humidity are easy to get wrong because of self-heating. Fix the temperature offset for your enclosure and the RH follows.
  • Everything is connected: RH/T compensates the other sensors, the other sensors heat the RH/T sensor, and the channels together tell you much more than any of them alone.

Further reading