解魔方的机器人攻略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#程序源代码。



对 “解魔方的机器人攻略22 – 蓝牙通讯” 的 25 条 评论

  1. Li Fanxi 说:

    我才知道原来脏活累活都是电脑做的…

  2. xin zhao 说:

    我给您写了一封电子邮件。
    希望得到您的回复。

  3. xin zhao 说:

    已经购买了教育版的套装,现在开始攒钱购买一份resource set.
    $99.95

  4. dead_lee 说:

    希望年中能夠升職, 給自己買套

  5. 忧郁飞花 说:

    在youtube上看见一个视频,一个牛人用诺基亚N95的摄像头读取魔方的6个面,然后分析以后把信息通过蓝牙传输给NXT brick…然后NXT就执行解魔方操作。。。解的是4*4*4魔方。。步骤比较多

  6. 忧郁飞花 说:

    请问一下关于蓝牙的那些API是包含在C#库里面的吗?。。我只会C++/VC++..不知道有没有蓝牙的库。。为了做这个机器人啊。。已经在自学JAVA了。。要是再叫我去学C#那要死翘了。。。

    • 实际上是串口通讯的库,VC++里肯定有,找找看吧:)

      • 忧郁飞花 说:

        果然还是用MS VC++顺手啊~~哈哈~~直接从工具箱里拖一个Serial Port,设置一下端口,然后就这么一句话,blueTooth->Open();连接就建立好啦~~~今天搞了一下午加晚上,尝试用NetBeans建一个GUI,并把Lejos提供的PC端蓝牙接口写到一个按钮事件中。。。结果USB连接倒是成功了,蓝牙连接就是抛出异常。。但是如果不用GUI,单独写个小程序倒是没有问题。。

      • 忧郁飞花 说:

        看了你的code,我发现C#和JAVA的写法挺像的,不知道C#是不是也摒弃了指针?。。如果是的话我倒是挺想去稍微了解一下C#。。应该写起来会比C++要省力些

  7. 忧郁飞花 说:

    博主,问你一个C#的问题,关于graphics的。。以前学习C++也有这个问题,但是不知道怎么解决。。。具体是这样的,比如在一个panel上建graphics,然后画个圆。。。正常情况下显示没问题,但是如果这个圆被另一个窗口挡住,然后窗口挪开以后圆就没了。。需要重新绘制。。。。把窗口最小化以后也是这样。。不知道这个应该如何解决?

    • 你在c#的form里还会遇到这种问题吗?应该不会了吧,我就是用Graphics画的图
      如果还有问题的话,把这段代码移到下面这个函数里面试试
      private void Form1_Paint(object sender, PaintEventArgs e)
      {
      Graphics …..
      }

  8. 忧郁飞花 说:

    代码样例
    Graphics gp = panel1.CreateGraphics();
    Brush b1 = new SolidBrush(Color.Black);
    gp.FillEllipse(b1, x, y, width, height);

  9. 姗姗来迟 说:

    老男孩,文章中说到发送数据之前要先发送两个字节,这是为什么?另外waitForConnection接受到什么信息就表示连接成功?

    • waitForConnection没有接受什么信息
      程序运行到这里就挂起,一直到连接成功后才执行后面的程序
      单线程的

      • 姗姗来迟 说:

        我没有学C#,PC端用JAVA编写,使用了蓝牙类API,可以搜索设备,不过总是搜索不到服务(也就是能搜索到NXT,但是无法与其建立连接),不知道你用C#有没有遇到类似的问题。C#中是用BluetoothConnection.Open()去进行连接的吗?

        • 我这里虽然是BluetoothConnection.open()
          但事实上是用的COM端口通讯
          也就是说手动先设置好蓝牙连接,然后利用蓝牙连接模拟出来的COM端口进行通讯

          你说的用类似0×80,0×90之类的蓝牙指令直接控制NXT,这个我最近才看到
          当时因为不知道有这种控制方式,不然应该会更方便一点
          不过我看的资料依然是.net的,java下面的开发没试过
          从另一个角度也说明,PC和NXT之间的通讯方式很多,你可以多试试
          至于这些远程控制的字节内容,那一定是乐高协议定义的
          我不知道哪里有这些命令的列表,从下面的网站下载源代码,倒是可以看到各种命令对应的字节内容
          http://nxtnet.codeplex.com/

          例如下面的一个enum对象:
          enum Command : byte
          {
          GetBatteryLevel = 0x0B,
          GetCurrentProgramName = 0×11,
          GetDeviceInfo = 0x9B,
          GetFirmwareVersion = 0×88,
          GetInputValues = 0×07,
          GetOutputState = 0×06,
          KeepAlive = 0x0D,
          LowSpeedGetStatus = 0x0E,
          LowSpeedRead = 0×10,
          LowSpeedWrite = 0x0F,
          MessageRead = 0×13,
          MessageWrite =0×09,
          PlayTone = 0×03,
          PlaySoundFile = 0×02,
          ResetInputScaledValue = 0×08,
          ResetMotorPosition = 0x0A,
          SetBrickName = 0×98,
          SetInputMode = 0×05,
          SetOutputState = 0×04,
          StartProgram = 0×00,
          StopProgram = 0×01,
          StopSoundPlayback = 0x0C
          }

          • 姗姗来迟 说:

            非常感谢你的回答。尤其是这篇博文前头提到的NXT从第三个数据开始读。一开始没有注意这一点,死活不能通信。现在问题基本都解决了,UI美化中……如果不是无意发现你的博客,根本不会想到在几十种NXT编程平台中选择轻便灵活的Lejos。祝萝卜头和小爱之后,杰作频仍!

          • 太好了,这个博客能帮到你
            我也感到很高兴,总算没有白花力气:)

  10. 这两个字节表示数据内容的长度
    这个长度在NXT中是默认加上的,而在PC端,需要手动加上

    • 姗姗来迟 说:

      老男孩,这些字节的内容为什么是这样的?我看到很多类似0xFF,0×80,0×90的字节内容可以用来远程控制NXT,这些是乐高协议定义的吗?这些内容应该到那里获得呢?

  11. [...] C#蓝牙相关博客链接:http://www.diy-robots.com/?p=410%20%E8%93%9D%E7%89%99 [...]

  12. [...] C#蓝牙相关博客链接:http://www.diy-robots.com/?p=410%20%E8%93%9D%E7%89%99 [...]

发表评论

可以使用下列 XHTML 标签:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>