2014-07-18

EV3與Arduino之間的I2C通訊(測試Dexter的EV3_I2C Block) - (II) 測試案例

依據前一篇對於EV3_I2C Block的功能摘要說明
本測試案例,將Arduino模擬成NXT I2C
類型的數位感應器,
透過預先規劃好的「I2C虛擬暫存器位址映對表(Register layout)」,提供作為回應Master(EV3)的多種功能需求,包含:

暫存器位址
執行功能
0x00
回傳版本資訊 "V1.00   " 8bytes
0x08
回傳廠商名稱 "Arduino " 8bytes
0x10
回傳裝置代號 "UNO     " 8bytes 
0x18
接收來自Master傳送的7個bytes資料並顯示在Serial Console 
0x19
執行開啟 Arduino板的L LED 
0x1A
執行熄滅 Arduino板的L LED  



測試案例
一、由EV3控制開啟Arduino板上的L-LED:
作法即使用EV3_I2C Block的「Write 1 byte」模式
寫入 25(0x19)到 Arduino,


Arduino 將會觸發receiveEvent()程序,再以Wire.read()讀取25這個值並透過負責執行指令的executeCommand()函式,
開啟L-LED



void receiveEvent(int howMany)
{  
   requestRegister = Wire.read();   
   while(Wire.available() > 0 && bytesRead < MAX_DATA_BUF_SIZE) {
     rcvData[bytesRead]=Wire.read();
     bytesRead++;
   }
   executeCommand(requestRegister);  
}

...

void executeCommand(byte execCmd)
{
   if(execCmd == COMMAND_LLED_ON) {
     Serial.println("Turn ON LED");
     digitalWrite(LLED, HIGH);
   }
   else if(execCmd == COMMAND_LLED_OFF) {
          Serial.println("Turn OFF LED");
          digitalWrite(LLED, LOW);
   }
   else if(execCmd == COMMANDD_SEND_7_BYTES) {     
          sprintf(strMsg, "%d bytes of data has been read.", bytesRead);
          Serial.println(strMsg);
          if(bytesRead>0) {
            for(int i=0; i<bytesRead; i++) { 
               Serial.println((char)rcvData[i]);
            }
            bytesRead=0;            
          } 
   } 
}


二、EV3傳送7個bytes資料給Arduino顯示於Serial Console:
作法即使用EV3_I2C Block的「Write 8 byte」模式,
在Byte 1寫入 24(0x18),
以通知Arduino還要繼續接收7個bytes的資料,而Byte 2~Byte8則分別寫入所要顯示7個字元的Ascii值,如以下所示:
 Arduino同樣經由receiveEvent()接收資料後,再交由executeCommand()顯示於Serial Console:

else if(execCmd == COMMANDD_SEND_7_BYTES) {     
          sprintf(strMsg, "%d bytes of data has been read.", bytesRead);
          Serial.println(strMsg);
          if(bytesRead>0) {
            for(int i=0; i<bytesRead; i++) { 
               Serial.println((char)rcvData[i]);
            }
            bytesRead=0;            
          } 
   } 


三、由EV3向Arduino請求傳送8個bytes的資料(例如廠商名稱)
作法會分成兩個步驟:
●首先
使用EV3_I2C Block的「Write 1 byte」模式,
    寫入 8(0x08)到 Arduino,
    Arduino同樣經由receiveEvent()接收到資料,
    以Wire.read()將0x08這個值存起來,
    並知道接下來會有廠商名稱("Arduino ")的請求資料需求。

 接著,再使用Read 8 byte」模式向Arduino請求傳送8個bytes的資料,
Arduino會觸發onRequest() handler,依據先前EV3所要求的資料項目0x08(vendorID),以Wire.write()回傳資料給EV3。
void requestEvent() 
  if(requestRegister == COMMAND_GET_VERSION) {
      Wire.write(softwareVersion, 8);
   }
   else if(requestRegister == COMMAND_GET_VENDOR) {
           Wire.write(vendorID, 8);
   }
   else if(requestRegister == COMMAND_GET_DEVICE_ID) {
           Wire.write(deviceID, 8);
   } 

● 接下來就可以由EV3_I2C block的8個Output plug讀取Arduino回傳的資料,不過有三點需要注意的地方:
 首先讀到是Ascii值而非字元(character),正常應該是要使用
    
Read 8 bytes ASCII」模式才能夠讀到
     不過目前這個模式的功能異常,無法使用。
 接下來,比較不解的是,
    回傳回來的資料在EV3_I2C block Output Plug 存放的順序
    居然是由 Byte 8開始,所以,以上圖為例,
    Byte 1~Byte 8依序所讀到的資料為:
                32, 111, 110, 105, 117, 100, 114
    

 最後,如果回傳的資料數目少於請求的資料數,
    則在Output Plug會讀到 -1 的值,    以下圖為例,Arduino只回傳了4個bytes,注意,資料還是由Byte 8開始存放 ???????    


Arduino Sketch
/* ********************************************************
 * Program ID: Arduino_RequestFm_EV3
 * Created by: CH, Chen (Taiwan)
 * Date: 2014.6.10
 * ******************************************************** */
#include <Wire.h>

#define I2C_SLAVE_ADDRESS                 0x04

#define COMMAND_GET_VERSION       0x00
#define COMMAND_GET_VENDOR        0x08
#define COMMAND_GET_DEVICE_ID    0x10
#define COMMANDD_SEND_7_BYTES  0x18
#define COMMAND_LLED_ON                0x19
#define COMMAND_LLED_OFF              0x1A

#define LLED                                     13
#define MAX_DATA_BUF_SIZE      32

uint8_t softwareVersion[9] = "V1.00   ";
uint8_t vendorID[9]           = "Arduino ";
uint8_t deviceID[9]            = "UNO     ";

byte requestRegister, rcvData[32];
char strMsg[256];
int bytesRead=0;

void setup()
   pinMode(LLED, OUTPUT); 
   digitalWrite(LLED, LOW);  
   Serial.begin(9600);
         
   Wire.begin(I2C_SLAVE_ADDRESS);
   Wire.onReceive(receiveEvent);    
   Wire.onRequest(requestEvent); 
   Serial.println("Ready ..");   
}

void loop() { }

void executeCommand(byte execCmd)
{
   if(execCmd == COMMAND_LLED_ON) {
     Serial.println("Turn ON LED");
     digitalWrite(LLED, HIGH);
   }
   else if(execCmd == COMMAND_LLED_OFF) {
          Serial.println("Turn OFF LED");
          digitalWrite(LLED, LOW);
   }
   else if(execCmd == COMMANDD_SEND_7_BYTES) {     
          sprintf(strMsg, "%d bytes of data has been read.", bytesRead);
          Serial.println(strMsg);
          if(bytesRead>0) {
            for(int i=0; i<bytesRead; i++) { 
               Serial.println((char)rcvData[i]);
            }
            bytesRead=0;            
          } 
   }  
}

void receiveEvent(int howMany)
{  
   requestRegister = Wire.read();   
   while(Wire.available() > 0 && bytesRead < MAX_DATA_BUF_SIZE) {
     rcvData[bytesRead]=Wire.read();
     bytesRead++;
   }
   executeCommand(requestRegister);  
}

void requestEvent() 
  if(requestRegister == COMMAND_GET_VERSION) {
      Wire.write(softwareVersion, 8);
   }
   else if(requestRegister == COMMAND_GET_VENDOR) {
           Wire.write(vendorID, 8);
   }
   else if(requestRegister == COMMAND_GET_DEVICE_ID) {
           Wire.write(deviceID, 8);
   } 

1 則留言: