/*  気圧センサーLPS25Hを使ったアナログ気圧計 _20180429LPS25Hbarometer ・アナログ時計のムーブメントで表示。スケールは、1hPa=50秒。 ・電池電圧低下で自動停止。指針は表示範囲外(1043hPa付近)に移動。 ・単三電池3本で3年以上の連続動作(予定) 2018/04/29: delayWDTをdelayWDT2に入替え(待機時消費電流34→14μA) ラジオペンチ http://radiopench.blog96.fc2.com/ */ #include // I2Cドライバ #include // delayWDTで使用 #include // dalayWDTで使用 #define divR 5.795 // 電池電圧測定の分圧比(divider Ratio デフォルト=5.7) #define pOffset 11.8 // 海面気圧への補正値(hPa)、設置場所で要調整 #define voltageLimit 3.4 // バッテリー電圧低下検出閾値 3.4V #define lowBatPosi 1043.0 // バッテリーアラーム表示位置 1043.0hPa // 時計のパルスモーター駆動波形パラメーター unsigned long cwPulse = 31000UL; // 順回転パルス幅(us) 標準は31000 unsigned long cwInterval = 25000UL; // 順回転パルス間隔(us) 限界は 25000 unsigned long ccwPulse1 = 5500UL; // 逆回転パルス幅(us) 5500 unsigned long ccwWait = 2000UL; // 反転待ち時間(us) 2000 unsigned long ccwPulse2 = 30000UL; // 逆回転パルス幅(us) 標準は30000 unsigned long ccwInterval = 60000UL; // 逆回転パルス間隔(us) 限界は60000 int sleep_n = 75; // スリープ回数 int sleep_t = 9; // スリープ時間指定 9=8秒 (75*8=600秒=10分) int dedZone = 5; // 時計ドライブの不感帯(このパルス以上なら動かす) boolean flag = true; // パルスモーター駆動極性フラグ float hPa, volt; // 気圧、電圧変数 long p24; // 24ビット形式の気圧測定結果デ−タ int LPS25H_adr = 0x5D; // I2C address. SDO=HIGHで0x5D (0x5Cでは消費電流が5μA増える) unsigned int clkPosi, clkPosiLast; // 針の位置 (1hPa=50pulse, max=65k=1310hPa) void setup() { pinMode(7, INPUT_PULLUP); // 動作モード指定スイッチ pinMode(8, INPUT_PULLUP); // 動作モード指定スイッチ pinMode(9, OUTPUT); // conect coil thrugh 220Ω pinMode(10, OUTPUT); // coil return pinMode(13, OUTPUT); // 動作表示LED analogReference(INTERNAL); // ADCを内部refで使う FS=1.1V Wire.begin(); Serial.begin(115200); Serial.println(); // 空送り if (digitalRead(7) == LOW) { // もしPin7がGNDなら for (;;) { // 無限ループで clockDemo(); // 時計の抵抗調整プログラム(リセットで脱出) } } if (digitalRead(8) == LOW) { // もしPin8がGNDなら動作確認用に sleep_n = 1; // ループは1回で sleep_t = 7; // 周期は2秒 dedZone = 0; // 表示不感帯無しで動かす(解除はリセット) } LPS25H_check(); // 気圧センサ(LPS25H)との通信確認 LPS25H_setup(); // LPS25H初期化 LPS25H_measure(); // 初回の気圧測定 clkPosiLast = hPa / 0.02; // 現在の表示位置を記録(針は合っているものとみなす) Serial.print("Start press = "); Serial.print(hPa); Serial.println("hPa"); Serial.println(); Serial.flush(); // 改行して送信完了待ち } void loop() { LPS25H_measure(); Serial.print(hPa, 2); Serial.print(", "); clkPosi = hPa / 0.02; // 針の位置を計算 moveTo(clkPosi); // 針を指定位置に動かす volt = batteryCheck(); // 電池電圧確認(電池が空なら関数内で処置) Serial.println(volt); // 電池電圧表示 Serial.flush(); // delay(10); for (int i = 0; i < sleep_n; i++) { // 次回測定まで待機(75回) // delayWDT(sleep_t); // パワーダウンスリープ delayWDT2(sleep_t); // パワーダウンスリープ } } void LPS25H_check() { if (LPS25H_read(0x0F) != 0xBD) { // I2C上にLPS25Hがあるか確認 Serial.print("LPS25H not found"); for (;;) { // ダメならここで無限ループ } } Serial.println("Barometer start. LPS25H found at I2C 0x5D"); } void LPS25H_setup() { // センサーの初期設定 LPS25H_write(0x20, 0x80); // 電源ON(PD=1), ブロックデーター更新(BDU=1) delay(1); // パワーダウンから通常モードに変わるまで待つ LPS25H_write(0x20, 0x84); // 電源ON(PD=1), ブロックデーター更新(BDU=1) LPS25H_write(0x10, 0x03); // アベレージ回数設定:温度8回,気圧512回 LPS25H_write(0x21, 0x01); // FIFO無効、ワンショット動作 delay(1); LPS25H_write(0x20, 0x04); // 電源を一旦OFF(PD=0), ブロックデーター更新(BDU=1) delay(100); } void LPS25H_measure() { digitalWrite(13, HIGH); LPS25H_write(0x20, 0x00); // Power down for clean start delay(1); LPS25H_write(0x20, 0x84); // シングルショットモードでPower ON LPS25H_write(0x21, 0x01); // 測定スタート while (LPS25H_read(0x21) != 0x00) { // 測定が終わるのを待つ } p24 = LPS25H_press(); // 気圧読み取り LPS25H_write(0x20, 0x00); // パワーダウンモードに戻す hPa = ((float)p24 / 4096.0) + pOffset; // 気圧計算 digitalWrite(13, LOW); } long LPS25H_press() { // 気圧データー読み取り処理 long x, x1, x2, x3; x1 = LPS25H_read(0x28); // 気圧データー読み出し x2 = LPS25H_read(0x29); x3 = LPS25H_read(0x2A); x = (x3 << 16) | (x2 << 8) | x1; // 気圧表示結果合成 return x; // 24ビット形式で気圧の値を返す } void LPS25H_write(byte adr, byte data) { // LPS25Hの指定アドレスに書き込み Wire.beginTransmission(LPS25H_adr); Wire.write(adr); Wire.write(data); Wire.endTransmission(); } byte LPS25H_read(byte adr) { // LPS25Hの指定アドレスを読み出し Wire.beginTransmission(LPS25H_adr); Wire.write(adr); Wire.endTransmission(); Wire.requestFrom(LPS25H_adr, 1); // 1バイトだけ読む return Wire.read(); } float batteryVoltage() { // バッテリー電圧測定 int data; float voltage; data = analogRead(0); // ADCを読む voltage = divR * 1.1 * data / 1024.0; // 電圧に換算 return voltage; // 電圧をfloatで返す } void moveTo(unsigned int x) { // 指定した位置に針を動かす int p; p = x - clkPosiLast; Serial.print(x); Serial.print(", "); Serial.print(p); Serial.print(", "); if (( p < -dedZone) || ( dedZone < p)) { // 移動量が不感帯以上あったら、 clockDrive(p); // 針を動かす clkPosiLast = x; // 針の位置を保存 } } void clockDrive(int x) { // 引数の値だけ針を動かす(引数は正負) if ( x != 0) { if ( x > 0) { cw(x); } else { x *= -1; ccw(x); } } } // clockDrive void cw(int n) { // 指定パルスだけ順回転 if (n != 0) { for (int x = 1; x <= n; x++) { cwP(); } } } void ccw(int n) { // 指定パルスだけ逆回転 if (n != 0) { for (int x = 1; x <= n; x++) { ccwP(); } } } void cwP() { // 順回転パルス発生 digitalWrite(13, HIGH); // LED flash flag = ! flag; if (flag == true) { digitalWrite(9, HIGH); // coil drive foward timeWait(cwPulse); // パルス幅 digitalWrite(9, LOW); // coil drive end } else { digitalWrite(10, HIGH); // coil drive revers timeWait(cwPulse); // パルス幅 digitalWrite(10, LOW); // coil drive end } digitalWrite(13, LOW); // LED flash end timeWait(cwInterval); // パルス間隔 } void ccwP() { // 逆回転パルス発生 digitalWrite(13, HIGH); // LED flash flag = ! flag; if (flag == true) { digitalWrite(9, HIGH); // coil drive foward timeWait(ccwPulse1); // 先頭パルス幅 digitalWrite(9, LOW); // coil drive end timeWait(ccwWait); // 反転待ち時間 digitalWrite(10, HIGH); // coil drive revers timeWait(ccwPulse2); // 反転パルス幅 digitalWrite(10, LOW); // coil drive end } else { digitalWrite(10, HIGH); // coil drive revers timeWait(ccwPulse1); // 先頭パルス幅 digitalWrite(10, LOW); // coil drive end timeWait(ccwWait); // 反転待ち時間 digitalWrite(9, HIGH); // coil drive foward timeWait(ccwPulse2); // 反転パルス幅 digitalWrite(9, LOW); // coil drive end } digitalWrite(13, LOW); // LED flash end timeWait(ccwInterval); // パルス間隔 } void clockDemo() { // 時計の動作確認プログラム(正負に10パルス動かす) clockDrive( 10); delay(300); clockDrive(-10); delay(600); clockDrive(-10); delay(300); clockDrive( 10); delay(600); } void timeWait(unsigned long xx) { // μsの値だけ待つ unsigned int x; if ( xx > 16000) { // 16000以上なら delay(xx / 1000); // dalay関数を使用(1000us以下の端数は切り捨て) } else { x = xx; delayMicroseconds(x); // 16000以下なら delayMicrosecondsを使用(引数はunsigned int) } } float batteryCheck() { // 電池電圧チェックと電池切れ処理 float v; v = batteryVoltage(); if (v < voltageLimit) { // バッテリー電圧が基準以下なら moveTo(lowBatPosi / 0.02); // 針をLowBattery表示ゾーンに移動させて while (1) { // 無限ループでLED点滅 digitalWrite(13, HIGH); // delayWDT(0); // LED Flash 16ms delayWDT2(0); // LED Flash 16ms digitalWrite(13, LOW); // delayWDT(8); // wait 4sec delayWDT2(8); // wait 4sec } } return v; // 電圧OKならバッテリー電圧を返す } void delayWDT2(unsigned long t) { // パワーダウンモードでdelayを実行 Serial.flush(); // シリアルバッファが空になるまで待つ delayWDT_setup(t); // ウォッチドッグタイマー割り込み条件設定 // ADCを停止(消費電流 147→27μA) ADCSRA &= ~(1 << ADEN); set_sleep_mode(SLEEP_MODE_PWR_DOWN); // パワーダウンモード指定 sleep_enable(); // BODを停止(消費電流 27→6.5μA) MCUCR |= (1 << BODSE) | (1 << BODS); // MCUCRのBODSとBODSEに1をセット MCUCR = (MCUCR & ~(1 << BODSE)) | (1 << BODS); // すぐに(4クロック以内)BODSSEを0, BODSを1に設定 asm("sleep"); // 3クロック以内にスリープ sleep_mode();では間に合わなかった sleep_disable(); // WDTがタイムアップでここから動作再開 ADCSRA |= (1 << ADEN); // ADCの電源をON(BODはハードウエアで自動再開される) } void delayWDT_setup(unsigned int ii) { // ウォッチドッグタイマーをセット。 // 引数はWDTCSRにセットするWDP0-WDP3の値。設定値と動作時間は概略下記 // 0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms // 6=1sec, 7=2sec, 8=4sec, 9=8sec byte bb; if (ii > 9 ) { // 変な値を排除 ii = 9; } bb = ii & 7; // 下位3ビットをbbに if (ii > 7) { // 7以上(7.8,9)なら bb |= (1 << 5); // bbの5ビット目(WDP3)を1にする } bb |= ( 1 << WDCE ); MCUSR &= ~(1 << WDRF); // MCU Status Reg. Watchdog Reset Flag ->0 // start timed sequence WDTCSR |= (1 << WDCE) | (1 << WDE); // ウォッチドッグ変更許可(WDCEは4サイクルで自動リセット) // set new watchdog timeout value WDTCSR = bb; // 制御レジスタを設定 WDTCSR |= _BV(WDIE); } ISR(WDT_vect) { // WDTがタイムアップした時に実行される処理 }