On nous déjà demandé plusieurs fois si on comptait faire un capteur de couleurs, aussi appelé capteur RGB. Honnêtement, la réponse est : "on ne sait pas trop". Vous allez comprendre: cette semaine on vous propose d'interfacer le capteur de couleur TCS34725 de TAOS à l'aide d'un Yocto-I2C.
La façon la plus simple de tester le capteur TCS34725 est probablement d'utiliser le breakout board d'Adafruit correspondant et c'est que qu'on fait.
Le capteur TCS34725 monté sur un PCB d'Adafruit
Connexions
La connexion I2C est assez triviale, le board peut être alimenté en 3.3V. En plus du capteur TCS34725, le board dispose d'une LED blanche. Par défaut, la LED est allumée, pour l'éteindre, il faut forcer l'entrée LED à 0V mais le Yocto-I2C n'a pas de sortie open-drain. Par contre, il y a une astuce pour utiliser la sortie PWR du Yocto-I2C. La LED est contrôlée par un mosfet dont l'entrée est tirée vers 3.3V à l'aide d'une résistance de 10KΩ.
La led est maintenue allumée par un pull-up du 10K (source Adafruit)
On peut contrer ce pull-up de 10KΩ avec un pull down de 1KΩ, du coup la LED sera éteinte par défaut, et pourra être allumée à l'aide de la sortie PWR Yocto-I2C. Il suffira de la mettre à 1.8V ou 3.3V pour allumer la LED.
Connexion au Yocto-I2C avec contrôle de la LED
Si vous n'avez pas besoin de la LED, contentez-vous de connecter directement l'entrée LED avec GND.
Protocole
L'adresse I2C du TCS34725 est 0x29: chaque communication en écriture commencera donc par le byte 0x52 et chaque communication en lecture devra commencer par 0x53. Le second byte est un byte de commande dont le bit 7 est toujours à 1. Les bits 6 et 5 servent à définir, entre autres, si le pointeur d'adresse doit s'incrémenter automatiquement. Les bits 4 à 0 définissent l'adresse à utiliser
Initialisation
Par défaut le capteur est en sommeil. Il faut le mettre en marche mettant à 1 les bits PON puis AEN du registre 0. Attention à respecter un délai d'au moins 2.4 ms entre les deux. Il faut aussi définir à quelle fréquence le capteur doit échantillonner. Mettre 0xd5 dans le registre 0x01 donne une période de 101 ms. La tâche d’initialisation ressemble donc à ceci.
writeLine {S}528001{P} wait 10 writeLine {S}528003{P} writeLine {S}5281D5{P} compute $init_done=1
Lecture
La synchronisation entre la tâche d'initialisation et celle de lecture est gérée par la variable init_done. Un fois initialisation terminée, il faut ensuite lire les données à partir de l'adresse 0x13 en veillant à que le pointeur s'incrémente automatiquement, d'où la valeur 0xB3 pour le registre de commande. Les données sont, dans l'ordre, le byte status dont le bit 0 indique si la dernière conversion s'est bien passée, ensuite les valeurs de lumière blanche, rouge, verte et bleue encodées sous forme de mot 16bits avec le byte de poids faible en premier. La tâche de lecture qui interroge les capteurs et mappe le résultat sur les genericsensors 1 à 4 ressemble donc à ceci.
assert isset($init_done) writeLine {S}52B3{P}{S}53xx{A}xx{A}xx{A}xx{A}xx{A}xx{A}xx{A}xx{A}xx{N}{P} expect 29:{A}{A} 29:{A}($S:BYTE)($C:WORDL)($R:WORDL)($G:WORDL)($B:WORDL) assert ($S & 1) ==1 compute $4=$C compute $1=$R compute $2=$G compute $3=$B
Résultats
On a placé le capteur à environ 4cm aussi d'une feuille avec des gros carrés de couleur unie, et on a essayé trois niveaux d'éclairage différent: lumière ambiante, éclairage avec la LED blanche intégrée et éclairage avec une lampe puissante.
Notre dispositif expérimental
A première vue, le capteur fonctionne comme on pourrait s'y attendre, si on le braque sur une surface rouge, on voit clairement une dominante rouge dans le résultat mesuré par le capteur. On remarque aussi que les valeurs pour le vert et le bleu ne sont clairement pas à zéro. Plus on éclaire plus les valeurs augmentent.
Trois niveaux d'éclairage sur une surface rouge
Par contre le capteur a beaucoup plus de mal a distinguer le bleu, à certains niveaux il distingue même plus de rouge que de bleu.
Trois niveaux d'éclairage sur une surface bleue
Il est donc clair que le niveau d'éclairage influe grandement sur le résultat: plus on éclaire la surface, plus les valeurs augmentent alors que la couleur de notre surface n'a pas changé. C'est assez logique puisque le capteur mesure en fait la lumière qui lui parvient: plus il y a de lumière plus notre surface va en réfléchir et plus notre capteur va en recevoir.
Mais cela illustre le problème de ce genre de capteur: il est difficile de mapper avec exactitude les valeurs du capteur avec une couleur fixe. Par contre, on peut essayer de tirer partit du fait que le capteur a un canal qui mesure la lumière blanche et essayer de normaliser les valeurs RGB par rapport au canal blanc. C'est ce que fait la librairie fournie par Adafruit. Le code divise la valeur de chaque canal par la valeur de lumière blanche, borne le résultat entre 0 et 1.0, met le tout à la puissance 2.5 pour compenser le manque linéarité du capteur et multiplie le tout par 255. On peut modifier notre job pour qu'il fasse la même chose:
assert isset($init_done) writeLine {S}52B3{P}{S}53xx{A}xx{A}xx{A}xx{A}xx{A}xx{A}xx{A}xx{A}xx{N}{P} expect 29:{A}{A} 29:{A}($S:BYTE)($C:WORDL)($R:WORDL)($G:WORDL)($B:WORDL) assert ($S & 1) ==1 compute $4=$C compute $R=$C>0?$R/$C:0 compute $R=$R<=1?$R:1 compute $R=255*($R**2.5) compute $G=$C>0?$G/$C:0 compute $G=$G<=1?$G:1 compute $G=255*($G**2.5) compute $B=$C>0?$B/$C:0 compute $B=$B<=1?$B:1 compute $B=255*($B**2.5) compute $1=$R compute $2=$G compute $3=$B
Du coup on obtient une différenciation des couleurs un peu meilleure, mais cela rajoute d'autres artefacts:
Trois niveaux d'éclairage sur un surface rouge, résultat normalisé
Pour le rouge sous un fort éclairage, comme le canal de lumière blanche augmente bien plus vite que le canal rouge, on obtient un niveau de rouge plus bas. Et pour le bleu l'algorithme a amplifié le problème lors de l'éclairage intermédiaire.
Trois niveaux d'éclairage sur une surface bleue, résultat normalisé
Pour en avoir le cœur net, on a monté le capteur dans un petite coupe noire pour pouvoir refaire l'expérience dans un éclairage mieux contrôlé: la seule source de lumière est la LED blanche du board. On a promené notre capteur sur notre feuille colorée. Le résultat est beaucoup plus stable et permet de reconnaître facilement chaque teinte dominante. Mais il apparaît clairement que la normalisation de chaque canal est loin d'être idéale.
On a refait l'expérience en contrôlant l'éclairage
Conclusion
Pour l'instant nous ne somme pas très convaincus. Ce genre de capteur marcherait probablement très bien pour classifier un ensemble de couleur prédéfinies sous un éclairage bien contrôlé, par contre il parait difficile de l'utiliser pour reconnaître et reproduire avec précision une couleur quelconque observée sous un éclairage arbitraire. Vous comprenez maintenant pourquoi nous sommes un peu réticents à en faire un module Yoctopuce à part entière. Ceci dit, on n'abandonne pas l'idée: il est possible qu'il existe des capteurs de couleurs plus facile à exploiter. Si vous souhaitez malgré tout utiliser le TCS34725 avec un Yocto-I2C, vous pouvez télécharger les deux jobs décrits dans cet article.