自作のEC計を作ろうと思ったら避けては通れないプログラムの作製(×_×;)
ただどのサイトを見ても具体的な作り方は書いていませんね。。何をどのようにすれば良いかお困りではありませんか( ಠωಠ)?
私もそんなひとりで、調べても見つからないので作ってみることにしました。プログラムも共有しますのでお困りでしたらご参考にしてください٩( ‘ω’ )و
プログラムを使うにはEC測定回路が必要!交流回路を使いましょう。
プログラムにさっそく取り掛かりたいですが、測定回路がないとプログラムを動かすことができません。これから使う予定の交流回路について簡単にご紹介しますね(๑•̀ㅂ•́)و✧
測定用の波形は交流!ウィーンブリッジ回路を使います。
前回の記事で作製した、交流波波形を作ることができるウィーンブリッジ回路でEC計測用の波形を作ります。
交流波形はこんな感じですね(〃’ω’)
交流波形を使うのは電気分解対策でした。
直流で作製される場合には確かにぶくぶく電気分解しちゃうのでお気をつけください(×_×;)
EC値の取得は2点のデータがとれればOKです。元の入力波形とEC抵抗の部分を計測します。
EC計は測りたい対象の抵抗を測るという単純なもののようです。ということは電流測定をしてその時の電圧を測ればよさそうですね(◍•ᴗ•◍)♡ ✧*。
まずは入力波形の電圧を測定します。交流波形なので変化するので常に測定する必要があるんです。
測定したい抵抗値の前で電圧を測れば流れる電流とそこから抵抗値を求めることができます。
実際の回路構成は?交流波形とADコンバーターを使うので少し複雑です。
回路は結構部品点数が多いです。詳細については前回の記事を参照してください。
ラベル OriginalWave / EcWaveがあるのがわかりますでしょうか?こちらのデータを取得すればEC抵抗値を測定できるはずです。
ただ、交流波形なので電圧がマイナスにスイングしてしまいます。ADコンバーターはマイナス電圧の処理ができないため、オフセットさせて測定する必要があります。
プラス側へシフトさせたのがラベルADC-Org / ADC-ECです。この値を処理しちゃいましょう。
ちなみにR8が電流測定用の抵抗。R9がEC測定する対象の抵抗です。本当に使うときにはここに電極を作ってその導通率を測ることになります。
ADCコンバーターは何を使う?2CHあるMCP3002を使います。
電流測定用に1ch、抵抗測定用にもう1ch使うので計2ch必要になります。
前回サーミスターの温度を取得するためにマイクロチップ社のMCP3002を使いました。
こちらは前回プログラムを作ってベースがあるのでこのICを使いたいと思います。
MCP3002を使う時の注意点!2ch同時に取得できません!
覚えておかなきゃいけないのは、MCP3002って2chのデータを一度に取れないんですよね。。
IC内部でCH1 / CH2取得モードと切り替えるので若干のタイムラグがあります。
交流波形を作るときには周波数を遅くしないと位相がずれたデータを取得することになる可能性があります。
2CHのアナログ波形を取得してEC値を求めるプログラムをご紹介します。
今回作製するプログラムは測定したい抵抗値を交流を用いて測ることができるようになっています。
交流を測ることになるのでなかなか再現性が悪いことがわかりました。
ですので、なるべく回数を多くとって平均化するとか、なるべく振幅が大きいところを使うように工夫しています。
メイン関数
メイン関数からEcMeasureのインスタンスを作ってMCP3002のSPIを設定したり、電流測定用の抵抗を設定して測定の準備をします。最後にEC値を取得して表示させています。
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 |
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "ecMeasure.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 EcMeasure ec = EcMeasure(); // initial setup before SPI communication start ec.SPISetUp(); ec.SetRefResistance(2200.0); printf("ch0 : %s %s %4.2f\n", strDateNow, strTimeNow, ec.GetEC()); return 0; } |
実際の測定を行うプログラムはこちら。
実際のEcMeasureクラスはこちらです。MCP3002のクラスを継承しています。
FindPeakメソッドで波形がどれくらいの振幅があるのかを確認しています。これを目安になるべく大きい振幅のデータを取得するようにしています。
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 |
#ifndef _ECMEASURE_H_ #define _ECMEASURE_H_ /* Sample program of ec measurement w/o thermal calibration from jitaku-yasai.com */ #include "mcp3002.hpp" #include "type.h" #define NUMSHOTS 10 class EcMeasure : public MCP3002 { public: void SetRefResistance(float refResistance); // Registance which devides voltage float GetEC(int numshots = NUMSHOTS); // GetEc ~EcMeasure(); // Destructor private: float ec; float resistance; float refResistance; float FindPeak(int ch = MCP3002_CHANNEL0, int numshots = NUMSHOTS); }; inline void EcMeasure::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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
/* Sample program of ec measurement w/o thermal calibration from jitaku-yasai.com */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <math.h> #include "ecMeasure.hpp" float EcMeasure::FindPeak(int ch, int numshots) { float peakAdcVal = 0.0; // input ch check. if (ch + 1 > MCP3002_CH_NUM) { printf("EcMeasure::FindPeak input ch is incorrect %d\n", ch); exit(-1); } for(int i=0; i<numshots; i++) { float measuredADVal = (float)WriteRead(ch) - MCP3002_RESOLUTION / 2; //printf("FindPeak : shot %d value = %4.2f\n", i, measuredADVal); if (measuredADVal > peakAdcVal) peakAdcVal = measuredADVal; } printf("Found Peak : value = %4.2f\n", peakAdcVal); return peakAdcVal; } float EcMeasure::GetEC(int numshots) { // Local variables float measuredADValCh0 = 0.0, measuredADValCh1 = 0.0; float peak; // Find peak to measure resitance in stable wave // Assume 90% of peak is enough to catch the top. peak = FindPeak(MCP3002_CHANNEL1, 100) * 0.9; for(int i=0; i<numshots; i++) { float ADValCh0 = 0.0, ADValCh1 = 0.0; while(peak > ADValCh1) { // CH0. Need to subtract offset for ADC input. // The offset point is set to the half resoluton of 10bit ADC ADValCh0 = (float)WriteRead(MCP3002_CHANNEL0) - MCP3002_RESOLUTION / 2; //printf("ADC CH0 value = %4.2f\n", measuredADValCh0); // CH1. Need to subtract offset for ADC input. ADValCh1 = (float)WriteRead(MCP3002_CHANNEL1) - MCP3002_RESOLUTION / 2; //printf("ADC CH1 value = %4.2f\n", measuredADValCh1); } measuredADValCh0 += ADValCh0 / (float)numshots; measuredADValCh1 += ADValCh1 / (float)numshots; } // Calculate current. // CH0 shows original sin voltage, // CH1 shows dropped voltage by resitance for current measument float curret = (measuredADValCh0 - measuredADValCh1) / refResistance; // Registance for the measument point return measuredADValCh1 / curret; } EcMeasure::~EcMeasure() { } |
MCP3002のクラスはこちらです。
WriteReadメソッドで取得したいチャネルを切り替えることができるようになっています。
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 */ #include "type.h" #define MCP3002_START_BIT 0b01000000 #define MCP3002_SGL_BIT 0b00100000 #define MCP3002_DIF_BIT 0b00000000 #define MCP3002_ODD_BIT 0b00000000 #define MCP3002_SIGH_BIT 0b00010000 #define MCP3002_MSBF_BIT 0b00001000 #define SPI_SPEED 500000 // SPI clock range : 500,000 through 32,000,000 #define MCP3002_CHANNEL0 0 #define MCP3002_CHANNEL1 1 #define MCP3002_CH_NUM 2 #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 uint theChannelOfRP = RP_SPI_CHANNEL0, uint theSpiSpeed = SPI_SPEED); virtual ~MCP3002(); // Destructor int SPISetUp(); // SPI setup int WriteRead(uint adcCH); // Send setting and recieve return value. private: unsigned int spiSpeed; // SPI communication speed unsigned char data[DATALEN]; // buffer to send/recieve for SPI communication unsigned 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 <stdlib.h> #include <unistd.h> #include <wiringPi.h> #include <wiringPiSPI.h> #include "mcp3002.hpp" MCP3002::MCP3002(uint theChannelOfRP, uint theSpiSpeed) { // Parameters channelOfRP = theChannelOfRP; spiSpeed = theSpiSpeed; } int MCP3002::SPISetUp() { return wiringPiSPISetup(channelOfRP, spiSpeed); } int MCP3002::WriteRead(uint adcCH) { // 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] |= (adcCH != MCP3002_CHANNEL0) ? MCP3002_SIGH_BIT : MCP3002_ODD_BIT; wiringPiSPIDataRW(channelOfRP, data, DATALEN); // Write and receive from same buffer return (int)((data[0] << 8 | data[1]) & 0x3FF); } MCP3002::~MCP3002() { } |
最後にmakefileですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
OBJS = ecMeasure.o mcp3002.o main.o TARGET = MeasureEc CC = g++ CFLAGS = -Wall -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) |
トライアル結果
何回か測定をしてみました。4.4kくらいの抵抗をつないだはずなのですが、10%程度誤差がある感じになりました。
再現性はある程度あるようなので、EC計としては使えそうです。
まとめと今後の予定
今回は交流波形を使って測定したい抵抗の抵抗値を取得することができました。自作のEC計の完成まであと少しといった感じです。
溶液の場合には電気伝導率が温度によって大きく変わるそうです。
ですので次回はサーミスターを組み入れて温度と抵抗値を取得できるようにしたいと思います。
そうするとMCP3002だとチャネルが足りないな。。MCP3004/3008とかもう少しチャネル数が多いものを準備する必要がありそうです。。
記事を読んでいただいてありがとうございます。この記事がいいなと思ったら下記のSNSボタンのクリックをお願いします。励みになります😁