2022.09.16
技術関連

IoTのためのクラウドシステム構築①

IoTのためのクラウドシステム構築①

こんにちは!園部です。

今回は先日記事にした、研修の「IoTのためのクラウドシステム構築」の内容を書いていきます。

※本記事で扱うプログラムは全てpythonです

◇ 全体の流れ

  1. マイコンへのセンサー接続
  2. クラウド側へのデータ保存
  3. センサデータの可視化
  4. センサデータに基づく電子メールの送信
  5. アクチュエータの制御

※本日はマイコンへのセンサー接続について説明します

◇ 用意する物

  • RaspberryPi
  • ブレッドボード
  • AE-BME280(温度センサ)
  • AE-DRV8830(モータードライバモジュール)
  • 積層セラミックコンデンサ 0.1μF、10μF、1000pF
  • モーター

※今回はマブチモーターのRE-280RAというモーターを使用しましたが、AE-DRV8830の過電流防止の仕様により、動作が安定しなかったため、もう少し消費電力の小さいモーターが良いと思います。

1.マイコンへのセンサー接続

まずはAE-BME280という温湿度気圧センサモジュールを使ってデータをコンソールに表示します。

■ RaspberryPiの用意

RaspberryPiを用意します。
今回はRaspberryPi4を使用しますが、RaspberryPiシリーズなら何でも良いです。
RaspberryPiの初期セットアップについては調べるとたくさん出てくるので他のブログを参考にしてください。
今回はTeraTeamを使ってSSHで操作しますので、RaspberryPi設定でSSHを有効化します。
またセンサはI2Cという通信方法を使用しますので、I2Cも有効にします。

■ センサモジュールの用意

今回のAE-BME280とAE-DRV8830は秋葉原の秋月電子で購入しました。
AE-BME280にピンヘッダをはんだ付けし、ブレッドボードに刺せるようにします。
今回はI2C接続にするので、モジュール右上のジャンパ設定のJ3をはんだでジャンパします。

bme画像

■ モータードライバモジュールの用意

すぐには使いませんが、とりあえず用意しておきます。
センサと同じように、AE-DRV8830にピンヘッダをはんだ付けし、ブレッドボードに刺せるようにします。

AE-DRV8830

■ ブレッドボードで回路作成

まずは図の通りにRaspberryPiのピンと接続します。

配線図

 

RaspberryPiはI2Cのチャンネルが1つしかないので、2つのモジュールを並列で接続しています。
コンデンサは電源電圧の安定化と、ノイズの吸収の役割をしています。
図はモーターとセンサの電源を電池ボックスとRaspberryPiの電源で分けていますが、どちらもRaspberryPiの電源で構いません。

配線後の画像

■ RaspberryPiにSSHで接続する

RaspberryPiがインターネットに繋がる状態にします。
TeraTermをインストールし、RaspberryPiのIPアドレスを入力し、接続します。
RaspberryPiのIPアドレスはRaspberryPiのターミナルで”ip a”とかコマンドを叩くと出ます。

■ プログラムの実行

I2Cをpythonから制御するためのライブラリをインストールします。

$ pip install smbus2

RaspberryPiはI2Cのチャンネルが1つしかないので、2つのモジュールを並列で接続しています。
i2ctoolsでセンサのI2Cアドレスが取れるか確認する。(0x64と0x76がとれるはず)

$ i2cdetect -y 1

例で載せている画像はDRV8830を接続していませんが、こんな感じで表示されます。

i2cアドレス画像

アドレスが確認出来たらハード的な準備は完了です。

■ pythonプログラムの作成

やり方はRaspberryPiのエディターを使っても良いですが、せっかくsshで繋げているので日頃使っているPCで作成して、winSCPなどのソフトを使ってRaspberryPiに渡すやり方が楽だと思います。

bme280_get_data.py

  1. #coding: utf-8
  2. import time
  3. import datetime
  4. import json
  5.  
  6. from smbus2 import SMBus
  7.  
  8. class BME280:
  9. def __init__(self):
  10. self.bus_number = 1
  11. self.i2c_address = 0x76
  12.  
  13. self.bus = SMBus(self.bus_number)
  14.  
  15. self.digT = []
  16. self.digP = []
  17. self.digH = []
  18.  
  19. self.t_fine = 0.0
  20.  
  21. #時間を文字列に変換する
  22. def support_datetime_default(sel,o):
  23. if isinstance(o, datetime.datetime):
  24. return o.isoformat()
  25.  
  26. def writeReg(self,reg_address, data):
  27. self.bus.write_byte_data(self.i2c_address,reg_address,data)
  28.  
  29. def get_calib_param(self):
  30. calib = []
  31.  
  32. for i in range (0x88,0x88+24):
  33. calib.append(self.bus.read_byte_data(self.i2c_address,i))
  34. calib.append(self.bus.read_byte_data(self.i2c_address,0xA1))
  35. for i in range (0xE1,0xE1+7):
  36. calib.append(self.bus.read_byte_data(self.i2c_address,i))
  37.  
  38. self.digT.append((calib[1] << 8) | calib[0])
  39. self.digT.append((calib[3] << 8) | calib[2])
  40. self.digT.append((calib[5] << 8) | calib[4])
  41. self.digP.append((calib[7] << 8) | calib[6])
  42. self.digP.append((calib[9] << 8) | calib[8])
  43. self.digP.append((calib[11]<< 8) | calib[10])
  44. self.digP.append((calib[13]<< 8) | calib[12])
  45. self.digP.append((calib[15]<< 8) | calib[14])
  46. self.digP.append((calib[17]<< 8) | calib[16])
  47. self.digP.append((calib[19]<< 8) | calib[18])
  48. self.digP.append((calib[21]<< 8) | calib[20])
  49. self.digP.append((calib[23]<< 8) | calib[22])
  50. self.digH.append( calib[24] )
  51. self.digH.append((calib[26]<< 8) | calib[25])
  52. self.digH.append( calib[27] )
  53. self.digH.append((calib[28]<< 4) | (0x0F & calib[29]))
  54. self.digH.append((calib[30]<< 4) | ((calib[29] >> 4) & 0x0F))
  55. self.digH.append( calib[31] )
  56.  
  57. for i in range(1,2):
  58. if self.digT[i] & 0x8000:
  59. self.digT[i] = (-self.digT[i] ^ 0xFFFF) + 1
  60.  
  61. for i in range(1,8):
  62. if self.digP[i] & 0x8000:
  63. self.digP[i] = (-self.digP[i] ^ 0xFFFF) + 1
  64.  
  65. for i in range(0,6):
  66. if self.digH[i] & 0x8000:
  67. self.digH[i] = (-self.digH[i] ^ 0xFFFF) + 1
  68.  
  69. #センサデータをmqttメッセージにして返す
  70. def getMessage(self):
  71. data = self.readData()
  72. sensor = {}
  73. sensor[‘temp’] = float(data[0])
  74. sensor[‘hum’] = float(data[2])
  75. sensor[‘pres’] = float(data[1])
  76.  
  77. message = {}
  78. message[‘timestamp’] = datetime.datetime.now() datetime.timedelta(hours=9)#KIBANAに入力される時間はUTC扱いになるため、9時間引いて日本時間にする
  79. message[‘sensor’] = sensor
  80.  
  81. messageJson = json.dumps(message, default=self.support_datetime_default)
  82. return messageJson
  83.  
  84.  
  85. #センサデータを読み取って、温度、湿度、気圧をリスト化して返す。
  86. def readData(self):
  87. self.setup()
  88. self.get_calib_param()
  89. data = []
  90. for i in range (0xF7, 0xF7+8):
  91. data.append(self.bus.read_byte_data(self.i2c_address,i))
  92. pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
  93. temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
  94. hum_raw = (data[6] << 8) | data[7]
  95.  
  96. t = self.compensate_T(temp_raw)
  97. p = self.compensate_P(pres_raw)
  98. h = self.compensate_H(hum_raw)
  99.  
  100. list = [t,p,h]
  101. return list
  102.  
  103. def compensate_P(self,adc_P):
  104. pressure = 0.0
  105.  
  106. v1 = (self.t_fine / 2.0) 64000.0
  107. v2 = (((v1 / 4.0) * (v1 / 4.0)) / 2048) * self.digP[5]
  108. v2 = v2 + ((v1 * self.digP[4]) * 2.0)
  109. v2 = (v2 / 4.0) + (self.digP[3] * 65536.0)
  110. v1 = (((self.digP[2] * (((v1 / 4.0) * (v1 / 4.0)) / 8192)) / 8) + ((self.digP[1] * v1) / 2.0)) / 262144
  111. v1 = ((32768 + v1) * self.digP[0]) / 32768
  112.  
  113. if v1 == 0:
  114. return 0
  115. pressure = ((1048576 adc_P) (v2 / 4096)) * 3125
  116. if pressure < 0x80000000:
  117. pressure = (pressure * 2.0) / v1
  118. else:
  119. pressure = (pressure / v1) * 2
  120. v1 = (self.digP[8] * (((pressure / 8.0) * (pressure / 8.0)) / 8192.0)) / 4096
  121. v2 = ((pressure / 4.0) * self.digP[7]) / 8192.0
  122. pressure = pressure + ((v1 + v2 + self.digP[6]) / 16.0)
  123. pres = format(pressure,“7.2f”)
  124.  
  125. print (“pressure : %7.2f hPa” % (pressure/100))
  126. return pres
  127.  
  128. def compensate_T(self,adc_T):
  129. v1 = (adc_T / 16384.0 self.digT[0] / 1024.0) * self.digT[1]
  130. v2 = (adc_T / 131072.0 self.digT[0] / 8192.0) * (adc_T / 131072.0 self.digT[0] / 8192.0) * self.digT[2]
  131. self.t_fine = v1 + v2
  132. temperature = self.t_fine / 5120.0
  133. temp = format(temperature,“-6.2f”)
  134. print (“temp : %-6.2f ℃” % (temperature))
  135. return temp
  136.  
  137. def compensate_H(self,adc_H):
  138. var_h = self.t_fine 76800.0
  139. if var_h != 0:
  140. var_h = (adc_H (self.digH[3] * 64.0 + self.digH[4]/16384.0 * var_h)) * (self.digH[1] / 65536.0 * (1.0 + self.digH[5] / 67108864.0 * var_h * (1.0 + self.digH[2] / 67108864.0 * var_h)))
  141. else:
  142. return 0
  143. var_h = var_h * (1.0 self.digH[0] * var_h / 524288.0)
  144. if var_h > 100.0:
  145. var_h = 100.0
  146. elif var_h < 0.0:
  147. var_h = 0.0
  148. print (“hum : %6.2f %” % (var_h))
  149. h = format(var_h,“6.2f”)
  150. return h
  151.  
  152.  
  153. def setup(self):
  154. osrs_t = 1 #Temperature oversampling x 1
  155. osrs_p = 1 #Pressure oversampling x 1
  156. osrs_h = 1 #Humidity oversampling x 1
  157. mode = 3 #Normal mode
  158. t_sb = 5 #Tstandby 1000ms
  159. filter = 0 #Filter off
  160. spi3w_en = 0 #3-wire SPI Disable
  161.  
  162. ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode
  163. config_reg = (t_sb << 5) | (filter << 2) | spi3w_en
  164. ctrl_hum_reg = osrs_h
  165.  
  166. self.writeReg(0xF2,ctrl_hum_reg)
  167. self.writeReg(0xF4,ctrl_meas_reg)
  168. self.writeReg(0xF5,config_reg)
  169.  
  170. def main():
  171. bme = BME280()
  172. data_list = bme.readData()
  173. if __name__ == ‘__main__’:
  174. main()

プログラムを実行します( 以下の例はホームディレクトリに作成した場合)

$ python bme280_get_data.py

温度、湿度、気圧が表示されれば成功です!

・ センサデータ出力

bme280_get_data.pyのメソッド

  • compensate_P(気圧)
  • compensate_T(気温)
  • compensate_H(湿度)

の中でprintしています。

今後センサを使うプログラムはこのプログラムを呼び出すようにします。

今回はマイコンへのセンサー接続を紹介していきました。

次からはいよいよクラウドが登場しますので、お楽しみに!

Contact

各サービスの詳しい資料のダウンロードや、
ご相談・お問い合わせ等お気軽にお問い合わせください。

資料ダウンロード一覧へ お問い合わせフォームへ