[Windows IoT Preview x RaspberryPi2]_讓HCSR04 Ultrasonic Sensor 超音波感測器運作在Windows IoT上

Windows IoT推出時大家對這個全新的OS在RPI2上面有滿滿的憧憬(還是其實只有我有???)

 

但實際上在實做的時候碰到了不少問題,我第一個碰到最嚴重的問題就是Sensor沒有Drvier,全部要自己的Driver自己寫…

又因能取得的底層開發的資料有限,所以第一步也沒有直接寫到Native C的程度,

而是用C#來做這件事情,幸好Ultrasonic的測距方式非常容易

 

是利用一個發送超音波脈衝,另一個接收,所以換成公式就是

 

(接收時間-發送時間) * 340.29(音速每秒340.29m) / 2(來回距離) = 相隔距離(meter)

 

 

Sample Download repo: https://bitbucket.org/thkaw/windows-iot-10-for-rpi2-hcsr04-ultrasonic

在我附上的sample project中,GPIO 23接echo Pin,24接TriggerPin

然後讓TriggerPin通一個高電位打出一個超音波脈衝,然後等echoPin讀取到這個脈衝後,

在計算當初TriggerPim打出去的時間跟讀取到的時間差,再套用上面的公式

 

但在做的過程中比較tricky的地方在於不能用Eventhandler的方式去做echoPin的ValueChangeEvent

 

原因在於目前Windows IoT還是有透過一層CLR執行,所以不會這麼即時

如果透過非同步的作法,例如用Task.Delay().Wait()或者Async等動作,

都會讓RaspberryPi2每一行C# Code約會慢到100~200ms,這個時間其實沒辦法解析出正確的距離,

所以在Timing Sensitive的Code,最好遵守下列建議(外國微軟同事內部討論出來的解法):

 

  1. 避免有UI Thread(應該是指用Windows IoT Template建立專案,這樣就不會有UI的部分)
  2. 避免使用使用Async方法
  3. 用System.Diagnostic.StopWatch與While迴圈來檢查經過的時間(類似取代delay)

 

 

甚麼叫做StopWatch間隔呢?

例如我要送出一個500ms的高電位:

     _triggerPin.Write(GpioPinValue.Low);
     //Task.Delay(TimeSpan.FromMilliseconds(500)).Wait();

     sw0.Start();
     while (sw0.Elapsed.TotalMilliseconds < 500) ;
     sw0.Reset();

     _triggerPin.Write(GpioPinValue.High);

 

是透過while迴圈持續檢查StopWatch所經過的時間

非常迂迴對吧?…

 

但這也是目前可以reach到一個200us週期方波反應時間的最佳方式,這個方式拿示波器去看跟真正print出來的值是有落差的,debug print出來的值可能跟你說已經到10~40us,但實際上並不會reach到這種程度…

下面這個連結,也有人做過類似的測量,結果相仿:

http://mattisenhower.com/2015/05/08/raspberry-pi-2-windows-iot-core/

 

 

好,這就是沒辦法很直覺的用ValueChangeEvent等方式快速的去判斷送出/接收的時間差

但透過stopwatch,時間精度夠高,就可以抓到正確的數值

最後是透過這一段放在我所寫的UltraSonicSensor物件底下的Timing()做到:

           _triggerPin.Write(GpioPinValue.High);

            now = DateTime.Now;

            sw0.Start();
            while (sw0.Elapsed.TotalMilliseconds < 0.01) ;
            Debug.WriteLine("Trigger Pulse length: " + sw0.Elapsed.TotalMilliseconds);  //參考用
            sw0.Reset();

            _triggerPin.Write(GpioPinValue.Low);

            while (_echoPin.Read() == GpioPinValue.Low) ;
            pulseLength.Start();


            while (this._echoPin.Read() == GpioPinValue.High) ;
            pulseLength.Stop();

            //Calculating distance
            TimeSpan timeBetween = pulseLength.Elapsed;
            //Debug.WriteLine("time diff : " + timeBetween.ToString());
            double distance = timeBetween.TotalMilliseconds * 34.029 / 2.0;
            Debug.WriteLine("Distance(cm): " + distance.ToString());

眼尖的人可以發現倒數第二行為何是乘上34.029呢?

因為要取得公分,而且時間差是ms不是sec,差了1000倍,一來一往就等於乘上0.1

所以就直接換掉啦~,其實除2也可以直接化簡,節省資源,但好懂好解說還是在code裡除2吧~

另外需要注意的是Debug.WriteLine寫出stopwatch的elaspsed不一定是真實的週期,

 

所以…做電子電路還是弄台示波器來吧!

2015-07-04_01-02-33

 

還有經過測試,這個版本如果<20cm,精度會開始飄移3~5公分,超過30cm之後比較穩定且準確。

 

本sample已經上傳bitbucket,可以自由取用修改,如執行的時候有send signal跟Trigger Pulse出現在VS的Output

但卻遲遲沒有Distance,可以檢查一下接線跟換一下反射的物體,拉長一點距離會比較ok~

 

還有這個專案是開在Windows Universal Template底下,如果換成Windows IoT Core Template也可以運作,沒有UI影響在時間差上會更準確,

但我懶得弄了 :P,請各位自行把MainPage.xaml.cs Main()內的code還有下面的Ultrasonic Sensor物件移動到IoT Core的StartupTask.cs內的Run()吧…

 

 

以上,還在跟DHT22 1-wire十分要求Timing的通訊協定奮鬥,希望有機會try出來。

Leave a comment

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料