對講機聯動模塊開發(樹莓派文字轉語音模塊對接)
最近公司方面有個業務需求,因爲我們做的是智慧社區項目,有一塊涉及到車輛異常出庫報警,比如車輛被盜是。
這時候如何能快速的通知到安保人員呢?原來想法是給安保人員配一臺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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.