{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"オリジナルの作成:2015/04/29"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 12-気温・気圧・湿度を測ってみる\n",
"## 1個で気温、湿度、気圧が測れる便利なモジュールBME280\n",
"スイッチサイエンスが販売しているBME280搭載 温湿度・気圧センサモジュールは、 1個のチップで気温、湿度、気圧が測れる便利なセンサーをブレッドボードでも使えるモジュールです。 Arduinoでの使い方も以下のページの「使い方」に紹介されています。\n",
"\n",
"- https://www.switch-science.com/catalog/2236/\n",
"\n",
"今回は、mbedの普及に努めている渡會さんが、BME280モジュールをmbedで使えるライブラリーを公開されたので、 これをlbeDuinoに移植してみました。\n",
"\n",
"- https://developer.mbed.org/users/MACRUM/code/BME280/\n",
"\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### BME280シールドの作成 \n",
"BME280とlbeDuinoとの接続は、渡會さんの資料を参考に以下の様に結線しました。\n",
"\n",
" | lbeDuino\t | BME280 | \n",
" |---|---|\n",
" | GND\t | 1, 5(SDO, GND) | \n",
" | 3.3V\t | 4, 6, 7(GSB, Vcore, Vio) | \n",
" | D8(SDA)\t | 3(SDI) I2CではSDA | \n",
" | D9(SCL)\t | 2(SCK) I2CではSCL | \n",
" \n",
" 今回も テクノペン を使って秋月の両面基板上に回路を描いています。\n",
" \n",
" \n",
" \n",
" 出来上がったMBE280シールドは、以下の様になります。\n",
" \n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## ライブラリーの移植\n",
"lbedは、mbedのインタフェースに合わせているので、ライブラリーの移植はインクルードファイルを mbed.hからlbed.hに変え、setup関数を追加するだけで良いのですが、そのままコンパイルするとサイズオーバーで、 LPC1114FNベースのlbeDuinoには載りませんでした。\n",
"\n",
"そこで、i2cのポインタを止めて、デストラクターを削除して、コンストラクターもsda, sclを使用するものに修正しました。\n",
"\n",
"```C++\n",
" // メソッドの修正部分\n",
" BME280(PinName sda, PinName sck, char slave_adr = DEFAULT_SLAVE_ADDRESS);\n",
" void setup();\n",
"\n",
" // メンバ変数の修正部分(ポインタ部分を削除)\n",
" I2C i2c;\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### スケッチ\n",
"I2cLCDシールドを上に載せたテスト用のスケッチ(プログラム)は、渡會さんのmain関数を参考に以下の様にしました。\n",
"\n",
"```C++\n",
"#include \"lbed.h\"\n",
"#include \"AQCM0802.h\"\n",
"#include \"BME280.h\"\n",
"\n",
"// D13番ピンにLEDを接続\n",
"DigitalOut led(D13);\n",
"// D8番ピンSDA, D9番ピンSCL\n",
"AQCM0802 lcd(D8, D9);\n",
"BME280 sensor(D8, D9);\n",
"\n",
"// タクトスイッチ\n",
"DigitalIn sw1(D2);\n",
"DigitalIn sw2(D3);\n",
"\n",
"int last_mode = 0;\n",
"char degree = 0xdf;\n",
"void setup() {\n",
" sw1.mode(PullUp);\n",
" sw2.mode(PullUp);\n",
" lcd.setup();\n",
" sensor.setup();\n",
" lcd.locate(0, 0); lcd.print(\"BME280\");\n",
" lcd.locate(0, 1); lcd.print(\"Demo\");\n",
" wait_ms(2000);\n",
"}\n",
"\n",
"void loop() {\n",
" led = ! led;\n",
" if (sw1 == 1) {\n",
" if (last_mode == 0)\n",
" lcd.cls();\n",
" lcd.locate(0, 0);\n",
" lcd.print(sensor.getTemperature(), 2);\n",
" lcd.print(degree); lcd.print(\"C\");\n",
" lcd.locate(0, 1);\n",
" lcd.print(sensor.getHumidity(), 2); lcd.print(\"%\");\n",
" last_mode = 1;\n",
" }\n",
" else {\n",
" if (last_mode == 1)\n",
" lcd.cls();\n",
" lcd.locate(0, 0);\n",
" lcd.print(sensor.getTemperature(), 2);\n",
" lcd.print(degree); lcd.print(\"C\");\n",
" lcd.locate(0, 1);\n",
" lcd.print(sensor.getPressure(), 2); lcd.print(\"hPa\");\n",
" last_mode = 0;\n",
" }\n",
" wait_ms(1000);\n",
"}\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Arduino3.3V版への移植\n",
"lbeDuinoは、Arduino3.3V版でも同じように動くことを売りにしているので、 出来上がったBME280のライブラリをArduino版に入れて実行したところ、 湿度を除いて、気温、気圧の値がまったく違ってでてきました。\n",
"\n",
"### mbedからArduino3.3Vへのライブラリの移植の注意点\n",
"mbedは、ARMの32bitマイコンを使っているのに対し、Arduinoは、AVRの8bitのAtmega328Pを使っています。\n",
"\n",
"ここで、注意が必要なのがintの評価をするときに、16bitでするか32bitで行うかという点です。 BME280では、気温と気圧は3バイトで返されるため、32bitのlongタイプが必要になります。\n",
"\n",
"スイッチサイエンスで公開されているArduino版の温度センサーの処理は、以下の様になっています。 データの取り込み部分readData()では、Wire.read()で取り込んだ1バイトのデータをuint32_tのdataの配列に セットしています。これで、temp_rawに32bitのデータが正しくセットされます。\n",
"\n",
"```C++\n",
"void readData()\n",
"{\n",
" int i = 0;\n",
" uint32_t data[8];\n",
" Wire.beginTransmission(BME280_ADDRESS);\n",
" Wire.write(0xF7);\n",
" Wire.endTransmission();\n",
" Wire.requestFrom(BME280_ADDRESS,8);\n",
" while(Wire.available()){\n",
" data[i] = Wire.read();\n",
" i++;\n",
" }\n",
" pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);\n",
" temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);\n",
" hum_raw = (data[6] << 8) | data[7];\n",
"}\n",
"```\n",
"\n",
"次に温度に変換するcalibration_Tを見てみると、dig_T1, dig_T2, dig_T3に対して(signed long int)等のキャストが 執拗に施されています。ここが8bitマイコンで32bitデータを扱うときに注意が必要な部分なのです。\n",
"\n",
"```C++\n",
"signed long int calibration_T(signed long int adc_T)\n",
"{\n",
" \n",
" signed long int var1, var2, T;\n",
" var1 = ((((adc_T >> 3) - ((signed long int)dig_T1<<1))) * ((signed long int)dig_T2)) >> 11;\n",
" var2 = (((((adc_T >> 4) - ((signed long int)dig_T1)) * ((adc_T>>4) - ((signed long int)dig_T1))) >> 12) * ((signed long int)dig_T3)) >> 14;\n",
" \n",
" t_fine = var1 + var2;\n",
" T = (t_fine * 5 + 128) >> 8;\n",
" return T; \n",
"```\n",
"\n",
"渡會さんのgetTemperatureでは、32bitマイコンなので、char cmdの配列をそのまま12bitシフトしても 問題なく、temp_rawにセットされ、dig_T1, dig_T2, dig_T3へのキャストも不要です。\n",
"\n",
"```C++\n",
"float BME280::getTemperature()\n",
"{\n",
" uint32_t temp_raw;\n",
" float tempf;\n",
" char cmd[4];\n",
" \n",
" cmd[0] = 0xfa; // temp_msb\n",
" i2c.write(address, cmd, 1);\n",
" i2c.read(address, &cmd[1], 1);\n",
" \n",
" cmd[0] = 0xfb; // temp_lsb\n",
" i2c.write(address, cmd, 1);\n",
" i2c.read(address, &cmd[2], 1);\n",
" \n",
" cmd[0] = 0xfc; // temp_xlsb\n",
" i2c.write(address, cmd, 1);\n",
" i2c.read(address, &cmd[3], 1);\n",
" \n",
" temp_raw = (cmd[1] << 12) | (cmd[2] << 4) | (cmd[3] >> 4);\n",
" \n",
" int32_t temp;\n",
" \n",
" temp =\n",
" (((((temp_raw >> 3) - (dig_T1 << 1))) * dig_T2) >> 11) +\n",
" ((((((temp_raw >> 4) - dig_T1) * ((temp_raw >> 4) - dig_T1)) >> 12) * dig_T3) >> 14);\n",
" \n",
" t_fine = temp;\n",
" temp = (temp * 5 + 128) >> 8;\n",
" tempf = (float)temp;\n",
" \n",
" return (tempf/100.0f);\n",
"}\n",
"```\n",
"\n",
"そこで、lbeDuinoのArduino3.3V版では、以下の様に修正しました。読み込んだcmdをuint32_tのdataにコピーし、 キャストもArduino版と同じように入れています。\n",
"\n",
"```C++\n",
" // Arduino版は、intが16bitなので、キャストがうまく処理できないので、領域を割り当て計算する。\n",
" uint32_t data[4];\n",
" for (int i = 0; i < 4; i++) data[i] = (uint8_t)cmd[i];\n",
" temp_raw = (data[1] << 12) | (data[2] << 4) | (data[3] >> 4);\n",
"\n",
" int32_t var1, var2, temp;\n",
"\n",
" // Arduino版では、キャストを追加\n",
" var1 = ((((temp_raw >> 3) - ((int32_t)dig_T1 << 1))) * ((int32_t)dig_T2)) >> 11;\n",
" var2 = (((((temp_raw >> 4) - ((int32_t)dig_T1)) * ((temp_raw >> 4) - ((int32_t)dig_T1))) >> 12) * ((int32_t)dig_T3)) >> 14;\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Arduino3.3V版でBME280シールドを動かしてみる\n",
"BME280ライブラリが修正でき、最初のスケッチがArduino3.3V版でも無事動作するようになりました。\n",
"(ちょっと分かりづらいですが、一番下にArduino3.3V版と変換シールドを載せ、BME280シールド、I2cLCDシールドを載せています)\n",
"\n",
""
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.13"
}
},
"nbformat": 4,
"nbformat_minor": 2
}