リチウムコイン電池で動くワイヤレス温湿度気圧ロガーを作ってみた-1-
秋月で温度・湿度・気圧をまとめて測定できるBME280というセンサーを買ったので
リチウムコイン電池で動くワイヤレスな装置を作ってみた。
(左:センサー・送信 右:受信)
主な部品リスト
名前 | 価格 |
---|---|
MSP430G2553 | 210円 |
BME280モジュール | 1080円 |
315MHz無線送信モジュール | 2160円 |
315MHz無線受信モジュール | 2160円 |
32.768kHz 水晶発振子 | 30円 |
ボタン電池基板取付用ホルダー CR2032用 | 50円 |
1.27mmピッチユニバーサル基板 | 100円 |
47kΩ | |
1.5kΩ | |
LED |
リチウムコイン電池でマイコンを動かすのは初めてなので色々試行錯誤してやってみたい。
マイコンはTiの超低消費電力マイコンMSP430をチョイス。使った事のないマイコンなのでMSP430 Value Line LaunchPad Development Toolと追加でMSP430G2553を購入。
ユニバーサル基板はハーフピッチなのはIM315のコネクタが1.27mmピッチなのでそれに合わせた。
回路概要
今回はUEWを使って少しずつ動作確認しながら作成したので回路図は書かなかった。
裏側はこんなかんじ↓
なので、ここでは接続の概要だけ記載しておく。
送信側
BM280はSPI4線でMSP430のUSCI_B0に接続、CSはP1.3を使った。
IM315TXとはUART(P1.2、P1.3)で接続。(送信頻度が少ないのでBusyは配線してない)
32.768kHz 水晶発振子はLPM3からの復帰用にXIN、XOUTに接続。
受信側
受信側はPCでロギングする為にIM315RXとFT232xを接続した。
MSP430プログラム
ソフトはmain.cとUARTでの文字送信用関数群のsimple_uart.hとBM280を制御するbm280.hに分かれている。
BM280の制御プログラムは以下ページのコードを使わせて頂きました。
www.mgo-tec.com
main.c
#include <msp430.h> #include <stdint.h> #include <simple_uart.h> #include <bme280.h> /* * main.c */ int main(void) { //WDTCTL = WDTPW + WDTHOLD; // Stop WDT WDTCTL = WDT_ADLY_1000; IE1 |= WDTIE; //クロック設定 DCOCTL = 0; BCSCTL1 = CALBC1_1MHZ; DCOCTL = CALDCO_1MHZ; //P1.5 SCLK P1SEL |= BIT5; P1SEL2 |= BIT5; //P1.6 SOMI P1SEL |= BIT6; P1SEL2 |= BIT6; //P1.7 SIMO P1SEL |= BIT7; P1SEL2 |= BIT7; //P1.3 SS P1DIR |= BIT3; P1OUT |= BIT3; //P1.1 RXD P1SEL |= BIT1; P1SEL2 |= BIT1; //P1.2 TXD P1SEL |= BIT2 ; P1SEL2 |= BIT2; //UART //enable software reset UCA0CTL1 = UCSWRST; //UART設定 UCA0CTL1 |= UCSSEL_2; // SMCLK UCA0BR0 = 104; // 1MHz 9600 UCA0BR1 = 0; // 1MHz 9600 UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1 //clear software reset UCA0CTL1 &= ~UCSWRST; //SPI //enable software reset UCB0CTL1 = UCSWRST; //SPI設定 UCB0CTL0 = UCCKPL|UCMODE0|UCMSB|UCMST|UCSYNC; //SCK設定 UCB0CTL1 = UCSSEL1; UCB0BR0 = 1; UCB0BR1 = 0; //clear software reset UCB0CTL1 &= ~UCSWRST; //BME280 uint8_t t_sb = 5; //stanby 1000ms uint8_t filter = 0; //filter O = off uint8_t spi3or4 = 0; //SPI 3wire or 4wire, 0=4wire, 1=3wire uint8_t osrs_t = 4; //OverSampling Temperature x4 uint8_t osrs_p = 4; //OverSampling Pressure x4 uint8_t osrs_h = 4; //OverSampling Humidity x4 uint8_t Mode = 3; //Normal mode uint8_t ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | Mode; uint8_t config_reg = (t_sb << 5) | (filter << 2) | spi3or4; uint8_t ctrl_hum_reg = osrs_h; writeReg(0xF2,ctrl_hum_reg); writeReg(0xF4,ctrl_meas_reg); writeReg(0xF5,config_reg); readTrim(); while (1) { __bis_SR_register(LPM3_bits+GIE); //LPM3に移行 volatile double temp_act = 0.0, press_act = 0.0, hum_act = 0.0, altitude_act = 0.0; int32_t temp_cal; uint32_t press_cal,hum_cal; readData(); temp_cal = calibration_T(temp_raw); press_cal = calibration_P(pres_raw); hum_cal = calibration_H(hum_raw); //temp_act = (double)temp_cal / 100.0; //press_act = (double)press_cal / 100.0; //hum_act = (double)hum_cal / 1024.0; //IM315TXへ送信 uart_puts("TXDT "); uart_putdec(temp_cal); uart_putdec(press_cal); uart_putdec(hum_cal); uart_puts("\r\n"); //_delay_cycles(1000000); // 1sec } } #pragma vector=WDT_VECTOR __interrupt void watchdog_timer (void) { __bic_SR_register_on_exit(LPM3_bits); // LPMを解除 } // スタートアップルーチンから呼び出される初期化処理 void __low_level_init(void) { WDTCTL = WDT_ADLY_1000; // WDTのクロック・ソースをACLK、 // 1sのインターバルタイマに設定 }
simple_uart.h
/* * simple_uart.h * */ #ifndef SIMPLE_UART_H_ #define SIMPLE_UART_H_ #include <stdint.h> void uart_putc(char c) { while (!(IFG2&UCA0TXIFG)); UCA0TXBUF = c; } int uart_puts(const char *s) { int n; for (n = 0; *s; s++, n++) { uart_putc(*s); } return n; } void uart_putdec(uint32_t value) { int i; // General purpose counter. uint32_t m; // Mask for decimal number parser. char nbuf[12]; // Buffer to store the decimal number. // Maximum power of 10 number in 32-bit. m = 1000000000; // Find the first masking number. while ((m > 1) && (value < m)) { m /= 10; } // Parse the value into a decimal number. i = 0; while (m >= 1) { nbuf[i++] = '0' + (value / m); value %= m; m /= 10; } nbuf[i++] = 0; // End of string. uart_puts(nbuf); // Put the decimal number. } #endif /* SIMPLE_UART_H_ */
bme280.h
/* * bme280.h * */ #ifndef BME280_H_ #define BME280_H_ #include <stdint.h> uint16_t read16bit(uint8_t reg); uint8_t read8bit(uint8_t reg); void SpiWrite(uint8_t data); uint8_t SpiRead(); uint32_t hum_raw, temp_raw, pres_raw; int32_t t_fine; uint16_t dig_T1; int16_t dig_T2; int16_t dig_T3; uint16_t dig_P1; int16_t dig_P2; int16_t dig_P3; int16_t dig_P4; int16_t dig_P5; int16_t dig_P6; int16_t dig_P7; int16_t dig_P8; int16_t dig_P9; uint8_t dig_H1; int16_t dig_H2; uint8_t dig_H3; int16_t dig_H4; int16_t dig_H5; int8_t dig_H6; double SeaLevelPressure_hPa = 1013.25; //標準は1013.25 //*****************初期値読み込み************************************** void readTrim(void) { dig_T1 = read16bit(0x88); dig_T2 = (int16_t)read16bit(0x8A); dig_T3 = (int16_t)read16bit(0x8C); dig_P1 = read16bit(0x8E); dig_P2 = (int16_t)read16bit(0x90); dig_P3 = (int16_t)read16bit(0x92); dig_P4 = (int16_t)read16bit(0x94); dig_P5 = (int16_t)read16bit(0x96); dig_P6 = (int16_t)read16bit(0x98); dig_P7 = (int16_t)read16bit(0x9A); dig_P8 = (int16_t)read16bit(0x9C); dig_P9 = (int16_t)read16bit(0x9E); dig_H1 = read8bit(0xA1); dig_H2 = (int16_t)read16bit(0xE1); dig_H3 = read8bit(0xE3); dig_H4 = (int16_t)((read8bit(0xE4) << 4) | (read8bit(0xE5) & 0x0F)); dig_H5 = (int16_t)((read8bit(0xE6) << 4) | (read8bit(0xE5) >> 4)); dig_H6 = (int8_t)read8bit(0xE7); } //***************BME280へ初期レジスタ書き込み関数**************************** void writeReg(uint8_t reg_address, uint8_t data) { P1OUT &= ~BIT3; SpiWrite(reg_address & 0x7F); // write, bit 7 low SpiWrite(data); P1OUT |= BIT3; } //***************BME280からの温度、湿度、気圧データ読み込み関数**************************** void readData() { uint32_t data[8]; uint8_t i; P1OUT &= ~BIT3; SpiWrite(0xF7 | 0x80); //0xF7 pressure msb read, bit 7 high for(i=0; i<8; i++){ data[i] = SpiRead(); } P1OUT |= BIT3; pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4); //0xF7, msb+lsb+xlsb=19bit temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4); //0xFA, msb+lsb+xlsb=19bit hum_raw = (data[6] << 8) | data[7]; //0xFD, msb+lsb=19bit(16:0) } //***************温度キャリブレーション関数**************************** int32_t calibration_T(int32_t adc_T) { int32_t var1, var2, T; var1 = ((((adc_T >> 3) - ((int32_t)dig_T1<<1))) * ((int32_t)dig_T2)) >> 11; var2 = (((((adc_T >> 4) - ((int32_t)dig_T1)) * ((adc_T>>4) - ((int32_t)dig_T1))) >> 12) * ((int32_t)dig_T3)) >> 14; t_fine = var1 + var2; T = (t_fine * 5 + 128) >> 8; return T; } //***************気圧キャリブレーション関数**************************** uint32_t calibration_P(int32_t adc_P) { int32_t var1, var2; uint32_t P; var1 = (((int32_t)t_fine)>>1) - (int32_t)64000; var2 = (((var1>>2) * (var1>>2)) >> 11) * ((int32_t)dig_P6); var2 = var2 + ((var1*((int32_t)dig_P5))<<1); var2 = (var2>>2)+(((int32_t)dig_P4)<<16); var1 = (((dig_P3 * (((var1>>2)*(var1>>2)) >> 13)) >>3) + ((((int32_t)dig_P2) * var1)>>1))>>18; var1 = ((((32768+var1))*((int32_t)dig_P1))>>15); if (var1 == 0) { return 0; } P = (((uint32_t)(((int32_t)1048576)-adc_P)-(var2>>12)))*3125; if(P<0x80000000) { P = (P << 1) / ((uint32_t) var1); }else{ P = (P / (uint32_t)var1) * 2; } var1 = (((int32_t)dig_P9) * ((int32_t)(((P>>3) * (P>>3))>>13)))>>12; var2 = (((int32_t)(P>>2)) * ((int32_t)dig_P8))>>13; P = (uint32_t)((int32_t)P + ((var1 + var2 + dig_P7) >> 4)); return P; } //***************湿度キャリブレーション関数**************************** uint32_t calibration_H(int32_t adc_H) { int32_t v_x1; v_x1 = (t_fine - ((int32_t)76800)); v_x1 = (((((adc_H << 14) -(((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * v_x1)) + ((int32_t)16384)) >> 15) * (((((((v_x1 * ((int32_t)dig_H6)) >> 10) * (((v_x1 * ((int32_t)dig_H3)) >> 11) + ((int32_t) 32768))) >> 10) + ((int32_t)2097152)) * ((int32_t) dig_H2) + 8192) >> 14)); v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4)); v_x1 = (v_x1 < 0 ? 0 : v_x1); v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1); return (uint32_t)(v_x1 >> 12); } //***************標高計算関数**************************************************** //double ReadAltitude(double SeaLevel_Pres, double pressure) { // double altitude = 44330.0 * (1.0 - pow(pressure / SeaLevel_Pres, (1.0/5.255))); // return altitude; //} //***************BME280から16bitデータ読み込み関数**************************** uint16_t read16bit(uint8_t reg) { uint16_t d1, d2; uint16_t data; P1OUT &= ~BIT3; SpiWrite(reg | 0x80); // read, bit 7 high d1 = SpiRead(); d2 = SpiRead(); data = (d2 << 8) | d1; P1OUT |= BIT3; return data; } //***************BME280から8bitデータ読み込み関数**************************** uint8_t read8bit(uint8_t reg) { uint8_t data; P1OUT &= ~BIT3; SpiWrite(reg | 0x80); // read, bit 7 high data = SpiRead(); P1OUT |= BIT3; return data; } //***************BME280へSPI信号データ送信関数**************************** void SpiWrite(uint8_t data) { volatile char hoge; while (!(IFG2 & UCB0TXIFG)); UCB0TXBUF = data; while (!(IFG2 & UCB0RXIFG)); hoge = UCB0RXBUF; } //***************BME280からのSPI信号データ読み込み関数**************************** uint8_t SpiRead() { uint8_t r_data = 0; UCB0TXBUF = 0x00; while (!(IFG2 & UCB0RXIFG)); r_data = UCB0RXBUF; return r_data; } #endif /* BME280_H_ */
電圧・電流測定
作成後、電圧をどこまで下げても動作するかとその際の電流値を測定した。
下の様に安定化電源で電源供給してDMMで電流測定している。
電流値は送信中とスリープ中で全く異なる値になるのでしばらく動かしてMaxを確認する。
電圧 | Imax | Imin |
---|---|---|
3.3V | 957uA | 541uA |
3.0V | 920uA | 531uA |
2.5V | 865uA | 516uA |
2.0V | 820uA | 502uA |
1.8V | 799uA | 496uA |
1.8Vまでは起動・送信が問題なかったが1.8Vを下回ると不安定になった。
これで動作確認もできたので次回は長時間のロギングをやってみる。
参考にしたサイト
BME280 – スイッチサイエンス
MSP430 Tutorial and Resources · Argenox Technologies
MSP430班(2006/05/02) ウォッチドッグタイマ2