Banner publicitario de PCBWay

Configuración UG65 Milesight + UC1114 LoRaWAN desde cero

torres.electronico

Well-known-Alfil
Tengo un UG65 Milesight y un UC1114, ambos comunican por LoRaWAN, y hay tan poca informacion potable dando vuelta, que no pude sacarle las telas de araña por estar tirados... Como ya comente anteriormente hace ya bastante tiempo que me estoy rompiendo la cabeza con el UG65, el UC114 (de dos entradas y dos salidas digitales) de la empresa Milesight y un Arduino Mega con el shield Ethernet W5100. Es tanta la frustración que le pego uno o dos días a pleno y después vuelven a sus cajas a empolvarse por otro tiempo mas...
Hay dos manuales del equipo en la red y ninguno te da la información completa para exprimir y sacarle todo el jugo al hardware. Luego de varios intentos, logré la comunicación entre los dos Lora, pero no logro sumar la configuración del puerto Ethernet para comunicar con el W5100. Como no guarde / hice backup y de yapa lo estoy agarrando cada tanto tiempo, es prácticamente como arrancar de cero por que no me acuerdo como configure para poder emparejar el UG con el UC.
En el caso de la comunicación vía internet con el w5100+Mega funciona bien usándolo directamente desde el modem de movistar, pero no logro la vinculación con el puerto de ethernet del UG65 y por ende, comunicar todo junto y es ahí donde ya te dan ganas de agarrar todo y tirarlo por la borda.
Recicle un viejo programa (Xscada-Vulkan) y estoy armando un ejemplo para el foro de control y monitoreo a distancia, pero si no encuentro una mano de dios que me oriente o de cachetazo en la nuca para despertarme, creo que de nuevo va a parar a sus respectivas cajas...
Alguien por casualidad trabajo con estos equipos?
 
Tengo un UG65 Milesight y un UC1114, ambos comunican por LoRaWAN, y hay tan poca informacion potable dando vuelta, que no pude sacarle las telas de araña por estar tirados... Como ya comente anteriormente hace ya bastante tiempo que me estoy rompiendo la cabeza con el UG65, el UC114 (de dos entradas y dos salidas digitales) de la empresa Milesight y un Arduino Mega con el shield Ethernet W5100. Es tanta la frustración que le pego uno o dos días a pleno y después vuelven a sus cajas a empolvarse por otro tiempo mas...
Hay dos manuales del equipo en la red y ninguno te da la información completa para exprimir y sacarle todo el jugo al hardware. Luego de varios intentos, logré la comunicación entre los dos Lora, pero no logro sumar la configuración del puerto Ethernet para comunicar con el W5100. Como no guarde / hice backup y de yapa lo estoy agarrando cada tanto tiempo, es prácticamente como arrancar de cero por que no me acuerdo como configure para poder emparejar el UG con el UC.
En el caso de la comunicación vía internet con el w5100+Mega funciona bien usándolo directamente desde el modem de movistar, pero no logro la vinculación con el puerto de ethernet del UG65 y por ende, comunicar todo junto y es ahí donde ya te dan ganas de agarrar todo y tirarlo por la borda.
Recicle un viejo programa (Xscada-Vulkan) y estoy armando un ejemplo para el foro de control y monitoreo a distancia, pero si no encuentro una mano de dios que me oriente o de cachetazo en la nuca para despertarme, creo que de nuevo va a parar a sus respectivas cajas...
Alguien por casualidad trabajo con estos equipos?

¿Pudiste vincular el W5100? Tanto el UG65 y el W5100 debes configurarlo con el DHCP, la biblioteca Ethernet.h tiene una función para habilitar DHCP. También deberás ingresar la máscara de subred correcta y la puerta de enlace predeterminada.

Para una configuración de IP estática, debes establecer una dirección IP para el UG65 que esté en la misma red que el W5100 pero que tenga una dirección IP diferente, y luego haces el ping a la dirección IP del W5100 desde la línea de comando de la puerta de enlace, para verificar que la conexión se haya establecido.

Bruja Kitana.png
 
Última edición:
¿Pudiste vincular el W5100? Tanto el UG65 y el W5100 debes configurarlo con el DHCP, la biblioteca Ethernet.h tiene una función para habilitar DHCP. También deberás ingresar la máscara de subred correcta y la puerta de enlace predeterminada.

Para una configuración de IP estática, debes establecer una dirección IP para el UG65 que esté en la misma red que el W5100 pero que tenga una dirección IP diferente, y luego haces el ping a la dirección IP del W5100 desde la línea de comando de la puerta de enlace, para verificar que la conexión se haya establecido.

Ver el archivo adjunto 334016


¿Pudiste vincular el W5100? Tanto el UG65 y el W5100 debes configurarlo con el DHCP, la biblioteca Ethernet.h tiene una función para habilitar DHCP. También deberás ingresar la máscara de subred correcta y la puerta de enlace predeterminada.

Para una configuración de IP estática, debes establecer una dirección IP para el UG65 que esté en la misma red que el W5100 pero que tenga una dirección IP diferente, y luego haces el ping a la dirección IP del W5100 desde la línea de comando de la puerta de enlace, para verificar que la conexión se haya establecido.

Buenas, recién veo tu mensaje y ya estoy desempolvando todo para ponerme de nuevo con este tema...
En su momento no pude vincular el W5100. Lo que hice fue conectar vía wifi el UG65 a mi red hogareña , y el w5100 busque ponerle una IP fija dentro del rango del UG65... El uc1114 no fue problema la vinculación, por que no depende de la configuración de red, ya que se comunican vía LoRaWAN y solo hay que configurar la frecuencia y canal vía software desde el UC y después en el UG65... Lo que no entendí, es por que no me figura en el monitor de ethernet del UG65 el W5100 y después, como encontrar de donde sacar las señales del UC11XX para usarlas en el dashboard que quiero hacer en el w5100 con arduino y en paralelo, en XSCADA. Este ultimo no me preocupa tanto; Primero necesito establecer conexión interna entre el UG65, el UC11XX y W5100... el resto seguro vendrá por decantación, metiendo mano y haciendo macanas :ROFLMAO:
Voy a re instalar los software y este fin de semana arranco de cero con todo esto. La verdad, es la primera vez que me encuentro con un equipo con muy poca informacion técnica :cry:
 
Bien, vamos con los avances 😶
primero, mediante la conexion a ethernet configuramos el UG65 para conectarlo a nuestra red wifi... aca un simple video del paso a paso:


una vez configurado nuestro UG65, entramos via WIFI a su configuración. En mi caso configure una IP estática (192.168.1.200) para no tener problemas con los dispositivos que sume en el futuro... Si no hicieron eso al seguir los pasos del video anterior, tienen que entrar en la dirección 192.168.1.1.
Dentro ya de la pantalla de configuración, nos vamos a la opción Network Server

config_networkserver_1.png

ahi nos dirigimos a la solapa "Aplications" y en el signo "+", vamos a crear una instancia...

config_networkserver_2.png

Yo la llame UC1114 ... Como voy a trabajar MQTT, en "Payload Codec", tenemos que seleccionar la opcion "Custom" y dentro de
Payload decoder function, vamos a pegar las siguientes lineas de comandos que luego las implementaremos para decodificar el hex que envia el UC1114:

Código:
// Decoder UC1114 - 2 DI + 2 DO
function Decode(fPort, bytes) {
    // bytes[8] → estado de entradas digitales
    // bytes[10] → estado de salidas digitales (relés)
    return {
        DI1:  (bytes[8] & 0x01) ? 1 : 0,
        DI2:  (bytes[8] & 0x02) ? 1 : 0,
        DO1:  (bytes[10] & 0x01) ? 1 : 0,
        DO2:  (bytes[10] & 0x02) ? 1 : 0
    };
}

config_networkserver_2a.png

En Payload encoder function, vamos a pegar las siguientes lineas:

Código:
// Encode permite enviar comandos desde el UG65 al UC1114
// obj puede tener {DO1:1, DO2:0} por ejemplo
function Encode(fPort, obj) {
    var bytes = new Array(11).fill(0); // Creamos arreglo de 11 bytes inicializados en 0
    if(obj.DO1 !== undefined) {
        if(obj.DO1) bytes[10] |= 0x01; // Activar DO1
        else bytes[10] &= ~0x01;       // Desactivar DO1
    }
    if(obj.DO2 !== undefined) {
        if(obj.DO2) bytes[10] |= 0x02; // Activar DO2
        else bytes[10] &= ~0x02;       // Desactivar DO2
    }
    return bytes;
}

config_networkserver_2b.png
En la seccion "Data Transmission", le damos click al signo "+" y seleccionamos "MQTT" y configuramos la dirección broker, el puerto y el ID del cliente (el UG65)

config_networkserver_2c.png

después, referenciamos con las siguientes direcciones las siguientes variables

config_networkserver_2d.png
No se olviden de salvar cada cambio que hagan ;) terminado este paso, nos vamos a "Profiles" y creamos con el signo "+" un perfil nuevo (en mi caso UC1114-915_OTAA)

config_networkserver_3.png

config_networkserver_3a.png

Realizada la anterior configuración y almacenada, nos vamos a la opción "Device"

config_networkserver_4.png

presionamos el signo "+" para agregar nuestro dispositivo LoRaWAN (en este caso el UC1114)... Para hacer esto, van a requerir tener a mano el numero de Device EUI, App EUI y App KEY... Si no lo tienen en la etiqueta o cartoncito de la caja, pueden implementar "Aplicación Milesight Toolbox " que por medio de un cable USB se conectan al UC1114...

config_networkserver_4a.png

Cuando terminemos de configurar todo, recuerden salvar y reinicien los dos equipos.... Volvemos a entrar y desde "Gateway" tendriamos que poder visualizar que esta conectado

config_networkserver_5.png

para verificar que hay vinculación con el UC1114, nos vamos a "Packets" y ya tendríamos que poder ver los paquetes... que igualmente, esto no significa que tengamos datos validos.... eso ya lo vamos a ver después

config_networkserver_6.png

Hasta acá, venimos bien hasta que me di cuenta que no tenia comunicación... Probé de todo, y nada... navegando y navegando con San Google, pude entender "parte" del por que no tenia comunicación vamos por partes para que se entienda...

A_ Por qué el Arduino Uno con W5100 no funciono?
Quizás me equivoque, por que algunas cosas las interpreto por descarte, otras por conocimientos básicos, y otra gracias a San Google... la idea original de implementar la conexión del Arduino y el UG65 via ethernet no funciono por

1_Capacidad de memoria y recursos limitados:
*Arduino Uno tiene solo 2 KB de RAM y 32 KB de flash... Manejar librerías de MQTT y HTTP simultáneamente, más buffers para el WebServer, excede fácilmente los recursos... Cuando compile una de las primeras betas, el IDE me indicaba que el programa tomaba un porcentaje elevado de los recursos de la ram y podria darme errores...
2_TLS y MQTT:
*Muchos gateways LoRaWAN como el UG65 soportan MQTT con TLS (seguridad SSL/TLS), y en una foro extranjero me vengo a enterar que el W5100 no soporta de forma nativa TLS ni librerías modernas de MQTT con SSL. Implementarlo iba a requerir un MCU más potente... Quizas un MEGA2560, pero para la funcion que le iba a dar, estime que era un derroche de plata...
*El ESP8266 y ESP32 tienen hardware suficiente y librerías (PubSubClient, AsyncTCP, AsyncWebServer) que soportan estas funciones de manera sencilla y son mucho mas economicos....

3_Conectividad Wi-Fi:
*Controversias mías :rolleyes: ... Arduino Uno + W5100 solo puede conectarse por Ethernet, mientras que en la práctica yo quería conectar el UG65 vía Wi-Fi y tener varios ESPs, mas el LoRaWAN en la misma red... Usar el hardware cableado me iba a demandar sumar una caja IP65, prensa cables especiales, etc etc... Iba a ser mas complicado y después me puse a pensar que el ESP8266/ESP32 ya tiene Wi-Fi integrado y se puede conectar al router o a la red Wi-Fi del UG65; Así que acá re arranque nuevamente...

Después de poder llegar a esta conclusión, tome un ESP8266 y arranque desde cero con el siguiente sketch:

CSS:
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
// ------------------- CONFIGURACION WIFI -------------------
const char* ssid = "blablablabla";
const char* password = "blablabla";
// IP estática
IPAddress local_IP(192, 168, 1, 36);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
// ------------------- CONFIGURACION DEL SERVIDOR -------------------
AsyncWebServer server(80);
// entradas y salidas
int DI1 = 0;
int DI2 = 0;
int DO1 = 0;
int DO2 = 0;

// HTML del dashboard
const char htmlPage[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
  <title>UG65_ESP8266_UC1114_dashboard</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    body { font-family: Arial; text-align:center; }
    button { width:120px; height:50px; font-size:16px; margin:10px; }
    .circle { width:50px; height:50px; border-radius:50%; display:inline-block; margin:10px; }
    .red { background:red; }
    .green { background:green; }
  </style>
</head>
<body>
  <h2>UC1114 Dashboard</h2>
  <div>
    <button onclick="toggleDO('DO1',1)">Activar DO1</button>
    <button onclick="toggleDO('DO1',0)">Desactivar DO1</button>
  </div>
  <div>
    <button onclick="toggleDO('DO2',1)">Activar DO2</button>
    <button onclick="toggleDO('DO2',0)">Desactivar DO2</button>
  </div>
  <h3>Entradas digitales</h3>
  <div>
    <div>DI1: <div id="di1" class="circle red"></div></div>
    <div>DI2: <div id="di2" class="circle red"></div></div>
  </div>

<script>
function toggleDO(doName,state) {fetch("/set?"+doName+"="+state);}

function updateInputs()
  {
  fetch("/status")
    .then(response => response.json())
    .then(data => {
      document.getElementById("di1").className = "circle " + (data.DI1==1?"green":"red");
      document.getElementById("di2").className = "circle " + (data.DI2==1?"green":"red");
    });
  }

setInterval(updateInputs, 1000);
</script>
</body>
</html>
)rawliteral";

// ------------------- FUNCIONES -------------------
void setup()
  {
   Serial.begin(115200);
   // Conectar a Wi-Fi
   Serial.println("Conectando a Wi-Fi...");
   if (!WiFi.config(local_IP, gateway, subnet))
     {
      Serial.println("Fallo al configurar IP estática");
     }
   WiFi.begin(ssid, password);
   while (WiFi.status() != WL_CONNECTED)
       {
       delay(500);
       Serial.print(".");
       }
   Serial.println("\nConectado a Wi-Fi!");
   Serial.print("IP del ESP8266: ");
   Serial.println(WiFi.localIP());

   // Servir dashboard
   server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
             request->send_P(200, "text/html", htmlPage);
            });

  // Endpoint para cambiar DO1/DO2
  server.on("/set", HTTP_GET, [](AsyncWebServerRequest *request)
      {
    if(request->hasParam("DO1"))
          {
      DO1 = request->getParam("DO1")->value().toInt();
      Serial.print("DO1 cambiado a "); Serial.println(DO1);
         }
    if(request->hasParam("DO2"))
          {
      DO2 = request->getParam("DO2")->value().toInt();
      Serial.print("DO2 cambiado a "); Serial.println(DO2);
          }
    request->send(200, "text/plain", "OK");
  });

  // Endpoint para enviar estado de entradas DI1 y DI2
  server.on("/status", HTTP_GET, [](AsyncWebServerRequest *request){
    String json = "{\"DI1\":" + String(DI1) + ",\"DI2\":" + String(DI2) + "}";
    request->send(200, "application/json", json);
  });

  server.begin();
  Serial.println("Servidor web iniciado");
}

void loop()
  {
  // Aquí deberías leer los estados reales del UC1114 vía UG65
  // Por ahora solo simulamos que cambian aleatoriamente (para test)
  static unsigned long lastUpdate = 0;
  if (millis() - lastUpdate > 5000)
     {
      DI1 = random(0,2);
      DI2 = random(0,2);
      lastUpdate = millis();
      Serial.printf("DI1: %d | DI2: %d | DO1: %d | DO2: %d\n", DI1, DI2, DO1, DO2);
     }
  }

y logre un pequeño avance :silbando: ... avance a medias por que solo pude ver el estado de las entradas, pero no pude tener control sobre las dos salidas del UC1114...

dashboard_beta.png

Por qué el sketch ESP8266 funcionó para leer entradas?
Esto me llevo practicamente 3 horas descubrirlo mirando ejemplos y googleando por varios foros... El sketch inicial del ESP8266 implementa un WebServer con endpoints /status.
La trampa que hice en si momento fue simular la lectura de DI1 y DI2:

CSS:
server.on("/status", HTTP_GET, [](AsyncWebServerRequest *request){
  String json = "{\"DI1\":" + String(DI1) + ",\"DI2\":" + String(DI2) + "}";
  request->send(200, "application/json", json);
});

Con esto, pude lograr que el dashboard mostrara el estado de las entradas con los círculos rojos/verde... O sea, la comunicación es directa vía HTTP GET desde el navegador al ESP, por lo que no depende de MQTT ni del UG65 para esta visualización básica... Como desvirtué la idea original, tome la decisión de descartarlo para la otra beta aunque funciono.

Por qué no pude activar los relés desde el dashboard
Falto la integración MQTT o enlace con UG65... El endpoint /set solo cambia variables locales DO1 y DO2 dentro del ESP.
Como no hay ninguna funcion incluida que convierta estas acciones en comandos hacia el UC1114 a través del UG65, por mas que el botón funciona en el dashboard, el UC1114 nunca recibe la orden.... Probé de todo después de descular esto y persistió el mismo problema...
Interpreto que el error esta en el Payload y formato de comando del UC1114:
A_El UC1114 espera un payload en un formato específico (hex/base64) a través del UG65 por MQTT o LoRaWAN.
B_Sin enviar el mensaje correcto a uc1114/down con la codificación requerida, el UC1114 no se va a mosquear...

Necesidad de PubSubClient y broker MQTT:
*Para enviar comandos reales, el ESP debe conectarse a un broker MQTT al que también esté conectado el UG65.
*Luego, el ESP publica los mensajes codificados hacia los topics que el UC1114 escucha.
*Sin esta capa de comunicación, los relés no cambian de estado.

Por donde sigo?
Buena pregunta... como primera instancia, cambiar la yerba del mate y trabajar desde cero en la configuracion del UG65 como cliente MQTT conectado a un broker local.
Tengo que ver como definir los topics de uplink y downlink (uc1114/up y uc1114/down) correctamente.
Trabajar y pulir en el ESP8266 de como publicar los comandos de los botones hacia el broker MQTT, pero para eso me esta faltando conocimientos para codificar DO1 y DO2 en base64/hex según formato UC1114; Subscribirse a uc1114/up para recibir cambios de DI1/DI2 y actualizar los círculos en tiempo real. (actualmente tiene un lag de casi un segundo)... O sea, hasta acá llegaron mis neuronas... y seguro que si no consigo una orientación, vuelve a la caja :facepalm:...
 

Adjuntos

  • config_networkserver_3a.png
    config_networkserver_3a.png
    48.7 KB · Visitas: 0
Estuve quemando pestañas y dándome la cabeza contra el teclado :facepalm: probando varias cosas que sinceramente, no sabia que el equipo tenia algunas capacidades en lo que respecta IOT vía WLan... Es un equipo que tiene sus cosas buenas y algunas, quizás dejan algo que desear... En ese trayecto, me encontré con un par de bug´s y eso me llevo a googlear un poco mas profundo y llegar a conseguir un poco mas de material de soporte para meter mano.
Lo primero que tuve que hacer, fue un UpGRADE del firmware que se descarga desde la pagina oficial .
De la versión 1.0.4, salte a la versión 1.1.14 y el cambio ya es notable en cuanto a funcionalidades... Había un firmware mas nuevo para actualizar, pero preferí quedarme en el medio por que la fecha de publicación del ultimo firmware, es de hace tan solo unos meses y no quería renegar con betas. En paralelo, encontré el repositorio de Milesight donde había ejemplos de como trabajar la sección de encoder y decoder del payload, pero para mi sorpresa, cuando inicie la programación en el nuevo firmware, este ya viene con la opción de precargar plantillas de equipos lora de usarlink preinstaladas, lo cual te lleva a ahorrar un 60% del trabajo... Ahora, tanto el firmware viejo, como el nuevo que instale, tiene un error grande de actualización del broker mqtt, ósea, mejor dicho seria "el tiempo mínimo" de refresco que es de 10 segundos (una eternidad o_O ) y el mismo sistema no te permite modificarlo para que sea un tiempo menor... Busque si había una opción de disparo de evento por registros de cambios y no hay nada... Cual seria el problema en todo esto? bien, por un lado todos los eventos en los ID programados se guardan en un registro que luego envía cuando despierta, pero en esos 10 segundos se pierde la conexión con mosquitto y en el caso del dashboard, si no hacemos una lógica de banderas, en el ejemplo que arme, los led se apagan sin importar el estado del relay o de los dos pulsadores en el UC1114...Trabaje un poco la lógica del dashboard y mejoro apenas un poco, pero el problema persiste.... Este es el ejemplo en el esp8266 del dashboard:

CSS:
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

// ------------------- CONFIGURACION WIFI -------------------
const char* ssid = "M-------";
const char* password = "------------";
const char* mqtt_server = "192.168.1.35"; //la ip local de mi notebook donde tengo instalado mosquitto
const int   mqtt_port = 1883;

// Tópicos MQTT DEL BROKER/UG65
const char* topic_downlink = "application/UC1114/device/24e------------/downlink";
const char* topic_uplink   = "application/UC1114/device/24e------------2/uplink";

ESP8266WebServer server(80);
WiFiClient espClient;
PubSubClient client(espClient);

// Variables de estado globales
int input1_state = -1;
int input2_state = -1;
int relay1_state = -1;
int relay2_state = -1;

// ------- FUNCION PARA ENVIAR COMANDOS MQTT (HEX) -------
void sendRelayCommand(int relay, bool state)
 {
  if (relay == 1)
    {
    client.publish(topic_downlink, state ? "0901" : "0900"); // ON=0901, OFF=0900
    }
     else if (relay == 2)
    {
    client.publish(topic_downlink, state ? "0A01" : "0A00"); // ON=0A01, OFF=0A00
    }
}

// ------- ENDPOINT PARA COMANDOS AJAX DE LA WEB -------
void handleCommand() {
  if (server.hasArg("relay") && server.hasArg("state")) {
    int relay = server.arg("relay").toInt();
    bool state = server.arg("state") == "1";
    sendRelayCommand(relay, state);
    server.send(200, "text/plain", "OK");
  } else {
    server.send(400, "text/plain", "Bad Request");
  }
}

// ------- DASHBOARD HTML -------
void handleRoot()
  {
  String html =
    "<!DOCTYPE html><html><head><meta charset='UTF-8'><title>UC1114/UG65/ESP8266 Dashboard prof.martintorres@educ.ar</title>"
    "<style>"
    "body{font-family:Arial;background:#f6f6f6;}"
    ".panel{background:#fff;max-width:430px;margin:30px auto;padding:18px;border-radius:10px;box-shadow:0 0 12px #aaa;text-align:center;}"
    ".led{display:inline-block;width:18px;height:18px;border-radius:50%;margin:0 8px 0 0;vertical-align:middle;box-shadow:0 0 4px #999;}"
    ".red{background:#c53232;}"
    ".green{background:#56c232;}"
    ".btop{width:145px;height:42px;border:none;border-radius:7px;font-size:15px;font-weight:bold;margin:6px 10px;cursor:pointer;transition:.2s;}"
    ".on{background:#29D649;color:#fff;}"
    ".off{background:#bbb;color:#222;}"
    "</style></head><body>"
    "<div class='panel'>"
    "<h2>UC1114/UG65/ESP8266 Dashboard</h2>"
    "<div style='margin-bottom:26px;'>"
    "<span class='led' id='led_in1'></span>Entrada 1&nbsp;&nbsp;"
    "<span class='led' id='led_in2'></span>Entrada 2"
    "</div>"
    "<h3>Control Relé 1</h3>"
    "<button class='btop on' onclick=\"comando(1,1)\">Relé 1 ON</button>"
    "<button class='btop off' onclick=\"comando(1,0)\">Relé 1 OFF</button><br>"
    "Estado: <span id='estado_r1'></span>"
    "<h3>Control Relé 2</h3>"
    "<button class='btop on' onclick=\"comando(2,1)\">Relé 2 ON</button>"
    "<button class='btop off' onclick=\"comando(2,0)\">Relé 2 OFF</button><br>"
    "Estado: <span id='estado_r2'></span>"
    "</div>"
    "<script>"
    "function comando(r,s){fetch('/cmd?relay='+r+'&state='+s);}"
    "function actualizar(){"
    " fetch('/status').then(r=>r.json()).then(d=>{"
    "  document.getElementById('led_in1').className='led '+(d.input1==1?'green':'red');"
    "  document.getElementById('led_in2').className='led '+(d.input2==1?'green':'red');"
    "  document.getElementById('estado_r1').innerHTML='<span class=\"led '+(d.relay1==1?'green':'red')+'\"></span>'+(d.relay1==1?'ON':'OFF');"
    "  document.getElementById('estado_r2').innerHTML='<span class=\"led '+(d.relay2==1?'green':'red')+'\"></span>'+(d.relay2==1?'ON':'OFF');"
    " });"
    "}"
    "setInterval(actualizar,1000);actualizar();"
    "</script></body></html>";
  server.send(200, "text/html", html);
}

// ------- DEVUELVE ESTADO JSON PARA AJAX -------
void handleStatus()
 {
  String resp = "{\"input1\":" + String(input1_state) +
                ",\"input2\":" + String(input2_state) +
                ",\"relay1\":" + String(relay1_state) +
                ",\"relay2\":" + String(relay2_state) + "}";
  server.send(200, "application/json", resp);
}

// ------- CALLBACK MQTT: RECIBE Y PROCESA JSON -------
void callback(char* topic, byte* payload, unsigned int length)
{
  Serial.print("MQTT recibido en topic: "); Serial.println(topic);
  for(unsigned int i=0; i<length; i++) Serial.print((char)payload[i]);
  Serial.println();
  if (String(topic) == topic_uplink)
    {
    StaticJsonDocument<256> doc;
    DeserializationError error = deserializeJson(doc, payload, length);
    if (!error)
      {
      input1_state  = doc.containsKey("gpio_input_1") ? doc["gpio_input_1"] : -1;
      input2_state  = doc.containsKey("gpio_input_2") ? doc["gpio_input_2"] : -1;
      relay1_state  = doc.containsKey("gpio_output_1") ? doc["gpio_output_1"] : -1;
      relay2_state  = doc.containsKey("gpio_output_2") ? doc["gpio_output_2"] : -1;
      Serial.print("Entradas: "); Serial.print(input1_state); Serial.print(", "); Serial.println(input2_state);
      Serial.print("Relés: "); Serial.print(relay1_state); Serial.print(", "); Serial.println(relay2_state);
      }
      else
      {
      Serial.println("Error al deserializar JSON MQTT");
      }
   }
}

// ------- CONEXION WIFI -------
void setup_wifi()
  {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); }
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  }

// ------- RECONEXION AUTOMATICA MQTT -------
void reconnect()
  {
  while (!client.connected())
   {
    Serial.print("Conectando a MQTT...");
    if (client.connect("ESP8266Dash"))
      {
      client.subscribe(topic_uplink);
      Serial.print("Suscripto a: "); Serial.println(topic_uplink);
     }
     else
     {
      Serial.print("Fallo MQTT (state="); Serial.print(client.state()); Serial.println(")");
      delay(2000);
     }
  }
}

// ------- SETUP -------
void setup()
  {
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);
  server.on("/", handleRoot);
  server.on("/cmd", handleCommand);
  server.on("/status", handleStatus);
  server.begin();
 }

// ------- LOOP -------
void loop()
 {
  if (!client.connected()) reconnect();
  client.loop();
  server.handleClient();
 }

Que sigue? tengo que seguir buscando ejemplos de dashboard por que sinceramente no manejo HTML y la creación de botones, led y cuadros de textos las pude aprender gracias a los aportes que hay dando vuelta por todos lados... Es prueba, error y analizar cada linea e ir aprendiendo... lento, pero de seguro me va a quedar una pequeña base para hacer futuros dashboard...
mas allá de seguir aprendiendo otros lenguajes de programación, tengo que ver como puedo cachetear al mosquitto para que no se desactive y me genere errores (manualmente refrescando http://192.168.1.36/status lo revivía, pero no es la idea)...
En paralelo a esto, voy a tener que cambiar la lógica de los led de estado y que se mantengan en un estado definido hasta que no entre otro string que diga lo contrario.
Ahora estoy peleando con un frame en el dashboard donde pongo las imágenes/video del espcam; Puntualmente el ejemplo de CameraWebServer que esta en el ide, pero con el ejemplo que tiene la función de reconocimiento de caras. En las ultimas versiones del IDE, cuando instalamos en el gestor de placas el ESP32, el ejemplo CameraWebServer ya no tiene esta funcionalidad, pero si seleccionamos bajar la versión, tenemos nuevamente este ejemplo... esto es un tips que de casualidad lo agarre de vuelo.
Pasando en limpio, para la conexión de sensores y actuadores Lora de Usarlink, el problema es el tiempo de actualización, por lo tanto no sirve para un ambito industrial con sensores de alarmas de estado critico o monitoreo en tiempo real... Ahora, si no usamos LoRa (el UC1114) e implementamos solo varios ESP8266/ESP32 y cámaras IP, es terrible la velocidad y muy amplio el horizonte de cosas que se pueden hacer... Esta semana me voy a dedicar a trabajar la mejora del dashboard y ver si puedo sacar el error de los estados de los LED, si no puedo y si no aparece algún alma caritativa que me oriente, descarto el UC1114 y seguimos con el UG65, el ESPcam, un par de ESP8266, el dashboard para ver y controlar fuera de casa que va a estar en paralelo con TRITON (La nueva versión de XScada, un software gratuito que en algún momento les mostré como hacer sus propios SCADAS), que lo voy a usar localmente... Esto ultimo por que lo prometí desde un principio
 
Bien, tema dashboard fue un dolor de cabeza y para no andar abriendo y cerrando la consola del broker, ver la seccion packet del UG65 y el monitor serial del IDE de Arduino, arme una version debug...
Captura de pantalla 2025-11-24 201315.png

De acá cerre en la version final donde tengo el dashboard y en paralelo, un iFrame del ESPCAM...

Captura de pantalla 2025-11-24 203447.png

Segui con los problemas de actualizacion y cosas raras, hasta que encontre una seccion en el UG65 donde te permite subir pequeñas apps en python.... Arme algo medio escueto y logre hacer que los payload funcionaran tanto con los downlink como los uplink, pero los tiempos de refresco te boicotean cualquier idea maravillosa que quieras hacer.... El sketch quedo de esta manera:

CSS:
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

// ----------- CONFIGURACIÓN WIFI Y RED --------------------
const char* ssid = "MovistarFibr------------";
const char* password = "D-----------------S";
IPAddress local_IP(192,168,1,80);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255,255,255,0);
IPAddress primaryDNS(8,8,8,8);

// ----------- MQTT BROKER & TOPICS ------------------------
const char* mqtt_server = "192.168.1.41";
const int mqtt_port = 1883;
const char* topic_downlink = "application/UC1114/device/24e------------2/downlink";
const char* topic_uplink   = "application/UC1114/device/24------------42/uplink";

ESP8266WebServer server(80);
WiFiClient espClient;
PubSubClient client(espClient);

int input1_state = -1;
int input2_state = -1;
int relay1_state = -1;
int relay2_state = -1;

// ----------- WIFI IP ESTATICA ----------------------
void setup_wifi() {
  WiFi.mode(WIFI_STA);
  WiFi.config(local_IP, gateway, subnet, primaryDNS);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); }
  Serial.print("IP address: "); Serial.println(WiFi.localIP());
}

// ----------- ENVÍO COMANDO BINARIO HEX -----------------
void sendRelayCommand(int relay, bool state) {
  char buf[96];
  const char* code = (relay == 1) ? (state ? "0901" : "0900") : (state ? "0A01" : "0A00");
  sprintf(buf,
    "{\"data\":\"%s\",\"fPort\":85,\"immediately\":true,\"confirmed\":false}",
    code
  );
  client.publish(topic_downlink, buf);
  Serial.print("Comando relay JSON enviado: "); Serial.println(buf);
}

// ----------- AJAX WEB: COMANDOS -------------------------
void handleCommand() {
  if (server.hasArg("relay") && server.hasArg("state")) {
    int relay = server.arg("relay").toInt();
    bool state = server.arg("state") == "1";
    sendRelayCommand(relay, state);
    server.send(200, "text/plain", "OK");
  } else {
    server.send(400, "text/plain", "Bad Request");
  }
}

// ----------- AJAX WEB: STATUS JSON ---------------------
void handleStatus() {
  String resp = "{\"input1\":" + String(input1_state) +
                ",\"input2\":" + String(input2_state) +
                ",\"relay1\":" + String(relay1_state) +
                ",\"relay2\":" + String(relay2_state) + "}";
  server.send(200, "application/json", resp);
}

// ----------- CALLBACK MQTT UPLINK ----------------------
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("\n-----CALLBACK RECEIVED-----\nMQTT TOPIC: [");
  Serial.print(topic);
  Serial.println("]");
  Serial.print("PAYLOAD RAW: [");
  for (unsigned int i=0; i<length; i++)
    Serial.print((char)payload[i]);
  Serial.println("]");
  Serial.print("PAYLOAD LENGTH: "); Serial.println(length);

  if (String(topic) == topic_uplink) {
    StaticJsonDocument<512> doc;
    DeserializationError error = deserializeJson(doc, payload, length);
    if (!error) {
      if (doc.containsKey("gpio_input_1")) input1_state = doc["gpio_input_1"];
      if (doc.containsKey("gpio_input_2")) input2_state = doc["gpio_input_2"];
      if (doc.containsKey("gpio_output_1")) relay1_state = doc["gpio_output_1"];
      if (doc.containsKey("gpio_output_2")) relay2_state = doc["gpio_output_2"];
      Serial.print("Estados-> Entrada1: "); Serial.print(input1_state);
      Serial.print(", Entrada2: "); Serial.print(input2_state);
      Serial.print(", Rele1: "); Serial.print(relay1_state);
      Serial.print(", Rele2: "); Serial.println(relay2_state);
    } else {
      Serial.print("Error DESERIALIZANDO JSON: ");
      Serial.println(error.c_str());
      Serial.print("JSON recibido: [");
      for (unsigned int i=0; i<length; i++) Serial.print((char)payload[i]);
      Serial.println("]");
    }
  }
}

// ----------- DASHBOARD VISUALIZACIÓN --------------------
void handleRoot() {
  String html =
    "<!DOCTYPE html><html><head><meta charset='UTF-8'><title>UC1114/UG65/ESP8266 Dashboard ETI Patagonia -prof,martintorres@educ.ar</title>"
    "<style>"
    "body{font-family:Arial;background:#f6f6f6;margin:0;}"
    ".container{display:grid;grid-template-columns:430px 1fr;min-height:100vh;}"
    ".panel{background:#fff;max-width:430px;margin:30px;padding:18px;border-radius:10px;box-shadow:0 0 12px #aaa;text-align:center;align-self:start;}"
    ".led{display:inline-block;width:18px;height:18px;border-radius:50%;margin:0 8px 0 0;vertical-align:middle;box-shadow:0 0 4px #999;}"
    ".red{background:#c53232;}"
    ".green{background:#56c232;}"
    ".btop{width:145px;height:42px;border:none;border-radius:7px;font-size:15px;font-weight:bold;margin:6px 10px;cursor:pointer;transition:.2s;}"
    ".on{background:#29D649;color:#fff;}"
    ".off{background:#bbb;color:#222;}"
    ".cam{background:#222;margin:30px 20px 30px 10px;padding:0;border-radius:10px;box-shadow:0 0 24px #666;display:flex;align-items:center;justify-content:center;}"
    ".cam iframe{border-radius:10px;border:none;width:100%;height:480px;max-width:700px;}"
    "</style></head><body>"
    "<div class='container'>"
    "<div class='panel'>"
    "<h2>UC1114/UG65/ESP8266 Dashboard</h2>"
    "<div style='margin-bottom:26px;'>"
    "<span class='led' id='led_in1'></span>Entrada 1&nbsp;&nbsp;"
    "<span class='led' id='led_in2'></span>Entrada 2"
    "</div>"
    "<h3>Control Relé 1</h3>"
    "<button class='btop on' onclick=\"comando(1,1)\">Relé 1 ON</button>"
    "<button class='btop off' onclick=\"comando(1,0)\">Relé 1 OFF</button><br>"
    "Estado: <span id='estado_r1'></span>"
    "<h3>Control Relé 2</h3>"
    "<button class='btop on' onclick=\"comando(2,1)\">Relé 2 ON</button>"
    "<button class='btop off' onclick=\"comando(2,0)\">Relé 2 OFF</button><br>"
    "Estado: <span id='estado_r2'></span>"
    "</div>"
    "<div class='cam'><iframe src='http://192.168.1.90'></iframe></div>"
    "</div>"
    "<script>"
    "function comando(r,s){fetch('/cmd?relay='+r+'&state='+s);}"
    "function actualizar(){"
    " fetch('/status').then(r=>r.json()).then(d=>{"
    "  document.getElementById('led_in1').className='led '+(d.input1==1?'green':'red');"
    "  document.getElementById('led_in2').className='led '+(d.input2==1?'green':'red');"
    "  document.getElementById('estado_r1').innerHTML='<span class=\"led '+(d.relay1==1?'green':'red')+'\"></span>'+(d.relay1==1?'ON':'OFF');"
    "  document.getElementById('estado_r2').innerHTML='<span class=\"led '+(d.relay2==1?'green':'red')+'\"></span>'+(d.relay2==1?'ON':'OFF');"
    " });"
    "}"
    "setInterval(actualizar,1000);actualizar();"
    "</script></body></html>";
  server.send(200, "text/html", html);
}

// ----------- RECONECCION AL BROKER MQTT ---------------------------
void reconnect() {
  while (!client.connected()) {
    Serial.print("Conectando a MQTT...");
    if (client.connect("ESP8266Dash")) {
      Serial.print("Suscribo tras reconexión topic: "); Serial.println(topic_uplink);
      client.subscribe(topic_uplink);
    } else {
      Serial.print("Fallo MQTT (state="); Serial.print(client.state()); Serial.println(")");
      delay(2000);
    }
  }
}

// ----------- SETUP --------------------------
void setup() {
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);
  server.on("/", handleRoot);
  server.on("/cmd", handleCommand);
  server.on("/status", handleStatus);
  server.begin();
  Serial.print("Me suscribo al topic: "); Serial.println(topic_uplink);
  client.subscribe(topic_uplink);
}

// ----------- LOOP ---------------------------
void loop() {
  if (!client.connected()) reconnect();
  client.loop();
  server.handleClient();
}

Sinceramente, no sirve trabajar el UG65 con el UC1114. Es mucho el tiempo minimo de refresco, y genera problemas con el broker mqtt, asi que voy a descartar el equipito LORA de Ursalink y cuando consiga un transmisor Lora, hare algo mas personalizado que no dependa de los limites a nivel programación que tiene este equipo... Ya estoy trabajando en ir viendo el limite de camaras y dispositivos ESp8266 que puedo ir sumando a la red y trabjar las señales mediante el broker MQTT MOSQUITTO... Toda la idea inicial va a morir en un ejemplo que van a poder replicar en un gateway como este que tengo, o bien, en sus propios enrutadores wifi hogareños ;) Ya que inverti tiempo en esto, por lo menos vamos a sacarle algo de jugo :rolleyes:
... Si se preguntan como se suben nuestras app de python, les comento que lo primero que tenemos que hacer es instalar la biblioteca para poder trabajar con phyton desde acá: Milesight Firmware & SDK en la seccion SDK... en el UG65 nos vamos a la seccion "APP" de la barra lateral derecha; Damos click en "Python" y nos abre tres funciones: Python / AppManager Configuration / Python APP ... desde "Python" en la parte donde dice "SDK Upload", cargamos el archivo oficial que descargamos del link anterior.... Luego nos vamos a "Python APP" y ahi vamos a cargar nuestra app en la seccion "App Package"... ahora... el formato que acepta el equipo es una carpeta en formato zip donde dentro vamos a tener dentro lo siguiente

appPYTHON_1.png
dentro del archivo ".ini" no ponemos nada... dentro del archivo "setup.py", ponemos:

CSS:
from setuptools import setup, find_packages
setup(
    name='relay_bridge',
    sdk_version='1.1.14',
    version='0.0.1',
    author='ETI Patagonia',
    author_email='prof.martintorres@educ.ar',
    description='Milesight UG65 Local Ctrl bridge',
    license='PRIVATE',
    packages=find_packages('SRC'),
    package_dir={ '' : 'SRC' },
    zip_safe=False,
    install_requires=[
        # Milesight SDK incluye umqtt y ujson
    ],
    entry_points = """
        [console_scripts]
        relay_bridge = main:main
        """
)

En la carpeta SRC, creamos un archivo con el nombre "main.py" y dentro de este, pondremos las siguientes lineas:

CSS:
import paho.mqtt.client as mqtt
import json

TOPIC_SUB = "application/UC1114/device/24e-------------2/downlink"
TOPIC_PUB = "application/UC1114/device/24e--------------/downlink"

def on_connect(client, userdata, flags, rc):
    print("[Bridge] Connected with result code:", rc)
    client.subscribe(TOPIC_SUB)
    print("[Bridge] Subscribed to:", TOPIC_SUB)

def on_message(client, userdata, msg):
    print("[Bridge] Received message!")
    print("[Bridge] Topic:", msg.topic)
    payload = msg.payload.decode()
    print("[Bridge] Payload:", payload)
    try:
        obj = json.loads(payload)
        # Si recibimos data/cmd, construimos el comando de downlink
        data_value = obj.get("data") or obj.get("cmd")
        if data_value:
            out = {
                "data": str(data_value),
                "fPort": obj.get("fPort", 85),
                "immediately": obj.get("immediately", True),
                "confirmed": obj.get("confirmed", False)
            }
            out_str = json.dumps(out)
            client.publish(TOPIC_PUB, out_str)
            print("[Bridge] Published command:", out_str)
        else:
            print("[Bridge] No 'data' or 'cmd' found in message")
    except Exception as e:
        print("[Bridge] Error in on_message:", e)

def main():
    print("[Bridge] Main started")
    client = mqtt.Client()
    client.on_connect = on_connect
    client.on_message = on_message
    print("[Bridge] Conectando con el broker ...")
    client.connect("127.0.0.1", 1883, 60)
    print("[Bridge] Loop forever ...")
    client.loop_forever()

if __name__ == "__main__":
    print("[Bridge] Entrypoint")
    main()

Recuerden que después de comprimir en formato zip, tenemos que verificar que al abrir el archivo, tengamos una carpeta con el nombre del proyecto y dentro de esta los archivos y la otra carpeta... algo asi:

zip_1.png
zip2.png
Una vez instalada la app, tocamos el boton aplicar y guardar... con esa accion ya ejecuta nuestra app en pyton...
Mañana o pasado les caigo con las nuevas... Sinceramente, con Python, con arbolito adornado con luces y guirnaldas o no, no es potable la implementacion de los equipos de esta marca que son LoRaWAN... uno se espera que este preparado para entornos de control industrial / IOT o lo que fuera, y nos encontramos con muchos bugs..... Asi que voy a volver con otra version dejando de lado el UC1114
1000101963.jpg
Dicen que la curiosidad mata el gato 🤣
No me resistí y lo abrí para ver el hardware de la etapa LoRaWAN y tiene un LoRa SX1302... Se me caen las babas 🤑 por sacárselo, pero va a generar error al iniciar el UG65 si no lo detecta

*************************************************************************************************************

EDIT/PD: Gente!!! no me manden privados ni correos... el tema lo tratamos acá para que toda la info quede disponible y potable para toda la comunidad :mad:
Les paso el programa del ESP32 CAM...
Como ya dije, desde el gestor de placa, buscamos ESP32 y vamos a instalar la de Espressif Systems.... una vez instalada, hacemos un downGRADE... si se fijan hay un cuadro donde podemos seleccionar la version... Seleccionen la 1.0.6 y listo.... Despues vamos a: ARCHIVOS/EJEMPLOS/ESP32/CAMERA y seleccionamos el unico ejemplo que hay "CameraWebServer"... adjunto los archivos, editen la parte donde dice ".txt" y listo....
Ese es el sketch que tiene reconocimiento de rostro, etc... recuerden que si no hacemos un downGRADE de la versión, por mas que compilen, no va a funcionar y les va aparecer que les faltan librerias, etc etc...
No es de mala onda no querer charlar en privado, pero entiendan que esto es una pequeña base "para todos" y si hablamos en privado, mucha gente queda excluida del conocimiento... Tratemos de colaborar, y si lo tratamos aca, mejor... no respondo mas mensajes personales... bechiossss 😘
 

Adjuntos

  • app_httpd.cpp.txt
    25.2 KB · Visitas: 0
  • camera_index.h.txt
    53.5 KB · Visitas: 0
  • camera_pins.h.txt
    4.4 KB · Visitas: 0
  • CameraWebServer.ino.txt
    3.5 KB · Visitas: 0
Última edición:
Bueno... por acá se dice que: Tanto da el agua al cántaro hasta que lo revienta. :aplauso::aplauso::aplauso:

Me hiciste recordar cuando desensamblé un CPM completo, para saber cómo adaptarle una salida a LPT (Solo venía para RS232). Tuve que aprender cómo funcionaba el puerto paralelo, configurar un 8051, cómo funcionaba la impresora y como agregar el módulo de software al diskette del SO sin violar el checksum original. Y... sí, funcionó.

Felicitaciones, Don Torres...
 
Atrás
Arriba