Bonjour tout le monde !
Fut un temps je cherchais un moyen de programmer un ATmega de la façon la plus tordu possible (oui j’ai que ça à foutre).
Dans le genre bien tordu j’avais trouvé ce programmateur d’AVR à base de clavier usb :
http://hackaday.com/2012/11/26/usb-keyboard-becomes-an-avr-programmer/
J’ai eu beau chercher je n’ai pas pu trouver d’idée encore plus tordue …
Du coup je suis parti sur une autre idée, émuler un port série au moyen d’une carte son !
–
Le montage :
Pour ce montage j’ai piqué le schéma ici :
http://robots-everywhere.com/re_wiki/index.php?title=Serial_on_Android_using_the_audio_port
En réalité c’est un bête montage avec un amplificateur opérationnel (en saturation), monté en inverseur.
Quand l’entrée dépasse le seuil fixé par le potentiomètre la sortie bascule.
Cela permet d’avoir un signal TTL 0v/5v "propre" à partir d’un signal audio de +/- 1v d’amplitude.
Comme une carte son possède (dans 99.9% des cas) une sortie stéréo il est possible d’avoir deux sorties.
Du coup on peut imaginer émuler autre chose qu’un port série, voire même faire des sorties TTL tout ce qui à de plus classique.
Remarque : dans mon montage physique (pas sur le schéma) j’ai inversé par erreur les entrées V+ et V-, du coup j’ai un montage non inverseur.
Ne vous faites pas avoir, il faut bien un montage inverseur, vous comprendrez par la suite pourquoi
–
Le prototype :
Sur la gauche les deux sorties et les alimentations +5v et GND.
Et juste à côté l’entrée audio (gauche, droite, masse).
Pour l’amplificateur opérationnel j’utilise un LM393 qui a l’avantage de fonctionner sans problème en +5V, contrairement à un TL082 par exemple qui a besoin d’une alimentation symétrique.
Oui je sais c’est dégueulasse …
(ne pas vous fier à mon câblage, rappel : j’ai inversé V+ et V-)
–
Découpage d’une trame série :
Pour pouvoir vous expliquer rapidement la composition d’une trame série j’avais besoin d’une trame.
L’image ci dessous provient donc de batsocks.co.uk et est disponible dans son contexte d’origine ici :
http://www.batsocks.co.uk/products/Other/SweeperMeter_Technical.htm
Une trame série se décompose en plusieurs parties :
- un bit de start
- 8 (ou 7) bits de données
- 1 (ou 2) bit de stop
Le bit de start correspond à un niveau bas (0v), un bit de stop correspond à un niveau haut (5v).
Au repos le signal doit être à l’état haut (historiquement cela permettait de détecter une coupure sur la ligne).
Concernant la durée des temps haut / temps bas c’est très simple à calculer.
Imaginons que l’on souhaite une vitesse de communication de 9600 bauds par seconde.
9600 bauds/s = 9600 bits/s -> durée d’une période = 1 / fréquence = 1 / 9600 = 0.104 millisecondes
De même pour connaître la durée d’une trame complète il suffit de multiplier le tout par le nombre de bits par octet.
Ici avec 8 bits de données et 1 bits de stop :
1 bit de start + 8 bits de données + 1 bits de stop = 10 bits
10 bits = 10 x (1 / 9600) = 1.04 millisecondes
Un niveau haut de plus d’une fois la durée d’un octet s’appelle un "break", si un récepteur série reçoit un "break" il est obligé de se recaler sur le bit de start qui suit, cela permet de ne pas perdre la synchronisation avec le signal série.
Remarque : on peut aussi déduire la vitesse réelle de transmission en octets par seconde :
9600 bits/s / 10 bits = 960 octets par seconde
–
Le logiciel :
Comprendre le fonctionnement d’une communication série asynchrone c’est une chose, émuler un signal série en est une autre.
Et émuler un signal série avec une carte son d’ordinateur est encore une autre histoire.
Pour me simplifier la tache j’ai conçue un script python prenant un fichier (texte ou binaire) en entrée et générant un fichier wave en sortie.
Pour lire le fichier il suffit d’ouvrir un éditeur comme audacity par exemple ou n’importe quel lecteur de musique supportant la lecture des .wav.
Voici un exemple de fichier audio généré et ouvert sous audacity :
Note : le signal présenté ci dessus est volontairement non-inversé, il ne devrait pas être dans le même sens que sur le schéma vu plus haut.
L’amplificateur inversant le signal audio celui ci doit être lui inversé pour au final avoir un signal série avec des niveaux logiques corrects.
Vous vous demandez sûrement pourquoi inverser le signal ?
Réfléchissons une petite seconde.
Le signal série doit être à l’état haut au repos, la carte son elle ne sort aucun signal au repos.
En utilisant un montage inverseur le signal est de base au niveau haut et passe au niveau bas lorsque la carte son envoie un signal.
il faut donc inverser le signal audio pour que le montage inverse de nouveau le signal et que celui ci finisse par être dans "le bon sens".
L’avantage de cette méthode est que si l’on débranche l’entrée audio le récepteur série ne se retrouvera pas avec un signal corrompu mais avec un "break" constant.
Cela évite aussi d’avoir à générer un signal haut avec la carte son au repos.
Le code du script (avec des commentaires pour expliquer chaque sous-parties) :
# -- Imports --
# sys: for argv / argc
# wave: for wave file generation
# struct: for pack
import sys, wave, struct
# Vitesse de communication série
SERIAL_RATE = 2400
# Fréquence d'échantillonnage (min 4000 ~ max 48000)
SAMPLE_RATE = 48000
# Vérification des arguments CLI
if len(sys.argv) < 3:
print "USAGE: data2wav.py <input file> <output file>"
exit(1)
# Nom de fichier source / sortie
i_filename = sys.argv[1]
o_filename = sys.argv[2]
# Ajoute l'extension .wav au fichier de sortie si nécessaire
if o_filename[-4:] != '.wav':
o_filename += '.wav'
# Ouvre le fichier source (en mode binaire)
fi = open(i_filename, 'rb')
if not fi:
print "Cannot open input file: %s" % i_filename
exit(1)
# Ouvre le fichier de sortie (en mode binaire)
fo = wave.open(o_filename, 'wb')
if not fo:
print "Cannot open output file: %s" % o_filename
exit(1)
# Configure l'entête du fichier Wave
fo.setnchannels(1) # Mono
fo.setsampwidth(1) # 8 bits
fo.setframerate(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 = SAMPLE_RATE / SERIAL_RATE
# Buffer audio
audio_output = []
# Ajoute un bit dans le buffer audio
def appendBit(bit):
for i in range(0, NB_SAMPLES_PER_BIT):
if bit: # Inversion du signal : 0 -> 1, 1 -> 0
audio_output.append(struct.pack('B', 0))
else:
audio_output.append(struct.pack('B', 255))
# Ajoute un "break" dans le buffer audio
def serialBreak():
for i in range(0, 20):
appendBit(True)
# Début du processus de conversion
b = fi.read(1)
serialBreak()
serialBreak()
while b != '':
# Converti la valeur d'un octet du fichier source en décimal
b = ord(b)
# Ajoute un bit de start au buffer audio
appendBit(False)
# Ajoute 8 bits de données au buffer audio
for i in range(0, 8):
appendBit(b & (1 << i))
# Ajoute un bit de stop au buffer audio
appendBit(True)
# Lit l'octet suivant du fichier source
b = fi.read(1)
# Écrit le contenu du buffer audio dans le fichier de sortie
fo.writeframes(''.join(audio_output))
# Fermeture du fichier source et du fichier de sortie
fi.close()
fo.close()
–
Test IRL :
Le montage finit :
Sur la photo :
- une carte son usb
- un dongle usb/série
- un câble audio jack bricolé
- quelques fils
- et le montage
Il suffit ensuite de lancer la lecture du fichier wave :
Et voila, un joli "Lorem ipsum" transmit au moyen d’un "vrai faux" port série émulé par une carte son !
Peut être qu’un jour on programmera nos arduino au moyen d’un simple lecteur de musique








tu est vraiment un gros taré :O
Publié par zilators | 11 février 2013, 22 h 27 minVoilà le genre de démarche intellectuelle que j’apprécie
Publié par artouste | 11 février 2013, 23 h 44 minau début des années 70 on faisait bien de la musique avec des imprimantes à impact; il y a même eu des essais avec un récepteur à transistors approché de l’unité centrale pour capter le bruit fait par un programme dont on gérait des boucles de durée variable
Publié par noui | 11 février 2013, 23 h 51 minPas mal, pas mal.
Et pourquoi ne pas rajouter des LED Infra rouge émettrices derrière les AOP pour pouvoir changer les chaines de sa TV en RC5 !
Publié par \Fab\ | 12 février 2013, 0 h 22 mintrès cool mon pot, très génial. tu n’as rien a proposé sur le hacking des souris optiques USB?
Publié par segun | 16 février 2013, 7 h 17 minprogramer avec un lecteur de musique portable sa peut etre une bonne idée
Publié par yui | 20 mars 2013, 23 h 36 min