2014-07-27

EV3 對 Arduino 的藍芽通訊測試

EV3軟體中的Messaging Block是唯一使用藍芽通訊的程式區塊,它的功能是在EV3之間傳送與接收郵件訊息(Message),所以只要能夠解析EV3的通訊格式,就同樣可以用來與Arduino進行無線的藍芽通訊。

配對與建立連結

在本次測試,Arduino使用的藍芽模組DFRobot Bluetooth Bee(2.0)

經由EV3進行配對之後,在EV3 UI的藍芽Favorites清單中所顯示的裝置名稱為:Bluebooth_Bee_V2


接下來就可以EV3程式中使用Bluetooth Connection Block,並選擇Initiate Mode與輸入Bluebooth_Bee_V2裝置名稱,建立Arduino藍芽模組之間的連結。

EV3通訊格式解析

2012測試NXTArduino之間的藍芽通訊,NXT有一份Bluetooth Developer kit可以參考,而EV3則是到目前為止都還沒有公佈任何關於firmware的官方技術文件。
唯一能夠參考的就是流傳於網路中的EV3 Source code,其中c_com.h關於EV3 System CommandDirect Command封包格式的相關註解。
由此推測與歸結出當使用EV3 Messaging BlockSend Mode,就是使用WRITEMAILBOX 的 System command
至於
封包格式為:
bbbb mmmm tt ss ll aaa LLLL ppp
而封包的資料內容如下表
Byte 0~1
bbbb = 整體訊息長度(bytes), Word型態, little endian
Byte 2~3
mmmm = message counter, 用途不明Word型態, little endian
Byte 4
tt = command類型,
值為0×81: System command, reply not required
Byte 5
ss = System command
值為0x9E: WRITEMAILBOX, write to mailbox
Byte 6
ll = 包含null結尾字元的Message Title字串長度
Byte 7~(6+ll)
aaa… = 包含null結尾字元的Message Title字串
Byte (7+ll)~(8+ll)
LLLL =包含null結尾字元的Message  Content字串長度,
Word型態, little endian
Byte (9+ll)~(8+ll+LL)
ppp… =包含null結尾字元的Message Content字串
在上表中封包內容裏的 Message TitleMessage Content兩個值,在 EV3 Messaging Block對應的input值如下圖:
因此,Arduino端接收來自EV3傳送的資料封包,解析後的內容如下:

參考連結


Arduino原始碼

#define SYSTEM_COMMAND_REPLY          0x01
#define SYSTEM_COMMAND_NO_REPLY  0x81
#define SYSTEM_REPLY                                 0x03
#define SYSTEM_REPLY_ERROR                  0x05
#define DIRECT_COMMAND_REPLY           0x00
#define DIRECT_COMMAND_NO_REPLY   0x80
#define DIRECT_REPLY                                  0x02
#define DIRECT_REPLY_ERROR                    0x04
// EV3 System command
#define WRITEMAILBOX                                0x9E

#define MIN_PACKET_SIZE                          11
#define MAX_PACKET_SIZE                         64
#define RCV_MSG_TIMEOUT                       5000

byte rcvMessage [MAX_PACKET_SIZE];

byte rcvByte;
int  bytesRead=0, packetLength=0;
char strMsg [256];

/* Listen to incoming messages */
void receiveMessage()
{
  bytesRead = 0;
  for(short xii=0; xii<MAX_PACKET_SIZE; xii++) rcvMessage[xii]=0x00;
  
  Serial.println("\n\r");
  Serial.println("Listening ..");   
  while(true)
  {    
    if(Serial.available() > 0)
    {
      rcvMessage[bytesRead] = Serial.read();
      bytesRead++;      
    }
    if(((bytesRead >= MIN_PACKET_SIZE) &&
        (bytesRead == (word(rcvMessage[1], rcvMessage[0])+2))) || (bytesRead > MAX_PACKET_SIZE))
        break; 

  }
}

/* Prints received message sent from EV3 */
boolean printMessage()
{
  //String strCmdType;
  short packetLength = word(rcvMessage[1], rcvMessage[0]);  
  if(packetLength == (bytesRead-2))
  {    
    sprintf(strMsg, "There are total %d bytes has been read.", bytesRead);
    Serial.println(strMsg);
    
    Serial.println("The message payload receviced from EV3: ");
        
    sprintf(strMsg, "(Byte 0-1) Packet size: %d bytes", packetLength);
    Serial.println(strMsg);
    
    sprintf(strMsg, "(Byte 2-3) Message counter: %d", word(rcvMessage[3], rcvMessage[2]));    
    Serial.println(strMsg);
   
    sprintf(strMsg, "(Byte 4) Command type: %X", rcvMessage[4]);
    Serial.println(strMsg);
    
    sprintf(strMsg, "(Byte 5) Command: %X", rcvMessage[5]);
    Serial.println(strMsg);
    
    short titleLength  = short(rcvMessage[6]);
    sprintf(strMsg, "(Byte 6) Message title length: %d", titleLength);
    Serial.println(strMsg);
    
    sprintf(strMsg,"(Byte 7-%d) Message title: ", titleLength+6);
    Serial.print(strMsg);
  
    for(int xii=0; xii<titleLength-1; xii++) Serial.print((char)rcvMessage[xii+7]);
    sprintf(strMsg,"\\%X", rcvMessage[titleLength+6]);
    Serial.println(strMsg);
      
    short msgLength = word(rcvMessage[titleLength+8], rcvMessage[titleLength+7]);
    sprintf(strMsg,"(Byte %d-%d) Message length: %d", titleLength+7, titleLength+8, msgLength);
    Serial.println(strMsg);
    
    sprintf(strMsg,"(Byte %d-%d) Message content: ", titleLength+9, titleLength+msgLength+8);
    Serial.println(strMsg);
    
    for(int xii=0; xii<msgLength-1; xii++) Serial.print((char)rcvMessage[xii+titleLength+9]);
    sprintf(strMsg,"\\%X", rcvMessage[titleLength+msgLength+8]);
    Serial.println(strMsg);
    
    Serial.println("\n\r");
    return true;
  }
  else return false;
}

/* Send a non-empty message to EV3 */
void sendMessage()
{
   char replyMessage[] = {0x1A, 0x00, 
                                        0x01, 0x00, 
                                        SYSTEM_COMMAND_NO_REPLY, WRITEMAILBOX,
                                        0x09, 'C','H','_','E','V','3','_','A','\0',
                                        0x0A, 0x00, 'H','e','l','l','o',' ','E','V','3','\0'};
  for(int xii=0; xii<(word(replyMessage[1], replyMessage[0])+2); xii++)
  {
    Serial.write(replyMessage[xii]);
    delay(1);
  }
  Serial.flush(); // Waits for the transmission of outgoing serial data to complete
}

/* Send a empty message to EV3 */
void sendEmptyMessage()
{
  char emptyMessage[] = {0x19, 0x00,
                                        0x01, 0x00,
                                        SYSTEM_COMMAND_NO_REPLY, WRITEMAILBOX,
                                        0x09, 'I','g','n','o','r','e','M','e','\0',
                                        0x09, 0x00, 'I','g','n','o','r','e','M','e','\0'};
  for(int xii=0; xii<(word(emptyMessage[1], emptyMessage[0])+2); xii++)
  {
    Serial.write(emptyMessage[xii]);
    delay(1);
  }
  Serial.flush();
}

void setup()
{
  Serial.begin(9600);  
  for(int xii=5; xii>0; xii--)
  {
    sprintf(strMsg, "Start to listen after %d seconds ..", xii);
    Serial.println(strMsg);
    delay(950);
  }
}

void loop()
{  
  receiveMessage();
  if(printMessage())
  delay(1000);
  {
    sendMessage();
    delay(1000);
    sendEmptyMessage();
    delay(1000);
  } 
}

沒有留言:

張貼留言