ラズパイと対話する植物と自称IoT女子

Tech

2016.8.19

Topics

こんにちは。ガリガリ君梨味ばっかり食べてます。五十嵐a.k.a自称IoT女子です😋

profile_image五十嵐a.k.a.自称IoT女子
2016入社☝️大学院で花の遺伝子を研究するも、日本のインダストリー4.0に夢中となり、その実現を目指す&その恩恵を直接受けるべく4月より入社し奮闘中✊💥💨
三度の飯よりでんぱ組.inc⚡️

前回うっかり自称を付け忘れてしまいましたが(IT農業とセンサーとIoT女子)、まだ自称IoT女子です😟
うっかりうっかり~(分かる人だけ分かればいいN●K番組ネタ)
 
そして今回は予告の通りの「slackと音声出力と自称IoT女子」ではありません。
さっくり、「対話する植物と自称IoT女子」でお送りします。
今回結構長いのですがお付き合いくださいませ。
 
 
前回はIT農業に必要なセンサーをつなぎ、データを取得しました。
今回はそのデータをslackに送って、遠隔で植物の状態を管理する、ということをやってみます。
ついでに、植物と対話できるようにしています😀
 

こんな感じでおはなしできるようにします

現在のIoT概要
 

実行したコードたち

上段2つのコードをもとに、下段のコードを作成し、実行しました。
コード関係
 
Googleに音声データを送るコード[gsa.py]

#! /usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import json
import urllib.parse
import urllib.request
class GoogleSpeechApi(object):
    endpoint = "http://www.google.com/speech-api/v2/recognize"
    def __init__(self,
                 api_key):
        self.api_key = api_key
    def send(self,
             file_name="test.wav",
             output="json",
             lang="ja-JP"):
        """Google Cloud Speech APIに音声ファイルを送る
        """
        query_string = {"output": output,
                        "lang": lang,
                        "key": self.api_key}
        query = urllib.parse.urlencode(query_string)
        url = "{0}?{1}".format(self.endpoint, query)
        with open(file_name, "rb") as f:
            voice_data = f.read()
        headers = {"Content-Type": "audio/l16; rate=16000"}
        request = urllib.request.Request(url, data=voice_data, headers=headers)
        response = urllib.request.urlopen(request).read().decode('utf-8')
        return self.parse_json(response)
    def parse_json(self, jsn_str):
        """Google Cloud Speech APIから来たデータをパースする
        """
        transcripts = []
        for s in jsn_str.split("\n"):
            if not s:
                continue
            js = json.loads(s)
            if not js["result"]:
                continue
            elif not js["result"][0]["alternative"]:
                return None
            for r in js["result"][0]["alternative"]:
                transcripts.append(r["transcript"])
        return transcripts

音声データを録音するコード[rec.py]

# -*- coding: utf-8 -*-
import pyaudio
import wave
import time
class Rec(object):
    frames = []
    def __init__(self,
                format=pyaudio.paInt16,
                channels=1,
                rate=16000,
                chunk=2**11,
                input=True,
                record_seconds=3):
        self.format = format
        self.channels = channels
        self.rate = rate
        self.chunk = chunk
        self.input = input
        self.record_seconds = record_seconds
        self.audio = pyaudio.PyAudio()
    def callback(self, in_data, frame_count, time_info, status):
        """コールバック関数
        """
        self.frames.append(in_data)
        return(None, pyaudio.paContinue)
    def record(self,
               is_comment=True,
               output_filename="test.wav"):
        """録音する関数
        """
        stream = self.audio.open(format=self.format,
                                 channels=self.channels,
                                 rate=self.rate,
                                 input=self.input,
                                 stream_callback=self.callback)
        if is_comment is True:
            print("録音開始")
        stream.start_stream()
        time.sleep(self.record_seconds)
        stream.stop_stream()
        stream.close()
        if is_comment is True:
            print("録音終了")
        with wave.open(output_filename, "wb") as f:
            f.setnchannels(self.channels)
            f.setsampwidth(self.audio.get_sample_size(self.format))
            f.setframerate(self.rate)
            f.writeframes(b"".join(self.frames))
        self.frames = []
        return self

センサーの値を取得して判定し、Slack、スピーカーに結果を出力するコード[sensor.py]

import os
import sys
import glob
import time
import datetime
import re
import subprocess
import spidev
import Adafruit_DHT
from slacker import Slacker
import RPi.GPIO as GPIO
from rec import Rec
from gsa import GoogleSpeechApi
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
TMP_DIR = BASE_DIR + "/tmp"
VOICE_DIR = BASE_DIR + "/voices"
class Sensor(object):
    def __init__(self, switch_pin=18):
        # SWITCH
        self.switch_pin = switch_pin
        GPIO.setup(self.switch_pin, GPIO.IN)
        self.spi = spidev.SpiDev()
        self.spi.open(0, 0)
    def get_switch_status(self):
        return GPIO.input(self.switch_pin)
    def get_temp_and_humid(self, sensor=11, pin=4):
        """温度湿度センサー(DHT11)から温度と湿度を取得する
        """
        # 湿度と温度を取得
        humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)
        # 湿度と温度が正常に取得できていたら、値を返す
        if humidity is not None and temperature is not None:
            return (temperature, humidity)
        else:
            print('Failed to get reading. Try again!')
            sys.exit(1)
    def get_moisture(self, channel):
        """ADCの指定したチャンネルのデータを取得する
        --------------
        パラメータ:
                channel ADCのチャンネル番号
        --------------
        返り値:
                整数 ADCの指定したチャンネルの値
        """
        adc = self.spi.xfer2([1,(8+channel)<<4,0])
        data = ((adc[1]&3) << 8) + adc[2]
        return data
    def get_current_time(self):
        """現在時刻を取得する
        """
        now = datetime.datetime.now()
        current_time = now.strftime("%Y-%m-%d %H:%M:%S")
        return current_time
    def get_sensor_data(self):
        """センサーデータを取得する
        """
        (temp, humid) = self.get_temp_and_humid()
        moisture = self.get_moisture(0)
        time = self.get_current_time()
        return {"temp": temp, "humid": humid, "moisture": moisture, "time": time}
    def judge_health(self, moisture):
        """乾燥しているかどうかを判定する
        """
        if moisture > 900:
            return "very_dry"
        elif moisture > 700:
            return "dry"
        elif moisture > 500:
            return "good"
        elif moisture > 400:
            return "wet"
        else:
            return "very_wet"
if __name__ == '__main__':
    GPIO.setmode(GPIO.BOARD)
    sensor = Sensor()
    rec = Rec()
    GPIO.setmode(GPIO.BOARD)
    sensor = Sensor()
    rec = Rec()
    gsa = GoogleSpeechApi("******Google APIキー******")
    slack = Slacker("******Slack APIキー******")
    pre_status = 0
    while True:
        # ボタンが押しっぱなしだったら終了
        if pre_status > 3:
            break
        # ボタンが押されたかを調べる
        button_current = sensor.get_switch_status()
        # ボタンを押していたら中を実行
        if button_current:
            # ボタンが押されたばかりだった場合に実行
            if pre_status == 1:
                # センサーデータを取得
                data = sensor.get_sensor_data()
                # 録音
                rec.record(output_filename=TMP_DIR + "/tmp.wav")
                # 録音された音声ファイルをGoogle Cloud Speech API に投げる
                word_candidate = gsa.send(file_name=TMP_DIR + "/tmp.wav")
                # テキストの候補を一つ一つ調べる
                for w in word_candidate:
                    # 乾 の文字が候補になったら、中を実行
                    if re.search("乾", w):
                        # 水分量の状態を調べる
                        health = sensor.judge_health(data["moisture"])
                        if health == "very_wet":
                            moisture_text = "よっぱらだて!"
                        elif health == "wet":
                            moisture_text = "多すぎらてば"
                        elif health == "good":
                            moisture_text = "いい塩梅だねっか"
                        elif health == "dry":
                            moisture_text = "もうちっとばかもらえねえかね"
                        elif health == "very_dry":
                            moisture_text = "水が足らねんと!"
                        # 表示/Slackに送るメッセージを決定する
                        message = "{}の状態をお知らせいたし候!\n室温は{}℃、湿度は{}%、{}。"
                        message = message.format(data["time"], data["temp"], data["humid"], moisture_text)
                        # メッセージを表示する
                        print(message)
                        # メッセージをSlackに送る
                        slack.chat.post_message(channel="it_agriculture_data",
                                                text=message)
                        # 水分量に応じて、音声ファイルを再生する
                        subprocess.run(["aplay", VOICE_DIR + "/" + health + ".wav"])
                        break
            pre_status += 1
        else:
            pre_status = 0
    GPIO.cleanup()

Google APIキーと Slack APIキー の取得方法は今回は割愛します。
 

こうなりました。


 

Slackでもお話してくれます

更に、slackのほうでメイさん(新潟出身)が新潟弁でNMD(ナンマイドン)の様子をお知らせしてくれます👇
メイさん2
ちょうどいい水分量だそうです👍
 
~メイさん(新潟出身)とは~
新潟県新発田市出身(新潟県の中で随一強めの方言を話す地域。ここの方言は話す人が話すと脅しにしか聞こえない。)好きな食べ物はルマンド。


HTS Voice “Mei”
released by MMDAgent Project Team
http://www.mmdagent.jp/
Copyright (c) 2009-2015  Nagoya Institute of Technology
Department of Computer Science

次回

さて、私はこの週末にSORACOMさんのハンズオンイベントに参加してきます🚴
次回はその様子をレポートしたいと思います😺
ちなみにはじめてLTをさせていただくので、緊張して滑舌がとんでもないことにならないかが一番不安です!😺
 
☆IoT女子のギャグはウケるのか―――――――――!

テックブログ新着情報のほか、AWSやGoogle Cloudに関するお役立ち情報を配信中!

Recommends

こちらもおすすめ

Special Topics

注目記事はこちら