IoT

Manipulação de arquivos: dados binários

binwalk - dados binários

Já pensou em transferir uma imagem via serial para uma MCU ou transferir uma imagem entre rádios? Essa transferência de dados binários poderia ser tanto um arquivo binário de qualquer tipo como uma imagem jpg, bmp etc. Existem casos muito específicos onde esse tipo de transferência é fundamental, mas para fazê-la de forma adequada é necessário ter um mínimo de entendimento sobre os bastidores da coisa.

Nesse artigo veremos os conceitos envolvidos e seguiremos com o estudo em uma próxima oportunidade. Mas não se preocupe, já dará para brincar um bocado com o que será disposto aqui.

Experimentação em Python

Para esse exemplo, estou utilizando o Python 3 e para escrever o código para execução em fluxo, utilizo o bpython3. Tudo o que faço é em Linux, se estiver utilizando Windows, use seu editor preferido. Sugiro o PyCharm.

Instale o bpython3, caso já não o tenha no sistema:

Criação de um bytearray

O bytearray em Python é uma forma de criar um objeto mutável, onde seus dados podem ser modificados a qualquer momento. Para exemplificar um dado binário, o utilizaremos no editor bpython3. Abra o programa em um terminal, simplesmente digitando bpython3. Depois crie um array vazio ou com dados iniciais.

Um byte corresponde a 8 bits, cujo valor pode variar entre 0 e 255. Esse valor é representado em hexadecimal, usando 2 bytes literais para representar o valor, variando entre 00FF. O formato apresentado (tanto na saída do print quanto para a especificação de um valor) é \x00\xFF para os valores anteriormente citados.

Para criar um bytearray, podemos fazer algo como:

Desse modo, definimos um array inicial de 4 bytes, que serão preenchidos com os valores \x00. Se não especificarmos o número de bytes, será criado um array vazio. Vejamos:

dados binários

Esse artigo não é sobre Python, mas é bom deixar claro o uso dos recursos envolvidos no exemplo. Podemos mudar um valor do bytearray do mesmo modo que mudamos um caractere em uma string, simplesmente passando sua posição. Para mudar o valor da posição 1 (lembrando que a primeira posição é sempre 0), bastaria fazer:

dados binários

Quando se cria o valor com a função bytes(), não é possível mudar os valores mais. No segundo exemplo utilizei a função bytearray(), que me permite fazer todo o tipo de manipulação nos valores assinalados. Portanto, para manipular os dados, utiliza-se a segunda função, enquanto para apenas atribuir, utiliza-se a primeira função. Nada impede que se use apenas bytearray().

Lendo e escrevendo dados binários

O Python oferece a biblioteca io, que contém recursos para uma leitura, escrita e conversão de forma direta. Como o intuito é interagir com MCUs e rádios, precisamos ter um pouco de material introdutório para compreender o que precisa ser feito. Utilizar a io.BytesIO() em Python facilitará a interação por parte do computador, mas ainda voltaremos a um nível mais baixo, ao interagir em C/C++.

io.BytesIO()

Em Python tudo vira objeto, é uma linguagem simples de aprender e, em minha humilde opinião, deveria ser a linguagem base para quem está iniciando.

Primeiramente, vamos criar um objeto para manipulação. Tudo o que escrevermos aqui pode ser colocado em um arquivo qualquer contendo a extensão .py e então executado com a precedência do interpretador na linha de comando. Se quiser ver resultados na hora, utilize o bpython3 ou use o interpretador nativo, digitando python3 sem parâmetros. A vantagem de utilizar bpython é que ele tem auto-complete, o que dá acesso a recursos das bibliotecas e facilita a redigitação de variáveis.

Ao código:

O método write retorna o número de bytes escritos. Em ambos os casos, deve retornar 14, considerando os espaços.

O cursor é a posição em que se encontra o próximo byte a ser escrito. Podemos mover o cursor para qualquer posição do buffer, por exemplo, posição 5:

Então podemos exibir o resultado assim:

E o resultado:

dados binários

Mudar os dados de um buffer existente

Como o conteúdo do objeto streaming é imutável, para modificar um dado do buffer devemos primeiramente copiá-lo. O método getbuffer() deverá ser utilizado para tal:

Não se esqueça de fazer o reposicionamento do cursos após a modificação do buffer, senão retornará um valor vazio. Exemplo completo:

dados binários

Como escrever e ler dados binários

A primeira coisa a se atentar é que, para escrever arquivos binários, o arquivo deve ser aberto em modo binário, senão teremos apenas um monte de sujeira. No exemplo anterior mostrei uma string codificada em UTF-8. Agora escreveremos apenas dados binários.

Abrir arquivo em modo binário para escrita

Em Python devemos fazer simplesmente:

Esse modo é convencional em Python porque dispensa a necessidade de abrir e fechar o arquivo. Como programo bastante em C/C++, prefiro o método tradicional. Seria algo como:

Nesse caso, em uma implementação real será necessário fazer tratamento de exceções; antes de abrir o arquivo, deve-se verificar se já não está aberto etc.

Abrir arquivo em modo binário para leitura

Às vezes podemos ter arquivos binários maiores do que a memória disponivel e carregá-lo todo de uma vez não seria possível nesse caso. Para essa questão, utilizamos a leitura linha a linha.

Para fazer uma transferência binária, também é melhor optar por ler e tirar o hash da linha, enviar e aguardar a resposta do hash para então enviar a próxima linha após validar a atual. Veremos mais detalhes sobre isso.

Pegando o tamanho do arquivo

Pode ser utilizado tanto para validar a forma que a leitura será feita (linha a linha ou total, conforme o tamanho) ou para checar o tamanho final do arquivo transferido via rádio ou serial.

Para isso, usamos um recurso da biblioteca os:

Inteiros para bytes

Às vezes precisamos escrever bytes para comunicação com determinados dispositivos. Peguei uma série de possibilidades em um exemplo da ddungeon:

Bytes para inteiros

A operação reversa também é possível, claro:

Tem mais uma montanha de coisas que é possível fazer, mas vou citar em outros artigos, conforme a necessidade. Vamos agora ver um pouco de manipulação de dados binários em C/C++.

Experimentação em C/C++ Arduino

Em C++ podemos utilizar fstream para leitura  e escrita de dados binários, mas não podemos utilizá-la em Arduino. Não tem. O Arduino utiliza a herança da classe Stream para isso, e por sua vez, não é chamada diretamente. Seu uso se dá através de bibliotecas como a SD, Wire e Serial. Quando chamamos read() ou write(), estamos utilizando intrinsecamente a classe Stream.

Em C utilizaríamos algo como:

Bem, o Arduino não tem sistema de arquivos, portanto não podemos contar com nenhum dos recursos anteriores. Já se estivéssemos usando ESP8266 ou ESP32, ambos seriam reconhecidos pelo respectivo compilador. Se o intuito for gravar em uma EEPROM, daí a biblioteca correspondente já oferece os recursos para a gravação do byte X em um endereço int Y.

Para um próximo artigo

No próximo artigo relacionado  devo mostrar mais algumas manipulações e acredito que você, leitor, gostará bastante, porque vamos fazer pelo menos 1 brincadeira interessante.

Até a próxima!

One comment

Comments are closed.

%d blogueiros gostam disto: