2011年12月21日 星期三

Talk with Arduino using C# and Firmata

C# 也可以用 Firmata 協定跟 Arduino 通訊,方法是用 Firmata.NET 這個套件。

要讓微控制器 (Microcontroller) 跟電腦建立對話,通常使用者會花很多時間在處理通訊方面的問題。用 Firmata.NET 寫 C# 程式,最大的好處就是使用者不必煩惱通訊的繁瑣細節,而且程式風格跟寫 Arduino 的程式幾乎是一致的。

由於涉及 Arduino 與電腦兩端的程式,所以問題分成兩個部份:

  1. Arduino 端: 這部份很簡單,因為 Arduino IDE 已內建 Firmata,所以只要上傳 StandardFirmata 程式到 Arduino 板子上就可以了。使用者完全不需要寫 Arduino 的程式。
  2. PC 端: 這部份便是使用 Firmat.NET 與 C# 撰寫程式。
上傳 StandardFirmata 到 Arduino

打開 Arduino IDE,點選 File > Examples > Firmata > StandardFirmata 並將程式上傳到 Arduino 板子上:

image
▲ Arduino IDE

注意一件事,StandardFirmata 這支 Arduino 程式所用的序列通訊速率是 57600 bps,所以當你在寫 PC 端的程式在開啟 Serial port 時,記得也要把通訊速率設成 57600 bps,這樣 Arduino 跟 PC 的程式兩邊才能溝通。

image

Firmata.NET 使用方法

請點底下的網址下載 Firmata.NET:

http://goo.gl/A2Fz7 

Firmata.NET 只有一個檔案,叫作 arduino.cs。安裝方法很簡單,只要把 arduino.cs 放到你的 C# 專案裏就好。

Firmata.NET 的使用方法如下:

1) 在建立 C# 專案,把 arduino.cs 放到 C# 專案資料夾裏。

2) 在程式裏引用 Firmata.NET 這個 namespace:

3) 假若你的 Arduino 接在 COM5,而且所用的通訊速率是 57600,那麼就這樣建立 Arduino 的通訊:

4) 接著就可以透過 arduino 物件呼叫 pinMode, digitalWrite, digitalRead, analogRead, analogWrite 這些 methods,就好像在寫 Arduino 的程式一樣:

Blink

這個範例會讓 Arduino 的 LED 燈不斷閃爍,每隔一秒切換一次燈號。可參考「Arduino 筆記 – Lab1 Blinking a LED」一文。

程式畫面為:

image

在選好 Serial Port 按下 Open 鈕後,程式就會用 Firmata.NET 建立與 Arduino 之間的連線,然後每隔一秒切換一次 Arduino 的 LED 燈號。

Open 鈕的程式碼如下:

btnOpen_Click 利用底下這兩行開啟與 Arduino 之間的連線:

portName 是使用者所選的 Serial Port,而第三個參數 true 是自動開啟 Serial Port,第四個參數則是 timeout 時間。Firmata.NET 預設的 timeout 時間是 8 秒鐘,如果使用預設值,程式執行的時候會先等 8 秒鐘才會開始跟 Arduino 通訊,這樣有點慢,所以我把時間設為 500 ms。

程式接著把 pin 13 設成 OUTPUT,然後啟用 Timer。Timer 預設 1 秒鐘會跑一次,Timer 的處程函式如下,程式邏輯很簡單,它會根據 pinState 的狀態決定該打開或關閉 LED:

AnalogRead

這個範例示範如何用 analogRead() 讀取 analog pin。程式畫面如下:

image

程式執行的時候,會不斷讀取 analog pin 0 的讀值,然後把讀到的數值 (範圍為 0-1023) 顯示在畫面下方的狀態列上。

AnalogRead 程式大致上跟 Blink 差不多,主要的不同在 Timer 的處理函式:

Timer 的 Interval 為 100,所以這段程式每 100 ms 會跑一次,它會用 arduino.analogRead(sensorPin) 讀取 sensorPin 腳位的讀值,然後再將讀值顯示在狀態列上。

Fade

這個範例利用 PWM (Pulse Width Modulation, 脈衝寬度調變) 控制 LED 燈光亮度。

image

Timer 的 Interval 為 50,所以底下這段程式每 50 ms 會跑一次,它會用 arduino.analogWrite(ledPin, brightness) 設定 pin 9 上的 LED 燈光亮度。相關資訊可參考「Arduino 筆記 – Lab3 控制 LED 燈光亮度」一文。

Fade 的 btnOpen_Click 程式碼如下:

要特別注意,因為我們要用 PWM 控制 pin 9,所以必須用 pinMode(ledPin, Arduino.PWM) 把 pin 9 設定 PWM,這樣才能正確使用 PWM 控制。

Arduino 只有特定的腳位才能做 PWM 控制,以 UNO 為例,可用的 PWM 腳位有 6 支: 3, 5, 6, 9, 10, 11。

下載範例程式

這篇文章的範例程式可以在底下的網址取得:

http://goo.gl/3OMLI

參考資料

15 意見:

Sven Wang 提到...
作者已經移除這則留言。
Sven Wang 提到...

請問Cooper~
如果我需要撰寫c#語言來控制arduino發射紅外線
那我還能在arduino程式裡使用#include嗎?

如果不能引用..那我要如何在c#裡面撰寫發射紅外線的訊號呢?

麻煩Cooper解惑~~~謝謝!!

Sven Wang 提到...

不知道為什麼我發文裡面include後面的"IRremote.h"都無法顯示......這邊補上:)

Cooper Maa 提到...

這篇是利用 Firmata 協定讓 Arduino 跟 C# 建立對話
如果要用 C# 控制 arduino 發射紅外線,Firmata 可能就不適合了

比較簡單的方式是自己定義 Serial 通訊的封包格式
C# 這端不使用 Firmata.Net,改成自己處理 SerialPort 的通訊

也就是按照我 "C# Serial Port Communication #1, #2, #3" 這系列筆記的方法做

Sven Wang 提到...

okok~~~了解啦~~~
謝謝Cooper :)))

Cooper Maa 提到...

之前我有玩一下 bitlash
我有一個想法,假如你想用 C# 對 Arduino "下命令" 叫他發射紅外線的話
也許可以利用 bitlash 寫個巨集指令,然後 C# 便可以呼叫 bitlash 巨集指令....

我最近在玩別的,沒有時間玩這個,哈~ 你可以研究看看,希望寫出來可以分享一下

Sven Wang 提到...

我研究出來囉~~其實也沒啥研究拉...就是照Cooper的方式實作XD

分享在這
下面是Arduino端的程式
其實也就是接收電腦端從com port傳來的數字訊號,然後在判讀訊號發射相對應的紅外線:))
至於C#那邊的程式太多拉...貼這會報掉XDD

#include // 引用 IRRemote 函式庫
IRsend irsend; // 定義 IRsend 物件來發射紅外線訊號

int incomingByte = 0; // 用來儲存收進來的 data byte

void setup() {
// 開啟 Serial port, 通訊速率為 9600 bps
Serial.begin(9600);
}

void loop() {
// 檢查是否有資料可供讀取
if (Serial.available() > 0) {
// 讀取一個 byte
incomingByte = Serial.read();

// 決定打開或關掉 LED
if (incomingByte == '1')
irsend.sendNEC(0xFB30CF, 32); //前進
else if(incomingByte == '2')
irsend.sendNEC(0xFBF20D, 32); //後退
else if(incomingByte == '3')
irsend.sendNEC(0xFB9867, 32);//左轉
else if(incomingByte == '4')
irsend.sendNEC(0xFB32CD, 32); //右轉
else
irsend.sendNEC(0xFBA05F, 32); //停止
}

}

Cooper Maa 提到...

太厲害了,這麼快就寫出來了! ^o^
Arduino 1.0 有新的 serialEvent 函式,程式可以用 serialEvent 來寫會更清晰:
http://coopermaa2nd.blogspot.tw/2011/12/arduino-10-serialevent.html

程式我沒試過,不過應該會動:

#include

IRsend irsend; // 定義 IRsend 物件來發射紅外線訊號
int incomingByte = 0; // 用來儲存收進來的 data byte

void setup() {
// 開啟 Serial port, 通訊速率為 9600 bps
Serial.begin(9600);
}

void loop() {
}

void serialEvent() {
// 讀取一個 byte
incomingByte = Serial.read();

// 決定打開或關掉 LED
if (incomingByte == '1')
irsend.sendNEC(0xFB30CF, 32); //前進
else if(incomingByte == '2')
irsend.sendNEC(0xFBF20D, 32); //後退
else if(incomingByte == '3')
irsend.sendNEC(0xFB9867, 32);//左轉
else if(incomingByte == '4')
irsend.sendNEC(0xFB32CD, 32); //右轉
else
irsend.sendNEC(0xFBA05F, 32); //停止

}

PEN 提到...

想請問cooper,PC跟版子的通訊,PC那端是使用rs232嗎?因為看你用serial portt所以猜想應該是。如果是的話你用的傳輸線是否為一端是rs232另一端是USB(方口)呢,因為這種線似乎不好找所以想你確認一下。

PEN 提到...

你好:
我查了一下COM5似乎是USB專用的所以等於不用轉RS232,但我電腦中找不到COM5,所以想請問cooper要如何才能使用COM5呢?

Cooper Maa 提到...

你是用 Arduino 嗎?
如果是 Arduino,需要一條 USB cable 把 Arduino 跟 PC 連接起來
接上 PC 灌好 USB Driver 後,PC 端會多出一個 COM Port
這個 COM Port 不是 RS232,而是 USB to Serial 的裝置

COM5 是我電腦裝 Arduino USB Driver 的時候產生的
COM Port 名稱不會固定是 COM5 喔
你可以參考一下這篇:
http://coopermaa2nd.blogspot.tw/2011/11/windows-xp-arduino-uno.html

PEN 提到...

你好:
  上面的問題解決了,我是使用uno的版子,作業系統為win7,目前遇到兩個問題,一個是IR紅外線include部分編譯器出現錯誤,另一個問題是C#通訊問題,我找到IR的COMPORT是COM3但C#上當我要new的時候他顯示COM3拒絕存取似乎是不能同時跟版子一起用如果不能的話要如何才能由C#傳訊號給版子呢,請cooper幫忙解答一下謝謝。

Cooper Maa 提到...

COM Port 不能讓多個程式同時開啟喔
你用 C# 程式開 COM Port 時,有沒有關掉 Arduino 的 Serial Monitor 或超級終端機之類的程式? 或者記憶體裏有沒有殘留的 C# 程式?

你是用 IRRemote Library 嗎?
#include 編譯過不了關,有可能跟 Arduino 軟體版本有關,因為 Arduino 1.0 的 #include 檔有改過名字,可以參考一下這篇:
http://coopermaa2nd.blogspot.tw/2011/12/irremote-arduino-10.html

PEN 提到...

你好:
  我下載的IR Library似乎是您已經改過可相容於1.0的版本,目前問題解決了,IR部分似乎是要重開機或重開軟體並由IDE自動引入標頭檔才行,而C#的問題則是因為我開了serial monitor,關掉就OK了,謝謝cooper撥空回答,如有問題再請教你。

Cooper Maa 提到...

沒錯,Arduino Library 裝好之後,必須重開 Arduino 軟體才能使用 Library。

問題都解決了,恭禧您!
You're welcome.