Libreria di codifica Base64 con Arduino
Base64 è un sistema di codifica che utilizza 64 simboli raggruppati in messaggi di lunghezza multipla di quattro. Questi messaggi (pacchetti di dati) vengono completati, se necessario, con un simbolo più (quindi se ne usano 65), spesso il segno uguale (=), se le informazioni utili codificate risultano di lunghezza inferiore.
Utilizzando 64 segni puoi lavorare con i 10 numeri e le lettere maiuscole e minuscole (26+26) del codice ASCII, il problema è che ci sono, diciamo, 62 simboli non ambigui più due che variano nelle diverse implementazioni. Anche se a volte indicato con l'espressione "personaggi ASCII stampabile", in realtà sono quelli che vanno da quello rappresentato dal codice 32 (spazio) al 126 (~) il 95 realmente stampabile.
L'implementazione della codifica Base64 più utilizzato, quello di PEM, utilizzato anche da MIMO, lavorare con i segni extra "+" e "/" e con il segno "=" per completare in modo che i pacchetti abbiano una lunghezza multipla di quattro. Le lettere A-Z occupano le posizioni 0-25, le lettere a-z occupano le posizioni 26-51, i numeri 0-9 occupano le posizioni 52-61, il segno più (+) le posizioni 62 e la posizione 63 è occupata dalla barra (/ ).
Il modo di rappresentare i dati in formato Base64 consiste nel prelevare, dai dati originali, gruppi di 6 bit che vengono rappresentati con il codice corrispondente. Se rimangono dei bit, vengono riempiti con zeri a destra. Se il numero di codici risultante non è multiplo di quattro, viene compilato con i segni uguali a destra.
L'immagine seguente mostra la codifica ASCII di un testo ("ohm") e il modo in cui viene convertito Base64. Poiché sono presenti 7 simboli, il messaggio finale dovrà essere riempito con un segno di uguale alla fine. Si potrebbe dire che il testo "ohm" in ASCII equivalente a «b2htaW8=" in Base64.
Usi specifici della codifica Base64 Di solito impongono anche una lunghezza massima della linea. L'implemento MIMO Limita ciascuna riga a 76 caratteri. Normalmente le righe verranno separate da un codice di fine riga (CR, rappresentato dal valore 0x0D in ASCII) e un'altra nuova riga (NL, che corrisponde al code ASCII 0x0A).
L'inconveniente che si aggiunge durante l'implementazione della codifica Base64 su un dispositivo con poche risorse, come spesso accade con a microcontrollore è che devi codificare man mano che arrivano le informazioni o con a bufferizzare minimo, che richiede anche di prevedere un sistema che indichi il raggiungimento della fine del messaggio originale, ad esempio aggiungendo un codice apposito, oppure utilizzando un pin il cui livello (sincronizzato con la ricezione) indichi lo stato del messaggio.
Il codice di esempio seguente è a libreria per Arduino da codificare in Base64 che viene implementato con entrambi i criteri: codificare l'informazione che arriva (senza a bufferizzare) e attendere la fine del segnale di avviso.
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”
}
}
|
La parte fondamentale del calcolo del codice Base64 Si fa con l'espressione:
(valor_original>>(2+(numero_valor%3)*2))|resto_base64
e il calcolo del resto con l'espressione:
(valor_original&(MASCARA_B64>>desplazamiento))<<desplazamiento
,
siendo desplazamiento
un valore che si calcola con l'espressione:
4-(numero_valor%3)*2
Il processo seguito per ottenere queste espressioni consiste nel generalizzare il calcolo di ciascuno dei quattro codici Base64 che risultano dalla rappresentazione di tre byte del valore originale.
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 |
Con il testo Base64
Lo pseudocodice sopra si riferisce al codice in Base64 quello viene calcolato. L'espressione è stata usata byte_n
per fare riferimento all'ennesimo byte da codificare. Il testo resto
rappresenta i bit rimanenti del byte da codificare. All'inizio del calcolo si assume che il resto sia zero
Per chiarezza, nello pseudocodice precedente è stata inclusa la maschera a 6 bit nel calcolo di tutti i codici, anche se è necessario determinare solo l'ultimo di essi, poiché gli altri vengono ruotati in modo che i due bit in maggior numero vadano sempre persi. significativo.
Come si può vedere, il quarto codice è tutto resto e non è necessario calcolare successivamente un resto; È quindi necessario eseguire solo tre passaggi, uno per byte codificato. È importante ricordare che, se un terzo byte di un pacchetto non fosse codificato, l'ultimo codice dovrebbe essere riempito con gli zeri a destra. Base64 ottenuto.
Per generalizzare, la rotazione a destra dell'espressione che calcola il codice in Base64 può essere rappresentato come 2+(numero_byte%3)*2
in modo che la parte all'interno delle parentesi ruoti da zero a due, risultando in 2, 4 e 6 ad ogni passaggio. Naturalmente non è l'unico modo per generalizzare, ma ho scelto questo per funzionalità e soprattutto per chiarezza. Dato che la maschera (AND) era necessaria solo nel quarto codice e si è già visto che non è necessario calcolarla (è tutto resto), non viene inserita nell'espressione finale per semplificarla, anche se bisogna ricordare che del tipo di dato utilizzato (byte ) vengano presi solo i 6 bit meno significativi.
La rotazione sinistra della pausa può essere generalizzata in modo analogo alla precedente. Si vede anche che la maschera applicata (AND) subisce la stessa rotazione di bit ma in senso opposto. Questo è il motivo per calcolare lo spostamento con 4-(numero_valor%3)*2
prima di applicarlo nel senso corrispondente a ciascuna parte dell'espressione.
L'esempio seguente mostra come utilizzare la libreria per codificare una stringa di testo (ricordatelo Base64 può essere utilizzato per qualsiasi set di dati, come ad esempio un'immagine). Nel codice seguente ci sono un paio di dettagli che è interessante chiarire. Innanzitutto, è stato utilizzato un simbolo speciale (il simbolo ~) per indicare la fine del testo, invece di un segnale hardware o che indica la lunghezza del testo. Logicamente, quel simbolo non può far parte dei dati codificati.
La seconda questione da considerare, tanto importante quanto ovvia, è che il decoder a destinazione deve sapere come vengono rappresentate le informazioni che gli arrivano. Il testo include caratteri che non appartengono al set ASCII stampabili (da 32 a 126), ad esempio lettere con accento. Arduino utilizzerà due byte (UTF-8) per rappresentare questi caratteri. Quello abituale non può essere semplicemente utilizzato \0
come terminatore del testo poiché, in molti casi, il primo byte con cui viene rappresentato un carattere sarà proprio 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()
{
}
|
La riga 26 dell'esempio precedente mostra l'uso di libreria per Arduino da codificare in Base64. E' necessario solo indicare la modalità convertir
ogni byte che si vuole codificare ed eventualmente se è l'ultimo o, in caso contrario, interrompere la conversione con il metodo terminar
quando arrivi alla fine.
Come si può vedere nello screenshot qui sotto, il programma di esempio del libreria per Arduino da codificare in Base64 visualizza prima il testo da codificare Base64, in questo caso, l'inizio della famosa canzone dei giganti Les Luthiers, e successivamente il risultato della codifica in Base64 utilizzando la lunghezza della riga del formato MIMO.
Invia commento