Удлинитель с розетками для Яндекс умный дом (4DUK)

Описание протокола - Что такое - Чипльдуплекс

/*
  Удлинитель 4DUK.
  GPIO14 - Розетка 1
  GPIO12 - Розетка 2
  GPIO13 - Розетка 3
  UDP port 9009 - 4duk broadcast port
  TCP 80 - http port www
*/


#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <TZ.h>
// Timezone Москва
#define MYTZ TZ_Europe_Moscow


#ifndef STASSID
#define STASSID "MYWIFI"
#define STAPSK "topsecret"
#endif


#define devnum1 "25062501"
#define devnum2 "25062502"
#define devnum3 "25062503"

#define devname1 "rl" devnum1
#define devname2 "rl" devnum2
#define devname3 "rl" devnum3

#define devstate1_on "device:" devname1 ":on_off:on\r\n"
#define devstate1_off "device:" devname1 ":on_off:off\r\n"
#define devstate2_on "device:" devname2 ":on_off:on\r\n"
#define devstate2_off "device:" devname2 ":on_off:off\r\n"
#define devstate3_on "device:" devname3 ":on_off:on\r\n"
#define devstate3_off "device:" devname3 ":on_off:off\r\n"
unsigned int localPort = 9009;  // Порт умного дома

// buffers for receiving and sending data
char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1];  // buffer to hold incoming packet,

int cur_level=128;
int is_start=0;
int packetSize=0;
String pb;
char *dv;
unsigned long t_mil;
int onoff_state[3]={1,1,1};
int onoff_state1=0;
int onoff_state2=0;
int force_sendstate=0;


#define IN 0
#define RELAY1 14
#define RELAY2 12
#define RELAY3 13
int relay[3]={RELAY1,RELAY2,RELAY3};

WiFiUDP Udp;
ESP8266WebServer server(80);

int detect_relay(String p_uri) {
  if (p_uri.indexOf("1")>0) {
    return 0;
  } else if (p_uri.indexOf("2")>0) {
    return 1;
  } else if (p_uri.indexOf("3")>0) {
    return 2;
  }
  return 0;
}

void handleRoot() {
  //digitalWrite(RELAY, 1);
  server.send(200, "text/html", "<html><head><title>SWITCH "  devname1 devname2 devname3  "</title></head><body>PRESS<br>1&nbsp;<a href=\"/on1\">ON</a>&nbsp;<a href=\"/off1\">OFF</a><br><br>PRESS<br>2&nbsp;<a href=\"/on2\">ON</a>&nbsp;<a href=\"/off2\">OFF</a><br><br>PRESS<br>3&nbsp;<a href=\"/on3\">ON</a>&nbsp;<a href=\"/off3\">OFF</a></body></html>");
  //digitalWrite(RELAY, 0);
}

void handleON() {
  int id=detect_relay(server.uri());
  digitalWrite(relay[id], 1);

    server.send(200, "text/html", "<html><head><title>SWITCH "  devname1 devname2 devname3  "</title></head><body>PRESS<br>1&nbsp;<a href=\"/on1\">ON</a>&nbsp;<a href=\"/off1\">OFF</a><br><br>PRESS<br>2&nbsp;<a href=\"/on2\">ON</a>&nbsp;<a href=\"/off2\">OFF</a><br><br>PRESS<br>3&nbsp;<a href=\"/on3\">ON</a>&nbsp;<a href=\"/off3\">OFF</a></body></html>");

  Serial.print("Device N ");
  Serial.print(id);
  Serial.println(" ON");
  onoff_state[id]=1;
  force_sendstate=1;

}

void handleOFF() {

  int id=detect_relay(server.uri());

    server.send(200, "text/html", "<html><head><title>SWITCH "  devname1 devname2 devname3  "</title></head><body>PRESS<br>1&nbsp;<a href=\"/on1\">ON</a>&nbsp;<a href=\"/off1\">OFF</a><br><br>PRESS<br>2&nbsp;<a href=\"/on2\">ON</a>&nbsp;<a href=\"/off2\">OFF</a><br><br>PRESS<br>3&nbsp;<a href=\"/on3\">ON</a>&nbsp;<a href=\"/off3\">OFF</a></body></html>");
  Serial.print("Device N ");
  Serial.print(id);
  Serial.println(" OFF");
  digitalWrite(relay[id], 0);
  onoff_state[id]=0;
  force_sendstate=1;
}





void handleNotFound() {

  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++) { message += " " + server.argName(i) + ": " + server.arg(i) + "\n"; }
  server.send(404, "text/plain", message);

}




void setup() {
  String mip;
  String mmc;
  pinMode(RELAY1,OUTPUT);
  pinMode(RELAY2,OUTPUT);
  pinMode(RELAY3,OUTPUT);
  //pinMode(IN,INPUT);
  onoff_state1=0;
  onoff_state2=0;
  force_sendstate=0;
  Serial.begin(115200);
  Serial.println("\r\nSystem boot.");
  Serial.println("Device name "  devname1 devname2 devname3);
  WiFi.mode(WIFI_STA);
  WiFi.begin(STASSID, STAPSK);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(50);
  }
  Serial.println("OK");
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println(WiFi.broadcastIP());
  mip=WiFi.localIP().toString();
  Serial.println(WiFi.macAddress());
  mmc=WiFi.macAddress();
  mmc.replace(":","-");
  Serial.printf("UDP server on port %d\n", localPort);
  Udp.begin(localPort);
  
  configTime(MYTZ, "pool.ntp.org");
  t_mil=millis();
  is_start=0;
  if (MDNS.begin("tv3sock")) { Serial.println("MDNS responder started"); }

  server.on("/", handleRoot);

  server.on("/on1", handleON);
  server.on("/off1", handleOFF);
  server.on("/on2", handleON);
  server.on("/off2", handleOFF);
  server.on("/on3", handleON);
  server.on("/off3", handleOFF);
  server.on("/inline", []() {
    server.send(200, "text/plain", "this works as well");
  });
  server.onNotFound(handleNotFound);
  server.begin();
  Serial.println("HTTP server started");
}

void loop() {
  server.handleClient();
  MDNS.update();
  // if there's data available, read a packet
  packetSize = Udp.parsePacket();
  if (packetSize) {
    Serial.printf("Received packet of size %d from %s:%d\n    (to %s:%d, free heap = %d B)\n", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort(), Udp.destinationIP().toString().c_str(), Udp.localPort(), ESP.getFreeHeap());

    // read the packet into packetBufffer
    int n = Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
    packetBuffer[n] = 0;
    Serial.println("Contents:");
    Serial.println(packetBuffer);

    //for (int i=0;i<3;i++) {
      if (strstr(packetBuffer,"device:" devname1) != NULL) {
        if (strstr(packetBuffer,"action:on_off") != NULL) {
          if (strstr(packetBuffer,"value:on") != NULL) {
            onoff_state[0]=1;
            digitalWrite(RELAY1,1);
          } else {
            onoff_state[0]=0;
            digitalWrite(RELAY1,0);
          }
        }
      }
      if (strstr(packetBuffer,"device:" devname2) != NULL) {
        if (strstr(packetBuffer,"action:on_off") != NULL) {
          if (strstr(packetBuffer,"value:on") != NULL) {
            onoff_state[1]=1;
            digitalWrite(RELAY2,1);
          } else {
            onoff_state[1]=0;
            digitalWrite(RELAY2,0);
          }
        }
      }
      if (strstr(packetBuffer,"device:" devname3) != NULL) {
        if (strstr(packetBuffer,"action:on_off") != NULL) {
          if (strstr(packetBuffer,"value:on") != NULL) {
            onoff_state[2]=1;
            digitalWrite(RELAY3,1);
          } else {
            onoff_state[2]=0;
            digitalWrite(RELAY3,0);
          }
        }
      }
    //}
    
    if (is_start==0) {
      Udp.write("devstate:" devname1 ":on_off:on\r\n");
      Udp.write("devstate:" devname2 ":on_off:on\r\n");
      Udp.write("devstate:" devname3 ":on_off:on\r\n");
    }
    if (millis()-t_mil > 100000) {
      t_mil=millis();
      Udp.beginPacket(WiFi.broadcastIP(), 9009);
        if (onoff_state[0]==0) {
      Udp.write("devstate:" devname1 ":on_off:off\r\n");
        } else {
      Udp.write("devstate:" devname1 ":on_off:on\r\n");
        }
      Udp.endPacket();
      Udp.beginPacket(WiFi.broadcastIP(), 9009);
        if (onoff_state[1]==0) {
      Udp.write("devstate:" devname2 ":on_off:off\r\n");
        } else {
      Udp.write("devstate:" devname2 ":on_off:on\r\n");
        }
      Udp.endPacket();
      Udp.beginPacket(WiFi.broadcastIP(), 9009);
        if (onoff_state[2]==0) {
      Udp.write("devstate:" devname3 ":on_off:off\r\n");
        } else {
      Udp.write("devstate:" devname3 ":on_off:on\r\n");
        }
      Udp.endPacket();
      Serial.println("SEND state");
    }

    // send a reply, to the IP address and port that sent us the packet we received
    //Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    //Udp.write(ReplyBuffer);
    //Udp.endPacket();
  }
  if (force_sendstate==1) {
    Udp.beginPacket(WiFi.broadcastIP(), 9009);
        if (onoff_state[0]==0) {
      Udp.write("devstate:" devname1 ":on_off:off\r\n");
        } else {
      Udp.write("devstate:" devname1 ":on_off:on\r\n");
        }
      Udp.endPacket();
      Serial.println("SEND state");
    Udp.beginPacket(WiFi.broadcastIP(), 9009);
        if (onoff_state[1]==0) {
      Udp.write("devstate:" devname2 ":on_off:off\r\n");
        } else {
      Udp.write("devstate:" devname2 ":on_off:on\r\n");
        }
      Udp.endPacket();
      Udp.beginPacket(WiFi.broadcastIP(), 9009);
        if (onoff_state[2]==0) {
      Udp.write("devstate:" devname3 ":on_off:off\r\n");
        } else {
      Udp.write("devstate:" devname3 ":on_off:on\r\n");
        }
      Udp.endPacket();
      Serial.println("SEND force state");
      force_sendstate=0;
  }
  if (is_start==0) {
    Serial.println("INIT state send done");
      
      
      Udp.beginPacket(WiFi.broadcastIP(), 9009);
      digitalWrite(RELAY1,1);
      Udp.write(devstate1_on);
      Udp.endPacket();
      
      Udp.beginPacket(WiFi.broadcastIP(), 9009);
      digitalWrite(RELAY2,1);
      Udp.write(devstate2_on);
      Udp.endPacket();
      
      Udp.beginPacket(WiFi.broadcastIP(), 9009);
      digitalWrite(RELAY3,1);
      Udp.write(devstate3_on);
      Udp.endPacket();
      is_start=1;
  }
  delay(200);
}

/*
  test (shell/netcat):
  --------------------
    nc -ub 192.168.esp.address 9009
*/

Как то уж больно немногословно ))

Вот подробней: “Покупайте наших слонов!”(с)

2 лайка

Нет. Можете сами сделать.

Завтра выложу схему и перечень деталей.

Схема подключения.

Для работы с Алисой нужен шлюз. В основной части это iptv приставка Smartlab. Стоимость около 500руб в маркетах. Прошивка приставки тут - Загрузки - Чипльдуплекс .
Но один из форумчанинов реализовал шлюз на esp32. Чуть позже разместим.

Если не углубляться в детали, то пока Шлюз будет “черным ящиком”. Он выдает в сеть пакеты для всех устройств сети с командами. Так же шлюз “ловит” пакеты от устройств сети и отправляет их на сервер. Данные пакеты содержат состояние устройства, которое отображается в интерфейсе приложения Умный дом с Алисой.

Команда от Алисы - это пакет UDP по broadcast адресу на порт 9009 в виде -
device:devname: action:actionname: value:val.
devname - имя устройства,
actionname - команда,
val - значение.

Передача состояния устройства
devstate:devname: controlname: controlvalue
devname - имя устройства,
controlname - имя умения,
controlvalue - значение

Компоненты.

Wemos D1 mini, 3 твердотельных реле OMRON OMRON G3MB-202P, блок питания YS-12V400-B.

Все влезло в корпус блока розеток (производитель неизвестен), который можно подобрать в любом магазине электрики. Провод питания удлинителя можно любой использовать.

В моем случае я сделал 4 розетки. Одна розетка без управления (под большую нагрузку), а 3 с управлением с нагрузкой до 2Ампер.

По алгоритму.

При старте:

  • установка режимов работы GPIO
  • получение broadcast адреса
  • иницилизация порта UDP
  • иницилизация WEB сервера

LOOP:

  • проверка прихода пакета UDP и выяснение имени устройства
  • проверка времени таймаута и отправка состояния
  • проверка изменения состояния и отправка состояния

Как это выглядит в приложении.

Уж очень всё сложно в данном проекте. Какие то приставки перепрограммировать надо. Хоть вы и говорите что дёшего получается, но на Tasmote по Matter (ESP32) детали те же, только всё напрямую в Алису. И этот путь самый простой для создания УД на сегодня. Стоимость 160 рублей за устройство. Хоть 10 и более реле подключай и управляй голосом через Алису.

Баловство всё это, игрушки. Примерно как “Часы на Ардуино”.
Гениальней простого выключателя ещё не изобрели.

Ага, а посылать выключателем клацнуть кого? ))

Это как? Ну включили за тебя свет в туалет, а дальше? Нужду по вайфаю справлять?:grinning_face_with_smiling_eyes:
В спальне можно и простенькую люстру с ИК-пультом.
Кстати, дети это лучший “умный дом”. Вот какая ещё электроника может помыть посуду, расставить её, вынести мусор(который ещё собрать надо) и потопчет спину?

Любимую дочку или внучку Алису))

Учтите, что для этого нужно иметь, как минимум Миди станцию. В моем случае Лайт.

Ну так Яндекс лайт станцию за 5к рублей по любому надо иметь для ввода голосовых команд. Это основа УД.

В том то и гениальность, что с одной стороны полный функционал, а с другой стороны индикация. Но! Перенести выключатель такой всегда достаточно сложно. У меня стоят выключатели механические и они включены, когда управляется Алисой. Я не менял проводку.