Como enviar comandos para dispositivos

É possível usar o Cloud IoT Core para enviar comandos a dispositivos. Os comandos são diretivas temporárias, únicas, enviadas para dispositivos conectados ao Cloud IoT Core e inscritos no tópico de comandos.

Em comparação com as configurações do dispositivo, os comandos são mais rápidos, podem ser enviados com mais frequência e são independentes de outros recursos do Cloud IoT Core. Ao escolher entre comandos e configurações, considere se você precisa de persistência/conhecimento ao longo do tempo (configurações) ou se prefere diretivas de velocidade e/ou tempo (comandos).

Os comandos podem ser úteis quando você quer:

  • Enviar mensagens rapidamente para vários dispositivos em um horário específico
  • Enviar mensagens de grande volume para muitos dispositivos em um horário específico
  • Enviar diretivas com prazo limitado que precisam expirar
  • Enviar configurações de dispositivo incrementais

Os comandos têm as seguintes características:

  • Enviado diretamente para os dispositivos conectados e inscritos
  • Não persistido no Cloud IoT Core
  • Ignorado para dispositivos que não estão inscritos e conectados no momento em que o comando é enviado.
  • Não é exclusivo (cópias podem ser enviadas, embora isso seja improvável)
  • Não são enviados em uma ordem específica (mas são entregues aproximadamente na ordem de envio).
  • Em qualquer formato (blob de dados)

Atualmente, o Cloud IoT Core é compatível com comandos apenas no MQTT (não HTTP).

Comandos em comparação com configurações

O Cloud IoT Core também é compatível com configurações de dispositivo. As configurações são mais consistentes e permanentes do que os comandos. As configurações são mantidas no Cloud IoT Core e, ao usar o MQTT, a configuração mais recente é entregue a todos os dispositivos inscritos, mesmo os que se inscreverão mais tarde.

A tabela a seguir pode ajudá-lo a decidir se quer usar um comando ou uma configuração:

Configurações Comandos
A configuração mais recente é repetida até ser entregue (MQTT) Nova tentativa de QoS 1, mas não há garantia de entrega
persistente; portanto, se um dispositivo se conectar (MQTT) ou pesquisas (HTTP) posteriormente, a configuração mais recente ainda será entregue; Não persistente; entregue apenas para os dispositivos conectados no momento em que o comando é enviado
Cada nova versão substitui a anterior Sem relacionamento nem ordem entre os comandos
Informa a um dispositivo o que "ser". corresponde ao estado no Cloud IoT Core Informa ao dispositivo o que fazer em um momento específico
Latência maior Latência menor
Normalmente pequenas (máximo de 64 KB) Até 256 KB
Arbitrário, blob definido pelo usuário Arbitrário, blob definido pelo usuário
1 atualização por segundo, por dispositivo 1.000 por segundo, por projeto (configurável)
2.500 por segundo, por registro

Como os comandos não são mantidos no Cloud IoT Core e não são repetidos indefinidamente, a telemetria de dispositivos ou os dados de estado não refletem um comando específico. O dispositivo pode não ter recebido o comando ou pode ter recebido comandos ou configurações subsequentes. Os comandos devem ser temporários e não fazem parte dos dados do dispositivo em longo prazo.

Para gerenciar a ordem dos comandos e as cópias, use a lógica do dispositivo ou aplicativos cliente.

Como enviar um comando

Para enviar um comando a um dispositivo, use o Console do Cloud, a gcloud ou a API Cloud IoT Core.

Console

Enviar um comando para um dispositivo

  1. Acesse a página Registros no Console do Cloud.

    Acessar a página Registros

  2. Clique no ID do registro do dispositivo.
  3. No menu de registro à esquerda, clique em Dispositivos.
  4. Clique no ID do dispositivo a que você quer enviar o comando.
  5. Na parte superior da página, clique em Enviar comando.
  6. Selecione o formato do comando:

    • Texto
    • Base64
  7. No campo Dados de comando, digite o comando.

  8. No campo opcional Subpasta, digite o nome de uma subpasta para este comando. Os dispositivos inscritos no tópico curinga receberão comandos enviados para subpastas.

  9. Clique em Enviar comando.

gcloud

Para enviar um comando a um dispositivo, execute o comando 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]\

Se os dados do comando contiver caracteres especiais, use --command-file em vez de --command-data

API

Use o método SendCommandToDevice para enviar um comando.

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!"

Como receber um comando

Para receber um comando, o dispositivo precisa:

  • Estar conectado ao Cloud IoT Core usando o protocolo MQTT
  • ser inscrito no tópico MQTT /devices/{device-id}/commands/# (o caractere curinga # é obrigatório);

Quando você faz a assinatura do tópico com caracteres curinga, o dispositivo recebe comandos enviados para devices/{device-id}/commands, além de comandos enviados para subpastas (como devices/{device-id}/commands/{subfolder}). Não é possível assinar uma subpasta específica.

Os comandos são entregues a dispositivos que estão conectados e inscritos naquele momento específico. Elas não são enfileiradas ou preservadas para dispositivos que se conectam e se inscrevem posteriormente.

Qualidade de Serviço (QoS, na sigla em inglês)

A entrega de comandos depende do nível de QoS que você está usando:

Nível de QoS Garantia
0 Sem garantia (somente melhor esforço), mesmo quando a solicitação retornar OK
1 Entrega do tipo "pelo menos uma vez" garantida se a solicitação sendCommandtoDevice retornar OK

Em outras palavras, no QoS 0, uma mensagem é considerada bem-sucedida assim que é enviada, independentemente da resposta do dispositivo. Na QoS 1, o sucesso significa que o dispositivo confirmou a entrega da mensagem. Observe que a entrega "pelo menos uma vez" significa que o dispositivo pode receber o comando várias vezes. O Cloud IoT Core não rastreia quantas vezes o comando foi recebido.

Erros

  • Se o tempo limite do comando (60 segundos, conforme indicado em Cotas e limites) for atingido, DEADLINE_EXCEEDED será retornado.

  • Se o dispositivo não estiver conectado ou estiver conectado, mas não estiver inscrito no tópico de caracteres curinga MQTT, FAILED_PRECONDITION será retornado.

Logging

Os comandos enviados e as confirmações por dispositivos são registrados no Cloud Logging.

Preços

Os comandos são cobrados como todas as outras mensagens enviadas por MQTT. Para mais detalhes, consulte Preços.