3615 TuVeuxMaPhoto?

Minitel ? Késako ?

Le Minitel (Médium interactif par numérisation d’information téléphonique) est un type de terminal informatique destiné à la connexion au service français de Vidéotex baptisé Télétel, commercialement exploité en France entre 1980 et 2012.

source : https://fr.wikipedia.org/wiki/Minitel

L’ensemble Minitel+Télétel était en quelque sorte l’ancêtre du Web actuel !

 

Le réseau Télétel du Minitel était accessible par différents numéros courts, dont le plus populaire était le 3615.

Il permettait l’accès, payant (60 francs – 9,15 € – par heure environ, payés par l’usager, dont 40 F – 6,10 € – pour le service et 20 F – 3,05 € – pour France Télécom), à de très nombreux services (jusqu’à 25 000 en 1996 !) :

  • presse écrite
  • jeux
  • vente par correspondance
  • SNCF
  • banques
  • rencontres
  • et même des recherches d’emploi :

Pour en savoir plus sur le Minitel : https://larevuedesmedias.ina.fr/du-minitel-linternet

 

L’objectif de ce mini projet est de redonner vie à cet objet mythique de l’histoire de l’informatique et des communications !

 


Utilisation du Minitel avec un Arduino

Le Minitel permet la transmission de données par voie série au moyen d’un connecteur DIN situé à l’arrière, avec des débits pouvant atteindre 4 800 bits/s, et même 9 600 bits/s avec le Minitel 2.

  • 2 : masse (GND)
  • 3 : émission de données (Tx)
  • 1 : réception de données (Rx)

 

A l’aide d’un microcontrôleur Arduino, on peut communiquer avec le minitel via le protocole série RS-232.

 

 

Coté écran, on peut bien sûr afficher du texte (encodage spécial dérivé de l’ASCII), mais il existe également un mode « graphique » : chaque caractère peut être décomposé en 6 pseudo-pixels.

Par exemple le caractère suivant, qui possède 4 pseudo-pixels blancs et 2 noirs, sera codé par le nombre (écrit en binaire) : 0b100111

 

 

Coté Arduino, nous utiliserons la bibliothèque Minitel1B_Hard.

#include <Minitel1B_Hard.h>

Une fois le Minitel relié aux ports Rx et TX de l’Arduino, il faut créer l’objet représentant le Minitel :
Minitel minitel(Serial);

puis configurer la vitesse de communication :
minitel.changeSpeed(minitel.searchSpeed());

Ensuite, cette bibliothèque permet, entre autres fonctionnalités très intéressantes, de :

  • effacer l’écran : minitel.newScreen()
  • passer en mode graphique : minitel.graphicMode()
  • écrire un caractère de 6 pseudo-pixels à l’écran :
    • à la position du curseur : minitel.graphic(0b110110)
    • à la position souhaitée (ici x=30, y=15) : minitel.graphic(0b110110, 30, 15)
Code source complet
/*
  Programme d'affichage d'une image en noir&blanc sur un Minitel

  Lecture d'un fichier sur la carte SD
  Affichage via port série

  Voir : https://info.blaisepascal.fr/3615-tuveuxmaphoto
  
 */

#include <SPI.h>
#include <SD.h>

File imgFile;

#include <Minitel1B_Hard.h>
  
// Minitel minitel(Serial);  // Le premier port série matériel de l'ATMega 1284P (RXD0 TXD0), 
                             // ou celui de l'Arduino UNO
Minitel minitel(Serial1);  // Le deuxième port série matériel de l'ATMega 1284P (RXD1 TXD1).

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ;
  }

  // fonction bloquante ... ???
  //minitel.changeSpeed(minitel.searchSpeed());
  minitel.newScreen();
  minitel.graphicMode();
  
  Serial.println("init ...");
  if (!SD.begin(4)) {
    Serial.println("init failed!");
    while (1);
  }
  Serial.println("init done.");

  // Affichage de l'image sur le minitel
  //afficher_image("spock_nb");
  afficher_image_couleur("spock");
}

void loop() {
}


/*********************************************************
  Fonctions d'affichage sur le minitel
**********************************************************/
void afficher_image(String imgFileName){
  imgFile = SD.open(imgFileName);
  if (imgFile) {
    Serial.println(imgFileName);

    // Lecture du fichier image
    byte pixel[1];
    while (imgFile.available()) {
       imgFile.read(pixel,1);
       minitel.graphic(pixel[0]);
       }
    // Fermeture du fichier
    imgFile.close();
    
    } else {
    Serial.print("Erreur ouverture ");
    Serial.println(imgFileName);
  }
}

void afficher_image_couleur(String imgFileName){
  imgFile = SD.open(imgFileName);
  if (imgFile) {
    Serial.println(imgFileName);

    // Lecture du fichier image
    byte pixel[3];
    while (imgFile.available()) {
       imgFile.read(pixel,3);
       minitel.attributs(pixel[0]);
       minitel.attributs(pixel[1]);
       minitel.graphic(pixel[2]);
       }
    // Fermeture du fichier
    imgFile.close();
    
    } else {
    Serial.print("Erreur ouverture ");
    Serial.println(imgFileName);
  }
}

 


L’objectif est de réaliser un programme Python qui permette de convertir n’importe quelle image numérique dans un format compatible avec le protocole d’affichage du Minitel.

Pour la phase de développement, nous utiliserons l’image suivante (cliquer pour télécharger) :

 


Image en noir et blanc

Pour manipuler les images, nous utiliserons la bibliothèque PIL , et plus particulièrement l’objet Image .

Ouverture de l’image

Écrire une fonction ouvrir_image(chemin_fichier) qui renvoie un objet PIL.Image à partir d’un chemin de fichier.

Cette fonction devra gérer les éventuels problèmes qui pourrait se poser :

  • Fichier introuvable : on testera l’existence du fichier à l’aide de la fonction os.path.isfile() ;
  • Fichier incompatible : on encadrera la fonction d’ouverture de fichier image par un try: … except: … .

Si l’image n’a pas pu être ouverte correctement, la fonction renvoie None .

 

 

Redimensionnement

Une fois l’objet Image créé, il faut en modifier les dimensions, car la résolution de l’écran du Minitel (en pseudo-pixels) est de 25×3 lignes pour 40×2 colonnes. (25 étant le nombre de lignes de caractères et 40 le nombres de caractères par ligne).

On définira les variables globales qui définissent les dimensions de l’écran (en caractères) :

# Dimensions écran Minitel
W, H = 40, 24

 

On se propose de coder 3 modes différents de redimensionnement :

  • mode 0 : l’image est simplement redimensionnée aux nouvelles dimensions, il peut donc y avoir une déformation ;

 

  • mode 1 : l’image est tronquée pour conserver le rapport largeur/hauteur d’origine ;

 

  • mode 2 : pour respecter le rapport largeur/hauteur sans tronquer l’image, du noir est ajouté autour.

 

 

 

Écrire une fonction redimensionner(img, mode = 0) qui renvoie un nouvel objet Image aux dimensions adaptées à l’écran du Minitel, en utilisant le mode donné en argument à la fonction.

 

Conversion en Noir et Blanc

La méthode .convert(mode) de l’objet Image renvoie un nouvel objet Image, convertit l’image selon différents modes, qui définissent la manière dont sont codés les pixels (profondeur, …). Voici un extrait de la documentation de PIL donnant quelques uns des modes disponibles :

  • « 1 » (1-bit pixels, black and white, stored with one pixel per byte)
  • « L » (8-bit pixels, black and white)
  • « RGB » (3×8-bit pixels, true color)
  • « RGBA » (4×8-bit pixels, true color with transparency mask)

 

Il existe donc un mode permettant de convertir directement en noir et blanc (2 niveaux !) mais cela ne permet pas de régler le seuil de luminosité délimitant les pixels noirs des pixels blanc.

Nous souhaitons pouvoir spécifier ce seuil afin d’optimiser l’apparence de l’image :

63 95 127 159 191

 

Écrire une fonction convertirNB(img, seuil = 127) qui renvoie une image convertie en 2 niveaux (noir et blanc) selon le seuil de luminosité spécifié (nombre entre 0 et 255).

 

Conversion au format Minitel

La trame d’octets envoyés au Minitel constituera l’ensemble des caractères (contenant chacun 6 pseudo-pixels) les uns à la suite des autres, en partant de la position x = 0 et y = 0 (en haut à gauche de l’écran).

Il va donc falloir construire chacun de ces caractères à partir des pixels de l’image.

Pour obtenir la valeur d’un pixel d’une image, on peut utiliser la méthode .getpixel((x, y)) .

ATTENTION, en mode « 1 » (1 bit par pixel) .getpixel() renvoie soit 0, soit 255.

 

 

Écrire la fonction convertirMinitel(img) qui renvoie la liste d’octets constituant la trame à envoyer.

 

Résultat attendu

 

 


Image en niveaux de gris

Les caractères peuvent être affichés en 8 couleurs différentes. Mais sur un écran en noir et blanc, cela rend des niveaux de gris.

La bibliothèque Arduino Minitel1B_Hard permet de spécifier les 2 couleurs pour chaque caractère : le caractère lui même et son fond.

 

Pour cela, avant d’afficher un caractère, on peut modifier les attributs d’affichage à l’aide de la fonction minitel.attributAttribut Un attribut est un identifiant (un nom) décrivant une information stockée dans une base. Les attributs correspondent aux colonnes si la relation à laquelle il appartient est représentée sous forme de table.()

Voici les codes des couleurs, tirés du code source de la bibliothèque :

// Couleur de caractère
#define CARACTERE_NOIR    0x40
#define CARACTERE_ROUGE   0x41
#define CARACTERE_VERT    0x42
#define CARACTERE_JAUNE   0x43
#define CARACTERE_BLEU    0x44
#define CARACTERE_MAGENTA 0x45
#define CARACTERE_CYAN    0x46
#define CARACTERE_BLANC   0x47
// Couleur de fond
#define FOND_NOIR         0x50
#define FOND_ROUGE        0x51
#define FOND_VERT         0x52
#define FOND_JAUNE        0x53
#define FOND_BLEU         0x54
#define FOND_MAGENTA      0x55
#define FOND_CYAN         0x56
#define FOND_BLANC        0x57

Par exemple, pour modifier la couleur du caractère, on exécutera :

minitel.attribut(CARACTERE_JAUNE);

Écrire une fonction convertir8(img) qui renvoie une image convertie en 8 niveaux de gris.
Chaque caractère ne pouvant contenir que 2 couleurs, il faut trouver une méthode pour convertir chaque groupe de 6 pixels à 8 niveaux de gris en 6 pixels à 2 niveaux de gris, de sorte que les motifs soient visuellement les plus proches possible :
Écrire une fonction convertirMinitel8(img) qui renvoie la liste d’octets constituant la trame à envoyer.
Chaque caractère sera codé par 3 octets consécutifs : la couleur de fond, la couleur du caractère, le code du caractère semi-graphique.
Attention : l’ordre des couleurs ne correspond pas avec celui des niveaux de gris ! Si on ne modifie pas cet ordre on risque d’obtenir ceci :

Voici ce qu’on voit lorsque l’on affiche les couleurs dans l’ordre de leur numéro de code :

 

Résultat attendu

Vous aimerez aussi...

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*

code