[Azure]_ConnectTheDots IoT範例:運用Raspberry Pi + Arduino當作IoT Sensor連上Azure回傳資料

//2015/03/23 有網友回報說內容有點不一樣,一些路徑也換了,還有部分原文說明連結失效,因為這個Project算是很初期的run,該repo也有微軟官方的人員持續維護、更新,我日後會做更新,盡力讓這篇跟Github上面repo版本操作方式一致。

//另外因為ms最近突然對這個repo的更新速度變得相當快速,本篇文章是實作

https://github.com/MSOpenTech/connectthedots/tree/667c05501a572abb0ded547c6c3c5a81f1e242b2

這個commit的版本,所以下文的project若與最新的檔案路徑有差異,

而不知道怎麼去對應新版名稱的話,建議可以先抓舊版的來搞懂整個思路做起來之後再嘗試去做新版的!

 

 

周末實作並改良了一個來自國外的Azure IOT Sample

原文網址在:https://github.com/MSOpenTech/connectthedots

 

先講一下整個架構:

 

ConnectTheDots Temp and Hmdt Architecture

這個Project支援兩種裝置,但這裡只實作並改良RPi + Arduino這個範例,

Arduino端在原先的範例中是使用Weather Shield 去做 取得溫溼度跟光線的感測器,但因為該shiled價格高昂

所以我就分開的使用溫溼度跟光感各三個sensor,價格大概差了6倍左右…

 

以及利用Raspberry Pi當作連去Azure的Gateway, 上面透過Mono環境運作著ConnectToDot(下稱CTD)這個PROJECT所撰寫的.NET C# CODE,

透過這支程式能夠讓RaspberryPi轉交來自Arduino透過USB傳來的JSON資料並且回傳至AZURE的EventHubs(等下在Project的名稱為EhDevices)

 

而在雲端的環境有一個目前仍在Preview階段的Stream Analytics,會接收來自EhDevices這個EventHubs的資料,並且作分析

會設置三個分析器,分別為Aggregates、LightSensor、Alerts,並且可在裏頭設定相關資料的Query腳本,在本範例中如果關燈或者溫度超過所設定的度數

就會發一個Event到EventHubs另外一個名為EhAlerts的EventHubs.

 

而Sample Project內也有一個由ASP .NET 所撰寫的Processor(或稱Listener,請看之前的EvnetHubs文章),可以部屬到Azure website或者standalong,

用來接收來自Azure EventHubs內EhDevices跟EhAlerts的資料作呈現顯示。

 

10923727_10202792445087298_6251767633794378520_o

 

 

///////以下正文範例開始///////

 

請先到原文的Github下載sample project:

https://github.com/MSOpenTech/connectthedots/archive/master.zip

 

接下來會分為三大設置部分

  • Azure 環境設置
  • Monitor WebSite 建置
  • RPi + Arduino 環境建置

 

///////Azure 環境設置///////

(原文位於:https://github.com/MSOpenTech/connectthedots/blob/master/Azure/CloudDeploy/ConnectTheDotsCloudDeploy/Docs/Temp%20and%20Humidity%20Azure%20setup.md)

 

1.首先要先處理Azure ServiceBus, EventHubs, Stream Analytics, 的建立,sample project有一個script會幫我們做完這件事情

不過由於Stream Analytics處於Preview的狀態,所以必須要手動的去Sign up.

https://account.windowsazure.com/PreviewFeatures

 

2.利用VS2013打開[放置的路徑]\ConnectTheDots\Azure\CloudDeploy\ConnecTheDotsCloudDeploy.sln 專案,並且建置(BUILD)起來。

 

3.去https://manage.windowsazure.com/publishsettings/下載AZURE 訂閱的 publishsetting file

注意,如果你有多個訂閱,必須要刪除你不要使用的訂閱,可以使用記事本或者Notepad++

<?xml version="1.0" encoding="utf-8"?>
<PublishData>
  <PublishProfile
    SchemaVersion="2.0"
    PublishMethod="AzureServiceManagementAPI">
     
    <!--舉例來說下面有兩個subscription, 只要留下一組你要用的subscription!--!>
    <Subscription
      ServiceManagementUrl="https://management.core.windows.net"
      Id="11fb1e3a-071c-4ad8-9469-1234567890ab"
      Name="Pay-As-You-Go"
      ManagementCertificate="keyyyyyyyyyy" />
    <Subscription
      ServiceManagementUrl="https://management.core.windows.net"
      Id="6eccfe6b-7a33-4e46-a756-1234567890fc"
      Name="Visual Studio Ultimate with MSDN"
      ManagementCertificate="keyyyyyyy" />
  </PublishProfile>
</PublishData>

 

4.用administrator的權限去運行命令提示字元,並切換到ConnecTheDotsCloudDeploy的執行目錄底下

cd /d ConnectTheDots\Azure\CloudDeploy\ConnecTheDotsCloudDeploy\bin\debug\

 

5.執行程式建立Azure相關服務(Service Bus, EventHubs)

ConnectTheDotsCloudDeploy.exe –n <你要的Service Bus Name> -ps <填入剛剛下載的訂閱 publishsetting file>

Service Bus的Name最少3個字元,最多47個,如果你執行之後有錯誤,很有可能是這邊的格式不符,盡量也不要有符號包含在裡面(像是包含「-」就會錯)

 

執行完之後你應該會得到以下畫面:

E:\Users\Nathaniel\Desktop\connectthedots-master\connectthedots-master\Azure\CloudDeploy\ConnectTheDotsCloudDeploy\bin\D
ebug>ConnectTheDotsCloudDeploy.exe -n DXRDAActdtest1 -ps "E:\Users\Nathaniel\Desktop\connectthedots-master\connectthedot
s-master\Azure\CloudDeploy\ConnectTheDotsCloudDeploy\bin\Debug\credentials.publishsettings"
Creating Service Bus namespace DXRDAActdtest1-ns in location Central US
Namespace DXRDAActdtest1-ns in state Activating. Waiting...
Namespace DXRDAActdtest1-ns in state Activating. Waiting...
Namespace DXRDAActdtest1-ns in state Activating. Waiting...
Creating Event Hub EHDevices
Service Bus connection string not valid yet. Waiting...
Service Bus connection string not valid yet. Waiting...
Creating Consumer Group WebSite on Event Hub EHDevices
Creating Consumer Group WebSiteLocal on Event Hub EHDevices
Creating Event Hub EHAlerts
Creating Consumer Group WebSite on Event Hub EHAlerts
Creating Consumer Group WebSiteLocal on Event Hub EHAlerts
Creating Storage Account dxrdaactdtest1storage in location Central US

Service Bus management connection string (i.e. for use in Service Bus Explorer):
Endpoint=sb://dxrdaactdtest1-ns.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=l9
13pkfvGkxcw7oHQNM8tHbofd021tXXXXXX/XXXXXXX

Device AMQP address strings (for Raspberry PI/devices):
amqps://D1:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%[email protected]
amqps://D2:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%[email protected]
amqps://D3:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%[email protected]
amqps://D4:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%[email protected]

Web.Config saved to E:\Users\Nathaniel\Desktop\connectthedots-master\connectthedots-master\Azure\WebSite\ConnectTheDotsW
ebSite\web.config

 

建議把這個畫面複製下來,因為下半部AMQP的連結TOKEN之後會用到…,如果你忘記了,也可以到Event Hubs裡面的EhDevices看到個個裝置的Token

這邊D1,D2,D3,D4..代表在EhDevices總共開了4個Sender。

 

而這個腳本會順便把等等WebSite所需要的EvnetHub EhDevices跟EhAlerts的token一併複製過去Website的專案資料夾底下

因此等等再做網站的部分「不需要」設定任何的token, 這裡已經幫你複製一份過去了。

 

6.接著就是Stream Analytics 的部分,要新增三個工作

如果你剛剛前面有得去sign up Stream Analytics的Preview,就會看到Azure Portal 左下方有可以切換的選項,

切換過去,然後按下左下角的+號開始新增,名子取”Aggregates“,目前Region只有歐洲跟美國中部,前面的script都把EvnetHubs跟相關服務建立在Central US,所以這裡就照舊了。

2015-01-20_17-40-10

 

 

7.接著點選剛剛建立好的JOB, 進去之後要先新增這個JOB的INPUT,切換到INPUT頁面

//2015/02/24補充:

建立JOB時,因為改版新增更多東西,

第一步驟請選Data Stream,

第二步驟請選Event Hub

第三步驟的SUBSCRIPTION選”Use Event Hub from Current Subscription”

 

然後在照舊繼續看下文內容

 

新增的詳細欄位順序如下:

Input Alias: “DevicesInput”

Subscription: “Use Event Hub from Current Subscription”

選擇剛剛建立好的EventHubs

Event Hub “ehdevices”

Policy Name: “StreamingAnalytics”

Serialization: JSON, UTF8

 

8.建立這個JOB的QUERY語法,切換到QUERY頁面

從Sample Project中ConnectTheDots\Azure\StreamingAnalyticsQueries資料夾找到

Aggregates.sql” ,用記事本打開複製裡面的內容貼到QUERY上頭

2015-01-20_17-46-27

 

9.建立這個JOB的輸出,切換到OUTPUT頁面

選擇剛剛建立的Namespace

Event Hub “ehalerts”

Policy name “StreamingAnalytics”

Serialization “JSON”, UTF8

 

10.回去該Stream Analytics JOB的Dashboard,然後啟動她

 

重複6~10的步驟新增Alerts,Query的語言參考”alert.sql“的內容

而我自己有調整了一下內容,要稍微看懂,依照自己的sensor要微調。

Select 'TempSpike' as alertType, 
       'Temperature over 30C' as message, 
       dspl as dsplAlert, 
       max(time) as timeStart, 
       max(time) as timeEnd, 
       Max(temp) as tempMax, 
       Min(temp) as tempMin, 
       Avg(temp) AS tempAvg
From DevicesInput TIMESTAMP BY time
Group by dspl, TumblingWindow(Second, 20)
Having tempMax > 30

 

一樣,重複6~10的步驟新增LightSensor,Query語法參考”lightsensor.sql”

而我也有自己調整了一下內容,可以參考一下

Select 'LightSensor' as alertType, 
       'The Light is turned OFF' as message, 
       dspl as dsplAlert, 
       max(time) as timeStart, 
       max(time) as timeEnd, 
       Max(lght) as lghtMax
From DevicesInput TIMESTAMP BY time
Group by dspl, TumblingWindow(Second, 20)
Having lghtMax > 900

 

 

///////Monitor WebSite 建置///////

(原文位於:https://github.com/MSOpenTech/connectthedots/blob/master/Azure/WebSite/ConnectTheDotsWebSite/Docs/Temp%20and%20Humidity%20Website%20setup.md)

 

1.打開ConnectTheDots\Azure\WebSite\ConnectTheDotsWebSite.sln 專案

2.右邊的方案總管,對網站方案右鍵,發行到Azure Website,內容看著填,不須Database,唯一要注意的是地區需要跟你的Stream Analytics一樣,在這裡統一都是Central US

3.發行,這時應該會有個頁面可以看,最下方應該會有Error,原因是因為還沒開socket連到EhDevices

 

//2015/02/24補充,最新版本碰上Azure EventHub修改,部屬網站上去之後,打開頁面會看到一頁錯誤,因為會針對EventHub Name大小寫作判斷之前不會(雖然在建立eh的時候有打大小寫,但建立之後一律被改成小寫)…所以網站的Web.config裡面原先

<add key="Microsoft.ServiceBus.EventHubDevices" value="EHDevices"/>

<add key="Microsoft.ServiceBus.EventHubAlerts" value="EHAlerts"/>

 

 

請改為

<add key="Microsoft.ServiceBus.EventHubDevices" value="ehdevices"/>

<add key="Microsoft.ServiceBus.EventHubAlerts" value="ehalerts"/>

 

 

ref:https://github.com/MSOpenTech/connectthedots/issues/39 

 

4.回到Azure, 選取你剛published的網站,切換到CONFIGURE頁面,把WEB SOCKET打開

2015-01-20_18-05-18

 

5.儲存後離開(建議RESTART網站),這時瀏覽網站最下方,若你在Azure建置的部分有作對,這邊就會告訴你Connected且顯示socket的位址。

 

 

///////RPi + Arduino 環境建置///////

(原文:https://github.com/MSOpenTech/connectthedots/blob/master/Devices/RaspberryPiGateway/RaspberryPiGateway/Docs/Raspberry%20Pi%20Gateway%20setup.md)

原文對RPi是抓NOOB的套件,但我稍微有經驗了..,所以我是用RASPBIAN做,

 

RPI的OS BUILD什麼的,就跳過了,假設OS都OK

 

1.需要安裝MONO環境跟MOZROOT

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install mono-complete
mozroots --import --ask-remove

 

 

2.建立RPi Project所需的工作目錄

mkdir /home/pi/RaspberryPiGateway

 

 

3.回到Sample Project, 打開ConnectTheDots\Devices\RaspberryPiGateway\RaspberryPiGateway.sln

 

4.編輯RaspberryPiGateway.exe.config

還記得剛剛Azure建置有D1,D2,D3,D4嗎?這裡派得上用場了

<?xml version="1.0" encoding="utf-8" ?>
<!-- NOTES
 * AMQPAddress: AMQP address string syntax is an example for Azure Service Bus/Event Hub. Any AMQP broker can be used 
   by using the proper address/target in format "amqps://<keyname>:<key>=@<namespace>.servicebus.windows.net", e.g.
   amqps://D1:AxGGuhEVRzm2b11TZt9QjOZKhsG5yr69Yl8PcHKPcf4%[email protected]
   Note you need to make sure to put in ASCII equivalents for characters such as =,/,\,+
  -->
<configuration>
  <appSettings>
    <add key ="EdgeGateway" value="RaspberryPi"/>
    <add key="AMQPAddress" value="amqps://D1:xxxxxxxxxxxxxxxxxxxxxxxxxxxxx%[email protected]" />
    <add key="EHtarget" value="ehdevices" />
  </appSettings>
</configuration>

 

修改AMQPAddress後面的Value對應前面的連結

 

5.建置這個專案,然後copy bin RaspberryPiGateway\Release(或者Debug)底下的檔案

  • RaspberryPiGateway.exe
  • Amqp.Net.dll
  • Newtonsoft.Json.dll
  • NLog.config
  • RaspberryPiGateway.exe.config
  • NLog.dll
  • 以及RaspberryPiGateway\Scripts\autorun.sh這個腳本到剛剛在RPi建立的/home/pi/RaspberryPiGateway 路徑底下。

 

原文是幫妳寫好一個SCPRPI.cmd放在RaspberryPiGateway\Scripts\底下,但需要安裝putty插件,因為我沒裝,我是用xshell的scp複製過去

 

6.更改腳本執行權限

sudo chmod 755 /home/pi/RaspberryPiGateway/autorun.sh

 

 

7.編輯/etc/rc.local,加入

/home/pi/RaspberryPiGateway/autorun.sh &

 

 

8.重新開機RPi

 

這樣RPi Gateway的部分就完成了~

 

>>>Arduino Part<<<

我這裡用Mega 2560+擴展版+DHT 22(溫溼度)+光感所完成,TOTAL價格便宜不少。

WP_20150120_18_27_43_Pro

 

官方給的ARDUINO CODE是基於Sparkfun Weather Shield的官方範例改的

所以我重新寫了較精簡的版本,在JSON傳遞上也沒有這麼多欄位需要傳遞了:

// Example testing sketch for various DHT humidity/temperature sensors
// Written by ladyada, public domain
// Extend Azure IoT by Nathaniel Chen.

#include "DHT.h"

#define DHTPIN 2     // what pin we're connected to

// Uncomment whatever type you're using!
//#define DHTTYPE DHT11   // DHT 11 
#define DHTTYPE DHT22   // DHT 22  (AM2302)
//#define DHTTYPE DHT21   // DHT 21 (AM2301)

// Connect pin 1 (on the left) of the sensor to +5V
// Connect pin 2 of the sensor to whatever your DHTPIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor


/*-----( Declare Constants )-----*/
#define LIGHTDIGIPIN 3     //Digital IO PIN3
#define LIGHTANLPIN 0
#define LEDPIN    13  // The onboard LED

/*-----( Declare Variables )-----*/
int  light_digital;  /* Holds the last state of the switch */
float  light_lvl;

float h;
float t;

char SensorSubject[] = "wthr";
char DeviceDisplayName[] = "DXRDAA_IOTSensor01";
char DeviceGUID[] = "81E79059-A393-0630-1112-526C3EF9D64B";

DHT dht(DHTPIN, DHTTYPE);

void setup() {
    Serial.begin(9600); 
    // Serial.println("DHTxx test!");

    dht.begin();
}

void loop() {
 
    h = dht.readHumidity();
    t = dht.readTemperature();
    light_lvl = analogRead(LIGHTANLPIN);
    light_digital = digitalRead(LIGHTDIGIPIN);  
    
    if (light_digital == LOW)
    {
    digitalWrite(LEDPIN, HIGH);
    //light_lvl = 1;
    }  
    else
    {
    digitalWrite(LEDPIN, LOW);
    //light_lvl = 0;
    }

  // check if returns are valid, if they are NaN (not a number) then something went wrong!
  if (isnan(t) || isnan(h)) {
    Serial.println("Failed to read from DHT");
  } else {
   /* Serial.print("Humidity: "); 
    Serial.print(h);
    Serial.print(" %\t");
    Serial.print("Temperature: "); 
    Serial.print(t);
    Serial.println(" *C");*/
    printWeather();
    delay(3000); //如果你不想傳送太快,可以調整這裡,這裡是3秒的意思
  }
}

int sequenceNumber =0;

void printWeather()
{
   
  //Serial.println();
  Serial.print("{");
  Serial.print("\"dspl\":");
  Serial.print("\"");
  Serial.print(DeviceDisplayName);
  Serial.print("\"");
  Serial.print(",\"Subject\":");
  Serial.print("\"");
  Serial.print(SensorSubject);
  Serial.print("\"");
  Serial.print(",\"DeviceGUID\":");
  Serial.print("\"");
  Serial.print(DeviceGUID);
  Serial.print("\"");
  Serial.print(",\"millis\":");
  Serial.print(millis());
  Serial.print(",\"seqno\":");
  Serial.print(sequenceNumber++);
 
  Serial.print(",\"hmdt\":");
  Serial.print(h, 1);
  Serial.print(",\"temp\":");
  Serial.print(t, 1);
  Serial.print(",\"tempH\":");
  Serial.print(t, 1);
 
  Serial.print(",\"lght\":");
  Serial.print(light_lvl,2);
  Serial.println("}");

}

 

 

DeviceDisplayName 這個變數可以改成你要的名子,這影響到再前面WebSite所呈現的Sensor Name

燒錄上去之後,接上RPi

 

這時瀏覽你前面部屬的Azure Website, 應該就會看到值了~

 

 

結束~

 

 

如果你沒有看到值,確認Website的socket已經有打開

或者重開RPi(Arduino接在上面重開就ok)

若還是沒辦法,可以看一下/home/pi/RsapberryPiGateway/logs底下的log是甚麼錯誤。

 

//2015/02/24補充

另外官方wiki之前其實有寫,怕有人沒去翻看到這頁

https://github.com/MSOpenTech/connectthedots/wiki/Things-to-Try

裡面提到就算你沒有RPi/Arduino,其中一個或者都沒有

你仍然可以用RPi Gateway那個專案的程式直接執行,因為他也是C#程式,

如果你沒有Arduino,可以單純在Windows或者RPi Run, 但須要把Program.cs裡面的

#define SIMULATEDATA

註解給拿掉,這時執行程式就會一直亂丟資料給EvnetHub(真的是亂丟…)

這時真的會瘋狂丟data…不想讓妳的azure credit 爆掉,請修改Program.cs片段內容:

#if! SIMULATEDATA
                        try
                        {
                            valuesJson = serialPort.ReadLine();
                        }
                        catch (Exception e)
                        {
                            logger.Error("Error Reading from Serial Portand sending data from serial port {0}: {1}", serialPortName, e.Message);
                            serialPort.Close();
                            serialPortAlive = false;
                        }
#else
                        Random r = new Random ();
                        valuesJson = String.Format("{{ \"temp\" : {0}, \"hmdt\" : {1}, \"lght\" : {2}, \"DeviceGUID\" : \"{3}\", \"Subject\" : \"{4}\", \"dspl\" : \"{5}\"}}", 
                            (r.NextDouble() * 120) - 10,
                            (r.NextDouble() * 100),
                            (r.NextDouble() * 100),
                            "81E79059-A393-4797-8A7E-526C3EF9D64B",
                            "wthr",
                            "Simulator");
 
                        Thread.Sleep(5000);  //這裡放入一個sleep,避免他random完東西就瘋狂送出
#endif

 

 

就可以在webpage上面看到模擬資料出來,這樣可以測試你在Azure的EventHub跟Stream Analytics 有沒有建置對~

 

如果你沒有RPi,  在Windows 執行,直接把Arduino插上電腦也可以,這時電腦就是被當作Gateway的腳色

 

Leave a comment

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