对讲机联动模块开发(树莓派文字转语音模块对接)

最近公司方面有个业务需求,因为我们做的是智慧社区项目,有一块涉及到车辆异常出库报警,比如车辆被盗是。
这时候如何能快速的通知到安保人员呢?原来想法是给安保人员配一台PAD,或者在手机上安装个APP,但这样也有很大的弊端。而安保人员人人必备对讲机,所以考虑能不能和对讲机联动。
自然就想到了TTS(TextToSpeech),之前和小i、科大讯飞、华声捷通都用过合作,对这块业务还算比较熟悉,本来打算在树莓派上直接内置一个TTS应用就行了,但这三家都没有免费的SDK,收费的还挺贵,所以这个想法基本就放弃了,但就在最后一刻,发现科大讯飞竟然有TTS芯片!!!
XFS5152CE,科大讯飞中英文语音合成芯片,淘宝搜了一下,竟然发现了做好的模块,一个是科大讯飞自己做的(98元),还有一个是基于SYN6288芯片做的(57元),于是都买回来测试了一下,SYN6288跟XFS5152CE一比简直就是扔货,所以这里就不说关于SYN6288的开发了,如果有时间的话,可以单独做一篇。

废话到此为止,干货在下面!

首先需要准备几样东西
一对对讲机、一条手咪线、一块TTS模块,另外,我还开了一块底板,一会做介绍。
对讲机联动模块开发(树莓派文字转语音模块对接)_第1张图片
对讲机联动模块开发(树莓派文字转语音模块对接)_第2张图片
先说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 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( "串口初始化失败!" );
          }
     }
}

芯片的开发指南
http://www.iflytek.com/upload/contents/2014/07/53be5e3ec4047.pdf

具体其他语音合成的方式,大家可以去参考一下开发文档,包括一些提示音的设置。

现在语音已经可以正常合成了,现在我们看如何做成一个和对讲机联动的成品。
和对讲机联动,用到了RDY引脚,当语音播放的时候,RDY引脚处于高电平,这时候我们给这个引脚加一个继电器,高电平是继电器导通,这样就把手咪线正负极导通了,也就相当于我们手动按下了对讲键,手咪线有四根,按我这个版本,黄色的是PPT(按键),红色的是MIC+,绿色的是负极,蓝色的是耳机线,直接按照这样接线就行了。
对讲机联动模块开发(树莓派文字转语音模块对接)_第3张图片
为此,我做了一块底板,上面有个继电器,再把讯飞的TTS模块焊上去,左边留下串口接口,右边留下手咪线的接口。
需要注意的是,对讲机的驱动用5V,讯飞TTS模块驱动用3.3V,正好树莓派就提供者两种电压输出,所以在树莓派一段,留的是RX、TX、GND、Vcc5V、Vcc3.3V五个口,依次对应GPIO14(8)、GPIO15(10)、GND(6)、5V(4)、3V(1),不明白的可以度娘一个树莓派3的GPIO图看看。
对讲机联动模块开发(树莓派文字转语音模块对接)_第4张图片
注意接线的时候,TX对应树莓派的RX,RX对应树莓派的TX

你可能感兴趣的:(树莓派)