騰訊IOT安卓開發初探
- 2021 年 1 月 7 日
- 筆記
騰訊IOT 安卓開發初探
目的:將Andorid端作為一個物聯網設備(device),然後將其安卓設備上面的數據發送到騰訊雲IOT開發平台上。(這裡我們將手機上面的GPS經緯度發送到騰訊雲IOT平台上)。
騰訊IOT開發平台://console.cloud.tencent.com/iotexplorer
騰訊IOT Java SDK GitHub://github.com/tencentyun/iot-device-java
開發工具:Android Studio
代碼Github:android_test_iot_for_tecent
Tecent IOT 開發平台的使用
開發平台的官方參考文檔網址://cloud.tencent.com/document/product/1081,不過個人覺得其文檔對於Java SDK的描述不夠詳細,需要去看其 Demo 源碼才能明白其工作流程。
騰訊雲IOT開發平台的項目結構如下所示:分為兩層——項目
和 產品
。用在使用其平台的時候,既需要創建project,也需要創建product。
我們可以將項目理解為智能家居整個系統,因此在項目中有很多產品,比如說智能空調,智能報警器等等產品。
新建項目
新建項目,項目名稱隨意就行,創建好項目後,進入項目,然後創建產品。
創建產品
創建產品的選項如下:
- 設備:因為我們是準備將安卓終端作為一台設備來使用的,因此,我們應該選擇」設備「,當然,如果我們是準備將它作為網關,則看着選就行了。
- 認證方式:認證方式選擇密鑰認證,這樣在代碼中間直接寫設備的密碼就行,比證書稍微方便一點(不過實際上證書方便一點)。
- 數據協議:使用數據模板即可。
添加自定義功能
物聯網設備,之所以叫物聯網,是因為大家想把傳感器獲得的數據放在雲端,或者通過雲端去控制物聯網設備。那麼放什麼數據,控制什麼功能,則需要我們去定義。在騰訊IOT中,可以使用新建功能
定義這些功能。
點擊進入產品,選擇新建功能。
自定義功能我們只需要兩個功能:
- 經度:position_x
- 緯度:position_y
建立經度如下,在功能類型中選擇屬性,數據類型我們選擇浮點型。(經度和緯度的範圍都在-180.0 ~180.0 )
同理將緯度配置為position_y
,功能類型為屬性,數據類型同樣為浮點型,範圍為-180.0 ~180.0 。
關於功能類型的不同,可以參考下面的表格。
以下來自官方文檔
功能元素 功能描述 功能標識符 屬性 用於描述設備的實時狀態,支持讀取和設置,如模式、亮度、開關等。 PropertiesId 事件 用於描述設備運行時的事件,包括告警、信息和故障等三種事件類型,可添加多個輸出參數,如環境傳感器檢測到空氣質量很差,空調異常告警等。 EventId 行為 用於描述複雜的業務邏輯,可添加多個調用參數和返回參數,用於讓設備執行某項特定的任務,例如,開鎖動作需要知道是哪個用戶在什麼時間開鎖,鎖的狀態如何等。 ActionId
點擊下一步,進入設備開發。
設備開發
因為我們使用的是Java SDK進行開發,沒有使用模組也沒有基於OS開發,因此直接點擊下一步。
點擊下一步就到了微信小程序配置。
微信小程序配置
騰訊IOT平台相比較於其他平台,有一個很大的特點就是可以很好的支持小程序。也就是說,在開發的階段,就可以使用小程序去驗證設備的功能。並且這個微信小程序不需要自己寫樣式代碼,只需要進行簡單的配置,就可以直接從小程序上面看到物聯網設備的數據。
因為這裡我們使用的數據很簡單,只有經度和緯度兩個數據,所以隨便配置一下面板即可。
面板配置
這裏面板類型選擇標準面板,然後配置一下模板樣式(配置長按鈕稍微好看一點),配置完效果圖如右邊所示。
新建設備
新建設備`的意義:創建一個設備代表啟動了一個賬號(這個設備會提供一個密鑰),我們的設備使用這個密鑰,就可以讓我們的設備連接騰訊雲IOT平台進行數據交互。
新建設備的步驟如下所示:
使用設備
點擊test_device,進入設備管理。
設備管理界面如下所示:
-
設備信息:這裏面是設備的一些基本屬性,其中通過設備
名稱
,設備密鑰
,和產品ID
就可以唯一定位一個設備
,然後對其進行操作。 -
設備日誌:設備日誌裏面保存着設備的上行和下行數據。
-
在線調試:通過在線調試,我們可以模擬設備的行為,或者對設備下發命令。
🆗,以上的所有就是騰訊IOT平台的介紹,通過上面的操作,就可以創建一個設備,獲得其name,key,id,然後對其進行開發了。
安卓開發
安卓開發實現的效果很簡單,就是實現一個頁面展示經緯度,然後將經緯度數據上傳到騰訊IOT平台就行。
前置配置
安卓開發,創建一個Android Studio項目,然後在APP的build gradle 中加入騰訊IOT的SDK
implementation 'com.tencent.iot.explorer:explorer-device-android:3.2.0'
然後新建兩個JSON文件(必做!!!!!),data.json
,代表的是設備的屬性(這個文件的來源會在後面解釋),然後是app-config.json
,這個代表的是設備的配置(來源後文解釋)。
data.json
data.json 文件一定要放在安卓的assets目錄下,安卓如何添加assets目錄可以看《Android studio 添加assets文件夾》。data.json需要存放一些數據。這個數據實際上就是自定義功能的數據,複製之後粘貼到data.json文件中。
app-config.json
app-config.json文件的位置一定不要放錯,它與src是同級目錄,在app的下一級目錄。
app-config裏面是device的信息,數據內容如下:
{
"PRODUCT_ID": "產品ID",
"DEVICE_NAME": "設備名稱",
"DEVICE_PSK": "設備密鑰",
"SUB_PRODUCT_ID": "",
"SUB_DEV_NAME": "",
"SUB_DEV_PSK": "",
"SUB_PRODUCT_ID2": "",
"SUB_DEV_NAME2": "",
"SUB_DEV_PSK2": ""
}
來源:
權限配置
位置權限,和聯網權限。在AndroidManifest.xml
中添加如下權限。值得注意的是,位置權限在安卓版本比較高的設備中,需要使用代碼申請位置權限。
<!-- 位置權限-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 聯網權限-->
<uses-permission android:name="android.permission.INTERNET" />
連接平台代碼
通過官方提供的SDK,接入騰訊IOT平台實現設備連接和數據上傳。代碼如下所示,具體的含義寫在注釋中。在使用中,我們就可以通過實例化IotCloudUtil
,然後使用connect()
函數來實現連接和propertyReport
函數來實現上傳數據。
package cc.weno.data_template;
import com.tencent.iot.explorer.device.android.common.Status;
import com.tencent.iot.explorer.device.android.data_template.TXDataTemplateClient;
import com.tencent.iot.explorer.device.android.data_template.TXDataTemplateDownStreamCallBack;
import com.tencent.iot.explorer.device.android.mqtt.TXMqttActionCallBack;
import com.tencent.iot.explorer.device.android.mqtt.TXMqttRequest;
import com.tencent.iot.explorer.device.android.utils.AsymcSslUtils;
import org.eclipse.paho.client.mqttv3.IMqttToken;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.json.JSONObject;
import java.util.concurrent.atomic.AtomicInteger;
import cc.weno.location.MainActivity;
/**
* 連接雲平台的類
*
* @author XiaoHui
*/
public class IotCloudUtil {
/**
* 服務器網址
*/
public static String mBrokerURL = "ssl://iotcloud-mqtt.gz.tencentdevices.com:8883";
/**
* 產品ID
*/
public static String mProductID = "9JXQQW7SR5";
/**
* 設備名稱
*/
public static String mDevName = "test_device";
/**
* 設備密鑰
*/
public static String mDevPSK = "pCIUP7zhTp7snmfxb/72+g==";
/**
* data.json的名字
*/
public static String mJsonFileName = "data.json";
/**
* MQTTAction的回調
*/
private TXMqttActionCallBack mMqttActionCallBack = null;
/**
* 下行消息的回調
*/
private TXDataTemplateDownStreamCallBack mDownStreamCallBack = null;
/**
* MQTT連接實例
*/
private TXDataTemplateClient mMqttConnection;
/**
* Activity實例
*/
private MainActivity context;
/**
* 請求ID
*/
private static AtomicInteger requestID = new AtomicInteger(199);
public IotCloudUtil(MainActivity context) {
this.context = context;
mDownStreamCallBack = new MyDownCallback();
mMqttActionCallBack = new MyMQttCallBack();
}
/**
* 建立MQTT連接
*/
public void connect() {
// 創建連接client
mMqttConnection = new TXDataTemplateClient(context, mBrokerURL, mProductID, mDevName, mDevPSK,
null, null, mMqttActionCallBack,
mJsonFileName, mDownStreamCallBack);
// 設置連接參數
MqttConnectOptions options = new MqttConnectOptions();
// 連接超時
options.setConnectionTimeout(8);
// 保持活躍的時間間隔
options.setKeepAliveInterval(240);
// 是否自動重連
options.setAutomaticReconnect(true);
// 因為我們是使用密鑰登錄,所以需要設置這個
options.setSocketFactory(AsymcSslUtils.getSocketFactory());
// 建立Request請求
TXMqttRequest mqttRequest = new TXMqttRequest("connect", requestID.getAndIncrement());
// 建立連接
mMqttConnection.connect(options, mqttRequest);
}
/**
* 斷開MQTT連接
*/
public void disconnect() {
TXMqttRequest mqttRequest = new TXMqttRequest("disconnect", requestID.getAndIncrement());
mMqttConnection.disConnect(mqttRequest);
}
/**
* 發送消息
*
* @param property 消息內容
* @param metadata 屬性的metadata,目前只包含各個屬性對應的時間戳,可以為NULL
* @return 狀態
*/
public Status propertyReport(JSONObject property, JSONObject metadata) {
return mMqttConnection.propertyReport(property, metadata);
}
/**
* MQTT的回調函數,暫時不考慮
*/
public static class MyMQttCallBack extends TXMqttActionCallBack {
@Override
public void onConnectCompleted(Status status, boolean reconnect, Object userContext, String msg) {
}
@Override
public void onConnectionLost(Throwable cause) {
}
@Override
public void onDisconnectCompleted(Status status, Object userContext, String msg) {
}
@Override
public void onPublishCompleted(Status status, IMqttToken token, Object userContext, String errMsg) {
}
@Override
public void onSubscribeCompleted(Status status, IMqttToken asyncActionToken, Object userContext, String errMsg) {
}
@Override
public void onMessageReceived(final String topic, final MqttMessage message) {
}
}
/**
* 實現下行消息處理的回調接口,暫時不考慮
*/
private static class MyDownCallback extends TXDataTemplateDownStreamCallBack {
@Override
public void onReplyCallBack(String msg) {
}
@Override
public void onGetStatusReplyCallBack(JSONObject data) {
}
@Override
public JSONObject onControlCallBack(JSONObject msg) {
return null;
}
@Override
public JSONObject onActionCallBack(String actionId, JSONObject params) {
return null;
}
}
}
安卓頁面配置
安卓頁面很簡單,就是展示經度和緯度的數據。
頁面代碼如下所示:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="//schemas.android.com/apk/res/android"
xmlns:tools="//schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:text="x軸:" />
<TextView
android:id="@+id/x_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="100dp"
android:text="0.00" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="100dp"
android:text="y軸:" />
<TextView
android:id="@+id/y_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="100dp"
android:layout_marginTop="100dp"
android:text="0.00" />
</RelativeLayout>
Activity代碼
在MainActivity,我們要實現如下的功能,申請位置權限,獲得經緯度的數據,然後進行頁面展示,最後將數據上傳到雲平台。
package cc.weno.location;
import android.Manifest;
import android.location.Location;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import com.tencent.iot.explorer.device.android.common.Status;
import org.json.JSONException;
import org.json.JSONObject;
import cc.weno.data_template.IotCloudUtil;
/**
* 主頁面,進行展示以及發送數據
*
* @author XiaoHui
*/
public class MainActivity extends AppCompatActivity {
/**
* 展示經度
*/
private TextView xPositionView;
/**
* 展示緯度
*/
private TextView yPositionView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
xPositionView = findViewById(R.id.x_position);
yPositionView = findViewById(R.id.y_position);
// 基本上現在的安卓機都需要申請位置權限了
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
// 獲得位置數據並且發送數據到雲平台
getAndSendLocation();
}
private void getAndSendLocation() {
// 獲得GPS工具類
GPSUtils gpsUtil = GPSUtils.getInstance(this);
// 獲得位置
Location location = gpsUtil.getLocation();
double positionX = location.getLatitude();
double positionY = location.getLongitude();
// 在手機頁面上展示
xPositionView.setText(String.valueOf(positionX));
yPositionView.setText(String.valueOf(positionY));
// IotCloudUtil
IotCloudUtil iotCloudUtil = new IotCloudUtil(this);
// 連接雲平台
iotCloudUtil.connect();
// 等待幾秒鐘,連接成功
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 調用發送數據的函數需要傳入JsonObject類型的數據
JSONObject property = new JSONObject();
try {
property.put("position_x", (float) positionX);
property.put("position_y", (float) positionY);
// 發送數據
Status status = iotCloudUtil.propertyReport(property, null);
if (status == Status.OK){
// 發送成功
}
} catch (JSONException e) {
e.printStackTrace();
}
}
其中GPS工具就不進行介紹了,因為其不是重點,關於具體的代碼可以參考GitHub。
微信小程序使用
前面我們說了,可以是用微信小程序對開發的物聯網設備進行開發調試,然後我們在如下的頁面得到設備的二維碼。
然後打開」騰訊連連「小程序,對二維碼進行掃描,即可將設備加入。
然後我們運行安卓程序,自動向騰訊IOT平台發送經緯度數據,然後在微信小程序上就可以看到最新的數據。
中間存在些許誤差,可能是因為double轉float的精度原因導致的。
總結
通過上面的操作我們創建了一個安卓程序,然後能夠在微信小程序上面看到安卓設備的經緯度。歸咎於原理,就是MQTT協議。使用平台提供的SDK,讓開發者省下了大量花費在通信協議上面的時間。然而,我們還是應該去關注MQTT協議本身。知其然,更要知其所以然。
參考
-
Github:android_test_iot_for_tecent
-
物聯網開發平台使用文檔:物聯網開發平台 – 文檔中心 – 騰訊雲 (tencent.com)
-
Github:iot-device-java