VNC環境とRaspberry Piで簡単電子工作(7)

2017年10月13日(金)
青柳 良介福田 紘也

はじめに

これまで、デジタル出力からPWM出力、アナログ入力までのデバイス周りの内容は一通り解説してきました。また、直近の2回ではRaspberry Piを使い易くするための環境について紹介しました。

今回からは本編に戻り、通信やシステム化などのテーマで解説していきます。今回は、主に有線での通信について解説します。

有線通信と無線通信との違い

通信の種類は有線と無線に大別されます。最近では無線LANやBluetoothなどが普及しているので、無線通信についても身近になってきていると思います。

まず、有線と無線の違いについて触れていきます。有線は直接つなぐことができため通信が安定し、大容量のデータにも対応可能で、セキュリティも比較的安全です。しかし、接続数に限界があり、通信の接続数が増えると物理的な問題が生じ易くなります。

一方、無線は通信の種類により異なる接続距離を考慮できれば、どこでも接続できるようになるため便利です。一方で、通信の不安定さやデータ転送、セキュリティなど、有線と比較して注意すべき点が多くあります。

このように、有線と無線それぞれの特徴を捉えることは、状況に応じた検討を行う際に非常に重要です。そのため、今回と次回で有線と無線を分けて解説することにします。

今回は、Raspberry PiとPCをシリアル通信で接続するI2C通信を行う方法を解説します。

用意するもの

今回はRaspberry PiとPC以外にも、様々なパーツを使用します。少し準備が大変かも知れませんが、頑張って集めましょう。また、第2回で作成した回路1も使用します(詳細は第2回を参照)。

  • Raspberry Pi 3B
  • microSDカード(OSインストール済みのもの。第1回を参照)
  • PC
  • USBシリアル変換モジュール(FT-232RQ)
  • 温湿度・気圧センサモジュール(AE-BME280)
  • キャラクタ液晶ディスプレイモジュール(AE-AQM0802+PCA9515)
  • はんだこて
  • はんだ
  • こて台
  • ブレッドボード
  • ジャンパーワイヤ オス-メス
  • ジャンパーワイヤ オス-オス

なお、第2回で作成した回路1は、下記のパーツを使用しています(詳細は第2回を参照)。

  • LED
  • 押しスイッチ
  • ブレッドボード
  • ジャンパーワイヤ オス-メス
  • 抵抗(330Ω×2:LED側/10kΩ:スイッチ側)

Raspberry PiとPCをシリアル通信で接続しよう

シリアル通信の準備

シリアル通信とはコンピュータを接続する方法の1つで、相互接続して1度に1ビットずつ逐次的にデータを送ることをいいます。1ビットは2進数(0と1)の1桁分の情報量です。

まず、下図のようにRaspberry PiとUSBシリアル変換モジュール(FT-232RQ)をつなぎます。

接続後、Raspberry Piに電源ケーブルをつなぎ、Raspberry Piを起動させます。

PC側で接続が認識されると、デバイスマネージャー上でポート(COM No.)が追加されます。

ポートを確認後(図の場合ではCOM5)、PC側でTera Termを立ち上げます。これまでは接続先としてTCP/IPを選択していましたが、今回はシリアルを選択します。先ほどのポートを選択し、Raspberry Piのシリアル通信のボー・レートは115200と設定されているので、「設定」→「シリアルポート」でTera Termのボー・レートの設定を変更します。

Tera Termの画面上には何も表示されていませんが、[Enter]キーを1回押すとログインプロンプトが表示されます。

raspberrypi loginに「pi」、パスワードに「raspberry」を入力して[Enter]キーを押します。下の画面が表示されると接続成功です。

なお、シリアル通信の接続は「exit」コマンドを入力することで終了できます。Raspberry Piも終了する場合は「sudo shutdown –h now」コマンドを入力します。

Lチカを試してみよう

それでは、シリアル通信で恒例のLチカテストを実行してみましょう。nanoエディタを立ち上げ、「light1.py」というファイルに以下のプログラムを入力します(第2回でも使用)。

from gpiozero import LED
from time import sleep

led = LED(17)
while True:
led.on()
sleep(1)
led.off()
sleep(1)

保存した後、プログラムを実行すると、2つのLEDが点滅します。このプログラムの実行結果は、下記の動画を参照してください。

Raspberry PiでI2C通信を使ってみよう

モジュールの準備

I2C通信とは、フィリップス社で開発されたシリアル通信の1種です。Raspberry Piとモジュールをシリアル通信でつなぐことができます。I2C通信はデータ線とクロック線の2本の信号線で接続しますが、この信号線にモジュールを並列に接続していくことで複数のモジュールをつなぐことも可能になります。

今回はモジュールとして、温湿度・気圧センサ(BME280)とキャラクタ液晶ディスプレイ(AQM0802)を使用しますが、各モジュールとも使用前にはんだ付けが必要です。なお、本稿でははんだ付け方法の解説は割愛しますので、各自インターネットや書籍などを参考に作業を行ってください。

はんだ付け後の各モジュールは以下のようになります。

AQM0802を動かしてみよう

AE-AQM0802+PCA9515は、I2C接続小型キャラクタLCD(AQM0802A)をRaspberry Piで使用するために専用IC(PCA9515)を実装したRaspberry Pi用変換モジュールです。Raspberry Piから発信された情報やセンサから取得した情報などをLCDに表示できます。

まずは、このモジュールを使って「Hello World!」をLCD上に表示させます。以下の表と図を参考に配線しましょう。

次に、PC上でRealVNCを立ち上げ、Raspberry Piのデスクトップ上にあるRaspberry Piのアイコンから「Preferences」→「Raspberry Pi Configuration」→「Interfaces」の画面を立ち上げ、I2Cが「enabled」になっているかを確認します。

確認後、以下のコマンドでI2Cライブラリを導入します。

$ sudo apt-get install i2c-tools
$ sudo apt-get python-smbus

ここで、AQM0802がRaspberry PiでI2C通信として認識されているかを確認します。以下のコマンドを入力します。

$ sudo i2cdetect -y 1

下図のように、「3e」(0x3eのアドレス)の表示があれば認識されています。

続いて、LCD上に文字を表示させます。Thonny Python IDEを立ち上げ、以下のプログラムを作成します。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import smbus
import time

class i2clcd:
    i2c = smbus.SMBus(1)
    addr = 0x3e
    contrast = 42   # 0~63

    def __init__(self):
        self.i2c.write_byte_data(self.addr, 0, 0x38)    # function set(IS=0)
        self.i2c.write_byte_data(self.addr, 0, 0x39)    # function set(IS=1)
        self.i2c.write_byte_data(self.addr, 0, 0x14)    # internal osc
        self.i2c.write_byte_data(self.addr, 0,
                    (0x70 | (self.contrast & 0x0f)))    # contrast
        self.i2c.write_byte_data(self.addr, 0,
                    (0x54 | ((self.contrast >> 4) & 0x03)))      # contrast/icon/power
        self.i2c.write_byte_data(self.addr, 0, 0x6B)    # follower control
        # self.i2c.write_byte_data(self.addr, 0, 0x6c)    # follower control
        time.sleep(0.2)

    def clear(self):
        self.i2c.write_byte_data(self.addr, 0, 0x38)    # function set(IS=0)
        self.i2c.write_byte_data(self.addr, 0, 0x70)    # Constract
        self.i2c.write_byte_data(self.addr, 0, 0x0C)    # Display On
        self.i2c.write_byte_data(self.addr, 0, 0x01)    # Clear Display
        self.i2c.write_byte_data(self.addr, 0, 0x06)    # Entry Mode Set
        time.sleep(0.2)

    def puts(self, msg):
        self.i2c.write_byte_data(self.addr, 0, 0x38)    # function set(IS=0)
        [self.i2c.write_byte_data(self.addr, 0x40, ord(c)) for c in msg]

    def setaddress(self, line, col):
        self.i2c.write_byte_data(self.addr, 0, 0x38)    # function set(IS=0)
        self.i2c.write_byte_data(self.addr, 0, 0x80 | (0x40 if line > 0 else 0) | col)

    def setcg(self, no, cg):
        self.i2c.write_byte_data(self.addr, 0, 0x38)    # function set(IS=0)
        self.i2c.write_byte_data(self.addr, 0, 0x40 | (no 

プログラムは「LCD.py」の名前で保存します。プログラムを実行すると、LCD上に「Hello World!」が表示されます。

BME280を動かしてみよう

AE-BME280は、ボッシュ社のBME280を搭載したセンサモジュールで、温度、湿度、気圧の3つの環境情報を同時に測定できます。Raspberry Piとの通信方式はSPIとI2Cを選択でき、今回はI2Cを選択します。

まずは、このモジュールを使ってshell上に温湿度と気温を表示させてみます。

以下の表と図を参考に、配線しましょう。

次に、I2C通信でRaspberry Piに認識されているか確認します。以下のコマンドを入力します。

$ sudo i2cdetect -y 1

下図のように、「76」(0x76のアドレス)の表示があれば認識されています。

shell上にデータを表示させます。以下のプログラムを作成します。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Bme280():
  def __init__(self, i2c_address= 0x76, bus_number=1):
    import smbus
    self.digT = []
    self.digP = []
    self.digH = []
    self.t_fine = 0
    self.bus_number = bus_number
    self.bus = smbus.SMBus(self.bus_number)
    self.i2c_address = i2c_address
    self.setup()
    self.get_calib_param()
    self.data = []
    for i in range (0xF7, 0xF7+8):
        self.data.append(self.bus.read_byte_data(self.i2c_address,i))
    self.press_raw = (self.data[0] > 4)
    self.temp_raw = (self.data[3] > 4)
    self.humid_raw  = (self.data[6] > 4) & 0x0F))
    self.digH.append( calib[31] )

    for i in range(1,2):
         if self.digT[i] & 0x8000: self.digT[i] = (-self.digT[i] ^ 0xFFFF) + 1

    for i in range(1,8):
         if self.digP[i] & 0x8000: self.digP[i] = (-self.digP[i] ^ 0xFFFF) + 1

    for i in range(0,6):
        if self.digH[i] & 0x8000: self.digH[i] = (-self.digH[i] ^ 0xFFFF) + 1  

  def  get_data(self):
    temp = self.get_temp()
    pressure = self.get_pressure()
    humid = self.get_humid()
    return temp, humid, pressure
    
  def get_pressure(self):
    pressure = 0.0
    v1 = (self.t_fine / 2.0) - 64000.0
    v2 = (((v1 / 4.0) * (v1 / 4.0)) / 2048) * self.digP[5]
    v2 = v2 + ((v1 * self.digP[4]) * 2.0)
    v2 = (v2 / 4.0) + (self.digP[3] * 65536.0)
    v1 = (((self.digP[2] * (((v1 / 4.0) * (v1 / 4.0)) / 8192)) / 8)  + ((self.digP[1] * v1) / 2.0)) / 262144
    v1 = ((32768 + v1) * self.digP[0]) / 32768
     
    if v1 == 0: return 0
    pressure = ((1048576 - self.press_raw) - (v2 / 4096)) * 3125
    if pressure  100.0:
         var_h = 100.0
    elif var_h 

プログラムは「BME280.py」の名前で保存します。プログラムを実行すると、shell上に温湿度と気圧が表示されます。

BME280で取得したデータをAQM0802に表示させよう

ここでは、BME280が取得した温湿度情報をLCD上に表示します。I2C通信を使うと、複数のモジュールを並列に接続できます。BME280を配線しているブレッドボードに、先ほどと同様にAQM0802を配線しましょう。

配線が完了したら、以下のコマンドを入力してI2C通信でRaspberry Piに認識されているかを確認します。

$ sudo i2cdetect -y 1

下図のように、「3e」(AQM0802のアドレス)、「76」(BME280のアドレス)の表示があれば認識されています。

LCD上に温湿度を表示させます。以下のプログラムを作成します。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import smbus
import time
from BME280 import Bme280
from LCD import i2clcd

bme280 = Bme280()
temp, humid, press = bme280.get_data()
lcd = i2clcd()
lcd.clear()

cgchar0 = (0b00000,
           0b00001,
           0b01100,
           0b10010,
           0b10000,
           0b10010,
           0b01100)
lcd.setcg(0, cgchar0)

while True:
        lcd.setaddress(0, 4)
        lcd.puts(str(round(temp,1)))
        lcd.setaddress(1, 4)
        lcd.puts(str(round(humid,1)))

プログラムは「LCD_BME280.py」の名前で保存します。プログラムを実行すると、LCD上に温湿度が表示されます。

ここで、ずっとプログラムを動かしたままにしておくと、以下のエラーが表示されてしまいます。

このエラー内容については、次回で解析していきたいと思います。

おわりに

いかがでしたか? 今回は有線通信をテーマに、シリアル通信とI2C通信を解説しました。特に、I2C通信で複数のモジュール(BME280とAQM0802)がRaspberry Piの2本の信号線(SCLとSDA)で並列につながっていることを確認いただけたでしょうか。これを応用すれば、さらに複雑な回路も設計できるので、活用の幅が大きく広がることが期待できます。

一方、BME280で読み取ったデータがAQM0802にうまく表示されたものの、エラーが起こりました。この内容は次回で解析していきますが、このような「動く」のに「エラー」が出るというのはある意味、I2C通信の特徴かもしれません。

また、今回のプログラムではBME280とAQM0802の基本プログラム(BME280.pyとLCD.py)を作成しておき、基本プログラムを呼び出しながら命令を実行(LCD_BME280.py)しています(今回のプログラムファイルはこちらからダウンロードできます(RaspberryPi07_program)。この考え方(クラス)は、PythonやI2Cにおいて非常に重要になるので、次回のエラー解析が終わった後に、詳細を解説したいと思います。お楽しみに!

Blooomin代表。半導体ベンチャーや米系通信機器メーカーを経て、組み込みシステムのコンサルディングからフィジカル・コンピューティングのコンテンツ開発などに従事。ハードやソフトにおける設計から保守までの勘所を横断的に網羅しております。
Blooomin:https://blooomin.com
S&F PARTNERS副社長。素材メーカーを経てコンサルティング会社で事業開発に従事。その後、ベンチャーにてIoTサービスの企画・運営。現在はロボットプログラミング教室の運営やIoTサービスの企画・開発を展開。S&F PARTNERS:http://www.sf-partners.jp/

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています