Ce site regroupe des contributions en français de bidouilleurs, bricoleurs, électroniciens, adeptes de bidouillabilités, pour faire court : de passionnés du « fait soi-même ».
L'Horloge/Réveil étant fin prête, il est temps de réviser la configuration de la WebRadio.
J'ai tout repris de zero… C'est qu'il s'est passé pas mal de temps depuis les premières expérimentations, et pas mal de version de Raspbian sont passées sous les ponts !
On va ici étudier la partie software, la partie hardware concernant le câblage des GPIO, relais et interfaçage horloge, pilotée par le programme mpc.py venant au prochain billet.
Placer la carte SD dans le Raspberry, et le mettre sous tension.
Exécuter raspi-config, et configurer tous les trucs comme on veut, notamment les variables locales (time zone, clavier, etc), et surtout le login automatique de l'utilisateur.
sudo raspi-config
Après le reboot, pour rappel :
Login : pi
Pass : raspberry
Effectuer les mises à jour :
sudo apt update
sudo apt upgrade
Mettre les fichiers temporaires en ram pour économiser sur la durée de vie la la carte SD.
Pour configurer le wifi, éditer le fichier /etc/wpa_supplicant/wpa_supplicant.conf et modifier comme suit la section wlan0 : (ou par raspi-config)
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=FR
network={
ssid="le nom du réseau wi-fi"
psk="le mot de passe du réseau wi-fi"
}
Relancer ensuite le réseau avec la commande :
sudo service networking restart
Pour régler la résolution d'écran afin de l'adapter au petit écran type radar de recul :
sudo nano /boot/config.txt
framebuffer_width=320
framebuffer_height=240
Configurer la sortie sonore sur le jack plutôt que le HDMI (par défaut) :
amixer cset numid=3 1
Régler le niveau sonore, puis sauvegarder les modifications :
alsamixer
sudo alsactl store 0
2 - Les logiciels pour la WebRadio :
Installer le lecteur de musique, le pilotage GPIO et la synthèse vocale :
sudo apt install mpd mpc wiringpi espeak
Configuration du lecteur mpc/mpd, éditer :
sudo nano /etc/mpd.conf
Indiquer les chemins où seront stockés les playlists et les fichiers mp3 :
Créer un fichier portant l’extension .m3u avec simplement la liste des URL des stations radio
nano /home/pi/playlists/radio.m3u
# StahlRadio - Metal - http://stahlradio.de/
http://streamplus52.leonex.de:22810/;
# Radio Metal - http://www.radiometal.com/
http://stream.radiometal.com:8010
# EBM Radio - Industrial, Futurepop, Synthipop, Dark Wave, Gothic, ... et Depeche Mode
http://ebm-radio.org:9000/ebm.ogg
# japanfm
http://radio.japanfm.fr/japan
# TSF Jazz
http://tsfjazz.ice.infomaniak.ch:80/tsfjazz-high
# Radio Neo - Français
http://stream.radioneo.org:8000/;chat.mp3
# Radio Equinoxe - Electo - http://radioequinoxe.com/radio/liens-decoute/
http://www.radioking.com/play/radio-equinoxe/76118
Écriture d'une playlist de fichier mp3 :
Créer un fichier portant l’extension .m3u avec simplement la liste des fichiers mp3 (stockés dans le dossier /home/pi/music)
nano /home/pi/playlists/FarCryBloodDragon.m3u
01. Rex Colt.mp3
02. Blood Dragon Theme.mp3
03. HELO-73.mp3
04. Warzone.mp3
05. Moment of Calm.mp3
06. Dr Elizabeth Darling.mp3
07. Power Core.mp3
08. Protektor.mp3
09. Sloan.mp3
10. Sloan's Assault.mp3
11. Blood Scope.mp3
12. Combat I.mp3
13. Combat II.mp3
14. Combat III.mp3
15. Katana.mp3
16. Omega Force.mp3
17. Nest.mp3
18. Rex's Escape.mp3
19. Love Theme.mp3
20. Sleeping Dragon.mp3
Et attention, pour que les fichiers mp3 soient pris en compte, il est nécessaire de scanner le dossier « music », sinon les mp3 ne fonctionneront pas !!
mpc update
La config est prête, voici le programme python mpc.py qui exploite tout ça pour fournir le service :
#!/usr/bin/env python2.7
# coding: utf-8
# on importe les modules nécessaires
import time
import os, sys
import RPi.GPIO as GPIO
import subprocess
import socket
# on met RPi.GPIO en mode notation BCM (numéro des pins)
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
# on initialise les GPIO en mode "écoute" (GPIO1 sur la nappe)
GPIO.setup(17, GPIO.OUT, initial = GPIO.LOW) # GPIO0 - pin11 - Relais Ampli
#GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP) # GPIO1 - pin12 - LIRC
GPIO.setup(27, GPIO.IN, pull_up_down=GPIO.PUD_UP) # GPIO2 - pin13 - Station suivante
GPIO.setup(22, GPIO.IN, pull_up_down=GPIO.PUD_UP) # GPIO3 - pin15 - PowerOFF
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_UP) # GPIO4 - pin16 - Station précédente
GPIO.setup(24, GPIO.IN, pull_up_down=GPIO.PUD_UP) # GPIO5 - pin18 - LCD ON/OFF
GPIO.setup(25, GPIO.IN, pull_up_down=GPIO.PUD_UP) # GPIO6 - pin22 - Play/pause/stop ne fonctionne plus à cause de la tempo 1H
GPIO.setup(4, GPIO.OUT, initial = GPIO.LOW) # GPIO7 - pin7 - Relais LCD
long_press = 1
very_long_press = 3
site = 'www.google.fr'
# Nettoyage lecteur média
os.system('mpc clear')
def is_connected(REMOTE_SERVER):
try:
# see if we can resolve the host name -- tells us if there is a DNS listening
host = socket.gethostbyname(REMOTE_SERVER)
# connect to the host -- tells us if the host is actually reachable
s = socket.create_connection((host, 80), 2)
return True
except:
pass
return False
# Chargement de la playlist
if is_connected(site):
# print "Site disponible"
os.system('mpc load radio')
else:
# print "Site indisponible"
os.system('mpc load foo')
# au bout de QUelques minutes, stopper la lecture
heure=0
fin=9999999999
# fonction qui sera appelée quand on appuiera sur le bouton 25
def bouton25(a):
global fin
button_press_timer = 0
GPIO.remove_event_detect(25) # empéche le bouton d'etre détecté x fois de plus
while True:
if(GPIO.input(a) == GPIO.LOW ): # bouton appuyé
button_press_timer += 0.2 # incrémente tant que le bouton est maintenu
else: # bouton relaché
if (button_press_timer > very_long_press):
# Arret du lecteur media
os.system("espeak 'Arrêt de la wéb-radio !' -v fr+f5 -s150 -p 75 -a 300") # Voix synthétique
subprocess.call(["mpc stop"], shell=True)
GPIO.output(17, GPIO.LOW) # relais ampli OFF
elif (button_press_timer > long_press):
# Pause du lecteur media
os.system("espeak 'playlist Far Cry Blood Dragon' -v en+f5 -s150 -p 75 -a 300")
os.system('mpc clear')
os.system('mpc load FarCryBloodDragon')
os.system('mpc play')
elif (button_press_timer > 0):
# Lecture du lecteur media
GPIO.output(17, GPIO.HIGH) # relais ampli ON
os.system('mpc clear')
# Chargement de la playlist
if is_connected(site):
# print "Site disponible"
os.system('mpc load radio')
os.system("espeak 'lecture' -v fr+f5 -s150 -p 75 -a 300")
os.system('mpc play 2')
fin=time.time()+3600 # attend 1h - 3600
fin=round(fin)
else:
#print "Site indisponible"
os.system('mpc load foo')
os.system("espeak 'lecture' -v fr+f5 -s150 -p 75 -a 300")
os.system('mpc play')
fin=time.time()+3600 # attend 1h - 3600
fin=round(fin)
button_press_timer = 0
time.sleep(0.2)
# fonction qui sera appelée quand on appuiera sur le bouton 27
def bouton27(channel):
# entrée suivante du lecteur media
os.system("espeak 'station suivante' -v fr+f5 -s150 -p 75 -a 300")
os.system("mpc next")
# fonction qui sera appelée quand on appuiera sur le bouton 23
def bouton23(channel):
# entrée précédente du lecteur media
os.system("espeak 'station précédente' -v fr+f5 -s150 -p 75 -a 300")
os.system("mpc prev")
# fonction qui sera appelée quand on appuiera sur le bouton 22
def bouton22(channel):
os.system("sudo poweroff")
# fonction qui sera appelée quand on appuiera sur le bouton 24
def bouton24(a):
global fin
button_press_timer = 0
GPIO.remove_event_detect(24) # empéche le bouton d'etre détecté x fois de plus
while True:
if(GPIO.input(a) == GPIO.LOW ): # bouton appuyé
button_press_timer += 0.2 # incrémente tant que le bouton est maintenu
else: # bouton relaché
if (button_press_timer > long_press):
GPIO.output(4, GPIO.LOW) # relais LCD OFF
os.system("espeak 'Éteindre lécran LCD' -v fr+f5 -s150 -p 75 -a 300")
GPIO.output(17, GPIO.LOW) # relais ampli OFF
elif (button_press_timer > 0):
os.system('mpc stop') # stop la radio
GPIO.output(4, GPIO.HIGH) # relais LCD ON
GPIO.output(17, GPIO.HIGH) # relais ampli ON
os.system("espeak 'Allumage de lécran LCD' -v fr+f5 -s150 -p 75 -a 300")
fin=time.time()+3600 # attend 1h - 3600
fin=round(fin)
button_press_timer = 0
time.sleep(0.2)
# on met le bouton en écoute
GPIO.add_event_detect(27, GPIO.FALLING, callback=bouton27, bouncetime=200)
GPIO.add_event_detect(22, GPIO.FALLING, callback=bouton22, bouncetime=200)
GPIO.add_event_detect(23, GPIO.FALLING, callback=bouton23, bouncetime=100)
GPIO.add_event_detect(24, GPIO.FALLING, callback=bouton24, bouncetime=200)
GPIO.add_event_detect(25, GPIO.FALLING, callback=bouton25, bouncetime=200)
# on lance une boucle infinie, pour garder le script actif
while True:
if (heure>=fin):
os.system("mpc stop")
GPIO.output(17, GPIO.LOW) # relais ampli OFF
GPIO.output(4, GPIO.LOW) # relais LCD OFF
fin=9999999999
else:
heure=time.time()
heure=round(heure)
# une petite pause entre chaque boucle, afin de réduire la charge sur le CPU
time.sleep(2)
# on réinitialise les ports GPIO en sortie de script
GPIO.cleanup()
Le fonctionnement est simple :
- Le bouton 25 pour allumer et éteindre la radio, ou lancer une playlist mp3, par des appuies plus ou moins long (l'ampli audio est géré automatiquement).
- Le bouton 27 pour passer à la station radio suivante.
- Le bouton 23 pour passer à la station radio précédente.
- Et enfin, le bouton 24 pour allumer et éteindre l'écran LCD (et l'ampli audio).
- Lorsque la radio est allumée, soit par l'alarme de l'horloge, ou par le bouton 25, elle fonctionne durant 1h avant de s'éteindre. Idem pour l'écran LCD.
- Si internet n'est pas disponible, la radio bascule sur une playlist mp3 de secours.
Pour démarrer automatiquement le programme python dés le démarrage du Raspberry, créer un service pistart.service :
sudo nano /etc/systemd/system/RadioReveil.service
[Unit]
Description=Démarre le script de la webradio
[Service]
ExecStart=/usr/bin/python /home/pi/mpc.py
[Install]
WantedBy=multi-user.target
sudo systemctl status RadioReveil.service
sudo systemctl start RadioReveil.service
3 - Installer et configurer Kodi :
Le médiacenter est facultatif, il ouvre cependant sur une galaxie de possibilités…
sudo apt-get install kodi
Pour lancer Kodi automatiquement après l'autologin, éditer le fichier .profile :
nano ~/.profile
Et ajouter les lignes suivantes à la fin :
tty=`tty`
# Start Kodi only if login in tty1
if [ $tty = '/dev/tty1' ]; then
kodi
fi
On prendra soins de configurer Kodi dans la résolution vidéo minimale (640x480), mais au vu de la faible résolution de l'écran LCD, il s'avère nécessaire d'afficher de grande police de caractère.
Pour cela éditer :
nano .kodi/addons/skin.confluence/720p/Font.xml
À la sous section <Name>font13, augmenter la valeur <Size> à 50 (initialement réglée à 20).
Utile au médiacenter, cette partie est donc également facultative !
Installer le soft de gestion de l'infrarouge :
sudo apt-get install lirc
Éditer hardware.conf pour régler les paramètres de LIRC :
sudo nano /etc/lirc/hardware.conf
########################################################
# /etc/lirc/hardware.conf
#
# Arguments which will be used when launching lircd
LIRCD_ARGS="--uinput"
# Don't start lircmd even if there seems to be a good config file
# START_LIRCMD=false
# Don't start irexec, even if a good config file seems to exist.
# START_IREXEC=false
#Try to load appropriate kernel modules
LOAD_MODULES=true
# Run "lircd --driver=help" for a list of supported drivers.
DRIVER="default"
# usually /dev/lirc0 is the correct setting for systems using udev
DEVICE="/dev/lirc0"
MODULES="lirc_rpi"
# Default configuration files for your hardware if any
LIRCD_CONF=""
LIRCMD_CONF=""
########################################################
Activer le module sur le RpiPi en éditant config.txt
sudo nano /boot/config.txt
Dé-commenter la ligne (sans autre argument, c'est le GPIO18 qui sera écouté par le module, sinon pour par exemple le GPIO23, on indiquera dtoverlay=lirc-rpi,gpio_in_pin=23) et redémarrer le RpiPi.
dtoverlay=lirc-rpi
Pour tester si les commandes infrarouge sont bien reçues :
sudo killall lircd
sudo mode2 -d /dev/lirc0
Si jamais le programme ça répond ceci :
Using driver devinput on device /dev/lirc0
Trying device: /dev/lirc0
Using device: /dev/lirc0
Partial read 8 bytes on /dev/lirc0
Alors on est en présence d'une Raspbian stretch (9), lircd buggée, pour corriger le problème, lancer le script :
sudo /usr/share/lirc/lirc-old2new
À chaque appuie sur une touche de la télécommande, devrait s'afficher ce genre de truc :
space 1223
pulse 281
space 1193
pulse 350
space 1199
pulse 264
space 1901
Bien, maintenant on va identifier la télécommande et associer chacun de ses boutons à une touche du clavier. Le programme va demander d'appuyer anarchiquement sur toutes les touches, puis de nommer les touches une à une. La syntaxe à respecter pour les noms est disponible en exécutant :
irrecord --list-namespace
Il est temps de procéder :
sudo irrecord -d /dev/lirc0 telecommande
Suivre la procédure indiquée par le programme…
J'ai choisis ces touches du clavier, qui correspondent aux raccourcis clavier les plus utile de Kodi, pour la simple raison que ça fonctionnera directement ! Pas la peine de configurer quoi que ce soit dans Kodi !
Voici le contenu de telecommande.lircd.conf' qui a été créé, le déplacer dans /etc/lirc/lircd.conf.d/ pour qu'il soit pris en compte par lirc''
begin remote
name remoteLongue
bits 16
flags SPACE_ENC|CONST_LENGTH
eps 30
aeps 100
header 9053 4449
one 609 1642
zero 609 514
ptrail 608
repeat 9051 2202
pre_data_bits 16
pre_data 0x807F
gap 108017
toggle_bit_mask 0x0
begin codes
KEY_Up 0x10EF
KEY_Down 0x08F7
KEY_Right 0x807F
KEY_Left 0x30CF
KEY_C 0xA05F
KEY_PageUp 0xB04F
KEY_Back 0x20DF
KEY_Rewind 0x708F
KEY_Stop 0x8877
KEY_PlayPause 0x50AF
KEY_FastForward 0x48B7
KEY_Enter 0x00FF
end codes
end remote
Reste à redémarrer le service LIRC
sudo service lircd restart
Un moyen simple de savoir si ça marche, c'est de simplement appuyer sur les touches de la télécommande, ça devrait agir dans la console GNU/Linux ![1]
Par exemple la touche correspondant à KEY_C affichera le caractère C, normal quoi, puisque LIRC permet ainsi d'assigner l'entièreté des touches du clavier, et plus encore.
Au lancement de Kodi la télécommande devrait fonctionner
Si ce n'est pas le cas, ressortir et vérifier que le processus lircd tourne bien.
ps -A | grep lirc
À suivre…
Note
[1] La console tty de votre choix connecté en HDMI, ça ne fonctionne pas depuis ssh !
Je vais (enfin !!) reprendre la conception de mon WebRadioReveilWifi, après que tant de temps ait passé…
Il est désormais fonctionnel et me réveille correctement tous les matins, et c'est dans une suite de plusieurs billets que je vais détailler la réalisation étape par étape.
Cette maquette m'a pas mal servi à apprendre Arduino et voir où les expérimentations me menaient, afin de bien dimensionner la suite.
C'est ainsi que j'ai pu router un circuit imprimé, puis graver percer et souder la plaque…
Cependant même si on croit avoir toujours tout prévu, j'ai tout de même dû modifier certaines choses sur mon circuit, une erreur fâcheuse et des modifications de dernières minutes.
C'est donc le circuit final réalisé sur Kicad que je vais livrer ici (en annexe du billet), il ne correspondra pas aux photos, sur lesquelles apparaissent donc les modifications et bricolage de rattrapages.
Le Schéma structurel de la carte principale :
Le circuit de la carte principale :
Liste des composants:
Nom Type et valeur
Shield1 Arduino Pro Mini 5V
P1, P6 Pin header x3
P2 Pin header x6
P5 Pin header x2
P3 Pin header HE10-10
P4 Bornier 2 vis
D1, D2 Led Verte
D3, D4 Led Rouge
D5 Photodiode LSL100 ou Photorésistance 5516
R6, R7 Résistances 1kΩ
R11 Résistances 2,2kΩ
R12 à R18 Résistances 39Ω
R19 Résistances 150Ω
R20 à R26 Résistances 39Ω
R27 Résistances 150Ω
R28 à R34 Résistances 39Ω
R35 Résistances 150Ω
R36 à R42 Résistances 39Ω
R43 Résistances 150Ω
Afficheur1, 2, 3, 4 Afficheur 7 segments SA10-21GWA-Kingbright 25,4 mm Vert
Q1, Q2, Q3, Q4, Q5 BC547
C7, C8, C9,C10 Condensateur 100nF
U1, U2, U3, U4 74HC595
Le Schéma structurel de la carte des boutons :
Le circuit de la carte des boutons :
Liste des composants:
Nom Type et valeur
C1, C2, C3, C4, C5, C6 Condensateur 100nF
SW1, SW2, SW3, SW4, SW5, SW6 Petit bouton poussoir
R1, R2, R3, R4, R5, R6 Résistances 10kΩ
P1 Pin header HE10-10
Une fois les plaques gravées, il restait à percer tous les trous, à 0,8 mm et 1 mm de diamètre avec ma perceuse à colonne imprimée3D .
Une petit video !
Voici les cartes et les composants parés pour la soudure.
Mais avant, car réalisé en simple face, je n'ai pu faire autrement que de passer quelques liaisons sur l'autre face, et donc ici de devoir souder quelques fils en lieu et place.
Pour plier les pattes des résistances, je me suis aidé d'un plioir imprimé3D… Étant donné le grand nombre de résistances, c'était pas du luxe !
Je pense que la suite se passe de trop de commentaire, avec la soudure progressive les composants, des plus maigres aux plus épais.
Le module RTC (Real Time Clock) à base de DS3231 peut se trouver tout fait, mais ici je disposait seulement du composant, voulant rester compatible avec le concept du module enfichable, j'ai tout simplement réalisé une petite carte qui fait le même job, avec sa pile de sauvegarde !
Liste des composants:
Nom Type et valeur
C6 Condensateur 100nF
R9, R10 Résistances 10kΩ
U5 DS3231N
BT1 Support de pile Bouton 3V (CR2032)
P1 Pin header x6
Voilà donc tout est prêt, avec la carte principale qui accueillera (en haut) les 4 afficheurs 7 segments, les quatre 74HC595, (à droite) l'Arduino, (à gauche) la plaquette de boutons reliée au centre de la carte principale par une nappe de connecteur, (en bas) le capteur de mouvement (PIR) et le module RTC.
L'ampli audio :
Dans l'épisode précédent, j'avais donc câblé un petit ampli à base de LM386 sur une plaquette labo afin d'en valider le fonctionnement, puis j'en ai dessiné un circuit sur Kicad qui a été réalisé en simple face et que l'on va voir ci-après.
Cependant, comme pour l'horloge, j'ai un moment perdu de vue mes desiderata, et en rajoutant une source audio, je me suis plus tard rendu compte que j'avais oublié une partie du circuit pour le mélange des signaux entrants.
Et ce n'est pas tout… En intégrant l'ampli dans le boîtier du WebRadioRéveil que nous verrons dans un autre billet, j'ai vite compris que j'allais devoir retirer un certains nombre de connecteurs.
Le circuit final réalisé sur Kicad (en annexe du billet), contient donc toutes les modifications et ne correspondra donc pas tout à fait aux photos.
Le schéma structurel de l'ampli :
Le circuit de l'ampli :
Liste des composants:
Nom Type et valeur
C13, C14, C15, C16 Condensateur électrochimique polarisé 47µF 16V
C2, C5, C8, C11 Condensateur électrochimique polarisé 10µF 16V
C3, C9 Condensateur électrochimique polarisé 470µF 16V
C4, C10 Condensateur 33nF
C6, C12 Condensateur 100nF
C1, C7 Condensateur 1µF
R4, R5, R6, R7 Résistances 2,2kΩ
R8, R9 Résistance 100Ω
R1, R3 Résistances 1,2kΩ
R2 Résistances 1,5kΩ
RV1, RV2 Potentiomètre Double 10kΩ
U1, U2 LM386N-3
D1 Led Jaune 3 mm
SP1, SP2 Petit Haut Parleur
Une petit video !
Tous les composants parés pour la soudure !
On verra donc plus tard que ce sont les deux jacks audio, le jack alim et le bornier que j'ai dû retirer. Et de rajouter une petite carte intégrant le mélangeur audio.
Le code :
Une fois le hardware terminé, il reste à revoir et améliorer le code commencé la dernière fois.
Tout le système assemblé proprement sur table pour coder sereinement.
À gauche de Raspberry Pi et sa carte de boutons pour piloter manuellement la radio, au dessus l'ampli audio relié au Raspberry Pi , et donc à droite l'horloge avec son Arduino, son module RTC et sa carte de boutons.
Le code et les deux librairies nécessaire pour Arduino sont disponible en annexe du billet.
/****** Domaine publique, youpi ! ******/
/****** Ce code est à utiliser avec Arduino 1.5.5 *****/
#include <Wire.h> // Communication avec le RTC
#include <Tone.h> // Sonnerie ChipTune, voir Librairie
#include "Chronodot.h" // Fonctions du Chronodot, voir Librairie modifiée
/****************/
/* Déclarations */
/****************/
#define CHRONODOT_ADDRESS 0x68
/****** Signaux ******/
#define datapin 10 // pin 10 pour les données vers les registres à décallages 74HC595
#define clockpin 11 // pin 11 pour l'horloge qui coordonne les registres à décallages
#define latchpin 8 // pin 8 pour le déclencheur des registres à décallages
/****** Boutons poussoir ******/
#define BoutonMinutes 12 // pin 12 pour le bouton poussoir d'incrément des minutes
#define BoutonHeures 13 // pin 13 pour le bouton poussoir d'incrément des heures
#define BoutonAlarme1 6 // pin 6 pour le bouton poussoir Alarme1
#define BoutonAlarme2 2 // pin 2 pour le bouton poussoir Alarme2, alias « interruption 0 »
#define BoutonHorloge 7 // pin 7 pour le bouton poussoir Horloge
#define BoutonSupp 1 // pin TX1 pour le bouton supplémentaire
#define relais A2 // pin analogique 2 pour le pilotage du relais ON/OFF AmpliAudio
#define Radio A3 // pin analogique 3 pour le déclenchement de la radio (relié par la carte d'interface 5V -> 3V) à l'un des GPIO du Rpi
/****** LED ******/
#define LedAlarm1 4 // pin 4 pour la led de l'alarme1
#define LedAlarm2 3 // pin 3 pour la led de l'alarme2
/****** Alimentation des 7 segments en PWM ******/
#define ledPin 9 // pin 9 pour le signal PWM de cadencement des afficheurs et leds
#define photodiodepin A0 // pin A0 pour la photodiode
/****** Variables d'état ******/
int EtatBoutonMinutes = 1; // pour stocker l'état du bouton aprés sa lecture
int EtatBoutonHeures = 1; // idem
int EtatBoutonAlarme1 = 1; // idem
int EtatBoutonAlarme2 = 1; // idem
int EtatBoutonHorloge = 1; // idem
int EtatBoutonSupp = 1; // idem
unsigned int ValeurPhotoDiode = 0; // pour stocker l'état de la photodiode aprés sa lecture
volatile int change = 0; // pour mémoriser l'état de l'interruption 0
/****** Variables temporelles ******/
unsigned int heuresHorloge; // pour stocker les heures
unsigned int heuresHorlogeINC; // pour stocker l'incrément des heures
unsigned int minutesHorloge; // pour stocker les minutes
unsigned int minutesHorlogeINC;// pour stocker l'incrément des minutes
unsigned int secondes; // pour stocker les secondes
unsigned int annee; // pour stocker l'annee
unsigned int mois; // pour stocker le mois
unsigned int jour; // pour stocker le jour
unsigned int heuresAlarme; // pour stocker les heures
unsigned int heuresAlarmeINC; // pour stocker l'incrément des heures
unsigned int minutesAlarme; // pour stocker les minutes
unsigned int minutesAlarmeINC;// pour stocker l'incrément des minutes
/****** Variables d'affichage ******/
int dizH = 0; // pour stocker les dizaine des heures
int unitH = 0; // pour stocker les unités des heures
int dizM = 0; // pour stocker les dizaine des minutes
int unitM = 0; // pour stocker les unités des minutes
byte AfficheurUN; // données binaire pour l'afficheur UN, situé le plus à droite
byte AfficheurDEUX; // données binaire pour l'afficheur DEUX
byte AfficheurTROIS; // données binaire pour l'afficheur TROIS
byte AfficheurQUATRE; // données binaire pour l'afficheur QUATRE, situé le plus à gauche
byte dataArray[10]; // Tableau de données
/****** Référenciel librairies ******/
Chronodot HORLOGE; // HORLOGE, nom choisi arbitrairement, référence à la librairie Chronodot
ChronodotAlarm1 RADIO;
ChronodotAlarm2 REVEIL;
/**** Variable de construction des sonneries *****/
Tone tone1;
#define OCTAVE_OFFSET 0
#define isdigit(n) (n >= '0' && n <= '9')
int notes[] = { 0,
NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4,
NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5,
NOTE_C6, NOTE_CS6, NOTE_D6, NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, NOTE_B6,
NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7, NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7
};
char *songA = "A-Team:d=8,o=5,b=125:4d#6,a#,2d#6,16p,g#,4a#,4d#.,p,16g,16a#,d#6,a#,f6,2d#6,16p,c#.6,16c6,16a#,g#.,2a#";
char *songB = "Hallowee:d=4,o=5,b=140:32p,8d6,8g,8g,8d6,8g,8g,8d6,8g,8d#6,8g,8d6,8g,8g,8d6,8g,8g,8d6,8g,8d#6,8g,8c#6,8f#,8f#,8c#6,8f#,8f#,8c#6,8f#,8d6,8f#,8c#6,8f#,8f#,8c#6,8f#,8f#,8c#6,8f#,8d6,8f#";
char *songC = "Muppets:d=4,o=5,b=250:c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,8a,8p,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,8e,8p,8e,g,2p,c6,c6,a,b,8a,b,g,p,c6,c6,a,8b,a,g.,p,e,e,g,f,8e,f,8c6,8c,8d,e,8e,d,8d,c";
char *songD = "TakeOnMe:d=4,o=4,b=160:8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5,8f#5,8e5,8f#5,8f#5,8f#5,8d5,8p,8b,8p,8e5,8p,8e5,8p,8e5,8g#5,8g#5,8a5,8b5,8a5,8a5,8a5,8e5,8p,8d5,8p,8f#5,8p,8f#5,8p,8f#5,8e5,8e5";
char *songE = "Gadget:d=16,o=5,b=50:32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,32d#,32f,32f#,32g#,a#,d#6,4d6,32d#,32f,32f#,32g#,a#,f#,a,f,g#,f#,8d#";
char *songF = "MahnaMahna:d=16,o=6,b=125:c#,c.,b5,8a#.5,8f.,4g#,a#,g.,4d#,8p,c#,c.,b5,8a#.5,8f.,g#.,8a#.,4g,8p,c#,c.,b5,8a#.5,8f.,4g#,f,g.,8d#.,f,g.,8d#.,f,8g,8d#.,f,8g,d#,8c,a#5,8d#.,8d#.,4d#,8d#.";
int song = 1; // variable pour changer de sonnerie aprés chaque utilisation de la sonnerie
/*******************/
/* Initialisations */
/*******************/
void setup () // Fonction d'initialisation obligatoire
{
// Serial.begin(9600); // uncomment to debug
// Serial.println("Initializing Chronodot."); // uncomment to debug
dataArray[0] = B00000011; // Case du tableau qui contient la valeur binaire pour afficher zero sur un afficheur 7 segments
dataArray[1] = B10011111; // Case du tableau qui contient la valeur binaire pour afficher un sur un afficheur 7 segments
dataArray[2] = B00100101; // Case du tableau qui contient la valeur binaire pour afficher deux sur un afficheur 7 segments
dataArray[3] = B00001101; // Case du tableau qui contient la valeur binaire pour afficher trois sur un afficheur 7 segments
dataArray[4] = B10011001; // Case du tableau qui contient la valeur binaire pour afficher quatre sur un afficheur 7 segments
dataArray[5] = B01001001; // Case du tableau qui contient la valeur binaire pour afficher cinq sur un afficheur 7 segments
dataArray[6] = B01000001; // Case du tableau qui contient la valeur binaire pour afficher six sur un afficheur 7 segments
dataArray[7] = B00011111; // Case du tableau qui contient la valeur binaire pour afficher sept sur un afficheur 7 segments
dataArray[8] = B00000001; // Case du tableau qui contient la valeur binaire pour afficher huit sur un afficheur 7 segments
dataArray[9] = B00001001; // Case du tableau qui contient la valeur binaire pour afficher neuf sur un afficheur 7 segments
pinMode(clockpin, OUTPUT); // pin correspondant à "clockpin" initialisée en sortie
pinMode(datapin, OUTPUT); // pin correspondant à "datakpin" initialisée en sortie
pinMode(latchpin, OUTPUT); // pin correspondant à "latchpin" initialisée en sortie
pinMode(BoutonMinutes, INPUT); // pin correspondant à "BoutonMinutes" initialisée en entrée
pinMode(BoutonHeures, INPUT); // pin correspondant à "BoutonHeures" initialisée en entrée
pinMode(BoutonAlarme1, INPUT); // pin correspondant à "BoutonHeures" initialisée en entrée
pinMode(BoutonAlarme2, INPUT); // pin correspondant à "BoutonHeures" initialisée en entrée
pinMode(BoutonHorloge, INPUT); // pin correspondant à "BoutonHeures" initialisée en entrée
pinMode(BoutonSupp, INPUT); // pin correspondant à "BoutonSupp" initialisée en entrée
pinMode(LedAlarm1, OUTPUT); // pin correspondant à "LedAlarm1" initialisée en sortie
pinMode(LedAlarm2, OUTPUT); // pin correspondant à "LedAlarm2" initialisée en sortie
pinMode(relais, OUTPUT); // pin correspondant à "relais" initialisée en sortie
pinMode(Radio, OUTPUT); // pin correspondant à "Radio" initialisée en sortie
tone1.begin(A1); // pin A1 pour le jack sonnerie
Wire.begin(); // initialisation du chronodot, référence à la librairie wire
HORLOGE.begin(); // initialisation du chronodot, référence à la librairie Chronodot
//************ MISE à L'heure manuelle du module RTC DS3231 ************
// HORLOGE.adjust(DateTime(2014,12,25,10,30,12)); // années, mois, jour, heures, minutes, secondes
// RADIO.adjust(AlarmTime1(6,30,0));
// REVEIL.adjust(AlarmTime2(7,30));
attachInterrupt(0, stop2, FALLING); // attache l'interruption externe n°0 (pin2 soit bouton Alarm2) à la fonction stop2
digitalWrite(Radio, HIGH); // Coupe la radio au démarrage
RADIO.Alarm1SetON(); // au démarrage, l'alarme Radio est forcée ON (en cas de coupure de courant)
digitalWrite (LedAlarm1, HIGH);
REVEIL.Alarm2SetON(); // au démarrage, l'alarme Réveil est forcée ON (en cas de coupure de courant)
digitalWrite (LedAlarm2, HIGH);
}
/*************/
/* Programme */
/*************/
void loop () // boucle du programme !
{
horloge (); // appelle la fct "horloge", récupération des données temporelles
AfficheHorloge (); // appelle la fct "affiche", transfert des données temporelles sur les afficheurs 7 segments
AjusteLuminosite(); // appelle la fct "AjusteLuminosite", plus il fait sombre, plus la lumière des afficheurs baisse
SurveilleBouton_Horloge_Heures_Minutes (); // Pour permettre de mettre l'horloge à l'heure
// SurveilleBouton_Supp ();
RadioON(); // envoie d'ordre au rpi
FaireSonnerSonnerie();
EtatBoutonAlarme1 = digitalRead(BoutonAlarme1); // Lit l'état du bouton (appuyé ou relaché) et stocke la valeur dans la variable "EtatBoutonAlarme1"
EtatBoutonAlarme2 = digitalRead(BoutonAlarme2); // Lit l'état du bouton (appuyé ou relaché) et stocke la valeur dans la variable "EtatBoutonAlarme2"
if (EtatBoutonAlarme1 == LOW) // sinon, si bouton alarme 1 appuyé
{
for (int i=0; i <= 800; i++) // faire durant 3 sec
{
alarme1();
AfficheAlarme(); // affiche l'alarme
SurveilleBouton_Alarme1_Heures_Minutes (); // régler l'alarme
SurveilleBouton_Alarme1_Horloge ();
}
}
if (EtatBoutonAlarme2 == LOW) // sinon, si bouton alarme 2 appuyé
{
// Stopper();
for (int j=0; j <= 800; j++) // faire durant 3 sec
{
alarme2();
AfficheAlarme(); // affiche l'alarme
SurveilleBouton_Alarme2_Heures_Minutes (); // régler l'alarme
SurveilleBouton_Alarme2_Horloge ();
}
}
if(change == 1) { // Si change = 1, stopper la sonnerie
change = 0;
// Serial.print(change); //
RADIO.Alarm1Stop();
REVEIL.Alarm2Stop();
song = song + 1; // pour une sonnerie différente la prochaine fois
if(song == 7) {
song = 1;
}
// Serial.print(", song:"); //
// Serial.print(song); //
// Serial.println();
}
}
/****************************************/
/***** Les Fonctions du programme *******/
/****************************************/
/***** Fonction d'horloge *******/
void horloge ()
{
DateTime now = HORLOGE.now(); // lecture de l'heure en cours dans la puce DS3231, référence à la librairie Chronodot
heuresHorloge = now.hour(), DEC; // stocke l'heure en décimale dans la variable "heures" grace à la fct "hour" de la lib chronodot
minutesHorloge = now.minute(), DEC; // stocke les minutes en décimale dans la variable "minutes" grace à la fct "minute" de la lib chronodot
secondes = now.second(), DEC; // stocke les secondes en décimale dans la variable "secondes" grace à la fct "second" de la lib chronodot
annee = now.year(), DEC; // stocke l'année en décimale dans la variable "annee" grace à la fct "year" de la lib chronodot
mois = now.month(), DEC; // stocke le mois en décimale dans la variable "mois" grace à la fct "month" de la lib chronodot
jour = now.day(), DEC; // stocke le jour en décimale dans la variable "jour" grace à la fct "day" de la lib chronodot
// Serial.print(annee); //
// Serial.print(':'); //
// Serial.print(mois); //
// Serial.print(':'); //
// Serial.print(jour); //
// Serial.print(':'); //
// Serial.print(heures); //décommenter pour débug
// Serial.print(':'); //
// Serial.print(minutes); //
// Serial.println(); //
}
/***** Fonction d'alarme 1 *******/
void alarme1 ()
{
AlarmTime1 now = RADIO.now(); // lecture de l'heure en cours dans la puce DS3231, référence à la librairie Chronodot
heuresAlarme = now.hourA1(), DEC; // stocke l'heure en décimale dans la variable "heures" grace à la fct "hour" de la lib chronodot
minutesAlarme = now.minuteA1(), DEC; // stocke les minutes en décimale dans la variable "minutes" grace à la fct "minute" de la lib chronodot
// Serial.print(heuresAlarme); //décommenter pour débug
// Serial.print(':'); //
// Serial.print(minutesAlarme); //
// Serial.println(); //
}
/***** Fonction d'alarme 2 *******/
void alarme2 ()
{
AlarmTime2 now = REVEIL.now(); // lecture de l'heure en cours dans la puce DS3231, référence à la librairie Chronodot
heuresAlarme = now.hourA2(), DEC; // stocke l'heure en décimale dans la variable "heures" grace à la fct "hour" de la lib chronodot
minutesAlarme = now.minuteA2(), DEC; // stocke les minutes en décimale dans la variable "minutes" grace à la fct "minute" de la lib chronodot
// Serial.print(heuresAlarme); //décommenter pour débug
// Serial.print(':'); //
// Serial.print(minutesAlarme); //
// Serial.println(); //
}
/***** Fonction d'affichage horloge sur les 7 segments *******/
void AfficheHorloge ()
{
dizH = heuresHorloge / 10; // par calcul, extrait la dizaine de "heures" et stocke le résultat dans "dizH"
unitH = heuresHorloge % 10; // par calcul, extrait l'unités de "heures" et stocke le résultat dans "unitH"
dizM = minutesHorloge / 10; // par calcul, extrait la dizaine de "minutes" et stocke le résultat dans "dizM"
unitM = minutesHorloge % 10; // par calcul, extrait l'unité de "minutes" et stocke le résultat dans "dizM"
AfficheurUN = dataArray[unitM]; // stocke la valeur binaire 7 segments de l'unité de minutes dans "AfficheurUN"
// AfficheurUN &= ~(1<<1);
AfficheurDEUX = dataArray[dizM]; // stocke la valeur binaire 7 segments de la dizaine de minutes dans "AfficheurDEUX"
AfficheurTROIS = dataArray[unitH]; // stocke la valeur binaire 7 segments de l'unité d'heures dans "AfficheurTROIS"
AfficheurQUATRE = dataArray[dizH]; // stocke la valeur binaire 7 segments de la dizaine d'heures dans "AfficheurQUATRE"
// AfficheurQUATRE &= ~(1<<1);
digitalWrite(latchpin, 1); // latch à l'état HAUT pour autoriser le transfert des données série
shiftOut(datapin, clockpin, LSBFIRST, AfficheurUN); // envoi l'unités minute au registre à décallage
shiftOut(datapin, clockpin, LSBFIRST, AfficheurDEUX); // envoi la dizaine minute au registre à décallage
shiftOut(datapin, clockpin, LSBFIRST, AfficheurTROIS); // envoi l'unités heure au registre à décallage
shiftOut(datapin, clockpin, LSBFIRST, AfficheurQUATRE); // envoi la dizaine heure au registre à décallage
digitalWrite(latchpin, 0); // latch à l'état BAS pour arreter le transfert des données série
}
/***** Fonction d'affichage alarmes sur les 7 segments *******/
void AfficheAlarme ()
{
dizH = heuresAlarme / 10; // par calcul, extrait la dizaine de "heures" et stocke le résultat dans "dizH"
unitH = heuresAlarme % 10; // par calcul, extrait l'unités de "heures" et stocke le résultat dans "unitH"
dizM = minutesAlarme / 10; // par calcul, extrait la dizaine de "minutes" et stocke le résultat dans "dizM"
unitM = minutesAlarme % 10; // par calcul, extrait l'unité de "minutes" et stocke le résultat dans "dizM"
AfficheurUN = dataArray[unitM]; // stocke la valeur binaire 7 segments de l'unité de minutes dans "AfficheurUN"
AfficheurDEUX = dataArray[dizM]; // stocke la valeur binaire 7 segments de la dizaine de minutes dans "AfficheurDEUX"
AfficheurTROIS = dataArray[unitH]; // stocke la valeur binaire 7 segments de l'unité d'heures dans "AfficheurTROIS"
AfficheurQUATRE = dataArray[dizH]; // stocke la valeur binaire 7 segments de la dizaine d'heures dans "AfficheurQUATRE"
digitalWrite(latchpin, 1); // latch à l'état HAUT pour autoriser le transfert des données série
shiftOut(datapin, clockpin, LSBFIRST, AfficheurUN); // envoi l'unités minute au registre à décallage
shiftOut(datapin, clockpin, LSBFIRST, AfficheurDEUX); // envoi la dizaine minute au registre à décallage
shiftOut(datapin, clockpin, LSBFIRST, AfficheurTROIS); // envoi l'unités heure au registre à décallage
shiftOut(datapin, clockpin, LSBFIRST, AfficheurQUATRE); // envoi la dizaine heure au registre à décallage
digitalWrite(latchpin, 0); // latch à l'état BAS pour arreter le transfert des données série
}
/***** Régler l'Horloge : Fonction Surveillance des BoutonHorloge, BoutonMinutes et BoutonHeures *******/
void SurveilleBouton_Horloge_Heures_Minutes ()
{
EtatBoutonHorloge = digitalRead(BoutonHorloge); // Lit l'état du bouton (appuyé ou relaché) et stocke la valeur dans la variable "EtatBoutonHorloge"
EtatBoutonHeures = digitalRead(BoutonHeures); // Lit l'état du bouton (appuyé ou relaché) et stocke la valeur dans la variable "EtatBoutonHeures"
EtatBoutonMinutes = digitalRead(BoutonMinutes); // Lit l'état du bouton (appuyé ou relaché) et stocke la valeur dans la variable "EtatBoutonMinutes"
if ((EtatBoutonHorloge == LOW) && (EtatBoutonHeures == LOW)) // si le bouton est appuyé
{
delay(170); // alors, attendre 170 ms, permet un appuie bref pour incrémenter de 1, et un appuie long pour incrémenter rapidement d'autant qu'on veut
RegleHorlogeHeures (); // et appeller la fonction "RegleHeures"
}
if ((EtatBoutonHorloge == LOW) && (EtatBoutonMinutes == LOW)) // si le bouton est appuyé
{
delay(170); // alors, attendre 170 ms, permet un appuie bref pour incrémenter de 1, et un appuie long pour incrémenter rapidement d'autant qu'on veut
RegleHorlogeMinutes (); // et appeller la fonction "RegleMinutes"
}
}
/***** Fonction d'incrément Heures *******/
void RegleHorlogeHeures ()
{
heuresHorlogeINC = heuresHorloge + 1; // additionne 1 à la valeur contenue dans la variable "heures" et stocke le résultat dans la variable "heuresHorlogeINC"
if (heuresHorlogeINC > 23) // si la valeur de "heuresHorlogeINC" dépasse 23 (23h)
{
heuresHorlogeINC = 0; // alors, et la variable "heuresHorlogeINC" à zero (minuit)
}
// Serial.print(heuresHorlogeINC);
// Serial.println();
HORLOGE.adjust(DateTime(annee,mois,jour,heuresHorlogeINC,minutesHorloge,0)); // récup des données temporelles depuis les différentes variables pour mise à l'heure la puce DS3231, secondes à zero
}
/***** Fonction d'incrément minutes *******/
void RegleHorlogeMinutes ()
{
minutesHorlogeINC = minutesHorloge + 1; // additionne 1 à la valeur contenue dans la variable "minutes" et stocke le résultat dans la variable "minutesHorlogeINC"
if (minutesHorlogeINC > 59) // si la valeur de "minutesHorlogeINC" dépasse 59 (59min)
{
minutesHorlogeINC = 0; // alors, met la variable "minutesHorlogeINC" à zero
}
// Serial.print(minutesHorlogeINC);
// Serial.println();
HORLOGE.adjust(DateTime(annee,mois,jour,heuresHorloge,minutesHorlogeINC,0)); // récup des données temporelles depuis les différentes variables pour mise à l'heure la puce DS3231, secondes à zero
}
/***** Régler l'Alarme 1 : Fonction Surveillance des BoutonAlarme1, BoutonMinutes et BoutonHeures *******/
void SurveilleBouton_Alarme1_Heures_Minutes ()
{
EtatBoutonHeures = digitalRead(BoutonHeures); // Lit l'état du bouton (appuyé ou relaché) et stocke la valeur dans la variable "EtatBoutonHeures"
EtatBoutonMinutes = digitalRead(BoutonMinutes); // Lit l'état du bouton (appuyé ou relaché) et stocke la valeur dans la variable "EtatBoutonMinutes"
EtatBoutonAlarme1 = digitalRead(BoutonAlarme1); // Lit l'état du bouton (appuyé ou relaché) et stocke la valeur dans la variable "EtatBoutonMinutes"
if ((EtatBoutonAlarme1 == LOW) && (EtatBoutonHeures == LOW)) // si le bouton est appuyé
{
delay(170); // alors, attendre 170 ms, permet un appuie bref pour incrémenter de 1, et un appuie long pour incrémenter rapidement d'autant qu'on veut
RegleAlarm1Heures (); // et appeller la fonction "RegleMinutes"
}
if ((EtatBoutonAlarme1 == LOW) && (EtatBoutonMinutes == LOW)) // si le bouton est appuyé
{
delay(170); // alors, attendre 170 ms, permet un appuie bref pour incrémenter de 1, et un appuie long pour incrémenter rapidement d'autant qu'on veut
RegleAlarm1Minutes (); // et appeller la fonction "RegleMinutes"
}
}
/***** Fonction d'incrément Alarme 1, Heures *******/
void RegleAlarm1Heures ()
{
heuresAlarmeINC = heuresAlarme + 1; // additionne 1 à la valeur contenue dans la variable "heures" et stocke le résultat dans la variable "heuresHorlogeINC"
if (heuresAlarmeINC > 23) // si la valeur de "heuresHorlogeINC" dépasse 23 (23h)
{
heuresAlarmeINC = 0; // alors, et la variable "heuresHorlogeINC" à zero (minuit)
}
// Serial.print(minutesHorlogeINC);
// Serial.println();
RADIO.adjust(AlarmTime1(heuresAlarmeINC,minutesAlarme,0)); // récup des données temporelles depuis les différentes variables pour mise à l'heure la puce DS3231, secondes à zero
}
/***** Fonction d'incrément Alarme 1, Minutes *******/
void RegleAlarm1Minutes ()
{
minutesAlarmeINC = minutesAlarme + 1; // additionne 1 à la valeur contenue dans la variable "minutes" et stocke le résultat dans la variable "minutesHorlogeINC"
if (minutesAlarmeINC > 59) // si la valeur de "minutesHorlogeINC" dépasse 59 (59min)
{
minutesAlarmeINC = 0; // alors, met la variable "minutesHorlogeINC" à zero
}
// Serial.print(minutesHorlogeINC);
// Serial.println();
RADIO.adjust(AlarmTime1(heuresAlarme,minutesAlarmeINC,0)); // récup des données temporelles depuis les différentes variables pour mise à l'heure la puce DS3231, secondes à zero
}
/***** Régler l'Alarme 2 : Fonction Surveillance des BoutonAlarme2, BoutonMinutes et BoutonHeures *******/
void SurveilleBouton_Alarme2_Heures_Minutes ()
{
EtatBoutonHeures = digitalRead(BoutonHeures); // Lit l'état du bouton (appuyé ou relaché) et stocke la valeur dans la variable "EtatBoutonHeures"
EtatBoutonMinutes = digitalRead(BoutonMinutes); // Lit l'état du bouton (appuyé ou relaché) et stocke la valeur dans la variable "EtatBoutonMinutes"
EtatBoutonAlarme2 = digitalRead(BoutonAlarme2); // Lit l'état du bouton (appuyé ou relaché) et stocke la valeur dans la variable "EtatBoutonMinutes"
if ((EtatBoutonAlarme2 == LOW) && (EtatBoutonHeures == LOW)) // si le bouton est appuyé
{
delay(170); // alors, attendre 170 ms, permet un appuie bref pour incrémenter de 1, et un appuie long pour incrémenter rapidement d'autant qu'on veut
RegleAlarm2Heures (); // et appeller la fonction "RegleMinutes"
}
if ((EtatBoutonAlarme2 == LOW) && (EtatBoutonMinutes == LOW)) // si le bouton est appuyé
{
delay(170); // alors, attendre 170 ms, permet un appuie bref pour incrémenter de 1, et un appuie long pour incrémenter rapidement d'autant qu'on veut
RegleAlarm2Minutes (); // et appeller la fonction "RegleMinutes"
}
}
/***** Fonction d'incrément Alarme 2, Heures *******/
void RegleAlarm2Heures ()
{
heuresAlarmeINC = heuresAlarme + 1; // additionne 1 à la valeur contenue dans la variable "heures" et stocke le résultat dans la variable "heuresHorlogeINC"
if (heuresAlarmeINC > 23) // si la valeur de "heuresHorlogeINC" dépasse 23 (23h)
{
heuresAlarmeINC = 0; // alors, et la variable "heuresHorlogeINC" à zero (minuit)
}
// Serial.print(minutesHorlogeINC);
// Serial.println();
REVEIL.adjust(AlarmTime2(heuresAlarmeINC,minutesAlarme)); // récup des données temporelles depuis les différentes variables pour mise à l'heure la puce DS3231, secondes à zero
}
/***** Fonction d'incrément Alarme 2, Minutes *******/
void RegleAlarm2Minutes ()
{
minutesAlarmeINC = minutesAlarme + 1; // additionne 1 à la valeur contenue dans la variable "minutes" et stocke le résultat dans la variable "minutesHorlogeINC"
if (minutesAlarmeINC > 59) // si la valeur de "minutesHorlogeINC" dépasse 59 (59min)
{
minutesAlarmeINC = 0; // alors, met la variable "minutesHorlogeINC" à zero
}
// Serial.print(minutesHorlogeINC);
// Serial.println();
REVEIL.adjust(AlarmTime2(heuresAlarme,minutesAlarmeINC)); // récup des données temporelles depuis les différentes variables pour mise à l'heure la puce DS3231, secondes à zero
}
/***** Fonction Surveillance des BoutonAlarme1 et Horloge *******/
void SurveilleBouton_Alarme1_Horloge ()
{
EtatBoutonAlarme1 = digitalRead(BoutonAlarme1); // Lit l'état du bouton (appuyé ou relaché) et stocke la valeur dans la variable "EtatBoutonAlarme1"
EtatBoutonHorloge = digitalRead(BoutonHorloge); // Lit l'état du bouton (appuyé ou relaché) et stocke la valeur dans la variable "EtatBoutonHorloge"
if ((EtatBoutonAlarme1 == LOW) && (EtatBoutonHorloge == LOW)) // si les boutons sont appuyé
{
delay(600); // alors, attendre 600 ms, permet un appuie bref pour activer ou désactiver l'alarme1 (radio)
switch (RADIO.Alarm1Flag())
{
case 0: // désactivée // 0AH = 0xxxxxxx
RADIO.Alarm1SetON();
digitalWrite (LedAlarm1, HIGH);
break;
case 1: // alarme activée
RADIO.Alarm1SetOFF();
digitalWrite (LedAlarm1, LOW); // 0AH = 1xxxxxxx
break;
}
}
}
/***** Fonction Surveillance des BoutonAlarme2 et Horloge *******/
void SurveilleBouton_Alarme2_Horloge ()
{
EtatBoutonAlarme2 = digitalRead(BoutonAlarme2); // Lit l'état du bouton (appuyé ou relaché) et stocke la valeur dans la variable "EtatBoutonAlarme2"
EtatBoutonHorloge = digitalRead(BoutonHorloge); // Lit l'état du bouton (appuyé ou relaché) et stocke la valeur dans la variable "EtatBoutonHorloge"
if ((EtatBoutonAlarme2 == LOW) && (EtatBoutonHorloge == LOW)) // si le bouton est appuyé
{
delay(600); // alors, attendre 600 ms, permet un appuie bref pour activer ou désactiver l'alarme2 (sonnerie)
switch (REVEIL.Alarm2Flag())
{
case 0: // alarme désactivée // 0AH = 0xxxxxxx
REVEIL.Alarm2SetON();
digitalWrite (LedAlarm2, HIGH);
break;
case 1: // alarme activée
REVEIL.Alarm2SetOFF();
digitalWrite (LedAlarm2, LOW); // 0AH = 1xxxxxxx
break;
}
}
}
/***** Fonction d'envoie d'ordre au GPIO du Rpi *******/
void RadioON()
{
if (RADIO.Alarm1Status()==1)
{
// digitalWrite(relais, HIGH); # fait now par le RPI lui meme
digitalWrite(Radio, LOW);
delay(300); // appuie bref
digitalWrite(Radio, HIGH);
// delay(10000);
change = 1;
}
}
/***** Fonction d'activation de la sonnerie ChipTune *******/
void FaireSonnerSonnerie()
{
if (REVEIL.Alarm2Status()==1)
{
digitalWrite(relais, HIGH); // AmpliAudio alimenté
delay(500); // attendre quelques millisec entre chaque bouctle de sonnerie
switch (song) {
case 1: // A-Team
sonnerie(songA);
// song = song + 1;
break;
case 2: // Hallowee
sonnerie(songB);
// song = song + 1;
break;
case 3: // Muppets
sonnerie(songC);
// song = song + 1;
break;
case 4: // TakeOnMe
sonnerie(songD);
// song = song + 1;
break;
case 5: // Gadget
sonnerie(songE);
// song = song + 1;
break;
case 6: // MahnaMahna
sonnerie(songF);
// song = 1;
break;
default: // cas par défaut
sonnerie(songA);
break; */
}
}
}
/***** Fonction pour stopper la sonneie *******/
void stop2 () //appelée directement via l'interruption 0 lorsqu'on appuie sur le bouton alarm2
{
change = 1;
digitalWrite(relais, LOW); // coupe l'alimentation de l'AmpliAudio
// Serial.print(change); //
}
/***** Fonction d'ajustement de la luminosité via la photodiode *******/
void AjusteLuminosite()
{
ValeurPhotoDiode = analogRead(photodiodepin); // Lit la valeur renvoyée par la photodiode, et stocke la valeur dans la variable "ValeurPhotoDiode"
if (ValeurPhotoDiode > 495) // si la valeur est supérieure à 495 (beaucoup de lumière reçue, genre en plein jour)
{
analogWrite(ledPin, 255); // alors, illumination des afficheurs et leds à 100% (PWM de 0 à 255)
}
else if (ValeurPhotoDiode > 462) // sinon, si la valeur est supérieure à 462
{
analogWrite(ledPin, 238); // alors, illumination des afficheurs et leds moins forte
}
else if (ValeurPhotoDiode > 429)
{
analogWrite(ledPin, 221);
}
else if (ValeurPhotoDiode > 396)
{
analogWrite(ledPin, 204);
}
else if (ValeurPhotoDiode > 363)
{
analogWrite(ledPin, 187);
}
else if (ValeurPhotoDiode > 330)
{
analogWrite(ledPin, 170);
}
else if (ValeurPhotoDiode > 297)
{
analogWrite(ledPin, 153);
}
else if (ValeurPhotoDiode > 264)
{
analogWrite(ledPin, 136);
}
else if (ValeurPhotoDiode > 231)
{
analogWrite(ledPin, 119);
}
else if (ValeurPhotoDiode > 198)
{
analogWrite(ledPin, 102);
}
else if (ValeurPhotoDiode > 165)
{
analogWrite(ledPin, 85);
}
else if (ValeurPhotoDiode > 132)
{
analogWrite(ledPin, 68);
}
else if (ValeurPhotoDiode > 99)
{
analogWrite(ledPin, 51);
}
else if (ValeurPhotoDiode > 66)
{
analogWrite(ledPin, 34);
}
else if (ValeurPhotoDiode > 33)
{
analogWrite(ledPin, 17);
}
else // sinon, (très peu voire pas du tout de lumière reçue, genre dans la nuit)
{
analogWrite(ledPin, 8); // alors, illumination des afficheurs et leds aux minimum 8
}
}
/***** Fonction Sonnerie *****/
void sonnerie(char *p)
{
// Absolutely no error checking in here
byte default_dur = 4;
byte default_oct = 6;
int bpm = 63;
int num;
long wholenote;
long duration;
byte note;
byte scale;
// format: d=N,o=N,b=NNN:
// find the start (skip name, etc)
while(*p != ':') p++; // ignore name
p++; // skip ':'
// get default duration
if(*p == 'd')
{
p++; p++; // skip "d="
num = 0;
while(isdigit(*p))
{
num = (num * 10) + (*p++ - '0');
}
if(num > 0) default_dur = num;
p++; // skip comma
}
// Serial.print("ddur: "); Serial.println(default_dur, 10);
// get default octave
if(*p == 'o')
{
p++; p++; // skip "o="
num = *p++ - '0';
if(num >= 3 && num <=7) default_oct = num;
p++; // skip comma
}
// Serial.print("doct: "); Serial.println(default_oct, 10);
// get BPM
if(*p == 'b')
{
p++; p++; // skip "b="
num = 0;
while(isdigit(*p))
{
num = (num * 10) + (*p++ - '0');
}
bpm = num;
p++; // skip colon
}
// Serial.print("bpm: "); Serial.println(bpm, 10);
// BPM usually expresses the number of quarter notes per minute
wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole note (in milliseconds)
// Serial.print("wn: "); Serial.println(wholenote, 10);
// now begin note loop
while(*p)
{
// first, get note duration, if available
num = 0;
while(isdigit(*p))
{
num = (num * 10) + (*p++ - '0');
}
if(num) duration = wholenote / num;
else duration = wholenote / default_dur; // we will need to check if we are a dotted note after
// now get the note
note = 0;
switch(*p)
{
case 'c':
note = 1;
break;
case 'd':
note = 3;
break;
case 'e':
note = 5;
break;
case 'f':
note = 6;
break;
case 'g':
note = 8;
break;
case 'a':
note = 10;
break;
case 'b':
note = 12;
break;
case 'p':
default:
note = 0;
}
p++;
// now, get optional '#' sharp
if(*p == '#')
{
note++;
p++;
}
// now, get optional '.' dotted note
if(*p == '.')
{
duration += duration/2;
p++;
}
// now, get scale
if(isdigit(*p))
{
scale = *p - '0';
p++;
}
else
{
scale = default_oct;
}
scale += OCTAVE_OFFSET;
if(*p == ',')
p++; // skip comma for next note (or we may be at the end)
// now play the note
if(note)
{
// Serial.print("Playing: ");
// Serial.print(scale, 10); Serial.print(' ');
// Serial.print(note, 10); Serial.print(" (");
// Serial.print(notes[(scale - 4) * 12 + note], 10);
// Serial.print(") ");
// Serial.println(duration, 10);
tone1.play(notes[(scale - 4) * 12 + note]);
delay(duration);
tone1.stop();
}
else
{
// Serial.print("Pausing: ");
// Serial.println(duration, 10);
delay(duration);
}
}
}
Pour faire l'interface entre l'horloge et la radio, les signaux de commande de l'Arduino opérants en 5 Volts et ceux du Raspberry en 3 Volts, il manque ici une carte d'interface. Elle sera étudié au prochain billet sur le sujet.
Mode l'emploi de l'horloge :
De gauche à droite, les boutons : [Alarme 2] [Alarme 1] [Horloge] [Heures] [Minutes]
Pour régler l'heure :
Appuyer (bref ou long) sur [Horloge] + [Heures] , et sur [Horloge] + [Minutes]
Pour régler l'alarme 1 :
Appuyer (bref ou long) sur [Alarme 1] + [Heures] , et sur [Alarme 1] + [Minutes]
Pour régler l'alarme 2 :
Appuyer (bref ou long) sur [Alarme 2] + [Heures] , et sur [Alarme 2] + [Minutes]
La partie tournante de l'éolienne étant quasi terminée, on va maintenant s'occuper de la potence.
J'avais dessiné le truc vite fait, histoire de ne pas trop oublier ce que j'aurais à faire entre deux sessions.
La potence sera constituée de barres d'acier en « U », fixées sur le mât avec des barres plate de soutiens.
Pour tenir ces bras de potence à 90° sur le poteau, on utilisera des barres de volet fixées à 45°.
Première étape, découper les « U » à dimension avec une tronçonneuse. Un adhésif est utilisé comme repére, car le métal est trop rouillé pour être tracé.
Ensuite, découpage d'un petit morceau de « U », lui même coupé en deux dans le sens de la longueur.
Ces équerres seront utilisées par former un petit logement pour les roulements.
Pour changer un peu, préparation des barres de volets, en découpant les extrémités.
Ensuite, retour sur les deux « U » dans le but de créer une cavité pour y caler le mât.
C'est fait en pratiquant des trous à la perceuse à colonne sur le périmètre des arcs, avant de découper le reste avec la tronçonneuse, puis de terminer le travail à la lime.
Travail similaire sur l'autre extrémité, cette fois dans le but de caler l'axe de rotation de la voilure.
Plus explicite avec une mise en situation :
Toutes les pièces sont prêtes à être assemblées.
Ce sera fait avec des boulons, y'aura donc encore des trous à faire avec la perceuse à colonne (J'aime bien la perceuse à colonne, surtout le 31 décembre à 22h00 )
Terminé l'été dernier, mais je n'avais pas pris le temps de publier les photos, comme un tas d'autres trucs… C'est que lorsque je rédige du compte rendu, et bah je ne bricole rien de neuf, et j'ai donc privilégié d'avancer sur les sujets qui viendront je l'espère remplir un peu plus le blog dans les jours à venir ^^
Déco Vinyle !
Je l'ai déjà mentionné, mais le « design » et moi, bah ça colle pas, surtout lorsqu'il s'agit de créer une composition de la sorte.
J'ai donc cherché longuement, puis péniblement agencé cette infographie pour décorer la borne, à base de scan d'artbook trouvé sur le net (oui c'est pas bien bouuuuhh).
J'ai aussi dessiné un truc pour mettre devant l'éclairage du marquee, et ce n'est pas très réussi, en plus d'être imprimé sur imprimante jet d'encre qui donne un rendu pas terrible… Et j'ai paumé le fichier.
Dés réception du vinyle, on commence par découper soigneusement le contour.
Viens ensuite le moment de la simulation de mise en place, avec du ruban adhésif de masquage… Un peu plus à droite, un peu plus en haut ?
Tandis que la partie inférieure du vinyle est maintenue en place sur la borne, décollage de la moitié du vinyle de son support, qu'on découpe ensuite afin de pouvoir lisser tranquillement le vinyle avec la spatule contre la borne. J'ai pris soins d'entourer la spatule de feutrine pour éviter d'abîmer la matière.
Une fois terminé, même travail pour la partie inférieure. J'aurais donc collé cette face en 3 fois.
Pas mal d'ajustement ont été réalisé au cutter sur les pourtours des deux côtés, afin que le rebord se termine harmonieusement.
En retirant le vinyle blanc temporaire du panel, j'ai eu la mauvaise demi-surprise de constater un arrachement quasi complet de la peinture et de fibres de bois, ce qui m'a contraint à refaire le surfaçage… J'étais bien content tiens…
Ici une bande du support du vinyle a été découpée/retirée, et le rebord préalablement solidarisé du bord du panel avec du ruban de masquage.
Ainsi collé sur le panel, en découpant au fur et à mesure des bandes du support.
Terminé ! Reste à découper les trous au cutter. Pour info, la spatule semi-souple et la feutrine.
Galerie !
Fin ?
Les boutons Seimitsu sont translucides, alors pourquoi ne pas achever le tout en ajoutant un éclairage sous le panel afin de les illuminer ? Ou pas…
Je ne suis pas très productif dernièrement (en articles de blog seulement).
Je vous propose dans ce billet de mettre en place un cadre photo, mais qui donne le temps qu'il fait. Le but est de mettre en valeur l'électronique utilisée et de ne pas la cacher pour une fois. Le tout est basé sur un écran de liseuse (e-paper) et un nodeMCU (ESP8266).
Allons, commençons !
Il y a deux aspects à prendre en compte : la récupération de données de météo et l'affichage sur l'écran e-paper.
Pour la récupération des données météo, il existe un paquet d'API qui propose différentes offres plus ou moins chères et plus ou moins intéressantes. J'ai exploré un peu mes possibilités et je suis tombé sur ce projet. Ce tuto utilise le site wunderground afin d'avoir les données et accessoirement, il propose une interface à cette API météo. En effet, il est nécessaire de comprendre ce qu'on fait étant donné que le code tourne sur un micro-contrôleur (la mémoire et la puissance de calcul sont très limitées). Ce tuto m'a permis de répondre à ma question du parsing des données retournées par le site. En effet, ces API météo répondent en XML ou en JSON d'un seul bloc, il est donc important de réaliser un parsing en mode "stream" afin de limiter la mémoire nécessaire. Merci à squix78, c'est toujours plus simple de trouver ce dont on a besoin :).
Récapitulons ! J'ai trouvé le site de météo et la librairie pour récupérer les données. Bon, je crois que le point 1 est fait.
Pour la gestion de l'écran e-paper, j'ai récupéré la librairie du constructeur mais modifiée pour ESP8266. En testant la librairie, j'ai eu une petite déconvenue : impossible de programmer l'ESP8266 une fois l'écran connecté. J'ai bien mis une minute à rien comprendre avant de réaliser que la programmation et la communication avec l'écran se font par UART ... du coup l'écran répond au programme de flash et tout plante. Il faudra donc un interrupteur pour couper l'alimentation de l'écran lors de la programmation. Le deuxième effet kiss cool vient du fait que pour préparer une image dans le format accepté par l'écran (4bits LE, 4 niveaux de gris), le programme fourni par le constructeur fonctionne sous Windows mais pas moi :(. Mais heureusement, les ressources d'internet sont infinies et après avoir tapé de nombreuses requêtes avec les mauvais mots-clés, je suis tombé sur un sauveur. J'étais prêt (désespéré) à coder un truc moi-même mais pas besoin :). Il y a également une section sur la gestion des fichiers dans l'écran (ça aide bien). Mais au final vous allez me dire, de quelles images on parle...? Eh bien de celles-ci.
Je crois qu'on a fait le tour, il est temps d'essayer. Et voilà, le premier test réussi :)
Hourra, les images fonctionnent (c'est ce qui m'a pris le plus de temps au final) ! Maintenant l'affichage entier.
Il ne reste plus qu'à tout assembler : un cadre, 8 entretoises filtées, 16 fils, un interrupteur et des fils.
J'ai tenté une déco à la main pour voir.
En passant, voici le code que j'ai écrit, il me semble que j'ai fait deux trois motifs dans le code initial de la librairie de récupération des infos météo pour que tout fonctionne correctement.
Le truc sympa est que quand on coupe l'alimentation, les prévisions restent affichées avec la date et l'heure. On peut donc choisir de l'allumer uniquement lorsque l'on a besoin et garder les infos toute la journée.
Après avoir utilisé quelques années mon ancienne imprimante 3D, je voulais en construire une plus précise et surtout plus fiable. Pas de réglage et d’ajustement sans fin comme la précédente. Simplicité et fiabilité. Je dispose maintenant d’un petit tour et d’une petite fraiseuse ce qui me permet de fabriquer des pièces précises en acier ou aluminium.
La nouvelle imprimante sera donc quasi intégralement en aluminium avec un design proche de la Prusa i3. Je conserve le principe de l’extrudeur déporté (bowden) pour avoir une tête d’impression légère et vibrant moins.
. Première étape, les tiges trapézoïdales du portique supportant l’axe X et Z sont percées pour être emboîtées / collées directement sur les arbres des deux moteurs Nema 17.
Perçage des vis trapézoïdales
Les vis sont collées sur l’arbre des moteursLe chassis
L’axe Y
Monté sur des plaques en alu épaisses, il est ajustable et renforce la rigidité de l’imprimante.
Supports de roulements pour Y
Simple mais efficace comme fixation.
Fraisage d’un des blocs pour X
C’est parti pour quelques copeaux.
Le bloc intégrant roulement et support moteur pour X
Préparation du tendeur de courroie pour X
le tendeur en place
La courroie est bien tendueFraisage d’un bloc de POMC
la fixation du chariot X portant Z
De rares plans sont réalisés avec LibreCAD.
Préparation des supports moteurs de Z
Tests de position du portiqueAvec le plateau YFabrication d’un support d’extrudeurL’extrudeur et son support
Le support est fabriqué rapidement à l’aide de la fraiseuse. Pour l’extrudeur, il est acheté. Sa fabrication ne serait pas rentable.
Le tendeur de courroie pour Y
Le portique X avec un capteur à induction et l’extrudeur
Un capteur à induction permet un nivellement automatique du plateau. Le firmware marlin gère une matrice de 9 points pour calculer l’assiette et la déformation éventuelle du plateau chauffant en aluminium.
Préparation de l’électroniqueIntégration d’un raspberry avec Octopi et d’un relais pour piloter l’alimentation
Un raspberry pi 3, une webcam et un relais viennent compléter le pilotage à distance de la machine. Via un navigateur web ou avec l’application mobile Printoid. Top !
Le pilotage à distance avec Octopi
L’imprimante est dans le garage et peut être ainsi surveillée à distance.
Le câblage avanceLe moteur de l’extrudeur
Vu le prix de 6$, inutile de chercher à le fabriquer.
Prête pour des testsRuban de LED intégré dans le chassis en aluminiunPremier « torture test »Coin coin
Les étapes qui restent à faire: Finir le carénage de l’imprimante, installer un ventilateur sur le portique X, réaliser quelques pièces pour améliorer l’imprimante: une chaîne porte câble, un boitier pour l’écran LCD, un cache pour le relais…
Les résultats sont à la hauteur de mes attentes, l’imprimante est fiable et la résolution à 0.1mm est atteinte sans problème à la vitesse de 80mm/s.
Alors, où en étais-je déjà…? C'est que j'ai passé pas mal de temps à la débugger cette imprimante.
La dernière fois je disais qu'elle fonctionnait, mais en fait elle marchotait à cause du cumul de plusieurs problèmes dû à la construction, à la qualité des pièces et à la conception, qui faisait que les impressions étaient une calamité à faire démarrer.
J'aurais l'occasion de détailler ces points un à un, et c'est aussi les raisons qui vont me pousser à modifier certaines pièces et façon de faire.
Changement d'Extrudeur : :
L'extrudeur précédent, bien que fonctionnel, n'était pas très pratique, le système à ressort laissait à désirer, et en plus de son design relativement moche il a fini par se casser du fait d'une impression de mauvaise qualité (décollement de couche, ma faute, fil pas assez chaud avec cette couleur jaune or).
J'ai aussi ajouté un écrou pour tenir solidement l'embout pneumatique.
J'en ai d'ailleurs racheté sur e-bay et il n'a pas été facile de s'y retrouver au début avant que je tombe sur un vendeur clair en indications :
Push Fit Pneumatic Coupling Coupler Fitting - PTFE Tubing / Bowden - 3D Printer :
PC4-M6 - 4mm OD PTFE Tubing - 6mm Thread
PC6-M6 - 6mm OD PTFE Tubing - 6mm Thread
PC4-01 - 4mm OD PTFE Tubing - 1/8" Screw - Thread has 9.6mm OD
PC6-01 - 6mm OD PTFE Tubing - 1/8" Screw - Thread has 9.6mm OD
Voilà donc j'ai pris :
- un Push Fit PC6-M6 côté extruder (filetage M6 et trou de 6 mm pour y mettre le tube en téflon de diamètre 6 mm (et donc 4 mm intérieur pour y passer du fil de 3 mm)).
- un Push Fit PC6-01 côté tête d'impression (filetage 1/8" et trou de 6 mm pour y mettre le tube en téflon de diamètre 6 mm.
Je n'ai malheureusement pas de photo de l'objet en pièces détachées, voici néanmoins le Matériel nécessaire :
- 1 moteur Nema 17.
- 3 vis M3x25 mm.
- 2 vis M3x40 mm.
- 2 écrous M3.
- 1 embout pneumatique PTFE PC6-M6.
- 1 roulement 608ZZ.
- 1 roulement MR105ZZ.
- 1 engrenage d’entraînement MK7.
Et des photos issues de 3Dator :
Prévue pour passer du fil de 1,75, une fois imprimé la pièce principale, j'ai repercé le trou guide à 3 mm de diamètre afin de l'élargir pour le passage du fil.
Le câblage de l'alimentation, épisode 2 :
Tout se passait à merveille du côté de l'alim, sauf que, comme pas mal de cas recensés, il est arrivé le même problème avec le connecteur vert de l'alimentation… À se demander pourquoi on continue d'utiliser ce type de connecteur
Il a cramé ! Ho pas trop cette fois-ci, en tous cas beaucoup moins que celui de la Prusa-i3 sur laquelle j'ai fait mes armes et qui aura subit le même sort qu'ici, sont remplacement par un connecteur Molex de disque dur.
J'ai donc dessoudé et découpé le connecteur mâle d'un vieux disque dur hors service (comme quoi, faut jamais rien jeter !) et l'ai simplement mis en place sur la carte RAMP
Jusqu'ici, rien de sorcier, le plus délicat est à venir…
Donc pour récupérer les embouts d'un connecteur de l'alim-ATX (évidemment, je les ai pas jeté ^^) et faire propre, il faut les défaire et réussir à écarter les pâtes métalliques pour ensuite y dispatcher d'autres câbles… Pas facile, mais on y arrive !
Pour l'astuce, avec une pointe à graver ou un truc très pointu et solide, j'écarte du mieux possible au bord les pâtes qui emprisonnent le fil multibrin, et avec une pince je saisis un brun et je tire dessus pour le retirer. Du coup les autres sont moins serrés et plus on en retire, plus ils sont facile à ôter, reste alors à finir le travail en écartant franchement les pâtes afin d’accueillir un nouveau câble qui sera sécurisé avec un point de soudure.
La ventilation de l'électronique :
Tant qu'on en est à causer de l'alimentation, à l'usage j'ai jugé qu'il faisait un peu chaud dans l'habitacle électronique et comme j'avais prévu le trou dans la structure pour un ventilateur de 80 mm, j'ai donc procédé à sa mise en place en l'alimentant par deux fils fin raccordés à l'intérieur de l'alimentation ATX, via un connecteur marron de récupération visible sur la dernière photo (connecteur de ventilo pris sur une carte mère d'ordi)
Donc ce ventilo aspire l'air extérieur et l'alim ATX recrache l'air chaud.
Câblage du plateau chauffant :
Matériel nécessaire :
- 1 plateau en alu MK3 12V.
- 1 thermistance.
- 1 prise Molex femelle de récup.
- 1 serre câble.
- 1 led et sa résistance.
- gaine thermorétractable.
- bande adhésif de polyimide.
- câble électrique multibrin.
- pâte thermique.
Pour le câble électrique, j'ai simplement dépouillé un câble d'alimentation 220V de sa gaine, afin de disposer d'un câble de section imposante pour encaisser, car le plateau chauffant capte à lui seul les 3/4 de l'énergie absorbée par l'imprimante.
Chauffer assez fortement (450°C) pour souder le câble et le couple résistance/Led aux emplacements prévu.
Fixer la thermistance dans le trou à l'aide du ruban de polyimide et le remplir de la pâte thermique.
À l'autre extrémité des fils, souder les embouts du connecteur Molex.
Sécuriser le tout avec un serre câble et une chaussette passe fil (facultatif).
Sur l'imprimante, côté carte Ramp on aura pris soins de préparer le câblage identique avec un connecteur Molex mâle. Voici donc un système de plateau interchangeable…
Le capteur « Bed auto-Leveling » :
Disons le tout net : c'est de la merde et je ne veux plus en entendre parler sur mon imprimante !
Voilà, si tu me crois sur parole, t'es pas obligé de lire la suite ^^;
J'avais donc en ma possession de capteur inductif (qui détecte le métal) à embout bleu, qui, prenant du 12V alors que la carte RAMP attendait du 5V, se voyait affublé d'un bête pont diviseur de tension pour… oui pour… !??? Nan mais osef sérieux, pas la peine d'expliquer ça.
Pourquoi ? Bah attend, je vais pas divulguer tout de suite, suspense !
Donc je le câble, ça marche et tout, et puis à l'usage c'est rigolo cet autoleveling, plutôt convainquant ! Sauf que les 3/4 du temps l'initialisation des impressions foire lamentablement car le fil n'accroche pas.
Bon, il faut me faire une raison, mon plateau n'est pas droit, pas plan quoi, il présente des creux et bosses, ce qui fait que lorsque l'auto-level se fait bien sur un coin, il se foire sur un autre en ne détectant pas le plateau et le jugeant trop loin, remonte celui-ci jusqu'à cogner la tête et forcer le moteur et la courroie… Arrêt d'urgence !
Pourtant d'autres fois ça passe, étrange…
Alors je met ça sur le compte de la pièce qui tiens le capteur en place, qui serait trop souple, et le câblage de tête tirant dessus, le capteur s'en retrouverait soulevé… J'ai même perdu mon temps à designer une pièce de support plus rigide qu'elle est 'achement bien pensée, avec sa molette de réglage…
Parce-que oui, tu passes clairement ton temps à la régler cette putain de hauteur de capteur… Atteeeennnndds, c'est pas fini ^^;
Donc wé finalement c'est difficile à voir à l'œil nu, mais le plateau en alu embouti n'est pas terrible, il aurait fallu qu'il soit « rectifié » , c'est à dire usiné par une fraiseuse pour lui assurer une planéité parfaite.
Alors qui faire ?? Idée ! Poser une plaque en verre dessus comme avec la Prusa, sauf que c'est vendu cher le borosilicate, et puis je veux bricoler tout de suite, là, maintenant !
Alors malin, je trouve un vieux miroir et j'ai bien entendu parlé et vu des gens qui utilisaient ça…
Je le découpe et le pose sur le plateau, et fait chauffer… 40, 50, 60… ça va péteeeer !
Ha bah non. 80, 95°C ça bouge pas !
Haaaaa mais c'est bien sûr, le miroir résiste à la chaleur, super, quand on sait que j'avais testé avec du verre classique de récupération et qu'il avait claqué à 70°C…
Allez, on imprime ^^
Auto-levelling impossible, le capteur ne sais pas capter à plus loin que 4 mm, or le miroir fait 4 mm d'épaisseur et le plateau en alu n'est alors pas détecté… Bon…
Du coup j’achète un capteur capacitif à embout orange… Avantage, il détecte presque tous les matériaux, jusqu'à 10 mm de distance selon leurs densités.
Alors j'adapte la pièce support (en blanc sur la photo) car le diamètre du capteur est de 18 mm au lieu de 16 mm, et je galère 20 mm avant de réussir à lancer une impression correcte t'imagines donc.
À défaut de bonne documentation technique sur l'objet, je lis partout qu'il lui faut aussi un diviseur de tension, alors je m'exécute, mais … ça marche pas.
Je dégaine le multimètre, et mesure des niveaux de tension trop faible pour déclencher un état sur le RAMP. WTF…
Ha, en fait internet colporte beaucoup de n'importe quoi en bullshit d'amalgame et de on va faire « comme ça parque le capteur il a la même couleur » de la part de personnes qui sont aussi paumés que moi ^^;
Pas besoin de diviser la tension, hop, on vire les résistances, et ça fonctionne !
Fin de l'histoire ? Attend, donc :
- Planéité du plateau, OK, grace au mirroir posé dessus.
- Détection du plateau OK, le capteur inductif fait le taf et capte le miroir.
- La machine imprime enfin… que quoi, 3 fois sur 4 la hauteur détectée n'est pas la même !???
Je règle avec ma super bague de réglage, ça passe, impression suivante et bim ! Le plateau rentre dans la tête !!
Maiiiiiis… n'est marre, je crois que ma souris est cassée ^^;
Et donc, pourquoi c'est de la merde et je ne veux plus entendre parler d'auto-leveling ?
Pour une raison sidérante, écoute bien !
Ces capteurs sont sensible à la chaleur, autant l'inductif que le capacitif, même si le diamètre et la couleur sont… ATTEND QUOI !!!! Sensible à la chaleur, sans déc… Ça veut dire quoi ?
Ha, que la mesure de la distance attendue risque d'être différente à chaque initialisation de l'imprimante…
OK, tout s'explique finalement, et moi je cumulais plusieurs problèmes rendant le diagnostique assez difficile…
Mais pt1, c'est qui le con qui a eu l'idée d'utiliser ce type de capteur alors que les imprimantes 3D, bah ça chauffe à plusieurs centaine de degré celsius ? Hein c'est quiiiii ?
Quand on gratte un peu, on découvre aussi que si l'on souhaite avoir un capteur le plus précis possible, il faut y mettre le prix, et c'est du capteur industriel à 200€ qu'il faut, pas ces merdes à 10€ vendues partout comme composant pour imprimante3D.
J'ai donc viré définitivement le support de capteur, et adapté un bon vieux « cliqueur » des familles, un Endstop à microswitch.
Haaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa !!
Quel plaisir de voir démarrer une impression à tous coups, là ou on galérait 20 min avant !
Attention à l'axe X !
Et donc t'imprimes un carré de 18 cm sans problème now ? Genre, tous les coins impec, plateau bien plat ?
Et bien oui, mais il restait à régler un problème, de ceux qui se cumulaient donc…
Après réglage des 4 coins à l'aide des vis à ressort, la distance tête-plateau était correcte sur toute la longueur Y, mais pas du tout en largeur X.
En effet, si au centre c'était bon, sur les deux extrémités, la tête s'éloignait du plateau, et donc les impressions de pièces très large étaient impossible, le fil n'accrochant pas.
Soupçonnant une tige métallique de l'axe X d'être un chouilla tordue, j'ai bien tenté de les tourner, de quart de tour en quart de tour, rien n'y fit, au point que j'en redécoupe d'autres afin de les changer… pour rien, car le problème était toujours là…
Pourtant c'est en remontant la machine qu'un truc m'a mis la puce à l'oreille, et en continuant l'investigation j'ai fini par comprendre que l'axe X n'était pas d'équerre.
En fait le serrage des tiges dans les pièces vertes n'était pas efficient, et avec le poids ou de simple manipulation, les deux pièces vertes s'affaissaient légèrement, créant ainsi le défaut décrit ici.
Pour résoudre le problème, j'ai juste plaqué le chariot X en rouge contre la piéce verte de droite, on voit le décalage, pas bien parallèle, alors je redresse, et ensuite j'ai serré le plus fort possible la vis. Problème résolu !
J'ai également ajouté une vis de serrage sur la partie inférieure de la pièce de gauche, car chose étrange elle n'étais pas prévue dans le design initiale.
La mécanique : Modifications sur l'axe Z :
Le principe coreXY a un défaut, c'est le plateau qui se déplace en Z.
Alors pour filmer un time/lapse c'est parfait étant donné que la pièce imprimée ne subit pas de déplacement latéraux comme sur une Prusai3, mais pour effectuer les réglages de hauteur de tête c'est moins évident mais on s'y fait…
Cependant il persiste un truc, c'est que dés que l'impression est terminée, les moteurs se relâchent et le plateau chute brusquement de sa hauteur.
Il vaut donc mieux amortir cette chute, mais en prenant soins de conserver le plateau à une hauteur de quelque millimètres par rapport au maximum baissé, une sorte de petite marge.
En effet j'ai pu constater un sale bruit de moteur qui force à chaque démarrage d'impression, dû au fait qu'à l'initialisation de l'axe Z le moteur fait marche arrière, descendant le plateau sur quelques millimètres avant de remonter tranquillement. Et si le plateau est en butée basse, et bien ça coince !
Pour résoudre ces deux problèmes, deux solution sont possibles, l'élastique et/ou le ressort :
- Un élastique pour un amortis contre le moteur, en fixant sous le plateau cette pièce dessinée par Bruno Bellamy.
- Des ressorts intégrés directement sur les tiges.
Autre soucis, la courroie avait tendance à pas mal frotter lors de son passage entre les différentes pièces, j'ai alors ajouté des anneaux de guidage supplémentaires pour arranger ça.
Révision du système de soutiens et de réglage du plateau : :
Comme je l'évoquais dans le billet précédent, j'ai revu le Bed leveling System.
En effet il avait le défaut d'être instable avec ses 4 coins indépendants qui ne se tenaient pas forcément bien droit.
Cette nouvelle version solidarise le tout pour éviter ce phénomène et va permettre aussi de poser le plateau en verre.
J'ai utilisé une barre d'acier de 16 mm de large et 2 mm d'épaisseur pour découper 4 plaquettes identiques, percées à 3 mm et taraudées pour accueillir des vis de montage. Matériel nécessaire :
- 4 vis à tête hexagonale M4x30 mm.
- 4 écrous M4.
- 4 ressorts.
- 8 vis a tête fraisée M3x10 mm.
- 1 barre de en acier plat 12 mm de large x 2 mm d'épaisseur.
Reste à tout assembler et réutilisant les mêmes vis et ressorts qu'auparavant.
Le plateau en verre, ici un miroir de récupération (verre qui de part son traitement est capable de résister au hautes températures) découpé aux dimensions exactes du plateau chauffant est placé dessus, et ainsi engoncé ne nécessite aucune autres pièces mécanique de maintiens, ce qui a l'avantage de ne jamais gêner la tête d'impression et de bénéficier du maximum de la surface d'impression.
Coffrage en verre synthétique :
Ce qui est bien avec la SmartRapcore, c'est que sa structure forme d'elle même un boitier, et autant une structure fermée est facultative pour imprimer en PLA, autant pour de l'ABS elle est indispensable, sans quoi on expose les impressions au courants d'airs provoquant délaminage, décollement de couche et warping important.
Pour fermer le boitier il suffit alors de clore deux faces, facile non ?
Oui mais chronophage comme souvent, et je m'en vais vous montrer ici ma méthode de découpe de panneaux d'acrylique de 9 et 4 mm, car c'est ce matériaux transparent que j'ai évidemment choisis.
Pour le tracé, plutôt que d'utiliser un feutre indélébile qui me faisait défaut, j'ai opté pour de l'adhésif de masquage tracé au crayon, qui a en plus l'avantage de ne pas trop arracher le film de protection de l'acrylique lors de la coupe.
Pour la découpe j'utilise une scie sauteuse avec, très important, une lame à chantourner (à métal ou à bois, peu importe), qui de part sa nature (dents orientées sur le devant et les côtés) permet une découpe correcte et non désaxée lorsqu'on utilise comme ici un guide pour avancer droit. (qui à tendance à pousser la lame dans le sens opposé au guide)
Sans un·e acolyte pour aider à soutenir la plaque durant l'opération, je fais appel à mon sèche linge…
Pour l'assemblage des plaques, des trous sont effectués et filetés dans tranches des plaques les plus épaisses.
Voici pour l'assemblage, où j'ai réutilisé des charnières dessinées lors du projet porte livre.
J'en ai profité pour confectionner une housse anti-poussière et ajouté un éclairage avec une bande de LED.
- J'ai depuis câblé un lecteur de carteSD pour pouvoir se passer de l'ordinateur, avec quelques fichiers Gcodes de commandes automatiques utiles.
- Il faudra aussi que je revienne sur les réglages logiciel que j'ai pu opérer, tant pour fiabiliser l'engin que pour simplifier son utilisation.
Voici la liste des liens Thingiverse pour télécharger l'imprimante :