Yahoo refuse tous les emails du site. Si vous avez une adresse chez un autre prestataire, c'est le moment de l'utiliser
En cas de soucis, n'hésitez pas à aller faire un tour sur la page de contact en bas de page.
Transformer une carte son ou un lecteur audio en port série TTL
For fun and profit

par skywodd | | Licence (voir pied de page)
Catégories : Projets | Mots clefs : Musique Serial UART Hack
Cet article n'a pas été mis à jour depuis un certain temps, son contenu n'est peut être plus d'actualité.
Dans ce second mini projet, je vais vous montrer comment utiliser une carte son ou n'importe quel lecteur audio en tant que port série TTL. Ce projet pourra vous servir de base pour concevoir des applications utilisant une sortie audio pour transmettre des trames série, infrarouge, voir même radio.
Sommaire
Bonjour à toutes et à tous !
Dans un précédent article, je vous avais montré comment transformer un port série en carte son minimaliste. C'était un mini projet fort sympathique, mais pas particulièrement utile dans la vraie vie.
Aujourd'hui, vous propose de faire l'exact opposé de mon précédent mini projet, à savoir transformer une carte son (ou plus généralement n'importe quel lecteur audio, smartphone inclus) en port série TTL.
Ce second mini projet peut sembler aussi inutile que le premier, mais c'est loin d'être le cas. Beaucoup de systèmes électroniques actuellement en vente utilisent ce genre d'astuce pour permettre aux utilisateurs de faire des mises à jour avec simplement via un PC, un smartphone ou un bête lecteur MP3.
Le plan
Illustration du format d'une trame série TTL
Dans mon précédent article, j'avais expliqué le format d'une trame série (c'est à dire comment un octet est physiquement transmis).
Je vous invite donc à lire (ou relire) le chapitre à ce sujet
Pour rappel, une trame série "standard" se compose :
D'un bit de start, signifiant le début d'un octet,
De 8 bits de données,
D'un bit de stop, signifiant la fin de l'octet.
De plus, par défaut, le port série est à l'état haut.
Signal d'une trame série TTL dans un fichier audio
L'idée de cet article est de générer par logiciel une trame série et de l'encoder sous la forme d'un fichier audio. Après tout, une carte son est un périphérique capable de sortie des signaux analogiques avec une grande précision (entre 16 et 24 bits pour les meilleures cartes son) et une fréquence assez rapide (au maximum entre 44100 et 48000 échantillons audio par seconde en fonction de la carte son). Or, tout périphérique capable de générer un signal analogique peut générer un signal numérique comme une trame série. Un signal numérique n'est rien de plus qu'un signal analogique avec seulement deux états possibles.
Le montage
Avant de générer le fichier audio, il faut être sûr que la partie matérielle va suivre le rythme. Pour cela, il va falloir réaliser un petit montage, assez simple, réalisable avec seulement 6 composants.
Le but de ce montage est de convertir le signal audio (+/- 1 volt) en un signal numérique "propre" avec deux états : 0 volt et 5 volts. Pour ce faire, le montage utilise un amplificateur opérationnel en comparateur inverseur de tension.
Pour réaliser ce premier montage, il va nous falloir :
Un module USB-série (et son câble USB),
Un amplificateur opérationnel fonctionnant en 5 volts (j'utilise ici un LM324, mais un TLC274 ferait aussi l'affaire, moyennant une modification du câblage),
Deux résistances de 4.7K ohms, code couleur jaune – violet – rouge,
Un potentiomètre de 10K ohms,
Un connecteur audio jack 3.5mm,
Une plaque d'essai et des fils pour câbler notre montage.
Pour commencer le câblage, il va falloir mettre la masse du module USB-série à la broche GND
de l'amplificateur opérationnel (broche 11 du LM324).
Il faudra aussi connecter le second anneau de la prise audio (celui du côté du câble) à la masse du module USB-série.
Il conviendra ensuite de relier l'alimentation du module USB-série à la broche VCC
de l'amplificateur opérationnel (broche 4 du LM324).
Vient ensuite le potentiomètre de réglage de 10K. Il faudra le câbler entre la masse du module USB-série et l'alimentation de celui-ci. La sortie du potentiomètre devra elle être connectée à l'entre non inverseuse de l'amplificateur opérationnel (broche 3 du LM324).
Il faudra câbler la pointe de la prise audio (annotée "tip") sur l'entrée inverseuse de l'amplificateur opérationnel (broche 2 du LM324). Il faudra aussi prendre soin de mettre une résistance de 4.7K ohms entre la pointe de la prise audio et la masse du circuit, de manière à ce que la carte son ou le lecteur audio génère une tension.
Une fois tout cela câblé, il suffit de câbler la sortie de l'amplificateur opérationnel (broche 1 du LM324) à l'entrée RX
du module USB-série pour terminer le montage.
Il faudra là aussi prendre soin de mettre une résistance de 4.7K ohms entre la sortie de l'amplificateur et l'alimentation du module USB-série, de manière à ce que l'amplificateur fonctionne correctement.
Le code
Le plus gros de cet article réside dans la partie programme qui génère le fichier audio. Pour garder le code simple et lisible, j'ai décidé de faire un script Python 3.
1 2 3 4 5 | # Vitesse de communication série
SERIAL_BAUDRATE = 2400
# Fréquence d'échantillonnage (min 4000 ~ max 48000)
AUDIO_SAMPLE_RATE = 44100
|
Le code commence par la déclaration de la vitesse du port série et la fréquence d'échantillonnage du fichier audio.
N.B. La vitesse du port série doit être au moins dix fois inférieur à la fréquence du fichier audio pour que le script fonctionne (un octet = 10 bits).
Une carte son ou un lecteur audio supporte en général une fréquence maximum de 44 100 Hertz, voir 48 000 Hertz pour les cartes son de PC. Certaines cartes son haut de gamme peuvent aller jusqu'à 96 000 Hertz.
Pour une compatibilité maximum avec les différents types de cartes son et lecteurs audio, j'utilise une fréquence de port série "audio" à 2400 bits par seconde et une fréquence d'échantillonnage de 44100 Hertz.
1 2 3 4 5 6 7 8 9 10 11 12 13 | def convert_binary_file(input_filename, output_filename):
"""
Transforme le fichier binaire fourni en un fichier audio contenant les données binaires du fichier source sous forme de trames série TTL.
"""
# Ouvre le fichier d'entrée et de sortie
with open(input_filename, 'rb') as fi:
with wave.open(output_filename, 'wb') as fo:
# Configure l'entête du fichier Wave
fo.setnchannels(1) # Mono
fo.setsampwidth(1) # 8 bits
fo.setframerate(AUDIO_SAMPLE_RATE)
|
La fonction convert_binary_file()
s'occupe de la conversion des données brutes en fichier audio.
La fonction commence par ouvrir le fichier source et le fichier de sortie en mode binaire. La fonction configure ensuite l'entête du fichier audio pour obtenir en sortie un fichier audio mono canal, 8 bits, non compressés, à la fréquence d'échantillonnage désirée.
1 2 3 4 5 6 7 8 9 10 11 | # Calcul le nombre d'échantillons audio par bit
# Plus il y a d'échantillons par bit plus la qualité du signal audio sera grande
# Une fréquence d'échantillonnage élevé donnera donc les meilleurs résultats
NB_SAMPLES_PER_BIT = AUDIO_SAMPLE_RATE // SERIAL_BAUDRATE
# Constantes pour la communication série
BIT_ONE = b'\x00' * NB_SAMPLES_PER_BIT
BIT_ZERO = b'\xFF' * NB_SAMPLES_PER_BIT
FRAME_BREAK = BIT_ZERO * 20
FRAME_IDLE = BIT_ONE * 20
writeframes = fo.writeframes
|
Plusieurs constantes sont ensuite calculées par la fonction :
le nombre d'échantillons audio par bit du port série,
l'équivalent audio d'un "1" et d'un "0",
l'équivalent audio d'un "break" (coupure de signal série) et d'un blanc (pas de transmission série).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # Début du fichier audio
writeframes(FRAME_BREAK)
writeframes(FRAME_IDLE)
# Conversion octet par octet
for data in fi.read():
# Ajoute un bit de start au buffer audio
writeframes(BIT_ZERO)
# Ajoute 8 bits de données au buffer audio
for i in range(0, 8):
if data & (1 << i):
writeframes(BIT_ONE)
else:
writeframes(BIT_ZERO)
# Ajoute un bit de stop au buffer audio
writeframes(BIT_ONE)
# Fin du fichier audio
writeframes(FRAME_IDLE * 10)
|
Pour finir, une boucle lit chaque octet du fichier source, convertit l'octet en données audio et ajoute les bits de start et de stop.
N.B. Un break suivi d'un blanc est ajouté en début de fichier audio pour être sûr que le port série recevant les données est dans un état connu. Dix blancs en fin de fichier audio permettent de s'assurer que les données en fin de communication sont bien lues correctement. Les lecteurs audio ont toujours tendance à faire n'importe quoi en fin de lecture.
Le code complet avec commentaires :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | """
Transforme un fichier binaire en un fichier audio contenant les données binaires sous forme de trames série TTL.
"""
import wave
import argparse
# Vitesse de communication série
SERIAL_BAUDRATE = 2400
# Fréquence d'échantillonnage (min 4000 ~ max 48000)
AUDIO_SAMPLE_RATE = 44100
def convert_binary_file(input_filename, output_filename):
"""
Transforme le fichier binaire fourni en un fichier audio contenant les données binaire du fichier source sous forme de trames série TTL.
"""
# Ouvre le fichier d'entrée et de sortie
with open(input_filename, 'rb') as fi:
with wave.open(output_filename, 'wb') as fo:
# Configure l'entête du fichier Wave
fo.setnchannels(1) # Mono
fo.setsampwidth(1) # 8 bits
fo.setframerate(AUDIO_SAMPLE_RATE)
# Calcul le nombre d'échantillons audio par bit
# Plus il y a d'échantillons par bit plus la qualité du signal audio sera grande
# Une fréquence d'échantillonnage élevé donnera donc les meilleurs résultats
NB_SAMPLES_PER_BIT = AUDIO_SAMPLE_RATE // SERIAL_BAUDRATE
# Constantes pour la communication série
BIT_ONE = b'\x00' * NB_SAMPLES_PER_BIT
BIT_ZERO = b'\xFF' * NB_SAMPLES_PER_BIT
FRAME_BREAK = BIT_ZERO * 20
FRAME_IDLE = BIT_ONE * 20
writeframes = fo.writeframes
# Début du fichier audio
writeframes(FRAME_BREAK)
writeframes(FRAME_IDLE)
# Conversion octet par octet
for data in fi.read():
# Ajoute un bit de start au buffer audio
writeframes(BIT_ZERO)
# Ajoute 8 bits de données au buffer audio
for i in range(0, 8):
if data & (1 << i):
writeframes(BIT_ONE)
else:
writeframes(BIT_ZERO)
# Ajoute un bit de stop au buffer audio
writeframes(BIT_ONE)
# Fin du fichier audio
writeframes(FRAME_IDLE * 10)
# CLI wrapper
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Turn binary data into a serial-like signal audio wave file.')
parser.add_argument('input_filename', metavar='<INPUT FILE>', help='The input binary file.')
parser.add_argument('output_filename', metavar='<OUTPUT FILE>', help='The output audio wave file.')
args = parser.parse_args()
convert_binary_file(args.input_filename, args.output_filename)
|
L'extrait de code ci-dessus est disponible en téléchargement sur cette page.
Le résultat
Pour tester le bon fonctionnement du montage et du code, j'ai converti une page de "Lorem Ipsum" en fichier audio avant de lire ledit fichier via la carte son de mon PC.
Exemple de transmission de données
Tout semble fonctionner à première vue. Les données sont biens reçues dans le terminal série en même temps que le fichier audio est lu.
Pas d'erreur de communication en vue
Pour m'assurer que la communication est parfaite (pas d'erreur de transmission ou de perte de données), j'ai enregistré l'intégralité des données reçues dans un fichier que j'ai comparé à l'original avec une somme de contrôle SHA-256. Résultat : aucune erreur de communication détectée, les données émises et reçues ne sont identiques.
Bonus : des idées de projets dérivés
Dans cet article, je me suis limité à faire de l'émission. Il serait tout à fait envisageable de faire de la réception avec un système d'écoute audio en temps réel. Toutes les cartes son d'ordinateurs ont une entrée micro. Seulement cela est (beaucoup) plus complexe à réaliser en terme de code.
Cependant, simplement en faisant de l'émission de signaux, on pourrait imaginer faire des choses fort sympathiques en modifiant le code ci-dessus. Voici quelques idées :
reprogrammer un microcontrôleur,
commander une télévision en simulant les signaux infrarouges de la télécommande,
transmettre des données via un module radio,
etc.
Conclusion
Ce mini projet est désormais terminé.
Si ce mini projet vous a plu, n'hésitez pas à le commenter sur le forum, à le partager sur les réseaux sociaux et à soutenir le site si cela vous fait plaisir.