I2C接続AQMシリーズのキャラクタ表示LCDをMicro:bitで使う (6) 湿度センサ

しめりけを感じよう

 筆者の学生時代、湿度は髪の毛の伸び縮みを利用した湿度計で測っていました。理科の実験では、ガラスの棒状温度計を二つ使い、一方をガーゼで湿らして温度差から湿度を求めていました。操作する人によって大きく値が異なります。

 専用の湿度センサが登場したのは最近です。マイコンにつないで利用できるセンサは各種あります。

  • DHT11、AM2322などの温湿度センサ。インターフェースは独自
  • HDC1000(後継HDC1010)やSHT31などの温湿度センサ・インターフェースはI2C
  • BME280の温湿度センサ+気圧計など複合タイプ

 ここではSHT31を使います。秋月電子通商から変換基板に実装されたモジュールを入手しました。

SHT31のスペック

  • 電源電圧;2.4~5.5V
  • 通信方式;I2C(最大データ転送速度1MHz)
  • 温度;-40~+125℃(確度±0.2℃)、分解能0.015℃
  • 相対湿度;0~100%(0~90℃時)、分解能0.01%
  • データ・ビット長;16ビット
  • スレーブ・アドレス;ADDRピンをオープンで0x45、GNDへつなぐと0x44

接続

 I2Cインターフェースは、データSDAとクロックSCLをパラレルにつないでいきます。マスタMicro:bit にぶら下がるデバイスのスレーブ・アドレスが異なれば、正しく通信できます。

Micro:bit SHT31 AQM1602
20 SDA SDA SDA
19 SCL SCL SCL
0V GND GND
3V +V(VDD) +V

 Micro:bit には、二つのI2Cデバイスが搭載されています。

  • 0x0e 磁気センサMAG3110
  • 0x1d 加速度センサMMA8653

 加えて、キャラクタLCDのAQM1602が0x3eです。これら三つとぶつからないようにします。SHT31をつないで、i2c_scannerというスケッチをインターネットからダウンロードして動かします。

 その前に、SDAとSCLのプルアップ抵抗を取り除きます。Micro:bitには1kΩのプルアップ抵抗が実装されています。とはいえ、SHT31モジュールには狭い場所に三つのチップ抵抗が取り付けられています。モジュールのデータシートによれば、SDA、SCL、ADDRのプルアップ抵抗です。はんだゴテで温めて、ぜんぶ取り去ります。SHT31に一番近いところにあるチップ部品はコンデンサと思われるので、そのままにしておきます。

 SHT31のスレーブ・アドレスが0x44だと確認できました。モジュールのデータシートには、ADDRをGNDにつなぐと0x44になると書かれていますが、テスタで測るとGNDにはつながっていません。SHT31のデータシートにはADDR端子を開放にしてはいけないと書かれているので、GNDにつなぎました。

SHT31のレジスタのフォーマット

 周期的連続測定を行います。サンプルの「繰り返し精度レベルは高、測定頻度は 1mps」のコマンドは0x2130です。

 周期的連続測定の読み出しは、0xE0、0x00を送ると温度と湿度データを送ってきます。実際には、連続測定のモードなので、0xE0、0x00を送らずともデータが読み出せました。厳密には正しくないかもしれません。

16ビット長 温度測定値   16ビット長 相対湿度測定値  
温度測定値
(MSB部)
温度測定値
(LSB部)
チェックサム(8ビット) 湿度測定値
(MSB部)
湿度測定値
(LSB部)
チェックサム(8ビット)

 読み出した16ビットのデータは、測定データを符号なし整数型です。温度(摂氏)を求める換算式は次のとおりです。Stempは測定値です。

  T = -45 + 175 * Stemp / 2^16 - 1

 相対湿度の換算式は次のとおりです。SRHは測定値です。

  RH = 100 * SRH / 2^16 - 1

スケッチ

 データシートによると、周期的連続測定コマンドは、繰り返し精度レベル、測定頻度によってコマンド・コードが数多く用意されています。事例に書かれている0x2130では読み出したデータの半分がエラーになりました。測定頻度を高めにした0x2334に設定すると、エラーがなくなりました。参考にしたAdafruitのプログラムでは0x2400が使われています。

 温度と湿度のデータにはデータに誤りがあるかをチェックするCRCコードが一緒に送られてきます。ここでは読み捨てました。

#include <Wire.h>
unsigned char SHT31_address = 0x44;
int Stemp;int SRH;
float temp;float RH;
byte readbuffer[6];

void setup() {
Wire.begin();
Wire.beginTransmission(SHT31_address);
Wire.write(0x23);Wire.write(0x34);
Wire.endTransmission();
Serial.begin(9600);
Serial.println("start ");
}

void loop() {
Wire.requestFrom(SHT31_address, 6);
readbuffer[0] = Wire.read();
readbuffer[1] = Wire.read();
Wire.read(); // CRC
readbuffer[2] = Wire.read();
readbuffer[3] = Wire.read();
Wire.read(); // CRC

Stemp = readbuffer[0] << 8 ;
Stemp = Stemp + readbuffer[1];
temp = -45 + Stemp * 175 / 65535.0;
SRH = readbuffer[2] << 8 ;
SRH = SRH + readbuffer[3];
RH = 100 * SRH / 65535;

Serial.print(" temp= ");Serial.print(temp,1);Serial.println("C ");
Serial.print(" RH%= ");Serial.print(RH,0);Serial.println(" ");
delay(500);
}

※参考プログラム;https://github.com/adafruit/Adafruit_SHT31/blob/master/Adafruit_SHT31.cpp

LCDへの表示

 LCDへの表示では、温度は小数点第2位を四捨五入、湿度は整数部分だけを表示しました。

#include <Wire.h>
unsigned char SHT31_address = 0x44;
unsigned char lcd_address = 0x3e;
int Stemp;int SRH;
float temp;float RH;
byte readbuffer[4];

int i2cwritecmd(byte cmd) {
Wire.beginTransmission(lcd_address);
Wire.write((byte)0x00);
Wire.write(cmd);
return Wire.endTransmission();
}

int i2cwritedata(byte data) {
Wire.beginTransmission(lcd_address);
Wire.write(0x40);
Wire.write(data);
return Wire.endTransmission();
}

void lcdcu_set(int x, int y) { // xは桁数、0から16が指定できる。yは行数で0が1行目、1が2行目
byte ca = (x + y * 0x40) | (0x80); i2cwritecmd(ca);
}

void lcdclear() {
i2cwritecmd(0x01);delay(1);
}

void i2cprint( String pdata) {
int n = pdata.length();
for (int i = 0; i < n; i = i + 1) {
i2cwritedata(pdata.charAt(i));
delay(1);
}
}

void init_lcd() { // LCDの初期化
delay(145);
i2cwritecmd(0x38);delay(1);
i2cwritecmd(0x39);delay(1);
i2cwritecmd(0x14);delay(1);
i2cwritecmd(0x73);delay(1);
i2cwritecmd(0x56);delay(1); // 3.3V
i2cwritecmd(0x6c);delay(300);
i2cwritecmd(0x38);delay(1);
i2cwritecmd(0x0c);delay(2);
i2cwritecmd(0x01);delay(2);
}

void setup() {
Wire.begin();
init_lcd();
i2cprint(String("Start"));
Wire.beginTransmission(SHT31_address);
Wire.write(0x23);Wire.write(0x34);
Wire.endTransmission();
Serial.begin(9600);
Serial.println("start ");
}

void loop() {
lcdclear();
Wire.requestFrom(SHT31_address, 6);
readbuffer[0] = Wire.read();
readbuffer[1] = Wire.read();
Wire.read(); // CRC
readbuffer[2] = Wire.read();
readbuffer[3] = Wire.read();
Wire.read(); // CRC

Stemp = readbuffer[0] << 8 ;
Stemp = Stemp + readbuffer[1];
temp = -45 + Stemp * 175 / 65535.0;
SRH = readbuffer[2] << 8 ;
SRH = SRH + readbuffer[3];
RH = 100 * SRH / 65535;

lcdcu_set(0,0);
i2cprint("Temp=");i2cprint(String(temp,1)); i2cprint("C");
lcdcu_set(0,1);
i2cprint("RH%=");i2cprint(String(RH,0)); i2cprint(" ");
Serial.print(" temp= ");Serial.print(temp,1);Serial.println("C ");
Serial.print(" RH%= ");Serial.print(RH,0);Serial.println(" ");
delay(500);
}

 実行例です。