Le cadre photo numérique

Article mis à jour en décembre 2024 : le cadre lit désormais des vidéos !

De nos jours, les photographies ne sont plus que numériques ou presque. On en prend en plus en plus, mais on les voit de moins en moins. On peut sûr en tirer une sélection au format papier, mais cela reste marginal. Le plus souvent, elles restent cantonnées, en vrac, au fond d’un disque dur ou d’un téléphone.

Pour remédier à ce problème auquel tout le monde est confronté, il existe dans le commerce des cadres photos numériques :

Ces petits écrans à poser sur un meuble remplacent la bonne vieille photo papier par un défilement de nos meilleurs clichés numériques. De quoi rêver de mieux ?

Pourtant, et je parle en connaissance de cause car j’ai possédé plusieurs de ces produits par le passé, ce n’est pas la solution idéale :

  • Déjà, la sélection des photos est longue et fastidieuse (sans parler du transfert via une carte mémoire ou clé USB). On le fait en général quelques fois au début, mais on continue rarement sur le long terme. Au final, on est rapidement ennuyés de voir toujours tourner les mêmes (vieilles) photos.
  • Les photos doivent être recadrées avec le même ratio largeur/hauteur que le cadre pour éviter des bandes noires assez disgracieuses.
  • Pour en profiter pleinement, il doit être allumé seulement quand on est présent. D’expérience, s’il est trop souvent allumé sans que personne n’en profite, il finit très rapidement débranché… Et devient pour le coup à jamais éteint.
  • Pour éviter cela, les appareils permettent souvent de paramétrer des créneaux horaires d’activité, mais il faut avoir un rythme régulier qui n’est pas le cas de tout le monde.
    C’est mieux lorsqu’ils possèdent également un capteur d’obscurité qui l’éteint dans le noir (car soit on dort, soit on n’est pas là). Certains modèlent possèdent aussi un capteur de mouvement, c’est l’idéal.
  • Le mode aléatoire des photos ne répond jamais à mes attentes. Sur tous les appareils que j’ai eus, la photo suivante est tirée aléatoirement parmi les photos disponibles. Cela signifie qu’une photo affichée récemment a autant de chances d’être à nouveau affichée que les autres. Sur le long terme, on a l’impression de souvent voir les mêmes.
    Il faudrait plutôt générer une liste contenant toutes les images dans un ordre aléatoire et ne la re-générer qu’une fois arrivé au bout. Ainsi, on garantit que toutes les images ont été affichées une seule fois.
  • Pour terminer, ces écrans dépassent rarement 10″ de diagonale, mais parfois, un écran plus grand serait avantageux.

Bref, vous l’aurez compris, malgré plusieurs essais je n’ai pas trouvé mon bonheur dans le commerce. Comme d’habitude, on n’est jamais aussi bien servi que par soi-même.

Au boulot !

Partie matérielle

Je pars d’un écran de PC 17″ de marque Belinea récupéré à la déchetterie. Ces vieux écrans ne sont jamais très difficiles à réparer. Dans mon cas, c’était une soudure cassée à force de manipulations au niveau du connecteur secteur.

Je ne suis pas sûr de sa référence précise, mais le modèle « 10 17 15 » semble beaucoup lui ressembler :

Je ne sais pas quelle est technologie de sa dalle LCD, mais les angles de vision et la qualité des couleurs sont excellents : parfait pour un cadre photo.

Je n’ai pas de photos du démontage, mais on retrouve, derrière la dalle, deux circuits imprimés :

  • Un premier pour l’alimentation : il est composé d’une alimentation secteur Flyback générant 12V à partir du réseau 230V et d’un autre convertisseur Flyback élevant ces 12V à quelques centaines de Volts pour alimenter les tubes CCFL du rétroéclairage.
  • Le second pour le pilotage de la dalle LCD à partir du signal d’entrée VGA.

J’ai prévu d’intégrer cette dalle dans un cadre en bois fait sur mesure, d’une épaisseur limitée. Malheureusement, le transformateur de l’alimentation secteur est trop épais pour cela. La partie générant la haute tension pour le rétroéclairage utilise par contre des composants relativement fins que je vais pouvoir conserver. Je sépare donc en deux le PCB de l’alimentation afin de ne garder que cette dernière partie :

À droite, l’alimentation 230V AC/12V DC que je n’utilise plus. Il est inscrit sur la sérigraphie qu’elle est capable de délivrer 1,25A. À gauche, l’élévateur de tension pour alimenter les deux tubes fluorescents du rétroéclairage : c’est la partie que je conserve. J’ai rajouté sur cette dernière une embase circulaire pour l’alimenter directement en 12V, en bas, ainsi qu’un départ pour alimenter le contrôleur de la dalle, en haut. En effet, je n’ai pas pu conserver le connecteur d’origine car il est situé sur la partie du circuit que je retire, à droite. Les fils rouge et noir sont l’alimentation tandis que les deux fils rose servent au pilotage de l’intensité du rétroéclairage via le menu OSD.

Sur la face supérieure de la partie conservée, on voit bien deux blocs distincts et similaires pour générer la haute tension nécessaires aux tubes CCFL. Ils sont composés d’un transformateur et d’un système auto-oscillant constitué de deux transistors et d’une capacité plastique. Une inductance de lissage est présente en amont afin que cet oscillateur très primitif ne pollue pas (trop) le rail d’alimentation 12V. Pour terminer, deux condensateurs haute tension sont connecté en série entre la sortie du transformateur et le tube. Le schéma de principe est le suivant :

Le circuit intégré en boîtier DIP8 que j’ai entouré en partie centrale est un double comparateur KA393A. Je n’ai pas étudié en détail son usage, mais je pense qu’il est utilisé dans le système de variation de luminosité des tubes.

Au verso du circuit imprimé se trouve un certain nombre de composants CMS, dont trois circuits intégrés en boîtier SOIC :

Le circuit central est, sans surprise, un double contrôleur de tube CCFL BI3101A. Il est entouré de deux circuits marqués « 4416 ADA W21B ». Il semblerait que ce soit deux MOSFET SI4416. Cela colle avec le schéma d’application typique du contrôleur CCFL :

En vert, les deux élévateurs auto-oscillants décrits plus haut. L’alimentation de chacun d’eux passe au travers d’un NMOS pour les activer ou non. La variation de luminosité des tubes est obtenue certainement en appliquant un signal PWM sur la grille de ces transistors, à condition que la fréquence de ce PWM soit très inférieure à la fréquence d’oscillation de l’élévateur.

Au verso de la partie 230V/12V que j’ai ôtée, on trouve la raison pour laquelle cet écran était à la déchetterie : une soudure du connecteur d’alimentation avait lâché suite aux différentes manipulations :

Bref, passons enfin aux choses sérieuses. Voici donc l’écran une fois débarrassé de sa coque plastique et des différents éléments désormais inutiles. J’ai collé directement les circuits imprimés sur la face arrière de la dalle, après avoir intercalé une feuille de plastique pour l’isolation électrique :

Pour info, avec le PCB d’alimentation entier, cela ressemblait à ceci :

Les deux connecteurs JST en bas à droite de la carte contrôleur (celle avec le vernis vert) sont dédiés à la connexion des haut-parleurs et du petit PCB sur lesquels sont soudés les boutons-poussoirs pour naviguer dans le menu OSD. J’ai déconnecté cette carte de l’écran car elle n’est pas utile au quotidien, mais je la conserve précieusement au cas où j’aie des réglages à faire à l’avenir :

Vue de face :

Pour remplacer l’alimentation secteur retirée, j’utilise un bloc secteur délivrant 12V sous 2A. Il était à l’origine conçu pour alimenter une Livebox. En blanc, il est plutôt discret et s’intégrera assez bien dans mon intérieur :

Au niveau informatique, le cœur du système est un Raspberry Pi Zero. Sa sortie vidéo est de type HDMI, non compatible avec ce moniteur qui n’accepte qu’un signal VGA. Plutôt que d’acheter un convertisseur HDMI/VGA actif, je préfère utiliser l’interface VGA666 : elle permet de créer un signal analogique VGA à partir de ses GPIOs.

En fait, il ne s’agit ni plus ni moins que de trois DACs résistifs de 6 bits dont le schéma est le suivant :

La tension sur Vout est égale à la somme des tensions sur chaque GPIO divisées par leur résistance associée :

Cette structure est extrêmement simple mais a toutefois un gros inconvénient : la tension de sortie du DAC est proportionnelle à la tension à l’état haut des GPIO, qui n’est pas vraiment précise, ni stable. De plus, avec 6 bits par couleurs, on peut en afficher « seulement » 262144, ce qui peut paraître peu par rapport aux 16 millions de couleurs –minimum– générées sur une sortie vidéo classique.

Générer un signal analogique sensible quel qu’un signal vidéo de cette manière peut donc être surprenant, mais en pratique, cela fonctionne plutôt bien !

Le schéma complet est le suivant. Les résistances sont en double car une double empreinte est présente sur le PCB vendu par l’auteur :

Au niveau logiciel, tout est prémâché : il suffit de quelques lignes à ajouter dans le fichier /boot/config.txt.

J’ai réalisé ce DAC sur du Veroboard avec des résistances à 1% aussi proches que possible des valeurs théoriques. J’ai aussi inclus un DC/DC pour générer le 5V nécessaire au Raspberry Pi à partir de l’alimentation 12V. Ce dernier est basé sur un circuit TL2575HV-5.0 dont il me restait des échantillons commandés pour réaliser l’onduleur de mon vélo. J’intègre pour finir un PMOS pour commuter l’alimentation du cadre à partir d’un GPIO du Raspberry Pi :

Le câble en haut amène le signal vidéo à la carte contrôleur de l’écran. J’ai utilisé un câble blindé pour acheminer les composantes analogiques R, V et B. Les deux fils bleus apportent les signaux de synchronisation horizontale et verticale.

Détail du circuit. On voit le DAC résistif à gauche, le DC/DC en haut à droite et les transistors pour piloter l’alimentation de l’écran au milieu en bas :

Le Raspberry Pi Zero qui n’existait pas en version W à l’époque ne dispose d’aucune connectivité réseau. Je lui adjoins donc une clé USB Wi-Fi. Comme il ne dispose que d’un port micro-USB, je dois lui ajouter un port USB type A de taille complète au bout d’un câble :

Je réalise ensuite l’encadrement en bois. Pour cela, j’ai acheté des tasseaux de 8×23 mm de côté et de 2,5m de longueur. Je les colle ensuite de manière à former un L qui va venir entourer la dalle LCD :

Je les ai d’abord collés perpendiculairement sur toute leur longueur afin de former un L de 2,5m de long. C’est seulement après que j’en ai coupé quatre morceaux à la scie à onglet pour former le cadre.

Vue depuis l’arrière :

Vue depuis l’avant :

Voici donc le résultat, une fois les cartes électroniques (alimentation de l’écran, carte à Raspberry Pi et contrôleur LCD) pistocollées à l’arrière :

Mise sous tension… et ça boote !

Yapluka écrire le soft maintenant.

Partie logicielle 1 : l’affichage des photos à proprement parler

La réalisation matérielle étant terminée, je dois maintenant écrire le logiciel qui affiche régulièrement des photos à l’écran.

Je n’ai jamais utilisé de gestionnaire de bureau graphique sur un Raspberry Pi : pour les applications embarquées que j’ai réalisées, une console m’a toujours suffi. Ce projet ne va pas bousculer mes habitudes : j’ai certes un écran à piloter, mais il me paraît bien plus simple d’utiliser le framebuffer que le système de fenêtrage X.

Les premiers essais sont faits en utilisant fbi, qui ne répond pas à mes attentes :

Je veux avoir le contrôle total de l’affichage et fbi ne me procure pas assez de souplesse pour cela. Je préfère donc écrire mon propre logiciel, en C. La bibliothèque graphique SDL me semble assez adaptée et de plus, je la connais déjà un peu : c’est grâce à elle que j’ai appris le langage sur feu Le Site du Zéro il y a déjà… un certain temps. Je m’empresse donc de réaliser mon Hello World :

Accessoirement, cela me permet de me rendre compte que je m’étais initialement emmêlé les pinceaux lors du câblage du DAC : certains poids binaires étaient inversés, parfois même avec des couleurs différentes. Cela se traduisait par des aberrations dans ce dégradé. Sur la photo ci-dessus, les erreurs ont été corrigées.

A noter que le Raspberry Pi est en permanence sous tension et que c’est le logiciel qui se charge d’allumer ou d’éteindre l’écran. Il est relativement simple et fonctionne de la manière décrite ci-dessous.

Il est d’abord contrôlé par deux fichiers textes stockées en mémoire volatile (ramdisk) pour limiter les cycles d’écriture sur la carte SD :

  • cadre_actif.txt : sa présence met le cadre en fonctionnement, son absence l’éteint.
  • update.txt : sa présence regénère la liste aléatoire des photos à afficher. Le répertoire contenant les images est donc listé, cette liste est mélangée pour obtenir un affichage aléatoire puis sauvegardée.

Deux autres fichiers, cette fois stockées sur la carte SD afin qu’ils soient conservés même après une extinction ou un redémarrage du système, gèrent la file d’affichage des photos :

  • liste.txt contient les chemins et l’ordre de toutes les images à afficher.
  • num_photo.txt contient le numéro dans la liste de la dernière photo affichée et permet de la retrouver au prochain allumage du cadre.

Pour réaliser les transitions entre les images, je souhaitais un fondu par transparence. Autrement dit, l’image suivante doit être affichée par dessus l’actuelle avec des niveaux de transparence successifs décroissants, de 100% à 0%. Cela demande beaucoup de ressources au Raspberry Pi Zero : chaque image intermédiaire nécessite plusieurs secondes pour être générée. Étant donné qu’il faut, d’après mes essais, une centaine d’images intermédiaires pour obtenir une transition fluide, cette dernière est très lente : plusieurs minutes. Je préférerais qu’elle ne dure que quelques secondes.

Je remédie à ce problème en mettant à profit le confortable espace de 512 Mo de RAM. Sitôt une image affichée, le programme s’attelle à générer toutes les images intermédiaires du fondu suivant et les conserve en RAM. Une fois toutes générées, elles sont affichées successivement avec 50ms entre chacune d’elles. La RAM est alors vidée et le cycle recommence.

La définition de l’écran (et donc de chaque image) étant de 1280×1024 pixels avec 8 bits par couleur, chacune occupe 3,75Mo. La transparence étant gérée sur 8 bits, 256 niveaux sont possibles. Calculer une image tous les deux niveaux de transparence donne 128 images à stocker, soit 480Mo : cela laisse trop peu de RAM pour le reste du système. Je me contente donc de calculer une image toutes les 3 valeurs de transparence pour en avoir 85. Les images occupent alors au maximum 318Mo de RAM, ce qui me paraît largement acceptable.

Voici en vidéo le rendu obtenu :

Quelques années plus tard, j’ai inclus le support des vidéos grâce à omxplayer. Ce logiciel écrit directement dans le framebuffer et remplace donc l’affichage courant par le sien. C’est vraiment pratique : après avoir extrait la première frame de la vidéo, le cadre calcule comme d’habitude un fondu entre l’image précédente et celle-ci. Ensuite, omxplayer est lancé et l’image se transforme en vidéo. Pendant sa lecture, le cadre calcule le fondu entre la dernière frame et la photo suivante. Ainsi, lorsqu’omxplayer se termine, il n’y a plus qu’à afficher le fondu vers l’image d’après.

Partie logicielle 2 : l’interface Web

Je fais presque toujours tourner une interface Web en parallèle d’un programme console écrit en C : c’est pour moi un bon moyen d’interagir graphiquement avec lui. Ce projet ne déroge pas à cette règle.

Je voudrais pouvoir visualiser dans une galerie photo les images présentes dans le cadre ainsi que l’allumer et l’éteindre. N’étant pas très bon designer ni développeur Web, je recycle une galerie photo que j’avais faite avec un ami il y a… très longtemps, ce qui explique le style un peu retro :

La galerie affiche les images dans le même ordre que le cadre et met en surbrillance l’image actuellement affichée (facile, puisque num_photo.txt stocke son numéro dans la liste). Les deux boutons On-Off du haut se contentent de créer ou de supprimer le fichier cadre_actif.txt.

Partie logicielle 3 : la mise à jour automatique et l’intégration avec la domotique

Pour rappel, les critères qui ne me convenaient pas dans les produits du commerce sont les suivants :

  • L’écran est trop petit.
  • La photo suivante à afficher est choisie aléatoirement parmi la totalité au lieu de la choisir parmi celles qui n’ont pas encore été affichées.
  • Ils peuvent être allumés lorsque l’on en a pas besoin, ou à l’inverse ne pas être allumés lorsque l’on est présent.
  • La mise à jour de la carte SD ou de la clé USB est fastidieuse… et manuelle.
  • La taille des photos n’est pas forcément adaptée à la taille de l’écran ce qui conduit à l’apparition de bandes noires (même si on pourrait bien sûr les recadrer préalablement).

Les deux premiers point ont déjà été résolus : il reste les trois suivants !

La détection de présence

Le plus simple serait certainement un capteur de mouvement PIR connecté à un GPIO du Raspberry Pi, mais cela aurait détérioré l’esthétique du cadre.

J’ai ensuite essayé de pinguer régulièrement le smartphone des occupants des lieux, après leur avoir donné un bail DHCP pour qu’ils aient une IP fixe. Toutefois, ce n’a pas été très concluant car la réponse aux pings des téléphones, lorsqu’ils sont en veille, est assez erratique. J’avais également peur que cela diminue significativement l’autonomie de leur batterie.

Finalement, je me suis rendu compte que l’éclairage à LED du plan de travail de la cuisine (ouverte sur mon salon) est vraiment corrélé à notre présence dans la pièce. Je décide donc de m’appuyer dessus pour savoir si le cadre doit être allumé ou éteint.

Cet éclairage utilise un bloc d’alimentation AC/DC 12V. Je tire donc un câble deux conducteurs jusqu’à l’interface du serveur. Là, il alimente un optocoupleur qui vient piloter une entrée de la carte d’interface. Cette dernière envoie alors une trame au serveur lors de l’allumage ou de l’extinction de la cuisine. Pour terminer, le serveur, lors de la réception de cette trame, fait une requête HTTP au serveur Web du cadre photo grâce à cURL.

Note : je décris (entre autres) cette interface matérielle dans l’épisode 4 de la domotique.

Il aurait été plus simple d’apporter le câble de l’éclairage directement au cadre photo plutôt que de passer par tous ces intermédiaires, mais cela me permet de ne pas avoir à le camoufler : l’ordre d’allumage ou d’extinction est reçu via le réseau Wi-Fi.

La mise à jour automatique des photos

Parce que c’est une chose essentielle quand on tient un minimum à ses données, le disque dur SSD de mon PC portable est régulièrement sauvegardé sur le serveur. Puisqu’il possède par ce biais une copie des photos, il est donc capable de maintenir le cadre à jour !

Il lui reste seulement à connaître quelles photos ont été sélectionnées pour être affichées sur le cadre : tous mes clichés, même si je ne conserve que les plus réussis, ne méritent pas d’être exposées. Sur mes cadres photos précédents, je dupliquais ou déplaçais les fichiers choisis dans des répertoires spécifiques. Cela engendrait une certaine lourdeur et je n’ai jamais été vraiment satisfait de cette solution. Pour cette version, j’utilise les méta-données des photos, en particulier la note de 1 à 5 qu’il est possible d’attribuer à chaque image grâce au tag Xmp.Rating. Ainsi, la note de chaque photo est intrinsèquement liée au fichier lui-même.

J’ai donc réalisé un autre programme qui tourne en tâche de fond sur le serveur et scrute régulièrement si une nouvelle sauvegarde a été effectuée. Dans ce cas, il passe en revue toutes les images et lit leur note. La liste des photos suffisamment bien notées est générée et comparée à la précédente. D’éventuelles photos rajoutées sont automatiquement redimensionnées et recadrées pour s’adapter parfaitement au cadre grâce à ImageMagick. J’étais initialement un peu réticent à l’idée de recadrer automatiquement les images : sans savoir ce que contenait les zones retirées, elles risquaient de perdre leur intérêt. Cela est particulièrement significatif sur les photos d’orientation Portrait recadrées en Paysage : la moitié de l’image est perdue. Toutefois, après essai, je trouve le résultat très acceptable pour la majorité des clichés. En tout cas, je préfère largement cela à la présence de bandes noires.

Pour terminer, les nouvelles photos recadrées et redimensionnées sont envoyées sur la carte SD du cadre photo via rsync. Comme leur définition a été abaissée à 1280×1024 pixels, elles n’occupent que peu de mémoire.

J’ai terminé d’écrire ce billet, j’espère qu’il vous a plu. A noter que j’ai créé un logiciel sur PC pour pouvoir facilement et surtout rapidement trier et noter les nombreuses photos, une fois de retour de vacances. Il fera l’objet d’un prochain article très prochainement 😉

[Mise à jour le 22/03/20] : il est maintenant disponible ici !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *