使用 Amazon CLI 创建 OTA 更新 - FreeRTOS
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

使用 Amazon CLI 创建 OTA 更新

在使用 Amazon CLI 创建 OTA 更新时,您将:

  1. 对固件映像进行数字签名。

  2. 创建经数字签名的固件映像的流。

  3. 启动 OTA 更新作业。

对固件更新进行数字签名

在使用 Amazon CLI 执行 OTA 更新时,您可以使用 Code Signing for Amazon IoT,也可以自行对固件更新进行签名。有关 Code Signing for Amazon IoT 支持的加密签名和哈希算法的列表,请参阅 SigningConfigurationOverrides。如果要使用适用于 Amazon IoT 的代码签名不支持的加密算法,您必须先对固件二进制文件进行签名,然后再将其上传到 Amazon S3 中。

使用 Code Signing for Amazon IoT 对固件映像进行签名

要使用适用于 Amazon IoT 的代码签名对固件映像进行签名,您可以使用 Amazon 开发工具包或命令行工具之一。有关 Code Signing for Amazon IoT 的更多信息,请参阅 Code Signing for Amazon IoT

安装并配置代码签名工具后,将未签名的固件映像复制到 Amazon S3 存储桶中,并使用以下 Amazon CLI 命令启动代码签名作业。put-signing-profile 命令用于创建一个可重复使用的代码签名配置文件。start-signing-job 命令用于启动签名作业。

aws signer put-signing-profile \ --profile-name your_profile_name \ --signing-material certificateArn=arn:aws:acm::your-region:your-aws-account-id:certificate/your-certificate-id \ --platform your-hardware-platform \ --signing-parameters certname=your_certificate_path_on_device
aws signer start-signing-job \ --source 's3={bucketName=your_s3_bucket,key=your_s3_object_key,version=your_s3_object_version_id}' \ --destination 's3={bucketName=your_destination_bucket}' \ --profile-name your_profile_name
注意

your-source-bucket-nameyour-destination-bucket-name 可以是同一 Amazon S3 存储桶。

以下是 put-signing-profilestart-signing-job 命令的参数:

source

指定未签名的固件在 S3 存储桶中的位置。

  • bucketName:S3 存储桶的名称。

  • key:固件在 S3 存储桶中的密钥(文件名)。

  • version:固件在 S3 存储桶中的 S3 版本。该版本不同于固件版本。要找到此版本,请转至 Amazon S3 控制台,选择存储桶,然后在页面顶部的版本旁选择显示

destination

S3 存储桶中已签名的固件将被复制到的设备上的目的地。该参数的格式与 source 参数是一样的。

signing-material

代码签名证书的 ARN。在将证书导入 ACM 中时,生成此 ARN。

signing-parameters

签名的键值对映射。其中可以包括签名过程中要使用的任何信息。

注意

在创建代码签名配置文件以使用 Code Signing for Amazon IoT 对 OTA 更新进行签名时,需要使用该参数。

platform

要将 OTA 更新分配到的硬件平台的 platformId

要返回可用的平台及其 platformId 值的列表,请使用 aws signer list-signing-platforms 命令。

签名作业启动,将已签名的固件映像写入目标 Amazon S3 存储桶。已签名的固件映像的文件名是 GUID。创建流时将需要此文件名。浏览至 Amazon S3 控制台并选择存储桶即可找到此文件名。如果没有看到带有 GUID 文件名的文件,可刷新浏览器。

此命令将显示作业 ARN 和作业 ID。稍后会需要这些值。有关 Code Signing for Amazon IoT 的更多信息,请参阅 Code Signing for Amazon IoT

手动签署固件映像

对固件映像进行数字签名,并将已签名的固件映像上传到 Amazon S3 存储桶。

创建固件更新流

流是设备可以使用的数据的抽象接口。流可以隐藏访问存储在不同位置或各种基于云的服务中的数据的复杂性。利用 OTA Update Manager 服务,您可以使用存储在 Amazon S3 中的各个位置的多个数据段来执行 OTA 更新。

在创建 Amazon IoT OTA 更新时,还可以创建包含已签名的固件更新的流。创建一个 JSON 文件 (stream.json),该文件标识已签名的固件映像。JSON 文件应包含以下内容。

[ { "fileId":"your_file_id", "s3Location":{ "bucket":"your_bucket_name", "key":"your_s3_object_key" } } ]

以下是 JSON 文件中的属性:

fileId

介于 0–255 之间的任意整数,用于标识固件映像。

s3Location

固件到流的存储桶和密钥。

bucket

存储未签名的固件映像的 Amazon S3 存储桶。

key

已签名的固件映像在 Amazon S3 存储桶中的文件名。可以在 Amazon S3 控制台中,通过查看存储桶的内容来找到该值。

如果使用的是 Code Signing for Amazon IoT,则文件名是 Code Signing for Amazon IoT 生成的 GUID。

使用 create-stream Amazon CLI 命令创建流。

aws iot create-stream \ --stream-id your_stream_id \ --description your_description \ --files file://stream.json \ --role-arn your_role_arn

以下是 create-stream Amazon CLI CLI 命令的参数:

stream-id

任意字符串,用于标识流。

description

流的描述(可选)。

files

JSON 文件的一个或多个引用,包含有关固件映像到流的数据。JSON 文件必须包含以下属性:

fileId

任意文件 ID。

s3Location

存储已签名的固件映像的存储桶名称,以及已签名的固件映像的密钥(文件名)。

bucket

存储已签名的固件映像的 Amazon S3 存储桶。

key

已签名的固件映像的密钥(文件名)。

如果使用 Code Signing for Amazon IoT,则此密钥为 GUID。

下面是一个 stream.json 示例文件。

[ { "fileId":123, "s3Location": { "bucket":"codesign-ota-bucket", "key":"48c67f3c-63bb-4f92-a98a-4ee0fbc2bef6" } } ]
role-arn

OTA 服务角色还授予对存储固件映像的 Amazon S3 存储桶的访问权限。

要查找已签名的固件映像的 Amazon S3 对象密钥,可使用 aws signer describe-signing-job --job-id my-job-id 命令,其中 my-job-idcreate-signing-job Amazon CLI CLI 命令所显示的作业 ID。describe-signing-job 命令的输出中包含已签名的固件映像的密钥。

... text deleted for brevity ... "signedObject": { "s3": { "bucketName": "ota-bucket", "key": "7309da2c-9111-48ac-8ee4-5a4262af4429" } } ... text deleted for brevity ...

创建 OTA 更新

可以使用 create-ota-update Amazon CLI 命令创建一个 OTA 更新作业。

注意

请勿在 OTA 更新作业 ID 中使用任何个人身份信息 (PII)。个人身份信息示例包括:

  • 名称。

  • IP 地址。

  • 电子邮件地址。

  • 位置。

  • 银行详细信息。

  • 医疗信息。

aws iot create-ota-update \ --ota-update-id value \ [--description value] \ --targets value \ [--protocols value] \ [--target-selection value] \ [--aws-job-executions-rollout-config value] \ [--aws-job-presigned-url-config value] \ [--aws-job-abort-config value] \ [--aws-job-timeout-config value] \ --files value \ --role-arn value \ [--additional-parameters value] \ [--tags value] \ [--cli-input-json value] \ [--generate-cli-skeleton]

cli-input-json 格式

{ "otaUpdateId": "string", "description": "string", "targets": [ "string" ], "protocols": [ "string" ], "targetSelection": "string", "awsJobExecutionsRolloutConfig": { "maximumPerMinute": "integer", "exponentialRate": { "baseRatePerMinute": "integer", "incrementFactor": "double", "rateIncreaseCriteria": { "numberOfNotifiedThings": "integer", "numberOfSucceededThings": "integer" } } }, "awsJobPresignedUrlConfig": { "expiresInSec": "long" }, "awsJobAbortConfig": { "abortCriteriaList": [ { "failureType": "string", "action": "string", "thresholdPercentage": "double", "minNumberOfExecutedThings": "integer" } ] }, "awsJobTimeoutConfig": { "inProgressTimeoutInMinutes": "long" }, "files": [ { "fileName": "string", "fileType": "integer", "fileVersion": "string", "fileLocation": { "stream": { "streamId": "string", "fileId": "integer" }, "s3Location": { "bucket": "string", "key": "string", "version": "string" } }, "codeSigning": { "awsSignerJobId": "string", "startSigningJobParameter": { "signingProfileParameter": { "certificateArn": "string", "platform": "string", "certificatePathOnDevice": "string" }, "signingProfileName": "string", "destination": { "s3Destination": { "bucket": "string", "prefix": "string" } } }, "customCodeSigning": { "signature": { "inlineDocument": "blob" }, "certificateChain": { "certificateName": "string", "inlineDocument": "string" }, "hashAlgorithm": "string", "signatureAlgorithm": "string" } }, "attributes": { "string": "string" } } ], "roleArn": "string", "additionalParameters": { "string": "string" }, "tags": [ { "Key": "string", "Value": "string" } ] }
cli-input-json 字段

名称

类型

描述

otaUpdateId

字符串

(max:128 min:1)

要创建的 OTA 更新的 ID。

description

字符串

(max:2028)

OTA 更新的描述。

targets

列表

接收 OTA 更新的目标设备。

protocols

列表

用于传输 OTA 更新映像的协议。有效值为 [HTTP]、[MQTT]、[HTTP, MQTT]。当同时指定 HTTP 和 MQTT 时,目标设备可以选择协议。

targetSelection

字符串

指定更新将继续运行 (CONTINUOUS),还是在指定作为目标的所有事物完成更新之后完成 (SNAPSHOT)。如果继续运行,则在检测到目标中出现更改时,更新也会在事物上运行。例如,当某个事物添加到目标组时会在该事物上运行更新,即使是组中原有的全部事物已经完成了更新。有效值:CONTINUOUS | SNAPSHOT。

枚举:CONTINUOUS | SNAPSHOT

awsJobExecutionsRolloutConfig

配置 OTA 更新的推广。

maximumPerMinute

integer

(max:1000 min:1)

每分钟启动的最大 OTA 更新任务执行次数。

exponentialRate

任务推出的增速。此参数允许您定义作业推出的指数增长速率。

baseRatePerMinute

integer

(max:1000 min:1)

在作业推出开始时,每分钟接收待处理作业通知的事物的最小数量。这是作业推出的初始速率。

rateIncreaseCriteria

启动任务推出速率提高的条件。

Amazon IoT 最多支持小数点后一位(例如,支持 1.5,但不支持 1.55)。

numberOfNotifiedThings

integer

(min:1)

当通知此事物数后,推出速率将提高。

numberOfSucceededThings

integer

(min:1)

当此事物数在作业执行中成功应用后,推出速率将提高。

awsJobPresignedUrlConfig

预签名 URL 的配置信息。

expiresInSec

long

预签名 URL 的有效时间长度 (以秒为单位)。有效值为 60 – 3600,默认值为 1800 秒。收到作业文档请求时,将生成预签名 URL。

awsJobAbortConfig

确定作业停止发生时间和方式的条件。

abortCriteriaList

列表

确定何时以及如何停止作业的条件列表。

failureType

字符串

可以启动作业停止的作业执行失败类型。

枚举:FAILED | REJECTED | TIMED_OUT | ALL

action

字符串

用于启动作业停止的作业操作类型。

枚举:CANCEL

minNumberOfExecutedThings

integer

(min:1)

作业可以停止之前必须接收作业执行通知的事物的最小数量。

awsJobTimeoutConfig

指定每个设备完成其任务执行所具有的时间。计时器在任务执行状态设置为 IN_PROGRESS 时启动。如果任务执行状态未在时间到期之前设置为其他最终状态,它会自动设置为 TIMED_OUT

inProgressTimeoutInMinutes

long

指定此设备完成该任务执行所具有的时间,以分钟为单位。超时间隔可以为 1 分钟到 7 天(1 到 10080 分钟)之间的任意长度。进行中计时器无法更新,将应用到该任务的全部任务执行。只要任务执行保持在 IN_PROGRESS 状态的时间长度超过了此间隔,任务执行将失败,并切换为最终 TIMED_OUT 状态。

files

列表

应由 OTA 更新流式处理的文件。

fileName

字符串

文件的名称。

fileType

integer

范围 – 最大值:255,最小值:0

您可以在作业文档中包含一个整数值,以允许您的设备识别从云接收的文件类型。

fileVersion

字符串

文件版本。

fileLocation

更新后固件的位置。

stream

包含 OTA 的流。

streamId

字符串

(max:128 min:1)

流 ID。

fileId

integer

(max:255 min:0)

与流关联的文件的 ID。

s3Location

更新后固件在 S3 中的位置。

bucket

字符串

(min:1)

S3 存储桶。

key

字符串

(min:1)

S3 键。

version

字符串

S3 存储桶版本。

codeSigning

文件的代码签名方法。

awsSignerJobId

字符串

已创建用于对文件签名的 AWSSignerJob 的 ID。

startSigningJobParameter

描述代码签名任务。

signingProfileParameter

描述代码签名配置文件。

certificateArn

字符串

证书 ARN。

platform

字符串

您设备的硬件平台。

certificatePathOnDevice

字符串

代码签名证书在设备上的位置。

signingProfileName

字符串

代码签名配置文件名称。

destination

编写代码签名文件的位置。

s3Destination

描述更新后固件在 S3 中的位置。

bucket

字符串

(min:1)

包含更新后固件的 S3 存储桶。

prefix

字符串

S3 前缀。

customCodeSigning

用于对文件执行代码签名的自定义方法。

signature

文件的签名。

inlineDocument

blob

代码签署签名的 base64 编码二进制表示形式。

certificateChain

证书链。

certificateName

字符串

证书的名称。

inlineDocument

字符串

代码签名证书链的 base64 编码二进制表示形式。

hashAlgorithm

字符串

用于对文件进行代码签名的哈希算法。

signatureAlgorithm

字符串

用于对文件进行代码签名的签名算法。

attributes

映射

名称/属性对的列表。

roleArn

字符串

(max:2048 min:20)

授予 Amazon IoT 访问 Amazon S3、Amazon IoT 作业和 Amazon 代码签名资源的权限以创建 OTA 更新任务的 IAM 角色。

additionalParameters

映射

其他 OTA 更新参数 (名称/值对) 的列表。

tags

列表

可用于管理更新的元数据。

Key

字符串

(max:128 min:1)

标签的键。

Value

字符串

(max:256 min:1)

标签的值。

输出

{ "otaUpdateId": "string", "awsIotJobId": "string", "otaUpdateArn": "string", "awsIotJobArn": "string", "otaUpdateStatus": "string" }
Amazon CLI 输出字段

名称

类型

描述

otaUpdateId

字符串

(max:128 min:1)

OTA 更新 ID。

awsIotJobId

字符串

与 OTA 更新关联的 Amazon IoT 作业 ID。

otaUpdateArn

字符串

OTA 更新 ARN。

awsIotJobArn

字符串

与 OTA 更新关联的 Amazon IoT 作业 ARN。

otaUpdateStatus

字符串

OTA 更新状态。

枚举:CREATE_PENDING | CREATE_IN_PROGRESS | CREATE_COMPLETE | CREATE_FAILED

以下示例说明了如何将 JSON 文件传递到 create-ota-update 命令,该命令使用 Code Signing for Amazon IoT。

[ { "fileName": "firmware.bin", "fileType": 1, "fileLocation": { "stream": { "streamId": "004", "fileId":123 } }, "codeSigning": { "awsSignerJobId": "48c67f3c-63bb-4f92-a98a-4ee0fbc2bef6" } } ]

以下示例说明了如何将 JSON 文件传递到 create-ota-update Amazon CLI 命令,该命令使用内联文件提供自定义代码签名材料。

[ { "fileName": "firmware.bin", "fileType": 1, "fileLocation": { "stream": { "streamId": "004", "fileId": 123 } }, "codeSigning": { "customCodeSigning":{ "signature":{ "inlineDocument":"your_signature" }, "certificateChain": { "certificateName": "your_certificate_name", "inlineDocument":"your_certificate_chain" }, "hashAlgorithm":"your_hash_algorithm", "signatureAlgorithm":"your_signature_algorithm" } } } ]

以下示例说明了如何将 JSON 文件传递到 create-ota-update Amazon CLI 命令,该命令允许 FreeRTOS OTA 启动代码签名作业并创建代码签名配置文件和流。

[ { "fileName": "your_firmware_path_on_device", "fileType": 1, "fileVersion": "1", "fileLocation": { "s3Location": { "bucket": "your_bucket_name", "key": "your_object_key", "version": "your_S3_object_version" } }, "codeSigning":{ "startSigningJobParameter":{ "signingProfileName": "myTestProfile", "signingProfileParameter": { "certificateArn": "your_certificate_arn", "platform": "your_platform_id", "certificatePathOnDevice": "certificate_path" }, "destination": { "s3Destination": { "bucket": "your_destination_bucket" } } } } } ]

以下示例说明了如何将 JSON 文件传递到 create-ota-update Amazon CLI 命令,该命令创建一个 OTA 更新以使用现有配置文件启动代码签名作业并使用指定的流。

[ { "fileName": "your_firmware_path_on_device", "fileType": 1, "fileVersion": "1", "fileLocation": { "s3Location": { "bucket": "your_s3_bucket_name", "key": "your_object_key", "version": "your_S3_object_version" } }, "codeSigning":{ "startSigningJobParameter":{ "signingProfileName": "your_unique_profile_name", "destination": { "s3Destination": { "bucket": "your_destination_bucket" } } } } } ]

以下示例说明了如何将 JSON 文件传递到 create-ota-update Amazon CLI 命令,该命令允许 FreeRTOS OTA 使用现有代码签名作业 ID 创建一个流。

[ { "fileName": "your_firmware_path_on_device", "fileType": 1, "fileVersion": "1", "codeSigning":{ "awsSignerJobId": "your_signer_job_id" } } ]

以下示例说明了如何将 JSON 文件传递到 create-ota-update Amazon CLI 命令,该命令创建一个 OTA 更新。该更新根据指定的 S3 对象创建一个流,并使用自定义代码签名。

[ { "fileName": "your_firmware_path_on_device", "fileType": 1, "fileVersion": "1", "fileLocation": { "s3Location": { "bucket": "your_bucket_name", "key": "your_object_key", "version": "your_S3_object_version" } }, "codeSigning":{ "customCodeSigning": { "signature":{ "inlineDocument":"your_signature" }, "certificateChain": { "inlineDocument":"your_certificate_chain", "certificateName": "your_certificate_path_on_device" }, "hashAlgorithm":"your_hash_algorithm", "signatureAlgorithm":"your_sig_algorithm" } } } ]

列出 OTA 更新

您可以使用 list-ota-updates Amazon CLI 命令获取所有 OTA 更新的列表。

aws iot list-ota-updates

list-ota-updates 命令输出如下所示。

{ "otaUpdates": [ { "otaUpdateId": "my_ota_update2", "otaUpdateArn": "arn:aws:iot:us-west-2:123456789012:otaupdate/my_ota_update2", "creationDate": 1522778769.042 }, { "otaUpdateId": "my_ota_update1", "otaUpdateArn": "arn:aws:iot:us-west-2:123456789012:otaupdate/my_ota_update1", "creationDate": 1522775938.956 }, { "otaUpdateId": "my_ota_update", "otaUpdateArn": "arn:aws:iot:us-west-2:123456789012:otaupdate/my_ota_update", "creationDate": 1522775151.031 } ] }

获取有关 OTA 更新的信息

您可以使用 get-ota-update Amazon CLI 命令获取 OTA 更新的创建或删除状态。

aws iot get-ota-update --ota-update-id your-ota-update-id

get-ota-update 命令的输出如下所示。

{ "otaUpdateInfo": { "otaUpdateId": "ota-update-001", "otaUpdateArn": "arn:aws:iot:region:123456789012:otaupdate/ota-update-001", "creationDate": 1575414146.286, "lastModifiedDate": 1575414149.091, "targets": [ "arn:aws:iot:region:123456789012:thing/myDevice" ], "protocols": [ "HTTP" ], "awsJobExecutionsRolloutConfig": { "maximumPerMinute": 0 }, "awsJobPresignedUrlConfig": { "expiresInSec": 1800 }, "targetSelection": "SNAPSHOT", "otaUpdateFiles": [ { "fileName": "my_firmware.bin", "fileType": 1, "fileLocation": { "s3Location": { "bucket": "my-bucket", "key": "my_firmware.bin", "version": "AvP3bfJC9gyqnwoxPHuTqM5GWENt4iii" } }, "codeSigning": { "awsSignerJobId": "b7a55a54-fae5-4d3a-b589-97ed103737c2", "startSigningJobParameter": { "signingProfileParameter": {}, "signingProfileName": "my-profile-name", "destination": { "s3Destination": { "bucket": "some-ota-bucket", "prefix": "SignedImages/" } } }, "customCodeSigning": {} } } ], "otaUpdateStatus": "CREATE_COMPLETE", "awsIotJobId": "AFR_OTA-ota-update-001", "awsIotJobArn": "arn:aws:iot:region:123456789012:job/AFR_OTA-ota-update-001" } }

otaUpdateStatus 返回的值包括:

CREATE_PENDING

OTA 更新的创建正在等待处理。

CREATE_IN_PROGRESS

正在创建 OTA 更新。

CREATE_COMPLETE

OTA 更新已创建。

CREATE_FAILED

OTA 更新的创建失败。

DELETE_IN_PROGRESS

正在删除 OTA 更新。

DELETE_FAILED

OTA 更新的删除失败。

注意

要在创建 OTA 更新后获取其执行状态,您需要使用 describe-job-execution 命令。有关更多信息,请参阅描述作业执行

删除与 OTA 相关的数据

目前还不能使用 Amazon IoT 控制台来删除流或 OTA 更新。可以使用 Amazon CLI 来删除流、OTA 更新以及在 OTA 更新过程中创建的 Amazon IoT 作业。

删除 OTA 流

在创建使用 MQTT 的 OTA 更新时,可以使用命令行或 Amazon IoT 控制台创建流,以将固件分割为多个数据块,以便能通过 MQTT 进行发送。您可以使用 delete-stream Amazon CLI 命令删除该流,如以下示例中所示。

aws iot delete-stream --stream-id your_stream_id

删除 OTA 更新

在创建 OTA 更新时,将创建以下各项:

  • OTA 更新作业数据库中的一个条目。

  • 执行更新的 Amazon IoT 作业。

  • 每个要更新的设备的 Amazon IoT 作业执行。

delete-ota-update 命令仅删除 OTA 更新作业数据库中的条目。必须使用 delete-job 命令来删除 Amazon IoT 作业。

可以使用 delete-ota-update 命令删除一个 OTA 更新。

aws iot delete-ota-update --ota-update-id your_ota_update_id
ota-update-id

要删除的 OTA 更新的 ID。

delete-stream

删除与 OTA 更新关联的流。

force-delete-aws-job

删除与 OTA 更新关联的 Amazon IoT 作业。如果未设置此标记且任务处于 In_Progress 状态,则作业不会被删除。

删除为 OTA 更新创建的 IoT 作业

在创建 OTA 更新时,FreeRTOS 会创建 Amazon IoT 作业。也会为处理作业的每个设备创建作业执行。您可以使用 delete-job Amazon CLI 命令删除一个作业及其关联的作业执行。

aws iot delete-job --job-id your-job-id --no-force

no-force 参数指定只能删除处于结束状态(COMPLETED 或 CANCELLED)的作业。可以通过传递 force 参数来删除处于非结束状态的作业。有关更多信息,请参阅 DeleteJob API

注意

如果删除状态为 IN_PROGRESS 的作业,则设备上处于 IN_PROGRESS 状态的任何作业执行都将中断,并可能导致设备处在不确定的状态。请确保已删除的正在执行作业的每个设备都可以恢复到已知状态。

删除作业可能需要几分钟时间,具体取决于为作业创建的作业执行数量以及其他因素。在删除作业的过程中,其状态为 DELETION_IN_PROGRESS。试图删除或取消已处于 DELETION_IN_PROGRESS 状态的作业将导致错误。

可以使用 delete-job-execution 删除作业执行。如果设备数量太少无法处理作业,您可能希望删除作业执行。这会删除单个设备的作业执行,如以下示例中所示。

aws iot delete-job-execution --job-id your-job-id --thing-name your-thing-name --execution-number your-job-execution-number --no-force

如同 delete-job Amazon CLI 命令一样,可以将 --force 参数传递到 delete-job-execution 以强制删除作业执行。有关更多信息,请参阅 DeleteJobExecution API

注意

如果删除状态为 IN_PROGRESS 的作业执行,则设备上处于 IN_PROGRESS 状态的任何作业执行都将中断,并可能导致设备处在不确定的状态。请确保已删除的正在执行作业的每个设备都可以恢复到已知状态。

有关使用 OTA 更新应用程序的更多信息,请参阅无线更新演示应用程序