Saudações, queridos!
Em algum momento de sua vida, cada caixa teimosa e teimosa deixa de sentir falta do Kantian Arduino como "coisas em si mesmas"
que simplesmente não podem! : Piscando o LED, pegando dados dos sensores e transferindo-os pelo fio para o PC é certamente divertido, mas o Santo Graal está em mobilidade, em libertação das “ligações de cobre”, em verdadeira liberdade entre as ondas do éter universal.
É aqui que a dura realidade dos canais de comunicação instáveis, erros de transmissão e mensagens não entregues se abre para nós.
Deus proíbe reivindicar originalidade nesta área: a humanidade há muito tempo usa muitos protocolos para todas as ocasiões.
Mas nosso objetivo é aprender e, como sou um fervoroso defensor do reconhecimento em combate, estudaremos inventando nosso próprio protocolo “bicicleta”.
Hoje, proponho desenvolver um protocolo que garanta entrega garantida, integridade e sequência de mensagens entre dois assinantes (ponto a ponto, ponto a ponto), saiba como e aplique o algoritmo
Nagle e o
pipelining de protocolo , seja lá o que isso signifique. Ao mesmo tempo, ele deve ter uma
sobrecarga mínima e apertar até o apertado UNO do Arduino.

Peço a todos os interessados a bordo, fechamos as escotilhas, abrimos as pedras do rei, enchemos os tanques de lastro. Temos uma excursão ao passado, destino: ano 1974!
Para os impacientes (eu mesmo sou assim!)Aqui está o repositório do github onde estão as implementações:
De acordo com a boa e velha tradição, pelo menos dois especialistas reconhecidos neste campo estão envolvidos na descrição de algoritmos e protocolos criptográficos, se alguém não os conhece, familiarize-se:
E
Primeiro, descrevemos uma tarefa simples
Alice e Bob estão sentados em trincheiras adjacentes e não podem levantar a cabeça para se ver. Eles podem falar apenas em voz alta, ao lado deles as balas apitam e os projéteis explodem, afogando seus gritos e, além disso, quando um deles fala, você precisa gritar para não ouvir nada.
A situação é complicada pelo fato de eles estarem sendo ouvidos pelos inimigos - e você precisa usar uma linguagem de código, por algum motivo que consiste em longas sequências de números.
Como Alice e Bob são pessoas, eles periodicamente precisam sair para comer ou ir ao banheiro, e são tão impacientes que podem ficar impacientes no momento mais inoportuno!
Como e por que estabelecer uma conexão?
Como podemos organizar uma transferência de dados confiável em uma situação tão deprimente, quando parece que tudo está simplesmente fadado ao fracasso?
A primeira solução que pode surgir é usar frases de código com
palavras de parada para iniciar e terminar a transferência.
Bem, digamos que, se Alice quiser enviar uma mensagem, ela precisa gritar "Comece a transmissão!" E espere até Bob responder "Comece a recepção!".
Se Alice não esperar pela resposta de Bob, ela simplesmente repetirá sua solicitação para iniciar a transferência. Naturalmente, você não deve fazer isso com muita frequência; caso contrário, como sabemos, você simplesmente não ouve a resposta de Bob.
Ótimo. Mas o que acontece se Alice em resposta ouvir da próxima trincheira, "Começar a transmissão!"?
Acontece que Bob também decidiu transferir algumas informações importantes agora. Alice tem um caráter moderado, e ela pode ter pensado: "Ok, vou esperar, minha mensagem é, em princípio, não urgente, deixe Bob passar primeiro." Pensando nisso, ela responde: "Comece a recepção!".
Como
em tempo de guerra o valor do seno pode chegar a quatro, a velocidade do som é finita e leva algum tempo para compreender o que Alice e Bob ouviram, e até Bob, como cavalheiro, pode decidir ceder à dama, ele dá de ombros e grita: "Estou começando a receber!"
Para ilustrar a indignação, usaremos gráficos de tempo. O tempo passa para eles.
O caso em que Alice e Bob não chegaram a acordo sobre o prazo:

O caso em que a mensagem foi perdida:

Isso é um fiasco. Tudo fica confuso demais e é agravado pelo fato de o destinatário poder ouvir ou não ouvir alguma das frases e, em cada caso, o interlocutor não sabe se sua mensagem foi ouvida pelo destinatário.
Agora, Alice e Bob estão esperando um bem-vindo. Seria lógico perceber que ocorreu um conflito e alguém precisa retomar a transmissão. Mas e se tudo acontecer novamente de uma nova maneira? E aqui estamos novamente onde começamos.
Se você acha que a situação é extremamente rara, lembre-se da última vez em que você conversou com alguém por voz, quando seu assinante ou você (ou ambos) tiver uma conexão lenta à Internet. "Olá, olá, olá, você desaparece." "Você não pode ouvir olá, olá."
Enquanto isso, nas trincheiras, a situação está esquentando, os comandantes exigem a transmissão de relatórios.
É hora
de recorrer às fontes principais: para estudar Marx, Engels retornará mais de 40 anos atrás e verá como esses problemas foram resolvidos pelos engenheiros da
DEC ao projetar o protocolo
DDCMP .
Segundo os desenvolvedores do DDCMP, Alice e Bob precisam rejeitar emoções e se tornarem
máquinas de estado finito .
Isso significa que, a partir de agora, nossa Alice e Bob terão apenas alguns estados fixos, as transições entre esses estados poderão ocorrer estritamente de acordo com certas regras quando certos eventos ocorrerem.
Primeiro, simplesmente listamos os estados:
- Interrompido
- INÍCIO INICIAL
- INÍCIO RECONHECIDO
- EXECUTANDO
Como você pode ver, existem apenas quatro deles. E agora, não importa o que aconteça, cada um dos assinantes sabe ao menos com certeza que o seu vis-à-vis está em apenas um desses estados. De fato, olhando um pouco à frente, direi que quase sempre um assinante saberá em que estado está o segundo assinante,
mas isso não é exato .
Vamos considerar os estados separadamente, em detalhes
HALTED é o estado mais simples, ninguém vai a lugar algum, todos permanecem em seus lugares, nada é transmitido e não recebido, nenhum estímulo externo é ignorado. Todos, exceto um - a vontade das autoridades superiores. No protocolo DDCMP original, a transição do estado
HALTED só pode estar no estado
INICIAL INÍCIO a pedido do usuário - Alice ou Bob recebem um pedido para estabelecer uma conexão.
O que acontece quando Alice ou Bob recebem esse pedido?
Eles notam imediatamente a si mesmos que o estado mudou de
INÍCIO INICIAL , e essa transição, como qualquer outra, envolve uma sequência de ações estritamente definida. Nesse caso, você precisa gritar “DO IT!” E ajustar o relógio. Só isso.
Então, Alice gritou o que era necessário e apertou um botão no cronômetro. Agora, para entender o que esperar de Bob, descobriremos o que pode acontecer com Alice quando ela estiver no estado
INICIAL INÍCIO .
- A partir do momento em que Alice notou que o tempo passou, digamos 10 segundos e ela não ouviu nenhuma reação de Bob (observe, eu não estou dizendo que Bob não gritou nada para ela - isso não é conhecido, mas apenas que Alice não sabe nada) ouvida durante esse período, Alice é uma mulher sábia e racional e depende apenas de fatos). Chamamos esse evento de tempo limite - o intervalo de espera foi excedido. Nesse caso, o protocolo nos diz para repetir: grite “FAÇA UMA VEZ!” E repita a hora. Ainda não grosso.
- Se Alice ouviu que Bob gritou a mesma coisa - "FAÇA UMA VEZ!", Então Alice entra de forma
não seletiva no estado
INÍCIO RECONHECIDO , sobre o qual ela deve gritar imediatamente "FAÇA DOIS!" E acerte o relógio novamente.
- Mais uma vez, se Alice ouviu Bob fazer "DOIS!", Então ela imediatamente entra no estado
EXECUTANDO (!), Grita "ACEITOU NOOOOOL!". Se o cronômetro foi iniciado, ela o desliga da linha da frente da cautela.
É muito importante não fazer movimentos desnecessários que não sejam previstos pelo estado atual. O que quer que Bob chore, não importa o quanto xingue ou implore, Alice só reage conforme combinado.
É conveniente apresentar essas coisas na forma de uma tabela. Então, vamos começar com os
estados HALTED e
INITIAL START já descritos e, em seguida, reabasteceremos mais a tabela.
Omiti conscientemente alguns pontos da descrição original do DDCMP - não precisamos deles, queremos não apenas repetir o DDCMP, mas construir com base
no mesmo, apenas outro novo protocolo.
Mas voltando à descrição de estados e transições. O próximo estado é
INÍCIO RECONHECIDO .
Sendo neste estado, tudo o que pode preocupar Alice ou Bob é:
- como antes, a expiração do tempo de espera, nesse caso, você precisa permanecer no mesmo estado, gritar "DOIS!" E iniciar o temporizador novamente
- o “DOIS!” ouvido se traduz no estado
RUNNING , enquanto grita “NOOOOL LOGO ACEITADO!” E pare o temporizador;
- o ouvido “DO IT!” Sai no mesmo estado, você precisa gritar “DOIS!” E iniciar o temporizador;
- ouviu “NOOOL ACCEPTED!” - transição para o estado
RUNNING , pare o temporizador.
Colocamos tudo isso em uma tabela.
Com um aperto de mão, quase tudo está pronto - resta considerar apenas um estado
RUNNING , porque um dos assinantes já pode entrar nele e o segundo - corre urgentemente para o banheiro, e quando ele volta, esquece tudo e tenta estabelecer uma nova conexão.
Do ponto de vista do procedimento de handshake (ainda não lidamos com a transferência de dados, para a qual tudo foi iniciado - esta é uma história separada) no estado
RUNNING , estamos interessados em dois eventos:
- se eles gritarem para nós "FAÇA UMA VEZ!" - tudo está muito ruim, é uma dessincronização completa, tudo precisa ser reiniciado. O protocolo original
instrui você a simplesmente entrar no estado
HALTED . Mas isso não nos ajudará de forma alguma - se, por algum motivo, isso aconteceu em um Arduino autônomo, que transmite alguns dados de alguns sensores, então, para nós, é uma falha completa. Como sabemos, de
HALTED você pode ir para
INICIAL START apenas por ordem das autoridades.
Portanto, estamos modificando o protocolo aqui: a recepção no estado
HALTED do
comando "DO ONCE!" Deve funcionar como uma ordem das autoridades - ou seja,
mude para o estado
INICIAL INÍCIO , grite “FAÇA UMA VEZ!”, inicie o cronômetro. Além disso, em alguns casos, é conveniente dar uma ordem para estabelecer comunicação imediatamente após o fornecimento de energia a si mesmo.
Assim, agora, no caso mais inconveniente, simplesmente redefiniremos a conexão.
- o segundo evento ao qual é necessário reagir no estado
RUNNING - se ouvirmos "DOIS!" De uma trincheira vizinha. Isso já é mais interessante. Nesse caso, você precisa gritar “ER ACEITO!”. Onde ER significa o número de mensagens recebidas com sucesso na atual sessão de comunicação. Este é um novo conceito. Abaixo, consideraremos tudo com mais detalhes, mas, por enquanto, traremos tudo o que aprendemos para o momento atual em uma tabela:
Agora, se Alice e Bob seguem estritamente o protocolo, eles simplesmente não têm opções
para entrar em algo
incompreensível , exceto como estabelecer uma conexão, alternando em conjunto para o estado
RUNNING ou, em um caso ruim, tentando estabelecê-lo antes da vitória ser
clicada .
Um leitor agressivo pode tentar examinar todas as opções e chegar à conclusão de que a série de estados e transições acaba sendo fechada e estritamente determinada. Nós (com a ajuda das mentes dos engenheiros do DEC) agora amarramos Alice e Bob com um conjunto de regras que simplesmente seguem as quais eles estabelecerão uma conexão, se nas condições atuais isso for geralmente possível em princípio.
Como transferir dados agora?
Ok, isso foi um bom treino. Período de doces-buquê no relacionamento de dois nós da rede. Lembre-se de que iniciamos um negócio: precisamos transferir dados com entrega garantida e prioridade! Com recuperação de desastres. Na medida em que os recursos de hardware permitem isso (afinal, Alice e Bob podem se mostrar controladores fracos de 8 bits com 2 kilobytes de RAM!).
Os engenheiros do DEC nos ensinam que as mensagens que precisamos numerar, precisamos contar quanto enviamos, quantas recebemos e quantas das mensagens que enviamos chegaram ao destinatário.
É hora de uma digressão!Admita. quando vi os nomes das variáveis na descrição do protocolo DDCMP, decidi que não foi por acaso: os americanos gostam muito de atrair belas abreviações por seus ouvidos.
Para nossa conveniência, existem até vários recursos em que os interessados podem tocar o belo.
O meu favorito é este -
Site de acrônimos astronômicos mudos ou excessivamente forçados (ou DOOFAAS)Quanto valem essas fabricações!
Aqui está um exemplo:
WASP - Espectrômetro analógico de banda larga (mas não exatamente o que você pensava!)
SAURON - Unidade Espectroscópica de
Área para Pesquisa em Nebulosas Ópticas
CISCO - Espectrógrafo infravermelho e câmera refrigerados para OHS (é isso que significa!)
E aqui, apenas atire:
SQUIRT (ah, sim, artigo 18+!) - Satettile QUICK Research Testbed
MERDA (nem mais nem menos!) - Telescópio Interferométrico Super Enorme, com a inscrição “procure você mesmo”, ao qual o link para o resumo está anexado ao artigo com o mesmo nome.
Portanto, as variáveis que indicam o número de pacotes recebidos, enviados e entregues no nó na descrição original do protocolo são denominadas
RNA .
Ah, por que eles não nomearam o protocolo dessa maneira - RNA! Uma espécie de rede de RNA. Os protocolos DECnet tinham todas as chances de se tornar protocolos da Internet se a história tivesse sido diferente.
Mas voltando às nossas trincheiras
O padrão de protocolo original define que todos os contadores são de 8 bits e aumentam o módulo 256. Isso significa que pode haver um máximo de 256 mensagens enviadas para as quais a confirmação ainda não foi recebida.
E, se a confirmação não for recebida, eles poderão precisar ser retransmitidos e, se necessário, deverão ser armazenados até a confirmação. Afinal, temos entrega garantida!
Os parâmetros físicos de nossa Alice e Bob nos ditam condições diferentes. No Arduino de 8 bits, essa quantidade de dados simplesmente não existe para armazenar e temos que comprometer. E não estou falando do fato de que, no padrão, o tamanho dos pacotes (mensagens) em bytes é limitado a um número de 16 bits, ou seja, 64 kilobytes é um luxo inadmissível!
Portanto, a conexão é estabelecida. O que vem a seguir?
No momento em que Alice ou Bob
entram no estado
RUNNING , os contadores são redefinidos.
Como já mencionei, o protocolo original envolve a numeração de mensagens no módulo 256, mas temos que reduzir esse número para se adequar à pequena quantidade de memória em coisas do tipo Arduino.
Para poder limitar imediatamente todos os incrementos de contadores, apresentaremos uma certa constante UMCP_PACKETS_NUMBER, e agora todos os incrementos ocorrerão neste módulo.
Se você usar UMCP_PACKETS_NUMBER = 8, e o tamanho máximo do pacote for UMCP_PACKET_DATA_SIZE - as partes dos dados transmitidos por vez são limitadas a 64 bytes, tudo se encaixará no Arduino UNO e permanecerá um pouco para as necessidades do usuário.
É importante lembrar que esses dois parâmetros devem ser os mesmos para ambas as partes.
Obviamente, agora, se Alice e Bob estabeleceram uma conexão com sucesso, e um deles precisa transferir dados, primeiro os dados devem ser divididos em partes que não excedam os 64 bytes de tamanho e, em segundo lugar, cada pacote também deve conter um estado dois contadores de remetentes: o número de mensagens recebidas e enviadas (R e N).
Veja como é fácil agora organizar os chamados pipelining e como é fácil lidar com situações de erro!
Se Alice enviar 3 pacotes seguidos logo após o estabelecimento da conexão, todos eles terão o contador R definido como 0 (ela ainda não recebeu nenhum pacote) e o contador N aumentará em um a cada novo pacote.
Se Bob aceitar todos eles com êxito, para confirmar o recebimento dos três pacotes, será suficiente enviar uma confirmação apenas para o último, de fato, se ele simplesmente enviar de volta o status de seus contadores R = 3 e N = 0, Alice entenderá imediatamente que todos os pacotes enviados suas mensagens chegaram ao destinatário.
Era um caso ideal quando não ocorria força maior. Vamos agora ver o que poderia dar errado e como lidar com isso.
Se, por algum motivo, Bob pular o primeiro pacote e aceitar um dos próximos, ele imediatamente chamará a atenção para o fato de que o contador N (o número de pacotes transmitidos por Alice) excede claramente o contador R do lado de Bob e Bob percebe facilmente que perdeu o primeiro pacote . Nesse caso, ele só precisa jogar o Capitão Evidence mais plano e informar a Alice o status de seu contador de pacotes recebidos (R = 0). Ao mesmo tempo, Alice entende que ela é N = 3 e Bob tem R = 0, ou seja, ela precisa transferir pacotes de uma nova maneira, começando pelo primeiro.
Se você observar esse esquema com atenção, poderá ver que qualquer transmissão do status de seus contadores por qualquer assinante informa imediatamente o resultado da transmissão de pacotes de dados, e a diferença entre o contador transmitido de um lado e recebido do outro indica quantos pacotes foram perdidos e de que número ele começou.
Ou seja, no pior dos casos, há uma retransmissão completa da transmissão; no caso médio, o contador A no lado do transmissor aumenta para o valor do contador R no lado receptor e “enviando” os pacotes perdidos.É fácil entender que, dessa maneira, a continuidade do incremento dos contadores é mantida, o que significa que a transmissão de mensagens (pacotes) é garantida.Além das variáveis de RNA, cada assinante possui dois sinalizadores SACK e SPEP. Se o primeiro estiver instalado, será necessário enviar uma confirmação (Enviar confirmação), se o segundo - você precisará enviar uma solicitação de confirmação (Enviar REPly para uma mensagem)., DDCMP — SNAK (Send Negative AcKnowledgement). - . , , , — .
, .
- . - . E é verdade. .
.
, — , . , , L, (, — R).
Agora dê uma olhada mais de perto, percorra todo o circuito na cabeça. Percebemos o que está faltando aqui.Na descrição original do DDCMP, da qual partimos bastante, isso é chamado de sinalizador SELECT - um nó (Alice ou Bob) pode ou não ser "escolhido".O que nos confundiu foi o fato de nenhum mecanismo estar autorizado a permitir ou proibir a transferência.Bem, aqui está: esta é a bandeira SELECT. É aplicado de maneira muito simples: se o sinalizador estiver definido, é possível transmitir, caso contrário, é impossível.Todas as mensagens de controle como ACK e REP devem conter esse sinalizador. O último pacote na fila também deve conter esse sinalizador. Se um nó "costura" um sinalizador em um pacote, ele "o distribui" e, portanto, não é mais instalado. O nó que detecta esse sinalizador no pacote, pelo contrário, deve instalá-lo por conta própria. É como passar um bastão ou jogar um picadinho (lembra-se disso?).O mais importante ao trabalhar com esse sinalizador é que um dos nós deve ter esse sinalizador por padrão e o outro não. Esse é outro cronômetro muito importante - o sinalizador SELECT retorna o cronômetro.Agora, temos um conjunto completo de regras para estabelecer uma conexão e transmitir dados por ela.Não tocamos apenas na implementação concreta desse conjunto de regras.Bem, conserte!Formação e Formato de Pacotes
Isso se chama Enquadramento de Mensagens - as regras para analisar e gerar mensagens e o formato.Vamos calcular quanto precisamos.1. No mínimo, precisamos de cada mensagem para conter o estado dos contadores R e N do remetente. Para o Arduino, concordamos que podemos ter no máximo 8 mensagens enviadas, mas não confirmadas. Mas, como transferimos bytes, colocamos ambos os contadores em um byte, deixando-os em 4 bits.Esse byte será formado assim: = (RL & 0x0F) | (NL << 4);
E leremos o status dos contadores assim:
NR = (c >> 4) & 0x0F; RR = c & 0x0F;
c - byte correspondente da mensagem
2. Lembramos também que cada mensagem deve conter o estado do sinalizador SELECT. E os diferentes tipos de mensagens serão:
Ou seja, apenas 6 tipos diferentes de mensagens. Todas as mensagens, exceto o DTA, "liberam" o sinalizador SELECT - elas precisam de uma resposta imediata do assinante remoto e, sem o sinalizador, ele não poderá transmiti-lo. A mensagem DTA não retorna um sinalizador para possibilitar o pipelining.
Em geral, temos 3 bits suficientes para o tipo de mensagem, mas, para não mexer com os bits, atribuímos um byte inteiro ao tipo - em caso de revisão, teremos alguma liberdade de ação.
Se a mensagem contiver dados, precisamos transferir sua quantidade e soma de verificação. Como o tamanho máximo do pacote é de 64 bytes, também será necessário um byte para a soma de verificação e o comprimento - de repente você terá que aumentar o tamanho do pacote.
3. Também precisamos de alguma assinatura do início da mensagem e uma soma de verificação separada para o cabeçalho.
Com tudo isso em mente, o cabeçalho (também conhecido como mensagens de controle) fica assim:
E o bloco de dados é assim:
Só isso. Esta é uma descrição completa do protocolo que recebemos do DDCMP.
Agora você pode passar pela implementação.
Como é organizado e como usá-lo?
Primeiro, um pouco sobre a estrutura do repositório.
Como mencionei no início, o código do projeto está no github:
uMCPInoPara ver como tudo funciona, você pode executar um
aplicativo de teste em um PC.
No arquivo morto, execute uMCPIno_Test.exe, selecione a porta COM desejada e tente como ela funciona.
Você pode verificar um par de portas COM virtuais (eu geralmente faço isso).
Por que você pode executar duas cópias do aplicativo. Apenas não esqueça de ativar “SELECTED BY DEFAULT” em uma cópia - esta será Master e na outra - desligue-a. By the way, se estiver interessado, você pode ver o que acontece se você não seguir esta regra =)
A opção EXTRAS permite ver todos os movimentos dos pensamentos dentro do cérebro do protocolo. Todas as alterações no estado dos sinalizadores SELECT, eventos dos temporizadores, alterações no estado do nó, bem como os valores das variáveis R e N nas mensagens transmitidas e recebidas serão exibidos.
Eu conecto meu Arduino UNO ao meu laptop através de um conversor USB UART <->. Os conectores de pinos permitem simular uma quebra de linha a qualquer momento:

Se você agora executar o aplicativo no laptop, depois de pressionar o botão "CONNECT", o arduina estabelecerá uma conexão:

E é assim que o sistema reage a uma tentativa de enviar através de uma linha "rasgada":

Para incorporar o uMCPIno no seu aplicativo para PC:
- O repositório possui uma biblioteca uMCPIno. Conecte-o às referências do seu projeto
- Ele contém a classe uMCPInoPort. Declaramos sua instância:
uMCPInoPort port; port = new uMCPInoPort("COM1", UCNLDrivers.BaudRate.baudRate9600, true, 8100, 2000, 64, 8);
Parâmetros em ordem: nome da porta, velocidade da porta, estado SELECT padrão, intervalo para SELECT, intervalo de tempo limite, tamanho do pacote e o número máximo de mensagens não reconhecidas.
- Inscrever-se para eventos:
quando o sinalizador SELECT - port.Select for alterado:
OnSelectChangedEventHandler
quando o estado muda - port.State:
OnStateChangedEventHandler
O host remoto confirma o recebimento do código:
OnDataBlockAcknowledgedEventHandler
quando chega o pacote de dados:
OnDataBlockReceivedEventHandler
- Antes do trabalho, abra a porta
port.Open();
- Para enviar dados, chamamos o método:
port.Send(byte[] data);
- Após a conclusão, feche a porta:
port.Close();
Basta enviar dois bytes!
Agora vamos à implementação do Arduino. Dois exemplos estão na pasta
github.com/AlekUnderwater/uMCPIno/tree/master/ArduinoO primeiro é apenas um conversor de e para o uMCP. O primeiro Serial serve para se comunicar com o Host e o Serial1 (se estiver na sua placa) ou o SoftwareSerial nos pinos 2 e 3 - para se comunicar com outro nó do uMCPIno. Você pode conectar o Bluetooth ou um módulo de rádio aqui.
O segundo é um modelo de projeto com suporte para o protocolo uMCPIno
Ambos os projetos têm configurações onde você pode e deve subir. Aqui estão elas:
O estado padrão do sinalizador SELECT. Se definido como (true), mesmo que o nó remoto não retorne o sinalizador, ele será definido como true pelo timer.
#define CFG_SELECT_DEFAULT_STATE (false)
Para definir o período desse timer, existe a seguinte configuração: intervalo para retornar o sinalizador SELECT em milissegundos
#define CFG_SELECT_DEFAULT_INTERVAL_MS (4000)
O intervalo para aguardar uma resposta em milissegundos, é melhor deixá-lo um pouco menos que o intervalo para retornar o sinalizador SELECT.
#define CFG_TIMEOUT_INTERVAL_MS (3000)
A taxa de transmissão real da linha. Este parâmetro é necessário para determinar quando a transferência será finalizada.
#define CFG_LINE_BAUDRATE_BPS (9600)
Intervalo de acumulação de dados para o algoritmo Nagle. Insolentemente, considere igual a 100 milissegundos. Durante esse tempo, estamos aguardando um conjunto de pacotes, se não for digitado, enviamos como está. A tarefa do algoritmo Nagle é livrar a rede de uma pilha de pacotes pequenos de um a vários bytes de tamanho.
#define CFG_NAGLE_DELAY_MS (100)
Essas configurações definem as velocidades da porta para comunicação com o sistema de controle (Host) e a linha. Não confunda a velocidade da porta com uma linha com uma taxa de transferência física.
#define CFG_HOST_CONNECTION_BAUDRATE_BPS (9600)
Se essa configuração estiver ativada, quando a energia for fornecida ao controlador, o próprio protocolo se comandará para iniciar o estabelecimento de uma conexão.
#define CFG_IS_AUTOSTART_ON_POWERON (true)
Este é o tamanho em bytes do buffer para pacotes de dados recebidos.
#define CFG_IL_RING_SIZE (255)
A seguir, vamos ver como é o loop de esboço principal:
void loop() { uMCP_ITimers_Process(); DC_Input_Process(); DC_Output_Process();
Agora vamos ver como o protocolo funciona. A lógica principal está contida na função uMCP_Protocol_Perform (); Aqui está o código dela:
void uMCP_Protocol_Perform() { if (state == uMCP_STATE_RUNNING) {
Um analisador de pacotes que vive em uma função
On_NewByte_From_Line
também organizado pelo princípio de uma máquina de estados finitos e funciona "byte a byte". Isso é feito para economizar memória.
O restante da implementação não é de particular interesse. Analisaremos melhor como o usuário interage com o protocolo. Neste exemplo, existem quatro "pontos de contato".
A primeira é a função de enviar dados na linha uMCPIno:
bool uMCPIno_SendData(byte* dataToSend, byte dataSize);
Tudo é simples aqui - você tem um buffer de bytes dataToSend, seu tamanho é dataSize. A função retorna true se o envio for possível (há espaço para adicionar dados) e false caso contrário.
Para não dirigir em vão, você pode verificar imediatamente a disponibilidade de espaço suficiente usando a função:
bool uMCP_IsCanSend(byte dataSize);
Para analisar os pacotes de dados recebidos, você precisa adicionar seu código ao corpo da função
void USER_uMCPIno_DataPacketReceived();
Os dados recebidos são gravados no buffer de anel il_ring. A leitura a partir dele pode ser organizada em bytes como este:
while (il_Cnt > 0) { c = il_ring[il_rPos]; il_rPos = (il_rPos + 1) % CFG_IL_RING_SIZE; il_Cnt--;
Para prazeres sofisticados existe uma função
void USER_uMCP_OnTxBufferEmptry();
Que é chamado quando todos os dados são enviados com sucesso. Também é possível e necessário colocar algum tipo de código nele.
Por que isso é tudo e onde?
Eu lidei principalmente com apenas por diversão. Além disso, eu precisava de um protocolo simples e, o mais importante, "leve" para enviar dados através de nossos modems de sonar
uWAVE . Como eles transmitem dados através da água a uma velocidade de apenas 80 bps e com seu alcance máximo de comunicação de 1000 metros e a velocidade do som na água de cerca de 1500 m / s, a transmissão está associada a atrasos visíveis e existe apenas um canal de sonar (se não o mais !) das mais barulhentas, mais lentas e mais instáveis.
Em grande parte devido a isso, tive que abandonar o mecanismo de reconhecimento negativo (NAK) - se é possível não transmitir - na água é melhor não transmitir 100%.
Na realidade, o protocolo foi útil ao transmitir dados por um canal de rádio usando módulos
DORJI e o
NS-012 conhecido por arduinos.
O que vem a seguir?
Se houver tempo, pretendo adicionar a possibilidade de endereçamento (que, a propósito, estava no DDCMP). Como a principal tarefa deste protocolo agora é fornecer comodidade para todos os tipos de testes de nossos modens de sonar e outras redes de sensores, então existem (literalmente!) Armadilhas lá. Só posso dizer que o problema não pode ser resolvido simplesmente adicionando os campos "Remetente" e "Alvo".
Talvez seja o
Roteamento Geográfico e todo esse jazz.
PS
Tradicionalmente, serei muito grato por críticas, desejos e sugestões construtivas. É sempre importante entender se você está fazendo algo útil para as pessoas ou perdendo tempo.
Talvez, ao tentar evitar a transição desse longread para o romance "Guerra e Paz", tenha perdido alguns detalhes - não hesite em perguntar.
PPS
Muito obrigado pela vergonha do meu analfabetismo, apontando erros (gramaticais e lógicos):
O projeto era originalmente de código aberto, mas agora o artigo também é de código aberto.