温湿度計を作ってみた
mbedと温湿度モジュールを使って温湿度計を作ってみました。
温湿度モジュールにはマルツで販売されているHSM20Gを使用しました。はじめは秋月の温度センサ・湿度センサが別々で売ってるのを使おうと思っていましたが、この湿度センサ、ちょっと高い割に発振回路を用意しないといけないので面倒だなと思ってました。他にないかなと調べてみたところマルツで温湿度センサが一体で載ったモジュールがあることを知り、発振回路も必要ないのでこっちを使うことにしました。ちなみに1つ800円。
上の写真の右下に見える緑色のがセンサモジュール。ブレッドボードにはデータシートで推奨されている回路をそのまま作りました。抵抗と電解コンデンサだけです。
このHSM20Gというセンサ、温度は抵抗出力、湿度は電圧出力となっています。
AD変換で読み取る値から実際の温度・湿度を求めるための式をデータシートの値から求めました。
温度・湿度に対して線形な変化ではないため、近似は4次近似、またmbedはUSB給電で動作させていると電源電圧が5Vではなくなる(実測4.8V)ため、電源電圧が5Vの場合と4.8Vの場合で求めています。プログラム中では、
HSM20G hsm(USB);
と宣言することで、useUSBpowerがtrueになり、値を返す部分で分岐が起こります。
if(useUSBpower == true) { // case : Vdd = 4.8V(USB power) return((float)(-465.59*inData*inData*inData*inData + 920.47*inData*inData*inData - 683.27*inData*inData + 302.69*inData - 20.933)); } else { // case : Vdd = 5V return((float)(-395.45*inData*inData*inData*inData + 814.37*inData*inData*inData - 629.7*inData*inData + 290.58*inData - 20.933)); }
excelでこんな感じで求めました。
手元のCASIOの電波時計に気温が表示されるので表示される値と比較してみたところ±0.7℃くらいの精度でした。センサ自体に±5%の誤差がある上、近似式を使っているのでその誤差も入っているため、精度はまぁこんなもんなんじゃないかなと思ってます。湿度は手元に湿度計がないため比較できてません。
プログラムはこんな感じになりました。今後も使い回せるように慣れないけどC++でクラス化して分かりやすくしてみたり。
更新は1秒間隔、過去10回の値を平均して表示しています。
#include "mbed.h" #include "TextLCD.h" #define USB true // USB power #define ACA false // AC adapter // variable TextLCD lcd(p24, p26, p27, p28, p29, p30); AnalogIn humiIn(p15); AnalogIn tempIn(p16); DigitalOut led1(LED1); // class class HSM20G { private: float tempADPrevData[10]; float humiADPrevData[10]; int tempCount, humiCount; bool useUSBpower; public: // default constructor HSM20G(void) { tempCount = 0; humiCount = 0; useUSBpower = false; // default 5V power } // USB or ACA constructor HSM20G(bool inKind) { tempCount = 0; humiCount = 0; useUSBpower = inKind; } // init void init(void) { float tempData, humiData; int i; tempData = tempIn.read(); humiData = humiIn.read(); for(i = 0; i < 10; i++) { tempADPrevData[i] = tempData; } for(i = 0; i < 10; i++) { humiADPrevData[i] = humiData; } } // tempGet float tempGet(void) { double inData; if(tempCount >= 10) { tempCount = 0; } tempADPrevData[tempCount++] = tempIn.read(); inData = 0; for(int i = 0; i < 10; i++) { inData += (double)tempADPrevData[i]; } inData /= 10; if(useUSBpower == true) { // case : Vdd = 4.8V(USB power) return((float)(-465.59*inData*inData*inData*inData + 920.47*inData*inData*inData - 683.27*inData*inData + 302.69*inData - 20.933)); } else { // case : Vdd = 5V return((float)(-395.45*inData*inData*inData*inData + 814.37*inData*inData*inData - 629.7*inData*inData + 290.58*inData - 20.933)); } } // humiGet int humiGet(void) { double inData; if(humiCount >= 10) { humiCount = 0; } humiADPrevData[humiCount++] = humiIn.read(); inData = 0; for(int i = 0; i < 10; i++) { inData += (double)humiADPrevData[i]; } inData /= 10; return((int)(-65.774*inData*inData*inData*inData + 290.48*inData*inData*inData -356.45*inData*inData + 258.87*inData - 32.645)); } }; // main program int main() { HSM20G hsm(USB); hsm.init(); while(1) { // print lcd.cls(); // キオン :xx.x℃ // シツド:xx% lcd.printf("%c%c%c :%.1f%cC\n%c%c%c%c:%d%%", 0xB7, 0xB5, 0xDD, hsm.tempGet(), 0xDF, 0xBC, 0xC2, 0xC4, 0xDE, hsm.humiGet()); // LED1 flash led1 = 1; wait(0.01); led1 = 0; wait(1); } }
これまでのPICでの実装に比べるとやりたいことを実現するまでの時間と手間がものすごく減りました。まさにmbedの思惑通りという感じな気がします。
でもPIC+アセンブリ言語で1つ1つ命令を書いていくあの感覚もやっぱり好きです。
(12/1)なんか表示がおかしいなと思っていたらバグにより正常な値が計算されない問題があったため一部修正しました。
原因:tempADPrevData[10], humiADPrevData[10]の配列にアクセスするカウンタ部分に共通のint countという変数を使用していたため、温度と湿度を毎回ペアで読み取ってインクリメントするこのプログラムでは、次の代入時にそれぞれの配列の1つ飛びの要素に代入されるようになっていました。温度、湿度用に別の変数を用意することで解決しました。初歩的なミス。