對講機聯動模塊開發(樹莓派文字轉語音模塊對接)

最近公司方面有個業務需求,因爲我們做的是智慧社區項目,有一塊涉及到車輛異常出庫報警,比如車輛被盜是。
這時候如何能快速的通知到安保人員呢?原來想法是給安保人員配一臺PAD,或者在手機上安裝個APP,但這樣也有很大的弊端。而安保人員人人必備對講機,所以考慮能不能和對講機聯動。
自然就想到了TTS(TextToSpeech),之前和小i、科大訊飛、華聲捷通都用過合作,對這塊業務還算比較熟悉,本來打算在樹莓派上直接內置一個TTS應用就行了,但這三家都沒有免費的SDK,收費的還挺貴,所以這個想法基本就放棄了,但就在最後一刻,發現科大訊飛竟然有TTS芯片!!!
XFS5152CE,科大訊飛中英文語音合成芯片,淘寶搜了一下,竟然發現了做好的模塊,一個是科大訊飛自己做的(98元),還有一個是基於SYN6288芯片做的(57元),於是都買回來測試了一下,SYN6288跟XFS5152CE一比簡直就是扔貨,所以這裏就不說關於SYN6288的開發了,如果有時間的話,可以單獨做一篇。

廢話到此爲止,乾貨在下面!

首先需要準備幾樣東西
一對對講機、一條手咪線、一塊TTS模塊,另外,我還開了一塊底板,一會做介紹。


先說TTS模塊的對接方式
根據XFS5152CE的開發文檔,我們用到RXD、TXD、GND、V3.3、RDY四個引腳,當TTS有語音輸出的時候RDY會輸出高電平,這塊一會我們用來控制手臺的信道佔用(就是我們手動按下呼叫按鈕),嚴重說一下,這裏用的電壓是3.3V的,因爲後面我們控制繼電器的時候用的是5V的,這裏千萬表混了,要不然98塊錢就會化作一縷青煙……

先用TTS模塊做測試,科大訊飛也提供測試程序了,我們只要把模塊用串口線接到電腦上(TTL電平),用他們的軟件測試就行了,還沒有做下一步和對講機聯動的,先用個耳機試試聲音。
當然那我們這裏肯定還是要寫程序的。
而且仍然用的是JAVA和RXTX做,如果不清楚樹莓派上怎麼調試的,出門左轉,有一篇專門介紹樹莓派3配置串口的文章。

直接上代碼
package com.marssoft.jyphon;
import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.TooManyListenersException;
/**
 * 科大訊飛XFS5152CE芯片語音合成測試程序。
 * @author Mars.CN
 *
 */
public class TTS {
     public static final byte STATE_IDLE=0X4F;       //空閒狀態
     public static final byte STATE_USE=0X4E;        //佔用中狀態
     public static final byte STATE_INITED = 0X4A;   //初始化成功
     public static final byte STATE_SUCCESS = 0X41;  //正確的命令幀
     public static final byte STATE_ERROR = 0X45;    //錯誤的命令幀
     public static final byte STATE_NULL = 0X00;     //設備空
     
     public static final byte COMMAND_QUERY_STATE = 0X21;          //查詢狀態命令
     public static final byte COMMAND_POWER_MODE_SAVING=(byte)0X88; //設置爲省點模式
     public static final byte COMMAND_POWER_MODE_USEING=(byte)0XFF; //設置爲喚醒模式
     public static final byte COMMAND_TTS=0X01;                //語音合成命令
     public static final byte COMMAND_STOP=0X02;               //停止語音合成
     public static final byte COMMAND_SUSPEND=0X03;            //暫停合成
     public static final byte COMMAND_RECOVERY=0X04;           //回覆合成
     
     
     private SerialPort port= null;
     private InputStream in = null;
     private OutputStream out = null;
     private TTS() {}
     /**
      * 初始化串口。
      * @param name
      */
     private TTS(String name) {
          //獲得本地所有串口列表,這裏其實只能獲得ttyS開頭的串口
          Enumeration<CommPortIdentifier> portList = CommPortIdentifier.getPortIdentifiers();
          while(portList.hasMoreElements()){
              //獲得串口的標識符
              CommPortIdentifier portId = portList.nextElement();
              //通過標識符得到串口名字,並判斷這個名字是不是我們需要的那個串口
              if(portId.getName().equals("/dev/"+name)){
                   SerialPort p=null;
                   try {
                        //如果確實是我們需要的串口,則打開這個串口
                        //open(串口占用進程名稱,串口等待超時時間)
                        p = (SerialPort) portId.open("TTSTest", 2000);
                        //給串口一個數據到達偵聽(觸發器)
                        p.addEventListener(new EventListener());
                        //把數據到達通知打開
                        p.notifyOnDataAvailable(true);
                        //設置串口的波特率,參數依次是(波特率,數據位,停止位,校驗位)
                        p.setSerialPortParams(9600,SerialPort.DATABITS_8, SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);
                        //獲得輸入輸出流,方便操作。
                        out = p.getOutputStream();
                        in = p.getInputStream();
                        port=p;
                   } catch (PortInUseException e) {
                        e.printStackTrace();
                   } catch (TooManyListenersException e) {
                        e.printStackTrace();
                   } catch (UnsupportedCommOperationException e) {
                        e.printStackTrace();
                   } catch (IOException e) {
                        e.printStackTrace();
                   }
              }
          }
     }
     /**
      * 打開串口
      *
      * @param port
      */
     public static final TTS open(String name) {
          TTS serial = new TTS(name);
          if(serial.port!=null){
              return serial;
          }
          return null;
     }
     
     /**
      * 發送數據。
      * @param data
      */
     public void send(byte[] data){
          try {
              out.write(data);
              out.flush();
          } catch (IOException e) {
              e.printStackTrace();
          }
     }
     
     /**
      * 播放信息
      * @param msg
      */
     public byte play(String msg){
          if(port!=null){
              try {
                   //把文字內容轉換成GB2312編碼的二進制數據(我記得硬件裏用的都是GBK,如果有問題的話大家用GBK試試看)
                   byte[] mdata = msg.getBytes("GB2312");
                   //根據語音合成指令,協議頭三個字節,分別是幀頭0XFD,數據長度高字節,數據長度低字節,語音合成命令0X01,編碼類型0X00(GB2312),後面是數據
                   byte[] datas = new byte[5+mdata.length];
                   datas[0]=(byte)0xFD;
                   datas[1]=(byte)((datas.length-3)/256);
                   datas[2]=(byte)((datas.length-3)%256);
                   datas[3]=COMMAND_TTS;
                   System.arraycopy(mdata, 0, datas, 5, mdata.length);
//                 System.out.println(HexFormat.format(datas));
                   //發送開始合成
                   out.write(datas);
                   return STATE_SUCCESS;
              } catch (IOException e) {
                   e.printStackTrace();
                   return STATE_ERROR;
              }
          }
          return STATE_NULL;
     }
     
     /**
      * 關閉串口。
      */
     public void close(){
          //當然,這裏可以做一下事件偵聽,再給close加個參數,這樣在串口異常報錯的時候能能捕獲到了。
          port.close();
          port = null;out=null;in=null;
     }
     
     private class EventListener implements SerialPortEventListener{
          @Override
          public void serialEvent(SerialPortEvent event) {
              switch (event.getEventType()) { 
              //這些屬性應該跟串口特性有關,我還沒搞清楚,暫時不解釋
             case SerialPortEvent.BI:
             case SerialPortEvent.OE:
             case SerialPortEvent.FE:
             case SerialPortEvent.PE:
             case SerialPortEvent.CD:
             case SerialPortEvent.CTS:
             case SerialPortEvent.DSR:
             case SerialPortEvent.RI:
             case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
                 break
             case SerialPortEvent.DATA_AVAILABLE:    //獲取到串口返回信息 
               int d = 0;
                 do{
                     try{
                        //讀取一個串口返回之,並判斷這個返回值是什麼狀態。
                         d = in.read();
                         switch(d){
                         case STATE_INITED:
                             System.out.println("初始化成功");
                             break;
                         case STATE_SUCCESS:
                             System.out.println("命令正確");
                             break;
                         case STATE_ERROR:
                             System.out.println("命令錯誤");
                             break;
                         case STATE_IDLE:
                             System.out.println("設備空閒");
                             break;
                         case STATE_USE:
                             System.out.println("語音合成中");
                             break;
                         }
                     }catch(IOException e){ 
                         return
                     } 
                 } 
                 while(d != -1);
                 System.out.println("退出關閉");
                 close();//這裏一定要用close()方法關閉串口,釋放資源 
                 break
             default
                 break
             } 
          }
     }
     
     /**
      * 測試語音
      * @param args
      */
     public static void main(String[] args) {
          TTS tts = TTS.open("S45");
          if(tts!=null){
              //發送測試語音
              tts.play("sound 101 你好世界 Hello world");
          }else{
              System.out.println("串口初始化失敗!");
          }
     }
}

芯片的開發指南

具體其他語音合成的方式,大家可以去參考一下開發文檔,包括一些提示音的設置。

現在語音已經可以正常合成了,現在我們看如何做成一個和對講機聯動的成品。
和對講機聯動,用到了RDY引腳,當語音播放的時候,RDY引腳處於高電平,這時候我們給這個引腳加一個繼電器,高電平是繼電器導通,這樣就把手咪線正負極導通了,也就相當於我們手動按下了對講鍵,手咪線有四根,按我這個版本,黃色的是PPT(按鍵),紅色的是MIC+,綠色的是負極,藍色的是耳機線,直接按照這樣接線就行了。

爲此,我做了一塊底板,上面有個繼電器,再把訊飛的TTS模塊焊上去,左邊留下串口接口,右邊留下手咪線的接口。
需要注意的是,對講機的驅動用5V,訊飛TTS模塊驅動用3.3V,正好樹莓派就提供者兩種電壓輸出,所以在樹莓派一段,留的是RX、TX、GND、Vcc5V、Vcc3.3V五個口,依次對應GPIO14(8)、GPIO15(10)、GND(6)、5V(4)、3V(1),不明白的可以度娘一個樹莓派3的GPIO圖看看。

注意接線的時候,TX對應樹莓派的RX,RX對應樹莓派的TX

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章