Protocolo PJON
Um dos pontos mais importantes deste projeto é o protocolo que a
rede implementada para poder comunicar os diferentes dispositivos de maneira
padronizada.
O PJON ® é o
primeiro protocolo de rede de código aberto capaz de ser executado em qualquer
computador ou microcontrolador e transmitir dados através de diferentes tipos
de protocolos de comunicação ou meio físico, como cabo (PJDL, Ethernet TCP/UDP, Serial e RS485), rádio (ASK, FSK, OOK, LoRa e Wifi) e pulsos de luz (PJDLS).
Ele pode ser facilmente compilado em várias arquiteturas como ATtiny, ATmega,
ESP8266, Teensy, Raspberry Pi e Windows X86. É uma ferramenta válida para criar
rapidamente uma rede de dispositivos. [16]
- O suporte à compilação cruzada com o sistema de interfaces chama abstração;
- Suporte multimídia com a abstração da camada de enlace de dados de estratégias;
- Suporte de troca a quente, sem necessidade de redefinição ou desligamento do sistema ao substituir ou adicionar dispositivos;
- Reconhecimento síncrono e/ou assíncrono configurável;
- Endereçamento configurável em 2 níveis (dispositivo e ID do barramento) para aplicativos escaláveis;
- Comprimento de pacote configurável de 1 ou 2 bytes (máx. 255 ou 65535 bytes);
- Verificação cíclica configurável de redundância sem tabela CRC8 ou CRC32;
- Gerenciador de pacotes para manipular, rastrear e, se necessário, retransmitir um envio de pacotes em segundo plano;
- Manipulação de erros.
Estratégias (Strategy)
PJON é uma ferramenta criada para simplificar a
comunicação entre dispositivos e engenharia de rede. Ele funciona com poucas
linhas de código e existem 7 estratégias disponíveis para comunicar dados em
várias mídias. [17]
• SoftwareBitBang | Médio: Cabo
É necessário 1 cabo ou,
opcionalmente, 2 e não possui hardware adicional para lidar com uma ou várias
comunicações em um único canal ou barramento. Pode ser executado com
microcontroladores baratos e de baixo desempenho. O comprimento máximo do
barramento é limitado principalmente pela resistência do elemento condutor
comum usado. Testados com cabos isolados de até 50 m de comprimento, os
resultados demonstram o mesmo alto desempenho alcançado com comprimentos mais
curtos. Está em conformidade com a especificação da camada de enlace de dados
PJDL v2.0.
• AnalogSampling | Médio: Leve
Ele foi projetado para amostrar dados digitais usando leituras
analógicas. Pode ser usado para comunicar dados sem fio através da luz. Ele é
otimizado para usar um único LED para transmissão e recepção de dados,
permitindo comunicação bidirecional bidirecional com um único par de LEDs (luz
visível, infravermelha ou ultravioleta) ou qualquer tipo de par de emissor e
receptor de foto. Tuned.
• Ethernet TCP | Médio: porta Ethernet, com
fio ou Wifi
Com a estratégia Ethernet
TCP PJON, vários dispositivos com portas Ethernet podem usar o PJON para se
comunicar em uma LAN, WAN ou na Internet.
• LocalUDP | Médio: porta Ethernet, com
fio ou Wifi
Com a estratégia Local UDP PJON, vários dispositivos com portas
Ethernet podem usar o PJON para se comunicar em uma sub-rede local, com fio ou
Wifi, ou em ambos.
• GlobalUDP | Médio: porta Ethernet, com
fio ou Wifi
Com a estratégia Global UDP PJON, vários dispositivos com portas
Ethernet podem usar o PJON para se comunicar em uma LAN, WAN ou na Internet.
• OverSampling | Médio: Rádio, Cabo
A estratégia de "superamostragem" vem do repositório
PJON_ASK e foi integrada ao repositório PJON a partir da versão 3.0 beta, como
uma estratégia da camada de enlace de dados. Os bits são "super
amostrados" para alta recuperação em cenários de alta interferência, como
o uso de transceptores de rádio ASK / FSK baratos em um ambiente urbano. Ele é
testado efetivamente com muitas versões dos módulos ASK / FSK 315 / 433Mhz
disponíveis no mercado com um alcance de até 5 km, mas funciona nominalmente
também através de cabos e do corpo humano.
• ThroughSerial | Médio: Porta serial de
hardware
Com a estratégia da camada de enlace de dados ThroughSerial, o
PJON pode ser executado através de uma porta serial emulada por software ou
hardware. Graças a esta opção, é possível aproveitar quase todos os
transceptores seriais compatíveis com Arduino, como RS485, rádio ou módulos de
infravermelho, mantendo o PJON inalterado no topo.
Esta última estratégia (ThroughSerial) é a utilizada neste
projeto, pois é perfeitamente adequada para comunicação de porta serial de
controladores e módulos CLP de banda estreita com MAC simples.
Documentação
O PJON implementa uma pilha de protocolo de rede
totalmente emulada por software em cerca de 1500 linhas de código.
Graças às interfaces de abstração, como visto na figura A. O PJON pode ser facilmente transportado em qualquer arquitetura, além de configurar o horário e as chamadas para o sistema IO (por exemplo, no Arduino: aleatório, micros, pinMode).
Figura A: Interfaces de abstração PJON. Fonte: https://www.pjon.org/ |
|
A biblioteca de documentação contém um conjunto de ferramentas para configurar e gerenciar facilmente a comunicação entre dispositivos:
• Endereçamento
• Configuração
• Transmissão de dados
• Recepção de dados
• Manipulação de erros
Endereçamento
O protocolo de comunicação PJON suporta dois tipos de
endereçamento e pode ser instanciado, passando um ID de dispositivo predefinido
e, opcionalmente, um ID de barramento.
No modo privado (modo local), não pode ser acessado de fora
(rede com fio em casa, empresa, setor industrial), onde qualquer tipo de
indexação de barramento pode ser usado sem se preocupar com a colisão do ID do
barramento.
No modo compartilhado, figura B. Existe a possibilidade de que
outros usuários do PJON possam estar transmitindo e recebendo dados; por esse
motivo, é altamente recomendável solicitar um ID de barramento PJON exclusivo
para o seu grupo de dispositivos. Isso pode ser obtido em https://www.pjon.org/get-bus-id.php
Figura
B: ID do barramento no modo compartilhado PJON. Fonte: https://www.pjon.org/
O PJON tem a possibilidade de endereçamento dinâmico, onde os escravos são conectados a um barramento e sua identificação é automaticamente atribuída pelo Mestre, que mantém uma lista atualizada de escravos conhecidos endereçados dinamicamente.
A identidade de cada dispositivo pode ser configurada
adicionando as seguintes linhas na programação em cada dispositivo:
PJON <
ThroughSerial > bus (44);
// Dispositivo PJON con id 44 en modo local
uint8_t bus_id [4] =
{1, 2, 3, 4};
PJON <
ThroughSerial > bus (bus_id, 44);
// ID de
dispositivo PJON 44 en bus id 1.2.3.4 en modo compartido
Neste projeto, é utilizado o endereçamento no modo local,
atribuindo diretamente a identidade de cada barramento diretamente.
Configuração
A seguir, são definidas as principais características do PJON para poder configurá-lo posteriormente, de acordo com nossas necessidades.
O protocolo PJON é capaz de suporte multimídia com estratégias de abstração na camada de enlace de dados. Ele suporta endereçamento mestre-escravo ou multimestre dinâmico e terá dois níveis configuráveis. Um ID do dispositivo e, no caso de modo compartilhado, um ID de barramento. Oferece suporte à troca de clientes, sem a necessidade de reiniciar ou desligar para substituir ou adicionar novos dispositivos. O reconhecimento de pacotes suporta uma configuração síncrona ou assíncrona. O comprimento do pacote pode ser definido como 1 ou 2 bytes, suportando assim um máximo de 255 ou 65535 bytes de dados. Evite uma colisão de pacotes quando definido como multimestre. Para detecção de erros, uma verificação de redundância cíclica de 8 ou 32 bits (CRC8, CRC32) pode ser configurada.
Antes de instanciar a classe PJON (# include <PJON.h>), é
possível restringir ou definir os pacotes e o comprimento do buffer.
# define PJON_MAX_PACKETS 10// número máximo de paquetes almacenado
# define PJON_PACKET_MAX_LENGTH 20 // Máxima Longitud por paquete en bytes
# include < PJON.h >
Com essa configuração, o PJON pode armazenar 10 pacotes de até 20 caracteres, menos a sobrecarga de pacotes, que pode variar de 5 a 22 bytes, dependendo da configuração.
Todas essas restrições afetam os requisitos de memória. Como menos memória é dedicada a esses buffers, mais memória pode ser usada para outra coisa. No Arduino, é importante, pois eles não têm uma grande memória de armazenamento.
Para reduzir ainda mais a memória dos programas, é necessário
definir a estratégia a ser usada, pois, por padrão, a classe PJON já está
incluída. Para isso, é indicado:
# define PJON_INCLUDE_TS // incluye ThroughSerial
O procedimento de endereçamento de protocolo PJON é baseado em
uma identificação de dispositivo de byte único, permitindo que eles se
comuniquem exclusivamente com outros dispositivos na rede local.
Para isso, é necessário
configurar o estado da rede a ser usada (local ou compartilhada) da seguinte
maneira.
bus.set_shared_network(false); // para estado de red local
Se for necessário evitar a
duplicação de pacotes devido a retransmissões ou erros, é possível adicionar um
ID de 2 bytes usado para garantir a exclusividade do pacote.
# define PJON_INCLUDE_PACKET_ID true
// Número máximo de ID de paquetes viejos almacenados para evitar la duplicación
# define PJON_MAX_RECENT_PACKET_IDS 10 // por defecto 10
//
Si se
produce la duplicación de paquetes, PJON_MAX_RECENT_PACKET_IDS más alta
Por fim, é necessário escolher entre o reconhecimento de pacotes
síncrono ou assíncrono. Para ajudar a uma transmissão mais estável e poder
verificar as comunicações ao vivo no console da porta serial, será escolhido um
reconhecimento síncrono. Dessa forma, o receptor responderá assim que a
transmissão completa estiver concluída, obtendo maior estabilidade no canal de
comunicação. Está configurado da seguinte maneira.
bus.set_synchronous_acknowledge
(true);
Transmissão de pacotes
Antes de instanciar a transmissão de pacotes, a função "start" deve ser ativada na parte de configuração, para evitar colisões. Indica:
bus.begin
();
A maneira mais fácil de enviar dados é usar o send_packet. Este
método compõe o pacote e tenta enviá-lo uma vez. Para isso, inclui um primeiro
parâmetro, a identificação do destinatário e, opcionalmente, pode passar a
identificação do barramento, se necessário. A carga útil e seu comprimento
estão listados abaixo. Essa chamada envolve uma única tentativa e não tem
garantia de sucesso, mas registra o resultado da tentativa de transmissão:
// Envía
al dispositivo ID 10 la cadena "Hello World!" de Longitud 12 bus.send_packet
(10,”Hello
World!”, 12);
// Envía al dispositivo ID 10 la cadena "Hello World!" de Longitud 12 bus.send_packet_blocking (10,”Hello World!”, 12);
Além disso, é possível enviar um valor repetidamente. Para isso,
é usado send_repeatedly, que é passado como o último parâmetro no intervalo de
microssegundos desejado entre cada envio:
// Ejemplo de envío local al id 100, Sting de longitud 23Bytes cada segundo uint16_t one_second_test =
bus.send_repeatedly ( 100 , " Prueba enviada cada segundo! “, 23, 1000000); / * IMPORTANTE: el intervalo máximo admitido es 4293014170 microsegundos o 71,55 minutos * /
Nos dois casos, a expedição é executada assim que o método é chamado e retorna os seguintes valores a serem exibidos no console da porta serial:
•
PJON_ACK (6) se uma recepção bem-sucedida ocorreu
•
PJON_BUSY (666) se uma transmissão estiver acontecendo para
outros dispositivos
•
PJON_FAIL (65535) se nenhum dado for recebido
Recebendo pacotes
Para receber pacotes, é necessário definir uma função do tipo
void que será chamada se uma mensagem correta for recebida. Esta função recebe
3 parâmetros: a carga útil recebida, seu comprimento e um ponteiro para uma
estrutura de dados do tipo PJON_Packet_Info que contém todos os metadados no
pacote:
void receiver_function (uint8_t * payload, uint16_t length, const PJON_Packet_Info & packet_info) {
/ * Utiliza la carga útil antes de enviar algo, el búfer al que apunta la carga útil se sobrescribe cuando se envía un nuevo mensaje * /
}
Graças à estrutura PJON_Packet_Info, é possível identificar quem é o bus_id que enviou o pacote, o ID do próprio pacote, assim como o cabeçalho e a quem é endereçado.
Para receber dados corretamente, é necessário chamar a função do
receptor pelo menos uma vez por ciclo:
Gracias a la estructura PJON_Packet_Info, es posible identificar quién es el bus_id que ha enviado el paquete, el id del propio paquete, así como la cabecera, y a quién va dirigido.
Para recibir datos correctamente, es necesario llamar a la
función receiver al menos una vez por ciclo:
uint16_t response = bus.receive ();
Como alternativa, um certo período de tempo (microssegundos) pode ser dedicadoentre cada recepção:
uint16_t response = bus.receive (1000);
A função de recebimento retorna os seguintes valores:
• PJON_ACK (6) se uma recepção
bem-sucedida ocorreu
•
PJON_NAK (21) se um erro for encontrado no CRC
•
PJON_BUSY (666) se uma transmissão estiver acontecendo para
outros dispositivos
•
PJON_FAIL (65535) se nenhum dado for recebido
Manipulação de erros
O PJON foi projetado para informar o usuário se um erro for detectado. Para fazer isso, defina uma função que recebe 2 parâmetros. O primeiro é o código do erro e o segundo é 1 byte de dados adicionais relacionados ao erro.
O código de erro pode assumir os seguintes valores:
• PJON_CONNECTION_LOST (valor 101), se o parâmetro contiver o índice do pacote perdido no buffer de pacote.
• PJON_PACKETS_BUFFER_FULL (valor 102), se o parâmetro contiver o comprimento total do buffer.
• PJON_CONTENT_TOO_LONG (valor 104), se o parâmetro contiver a duração do conteúdo por muito tempo.
Para fazer isso, ative esta opção na parte de configuração,
instanciando:
bus.set_error (manipulador de erros);
E abaixo está uma função, como mostrado no exemplo a seguir:
Especificações do PJON
Esta seção descreve as diferentes especificações nas quais o protocolo é implementado e, especialmente, as usadas para a estratégia ThroughSerial.
Na figura C. O modelo conceitual que caracteriza e padroniza a comunicação é mostrado. Seu objetivo é a interoperabilidade de diversos sistemas em uma ampla variedade de mídias com o uso de um novo conjunto de protocolos de código aberto. As partições gráficas representam as 7 camadas de abstração do modelo OSI e sua equivalência com o PJON.
Figura C: Camadas de abstração, modelo OSI conceitual
Observe as especificações que se enquadram nas camadas de rede e enlace de dados. Como são eles que definem o modo de operação do protocolo PJON.
Camada de rede
Na camada de rede, são definidas as especificações de endereçamento, reconhecimento de dados e a especificação do protocolo PJON v3.0.
Camada de link de dados
Na camada de enlace de dados, diferentes especificações estão contidas nas diferentes maneiras de conectar dispositivos. Entre essas especificações estão as de PJDL e PJDL v2.0 (Para conectar dispositivos através de um link direto como um único cabo). PJDLR v2.0 (Para comunicações entre dispositivos usando transceptores de rádio). PJDLS v2.0 (Para comunicações entre dispositivos limitados por baixa precisão do relógio). TSDL v2.0. (Para um link ponto a ponto entre dispositivos usando a porta serial). E, finalmente, a especificação SFPS v1.0 (Secure Frame Separation Protocol)
A seguir, as especificações usadas neste projeto serão
explicadas em detalhes.
Especificação do protocolo
PJON® v3.0
O protocolo PJON no modo local suporta conectividade para até 254 dispositivos, enquanto no modo compartilhado suporta conectividade para até 4.294.967.295 barramentos (grupos de dispositivos) e até 1.090.921.692.930 dispositivos. Graças à sua modularidade, formato de pacote dinâmico, pouco espaço na memória e baixo custo (5-22 bytes), o PJON pode ser usado como uma alternativa ao 1-Wir2e ou i2c3 para conectar microcontroladores com recursos limitados, mas também pode ser aplicado em vez de TCP-IP para interconectar redes mais complexas.
O protocolo também é baseado em uma série de conceitos básicos detalhados abaixo.
A transmissão ocorre apenas se o meio de comunicação não estiver em uso. Além disso, os pacotes são regulados por um cabeçalho de 1 byte.
Dentro de uma rede de barramento PJON, figura D. cada dispositivo tem o mesmo direito de transmitir e receber, onde cada um possui um ID de 1 byte exclusivo. Enquanto o barramento tem uma identificação exclusiva de 4 bytes. Além disso, várias redes de barramento podem coexistir no mesmo meio.
Comunicação no modo BUS
Um barramento PJON é composto de um grupo de até 254
dispositivos que transmitem e recebem no mesmo meio, como mostra a Figura D.
A comunicação entre dispositivos ocorre através de pacotes e baseia-se na
equidade ou no direito de cada dispositivo de compartilhar igualmente a largura
de banda disponível.
Figura D: modo BUS local
Formato do pacote
O cabeçalho é um bitmap dos metadados contidos que requer
configuração. Na figura E. Você pode ver o formato do cabeçalho e os
diferentes campos que, dependendo da configuração, terão valores de 0 ou 1.
Figura E: Formato do pacote
• [PACKET ID]: bit que informa
se o pacote contém (valor 1) ou não (valor 0) uma identificação de pacote de 2
bytes
• [EXT. COMPRIMENTO]: bit que informa se o pacote contém 1 (valor 0) ou 2 (valor 1) byte
•
[CRC]: sinais de bits usados por CRC, CRC8 (valor 0) ou CRC32
(valor 1)
• [PORT]: bit informa se o pacote contém um identificador de porta de 2 bytes (valor 1) ou não (valor 0)
• [ACK MODE]: modo de confirmação de sinais síncronos (valor 0) ou assíncronos (valor 1)
•
[ACK]: bit que informa se o reconhecimento é solicitado (valor
1) ou não (valor 0)
• [TX INFO]: bit que informa
se as informações do remetente estão incluídas (valor 1) ou não (valor 0)
• [MODE]: bit que informa se o
pacote está formatado no modo compartilhado (valor 1) ou no modo local (valor
0)
Na rede proposta, são utilizados PACKET_ID, ACK no modo síncrono, CRC8, TX INFO e MODO local. Portanto, o cabeçalho que aparece nas transmissões é: [10000110] = [134, decimal].
Observe que o PJON suporta CRC8 e CRC32 para garantir a segurança em uma ampla variedade de casos de uso e comprimentos de pacotes. sendo 8bit CRC o que finalmente foi usado.
Transmissão de pacotes
Uma transmissão de pacote local padrão é uma comunicação bidirecional opcional entre dois dispositivos que podem ser divididos em 3 fases diferentes:
• Análise de canal
• Transmissão
• Resposta opcional
No transmissor, na fase de análise de canal, o estado do meio é avaliado antes de iniciar a transmissão para evitar colisão. Se o meio estiver livre para uso, ele passará para a fase de transmissão em que o pacote é completamente transmitido. O dispositivo receptor calcula o CRC e inicia a fase de resposta transmitindo um único byte, PJON_ACK (decimal 6), no caso de uma recepção correta dos dados, como na figura F.
Se nenhuma confirmação for recebida, após um
atraso de reversão exponencial, o dispositivo transmissor tentará novamente até
que a confirmação seja recebida ou que um número máximo de tentativas seja
alcançado e a transmissão de pacotes seja descartada.
2 Especificação TSDL v2.0
TSDL (Tardy Serial Data Link) é um link de dados serial para
comunicações simples ou half-duplex. Ele suporta prevenção de colisões,
separação segura de quadros (SFSP) e resposta síncrona opcional às transmissões
de quadros.
O TSDL pode ser usado para estabelecer um link ponto a ponto
entre dois dispositivos, se usado com um link de porta serial, ou para suportar
uma ou mais comunicações com camadas físicas que suportam esse recurso, como
transceptores RS485. Também para nossos módulos CLP de banda estreita com MAC
simples.
Uma transmissão de quadro pode opcionalmente ser seguida por uma
resposta síncrona de seu destinatário. Figura G.
Entre a transmissão de
quadros e a resposta síncrona, há um período de tempo variável influenciado
pela latência e pelo tempo de computação da CRC. O tempo máximo gasto no
recebimento de reconhecimento potencial deve ser menor que o tempo de
transmissão (para impedir que outros dispositivos interrompam a troca de
respostas) e é estimado adicionando o tempo máximo de cálculo do CRC ao
comprimento de quadro à latência esperado.
SFSP (Secure Frame
Separation Protocol) v1.0
SFSP é um protocolo especificado para obter separação segura de quadros usando preenchimento de bytes e sinalizadores de sinalizador.
Como mostra a figura H. A transmissão do quadro começa com o
indicador START, seguido pelos bytes de dados. Quando os sinalizadores START,
END ou ESC ocorrem nos dados, eles são anexados com um sinalizador ESC e seu
valor é sinalizado com um sinalizador ESC para evitar a presença de
sinalizadores START, END ou ESC nos dados. O indicador END está anexado ao
final do quadro para finalizá-lo.
O receptor identifica o início de um quadro quando recebe o indicador START. Se o sinalizador ESC ocorrer durante a recepção, ele será ignorado e o próximo byte será fechado com o sinalizador ESC para retornar ao seu valor original. Qualquer valor dos sinalizadores START, END ou ESC faz com que o receptor solte o quadro e esteja pronto para receber o próximo byte. O final do quadro é detectado quando um sinalizador END é recebido.
O sinalizador END pode ser usado para verificar se o quadro e o
final do pacote são coincidentes. Se uma abstração de nível superior espera ler
o último byte de um pacote e não segue um sinalizador END, ocorreu um erro de
análise e o recebimento é descartado.
Referências:
https://repositorio.unican.es/xmlui/bitstream/handle/10902/14012/408952.pdf?sequence=1
https://github.com/gioblu/PJON
Sobre a SMARTCORE