分享
 
 
 

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

王朝other·作者佚名  2007-01-19
窄屏简体版  字體: |||超大  

终于闲下来了,准备总结下之前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;

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有