

# 使用 适用于 LoRaWAN 的 AWS IoT Core 的 CUPS 服务更新网关固件
<a name="lorawan-update-firmware"></a>

您的网关上运行的 [LoRa Basics Station](https://doc.sm.tc/station) 软件使用 Configuration and Update Server (CUPS) 协议提供凭证管理和固件更新接口。CUPS 协议借助 ECDSA 签名提供安全的固件更新交付。

您必须经常更新网关的固件。您可以使用 适用于 LoRaWAN 的 AWS IoT Core 的 CUPS 服务来向能够签署更新的网关提供固件更新。要更新网关固件，您可以使用 SDK 或 CLI，但不要使用控制台。

该升级过程可能最多需要 45 分钟才能完成。如果您是首次设置网关来连接到 适用于 LoRaWAN 的 AWS IoT Core，则可能需要更长的时间。网关制造商通常会提供自己的固件更新文件和签名，因此您可以使用它们并前进至 [将固件文件上载到 S3 存储桶并添加 IAM 角色](lorawan-upload-firmware-s3bucket.md)。

如果您没有固件更新文件，请参阅 [生成固件更新文件和签名](lorawan-script-fwupdate-sigkey.md) 作为示例，您可以使用该示例来修改您的应用程序。

**Topics**
+ [生成固件更新文件和签名](lorawan-script-fwupdate-sigkey.md)
+ [将固件文件上载到 S3 存储桶并添加 IAM 角色](lorawan-upload-firmware-s3bucket.md)
+ [使用任务定义安排并运行固件更新](lorawan-schedule-firmware-update.md)

# 生成固件更新文件和签名
<a name="lorawan-script-fwupdate-sigkey"></a>

本流程的步骤是可选的，取决于您使用的网关。网关制造商会以更新文件或脚本的形式提供自己的固件更新，Basics Station 会在后台运行此脚本。在这种情况下，您很可能会在所使用网关的发布说明中找到固件更新文件。然后，您可以改为使用该更新文件或脚本，然后前进至 [将固件文件上载到 S3 存储桶并添加 IAM 角色](lorawan-upload-firmware-s3bucket.md)。

如果您没有此脚本，下面显示了用于生成固件更新文件的命令。您还可以签署更新，以确保代码未被更改或损坏，并且设备仅运行由受信任的作者发布的代码。

**Topics**
+ [生成固件更新文件](#lorawan-firmware-update-script)
+ [为固件更新生成签名](#lorawan-generate-signature-fwupdate)
+ [查看后续步骤](#lorawan-fwupdate-sigkey-next-steps)

## 生成固件更新文件
<a name="lorawan-firmware-update-script"></a>

在网关上运行的 LoRa Basics Station 软件能够接收 CUPS 响应中的固件更新。如果您没有制造商提供的脚本，请参阅以下固件更新脚本，该脚本是为基于 Raspberry Pi 的 RAKWireless Gateway 编写的。我们有一个基本脚本，而新工作站二进制文件、版本文件和 `station.conf` 将被附加其上。

**注意**  
该脚本特定于 RAKWireless Gateway ，因此您必须根据您使用的网关对其进行调整，以使其适用于您的应用程序。

**基本脚本**  
下面显示了基于 Raspberry Pi 的 RAKWireless Gateway 的基本脚本示例。您可以将以下命令保存在文件 `base.sh` 中，然后在 Raspberry Pi 的 Web 浏览器终端中运行脚本。

```
*#!/bin/bash*
execution_folder=/home/pi/Documents/basicstation/examples/aws_lorawan
station_path="$execution_folder/station"
version_path="$execution_folder/version.txt"
station_conf_path="$execution_folder/station_conf"

# Function to find the Basics Station binary at the end of this script 
# and store it in the station path
function prepare_station()
{
 match=$(grep --text --line-number '^STATION:$' $0 | cut -d ':' -f 1) 
 payload_start=$((match + 1)) 
 match_end=$(grep --text --line-number '^END_STATION:$' $0 | cut -d ':' -f 1) 
 payload_end=$((match_end - 1)) 
 lines=$(($payload_end-$payload_start+1)) 
 head -n $payload_end $0 | tail -n $lines  > $station_path
}

# Function to find the version.txt at the end of this script 
# and store it in the location for version.txt
function prepare_version()
{
  match=$(grep --text --line-number '^VERSION:$' $0 | cut -d ':' -f 1) 
  payload_start=$((match + 1))        
  match_end=$(grep --text --line-number '^END_VERSION:$' $0 | cut -d ':' -f 1) 
  payload_end=$((match_end - 1)) 
  lines=$(($payload_end-$payload_start+1)) 
  head -n $payload_end $0 | tail -n $lines  > $version_path
}

# Function to find the version.txt at the end of this script 
# and store it in the location for version.txt
function prepare_station_conf()
{
 match=$(grep --text --line-number '^CONF:$' $0 | cut -d ':' -f 1) 
 payload_start=$((match + 1)) 
 match_end=$(grep --text --line-number '^END_CONF:$' $0 | cut -d ':' -f 1) 
 payload_end=$((match_end - 1)) 
 lines=$(($payload_end-$payload_start+1)) 
 head -n $payload_end $0 | tail -n $lines  > $station_conf_path
}

# Stop the currently running Basics station so that it can be overwritten
# by the new one
killall station

# Store the different files
prepare_station
prepare_versionp
prepare_station_conf

# Provide execute permission for Basics station binary
chmod +x $station_path

# Remove update.bin so that it is not read again next time Basics station starts
rm -f /tmp/update.bin

# Exit so that rest of this script which has binaries attached does not get executed
exit 0
```

**添加负载脚本**  
在基本脚本中，我们会附加 Basics Station 二进制文件、标识要更新版本的 version.txt 和名为 `addpayload.sh` 脚本中的 `station.conf`。然后，运行此脚本。

```
*#!/bin/bash
*
base.sh > fwstation

# Add station
echo "STATION:" >> fwstation
cat $1 >> fwstation
echo "" >> fwstation
echo "END_STATION:" >> fwstation

# Add version.txt
echo "VERSION:" >> fwstation
cat $2 >> fwstation
echo "" >> fwstation
echo "END_VERSION:" >> fwstation

# Add station.conf
echo "CONF:" >> fwstation
cat $3 >> fwstation
echo "END_CONF:" >> fwstation

# executable
chmod +x fwstation
```

运行这些脚本后，您可以在终端中运行以下命令来生成固件更新文件，`fwstation`。

```
$ ./addpayload.sh station version.txt station.conf
```

## 为固件更新生成签名
<a name="lorawan-generate-signature-fwupdate"></a>

LoRa Basics Station 软件提供带有 ECDSA 签名的已签名固件更新。要支持已签名的更新，您需要：
+ 必须由 ECDSA 私有秘钥生成且少于 128 字节的签名。
+ 用于签名的私有密钥且必须存储在网关中，其文件名格式应为 `sig-%d.key`。建议使用 `sig-0.key` 作为文件名。
+ 私有密钥上的 32 位 CRC。

签名和 CRC 将传递给 适用于 LoRaWAN 的 AWS IoT Core API。要生成以前的文件，可以使用以下脚本，`gen.sh`。该脚本是根据 GitHub 存储库中的 [ basicstaion](https://github.com/lorabasics/basicstation/blob/master/examples/cups/prep.sh) 示例编写的。

```
*#!/bin/bash

*function ecdsaKey() {
    # Key not password protected for simplicity    
    openssl ecparam -name prime256v1 -genkey | openssl ec -out $1
}

# Generate ECDSA key
ecdsaKey sig-0.prime256v1.pem

# Generate public key
openssl ec -in sig-0.prime256v1.pem -pubout -out sig-0.prime256v1.pub

# Generate signature private key
openssl ec -in sig-0.prime256v1.pub -inform PEM -outform DER -pubin | tail -c 64 > sig-0.key

# Generate signature
openssl dgst -sha512 -sign sig-0.prime256v1.pem $1 > sig-0.signature

# Convert signature to base64
openssl enc -base64 -in sig-0.signature -out sig-0.signature.base64

# Print the crc
crc_res=$(crc32 sig-0.key)printf "The crc for the private key=%d\n" $((16#$crc_res))

# Remove the generated files which won't be needed later
rm -rf sig-0.prime256v1.pem sig-0.signature sig-0.prime256v1.pub
```

脚本生成的私有秘钥应保存到网关中。密钥文件采用二进制格式。

```
./gen_sig.sh fwstation 
read EC key
writing EC key
read EC key
writing EC key
read EC key
writing EC key
The crc for the private key=3434210794

$ cat sig-0.signature.base64 
MEQCIDPY/p2ssgXIPNCOgZr+NzeTLpX+WfBo5tYWbh5pQWN3AiBROen+XlIdMScv
AsfVfU/ZScJCalkVNZh4esyS8mNIgA==

$ ls sig-0.key
sig-0.key

$ scp sig-0.key pi@192.168.1.11:/home/pi/Documents/basicstation/examples/iotwireless
```

## 查看后续步骤
<a name="lorawan-fwupdate-sigkey-next-steps"></a>

现在您已经生成了固件和签名，请转到下一个主题，将固件文件 `fwstation` 上载到 Amazon S3 存储桶。存储桶是将固件更新文件作为对象存储的容器。您可以添加 IAM 角色，该角色将授予 CUPS 服务器读取 S3 存储桶中的固件更新文件的权限。

# 将固件文件上载到 S3 存储桶并添加 IAM 角色
<a name="lorawan-upload-firmware-s3bucket"></a>

您可以使用 Amazon S3 创建*存储桶*，这是一个可以存储固件更新文件的容器。您可以将文件上载到 S3 存储桶，并添加允许 CUPS 服务器从存储桶读取更新文件的 IAM 角色。有关 Amazon S3 的更多信息，请参阅 [Amazon S3 入门](https://docs.aws.amazon.com/AmazonS3/latest/userguide/GetStartedWithS3.html)。

要上载的固件更新文件取决于您使用的网关。如果您遵循类似于 [生成固件更新文件和签名](lorawan-script-fwupdate-sigkey.md) 中所述的流程，则您将上载通过运行脚本所生成的 `fwstation` 文件。

完成本流程需要大约 20 分钟。

**Topics**
+ [创建 Amazon S3 存储桶并上载更新文件](#lorawan-create-s3-bucket)
+ [创建具有读取 S3 存储桶权限的 IAM 角色](#lorawan-s3-iam-permissions)
+ [查看后续步骤](#lorawan-s3iam-next-steps)

## 创建 Amazon S3 存储桶并上载更新文件
<a name="lorawan-create-s3-bucket"></a>

您将使用 AWS 管理控制台 创建 Amazon S3 存储桶，然后将固件更新文件上载到存储桶中。

**创建 S3 存储桶**  
要创建 S3 存储桶，请打开 [Amazon S3 控制台](https://console.aws.amazon.com/s3/home#)。登录（如果尚未登录），然后执行以下步骤：

1. 选择**创建存储桶**。

1. 输入唯一且有意义的名称作为 **Bucket name**（存储桶名称），例如 `iotwirelessfwupdate`。有关存储桶的建议命名约定，请参阅 [https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html)。

1. 确保您选择了AWS 区域来创建 LoRaWAN 网关和设备，并且选中 **Block all public access**（阻止所有公有访问）设置，以便您的存储桶使用默认权限。

1. 为 **Bucket versioning**（存储桶版本控制）选择 **Enable**（启用），这将帮助您将固件更新文件的多个版本保留在同一存储桶中。

1. 确认将 **Server-side encryption**（服务器端加密）设置为 **Disable**（禁用），然后选择 **Create bucket**（创建存储桶）。

**上载固件更新文件**  
现在，您可以在显示在 AWS 管理控制台 的存储桶列表中看到您的存储桶了。选择您的存储桶并完成以下步骤以上载您的文件。

1. 选择您的存储桶，然后选择 **Upload**（上载）。

1. 选择 **Add file**（添加文件），然后上载固件更新文件。如果您按照 [生成固件更新文件和签名](lorawan-script-fwupdate-sigkey.md) 中所述的流程执行，您应上载 `fwstation` 文件，否则请上载网关制造商提供的文件。

1. 确保所有设置都保留为默认值。请确保 **Predefined ACL**（预定义 ACL）设置为 **private**（私有），然后选择 **Upload**（上载）以上载您的文件。

1. 复制您所上载文件的 S3 URI。选择您的存储桶，您将看到上载的文件显示在 **Objects**（对象）列表中。选择您的文件，然后选择 **Copy S3 URI**（复制 S3 URI）。如果您将存储桶命名为类似于前述示例 (`fwstation`)，则 URI 将类似于：`s3://iotwirelessfwupdate/fwstation`。在创建 IAM 角色时，您将使用 S3 URI。

## 创建具有读取 S3 存储桶权限的 IAM 角色
<a name="lorawan-s3-iam-permissions"></a>

现在，您将创建 IAM 角色和策略，该角色和策略将授予 CUPS 从 S3 存储桶读取固件更新文件的权限。

**为您的角色创建 IAM 策略**  
要为 适用于 LoRaWAN 的 AWS IoT Core 目标角色创建 IAM 策略，请打开 [IAM 控制台的策略中心](https://console.aws.amazon.com/iam/home#/policies)，然后完成以下步骤：

1. 选择 **Create policy**（创建策略），然后选择 **JSON** 选项卡。

1. 从编辑器中删除所有内容并粘贴此策略文档。策略提供访问 `iotwireless` 存储桶的权限，固件更新文件 `fwstation` 存储在对象内。

   ```
   {
       "Version": "2012-10-17",
       "Statement": [
           {
               "Sid": "VisualEditor0",
               "Effect": "Allow",
               "Action": [
                   "s3:ListBucketVersions",
                   "s3:ListBucket",
                   "s3:GetObject"
               ],
               "Resource": [
                   "arn:aws:s3:::iotwirelessfwupdate/fwstation",
                   "arn:aws:s3:::iotwirelessfwupdate"
               ]
           }
       ]
   }
   ```

1. 选择 **Review policy**（查看策略），在 **Name**（名称）中，输入此策略的名称（例如，`IoTWirelessFwUpdatePolicy`）。在下一流程中，您将需要使用此名称。

1. 选择**创建策略**。

**使用附加的策略创建 IAM 角色。**  
现在，您将创建一个 IAM 角色并附加以前为访问 S3 存储桶而创建的策略。打开 [IAM 控制台的角色中心](https://console.aws.amazon.com/iam/home#/roles)并完成以下步骤：

1. 选择**创建角色**。

1. 在 **Select type of trusted entity**（选择受信任实体的类型）下，选择 **Another AWS 账户**（另一个亚马逊云科技账户）。

1. 在 **Account ID**（账户 ID），请输入您的AWS 账户 ID，然后选择 **Next: Permission**（下一步：权限）。

1. 在搜索框中，输入您在上一流程中创建的 IAM 策略名称。在搜索结果中检查您之前创建的 IAM 策略（例如 `IoTWirelessFwUpdatePolicy`），然后选择该策略。

1. 依次选择 **Next: Tags**（下一步：标签）和 **Next: Review**（下一步：查看）。

1. 对于 **Role name**（角色名称），输入此角色的名称（例如 `IoTWirelessFwUpdateRole`），然后选择 **Create role**（创建角色）。

**编辑 IAM 角色的信任关系**  
在运行上一步后显示的确认信息中，选择您创建的角色的名称进行编辑。您将编辑角色以添加以下信任关系。

1. 在刚刚创建的角色的 **Summary**（摘要）页面上，选择 **Trust relationship**（信任关系）选项卡，然后选择 **Edit trust relationship**（编辑信任关系）。

1. 在 **Policy Document**（策略文档）中，更改 `Principal` 属性以使其类似于此示例。

   ```
   "Principal": { 
       "Service": "iotwireless.amazonaws.com" 
   },
   ```

   在您更改 `Principal` 属性后，完整的策略文档应该如此示例所示。

   ```
   {
     "Version": "2012-10-17",
     "Statement": [
       {
         "Effect": "Allow",
         "Principal": {
           "Service": "iotwireless.amazonaws.com"
         },
         "Action": "sts:AssumeRole",
         "Condition": {}
       }
     ]
   }
   ```

1. 要保存更改并退出，请选择 **Update Trust Policy**（更新信任策略）。

1. 获取角色的 ARN。选择在“摘要”部分您的 IAM 角色，您将看到 **Role ARN**（角色 ARN），例如 `arn:aws:iam::123456789012:role/IoTWirelessFwUpdateRole`。复制此 **Role ARN**（角色 ARN）。

## 查看后续步骤
<a name="lorawan-s3iam-next-steps"></a>

现在，您已创建 S3 存储桶和允许 CUPS 服务器读取 S3 存储桶的 IAM 角色，请转到下一个主题以安排并运行固件更新。保留您之前复制的 **S3 URI** 和 **Role ARN**（角色 ARN），以便您可以输入这些信息以创建将运行的任务定义，以执行固件更新。

# 使用任务定义安排并运行固件更新
<a name="lorawan-schedule-firmware-update"></a>

您可以使用任务定义包含有关固件更新的详细信息并定义更新。适用于 LoRaWAN 的 AWS IoT Core 将根据与网关关联的以下三个字段中的信息提供固件更新。
+ 

**Station**  
Basics Station 软件的版本和构建时间。要识别此信息，您还可以使用网关运行的 Basics Station 软件生成该信息（例如，`2.0.5(rpi/std) 2021-03-09 03:45:09`）。
+ 

**PackageVersion**  
固件版本，由网关中的文件 `version.txt` 指定。虽然网关中可能不存在此信息，但我们建议您使用此方法定义固件版本（例如，`1.0.0`）。
+ 

**模型**  
网关正在使用的平台或模型（例如，Linux）。

此流程需要 20 分钟时间完成。

**Topics**
+ [获取网关上运行的当前版本](#lorawan-gateway-current-version)
+ [创建无限网关任务定义](#lorawan-create-task-definition)
+ [运行固件更新任务并跟踪进度](#lorawan-run-fwupdate-task)

## 获取网关上运行的当前版本
<a name="lorawan-gateway-current-version"></a>

要确定网关是否有资格进行固件更新，CUPS 服务器将检查所有三个字段：`Station`、`PackageVersion` 和 `Model`，网关在 CUPS 请求期间提供这些信息时，服务器将进行匹配。使用任务定义时，这些字段将作为 `CurrentVersion` 字段的一部分予以存储。

您可以使用 适用于 LoRaWAN 的 AWS IoT Core API 或 AWS CLI 为您的网关获取 `CurrentVersion`。以下命令演示如何使用 CLI 获取此信息。

1. 如果您已经预调配了网关，则可以使用 [get-wireless-gateway](https://docs.aws.amazon.com/cli/latest/reference/iotwireless/get-wireless-gateway.html) 命令获取有关网关的信息。

   ```
   aws iotwireless get-wireless-gateway \ 
       --identifier 5a11b0a85a11b0a8 \ 
           --identifier-type GatewayEui
   ```

   下面显示了此命令的示例输出。

   ```
   {
       "Name": "Raspberry pi",
       "Id": "1352172b-0602-4b40-896f-54da9ed16b57",
       "Description": "Raspberry pi",
       "LoRaWAN": {
           "GatewayEui": "5a11b0a85a11b0a8",
           "RfRegion": "US915"
       },
       "Arn": "arn:aws:iotwireless:us-east-1:231894231068:WirelessGateway/1352172b-0602-4b40-896f-54da9ed16b57"
   }
   ```

1. 借助 `get-wireless-gateway` 命令报告的无线网关 ID，您可以使用 [get-wireless-gateway-firmware-information](https://docs.aws.amazon.com/cli/latest/reference/iotwireless/get-wireless-gateway-firmware-information.html) 命令来获取 `CurrentVersion`。

   ```
   aws iotwireless get-wireless-gateway-firmware-information \
       --id "3039b406-5cc9-4307-925b-9948c63da25b"
   ```

   下面显示了该命令的示例输出，其中包含 `CurrentVersion` 显示的所有三个字段的信息。

   ```
   {
       "LoRaWAN": {
           "CurrentVersion": {
               "PackageVersion": "1.0.0",
               "Model": "rpi",
               "Station": "2.0.5(rpi/std) 2021-03-09 03:45:09"
           }
       }
   }
   ```

## 创建无限网关任务定义
<a name="lorawan-create-task-definition"></a>

在创建任务定义时，我们建议您使用 AutoCreateTasks 参数指定任务自动创建。`AutoCreateTasks` 适用于与前面提到的所有三个参数匹配的任何网关。如果禁用此参数，则必须手动将参数分配给网关。

您可以使用 适用于 LoRaWAN 的 AWS IoT Core API 或 AWS CLI 创建无限网关任务定义。以下命令演示如何使用 CLI 创建任务定义。

1. 创建一个文件，`input.json`，它将包含要传递给 `CreateWirelessGatewayTaskDefinition` API 的信息。在 `input.json` 文件中，提供之前获取的以下信息：
   + 

**UpdateDataSource**  
提供指向对象的链接，该对象中包含您上载到 S3 存储桶的固件更新文件（例如，`s3://iotwirelessfwupdate/fwstation`）。
   + 

**UpdateDataRole**  
提供指向您创建的 IAM 角色的角色 ARN 的链接，该角色提供读取 S3 存储桶的权限（例如，`arn:aws:iam::123456789012:role/IoTWirelessFwUpdateRole`）。
   + 

**SigKeyCRC 和 UpdateSignature**  
此信息可能由您的网关制造商提供，但如果您遵循 [生成固件更新文件和签名](lorawan-script-fwupdate-sigkey.md) 中所述的流程，则您将在生成签名时找到此信息。
   + 

**CurrentVersion**  
提供您之前通过运行 `get-wireless-gateway-firmware-information ` 命令获得的 `CurrentVersion` 输出。

     ```
     cat input.json
     ```

     下面显示的是 `input.json` 文件的内容。

     ```
     {
         "AutoCreateTasks": true,
         "Name": "FirmwareUpdate",
         "Update":
         {
             "UpdateDataSource" : "s3://iotwirelessfwupdate/fwstation",
             "UpdateDataRole" : "arn:aws:iam::123456789012:role/IoTWirelessFwUpdateRole",
             "LoRaWAN" :
             {
                 "SigKeyCrc": 3434210794,
                 "UpdateSignature": "MEQCIDPY/p2ssgXIPNCOgZr+NzeTLpX+WfBo5tYWbh5pQWN3AiBROen+XlIdMScvAsfVfU/ZScJCalkVNZh4esyS8mNIgA==",
                 "CurrentVersion" :
                 {
                 "PackageVersion": "1.0.0",
                 "Model": "rpi",
                 "Station": "2.0.5(rpi/std) 2021-03-09 03:45:09"
                 }
             }
         }
     }
     ```

1. 将 `input.json` 文件传递到 [create-wireless-gateway-task-definition](https://docs.aws.amazon.com/cli/latest/reference/iotwireless/get-wireless-gateway-task-definition.html) 命令以创建任务定义。

   ```
   aws iotwireless create-wireless-gateway-task-definition \ 
       --cli-input-json file://input.json
   ```

   以下显示了命令的输出。

   ```
   {
       "Id": "4ac46ff4-efc5-44fd-9def-e8517077bb12",
       "Arn": "arn:aws:iotwireless:us-east-1:231894231068:WirelessGatewayTaskDefinition/4ac46ff4-efc5-44fd-9def-e8517077bb12"
   }
   ```

## 运行固件更新任务并跟踪进度
<a name="lorawan-run-fwupdate-task"></a>

网关已准备好接收固件更新，一旦打开电源，就会连接到 CUPS 服务器。当 CUPS 服务器在网关版本中找到匹配项时，它会计划固件更新。

任务是正在进行的任务定义。当您通过将 `AutoCreateTasks` 设置为 `True` 指定自动任务创建时，一旦找到匹配的网关，固件更新任务就会立即启动。

您可以使用 `GetWirelessGatewayTask` API 跟踪任务的进度。当您首次运行 [get-wireless-gateway-task](https://docs.aws.amazon.com/cli/latest/reference/iotwireless/get-wireless-gateway-task.html) 命令时，它会将任务状态显示为 `IN_PROGRESS`。

```
aws iotwireless get-wireless-gateway-task \ 
    --id 1352172b-0602-4b40-896f-54da9ed16b57
```

以下显示了命令的输出。

```
{
    "WirelessGatewayId": "1352172b-0602-4b40-896f-54da9ed16b57",
    "WirelessGatewayTaskDefinitionId": "ec11f9e7-b037-4fcc-aa60-a43b839f5de3",
    "LastUplinkReceivedAt": "2021-03-12T09:56:12.047Z",
    "TaskCreatedAt": "2021-03-12T09:56:12.047Z",
    "Status": "IN_PROGRESS"
}
```

下次运行命令时，如果固件更新生效，它将显示更新后的字段，`Package`、`Version` 和 `Model`，而任务状态将变为 `COMPLETED`。

```
aws iotwireless get-wireless-gateway-task \ 
    --id 1352172b-0602-4b40-896f-54da9ed16b57
```

以下显示了命令的输出。

```
{
    "WirelessGatewayId": "1352172b-0602-4b40-896f-54da9ed16b57",
    "WirelessGatewayTaskDefinitionId": "ec11f9e7-b037-4fcc-aa60-a43b839f5de3",
    "LastUplinkReceivedAt": "2021-03-12T09:56:12.047Z",
    "TaskCreatedAt": "2021-03-12T09:56:12.047Z",
    "Status": "COMPLETED"
}
```

在此示例中，我们向您展示了使用基于 Raspberry Pi 的 RAKWireless 网关的固件更新。固件更新脚本停止 BasicStation 的运行，以存储更新后的 `Package`、`Version` 和 `Model` 字段，因此必须重新启动 BasicStation。

```
2021-03-12 09:56:13.108 [CUP:INFO] CUPS provided update.bin
2021-03-12 09:56:13.108 [CUP:INFO] CUPS provided signature len=70 keycrc=37316C36
2021-03-12 09:56:13.148 [CUP:INFO] ECDSA key#0 -> VERIFIED
2021-03-12 09:56:13.148 [CUP:INFO] Running update.bin as background process
2021-03-12 09:56:13.149 [SYS:VERB] /tmp/update.bin: Forked, waiting...
2021-03-12 09:56:13.151 [SYS:INFO] Process /tmp/update.bin (pid=6873) completed
2021-03-12 09:56:13.152 [CUP:INFO] Interaction with CUPS done - next regular check in 10s
```

如果固件更新失败，您将从 CUPS server 看到 `FIRST_RETRY` 的状态，而网关会发送相同的请求。如果 `SECOND_RETRY` 后 CUPS 服务器无法连接到网关，它将显示 `FAILED` 的状态。

在上一任务处于 `COMPLETED` 或者 `FAILED` 之后，使用 [delete-wireless-gateway-task](https://docs.aws.amazon.com/cli/latest/reference/iotwireless/delete-wireless-gateway-task.html) 命令删除旧任务，然后再启动一个新任务。

```
aws iotwireless delete-wireless-gateway-task \ 
    --id 1352172b-0602-4b40-896f-54da9ed16b57
```