Comunicação¶
1. FIRMWARE¶
Em main.cpp há três objetos maiores (mais complexos): um da classe Robo, um da classe USB_STM32 (usb) e outro da classe NRF24L01P (declarados em radio/bsp.h e instanciados em src/bsp.cpp)
Além do objeto auxiliar _usbserialbuffer, um buffer circular de uint8_t
OBS: quando a classe Robo é instanciada em bsp.cpp, pode-se escolher um modo de teste, no qual apenas a serial (e a linha de comando acessada pelo Tera Term) é usada.
1.1 CLASSE NR24L01P¶
NR24L01P está declarada em /inc/hal/nrf24l01p.h e herda publicamente da classe de abstrata MODEM (declarada em /inc/hal/modem.h
Seu contrutor recebe como parâmetro um objeto da classe abstrata SPI (declarada em /inc/hal/spi.h com métodos para escrita e início/término de comunicação) e objetos da classe abstrata IO_Pin (declarada em /inc/hal/io_pin.h, com métodos para leitura, escrita e configuração de pinos).
Possui um buffer circular para dados transmitidos (_txbuffer) e outro para dados recebidos (_rxbuffer).
Esta classe conta com métodos para:
sinalizar que o transmissor está disponível para receber uma payload (TxReady());
configurar o transmissor e mandar uma payload para a fila de transmissão (TxPackage() e TxPackage_ESB());
sinalizar que o receptor tem uma ou mais payloads disponíveis para leitura (RxSize());
ler uma payload para um buffer (RxData(...));
função para configurar como receptor e iniciar o monitoramento de pacotes (StartRX() e StartRX_ESB());
funções de configuração (SetRXFrequency() e Config()) e inicialização (Init());
Uma função de “manutenção” InterruptCallback(), que é chamada de tempos em tempos e quando o pino de IRQ do nRF24 for a nível baixo. Essa função faz uma verificação de alguns registradores, para lidar com os casos de máximo número de retransmissões, chegada de pacote válido, pacote transmitido com “sucesso” (critérios diferentes se o ACK estiver ou não habilitado) ou fila de transmissão cheia, além de fazer a temporização dos leds.Por exemplo, se um pacote de tamanho válido foi recebido, a payload é lida e colocada no _rxbuffer da classe (outras classes ou interrupções não devem acessar o hardware do nRF24, apenas acessam os buffers circulares). Se um pacote foi transmitido, o pino CE é resetado, pacotes na fila de recepção são ignorados.
OBS: em caso de sucesso, o led verde da Transmissora e o led azul da receptora acendem. Caso, o receptor não receba, nenhum led acende.
1.2 CLASSE USB_STM32¶
Classe declarada em /inc/hal_stm32/usb_stm32.h e herda publicamente da classe USB (declarada em /inc/hal_stm32/usb.h. Esta classe é amiga da classe
USB_DEVICE_CLASS, o que lhe permite acessar métodos privados ou protegidos de USB_DEVICE_CLASS.
O microcontrolador possui um controlador OTG_FS e um OTG_HS, totalmente conforme à especificação USB 2.0 . Usamos o OTG_FS.
Nota: USB On-The-Go, abreviada USB OTG, o dispositivo pode atuar tanto como mestre quanto como escravo.
É uma Virtual COM Port, emula uma porta serial (porta COM).
O dispositivo USB emula uma virtual COM port.
CDC (communication devices class)
A função OTG_FS_IRQHandler() implementada em /src/bsp.cpp é chamada quando o periférico OTG_FS da Discovery dispara uma interrupção para indicar que dados foram recebidos, por exemplo.
1.3 CLASSE USB_DEVICE_CLASS_CDC_VCP¶
Declarada em /inc/usb/usb_device_class_cdc_vcp.h, herda publicamente de USB_DEVICE_CLASS. Como alguns métodos de USB_DEVICE_CLASS_CDC_VCP sobrecarregam métodos de USB_DEVICE_CLASS, da qual USB_STM32 é amiga, USB_STM32 pode acessar estes métodos.
USB_DEVICE_CLASS_CDC_VCP implementa uma virtual COM port (vcp), possui um buffer circular de envio (_datainbuffer) e um de recepção (_dataoutbuffer). Para enviar dados, usa-se um dos métodos USB_DEVICE_CLASS_CDC_VCP.SendData(). Para receber, usa-se um dos métodos USB_DEVICE_CLASS_CDC_VCP.GetData().
1.4 PROTOBUF¶
Utiliza-se a biblioteca nanopb (https://github.com/nanopb/nanopb), uma implementação em ANSI C do protobuf, protocolo de serialização desenvolvido pela Google (veja https://developers.google.com/protocol-buffers/).
Nanopb is an ANSI-C library for encoding and decoding messages in Google's Protocol Buffers format with minimal requirements for RAM and code space. It is primarily suitable for 32-bit microcontrollers. https://jpa.kapsi.fi/nanopb/docs/index.html
Nanopb é uma biblioteca em ANSI-C para codificação e decodificação de mensagens no formato Protocol Buffers da Google com requisitos mínimos de RAM e tamanho de código. É adequado principalmente para uso em microcontroladores de 32 bits.
To begin with, you must provide a .proto file describing your messsage’s format:
For starters, consider this simple message:
message Example {
required int32 value = 1;
}
Save this in message.proto and compile it using protoc:
user@host:~$ protoc -omessage.pb message.proto
user@host:~$ python nanopb/generator/nanopb_generator.py message.pb
You should now have in message.pb.h:
typedef struct {
int32_t value;
} Example;
extern const pb_field_t Example_fields2;
This was how you would generate the files message.pb.c (contains initializers for const arrays) and message.pb.h (contains type declarations). Once you have finished this step, you will need only encode and decode messages.
Now in your main program do this to encode a message:
Example mymessage = {42}; //initialize your structure
uint8_t buffer [10]; //declares a buffer for the encoded message
// generates a auxiliary object
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
// encodes the message. Example_fields teaches the format
pb_encode(&stream, Example_fields, &mymessage);
After that, buffer will contain the encoded message. The number of bytes in the message is stored in stream.bytes_written.
Example mymessage; //declares your structure
uint8_t buffer [10] =...;// buffer filled with the encoded message
// generates a auxiliary object
pb_istream_t stream = pb_istream_from_buffer(buffer, sizeof(buffer));
// decodes the message. Example_fields teaches the format
pb_decode(&stream, Example_fields, &mymessage);
Using Protocol Buffers is a way of serializing structured data (e.g. the object representing our robot) for communications with ease, efficiency and maintaining compatibility with older versions.
2. LABVIEW¶
O arquivo principal do projeto é SSL Vision Log Player.vi, que possui um loop while de rótulo “Communication”, no qual clusters(análogos a structs de C) são continuamente lidos de uma “Lossy stream”, os quais representam nosso time, o time adversário, as posições onde a bola foi detectada, a geometria do campo, informações do juiz e a Time Stamp recebida. A Time Stamp recebida menos a Time Stamp medida no momento da execução é armazenada em Total Delay. our_robots (um vetor de Robot) é convertido para grSim Robot Commando e é mandado para transmissão, a qual pode ser via UDP (UDP TX, grSim Packet Command UDP Test.vi transmite para o grSim) ou via serial (SERIAL TX, Serial Transmitter.vi, que codifica usando protobuf e escreve na porta COM implementada na Discovery dedicada a transmitir para os robôs).
2.1 SERIAL TX (Serial Transmitter.vi)¶
Um dos controles (Porta:) permite selecionar a porta que será usada, por exemplo, uma porta COM, um feedback node armazena o valor anterior de Porta: e se eles diferirem (ou não havia porta selecionada antes) ou se houve um erro na escrita anterior, Visa Close fecha a porta anteriormente aberta, faz-se error_out.status=FALSE para limpar qq erro e a vi Visa Configure Serial Port abre e configura a porta serial. Por outro lado, se a porta não mudou e não houve erro, apenas passa-se a porta anterior (via feedback node) para a função VISA Write, que escreve na serial uma string gerada a partir da saída da vi grSim Robot Command, que recebe um grSim Robot Command, o codifica usando protobuf e o transforma em um array de bytes. Antes de passar o cluster para a serialização, um ajuste é feito: os campos velnormal e veltangent são trocados e a veltangent final tem seu sinal invertido, para corrigir uma confusão quanto à definição dessas velocidades.
Atualizado por Onias Castelo Branco há mais de 7 anos · 4 revisões