产品定价 立即试用
目录
如何将 VS121 AI workplace sensor 连接至 ThingsBoard?
  • Hardware type: Other devices
  • Connectivity: LoRaWAN
  • Industry: Healthcare, Smart Buildings
  • Use cases: Smart Office
  • Platforms: Community Edition, Professional Edition, Cloud

概述

Milesight VS121是一款AI工作场所传感器,用于监测现代工作空间的占用率和利用情况,基于AI算法识别率可达98%。 Milesight VS121提供LoRaWAN®和以太网两种版本,适用于不同应用场景。基于标准LoRaWAN协议,VS121可与Milesight网关配合使用。 设备配备Wi-Fi功能,无需额外配置工具即可轻松配置。将此设备连接到ThingsBoard,可获得更强大的可视化和数据管理能力。


文档信息图标
ThingsBoard PE 功能

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

前置条件

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

设备连接

根据官方用户手册和本指南,您可以将设备连接到网络,并通过无线连接访问Web UI。 由于该设备只能通过LoRaWAN®网关运行,我们必须先将其连接到已配置ThingsBoard集成的网络服务器。

设备配置

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

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

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

VS121传感器提供友好的Web配置界面,用户可通过Wi-Fi连接访问。设备默认SSID为:Workplace Sensor。

请按以下步骤操作:

  1. 通过Type-C电源口为设备供电;
  2. 在电脑上启用无线网络连接,搜索并连接对应的接入点;
  3. 打开浏览器,输入192.168.1.1访问Web管理界面(需在同一子网内);
  4. 首次使用时需设置密码,还可选择设置三个安全问题;
  5. 设置完成后,使用用户名(admin)和自定义密码登录传感器。

要获取所需参数,请按以下步骤操作:

  • 在左侧面板中进入 IoT 页面,切换到 LoRa 选项卡;
  • 记录 Device EUIApp EUI
  • Application Key 字段中生成并输入十六进制格式的新密钥,并记录下来;
  • 向下滚动并点击 Save 按钮。

以上参数为连接所必需。

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

在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
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
var historyData = {};
    var decoded = {};
    decoded.hexString = bytesToHex(input);
    for (var i = 0; i < input.length; ) {
        var channel_id = input[i];
        var channel_type = input[i+1];

        i += 2;

        // PROTOCOL VERSION
        if (channel_id == -1 && channel_type == 1) {
            decoded.protocol_version = input[i];
            i += 1;
        }
        // SERIAL NUMBER
        else if (channel_id == -1 && channel_type == 8) {
            var temp = [];
            var last_index_sn = i + 6;
            for (var idxsn = i; idxsn < last_index_sn; idxsn++) {
                temp.push(bytesToHex([input[idxsn] & 0xff]));
            }
            decoded.serialNumber = temp.join("");
            i += 6;
        }
        // HARDWARE VERSION
        else if (channel_id == -1 && channel_type == 9) {
            var temphv = [];
            var last_index_hv = i + 2;
            for (var idxhv = i; idxhv < last_index_hv; idxhv++) {
                temphv.push((input[idxhv] & 0xff).toString());
            }
            decoded.hardwareVersion = temphv.join(".");
            i += 2;
        }
        // FIRMWARE VERSION
        else if (channel_id == -1 && channel_type == 31) {
            var tempfv = [];
            var last_index_fv = i + 4;
            for (var idxfv = i; idxfv < last_index_fv; idxfv++) {
                tempfv.push((input[idxfv] & 0xff).toString());
            }
            decoded.firmwareVersion = tempfv.join(".");
            i += 4;
        }
        // PEOPLE COUNTER
        else if (channel_id == 4 && channel_type == -55) {
            decoded.peopleCountAll = input[i];
            decoded.regionCount = input[i + 1];
            var region = parseBytesToInt(input, i + 2, 2, false);
            for (var idxpc = 0; idxpc < decoded.regionCount; idxpc++) {
                var tmp = "region" + (idxpc + 1);
                decoded[tmp] = (region >> idxpc) & 1;
            }
            i += 4;
        }
        // PEOPLE IN/OUT
        else if (channel_id == 5 && channel_type == -52) {
            decoded.peopleIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // PEOPLE MAX
        else if (channel_id == 6 && channel_type == -51) {
            decoded.peopleCountMax = input[i];
            i += 1;
        }
        // REGION COUNTER
        else if (channel_id == 7 && channel_type == -43) {
            decoded.region1Count = input[i];
            decoded.region2Count = input[i + 1];
            decoded.region3Count = input[i + 2];
            decoded.region4Count = input[i + 3];
            decoded.region5Count = input[i + 4];
            decoded.region6Count = input[i + 5];
            decoded.region7Count = input[i + 6];
            decoded.region8Count = input[i + 7];
            i += 8;
        }
        // REGION COUNTER
        else if (channel_id == 8 && channel_type == -43) {
            decoded.region9Count = input[i];
            decoded.region10Count = input[i + 1];
            decoded.region11Count = input[i + 2];
            decoded.region12Count = input[i + 3];
            decoded.region13Count = input[i + 4];
            decoded.region14Count = input[i + 5];
            decoded.region15Count = input[i + 6];
            decoded.region16Count = input[i + 7];
            i += 8;
        }
        // A FLOW
        else if (channel_id == 9 && channel_type == -38) {
            decoded.aToA = parseBytesToInt(input, i, 2, false);
            decoded.aToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.aToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.aToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // B FLOW
        else if (channel_id == 10 && channel_type == -38) {
            decoded.bToA = parseBytesToInt(input, i, 2, false);
            decoded.bToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.bToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.bToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // C FLOW
        else if (channel_id == 11 && channel_type == -38) {
            decoded.cToA = parseBytesToInt(input, i, 2, false);
            decoded.cToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.cToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.cToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // D FLOW
        else if (channel_id == 12 && channel_type == -38) {
            decoded.dToA = parseBytesToInt(input, i, 2, false);
            decoded.dToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.dToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.dToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // TOTAL IN/OUT
        else if (channel_id == 13 && channel_type == -52) {
            decoded.peopleTotalIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleTotalOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // DWELL TIME
        else if (channel_id == 14 && channel_type == -28) {
            var region1 = input[i];
            decoded.region = region1;
            decoded.dwellTimeAvg = parseBytesToInt(input, i + 1, 2, false);
            decoded.dwellTimeMax = parseBytesToInt(input, i + 3, 2, false);
            i += 5;
        }
        // TIMESTAMP
        else if (channel_id == 15 && channel_type == -123) {
            decoded.timestamp = parseBytesToInt(input, i, 4, false);
            i += 4;
        } else {
            break;
        }
    }

    output.telemetry = decoded;

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

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
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);

    var historyData = {};
    var decoded = {};
    decoded.hexString = bytesToHex(input);
    for (var i = 0; i < input.length; ) {
        var channel_id = input[i];
        var channel_type = input[i+1];

        i += 2;

        // PROTOCOL VERSION
        if (channel_id == -1 && channel_type == 1) {
            decoded.protocol_version = input[i];
            i += 1;
        }
        // SERIAL NUMBER
        else if (channel_id == -1 && channel_type == 8) {
            var temp = [];
            var last_index_sn = i + 6;
            for (var idxsn = i; idxsn < last_index_sn; idxsn++) {
                temp.push(bytesToHex([input[idxsn] & 0xff]));
            }
            decoded.serialNumber = temp.join("");
            i += 6;
        }
        // HARDWARE VERSION
        else if (channel_id == -1 && channel_type == 9) {
            var temphv = [];
            var last_index_hv = i + 2;
            for (var idxhv = i; idxhv < last_index_hv; idxhv++) {
                temphv.push((input[idxhv] & 0xff).toString());
            }
            decoded.hardwareVersion = temphv.join(".");
            i += 2;
        }
        // FIRMWARE VERSION
        else if (channel_id == -1 && channel_type == 31) {
            var tempfv = [];
            var last_index_fv = i + 4;
            for (var idxfv = i; idxfv < last_index_fv; idxfv++) {
                tempfv.push((input[idxfv] & 0xff).toString());
            }
            decoded.firmwareVersion = tempfv.join(".");
            i += 4;
        }
        // PEOPLE COUNTER
        else if (channel_id == 4 && channel_type == -55) {
            decoded.peopleCountAll = input[i];
            decoded.regionCount = input[i + 1];
            var region = parseBytesToInt(input, i + 2, 2, false);
            for (var idxpc = 0; idxpc < decoded.regionCount; idxpc++) {
                var tmp = "region" + (idxpc + 1);
                decoded[tmp] = (region >> idxpc) & 1;
            }
            i += 4;
        }
        // PEOPLE IN/OUT
        else if (channel_id == 5 && channel_type == -52) {
            decoded.peopleIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // PEOPLE MAX
        else if (channel_id == 6 && channel_type == -51) {
            decoded.peopleCountMax = input[i];
            i += 1;
        }
        // REGION COUNTER
        else if (channel_id == 7 && channel_type == -43) {
            decoded.region1Count = input[i];
            decoded.region2Count = input[i + 1];
            decoded.region3Count = input[i + 2];
            decoded.region4Count = input[i + 3];
            decoded.region5Count = input[i + 4];
            decoded.region6Count = input[i + 5];
            decoded.region7Count = input[i + 6];
            decoded.region8Count = input[i + 7];
            i += 8;
        }
        // REGION COUNTER
        else if (channel_id == 8 && channel_type == -43) {
            decoded.region9Count = input[i];
            decoded.region10Count = input[i + 1];
            decoded.region11Count = input[i + 2];
            decoded.region12Count = input[i + 3];
            decoded.region13Count = input[i + 4];
            decoded.region14Count = input[i + 5];
            decoded.region15Count = input[i + 6];
            decoded.region16Count = input[i + 7];
            i += 8;
        }
        // A FLOW
        else if (channel_id == 9 && channel_type == -38) {
            decoded.aToA = parseBytesToInt(input, i, 2, false);
            decoded.aToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.aToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.aToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // B FLOW
        else if (channel_id == 10 && channel_type == -38) {
            decoded.bToA = parseBytesToInt(input, i, 2, false);
            decoded.bToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.bToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.bToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // C FLOW
        else if (channel_id == 11 && channel_type == -38) {
            decoded.cToA = parseBytesToInt(input, i, 2, false);
            decoded.cToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.cToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.cToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // D FLOW
        else if (channel_id == 12 && channel_type == -38) {
            decoded.dToA = parseBytesToInt(input, i, 2, false);
            decoded.dToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.dToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.dToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // TOTAL IN/OUT
        else if (channel_id == 13 && channel_type == -52) {
            decoded.peopleTotalIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleTotalOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // DWELL TIME
        else if (channel_id == 14 && channel_type == -28) {
            var region1 = input[i];
            decoded.region = region1;
            decoded.dwellTimeAvg = parseBytesToInt(input, i + 1, 2, false);
            decoded.dwellTimeMax = parseBytesToInt(input, i + 3, 2, false);
            i += 5;
        }
        // TIMESTAMP
        else if (channel_id == 15 && channel_type == -123) {
            decoded.timestamp = parseBytesToInt(input, i, 4, false);
            i += 4;
        } else {
            break;
        }
    }

    output.telemetry = decoded;

    // --- 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
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
var historyData = {};
    var decoded = {};
    decoded.hexString = bytesToHex(input);
    for (var i = 0; i < input.length; ) {
        var channel_id = input[i];
        var channel_type = input[i+1];

        i += 2;

        // PROTOCOL VERSION
        if (channel_id == -1 && channel_type == 1) {
            decoded.protocol_version = input[i];
            i += 1;
        }
        // SERIAL NUMBER
        else if (channel_id == -1 && channel_type == 8) {
            var temp = [];
            var last_index_sn = i + 6;
            for (var idxsn = i; idxsn < last_index_sn; idxsn++) {
                temp.push(bytesToHex([input[idxsn] & 0xff]));
            }
            decoded.serialNumber = temp.join("");
            i += 6;
        }
        // HARDWARE VERSION
        else if (channel_id == -1 && channel_type == 9) {
            var temphv = [];
            var last_index_hv = i + 2;
            for (var idxhv = i; idxhv < last_index_hv; idxhv++) {
                temphv.push((input[idxhv] & 0xff).toString());
            }
            decoded.hardwareVersion = temphv.join(".");
            i += 2;
        }
        // FIRMWARE VERSION
        else if (channel_id == -1 && channel_type == 31) {
            var tempfv = [];
            var last_index_fv = i + 4;
            for (var idxfv = i; idxfv < last_index_fv; idxfv++) {
                tempfv.push((input[idxfv] & 0xff).toString());
            }
            decoded.firmwareVersion = tempfv.join(".");
            i += 4;
        }
        // PEOPLE COUNTER
        else if (channel_id == 4 && channel_type == -55) {
            decoded.peopleCountAll = input[i];
            decoded.regionCount = input[i + 1];
            var region = parseBytesToInt(input, i + 2, 2, false);
            for (var idxpc = 0; idxpc < decoded.regionCount; idxpc++) {
                var tmp = "region" + (idxpc + 1);
                decoded[tmp] = (region >> idxpc) & 1;
            }
            i += 4;
        }
        // PEOPLE IN/OUT
        else if (channel_id == 5 && channel_type == -52) {
            decoded.peopleIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // PEOPLE MAX
        else if (channel_id == 6 && channel_type == -51) {
            decoded.peopleCountMax = input[i];
            i += 1;
        }
        // REGION COUNTER
        else if (channel_id == 7 && channel_type == -43) {
            decoded.region1Count = input[i];
            decoded.region2Count = input[i + 1];
            decoded.region3Count = input[i + 2];
            decoded.region4Count = input[i + 3];
            decoded.region5Count = input[i + 4];
            decoded.region6Count = input[i + 5];
            decoded.region7Count = input[i + 6];
            decoded.region8Count = input[i + 7];
            i += 8;
        }
        // REGION COUNTER
        else if (channel_id == 8 && channel_type == -43) {
            decoded.region9Count = input[i];
            decoded.region10Count = input[i + 1];
            decoded.region11Count = input[i + 2];
            decoded.region12Count = input[i + 3];
            decoded.region13Count = input[i + 4];
            decoded.region14Count = input[i + 5];
            decoded.region15Count = input[i + 6];
            decoded.region16Count = input[i + 7];
            i += 8;
        }
        // A FLOW
        else if (channel_id == 9 && channel_type == -38) {
            decoded.aToA = parseBytesToInt(input, i, 2, false);
            decoded.aToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.aToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.aToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // B FLOW
        else if (channel_id == 10 && channel_type == -38) {
            decoded.bToA = parseBytesToInt(input, i, 2, false);
            decoded.bToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.bToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.bToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // C FLOW
        else if (channel_id == 11 && channel_type == -38) {
            decoded.cToA = parseBytesToInt(input, i, 2, false);
            decoded.cToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.cToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.cToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // D FLOW
        else if (channel_id == 12 && channel_type == -38) {
            decoded.dToA = parseBytesToInt(input, i, 2, false);
            decoded.dToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.dToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.dToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // TOTAL IN/OUT
        else if (channel_id == 13 && channel_type == -52) {
            decoded.peopleTotalIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleTotalOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // DWELL TIME
        else if (channel_id == 14 && channel_type == -28) {
            var region1 = input[i];
            decoded.region = region1;
            decoded.dwellTimeAvg = parseBytesToInt(input, i + 1, 2, false);
            decoded.dwellTimeMax = parseBytesToInt(input, i + 3, 2, false);
            i += 5;
        }
        // TIMESTAMP
        else if (channel_id == 15 && channel_type == -123) {
            decoded.timestamp = parseBytesToInt(input, i, 4, false);
            i += 4;
        } else {
            break;
        }
    }

    output.telemetry = decoded;

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

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
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);

    var historyData = {};
    var decoded = {};
    decoded.hexString = bytesToHex(input);
    for (var i = 0; i < input.length; ) {
        var channel_id = input[i];
        var channel_type = input[i+1];

        i += 2;

        // PROTOCOL VERSION
        if (channel_id == -1 && channel_type == 1) {
            decoded.protocol_version = input[i];
            i += 1;
        }
        // SERIAL NUMBER
        else if (channel_id == -1 && channel_type == 8) {
            var temp = [];
            var last_index_sn = i + 6;
            for (var idxsn = i; idxsn < last_index_sn; idxsn++) {
                temp.push(bytesToHex([input[idxsn] & 0xff]));
            }
            decoded.serialNumber = temp.join("");
            i += 6;
        }
        // HARDWARE VERSION
        else if (channel_id == -1 && channel_type == 9) {
            var temphv = [];
            var last_index_hv = i + 2;
            for (var idxhv = i; idxhv < last_index_hv; idxhv++) {
                temphv.push((input[idxhv] & 0xff).toString());
            }
            decoded.hardwareVersion = temphv.join(".");
            i += 2;
        }
        // FIRMWARE VERSION
        else if (channel_id == -1 && channel_type == 31) {
            var tempfv = [];
            var last_index_fv = i + 4;
            for (var idxfv = i; idxfv < last_index_fv; idxfv++) {
                tempfv.push((input[idxfv] & 0xff).toString());
            }
            decoded.firmwareVersion = tempfv.join(".");
            i += 4;
        }
        // PEOPLE COUNTER
        else if (channel_id == 4 && channel_type == -55) {
            decoded.peopleCountAll = input[i];
            decoded.regionCount = input[i + 1];
            var region = parseBytesToInt(input, i + 2, 2, false);
            for (var idxpc = 0; idxpc < decoded.regionCount; idxpc++) {
                var tmp = "region" + (idxpc + 1);
                decoded[tmp] = (region >> idxpc) & 1;
            }
            i += 4;
        }
        // PEOPLE IN/OUT
        else if (channel_id == 5 && channel_type == -52) {
            decoded.peopleIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // PEOPLE MAX
        else if (channel_id == 6 && channel_type == -51) {
            decoded.peopleCountMax = input[i];
            i += 1;
        }
        // REGION COUNTER
        else if (channel_id == 7 && channel_type == -43) {
            decoded.region1Count = input[i];
            decoded.region2Count = input[i + 1];
            decoded.region3Count = input[i + 2];
            decoded.region4Count = input[i + 3];
            decoded.region5Count = input[i + 4];
            decoded.region6Count = input[i + 5];
            decoded.region7Count = input[i + 6];
            decoded.region8Count = input[i + 7];
            i += 8;
        }
        // REGION COUNTER
        else if (channel_id == 8 && channel_type == -43) {
            decoded.region9Count = input[i];
            decoded.region10Count = input[i + 1];
            decoded.region11Count = input[i + 2];
            decoded.region12Count = input[i + 3];
            decoded.region13Count = input[i + 4];
            decoded.region14Count = input[i + 5];
            decoded.region15Count = input[i + 6];
            decoded.region16Count = input[i + 7];
            i += 8;
        }
        // A FLOW
        else if (channel_id == 9 && channel_type == -38) {
            decoded.aToA = parseBytesToInt(input, i, 2, false);
            decoded.aToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.aToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.aToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // B FLOW
        else if (channel_id == 10 && channel_type == -38) {
            decoded.bToA = parseBytesToInt(input, i, 2, false);
            decoded.bToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.bToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.bToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // C FLOW
        else if (channel_id == 11 && channel_type == -38) {
            decoded.cToA = parseBytesToInt(input, i, 2, false);
            decoded.cToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.cToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.cToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // D FLOW
        else if (channel_id == 12 && channel_type == -38) {
            decoded.dToA = parseBytesToInt(input, i, 2, false);
            decoded.dToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.dToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.dToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // TOTAL IN/OUT
        else if (channel_id == 13 && channel_type == -52) {
            decoded.peopleTotalIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleTotalOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // DWELL TIME
        else if (channel_id == 14 && channel_type == -28) {
            var region1 = input[i];
            decoded.region = region1;
            decoded.dwellTimeAvg = parseBytesToInt(input, i + 1, 2, false);
            decoded.dwellTimeMax = parseBytesToInt(input, i + 3, 2, false);
            i += 5;
        }
        // TIMESTAMP
        else if (channel_id == 15 && channel_type == -123) {
            decoded.timestamp = parseBytesToInt(input, i, 4, false);
            i += 4;
        } else {
            break;
        }
    }

    output.telemetry = decoded;

    // --- 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
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
var historyData = {};
    var decoded = {};
    decoded.hexString = bytesToHex(input);
    for (var i = 0; i < input.length; ) {
        var channel_id = input[i];
        var channel_type = input[i+1];

        i += 2;

        // PROTOCOL VERSION
        if (channel_id == -1 && channel_type == 1) {
            decoded.protocol_version = input[i];
            i += 1;
        }
        // SERIAL NUMBER
        else if (channel_id == -1 && channel_type == 8) {
            var temp = [];
            var last_index_sn = i + 6;
            for (var idxsn = i; idxsn < last_index_sn; idxsn++) {
                temp.push(bytesToHex([input[idxsn] & 0xff]));
            }
            decoded.serialNumber = temp.join("");
            i += 6;
        }
        // HARDWARE VERSION
        else if (channel_id == -1 && channel_type == 9) {
            var temphv = [];
            var last_index_hv = i + 2;
            for (var idxhv = i; idxhv < last_index_hv; idxhv++) {
                temphv.push((input[idxhv] & 0xff).toString());
            }
            decoded.hardwareVersion = temphv.join(".");
            i += 2;
        }
        // FIRMWARE VERSION
        else if (channel_id == -1 && channel_type == 31) {
            var tempfv = [];
            var last_index_fv = i + 4;
            for (var idxfv = i; idxfv < last_index_fv; idxfv++) {
                tempfv.push((input[idxfv] & 0xff).toString());
            }
            decoded.firmwareVersion = tempfv.join(".");
            i += 4;
        }
        // PEOPLE COUNTER
        else if (channel_id == 4 && channel_type == -55) {
            decoded.peopleCountAll = input[i];
            decoded.regionCount = input[i + 1];
            var region = parseBytesToInt(input, i + 2, 2, false);
            for (var idxpc = 0; idxpc < decoded.regionCount; idxpc++) {
                var tmp = "region" + (idxpc + 1);
                decoded[tmp] = (region >> idxpc) & 1;
            }
            i += 4;
        }
        // PEOPLE IN/OUT
        else if (channel_id == 5 && channel_type == -52) {
            decoded.peopleIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // PEOPLE MAX
        else if (channel_id == 6 && channel_type == -51) {
            decoded.peopleCountMax = input[i];
            i += 1;
        }
        // REGION COUNTER
        else if (channel_id == 7 && channel_type == -43) {
            decoded.region1Count = input[i];
            decoded.region2Count = input[i + 1];
            decoded.region3Count = input[i + 2];
            decoded.region4Count = input[i + 3];
            decoded.region5Count = input[i + 4];
            decoded.region6Count = input[i + 5];
            decoded.region7Count = input[i + 6];
            decoded.region8Count = input[i + 7];
            i += 8;
        }
        // REGION COUNTER
        else if (channel_id == 8 && channel_type == -43) {
            decoded.region9Count = input[i];
            decoded.region10Count = input[i + 1];
            decoded.region11Count = input[i + 2];
            decoded.region12Count = input[i + 3];
            decoded.region13Count = input[i + 4];
            decoded.region14Count = input[i + 5];
            decoded.region15Count = input[i + 6];
            decoded.region16Count = input[i + 7];
            i += 8;
        }
        // A FLOW
        else if (channel_id == 9 && channel_type == -38) {
            decoded.aToA = parseBytesToInt(input, i, 2, false);
            decoded.aToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.aToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.aToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // B FLOW
        else if (channel_id == 10 && channel_type == -38) {
            decoded.bToA = parseBytesToInt(input, i, 2, false);
            decoded.bToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.bToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.bToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // C FLOW
        else if (channel_id == 11 && channel_type == -38) {
            decoded.cToA = parseBytesToInt(input, i, 2, false);
            decoded.cToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.cToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.cToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // D FLOW
        else if (channel_id == 12 && channel_type == -38) {
            decoded.dToA = parseBytesToInt(input, i, 2, false);
            decoded.dToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.dToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.dToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // TOTAL IN/OUT
        else if (channel_id == 13 && channel_type == -52) {
            decoded.peopleTotalIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleTotalOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // DWELL TIME
        else if (channel_id == 14 && channel_type == -28) {
            var region1 = input[i];
            decoded.region = region1;
            decoded.dwellTimeAvg = parseBytesToInt(input, i + 1, 2, false);
            decoded.dwellTimeMax = parseBytesToInt(input, i + 3, 2, false);
            i += 5;
        }
        // TIMESTAMP
        else if (channel_id == 15 && channel_type == -123) {
            decoded.timestamp = parseBytesToInt(input, i, 4, false);
            i += 4;
        } else {
            break;
        }
    }

    output.telemetry = decoded;

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

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
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);

    var historyData = {};
    var decoded = {};
    decoded.hexString = bytesToHex(input);
    for (var i = 0; i < input.length; ) {
        var channel_id = input[i];
        var channel_type = input[i+1];

        i += 2;

        // PROTOCOL VERSION
        if (channel_id == -1 && channel_type == 1) {
            decoded.protocol_version = input[i];
            i += 1;
        }
        // SERIAL NUMBER
        else if (channel_id == -1 && channel_type == 8) {
            var temp = [];
            var last_index_sn = i + 6;
            for (var idxsn = i; idxsn < last_index_sn; idxsn++) {
                temp.push(bytesToHex([input[idxsn] & 0xff]));
            }
            decoded.serialNumber = temp.join("");
            i += 6;
        }
        // HARDWARE VERSION
        else if (channel_id == -1 && channel_type == 9) {
            var temphv = [];
            var last_index_hv = i + 2;
            for (var idxhv = i; idxhv < last_index_hv; idxhv++) {
                temphv.push((input[idxhv] & 0xff).toString());
            }
            decoded.hardwareVersion = temphv.join(".");
            i += 2;
        }
        // FIRMWARE VERSION
        else if (channel_id == -1 && channel_type == 31) {
            var tempfv = [];
            var last_index_fv = i + 4;
            for (var idxfv = i; idxfv < last_index_fv; idxfv++) {
                tempfv.push((input[idxfv] & 0xff).toString());
            }
            decoded.firmwareVersion = tempfv.join(".");
            i += 4;
        }
        // PEOPLE COUNTER
        else if (channel_id == 4 && channel_type == -55) {
            decoded.peopleCountAll = input[i];
            decoded.regionCount = input[i + 1];
            var region = parseBytesToInt(input, i + 2, 2, false);
            for (var idxpc = 0; idxpc < decoded.regionCount; idxpc++) {
                var tmp = "region" + (idxpc + 1);
                decoded[tmp] = (region >> idxpc) & 1;
            }
            i += 4;
        }
        // PEOPLE IN/OUT
        else if (channel_id == 5 && channel_type == -52) {
            decoded.peopleIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // PEOPLE MAX
        else if (channel_id == 6 && channel_type == -51) {
            decoded.peopleCountMax = input[i];
            i += 1;
        }
        // REGION COUNTER
        else if (channel_id == 7 && channel_type == -43) {
            decoded.region1Count = input[i];
            decoded.region2Count = input[i + 1];
            decoded.region3Count = input[i + 2];
            decoded.region4Count = input[i + 3];
            decoded.region5Count = input[i + 4];
            decoded.region6Count = input[i + 5];
            decoded.region7Count = input[i + 6];
            decoded.region8Count = input[i + 7];
            i += 8;
        }
        // REGION COUNTER
        else if (channel_id == 8 && channel_type == -43) {
            decoded.region9Count = input[i];
            decoded.region10Count = input[i + 1];
            decoded.region11Count = input[i + 2];
            decoded.region12Count = input[i + 3];
            decoded.region13Count = input[i + 4];
            decoded.region14Count = input[i + 5];
            decoded.region15Count = input[i + 6];
            decoded.region16Count = input[i + 7];
            i += 8;
        }
        // A FLOW
        else if (channel_id == 9 && channel_type == -38) {
            decoded.aToA = parseBytesToInt(input, i, 2, false);
            decoded.aToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.aToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.aToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // B FLOW
        else if (channel_id == 10 && channel_type == -38) {
            decoded.bToA = parseBytesToInt(input, i, 2, false);
            decoded.bToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.bToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.bToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // C FLOW
        else if (channel_id == 11 && channel_type == -38) {
            decoded.cToA = parseBytesToInt(input, i, 2, false);
            decoded.cToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.cToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.cToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // D FLOW
        else if (channel_id == 12 && channel_type == -38) {
            decoded.dToA = parseBytesToInt(input, i, 2, false);
            decoded.dToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.dToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.dToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // TOTAL IN/OUT
        else if (channel_id == 13 && channel_type == -52) {
            decoded.peopleTotalIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleTotalOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // DWELL TIME
        else if (channel_id == 14 && channel_type == -28) {
            var region1 = input[i];
            decoded.region = region1;
            decoded.dwellTimeAvg = parseBytesToInt(input, i + 1, 2, false);
            decoded.dwellTimeMax = parseBytesToInt(input, i + 3, 2, false);
            i += 5;
        }
        // TIMESTAMP
        else if (channel_id == 15 && channel_type == -123) {
            decoded.timestamp = parseBytesToInt(input, i, 4, false);
            i += 4;
        } else {
            break;
        }
    }

    output.telemetry = decoded;

    // --- 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
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
var historyData = {};
    var decoded = {};
    decoded.hexString = bytesToHex(input);
    for (var i = 0; i < input.length; ) {
        var channel_id = input[i];
        var channel_type = input[i+1];

        i += 2;

        // PROTOCOL VERSION
        if (channel_id == -1 && channel_type == 1) {
            decoded.protocol_version = input[i];
            i += 1;
        }
        // SERIAL NUMBER
        else if (channel_id == -1 && channel_type == 8) {
            var temp = [];
            var last_index_sn = i + 6;
            for (var idxsn = i; idxsn < last_index_sn; idxsn++) {
                temp.push(bytesToHex([input[idxsn] & 0xff]));
            }
            decoded.serialNumber = temp.join("");
            i += 6;
        }
        // HARDWARE VERSION
        else if (channel_id == -1 && channel_type == 9) {
            var temphv = [];
            var last_index_hv = i + 2;
            for (var idxhv = i; idxhv < last_index_hv; idxhv++) {
                temphv.push((input[idxhv] & 0xff).toString());
            }
            decoded.hardwareVersion = temphv.join(".");
            i += 2;
        }
        // FIRMWARE VERSION
        else if (channel_id == -1 && channel_type == 31) {
            var tempfv = [];
            var last_index_fv = i + 4;
            for (var idxfv = i; idxfv < last_index_fv; idxfv++) {
                tempfv.push((input[idxfv] & 0xff).toString());
            }
            decoded.firmwareVersion = tempfv.join(".");
            i += 4;
        }
        // PEOPLE COUNTER
        else if (channel_id == 4 && channel_type == -55) {
            decoded.peopleCountAll = input[i];
            decoded.regionCount = input[i + 1];
            var region = parseBytesToInt(input, i + 2, 2, false);
            for (var idxpc = 0; idxpc < decoded.regionCount; idxpc++) {
                var tmp = "region" + (idxpc + 1);
                decoded[tmp] = (region >> idxpc) & 1;
            }
            i += 4;
        }
        // PEOPLE IN/OUT
        else if (channel_id == 5 && channel_type == -52) {
            decoded.peopleIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // PEOPLE MAX
        else if (channel_id == 6 && channel_type == -51) {
            decoded.peopleCountMax = input[i];
            i += 1;
        }
        // REGION COUNTER
        else if (channel_id == 7 && channel_type == -43) {
            decoded.region1Count = input[i];
            decoded.region2Count = input[i + 1];
            decoded.region3Count = input[i + 2];
            decoded.region4Count = input[i + 3];
            decoded.region5Count = input[i + 4];
            decoded.region6Count = input[i + 5];
            decoded.region7Count = input[i + 6];
            decoded.region8Count = input[i + 7];
            i += 8;
        }
        // REGION COUNTER
        else if (channel_id == 8 && channel_type == -43) {
            decoded.region9Count = input[i];
            decoded.region10Count = input[i + 1];
            decoded.region11Count = input[i + 2];
            decoded.region12Count = input[i + 3];
            decoded.region13Count = input[i + 4];
            decoded.region14Count = input[i + 5];
            decoded.region15Count = input[i + 6];
            decoded.region16Count = input[i + 7];
            i += 8;
        }
        // A FLOW
        else if (channel_id == 9 && channel_type == -38) {
            decoded.aToA = parseBytesToInt(input, i, 2, false);
            decoded.aToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.aToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.aToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // B FLOW
        else if (channel_id == 10 && channel_type == -38) {
            decoded.bToA = parseBytesToInt(input, i, 2, false);
            decoded.bToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.bToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.bToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // C FLOW
        else if (channel_id == 11 && channel_type == -38) {
            decoded.cToA = parseBytesToInt(input, i, 2, false);
            decoded.cToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.cToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.cToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // D FLOW
        else if (channel_id == 12 && channel_type == -38) {
            decoded.dToA = parseBytesToInt(input, i, 2, false);
            decoded.dToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.dToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.dToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // TOTAL IN/OUT
        else if (channel_id == 13 && channel_type == -52) {
            decoded.peopleTotalIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleTotalOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // DWELL TIME
        else if (channel_id == 14 && channel_type == -28) {
            var region1 = input[i];
            decoded.region = region1;
            decoded.dwellTimeAvg = parseBytesToInt(input, i + 1, 2, false);
            decoded.dwellTimeMax = parseBytesToInt(input, i + 3, 2, false);
            i += 5;
        }
        // TIMESTAMP
        else if (channel_id == 15 && channel_type == -123) {
            decoded.timestamp = parseBytesToInt(input, i, 4, false);
            i += 4;
        } else {
            break;
        }
    }

    output.telemetry = decoded;

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

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
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);

    var historyData = {};
    var decoded = {};
    decoded.hexString = bytesToHex(input);
    for (var i = 0; i < input.length; ) {
        var channel_id = input[i];
        var channel_type = input[i+1];

        i += 2;

        // PROTOCOL VERSION
        if (channel_id == -1 && channel_type == 1) {
            decoded.protocol_version = input[i];
            i += 1;
        }
        // SERIAL NUMBER
        else if (channel_id == -1 && channel_type == 8) {
            var temp = [];
            var last_index_sn = i + 6;
            for (var idxsn = i; idxsn < last_index_sn; idxsn++) {
                temp.push(bytesToHex([input[idxsn] & 0xff]));
            }
            decoded.serialNumber = temp.join("");
            i += 6;
        }
        // HARDWARE VERSION
        else if (channel_id == -1 && channel_type == 9) {
            var temphv = [];
            var last_index_hv = i + 2;
            for (var idxhv = i; idxhv < last_index_hv; idxhv++) {
                temphv.push((input[idxhv] & 0xff).toString());
            }
            decoded.hardwareVersion = temphv.join(".");
            i += 2;
        }
        // FIRMWARE VERSION
        else if (channel_id == -1 && channel_type == 31) {
            var tempfv = [];
            var last_index_fv = i + 4;
            for (var idxfv = i; idxfv < last_index_fv; idxfv++) {
                tempfv.push((input[idxfv] & 0xff).toString());
            }
            decoded.firmwareVersion = tempfv.join(".");
            i += 4;
        }
        // PEOPLE COUNTER
        else if (channel_id == 4 && channel_type == -55) {
            decoded.peopleCountAll = input[i];
            decoded.regionCount = input[i + 1];
            var region = parseBytesToInt(input, i + 2, 2, false);
            for (var idxpc = 0; idxpc < decoded.regionCount; idxpc++) {
                var tmp = "region" + (idxpc + 1);
                decoded[tmp] = (region >> idxpc) & 1;
            }
            i += 4;
        }
        // PEOPLE IN/OUT
        else if (channel_id == 5 && channel_type == -52) {
            decoded.peopleIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // PEOPLE MAX
        else if (channel_id == 6 && channel_type == -51) {
            decoded.peopleCountMax = input[i];
            i += 1;
        }
        // REGION COUNTER
        else if (channel_id == 7 && channel_type == -43) {
            decoded.region1Count = input[i];
            decoded.region2Count = input[i + 1];
            decoded.region3Count = input[i + 2];
            decoded.region4Count = input[i + 3];
            decoded.region5Count = input[i + 4];
            decoded.region6Count = input[i + 5];
            decoded.region7Count = input[i + 6];
            decoded.region8Count = input[i + 7];
            i += 8;
        }
        // REGION COUNTER
        else if (channel_id == 8 && channel_type == -43) {
            decoded.region9Count = input[i];
            decoded.region10Count = input[i + 1];
            decoded.region11Count = input[i + 2];
            decoded.region12Count = input[i + 3];
            decoded.region13Count = input[i + 4];
            decoded.region14Count = input[i + 5];
            decoded.region15Count = input[i + 6];
            decoded.region16Count = input[i + 7];
            i += 8;
        }
        // A FLOW
        else if (channel_id == 9 && channel_type == -38) {
            decoded.aToA = parseBytesToInt(input, i, 2, false);
            decoded.aToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.aToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.aToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // B FLOW
        else if (channel_id == 10 && channel_type == -38) {
            decoded.bToA = parseBytesToInt(input, i, 2, false);
            decoded.bToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.bToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.bToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // C FLOW
        else if (channel_id == 11 && channel_type == -38) {
            decoded.cToA = parseBytesToInt(input, i, 2, false);
            decoded.cToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.cToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.cToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // D FLOW
        else if (channel_id == 12 && channel_type == -38) {
            decoded.dToA = parseBytesToInt(input, i, 2, false);
            decoded.dToB = parseBytesToInt(input, i + 2, 2, false);
            decoded.dToC = parseBytesToInt(input, i + 4, 2, false);
            decoded.dToD = parseBytesToInt(input, i + 6, 2, false);
            i += 8;
        }
        // TOTAL IN/OUT
        else if (channel_id == 13 && channel_type == -52) {
            decoded.peopleTotalIn = parseBytesToInt(input, i, 2, false);
            decoded.peopleTotalOut = parseBytesToInt(input, i + 2, 2, false);
            i += 4;
        }
        // DWELL TIME
        else if (channel_id == 14 && channel_type == -28) {
            var region1 = input[i];
            decoded.region = region1;
            decoded.dwellTimeAvg = parseBytesToInt(input, i + 1, 2, false);
            decoded.dwellTimeMax = parseBytesToInt(input, i + 3, 2, false);
            i += 5;
        }
        // TIMESTAMP
        else if (channel_id == 15 && channel_type == -123) {
            decoded.timestamp = parseBytesToInt(input, i, 4, false);
            i += 4;
        } else {
            break;
        }
    }

    output.telemetry = decoded;

    // --- 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上查看数据

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


为获得更直观的视图,可使用仪表板。 可下载适用于本设备的简易仪表板。该仪表板配置为显示设备名称为 “eui-24e124538b223213” 的设备的 “Count people all”、”Max count people”、”Signal strength”、”Region count” 和 “Occupancy” 时序数据。

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

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

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

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

  • 仪表板已导入。

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

按以下步骤操作:

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

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

  • 应用所有更改。

现在应能看到设备的数据。

带数据的仪表板示例:

总结

现在您可以轻松将VS121 AI workplace sensor连接到ThingsBoard并开始发送数据。

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

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