24
févr.
'12

La face cachée des tickets RATP

Publié le 24 février 2012

Grand utilisateur de tickets de métro (émis par le STIF/RATP), je me suis posé la question un jour (oui, j'ai de ces idées pourries des fois) de savoir comment sont codées les informations sur ce support magnétique. L'idée m'a semblé intéressante à creuser, mais nécessitait un brin de bidouille électronique. L'occasion de tester mon nouveau fer à souder reçu à Noël, et de découvrir l'univers des bandes magnétiques et plus particulièrement celui des tickets de la RATP.

Les bandes magnétiques

J'ai fureté pendant des heures sur Internet, à la recherche de documentation sur la lecture des bandes magnétiques, ainsi que sur les normes de codage employées. J'ai déniché quelques papiers intéressants:

Ces trois ressources sont très intéressantes, le white paper du CCC étant celle qui m'a apporté le plus. J'en ai discuté au boulot avec Tixlegeek, et il m'a lui aussi soumis l'idée d'utiliser la carte son de mon ordinateur portable pour échantillonner les données stockées sur le ticket (à l'instar de ce qui est documenté dans le papier de Joseph Battaglia). Il s'agit là d'un hack archi-simple, vu qu'il suffit de trouver une tête de lecture d'un vieux lecteur cassette et de la souder sur un jack 3.5 puis de le brancher dans la prise micro. La carte son fera le reste.

En ce qui concerne le codage des données sur bande, celui-ci peut se faire de deux manières:

La première méthode est celle employée pour stocker des données audio, mais est peu robuste. La seconde permet de stocker de manière plus robuste, et indépendamment de la vitesse de lecture ou d'écriture, des données binaires. C'est celle qui est préférée dans la grande majorité des systèmes de stockage numériques sur bande.

Le codage de fréquence le plus classique a un joli surnom: F2F. Ou plus généralement connu sous le nom de Aiken Biphase. L'idée est de coder le 0 avec un signal de fréquence F, et le 1 avec un signal de fréquence 2F. L'avantage de ce codage, c'est que peu importe la vitesse de lecture (qui est variable selon les périphériques), le décodage reste fiable. Bon par contre ça complique un brin la phase de décodage. Patrick Gueulle explique très bien cela dans son livre "Carte magnétique et PC".

Traitement du signal

Le papier du CCC [2] présente deux programmes (nommés DAB et DMBS) visant à respectivement décoder le codage Aiken Biphase (F2F) et à extraire les données selon les normes ISO781X. Ces mêmes programmes ont été utilisés par K1wy dans [1]. Le souci c'est que ceux-ci ont été principalement conçus pour les cartes magnétiques rigides à trois pistes, pas forcément pour les tickets de métro. De plus, selon les réglages audio de l'ordinateur servant à la capture (Mic-Boost notamment). Il me fallait une solution plus malléable, car ces deux programmes sont écrits en C et pas forcément bien documentés.

J'ai trouvé plusieurs modules python permettant de réaliser la capture d'un signal sur l'entrée micro et le traitement de celui-ci:

A l'aide de ces modules, j'ai réalisé quelques scripts de capture de signal et de sauvegarde des données sous format PCM et WAV. l'avantage de pouvoir sauvegarder les captures audio est double: on peut facilement refaire des analyses et surtout on peut les lire avec des outils comme Audacity. J'ai donc réalisé un script de capture du signal, et un script de traitement visant à traiter le signal afin de mettre en évidence le codage fréquentiel.

J'ai réalisé les tests de capture à l'aide d'un micro, et utilisé pylab pour afficher l'allure du signal. Pour le coup, c'était assez efficace:

Il m'a fallut ensuite réaliser mon lecteur de bande magnétique à la manière du CCC, à l'aide d'un vieux lecteur de cassette audio, d'un fer à souder, d'un jack 3.5 et d'un cable IDE =). J'ai du sacrifier mon super lecteur CD/K7 de mes 18 ans (de toute façon je n'ai plus de K7), mais c'est pas grave.

Fabrication du lecteur de bande magnétique maison

Premièrement, j'ai démonté le lecteur pour pouvoir accéder à la partie électro-mécanique au niveau du lecteur de K7:

Une fois les têtes de lecture et d'écriture démontées, j'ai pu souder la tête de lecture (stéréo, mais on s'en moque) sur le jack 3,5mm. Bon la méthode de connection étant pas trop documentée, j'ai un peu galéré mais j'y suis arrivé. Pour information, si vous essayez de refaire le montage chez vous et que vous employez un jack 3,5mm stéréo, faites très attention au contact central: il délivre du 5V (cette tension est utile pour les microphones de type Electret). Pour ma part j'ai pas gazé car j'aurais du relier la sortie de la seconde bobine de lecture à la première, pour cumuler les tensions de sortie, alors que dans mon montage je l'ai mise à la masse (donc je n'utilise qu'une seule des deux bobines de lecture).

J'ai utilisé trois fils couplés d'un cable IDE, de manière à avoir de quoi manipuler la tête de lecture et l'éloigner de l'ordinateur portable (pour limiter les interférences). Bon au final ça n'a pas tellement aidé, le cable IDE faisant une merveilleuse antenne. Une meilleure solution serait d'utiliser un cable blindé à trois brins dont le blindage est mis à la masse. Une fois celui-ci connecté à mon ordinateur portable, j'ai pu balayer un ticket de métro et capturer avec mon précédent script le signal issu de la tête de lecture (signal brut).

Décodage du signal et analyse

A l'aide de ce lecteur de bande magnétique fait maison et de mes quelques scripts, j'ai codé un second script permettant de mettre en évidence les données codées sur la bande magnétique, et ça n'a pas été une paire de manche. J'ai réalisé trois décodages pour la forme:

Et voilà les données codées (Aiken Biphase, ou F2F) extraites des captures de signaux:

Le premier est issu d'un ticket neuf, et les deux suivants de deux tickets a priori validés à Saint Lazare (Paris). Il est flagrant de voir qu'un motif général est présent, motif que l'on retrouve en partie dans le ticket de métro neuf. Chose intrigante, le ticket de métro neuf possède moins d'information que les tickets compostés. J'en déduis donc que des données sont ajoutées lors de la validation, et qu'un ticket de métro neuf est codé d'une manière particulière.Si vous regardez bien, il y a de légères variations de période, mais cela est dû au fait que je scanne manuellement, et ma vitesse de balayage n'est pas constante.

J'ai donc entrepris de décoder les premiers bits significatifs communs aux trois tickets, et voici ce qui en ressort:

Ticket neuf:
11111111 00100 11111 11111 11111 0

Ticket #1:
11111111 00100 11111 00110 01001 0

Ticket #2:
11111111 00100 11111 00110 01110 1

Les espaces sont de mon fait ;). J'ai pu déduire le rôle de chacun (ou du moins tenter de deviner) à partir des informations décodées:

Si on regarde de plus près le codage, on peut voir que sur le premier ticket validé les troisième et quatrième séquences de 5 bits correspondent aux valeurs 69 et 6E. Ou plus précisemment aux valeurs 6 et 9, et 6 et 14. Cela ressemble bien aux codes de stations connus de Paris, tels que décrit sur Wikipédia. Cela signifie qu'un de mes tickets a été validé à Saint-Lazare même (code station 0609), l'autre à la station ayant pour code 0614 (inconnu sur Wikipédia oO). Le dernier bit que j'ai repéré semble être un bit de parité.

Le plus drôle dans l'histoire, c'est de voir que le ticket vierge n'a aucune valeur paramétrée, si ce n'est le type de ticket (première séquence de 5 bits). Donc facile à cloner.

Conclusion

La sécurité des tickets de métro ... heuu.... quelle sécurité ? Il est a priori aisé de dupliquer des tickets neufs (aucune limite dans le temps ni dans l'espace}, la seule limitation est technique (il faut un matériel particulier). De même, je n'ai pas détaillé ici le décodage complet mais la suite des bits stockés représentent très certainement la date de validation et l'heure de validation, mais je n'ai pas vraiment cherché à le décoder.

Heureusement qu'il y a le passe Navigo pour tous nous sauver (spéciale dédicace à Nono2357 ;).

17
févr.
'12

Jiwa v3, retour à la case départ

Publié le 17 février 2012

Je suis retourné sur Jiwa aujourd'hui. Je m'attendais à trouver ma plateforme de musique habituelle, mais je me suis rendu compte que cela faisait un bail que je n'y avais pas été. Le site a complètement changé de look et a été repris par *Allomusic* qui en a profité pour le PURifier à l'Hadopi. Vu que je m'étais bien intéressé à cette plateforme il y a de cela quelques années, je me suis demandé si les choses avaient changé. Et puis parce que cette satanée limitation de 30 secondes d'écoute m'a été insupportable.

Jiwa version 3

Comme en témoigne le code source de Jiwa, le site est passé en vesion 3:

<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# jiwavtrois: http://ogp.me/ns/fb/jiwavtrois#">

Jiwa avait fait de nombreux efforts il y a quelques années pour empêcher le téléchargement des fichiers MP3 et leur sauvegarde. J'avais d'ailleurs à l'époque rédigé quelques articles et développé une extension Firefox dédiée. Il semble donc raisonnable de penser que les protections mises en place dans le code sont identiques ou meilleures que les dernières implémentées.

J'ai donc repris mes anciens Proof of Concept, et je me suis attelé à la tâche. J'ai tout simplement réessayé mes outils, sans succès. J'ai donc sorti l'artillerie lourde (paros + firefox) et commencé à regarder les requêtes et là, ce fut la surprise. Une grosse suprise. Non seulement quelques détails ont changé, certes très minimes, mais j'ai pu retrouver des requêtes similaires à celles employées quelques années plus tôt sur Jiwa ... Comme si on avait fait un bond en arrière de quelques années.

C'est reparti comme en 40 !

Autant dans la version précédente de Jiwa, on pouvait toujours écouter sans limite, autant avec la version actuelle Jiwa ne sert que d'appât: les pages redirigent pour l'écoute vers le site d'Allomusic, qui bride énormément les contenus et force un poil la main pour amener le visiteur à prendre un compte Premium.

Il faut dire qu'à l'époque où je m'étais interessé à Jiwa, l'écoute en ligne était toujours possible, ce qui par ailleurs me satisfaisait pleinement. Mais dans la configuration actuelle, je trouve ça un brin décevant de se servir de l'ancienne image de Jiwa pour essayer de remonter l'image d'un autre service.

On s'éloigne quand même du modèle original de Jiwa, et même de celui de Deezer: il ne s'agit plus de streaming gratuit en ligne reposant sur de la pub mais d'une version lite qui vise à convertir les utilisateurs en comptes Premium. Certainement une preuve que le modèle économique qui se veut reposer sur la pub n'est pas viable, comme a pu l'expérimenter Deezer. Sans parler des tarifs faramineux demandés par les majors comme Universal Music.

Alors bon, je ne suis pas un fervent adepte de ce système, bon nombre de lecteurs le savent, et j'ai adapté mes précédents scripts pour prendre en charge la version actuelle de Jiwa. Plusieurs restrictions ont été mises en œuvres pour éviter le téléchargement:

Le détail amusant c'est que même si l'écoute est limitée à 30 secondes, le fichier MP3 complet est téléchargé. Le bridage par user-agent est même complètement inefficace, je soupçonne même le script distant de bugger quasi aléatoirement ...

iJaw nouvelle génération

Pour ceux qui seraient intéressés, je diffuse ci-après le code source de mon Proof of Concept amélioré. Ce bout de code python est un poil sale, mais je ne compte pas le maintenir. C'est une preuve de concept, juste présente pour démontrer ce que j'affirme dans ce billet.

"""
Crapy Jiwa download tool
"""

import re
import sys
import json
import time
from hashlib import md5
from random import choice
import urllib,httplib,urllib2

def filter(s):
        return s.replace('\n',' ')

def randomUA():
        rstr = ''.join([choice('0123456789') for i in range(32)])
        return 'Mozilla Firefox (%s)'%rstr

def getSongDetails(sid):
        r = urllib2.Request('http://www.jiwa.fr/song/%d/play/'%sid)
        resp = urllib2.urlopen(r).read()
        return json.loads(resp)

def lookupSongs(keywords):
        try:
                r = urllib2.Request('http://www.jiwa.fr/search?q=%s'%urllib.quote(keywords))
                resp = urllib2.urlopen(r).read()
                matches = re.findall('/track/.*-([0-9]+)\.html',resp)
                res = []
                for match in list(set(matches))[:5]:
                        res.append(getSongDetails(int(match)))
                return res
        except urllib2.Error,e:
                return None


def dlTrack(sid,filename):
        errors = 0
        while errors<3:
                token = getToken(sid)
                _t = token.split('=')
                url = 'http://m.jiwa.fm/play.php'
                l1 = 'gwqd29ydg7sqys_qsh0'
                l3 = _t[0]
                l4 = _t[1]
                l5 = _t[2]
                l6 = _t[3]
                l7 = md5(l1).hexdigest()
                l8 = md5(l3+l7+str(sid)).hexdigest()
                url = "%s?r=%s&s=%s&t=%s&m=%s&from=1" % (url,l4,str(sid),l8,l6)

                r = httplib.HTTPConnection('m.jiwa.fm:80')
                r.putrequest('GET',url)
                r.putheader('Host','m.jiwa.fm:80')
                r.putheader('User-Agent',randomUA())
                r.endheaders()
                token = r.getresponse().read()
                if token=='-1':
                        errors += 1
                        time.sleep(1)
                else:
                        f = open(filename,'wb')
                        f.write(token)
                        f.close()
                        return True
        return False

def getToken(sid):
        params = *
        's':str(sid),
        *
        p = urllib.urlencode(params)

        r = httplib.HTTPConnection('m.jiwa.fm:80')
        r.putrequest('POST','http://m.jiwa.fm/token.php?r=1')
        r.putheader('Host','m.jiwa.fm:80')
        r.putheader('User-Agent',randomUA())
        r.putheader('Content-Type','application/x-www-form-urlencoded')
        r.putheader('Content-Length',str(len(p)))
        r.endheaders()
        r.send(p)

        token = r.getresponse().read()
        return token


if __name__ == '__main__':

        print '[>] iJaw - A Jiwa search & download tool'
        print '[>] author: virtualabs (http://www.virtualabs.fr)'
        print '[>]'

        if len(sys.argv)>2:
                operator = sys.argv[1]
                if operator == 'search':
                        kw = ' '.join(sys.argv[2:])
                        print '[+] Looking for songs ...'
                        tracks = lookupSongs(kw)
                        if tracks:
                                if len(tracks)>0:
                                        print ''
                                        print 'Song ID  - Song Name [Artist, Album]'
                                        print ''
                                        for t in tracks:
                                                print '%s - %s [%s, %s]' % (t['songId'],filter(t['songName']),filter(t['artistName']),filter(t['albumName']))
                                        print ''
                                        print '%d songs found' % len(tracks)
                                else:
                                        print '[!] No songs found.'
                        else:
                                print '[!] No songs found.'
                elif operator == 'get':
                        tracks = [t for t in sys.argv[2:]]
                        for sid in tracks:
                                print '[+] getting track info ...'
                                _track = getSongDetails(int(sid))
                                filename = '%s_%s_%s.mp3' % (_track['artistName'].replace(' ','_'),_track['albumName'].replace(' ','_'),_track['songName'].replace(' ','_'))
                                print '[+] downloading track ...'
                                if dlTrack(sid,filter(filename)):
                                        print '[i] saved to %s.' % filter(filename)
                                else:
                                        print '[!] Unable to download song.'
                elif operator == 'dl':
                        kw = ' '.join(sys.argv[2:])
                        tracks = lookupSongs(kw)
                        if tracks:
                                for t in tracks:
                                        _track = getSongDetails(t['songId'])
                                        filename = '%s_%s_%s.mp3' % (_track['artistName'].replace(' ','_'),_track['albumName'].replace(' ','_'),_track['songName'].replace(' ','_'))
                                        print '[+] downloading song "%s [%s]" ...' % (_track['songName'],_track['artistName'])
                                        if dlTrack(int(t['songId']),filter(filename)):
                                                print '[i] saved to %s.' % filter(filename)
                                        else:
                                                print '[!] Failed to download song: %s' % _track['songName']
                                print '[+] Done.'
                        else:
                                print '[!] No songs found'
        else:
                print '[i] usage: %s [search|get|dl] [second arg]'
                print '[i]'
                print '[i] + you can look for songs:'
                print '[i]'
                print '[i]  $ ijaw.py search Madonna like a virgin'
                print '[i]  $ 12345 - Like a virgin [Madonna, ...]'
                print '[i]'
                print '[i] + and download a given song with its ID:'
                print '[i]  $ ijaw.py get 12345'
                print '[i]'
                print '[i] + or download all tracks found:'
                print '[i]'
                print '[i]  $ ijaw.py dl Madonna like a virgin'
                print '[i]'
                print '[i] + Have phun !'

Conclusion

Il est étonnant que le passage de Jiwa sous la houlette d'Allomusic n'ait pas introduit un nouveau système plus fiable de lecture de musique en ligne, et il est franchement dommage qu'une plateforme labellisée PUR force autant la main de l'utilisateur vers les comptes payants, en tentant de promouvoir l'écoute gratuite mais très limitée. OK, le modèle économique basé sur les pubs n'est pas forcément viable, mais on est pas QUE des vaches à lait. A bon entendeur ...

11
févr.
'12

Découverte du TI Launchpad

Publié le 11 février 2012

Lors du dernier meeting HZV, Mandarine a présenté le TI Launchpad, un circuit d'expérimentation réalisé par TI et basé sur un micro-contrôleur MSP430. J'avais auparavant entendu parler d'Arduino, et même eu dans les mains un Arduino prêté par l'ami Tixlegeek, mais j'hésitais à en acheter un. Et je dois avouer que le MSP430 m'a fait craquer.

Arduino vs. TI Launchpad

Lorsque Mandarine a présenté le TI Launchpad, je m'attendais à quelque chose d'un peu roots, et je n'ai pas été déçu. Contrairement à l'Arduino, le Launchpad ne possède aucun module d'extension officiel (ce qu'on nomme shields chez Arduino), et il faut donc tout concevoir. Cette approche me plaît plus qu'avec Arduino, car cela nécessite une documentation et un effort de la part de celui qui veut concevoir un circuit basé sur un MSP430. Mais c'est vrai que cela peut faire peur. Toutefois, mis à part cet aspect rustique de la board de TI, plusieurs éléments m'ont séduit.

Le premier d'entre eux est l'existence de plusieurs packages Debian et Ubuntu offrant la possibilité de développer en C et même en C++ sur MSP430, en s'affranchissant royalement d'une surcouche, contrairement à Arduino où il faut employer l'IDE Arduino et la couche Arduino, qui n'améliore pas les performances. Ces outils sont opensource, et dérivés des outils standards GNU comme binutils et gcc. J'aime.

Second point, le prix. C'est idiot, mais à 4 dollars et 30 cents le TI Launchpad, cela vaut vraiment le coup. Sans compter qu'un MSP430 de rechange est fourni (si vous grillez celui installé sur la board), ainsi que des contacteurs et un micro-quartz que l'on peut souder pour améliorer la stabilité de l'horloge. Et un cordon USB. Pour seulement un peu plus de 4 dollars. Comparé aux 20 et quelques euros réclamés pour un Arduino UNO, c'est très intéressant.

J'ai donc décidé d'en commander deux, pour voir exactement ce que c"est et comment cela pourrait me servir dans mes projets hardware. Et je viens de les recevoir.

Unboxing du TI Launchpad

Je dois avouer que tout cela est très bien packagé: une belle boîte (quoique, dans mon cas recouvertes d'étiquettes en tout genre), une présentation soignée, et des stickers de surcroît !

Boîte du Launchpad Unboxing du Launchpad ZE Launchpad Les accessoires

Installation de la toolchain

La toolchain est en réalité un ensemble d'outils permettant de développer et compiler des programmes pour le MSP430 du Launchpad. Heureusement, plusieurs paquets existent pour les distributions Debian et Ubuntu:

msp430-gcc
msp430-libc
mspdebug

Mais en réalité, ces paquets ne sont disponibles que sur Ubuntu 10.10 ou Debian sid (testing). Or ma distribution de choix est Squeeze (Debian 6.0), et ces paquets ne sont pas compatibles. J'ai bien essayé de les installer à la barbare, mais rien n'y fait. Pas le choix donc que de trouver une solution alternative: la compilation from scratch.

Pour les heureux possesseurs d'une Debian Squeeze qui souhaiteraient comme moi tester le Launchpad de TI et son MSP430, je vous livre dans les lignes suivantes la technique pour installer proprement et correctement la toolchain, testé et approuvé.

L'outil msp430-gcc est en réalité une version modifiée du célèbre compilateur GNU GCC, et un projet créé par des adorateurs ultimes du MSP430 a été créé de manière à fournir aux libristes un moyen de créer cette toolchain: j'ai nommé le projet mspgcc. Ce projet contient un ensemble de patches à appliquer à des versions publiées et stables d'outils GNU (comme binutils et gcc) afin d'obtenir des binaires permettant de compiler des programmes pour MSP430.

Avant toute chose, il faut passer root sur sa machine, et créer un dossier dédié à la construction de la toolchain:

$ su -
# mkdir msp430
# cd msp430

On s'assure ensuite d'avoir tous les paquets nécessaires à la compilation:

msp430:/# apt-get install libmpfr-dev libncurses5-dev zlibc zlib1g-dev libx11-dev libusb-dev libreadline6-dev libgmp3-dev libmpc-dev make

Ensuite, on récupère la version LTS (Long-Term Support) de mspgcc :

msp430/:# wget -O mspgcc.tar.bz2 http://downloads.sourceforge.net/project/mspgcc/mspgcc/mspgcc-20120406.tar.bz2?r=&ts=1359415411&use_mirror=ignum
msp430/:# tar xvjf mspgcc.tar.bz2
msp430/:# mv mspgcc-20120406 mspgcc
msp430/:# cd mspgcc
msp430/mspgcc/:#

On jette un oeil aux versions des logiciels requis pour les patches:

msp430/mspgcc/:# ls -Al
drwxr-sr-x 2 root root   4096  6 avril  2012 docs
drwxr-sr-x 4 root root   4096  6 avril  2012 htdocs
-rw-r--r-- 1 root root 342378  6 avril  2012 msp430-binutils-2.21.1a-20120406.patch
-rw-r--r-- 1 root root 390548  6 avril  2012 msp430-gcc-4.6.3-20120406.patch
-rw-r--r-- 1 root root 382048  6 avril  2012 msp430-gdb-7.2a-20111205.patch
-rw-r--r-- 1 root root      9  6 avril  2012 msp430-libc.version
-rw-r--r-- 1 root root      9  6 avril  2012 msp430mcu.version
-rw-r--r-- 1 root root   2955  6 avril  2012 README
-rw-r--r-- 1 root root  52679  6 avril  2012 RELEASES.TXT

Cette version de mspgcc se base sur la version 2.21.1a de binutils, la version 4.6.3 de gcc et la version 7.2a de gdb. On télécharge ces logiciels sous forme d'archive de code source dans le dossier courant:

msp430/mspgcc/:# wget ftp://ftp.gnu.org/pub/gnu/binutils/binutils-2.21.1a.tar.bz2
msp430/mspgcc/:# wget ftp://ftp.gnu.org/pub/gnu/gcc/gcc-4.6.3/gcc-4.6.3.tar.bz2
msp430/mspgcc/:# wget ftp://ftp.gnu.org/pub/gnu/gdb/gdb-7.2a.tar.bz2

Et on les décompresse dans le dossier courant:

msp430/mspgcc/:# tar xvjf binutils-2.21.1a.tar.bz2
msp430/mspgcc/:# tar xvjf gcc-4.6.3.tar.bz2
msp430/mspgcc/:# tar xvjf gdb-7.2a.tar.bz2

On applique ensuite les patches du projet mspgcc. Attention, cette étape est cruciale, si vous l'oubliez la suite ne fonctionnera pas !

msp430/mspgcc/:# cd binutils-2.21.1
msp430/mspgcc/binutils-2.21.1/:# patch -p1 < ../msp430-binutils-2.21.1a-20120406.patch
msp430/mspgcc/:# cd ../gcc-4.6.3/
msp430/mspgcc/gcc-4.6.3/:# patch -p1 < ../msp430-gcc-4.6.3-20120406.patch
msp430/mspgcc/:# cd ../gdb-7.2/
msp430/mspgcc/gdb-7.2/:# patch -p1 < ../msp430-gdb-7.2a-20111205.patch
msp430/mspgcc/gdb-7.2/:# cd ..

Avant de commencer à construire la toolchain, il faut au préalable créer un dossier de destination dans lequel on installera les binaires. J'ai opté pour /opt/msp430, mais libre à vous de choisir le nom et l'emplacement qui vous plaît.

msp430/mspgcc/:# mkdir /opt/msp430
msp430/mspgcc/:# export BUILD=/opt/msp430

On configure et on compile les binutils:

msp430/mspgcc/:# cd binutils-2.21.1
msp430/mspgcc/binutils-2.21.1/:# ./configure --target=msp430 --prefix=$BUILD
msp430/mspgcc/binutils-2.21.1/:# make && make install
msp430/mspgcc/binutils-2.21.1/:# cd ..

On configure et on compile gcc:

msp430/mspgcc/:# cd gcc-4.6.3
msp430/mspgcc/gcc-4.6.3/:# mkdir gcc-build
msp430/mspgcc/gcc-4.6.3/gcc-build/:# ../configure --target=msp430 --enable-languages=c,c++ --prefix=$BUILD
msp430/mspgcc/gcc-4.6.3/gcc-build/:# make && make install

Et pour terminer, on configure et on compile gdb:

msp430/mspgcc/:# cd gdb-7.2
msp430/mspgcc/gdb-7.2/:# ../configure --target=msp430 --prefix=$BUILD
msp430/mspgcc/gdb-7.2/:# make && make install

Les outils de compilation ont été installés dans le dossier /opt/msp430 (ou celui que vous avez paramétré). Il ne reste plus qu'à installer la libc compilée pour le MSP430 et les entêtes. Pour cela, il faut récupérer sur le site du projet les fichiers suivants:

msp430mcu-20120716.tar.bz2
msp430-libc-20120716.tar.bz2

La compilation de la libc nécessite que le binaire msp430-gcc, produit lors de la compilation de gcc pour msp430, soit accessible dans le PATH:

msp430/:# export PATH=$*PATH*:$BUILD/bin

On peut ensuite décompresser la libc, la configurer, la compiler et l'installer:

msp430/:# tar xvjf msp430-libc-20120716.tar.bz2
msp430/:# cd msp430-libc-20120716
msp430/msp430-libc-20120716/:# ./configure --prefix=$BUILD
msp430/msp430-libc-20120716/:# cd src
msp430/msp430-libc-20120716/src:# make && make install
msp430/msp430-libc-20120716/src:# cd ../../

Et terminer par l'installation des entêtes de développement:

msp430/:# tar xvjf msp430mcu-20120716.tar.bz2
msp430/:# cd msp430mcu-20120716
msp430/msp430mcu-20120716/:# export MSP430MCU_ROOT=$(pwd)
msp430/msp430mcu-20120716/:# scripts/install.sh $BUILD

Bon, voilà une bonne chose de faite ! On a installé gcc, gdb, la libc et les entêtes de développement. Nous allons aussi modifier le fichier /etc/profile pour qu'il prenne en compte nos binaires créés. Pour cela on l'édite, et on ajoute la ligne suivante à la fin du fichier:

export PATH=$*PATH*:/opt/msp430/bin

On recharge ensuite notre profil:

# source /etc/profile

Installation de mspdebug

Mspdebug est l'outil nécessaire pour programmer et déboguer le MSP430. Il s'agit encore une fois d'un projet opensource, que l'on va installer sur notre machine. Téléchargez la dernière version sur Sourceforge, puis installez-la:

msp430/:# tar xvzf mspdebug-0.21.tar.gz
msp430/:# cd mspdebug-0.21/
msp430/mspdebug-0.21/:# ./configure && make && make install

Configuration d'UDEV

Afin que notre Launchpad soit détecté par USB, il faut s'assurer qu'une règle est bien présente dans la configuration d'UDEV. Si elle n'y est pas, il faut la créer:

# cat /etc/udev/rules.d/46-launchpad.rules
ATTRS*idVendor*=='0451',ATTRS*idProduct*=='f432',MODE='0660',GROUP='plugdev'

Une fois cela effectué, vous pouvez vous amuser avec votre launchpad. Vous trouverez notamment un programme exemple sur Hack a Day, et la manière de programmer le MSP430 avec mspdebug. Je ferai certainement un prochain billet sur le développement sur Launchpad.

Bon, pour la route je vous donne mon Helloworld:

int main(void) {
  /* disable watchdog */
  WDTCTL = WDTPW + WDTHOLD;

  P1DIR = BIT0|BIT6;
  P1OUT = BIT6;

  while(1) {
        if ((P1IN & BIT3)==BIT3)
                P1OUT = BIT6;
        else
                P1OUT = BIT0;
  }
}

Et mon Makefile:

CC=msp430-gcc
CFLAGS=-Os -Wall -g -mmcu=msp430g2231

OBJS=main.o


all: $(OBJS)
    $(CC) $(CFLAGS) -o main.elf $(OBJS)

%.o: %.c
    $(CC) $(CFLAGS) -c $<

clean:
    rm -fr main.elf $(OBJS)

Conclusion

Le Launchpad de TI est tout à fait valable, car pas cher et quand même très bien fourni avec son MSP430. Certes, cela nécessite plus de travail qu'avec un Arduino, mais c'est un peu comme avec Linux, on a plus de contrôle sur le système et on peut donc en faire exactement ce que l'on veut. Une philosophie que j'apprécie tout particulièrement. Testez, découvrez, bidouillez avec le Launchpad de TI.



Les contenus disponibles sur ce blog sont publiés sous licence Creative Commons BY-NC-SA.
Vous pouvez réutiliser tout ou partie de ces contenus à condition de citer l'auteur et l'origine, vous ne pouvez en faire une utilisation commerciale, et enfin vous devez partager tout travail ou œuvre dérivée sous les mêmes conditions — c'est-à-dire avec la même licence d'utilisation Creative Commons.