Utiliser un lecteur série de fichiers MP3 avec une carte Arduino / Genuino

Décorticage de documentation chinoise en musique

Image d'entête

par skywodd | | Licence (voir pied de page)

Catégories : Tutoriels Arduino | Mots clefs : Arduino Genuino Serial MP3 KT403A


Dans ce tutoriel, nous allons voir ensemble comment mettre en oeuvre un module série de lecture de fichiers MP3. Ces modules permettent de lire des musiques stockées sur une carte SD en fonction de commandes transmises par une carte maître. Dans cet article, la carte maître sera une carte Arduino / Genuino, mais le code peut être adapté pour n'importe quelle plateforme sans grande difficulté.

Sommaire

Bonjour à toutes et à tous !

Si vous trainez sur des sites de vente comme Ebay ou DealExtreme, ou que vous passez votre temps sur la boutique de Seeedstudio ou DFRobots, vous avez peut-être déjà vu ou entendu parler de cartes "lecteurs MP3 série".

Module lecteur MP3 série Grove V2.0 de Seeedstudio (recto)

Module "MP3 Grove V2.0" de Seeedstudio

Module lecteur MP3 série Grove V2.0 de Seeedstudio (verso)

Module "MP3 Grove V2.0" de Seeedstudio

Ceux sont des cartes assez simplistes avec un unique circuit intégré, un connecteur pour carte SD, une sortie jack audio et un port série TTL. Elles se trouvent en général pour quelques euros piéce.

Le principe de fonctionnement est assez simple sur le papier : on place des fichiers de musique au format MP3 sur une carte micro SD. On insert ladite carte micro SD dans le lecteur. On branche un casque d'un côté, une carte Arduino de l'autre. Et voilà, on obtient un lecteur de fichiers MP3 contrôlable via un simple port série. C'est un moyen simple et efficace d'ajouter un peu de vie (et de bruit) à une application cruellement silencieuse.

Dans mon cas, j'avais besoin de mettre en oeuvre un de ces modules pour un projet commandé par un client du TamiaLab. Le but était de concevoir une sorte de boite à musique numérique. J'en reparlerai sous peu dans un article dédié ;)

Mais où donc est la doc ?

Comme toujours avec les cartes chinoises, le plus dur est de trouver une documentation, de préférence compréhensible. Or, trouver une documentation pour ces modules MP3 série s'est avéré très compliqué. C'est même la raison principale qui m'a poussé à écrire cet article.

Ces modules sont vendus sous diverses références (comme beaucoup de produits chinois). Il est impossible de savoir qui fabrique vraiment le circuit intégré au coeur de ces cartes et sous quelle référence. Comble de malchance, le lien vers la documentation de mon module sur la page produit de Seeedstudio n'est pas fonctionnel au moment où j'écris ces lignes.

Cependant, en cherchant sur Google, j'ai pu trouver des documentations pour d'autres modules similaires (qui utilisent le même circuit intégré sous un nom différent, j'en suis absolument certain) :

N.B. En lisant ces documents, j'ai remarqué quelques incohérences. Les extraits de code du chapitre plus bas ont été testés sur une carte Grove MP3 v2.0 de Seeedstudio. En principe, le code devrait marcher de manière identique sur les autres cartes du même style, mais des variantes dans le comportement des commandes ne sont pas impossibles.

Le montage

Avant d'attaquer la partie croustillante de ce tutoriel, préparons un petit montage de tests autour d'un de ces modules MP3.

Photographie du matériel nécessaire à la réalisation du montage de test d'un module lecteur MP3 série

Matériel nécessaire

Pour réaliser ce montage, il va nous falloir :

  • Une carte Arduino UNO (et son câble USB),

  • Une carte micro SD (formatée en FAT32, c'est très important),

  • Un module MP3 série (j'utilise un module Grove MP3 v2.0 de Seeedstudio),

  • Une paire de haut-parleurs pour ordinateur ou un simple casque,

  • Une plaque d'essai et des fils pour câbler notre montage.

Vue prototypage du montage de test d'un module lecteur MP3 série

Vue prototypage du montage

Vue schématique du montage de test d'un module lecteur MP3 série

Vue schématique du montage

Le montage est ultra simpliste :

  • La broche 5V de la carte Arduino va sur la broche VCC du module MP3.

  • La broche GND de la carte Arduino va sur la broche GND du module MP3.

  • Et pour finir, la broche D1 de la carte Arduino va sur la broche RX du module MP3.

Photographie du montage de test d'un module lecteur MP3 série

Le montage fini

PS Si vous souhaitez utiliser le retour d'informations du module MP3 (je vous souhaite bonne chance si c'est le cas, personnellement j'ai abandonné), il faudra câbler la broche TX du module MP3 à la broche D0 de la carte Arduino. Attention, il ne sera alors plus possible d'utiliser le moniteur série pour debug le code.

Le protocol de communication

Le module MP3 utilise un jeu de commande série assez complet pour permettre le contrôle de la lecture de la musique. La communication série se fait à une vitesse standard de 9600 bauds.

Format d'une trame série d'un lecteur MP3 série (KT403A)

Format d'une trame série

Chaque commande commence par un octet d'entête (toujours 0x7E), suivi d'un numéro de version (toujours 0xFF), puis de la taille de la commande (toujours 0x06). Vient ensuite la commande elle-même (un octet), suivi d'un octet permettant d'activer ou non le retour d'informations et de deux octets de paramètres pour la commande en cours. Pour finir, une commande se termine toujours par l'octet 0xEF.

Sur un des documents, il est précisé qu'une somme de contrôle sur 16 bits se trouve avant l'octet final 0xEF. C'est là que les choses deviennent croustillantes.

En fait, cette somme de contrôle est optionnelle (les puristes viennent à coup sûr de s'étrangler en lisant cette phrase). Comme la méthode de calcul de cette somme de contrôle n'est expliquée nulle part dans les documents, presque aucun document ne l'inclut dans ses exemples. Le plus bizarre, c'est que le module accepte une commande avec ou sans la somme de contrôle sans problème et ne vérifie même pas la longueur de la commande fournie juste avant. Qu'une commande de 6 octets ne fasse que 4 octets ne gêne absolument pas le module … It's not a bug, it's a feature ;)

PS Si vous êtes d'ore et déjà tenté de faire une blague en prétextant que les concepteurs du circuit intégré avaient clairement abusé de l'alcool de riz. Attendez de voir la suite ;)

Si on résume tout cela en code Arduino, voilà ce qu'on obtient :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/* Constantes pour la communication avec le module KT403A */
const byte COMMAND_BYTE_START = 0x7E;
const byte COMMAND_BYTE_VERSION = 0xFF;
const byte COMMAND_BYTE_STOP = 0xEF;

/** Fonction de bas niveau pour envoyer une commande au module KT403A */
void send_kt_command(byte command, byte data_h, byte data_l, unsigned long cmd_delay) {
  Serial.write(COMMAND_BYTE_START);
  Serial.write(COMMAND_BYTE_VERSION);
  Serial.write((byte) 0x06);
  Serial.write(command);
  Serial.write((byte) 0x00);
  Serial.write(data_h);
  Serial.write(data_l);
  Serial.write(COMMAND_BYTE_STOP);
  // 16-bits checksum is optionnal
  delay(cmd_delay);
}

Les trois constantes contiennent les données invariables d'une commande, comme l'octet de début, l'octet de version et l'octet de fin.

La fonction send_kt_command() permet d'envoyer une commande au module MP3. Cette fonction prend en argument la commande à transmettre (un octet), deux octets de paramètres pour la commande et un délai en millisecondes.

Le délai en fin de commande est nécessaire, car certaines commandes ont besoin de plusieurs dizaines de millisecondes pour s'exécuter. Ai-je précisé que ces délais ne sont absolument pas documentés ? Ce serait beaucoup trop simple ;)

Les commandes

J'ai essayé de classer les commandes par fonctionnalité pour réduire le nombre de chapitres.

Pour chaque fonction correspond une commande série. J'ai mis entre parenthèses l'octet de la commande utilisée par chaque fonction pour ceux qui voudraient porter les extraits de code sur une autre plateforme qu'une carte Arduino.

Lire un fichier MP3 via son index

Commençons par le plus simple, ou pas.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void playTrack(uint16_t track_number) {
 send_kt_command(0x03, highByte(track_number), lowByte(track_number), 10);
}

void playNextSong() {
 send_kt_command(0x01, 0, 0, 10);
}

void playPreviousSong() {
  send_kt_command(0x02, 0, 0, 10);
}

La fonction playTrack() (0x03) permet de lire un fichier audio en fonction de son index. Les fonctions playNextSong() (0x01) et playPreviousSong() (0x02) permettent respectivement de passer au fichier suivant et au fichier précédent (toujours en fonction de l'index du fichier).

La notion "d'index de fichier" est assez compliquée à comprendre. Les concepteurs du module MP3 devaient vraiment avoir l'esprit tordu, une belle gueule de bois, ou un client avec des contraintes très particulières. En effet, l'index correspond au numéro du fichier dans la liste de tous les fichiers sur la carte SD (sous dossiers inclus), triés par date de création physique (le moment où vous copier / coller le fichier ou que vous le créer directement sur la carte).

Le premier fichier que vous copié sur la carte SD est le numéro 1, le second est le numéro 2, etc. L'index est exclusivement basé sur l'ordre de création de chaque fichier. C'est tordu et si peu pratique qu'une des documentations que j'ai récupérées précise : "si vous ne comprenez pas le principe, laissez tomber et utiliser la commande de lecture par nom de fichier". Et pour rendre la chose plus "simple", cet index commence à 1, et non à 0.

PS Les commandes playNextSong() et playPreviousSong() fonctionnement qu'importe le fichier lu (par index ou par nom de fichier). Par contre, le fichier suivant / précédent est toujours déterminé par l'index du fichier en cours de lecture.

Lire en boucle un fichier MP3 via son index

Le mode de lecture par index support aussi deux modes de lectures en boucles : lecture d'un fichier en boucle ou de tous les fichiers (sous dossiers inclut) :

1
2
3
4
5
6
7
void repeatSingleTrack(uint16_t track_number) {
 send_kt_command(0x08, highByte(track_number), lowByte(track_number), 10);
}

void loopAllMusic(byte enabled) {
  send_kt_command(0x11, 0, !!enabled, 10);
}

La fonction repeatSingleTrack() (0x08) permet de lire un fichier en boucle en fonction de son index. La fonction loopAllMusic() (0x11) permet de lire tous les fichiers sur la carte SD (sous dossiers inclut) en boucle.

N.B. Avec la fonction loopAllMusic(), il est possible d'entrer ou de sortir du mode de lecture en boucle en passant respectivement true ou false en paramètre de la fonction.

Gestion du volume

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
void volumeUp() {
 send_kt_command(0x04, 0, 0, 10);
}

void volumeDown() {
 send_kt_command(0x05, 0, 0, 10);
}

void setVolume(byte volume) {
 if (volume > 30) volume = 30;
 send_kt_command(0x06, 0, volume, 10);
}

void muteSound(byte enabled) {
  send_kt_command(0x1A, 0, !!enabled, 10);
}

La fonction volumeUp() (0x04) permet d'augmenter le volume. La fonction volumeDown() (0x05) permet de diminuer le volume. Pour finir, la fonction setVolume() (0x06) permet de choisir directement le volume souhaité entre 0 et 30 (inclus).

La fonction muteSound() (0x1A) permet d'activer ou de désactiver la sourdine en passant respectivement true ou false en paramètre de la fonction.

Gestion de l'équalizer

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/* Constantes pour la commande setEqualizer() */
const byte EQUALIZER_NORMAL = 0x00;
const byte EQUALIZER_POP = 0x01;
const byte EQUALIZER_ROCK = 0x02;
const byte EQUALIZER_JAZZ = 0x03;
const byte EQUALIZER_CLASSIC = 0x04;
const byte EQUALIZER_BASS = 0x05;

/** Set the equalizer to the given audio mode */
void setEqualizer(byte mode) {
  send_kt_command(0x07, 0, mode, 20);
}

J'étais aussi étonné que vous de découvrir qu'un développeur chinois s'est embêté à implémenter un équalizer avec cinq profils audio différents.

La fonction setEqualizer() (0x07) permet de choisir le profil audio de l'équalizer : normal, pop, rock, jazz, classic ou basse.

Gestion du module

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/* Constantes pour la commande selectSourceDevice() */
const byte DEVICE_UDISK = 0x01;
const byte DEVICE_SDCARD = 0x02;
const byte DEVICE_AUX = 0x03; // Not used
const byte DEVICE_PC = 0x04; // Debug only
const byte DEVICE_FLASH = 0x05;
const byte DEVICE_SLEEP = 0x06;

void selectSourceDevice(byte device) {
  send_kt_command(0x09, 0, device, 200);
}

La fonction selectSourceDevice() (0x09) permet de choisir le périphérique source pour la lecture des fichiers MP3.

D'après mes recherches, le module MP3 gère 5 périphériques sources différents : clef USB (UDISK), carte SD (SDCARD), AUX (aucune idée de ce que c'est), PC (pour du debug d'après la doc), FLASH (mémoire NAND) ou SLEEP (mode veille).

Seul le mode carte SD fonctionne avec ma carte. Pour l'USB, voir l'annexe en fin d'article ;)

1
2
3
4
5
6
7
void enterStandbyMode() {
 send_kt_command(0x0A, 0, 0, 20);
}

void exitStandbyMode() {
  send_kt_command(0x0B, 0, 0, 20);
}

Les fonctions enterStandbyMode() (0x0A) et exitStandbyMode() (0x0B) permettent de mettre le module MP3 en veille et de le réveiller.

N.B. Il est nécessaire de resélectionner le périphérique source avec la fonction selectSourceDevice() après avoir réveillé le module.

1
2
3
void resetPlayer() {
  send_kt_command(0x0C, 0, 0, 100);
}

La fonction resetPlayer() (0x0C) permet de réinitiliaser entièrement le module.

Play / Pause / Stop

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
void playResumeMusic() {
 send_kt_command(0x0D, 0, 0, 20);
}

void pauseMusic() {
 send_kt_command(0x0E, 0, 0, 20);
}

void stopMusic() {
  send_kt_command(0x16, 0, 0, 20);
}

La fonction playResumeMusic() (0x0D) permet de relancer la lecture d'un fichier en pause. La fonction pauseMusic() (0x0E) permet de mettre la lecture d'un fichier en pause. Et pour finir, la fonction stopMusic() (0x16) permet d'arrêter la lecture du fichier en cours.

N.B. La fonction playResumeMusic() lit le fichier suivant (par index de fichier) si aucun fichier n'est en cours de lecture.

N.B. La fonction stopMusic() permet aussi d'arrêter une lecture en boucle d'un fichier ou d'un dossier.

Lecture aléatoire

1
2
3
void shufflePlay() {
  send_kt_command(0x18, 0, 0, 20);
}

La fonction shufflePlay() (0x18) permet de lire aléatoirement les fichiers sur la carte SD, sous dossiers inclus.

Lecture d'un fichier nommé dans un dossier nommé

1
2
3
void selectSourceFolderAndTrack(byte folder_number, byte track_number) {
  send_kt_command(0x0F, folder_number, track_number, 10);
}

La fonction selectSourceFolderAndTrack() (0x0F) permet de lire un fichier bien précis dans un dossier bien précis. C'est la méthode simple pour lire un fichier sans se prendre la tête avec l'ordre de création du fichier en question.

Le dossier DOIT être nommé par un identifiant numérique à deux chiffres. Pour les numéros de dossiers inférieurs à 10, un 0 doit être ajouté pour obtenir deux chiffres. Exemple : 00, 01, 02, 03, 15, 25, 99.

N.B. Le numéro de dossier va de 00 à 99. Il n'est pas possible d'avoir plus de 100 dossiers.

Le(s) fichier(s) dans le dossier DOIT être nommé par un identifiant numérique à trois chiffres. Pour les numéros de fichiers inférieurs à 100, un ou deux 0 doit être ajouté pour obtenir trois chiffres. Exemple : 000.mp3, 001.mp3, 002.mp3, 015.mp3, 025.mp3, 100.mp3, 125.mp3, 255.mp3.

N.B. Le numéro de fichier va de 000 à 255. Il n'est pas possible d'avoir plus de 256 fichiers par dossier avec cette fonction.

PS vous pouvez ajouter du texte APRÈS le nombre. Exemple : 042_lagrandequestionsurlavie.mp3

1
2
3
4
void selectSourceBigFolderAndTrack(byte folder, uint16_t track_number) {
  // 0b1100 011111001111 = 4bits folder, 12 bits track number
  send_kt_command(0x14, (folder << 4) | highByte(track_number) & 0x0f, lowByte(track_number), 10);
}

Si vous avez besoin de lire plus de 25600 fichiers (100 dossiers * 256 fichiers), la fonction selectSourceBigFolderAndTrack() (0x14) permet de lire un fichier bien précis dans un dossier bien précis, mais avec un nombre de fichiers par dossier bien plus grand (16 dossiers * 10000 fichiers).

Le dossier DOIT être nommé par un identifiant numérique à deux chiffres. Pour les numéros de dossiers inférieurs à 10, un 0 doit être ajouté pour obtenir deux chiffres. Exemple : 00, 01, 02, 03, 15.

N.B. Le numéro de dossier va de 00 à 15. Il n'est pas possible d'avoir plus de 16 dossiers avec cette fonction.

Le(s) fichier(s) dans le dossier DOIT être nommé par un identifiant numérique à quatre chiffres. Pour les numéros de fichiers inférieurs à 1000, un, deux ou trois 0 doit être ajouté pour obtenir quatre chiffres. Exemple : 0000.mp3, 0001.mp3, 0002.mp3, 0015.mp3, 0025.mp3, 0100.mp3, 0125.mp3, 1000.mp3, 9999.mp3.

N.B. Le numéro de fichier va de 0000 à 9999. Il n'est pas possible d'avoir plus de 10000 fichiers par dossier avec cette fonction.

PS vous pouvez ajouter du texte APRÈS le nombre. Exemple : 0042_lagrandequestionsurlavie.mp3

Faite votre choix

Trois chiffres ou quatre chiffres, selectSourceFolderAndTrack() ou selectBigSourceFolderAndTrack(), faites votre choix en fonction du nombre de fichiers à lire. Les deux conventions de nommage ne sont pas compatibles.

Lecture d'un fichier dans le dossier "MP3"

Pour ceux qui veulent lire jusqu'à 10000 fichiers sans se prendre la tête. Il existe une solution miracle.

1
2
3
void selectSourceTrackInMp3Directory(uint16_t track_number) {
  send_kt_command(0x12, highByte(track_number), lowByte(track_number), 10);
}

La fonction selectSourceTrackInMp3Directory() (0x12) permet de lire un fichier précis dans le dossier MP3 (les majuscules / minuscules ne sont pas prises en compte).

Le(s) fichier(s) dans le dossier MP3 DOIT être nommé par un identifiant numérique à quatre chiffres. Pour les numéros de fichiers inférieurs à 1000, un, deux ou trois 0 doit être ajouté pour obtenir quatre chiffres. Exemple : 0000.mp3, 0001.mp3, 0002.mp3, 0015.mp3, 0025.mp3, 0100.mp3, 0125.mp3, 1000.mp3, 9999.mp3.

N.B. Le numéro de fichier va de 0000 à 9999. Il n'est pas possible d'avoir plus de 10000 fichiers par dossier avec cette fonction.

PS vous pouvez ajouter du texte APRÈS le nombre. Exemple : 0042_lagrandequestionsurlavie.mp3

C'est la solution la plus simple à mettre en oeuvre. Il suffit de créer un dossier MP3 (ou mp3, peut importe) à la racine de la carte SD et de renommer chaque fichier avec un nombre à quatre chiffres en début de nom. Pas d'index de fichier à gérer, pas de mauvaise surprise avec la convention de nommage, ça marche.

Insertion d'une pause publicitaire

Vous avez bien lu, ces modules MP3 sont conçus à l'origine pour faire des systèmes publicitaires. Il est donc normal de trouver deux commandes permettant l'insertion d'une coupure pub au milieu de la lecture d'un fichier.

1
2
3
4
5
6
7
void insertSongFromAdvertDirectory(uint16_t track_number) {
 send_kt_command(0x13, highByte(track_number), lowByte(track_number), 10);
}

void stopInsertedSongAndResumePlaying() {
  send_kt_command(0x15, 0, 0, 20);
}

La fonction insertSongFromAdvertDirectory() (0x13) permet d'insérer une coupure pub. Le fichier est lu depuis le dossier ADVERT avec des noms de fichiers commençant par des nombres à quatre chiffres (même principe qu'avec le dossier MP3).

N.B. Une fois la coupure pub terminée, la lecture du fichier reprend normalement là où elle s'est arrêtée.

La fonction stopInsertedSongAndResumePlaying() (0x15) permet d'arrêter la coupure pub immédiatement et de continuer la lecture du fichier là où elle avait été interrompue.

Lecture en boucle d'un fichier ou d'un dossier

1
2
3
void repeatCurrentTrack(byte enabled) {
  send_kt_command(0x19, 0, !enabled, 10);
}

La fonction repeatCurrentTrack() (0x19) permet d'activer ou désactiver la lecture en boucle du fichier en cours de lecture.

1
2
3
void loopFolder(byte folder_number) {
  send_kt_command(0x17, 0, folder_number, 10);
}

La fonction loopFolder() (0x17) permet de lire en boucle le contenu d'un dossier nommé par un nombre à deux chiffres (même principe qu'avec la fonction selectSourceFolderAndTrack()).

Exemple minimaliste de code

Voici un exemple minimaliste de code Arduino permettant d'initialiser le port série, le module MP3 et de lire un fichier sur la carte SD :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/** Fonction setup() */
void setup() {
 
 /* Initialise le port série */ 
 Serial.begin(9600);
  
  /* Initialise le module MP3 */
  resetPlayer();
  selectSourceDevice(DEVICE_SDCARD);
  setVolume(15);

  /* Lit le premier fichier sur la carte SD */
  playTrack(1);
}

/** Fonction loop() */
void loop() {

}

Bonus : un terminal de test / démonstration

Afin de tester le bon fonctionnement et le comportement de mon module, j'ai écrit un terminal de test permettant de tester manuellement chaque fonction via le moniteur série. Il est ainsi possible de lancer la lecture d'un fichier nommé 0002.mp3 dans le dossier MP3 avec la commande m2 par exemple. Ou de lire un fichier nommé 017.mp3 dans le dossier 06 avec f6 17 par exemple.

N.B. Le moniteur série doit être configuré à 9600 bauds et en mode "Nouvelle ligne".

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
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
/**
 * Code de démonstration d'utilisation d'un module MP3 KT403A avec une carte Arduino.
 */

/* Constantes pour la communication avec le module KT403A */
const byte COMMAND_BYTE_START = 0x7E;
const byte COMMAND_BYTE_VERSION = 0xFF;
const byte COMMAND_BYTE_STOP = 0xEF;

/* Constantes pour la commande setEqualizer() */
const byte EQUALIZER_NORMAL = 0x00;
const byte EQUALIZER_POP = 0x01;
const byte EQUALIZER_ROCK = 0x02;
const byte EQUALIZER_JAZZ = 0x03;
const byte EQUALIZER_CLASSIC = 0x04;
const byte EQUALIZER_BASS = 0x05;

/* Constantes pour la commande selectSourceDevice() */
const byte DEVICE_UDISK = 0x01;
const byte DEVICE_SDCARD = 0x02;
const byte DEVICE_AUX = 0x03; // Not used
const byte DEVICE_PC = 0x04; // Debug only
const byte DEVICE_FLASH = 0x05;
const byte DEVICE_SLEEP = 0x06;


/** Play the next song (all folders) WARNING: files are ordered by creation date! */
void playNextSong() {
  send_kt_command(0x01, 0, 0, 10);
}

/** Play the previous song (all folders) WARNING: files are ordered by creation date! */
void playPreviousSong() {
  send_kt_command(0x02, 0, 0, 10);
}

/** Play the given track number (0 ~ 2999) (all folders) WARNING: files are ordered by creation date! */
void playTrack(uint16_t track_number) {
  send_kt_command(0x03, highByte(track_number), lowByte(track_number), 10);
}

/** Increase the volume */
void volumeUp() {
  send_kt_command(0x04, 0, 0, 10);
}

/** Decrease the volume */
void volumeDown() {
  send_kt_command(0x05, 0, 0, 10);
}

/** Set the volume to the given level (0 ~ 30) */
void setVolume(byte volume) {
  if (volume > 30) volume = 30;
  send_kt_command(0x06, 0, volume, 10);
}

/** Set the equalizer to the given audio mode */
void setEqualizer(byte mode) {
  send_kt_command(0x07, 0, mode, 20);
}

/** Repeat a single track (0 ~ 2999) (all folders) WARNING: files are ordered by creation date! */
void repeatSingleTrack(uint16_t track_number) {
  send_kt_command(0x08, highByte(track_number), lowByte(track_number), 10);
}

/** Select the source device for playing */
void selectSourceDevice(byte device) {
  send_kt_command(0x09, 0, device, 200);
}

/** Enter standby / low power mode */
void enterStandbyMode() {
  send_kt_command(0x0A, 0, 0, 20);
}

/** Exit standby / low power mode */
void exitStandbyMode() {
  send_kt_command(0x0B, 0, 0, 20);
}

/** Reset the MP3 module */
void resetPlayer() {
  send_kt_command(0x0C, 0, 0, 100);
}

/** Play/resume the music */
void playResumeMusic() {
  send_kt_command(0x0D, 0, 0, 20);
}

/** Pause the music */
void pauseMusic() {
  send_kt_command(0x0E, 0, 0, 20);
}

/** Select the source folder and track for playing */
void selectSourceFolderAndTrack(byte folder_number, byte track_number) {
  send_kt_command(0x0F, folder_number, track_number, 10);
}

// 0x10 "Volume adjust set" - NOT SUPPORTED

/** Enable or disable looping of all music files */
void loopAllMusic(byte enabled) {
  send_kt_command(0x11, 0, !!enabled, 10);
}

/** Select the source track from the "MP3" (case insensitive) folder */
void selectSourceTrackInMp3Directory(uint16_t track_number) {
  // Assert 0 ~ 9999
  send_kt_command(0x12, highByte(track_number), lowByte(track_number), 10);
}

/** Pause the current track and insert an advertisement song from the "ADVERT" (case insensitive) folder */
void insertSongFromAdvertDirectory(uint16_t track_number) {
  // Assert 0 ~ 9999
  send_kt_command(0x13, highByte(track_number), lowByte(track_number), 10);
}

/** Play the given song in the given directory (big directory version) */
void selectSourceBigFolderAndTrack(byte folder, uint16_t track_number) {
  // Assert folder 0 ~ 15, track number 0 ~ 9999
  // 0b1100 011111001111 = 4bits folder, 12 bits track number
  send_kt_command(0x14, (folder << 4) | highByte(track_number) & 0x0f, lowByte(track_number), 10);
}

/** Stop playing the inserted advertisement song and resume playing the user track */
void stopInsertedSongAndResumePlaying() {
  send_kt_command(0x15, 0, 0, 20);
}

/** Stop the music */
void stopMusic() {
  send_kt_command(0x16, 0, 0, 20);
}

/** Loop all music in the given folder */
void loopFolder(byte folder_number) {
  send_kt_command(0x17, 0, folder_number, 10);
}

/** Random playing */
void shufflePlay() {
  send_kt_command(0x18, 0, 0, 20);
}

/** single repeat the currently playing track track */
void repeatCurrentTrack(byte enabled) {
  send_kt_command(0x19, 0, !enabled, 10);
}

/** Mute sound output **/
void muteSound(byte enabled) {
  send_kt_command(0x1A, 0, !!enabled, 10);
}


/** Fonction de bas niveau pour envoyer une commande au module KT403A */
void send_kt_command(byte command, byte data_h, byte data_l, unsigned long cmd_delay) {
  Serial.write(COMMAND_BYTE_START);
  Serial.write(COMMAND_BYTE_VERSION);
  Serial.write((byte) 0x06);
  Serial.write(command);
  Serial.write((byte) 0x00);
  Serial.write(data_h);
  Serial.write(data_l);
  Serial.write(COMMAND_BYTE_STOP);
  // 16-bits checksum is optionnal
  delay(cmd_delay);
}


/** Fonction setup() */
void setup() {
 
  /* Initialise le port série */  
  Serial.begin(9600);
  Serial.setTimeout(60000);
  
  /* Initialise le module KT403A */
  resetPlayer();
  selectSourceDevice(DEVICE_SDCARD);
  setVolume(15);
}

/** Fonction loop() */
void loop() {
  
  /* Attends une commande */
  while(!Serial.available());
 
  /* Lit l'octet de commande */
  switch(Serial.read()) {
    
    case '>': {  // OK (ok dans tt dossiers)
      playNextSong();
      break;
    }
      
    case '<': {  // OK (ok dans tt dossiers)
      playPreviousSong();
      break;
    }
      
    case 't':
    case 'T': {  // OK 1-65535 (lecture par ordre création tt dossiers confondu)
      long track_number = Serial.parseInt();
      playTrack(track_number & 0xFFFF);
      break;
    }
    
    case '+': {  // OK
      volumeUp();
      break;
    }
      
    case '-': {  // OK
      volumeDown();
      break;
    }
      
    case 'v': 
    case 'V': {  // OK 0-30
      long volume = Serial.parseInt();
      setVolume(volume & 0xFF);
      break;
    }
    
    case 'q': {  // OK
      muteSound(true);
      break;
    }
    
    case 'Q': {  // OK
      muteSound(false);
      break;
    }
    
    case 'e': 
    case 'E': {  // OK
      long mode = Serial.parseInt();
      setEqualizer(mode & 0x0F);
      break;
    }
      
    case 'u': 
    case 'U': {  // NOPE
      selectSourceDevice(DEVICE_UDISK);
      break;
    }
      
    case 'd': 
    case 'D': {  // OK
      selectSourceDevice(DEVICE_SDCARD);
      break;
    }
    
    case 'z':
    case 'Z': {  // OK
      enterStandbyMode();
      break;
    }
    
    case 'y':
    case 'Y': { // OK (need select device aprés réveil)
      exitStandbyMode();
      break;
    }

    case '?': {  // OK
      shufflePlay();
      break;
    }
      
    case '!': {  // OK
      resetPlayer();
      break;
    }
    
    case 'r':
    case 'R': {  // OK (lecture suivant si pas musique)
      playResumeMusic();
      break;
    }
      
    case 'p': 
    case 'P': {  // OK
      pauseMusic();
      break;
    }
      
    case 's': 
    case 'S': {  // OK
      stopMusic();
      break;
    }
    
    case 'f': 
    case 'F': {  // OK folder 00-99, track 000-255
      long folder_number = Serial.parseInt();
      long track_number = Serial.parseInt();
      selectSourceFolderAndTrack(folder_number & 0xFF, track_number & 0xFF);
      break;
    }
      
    case 'b':
    case 'B': {  // OK folder 00-15, track 0000-9999
      long folder_number = Serial.parseInt();
      long track_number = Serial.parseInt();
      selectSourceBigFolderAndTrack(folder_number & 0x0F, track_number & 0x0FFF);
      break;
    }
      
    case 'm': 
    case 'M': {  // OK 0000-9999
      long track_number = Serial.parseInt();
      selectSourceTrackInMp3Directory(track_number & 0xFFFF);
      break;
    }
    
    case 'a': 
    case 'A': {  // OK 0000-9999
      long track_number = Serial.parseInt();
      insertSongFromAdvertDirectory(track_number & 0xFFFF);
      break;
    }
      
    case 'c':
    case 'C': {  // OK
      stopInsertedSongAndResumePlaying();
      break;
    }
    
    case 'l': {  // OK (arréte la lecture immédiatement)
      loopAllMusic(false);
      break;
    }
      
    case 'L': {  // OK (démarre automatiquement la lecture si off)
      loopAllMusic(true);
      break;
    }
      
    case '@': {  // OK 00-99 (boucle sur les dates de création des fichiers, pas sur les index)
      long folder_number = Serial.parseInt();
      loopFolder(folder_number & 0xFF);
      break;
    }
    
    case ':': {  // OK
      repeatCurrentTrack(false);
      break;
    }
      
   case ';': {  // OK
      repeatCurrentTrack(true);
      break;
    }
      
    case '#': {  // OK 0000-9999 (sur les dates de création des fichiers)
      long track_number = Serial.parseInt();
      repeatSingleTrack(track_number & 0xFFFF);
      break;
    }
  }
}

L'extrait de code ci-dessus est disponible en téléchargement sur cette page (le lien de téléchargement en .zip contient le projet Arduino prêt à l'emploi).

Annexe : Cacher ce port USB que je ne saurais voir

Diagramme interne d'un module lecteur MP3 série (KT403A)

Diagramme interne d'un module KT403A

Le circuit intégré dispose d'un port USB. C'est une évidence. Le diagramme interne le montre clairement et la carte de Seeeduino dispose même de deux points de test pour le port USB. Cependant, je n'ai pas été en mesure de le faire fonctionner.

D'après les diverses documentations, le port USB peut être utilisé en mode "host" pour connecter une clef USB (qui remplace alors la carte SD) ou en mode "device" pour avoir une connexion avec un PC (pour du "debug" d'après la documentation, mais à mon avis, il s'agit plutôt d'un moyen d'accéder à la mémoire interne / carte SD directement depuis le PC).

Les commandes pour l'USB sont bien dans la doc, mais rien à faire, je n'ai pas pu faire fonctionner le port USB, aussi bien en mode "device" que "host". Il doit sûrement y avoir une astuce.

Si un lecteur arrive à trouver l'astuce pour faire fonctionner le port USB, qu'il le fasse savoir en commentaires ;)

Conclusion

Ce tutoriel est désormais terminé.

Si ce tutoriel 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.