将命令发送到设备

您可以使用 Cloud IoT Core 向设备发送命令。命令是发送到 Cloud IoT Core 并已订阅命令主题的设备的一次性一次性指令。

与设备配置相比,命令的执行速度更快、发送频率更高,并且不受其他 Cloud IoT Core 功能的影响。在命令和配置之间进行选择时,请考虑您是需要随时间变化/保留配置(配置),还是更希望使用速度和/或时间指令(命令)。

在以下情况下,命令非常有用:

  • 在特定时间向多种设备快速发送消息
  • 在特定时间向多台设备发送大量消息
  • 发送有时限的指令,这些指令应到期
  • 发送增量设备设置

命令具有以下特征:

  • 直接发送至订阅的已连接设备
  • 未保留在 Cloud IoT Core 中
  • 对于在发送命令时未订阅和连接的设备,会被丢弃
  • 不是唯一的(可能会发送重复内容,但不太可能)
  • 未按特定顺序发送(但大致按发送顺序发送)
  • 任何格式(数据 blob)

目前,Cloud IoT Core 仅支持通过 MQTT(而非 HTTP)运行命令。

与配置相比较的命令

Cloud IoT Core 还支持设备配置。配置比命令更一致且更持久。配置将保留在 Cloud IoT Core 中。使用 MQTT 时,最新的配置最终将传送到所有订阅的设备,甚至包括后来订阅的设备。

下表可帮助您决定是使用命令还是使用配置:

配置 命令
系统会重试最新配置,直到传送完毕 (MQTT) 针对 QoS 1 进行重试,但不保证传送
持续,因此如果设备稍后连接 (MQTT) 或轮询 (HTTP),系统仍会提供最新的配置 非永久性;仅传送到发送命令时连接的设备
每个新版本都会取代之前的版本 命令之间没有关系或顺序
告知设备充当什么角色;对应于 Cloud IoT Core 中的状态 告知设备在特定时间执行什么操作
延迟时间较长 延迟时间较短
通常较小(不超过 64 KB) 最多 256 KB
用户定义的任意 blob 用户定义的任意 blob
每台设备每秒 1 次更新 每个项目每秒 1000 次(可配置)
每个注册表每秒 2500 次

由于命令不会持久保留在 Cloud IoT Core 中,也不会无限期地重试,因此您不应期望设备遥测或状态数据反映特定的命令。设备可能尚未接收该命令,也可能已收到后续命令或配置。命令是暂时的,不属于长期设备数据的一部分。

如需管理命令排序和重复项,请使用设备逻辑或客户端应用。

发送命令

要向设备发送命令,请使用 Google Cloud Console、gcloud 或 Cloud IoT Core API。

控制台

如需向设备发送命令,请执行以下操作:

  1. 转到 Google Cloud Console 中的注册表页面。

    转到注册表页面

  2. 点击设备的注册表的 ID。
  3. 在左侧的注册表菜单中,点击设备
  4. 点击要向其发送命令的设备的 ID。
  5. 点击页面顶部的发送命令
  6. 选择命令的格式:

    • 文本
    • Base64
  7. 命令数据字段中,输入相应命令。

  8. 在可选的 Subfolder 字段中,输入此命令的子文件夹的名称。已订阅通配符主题的设备会收到发送到子文件夹的命令。

  9. 点击发送命令

gcloud

如需将命令发送到设备,请运行 gcloud iot devices commands send 命令:

gcloud iot devices commands send \
    { --command-file=COMMAND_FILE | --command-data=COMMAND_DATA } \
    --region=REGION  \
    --registry=REGISTRY_ID \
    --device=DEVICE_ID \
    [--subfolder=SUBFOLDER]\

如果命令数据包含特殊字符,请使用 --command-file 而不是 --command-data

API

使用 SendCommandToDevice 方法发送命令。

C#

public static object SendCommand(string deviceId, string projectId,
    string cloudRegion, string registryName, string data)
{
    var cloudIot = CreateAuthorizedClient();

    var devicePath = String.Format("projects/{0}/locations/{1}/registries/{2}/devices/{3}",
        projectId, cloudRegion, registryName, deviceId);
    // Data sent through the wire has to be base64 encoded.
    SendCommandToDeviceRequest req = new SendCommandToDeviceRequest()
    {
        BinaryData = Convert.ToBase64String(Encoding.UTF8.GetBytes(data))
    };

    Console.WriteLine("Sending command to {0}\n", devicePath);

    var res =
        cloudIot.Projects.Locations.Registries.Devices.SendCommandToDevice(req, devicePath).Execute();

    Console.WriteLine("Command response: " + res.ToString());
    return 0;
}

Go


// sendCommand sends a command to a device listening for commands.
func sendCommand(w io.Writer, projectID string, region string, registryID string, deviceID string, sendData string) (*cloudiot.SendCommandToDeviceResponse, error) {
	// Authorize the client using Application Default Credentials.
	// See https://g.co/dv/identity/protocols/application-default-credentials
	ctx := context.Background()
	httpClient, err := google.DefaultClient(ctx, cloudiot.CloudPlatformScope)
	if err != nil {
		return nil, err
	}
	client, err := cloudiot.New(httpClient)
	if err != nil {
		return nil, err
	}

	req := cloudiot.SendCommandToDeviceRequest{
		BinaryData: b64.StdEncoding.EncodeToString([]byte(sendData)),
	}

	name := fmt.Sprintf("projects/%s/locations/%s/registries/%s/devices/%s", projectID, region, registryID, deviceID)

	response, err := client.Projects.Locations.Registries.Devices.SendCommandToDevice(name, &req).Do()
	if err != nil {
		return nil, err
	}

	fmt.Fprintln(w, "Sent command to device")

	return response, nil
}

Java

protected static void sendCommand(
    String deviceId, String projectId, String cloudRegion, String registryName, String data)
    throws GeneralSecurityException, IOException {
  GoogleCredentials credential =
      GoogleCredentials.getApplicationDefault().createScoped(CloudIotScopes.all());
  JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
  HttpRequestInitializer init = new HttpCredentialsAdapter(credential);
  final CloudIot service =
      new CloudIot.Builder(GoogleNetHttpTransport.newTrustedTransport(), jsonFactory, init)
          .setApplicationName(APP_NAME)
          .build();

  final String devicePath =
      String.format(
          "projects/%s/locations/%s/registries/%s/devices/%s",
          projectId, cloudRegion, registryName, deviceId);

  SendCommandToDeviceRequest req = new SendCommandToDeviceRequest();

  // Data sent through the wire has to be base64 encoded.
  Base64.Encoder encoder = Base64.getEncoder();
  String encPayload = encoder.encodeToString(data.getBytes(StandardCharsets.UTF_8.name()));
  req.setBinaryData(encPayload);
  System.out.printf("Sending command to %s%n", devicePath);

  service
      .projects()
      .locations()
      .registries()
      .devices()
      .sendCommandToDevice(devicePath, req)
      .execute();

  System.out.println("Command response: sent");
}

Node.js

// const cloudRegion = 'us-central1';
// const deviceId = 'my-device';
// const commandMessage = 'message for device';
// const projectId = 'adjective-noun-123';
// const registryId = 'my-registry';
const iot = require('@google-cloud/iot');
const iotClient = new iot.v1.DeviceManagerClient({
  // optional auth parameters.
});

async function sendCommand() {
  // Construct request
  const formattedName = iotClient.devicePath(
    projectId,
    cloudRegion,
    registryId,
    deviceId
  );

  const binaryData = Buffer.from(commandMessage);

  const request = {
    name: formattedName,
    binaryData: binaryData,
  };

  const [response] = await iotClient.sendCommandToDevice(request);
  console.log('Sent command: ', response);
}

sendCommand();

PHP

use Google\Cloud\Iot\V1\DeviceManagerClient;

/**
 * Sends a command to a device.
 *
 * @param string $registryId IOT Device Registry ID
 * @param string $deviceId IOT Device ID
 * @param string $command The command sent to a device
 * @param string $projectId Google Cloud project ID
 * @param string $location (Optional) Google Cloud region
 */
function send_command_to_device(
    $registryId,
    $deviceId,
    $command,
    $projectId,
    $location = 'us-central1'
) {
    print('Sending command to device' . PHP_EOL);

    // Instantiate a client.
    $deviceManager = new DeviceManagerClient();
    $deviceName = $deviceManager->deviceName($projectId, $location, $registryId, $deviceId);

    // Response empty on success
    $deviceManager->sendCommandToDevice($deviceName, $command);

    printf('Command sent' . PHP_EOL);
}

Python

print("Sending command to device")
client = iot_v1.DeviceManagerClient()
device_path = client.device_path(project_id, cloud_region, registry_id, device_id)

# command = 'Hello IoT Core!'
data = command.encode("utf-8")

return client.send_command_to_device(
    request={"name": device_path, "binary_data": data}
)

Ruby

# project_id  = "Your Google Cloud project ID"
# location_id = "The Cloud region the registry is located in"
# registry_id = "The registry containing the device to send commands to"
# device_id   = "The identifier of the device to send commands to"
# data        = "The command, e.g. {move: forward} to send to the device"

require "google/apis/cloudiot_v1"

# Initialize the client and authenticate with the specified scope
Cloudiot   = Google::Apis::CloudiotV1
iot_client = Cloudiot::CloudIotService.new
iot_client.authorization = Google::Auth.get_application_default(
  "https://www.googleapis.com/auth/cloud-platform"
)

# The resource name of the location associated with the project
parent   = "projects/#{project_id}/locations/#{location_id}"
resource = "#{parent}/registries/#{registry_id}/devices/#{device_id}"

command_req = Cloudiot::SendCommandToDeviceRequest.new
command_req.binary_data = data

# Set configuration for the provided device
iot_client.send_project_location_registry_device_command_to_device resource, command_req

puts "Command sent!"

接收命令

要接收命令,设备必须:

  • 使用 MQTT 协议连接到 Cloud IoT Core
  • 订阅 MQTT 主题 /devices/{device-id}/commands/#(必须提供 # 个通配符)

通过订阅通配符主题,设备将接收发送至 devices/{device-id}/commands 的命令,以及发送到子文件夹(例如 devices/{device-id}/commands/{subfolder})的命令。不支持订阅特定子文件夹。

命令将传送至在特定时间连接和订阅的设备。系统不会在之后连接和订阅的设备排队或保留这些队列。

服务质量 (QoS)

命令传递取决于您使用的服务质量 (QoS) 级别

服务质量 (QoS) 等级 保证
0 不能保证(仅限尽力而为),即使请求返回 OK 也是如此
1 如果 sendCommandtoDevice 请求返回 OK,则保证至少传送一次

换言之,对于 QoS 0,无论设备响应如何,消息一旦发送就会被视为成功。对于 QoS 1,成功表示设备已确认消息传送。请注意,“至少一次”传送意味着设备可能会多次接收该命令;Cloud IoT Core 不会跟踪该命令的接收次数。

错误

  • 如果达到命令超时(60 秒,如配额和限制所述),则返回 DEADLINE_EXCEEDED

  • 如果设备未连接或已连接但未订阅 MQTT 通配符主题,则返回 FAILED_PRECONDITION

日志记录

发送到设备的命令以及设备发出的确认信息会记录在 Cloud Logging 中。

价格

命令的计费方式与通过 MQTT 发送的所有其他消息相同。如需了解详情,请参阅价格