使用 Amazon Sidewalk 和 LoRaWAN 网络实现无缝资产跟踪
介绍
通过这个双设备演示体验无缝网络集成。该设置包含多功能的 Seeed Studio Wio Tracker 开发板和强大的 Seeed Studio SenseCAP T1000-S 跟踪器设备,两者都展示了在 LoRaWAN 和 Sidewalk 网络之间的无缝切换,以实现最佳覆盖。
- 双网络支持:只需按一下按钮,即可在 LoRaWAN 和 Sidewalk 之间切换,保持持续连接。
- 云端连接:实时观察数据传输到 AWS IoT Core,通过笔记本电脑显示器/监视器上的 AWS Web 应用程序进行可视化。
- 电池供电效率:设备确保持续运行,无需电线束缚,实现真正的移动体验。
该过程分为以下主要步骤:
- SDK 安装和设置
- 固件烧录
- AWS IoT Core 配置
- Amazon Location 配置
- Web 应用程序配置
先决条件
安装 nRF Connect SDK
有多种方式安装 nRF Connect SDK,取决于您首选的开发环境和工具链管理工具:
- 
使用 Visual Studio Code 和 nRF Connect for VS Code 扩展(推荐) 
- 
使用命令行和 nRF Util 
步骤 1:更新操作系统
在开始设置工具链之前,请为您的操作系统安装可用更新。有关支持的操作系统信息,请参阅要求。
步骤 2:安装先决条件
- Visual Studio Code
- Command Line
- 
最新版本的 nRF Command Line Tools 包。从 nRF Command Line Tools 页面下载。 
- 
适用于您操作系统的最新版本 Visual Studio Code,从 Visual Studio Code 下载页面获取。 
- 
在 Visual Studio Code 中,安装最新版本的 nRF Connect for VS Code Extension Pack。 
- 最新版本的 nRF Util 开发工具,这是 Nordic 产品的统一命令行实用程序。
注意:
下载 nRF Util 可执行文件后,将其移动到系统 PATH 中的目录。在 macOS 和 Linux 上,下载的文件还需要通过输入 chmod +x nrfutil 或在文件属性中勾选复选框来获得执行权限。
- 最新版本的 nRF Command Line Tools 包,从 nRF Command Line Tools 页面下载。
注意:
下载并安装工具后,在环境变量中将 nrfjprog 添加到系统 PATH。
步骤 3:安装 nRF Connect SDK 工具链
- nRF Connect for Visual Studio Code
- Command Line
- 通过点击 Activity Bar中的图标在 Visual Studio Code 中打开 nRF Connect 扩展。
- 在扩展的 Welcome View中,点击Install Toolchain。
- 选择要安装的工具链版本。工具链版本应与您将要使用的 nRF Connect SDK 版本匹配。我们使用 V2.5.0(推荐)。

安装工具链后,您可以通过点击 Manage toolchains 来访问 Install Toolchain 选项。

- 
打开终端窗口。 
- 
运行以下命令安装 nRF Util toolchain-manager 命令: 
nrfutil install toolchain-manager
- 运行以下命令列出可用的安装:
nrfutil toolchain-manager search
此列表中的版本对应于 nRF Connect SDK 版本,将在以下步骤中进行版本控制。
- 运行以下命令为您选择的 SDK 版本安装工具链版本:
nrfutil toolchain-manager install --ncs-version version
For example:
nrfutil toolchain-manager install --ncs-version v2.5.0
此示例命令安装 nRF Connect SDK v2.5.0(推荐)所需的工具链。
工具链默认安装在 Windows 的 C:/ncs/toolchains、Linux 的 ~/ncs/toolchains 和 macOS 的 /opt/nordic/ncs/toolchains。
要检查当前配置设置,请使用 nrfutil toolchain-manager config --show 命令。
要了解更多关于这些命令的信息,请使用 nrfutil toolchain-manager --help 命令。
步骤 4:获取 nRF Connect SDK 代码
- nRF Connect for Visual Studio Code
- Command Line
要克隆 nRF Connect SDK 代码,请完成以下步骤:
- 
通过点击 Activity Bar中的图标在 Visual Studio Code 中打开 nRF Connect 扩展。
- 
在扩展的 Welcome View中,点击Manage SDKs。操作列表将出现在 Visual Studio Code 的快速选择中。
- 
点击 Install SDK。可用的 SDK 版本列表将出现在 Visual Studio Code 的快速选择中。
- 
选择要安装的 SDK 版本,我们使用 V2.5.0。

SDK 安装开始,可能需要几分钟时间。
要克隆仓库,请完成以下步骤:
- 
在命令行中,打开目录 ncs。默认情况下,这比您安装工具链的位置高一级。此目录将保存所有 nRF Connect SDK 仓库。
- 
使用以下命令为您的操作系统启动工具链环境: Windows nrfutil toolchain-manager launch --terminalLinux/macOS nrfutil toolchain-manager launch --shell
- 
确定您想要使用的 nRF Connect SDK 修订版本的标识符。有关更多信息,请参见上表。当您首次安装 nRF Connect SDK 时,建议安装 SDK 和工具链的最新发布版本。 
- 
使用您想要检出的 nRF Connect SDK 修订版本初始化 west,将 nRFConnectSDK_revision 替换为标识符: west init -m https://github.com/nrfconnect/sdk-nrf --mr nRFConnectSDK_revision
For example:
特定版本:要检出 v2.5.0 版本,请输入以下命令:
west init -m https://github.com/nrfconnect/sdk-nrf --mr v2.5.0
开发标签:要检出 v1.9.2-dev1 标签,请输入以下命令:
west init -m https://github.com/nrfconnect/sdk-nrf --mr v1.9.2-dev1
分支:要检出包含最新开发状态的主分支,请输入以下命令:
west init -m https://github.com/nrfconnect/sdk-nrf --mr main
这将把清单仓库 sdk-nrf 克隆到 nrf 目录中。
使用清单文件的特定版本初始化 west 并不会将您的仓库锁定到此版本。在 sdk-nrf 仓库中检出不同的分支或标签并运行 west update 会改变您使用的 nRF Connect SDK 版本。
注意:
如果在运行 west 时收到错误消息,请将 west 更新到最新版本。更多信息请参见 Zephyr 文档中的 West 故障排除。
- 
输入以下命令来克隆项目仓库: west update
根据您的连接情况,这可能需要一些时间。
- 
导出一个 Zephyr CMake 包。这允许 CMake 自动加载构建 nRF Connect SDK 应用程序所需的样板代码: west zephyr-export
查看 安装 nRF Connect SDK 了解更多详情。
设置 Sidewalk 环境
按照以下步骤为 nRF Connect SDK 下载 Sidewalk 应用程序:
- 打开终端窗口。您的目录结构应如下所示:
.
|___ .west
|___ bootloader
|___ modules
|___ nrf
|___ nrfxlib
|___ zephyr
|___ ...
- 确保清单路径指向 nrf 目录内的 west.yml:
west manifest --path
/path-to-ncs-folder/nrf/west.yml
如果你的清单路径指向不同的文件,请使用以下命令:
west config manifest.path nrf
- 为西部启用人行道组过滤器。
west config manifest.group-filter "+sidewalk"
检查西边是否存在人行道:
west list sidewalk
sidewalk     sidewalk                     <sidewalk_revision> https://github.com/nrfconnect/sdk-sidewalk
- Update all repositories:
west update
根据您的网络连接情况,更新可能需要一些时间。
- 安装 Sidewalk 的 Python 依赖项。
pip install -r sidewalk/requirements.txt
将 LR11xx 添加到 nRF Connect SDK Sidewalk 扩展
此仓库包含软件驱动程序,使 LR11xx 系列 芯片能够在与 Nordic nRF52840 MCU 和 nRF Connect SDK 配对时支持 Sidewalk 协议。该驱动程序以二进制形式提供,作为静态库实现支持 LoRa 或 FSK 连接所需的"平台抽象层"接口。静态库内部包含 Semtech SWDR001(LR11xx 驱动程序)的完整实现,可用于访问 LR11xx 芯片的其他功能,如 WIFI 和 GNSS 扫描及测距。
- 
下载 SWDM016 
- 
在克隆的 nordic 仓库的工作目录中,位于顶级目录,即 ~/ncs/<version>/sidewalk:
patch -p1 < ../nRF52840_LR11xx_driver_v010000.diff
父目录路径 .. 假设您将差异文件放在那里,否则您可以指定其位置的完整路径。
- 
将无线电驱动程序库 lib*.a复制到 sidewalk 项目的~/ncs/<version>/sidewalk/lib/lora_fsk/目录中
 提供了两个库,一个启用了LOG_RUNTIME_FILTERING,另一个没有启用。
- 
将 ~/template_lbm_wio_tracker/boards/arm/wio_tracker_1110文件夹复制到~/ncs/v2.5.0/zephyr/boards/arm。
·
├─── .west/
├─── modules/
├─── nrf/
├─── ...
└─── zephyr/
     └─── Boards/
          └─── arm/
               └─── wio_tracker_1110/
创建资源
步骤 1:部署 Cloud9 环境
在本节中,您将创建开始之前所需的所有资源。首先,您将创建一个 Cloud9 工作空间,用于构建和部署其他资源。然后,您将部署一个包含资产跟踪器应用程序所有后端资源的 CDK 堆栈。最后,您将安装所有前端依赖项并配置应用程序。
- 打开 AWS Cloud9 控制台,点击 Create Environment

- 除了 实例类型 外,其他所有设置保持默认。选择 m5.large。

步骤 2:配置先决条件
- 打开 Cloud9 IDE。

- 在您的 Cloud9 环境终端中输入以下命令来克隆 github 仓库:
git clone --recurse-submodules https://github.com/aws-samples/aws-iot-asset-tracker-demo.git /home/ec2-user/environment/asset-tracking-workshop

- 导航到示例应用程序目录:
cd ~/environment/asset-tracking-workshop
- 调整底层 EC2 实例的 EBS 卷大小。
npm run utils:resizeC9EBS
- Install the project's dependencies:
npm ci
- Deploy the backend infrastructure:
# Prepare the AWS account for CDK
npm run infra:bootstrap
# Deploy the backend resources
npm run infra:deploy
- Create a config file:
npm run utils:createConfig
LoRaWAN 配置
在 AWS 上添加 LoRaWAN 网关
查看此入门指南,将 SenseCAP M2 多平台网关添加到 AWS IoT Core。
在 AWS 上添加 LoRaWAN 设备
步骤 1:定义密钥
在 src/lorawan_v4/example_options.h 中定义 DevEUI/JoinEUI/APPkey 和 REGION。
JoinEUI 也称为 AppEUI

步骤 2:创建配置文件
登录到 AWS IoT 控制台,导航到 Devices,点击 Profiles。
- 设备配置文件
设备配置文件定义了网络服务器用于设置 LoRaWAN 无线接入服务的设备功能和启动参数。它包括 LoRa 频段、LoRa 区域参数版本和设备 MAC 版本等参数的选择。

要了解不同的频段,请参阅为您的网关和设备连接考虑选择 LoRa 频段。
- 服务配置文件
我们建议您保持 AddGWMetaData 设置启用,这样您将收到每个有效载荷的额外网关元数据,例如数据传输的 RSSI 和 SNR。

步骤 3:添加设备
导航到 LPWAN devices > Devices,点击 Add wireless device。
Wireless device specification:OTAAv1.0x
选择您在上一步中创建的设备配置文件和目标。

导航到设备页面并选择您之前添加的设备。
Sidewalk 配置
设置 Sidewalk 网关(可选)
您可以设置 Sidewalk 网关,配置它,并将您的网关与您的 Amazon 账户关联。您的 Sidewalk 端点在注册到 Amazon Sidewalk 后将连接到 Sidewalk 网关并与其通信。
查看设置 Sidewalk 网关了解更多详情。
设置您的 Sidewalk 设备
添加您的 Sidewalk 设备
步骤 1:添加您的设备配置文件和 Sidewalk 终端设备
在创建无线设备之前,首先创建设备配置文件。
导航到设备中心的 Sidewalk 选项卡,选择 Provision device,然后执行以下步骤。
步骤 2:获取设备 JSON 文件
要获取用于配置您的 Sidewalk 设备的 JSON 文件:
- 
转到 Sidewalk 设备中心。 
- 
选择您添加到 AWS IoT Core for Amazon Sidewalk 的设备以查看其详细信息。 
- 
通过在您添加的设备详细信息页面中选择 Download device JSON file来获取 JSON 文件。
将下载一个包含配置您的终端设备所需信息的 certificate.json 文件。

步骤 3:配置您的 Sidewalk 端点
生成二进制镜像
- 安装需求文件
转到 Sidewalk SDK 文件夹 $[Amazon Sidewalk repository]/tools/scripts/public/provision/,然后运行以下命令来安装 requirements 文件。
pip3 install -r requirements.txt
- 生成制造二进制镜像
运行 provision.py 脚本来生成制造二进制镜像文件,该文件将用于配置您用作 Sidewalk 端点的开发板。
- 如果您使用的是从 AWS IoT 控制台获得的组合设备 JSON 文件,请在运行配置脚本时使用 certificate_json 参数来指定此文件作为输入。
python3 provision.py aws --output_bin mfg.bin --certificate_json certificate.json \ 
   --config config/[device_vendor]/[device]_dk/config.yaml
如果您使用的是从 GetDeviceProfile 和 GetWirelessDevice API 操作响应中获得的单独设备 JSON 文件,请在运行配置脚本时使用 wireless_device_json 和 device_profile_json 参数来指定这些文件作为输入。
python3 provision.py aws --output_bin mfg.bin \  
   --wireless_device_json wireless_device.json \
   --device_profile_json device_profile.json \ 
   --config config/[device_vendor]/[device]_dk/config.yaml
您应该看到以下输出:

- 烧录 mfg.hex 文件
您的配置文件通常位于 EdgeDeviceProvisioning 目录中。
要烧录二进制镜像,请使用地址 0xFD000 在 Nordic Semiconductor HDK 上加载二进制镜像。有关烧录二进制镜像的信息,请参阅 Nordic Semiconductor 文档。
步骤 4:构建并烧录演示程序
- 
打开终端窗口。 
- 
进入 template_lbm_wio_tracker目录。
例如:
cd /opt/nordic/ncs/v2.5.0/sidewalk/samples/template_lbm_wio_tracker
- 使用以下west命令构建应用程序:
west build --board wio_tracker_1110 -- -DRADIO=LR1110_SRC
或使用预编译的无线电驱动程序库:
west build --board wio_tracker_1110 -- -DRADIO=LR1110

- 使用以下 west 命令刷写应用程序:
west flash

Sidewalk 注册
在您配置了 Sidewalk 端点后,必须注册该端点,以便它能够通过 Sidewalk 网络进行通信。
要注册您的 Sidewalk 端点,可以使用 Sidewalk 无忧网络 (FFN) 的自动无接触注册,或者使用运行注册脚本的 Mac 或原生 Ubuntu 机器手动注册您的设备。
| 标准 自动 | 注册(使用 Sidewalk FFN) | 手动注册 | 
|---|---|---|
| 用户和端点关联 | 此注册方法不需要 Sidewalk 端点与用户之间的任何关联。端点可以加入 Sidewalk 网络而无需与任何用户关联。 | 此注册方法需要 Sidewalk 端点与用户的 Amazon 账户之间的关联。 | 
| LWA(使用 Amazon 登录) | 不需要 LWA。 | 需要 LWA 来链接用户的 Amazon 账户和 Sidewalk 端点开发者使用的 AWS 账户。 | 
使用 Sidewalk FFN 执行注册:
- 您的 Sidewalk 网关和端点必须已开机。
- 您的网关必须已选择加入 Sidewalk,并且在端点的近距离范围内。我们建议您将设备保持在彼此 10 米范围内。
有关 手动 Sidewalk 注册 和其他详细信息,请查看 这里。
网络切换
默认为 LoRaWAN 网络,点击 用户按钮 切换网络。

查看消息
添加目标
在 IoT Core 控制台 中,从左侧菜单选择 LPWAN devices,然后选择 Destinations。
选择 Edit 并选择 Publish to AWS IoT Core message broker。在主题文本框中,输入 assets 作为 MQTT 主题。
在 Permissions 下选择 Create a new service role 并将 Role name 留空。
- ExpressionType: MqttTopic
- Expression: EmbeddedWorldTrackerDemo
添加解码器规则
导航到 Message routing 选项卡 → Rules,然后点击 Create Rule 按钮。

为您的规则命名并提交。

从 IoT Core 规则中,选择 Lambda 函数。然后点击 Create a Lambda function。
从头开始创建
Function name: 为您的函数命名。
Runtime: Node.js 14.x
Architexture: x86_64
点击 Create function 按钮创建新函数

在以下函数配置页面上,删除所有代码并用以下脚本替换,然后点击 Deploy 按钮。

Lambda 代码
const {IoTDataPlaneClient, PublishCommand} = require("@aws-sdk/client-iot-data-plane");
const {IoTWirelessClient, GetWirelessDeviceCommand} = require("@aws-sdk/client-iot-wireless");
const client = new IoTDataPlaneClient({
    "region": "us-east-1"
});
const wireless_client = new IoTWirelessClient({
    "region": "us-east-1"
});
function decodeUplink(input) {
    const originMessage = input.toLocaleUpperCase()
    const decoded = {
        valid: true,
        err: 0,
        payload: input,
        messages: []
    }
    let measurement = messageAnalyzed(originMessage)
    if (measurement.length === 0) {
        decoded.valid = false
        return {data: decoded}
    }
    for (let message of measurement) {
        if (message.length === 0) {
            continue
        }
        let elements = []
        for (let element of message) {
            if (element.errorCode) {
                decoded.err = element.errorCode
                decoded.errMessage = element.error
            } else {
                elements.push(element)
            }
        }
        if (elements.length > 0) {
            decoded.messages.push(elements)
        }
    }
    return {data: decoded}
}
function messageAnalyzed(messageValue) {
    try {
        let frames = unpack(messageValue)
        let measurementResultArray = []
        for (let i = 0; i < frames.length; i++) {
            let item = frames[i]
            let dataId = item.dataId
            let dataValue = item.dataValue
            let measurementArray = deserialize(dataId, dataValue)
            measurementResultArray.push(measurementArray)
        }
        return measurementResultArray
    } catch (e) {
        return e.toString()
    }
}
function unpack(messageValue) {
    return [{dataId: 0, dataValue: messageValue}]
}
function deserialize(dataId, dataValue) {
    let measurementArray = null
    measurementArray = [
        {
            measurementId: '4198',
            type: 'Latitude',
            measurementValue: parseFloat(getSensorValue(dataValue.substring(0, 8), 1000000))
        },
        {
            measurementId: '4197',
            type: 'Longitude',
            measurementValue: parseFloat(getSensorValue(dataValue.substring(8, 16), 1000000))
        },
        {
            measurementId: '4097',
            type: 'Air Temperature',
            measurementValue: getSensorValue(dataValue.substring(16, 20), 10)
        },
        {
            measurementId: '4098',
            type: 'Air Humidity',
            measurementValue: getSensorValue(dataValue.substring(20, 22))
        }
    ]
    return measurementArray
}
function getSensorValue(str, dig) {
    if (str === '8000') {
        return null
    } else {
        return loraWANV2DataFormat(str, dig)
    }
}
function bytes2HexString(arrBytes) {
    var str = ''
    for (var i = 0; i < arrBytes.length; i++) {
        var tmp
        var num = arrBytes[i]
        if (num < 0) {
            tmp = (255 + num + 1).toString(16)
        } else {
            tmp = num.toString(16)
        }
        if (tmp.length === 1) {
            tmp = '0' + tmp
        }
        str += tmp
    }
    return str
}
function loraWANV2DataFormat(str, divisor = 1) {
    let strReverse = bigEndianTransform(str)
    let str2 = toBinary(strReverse)
    if (str2.substring(0, 1) === '1') {
        let arr = str2.split('')
        let reverseArr = arr.map((item) => {
            if (parseInt(item) === 1) {
                return 0
            } else {
                return 1
            }
        })
        str2 = parseInt(reverseArr.join(''), 2) + 1
        return '-' + str2 / divisor
    }
    return parseInt(str2, 2) / divisor
}
function bigEndianTransform(data) {
    let dataArray = []
    for (let i = 0; i < data.length; i += 2) {
        dataArray.push(data.substring(i, i + 2))
    }
    return dataArray
}
function toBinary(arr) {
    let binaryData = arr.map((item) => {
        let data = parseInt(item, 16)
            .toString(2)
        let dataLength = data.length
        if (data.length !== 8) {
            for (let i = 0; i < 8 - dataLength; i++) {
                data = `0` + data
            }
        }
        return data
    })
    return binaryData.toString().replace(/,/g, '')
}
exports.handler = async (event) => {
    try {
        let device_id = event['WirelessDeviceId'];
        let lorawan_info = null;
        let sidewalk_info = null;
        let payload = null
        let timestamp = null
        let queryDeviceRequest = {
            Identifier: device_id,
            IdentifierType: "WirelessDeviceId"
        }
        let deviceInfo = await wireless_client.send(new GetWirelessDeviceCommand(queryDeviceRequest))
        console.log("device_info:" + JSON.stringify(deviceInfo))
        if (!deviceInfo || deviceInfo.name) {
            return {
                statusCode: 500,
                body: 'can not find this wirelessDeviceId: ' + device_id
            };
        }
        let device_name = deviceInfo.Name
        if (event["WirelessMetadata"]["LoRaWAN"]) {
            lorawan_info = event["WirelessMetadata"]["LoRaWAN"]
            timestamp = lorawan_info["Timestamp"]
            let bytes = Buffer.from(event["PayloadData"], 'base64');
            payload = bytes2HexString(bytes)
        } else if (event["WirelessMetadata"]["Sidewalk"]) {
            timestamp = new Date().getTime()
            let origin = new Buffer(event["PayloadData"], 'base64')
            payload = origin.toString('utf8')
        }
        console.log(`event.PayloadData: ${payload}`)
        const resolved_data = decodeUplink(payload);
        
        // publish all measurement data
        const input = { // PublishRequest
            topic: `tracker/EmbeddedWorldTrackerDemo/sensor/${device_id}`,
            qos: 0,
            retain: false,
            payload: JSON.stringify({
                DeviceName: "assettracker",
                timestamp: timestamp,
                data: resolved_data.data,
                WirelessDeviceId: device_id,
                PayloadData: event['PayloadData'],
                WirelessMetadata: event["WirelessMetadata"]
            })
        };
        const command = new PublishCommand(input);
        const response = await client.send(command);
        console.log("response: " + JSON.stringify(response));
        return {
            statusCode: 200,
            body: 'Message published successfully' + JSON.stringify(event)
        };
    } catch (error) {
        console.error('Error publishing message:', error);
        return {
            statusCode: 500,
            body: 'Error publishing message'
        };
    }
};

现在回到 Device Destination,选择 Enter a rule name 并输入我们刚刚创建的名称。
导航到 AWS IoT Core Console 并选择 MQTT Test Client 并订阅该主题。
添加跟踪器规则
重复上述步骤创建一个新规则,并复制以下 Lambda 代码:
Lambda 代码
const {IoTDataPlaneClient, PublishCommand} = require("@aws-sdk/client-iot-data-plane");
const {LocationClient, BatchUpdateDevicePositionCommand} = require("@aws-sdk/client-location")
const {IoTWirelessClient, UpdateResourcePositionCommand } = require("@aws-sdk/client-iot-wireless");
const client = new IoTDataPlaneClient({
    "region": "us-east-1"
});
const wireless_client = new IoTWirelessClient({
    "region": "us-east-1"
});
exports.handler = async (event) => {
    console.log(`message received: ${JSON.stringify(event)}`)
    let device_id = event['WirelessDeviceId']
    let device_name = event['DeviceName']
    let measurements = event['data']['messages']
    let resolver_time = event['timestamp']
    let network = 1; // 1: lorawan 2: sidewalk
    if (event["WirelessMetadata"] && event["WirelessMetadata"]["Sidewalk"]) {
        network = 2
    }
    let longitude;
    let latitude;
    let gps_data = null
    let sensor_map = {}
    if (measurements && measurements.length > 0) {
        for (let i = 0; i < measurements.length; i++) {
            for (let j = 0; j < measurements[i].length; j++) {
                if (measurements[i][j].measurementId === "4097") {
                    sensor_map["Temperature"] = measurements[i][j].measurementValue;
                }
                if (measurements[i][j].measurementId === "4098") {
                    sensor_map["Humidity"] = measurements[i][j].measurementValue;
                }
                if (measurements[i][j].measurementId === "4197") {
                    longitude = measurements[i][j]["measurementValue"];
                }
                if (measurements[i][j].measurementId === "4198") {
                    latitude = measurements[i][j]["measurementValue"];
                }
                if (latitude && longitude) {
                    try {
                        gps_data = {
                            "type": "Point",
                            "coordinates": [longitude, latitude]
                            // "coordinates": [33.3318, -22.2155, 13.123]
                        }
                    } catch (e) {
                        console.log(`===>error`, e)
                    }
                }
            }
        }
    }
    if (gps_data) {
        console.log(`update device location : ${JSON.stringify(gps_data)}`)
        await updateDevicePosition(gps_data, device_id);
        const input = { // PublishRequest
            topic: `tracker/EmbeddedWorldTrackerDemo/location/${device_id}`,
            qos: 0,
            retain: false,
            payload: JSON.stringify({
                timestamp: resolver_time,
                deviceId: device_id,
                deviceName: device_name,
                latitude: gps_data.coordinates[1],
                longitude: gps_data.coordinates[0],
                positionProperties: {'batteryLevel': 90, "sensor:": 60}
            })
        };
        const command = new PublishCommand(input);
        const response = await client.send(command);
        console.log("mqtt push response: " + JSON.stringify(response));
        let locationClient = new LocationClient()
        let location_info = {
            TrackerName: 'AssetTracker',
            Updates: [
                {
                    DeviceId: 'assettracker',
                    SampleTime: new Date(resolver_time),
                    Position: [
                        gps_data.coordinates[0], gps_data.coordinates[1]
                    ],
                    Accuracy: {
                        Horizontal: 1,
                    },
                    PositionProperties: {
                        "context": JSON.stringify({net: network}),
                        "sensor": JSON.stringify(sensor_map)
                    }
                }
            ]
        }
        let loc_response = await locationClient.send(new BatchUpdateDevicePositionCommand(location_info))
        console.log("loc update response: " + JSON.stringify(loc_response));
    }
}
async function updateDevicePosition(gps_data, device_id) {
    const input = { // UpdateResourcePositionRequest
        ResourceIdentifier: device_id, // required
        ResourceType: "WirelessDevice", // required
        GeoJsonPayload: JSON.stringify(gps_data),
    };
    const command = new UpdateResourcePositionCommand(input);
    const wireless_response = await wireless_client.send(command);
    console.log(wireless_response)
}
构建 Web 应用
我们将部署必要的 Amazon Location Service 资源,以便在地图上显示我们的设备。
创建地图
首先,您需要创建一个新的 Amazon Location Service 地图资源。您将使用 AWS 控制台来完成此操作。
- 
然后展开屏幕左侧的导航栏,选择地图。 
- 
在此屏幕中,创建一个新地图: 
- 
输入地图名称并选择 HERE Explore地图样式,然后点击创建地图。

创建路由计算器
- 
然后展开屏幕左侧的导航栏,选择 路由计算器。

继续选择 HERE 作为数据提供商,点击 创建路由计算器 按钮。

创建跟踪器
导航到 跟踪器 -> 创建跟踪器:
输入跟踪器名称,并在位置过滤下选择 基于时间的过滤。

然后向下滚动,在 EventBridge 配置下标记 启用 EventBridge 事件 设置,然后点击 创建跟踪器。

创建地理围栏集合
导航到 地理围栏集合,点击 创建地理围栏集合。

显示 Web 应用
将应用部署到 Cloudfront
- 在您的 Cloud9 终端中,导航到 /home/ec2-user/environment/asset-tracking-workshop:
cd /home/ec2-user/environment/asset-tracking-workshop
- Run the following command:
npm run frontend:publish
- 完成后,您将收到网站URL。

- 在浏览器中导航到此URL以查看您的跟踪应用程序。
