Um ring buffer em flash: como um gateway off-grid não perde dados enquanto dorme
Num apiário não há corrente elétrica, por isso o gateway tem de estar desligado quase toda a noite. Como um ring buffer não volátil em flash guarda a telemetria das colmeias, sobrevive aos reinícios e não apaga nada antes da confirmação da cloud.

Num apiário não há uma tomada de corrente. É uma frase banal, e decide grande parte da arquitetura do bjtOS. De vez em quando obriga também a reabrir escolhas que pareciam fechadas: foi o que aconteceu quando enfrentei o buffering do gateway, ou seja, a caixa que recolhe os dados de todas as colmeias de um apiário e os envia para a cloud.
O resultado é um ring buffer não volátil em flash: um buffer circular escrito em memória permanente, que sobrevive ao desligar e aos reinícios. Neste texto conto porque é necessário, como é feito, e a coisa que mais me surpreendeu ao projetá-lo: que o instinto generalizado de “não escrevas com demasiada frequência na flash, gasta-la” só é verdade a meias, e a metade que falta é precisamente a que serve para decidir.
O problema: a peça mais capaz é também a mais esfomeada
O gateway tem lá dentro dois cérebros muito diferentes.
Um é um pequeno computador Linux. Sabe falar com a rede celular, cifrar, gerir filas, tentar de novo quando algo corre mal. É cómodo, e é também esfomeado: ligado vinte e quatro horas por dia pede quase dez watt-hora numa noite de inverno.
O outro é um microcontrolador com o rádio, aquele que fica à escuta dos nós das colmeias. Consome uns quarenta miliwatt, uma ordem de grandeza menos, e pode ficar ligado para sempre sem pesar no balanço energético.
A conta faz-se depressa. Se quero um gateway alimentado por um painel solar de tamanho razoável, como contei ao falar de quanto dura uma colmeia a bateria, o computador não pode estar ligado toda a noite. Tem de estar desligado quase sempre, e acender-se duas ou três vezes entre o pôr do sol e o amanhecer para enviar tudo em bloco.
Muito bem. Mas os sensores das colmeias não sabem que o computador dorme. As mensagens rádio continuam a chegar durante toda a noite. Alguém tem de tomar nota.
Porque não basta a memória volátil
A primeira resposta óbvia é manter os dados em RAM no microcontrolador, que afinal está sempre ligado. É também a resposta errada, e só se chega lá pensando em como é realmente uma noite off-grid.
De noite o painel não produz. A bateria cede, sobretudo com frio, e sobretudo quando o modem celular pede corrente para se agarrar à rede. Um brownout, ou seja, uma quebra de tensão que faz reiniciar o microcontrolador, não é um incidente exótico a colocar entre as eventualidades remotas: é a condição normal que o sistema tem de aguentar.
E um reinício apaga a RAM. Uma noite inteira de dados desaparecida, em silêncio, sem que ninguém dê por isso até à manhã seguinte.
Portanto o buffer tem de ficar em memória não volátil, ou seja, em flash: uma memória que conserva o que escreveu mesmo com a corrente desligada.
Program e erase não são a mesma operação
Aqui chega a parte que me mudou a forma de projetar a coisa.
Quem trabalha com embedded traz consigo um reflexo condicionado: não escrevas muito na flash, gasta-la. É uma indicação sã, mas se parares aí acabas por projetar à volta do problema em vez de o atravessar.
Na flash NOR, aquela integrada nos microcontroladores que usamos, existem duas operações bem distintas:
- Program, a escrita propriamente dita. Coloca a zero os bits necessários, faz-se aos bocadinhos, mesmo muitas vezes dentro do mesmo setor, desde que se escreva em espaço ainda virgem. Não desgasta de forma apreciável.
- Erase, o apagamento. Repõe um setor inteiro, tipicamente 4 KB, a tudo uns. É esta a operação com a endurance limitada, da ordem das cem mil ciclos por setor.
A consequência prática inverte o instinto. Escrever cada mensagem individual mal ela chega não é um desperdício: é um program, custa pouco, e torna o buffer resistente às quebras de corrente, porque se o microcontrolador morrer um milissegundo depois o dado já lá está. O que há a contar não é o número de escritas, mas o número de voltas que o buffer dá sobre a memória, porque é a cada volta que se paga um apagamento nos setores reciclados.
É uma distinção que permite escolher o comportamento mais prudente, ou seja, escrevo já e sempre, sem o pagar em durabilidade do hardware.
Um anel, dois índices, e a capacidade de se reconstruir
A estrutura do buffer é a mais antiga do ofício: um buffer circular, ou ring buffer. Uma cabeça que avança escrevendo, uma cauda que avança lendo, um anel de casas sobre o qual ambas giram. Quando a cabeça chega ao fim recomeça do início, reutilizando o espaço já lido. É uma FIFO de dimensão fixa: nunca cresce, nunca fragmenta, e tem um limite superior de ocupação conhecido desde o primeiro momento. Num dispositivo que tem de funcionar sozinho durante anos, a previsibilidade vale mais do que a elegância.
Cada registo escrito leva um cabeçalho mínimo: um marcador para o reconhecer, o comprimento, e um CRC, ou seja, um código de controlo que diz se o conteúdo está íntegro. Servem para o pior momento, que é o reinício.
Ao arrancar, o microcontrolador não confia em nada: percorre de novo a partição e reconstrói cabeça e cauda de raiz, lendo os registos um a um. Se a corrente caiu a meio de uma escrita, esse registo falha a verificação de CRC, é descartado, e a cauda mantém-se consistente. O buffer repara-se sozinho, sem precisar que alguém lhe tenha deixado dito o que estava a fazer um instante antes de morrer.
Por cima da flash em bruto pode assentar um sistema de ficheiros pensado para microcontroladores, que traz de dote o wear leveling, ou seja, a distribuição do desgaste por todos os setores, e a resistência à perda de corrente já resolvidas. Paga-se um pouco mais de escrita para os metadados, o que aos nossos volumes é irrelevante, e em troca evita-se reescrever à mão uma classe de bugs que outros já pagaram. A gestão manual da partição em bruto continua a ser a opção de reserva, se um dia fizer falta uma eficiência mais determinística.
Os números, que são a parte tranquilizadora
Tomemos um apiário de vinte colmeias, o nosso caso de estudo típico.
Um registo cabe em uns quarenta bytes, portanto num setor de 4 KB cabe uma centena. Uma noite de inverno longa de catorze horas, a um ritmo de transmissão cerrado, produz qualquer coisa como quinhentas e sessenta mensagens: pouco mais de vinte kilobytes.
Vinte kilobytes. Numa partição à qual dedicamos entre quinhentos e mil.
E então porquê tão larga, se o pico é tão pequeno? Não pela noite normal. Pela noite em que o modem não se agarra à rede. Nesse caso o computador acorda, tenta, falha, e o buffer não é esvaziado: os dados ficam lá e voltam a partir na janela seguinte. A margem não serve para conter o tráfego, serve para comprar dias de autonomia quando falta a cobertura celular.
Sobre o desgaste: cerca de mil e trezentos registos por dia dão treze setores por dia. Numa partição de um megabyte, ou seja, duzentos e cinquenta e seis setores, o anel dá uma volta a cada vinte dias escassos. São menos de vinte apagamentos por setor por ano, contra uma endurance de cem mil ciclos. O constrangimento, muito simplesmente, não é este: estamos numa escala que o hardware nunca chegará a ver. Verifica-se uma vez com uma folha de cálculo e depois deixa-se de olhar para isso.
Vale a pena dizê-lo porque é o tipo de conta que vale sempre a pena fazer antes: poupou-me a otimizar contra um problema que não existe, e deixou-me escolher o comportamento mais seguro sem remorsos.
A regra que torna tudo honesto: só se apaga depois da confirmação
A última peça é a que transforma um buffer numa garantia.
Quando o computador acorda, o microcontrolador não lhe entrega os dados para depois se livrar deles. Passa-lhos lendo-os da cauda, e a cauda só avança quando chega a confirmação de que esses dados foram aceites do outro lado, na cloud. Nenhum apagamento antes da confirmação. Nunca.
O resultado é uma entrega at-least-once, pelo menos uma vez: no pior caso a mesma mensagem chega duas vezes, e a jusante é reconhecida como duplicada e eliminada, porque cada mensagem leva consigo um identificador estável. O pior caso aceitável é o duplicado. A perda não é.
As duas filas trabalham portanto em série, e cada uma tem o seu ofício. A que está em flash no microcontrolador acumula a longo prazo e sobrevive ao computador desligado. A que está no computador, que no nosso sistema já existe e está testada, gere a sessão individual para a cloud com as suas reconexões e as suas tentativas. É a mesma filosofia com que nasceu toda a cadeia de transporte do bjtOS, que contei em construí a estrada antes dos sensores: o dado nunca se perde, acumula-se até haver forma de o entregar.
O que fica em aberto
Seria desonesto contá-la como coisa feita. Este é um projeto escrito, dimensionado e argumentado, ainda não código a correr num apiário. O que falta sei-o com precisão, e é a parte mais instrutiva:
- o protocolo de descarga, ou seja, como os dois cérebros falam entre si durante o transvase, com os marcadores de início e fim de sessão e o avanço da cauda ligado à confirmação;
- a hora, que hoje o microcontrolador recebe do computador, o qual a vai buscar à rede. Com o computador desligado tem de a manter sozinho entre um despertar e outro, e realinhar-se quando o outro volta a ligar;
- o caminho urgente: um alarme antirroubo não pode esperar pela janela noturna, tem de ligar o computador imediatamente. E para que o recetor o possa reconhecer sem abrir o conteúdo da mensagem, é preciso um bit de prioridade legível no cabeçalho, que tem de ser coordenado com o firmware dos nós e portanto com o contrato wire versionado;
- o watchdog: o que acontece se o computador se liga e nunca confirma, porque ficou bloqueado no arranque ou na ligação à rede.
O critério: o que acontece no pior momento
A escolha de fundo, contudo, está tomada, e aguenta um critério simples que uso muitas vezes quando projeto: o que acontece no pior momento?
O pior momento aqui é uma noite de fevereiro, com a bateria em baixo, o modem que não encontra rede e a tensão a dançar. Nesse momento, algures, tem de haver um caderno onde já está tudo escrito, e que não se apaga quando a luz se vai.
É o mesmo princípio que guia todo o resto do sistema: uma colmeia nativa digital projeta-se a partir dos constrangimentos, não a partir dos sensores. Se te interessa o quadro de conjunto há como funciona uma colmeia inteligente, e sobre o que estamos a construir encontras a página dedicada à tecnologia e a ao produto.