HDC1000
温度、湿度が分かれば不快指数が出せるので、秋月で売っているHDC1000というI2C接続の温湿度モジュールを使って、不快指数計を作ってみる。HDC1000は以前7Seg LEDを使って実験したことがある。
Nokia 5110
表示器には不快指数のアイコンを表示できたらな~と思って、aitendoで売っているSPI接続のグラフィックLCDのLCD5110を使ってみた。これもArduinoやNucleoで何度か実験したことがある。
HDC1000は動作電圧が3V~5Vなのでそのまま5V駆動のArduino Unoで使えるが、Nokia 5110は3.3V駆動なので、74HC4050を使ってSPI信号の5V->3.3Vの電圧の変換を行った。(参考:http://dad8893.blogspot.jp/search?q=74HC4050)
Nokia5110のライブラリには、Rinky-Dink ElectronisさんのLCD5110_Basic Library を使った。最初は色々なLCDに対応しているu8glibを使ってみたが、LCD5110_Basicは5110のスリープ・モードに対応しているのでこちらにした。
スリープ機能
電池駆動させようと思ったので、消費電力節約のために、AVRのSleep機能を使って見た。使い方は「初心者だけど、一歩ずつ Arduino 超小型マイコン電子工作」さんの記事を参考にした。
オルタナティブのタクトスイッチでスリープモードからの復帰、モーメンタリのスイッチでスリープ有り/無しのモードを切り替える。
自作の簡易電流計で測定したら、
動作時:約44mA
スリープ時:約12mA
となった。Arduinoボード上にLEDがついているのでこれの消費電流がそこそこあるのかもしれない。
HDC1000はI2Cのコマンドを送信しないとスリープモードに入るようなので(←ちゃんとは確認していないです)プログラムでは特には何もしていない。
Arduinoのスケッチ
HDC1000_Thermometer.ino/* * HDC100_Thermometer * * 2017.02.26 * */ #include <Wire.h> #include <LCD5110_Basic.h> #include <avr/sleep.h> #include <avr/interrupt.h> #define HDC1000_ADDRESS 0x40 /* or 0b1000000 */ #define HDC1000_RDY_PIN A3 /* Data Ready Pin */ #define HDC1000_TEMPERATURE_POINTER 0x00 #define HDC1000_HUMIDITY_POINTER 0x01 #define HDC1000_CONFIGURATION_POINTER 0x02 #define HDC1000_SERIAL_ID1_POINTER 0xfb #define HDC1000_SERIAL_ID2_POINTER 0xfc #define HDC1000_SERIAL_ID3_POINTER 0xfd #define HDC1000_MANUFACTURER_ID_POINTER 0xfe #define HDC1000_CONFIGURE_MSB 0x10 /* Get both temperature and humidity */ #define HDC1000_CONFIGURE_LSB 0x00 /* 14 bit resolution */ #define WAKEUP_PIN 2 #define SLEEP_MODE_PIN 3 #define BACK_LIGHT_PIN 7 #define WAKEUP_PERIOD 5 LCD5110 myGLCD(8,9,10,11,12); bool isSleepMode = true; int count; extern uint8_t SmallFont[]; extern uint8_t MediumNumbers[]; extern uint8_t BigNumbers[]; void setup() { // Sleep Mode pinMode(WAKEUP_PIN, INPUT_PULLUP); pinMode(SLEEP_MODE_PIN, INPUT_PULLUP); // LCD Power pinMode(BACK_LIGHT_PIN, OUTPUT); digitalWrite(BACK_LIGHT_PIN, HIGH); // LCD myGLCD.InitLCD(); // HDC1000 Wire.begin(); pinMode(HDC1000_RDY_PIN, INPUT); delay(15); /* Wait for 15ms */ configure(); Serial.begin(9600); Serial.print("Manufacturer ID = 0x"); Serial.println(getManufacturerId(), HEX); Serial.println(); } float calcDiscomfort(float temperature, float humidity) { return 0.81 * temperature + 0.01 * humidity * (0.99 * temperature - 14.3) + 46.3; } void loop() { float temperature, humidity, discomfort; getTemperatureAndHumidity(&temperature, &humidity); discomfort = calcDiscomfort(temperature, humidity); Serial.print("Temperature = "); Serial.print(temperature); Serial.print(" degree, Humidity = "); Serial.print(humidity); Serial.print("%, Discomfort = "); Serial.println(discomfort); myGLCD.clrScr(); myGLCD.setFont(BigNumbers); myGLCD.printNumF(temperature, 1, RIGHT, 0); myGLCD.setFont(MediumNumbers); myGLCD.printNumF(humidity, 1, LEFT, 32); myGLCD.printNumF(discomfort, 0, RIGHT, 32); myGLCD.setFont(SmallFont); if (isSleepMode) { myGLCD.print("SLP", LEFT, 0); } else { myGLCD.print("CON", LEFT, 0); } delay(1000); // Check Sleep Mode isSleepMode = digitalRead(SLEEP_MODE_PIN); // Sleep if (isSleepMode) { count++; if (count >= WAKEUP_PERIOD) { Serial.println("Sleep mode start!!"); count = 0; sleepAndWakeup(WAKEUP_PIN); } } } //----------------------------------------------------------------------------------------------- // Sleep Mode //----------------------------------------------------------------------------------------------- void wakeup() { Serial.println("Wakeup!!"); } int sleepAndWakeup(int interruptNo) { Serial.println("sleepAndWake Process start!!"); if (digitalRead(WAKEUP_PIN) == LOW) { Serial.println("WAKEUP_PIN Low Level"); } else { Serial.println("WAKEUP_PIN High Level"); } Serial.println("sleep enable"); delay(100); // Sleep digitalWrite(BACK_LIGHT_PIN, LOW); myGLCD.enableSleep(); attachInterrupt(digitalPinToInterrupt(interruptNo), wakeup, FALLING); noInterrupts(); set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_enable(); interrupts(); sleep_cpu(); // Wakeup sleep_disable(); detachInterrupt(digitalPinToInterrupt(interruptNo)); digitalWrite(BACK_LIGHT_PIN, HIGH); myGLCD.disableSleep(); return 0; } //----------------------------------------------------------------------------------------------- // HDC1000 //----------------------------------------------------------------------------------------------- void configure() { Wire.beginTransmission(HDC1000_ADDRESS); Wire.write(HDC1000_CONFIGURATION_POINTER); Wire.write(HDC1000_CONFIGURE_MSB); Wire.write(HDC1000_CONFIGURE_LSB); Wire.endTransmission(); } int getManufacturerId() { int manufacturerId; Wire.beginTransmission(HDC1000_ADDRESS); Wire.write(HDC1000_MANUFACTURER_ID_POINTER); Wire.endTransmission(); Wire.requestFrom(HDC1000_ADDRESS, 2); while (Wire.available() < 2) { ; } manufacturerId = Wire.read() << 8; manufacturerId |= Wire.read(); return manufacturerId; } void getTemperatureAndHumidity(float *temperature, float *humidity) { unsigned int tData, hData; Wire.beginTransmission(HDC1000_ADDRESS); Wire.write(HDC1000_TEMPERATURE_POINTER); Wire.endTransmission(); while (digitalRead(HDC1000_RDY_PIN) == HIGH) { ; } Wire.requestFrom(HDC1000_ADDRESS, 4); while (Wire.available() < 4) { ; } tData = Wire.read() << 8; tData |= Wire.read(); hData = Wire.read() << 8; hData |= Wire.read(); *temperature = tData / 65536.0 * 165.0 - 40.0; *humidity = hData / 65536.0 * 100.0; }
Arduinoで書き込んだAVRを生で使う
Arduinoはプログラムを書くのが楽でいいんだが、そのまま使うとお金もかかるし場所も取るのでArduinoからATMega328Pを取り外して、Fuse Bitを書き換えて内蔵RCクロックで動作するようにして動くかどうか試してみた。
Arduino Unoは外付け水晶振動子で16MHzで動いているが、Fuse Bitで内蔵RC1MHzに設定してやると遅~いスピードで動作するようだ。
Arduino Unoで使う場合のFuse Bit
h: D6
l: FF
e: 05
内蔵RC(1MHz)で使ったFuse Bit
h: D9
l: 62
e: ff(←書き込み時)
I2C、SPIの通信速度もあるので、もうちょっと検証が必要。
Arduinoから取り外してクロック数が少ない内蔵RCをクロックとして使えば消費電流も少なくて済むし3.3V駆動もできるので電池で使うにはよさそう。
3.3V駆動できれば、LCDのロジックレベル変換用に使っている74HC4050も不要になる。
メモ:
Sleep時にはLCDのバックライトだけでなく、AVR以外の電源をバッサリ切るようにする?⇛復帰時にデバイスの初期化をし直さないとだめ?外部電源(3.3V)が使える場合は、UART通信できるようにしておく?⇛3.3VLDO+USBシリアル変換
Arduino Pro mini(8MHz)も参考にする?