Hier, 11:32 PM
Pour le fun: voici les photos et un lien vers le code d'un petit projet de télécommande Devialet réalisé avec mon fils (17 ans) en attendant la reprise des cours. Projet plaisant car nous étions dans une vraie collaboration et pas prof/élève.
Principales fonctions :
L'alimentation se fait par USB sur le PCHC familial, un cable série se branche sur le connecteur RS232 du D400 Master, un cable trigger va vers le caisson.
En service et adopté par la famille.
Le code ici :
![[Image: Remote1.jpeg?rlkey=fextt96xw63aebyg68ixxsuga&raw=1]](https://www.dropbox.com/scl/fi/6uzld0evrzk9ezgersgqk/Remote1.jpeg?rlkey=fextt96xw63aebyg68ixxsuga&raw=1)
![[Image: Remote2.jpeg?rlkey=k8vye3oqpkrf9a9aidt95p90g&raw=1]](https://www.dropbox.com/scl/fi/huacrkkaty1hosa8r9ofr/Remote2.jpeg?rlkey=k8vye3oqpkrf9a9aidt95p90g&raw=1)
Principales fonctions :
- affichage de la source active
- affichage du statut power (ampli allumé / éteint)
- sélection de source
- si ampli éteint : sélectionner une source allume l'ampli et le bascule sur cette source
- allumage/extinction de l'ampli
- levier "basses de la mort" qui déclenche le trigger pour allumer le caisson et met l'ampli en mode PREOUT ON (une deuxième activation du levier éteint le caisson et remet l'ampli en mode PREOUT OFF) + LED de statut caisson activé / désactivé
- entièrement compatible et synchro avec la télécommande Devialet d'origine, les deux fonctionnent de concert (par exemple un changement de source à la télécommande d'origine se verra répercuté sur l'afficheur de la télécommande Arduino)
- des petites animations de LED un peu gadget lors des phases d'allumage ou d'extinction.
L'alimentation se fait par USB sur le PCHC familial, un cable série se branche sur le connecteur RS232 du D400 Master, un cable trigger va vers le caisson.
En service et adopté par la famille.
Le code ici :
Code :
// Arduino based remote for Devialet Expert amplifier
// =================================
// Version 1.0 dated Nov 4th, 2025
// Hardware and Software by E&R
//
// Works in full sync with Devialet amp and original remote
// 4 buttons and LEDs for source selection and display. In case amp is off, button powers it on the selected source
// 1 button and LED for amp on/off
// 1 button for PREOUT on/off : sets Devialet in PREOUT = ON/OFF mode and activate/deactivate trigger relay to switch on subwoofer
// NB: forces PREOUT = off at Devialet Expert startup overriding SDCARD configuration
// remote connects to Devialet through basic serial cable into the RS232 amplifier connector
//
//
// questions: alec_eiffel on forum-hifi.fr or Roon community forums
//
//
#include <SoftwareSerial.h>
// pin labelling on Arduino board
const int SOURCE1BOUTON = 0; // sources push buttons
const int SOURCE2BOUTON = 1;
const int SOURCE3BOUTON = 2;
const int SOURCE4BOUTON = 3;
const int DEVIALETBOUTON = 4; // Devialet on/off push button
const int BASSESDELAMORTLEVIER = 13; // for subwoofer button/lever
const int rxPin = 12 ; // Devialet serial rx
const int txPin = 7 ; // Devialet serial tx
const int REVERSE_SERIAL_LOGIC_DEVIALET = true; // true for older models, false for newer models, try to change if issues
const int TIMEOUT_SERIAL = 20 ; // in milliseconds, don't go above 50
const int RELAISPIN = 8; // for subwoofer trigger 5V relay
const int LEDSOURCE1 = 9; // source LEDs
const int LEDSOURCE2 = 10;
const int LEDSOURCE3 = 11;
const int LEDSOURCE4 = 5;
const int LEDDEVIALET = 6; // Devialet on/off LED
const int LEDSOURCELIST[] = {LEDSOURCE1, LEDSOURCE2, LEDSOURCE3, LEDSOURCE4};
// Devialet configuration
// Devialet serial port init
// Third argument is either true or false depending on your Devialet Expert (earlier models: false, older models: true).
SoftwareSerial devialet(rxPin, txPin, REVERSE_SERIAL_LOGIC_DEVIALET);
const String DEVIALET_ID = "devialet"; // needs to be as configured in Devialet configuration / RS232 - default is "Devialet"
// Important : make sure RS232 Devialet is set to "command acknowledged" only
const long RS232_SPEED = 38400; // higher values can work, set same value in Devialet RS232 Configuration
// Devialet Sources - adjust to your setup
const String SOURCE1 = "TV";
const String SOURCE2 = "TchiTcha";
const String SOURCE3 = "ETH";
const String SOURCE4 = "Vinyl";
// Devialet commands
const String POWER_REQUEST = "[" + DEVIALET_ID + ">POWER=?]";
const String POWER_ON = "[" + DEVIALET_ID + ">POWER=1]";
const String POWER_OFF = "[" + DEVIALET_ID + ">POWER=0]";
const String PREOUT_REQUEST = "[" + DEVIALET_ID + ">PREOUT=?]";
const String PREOUT_ON = "[" + DEVIALET_ID + ">PREOUT=1]";
const String PREOUT_OFF = "[" + DEVIALET_ID + ">PREOUT=0]";
const String SOURCE_REQUEST = "[" + DEVIALET_ID + ">SOURCE=?]";
const String SOURCE1_SWITCH = "[" + DEVIALET_ID + ">SOURCE=" + SOURCE1 +"]";
const String SOURCE2_SWITCH = "[" + DEVIALET_ID + ">SOURCE=" + SOURCE2 +"]";
const String SOURCE3_SWITCH = "[" + DEVIALET_ID + ">SOURCE=" + SOURCE3 +"]";
const String SOURCE4_SWITCH = "[" + DEVIALET_ID + ">SOURCE=" + SOURCE4 +"]";
// GENERAL VARIABLES
int power_ampli = 0;
int source_active_devialet = 0;
int source_souhaitee = 0;
int preout_actif_devialet = 0;
int preout_souhaite = -1;
int preout_status = 0;
unsigned long last_levier_baisse = 0; // in milliseconds
const unsigned long DELAI_PRISE_EN_COMPTE_LEVIER = 20000; // délai pendant lequel on accepte la prise en compte d'une commande levier quand le Devialet est OFF ou en démarrage
unsigned long last_bouton_power = 0;
const unsigned long DELAI_PRISE_EN_COMPTE_BOUTON_POWER = 15000; // délai pendant lequel on va faire clignoter la LED au démarrage via bouton power de l'automate
int value = 0 ; // pour les resultats d'extract_value_xxx
int i = 0;
String dump = ""; // to dump Devialet ACK acknowledgements and purge serial line
const int ACK = 2;
const unsigned long int PERIODE_DEVIALET = 800; // en millisecondes
const unsigned long int PERIODE_BOUTON = 100; // en millisecondes
int ctr_scan_devialet = 1; // compteur qui s'incrémente à chaque PERIODE_DEVIALET jusqu'à 3 puis revient à 1
// 1 = scan POWER, 2 = scan SOURCE, 3 = scan PREOUT
// ceci pour ne faire qu'une seule requête au Devialet par cycle
unsigned long int last_boucle_devialet = 0;
unsigned long int last_boucle_bouton = 0;
unsigned long int time = 0;
// parameters and variabe for LEDs intensity and blinking management
const int HIGH_BUTTON = 24; // between 0 and 255 - higher values = more intensity. Adjust according to your setup.
const int NO_BLINK = 0;
const int SLOW_BLINK = 1; // blink mode 1
const int FAST_BLINK = 2; // blink mode 2
const int BLINK_MOTIF_LONGUEUR = 20;
const int BLINK_MOTIF_1[] = {LOW,HIGH_BUTTON/20, HIGH_BUTTON/18,HIGH_BUTTON/16,HIGH_BUTTON/13,HIGH_BUTTON/10,HIGH_BUTTON/8,
HIGH_BUTTON/6,HIGH_BUTTON/4,HIGH_BUTTON/2,HIGH_BUTTON,
HIGH_BUTTON/2,HIGH_BUTTON/4,HIGH_BUTTON/6,HIGH_BUTTON/8,HIGH_BUTTON/10,
HIGH_BUTTON/13,HIGH_BUTTON/16,HIGH_BUTTON/18,HIGH_BUTTON/20}; // chronogramme mode 1, longeur BLINK_MOTIF_LONGUEUR
const int BLINK_MOTIF_2[] = {LOW,HIGH_BUTTON,LOW,HIGH_BUTTON,LOW,HIGH_BUTTON,LOW,HIGH_BUTTON,LOW,HIGH_BUTTON,LOW,HIGH_BUTTON,LOW,HIGH_BUTTON,LOW,HIGH_BUTTON,LOW,HIGH_BUTTON,LOW,HIGH_BUTTON}; // chronogramme mode 2, longeur BLINK_MOTIF_LONGUEUR
int blink_position[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // tableau d'indice dans motif blink pour la led i ! blink_position[i]
// FUNCTIONS
int contient(String s2, String s1)
{
if (s2.indexOf(s1) != -1)
{
return 1;
} else {
return 0;
}
}
int extract_value_power(String message)
{
if (contient(message, "POWER:1]"))
{
return (1);
}
else if (contient(message, "POWER:0]"))
{
return (0);
}
else if (contient(message, "POWER=ACK]"))
{
return (ACK);
}
else
{
return (-1);
}
}
int extract_value_preout(String message)
{
if (contient(message, "PREOUT:1]"))
{
return (1);
}
else if (contient(message, "PREOUT:0]"))
{
return (0);
}
else if (contient(message, "PREOUT=ACK]"))
{
return (ACK);
}
else
{
return (-1);
}
}
int extract_ack_source(String message)
{
if (contient(message, "SOURCE=ACK]"))
{
return (ACK);
}
else
{
return (-1);
}
}
int extract_source(String message)
{
if (contient(message, SOURCE1))
{
return (1);
}
if (contient(message, SOURCE2))
{
return (2);
}
if (contient(message, SOURCE3))
{
return (3);
}
if (contient(message, SOURCE4))
{
return (4);
}
else
{
return (-1);
}
}
String receive_message() {
devialet.listen();
String message = "";
unsigned long lastReceive = millis();
while (millis() - lastReceive < TIMEOUT_SERIAL)
{
int c = devialet.read();
if (c != -1) {
message += (char)c;
lastReceive = millis();
}
}
return (message);
}
int lire_bouton(int bouton)
{
if (bouton == 1)
{
if (digitalRead(SOURCE1BOUTON) == LOW)
{
return (1);
}
else
{
return (0);
}
}
else if (bouton == 2)
{
if (digitalRead(SOURCE2BOUTON) == LOW)
{
return (1);
}
else
{
return (0);
}
}
else if (bouton == 3)
{
if (digitalRead(SOURCE3BOUTON) == LOW)
{
return (1);
}
else
{
return (0);
}
}
else if (bouton == 4)
{
if (digitalRead(SOURCE4BOUTON) == LOW)
{
return (1);
}
else
{
return (0);
}
}
else if (bouton == 5)
{
if (digitalRead(DEVIALETBOUTON) == LOW)
{
return (1);
}
else
{
return (0);
}
}
else if (bouton == 6)
{
if (digitalRead(BASSESDELAMORTLEVIER) == LOW)
{
return (1);
}
else
{
return (0);
}
}
}
void led_toggle_source(int led)
{
if (led == 1)
{
analogWrite(LEDSOURCE1, HIGH_BUTTON);
analogWrite(LEDSOURCE2, LOW);
analogWrite(LEDSOURCE3, LOW);
analogWrite(LEDSOURCE4, LOW);
}
else if (led == 2)
{
analogWrite(LEDSOURCE2, HIGH_BUTTON);
analogWrite(LEDSOURCE1, LOW);
analogWrite(LEDSOURCE3, LOW);
analogWrite(LEDSOURCE4, LOW);
}
else if (led == 3)
{
analogWrite(LEDSOURCE3, HIGH_BUTTON);
analogWrite(LEDSOURCE2, LOW);
analogWrite(LEDSOURCE1, LOW);
analogWrite(LEDSOURCE4, LOW);
}
else if (led == 4)
{
analogWrite(LEDSOURCE4, HIGH_BUTTON);
analogWrite(LEDSOURCE2, LOW);
analogWrite(LEDSOURCE3, LOW);
analogWrite(LEDSOURCE1, LOW);
}
else if (led == 0)
{
analogWrite(LEDSOURCE4, LOW);
analogWrite(LEDSOURCE2, LOW);
analogWrite(LEDSOURCE3, LOW);
analogWrite(LEDSOURCE1, LOW);
}
}
void scan_sources_boutons()
{
if (power_ampli != 0)
{
if ((lire_bouton(1)) && (source_active_devialet != 1))
{
while (lire_bouton(1))
{
}
devialet.println(SOURCE1_SWITCH);
value = extract_ack_source(receive_message());
if (value == ACK)
{
led_toggle_source(1);
}
source_souhaitee = 1;
}
else if ((lire_bouton(2)) && (source_active_devialet != 2))
{
while (lire_bouton(2))
{
}
devialet.println(SOURCE2_SWITCH);
value = extract_ack_source(receive_message());
if (value == ACK)
{
led_toggle_source(2);
}
source_souhaitee = 2;
}
else if ((lire_bouton(3)) && (source_active_devialet != 3))
{
while (lire_bouton(3))
{
}
devialet.println(SOURCE3_SWITCH); // change source name depending on your setup
value = extract_ack_source(receive_message());
if (value == ACK)
{
led_toggle_source(3);
}
source_souhaitee = 3;
}
else if ((lire_bouton(4)) && (source_active_devialet != 4))
{
while (lire_bouton(4))
{
}
devialet.println(SOURCE4_SWITCH); // change source name depending on your setup
value = extract_ack_source(receive_message());
if (value == ACK)
{
led_toggle_source(4);
}
source_souhaitee = 4;
}
}
else
{
i = 1;
int bouton_appuye = 0;
while (i <= 4)
{
if (lire_bouton(i))
{
while (lire_bouton(i))
{
}
bouton_appuye = 1;
source_souhaitee = i;
last_bouton_power = millis();
}
i++;
}
if (bouton_appuye)
{
analogWrite(LEDDEVIALET, HIGH_BUTTON);
devialet.println(POWER_ON);
dump = receive_message();
// reset blink position
i = 0;
while (i < sizeof (blink_position))
{
blink_position[i] = 0;
i++;
}
}
}
}
void scan_bouton_devialet()
{
if (digitalRead(DEVIALETBOUTON) == LOW)
{
while (digitalRead(DEVIALETBOUTON) == LOW)
{
}
if (power_ampli == 1)
{
// protocole extinction et mise à jour des variables, arrêt du caisson...
led_toggle_source(0);
analogWrite(LEDDEVIALET, LOW);
devialet.println(PREOUT_OFF);
dump = receive_message();
delay (200);
devialet.println(POWER_OFF);
dump = receive_message();
source_active_devialet = 0;
source_souhaitee = 0;
preout_souhaite = -1;
preout_actif_devialet = 0;
preout_status = 0;
power_ampli = 0;
last_bouton_power = 0;
last_levier_baisse = 0;
digitalWrite(RELAISPIN, LOW);
// led blink show at power off
i = 0;
blink_position[LEDDEVIALET] = 0;
while (i < (BLINK_MOTIF_LONGUEUR+1))
{
blink (LEDDEVIALET,FAST_BLINK);
delay(100);
i++;
}
i = 0;
blink_position[LEDDEVIALET] = 0;
while (i < (BLINK_MOTIF_LONGUEUR+1))
{
blink (LEDDEVIALET,SLOW_BLINK);
delay(100);
i++;
}
devialet.println(POWER_OFF); // une deuxième fois par précaution
dump = receive_message();
delay(3000); // impossible de rallumer avant 3 secondes
}
else
{
devialet.println(POWER_ON);
dump = receive_message();
last_bouton_power = millis();
}
}
}
void scan_levier_bassesdelamort()
{
if (power_ampli == 1)
{
if (lire_bouton(6))
{
while (lire_bouton(6))
{
}
last_levier_baisse = millis();
if (preout_status == 0)
{
preout_souhaite = 1;
devialet.println(PREOUT_ON);
value = extract_value_preout(receive_message());
if (value == ACK)
{
digitalWrite(RELAISPIN, HIGH);
}
}
else if (preout_status == 1)
{
preout_souhaite = 0;
devialet.println(PREOUT_OFF);
value = extract_value_preout(receive_message());
if (value == ACK)
{
digitalWrite(RELAISPIN, LOW);
}
}
}
}
else
{
if (lire_bouton(6))
{
while (lire_bouton(6))
{
}
last_levier_baisse = millis();
if (preout_status == 0)
{
preout_souhaite = 1;
}
else if (preout_status == 1)
{
preout_souhaite = 0;
}
}
}
}
void blink (int led, int mode)
{
if (mode != 0)
{
if (mode < 3)
{
if (mode == 1) // slow blinking
{
analogWrite(led, BLINK_MOTIF_1[blink_position[led]]);
}
else if (mode == 2) // fast blinking
{
analogWrite(led, BLINK_MOTIF_2[blink_position[led]]);
}
blink_position[led]++;
if (blink_position[led] >= BLINK_MOTIF_LONGUEUR)
{
blink_position[led] = 0;
}
}
}
else
{
analogWrite (led,HIGH_BUTTON);
}
}
// initialisation
void setup()
{
// configure pins
pinMode(SOURCE1BOUTON, INPUT_PULLUP);
pinMode(SOURCE2BOUTON, INPUT_PULLUP);
pinMode(SOURCE3BOUTON, INPUT_PULLUP);
pinMode(SOURCE4BOUTON, INPUT_PULLUP);
pinMode(DEVIALETBOUTON, INPUT_PULLUP);
pinMode(BASSESDELAMORTLEVIER, INPUT_PULLUP);
pinMode(RELAISPIN, OUTPUT);
pinMode(LEDSOURCE1, OUTPUT);
pinMode(LEDSOURCE2, OUTPUT);
pinMode(LEDSOURCE3, OUTPUT);
pinMode(LEDSOURCE4, OUTPUT);
pinMode(LEDDEVIALET, OUTPUT);
// init Devialet serial port and get Power on/off status
devialet.begin(RS232_SPEED);
delay(200);
devialet.println(POWER_REQUEST);
value = extract_value_power(receive_message());
if ((value == 0 || value == 1))
{
power_ampli = value;
}
delay(200);
devialet.println(SOURCE_REQUEST);
value = extract_source(receive_message());
if ((value >=1 && value <= 4))
{
source_active_devialet = value;
}
delay (200);
devialet.println(PREOUT_REQUEST);
value = extract_value_preout(receive_message());
if ((value == 0 || value == 1))
{
preout_actif_devialet = value;
}
preout_status = preout_actif_devialet;
// remote will only be operational after DELAI_PRISE_EN_COMPTE_LEVIER milliseconds to ensure code is working well, meanwhile blink leds like crazy
while (millis() <= DELAI_PRISE_EN_COMPTE_LEVIER )
{
blink (LEDDEVIALET, SLOW_BLINK);
blink (LEDSOURCE1, SLOW_BLINK);
blink (LEDSOURCE2, SLOW_BLINK);
blink (LEDSOURCE3, SLOW_BLINK);
blink (LEDSOURCE4, SLOW_BLINK);
delay(50);
}
}
// main loop
void loop()
{
time = millis();
if (time > (last_boucle_devialet + PERIODE_DEVIALET))
{
ctr_scan_devialet = ctr_scan_devialet + 1;
if (ctr_scan_devialet > 3)
{
ctr_scan_devialet = 1;
}
if (ctr_scan_devialet == 1)
// fonctions POWER dans boucle Devialet
{
devialet.println(POWER_REQUEST);
value = extract_value_power(receive_message());
if ((value == 1) && (power_ampli == 0))
{
devialet.println(PREOUT_OFF); // force PREOUT=0 at amp's startup
dump = receive_message();
}
if ((value == 0 || value == 1))
{
power_ampli = value;
}
if (power_ampli == 0)
{
if (time > (last_bouton_power + DELAI_PRISE_EN_COMPTE_BOUTON_POWER)) // test if not in a blink situation and if not switch off LEDs
{
led_toggle_source(0);
analogWrite(LEDDEVIALET, LOW);
}
preout_actif_devialet = 0;
preout_status = 0;
digitalWrite(RELAISPIN, LOW);
}
else
{
analogWrite(LEDDEVIALET, HIGH_BUTTON);
}
}
else if (ctr_scan_devialet == 2 && power_ampli != 0)
// fonctions PREOUT dans boucle Devialet
{
devialet.println(PREOUT_REQUEST);
value = extract_value_preout(receive_message());
if ((value == 0 || value == 1))
{
preout_actif_devialet = value;
}
if (preout_souhaite == preout_actif_devialet)
{
preout_status = preout_actif_devialet;
preout_souhaite = -1;
}
else
{
if ((preout_souhaite == 1) && (time < (last_levier_baisse + DELAI_PRISE_EN_COMPTE_LEVIER)))
{
devialet.println(PREOUT_ON);
if (extract_value_preout(receive_message()) == ACK)
{
digitalWrite(RELAISPIN, HIGH);
last_levier_baisse = 0 ;
}
}
else if ((preout_souhaite == 0) && (time < (last_levier_baisse + DELAI_PRISE_EN_COMPTE_LEVIER)))
{
devialet.println(PREOUT_OFF);
if (extract_value_preout(receive_message()) == ACK)
{
digitalWrite(RELAISPIN, LOW);
last_levier_baisse = 0;
}
}
else if (preout_actif_devialet != preout_status)
{
preout_status = preout_actif_devialet;
if (preout_status == 0)
{
digitalWrite(RELAISPIN, LOW);
}
else
{
digitalWrite(RELAISPIN, HIGH);
}
}
}
}
else if (ctr_scan_devialet == 3 && power_ampli != 0)
// fonctions SOURCE dans boucle Devialet
{
devialet.println(SOURCE_REQUEST);
value = extract_source(receive_message());
if ((value >=1) && (value <= 4))
{
source_active_devialet = value;
}
if (source_souhaitee == 0)
{
led_toggle_source(source_active_devialet);
}
if (source_active_devialet == source_souhaitee)
{
source_souhaitee = 0;
}
else if (source_souhaitee != 0)
{
if (source_souhaitee == 1)
{
devialet.println(SOURCE1_SWITCH);
}
else if (source_souhaitee == 2)
{
devialet.println(SOURCE2_SWITCH);
}
else if (source_souhaitee == 3)
{
devialet.println(SOURCE3_SWITCH);
}
else if (source_souhaitee == 4)
{
devialet.println(SOURCE4_SWITCH);
}
dump = receive_message();
}
}
time = millis();
}
if (time > (last_boucle_bouton + PERIODE_BOUTON))
{
scan_sources_boutons();
scan_bouton_devialet();
scan_levier_bassesdelamort();
// blink buttons at startup
if ((power_ampli == 0) && (time < (last_levier_baisse + DELAI_PRISE_EN_COMPTE_LEVIER)))
{
blink(LEDDEVIALET, FAST_BLINK); // levier baisse récemment, mode blink prioritaire et plus flashy sur led power;
if (source_souhaitee > 0 ) // pour le démarrage via bouton source
{
blink (LEDSOURCELIST[source_souhaitee - 1], SLOW_BLINK);
}
}
else if ((power_ampli == 0) && (time < (last_bouton_power + DELAI_PRISE_EN_COMPTE_BOUTON_POWER)))
{
blink(LEDDEVIALET, SLOW_BLINK);
if (source_souhaitee > 0 ) // pour le démarrage via bouton source
{
blink (LEDSOURCELIST[source_souhaitee - 1], SLOW_BLINK);
}
}
last_boucle_bouton = millis();
}
}![[Image: Remote1.jpeg?rlkey=fextt96xw63aebyg68ixxsuga&raw=1]](https://www.dropbox.com/scl/fi/6uzld0evrzk9ezgersgqk/Remote1.jpeg?rlkey=fextt96xw63aebyg68ixxsuga&raw=1)
![[Image: Remote2.jpeg?rlkey=k8vye3oqpkrf9a9aidt95p90g&raw=1]](https://www.dropbox.com/scl/fi/huacrkkaty1hosa8r9ofr/Remote2.jpeg?rlkey=k8vye3oqpkrf9a9aidt95p90g&raw=1)
HP Atohm Eurus 1.0 modifiées - Devialet D400 - Roon+HTPC
![[Image: banniereforumhifi.jpg]](https://i.postimg.cc/wxwWFvzj/banniereforumhifi.jpg)




Ça c’est du vrai DIY ! Le levier de vitesse en cuivre de plombier, chapeau!