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ビット
- インターフェース;I2C
- スレーブ・アドレス;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);
}
実行例です。