NDEFデータを読み取って発話するデモ

 音声強化版PaPeRo i のTinker Board S にソニー製カードリーダーライターPaSoRi RC-S380を接続してNFCのNDEFデータを読むことができたので、カードがタッチされてNDEFデータを読み取れたらそれをパペロが発話するデモを作成しました。

構成

  • NDEFテキストレコードが読み取れたらそのテキストをそのまま発話する
  • 同時に音声認識を行いpapebot.do_chatbot_operation()による雑談返答を行う
  • paperotserver(こちら末尾のリンクからダウンロードしてください)経由で音声認識機能を利用する(PCでデバッグできる)
  • stpypaperoを使用した状態遷移プログラム

ソースコード

ndefspeechdemo.py

import threading
from logging import (getLogger, Formatter, debug, info, warn, error, critical,
                     DEBUG, INFO, WARN, ERROR, CRITICAL, basicConfig)

import tornado.ioloop

import papebotclient
from stpypapero import StPyPaperoThread
import ndefcardreader

from papebotcom import (
    MID_RECOGNIZED,
    MID_DO_CHATBOT_OPERATION_RES,
    KEY_SENTENCE,
)

MID_READY = 'Ready'
MID_TIMER = '_timer'
MID_END_OF_SPEECH = '_endOfSpeech'
MID_NDEF_DATA = '@ndefData'

KEY_STATUS = 'Status'
KEY_TEXT = 'text'

logger = getLogger(__name__)


class App(StPyPaperoThread):
    def initialize(self, *args, **kwargs):
        self.handler = self.in_init
        self.papebot = None
        self.ndef_text = None

    def set_papebot(self, papebot):
        self.papebot = papebot

    def unmute(self):
        if self.papebot is not None:
            self.papebot.voice_unmute()

    def mute(self):
        if self.papebot is not None:
            self.papebot.voice_mute()

    def speech(self, text):
        self.mute()
        super().speech(text=text)

    def nfc_touch_speech(self, txt='タッチありがとう'):
        self.speech(txt)
        self.ndef_text = None

    def do_chat(self, text):
        if self.papebot is not None:
            self.papebot.do_chatbot_operation(sentence=text, talk=False)
            self.trans_state(self.in_chat_wait)

    def in_init(self, name, msg):
        # 初期状態イベント処理
        if name == MID_READY:
            self.start_face_tracking()
            self.speech('こんにちは')
            self.trans_state(self.in_speech)

    def in_speech(self, name, msg):
        # 発話中状態イベント処理
        if name == MID_END_OF_SPEECH:
            if self.ndef_text is not None:
                self.nfc_touch_speech(self.ndef_text)
            else:
                self.unmute()
                self.trans_state(self.in_idle)
        elif name == MID_NDEF_DATA:
            self.ndef_text = msg.get(KEY_TEXT)

    def in_idle(self, name, msg):
        # アイドル状態イベント処理
        if name == MID_RECOGNIZED:
            txt = msg.get(KEY_SENTENCE)
            logger.info('event: {} txt: {}'.format(name, txt))
            if txt is not None and 0 < len(txt):
                self.do_chat(txt)
        elif name == MID_NDEF_DATA:
            self.nfc_touch_speech(msg.get(KEY_TEXT))
            self.trans_state(self.in_speech)
        else:
            logger.info('event: {}'.format(name))

    def in_chat_wait(self, name, msg):
        # 雑談返答問い合わせ中イベント処理
        if name == MID_DO_CHATBOT_OPERATION_RES:
            logger.info(msg)
            txt = msg.get(KEY_TEXT)
            if txt is not None and 0 < len(txt):
                self.speech(txt)
                self.trans_state(self.in_speech)
            else:
                if self.ndef_text is not None:
                    self.nfc_touch_speech(self.ndef_text)
                    self.trans_state(self.in_speech)
                else:
                    self.unmute()
                    self.trans_state(self.in_idle)
        elif name == MID_NDEF_DATA:
            self.ndef_text = msg.get(KEY_TEXT)


def main():
    logger.info('start.')

    simid = ''
    ipnet = '192.168.5'
    papeurl = 'ws://{}.1:8088/papero'.format(ipnet)
    boturl = 'ws://{}.100:8867/papebot'.format(ipnet)
    if 0 < len(simid):
        papeurl = ''  # 空文字でシミュレータに接続
    stpapero = App(simid=simid, simname='', url=papeurl)

    def on_ndef_data(ndef_data):
        # NFCタッチ時のコールバック:
        # NDEF_DATAイベントを送る。最初に見つかったtextレコードのtextを送る
        if isinstance(ndef_data, list):
            for rec in ndef_data:
                logger.info(rec)
                if rec.type == 'urn:nfc:wkt:T':
                    txt = rec.text
                    stpapero.put_ex_event(MID_NDEF_DATA, {KEY_TEXT: txt})
                    break

    # カードエミュレータクラスインスタンス
    cardreader = ndefcardreader.NdefCardReader(on_ndef_data=on_ndef_data)

    def eventcb(rpapebot, msg):
        # papebotイベントコールバック
        if msg is None:
            logger.error('None.')
            return False
        name = msg.get('Name')
        if name is not None:
            # イベントをそのままパペロ側にputする
            stpapero.put_ex_event(name, msg)

    try:
        # tw = True   # 「パペロ」必要
        tw = False  # 「パペロ」不要
        module_list = ['weather', 'wikipedia']
        rmt = papebotclient.PapebotClient(boturl, event_callback=eventcb,s
                                          triggerword_onoff=tw, chat_talk=False,
                                          module_list=module_list)
        stpapero.start()
        stpapero.set_papebot(rmt)
        rmt.start()
        threading.Thread(target=tornado.ioloop.IOLoop.current().start).start()
        cardreader.run()
    except KeyboardInterrupt:
        logger.info('KeyboardInterrupt!')
    except Exception as e:
        logger.error(e)

    logger.info('end.')


if __name__ == '__main__':
    loglevel = INFO
    LOG_FMT = '%(asctime)s %(levelname)s %(thread)d.%(name)s.%(funcName)s %(message)s'
    basicConfig(level=loglevel, format=LOG_FMT)

    main()

動作確認

(1) Tinker Board S でpapebotserverを起動してください
python3 papebotserver.py
(2) Androidでカードエミュレーションでパペロに発話させたい文字列をNDEFテキストレコードで返すアプリを動かしてください
変更例:

NdefRecord record = NdefRecord.createTextRecord("ja", "エヌデフ通信テストです");

(3) パペロのIPアドレスが直書きになっているので修正した上でPCまたはTinker Board S でndefspeechdemo.pyを実行してください
sudo python3 ndefspeechdemo.py