1. 程式人生 > >onenet物聯網平臺使用

onenet物聯網平臺使用

    OneNET平臺提供裝置全生命週期管理相關工具,幫助個人、企業快速實現大規模裝置的雲端管理;開放第三方API介面,推進個性化應用系統構建;提供定製化“和物”APP,加速個性化智慧應用生成。註冊地址為https://open.iot.10086.cn/,使用者可以根據其中的官方文件註冊賬號,建立裝置應用。

本教程使用stm32系列晶片,完成硬體與onenet物聯網平臺進行互動。 stm32外接Enc28j60網絡卡晶片,該晶片內建乙太網mac,phy層。使用該晶片完成網路傳輸,需要在該晶片上搭建一個軟體TCP/IP協議棧,而UIP協議棧是一個很好的選擇。在stm32上移植該協議棧,就可以把stm32採集到的感測器資料上傳到onenet雲平臺。onenet平臺支援多種協議,比如Http,MQTT,EDP等協議,而通過HTTP方式上傳至雲平臺是一種常見而且簡單的方式,只需要把stm32作為HTTP客戶端,通過HTTP的一些標準請求,比如Get,Post等,就可以把資料上傳到雲端,或者從雲端獲取資料。 RestFul API 基於HTTP 協議(詳見//

www.w3.org/Protocols/HTTP/1.0/spec.html) 和json資料格式(詳見//www.json.org/json-zh.html) ,適合平臺資源管理、平臺與平臺之間資料對接、使用短連線上報終端資料、時間序列化資料儲存等場景。

HTTP協議

HTTP是一個基於TCP/IP通訊協議來傳遞資料,HTTP連線報文分為請求和響應兩種,一次請求訊息包含請求行,請求頭部,空行和請求資料四部分組成。

  • 請求行

用來說明請求型別,要訪問的資源目錄以及HTTP使用版本,HTTP請求型別包含GET,POST等,它們的具體差別可以檢視相關文件

  • 請求頭部

接著請求行之後的部分,包含若干屬性值,比如指定主機HOST,和onenet互動的API

  • 空行

空行位於報文首部和報文主體之間。

  • 請求資料

對於不同的請求過程,得到的請求資料不一樣,比如HTML,JSON等.

 

OneNet HTTP API按照RESTful的方式向外提供服務,其資源模型中包含的資源種類有:使用者、裝置(device)、資料流(datastream)、資料點(datapoint)、觸發器(trigger)、API key、命令等。

  • 資料點(Datapoint)

即一個數據流中的一個具體的資料值。資料點採用“Key-Value”的方式儲存。其中Key的組成包括裝置ID、資料流ID、時間等資訊,value部分可以為任何資料物件,如整數、字串或者JSON資料型別。

  • 資料流(Datastream)

一個數據流可以理解為一類資料,如感測器之溫度、位置之經緯度,空氣之溼度等。使用者可以自定義資料流名稱,即資料流ID;一個裝置可以新增多個數據流。

根據onenet官方文件,使用onenet上傳資料的POST請求如下所示,請求頭部需要指定三個屬性,分別為api-key,Host,Content-Length。Content-Length為請求資料的位元組大小,在報文首部和報文主體之間還存在空行。使用restful上傳的資料格式為json,請求資源的目錄為 /devices/509226975/datapoints,其中509226975為建立的裝置ID。

POST /devices/509226975/datapoints HTTP/1.1 
api-key:aH2orrDNlT1gKXrdurQBoPbm5SI= 
Host:api.heclouds.com 
Content-Length:63 

{"datastreams":[{"id":"sys_time","datapoints":[{"value":50}]}]}

 

先使用上面的POST請求內容,通過網路除錯組手上傳到onenet伺服器,由以下的截圖得出,資料上傳之後,伺服器就會給客戶端回覆響應的響應。HTTP響應也由狀態行、訊息報頭、空行和響應正文四個部分組成,其中響應的第一行為狀態行,有HTTP協議版本號(HTTP/1.1),狀態碼(200),狀態訊息(OK)。其中第二行和第三行為訊息報頭,用來說明客戶端要使用的附加資訊,Date:生成響應的日期和時間;Content-Type:指定了MIME型別的json,Content-Length為效能感應正文的大小。第三部分為空行,訊息報頭後面的空行是必須的。第四部分為響應正文,伺服器返回給客戶端的文字資訊,圖中是返回得到的json資料。

 

curl是利用URL語法在命令列方式下工作的開原始檔傳輸工具,在ubuntu主機上可以通過apt-get方式安裝,使用它可以使用命令列的方式,完成一些HTTP請求。在請求行中指定http方法,使用--request GET或--request POST來指定,請求頭部使用--header "請求頭內容" 來指定,報文內容使用--data "請求內容"來指定。ubuntu上通過curl工具來向onenet請求獲取資料命令如下所示:

//向裝置513591948查詢資料 
curl --request GET --header "api-key: oueSPcHZ0YdDypDfxY56ynJs=4o=" http://api.heclouds.com/devices/513591948/datastreams
 //向裝置509226975上傳資料 
curl --request POST --data "{\"datastreams\":[{\"id\":\"sys_time\",\"datapoints\":[{\"value\":888}]}]}" --header "api-key: aH2orrDNlT1gKXrdurQBoPbm5SI=" http://api.heclouds.com/devices/509226975/datapoints

 

 

stm32上傳溫度資料給onenet,使用stm32開啟一個定時器,在定時器中將傳送標誌位置位,在UIp狀態機空閒時不斷判斷該標誌位是否置位,如果置位就將採集到的溫度資料打包成上述講解的HTTP報文並通過POST上傳到onenet,然後使用者在瀏覽器就可以看到相關資料。相關核心程式碼如下所示:

void onenet_send()
{
		float value=get_temperature();//獲取stm32內部溫度感測器資料
             //打包請求資料
		sprintf(http_content,"{\"datastreams\":[{\"id\":\"temp\",\"datapoints\":[{\"value\":%3.1f}]}]}",value);

             //請求行
		sprintf(http_header,"POST %s HTTP/1.1\r\n",remote_path);
		strcpy(http_request,http_header);
		//請求頭部的屬性
		sprintf(http_attribute,"Host:%s\r\n",remote_server);
		strcat(http_request,http_attribute);

		memset(http_attribute,0,sizeof(http_attribute));
		//新增請求頭部的屬性
		sprintf(http_attribute,"api-key:%s\r\n",apikey);
		strcat(http_request,http_attribute);
		memset(http_attribute,0,sizeof(http_attribute));
		//新增請求頭部的屬性
		sprintf(http_attribute,"Content-Length:%d\r\n",strlen(http_content));
		strcat(http_request,http_attribute);
		memset(http_attribute,0,sizeof(http_attribute));

		strcat(http_request,"\r\n");
		strcat(http_request,http_content);
		uip_send(http_request,sizeof(http_request));
}
//uip回撥介面
void tcp_client_appcall()
{
	if(uip_connected())//已經連線上伺服器?
	{
		uip_sock_flag =CLIENT_CONNECT;
		uip_log("tcp_client connected!\r\n");//	
		return;
	}
	if(uip_aborted())//意外終止?
	{
		//uip_abort();
		uip_sock_flag =CLIENT_DISCONNECT;
		uip_log("tcp_client aborted!\r\n");//´òÓ¡log
		tcp_client_reconnect();
	}
	if(uip_timedout())//超時?
	{

		uip_sock_flag =CLIENT_TIMEOUT;
		uip_log("tcp_client timeout!\r\n");//
		tcp_client_reconnect();
	}
	if(uip_acked())//確認發出資料?
	{
            uip_ack_flag=1;
		 uip_log("tcp_client acked!\r\n");//±íʾ³É¹¦·¢ËÍ		
	}
	if(uip_newdata())//遠端主機已經發出資料
	{
		if(uip_ack_flag)
		{
		 uip_log("*************tcp_client recv data!*********\r\n");//±íʾ³É¹¦·¢ËÍ		
		 printf("\r\n%s\r\n",uip_appdata);
		}
	}
	if(uip_rexmit())//重發?
	{
		 uip_log("tcp_rexmit\r\n");
		 onenet_send();
		return;
	}
	if(uip_poll())//應用程式迴圈執行
	{
		   uip_log("uip_poll\r\n");
// 		if(uip_sock_flag!=CLIENT_CONNECT)
// 		{
// 			uip_abort();
// 		}
  		if(uip_send_flag)//判斷髮送標誌位是否置位?
 		  {
 			  uip_send_flag=0;
		           onenet_send();
   		}
	}
	if(uip_closed())//由於onenet上傳一次資料,連線會自動關閉  關閉後就重連
	{
		uip_ack_flag=0;
		uip_sock_flag=CLIENT_DISCONNECT;
		uip_log("tcp_client closed! reconnect!\r\n");//´òÓ¡log
		tcp_client_reconnect();
		return;
	}
}
//定時器中斷服務函式 
void TIM3_IRQHandler(void)
{ 
	static u8  tcnt;
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //
	{
  	  GPIOE->ODR ^= GPIO_Pin_8;

	   if(uip_sock_flag == CLIENT_CONNECT)//連線是否還存在?
	   {
		     uip_send_flag=1;

	   }
	} 
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //
}	

 

開啟瀏覽器,進入onenet中的裝置列表,進入此裝置的裝置流展示的選項,就可以觀察到溫度資料已經上傳到雲平臺上了。

 

 

以上是通過一個POST請求將上傳上傳到雲平臺的例子,首先在網路除錯助手中,通過一個get請求查詢資料,使用報文如下:

GET /devices/513591948/datastreams HTTP/1.1 
Host:api.heclouds.com 
api-key:oueSPcHZ0YdDypDfxY56ynJs=4o=

下面將使用stm32通過get請求不斷查詢伺服器的資料,來控制板載LED燈的量滅,在使用過程中開啟一個定時器每隔一段時間通過一個Get請求,查詢其中的資料,然後在uip狀態機的接受過程中接收伺服器響應的資料,得到資料就解析其中的響應報文,然後解析json中的資料包,然後得到開關量,控制LED的亮滅

//傳送一個GET請求
void onenet_send()
{
              //打包請求資料
		sprintf(http_header,"GET %s HTTP/1.1\r\n",remote_path);
		strcpy(http_request,http_header);
		//請求行
		sprintf(http_attribute,"Host:%s\r\n",remote_server);
		strcat(http_request,http_attribute);

		memset(http_attribute,0,sizeof(http_attribute));
		//請求行新增屬性
		sprintf(http_attribute,"api-key:%s\r\n",apikey);
		strcat(http_request,http_attribute);
		memset(http_attribute,0,sizeof(http_attribute));


		strcat(http_request,"\r\n");
	
		uip_send(http_request,sizeof(http_request));
}
//uip回撥介面
void tcp_client_appcall()
{
	if(uip_connected())//已經連線上伺服器?
	{
		uip_sock_flag =CLIENT_CONNECT;
		uip_log("tcp_client connected!\r\n");//±íʾÁ¬½Ó³É¹¦	
		return;
	}
	if(uip_aborted())//意外終止?
	{
		//uip_abort();
		uip_sock_flag =CLIENT_DISCONNECT;
		uip_log("tcp_client aborted!\r\n");//´òÓ¡log
		tcp_client_reconnect();
		//uip_resolv_connect(remote_server,80);
	}
	if(uip_timedout())//超時?
	{

		uip_sock_flag =CLIENT_TIMEOUT;
		uip_log("tcp_client timeout!\r\n");//´òÓ¡log
		tcp_client_reconnect();
		//uip_resolv_connect(remote_server,80);
		return;
	}
	if(uip_acked())//確認已經發出資料?
	{
     uip_ack_flag=1;
		 uip_log("tcp_client acked!\r\n");//±íʾ³É¹¦·¢ËÍ		
	}
	if(uip_newdata())//遠端主機已經發出資料
	{
		if(uip_ack_flag)
		{
			 uip_ack_flag=0;
		 uip_log("*************tcp_client recv data!*********\r\n");//±íʾ³É¹¦·¢ËÍ		
		
			len_temp=uip_datalen();
			memcpy(tcp_client_databuf,uip_appdata,len_temp);
			 printf("\r\n%s\r\n",uip_appdata);
			value_info=(u8 *)strstr(tcp_client_databuf,"\"current_value\"");//擷取"\"current_value\""開頭的子串
			if(value_info!=NULL)
			{
				len_temp=strlen("\"current_value\":");
				
				status=*(value_info+len_temp);
				printf("\r\n%c\r\n",status);
				if(status =='0')
				{
					printf("switch close!\r\n");
					// GPIOE->ODR |= GPIO_Pin_8;
					 GPIOE->BRR = GPIO_Pin_8;
				}
				else
				{
					printf("switch open!\r\n");
					//GPIOE->ODR &= ~GPIO_Pin_8;
					 GPIOE->BSRR = GPIO_Pin_8;
				}
			}
			memset(tcp_client_databuf,0,sizeof(tcp_client_databuf));
		}
	}
	if(uip_rexmit())//重發
	{
		 uip_log("tcp_rexmit\r\n");
		 onenet_send();
		return;
	}
	if(uip_poll())//應用程式迴圈執行
	{
		   //uip_log("uip_poll\r\n");
 		if(uip_sock_flag!=CLIENT_CONNECT)
 		{
 			uip_abort();
 		}
  		if(uip_send_flag)//傳送標誌位是否置位
 		  {
 			  uip_send_flag=0;
		    onenet_send();
   		}
			return;
	}
	if(uip_closed())//由於onenet上傳一次資料,連線會自動關閉  關閉後就重連
	{
		uip_ack_flag=0;
		uip_sock_flag=CLIENT_DISCONNECT;
		uip_log("tcp_client closed! reconnect!\r\n");//´òÓ¡log
		tcp_client_reconnect();
		return;
	}
}
//定時器3中斷服務函式	 
void TIM3_IRQHandler(void)
{ 
	static u8  tcnt;
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) 
	{
  	 

	   if(uip_sock_flag == CLIENT_CONNECT)//還存在連線
	   {
		     uip_send_flag=1;

	   }
	} 
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //
}

 

在瀏覽器中進入onenet中的應用管理,開啟該應用,按下ON/OFF按鈕,就可以控制板載LED燈的亮滅