Thermomètre Revisité

De Wiki LOGre
Aller à la navigation Aller à la recherche

Présentation

Le Thermomètre

Modernisation d'un thermomètre d'extérieur 'vintage' en tôle émaillé à colonne d'alcool, avec des inscriptions désuètes du début du siècle (dernier). Thermomètre avec une publicité de la marque de chocolat Révillon, la même qui fait les papillotes de Noël vendues en supermarchés. Ce thermomètre extrait de vielleries d'un vidage de garage, est recherché par les collectionneurs et on peut trouver le même dans les enchères d'eBay à un prix stupéfiant.

le but du projet est de le restaurer avec un affichage de la température réalisé par une bande de diodes RGB, un capteur de température en semi-conducteur et un arduino pour piloter tout ça.

Composants

  • Bande de 78 diodes RGB type 2811/2812 avec une densité linéaire de 144 LEDs par mètre. Par bonheur, cette densité permet d'avoir exactment une diode par degré (vrai coup de chance) !
    • Bande achetée sur eBay
    • Utilisation d'une bibliothèque FastLED pour faciliter le pilotage de la bande de diodes (détails dans chapitre 'code').
  • Capteur de température LM35
    • Mesure en différentielle pour acquérir les températures négatives
  • Photo résistance pour capter la luminosité ambiante et pour créer une interruption permettant le changement de séquencement d'affichage
    • Mini module de mesure de lumière (marque générique chinoise) idem celui-ci
  • Mini buzzer pour signaler une interruption logicielle prise en compte par l'Arduino
  • Clone d'Arduino Uno pour le pilotage de l'ensemble
  • Tube de plexiglass pour reprendre le look du tube d'origine et pour protéger la bande de diodes, acheté en allemagne chez Plexiglas-shop
  • Carte fille de bidouille pour souder les composants


Modes d'affichage

  • L'affichage de la température s'effectue par l'empilage de 'gouttes' qui tombent du sommet de la colonne.

Chaque goutte a une couleur différente variant d'un bleu profond pour la température la plus froide jusqu'au vert pour la température maximale de 43°C. En jouant avec le codage des couleurs HSV (Hue, Saturation, Value), on arrive à générer de jolis dégradés de couleurs (style arc en ciel). La library FastLED est particulièreent riche en effet de commande de la couleur et de son intensité.

  • Avec la version 0.5, la température est désormais affichée suivant 6 modes différents.
  • Déjà les suggestions fusent de la part des membres du LOG:
    • Rajouter l'affichage d'une diode blanche qui conserve la température même pendant le jeu de lumières (essayée mais abandonnée, pas très beau)
    • Transformer le thermomètre en PONG entre le bas et le haut de la colonne (certainement sur un autre projet)
    • Afficher l'heure ..... ils ne sont pas en manque d'imagination :) Finalement j'ai eu une autre idée ; le GalvaCuckoo (coucou suisse analogique) voir la page du projetGalvaCuckoo

Branchement

  • Bande de LEDs
    • La bande de diode possède 3 contacts
      • Alim +5v
      • Ground 0v, qui doit être commun avec le 0v de l'Arduino pour le signal de data
      • Data, ligne de communication série gérée par la library FastLED
    • la commande des Leds (Data) sera connectée à la broche D6 de l'Arduino (paramètrable dans les DEFINE du programme) via une résistance de 330 (ou 220) Ohms (recommandation du constructeur)
  • Capteur de température LM35
    • Alimentation en +5V
    • Ground du LM35 connecté à celui de l'Arduino via 2 diodes (type 1N4004) en série.
    • Sortie Data (pin du milieu du LM35) connectée à A0 (entrée analogique de l'Arduino) avec une résistance de 19 kohms de pull down vers le zéro.
    • Broche Ground du LM35 connectée à A1 deviendra un ground virtuel (> entrée analogique de l'Arduino)
    • la mesure de température sera effectuée en mode différentielle entre V(A0) et V(A1)
  • Module de mesure de lumière
    • Remplacement de la photo résistance d'origine montée sur le module par un CL705CL (en boitier TO3 qui sèchait dans un tiroir depuis 40 ans) collé sur le thermomètre au bout de 20 cm de câble.
    • Le signal analogique A0 du module sera connecté à A2 (entrée analogique de l'Arduino)
  • Buzzer
    • Mini buzzer connecté entre la sorte digitale D10 et le Ov.


  • Plan de câblage
Thermometre-revisite-v1.png

Mécanique

Le thermomètre en tôle emaillée est collé à une base en MDF de 40, évidée pour héberger le boitier de l'arduino et diminuer la masse de l'ensemble.

Le capteur de lumière ambiante est placé derrière le trou de la fixation d'origine, en bas du thermomètre.

Code

J'utilise une bibliothèque 'concurrente' de celle d'Adafruit (Neopixel) qui est 'FastLED.h'. Elle est maintenue et en évolution permanente, avec une communauté d'utilisateurs qui alimente le blog et qui aide les utilisateurs (novices) : [1]

La bibliothèque est disponible ici : github pour la télécharger

/* Temperature Display with Falling drops and other effects by pja
using code originally developped by: Mark Kriegsman 
Date: March 2016
Please use FastLED 3.1 or greater.
version 0.5
- changing Leds' brightness is triggered by AmbiantLight with analog reading
- LM35 is wired for negative temperature, reading is differential mode
*/

#include "FastLED.h"        // FastLED library. the latest copy of FastLED 3.1.

//#if FASTLED_VERSION < 3001000
//#error "Requires FastLED 3.1 or later; check github for latest code."
//#endif 

// Fixed definitions cannot change on the fly.
//#define DEBUG
#define LED_DT 6             // Data pin to connect to the strip.
#define COLOR_ORDER BGR      // Are they RGB, GRB or what??
#define LED_TYPE WS2811      // Don't forget to change LEDS.addLeds
#define NUM_LEDS 78          // Number of LED's.
#define OFFSET   33          // variable adjustment
#define MAX_BRIGHT 255		 // Max bright for worm (will be divided by 2)

CRGB leds[NUM_LEDS];  // Initialize our LED array.


// Define variables used by the sequences.
uint8_t max_bright = 35;     // Overall brightness definition. It will be changed on the fly by light Sensor
int    myhue =   100;
uint8_t gHueShow = 100;      // rotating "base color" used by many of the patterns
int gHue = 0;                // rotating "base color" used by many of the patterns
int pos = 81; 				 // trick for starting the antialiasing worm (81=NUM_LEDS +3)
int delayval = 10; 
int filling =0;
uint16_t colorsurface ;
int surface_color[NUM_LEDS]; // table with each drops' color
int sensorValueTemp = A0;    // LM35 output on pin A5
int virtual_ground  = A1;    // virtual ground pin of LM35
int sensorValueAmbiantLight = A2;    // light sensor output on pin A2
// interrupt 0 on pin D3 for Arduino UNO or Leonardo
int duration = 1000;        // time unit for displaying new effect
//int buzzer =10;             // connexion buzzer
int LedToDisplay;
// variables for worm()
int     F16pos = 0; 		// position of the "fraction-based bar"
int     F16delta = 1; 		// how many 16ths of a pixel to move the Fractional Bar
uint8_t Fhue = 100; 		// color for Fractional Bar
uint8_t Finalhue = 100; 	// color at end of worm race
int Width  = 2; 			// width of worm in pixels
int InterframeDelay = 40; 	//ms


void setup() {
  delay(500);    
  //pinMode(buzzer,OUTPUT);  
  //digitalWrite(buzzer, LOW);               // silent start up
  Serial.begin(9600);
  //attachInterrupt(0, SwitchShow, RISING);  // interrupt 0 on pin D3
  LEDS.addLeds<LED_TYPE, LED_DT, COLOR_ORDER>(leds, NUM_LEDS);  // Use this for WS2811
  FastLED.setBrightness(max_bright);
  //set_max_power_in_volts_and_milliamps(5, 500);               // FastLED 2.1 Power management set at 5V, 500mA.
} // end setup()


void loop () {
#ifdef DEBUG
  Serial.println("++++ debug +++++");
  Serial.print("BigShowOn/Off : ");Serial.println(BigShowOn);
#endif
// init
 FastLED.setBrightness(getAmbiantLight());
 fadeToBlackBy(leds, NUM_LEDS, 150);
 FastLED.show();  
 
 // display mode #1 : dropping falls
 DroppingTemp(int(getTemp()));      // filling the tube with multi color drops
 delay(2000);                     // tempo for steady display
 fadeToBlack();
 // display mode #2 : Haley's comet
  FastLED.setBrightness(getAmbiantLight());
 halley();
 fadeToBlack(); 
 // display mode #3 : worm
 worm();
 for(int iter=0;iter<duration*3;iter++)
   {
    bpm_pja( 20,5,LedToDisplay);
    FastLED.show();  
    }
	 FastLED.setBrightness(getAmbiantLight());
 for(int iter=0;(iter<duration*3);iter++)
	 {gHue++; 
	  bpm(62, 3);
	  bpm(15, 1);
	  FastLED.show();  
	  } 
	   FastLED.setBrightness(getAmbiantLight());
  for(int iter=0;(iter <duration);iter++)
	  {
	   rainbowWithGlitter();
	   FastLED.show();  
	   } 
  gHueShow=20;
   FastLED.setBrightness(getAmbiantLight());
  for(int iter=0;(iter <duration*2);iter++)
	  {
	   confetti(); 
	   FastLED.show();  
	  }
	   FastLED.setBrightness(getAmbiantLight());
  for(int iter=0;(iter <duration*2);iter++)
	  {
	   juggle(); 
	   FastLED.show();  
	  }
	   FastLED.setBrightness(getAmbiantLight());
  for(int iter=0;(iter <duration);iter++)
	  {
	   applause(); 
	   FastLED.show();  
	  }
  fadeToBlack(); 
} // end main loop()


void DroppingTemp(uint8_t TempToDisplay)
//----- function dispay temperature on the LED column
{
#ifdef DEBUG
 Serial.println("Start falling drops");
#endif
  gHue=120;  // blue color to start with cold temperature at bottom of the column
  filling=0;
  LedToDisplay = TempToDisplay + OFFSET;  		// OFFSET is the number of LEDs to reach Zero deg. celcius. It depends of building the column
  for(filling;filling <LedToDisplay;filling++)
  {
    for(int pos=NUM_LEDS;pos>filling;pos--)
    {
    fadeToBlackBy(leds, NUM_LEDS, 60);
    leds[pos] += CHSV( gHue++/48, 255, 192);
    surface_color[filling]=gHue++/48;
    for(int surface=0;surface <filling+1;surface++)
      {
      leds[surface] = CHSV( surface_color[surface], 255, 192);
      }
    FastLED.show();  
    delay(10);
   }
  } // end filling column
  // clear top of tube 
  filling = LedToDisplay;
  for(filling;filling <NUM_LEDS;filling++)
  {
  leds[filling] = CHSV(45, 0, 0);  // S=0, V=0
  }
  FastLED.show();  
}

float getTemp()
// function getTemperature from LM35 with average on 10 readings
{ 
  float SumOfTemp = 0.0;
  float voltage, reading,reading2 = 0.0;
  float temperatureC = 0.0;
  for(int iter=0;iter <10;iter++)
  {
    reading = analogRead(sensorValueTemp);  
    reading2 = analogRead(virtual_ground);   
    voltage = (reading-reading2) * 5000 / 1024.0; 
    temperatureC = (voltage)/10 ;  //converting from 10 mv per degree
    SumOfTemp+=temperatureC;
  }      
  temperatureC=SumOfTemp/10.0;     // average on 10 readings                       
#ifdef DEBUG
 Serial.print("Temperature :");
 Serial.println(int(temperatureC),DEC);
#endif
 return temperatureC;
}


uint8_t getAmbiantLight()
// ----- function getAmbiantLight to control brightness using CL-705-HL light sensor
{
 int lightLevel = (100 - (analogRead(sensorValueAmbiantLight)/10));
 int TempBrightness = constrain(lightLevel,20,75);
#ifdef DEBUG
  Serial.print("Brightness : ");Serial.println(TempBrightness, DEC);
#endif
 return TempBrightness;
}

void rainbowWithGlitter() 
// ----- function rainbow built-in FastLED rainbow, plus some random sparkly glitter
{
  rainbow();
  addGlitter(80);
}

void rainbow() 
// FastLED's built-in rainbow generator
{
  fill_rainbow( leds, LedToDisplay, gHueShow, 5);
}

void addGlitter( fract8 chanceOfGlitter) 
{
  if( random8() < chanceOfGlitter) {
    leds[ random16(LedToDisplay) ] += CRGB::White;
  }
}

void confetti() 
  // ---- function confetti: random colored speckles that blink in and fade smoothly
{
  fadeToBlackBy( leds, LedToDisplay, 10);
  int pos = random16(LedToDisplay);
  leds[pos] += CHSV( gHueShow + random8(64), 200, 255);
}

void juggle()
// eight colored dots, weaving in and out of sync with each other 
{
  fadeToBlackBy( leds, LedToDisplay, 20);
  byte dothue = 0;
  for( int i = 0; i < 8; i++) {
    leds[beatsin16(i+7,0,LedToDisplay)] |= CHSV(dothue, 200, 255);
    dothue += 32;
  }
}

void applause()
{
  static uint16_t lastPixel = 0;
  fadeToBlackBy( leds, LedToDisplay, 32);
  leds[lastPixel] = CHSV(random8(HUE_BLUE,HUE_PURPLE),255,255);
  lastPixel = random16(LedToDisplay);
  leds[lastPixel] = CRGB::Yellow;
}

void fadeToBlack()
// as name said : fade to black
{
    for(int iter=0;iter <200;iter++)
    {
    fadeToBlackBy( leds, NUM_LEDS, 2);
     FastLED.show();  
    }
}

void bpm( uint8_t bpmSpeed, uint8_t stripeWidth)
{
  // colored stripes pulsing at a defined Beats-Per-Minute (BPM)
  uint8_t BeatsPerMinute = bpmSpeed;
  CRGBPalette16 palette = PartyColors_p;
  uint8_t beat = beatsin8( BeatsPerMinute, 64, 255);
  for( int i = 0; i < LedToDisplay; i++) {
    leds[i] = ColorFromPalette(palette, gHue+(i*stripeWidth), beat);
  }
}

void bpm_pja( uint8_t bpmSpeed, uint8_t stripeWidth,uint8_t length )
{ uint8_t BeatsPerMinute = bpmSpeed;
  CRGBPalette16 palette = PartyColors_p;
  uint8_t beat = beatsin8( BeatsPerMinute, 64, MAX_BRIGHT/2);
  for( int pos = 0; pos < length; pos++) {
    leds[pos] = ColorFromPalette(palette, Finalhue+(pos*stripeWidth), beat);
  }
}

void halley()
{
 // effect like halley's comet
 gHue=0;
 for(int iter=0;iter<duration*3;iter++)
 {
  gHue++; 
  sinelon(20,1,LedToDisplay);
  FastLED.show();  
  } 
}

void sinelon( uint8_t bpmSpeed, uint8_t fadeAmount, uint8_t UpperLimit )
{
  // a colored dot sweeping back and forth, with fading trails
  fadeToBlackBy( leds, UpperLimit, fadeAmount);
  int pos = beatsin16(bpmSpeed, 0, UpperLimit);
  leds[pos] += CHSV( gHue, 255, 192);
}

void drawFractionalBar( int pos16, int width, uint8_t hue)
// tailored antialiasing function from Mark Kriegsman
// each step is divided into 16th micro-steps that drive the brightness	
{
  int i = pos16 / 16; 				// convert from pos to raw pixel number
  uint8_t frac = pos16 & 0x0F; 		// extract the 'factional' part of the position
  uint8_t firstpixelbrightness = MAX_BRIGHT;	// trick: MAX_BRIGHT is kept to 255, otherwise the 16th steps are not smooth
  uint8_t brightness_decrease = MAX_BRIGHT - (frac * 16);
  uint8_t lastpixelbrightness  = MAX_BRIGHT - brightness_decrease;
  uint8_t bright;
  for( int n = 0; n <= width; n++) {
    if( n == 0) {
      // first pixel in the bar
      bright = firstpixelbrightness/2;		//second trick: maximum brightness is limited to 50% to be coherent with the other display modes.
    } else if( n == width ) {
      // last pixel in the bar
     bright = lastpixelbrightness/2;
    } else {
      // middle pixels
      bright = MAX_BRIGHT/2;				//second trick: maximum brightness is limited to 50% to be coherent with the other display modes.
    }
    leds[i] = CHSV( hue, 255, bright);		// full saturation is chosen (s=255)
    i++;
  }
  for( int n = 0; n <= i-2; n++) {
   leds[n] = CHSV( hue, 255, MAX_BRIGHT/2);
  }
}
 
void worm()
// dispaly effect; a worm is climbing the tube and the color is changing every led up
{
  FastLED.setBrightness(MAX_BRIGHT);
  Fhue = 100;
  Finalhue = 100;
  F16pos=0;
  memset8(leds, 0, NUM_LEDS * sizeof(CRGB));
  for( int n = 0; n < ((LedToDisplay-2)* 16)-1; n++) { // trick on end of loop: worm is 2 pixels long
    F16pos += F16delta;
  	if( F16pos >= (LedToDisplay * 16)) {
   		F16pos -= (LedToDisplay * 16);
  		} 
		Finalhue +=Fhue++/80;
    drawFractionalBar( F16pos, Width, Finalhue);
    FastLED.show();
    delay(InterframeDelay);
  }
}