Posts Tagged ‘蓝牙’

最近入手的几样小东西

最近没干啥大事,主要是在加班之余买点东西。虽说激光二极管很危险,但还是忍不住出手了。没敢买1000mw的(不仅危险,而且还很贵),最后弄了两个200mw的红光二极管激光头。

激光二极管

激光二极管

其实已经买了好几天了,因为圈妈不让带回家,只好在公司放着。做过简单的测试,得到以下几个小结果:
1,激光二极管不是我想象的那样,直接发射的就是直线光束,它其实跟手电一样,需要一个聚焦透镜才能工作,可能优点是光源的点非常小,这样聚焦以后的能量才可能集中;
2,在50cm左右的距离对焦,可以在2秒左右点燃红色的火柴头,这是在前几天北京的大风天气时测试的;
3,烧黑色的纸,瞬间就可以冒烟,白色的反应很慢;短时间照在手上,有微微的灼烧感;
4,如果用来做激光炮打蚊子,完全不可行,一方面是距离远时光斑太大,另一方面是能量不够大,无法做到秒杀;
5,用眼睛看光斑(注意,是照着东西上的光斑,直视光源的话必瞎无疑!)会很难受,最好弄个墨镜带着;

发一个关灯版的:

红色激光

红色激光

总体说来,打蚊子的激光炮已经判定不可行,已经零七八碎的买了好多相关的东西,看来只能收起来等待发掘创意了。

这是一对儿45°的天顶镜,我企图用它们来折射光线,转到瞄准镜上:

一对天顶镜

一对天顶镜

这是一个小型的望远镜,可以用来做瞄准镜:

小望远镜

小望远镜

下面是我设想的原理图,有兴趣的同学可以试试是否可行。BTW,虽然我想的时候是激光炮,但是图画出来越看越像个激光枪。这个带瞄准镜的激光枪,好处是“所见即所得”,因为激光枪不会像子弹一样有抛物线弹道,也不会受风速影响。经过调校以后,你瞄准一个点的时候,就可以准确命中它。天顶镜2的中心可以打一个很小的孔,仅供光束通过,应该不影响观察。
(以上歪理纯属在意念中完成,未经验证)

激光枪的光路构想图

激光枪的光路构想图

最后是一个可以用在Arduino上的蓝牙小电路,这个倒是可以做别的用途,有空的时候准备试试和手机的蓝牙通信。

这个小板子比一般的蓝牙模块多2个脚,其中一个是用来做状态指示,未连接时输出方波,连接成功后输出高电平,在Arduino板子上可以监控状态;另一个是使能开关,默认挂空时是使能,输入高电平时蓝牙模块被中断,这样就可以在程序里开关蓝牙了。

蓝牙小电路

蓝牙小电路

解魔方的机器人攻略25 – 解魔方

现在我们的工作已经接近尾声了,看看怎么把电脑变成一个NXT的蓝牙遥控器。这个部分大家其实可以自由发挥,我设计的数据通讯流程是这样的:

1,蓝牙连接成功
2,NXT扫描魔方,发送6个面,每个面9块共54组颜色数据到电脑
3,NXT发送一个字节(0xFF)到电脑,表示颜色读取完毕
4,电脑开始计算解法,得到解魔方的步骤,一共N步
5,电脑发送一个字节N到NXT
6,NXT进行从1到N的循环,每次发送一个字节n到电脑,请求第n步操作
7,电脑发送第n步操作给NXT
8,NXT执行完全部N个操作,发送一个字节(0xFE)到电脑,通知解魔方完成
9,电脑清空步骤和颜色数组,准备迎接下一次任务
10,按下Escape按钮,NXT发送三个(0XFF)给电脑,关闭蓝牙连接并退出

同学们松了一口气,核心算法都搞定了,这点任务算啥,准备十分钟交卷吧。。。。

且慢,我们得到的步骤是类似F1 U2 F2 D3 L2 D1 F1 U3 L2 D1这样的序列,但是萝卜头永远只能旋转最下面一层,怎么办?

这个也简单,把相应的面翻到底面就好了,毕竟萝卜头的胳膊也不是个摆设。

问题又来了,第一步F1时,把F变成了底面;这时候魔方已经经过了某些翻转操作,那么第二步U2该转哪一面呢?这下有点麻烦了…

如果每次都还原到原来的位置,会增加非常多的步骤。

最好的方法是每次都通过最近的路径把需要旋转的面翻到最底层,然后旋转它。

所以我们需要保存一个坐标系,在翻转魔方的时候,让这个坐标系永远跟魔方的真实位置同步,请看CenterColor类,用来记录六个面的中心位置:

public class CubeCenter
{
    public string[] CenterColor = new string[6] { "U", "R", "D", "L", "F", "B" };

    public void RotateBottom(bool colockwise)
    {
        if (colockwise)
        {
            string n = CenterColor[5];
            CenterColor[5] = CenterColor[1];
            CenterColor[1] = CenterColor[4];
            CenterColor[4] = CenterColor[3];
            CenterColor[3] = n;
        }
        else
        {
            string n = CenterColor[5];
            CenterColor[5] = CenterColor[3];
            CenterColor[3] = CenterColor[4];
            CenterColor[4] = CenterColor[1];
            CenterColor[1] = n;
        }
    }

    public void RotatePaw()
    {
        //Only can move forward
        string n = CenterColor[0];
        CenterColor[0] = CenterColor[3];
        CenterColor[3] = CenterColor[2];
        CenterColor[2] = CenterColor[1];
        CenterColor[1] = n;
    }

    public int FindCenter(string position)
    {
        int center = -1;
        for (int i = 0; i < 6; i++)
        {
            if (CenterColor[i] == position) center = i;
        }
        return center;
    }
}

有了这个参考坐标系,我们就可以把URDLFB表示法的解魔方步骤,转化成萝卜头能识别的PBS表示法。嗯,不用去Google搜索,这个PBS表示法是我发明的(也就是瞎编的^_^ ),它表示
P: Paw 爪子翻动一次
B:RotateBottom 从底面旋转魔方,后面需要接一个1~3的数字
S:RotateBottomSide 旋转魔方的底面,跟B的区别是这时候爪子抓住上两层,然后旋转底面

下面这段代码描述了从URDLFB操作到PBS操作的转换:

int findSidePosition = CenterStatus.FindCenter(targetSide);

//Rotate to corrent bottom
switch (findSidePosition)
{
    case 2:
        //Do Nothing
        break;
    case 1:
        CenterStatus.RotatePaw();
        Steps.Add(new MoveStep(MoveType.RotatePaw, 0));
        break;
    case 0:
        CenterStatus.RotatePaw();
        Steps.Add(new MoveStep(MoveType.RotatePaw, 0));
        CenterStatus.RotatePaw();
        Steps.Add(new MoveStep(MoveType.RotatePaw, 0));
        break;
    case 3:
        CenterStatus.RotateBottom(true);
        CenterStatus.RotateBottom(true);
        Steps.Add(new MoveStep(MoveType.RotateBottom, 2));
        CenterStatus.RotatePaw();
        Steps.Add(new MoveStep(MoveType.RotatePaw, 0));
        break;
    case 4:
        CenterStatus.RotateBottom(true);
        Steps.Add(new MoveStep(MoveType.RotateBottom, 1));
        CenterStatus.RotatePaw();
        Steps.Add(new MoveStep(MoveType.RotatePaw, 0));
        break;
    case 5:
        CenterStatus.RotateBottom(false);
        Steps.Add(new MoveStep(MoveType.RotateBottom, 3));
        CenterStatus.RotatePaw();
        Steps.Add(new MoveStep(MoveType.RotatePaw, 0));
        break;
}
Steps.Add(new MoveStep(MoveType.RotateBottomSide, Convert.ToInt32(rotateCount)));
Steps[Steps.Count - 1].OrginStep = currentStep;

下面是一个PBS表示法的步骤示例,基本上一个URDLFB旋转操作,会对应1~3个PBS操作:
P B3 P S2 B1 P S1

为了减少发送的数据量,我们用下面的规则来发送PBS表示法的步骤,每个步骤用一个字节来描述:

switch (MoveType)
{
    case MoveType.RotatePaw:
        return (byte)10;
    case MoveType.RotateBottom:
        return (byte)(20 + Count);
    case MoveType.RotateBottomSide:
        return (byte)(30 + Count);
    default:
        return (byte)0;
}

在NXT上对应的解析操作是:

//Get result
int step = BlueTooth.ReadBytes()[0];
if(step==10)
{
	//Rotate paw
	Robot.RotatePaw();
}
else if(step>=20 && step<30)
{
	//Rotate Bottom
	int count = step - 20;
	if(count == 3) count = -1;
	Robot.RotateBottom(count);
}
else if(step>=30 && step<40)
{
	//Rotate Bottom Side
	int count = step - 30;
	if(count == 3) count = -1;
	Robot.RotateBottomSide(count);
}

开始编译工程,佛祖&上帝&安拉&比尔盖子同时保佑,程序编译通过了。如果运气好的话,蓝牙连接成功以后,萝卜头就可以顺利解魔方了。

好了,所有的代码都介绍完了,之后还会介绍一些收尾和改进的工作,主要包括:
1,用超声波测距传感器(就是那对眼睛)制作“开关”;
2,读色错误,卡住等情况的异常处理
3,语音提示,让萝卜头开口说话
4,暂停功能,帮助我们进行调试

蓝牙手机变身小车遥控器 (3)

发现已经一星期没有更新博客了,真是非常抱歉。上周做完手机遥控器以后,一直没有其他进展,也没来得及发攻略。接下来的时间可能会更忙,只能先跟大家说声抱歉了。在此严重羡慕一下大苹果同学,这个家伙周末带mm飞去武汉大学看樱花了,眼红啊。。。

今天跟大家分享一下在Windows Mobile环境里,如何配置和调用Web Service(就是上次说的四个程序中的第二个)。

一、首先配置开发环境,需要安装下面的几个东东:
1,VS2008 及其 SP1
2,Windows Mobile 6 Professional SDK Refresh.msi
3,Windows Mobile 6.5 Professional Developer Tool Kit (CHS).msi

安装之后,在“新建工程”菜单里,就可以看到智能手机的选项:

新建智能手机项目

新建智能手机项目

新建智能手机项目只有一个选项,输入项目名称后,单击进入下一步,选择平台版本:

选择手机版本

选择手机版本

单击确定,一个新的手机项目就建好了。

新建的工程

新建的工程

二、控件,编译和部署
试着拖两个按钮到手机界面上去,你可以发现跟普通的windows应用程序开发是类似的。我添加了三个按钮,分别是“启动”,“停止”和“退出”。双击“退出”按钮,对按钮添加相应的事件,其他两个按钮稍后再说:

private void ExitButton_Click(object sender, EventArgs e)
{
    this.Dispose();
    this.Close();
}

选择菜单中的“编译->编译项目”,然后到这个项目的文件夹目录里面翻一翻,在 \PhoneRemoteControl\bin\Debug目录下,可以发现多了个exe文件。

先别激动,这个文件在电脑上双击是不能运行的。必须要复制到手机上,用你的手指头单击它,然后发现……还是不能运行。

嗯,这是因为你的人品不够好,弥补办法是去微软网站上下载一个叫“NET Compact Framework”的东西并安装上。

好了,再用一阳指点一下,这次可以看到软件的界面了,再点一下“退出”按钮,程序就退出了(好像是废话)

三、调用Web Service
首先要在工程中引用WebService:在工程名字上用右键,弹出的菜单中选择“add web reference”。我这个是英文版,不知道中文版里叫什么,参考一下吧:

add reference

add reference

在接下来的设置窗口里,输入上一步中做好的web service地址,另外给它起个名字叫“PingService”:

输入service地址

输入service地址

添加了service之后,咱们看看怎么调用,非常简单,用一个函数就可以了:

private void WCFRequestCall(string data)
{
    PingService.Ping client = new PhoneRemoteControl.PingService.Ping();
    client.Url = "http://chenwu03/service/ping.asmx";
    client.SetData(data);
}

这里有一句 client.Url=”…”; 这个为部署修改用的,例如你开发的时候用chenwu03,将来可能换到另一台电脑上,那么在这里改成chenwu04就可以了。建议把这个参数放到配置文件里,这样如果需要修改的话,不需要改代码重新编译,直接用写字板改一下配置文件即可。

四、在手机上画方向盘
其实想要遥控小车,用前进后退,左转右转几个按钮就可以了。但是为了捍卫Dev的尊严,我决定做的花哨一点,于是很骚包的画了一个方向盘。在Form中绘图,可以在一个叫Form1_Paint的函数中添加程序,不说废话,看代码吧:

private void Form1_Paint(object sender, PaintEventArgs e)
{
    int windowWidth = 480;
    int borderOffset = 40;
    int topStart = 160;
    int lineOffset = 25;
    int r = windowWidth / 2 - borderOffset;

    Graphics g = this.CreateGraphics();

    Pen pen = new Pen(Color.Red);
    pen.Width = 3;
    pen.DashStyle = DashStyle.Dash;
    g.DrawLine(pen, borderOffset-lineOffset, topStart + r, windowWidth - borderOffset+lineOffset, topStart + r);
    g.DrawLine(pen, windowWidth / 2, topStart - lineOffset, windowWidth / 2, topStart + 2 * r + lineOffset);

    pen.Color = Color.Blue;
    pen.DashStyle = DashStyle.Solid;
    pen.Width = 8;
    g.DrawEllipse(pen, borderOffset, topStart, 2 * r, 2 * r);

    steeringWheel.CenterX = windowWidth / 2;
    steeringWheel.CenterY = topStart + r;
    steeringWheel.R = r;
}

画出来的效果如下:

在手机上的效果

在手机上的效果

五,计算小车的速度
因为Arduino中,直流电机的控制是通过PWM管脚输出的,值范围是0~1023;所以我让手机发出的数据就是简单的“VLeft,VRight”,值的范围都是从0到1023,用正负号表示前进还是后退。

设计的思路是,点击圆圈的正上方时,两个轮子都向前走,离圆心越近速度越慢,最远的地方发送的数据是“1023,1023”;
类似的,点击最下面时,发送的数据是“-1023,-1023”;
当点击圆圈的左边时,我会把右侧的速度变大,左侧的速度变小,这样小车就向左转了;
当点击到圆圈外面时,直接忽略掉。

需要注意的是,在实际操作中,当输出速度低于300时,电机其实已经带不动了,所以我把低于300的值全都重置成0。这部分代码就不贴了,反正大家可以按照自己的喜好编写控制代码。需要的同学可以下载代码回去参考下。
至于上面提到的“启动”和“停止”按钮,我们就发送“600,600”,“0,0”两组数据即可

六、发送数据
当你的咸猪手在手机上乱摸时,我是用Form1_MouseMove来响应事件的。事实证明,这个数据发送的频率太高了,每秒钟会发送几百组速度。为了回避这个问题,我们设置一个定时器,每隔150毫秒执行一次,中间MouseMove的事件全部扔掉。

private string currentCommand = "";
private string lastCommand = "";

private void SendTimer_Tick(object sender, EventArgs e)
{
    if (!string.IsNullOrEmpty(lastCommand))
    {
        currentCommand = lastCommand;
        lastCommand = "";
    }
    if (!string.IsNullOrEmpty(currentCommand))
    {
        WCFRequestCall(currentCommand);
        currentCommand = "";
    }
}

好了,接下来是检查工作成果。用手指头在屏幕上一阵乱摸,可以从端口监视器里看到不断跳出来的数组:

端口监视器

端口监视器

有经验的同学可能有两个问题:
1,为什么不直接用WebRequest发起请求呢?
这个我试过,手机容易死机,原因还不清楚
2,为什么不直接用TCP/IP发送数据到PC端口呢,绕这么大一圈?
这个我也试过,程序不会出错,但是电脑上也没有接到数据,原因依然不清楚

不管怎么说,反正试出来一个可行的方法。这篇博客中提到的源代码在这里下载,其他部分下回继续:
http://www.diy-robots.com/Resources/XiaoI/PhoneRemoteControl.zip

蓝牙手机变身小车遥控器 (1)

上篇博客提到我企图把手机改造成小爱的遥控器,这几天终于有点眉目了。这个小车刚组装好的时候,我就接上电池试运行了一下,结果它在屋里一溜小跑,紧赶慢赶才没让它撞到东西。这就是决定做一个遥控器的原因。本着“变废为宝,物尽其能”的DIY精神,我打算利用现有的手机来改造。

说起变废为宝,老婆大人一定心情很惆怅。因为大多数情况下,我喜欢拆东西,在家里干的都是些“变宝为废”的事情。但这次绝对是废物利用。我的手机是HTC钻石2,这款手机的系统是Windows Mobile;优点是CPU强大,屏幕细腻;缺点是经常丢短信,漏电话。也就是说,这款手机作为一个手机,基本可以认为是废物点心。好,接下来开始我们的变废为宝之旅吧。

第一次做手机上的开发,这段时间真是颇费周折。下面是我尝试的几个方案,供大家参考:

最初我想用类似控制萝卜头的办法,使用蓝牙串口向PC发送数据,结果发现并不是所有蓝牙连接都具有虚拟端口的功能。可以查看蓝牙支持的功能,那个Serial Port就是乐高机器人用来通讯的。

HTC手机的蓝牙,不支持Serial Port通信

HTC手机的蓝牙,不支持Serial Port通信

第二个方案是改用手机的WIFI功能,在PC上搭建一个网页,通过URL Request的方式向PC发送数据,非常杯具的是WIFI可以向公网IP发数据,但是不能向局域网内的笔记本发数据。经验证,我放在博客主机上的测试页面可以收到数据,虽然最终也没有成功,不过也算是进了一步。

第三次尝试又用回蓝牙,发现在ActiveSync连接模式下,手机共享的是笔记本的网络资源,这样就可以向自身发送URL Request了。经测试,数据流可以走通。正在欢欣鼓舞的时候,发现一件更杯具的事情:不知道为什么,每发送五六条数据之后,手机就像死机了一样,必须休息几分钟之后才能继续发送。这要是用来当遥控器,小爱怕早就把电视撞烂了。

接下来的几天没有什么进展,非常郁闷,以至于我经常萌生一些邪念(例如把手机扔进马桶)。事实证明最痛苦的时候往往已经接近成功了。有一天我把URL Request换成Web Service做了一次尝试,神奇的发现手机调用Web Service居然又快又不会死机。接下来的事情只能用峰回路转来形容,Web Service再通过TCP/IP协议把数据转发到一个Socket端口,一个端口监听程序再把数据通过USB发给Arduino开发板。经过这么多热心的同志,终于把鸡毛信从手机送到电机了,看看数据流图(其中WebService服务器和笔记本逻辑上是独立的,物理上是同一台电脑):

手机遥控器的数据流图

手机遥控器的数据流图

不知道会不会被手机开发的业内人士们鄙视,不过我至少摸索了一条没人走过的曲折道路,整个流程一共需要写四个程序:
1,在手机上运行的小程序,我画了一个方向盘,用手指触摸的时候,会换算成电机速度发送给Web Service。
   开发环境是Windows Mobile 6.5,使用的语言是C#
2,Web Service程序,接受指令之后,通过TCP/IP的方式转发。使用的语言还是C#,服务器是IIS。
3,端口监听以及Arduino开发板通信的程序。一边通过监听Socket端口接受2发来的指令,一边把指令通过模拟的串口发送给Arduino开发板。使用的开发语言还是C#。
4,Aruino开发板程序,这部分是通过模拟的串口接受指令,转换成电机对应的占空比电流脉冲,从而控制小车的两个直流电机。开发环境是Arduino 0017,开发的语言怀疑是类Java(软件上面有一个咖啡杯的图标)

回头一看,发现自己真够啰嗦的,写了半天还没见到代码,赶紧给标题后面加了个“1”。先贴两个程序的截图吧,改天再把代码发上来。

手机上的程序截图

手机上的程序截图

端口监听及转发程序截图

端口监听及转发程序截图

解魔方的机器人攻略22 – 蓝牙通讯

前面提到了分辨颜色的三部曲,今天给大家介绍一下NXT和电脑之间的蓝牙通讯。其中在NXT端使用的是Lejos自带的Bluetooth类,在PC端使用的开发工具是VS2008,使用的语言是c#。

有些人鄙视这种连接PC的做法,在他们的眼里,连接了PC以后,乐高就变成了一个遥控玩具。其实对编程开发来说,用Java还是用c#并没有本质的区别。魔方的算法也可以写成Java的版本,无奈的是NXT的内存不足,只能把这种体力活交给电脑了。

1. 蓝牙配对

正所谓千里姻缘一线牵,首先我们要给NXT和PC安排一个相亲大会。NXT已经内置了蓝牙模块,要把它设置成打开并且可见的状态。设置方法请看Lejos的中文教程“蓝牙菜单”。现在很多笔记本也自带了蓝牙模块,如果没有的话,必须买一个蓝牙适配器。注意WinXP开始就都已经自带蓝牙驱动了,如果你的电脑安装了第三方的蓝牙驱动,最好先删除。

蓝牙适配器

蓝牙适配器

准备好定情信物以后,就该安排PC和NXT见面了。PC比较主动,由他开负责寻找:

控制面板中旋转“添加新的蓝牙设备”,可以找到当前可见的NXT

控制面板中旋转“添加新的蓝牙设备”,可以找到当前可见的NXT

找到NXT后,两人会羞答答的先来个握手协议,接下来是交换电话号码。Lejos设置的蓝牙连接密码是1234

输入蓝牙连接密码

输入蓝牙连接密码

你看他们一个是能力超强,名车豪宅,另一个能歌善舞,秀色可餐。简直就是一拍即合啊。到此牵线完毕,以后他们就可以直接通讯了。我们查看一下电脑上的NXT属性,可以看到有个带“DevB”的端口,这个相当于是他们之间的私人电话,记下来后面会用到。

注意看端口号

注意看端口号

2. C#中使用蓝牙通讯

其实配对以后,蓝牙就被模拟成了一个端口,我们可以用最简单的端口通讯来收发信息。首先,在每次启动时,需要连接端口:

BluetoothConnection = new SerialPort();
ConnectButton.Enabled = false;
BluetoothConnection.PortName = PortList.SelectedItem.ToString();
BluetoothConnection.Open();
BluetoothConnection.ReadTimeout = 10000;
BluetoothConnection.DataReceived += new SerialDataReceivedEventHandler(BlueToothDataReceived);

然后可以通过这个端口来发送信息。需要注意的是,在发送的原始数据之前,需要添加两个表示长度的字节,Byte[0]+Byte[1]*255=length。所以发送数据的函数如下:

private void BlueToothDataSend(byte[] data)
{
    int length = data.Length;
    byte[] readData = new byte[length + 2];
    readData[0] = (byte)(length % 255);
    readData[1] = (byte)(length / 255);
    for (int i = 0; i < length; i++)
    {
        readData[i + 2] = data[i];
    }
    BluetoothConnection.Write(readData, 0, length + 2);
    Status = "发送数据字节数:" + length;
}

收到数据的时候,也是类似的情况,头两个字节表示了数据的长度,然后才是真正的数据内容:

private void BlueToothDataReceived(object o, SerialDataReceivedEventArgs e)
{
    int length = BluetoothConnection.ReadByte();
    length += BluetoothConnection.ReadByte() * 256;

    byte[] data = new byte[length];
    BluetoothConnection.Read(data, 0, length);
    for (int i = 0; i < length; i++)
    {
        BlueToothReceivedData += string.Format("data[{0}] = {1}\r\n", i, data[i]);
    }
}

断开蓝牙连接的命令如下:

BluetoothConnection.Close();
BluetoothConnection.Dispose();
BluetoothConnection = null;

3. Lejos中使用蓝牙通讯

在Lejos中使用蓝牙有几点区别:首先,Lejos中不支持收到消息的事件触发(我怀疑用多线程可以实现,不过对Java不太熟悉,没有调试成功)所以在需要接受PC信息时,只能挂起等候消息传来;其次,虽然PC发来的信息头两个字节表示长度,但是Lejos接收时,是从第三个字节开始显示的;另外,Lejos发送蓝牙信息时,不需要添加那两个字节的长度信息。

下面是建立蓝牙连接的方式:

public static void Connect() throws Exception
{
	LCD.clear();
	LCD.drawString("Waiting BTC...",0,0);
	btc = Bluetooth.waitForConnection();
	LCD.drawString("Connected",0,2);
	LCD.refresh();
	dis = btc.openDataInputStream();
	dos = btc.openDataOutputStream();
}

接受蓝牙信息:

public static byte[] ReadBytes() throws Exception
{
  byte[] buffer = new byte[255];
  int length = btc.read(buffer, buffer.length);
  if(length==-2)
  {
   //lost data, re-sync
   btc.read(null, 255);
   return new byte[0];
  }
  else
  {
   byte[] data = new byte[length];
   for(int i=0;i<length;i++)
   {
    data[i] = buffer[i];
   }
   return data;
  }
}

发送蓝牙信息

public static void WriteBytes(byte[] data) throws Exception
{
 for(int i=0;i<data.length;i++)
 {
  dos.writeByte(data[i]);
 }
 dos.flush();
}

关闭蓝牙连接

public static void Disconnect() throws Exception
{
   if(btc!=null)
   {
    WriteBytes(new byte[]{(byte)255,(byte)255,(byte)255});
    Thread.sleep(100);
    dos.close();
    dis.close();
    btc.close();
   }
}

4. 蓝牙通讯小实验

下面进行一个小实验,在PC上运行一个程序。
当发送1时,NXT初始化魔方底盘位置;
当发送2时,NXT初始化颜色传感器位置;
当发送3时,NXT读取颜色信息,并回传给电脑;
当发送其他数字时,NXT断开蓝牙连接,并退出程序

蓝牙连接通讯实验

蓝牙连接通讯实验

大部分函数在前面都介绍过了,只需要在main函数中指定操作即可:

BlueTooth.Connect();
byte[] colorData = new byte[6];

while(true)
{
 byte[] readData = BlueTooth.ReadBytes();
 if(readData.length > 0)
 {
  int action = readData[0];
   switch(action)
  {
  case 1:
   Robot.FixBasePosition();
   break;
  case 2:
   Robot.FixColorSensorPosition();
   break;
  case 3:
      colorData[0] = (byte) color.getRed();
      colorData[1] = (byte) color.getGreen();
      colorData[2] = (byte) color.getBlue();
      colorData[3] = (byte) (color.getRawRed() / 3);
      colorData[4] = (byte) (color.getRawGreen() / 3);
      colorData[5] = (byte) (color.getRawBlue() / 3);
      BlueTooth.WriteBytes(colorData);
      break;
  default:
   BlueTooth.Disconnect();
   return;
  }
 }
 Thread.sleep(1000);
}

好了,其余部分自己看代码吧,搭车赠送一个生成三维魔方图形的小程序。点此查看运行在NXT中Java源代码代码;点此下载运行在电脑上的C#程序源代码。