ラズパイとBME280で温度・湿度・気圧を計測してInfuxDBに送信、Grafanaで表示するまでの作業をしてみます。
2019年の初めころに完成してましたが、記事にしてなかったので紹介します。といっても多くの他のスゴイ方がやっているのであまり参考にならないかもしれませんが・・・備忘録的に書いておきます。

BME280とラズパイで温度・湿度・気圧を計測
BME280買ったとこ
ebayが安かったので送料無料で送ってもらいました。
こっち↓のBME280のほうがもっとちゃんとしてるっぽいけど同じかな?
湿度を取得できないBMP280も誤って買ってしまった。誤ってというより安さに飛びついて買ってしまった・・・BMP280は買わないほうがいいと思います。
では、ラズパイと接続していきましょう。
PIN
I2C(Inter-Integrated Circuit)通信を使うのでGPIO2(SDA),GPIO3(SCL)に接続します。
反対側にGND・3.3V・SDA・SDLがあるけど使わない。
BME280 | Raspberry Pi |
---|---|
GND | GND |
3.3V | VCC |
SCK | GPIO3(SCL) |
SDO | – |
SDI | GPIO2(SDA) |
SDD | – |
i2cを有効にする
raspi-configでI2Cを有効にする。
$ sudo raspi-config [5 Interfacing Options] [I2C] [enabled?はい]
modulesで「i2c-dev」が記入されているか確認します。
$ sudo vim /etc/modules i2c-dev
i2cdetectで確認
BME280をラズパイと接続後、ラズパイに認識されているかi2cdetectコマンドで確認します。
$ sudo i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- 76 --
76に来てました。
SWITCHSCIENCEのサンプル
こちらのサンプルを使わせていただきました。
$ wget https://github.com/SWITCHSCIENCE/samplecodes/tree/master/BME280/Python27/bme280_sample.py $ sudo apt-get -y install python-pip #pip無い場合はインストールする。 $ sudo pip install smbus2 #smbus2が必要なのでインストールする。
サンプルを実行してみます。
$ python bme280_sample.py temp : 23.10 ℃ pressure : 1000.65 hPa hum : 37.07 %
正常に取得できているみたい!
温度・湿度・気圧を取得してInfuxDBに投げるスクリプト
サンプルスクリプトをちょっと改造してInfluxDBに登録できるようにした。
#coding: utf-8 # encoding: utf-8 from influxdb import InfluxDBClient client = InfluxDBClient(host='localhost', port=8086, username='root', password='xxxx', database='sensor') measurement = 'air5' tags = {'place': 'leaf-bme280','host': 'raspi3B+'} from smbus2 import SMBus import time bus_number = 1 i2c_address = 0x76 bus = SMBus(bus_number) digT = [] digP = [] digH = [] t_fine = 0.0 def writeReg(reg_address, data): bus.write_byte_data(i2c_address,reg_address,data) def get_calib_param(): calib = [] for i in range (0x88,0x88+24): calib.append(bus.read_byte_data(i2c_address,i)) calib.append(bus.read_byte_data(i2c_address,0xA1)) for i in range (0xE1,0xE1+7): calib.append(bus.read_byte_data(i2c_address,i)) digT.append((calib[1] << 8) | calib[0]) digT.append((calib[3] << 8) | calib[2]) digT.append((calib[5] << 8) | calib[4]) digP.append((calib[7] << 8) | calib[6]) digP.append((calib[9] << 8) | calib[8]) digP.append((calib[11]<< 8) | calib[10]) digP.append((calib[13]<< 8) | calib[12]) digP.append((calib[15]<< 8) | calib[14]) digP.append((calib[17]<< 8) | calib[16]) digP.append((calib[19]<< 8) | calib[18]) digP.append((calib[21]<< 8) | calib[20]) digP.append((calib[23]<< 8) | calib[22]) digH.append( calib[24] ) digH.append((calib[26]<< 8) | calib[25]) digH.append( calib[27] ) digH.append((calib[28]<< 4) | (0x0F & calib[29])) digH.append((calib[30]<< 4) | ((calib[29] >> 4) & 0x0F)) digH.append( calib[31] ) for i in range(1,2): if digT[i] & 0x8000: digT[i] = (-digT[i] ^ 0xFFFF) + 1 for i in range(1,8): if digP[i] & 0x8000: digP[i] = (-digP[i] ^ 0xFFFF) + 1 for i in range(0,6): if digH[i] & 0x8000: digH[i] = (-digH[i] ^ 0xFFFF) + 1 def readData(): data = [] for i in range (0xF7, 0xF7+8): data.append(bus.read_byte_data(i2c_address,i)) pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4) temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4) hum_raw = (data[6] << 8) | data[7] temp = compensate_T(temp_raw) pres = compensate_P(pres_raw) humi = compensate_H(hum_raw) print round(temp,2) print round(pres,2) print round(humi,2) json_body = [ { 'measurement': measurement, 'tags': tags, 'fields': {'temp': round(temp,2) , 'humi': round(humi,2) , 'hpa': round(pres,2)} } ] client.write_points(json_body) print(json_body) def compensate_P(adc_P): global t_fine pressure = 0.0 v1 = (t_fine / 2.0) - 64000.0 v2 = (((v1 / 4.0) * (v1 / 4.0)) / 2048) * digP[5] v2 = v2 + ((v1 * digP[4]) * 2.0) v2 = (v2 / 4.0) + (digP[3] * 65536.0) v1 = (((digP[2] * (((v1 / 4.0) * (v1 / 4.0)) / 8192)) / 8) + ((digP[1] * v1) / 2.0)) / 262144 v1 = ((32768 + v1) * digP[0]) / 32768 if v1 == 0: return 0 pressure = ((1048576 - adc_P) - (v2 / 4096)) * 3125 if pressure < 0x80000000: pressure = (pressure * 2.0) / v1 else: pressure = (pressure / v1) * 2 v1 = (digP[8] * (((pressure / 8.0) * (pressure / 8.0)) / 8192.0)) / 4096 v2 = ((pressure / 4.0) * digP[7]) / 8192.0 pressure = pressure + ((v1 + v2 + digP[6]) / 16.0) return (pressure/100) #print "pressure : %7.2f hPa" % (pressure/100) def compensate_T(adc_T): global t_fine v1 = (adc_T / 16384.0 - digT[0] / 1024.0) * digT[1] v2 = (adc_T / 131072.0 - digT[0] / 8192.0) * (adc_T / 131072.0 - digT[0] / 8192.0) * digT[2] t_fine = v1 + v2 temperature = t_fine / 5120.0 return temperature #print "temp : %-6.2f ℃" % (temperature) def compensate_H(adc_H): global t_fine var_h = t_fine - 76800.0 if var_h != 0: var_h = (adc_H - (digH[3] * 64.0 + digH[4]/16384.0 * var_h)) * (digH[1] / 65536.0 * (1.0 + digH[5] / 67108864.0 * var_h * (1.0 + digH[2] / 67108864.0 * var_h))) else: return 0 var_h = var_h * (1.0 - digH[0] * var_h / 524288.0) if var_h > 100.0: var_h = 100.0 elif var_h < 0.0: var_h = 0.0 return var_h #print "hum : %6.2f %" % (var_h) def setup(): osrs_t = 1 #Temperature oversampling x 1 osrs_p = 1 #Pressure oversampling x 1 osrs_h = 1 #Humidity oversampling x 1 mode = 3 #Normal mode t_sb = 5 #Tstandby 1000ms filter = 0 #Filter off spi3w_en = 0 #3-wire SPI Disable ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode config_reg = (t_sb << 5) | (filter << 2) | spi3w_en ctrl_hum_reg = osrs_h writeReg(0xF2,ctrl_hum_reg) writeReg(0xF4,ctrl_meas_reg) writeReg(0xF5,config_reg) setup() get_calib_param() if __name__ == '__main__': try: readData() except KeyboardInterrupt: pass
Cronで一定時間おきに計測
$ crontab -e */3 * * * * /usr/bin/python /home/pi/script/bme280.py
これで定期的にラズパイ+BME280で温度・湿度・気圧の計測をしてInfuxDBに投げてGrafanaに表示できるようになりました。
グラフ表示ツールGrafanaのjsonコード
BME280で取得したデータをInfuxDBに保存してGrafanaで表示してみました。BME280は以下のRaspi4Bのデータです。その他はWxBeacon2やESP8266にセンサーをつけて計測しています。

以下をjsonコードをコピペすると簡単に上画像のようなGrafanaのグラフを表示できます。
jsonのインサート方法は以下のページをご覧ください。

{ "annotations": { "list": [ { "builtIn": 1, "datasource": "-- Grafana --", "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "gnetId": null, "graphTooltip": 0, "id": 6, "links": [], "panels": [ { "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, "datasource": "Sensor", "fill": 1, "fillGradient": 0, "gridPos": { "h": 6, "w": 11, "x": 0, "y": 0 }, "id": 2, "legend": { "avg": false, "current": true, "max": false, "min": false, "rightSide": true, "show": true, "total": false, "values": true }, "lines": true, "linewidth": 1, "links": [], "nullPointMode": "null", "options": { "dataLinks": [] }, "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "alias": "Raspi4B", "groupBy": [], "measurement": "air5", "orderByTime": "ASC", "policy": "default", "refId": "G", "resultFormat": "time_series", "select": [ [ { "params": [ "temp" ], "type": "field" } ] ], "tags": [ { "key": "host", "operator": "=", "value": "raspi4B" } ] } ], "thresholds": [], "timeFrom": null, "timeRegions": [], "timeShift": null, "title": "温度(TEMP)", "tooltip": { "shared": true, "sort": 0, "value_type": "individual" }, "type": "graph", "xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", "label": "°C", "logBase": 1, "max": null, "min": null, "show": true }, { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true } ], "yaxis": { "align": false, "alignLevel": null } }, { "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, "datasource": "Sensor", "fill": 1, "fillGradient": 0, "gridPos": { "h": 5, "w": 11, "x": 0, "y": 6 }, "id": 4, "legend": { "avg": false, "current": true, "max": false, "min": false, "rightSide": true, "show": true, "total": false, "values": true }, "lines": true, "linewidth": 1, "links": [], "nullPointMode": "null", "options": { "dataLinks": [] }, "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "alias": "Raspi4B", "groupBy": [], "hide": false, "measurement": "air5", "orderByTime": "ASC", "policy": "default", "refId": "G", "resultFormat": "time_series", "select": [ [ { "params": [ "humi" ], "type": "field" } ] ], "tags": [ { "key": "place", "operator": "=", "value": "leaf-bme280" } ] } ], "thresholds": [], "timeFrom": null, "timeRegions": [], "timeShift": null, "title": "湿度(Humi)", "tooltip": { "shared": true, "sort": 0, "value_type": "individual" }, "type": "graph", "xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", "label": "%", "logBase": 1, "max": null, "min": null, "show": true }, { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true } ], "yaxis": { "align": false, "alignLevel": null } }, { "aliasColors": {}, "bars": false, "dashLength": 10, "dashes": false, "datasource": "Sensor", "fill": 1, "fillGradient": 0, "gridPos": { "h": 5, "w": 11, "x": 0, "y": 11 }, "id": 6, "legend": { "avg": false, "current": true, "max": false, "min": false, "show": true, "total": false, "values": true }, "lines": true, "linewidth": 1, "links": [], "nullPointMode": "null", "options": { "dataLinks": [] }, "percentage": false, "pointradius": 5, "points": false, "renderer": "flot", "seriesOverrides": [], "spaceLength": 10, "stack": false, "steppedLine": false, "targets": [ { "alias": "Raspi4B", "groupBy": [], "measurement": "air5", "orderByTime": "ASC", "policy": "default", "refId": "B", "resultFormat": "time_series", "select": [ [ { "params": [ "hpa" ], "type": "field" } ] ], "tags": [ { "key": "place", "operator": "=", "value": "leaf-bme280" } ] } ], "thresholds": [], "timeFrom": null, "timeRegions": [], "timeShift": null, "title": "気圧", "tooltip": { "shared": true, "sort": 0, "value_type": "individual" }, "type": "graph", "xaxis": { "buckets": null, "mode": "time", "name": null, "show": true, "values": [] }, "yaxes": [ { "format": "short", "label": "hPa", "logBase": 1, "max": null, "min": null, "show": true }, { "format": "short", "label": null, "logBase": 1, "max": null, "min": null, "show": true } ], "yaxis": { "align": false, "alignLevel": null } } ], "refresh": false, "schemaVersion": 20, "style": "dark", "tags": [], "templating": { "list": [] }, "time": { "from": "now-6h", "to": "now" }, "timepicker": { "refresh_intervals": [ "5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d" ] }, "timezone": "", "title": "BME280", "uid": "oJBW1Xzgz", "version": 7 }
無事にラズパイで温度・湿度・大気圧を計測することができました♪
以下、BMP280などの備忘録メモです。読み飛ばしてください。
その後、抜いたり刺したりしたら77になった・・・
GPIOを抜き差ししているときに0x76から0x77になった?ので変更する。こんなことってあるのかな?ピン間違えてる?
$ sudo i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- 23 -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- 77
13行目「i2c_address = 0x77」この部分の「0x76」から「0x77」に変更した。
$ python bme280.py 24.98 1009.07 31.86 [{'fields': {'hpa': 1009.07, 'humi': 31.86, 'temp': 24.98}, 'tags': {'host': 'raspi3B+', 'place': 'leaf-bme280'}, 'measurement': 'air5'}]
問題なく取得できた。
GNDに指すSDOを3.3Vに刺したら77になった。
BMP280は湿度センサがないので使わない
BME280が本物?らしい・・・BMP280は湿度センサがないらしい・・・意味ないなぁ。
BMP280は以下のソースでちゃんと取得できる。
PIN
BMP280 | Raspberry Pi |
---|---|
VCC | 3.3V |
GND | GND |
SCL | GPIO3(SCL) |
SDA | GPIO2(SDA) |
CSB | – |
SDD | – |
下準備
$ sudo i2cdetect -y 1 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- 76 --
BME280用のPythonは動かなかった。
$ wget https://github.com/SWITCHSCIENCE/samplecodes/tree/master/BME280/Python27/bme280_sample.py $ sudo pip install smbus2 $ sudo python bme280_sample.py Traceback (most recent call last): File "bme280_sample.py", line 138, in <module> setup() File "bme280_sample.py", line 133, in setup writeReg(0xF2,ctrl_hum_reg) File "bme280_sample.py", line 19, in writeReg bus.write_byte_data(i2c_address,reg_address,data) File "/usr/local/lib/python2.7/dist-packages/smbus2/smbus2.py", line 391, in write_byte_data ioctl(self.fd, I2C_SMBUS, msg) IOError: [Errno 5] Input/output error
コメント