symbian 利用蓝牙串口服务输出调试信息

       终于闲下来了,准备总结下之前symbian上的一些经验,也算是告一个段落吧,由于工作原因基本上有半年多没有碰symbian了,已经跟不上Nokia的发展了,新的SDK也没有下下来试试,玩玩什么新的功能什么的,惭愧啊,嘿嘿,废话不说了,开始今天的主题,这也是我觉得在任何一个新平台上开发程序最先要解决的问题――调试环境。
       先说下我使用到的symbian输出trace的几种方法,一是把trace直接通过控制台输出到stdout,但是因为屏幕太小看不了几行trace,小程序还能勉强使用,trace多了就很痛苦了,虽然可以加个getchar函数其等待我按键再往下跑,不过这样使用起来还是极其的不方便;二是打到文件里面,也有一个很麻烦的问题,为了获得良好的分析环境,肯定不能在手机上直接看打出来的trace,还是因为屏幕太小,又懒得每次都需要把文件发到电脑上;三是曾经在XX论坛上看到有人使用symbian提供的调试工具好像是GDB(懒的查证了),可以在PC上调试手机上跑的程序,搞了好一阵一直都没有成功过,就放弃了。碰巧当时研究了下蓝牙相关的东西,就想到干脆自己搞个方便点的trace输出功能吧:注册一个蓝牙服务,手机上跑的程序通过这个服务把trace输出到PC上,手机做为服务器,PC做为客户端在程序每次运行时连接一下就可以了,至于PC这边的客户端完全可以用超级终端来代替,这样对我这种写EXE的人来说再好不过了。
       再说下蓝牙服务的相关概念,目前支持蓝牙的手机大多会支持几个标准的蓝牙服务,比如OPP(object push profile),FTP(file transfer profile)什么的,都是两个设备之间用来相互传送资料的,也有一些蓝牙耳机、拨号上网服务,蓝牙允许用户自定义服务,以便对端设备来访问,他们都是工作于蓝牙RFCOMM层之上的,RFCOMM是一个串口仿真协议,这样可以把某一个蓝牙服务虚拟为一个串口方便程序的编写。比如在蓝牙配对完成后,PC首先会去查询对方的SDP(Service Discovery Application Profile)服务,这其中有所有对端(这里就是手机)支持的服务的详细信息,PC得到这个信息后就会显示给用户对方有哪些服务,用户可以自由的选择使用哪些服务。这里举例说明我们的DEBUG服务,PC发现了我们在手机上注册的这个DEBUG串口服务(我们注册的当然可以是串口服务,标准中叫他SPP),将其显示给用户,用户在选择连接,在蓝牙链路连接成功后PC会将其虚拟为一个PC上的串口设备,这样我们就可以通过这个串口给手机通讯了。
       现在看看我们要实现的这个功能,主要就是两个功能,一是创建一个蓝牙服务,能处理pc过来的连接请求,并建立蓝牙连接,断开后继续监听等待下一次连接,第二是提供一个send函数发数据就可以了。所以我将其分为一个父类CBtSvr来处理第一个问题,再写一个BtDbg子类来处理第二个问题。
       先看看CBtSvr需要实现的功能,用一个活动对象来实现
class  CBtSvr :  public   public  CActive
    {
public  :
       
//  当前服务器的状态
        enum  CBtBaseSvrStat {
              
//  空闲状态
              EWaitingToGetConnection,
              
//  监听状态
              EGettingConnection,
              
//  连接状态
              EInConnection,
       }     ;
 
       CBtSvr();
       
virtual   ~ CBtSvr(){} ;
       
//  类似消息处理的主循环
        virtual   void  RunL();
       
//  启动服务器
        virtual   int  StartL();
       
//  关闭服务器
        virtual   void  CloseL();
       
//  取消当前提交的请求
        virtual   void  DoCancel();
       
//  返回服务器当先状态当前状态
       inline CBtBaseSvrStat Status( void ) {  return  iStat; };
 
protected :
       
//  socket 服务器
       RSocketServ sockSvr;
       
//  监听socket
       RSocket listenSock;
       
//  连接 socket
       RSocket connSock;
       
//  send状态标志
       TRequestStatus iSendStatus;
       
//  服务的当前状态
       CBtBaseSvrStat iStat;
       TSockXfrLength iLen;
       TInt channelNum;
       
protected :
       
//  绑定服务,留给子类实现
        virtual   int  BindL( void =   0 ;
       
//  监听
        virtual   int  ListenL( void );
       
//  接受连接请求
        virtual   int  AcceptL( void );
       
//  注册蓝牙服务中的Protocol段
        virtual   void  BuildProtocolDescriptionL(CSdpAttrValueDES *  aProtocolDescriptor, TInt aPort);
       
//  设置蓝牙服务安全
        void  SetSecurityOnChannelL(TBool aAuthentication, TBool aEncryption, TBool aAuthorisation, TInt aChannel);
       
//  注册蓝牙服务
        int  RegieterBlueToothServerL( const  TDesC &  KServiceName, TInt KSerialClassID);
       
//  绑定蓝牙服务名
        int  BuildSerivce( const  TDesC &  ServiceName, TInt KSerialClassID);
};
构造函数,初始化状态并加入活动对象调度器
CBtSvr::CBtSvr() 
       : iStat(EWaitingToGetConnection),CActive(
0 )
{
       CActiveScheduler::Add(
this );
}
开启蓝牙debug服务
int  CBtSvr::StartL()
{
       
//  如果当前状态不对或者已经提交了事件
        if  (iStat  !=  EWaitingToGetConnection  ||  IsActive()) {
              
return   - 1 ;
       }
       
//  建立绑定debug服务
        if  (BindL()  <   0 ) {
              
return   - 1 ;
       }
       
//  开始监听
        if  (ListenL()  <   0 ) {
              listenSock.Close(); 
              sockSvr.Close();
              
return   - 1 ;
       }
       
//  接受连接
        if  (AcceptL()  <   0 ) {
              listenSock.Close(); 
              sockSvr.Close();
              
return   - 1 ;
       }
       
return   0 ;
}
//  监听函数
int  CBtSvr::ListenL( void )
{
       assert(iStat 
==  EWaitingToGetConnection);
 
       
//  只支持一个连接
        if (listenSock.Listen( 1 !=  KErrNone) {
              
return   - 1 ;
       }
       
return   0 ;
}
//  提交接受连接请求
int  CBtSvr::AcceptL( void )
{
       
if  (connSock.Open(sockSvr)  !=  KErrNone) {
              
return   - 1 ;
       }     
       listenSock.Accept(connSock, iStatus); 
       iStat 
=  EGettingConnection;
       SetActive();
       
return   0 ;
}
//  实现CActive的doCancel函数供取消事件请求时调用
void  CBtSvr::DoCancel()
{
       
if  ( ! IsActive())
              
return ;
 
       
switch  (iStat) {
       
case  EGettingConnection:
              listenSock.CancelAll();
              
break ;
       
case  EInConnection:
              connSock.CancelAll();
              
break ;
       
default :
              
break ;
       }
}
//  服务器关闭函数
void  CBtSvr::CloseL()
{
       Cancel();
       iStat 
=  EWaitingToGetConnection;
       listenSock.Close();
       sockSvr.Close();
}
//  主事件循环
void  CBtSvr::RunL()
{
       
if  (iStatus  !=  KErrNone) {
              
//  出错处理
               switch  (iStat) {
                     
case  EGettingConnection:
                            iStat 
=  EWaitingToGetConnection;
                            
break ;
 
                     
case  EInConnection:
                            
//  可能是对端断开连接
                            iStat  =  EWaitingToGetConnection;
                            
//  重新提交接受连接事件
                            AcceptL();
                            
break ;
 
                     
default :
                            
break ;
              }
    }
       
else  {
              
switch  (iStat) {
                     
case  EGettingConnection:
                            
//  连接建立成功
                            iStat  =  EInConnection;
                            
break ;
 
                     
case  EInConnection:
                            
//  收到数据不做任何处理
                             break ;
 
                     
default :
                            
break ;
              }
    }
}
 

//  这个函数是设置蓝牙服务安全,没有仔细研究,simple中搬出来
void  CBtSvr::SetSecurityOnChannelL(TBool aAuthentication,
                          TBool aEncryption, 
                          TBool aAuthorisation,
                          TInt aChannel)
{
    
const  TUid KUidBTMobTimeObexAppValue  =  { 0x0 };
    
//  a connection to the security manager
    RBTMan secManager;
 
    
//  a security session
    RBTSecuritySettings secSettingsSession;
 
    
//  define the security on this port
    User::LeaveIfError(secManager.Connect());
    CleanupClosePushL(secManager);
    User::LeaveIfError(secSettingsSession.Open(secManager));
    CleanupClosePushL(secSettingsSession);
 
    
//  the security settings 
    TBTServiceSecurity serviceSecurity(KUidBTMobTimeObexAppValue, KSolBtRFCOMM,  0 );
 
    
// Define security requirements
    serviceSecurity.SetAuthentication(aAuthentication);
    serviceSecurity.SetEncryption(aEncryption); 
    serviceSecurity.SetAuthorisation(aAuthorisation);
 
    serviceSecurity.SetChannelID(aChannel);
    TRequestStatus status;
    secSettingsSession.RegisterService(serviceSecurity, status);
    
    User::WaitForRequest(status); 
//  wait until the security settings are set
    User::LeaveIfError(status.Int());
    
    CleanupStack::PopAndDestroy(); 
//  secManager
    CleanupStack::PopAndDestroy();  //  secSettingsSession
}
//  注册蓝牙串口服务
int  CBtSvr::RegieterBlueToothServerL( const  TDesC &  KServiceName, TInt KSerialClassID)
{
       
//  reg the sdp server database
       RSdp sdp;
       RSdpDatabase iSdpDatabase;
       TSdpServRecordHandle iRecord;
 
       
//  sdp服务器连接
        if (sdp.Connect()  !=  KErrNone) {
              
return   - CNSE_SYS_ERR;
       }
       
//  打开数据库
        if (iSdpDatabase.Open(sdp)  !=  KErrNone) {
              
return   - CNSE_SYS_ERR;
       }
       
//  创建一个服务
       iSdpDatabase.CreateServiceRecordL(KSerialClassID, iRecord);
 
       
//  add a Protocol to the record
       CSdpAttrValueDES *  vProtocolDescriptor  =  CSdpAttrValueDES::NewDESL(NULL);
       CleanupStack::PushL(vProtocolDescriptor);
       
//  设置protocl相关信息
       BuildProtocolDescriptionL(vProtocolDescriptor, channelNum);
 
       iSdpDatabase.UpdateAttributeL(iRecord, 
                                                         KSdpAttrIdProtocolDescriptorList, 
                                                         
* vProtocolDescriptor);
       
       
//  Add 0x5 display 设置为可见
       CSdpAttrValueDES *  browseGroupList  =  CSdpAttrValueDES::NewDESL(NULL);
       CleanupStack::PushL(browseGroupList);
       browseGroupList
       
-> StartListL()    //  List of protocols required for this method
                      -> BuildUUIDL(TUUID(TUint16( 0x1002 )))
       
-> EndListL();
       iSdpDatabase.UpdateAttributeL(iRecord, KSdpAttrIdBrowseGroupList, 
* browseGroupList);
       CleanupStack::PopAndDestroy(
2 );
 
       
//  Add a name to the record,名字
       iSdpDatabase.UpdateAttributeL(iRecord, 
                                     KSdpAttrIdBasePrimaryLanguage 
+  
                                                         KSdpAttrIdOffsetServiceName, 
                                     KServiceName);
 
       
//  Add a description to the record,描述
       iSdpDatabase.UpdateAttributeL(iRecord, 
                                     KSdpAttrIdBasePrimaryLanguage 
+  
                                                         KSdpAttrIdOffsetServiceDescription, 
                                     KServiceName);
       iSdpDatabase.Close();
       sdp.Close();
       
return   0 ;
}
//  这里设置蓝牙SDP服务中的协议相关信息
void  CBtSvr::BuildProtocolDescriptionL(CSdpAttrValueDES *  aProtocolDescriptor, TInt aPort)
{
    TBuf8
< 1 >  channel;
    channel.Append((TChar)aPort);
 
    aProtocolDescriptor
    
-> StartListL()
        
-> BuildDESL()
        
-> StartListL()    //  Details of lowest level protocol
                     
//  L2CAP层之上
             -> BuildUUIDL(KL2CAP)
        
-> EndListL()
 
        
-> BuildDESL()
        
-> StartListL()
            
-> BuildUUIDL(KRFCOMM)
                     
//  这里是绑定的RFCOMM的端口号
             -> BuildUintL(channel)
        
-> EndListL()
    
-> EndListL();
}
 
int  CBtSvr::BuildSerivce( const  TDesC &  ServiceName, TInt KSerialClassID)
{
       TBTSockAddr add;
       
int  ret;
 
       _LIT(KRFCOMM, 
" RFCOMM " );
       
//  connect to server
        if  (sockSvr.Connect()  !=  KErrNone) { 
              
return   - 1 ;
       }
       
//  Open a socket
        if (listenSock.Open(sockSvr, KRFCOMM)  !=  KErrNone) {  //  ERR_OPEN
              ret  =   - 1 ;
              
goto  ERR_CONN;
       }
       
//  得到一个可用的RFCOMM端口号
       listenSock.GetOpt(KRFCOMMGetAvailableServerChannel, KSolBtRFCOMM, channelNum);
       add.SetPort(channelNum);
       
if (listenSock.Bind(add)  !=  KErrNone) {
              ret 
=   - 1 ;
              
goto  ERR_OPEN;
       }
       
else  {
              
//  设置安全信息
              TRAPD(err, (SetSecurityOnChannelL(EFalse, EFalse, ETrue, channelNum)));
              
if  (err  !=  KErrNone) {
                     ret 
=   - 1 ;
                     
goto  ERR_OPEN;
              }
              
//  注册SDP服务
              TRAP(err, (RegieterBlueToothServerL(ServiceName, KSerialClassID)));
              
if  (err  !=  KErrNone) {
                     ret 
=   - 1 ;
                     
goto  ERR_OPEN;
              }
              
return   0 ;
       }
 
ERR_OPEN:
       listenSock.Close();
ERR_CONN:
       sockSvr.Close();
       
return  ret;
}
整个CBtSvr就完成了,其实看着代码比较多,其实就实现了两个功能。蓝牙服务如果有什么不清楚可以多看看SDK自带的那几个示例代码,还有一些nokia提供的相关文档。
现在再派生出一个子类BtDbg,实现send功能
class  BtDbg :  public  CBtSvr
    {
public  :
       
~ BtDbg();
       
static  BtDbg *  NewLC();
       
static  BtDbg *  NewL();
       
void  ConstructL();
       
int  Send( const  TDesC8 &  aDesc);
       friend 
int  BtDbg_printf( const   char *  format, ...);
private :
       
int  BindL( void );
};
 
BtDbg
*  BtDbg::NewLC()
{
       BtDbg
*  result  =   new  (ELeave) BtDbg();
       CleanupStack::PushL( result );
       result
-> ConstructL();
       
return  result;
}
 
BtDbg
*  BtDbg::NewL()
{
       BtDbg
*  result  =  BtDbg::NewLC();
       CleanupStack::Pop( result );
       
return  result;
}
 
void  BtDbg::ConstructL()
{
}
 
BtDbg::
~ BtDbg()
{
}
 
int  BtDbg::BindL( void )
{     
       _LIT(ServiceName, 
" Debug " );
       
return  BuildSerivce(ServiceName,  0x1101 );
}
 
int  BtDbg::Send( const  TDesC8 &  aDesc)
{     
       
if  (iStat  !=  EInConnection) {
              
return   - 1 ;
       }
       
//  同步的发送数据
       connSock.Write(aDesc, iSendStatus);
       User::WaitForRequest(iSendStatus);
       
if (iSendStatus  !=  KErrNone) {
              
return   - 1 ;
       }
       
return   0 ;
}
由于比较习惯c的printf来打印调试信息,所以我一般实例化一个BtDbg类,然后再赋给一个全局的指针,写exe程序用全局的东西比较方便。最后封装一个BtDbg_printf来打印trace。
下面代码中的btDbg是事先实例化好的一个BtDbg对象。
static   char  rxBuf[ 1024   *   10 ];
 
int  BtDbg_printf( const   char *  format, ...)
{
       va_list ap;
       
int  len  =   0 ;
 
       va_start(ap, format);
       vsprintf((
char * )rxBuf, format, ap);
       va_end(ap);
       TPtrC8 pBuf 
=  TPtrC8::TPtrC8((unsigned  char * )rxBuf);
       
if  (btDbg  &&  btDbg -> Status()  ==  CBtSvr::EInConnection) {
             len 
=  btDbg -> Send(pBuf);
       }
       
return  len;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值