Bonjour à tous,
Je suis nouveau dans l’univers Jeedom depuis 2 semaines et je suis déjà franchement emballé par les possibilités !
Par contre je suis confronté à un problème :
Je cherche à domotiser des volets roulants Profalux. Ils sont pilotés en radio mais le code est propriétaire donc visiblement c’est à oublier…
Cependant sur les boitiers radio qui sont fixés au mur, Profalux à prévu 3 pins qui une fois reliée à une 4em pin de masse permettent une commande externe. (Monté, descente, stop).
(Source : Domotiser des volets non domotisables - C'est possible ! - La Domotique de Sarakha63)
Ces boitiers sont autonomes et fonctionne avec une pile 3V.
Donc dans l’idéal j’aimerais trouver un « mini module » auto alimenté avec une pile 3V par exemple. Et malgré mes recherches je n’ai trouvé aucun produit existant et peu de problématique similaires… (étrange !)
J’ai pensé à deux solutions :
1/ Je suis très content de mon installation zigbee (via pizigate et plugin zigate)
et des capteurs Xiaomi (température, bouton, relai etc). Je pensais utiliser un relais Reed qui a ma connaissance est le contact sec le moins gourmand en énergie. En détournant la partie zigbee d’une prise connecté par exemple ?
Mais c’est très limité, j’aurai une sortie maxi j’imagine… (a condition que ça soit faisable de modifier l’alimentation également)
2/Toujours avec du Reed et du zigbee (pour la faible consommation et alimentation sur pile bouton 3V) : acheter un module zigbee
et faire mon montage perso, mais comment faire pour l’intégrer dans Jeedom ?
(E18-MS1-PCB Zigbee Module émetteur récepteur sans fil Zigbee IO CC2530 E18 MS1 PCB Ghz 2.4 mW, antenne PCB IoT uhf, réseau maillé, Module émetteur récepteur, 2.5 | AliExpress)
Bref il me manque des éléments dans les deux solutions pour être sur que ça puisse être réalisable. Si vous avez des idées, des pistes, même une solution à partir de zéro, n’hésitez pas!
Je n’ai pas un gros niveau d’électronique, ni de programmation mais ça ne me fait pas spécialement peur d’y passer un peu de temps.
Merci d’avance pour ces échanges !
EDIT
'**** SOLUTION '***
Grand merci à @PanoLyon qui a répondu à ma problématique en me proposant l’ESP32 alimenté par batterie ainsi que pour ces explications en programmation !
Depuis j’ai ajouté quelques options aux codes, étant donné que j’ai trouvé peu d’infos dans mes recherches je vais essayer de faire un résumer pour le(s) suivant(s) :
Depuis Jeedom, (plugin MQTT) je déclenche des ordres à travers des topics qui seront interprétés par l’ESP32. Il peut ainsi exécuter les actions associées aux différents ordres. Dans mon cas actionner des micro-relais (SIP-1A03) pour relier les pins de mes télécommandes de volets (exemple ici)
Dès que l’action est effectuée par L’ESP32 il publie un retour d’état pour confirmé à Jeedom que l’ordre à été reçu.
Le but était d’avoir quelque chose de sans fil, compact et autonome pour être implanté près des boîtiers muraux. Pour avoir une bonne autonomie l’ESP32 rentre en mode DeepSleep, lors de son réveil il vérifie si un ordre est en attente, l’exécute le cas échéant puis repasse en mode sommeil. L’autonomie est donc en effet intimement lié au temps de réponse désiré…
Pour palier en parti à ce problème je synchronise l’horloge interne de l’ESP32 avec un serveur NTP. Cela me permet d’ajuster le temps de sommeil en fonction des moments de la journée : 25 secondes pendant les périodes d’utilisation des volets (matin/soir) et 1h en pleine après-midi par exemple. Un fois la syntonisation du temps effectué je le rentre dans l’horloge interne cela me permet d’être à l’heure lors du réveil de l’ESP → pas de besoin de resynchroniser avec le serveur à chaque fois. Ça évite de surcharger les serveurs NTP et ça augmente mon autonomie. D’ailleurs le code permet également la mesure et l’envoi du niveau de batterie à travers un topic MQTT. (en utilisant une pin dédié du lolin D32)
En fouillant sur le net j’ai trouvé plusieurs exemples de code et de documentations de ces fonctions. Mais à chaque fois individuellement. Il ne m’a pas été facile de faire coordonner toutes ces fonctions → Voici donc mon code qui je l’espère pourra servir de bonne base aux prochains rencontrant cette problématique !
#include <Arduino.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <esp_wifi.h>
#include <esp_bt.h>
#include <time.h>
#include <ezTime.h>
#include <sys/time.h>
// Replace the next variables with your SSID/Password combination
const char* ssid = "******";
const char* password = "*************";
// Add your MQTT Broker IP address, example:
//const char* mqtt_server = "192.168.1.144";
const char* mqtt_server = "****";
const char* user = "******";
const char* user_pwd = "*****";
// Set your Static IP address & Gateway IP address -> Consomation réduite (/DHCP)
IPAddress local_IP(192, 168, 1, 10);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
WiFiClient espClient;
PubSubClient client(espClient);
#define WIFI_TIMEOUT 10000 /* 10 secondes in milliseconds */
#define GLOBAL_TIMEOUT 15000 /* 20 secondes in milliseconds */
#define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */
unsigned long chronoGlobal = millis(); // Initialisation Chrono time_timeout
// Batterie
float bat = 0;
int bat_pourcentage = 0;
float bat_volt = 0;
RTC_DATA_ATTR int previous_day = 0;
RTC_DATA_ATTR bool autorisation_envoi_Nbat = true;
// Conversion EZTime to EPOCH
int Year_conv = 0;
int Mois_conv = 0;
int J_conv = 0;
int H_conv = 0;
int m_conv = 0;
int sec_conv = 0;
//Tempo Volet
const int Tempo = 1; /* Tempo Volet ON actif (secondes) */
int TempoMS = Tempo * 1000; /* Tempo Volet en ms) */
//Heures modulation temps DeepSleep
const int H1_Deepsleep = 5;
const int H2_Deepsleep = 11;
const int H3_Deepsleep = 16;
const int H4_Deepsleep = 23;
// Volet Pin
const int MB = 16;
const int DB = 17;
const int MH = 5;
const int DH = 21;
// Initialisation structure stockage temps
struct tm timeinfo;
unsigned int duree_sleep = 25; //4200 max
void get_time() {
time_t now;
time(&now);
localtime_r(&now, &timeinfo);
}
void afficher_temps() {
Serial.print("Année: ");
Serial.println(timeinfo.tm_year+1900);
Serial.print("Jour: ");
Serial.println(timeinfo.tm_mday);
Serial.print("Mois: ");
Serial.println(timeinfo.tm_mon+1);
Serial.print("Heure: ");
Serial.print(timeinfo.tm_hour);
Serial.print(":");
Serial.print(timeinfo.tm_min);
Serial.print(":");
Serial.println(timeinfo.tm_sec);
}
int ConvEZTime() {
time_t t_of_day;
struct tm t;
t.tm_year = Year_conv-1900;
t.tm_mon = Mois_conv-1; // Month, 0 - jan
t.tm_mday = J_conv; // Day of the month
t.tm_hour = H_conv;
t.tm_min = m_conv;
t.tm_sec = sec_conv+1;
t_of_day = mktime(&t);
return t_of_day;
}
void Set_time() {
int epoch_time = ConvEZTime();
timeval epoch = {epoch_time, 0};
const timeval *tv = &epoch;
timezone utc = {0,0};
const timezone *tz = &utc;
settimeofday(tv, tz);
}
void calculDSleep(){
get_time();
if (timeinfo.tm_year+1900 < 2019) {
duree_sleep = 25; //Durée par défaut Sleep (secondes)
Serial.println("Année inférieur a 2019 - Pas de synchro");
}
else
{
if (timeinfo.tm_hour < H1_Deepsleep)
{
duree_sleep = 3600; //Durée augmenté Sleep (secondes)
}
else if (timeinfo.tm_hour < H2_Deepsleep && timeinfo.tm_hour > H3_Deepsleep)
{
duree_sleep = 3600; //Durée augmenté Sleep (secondes)
}
else if (timeinfo.tm_hour > H4_Deepsleep)
{
duree_sleep = 3600; //Durée augmenté Sleep (secondes)
}
else
{
duree_sleep = 25; //Durée par défaut Sleep (secondes)
}
}
}
void goToDeepSleep()
{
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
esp_wifi_stop();
Serial.println();
calculDSleep();
Serial.print("Temps de sommeil (en s): ");
Serial.println(duree_sleep);
afficher_temps();
esp_sleep_enable_timer_wakeup(duree_sleep * uS_TO_S_FACTOR);
esp_deep_sleep_start();
}
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
/*if (!WiFi.config(local_IP, gateway, subnet)) {
Serial.println("STATIC IP Failed to configure");
} */
WiFi.begin(ssid, password);
unsigned long startAttemptTime = millis(); // Chrono 1er connection
while (WiFi.status() != WL_CONNECTED &&
millis() - startAttemptTime < WIFI_TIMEOUT) {
delay(500);
}
if(WiFi.status() != WL_CONNECTED){
Serial.println("WIFI CONNECTION FAILED");
goToDeepSleep();
}
//Serial.println("");
//Serial.println("WiFi connected");
//Serial.println("IP address: ");
//Serial.println(WiFi.localIP());
}
// Mesure de la tension et estimation du niveau de batterie (%) pour envoi MQTT
void batterie()
{
bat = analogRead(35);
bat_volt = (bat * 7.65)/(4096); //7.445
char batVString[8];
dtostrf(bat_volt, 1, 2, batVString);
//Serial.print("Voltage batterie ");
//Serial.println(batVString);
client.publish("esp32/V_battery", batVString);
bat_pourcentage =((-0.9759)*pow(bat_volt,3)+11.58*pow(bat_volt,2)-(44.232*bat_volt)+54.861)*100;
if (bat_pourcentage > 100){
bat_pourcentage = 100;
}
char batPString[8];
dtostrf(bat_pourcentage, 1, 2, batPString);
//Serial.print("Poucentage batterie ");
//Serial.println(batPString);
delay(200);
client.publish("esp32/p_battery", batPString);
}
// MQTT
void callback(char* topic, byte* message, unsigned int length) {
Serial.print("Message arrived on topic: ");
Serial.print(topic);
Serial.print(". Message: ");
String messageTemp;
for (int i = 0; i < length; i++) {
Serial.print((char)message[i]);
messageTemp += (char)message[i];
}
Serial.println();
// Feel free to add more if statements to control more GPIOs with MQTT
// If a message is received on the topic esp32/output, you check if the message is either "on" or "off".
// Changes the output state according to the message
if (String(topic) == "esp32/output") {
//Serial.print("Changing output to ");
if (messageTemp == "Pause"){
client.publish("esp32/Etatvolet", "Pause");
Serial.println("Sleep - Pause");
goToDeepSleep();
}
else {
if(messageTemp == "MBon"){
//Serial.println("MBas on");
digitalWrite(MB, HIGH);
delay(TempoMS);
digitalWrite(MB, LOW);
client.publish("esp32/Etatvolet", "1"); //Confirmation reception ordre
}
else if(messageTemp == "DBon"){
//Serial.println("DBas on");
digitalWrite(DB, HIGH);
delay(TempoMS);
digitalWrite(DB, LOW);
client.publish("esp32/Etatvolet", "1");
}
else if(messageTemp == "MHon"){
//Serial.println("MHaut on");
digitalWrite(MH, HIGH);
delay(TempoMS);
digitalWrite(MH, LOW);
client.publish("esp32/Etatvolet", "1");
}
else if(messageTemp == "DHon"){
//Serial.println("DHaut on");
digitalWrite(DH, HIGH);
delay(TempoMS);
digitalWrite(DH, LOW);
client.publish("esp32/Etatvolet", "1");
}
else if(messageTemp == "BAT"){
delay(500);
batterie();
client.publish("esp32/Etatvolet", "1");
}
else{
Serial.print("Message different - Sleep");
goToDeepSleep();
}
}
}
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("ESP32Client", user, user_pwd)) {
Serial.println("connected");
// Subscribe
if (!client.subscribe("esp32/output")) {
Serial.println("Sleep Subscribe failed");
goToDeepSleep();
}
}
else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.print("Go to sleep");
goToDeepSleep();
}
}
}
void setup() {
pinMode(MB, OUTPUT);
pinMode(DB, OUTPUT);
pinMode(MH, OUTPUT);
pinMode(DH, OUTPUT);
btStop();
esp_bt_controller_disable();
Serial.begin(115200);
setup_wifi();
get_time();
afficher_temps();
if(previous_day != timeinfo.tm_mday){ // Syncornisation 1 fois par jour - Si plus régulier utiliser "time_t lastNtpUpdateTime();"
if(waitForSync(5)) { //Attente syncro NTP Timeout => sec
Timezone France;
France.setLocation("Europe/Paris");
Serial.println("France: " + France.dateTime());
Year_conv = France.dateTime("Y").toInt();
Mois_conv = France.dateTime("n").toInt();
J_conv = France.dateTime("j").toInt();
H_conv = France.dateTime("H").toInt();
m_conv = France.dateTime("i").toInt();
sec_conv= France.dateTime("s").toInt();
Set_time();
previous_day = J_conv;
}
else{
Serial.println("Echec Synchronisation");
}
autorisation_envoi_Nbat = true; //Autorise l'envoi du niveau de batterie
}
client.setServer(mqtt_server, 1883); //MQTT
client.setCallback(callback); //MQTT
}
void loop() {
while (millis() - chronoGlobal <= GLOBAL_TIMEOUT) {
if (!client.connected()) {
reconnect();
}
get_time();
if (timeinfo.tm_hour > H2_Deepsleep && timeinfo.tm_hour < H3_Deepsleep && autorisation_envoi_Nbat == true) { // envoi batterie
delay(500); // Evite la mesure du pique de tension au démarrage ?
batterie();
autorisation_envoi_Nbat = false;
}
delay(10);
client.loop(); //Necessaire pour maintenir la conection MQTT
}
Serial.print("Global Timeout");
goToDeepSleep(); //En cas d'impasse dans le programme -> Sommeil profond pour ne pas vider la batterie
}
Nota : Je suis loin d’être un expert en programmation, si vous rencontrez des bugs ou si vous avez des suggestions d’améliorations je serais très content de les entendre.
Nota 2 : Il existe une dérive notable de la l’horloge interne en fonction des modèles la fréquence de synchronisation est donc à adapté avec son application (pour mon application 1 fois par jour me semble suffisant)
Nota 3 : La bibliothèque EZtime simplifie beaucoup la programmation de la synchronisation NTP, mais une conversion est nécessaire pour enregistrer le temps dans l’horloge interne de l’ESP32