一、阿里雲IoT物聯網平臺介紹
Link Kit SDK由阿里雲提供給設備廠商,由設備廠商集成到設備上後通過該SDK將設備安全的接入到阿里雲IoT物聯網平臺,從而讓設備可以被阿里雲IoT物聯網平臺進行管理。設備需要支持TCP/IP協議棧才能集成Link Kit SDK,zigbee、433、KNX這樣的非IP設備需要通過網關設備接入到阿里雲IoT物聯網平臺,網關設備需要集成Link Kit SDK。
阿里雲IoT物聯網平臺在雲端提供智能生活、智能製造、智能人居等多個行業解決方案/服務,設備使用Link Kit SDK接入到阿里雲IoT物聯網平臺後即可以被這些行業解決方案管理,也即,阿里雲並沒有爲不同的行業解決方案推出不同的設備接入SDK。阿里雲IoT物聯網行業解決方案與Link Kit SDK的關係如下圖所示:
軟件結構與功能
Link Kit SDK的軟件結構及功能如下圖所示:
- 應用編程接口(API)
Link Kit SDK提供API給設備調用,用於對SDK提供的各個功能模塊進行控制
- 功能模塊
Link Kit SDK提供了一系列功能模塊供設備調用:
- 設備連雲:提供MQTT、CoAP、HTTP/S等多種方式連接阿里雲IoT物聯網平臺
- 設備身份認證:提供一機一密、一型一密對設備進行身份認證
- OTA:提供設備固件升級
- 子設備管理:接入子設備
- WiFi配網:將無線路由器AP的SSID、密碼傳輸給WiFi設備
- 設備管理:提供屬性、服務、事件來對設備進行管理和控制
- 用戶綁定:提供安全綁定token來支持用戶與設備進行綁定
- 設備本地控制:對於使用WiFi和以太網接入的設備,手機或者網關如果與設備位於同一個局域網,則可以通過局域網對設備進行控制而不是通過雲端進行控制,從而讓控制更快捷更可靠
- 硬件適配接口(Hardware Abstraction Layer, HAL)
有的功能模塊需要設備廠商提供一些信息或者處理函數,Link Kit SDK爲這些接口定義了HAL讓設備廠商用於實現
Link Kit SDK適用產品
Link Kit SDK適用於一切連接阿里雲IoT物聯網平臺的產品,下面是一些通過集成Link Kit SDK連接阿里雲物聯網平臺的產品供廠商參考:
- 生活類單品
這類設備大多通過WiFi連接家庭中的無線路由器,繼而連接互聯網並與阿里雲IoT物聯網平臺進行通信。常見的設備包括:電子貓眼、智能鎖、風扇、掃地機器人、空調、冰箱、接線板、空氣進化器、取暖器、窗簾、燈具、電熱水器、油煙機、微波爐、烤箱等等。此類產品的網絡接入示意圖如下所示:
注:
- 目前大多數的生活類WiFi單品在硬件上集成了使用AliOS的WiFi模組,AliOS通過集成Link Kit SDK讓設備連接阿里雲IoT物聯網平臺;
- 使用以太網接入無線路由器的單品也可以集成Link Kit SDK連接阿里雲IoT物聯網平臺
- 網關類產品
有的設備不支持TCP/IP協議,無法直接集成Link Kit SDK,這樣的設備需要通過集成了Link Kit SDK的網關接入阿里雲IoT物聯網平臺,包括:zigbee網關、藍牙網關、433網關、KNX網關等。網關類產品的網絡接入示意圖如下所示:
注:Link Kit SDK提供了子設備管理能力,網關通過集成Link Kit SDK,可以將自己連接的非IP設備(又稱子設備)通過自己連接到阿里雲IoT物聯網平臺,在子設備上無需做任何軟件改動。
- 蜂窩網接入產品
使用電信運營商的蜂窩網連接的IoT產品,大多應用於農業、城市等覆蓋地域較廣的場景,或者設備處於移動的場景,比如:物流運輸車、自動售貨機、氣象收集系統、水文收集系統、智能電錶、智能水錶等等。此類產品的接入網絡示意圖如下所示:
二、案例實踐
1.概述
隨着新型冠狀病毒疫情發展,社區居家隔離成爲有效手段,而體溫排查是社區工作的重中之重!藉助IoT物聯網技術可以方便的完成居民體溫實時監控和歷史數據的完整追溯。
2.技術架構方案
基於穩定性,高併發,低時延的考量我們選擇阿里雲IoT物聯網平臺搭建整套系統。首先手持測溫槍通過藍牙連接到DTU模塊,DTU模塊以MQTT協議接入物聯網平臺。數據上雲後,通過規則引擎流轉服務端訂閱的AMQP消費組,實時推送到我們業務服務器。管理人員使用手機小程序即可實時看到出入人員的體溫數據。
3.雲端開發
3.1 產品創建
進入物聯網平臺控制檯,創建產品。
在產品詳情Topic列表,增加用於數據傳輸的Topic,如下:
3.2 註冊設備
產品定義好後,我們基於這個產品創建一個具體設備,獲取到設備身份三元組。
3.3 創建消費組
接下來,我們要在服務端訂閱創建用來接收數據的消費組,查看下圖:
3.4 配置規則引擎
最後,我們通過規則引,把設備上報的數據做業務處理後,流轉到我們服務器的消費組,從而實現企業自己的設備採集的業務數據到達企業自己的後臺服務器的流轉過程。
4.設備開發
在完成了雲上控制檯的配置工作後,我們要做的就是設備端業務開發。這裏我們在Mac上用nodejs腳本模擬設備業務行爲,設備MQTT連接,數據上報。
完整代碼如下:
// 引入依賴mqtt庫,或自己實現
const mqtt = require('aliyun-iot-mqtt');
// 設備身份
var options = {
productKey: "設備pk",
deviceName: "設備dn",
deviceSecret: "設備ds",
regionId: "cn-shanghai"
};
// 1.建立連接
const client = mqtt.getAliyunIotMqttClient(options);
// 2.設備接收雲端指令數據
client.on('message', function(topic, message) {
console.log("topic " + topic)
console.log("message " + message)
})
// 3. 模擬設備 上報數據(原始報文)
setInterval(function() {
client.publish(`/${options.productKey}/${options.deviceName}/user/data`, getPostData(),{qos:1});
}, 1000);
// 模擬 設備原有報文格式
function getPostData() {
let payload = {
temperature:Math.floor((Math.random() * 20) + 10)
};
console.log("payload=[ " + payload+" ]")
return JSON.stringify(payload);
}
5.服務端開發
服務端我們以Java爲例,演示如何接收IoT平臺推送過來的設備上報數據。
5.1 業務服務器接收IoT數據
參考服務端訂閱AMQP文檔 https://help.aliyun.com/document_detail/143601.html
完整代碼如下:
package com.aliyun.iot;
import org.apache.commons.codec.binary.Base64;
import org.apache.qpid.jms.JmsConnection;
import org.apache.qpid.jms.JmsConnectionListener;
import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.jms.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import java.net.URI;
import java.util.Hashtable;
public class AMQPClient {
private final static Logger logger = LoggerFactory.getLogger(AMQPClient.class);
//消費組配置參數
private static String accessKey = "阿里雲賬號ak";
private static String accessSecret = "阿里雲賬號as";
private static String consumerGroupId = "服務端訂閱消費組ID";
private static String aliUID = "替換你的阿里雲賬號UID";
public static void main(String[] args) throws Exception {
long timeStamp = System.currentTimeMillis();
//簽名方法
String signMethod = "hmacsha1";
//控制檯服務端訂閱中消費組狀態頁客戶端ID一欄將顯示clientId參數。
//建議使用機器UUID、MAC地址、IP等唯一標識等作爲clientId。便於您區分識別不同的客戶端。
String clientId = "ecs_"+System.currentTimeMillis();
//UserName組裝
String userName = clientId + "|authMode=aksign"
+ ",signMethod=" + signMethod
+ ",timestamp=" + timeStamp
+ ",authId=" + accessKey
+ ",consumerGroupId=" + consumerGroupId
+ "|";
//password組裝
String signContent = "authId=" + accessKey + "×tamp=" + timeStamp;
String password = doSign(signContent,accessSecret, signMethod);
//按照qpid-jms的規範,組裝連接URL。
String connectionUrl = "failover:(amqps://"+aliUID+".iot-amqp.cn-shanghai.aliyuncs.com:5671?amqp.idleTimeout=80000)"
+ "?failover.reconnectDelay=30";
Hashtable<String, String> hashtable = new Hashtable<>();
hashtable.put("connectionfactory.SBCF",connectionUrl);
hashtable.put("queue.QUEUE", "default");
hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory");
Context context = new InitialContext(hashtable);
ConnectionFactory cf = (ConnectionFactory)context.lookup("SBCF");
Destination queue = (Destination)context.lookup("QUEUE");
// 創建和IoT平臺的AMQP連接
Connection connection = cf.createConnection(userName, password);
((JmsConnection) connection).addConnectionListener(myJmsConnectionListener);
// 創建 Session
// Session.CLIENT_ACKNOWLEDGE: 收到消息後,需要手動調用message.acknowledge()
// Session.AUTO_ACKNOWLEDGE: SDK自動ACK(推薦)
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
connection.start();
// 創建消費者
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(messageListener);
}
private static MessageListener messageListener = new MessageListener() {
@Override
public void onMessage(Message message) {
try {
byte[] body = message.getBody(byte[].class);
String content = new String(body);
String topic = message.getStringProperty("topic");
String messageId = message.getStringProperty("messageId");
logger.info("receive message"
+ ", topic = " + topic
+ ", messageId = " + messageId
+ ", content = " + content);
System.out.println();
//如果創建Session選擇的是Session.CLIENT_ACKNOWLEDGE,這裏需要手動ACK。
//message.acknowledge();
//如果要對收到的消息做耗時的處理,請異步處理,確保這裏不要有耗時邏輯。
} catch (Exception e) {
e.printStackTrace();
}
}
};
private static JmsConnectionListener myJmsConnectionListener = new JmsConnectionListener() {
/**
* 連接成功建立。
*/
@Override
public void onConnectionEstablished(URI remoteURI) {
logger.info("onConnectionEstablished, remoteUri:{}", remoteURI);
}
/**
* 嘗試過最大重試次數之後,最終連接失敗。
*/
@Override
public void onConnectionFailure(Throwable error) {
logger.error("onConnectionFailure, {}", error.getMessage());
}
/**
* 連接中斷。
*/
@Override
public void onConnectionInterrupted(URI remoteURI) {
logger.info("onConnectionInterrupted, remoteUri:{}", remoteURI);
}
/**
* 連接中斷後又自動重連上。
*/
@Override
public void onConnectionRestored(URI remoteURI) {
logger.info("onConnectionRestored, remoteUri:{}", remoteURI);
}
@Override
public void onInboundMessage(JmsInboundMessageDispatch envelope) {}
@Override
public void onSessionClosed(Session session, Throwable cause) {}
@Override
public void onConsumerClosed(MessageConsumer consumer, Throwable cause) {}
@Override
public void onProducerClosed(MessageProducer producer, Throwable cause) {}
};
/**
* password簽名計算方法,請參見上一篇文檔:AMQP客戶端接入說明。
*/
private static String doSign(String toSignString, String secret, String signMethod) throws Exception {
SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(), signMethod);
Mac mac = Mac.getInstance(signMethod);
mac.init(signingKey);
byte[] rawHmac = mac.doFinal(toSignString.getBytes());
return Base64.encodeBase64String(rawHmac);
}
}
6.設備運行日誌
6.1 運行數據上報
6.2 數據流轉日誌
6.3 服務端訂閱消費組情況