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 « semi-graphique » où 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 de communication série 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)
- à la position du curseur :
- …
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 pourraient 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.
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 |
![]() |
![]() |
![]() |
![]() |
![]() |
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.
convertirMinitel(img)
qui renvoie la liste d’octets constituant la trame à envoyer.
Envoi au minitel
Quel que soit le moyen choisi pour envoyer les données au Minitel, il faut commencer par convertir la liste d’entiers en tableau d’octet, une type à part entière appelé bytearray.
b = bytearray(m)
Version fichier
try: with open(fichier_trame, 'wb') as f: f.write(bytearray(m)) except: print("Impossible d'écrire dans le fichier %s !" %fichier_trame) sys.exit(1)
Version serveur Web
Pour la version Web, on commence par afficher la trame au format base64 (un format qui permet de convertir un tableau d’octets en une chaîne de caractères « imprimables »
print(base64.b64encode(bytearray(m)))
Ensuite, à l’aide d’un « simple » copier coller, on peut passer cette chaîne de caractères comme paramètres dans une URL :
http://172.16.16.177/?img=KAAAAAAAABQA ... AAAAA
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.attribut()
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);
convertir8(img)
qui renvoie une image convertie en 8 niveaux de gris.
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 :