Skyduino:~#
Articles
arduino, Corrigé, programmation

[Arduino] Générateur de code barre (type 39)

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

About these ads

Discussion

2 réflexions sur “[Arduino] Générateur de code barre (type 39)

  1. 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

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

Archives

Wow. Dogecoin. Amaze.

Laissez un tip en Dogecoin

DMMNFk6WBVTpx2Wu1Z35GL61QXSd6r6WQx

Suivre

Recevez les nouvelles publications par mail.

Joignez-vous à 674 followers