製作モジュール一覧

ラベル HDC1000 の投稿を表示しています。 すべての投稿を表示
ラベル HDC1000 の投稿を表示しています。 すべての投稿を表示

2017年3月1日水曜日

HDC1000をArduinoで使って不快指数計を作る その2

Arduino Unoでスケッチを書き込み、Fuse Bitを書き換えてATMega328P単体で内蔵RC8MHz/3.3Vで動作させてみた。

ブレッドボード図


Arduinoのスケッチ

HDC1000_Thermometer.ino

/*
 * HDC100_Thermometer
 * 
 * 2017.02.26
 * 
 * LCD5110_Basic Library: http://www.rinkydinkelectronics.com/library.php?id=44
 * 
 */

#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 IO_PWR_PIN 7

#define MEASURE_INTERVAL  (1000/2)
#define WAKEUP_PERIOD 10

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);

  // I/O Power
  pinMode(IO_PWR_PIN, OUTPUT);
  digitalWrite(IO_PWR_PIN, HIGH);

  // LCD
  myGLCD.InitLCD();

  // HDC1000
  Wire.begin();
  Wire.setClock(400000);
  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(MEASURE_INTERVAL);

  // 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
  myGLCD.enableSleep();
  digitalWrite(IO_PWR_PIN, LOW);
  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(IO_PWR_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;
}

ATMega328P単体で内蔵RC8MHzで動作はできた。1MHzでも動作はするが、LCDの表示が遅い。

電池駆動しようと思うので電源に電流計(テスタ)をつないで消費電流を測定してみた。

内蔵RC8MHz駆動

動作中: 8.4mA
スリープ中: 1.6mA

内蔵RC1MHz駆動

動作中: 6.2mA
スリープ中: 1.5mA

ATMega328PのDATASHEETを見ると、スリープ時はもっと少なくても良さそうな気がする。予想よりかなり多い。スリープ中にHDC1000とLCDにまわしている電源ラインを抜いてもほとんど変わらない。

スイッチをプルアップしている抵抗には通常時は電流が流れないと思うし、I2Cのプルアップもオープンドレインなので無信号時にはほとんど流れないと思う。もうちょっとAVRのDATASHEETをちゃんと読まないとダメかな~?

ボタン電池ではもちろん、700mAh程度ある単4を2本直列にして3V駆動させても、700mAh / 1.6mA ≒ 438hで18日程度しか持たなさそうだ。

めんどくさそうなので当分保留(^q^;


Github:
https://github.com/ryood/HDC1000_Thermometer

2017年2月27日月曜日

HDC1000をArduinoで使って不快指数計を作る ブレッドボードで実験

ブレッドボード図


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)も参考にする?

2016年5月3日火曜日

HDC1000と7Seg LEDを使って温度湿度を測定

小ネタ実験です。実用性はほとんどなさそうですがArduinoで実験してみました。

回路図(7Seg LED)

aitendoの福袋に入っていた7Segx4のLEDを使ってみた。カソード・コモンでアノード側はABCDEFGの7セグメント+ドット(P)の8本。カソードは4桁分の4本。

アノード側に電流制限抵抗(R1~R8)を入れた。抵抗値は220Ω。カソードは直接Arduinoのピンにつないでsinkさせると電流が大きすぎるのでQ1~Q4のNch MOSFET(手持ちの2N7000)でスイッチングさせた。

NPNトランジスタでもスイッチングできるが、ベース電流を制限する抵抗が必要になる。

7Segx4のLEDのピン配列

なんとも複雑なピン配置。

ブレッドボード図

上側のブレッドボードが7Seg LEDで下側のブレッドボードがHDC1000。


7Segの方はたった4桁の数値を表示させるだけなのにそこそこ複雑な配線になった。

HDC1000の方はI2C接続でプルアップ抵抗も基板上に載っているのでシンプルだ。SCL、SDAはArduinoのハードウェアI2C用のA0、A1 A4、A5ピン、RDYもA2 A3に接続。

Arduinoのスケッチ


7Segの表示にはSevSegライブラリを使用。デフォルトで4桁の7 Seg LEDに対応していてほとんどそのまま使えた。

コモン・カソードのLEDでカソードをNch MOSFETでスイッチングしているので、
void begin(byte hardwareConfig, byte numDigitsIn, byte digitPinsIn[], byte segmentPinsIn[]);
のhardwareConfigパラメーターに「NP_COMMMON_CATHODE」を指定した。

HDC1000の制御はgarretlabさんのスケッチを使わせていただいた。

動作としては温度と湿度を交互に表示するようにした。

/*
  HDC1000で温度/湿度を読み取り7Seg4桁のLEDに表示
  
  2016.05.03
*/
#include <Wire.h>  
#include "SevSeg.h"

// HDC1000
#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 */

// SevSeg
SevSeg sevseg; //Instantiate a seven segment controller object

#define DISPLAY_TEMPERATURE  0
#define DISPLAY_HUMIDITY    1
int displayMode;

void setup() {
  byte numDigits = 4;   
  byte digitPins[] = {2, 3, 4, 5};
  byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12, 13};

  sevseg.begin(N_TRANSISTORS, numDigits, digitPins, segmentPins);
  sevseg.setBrightness(90);
  
  Wire.begin();
  pinMode(HDC1000_RDY_PIN, INPUT);
 
  delay(15); /* Wait for 15ms */
  configure();
}

void loop() {
  static int cnt = 100;
  float temperature, humidity;

  cnt++;
  if (cnt > 100) {
    cnt = 0;
    getTemperatureAndHumidity(&temperature, &humidity);
    if (displayMode == DISPLAY_TEMPERATURE) {
      sevseg.setNumber(temperature, 2);
      displayMode = DISPLAY_HUMIDITY;
    } else {
      sevseg.setNumber(humidity, 2);
      displayMode = DISPLAY_TEMPERATURE;
    }
  }
    
  sevseg.refreshDisplay(); // Must run repeatedly
}

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;
}
/// END ///

メモ:

ダイナミック表示という、各桁を順番に表示させて残像で全桁が表示されているように見せかける方法で表示している。数値を変更して書き換える頻度が高いとかなりちらつく。(0.1ms程度)

LEDの表示だけでArduinoのD2~D13とデジタルピンをほとんど使ってしまう。

配線めんどくさくてLEDは消費電流も大きいので使いどころがあるかどうか。