Demonstração de compartilhamento de conexão coreMQTT - FreeRTOS

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Demonstração de compartilhamento de conexão coreMQTT

Importante

Esta é uma versão arquivada do Guia do usuário do FreeRTOS para usar com a versão 202012.00 do FreeRTOS. Para obter a versão mais recente deste documento, consulte o Guia do usuário do FreeRTOS.

Introdução

O projeto de demonstração do compartilhamento de conexão CoreMQTT mostra como usar um aplicativo multiencadeado para estabelecer uma conexão com o agente AWS MQTT usando TLS com autenticação mútua entre o cliente e o servidor. Esta demonstração usa uma implementação de interface de transporte baseada em mbedTLS para estabelecer uma conexão TLS autenticada pelo servidor e pelo cliente e demonstra um fluxo de trabalho de publicação e assinatura de MQTT no nível de QoS 1. A demonstração assina um filtro de tópicos, publica em tópicos que correspondem ao filtro e, em seguida, aguarda para receber essas mensagens do servidor no nível de QoS 1. Esse ciclo de publicar para o agente e receber de volta a mesma mensagem do agente se repete indefinidamente. As mensagens nesta demonstração são enviadas QoS 1, o que garante pelo menos uma entrega de acordo com a especificação MQTT.

nota

Para configurar e executar as demonstrações do FreeRTOS, siga as etapas em Conceitos básicos do FreeRTOS.

Esta demonstração usa uma fila de threads segura para manter comandos para interagir com a API MQTT. Tome nota de quatro tarefas nesta demonstração.

  • Uma tarefa de comando (principal) recebe comandos da fila de comandos e os processa. As outras tarefas colocam comandos nessa fila para serem processados. Essa tarefa entra em um loop, durante o qual processa os comandos. Se um comando de encerramento for recebido, essa tarefa escapará do loop.

  • Uma tarefa de publicador síncrona cria uma série de operações de publicação e as envia para a fila de comandos. Em seguida, essas operações são executadas pela tarefa de comando. Essa tarefa usa publicação síncrona, o que significa que ela aguardará a conclusão de cada operação de publicação antes de programar a próxima.

  • Uma tarefa de publicador assíncrona cria uma série de operações de publicação e as envia para a fila de comandos. Em seguida, essas operações são executadas pela tarefa de comando. A diferença entre essa tarefa e a anterior é que ela não espera a conclusão de uma operação de publicação antes de programar a próxima. Ela verifica o status de cada operação de publicação após todas as operações de publicação serem adicionadas à fila. Observe que a diferença entre a publicação síncrona e assíncrona está apenas no comportamento dessas tarefas. Não há diferenças nos comandos de publicação propriamente ditos.

  • Uma tarefa de assinante cria uma assinatura MQTT em um filtro de tópicos que corresponde aos tópicos de todas as mensagens publicadas pelas duas tarefas do publicador. Essa tarefa entra em um loop e espera receber as mensagens que foram publicadas pelas outras tarefas.

As tarefas podem ter filas para manter as mensagens recebidas. A tarefa de comando enviará as mensagens recebidas para a fila de qualquer tarefa assinada no tópico de entrada.

Esta demonstração usa uma conexão TLS com autenticação mútua para se conectar a AWS. Se a rede for desconectada inesperadamente durante a demonstração, o cliente tentará se reconectar usando a lógica de recuo exponencial. Se o cliente se reconectar com êxito, mas o agente não conseguir retomar a sessão anterior, o cliente assinará novamente os mesmos tópicos da sessão anterior.

Thread única versus thread múltipla

Existem dois modelos de uso da coreMQTT, o com thread única e o com threads múltiplas (multitarefas). Esta demonstração mostra como você pode criar o próprio esquema com threads múltiplas. Há também outro exemplo com threads múltiplas que executa o protocolo MQTT em segundo plano em uma tarefa de agente (ou daemon). Para obter mais informações, consulte Agente e demonstrações MWTT usando a coreMQTT. Se você executar o protocolo MQTT em uma tarefa do agente, não será necessário gerenciar explicitamente nenhum estado do MQTT ou chamar a função MQTT_ProcessLoop. Além disso, se você usar uma tarefa de agente, várias tarefas de aplicação poderão compartilhar uma única conexão MQTT sem a necessidade de primitivas de sincronização, como mutexes.

Código-fonte

O arquivo fonte de demonstração tem um nome mqtt_demo_connection_sharing.c e pode ser encontrado no freertos/demos/coreMQTT/ diretório e no GitHubsite.

Funcionalidade

Essa demonstração cria quatro tarefas no total: três que solicitam chamadas à API MQTT e uma que processa essas solicitações e é a tarefa principal. Nesta demonstração, a tarefa principal entra em um loop que cria as três subtarefas, chama o loop de processamento e depois faz a limpeza. A tarefa principal cria uma conexão MQTT única com o agente que é compartilhada entre as subtarefas. Duas das subtarefas publicam mensagens para o agente e a terceira recebe as mensagens de volta, usando uma assinatura MQTT para um filtro de tópicos que corresponde a todos os tópicos das mensagens publicadas.

Typedefs

A demonstração define as seguintes estruturas, enumerações e ponteiros de função.

Comandos

Em vez de fazer chamadas diretamente à API MQTT, as tarefas usam estruturas Command_t para criar comandos que instruem a tarefa principal a chamar a operação de API apropriada para elas. Os comandos têm os tipos a seguir:

  • PROCESSLOOP

  • PUBLISH

  • SUBSCRIBE

  • UNSUBSCRIBE

  • PING

  • DISCONNECT

  • RECONNECT

  • TERMINATE

O comando TERMINATE não tem uma operação de API MQTT correspondente. Ele é usado na demonstração para instruir a tarefa principal a interromper o processamento de comandos e iniciar as operações de limpeza. Como algumas informações adicionais, por exemplo, informações de publicação ou assinatura, são necessárias para alguns comandos MQTT como MQTT_Publish, MQTT_Subscribe e MQTT_Unsubscribe, usamos o campo CommandContext_t. Esse campo é obrigatório para esses três comandos e é opcional para outros.

Como esse contexto é obrigatório para esses comandos, não altere esse valor depois que o comando for colocado na fila, até que o comando seja concluído. Quando um comando é concluído, um retorno de chamada opcional pode ser invocado. Nesta demonstração, usamos um retorno de chamada que cria uma notificação de tarefa para informar à tarefa de chamada que o comando foi concluído. Para operações MQTT que exigem confirmações (assinaturas, cancelamentos de assinaturas e publicações com QoS maior que 0), o comando será considerado concluído quando a confirmação for recebida. Caso contrário, o comando será concluído quando a chamada de API MQTT correspondente for retornada.

As definições a seguir podem ser encontradas no arquivo mqtt_demo_connection_sharing.c:

Confirmações

Como algumas operações do MQTT exigem uma confirmação, elas usam uma matriz de AckInfo_t que contém o identificador do pacote da confirmação esperada e o comando original que a espera, para que seu retorno de chamada de conclusão possa ser invocado.

Assinaturas

Essa demonstração pode rastrear assinaturas para cada tarefa. Para fazer isso, cada tarefa que solicita uma assinatura deve fornecer uma fila de mensagens na qual receberá de volta as mensagens publicadas (SubscriptionElement_t). Várias tarefas podem assinar o mesmo filtro de tópicos, pois espera-se que usem filas de resposta separadas.

Mensagens publicadas recebidas

Como as tarefas são executadas paralelamente à tarefa principal, seria difícil e demorado se a tarefa principal tivesse que esperar cada tarefa assinada ler uma mensagem publicada recebida. Portanto, cada mensagem recebida é copiada para a fila de respostas de qualquer tarefa que esteja inscrita no tópico da mensagem publicada (PublishElement_t). Como os pacotes de publicação recebidos do cliente MQTT contêm ponteiros para o buffer de rede do cliente, a carga e o nome do tópico da mensagem recebida são copiados em buffers separados antes de serem inseridos em uma fila de resposta. Dessa forma, a tarefa assinada ainda poderá ler as informações recebidas após o cliente MQTT limpar o buffer de rede.

Tarefa principal

A tarefa principal do aplicativo, RunCoreMQTTConnectionSharingDemo, estabelece uma sessão MQTT persistente, cria três subtarefas e executa o loop de processamento até que um comando de encerramento seja recebido. Uma sessão persistente será usada, pois se a rede for desconectada inesperadamente, a demonstração se reconectará ao agente em segundo plano, sem perder assinaturas ou mensagens publicadas recebidas do agente. Para criar uma nova sessão persistente para cada execução, a demonstração se conecta ao agente com o sinalizador clean session definido, depois se desconecta e se reconecta com o sinalizador não definido. Após o loop de processamento encerrar, ele se desconecta do agente e faz loop novamente a partir do ponto em que fez a reconexão da rede.

A conclusão da demonstração com êxito gerará um resultado semelhante ao da imagem a seguir.

Saída do terminal de demonstração do compartilhamento de conexão MQTT após conclusão com êxito
Loop de comando

O loop de comando, prvCommandLoop, espera que os comandos sejam colocados na fila de comandos e, em seguida, chama a API MQTT apropriada. Todos os comandos, exceto DISCONNECT e TERMINATE, resultam na chamada de MQTT_ProcessLoop. Esta demonstração define um retorno de chamada de ativação do soquete para adicionar um comando PROCESSLOOP à fila quando os dados estão disponíveis no soquete. No entanto, podem existir muitos comandos à frente na fila nesse momento. Para garantir que não negligenciemos os dados recebidos enquanto outros comandos são processados, MQTT_ProcessLoop é chamado para uma iteração única após cada comando.

Comandos de processamento

Veja a prvProcessCommandfunção.

Tarefa síncrona do publicador

A tarefa síncrona do editor, prvSyncPublishTarefa, cria PUBLISH operações de forma síncrona e espera que cada operação seja concluída antes de programar a próxima. Esta demonstração usa QoS 1 para publicar mensagens, isso significa que essas operações não são consideradas concluídas até que o pacote de confirmação de publicação seja recebido.

Tarefa de publicador assíncrona

A tarefa assíncrona do editor, prvAsyncPublishTarefa, não espera a conclusão de uma publicação antes de colocar a próxima na fila. Isso demonstra que nem sempre é necessário que uma tarefa aguarde a conclusão de uma operação MQTT para poder ser retomada. Como cada comando de publicação exige a própria estrutura de contexto, essa tarefa não pode reutilizar uma estrutura de contexto única como faz a tarefa síncrona do publicador, porque um comando anterior ainda pode precisar dela. Portanto, ela aloca memória para cada estrutura de contexto e, em seguida, espera para liberar toda a memória alocada após todas as mensagens que serão publicadas serem colocadas na fila.

Tarefa do assinante

A tarefa do assinante, prvSubscribeTask, assina um filtro de tópicos que corresponde a todos os tópicos das mensagens publicadas nas tarefas síncronas e assíncronas. Em seguida, ela espera receber todas as mensagens publicadas antes de cancelar a assinatura. Essa tarefa também é responsável por criar a operação TERMINATE que sinaliza a tarefa principal para encerrar o loop de comando.