Bonjour tout le monde !
Aujourd’hui je vous ai préparé deux montages (le grand luxe quoi
).
Le thème du jour : générer des codes barres (type 39) avec une carte arduino !
–
Code barre type 39, kesako ?
Les codes barres type 39 sont des codes barres tout ce qu’il y a de plus classique que l’on retrouve un peu partout dans la vie quotidienne.
Je passe les détails historique, wikipedia est bien plus calé que moi sur le sujet ![]()
http://fr.wikipedia.org/wiki/Code_39
Tout ce qu’il faut se rappeler c’est qu’un code barre type 39 est composé de 9 éléments :
- 5 barres
- 4 espaces
–> 3 barres plus épaisses que les autres
–> ratio de 1/3 ou 1/2 entre une barre épaisse et une barre normale
Dans mon code vous remarquerez que je stocke les caractères type-39 sous forme de valeurs binaire sur 16 bits.
Voici quelques détails sur ce point assez particulier qui fait que mon code ne demande pas de calcul compliqués
Exemple avec le caractére A :
En code 39 : A = 100001001 (1 = barre large, 0 = barre normale)
Les codes barres commencent toujours par une ligne blanche de 1px servant de séparateur entre caractères, suivit d’une barre noir (large ou normale), puis de 8 barres blanche/noir/blanche/noir/…
J’ai choisi d’utiliser un ratio de 1/3, par conséquent une ligne épaisse = 3px et une ligne normale = 1px.
Si on fait les comptes (1 pixel (blanc / noir) = 1 bit ("0" / "1")) :
- 3 barres épaisses = 3 * 3 pixels = 9 pixels
- 2 barres normales = 2 * 1 = 2 pixels
- 4 espaces = 4 * 1 = 4 pixels
- 1 ligne de séparation = 1 pixel
9 + 2 + 4 + 1 = 16 pixels par caractère, codé sur un unsigned int (16 bits) ça passe pile poil
Pour ce qui est du calcul des pixels noir / blanc :
- A = 100001001 (1 = barre large, 0 = barre normale)
- une ligne épaisse = 3px et une ligne normales = 1px
- première barre noir puis inversion blanc / noir une barre sur deux
–> A = 0 111 0 1 0 1 000 1 0 111 (1 = pixel noir, 0 = pixel blanc)
(pixels -> barre)
0 -> ligne séparatrice
111 -> 1
0 -> 0
1 -> 0
0 -> 0
1 -> 0
000 -> 1
1 -> 0
0 -> 0
111 -> 1
–
Le montage n°1 :
Bon à mon avis seulement une minorité de personne ont suivi le calcul ci dessus jusqu’à la fin ![]()
Allez passons à quelque chose de plus visuel
Le premier montage est réalisé avec comme base un écran LCD TFT de 320×240 pixels.
Les personnes suivant mon blog remarqueront de suite qu’il s’agit de mon fidèle écran MI0283QT2 avec sa shield arduino
Pas de câblage, juste du lego
–
Le code n°1 (version lcd) :
Testé et commenté avec amour comme d’habitude
/*
* Générateur arduino de code barre type-39 (version lcd)
*/
/* Includes */
#include <avr/pgmspace.h> // Pour PROGMEM
#include <MI0283QT2.h> // Pour l'écran
/* Defines */
#define STR_BUFFER_SIZE 32 // Taille du buffer pour les caractères (taille max du texte + 2 étoiles + \0)
#define BARCODE_HEIGHT 100 // Hauteur du code barre en pixels
#define X_OFFSET 5 // Décalage en X
#define Y_OFFSET 70 // Décalage en Y
/**
* Table Ascii des caractères utilisable en type-39
*
* @var ascii_table
*/
const uint16_t PROGMEM ascii_table[44] = { // Ratio 1/3 -> "0"=x "1"=xxx
0b111010100010111, // A
0b101110100010111, // B
0b111011101000101, // C
0b101011100010111, // D
0b111010111000101, // E
0b101110111000101, // F
0b101010001110111, // G
0b111010100011101, // H
0b101110100011101, // I
0b101011100011101, // J
0b111010101000111, // K
0b101110101000111, // L
0b111011101010001, // M
0b101011101000111, // N
0b111010111010001, // O
0b101110111010001, // P
0b101010111000111, // Q
0b111010101110001, // R
0b101110101110001, // S
0b101011101110001, // T
0b111000101010111, // U
0b100011101010111, // V
0b111000111010101, // W
0b100010111010111, // X
0b111000101110101, // Y
0b100011101110101, // Z
0b101000111011101, // 0
0b111010001010111, // 1
0b101110001010111, // 2
0b111011100010101, // 3
0b101000111010111, // 4
0b111010001110101, // 5
0b101110001110101, // 6
0b101000101110111, // 7
0b111010001011101, // 8
0b101110001011101, // 9
0b100010101110111, // (moins)
0b111000101011101, // (point)
0b100011101011101, // (espace)
0b100010001000101, // (dollar)
0b100010001010001, // (slash)
0b100010100010001, // (plus)
0b101000100010001, // (modulo)
0b100010111011101 // (étoile)
};
/**
* Buffer pour le texte formaté / normalisé au standard type-39
*
* @var str_buffer
*/
char str_buffer[STR_BUFFER_SIZE];
/**
* Déclaration de l'objet MI0283QT2 pour la sortie vidéo
*/
MI0283QT2 lcd;
/**
* Fonction de dessin d'un caractére type-39
*
* @param index Position du caractéres dans le buffer (0 ~ ...)
* @param chrbin Représentation binaire type-39 du caractére (stocké dans la table ASCII)
*/
void draw_char(uint16_t index, uint16_t chrbin) {
index = (index * 16) + X_OFFSET; // Conversion index -> position en pixels + décalage
// Pour chaque bits (16 bits) du caractéres type-39
for(uint16_t bitmask = 0b1000000000000000; bitmask != 0; bitmask >>= 1, ++index) {
if(chrbin & bitmask) { // Si le bit est à "1" logique
lcd.drawLine(index, Y_OFFSET, index, Y_OFFSET + BARCODE_HEIGHT, COLOR_BLACK); // Traçage d'une ligne noir
}
}
}
/**
* Fonction de génération d'un code barre type 39
*
* @param str Texte à inclure dans le code barre
*/
void draw_barcode(const char *str) {
// Normalisation en standard type-39 de la chaine de caractére
byte i_str = 0, i_buf = 1;
while(str[i_str] != 0 && i_buf < (STR_BUFFER_SIZE - 3)) { // Pour chaque caractère du texte (dans la limite du buffer)
switch(str[i_str]) {
case 'a'...'z':
str_buffer[i_buf] = str[i_str] - 32; // Stockage en majuscule
break;
case 'A'...'Z':
case '0'...'9':
case '-':
case '.':
case ' ':
case '$':
case '/':
case '+':
case '%':
//case '*':
str_buffer[i_buf] = str[i_str]; // Stockage tel quel
break;
default:
++i_str; // Tout autre caractéres (non valide) sont ignorés
continue;
}
++i_str;
++i_buf;
}
// Dessine le code barre seulement si la chaîne de caractères finale n'est pas vide
if(i_buf == 0) return;
// Ajoute les délimiteurs du code barre (* + texte + * + \0)
str_buffer[0] = '*';
str_buffer[i_buf] = '*';
str_buffer[++i_buf] = '\0';
// Dessine chaque caractère
i_buf = 0;
uint16_t chrbin;
while(str_buffer[i_buf] != 0) { // Jusqu'à la fin de la chaîne de caractères
switch(str_buffer[i_buf]) { // Suivant le caractère
case 'A'...'Z':
chrbin = pgm_read_word(ascii_table + (str_buffer[i_buf] - 'A'));
break;
case '0'...'9':
chrbin = pgm_read_word(ascii_table + (str_buffer[i_buf] - '0') + 26);
break;
case '-':
chrbin = pgm_read_word(ascii_table + 36);
break;
case '.':
chrbin = pgm_read_word(ascii_table + 37);
break;
case ' ':
chrbin = pgm_read_word(ascii_table + 38);
break;
case '$':
chrbin = pgm_read_word(ascii_table + 39);
break;
case '/':
chrbin = pgm_read_word(ascii_table + 40);
break;
case '+':
chrbin = pgm_read_word(ascii_table + 41);
break;
case '%':
chrbin = pgm_read_word(ascii_table + 42);
break;
case '*':
chrbin = pgm_read_word(ascii_table + 43);
break;
}
draw_char(i_buf, chrbin); // Affiche du caractère
++i_buf;
}
}
/* Setup */
void setup() {
lcd.init(2); // Initialisation de l'écran lcd
lcd.led(30); // Rétro éclairage à 30%
lcd.clear(COLOR_WHITE); // Écran blanc
draw_barcode("HELLO WORLD"); // Message d'exemple, uniquement limité par la taille de l'écran en horizontal
}
/* Loop() */
void loop() {
// Rien à faire ...
}
–
Le résultat n°1 (version lcd) :
–> Le résultat sur l’écran
–> Le code barre généré pour le texte : "Hello World"
(vous pouvez vous amuser à le scanner, promis pas de trucage c’est bien l’arduino qui l’a généré
)
–> Le résultat du scan avec "barcode scanner" pour android
–
Le montage n°2 :
Le deuxième montage n’est rien d’autre qu’une adaptation du premier pour montrer comment porter le code sur une autre plate-forme.
Et oui, quand je code quelque chose j’essaye toujours de rendre les choses génériques ![]()
Vous pouvez utiliser mon code avec n’importe quel système graphique dans la mesure ou il peut dessiner des lignes droites
Ce deuxième montage utilise la librairie TVout pour afficher le code barre sur une télévision.
Par contre la résolution horizontale étant moindre que celle de l’écran lcd il n’est pas possible d’afficher autant de texte.
(en bonus j’ai ajouté un affichage (centré) du texte en dessous du code barre … pour le fun
)
–
Le code n°2 (version TVout) :
Testé et commenté avec amour comme le premier
/*
* Générateur arduino de code barre type-39 (version Tvout)
*/
/* Includes */
#include <avr/pgmspace.h> // Pour PROGMEM
#include <TVout.h> // Pour TV-out (librairie principale)
#include <fontALL.h> // Pour TV-out (police de caractères)
/* Defines */
#define STR_BUFFER_SIZE 9 // Taille du buffer pour les caractères (taille max du texte + 2 étoiles + \0)
#define BARCODE_HEIGHT 64 // Hauteur du code barre en pixels
#define X_OFFSET 4 // Décalage en X
#define Y_OFFSET 10 // Décalage en Y
/**
* Table Ascii des caractères utilisables en type-39
*
* @var ascii_table
*/
const uint16_t PROGMEM ascii_table[44] = { // Ratio 1/3 -> "0"=x "1"=xxx
0b111010100010111, // A
0b101110100010111, // B
0b111011101000101, // C
0b101011100010111, // D
0b111010111000101, // E
0b101110111000101, // F
0b101010001110111, // G
0b111010100011101, // H
0b101110100011101, // I
0b101011100011101, // J
0b111010101000111, // K
0b101110101000111, // L
0b111011101010001, // M
0b101011101000111, // N
0b111010111010001, // O
0b101110111010001, // P
0b101010111000111, // Q
0b111010101110001, // R
0b101110101110001, // S
0b101011101110001, // T
0b111000101010111, // U
0b100011101010111, // V
0b111000111010101, // W
0b100010111010111, // X
0b111000101110101, // Y
0b100011101110101, // Z
0b101000111011101, // 0
0b111010001010111, // 1
0b101110001010111, // 2
0b111011100010101, // 3
0b101000111010111, // 4
0b111010001110101, // 5
0b101110001110101, // 6
0b101000101110111, // 7
0b111010001011101, // 8
0b101110001011101, // 9
0b100010101110111, // (moins)
0b111000101011101, // (point)
0b100011101011101, // (espace)
0b100010001000101, // (dollar)
0b100010001010001, // (slash)
0b100010100010001, // (plus)
0b101000100010001, // (modulo)
0b100010111011101 // (étoile)
};
/**
* Buffer pour le texte formaté / normalisé au standard type-39
*
* @var str_buffer
*/
char str_buffer[STR_BUFFER_SIZE];
/**
* Déclaration de l'objet TVout pour la sortie vidéo
*/
TVout TV;
/**
* Fonction de dessin d'un caractère type-39
*
* @param index Position du caractère dans le buffer (0 ~ ...)
* @param chrbin Représentation binaire type-39 du caractère (stocké dans la table ASCII)
*/
void draw_char(uint16_t index, uint16_t chrbin) {
index = (index * 16) + X_OFFSET; // Conversion index -> position en pixels + décalage
// Pour chaque bits (16 bits) du caractère type-39
for(uint16_t bitmask = 0b1000000000000000; bitmask != 0; bitmask >>= 1, ++index) {
if(chrbin & bitmask) { // Si le bit est à "1" logique
TV.draw_line(index, Y_OFFSET, index, Y_OFFSET + BARCODE_HEIGHT, BLACK); // Traçage d'une ligne noir
}
}
}
/**
* Fonction de génération d'un code barre type 39
*
* @param str Texte à inclure dans le code barre
*/
void draw_barcode(const char *str) {
// Normalisation en standard type-39 de la chaine de caractére
byte i_str = 0, i_buf = 1;
while(str[i_str] != 0 && i_buf < (STR_BUFFER_SIZE - 3)) { // Pour chaque caractére du texte (dans la limite du buffer)
switch(str[i_str]) {
case 'a'...'z':
str_buffer[i_buf] = str[i_str] - 32; // Stockage en majuscule
break;
case 'A'...'Z':
case '0'...'9':
case '-':
case '.':
case ' ':
case '$':
case '/':
case '+':
case '%':
//case '*':
str_buffer[i_buf] = str[i_str]; // Stockage tel quel
break;
default:
++i_str; // Tout autre caractère (non valide) est ignoré
continue;
}
++i_str;
++i_buf;
}
// Dessine le code barre seulement si la chaine de caractères finale n'est pas vide
if(i_buf == 0) return;
// Ajoute les délimiteurs du code barre (* + texte + * + \0)
str_buffer[0] = '*';
str_buffer[i_buf] = '*';
str_buffer[++i_buf] = '\0';
// Dessine chaque caractéres
i_buf = 0;
uint16_t chrbin;
while(str_buffer[i_buf] != 0) { // Jusqu'à la fin de la chaine de caractères
switch(str_buffer[i_buf]) { // Suivant le caractère
case 'A'...'Z':
chrbin = pgm_read_word(ascii_table + (str_buffer[i_buf] - 'A'));
break;
case '0'...'9':
chrbin = pgm_read_word(ascii_table + (str_buffer[i_buf] - '0') + 26);
break;
case '-':
chrbin = pgm_read_word(ascii_table + 36);
break;
case '.':
chrbin = pgm_read_word(ascii_table + 37);
break;
case ' ':
chrbin = pgm_read_word(ascii_table + 38);
break;
case '$':
chrbin = pgm_read_word(ascii_table + 39);
break;
case '/':
chrbin = pgm_read_word(ascii_table + 40);
break;
case '+':
chrbin = pgm_read_word(ascii_table + 41);
break;
case '%':
chrbin = pgm_read_word(ascii_table + 42);
break;
case '*':
chrbin = pgm_read_word(ascii_table + 43);
break;
}
draw_char(i_buf, chrbin); // Affiche du caractère
++i_buf;
}
// Bonus : affichage du texte en dessous du code barre
uint8_t text_align_center_offset = ((i_buf - 1) * 8) / 2;
TV.set_cursor(X_OFFSET + text_align_center_offset, Y_OFFSET + BARCODE_HEIGHT + 4);
TV.print(str_buffer);
}
/* Setup */
void setup() {
TV.begin(NTSC, 120, 96); // Mode NTSC, 120x96 pixels
TV.select_font(font8x8); // Police de caractères 8x8 pixels
TV.fill(WHITE); // Écran blanc
draw_barcode("HELLO"); // Petit (très petit même) message, limité par la taille (réduite) de l'écran en horizontal
}
/* Loop() */
void loop() {
// Rien à faire ...
}
–
Le résultat n°2 (version TVout) :
–> Résultat sur l’écran avec le message "Hello" (on peut déja voir la limite que cause le manque de résolution …)
–> Le résultat du scan avec "barcode scanner" pour android









Salut skywodd super tuto mais le plus impressionant c’est le prix de l’écran (vu sur un site allemand) 20 euros !!!
À ce prix je vais m’en acheter un mais y’a t-il un site français qui le propose ????
Publié par Fabien | 21 août 2012, 10 h 38 min>> Salut skywodd super tuto mais le plus impressionant c’est le prix de l’écran (vu sur un site allemand) 20 euros !!!
Ce genre d’écran TFT coute moins de 15$ en chine
C’est des écrans hyper classique que tu peut trouver partout sur ebay ou dealextreme.
>> À ce prix je vais m’en acheter un mais y’a t-il un site français qui le propose ????
Cet écran + shield est une exclusivité de watterott (conçu et fabriqué par eux).
J’achète tout mon matos chez eux, tu trouvera rien d’équivalent en prix/qualité en France.
)
C’est une trés bonne boutique que je ne peut que recommander ("qualité Allemande" comme dirait la pub
Publié par skywodd | 21 août 2012, 14 h 49 min