みなさんは温度管理をどうやっていますか?
できることなら自作してIoTを自宅で自由にやりたいですよね。
今回はIoTが簡単にできるラズパイで温度のデータを取得して見たいと思います(◍•ᴗ•◍)♡ ✧*。
前回の記事では温度センサーを使いましたが、今回はラズパイでサーミスタからのアナログ値を読み取ってみたいと思います。
ラズパイにはADコンバーターがないのでADコンバーターの導入方法とサーミスタの活用をプログラムつきで詳しくご紹介します(ง°̀ロ°́)ง
温度管理はなんで重要?
このサイトでは自宅での水耕栽培方法をご紹介していますが、水の温度って植物の生育にかなり影響するのでちゃんと管理する必要があるんです( ಠωಠ)
季節によって温度が大きく変わります。
当たり前ですが、温度は季節によって変わります。真夏と真冬は野菜によってはもとも厳しいシーズンになります。。
夏の場合:酸素が不足しちゃいます。
夏は気温が嫌になるくらい上がりますが、温度が上がると水に溶け込んでいる酸素の量”溶存酸素濃度”がどんどん低下していきます(×_×;)
冬の場合:寒すぎて野菜には厳しいかも。
野菜には適温があります。種の袋にかいてありますね。
真冬は気温が低すぎて野菜によってはうまく育ちません。
ラズパイのIoTで解決しよう。
ラズパイが温度を感知できれば、対応策は色々できます。
気温が高かったらエアーポンプを稼働して酸素を供給しましょう。
楽天へのリンク : GEX メタルオートヒーター SH160 【熱帯魚・アクアリウム/保温器具/ヒーター・サーモスタット一体型】
ラズパイにはGPIOがたくさんあるので、ACコンセントを自分で作れば制御できちゃいます。
ということで後は温度を測れればIoTを実現できます(๑•̀ㅂ•́)و✧
方法をご紹介しますね(〃’ω’)
ラズパイの温度測定のために準備するもの。
I2Cで通信できる便利な温度センサーもありますが、さすがに水温を測るために水没させることもできないのでサーミスタを使おうと思います(〃’ω’)
ということで使うのはADコンバーターとサーミスタです。
高機能なラズパイにADコンバーターがないのは残念ですね(×_×;)
- ADコンバーター : 秋月電子へのリンク MCP3002-I/P
- サーミスター : 秋月電子へのリンク サーミスター
- 10kオームの抵抗
今回使うADコンバーターとサーミスタについて簡単にご紹介します(◍•ᴗ•◍)♡ ✧*。
ADコンバーターMCP3002はどんなもの?
- 10bitの分解能を持つADコンバーターです。
- 2CHついています。
- データのやり取りはSPI通信で行います。
- 価格はひとつ180円と安いです。
ちなみにこちらにスペックシートがあります。
サーミスターってどんなもの?
サーミスタは温度が上がると抵抗値が指数関数的に変わる素子です。
秋月電子のサイトに変換式が記載されていて、下記のような式になるようです。
今回使うサーミスタは
25度での抵抗値 : 10kOhm +/1 %
B定数(25~85度) : 3435K +/-1 %
とのことです。
グラフにしてみました。
サーミスタの抵抗値から温度を求めるには?
サーミスタの抵抗と温度の変換式を変換して温度を求められる式にします。
まずは温度の部分からeを外して、
最後に左辺に温度をおけば完成ですかね。
今回使うサーミスタは?
今回のサーミスタは水による腐食が防げそうなので選びました。
水温を測るので重要です(๑•̀ㅂ•́)و✧
みなさんも用途に応じて選んでくださいね。
回路の準備
それでは回路を組んで見ます。
回路構成はこんな感じです。
ブレッドボードにこんな風に差し込みました。
CQ出版のサイトを見ているとローパスフィルターを入れているようなので真似して見ました。
4.7kOhmと100uFのキャパシタで作ったのでカットオフ周波数は0.3Hzくらいになっています。
DC値を見るのでこれくらいあれば十分そうですね。
プログラムの準備
まずはラズパイでSPI通信をできるようにします。そのあとプログラムを好きなところに置いてmakeしましょう。
まずはSPIの設定をしましょう。
このICを使うためにはラズパイでSPI通信ができるように設定する必要があります。
私の持っているRaspberry pi zero Wでの設定はラズパイでSPI通信を有効にするにはどうすればいい?でまとめていますので参考にしてください。
ADコンバーターMCP3002のクラスを準備
まずはADコンバーターのクラスを作ります。ADコンバーターを今後も汎用的に使いたいのでこのようにしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#ifndef _MCP30002_H_ #define _MCP30002_H_ /* Sample program of MCP3002 from jitaku-yasai.com MCP3002 specification : http://ww1.microchip.com/downloads/en/DeviceDoc/21294E.pdf */ #define MCP3002_START_BIT 0b01000000 #define MCP3002_SGL_BIT 0b00000000 #define MCP3002_DIF_BIT 0b00100000 #define MCP3002_ODD_BIT 0b00000000 #define MCP3002_SIGH_BIT 0b00010000 #define MCP3002_MSBF_BIT 0b00001000 #define MCP3002_SPEED 500000 // SPI clock range : 500,000 through 32,000,000 #define MCP3002_CHANNEL0 0 #define RP_SPI_CHANNEL0 0 // Raspberry Pi has two SPI channel. #define DATALEN 2 #define MCP3002_RESOLUTION 1024 class MCP3002 { public: MCP3002( // Constructor with default value int theChannelOfRP = RP_SPI_CHANNEL0, int theMCP3002lChannel = MCP3002_CHANNEL0, int theMCP3002Speed = MCP3002_SPEED); virtual ~MCP3002(); // Destructor int SPISetUp(); // SPI setup int WriteRead(); // Send setting and recieve return value. private: struct MCP3002Parameters{ int channel; // A/D converter channel 0/1 int speed; // SPI speed } MCP3002Param ; // Struct for A/D channel and SPI speed with RP unsigned char data[DATALEN]; // buffer to send/recieve for SPI communication int channelOfRP; // channel for RP communication }; #endif |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
/* Sample program of MCP3002 from jitaku-yasai.com MCP3002 specification : http://ww1.microchip.com/downloads/en/DeviceDoc/21294E.pdf */ #include <stdio.h> #include <unistd.h> #include <wiringPi.h> #include <wiringPiSPI.h> #include "mcp3002.hpp" MCP3002::MCP3002(int theChannelOfRP, int theMCP3002lChannel, int theMCP3002Speed) { // Parameters channelOfRP = theChannelOfRP; MCP3002Param.channel = theMCP3002lChannel; MCP3002Param.speed = theMCP3002Speed; } int MCP3002::SPISetUp() { return wiringPiSPISetup(channelOfRP, MCP3002Param.speed); } int MCP3002::WriteRead() { // set buffer for inquiry data[0] = MCP3002_START_BIT | MCP3002_SGL_BIT | MCP3002_MSBF_BIT | MCP3002_MSBF_BIT; // if host requests to use other than channel #0 of MCP3002, set to use channel #1 data[0] |= (MCP3002Param.channel != MCP3002_CHANNEL0) ? MCP3002_SIGH_BIT : 0; wiringPiSPIDataRW(channelOfRP, data, DATALEN); // Write and receive from same buffer return (int)((data[0] << 8 | data[1]) & 0x3FF); } MCP3002::~MCP3002() { } |
サーミスタのクラスを準備
あとは今回使うサーミスタをMCP3002のクラスを使って作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#ifndef _THERMISTOR_H_ #define _THERMISTOR_H_ /* Sample program of Thermistor from jitaku-yasai.com Thermistor specification : http://akizukidenshi.com/catalog/g/gP-07257/ */ #include "mcp3002.hpp" #define CONST_B 3435 #define CONST_T 25 #define CONST_R 10000 #define ABSTEMP 273 #define NUMSHOTS 10 class Thermistor : public MCP3002 { public: void SetRefResistance(float refResistance); // Registance which devides voltage float GetTemperature(int numshots = NUMSHOTS); // GetTemperature ~Thermistor(); // Destructor private: float temperature; float resistance; float refResistance; }; inline void Thermistor::SetRefResistance(float theRefResistance) { refResistance = theRefResistance; } #endif |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
/* Sample program of Thermistor from jitaku-yasai.com Thermistor specification : http://akizukidenshi.com/catalog/g/gP-07257/ */ #include <stdio.h> #include <unistd.h> #include <math.h> #include <wiringPi.h> #include <wiringPiSPI.h> #include "thermistor.hpp" float Thermistor::GetTemperature(int numshots) { // From ADC value, culculate thermistar resistance. float measuredADVal = 0.0; for (int i = 0; i < numshots; i++) { measuredADVal += (float)WriteRead(); } measuredADVal /= (float)numshots; printf("ADC ave value = %4.2f\n", measuredADVal); resistance = (MCP3002_RESOLUTION - measuredADVal) / measuredADVal * CONST_R; printf("resistance = %4.2f\n", resistance); // Intermediate value derived from resistance float calcFromResistance = log(resistance / refResistance) / (CONST_B); // Intermediate value from reference temperature of using thermistor float calcFromConstTemp = 1.0 / (CONST_T + ABSTEMP); // Calculate temperature from Intermediate values temperature = 1.0 / (calcFromResistance + calcFromConstTemp) - ABSTEMP; return temperature; } Thermistor::~Thermistor() { } |
メイン関数の準備
クラスの準備ができたのでMain関数を定義します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "thermistor.hpp" int main(int argc, char *argv[]) { // current time time_t now = time(NULL); struct tm *pnow = localtime(&now); char strDateNow[16]; char strTimeNow[16]; sprintf(strDateNow, "%04d-%02d-%02d", pnow->tm_year+1900, pnow->tm_mon+1, pnow->tm_mday); sprintf(strTimeNow, "%02d:%02d:%02d", pnow->tm_hour, pnow->tm_min, pnow->tm_sec); // initialization of classes and operation Thermistor thermistor = Thermistor(); // initial setup before SPI communication start thermistor.SPISetUp(); thermistor.SetRefResistance(10000.0); printf("%s %s %4.2f\n", strDateNow, strTimeNow, thermistor.GetTemperature()); return 0; } |
最後にmakefile
最後にmakefileですね。SQLのライブラリは不要ですけどどうせあとで使うのでこのままにしておきます。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
OBJS = thermistor.o mcp3002.o main.o TARGET = thermistorAtWater CC = g++ CFLAGS = -Wall -lmysqlclient -L/usr/lib64/mysql -lwiringPi .SUFFIXES: .cpp .c .o $(TARGET): $(OBJS) $(CC) $(OBJS) -o $(TARGET) $(CFLAGS) .c.o: $< $(CC) -c $(CFLAGS) $< .cpp.o: $< $(CC) -c $(CFLAGS) $< clean: rm -f $(OBJS) $(TARGET) |
1 |
作った回路とプログラムを検証してみました。
サーミスタを電気ポットで沸かしたお湯に浸してみました。
実行画面で80度くらい出たのでうまくいっているようです。
まとめ
ラズパイでサーミスタを使った温度測定の方法を細かくご紹介しました。みなさんも自分でカスタマイズできる温度計作製にチャレンジしてください(◍•ᴗ•◍)♡ ✧*。
記事を読んでいただいてありがとうございます。この記事がいいなと思ったら下記のSNSボタンのクリックをお願いします。励みになります😁
ラズパイを使って、多点式温度計ロガーを作りたいので、貴ブログなどを勉強中のものです。よく原理等が分かっていないので、とんでもない間違いをしている事に躊躇せず、質問させて下さい。サーミスタからの抵抗値(アナログ信号)をラズパイに送って、そこで抵抗値ー温度換算をおこなうことはできないのでしょうか? DA変換をして、デジタル値から温度を求めるのは無駄?な感じがするのですが。
モンターニュさん
こんにちは。
ラズパイにはADコンバータがないのでMC3004などのICを使っています。
抵抗値はアナログですね。
PICであればADコンバータ付きがあるようです。
私は多チャネルが必要だったのでどっちにしてもICが必要でした。。