The Dot

Capsule lumino-temporelle. Lumière en lévitation, temps en suspension. Voici une horloge originale sous Arduino.


Voici une première réalisation personnelle utilisant le microcontrôleur Arduino et quelques matériaux de récupération.
Il s’agit d’une horloge lumineuse et décorative. La teinte change en fonction de l’heure, et un petit afficheur lumineux old-school notifie l’heure en cours.
Projet assez simple en apparence, mais qui m’a donné du fil à retordre vu les différents composants impliqués, mes modestes compétences en soudure, et les notions de programmation nécessaires.
C’est donc sûrement optimisable, mais tout fonctionne à merveille, et quelle satisfaction de finaliser un projet ! 🙂

The Dot : origine, concept & design

L’objectif final est d’obtenir une oeuvre décorative et utile. Voici le cheminement créatif.

thedot-gotaLe patient zéro est cette belle et banale bouteille d’eau en verre opaque, achetée sur un coup de coeur pour l’objet.
Une fois bue, hips, je l’ai entreposée parmi mon bric-à-brac de matières à recycler.

Quelques temps plus tard, cherchant une idée de projet sous Arduino, je tombe sur cette bouteille, et tilt !
Une LED RGB placée en son centre va donner un rendu sympathique de type halo lumineux avec l’opacité du verre.

Cool, mais une couleur statique c’est un peu monotone. Alors faisons varier la teinte de la LED… mais selon quelle logique ?

thedot-colourclockEt là je me suis souvenu de différents sites (The Colour Clock) qui affichent une couleur différente chaque seconde en fonction de l’heure. Voilà un concept parfait pour animer la LED.

thedot-hsvAprès divers tâtonnements et recherches sur internet pour trouver la méthode à utiliser pour faire varier la teinte en douceur, j’ai opté pour l’utilisation du modèle coloriel HSV (Hue Saturation Value, ou HSB : Hue Saturation Brightness, ou en français TSV : Teinte Saturation Valeur) .
La teinte est codée suivant l’angle qui lui correspond sur le cercle chromatique. La saturation est « l’intensité » de la couleur (apparence plus ou moins fade/pure). Et la valeur correspond à la « brillance » de la couleur (du plus sombre au plus clair).

thedot-hue
J’ai défini qu’en 1 heure je balaye toutes les teintes du cercle chromatique.
A l’usage cela me permet d’un coup d’oeil de savoir dans quel quart horaire je suis !
L’ajustement de teinte est effectué chaque seconde, les transitions sont donc douces et imperceptibles.

Au niveau de l’algorithme c’est assez simple :

  • on compte le nombre de secondes écoulées dans l’heure actuelle : (minutes*60)+secondes
  • on mappe ce nombre sur une plage entre 0 et 359°
  • Et voilà nous avons notre teinte (H). La saturation (S) et la valeur (V) restent invariables à leur valeur max
  • A l’aide de savantes fonctions récupérées online, je convertis la couleur du modèle HSV vers le modèle RGB pour configurer la LED

Chaque minute j’effectue une rapide baisse de luminosité, pour humaniser l’objet. Son cœur bat et nous rappelle que le temps passe 🙂

thedot-bubbleJ’ai ajouté un afficheur lumineux pour une utilité supplémentaire : indiquer l’heure exacte.
Mon choix s’est porté sur un afficheur 4 segment de type ‘Bubble Display’ qui était utilisé sur nos anciennes calculatrices. On peut encore en trouver sur internet. Le côté rétro et la finesse de l’affichage me plaisent bien.
Autre petit détail : chaque seconde le point entre les heures et les minutes clignotte.

Pour le design physique du projet voici quelques détails :

  • Le montage vient se loger dans une potence en bois récupérée dans une brocante.
  • La bouteille y est suspendu en son centre, la lumière lévite, le temps est en suspension.
  • La carte Arduino et son shield sont vissés sur le socle de la potence et restent apparents. J’aime le mélange des genres et des matériaux. L’alliance entre nature et technologie.
  • Les fils électriques sont en partie camouflés, le long du dos de la potence.

The Dot : la partie technique

Hardware : Composants & Schéma Fritzing
  Une carte Arduino Uno
  Un module DS1307 pour l’horloge
  Un shield vierge pour y souder le montage final
  Un afficheur 4 segment de type ‘Bubble’ (modèle choisi à cathode commune)
  Une Led RGB (modèle choisi à cathode commune, cablée en RBG)
  Un Shift Register 74HC595 pour piloter le Bubble Display qui demande 6 entrées
  C1 100nF (104) – Condensateur de découplage
  R1 180Ω – LED composante Rouge
  R2 200Ω – LED composante Vert
  R3 51Ω – LED composante Bleu
  R4 à R11 1kΩ – Anodes du Bubble Display

Voilà le montage réalisé sous Fritzing, j’ai fait au mieux, pas évident avec toutes ces connexions !
The Dot : Schéma électronique

Software : The Code
Le code source est librement réutilisable et modifiable. Il est bien commenté pour vous aider à le comprendre.
Deux librairies sont utilisées pour contrôler le module d’horloge DS1307 : Wire et RTClib.
Le reste des composants est contrôlé via des fonctions en partie récupérées sur d’autres sources et adaptées pour mes besoins.

// The Dot
// Version 1.0
// (c) 2015 bicouni
// CC BY 4.0 : The Dot de bicouni est mis à disposition selon les termes de la licence Creative Commons Attribution 4.0 International
// http://creativecommons.org/licenses/by/4.0/
// https://studio.loiseaucreatif.com/the-dot/


// Libraries
#include <Wire.h>
#include "RTClib.h"

// initializing RTC module
RTC_DS1307 rtc;

// Pins of the RGB Led
#define red 3
#define green 5
#define blue 6

// Cathode Pins of the Bubble Display
const int digitPins[4] = {12,11,9,8};

// Definition of datas for a digit number
const byte digit[10] =      //seven segment digits
{
  0b11111100, // 0
  0b01100000, // 1
  0b11011010, // 2
  0b11110010, // 3
  0b01100110, // 4
  0b10110110, // 5
  0b10111110, // 6
  0b11100000, // 7
  0b11111110, // 8
  0b11110110  // 9
};
int digitBuffer[4] = {0};
int digitScan = 0;
float bubbleTiming = 0.0;
boolean dotPoint = false;

// Pins of the Shift Register 74HC595
const int clockPin = 2;    //74HC595 Pin 11
const int latchPin = 4;    //74HC595 Pin 12
const int dataPin = 7;     //74HC595 Pin 14

// RGB Fading Configurator - Varation de la luminance chaque minute
// Durée du fading
#define fading 32
// Profondeur du fading, plus la valeur est faible plus on tend vers une luminance nulle (a respecter : deep < fading)
#define deep 4

// global values
int minuteValue = 0;
int minuteOld = 0;
int redOld = 0;
int greenOld = 0;
int blueOld = 0;

unsigned long currentMillis = 0;
long previousMillis = 0;
long dotMillis = 0;
int interval = 10;

long factor = 60;
long timing = 0;
float teinte = 0;
float luminance = 1;

int heures = 0;
int minutes = 0;
int minutesOld = 0;
int secondes = 0;

int pos = 0;


struct HSV{                  // structure to hold HSV values
  float hue;
  float sat;
  float lum;
};

struct RGB{                  // structure to hold RGB values
  byte R;
  byte G;
  byte B;
};

struct HSV hsv;              // current HSV values
struct RGB rgb;              // current RGB values




void setup()
{
  Serial.begin(9600);  
  
  Serial.println("");
  Serial.println("The Dot");
  Serial.println("(c) 2015 bicouni - bicouni.org");
  Serial.println("");
  
// checking RTC module  
  Wire.begin();
  rtc.begin();
  if (! rtc.isrunning())
  {
    Serial.println("Error RTC module: RTC is NOT running");
    return;
  }

// aJustement de l'heure si besoin
//rtc.adjust(DateTime(__DATE__, __TIME__));

// Initializing led pins as outputs
  pinMode(red, OUTPUT);
  pinMode(green, OUTPUT);
  pinMode(blue, OUTPUT);

// All leds off
  digitalWrite(red, LOW);
  digitalWrite(green, LOW);
  digitalWrite(blue, LOW);

// Initializing Bubble Display pins as outputs
for(int i=0;i<4;i++)
{
  pinMode(digitPins[i],OUTPUT);
}

// Initializing 74HC595 pins as outputs
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT); 

// Shutdown the Bubble Display
clearDisp();

// Shutdown the internal LED of Arduino UNO (i don't know why she is on by default..)
pinMode(13, OUTPUT);
digitalWrite(13, LOW);

}




void loop()
{

  currentMillis = millis();
  
// Actualising color every 'interval' seconds
  if(currentMillis - previousMillis > interval)
  {
    previousMillis = currentMillis;
    timeColor();
  }

}




void timeColor()
{
  DateTime now = rtc.now();
  heures = now.hour();
  secondes = now.second();
  minutes = now.minute();
  
  // Cercle chromatique complet en 1 journée
  //timing = (heures*factor*factor) + (minutes*factor) + secondes;
  //teinte = map(timing, 0, 86399, 0, 359)/360.0;

  // Cercle chromatique complet en 1 heure  
  timing = (minutes*factor) + secondes;
  teinte = map(timing, 0, 3599, 0, 359)/360.0;
  
  hsv.hue = teinte;
  hsv.sat = 1.0;

  if (minutesOld != minutes)
  {

   clearDisp(); 
   
   // Time conversion for Bubble Display
   bubbleTiming = float(heures+minutes/100.0);
    
   for (pos=fading; pos>deep; pos--)
    {     
     luminance = float(pos)/fading;
     hsv.lum = luminance;
     HSV2RGB(&hsv,&rgb);
     analogWrite(red, rgb.R); 
     analogWrite(green, rgb.G); 
     analogWrite(blue, rgb.B);  
     updateDisp(bubbleTiming); 
    }
    
   for (pos=deep; pos<fading+1; pos++)
    {
     luminance = float(pos)/fading;
     hsv.lum = luminance;
     HSV2RGB(&hsv,&rgb);
     analogWrite(red, rgb.R); 
     analogWrite(green, rgb.G); 
     analogWrite(blue, rgb.B);   
     updateDisp(bubbleTiming);
    }
    
    minutesOld = minutes;
    updateDisp(bubbleTiming);  
  }
  else
  {
   hsv.lum = 1.0;
   HSV2RGB(&hsv,&rgb);
   analogWrite(red, rgb.R); 
   analogWrite(green, rgb.G); 
   analogWrite(blue, rgb.B);
   updateDisp(bubbleTiming);  
  }
  
}




/*
    HSV2RGB
    converts value from HSV space to RGB one
    http://www.easyrgb.com/index.php?X=MATH&H=21#text21
    
    input s_hsv an HSV structure holding H S and V values
          s-rgb an RGB structure holding R G and B values
          
    output return nothing
           s_rgb updated
*/
void HSV2RGB(struct HSV *s_hsv, struct RGB *s_rgb){
float huevalue = s_hsv->hue; // variable to keep actual hue value
float satvalue = s_hsv->sat; // variable to keep actual saturation value
float lumvalue = s_hsv->lum; // variable to keep actual luma value
float var_r;                 // temporary variable for R
float var_g;                 // temporary variable for G
float var_b;                 // temporary variable for B
unsigned char R;             // final value for R
unsigned char G;             // final value for G     
unsigned char B;             // final value for B
  
  if ( satvalue < 0.003 )                       //HSV from 0 to 1
  {
     R = (unsigned char)(lumvalue * 255.0);
     G = (unsigned char)(lumvalue * 255.0);
     B = (unsigned char)(lumvalue * 255.0);
  }
  else
  {
     float var_h = huevalue * 6.0;
     if ( var_h > 5.99 ) var_h = 0.0;      //H must be < 1
     unsigned char var_i = (unsigned char)( var_h );       //Or ... var_i = floor( var_h )
     float var_1 = lumvalue * ( 1.0 - satvalue );
     float var_2 = lumvalue * ( 1.0 - satvalue * ( var_h - var_i ) );
     float var_3 = lumvalue * ( 1.0 - satvalue * ( 1.0 - ( var_h - var_i ) ) );
     
     if      ( var_i == 0 ) { var_r = lumvalue ; var_g = var_3    ; var_b = var_1; }
     else if ( var_i == 1 ) { var_r = var_2    ; var_g = lumvalue ; var_b = var_1; }
     else if ( var_i == 2 ) { var_r = var_1    ; var_g = lumvalue ; var_b = var_3; }
     else if ( var_i == 3 ) { var_r = var_1    ; var_g = var_2    ; var_b = lumvalue; }
     else if ( var_i == 4 ) { var_r = var_3    ; var_g = var_1    ; var_b = lumvalue; }
     else                   { var_r = lumvalue ; var_g = var_1    ; var_b = var_2; }
  
     R = (unsigned char) (var_r * 255.0);                  //RGB results from 0 to 255
     G = (unsigned char) (var_g * 255.0);
     B = (unsigned char) (var_b * 255.0);
  }

    s_rgb->R=R;
    s_rgb->G=G;
    s_rgb->B=B;
}




// Display a float value on the Bubble Display (4 digits + dot)
void updateDisp(float value){
  clearDisp();
  digitBuffer[3] = (int(value) % 1000)/10;
  digitBuffer[2] = (int(value) % 10);
  digitBuffer[1] = (int(value*10) % 10);
  digitBuffer[0] = (int(value*100) % 10);
  
  // Each second, put on or off the dot point : check here if 1 second is elapsed
  currentMillis = millis();
  if (currentMillis - dotMillis > 1000) { dotPoint = !dotPoint; dotMillis = currentMillis; }

  for(int i=0; i<4; i++){
    clearDisp();
    digitalWrite(digitPins[i], LOW); //Changed to LOW for turning the leds on.
    
    if(i==2 && dotPoint) //Add decimal dot
      shiftOut(dataPin, clockPin, LSBFIRST, digit[digitBuffer[i]] | 0b00000001);
    else
      shiftOut(dataPin, clockPin, LSBFIRST, digit[digitBuffer[i]]);
    
    digitalWrite(latchPin, HIGH);
    digitalWrite(latchPin, LOW);
    
    delay(7); //If not delayed, digits are seen brurry, if the value is 8 you migth see the display frickering.
  }
}




// Shutdown the Bubble Display
void clearDisp(){
  for(byte j=0; j<4; j++) {digitalWrite(digitPins[j], HIGH);} // Turns the display off. Changed to HIGH
}

Notes techniques supplémentaires
Précalibrage de la luminosité de la led
Ne connaissant pas les caractéristiques de cette led RGB de source inconnue, j’ai effectué un calibrage à l’oeil des 3 composantes RGB en ajustant les résistances afin que les 3 teintes aient visuellement la même luminosité.
Le résultat est le suivant :
Résistance sur composante Rouge : 180Ω
Résistance sur composante Verte : 200Ω
Résistance sur composante Bleu : 51Ω

Une simple petite boucle de rotation des 3 teintes m’a permis de faire le test visuel :

analogWrite(red, 255);
delay(1000);
analogWrite(red, 0);
analogWrite(green, 255);
delay(1000);
analogWrite(green, 0);
analogWrite(blue, 255);
delay(1000);
analogWrite(green, 0);
analogWrite(blue, 0);

Sur le blog de World of GZ vous trouverez un article intéressant au sujet des LEDs RGB

 

Afficheur Bubble Display
Pour gérer cet afficheur à l’aide du registre à décalage 74HC595, on allume successivement un à un les 4 digits, mais ils ne sont jamais allumés tous en même temps.
Vu que ce changement est très rapide (7ms environ), la persistence rétinienne humaine (POV) fait que l’on perçoit les 4 digits allumés en même temps.
Pour le câblage des résistances, j’ai opté pour 8 résistances sur les anodes, plutôt que 4 sur les cathodes. Cela me permet d’avoir une brillance identique sur les 4 digits, quelque soit le caractère affiché (sinon on se retrouve avec des caractères plus lumineux que d’autres selon le nombre de segments activés).

Voici quelques liens vers d’autres blogs traitant du sujet, dont je me suis inspiré :
Sparkfun  |  Open tech and DIY hacking  |  MultiWingSpan

 

Module temporel DS1307
Pour régler l’horloge, il faut décommenter la ligne suivante, puis transférer le code dans l’Arduino pour que le DS1307 se synchronise avec l’heure de l’ordinateur.

rtc.adjust(DateTime(__DATE__, __TIME__));

Une fois l’horloge à jour, recommenter la ligne et retransférer le code.

J’ai constaté que l’horloge à tendance à avancer un peu au fil des jours, de quelques minutes. Apparemment c’est un problème fréquent avec ce type de module. Voici quelques pistes sur les causes possibles :
– Proximité d’un circuit « bruyant », comme le quartz de l’Arduino
– Bruit généré par les alimentations
– Circuit ou Quartz de mauvaise qualité, mal implanté, copie chinoise…

Pour limiter le bruit j’ai placé un condensateur de découplage (100nF) au niveau de l’alimentation du DS1307. Finalement cela n’a pas réglé mon problème 🙁

 

Alimentation
J’ai prévu de laisser l’objet allumé en permanence, il sera donc alimenté via un chargeur USB et le port USB de l’Arduino.

Pour optimiser le tout on pourrai utiliser un autre modèle de carte Arduino moins gourmand en énergie, utiliser les Timers de l’Arduino, ajouter une fonction thermomètre, passer le projet sur batteries, ajouter un module de rechargement solaire…
Pas de limite à l’imagination, à vous de jouer !

J’espère que ce projet et son histoire vous ont plu, n’hésitez pas à partager et commenter, cela fait toujours plaisir et c’est encourageant 😉


 Licence Creative Commons
The Dot de bicouni est mis à disposition selon les termes de la licence Creative Commons Attribution 4.0 International.
Fondé(e) sur une œuvre à https://studio.loiseaucreatif.com/the-dot/.

2 Comments

Submit a Comment
  • Répondre

    Denis

    Bonjour,je vais m’y mettre tout de suite , je trouve votre réalisation très originale.

    • Répondre

      bicouni

      Merci !
      Je suis maintenant curieux de découvrir votre réalisation suite à cette source d’inspiration 🙂

Submit a CommentPlease be polite. We appreciate that.

Your Comment

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.