ラズベリーパイで土壌水分センサーを使う part1

今回はラズベリーパイで土壌水分センサーを使ってみました

この記事では、ラズベリーパイで土壌水分センサーを使用し、水分値を読み取るための配線方法と、プログラミングをして実際に水分値を読み取るまでの手順を紹介します

道具の準備

購入したもの

  • 水やり装置キット(amazon: 2280円)
  • MCP3008(秋月電子: 350円)

すでに持っているもの

  • ラズベリーパイ本体(Raspberry Pi 4B)
  • ブレッドボード
  • ケーブル類

MCP3008でアナログ値を読み取る

土壌水分センサーは、土壌中の水分量を測定するために使われます。このセンサーは、測定面の水分が増えると電気抵抗値が上がるという単純な仕組みを利用して測定されます。そのため、センサーを利用する際には、測定された電圧の変化を読み取ることになります。

一方、ラズベリーパイには電圧値を直接読み取ることができるピンが存在しないため、デジタル通信を用いて値を読み出す必要があります。そのため、アナログ値をデジタル値に変換するチップを仲介させることで、ラズベリーパイからデジタル信号として値を読み出すことができます。

MCP3008は、アナログ信号をデジタル信号に変換するためのデバイスです。このデバイスは、最大8つのアナログ信号を読み取ることができます。

(※ ChatGPTに書いてもらいました)

配線

MCP3008(チップ)の左側が土壌水分センサーの配線、右側がラズベリーパイの配線です

MCP3008チップの左側はアナログのインプットが8個並んでいます。画像では土壌水分センサーの出力ケーブルが2本刺さってます。ブレッドボードの左端レーンからはセンサーに電源を供給しています。

チップの右側は上から順に、ラズベリーパイの

  • 3.3V
  • 3.3V
  • GND
  • GPIO11(SPI0_CLK)
  • GPIO9(SPI0_MISO)
  • GPIO10(SPI0_MOSI)
  • GPIO8(SPI_CE0_N)
  • GND

に繋いでいます

ラズベリーパイには、SPI通信するためのピンがあらかじめ決められています

ちなみに

CLKはクロック信号を送る(チップを動作させる)

MISOは(Master Input Slave Output)の略で、チップ側の出力に繋いでくださいという意味

MOSIは(Master Output Slave Input)の略で、チップ側の入力に繋いでくださいという意味

CSはチップセレクトの略で、複数のチップを並列に繋いで使う場合に、チップ選択をするための信号を送ります

SPI通信は、これらの4つの配線を繋ぐことで外部の機器とデジタル通信を行います

エクセルで配線を図にしてみました

MCP3008 配線

特に抵抗などを使うこともなく、簡単に配線することができます

また、ラズベリーパイでSPI通信を行うためには、「ラズベリーパイの設定」からSPI通信をONにする必要があります。(設定後は再起動)

プログラミング

配線が終わったら次はプログラムを書きます

ChatGPTにお願いするとササッとコードを書いてくれました

Raspbery PiとMCP3008でアナログ値をやり取りするPythonコード

Raspberry PiとMCP3008を使用してアナログ値をやり取りするPythonコードの例を次に示します。
まず、必要なPythonモジュールをインポートします。

import spidev # SPIデバイス用のモジュール
import time   # time.sleep用のモジュール

次に、SPIデバイスを開き、MCP3008と通信するためのインスタンスを作成します。

# SPIデバイスのインスタンスを作成
spi = spidev.SpiDev()

# MCP3008と接続されたSPIデバイスを開く
spi.open(0, 0)

そして、MCP3008からアナログ値を取得するための関数を定義します。

def read_mcp3008(ch):
    # MCP3008に対してアナログ値を取得するためのコマンドを送信
    cmd = [1, (8 + ch) << 4, 0]
    reply = spi.xfer2(cmd)

    # 取得したデータからアナログ値を計算
    value = ((reply[1] & 3) << 8) + reply[2]

    return value

最後に、定期的にアナログ値を取得して表示するプログラムを書きます。

while True:
    # MCP3008の1番ピンからアナログ値を取得
    value = read_mcp3008(1)

    # 取得した値を表示
    print(value)

    # 0.1秒間待機
    time.sleep(0.1)

うーん、凄いw

修正を加えた最終的なコードはこちら

import spidev # SPIデバイス用のモジュール
import time   # time.sleep用のモジュール

# SPIデバイスのインスタンスを作成
spi = spidev.SpiDev()
# MCP3008と接続されたSPIデバイスを開く
spi.open(0, 0)
spi.max_speed_hz = 100000

def read_mcp3008(ch):
    # MCP3008に対してアナログ値を取得するためのコマンドを送信
    cmd = [1, (8 + ch) << 4, 0]
    reply = spi.xfer2(cmd)

    # 取得したデータからアナログ値を計算
    value = ((reply[1] & 3) << 8) + reply[2]
    return value

while True:
    # MCP3008の1番ピンからアナログ値を取得
    value = [read_mcp3008(i) for i in range(8)]

    # 取得した値を表示
    print(value)

    # 0.1秒間待機
    time.sleep(1)

3.3Vの場合、spiのスピードを10KHzにしないといけないという記事を見つけたので、それを適用しています

8ピン同時にアナログ値を取得して表示するようにしました

出力結果

MCP3008の分解能が10bitなので電圧を0~1023までの数値で取得できます

(※ 繋いでないピンからはノイズ的な数値を拾ってます)

空気中だと873という数値が取得できました

手で握ると520くらいまで下がりました

水分が高くなるほど電気抵抗が上がり、電圧が下がるためアナログ値は低くなります

そのため、水分を%表示にするには換算が必要です。

次に、水に浸します

結果

424くらいまで下がりました

先端だけ漬けた場合や、半分だけ浸した場合は数値が高く出たため、土壌で使う場合は基準線の位置までしっかり刺さないといけませんね

この仕組みなら、高さ方向で平均的な水分を計測してくれるため、層による水分値のバラツキを意識しなくて良さそうです

水分0%のときにアナログ値873、水分100%のときにアナログ値423と考えると、水分%を導き出す計算式は

(873 – アナログ値) * 100 / 450

で良さそうです

一応、ダメ元でChatGPTに聞いてみると

アナログ値873を0%、アナログ値423を100%としたときにパーセント表示で出力できるようにできませんか?

Raspberry PiとMCP3008を使用してアナログ値を取得し、その値を0%から100%の値に変換して表示することができます。
まず、前述の例のプログラムに、0%に対応するアナログ値を表す変数 zero、100%に対応するアナログ値を表す変数 hundred を追加します。

import spidev # SPIデバイス用のモジュール
import time   # time.sleep用のモジュール

# 0%に対応するアナログ値
zero = 873

# 100%に対応するアナログ値
hundred = 423

# SPIデバイスのインスタンスを作成
spi = spidev.SpiDev()

# MCP3008と接続されたSPIデバイスを開く
spi.open(0, 0)

def read_mcp3008(ch):
    # MCP3008に対してアナログ値を取得するためのコマンドを送信
    cmd = [1, (8 + ch) << 4, 0]
    reply = spi.xfer2(cmd)

    # 取得したデータからアナログ値を計算
    value = ((reply[1] & 3) << 8) + reply[2]

    return value

次に、取得したアナログ値を0%から100%の値に変換するための関数を定義します。

def to_percent(value):
    # アナログ値からパーセント値に変換
    percent = (value - zero) / (hundred - zero) * 100

    return percent

最後に、定期的にアナログ値を取得して、その値を0%から100%の値に変換して表示するようにプログラムを書き換えます。

while True:
    # MCP3008の1番ピンからアナログ値を取得
    value = read_mcp3008(1)

    # 取得した値を0%から100%の値に変換
    percent = to_percent(value)

    # 変換した値を表示
    print(percent)

    # 0.1秒

どひゃー(目玉が飛び出る顔)

どひゃー(目玉が飛び出る顔)とは、驚きや楽しみ、ほめるなどの感情を表すために使われる顔文字です。 この顔文字は、「~で驚いている」という意味で、大きな目玉が飛び出した顔を表しています。
例えば、以下のような文章とともに使われることがよくあります。
どひゃー!この料理、美味しい!
どひゃー!この絵、かわいい!
どひゃー!この話、面白い!
顔文字は、文章や会話に自然な感情を表す効果があるため、よく使われます。 どひゃー(目玉が飛び出る顔)も、その一つです。

ChatGPT先生と呼ばせてください

使い回しやすいように外部モジュール化しました

import spidev # SPIデバイス用のモジュール

# SPIデバイスのインスタンスを作成
spi = spidev.SpiDev()
# MCP3008と接続されたSPIデバイスを開く
spi.open(0, 0)
spi.max_speed_hz = 100000

def read_mcp3008(ch):
    # MCP3008に対してアナログ値を取得するためのコマンドを送信
    cmd = [1, (8 + ch) << 4, 200]
    reply = spi.xfer2(cmd)

    # 取得したデータからアナログ値を計算
    value = ((reply[1] & 3) << 8) + reply[2]

    return value

def read_moisture(ch):
    return round((1023 - read_mcp3008(ch) - 150)*100/450,1)

def get_moisture_count():
    return 8

def get_moisture_name(i):
    return "水分計" + str(i)

このファイルをメインプログラムと同フォルダに置き

from moisture import *

と書いてインポートすることで、read_moisture関数を使って土壌水分センサーから水分値を読み取ることができるようになります

まとめ

今回はラズベリーパイから土壌水分センサーを扱う方法を具体的に紹介しました

要点をまとめると

  • センサー類のアナログ値は、電気抵抗により変化した電圧を読み取る
    • ラズベリーパイからアナログ値を読み取るためにはアナログ変換チップ(ADCコンバータ)が必要
    • MCP3008を使えば最大8個まで同時にアナログ値を読み取ることが出来る
    • ラズベリーパイとMCP3008はSPI通信でやりとりする
    • SPI通信には4本の線が必要であり、使用するピンは決まっている
    • アナログ値は0~1023までの電圧の割合を示す数値で取得され、人間が分かりやすい数値にするためには換算する必要がある
    • 土壌センサーは接触面積によって数値が変わるため、基準線までしっかり挿す必要がある

    という事でした

    次回は、実際に土壌の水分を測定したり、水分のコントロールに挑戦してみます

    おすすめ

    1件の返信

    1. 2023年1月1日

      […] 今回は、実際に土に対して土壌水分センサーを使用していきたいと思います。(前回の記事) […]

    コメントを残す

    メールアドレスが公開されることはありません。 が付いている欄は必須項目です