I compared the humidity readings from two DHT22 and was alarmed by the difference between them.
Although they are cheap, the Bosch BME280 doesn't have to cost that much more, and provides pressure measurement in addition to humidity and temperature.
Although many of the BME280s available cost around £20, I splashed out £7.61 and bagged a bargain on Amazon.
Having discovered that the difference in humidity reading between my two samples of DHT22 was around 12%, I did a bit of net research and found others were complaining of accuracy outside the quoted spec.
One example is Robert Smiths comparison which concluded that the Bosch device was a clear winner.
The Bosch Sensortec BME280
Unlike the DHT22, the Bosch sensor is mounted on a small pcb assembly and has both SPI and i2c interface. Its address can be set to either 0x76 or 0x77 depending upon the level set on the SDO pin (i.e. either +V or 0V).
Each device is tested during manufacture and programmed with 18 calibration values. These values must be read by your program and used to "trim" the raw readings to produce accurate results for temperature, humidity and air pressure.
My device is branded "MagiDeal" and is supplied with header pins which require soldering into the board. As I just wanted to use i2c, I only soldered 4 pins to the board.
I needed to use this device on a Raspberry Pi server, which already had an Energenie board attached. So I had to solder wires to the corresponding PiMote pin pads (pin 1 = 3V3, 3 = SDA, 5 = SCL, 6 = 0V).
Linking the sensor via about 2.5m of cable doesn't seem to be a problem. Although I'm using CAT5 cable, I've simply joined each of the twisted pair cores together to create a simple 4 core cable.
Preparing the Pi
The first job is to enable i2c on the Pi. This can normally be done via the raspi-config utility. In my case, for whatever reason, I had to manually add a couple of lines to the /etc/modules file:-
...this may have been due to old firmware on my ancient model B Pi.
You also need to install some i2c stuff:-
sudo apt-get install i2c-tools libi2c-dev
...and with the BME280 connected, check its address by typing in a terminal:-
sudo i2cdetect 1
...or use "0" if you have an old model B
Time for the software
Initially I downloaded some C code for the bme280 by Andrei Vainik for testing purposes.
If you have the Geany programmers editor installed on your Pi, it is quite easy to compile, make and run Andrei's project from Geany, and verify that the device is working.
In my case I had to edit the header file (bme280.h) to change the device address reference from 0x76 to 0x77.
A Gambas version
I decided to write a Gambas application for the BME280, using Gordon Henderson's wiringPi library to manage the i2c stuff.
Once again using Andrei's project, this time as a reference, I was able to recreate the complicated maths required.
When reading calibration values from the BME280 memory, it is important to know whether the value is signed or unsigned, and use appropriate data types.
Unsigned 8 and 16 bit values can be read straight into Gambas Integers. e.g. for 8 bit:-
CDigH1 = wiringPiI2CReadReg8(intDeviceHandle, CAL_DIG_H1)
For signed 8 bit values, I convert them to Gambas bytes, then store in an integer:-
CDigH6 = CByte(wiringPiI2CReadReg8(intDeviceHandle, CAL_DIG_H6))
The reason for this is that negative values are stored as 2s complement. So although "5" in 8 bit binary looks quite friendly:-
"-5" is a bit more intimidating:-
...which if saved straight into a 32 bit integer would = 251
Likewise for signed 16 bit values I convert to Short before saving as integers:-
CDigP2 = CShort(wiringPiI2CReadReg16(intDeviceHandle, CAL_DIG_P2))
There are also two 12bit humidity calibration values (H4 & H5) that use nibbles from the same byte. So I read these like this:-
msb = wiringPiI2CReadReg8(intDeviceHandle, CAL_DIG_H4)
lsb = wiringPiI2CReadReg8(intDeviceHandle, CAL_DIG_H4 + 1)
CDigH4 = Shl(msb, 4) Or (lsb And 15) 'mask 15 = 0x0F
msb = wiringPiI2CReadReg8(intDeviceHandle, CAL_DIG_H5)
lsb = wiringPiI2CReadReg8(intDeviceHandle, CAL_DIG_H4 + 1)
CDigH5 = Shl(msb, 4) Or Shr(lsb, 4)
Bit shifting (left and right) features heavily in this program.
If we left-shift a byte one position containing (say) decimal 4, the value is doubled, e.g.:-
Shl(4, 1) changes 00000100 to 00001000
If we right-shift a byte by one position, the value is divided by 2. But note that any bits which 'shift out' (left or right) are lost. So if we right shift a value of 5 by one position we should get 2.5 but lose the remainder:-
00000101 to 00000010
Here is an example from my program that is rather more complicated:-
intVar1 = Shr((Shr(lngRawTemp, 3) - Shl(CDigT1, 1)) * CDigT2, 11)
You will find the Gambas gui version of this code on GitHub. I went on to develop a command-line version which saves data in a CSV file, and is the program I am now using on my server. The CSV file is read by a PHP program so I can view temperature & humidity graphically via a web browser.
I have no serious way of evaluating the accuracy of humidity sensors. However, I can use my cheap portable monitor to make rough comparisons.
When running any single sensor alongside my monitor, all seem to agree on temperature within a span of less than 1'C, over the range 15-20'C. I can't say what happens outside this range.
For humidity readings, the BME280 is about 4% higher than my monitor, my 1st DHT22 about 2% higher than my monitor, and the 2nd DHT22 is about 14% higher. These figures were observed for RH readings around 70%.
Just to restate the fact that these readings are simply comparative, and not a true indication of either accuracy or precision. Please refer to Robert Smiths results as mention earlier.
Also note that my first DHT22 has now failed after less than a 10 months use. The failure mode is that it continuously reports 80% RH. I haven't had a chance to investigate further, but to be honest, its probably going in the bin.
I've lost confidence in the DHT22 in general and consider it to be worse-than-useless. Like many low-cost electrical components, it is a matter of luck whether you get your hands on a good unit. Production quality rarely lives up to design performance.