Biblioteca de codificação Base64 com Arduino
Base 64 é um sistema de codificação que utiliza 64 símbolos agrupados em mensagens com comprimento múltiplo de quatro. Essas mensagens (pacotes de dados) são completadas, se necessário, com um símbolo de mais (portanto, 65 são usados), geralmente o sinal de igual (=), se a informação útil codificada resultar em um comprimento menor.
Usando 64 sinais você pode trabalhar com os 10 números e letras maiúsculas e minúsculas (26+26) do código ASCII, o problema é que existem 62, digamos, símbolos inequívocos mais dois que variam em diferentes implementações. Embora às vezes referido pela expressão "personagens ASCII imprimíveis", na verdade são aqueles que vão desde aquele representado pelo código 32 (espaço) até 126 (~) os 95 verdadeiramente imprimíveis.
A implementação da codificação Base 64 mais utilizado, o de PEM, que também é usado por MIME, trabalhe com os sinais extras "+" e "/" e o sinal "=" no preenchimento para que os pacotes tenham um comprimento múltiplo de quatro. As letras AZ ocupam as posições 0 a 25, as letras az ocupam as posições 26 a 51, os números 0 a 9 ocupam as posições 52 a 61, o sinal de mais (+) ocupa as posições 62 e a posição 63 é ocupada pela barra (/).
A maneira de representar dados em formato Base 64 consiste em retirar, dos dados originais, grupos de 6 bits que são representados com o código correspondente. Se sobrarem bits, eles serão preenchidos com zeros à direita. Se o número de códigos resultante não for múltiplo de quatro, é preenchido com sinais de igual à direita.
A imagem a seguir mostra a codificação ASCII de um texto ("ohm") e a forma como ele é convertido para Base 64. Como existem 7 símbolos, a mensagem final precisaria ser preenchida com um sinal de igual no final. Pode-se dizer que o texto "ohm" em ASCII equivalente a «b2htaW8=" em Base 64.
Usos específicos de codificação Base 64 Eles também costumam impor um comprimento máximo de linha. A implementação MIME Limita cada linha a 76 caracteres. Normalmente as linhas serão separadas por um código de final de linha (CR, representado pelo valor 0x0D em ASCII) e outra nova linha (NL, que corresponde ao código ASCII 0x0A).
A inconveniência adicionada ao implementar a codificação Base 64 em um dispositivo com poucos recursos, como costuma acontecer com um microcontrolador é que você tem que codificar conforme a informação chega ou com um amortecer mínimo, o que exige também a disponibilização de um sistema que indique que o fim da mensagem original foi atingido, por exemplo, através da adição de um código especial, ou da utilização de um pin cujo nível (sincronizado com a recepção) indique o estado da mensagem.
O código de exemplo abaixo é um biblioteca para Arduino codificar em Base64 que é implementado com ambos os critérios: codificar a informação que chega (sem amortecer) e aguarde o término do sinal de alerta.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
//base64.h
#include <string.h> // memcpy/strncpy
#define LONGITUD_LINEA 76
#define MASCARA_B64 0B00111111
#define ULTIMO_CODIGO_BASE64 64 // 64 caracteres más el signo igual (empezando a contar desde cero)
#define MAXIMA_LONGITUD_RESULTADO 6 // Máximo número de caracteres del resultado parcial de la codificación. Puede ser 1 si no se ha llegado al final de un bloque (2 bytes en el original, 4 en la conversión), 2 si se ha llegado al final de un bloque, 3 si no se ha llegado al final de un bloque pero se supera la longitud máxima de la línea, 4 si se llega al final de un bloque y se supera la longitud máxima de la línea, 5 si hay que rellenar con un signo igual o 6 si hay que rellenar con dos signos igual
class Base64
{
private:
unsigned char simbolo_base64[ULTIMO_CODIGO_BASE64+1]; // Espacio para la codificación Base64, el relleno (=) una terminación en \0
unsigned int numero_valor; // Posición (empezando en cero) que ocupa el valor que se desea convertir en el mensaje completo original
unsigned int numero_codigo; // Posición del último código calculado. Podría limitarse al ancho de la línea (LONGITUD_LINEA, 76 caracteres) pero usando un contenedor alto se podría implementar también una cuenta estadística
unsigned char resto_base64; // Último resto obtenido al calcular el último código
unsigned char resultado[MAXIMA_LONGITUD_RESULTADO+1]; // Resultado de la conversión actual. Si es terminal puede incluir el caracter 65 (=) una o dos veces
unsigned char contador_caracteres_resultado=0;
void acumular_resultado(unsigned char valor);
public:
Base64();
~Base64();
void iniciar_conversion();
unsigned char *convertir(unsigned char valor_original, bool terminar_conversion);
unsigned char *convertir(unsigned char valor_original);
unsigned char *terminar();
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
//base64.cpp
#include “base64.h”
Base64::Base64() // Constructor
{
memcpy(simbolo_base64,“ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=”,ULTIMO_CODIGO_BASE64+1);
iniciar_conversion();
}
Base64::~Base64() // Destructor
{
}
void Base64::iniciar_conversion()
{
numero_valor=0;
numero_codigo=0;
resto_base64=0;
}
unsigned char *Base64::convertir(unsigned char valor_original, bool terminar_conversion) // Valor que se desea convertir a Base64
{
convertir(valor_original);
if(terminar_conversion)
{
terminar();
}
return resultado;
}
unsigned char *Base64::convertir(unsigned char valor_original) // Valor que se desea convertir a Base64
{
unsigned char desplazamiento;
contador_caracteres_resultado=0;
acumular_resultado((valor_original>>(2+(numero_valor%3)*2))|resto_base64);
desplazamiento=4–(numero_valor%3)*2;
resto_base64=(valor_original&(MASCARA_B64>>desplazamiento))<<desplazamiento;
if(((numero_codigo+1)%4==0))
{
acumular_resultado(resto_base64);
resto_base64=0;
}
numero_valor++;
resultado[contador_caracteres_resultado]=0;
return resultado;
}
unsigned char *Base64::terminar()
{
if(numero_codigo%4)
{
acumular_resultado(resto_base64);
while(numero_codigo%4)
{
acumular_resultado(ULTIMO_CODIGO_BASE64);
}
}
resultado[contador_caracteres_resultado]=0;
iniciar_conversion();
return resultado;
}
void Base64::acumular_resultado(unsigned char valor)
{
numero_codigo++;
resultado[contador_caracteres_resultado++]=simbolo_base64[valor];
if((numero_codigo%LONGITUD_LINEA)==0)
{
resultado[contador_caracteres_resultado++]=13; // CR “\r”
resultado[contador_caracteres_resultado++]=10; // LF “\n”
}
}
|
A parte fundamental do cálculo do código Base 64 Isso é feito com a expressão:
(valor_original>>(2+(numero_valor%3)*2))|resto_base64
e o cálculo do restante com a expressão:
(valor_original&(MASCARA_B64>>desplazamiento))<<desplazamiento
,
ser desplazamiento
um valor que é calculado com a expressão:
4-(numero_valor%3)*2
O processo seguido para obter estas expressões consiste em generalizar o cálculo de cada um dos quatro códigos Base 64 que resultam da representação de três bytes do valor original.
Base64=((byte_1>>2)|resto)&0b00111111 |
resto=(byte_1&0b00000011)<<4 |
Base64=((byte_2>>4)|resto)&0b00111111 |
resto=(byte_2&0b00001111)<<2 |
Base64=((byte_3>>6)|resto)&0b00111111 |
resto=(byte_3&0b00111111)<<0 |
Base64=((byte_3>>0)|resto)&0b00111111 |
resto=(byte_3&0b00111111)<<0 |
com o texto Base64
O pseudocódigo acima refere-se ao código em Base 64 que está sendo calculado. A expressão tem sido usada byte_n
para se referir ao enésimo byte que está sendo codificado. O texto resto
representa os bits restantes do byte que está sendo codificado. No início do cálculo assume-se que o resto é zero
Para maior clareza, no pseudocódigo anterior a máscara de 6 bits foi incluída no cálculo de todos os códigos, embora seja necessário apenas determinar o último deles, pois os demais são girados para que os dois bits mais numerosos sejam sempre perdidos. significativo.
Como pode ser visto, o quarto código é todo resto e não há necessidade de calcular o resto posteriormente; Portanto, é necessário apenas realizar três etapas, uma por byte codificado. É importante lembrar que, se um terceiro byte de um pacote não fosse codificado, o último código deveria ser preenchido com zeros à direita. Base 64 obtido.
Para generalizar, a rotação à direita da expressão que calcula o código em Base 64 pode ser representado como 2+(numero_byte%3)*2
para que a parte dentro dos parênteses girasse de zero a dois, resultando em 2, 4 e 6 em cada etapa. Claro que não é a única forma de generalizar, mas escolhi esta pela funcionalidade e sobretudo pela clareza. Como a máscara (AND) só foi necessária no quarto código e já foi visto que não é necessário calculá-la (é tudo resto), ela não está incluída na expressão final para simplificá-la, embora devamos lembrar que o tipo de dados utilizado (byte) apenas os 6 bits menos significativos sejam considerados.
A rotação esquerda do resto pode ser generalizada de forma análoga à anterior. Pode-se observar também que a máscara aplicada (AND) sofre a mesma rotação de bit, mas no sentido oposto. Essa é a razão para calcular o deslocamento com 4-(numero_valor%3)*2
antes de aplicá-lo no sentido correspondente a cada parte da expressão.
O exemplo a seguir mostra como usar a biblioteca para codificar uma string de texto (lembre-se que Base 64 pode ser usado para qualquer conjunto de dados, como uma imagem, por exemplo). No código a seguir há alguns detalhes que é interessante esclarecer. Primeiro, um símbolo especial (o símbolo ~) foi usado para indicar o final do texto, em vez de um sinal de hardware ou indicar o comprimento do texto. Logicamente, esse símbolo não pode fazer parte dos dados codificados.
A segunda questão que deve ser considerada, tão importante quanto óbvia, é que o decodificador no destino deve saber como é representada a informação que chega até ele. O texto inclui caracteres que não pertencem ao conjunto ASCII imprimíveis (de 32 a 126), letras com acento, por exemplo. Arduino usará dois bytes (UTF-8) para representar esses caracteres. O habitual não pode simplesmente ser usado \0
como terminador de texto, pois, em muitos casos, o primeiro byte com o qual um caractere é representado será precisamente zero.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
#include “base64.h”
char texto_prueba[]=“La bella y graciosa moza marchose a lavar la ropa.\nLa mojó en el arroyuelo y cantando la lavó.\nLa frotó sobre una piedra, la colgó de un abedul.\nDespués de lavar la ropa, la niña se fue al mercado.\nUn pastor vendía ovejas pregonando a viva voz:\nved qué oveja, ved qué lana, ved qué bestia, qué animal.\nLa niña la vio muy flaca, sin embargo le gustó.\nYo te pago veinte escudos y no discutamos más.\nVuelve la niña cantando muy contenta con su oveja.\nCuando llegaron al bosque la ovejita se escapó.\nLa niña desesperada arrojose encima de ella.\nVelozmente y con destreza aferrola por detrás.\nLlegaba por el camino jinete de altivo porte.\nDescendió de su caballo y a la niña le cantó…~”;
char *resultado;
Base64 base64;
void setup()
{
Serial.begin(9600);
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__) // ¿Es un Arduino Leonardo (ATmega32U4)?
while(!Serial){}; // Esperar a Arduino Leonardo
#endif
// Mostrar el texto original
unsigned int contador=0;
while(texto_prueba[contador]!=‘~’)
{
//Serial.println(String(texto_prueba[contador])+”=”+String(texto_prueba[contador],DEC));
Serial.print(String(texto_prueba[contador]));
contador++;
}
Serial.println(“\n”);
// Mostrar el texto codificado en Base64
contador=0;
while(texto_prueba[contador]!=‘~’)
{
resultado=base64.convertir(texto_prueba[contador],texto_prueba[contador+1]==‘~’);
byte contador_resultado=0;
while(resultado[contador_resultado]>0)
{
Serial.print(String(resultado[contador_resultado]));
contador_resultado++;
}
contador++;
}
}
void loop()
{
}
|
A linha 26 do exemplo anterior mostra o uso do biblioteca para Arduino codificar em Base64. Basta indicar o método convertir
cada byte que deseja codificar e opcionalmente se é o último ou, caso contrário, interrompa a conversão com o método terminar
quando você chegar ao fim.
Como pode ser visto na imagem abaixo, o programa de exemplo do biblioteca para Arduino codificar em Base64 primeiro exibe o texto a ser codificado em Base 64, neste caso, o início da famosa canção dos gigantes Les Luthiers, e posteriormente o resultado da codificação em Base 64 usando comprimento de linha de formato MIME.
Postar Comentário