kun432's blog

Alexaなどスマートスピーカーの話題中心に、Voiceflowの日本語情報を発信してます。たまにAWSやkubernetesなど。

〜スマートスピーカーやVoiceflowの記事は右メニューのカテゴリからどうぞ。〜

Raspberry Piで観葉植物の監視をする②

f:id:kun432:20220219171625p:plain

前回の続き。

土壌水分センサーがなんとなくうまく動いていないさそう。ということで、ちょっとコードを追っかけてみた。

公式サンプルコード

Python全然わからないけど、

  • get_adcで各センサの電圧値を取得
  • valmapに電圧値を渡してパーセンテージ変換

してるように見える。

def valmap(value, istart, istop, ostart, ostop):
    value = ostart + (ostop - ostart) * ((value - istart) / (istop - istart))
    if value > ostop:
       value = ostop
    return value
                moisture1 = round(valmap(sensor1, 5, 3.5, 0, 100), 0)

5Vなら0%、3.5Vなら100%、というように考えて、センサー値のパーセンテージを算出してるみたいなんだけど、この53.5はどこから算出された数字なのかな?

soil-moistore-sensors.pyから余計なものを取っ払ってセンサーの電圧値だけを表示するコードを書いてみた。

import signal
import sys
import time
import spidev

spi_ch = 0

# Enable SPI
spi = spidev.SpiDev(0, spi_ch)
spi.max_speed_hz = 1200000

def close(signal, frame):
    sys.exit(0)

signal.signal(signal.SIGINT, close)

def get_adc(channel):

    # Make sure ADC channel is 0 or 1
    if channel != 0:
        channel = 1

    # Construct SPI message
    #  First bit (Start): Logic high (1)
    #  Second bit (SGL/DIFF): 1 to select single mode
    #  Third bit (ODD/SIGN): Select channel (0 or 1)
    #  Fourth bit (MSFB): 0 for LSB first
    #  Next 12 bits: 0 (don't care)
    msg = 0b11
    msg = ((msg << 1) + channel) << 5
    msg = [msg, 0b00000000]
    reply = spi.xfer2(msg)

    # Construct single integer out of the reply (2 bytes)
    adc = 0
    for n in reply:
        adc = (adc << 8) + n

    # Last bit (0) is not part of ADC value, shift to remove it
    adc = adc >> 1

    # Calculate voltage form ADC value
    # considering the soil moisture sensor is working at 5V
    voltage = (5 * adc) / 1024

    return voltage

if __name__ == '__main__':
    # Report the channel 0 and channel 1 voltages to the terminal
    try:
        while True:
            adc_0 = get_adc(0)
            adc_1 = get_adc(1)
            sensor1 = round(adc_0, 2)
            sensor2 = round(adc_1, 2)
            print("Soil Moisture Sensor 1:", sensor1, " Soil Moisture Sensor 2:", sensor2)
            time.sleep(0.5)
    except: KeyboardInterrupt

実行してみた。Sensor 1の方。まず水につけない場合。

$ python test-soil-moistore-sensors.py
Soil Moisture Sensor 1: 3.58  Soil Moisture Sensor 2: 0.0
Soil Moisture Sensor 1: 3.57  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 3.57  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 3.57  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 3.57  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 3.57  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 3.57  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 3.58  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 3.58  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 3.57  Soil Moisture Sensor 2: 0.01
(...snip...)

水につけた場合。

$ python test-soil-moistore-sensors.py
Soil Moisture Sensor 1: 1.9  Soil Moisture Sensor 2: 0.0
Soil Moisture Sensor 1: 1.9  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 1.9  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 1.9  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 1.9  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 1.89  Soil Moisture Sensor 2: 0.02
Soil Moisture Sensor 1: 1.9  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 1.9  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 1.9  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 1.89  Soil Moisture Sensor 2: 0.01
Soil Moisture Sensor 1: 1.9  Soil Moisture Sensor 2: 0.0
Soil Moisture Sensor 1: 1.9  Soil Moisture Sensor 2: 0.0
(...snip...)

REPLでvalmapを実行してみる。

$ python
Python 3.9.2 (default, Mar 12 2021, 04:06:34)
[GCC 10.2.1 20210110] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def valmap(value, istart, istop, ostart, ostop):
...     value = ostart + (ostop - ostart) * ((value - istart) / (istop - istart))
...     if value > ostop:
...        value = ostop
...     return value
...
>>> print(round(valmap(3.67, 5, 3.5, 0, 100),0))
95.0
>>> print(round(valmap(1.9, 5, 3.5, 0, 100),0))
100

うーん、この計算だと、水分0のときでも95%ぐらいになってしまうよね・・・

さすがにget_adcの中を読み解くのは電子工作初心者には厳しいし、何が間違っているのかはわからないけど、とりあえず実際の値を踏まえて算出するように書き換えてみる。

import signal
import sys
import time
import spidev
import RPi.GPIO as GPIO

# Pin 15 on Raspberry Pi corresponds to GPIO 22
LED1 = 15
# Pin 16 on Raspberry Pi corresponds to GPIO 23
LED2 = 16

MAX=3.57
MIN=1.91

spi_ch = 0

# Enable SPI
spi = spidev.SpiDev(0, spi_ch)
spi.max_speed_hz = 1200000

# to use Raspberry Pi board pin numbers
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)

# set up GPIO output channel
GPIO.setup(LED1, GPIO.OUT)
GPIO.setup(LED2, GPIO.OUT)

def close(signal, frame):
    GPIO.output(LED1, 0)
    GPIO.output(LED2, 0)
    sys.exit(0)

signal.signal(signal.SIGINT, close)

def valmap(value, istart, istop, ostart, ostop):
    value = ostart + (ostop - ostart) * ((value - istart) / (istop - istart))
    if value > ostop:
       value = ostop
    if value < ostart:
       value = ostart
    return value

def get_adc(channel):

    # Make sure ADC channel is 0 or 1
    if channel != 0:
        channel = 1

    # Construct SPI message
    #  First bit (Start): Logic high (1)
    #  Second bit (SGL/DIFF): 1 to select single mode
    #  Third bit (ODD/SIGN): Select channel (0 or 1)
    #  Fourth bit (MSFB): 0 for LSB first
    #  Next 12 bits: 0 (don't care)
    msg = 0b11
    msg = ((msg << 1) + channel) << 5
    msg = [msg, 0b00000000]
    reply = spi.xfer2(msg)

    # Construct single integer out of the reply (2 bytes)
    adc = 0
    for n in reply:
        adc = (adc << 8) + n

    # Last bit (0) is not part of ADC value, shift to remove it
    adc = adc >> 1

    # Calculate voltage form ADC value
    # considering the soil moisture sensor is working at 5V
    voltage = (5 * adc) / 1024

    return voltage

if __name__ == '__main__':
    # Report the channel 0 and channel 1 voltages to the terminal
    try:
        while True:
            adc_0 = get_adc(0)
            adc_1 = get_adc(1)
            sensor1 = round(adc_0, 2)
            if sensor1 < 0.5:
                moisture1 = 0
            else:
                moisture1 = round(valmap(sensor1, MAX, MIN, 0, 100), 0)
            sensor2 = round(adc_1, 2)
            if sensor2 < 0.5:
                moisture2 = 0
            else:
                moisture2 = round(valmap(sensor2, 5, 3.5, 0, 100), 0)
            print(f"Soil Moisture Sensor 1: {moisture1}% ({sensor1}) Soil Moisture Sensor 2: {moisture2}% ({sensor2})")
            if moisture1 < 40 or moisture2 < 40:
                GPIO.output(LED1, 1)
                GPIO.output(LED2, 0)
            else:
                GPIO.output(LED1, 0)
                GPIO.output(LED2, 1)
            time.sleep(0.5)
    finally:
        GPIO.cleanup()

テストで算出した電圧の最大値・最小値を定義しておいて、それに比例したパーセンテージを出すようにしてみた。最大値・最小値の範囲を超えるものはカットしてる。ついでに電圧も表示するようにした。

水につけていない状態。

$ python soil-moistore-sensors.py
Soil Moisture Sensor 1: 1.0% (3.56) Soil Moisture Sensor 2: 0% (0.0)
Soil Moisture Sensor 1: 1.0% (3.56) Soil Moisture Sensor 2: 0% (0.01)
Soil Moisture Sensor 1: 1.0% (3.56) Soil Moisture Sensor 2: 0% (0.01)
Soil Moisture Sensor 1: 1.0% (3.56) Soil Moisture Sensor 2: 0% (0.01)
Soil Moisture Sensor 1: 1.0% (3.56) Soil Moisture Sensor 2: 0% (0.01)
Soil Moisture Sensor 1: 1.0% (3.56) Soil Moisture Sensor 2: 0% (0.0)
Soil Moisture Sensor 1: 1.0% (3.56) Soil Moisture Sensor 2: 0% (0.0)
Soil Moisture Sensor 1: 1.0% (3.56) Soil Moisture Sensor 2: 0% (0.01)
Soil Moisture Sensor 1: 1.0% (3.56) Soil Moisture Sensor 2: 0% (0.01)
Soil Moisture Sensor 1: 1.0% (3.56) Soil Moisture Sensor 2: 0% (0.01)
Soil Moisture Sensor 1: 1.0% (3.56) Soil Moisture Sensor 2: 0% (0.01)
$ python soil-moistore-sensors.py
Soil Moisture Sensor 1: 99.0% (1.93) Soil Moisture Sensor 2: 0% (0.01)
Soil Moisture Sensor 1: 99.0% (1.93) Soil Moisture Sensor 2: 0% (0.01)
Soil Moisture Sensor 1: 99.0% (1.93) Soil Moisture Sensor 2: 0% (0.01)
Soil Moisture Sensor 1: 99.0% (1.93) Soil Moisture Sensor 2: 0% (0.01)
Soil Moisture Sensor 1: 99.0% (1.93) Soil Moisture Sensor 2: 0% (0.0)
Soil Moisture Sensor 1: 99.0% (1.93) Soil Moisture Sensor 2: 0% (0.0)
Soil Moisture Sensor 1: 99.0% (1.93) Soil Moisture Sensor 2: 0% (0.01)
Soil Moisture Sensor 1: 98.0% (1.94) Soil Moisture Sensor 2: 0% (0.01)
Soil Moisture Sensor 1: 99.0% (1.93) Soil Moisture Sensor 2: 0% (0.01)
Soil Moisture Sensor 1: 99.0% (1.93) Soil Moisture Sensor 2: 0% (0.01)
Soil Moisture Sensor 1: 99.0% (1.93) Soil Moisture Sensor 2: 0% (0.01)

もうちょっと細かく詰めないといけないと思うけど、とりあえずそれっぽい数字にはなったんじゃないかなー。センサーの値があんまり安定しないけど、まあそんなシビアなものでもないのでこれで十分かなという気がしてる。

Pythonほとんど書かないけど、見様見真似でなんとかなるもんだ。

参考

とても参考になりました。