-
Notifications
You must be signed in to change notification settings - Fork 0
pt br
Os objetivos desta biblioteca são:
- Provê uma biblioteca para desenhar códigos de barras, em sistemas embarcados;
- Fácil utilização da biblioteca;
- Independência da arquitetura;
- Otimização para dispositivos embarcados.
No mundo dos códigos de barras existem diversos padrões, para os mais diversos fins. Entretanto nessa biblioteca será adotado somente um deles, o EAN-13.
O motivo de escolher somente um, dentre tantos, é o fato deste padrão, o EAN-13, ser o mais utilizado do mundo e ser compatível com outros tipos de códigos de barras. Por exemplo, podemos representar um código UPC-A (padrão utilizado nos EUA e Canadá), de 12 dígitos, como um código EAN-13, de 13 dígitos, apenas adicionado um zero no primeiro dígito.
Os 13 dígitos do EAN-13 pode ser dividido em quatro grupos:
- Prefixo GS1: os 3 primeiros dígitos indicam o país onde o produto foi registrado.
- Código do fabricante: de tamanho variável, estes dígitos indicam o código do fabricante.
- Código do produto: de tamanho variável, estes dígitos indicam o código do produto.
- Dígito verificador: último dígito do código, responsável por detectar possíveis erros.
Um código de barras é composto não somente de números, representados por barras, mas também por marcadores. Existem dois tipos de marcadores, as Quiet Zones e os Marcadores de Posição.
As quiet zones são os espaços em brancos antes e depois das barras. As quite zones são utilizadas para garantir que a leitura, dos scanners de códigos de barras, serão feitas corretamente. A quiet zone da esquerda é composta de 11 barras claras. Enquanto a quiet zone da direita é composta de 7 barras claras.
Os marcadores de posição, assim como as quiet zones, garantem que o código de barras será lido de forma correta pelos scanners. Existem três marcadores de posição: 1 no início, 1 no centro (após o 7º dígito) e 1 no final. Estes marcadores são formados por barras verticais.
O marcador do início e do final são compostos por 3 barras e possuem o mesmo formato: 1 barra escura, 1 barra clara, 1 barra escura. O marcador do centro, exemplificado na Figura 2, possui 5 barras e o seguinte formato: 1 barra clara, 1 barra escura, 1 barra clara, 1 barra escura, 1 barra clara. Um exemplo dos marcadores se encontra na Figura 1, cujo os macadores de início, centro e final são descrito na figura, respectivamente, por: C1, C2 e C3.
Dividimos o códigos em: 1 dígito e 2 grupos. O primeiro dígito fica separado, os próximos 6 dígitos ficam no primeiro grupo e os últimos dígitos ficam no segundo grupo. O primeiro dígito do código de barras não é representado por barras verticais. A função do primeiro dígito é descrita no próximo paragrafo.
Para desenhar as barras do primeiro grupo, existe dois tipos de codificação possíveis: L-code, G-code. A codificação dos dígitos são independentes entre si. Ou seja, a codificação do segundo dígito pode seguir a codificação L-code, enquanto o terceiro dígito pode seguir a codificação G-code. A codificação de cada dígito, será determinada pelo primeiro dígito. A Tabela 1 é utilizada para fazer a correspondência entre: o primeiro dígito do código e as codificações que serão utilizadas.
| Primeiro dígito | Primeiro grupo de 6 dígitos |
|---|---|
| 0 | LLLLLL |
| 1 | LLGLGG |
| 2 | LLGGLG |
| 3 | LLGGGL |
| 4 | LGLLGG |
| 5 | LGGLLG |
| 6 | LGGGLL |
| 7 | LGLGLG |
| 8 | LGLGGL |
| 9 | LGGLGL |
Tabela 1
Todos os dígitos do segundo grupo são codificados de acordo com a codificação R-code. As tabelas da codificação L-code, G-code e R-code são descritas na Tabela 2. Um exemplo das codificações é mostrado na Figura 2.
| Dígito | L-code | G-code | R-code |
|---|---|---|---|
| 0 | 0001101 | 0100111 | 1110010 |
| 1 | 0011001 | 0110011 | 1100110 |
| 2 | 0010011 | 0011011 | 1101100 |
| 3 | 0111101 | 0100001 | 1000010 |
| 4 | 0100011 | 0011101 | 1011100 |
| 5 | 0110001 | 0111001 | 1001110 |
| 6 | 0101111 | 0000101 | 1010000 |
| 7 | 0111011 | 0010001 | 1000100 |
| 8 | 0110111 | 0001001 | 1001000 |
| 9 | 0001011 | 0010111 | 1110100 |
Tabela 2

Figura 2
A separação em dois arquivos: um header (.h) e um source (.c), foi escolhida para oferecer uma maior organização e clareza para os usuários da biblioteca. Assim, podemos obter os protótipos das funções no arquivo header e suas implementações no arquivo source.
Um fator importante em qualquer sistema é a segurança. Um forma de aumentar a segurança do sistema é encapsular as variáveis, e algumas funções, dentro de seus módulos. Ou seja, torna-las invisíveis a qualquer outra parte externa ao módulo onde foram declaradas. Para garantir que estas variáveis ficarão "escondidas" dentro dos módulos, utilizaremos a palavra reservada static. Esta palavra reservada, quando utilizada em variáveis globais, delimitam a utilização das variáveis, ou funções, somente no módulo onde foram declaradas.
Para desenhar o código de barras precisamos da Tabela 1 e da Tabela 2.
Na implementação da Tabela 1, utilizamos um array de uint8_t com 10 posições. O índice do array, representa a primeira coluna da Tabela 1, enquanto o conteúdo, deste array, representa a segunda coluna.
Para a Tabela 2, utilizamos o mesmo formato da Tabela 1. Isto é, o índice do array representa o dígito e o conteúdo representa a codificação L-code.
A codificação R-code é obtida realizando a operação bitwise NOT (~) na codificação L-code. A codificação G-code é obtida realizando a operação bitwise NOT (~), na codificação L-code, e invertendo a ordem do byte.
Para inverter a ordem de um byte, utilizamos uma função privada. Esta função utiliza somente operações bitwise e deslocamentos.
Em todas as bibliotecas gráficas, é utilizada uma função atômica que permite pintar um pixel da tela. Como o foco dessa biblioteca é em sistemas embarcados, não temos uma função padrão (ou mais utilizada) para pintar um pixel na tela. Assim, devemos permitir, de alguma forma, que o usuário nos diga qual será a função utilizada por ele. Para isto, podemos utilizar um ponteiro de função. Deste modo, podemos receber este ponteiro, na inicialização da biblioteca, e guarda-lo numa variável global e estática, para ser utilizada depois.
Entretanto, esta função não pode ser puramente genérica. Devemos definir alguns parâmetros que esta função deve receber.
Os dois primeiros parâmetros determinam a posição (x e y) do pixel que será pintado. Esta posição é determinada por uma base ortonormal, com origem no canto superior esquerdo da tela. Sendo que o eixo x cresce da esquerda para a direita e o eixo y cresce de cima para baixo. Cada eixo possui um alcance de 0 até 65536. Se uma tela, com dimensões maiores do que 65536 pixels, for utilizada, é necessário aumentar o tamanho dos parâmetros x e y. Atualmente, estes parâmetros, são definidos como uint16_t.
Outro parâmetro necessário é: a cor que será utilizada para pintar o pixel. Para representar a cor utilizamos um uint8_t. Deste modo, podemos utilizar 256 cores diferentes. Para representar mais cores, é necessário aumentar o tamanho do parâmetro cor.
Foi definido quatro constantes: WHITE, BLACK, BAR_WIDTH, BAR_HEIGHT. As duas primeiras constantes representam a cor clara e a cor escura que será utilizada no código de barras, respectivamente. As duas últimas, representam a largura e altura de cada barra, respectivamente. Existem mais três constantes que NÃO PODEM SER ALTERADAS, elas são: LEFT_QUIET_ZONE_LENGTH, RIGHT_QUIET_ZONE_LENGTH, CODE_LENGTH. Se uma destas constantes forem alteras, a biblioteca apresentará problemas na renderização do código de barras.
A função principal da biblioteca recebe, como entrada, um array de 13 dígitos, representando o código de barras. Como o código de barras pode ser representado de outras formas (string ou uint64_t), foram criadas 4 funções utilitárias, visando o quesito "facilidade de utilização:
- String2Code: Esta função permite transformar uma string de 13 caracteres, em um array de inteiros com 13 posições.
- Code2String: Esta função permite transformar um array de inteiros com 13 caracteres, em uma string com 13 caracteres.
- Rawcode2Code: Esta função permite transformar um número inteiro, sem sinal, de 64 bits (uint64_t), em um array de interos com 13 posições.
- Rawcode2String: Esta função permite transformar um número inteiro, sem sinal, de 64 bits (uint64_t), em uma string com 13 caracteres.
Com o intuito de deixar o código da função principal mais limpo, foi utilizada uma função auxiliar que desenha as barras verticais. Cada vez que esta função é chamada, a posição x é incrementada de acordo com o valor de BAR_WIDTH.
O algoritmo da função principal da biblioteca é descrito a seguir:
- Guardar o primeiro dígito do código de barras (posição 13 do array);
- Desenhar a quite zone esquerda;
- Desenhar a marca de posição esquerda;
- Desenhar o primeiro grupo de 6 dígitos (baseado na tabela L-code e no primeiro dígito do código de barras);
- Desenhar a marca de posição central;
- Desenhar o segundo grupo de 6 dígitos (baseado na tabela L-code e no primeiro dígito do código de barras);
- Desenhar a marca de posição direita;
- Desenhar a quite zone direita;
Para testar a biblioteca foi criado uma sandbox, utilizando C++ e OpenCV. Após os testes realizados na sandbox, a biblioteca foi testada num stm32L1xx, utilizando, como display, um E-paper de 2.9". As imagens dos testes são apresentadas a seguir:
Em códigos de barras NÃO SE PODE UTILIZAR A COR VERMELHA. Como os lasers, dos scanners, utilizam o espectro vermelho, utilizar a cor vermelha nas barras, ou como cor de fundo, pode impossibilitar a leitura do código de barras, por meio desses aparelhos.