連日のMARK-X赤道儀モータードライブ製造 ― 2021年09月23日 20時36分12秒
コピペ記事が技術をダメにする ― 2017年09月28日 06時25分30秒
Sensirion製温湿度センサーの湿度が100%になる? ― 2016年05月09日 07時34分56秒
現象
Weatherduinoという製品のサンプルライブラリをベースにセンシリオン製のSHT-11という温湿度センサーからのデータ取得を行っておりました。
ソースコードを何度確認してもデータシート通りの演算のように見えるのに、観測結果の湿度がいとも簡単に頻繁に100%に達してしまうのです。
Weatherduino用のライブラリはこちらから入手可能です。
このセンサーの入手はストロベリーリナックスが比較的安くて良いでしょう。
Weatherduinoのサンプルプログラムのバグ
さて、本題です。 上記の菅工房のサンプルライブラリG7Weather3.cppのSHT-11の処理では以下の演算部分があります。
// SHT11 温度(℃)・湿度(%)取得 void G7Weather::get_sht11 (float *temp, float *humi) { unsigned short sot, sorh; float t, rhl, rht; // 温度読込み sht_tsseq(); sht_write(0x03); SHT_SDA_H; while (SHT_SDA); // 変換終了待機 sot = (unsigned short)sht_read(1) << 8; sot |= sht_read(1); sht_read(0); // 湿度読込み sht_tsseq(); sht_write(0x05); SHT_SDA_H; while (SHT_SDA); // 変換終了待機 sorh = (unsigned short)sht_read(1) << 8; sorh |= sht_read(1); sht_read(0); // 温度補正 14bit (3.3V) sot &= 0x3fff; t = -39.66 + 0.01 * (float)sot; *temp = t; // 湿度補正 12bit sorh &= 0x0fff; // rhl = -4.0 + 0.0405 * sorh + (-2.8 * 0.000001) * (sorh * sorh); // Ver.3 rhl = -2.0468 + 0.0367 * (float)sorh + (-1.5955 / 1000000.0) * (float)(sorh * sorh); // Ver.4 rht = (t - 25.0) * (0.01 + 0.00008 * (float)sorh) + rhl; if (rht > 99.0) rht = 100.0; *humi = rht; }
この処理の中でのバグは以下です。
(float)(sorh * sorh)
この部分だけですね。問題があるのは。 sorhという変数はunsigned shortで有効なデータは12ビット分です。単独で使う場合は16ビットで十分ですが2乗すると演算結果は16ビットでは足りなくなります。
バグ修正
なので、以下のように修正の必要があります。
(float)((long)sorh * (long)sorh)
観測データは12ビットなので最大値は4095となります。 それを2乗すると16769025となり16ビットの最大値である65535を 超えてしまっていたわけです。
一方、符号付のlongの正の最大値は2147483647ですので十分に格納可能になります。 この修正により、湿度が簡単に100%になる問題は解消しました。 原因解明に3年かかりました。
ちなみに、写真の基板はWeatherduinoではなく自作品です。 ArduinoUnoとの組み合わせで使っています。
最近のコメント