产品定价 立即试用
目录
如何将 WS101 LoRaWAN® Smart Button 连接至 ThingsBoard?
  • Hardware type: Other devices
  • Connectivity: LoRaWAN
  • Industry: Smart Buildings, Smart Cities, Retail, Healthcare
  • Use cases: Smart Office, Health Care
  • Platforms: Community Edition, Professional Edition, Cloud

概述

WS101智能按钮是一款基于LoRaWAN®的智能按钮,用于无线控制、触发和报警。
WS101支持多种按压操作,所有操作均可由用户自定义,用于控制设备或触发场景。 WS101可广泛应用于智能家居、智能办公室、学校、仓库等场景。
传感器数据通过标准LoRaWAN®协议实时传输。
LoRaWAN®支持加密无线传输,可在低功耗条件下实现远距离通信。

文档信息图标
ThingsBoard PE 功能

专业版支持Platform Integrations功能。
请使用ThingsBoard Cloud自行安装平台实例。


前置条件

继续本指南前,需准备以下内容:

设备连接

根据官方用户手册,我们需要一部支持NFC的智能手机和ToolBox应用来连接传感器。
由于该设备只能通过LoRaWAN®网关运行,我们必须先将其连接到已配置ThingsBoard集成的网络服务器。
之后即可将其配置到ThingsBoard。

Device configuration

连接并发送数据前,需配置设备和网络服务器。
首先配置设备,并保存网络服务器配置所需信息。
要将设备加入网络服务器并从中获取信息,需准备以下设备参数:

  • Device EUI - 设备标识符
  • Application EUI - 应用标识符
  • Application Key - 用于识别设备的应用密钥。建议使用生成的密钥,而非示例中的密钥!

以上参数为连接所必需。

根据网络服务器要求,可能还需提供接入类型(OTAA)、LoRaWAN版本。

通过NFC配置设备时,请如右图所示将智能手机靠近设备:
NFC zone

在智能手机上读写设备配置,可按以下步骤操作:

  • 打开 ToolBox 应用。

  • 点击 NFC Read 按钮,将智能手机靠近设备。

  • 进入 Setting 选项卡,设置并保存所需字段及其他配置。

  • 点击 Write 按钮,将智能手机靠近设备。

若在保护壳内配置设备与智能手机时遇到问题,建议取下保护壳后再试。

配置完成后,设备即可向网络服务器发送数据。

配置设备时还需要将其添加到网络服务器,请选择您的网关所连接的网络服务器:

在ChirpStack上添加设备

我们需要在 ChirpStack 上添加设备。

要添加设备,请按以下步骤操作:

  • 登录ChirpStack服务器。

  • 进入设备配置页面,点击添加设备配置按钮。

  • 填写字段并点击提交按钮。

  • 进入应用页面,点击您的应用后按添加设备按钮。

  • 使用设备配置中的值填写参数。然后选择之前创建设备配置并点击提交按钮。

  • 应用密钥填入相应字段并点击提交按钮保存设备。

ThingsBoard集成中的上行数据转换器

由于我们已连接网关并配置了集成,需要修改转换器并添加解析设备上行数据负载的能力。

为此,您可以将代码添加到”解码块“中,该块位于转换器内”// — Decoding code — //“注释之间(若使用ThingsBoard v3.5.2及以上版本的默认转换器)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
if (input.length > 0) {
        for (var i = 0; i < input.length; ) {
            var channel_id = bytes[i++];
            if (i < input.length) {
                var channel_type = bytes[i++];
                // BATTERY
                if (channel_id === 0x01 && channel_type === 0x75) {
                    output.telemetry.battery = bytes[i];
                    i += 1;
                }
                // PRESS STATE
                else if (channel_id === 0xff && channel_type === 0x2e) {
                    switch (bytes[i]) {
                        case 1:
                            output.telemetry.press = "short";
                            break;
                        case 2:
                            output.telemetry.press = "long";
                            break;
                        case 3:
                            output.telemetry.press = "double";
                            break;
                    }
                    i += 1;
                }
            }
        }
    }

或者,您也可以复制完整的转换器代码并粘贴到您的转换器中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
var data = decodeToJson(payload);
var deviceName = data.deviceInfo.deviceName;
var deviceType = data.deviceInfo.deviceProfileName;
// var groupName = 'IAQ devices';
// var customerName = 'Customer A';
// use assetName and assetType instead of deviceName and deviceType
// to automatically create assets instead of devices.
// var assetName = 'Asset A';
// var assetType = 'building';

// If you want to parse incoming data somehow, you can add your code to this function.
// input: bytes
// expected output:
//  {
//    "attributes": {"attributeKey": "attributeValue"},
//    "telemetry": {"telemetryKey": "telemetryValue"}
//  }
//
// In the example - bytes will be saved as HEX string and also parsed as light level, battery level and PIR sensor value.
//

function decodePayload(input) {
    var output = { attributes:{}, telemetry: {} };
    // --- Decoding code --- //

    output.telemetry.HEX_bytes = bytesToHex(input);

    if (input.length > 0) {
        for (var i = 0; i < input.length; ) {
            var channel_id = bytes[i++];
            if (i < input.length) {
                var channel_type = bytes[i++];
                // BATTERY
                if (channel_id === 0x01 && channel_type === 0x75) {
                    output.telemetry.battery = bytes[i];
                    i += 1;
                }
                // PRESS STATE
                else if (channel_id === 0xff && channel_type === 0x2e) {
                    switch (bytes[i]) {
                        case 1:
                            output.telemetry.press = "short";
                            break;
                        case 2:
                            output.telemetry.press = "long";
                            break;
                        case 3:
                            output.telemetry.press = "double";
                            break;
                    }
                    i += 1;
                }
            }
        }
    }

    // --- Decoding code --- //
    return output;
}

// --- attributes and telemetry objects ---
var telemetry = {};
var attributes = {};
// --- attributes and telemetry objects ---

// --- Timestamp parsing
var dateString = data.time;
var timestamp = -1;
if (dateString != null) {
    timestamp = new Date(dateString).getTime();
    if (timestamp == -1) {
        var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;
        var millisecondsEndIndex = dateString.lastIndexOf('+');
        if (millisecondsEndIndex == -1) {
            millisecondsEndIndex = dateString.lastIndexOf('Z');
        }
        if (millisecondsEndIndex == -1) {
            millisecondsEndIndex = dateString.lastIndexOf('-');
        }
        if (millisecondsEndIndex == -1) {
        if (dateString.length >= secondsSeparatorIndex + 3) {
            dateString = dateString.substring(0, secondsSeparatorIndex + 3);
        }
    } else {
        dateString = dateString.substring(0, secondsSeparatorIndex + 3) +
        dateString.substring(millisecondsEndIndex, dateString.length);
    }
    timestamp = new Date(dateString).getTime();
    }
}
// If we cannot parse timestamp - we will use the current timestamp
if (timestamp == -1) {
    timestamp = Date.now();
}
// --- Timestamp parsing

// You can add some keys manually to attributes or telemetry
attributes.deduplicationId = data.deduplicationId;

// You can exclude some keys from the result
var excludeFromAttributesList = ["deviceName", "rxInfo", "confirmed", "data", "deduplicationId","time", "adr", "dr", "fCnt"];
var excludeFromTelemetryList = ["data", "deviceInfo", "txInfo", "devAddr", "adr", "time", "fPort", "region_common_name", "region_config_id", "deduplicationId"];

// Message parsing
// To avoid paths in the decoded objects we passing false value to function as "pathInKey" argument.
// Warning: pathInKey can cause already found fields to be overwritten with the last value found.

var telemetryData = toFlatMap(data, excludeFromTelemetryList, false);
var attributesData = toFlatMap(data, excludeFromAttributesList, false);

var uplinkDataList = [];

// Passing incoming bytes to decodePayload function, to get custom decoding
var customDecoding = decodePayload(base64ToBytes(data.data));

// Collecting data to result
if (customDecoding.?telemetry.size() > 0) {
    telemetry.putAll(customDecoding.telemetry);
}

if (customDecoding.?attributes.size() > 0) {
    attributes.putAll(customDecoding.attributes);
}

telemetry.putAll(telemetryData);
attributes.putAll(attributesData);

var result = {
    deviceName: deviceName,
    deviceType: deviceType,
//  assetName: assetName,
//  assetType: assetType,
//  customerName: customerName,
//  groupName: groupName,
    attributes: attributes,
    telemetry: {
        ts: timestamp,
        values: telemetry
    }
};

return result;

在The Things Stack社区版上添加设备

我们需要在 The Things Stack社区版 上添加设备。

要添加设备,请按以下步骤操作:

  • 登录云端并打开控制台。

  • 进入 应用 页面。然后选择您的应用并点击应用名称。

  • 点击 注册终端设备 按钮。

  • APP EUI 值填入 JoinEUI 字段。点击 确认 按钮。

  • 填写其余参数并点击 注册终端设备 按钮。

ThingsBoard集成中的上行数据转换器

由于我们已连接网关并配置了集成,需要修改转换器并添加解析设备上行数据负载的能力。

为此,您可以将代码添加到”解码块“中,该块位于转换器内”// — Decoding code — //“注释之间(若使用ThingsBoard v3.5.2及以上版本的默认转换器)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
if (input.length > 0) {
        for (var i = 0; i < input.length; ) {
            var channel_id = bytes[i++];
            if (i < input.length) {
                var channel_type = bytes[i++];
                // BATTERY
                if (channel_id === 0x01 && channel_type === 0x75) {
                    output.telemetry.battery = bytes[i];
                    i += 1;
                }
                // PRESS STATE
                else if (channel_id === 0xff && channel_type === 0x2e) {
                    switch (bytes[i]) {
                        case 1:
                            output.telemetry.press = "short";
                            break;
                        case 2:
                            output.telemetry.press = "long";
                            break;
                        case 3:
                            output.telemetry.press = "double";
                            break;
                    }
                    i += 1;
                }
            }
        }
    }

或者,您也可以复制完整的转换器代码并粘贴到您的转换器中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
var data = decodeToJson(payload);

var deviceName = data.end_device_ids.device_id;
var deviceType = data.end_device_ids.application_ids.application_id;
// var groupName = 'IAQ devices';
// var customerName = 'Customer A';
// use assetName and assetType instead of deviceName and deviceType
// to automatically create assets instead of devices.
// var assetName = 'Asset A';
// var assetType = 'building';

// If you want to parse incoming data somehow, you can add your code to this function.
// input: bytes
// expected output:
//  {
//    "attributes": {"attributeKey": "attributeValue"},
//    "telemetry": {"telemetryKey": "telemetryValue"}
//  }
//
// In the example - bytes will be saved as HEX string and also parsed as light level, battery level and PIR sensor value.
//

function decodeFrmPayload(input) {
    var output = {
        attributes: {}, telemetry: {}
    };
    // --- Decoding code --- //

    output.telemetry.HEX_bytes = bytesToHex(input);

    if (input.length > 0) {
        for (var i = 0; i < input.length; ) {
            var channel_id = bytes[i++];
            if (i < input.length) {
                var channel_type = bytes[i++];
                // BATTERY
                if (channel_id === 0x01 && channel_type === 0x75) {
                    output.telemetry.battery = bytes[i];
                    i += 1;
                }
                // PRESS STATE
                else if (channel_id === 0xff && channel_type === 0x2e) {
                    switch (bytes[i]) {
                        case 1:
                            output.telemetry.press = "short";
                            break;
                        case 2:
                            output.telemetry.press = "long";
                            break;
                        case 3:
                            output.telemetry.press = "double";
                            break;
                    }
                    i += 1;
                }
            }
        }
    }

    // --- Decoding code --- //
    return output;
}

// --- attributes and telemetry objects ---
var telemetry = {};
var attributes = {};
// --- attributes and telemetry objects ---

// --- Timestamp parsing
var dateString = data.uplink_message.received_at;
// If data is simulated or device doesn't send his own date string - we will use date from upcoming message, set by network server
if ((data.simulated != null && data.simulated) || dateString == null) {
    dateString = data.received_at;
}
var timestamp = new Date(dateString).getTime();
var timestamp = -1;
if (dateString != null) {
    timestamp = new Date(dateString).getTime();
    if (timestamp == -1) {
        var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;
        var millisecondsEndIndex = dateString.lastIndexOf('+');
        if (millisecondsEndIndex == -1) {
            millisecondsEndIndex = dateString.lastIndexOf('Z');
        }
        if (millisecondsEndIndex == -1) {
            millisecondsEndIndex = dateString.lastIndexOf('-');
        }
        if (millisecondsEndIndex == -1) {
            if (dateString.length >= secondsSeparatorIndex + 3) {
                dateString = dateString.substring(0, secondsSeparatorIndex + 3);
            }
        } else {
            dateString = dateString.substring(0, secondsSeparatorIndex + 3) +
                dateString.substring(millisecondsEndIndex, dateString.length);
        }
        timestamp = new Date(dateString).getTime();
    }
}
// If we cannot parse timestamp - we will use the current timestamp
if (timestamp == -1) {
    timestamp = Date.now();
}
// --- Timestamp parsing

// You can add some keys manually to attributes or telemetry
attributes.devEui = data.end_device_ids.dev_eui;
attributes.fPort = data.uplink_message.f_port;
// We want to save correlation ids as single object, so we are excluding them from attributes parse and add manually
attributes.correlation_ids = data.correlation_ids;

// You can exclude some keys from the result
var excludeFromTelemetryList = ["uplink_token", "gateway_id", "settings", "f_port", "time", "timestamp", "received_at", "network_ids"];
var excludeFromAttributesList = ["uplink_token", "gateway_id", "f_port", "time", "timestamp", "received_at", "session_key_id", "dev_eui"];

// Message parsing
// To avoid paths in the decoded objects we passing false value to function as "pathInKey" argument.
// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.
var telemetryData = toFlatMap(data.uplink_message, excludeFromTelemetryList, false);
var attributesData = {};
attributesData.putAll(toFlatMap(data.uplink_message.settings, excludeFromAttributesList, false));
attributesData.putAll(toFlatMap(data.uplink_message.network_ids, excludeFromAttributesList, false));
attributesData.putAll(toFlatMap(data.end_device_ids, excludeFromAttributesList, false));

// Passing incoming bytes to decodeFrmPayload function, to get custom decoding
var customDecoding = {};
if (data.uplink_message.get("frm_payload") != null) {
    customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));
}

// Collecting data to result
if (customDecoding.?telemetry.size() > 0) {
    telemetry.putAll(customDecoding.telemetry);
}

if (customDecoding.?attributes.size() > 0) {
    attributes.putAll(customDecoding.attributes);
}

telemetry.putAll(telemetryData);
attributes.putAll(attributesData);

var result = {
    deviceName: deviceName,
    deviceType: deviceType,
//  assetName: assetName,
//  assetType: assetType,
//  customerName: customerName
//  groupName: groupName,
    attributes: attributes,
    telemetry: {
        ts: timestamp,
        values: telemetry
    }
};

return result;

在The Things Industries上添加设备

我们需要在 The Things Industries云 上添加设备。

要添加设备,请按以下步骤操作:

  • 登录云端并打开控制台。

  • 进入 应用 页面。然后选择您的应用并点击应用名称。

  • 点击 注册终端设备 按钮。

  • APP EUI 值填入 JoinEUI 字段。点击 确认 按钮。

  • 填写其余参数并点击 注册终端设备 按钮。

ThingsBoard集成中的上行数据转换器

由于我们已连接网关并配置了集成,需要修改转换器并添加解析设备上行数据负载的能力。

为此,您可以将代码添加到”解码块“中,该块位于转换器内”// — Decoding code — //“注释之间(若使用ThingsBoard v3.5.2及以上版本的默认转换器)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
if (input.length > 0) {
        for (var i = 0; i < input.length; ) {
            var channel_id = bytes[i++];
            if (i < input.length) {
                var channel_type = bytes[i++];
                // BATTERY
                if (channel_id === 0x01 && channel_type === 0x75) {
                    output.telemetry.battery = bytes[i];
                    i += 1;
                }
                // PRESS STATE
                else if (channel_id === 0xff && channel_type === 0x2e) {
                    switch (bytes[i]) {
                        case 1:
                            output.telemetry.press = "short";
                            break;
                        case 2:
                            output.telemetry.press = "long";
                            break;
                        case 3:
                            output.telemetry.press = "double";
                            break;
                    }
                    i += 1;
                }
            }
        }
    }

或者,您也可以复制完整的转换器代码并粘贴到您的转换器中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
var data = decodeToJson(payload);

var deviceName = data.end_device_ids.device_id;
var deviceType = data.end_device_ids.application_ids.application_id;
// var groupName = 'IAQ devices';
// var customerName = 'Customer A';
// use assetName and assetType instead of deviceName and deviceType
// to automatically create assets instead of devices.
// var assetName = 'Asset A';
// var assetType = 'building';

// If you want to parse incoming data somehow, you can add your code to this function.
// input: bytes
// expected output:
//  {
//    "attributes": {"attributeKey": "attributeValue"},
//    "telemetry": {"telemetryKey": "telemetryValue"}
//  }
//
// In the example - bytes will be saved as HEX string and also parsed as light level, battery level and PIR sensor value.
//

function decodeFrmPayload(input) {
    var output = { attributes:{}, telemetry: {}};
    // --- Decoding code --- //

    output.telemetry.HEX_bytes = bytesToHex(input);

    if (input.length > 0) {
        for (var i = 0; i < input.length; ) {
            var channel_id = bytes[i++];
            if (i < input.length) {
                var channel_type = bytes[i++];
                // BATTERY
                if (channel_id === 0x01 && channel_type === 0x75) {
                    output.telemetry.battery = bytes[i];
                    i += 1;
                }
                // PRESS STATE
                else if (channel_id === 0xff && channel_type === 0x2e) {
                    switch (bytes[i]) {
                        case 1:
                            output.telemetry.press = "short";
                            break;
                        case 2:
                            output.telemetry.press = "long";
                            break;
                        case 3:
                            output.telemetry.press = "double";
                            break;
                    }
                    i += 1;
                }
            }
        }
    }

    // --- Decoding code --- //
    return output;
}

// --- attributes and telemetry objects ---
var telemetry = {};
var attributes = {};
// --- attributes and telemetry objects ---

// --- Timestamp parsing
var dateString = data.uplink_message.received_at;
// If data is simulated or device doesn't send his own date string - we will use date from upcoming message, set by network server
if ((data.simulated != null && data.simulated) || dateString == null) {
    dateString = data.received_at;
}
var timestamp = -1;
if (dateString != null) {
    timestamp = new Date(dateString).getTime();
    if (timestamp == -1) {
        var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;
        var millisecondsEndIndex = dateString.lastIndexOf('+');
        if (millisecondsEndIndex == -1) {
            millisecondsEndIndex = dateString.lastIndexOf('Z');
        }
        if (millisecondsEndIndex == -1) {
            millisecondsEndIndex = dateString.lastIndexOf('-');
        }
        if (millisecondsEndIndex == -1) {
            if (dateString.length >= secondsSeparatorIndex + 3) {
                dateString = dateString.substring(0, secondsSeparatorIndex + 3);
            }
        } else {
            dateString = dateString.substring(0, secondsSeparatorIndex + 3) +
                dateString.substring(millisecondsEndIndex, dateString.length);
        }
        timestamp = new Date(dateString).getTime();
    }
}
// If we cannot parse timestamp - we will use the current timestamp
if (timestamp == -1) {
    timestamp = Date.now();
}
// --- Timestamp parsing

// You can add some keys manually to attributes or telemetry
attributes.devEui = data.end_device_ids.dev_eui;
attributes.fPort = data.uplink_message.f_port;
// We want to save correlation ids as single object, so we are excluding them from attributes parse and add manually
attributes.correlation_ids = data.correlation_ids;

// You can exclude some keys from the result
var excludeFromTelemetryList = ["uplink_token", "gateway_id", "settings", "f_port", "time", "timestamp", "received_at", "network_ids"];
var excludeFromAttributesList = ["uplink_token", "gateway_id", "f_port", "time", "timestamp", "received_at", "session_key_id", "dev_eui"];

// Message parsing
// To avoid paths in the decoded objects we passing false value to function as "pathInKey" argument.
// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.
var telemetryData = toFlatMap(data.uplink_message, excludeFromTelemetryList, false);
var attributesData = {};
attributesData.putAll(toFlatMap(data.uplink_message.settings, excludeFromAttributesList, false));
attributesData.putAll(toFlatMap(data.uplink_message.network_ids, excludeFromAttributesList, false));
attributesData.putAll(toFlatMap(data.end_device_ids, excludeFromAttributesList, false));

// Passing incoming bytes to decodeFrmPayload function, to get custom decoding
var customDecoding = {};
if (data.uplink_message.get("frm_payload") != null) {
    customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));
}

// Collecting data to result
if (customDecoding.?telemetry.size() > 0) {
    telemetry.putAll(customDecoding.telemetry);
}

if (customDecoding.?attributes.size() > 0) {
    attributes.putAll(customDecoding.attributes);
}

telemetry.putAll(telemetryData);
attributes.putAll(attributesData);

var result = {
    deviceName: deviceName,
    deviceType: deviceType,
//  assetName: assetName,
//  assetType: assetType,
//  customerName: customerName,
//  groupName: groupName,
    attributes: attributes,
    telemetry: {
        ts: timestamp,
        values: telemetry
    }
};

return result;

在Loriot上添加设备

我们需要在 Loriot 上添加设备。

要添加设备,请按以下步骤操作:

  • 登录Loriot服务器。我们使用 eu2.loriot.io,具体取决于注册时选择的区域。

  • 在左侧菜单中进入“应用”页面。

  • 打开您的应用,我们的示例为“SampleApp”。

  • 进入“注册设备”页面。使用设备配置中的值填写字段,然后点击“注册”按钮。

ThingsBoard集成中的上行数据转换器

由于我们已连接网关并配置了集成,需要修改转换器并添加解析设备上行数据负载的能力。

为此,您可以将代码添加到”解码块“中,该块位于转换器内”// — Decoding code — //“注释之间(若使用ThingsBoard v3.5.2及以上版本的默认转换器)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
if (input.length > 0) {
        for (var i = 0; i < input.length; ) {
            var channel_id = bytes[i++];
            if (i < input.length) {
                var channel_type = bytes[i++];
                // BATTERY
                if (channel_id === 0x01 && channel_type === 0x75) {
                    output.telemetry.battery = bytes[i];
                    i += 1;
                }
                // PRESS STATE
                else if (channel_id === 0xff && channel_type === 0x2e) {
                    switch (bytes[i]) {
                        case 1:
                            output.telemetry.press = "short";
                            break;
                        case 2:
                            output.telemetry.press = "long";
                            break;
                        case 3:
                            output.telemetry.press = "double";
                            break;
                    }
                    i += 1;
                }
            }
        }
    }

或者,您也可以复制完整的转换器代码并粘贴到您的转换器中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
var data = decodeToJson(payload);
var deviceName = data.EUI;
var deviceType = "LoraDevices";
// groupName = 'IAQ devices';
// var customerName = 'Customer A';
// use assetName and assetType instead of deviceName and deviceType
// to automatically create assets instead of devices.
// var assetName = 'Asset A';
// var assetType = 'building';
var gatewayDeviceType = "LoraGateway";

// If you want to parse incoming data somehow, you can add your code to this function.
// input: bytes
// expected output:
//  {
//    "attributes": {"attributeKey": "attributeValue"},
//    "telemetry": {"telemetryKey": "telemetryValue"}
//  }
//
// In the example - bytes will be saved as HEX string and also parsed as light level, battery level and PIR sensor value.
//

function decodePayload(input) {
    var output = { attributes:{}, telemetry: {} };
    // --- Decoding code --- //

    output.telemetry.HEX_bytes = bytesToHex(input);

    if (input.length > 0) {
        for (var i = 0; i < input.length; ) {
            var channel_id = bytes[i++];
            if (i < input.length) {
                var channel_type = bytes[i++];
                // BATTERY
                if (channel_id === 0x01 && channel_type === 0x75) {
                    output.telemetry.battery = bytes[i];
                    i += 1;
                }
                // PRESS STATE
                else if (channel_id === 0xff && channel_type === 0x2e) {
                    switch (bytes[i]) {
                        case 1:
                            output.telemetry.press = "short";
                            break;
                        case 2:
                            output.telemetry.press = "long";
                            break;
                        case 3:
                            output.telemetry.press = "double";
                            break;
                    }
                    i += 1;
                }
            }
        }
    }

    // --- Decoding code --- //
    return output;
}

// --- attributes and telemetry objects ---
var telemetry = {};
var attributes = {};
// --- attributes and telemetry objects ---

// --- Timestamp parsing
var timestamp = data.ts;
// If we cannot parse timestamp - we will use the current timestamp
if (timestamp == -1) {
    timestamp = Date.now();
}
// --- Timestamp parsing

// You can add some keys manually to attributes or telemetry
attributes.fPort = data.port;
attributes.dataRange = data.dr;

// You can exclude some keys from the result
var excludeFromAttributesList = ["data", "gws", "EUI", "ts", "cmd", "port", "seqno", "fcnt", "toa", "dr", "ack", "bat", "snr", "rssi"];
var excludeFromTelemetryList = ["gws", "EUI", "ts", "freq", "port", "data", "cmd", "dr", "offline"];

// Message parsing
// To avoid paths in the decoded objects we passing false value to function as "pathInKey" argument.
// Warning: pathInKey can cause already found fields to be overwritten with the last value found.

var telemetryData = toFlatMap(data, excludeFromTelemetryList, false);
var attributesData = toFlatMap(data, excludeFromAttributesList, false);

var uplinkDataList = [];

// Passing incoming bytes to decodePayload function, to get custom decoding
var customDecoding = decodePayload(hexToBytes(data.data));

// Collecting data to result
if (customDecoding.?telemetry.size() > 0) {
    telemetry.putAll(customDecoding.telemetry);
}

if (customDecoding.?attributes.size() > 0) {
    attributes.putAll(customDecoding.attributes);
}

telemetry.putAll(telemetryData);
attributes.putAll(attributesData);

var deviceInfo = {
    deviceName: deviceName,
    deviceType: deviceType,
//  assetName: assetName,
//  assetType: assetType,
//  customerName: customerName,
//  groupName: groupName,
    attributes: attributes,
    telemetry: {
        ts: timestamp,
        values: telemetry
    }
};

uplinkDataList.add(deviceInfo);

if (data.cmd == "gw") {
    foreach( gatewayInfo : data.gws ) {
        var gatewayInfoMsg = {
            deviceName: gatewayInfo.gweui,
            deviceType: gatewayDeviceType,
            attributes: {},
            telemetry: {
                "ts": gatewayInfo.ts,
                "values": toFlatMap(gatewayInfo, ["ts", "time", "gweui"], false)
            }
        };
        uplinkDataList.add(gatewayInfoMsg);
    }
}

return uplinkDataList;

在ThingsBoard上查看数据

设备已添加,若其发送了数据,将显示在 设备 中。
可在 实体 部分打开 设备 页面查看。 设备将出现在设备列表中,点击设备并打开 属性最新遥测 选项卡即可查看数据。

Check telemetry on device


为获得更直观的视图,可使用仪表板
可下载适用于本设备的简易仪表板,该仪表板配置为显示设备名称为 “eui-24e124538b223213” 的设备的 “press” 和 “battery” 遥测数据。

ThingsBoard支持创建和自定义用于监控与管理数据、设备的交互式可视化(仪表板)。
通过ThingsBoard仪表板,您可以高效管理与监控IoT设备及数据。下面为我们的设备创建仪表板。

将仪表板添加到ThingsBoard需要导入。请按以下步骤操作:

  • 进入 仪表板 页面。默认进入仪表板组 “All”。点击右上角 + 图标,选择 导入仪表板

  • 在仪表板导入窗口中上传JSON文件并点击 导入 按钮。

  • 仪表板已导入。

打开导入的仪表板,点击进入。然后需在仪表板的实体别名中指定您的设备。

请按以下步骤操作:

  • 打开仪表板并进入编辑模式。点击“实体别名”图标,在弹窗中点击别名旁的“编辑别名”图标。

  • 在编辑别名窗口的下拉列表中选择您的设备并保存实体别名。

  • 应用所有更改。

此时应能看到设备数据。

带数据的仪表板示例:

Dashboard

总结

现在您可以轻松将WS101 LoRaWAN® Smart Button连接到ThingsBoard并开始发送数据。

进一步了解可查阅ThingsBoard文档, 学习创建仪表板可视化遥测、 配置告警规则实时监控设备行为等核心功能。

发现即插即用硬件,助力您的解决方案
合作伙伴图标