Needing to measure relative humidity, I went to a local electronic components store and asked for a sensor. All they had was the H25K5A (datasheet). As it turns out, this is the cheapest type of relative humidity sensor, and a real pain to read with a microcontroller. Well, not really that hard, but there is not much info on the web and it took a while to figure out. I am going to outline my method and proceedings below with the hope that some lost soul will someday find this work and thank me in their mind.
The first thing there is to know about resistive humidity sensors is they don’t like DC. Any DC current passing through them will cause irreversible damage to the sensor because of electrolysis going on inside its sensitive guts. AC only please, no DC bias whatsoever! The second thing is it has a significant amount of stray capacitance which needs to be taken into account, but its actual value is not mentioned anywhere in the datasheet. Thank you, China and company, for your high standards.
After a couple of days of Google searches, I found this gem. It is written in very poor English, it misses out on quite a few details … generally sounds like bullshit. But the procedure described in it actually works! The really cool thing about it is that no external components are required, except for a resistor. It takes 2 IO pins of the mcu and an ADC input:

Basic RH measurement circuit
The IO pins alternate their levels during measurement. After each transition, it takes some time (on the order of μs) for the voltage to stabilize — that’s when ADC reading must be fired. Measurements can be taken in only one state, or in both states of the IO pins. So I decided to measure both V1 and V2:

IO and ADC voltages
Resistance of the sensor — R(rh) — can be calculated from the ADC readings, granted Vcc = Vref, where Vref is the reference voltage for ADC:
R(rh) = (Vcc x R1)/V1 - R1
R(rh) = (V2 x R1)/(Vcc - V2)
Actually, that second equation will fail at V2 = 0, but it’s also true that Vcc = V1 + V2. So, take a bunch of readings for both states, average each out, then have
V1,average = (V1 + (Vcc – V2)) / 2
This approach gives the maximum number of ADC readings for the time current passes through the sensor. Did I mention that even with pure AC these sensors eventually give up? People say that you should pass the least possible amount of current though them.
At this point I built a prototype and it worked! The sum of averaged ADC reads for the 2 IO states was 1023±3 (10-bit ADC) — fair enough. The numbers reacted pretty well to me breathing into the sensor too. Now, how do I get from a number between 3 and 1020 to an actual figure for relative humidity in percent? Pretty simple — just take the calibration data from the datasheet, reformat it into data points, go to zunzun.com and stare in dismay. That website blew my mind, but more on it later.
First, let’s take a look at the correlation between ADC values and R(rh):

ADC vs. R(rh) value for different values of R1
The dynamic range of my sensor is from around 2KOhm to about 21MOhm. That’s quite some range! Using a simple voltage divider will obviously not yield good results in terms of accuracy, but especially in terms of resolution at the far ends of the range — whichever one line on the graph I pick, there will be regions where my device will interpret a single least-significant bit change as a huge difference in relative humidity. To solve this, I do not use one voltage divider, but 3. Actually, more can be added, I just like the number 3:

This allows good dynamic range to be achieved by using either one of IO2, IO3 and IO4 for the opposite end of the voltage divider. So I find the derivatives of the above plots, find the points where they intersect and define the interval in which each curve is optimal.
ADC value expressed as function of R(rh)
ADC(R(rh)) = 1023 * R1 / (R(rh) + R1)
First Derivative
ADC'(R(rh)) = 1023 * R1 / (R(rh) + R1)^2
Find points where derivatives intersect
Solve ADC(R1=3)'(R(rh)) = ADC(R1=30)'(R(rh)) -> R(rh) = 3 * sqrt(10)
Solve ADC(R1=30)'(R(rh)) = ADC(R1=300)'(R(rh)) -> R(rh) = 30 * sqrt(10)
Therefore, resistances below ~9.48 KOhm should be measured with the 3KOhm bridge, such above 94.8KOhm should be measured with the 300KOhm bridge, everything else falls in the middle.
The next step is to get that value and turn it into RH%. Did I mention the sensor is heavily temperature-dependent? I should have, but you should have known if you looked at the datasheet. Adding a thermometer to an AVR-based device is fairly simple, I’ll just outline what I did next:
Pasted the reference values table from the datasheet into a spreadsheet, aligned it, transformed it into one-data-point-per-row format, copy/paste into zunzun.com’s 3D Function Finder. After it was done I had a piece of code I could readily paste into my program. Cool. The only problem is that the best interpolation gives ±15% error, and that is a lot. My plan is to slice the data into 3 temperature regions (or maybe slice it by resistance) and see if individual interpolations for each will yield better results.
This is a work in progress, part of a project I intend to release as open hardware. There’s a lot more to be done though, so stay tuned if you are interested. As always, comments are welcome, except in Russian.
Update: making some progress on the project, current code can be seen here, rendered schematics and links are also available.