domingo, 10 de septiembre de 2017

Proyecto #1 ESP8266 – Micrófono Espía Versión 1

Este es un proyecto de IOT (internet de las cosas), que consiste en hacer un pequeño dispositivo VOIP (voz sobre ip) usando un NodeMCU (ESP8266). El circuito está compuesto por un preamplificador de micrófono electret, que alimenta la entrada analógica de un NodeMCU (ESP8266). El ESP8266 convierte ese audio en paquetes UDP que luego son enviados por la red, por último, un scritp de Python se encarga de tomar esos datagramas UDP y reproducir el sonido.



Lista de Materiales:

1) NodeMCU (ESP8266)
1) Micrófono electret
1) Transistor BC377
2) Resistencias de 10K
1) Resistencia de 680K
1) Resistencia de 330
1) Preset de 100K
1) Led
2) Capacitores de 0,1uf (104)
1) Capacitor electrolítico de 470uF 16V



microfonoNivel:

void setup()
{
  Serial.begin(115200);
  Serial.println();
}

void loop()
{
  int analog = analogRead(17);
  Serial.print("0, 512, 1024, ");
  Serial.println(analog);
}

microfonoUDP:

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>

const char* ssid = "______";
const char* password = "______";
int contconexion = 0;

WiFiUDP Udp;

void setup()
{
  Serial.begin(115200);
  Serial.println();

  pinMode(5, OUTPUT);  //D1 Led de estado
  digitalWrite(15, LOW);

  WiFi.mode(WIFI_STA); //para que no inicie el SoftAP en el modo normal
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED and contconexion <50) { //Cuenta hasta 50 si no se puede conectar lo cancela
    ++contconexion;
    delay(250);
    Serial.print(".");
    digitalWrite(5, HIGH);
    delay(250);
    digitalWrite(5, LOW);
  }
  if (contconexion <50) {
      //para usar con ip fija
      IPAddress Ip(192,168,1,180); 
      IPAddress Gateway(192,168,1,1); 
      IPAddress Subnet(255,255,255,0); 
      WiFi.config(Ip, Gateway, Subnet); 
      
      Serial.println("");
      Serial.println("WiFi conectado");
      Serial.println(WiFi.localIP());
      digitalWrite(5, HIGH);  
  }
  else { 
      Serial.println("");
      Serial.println("Error de conexion");
      digitalWrite(15, LOW);
  }
}

void loop()
{
  Udp.beginPacket("192.168.1.101", 1234);

  for(int i=0; i<1024;i++){
    int old=micros();

    float analog = analogRead(17);

    analog = ((analog / 1) - 385);
    if (analog > 255){
      analog = 255;
    }
    else if (analog < 0){
      analog = 0;
    }
    
    Udp.write(int(analog));
    while(micros()-old<87); // 90uSec = 1Sec/11111Hz - 3uSec para los otros procesos
  }
  Udp.endPacket();
  delay(5);
}


reproductor.py:

import pyaudio
import socket

UDP_IP = "192.168.1.101"
UDP_PORT = 1234

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))

p = pyaudio.PyAudio()

stream = p.open(format=32, channels=1, rate=11111, output=True)

try:
    while True:
        data, addr = sock.recvfrom(1024) # buffer de 1024 bytes
        stream.write(data)
        
except KeyboardInterrupt:  #precionar Crtl + C para salir
    print("Cerrando...")
    stream.stop_stream()
    stream.close()
    p.terminate()

http://people.csail.mit.edu/hubert/pyaudio/

sábado, 2 de septiembre de 2017

Tutorial #19 ESP8266 (Parte9) – MQTT – Enviar email usando Gmail y Python

Este es otro tutorial sobre internet de las cosas (IOT) usando MQTT en un ESP8266, en el que muestro como usar una script de Python que monitorea MQTT para enviar un email cuando la temperatura que lee el sensor LM35 supera un determinado valor.


tutorial19.py:

#!/usr/bin/env python 1
# -*- coding: utf-8 -*-

import paho.mqtt.client as mqtt
import sys
import smtplib
import mimetypes

global supero
supero = False

# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
    print("Conectado - Codigo de resultado: "+str(rc))

    # Subscribing in on_connect() means that if we lose the connection and
    # reconnect then subscriptions will be renewed.
    client.subscribe("/#")

# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
    print(msg.topic+" "+str(msg.payload))
    lista = msg.topic.split("/")

    global supero

    if str(lista[2]) == "temperatura" and float(msg.payload) < 26:
       supero = False

    if supero == False and str(lista[2]) == "temperatura" and float(msg.payload) > 26:

        supero = True

        from email.MIMEText import MIMEText
     
        emisor = "____@gmail.com"
        receptor = "____@gmail.com"
        
        print("Enviando Email...")
     
        # Configuracion del mensaje
        mensaje = MIMEText("La temperatura de la " + lista[1] + " es de: " + str(msg.payload))
        mensaje['From']=emisor
        mensaje['To']=receptor
        mensaje['Subject']= "La temperatura de la " + lista[1] + " superó los 26ºC"
     
        try:
            # Nos conectamos al servidor SMTP de Gmail
            serverSMTP = smtplib.SMTP('smtp.gmail.com',587)
            serverSMTP.ehlo()
            serverSMTP.starttls()
            serverSMTP.ehlo()
            serverSMTP.login(emisor,"__password__")
            print("Conectado con exito al servidor SMTP...")
        except:
            print("Error al intentar conectarse al servidor SMTP...")
     
        try:
            # Enviamos el mensaje
            serverSMTP.sendmail(emisor,receptor,mensaje.as_string())
            # Cerramos la conexion
            serverSMTP.close()
            print("Email enviado con exito...")
        except:
            print("Error al intentar enviar el email...")

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message

try:
    client.connect("m13.cloudmqtt.com", 11948, 60)
except:
    print("No se pudo conectar con el MQTT Broker...")
    print("Cerrando...")
    sys.exit()
    
client.username_pw_set("lqeamtbn", "vh0cU_Vcszxp")

try:
    client.loop_forever()
except KeyboardInterrupt:  #precionar Crtl + C para salir
    print("Cerrando...")

domingo, 27 de agosto de 2017

Tutorial #18 ESP8266 (Parte8) – MQTT - Graficar los datos de MySQL usando PHP y Highcharts

En el tutorial anterior de IOT (internet de las cosas) usando MQTT en un ESP8266; explique cómo guardar los datos de MQTT en MySQL usando un script de Python; en este video les explico cómo hacer una tabla con los topics y graficar la temperatura. Para realizar esto vamos a usar MySQL, PHP y Highcharts.


consulta.php:

<!DOCTYPE html>
<html >
<head>
  <meta charset="UTF-8">
  <title>Consulta MySQL- Tutorial 18</title>
</head>
<body>
<?php
$conexion = mysql_connect("localhost", "root", "root");
mysql_select_db("tutorial17", $conexion);
mysql_query("SET NAMES 'utf8'");

$resultado = mysql_query("SELECT DISTINCT `usuario` FROM `tabla` WHERE 1");

?>
<form action="respuesta.php" method="POST">
  <select name="usuario">
  <?php
    while ($row=mysql_fetch_array($resultado))
      {
       echo "<option>";
        echo $row[0];
        echo "</option>";
      }
    mysql_close();
  ?>
  </select><br>
  <input type="date" name="fecha" ><br>
  <input type="submit" name="Enviar" >
</form>
</body> 
</html>

respuesta.php:

<!DOCTYPE html>
<html>
<head>
    <title>Respuesta MySQLL - Tutorial 18</title>
    <meta charset="UTF-8">
</head>
<body>
<?php
$conexion = mysql_connect("localhost", "root", "root");
mysql_select_db("tutorial17", $conexion);
mysql_query("SET NAMES 'utf8'");

function temperatura_diaria ($usuario,$ano,$mes,$dia) {

 $resultado=mysql_query("SELECT UNIX_TIMESTAMP(fecha), mensaje FROM tabla WHERE year(`fecha`) = '$ano' AND month(`fecha`) = '$mes' AND day(`fecha`) = '$dia' AND `usuario`= '$usuario' AND `topic`= 'temperatura'");

 while ($row=mysql_fetch_array($resultado))
 {
  echo "[";
  echo ($row[0]*1000)-10800000; //le resto 3 horas = 10800000 mill
  echo ",";
  echo $row[1];
  echo "],";
 }
}
?>

<div id="container1" style="width: 100%; height: 400px;"></div>

<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>

<script> 

$(function () {
    $('#container1').highcharts({
        chart: {
            type: 'line',
            zoomType: 'x'
        },
        colors: ['#337ab7', '#cc3c1a'],
        title: {
            text: 'Temperatura'
        },
        xAxis: {
             type: 'datetime',
             
        },
        yAxis: {
            title: {
                text: 'Temperatura'
            }
        },

        
        series: [{
            name: 'Temp',
            data: [<?php 
                $usuario = $_POST ['usuario'];
                $fecha = $_POST ['fecha'];
                $ano = substr("$fecha", 0, 4);
                $mes = substr("$fecha", 5, 2);
                $dia = substr("$fecha", 8, 2);
                temperatura_diaria($usuario, $ano , $mes, $dia);
                ?>     
        ]}, 
      
    ],
    });
});
</script>

<?php
function tablaDeTopics ($usuario,$ano,$mes,$dia) {
    
    $resultado=mysql_query("SELECT fecha, topic, mensaje FROM tabla WHERE year(`fecha`) = '$ano' AND month(`fecha`) = '$mes' AND day(`fecha`) = '$dia' AND `usuario`= '$usuario' AND `topic`!= 'temperatura'");

    echo "<table border='1'>";
    echo "<tr>";
    echo "<td>";
    echo "Fecha";
    echo "</td>";
    echo "<td>";
    echo "Topic";
    echo "</td>";
    echo "<td>";
    echo "Mensaje";
    echo "</td>";
    echo "</tr>";

    while ($row=mysql_fetch_array($resultado))
    {
        echo "<tr>";
        echo "<td>";
        echo $row[0];
        echo "</td>";
        echo "<td>";
        echo $row[1];
        echo "</td>";
        echo "<td>";
        echo $row[2];
        echo "</td>";
        echo "</tr>";
    }
    echo "</table>";
    mysql_close();
}
tablaDeTopics($usuario, $ano , $mes, $dia)
?>
</body>
</html>

domingo, 20 de agosto de 2017

Tutorial #17 ESP8266 (Parte7) – Integrar MQTT con MySQL usando Python

Este es el séptimo tutorial de IOT (internet de las cosas) usando MQTT en un ESP8266; en estos videos (Parte A y B) explico cómo integrar MQTT (usando el bróker CloudMQTT) con MySQL, para eso vamos a usar Python y dos librerías, “paho-mqtt 1.3.0” para conectarse con MQTT y “MySQL-python 1.2.5” para trabajar con la base de datos. En la parte B del tutorial termino de explicar cómo crear la base de datos usando la herramienta phpMyAdmin incluida en el servidor local WAMP “Uniform Server – Coral 8.9.2”; y agrego lo que falta del código de Python como trabajar con la base de datos.

Parte A:


Parte B:


tutorial17.py:

#!/usr/bin/env python 1
# -*- coding: utf-8 -*-

import paho.mqtt.client as mqtt
import sys

# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
    print("Conectado - Codigo de resultado: "+str(rc))

    # Subscribing in on_connect() means that if we lose the connection and
    # reconnect then subscriptions will be renewed.
    client.subscribe("/#")

# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
    print(msg.topic+" "+str(msg.payload))

client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message

try:
    client.connect("m13.cloudmqtt.com", 11948, 60)
except:
    print("No se pudo conectar con el MQTT Broker...")
    print("Cerrando...")
    sys.exit()
    
client.username_pw_set("lqeamtbn", "vh0cU_Vcszxp")

try:
    client.loop_forever()
except KeyboardInterrupt:  #precionar Crtl + C para salir
    print("Cerrando...")

tutorial17b.py:

#!/usr/bin/env python 1
# -*- coding: utf-8 -*-

import paho.mqtt.client as mqtt
import sys
import MySQLdb

# Abrir conexión con bases de datos
try:
    db = MySQLdb.connect("127.0.0.1","root","root","tutorial17")
except:
    print("No se pudo conectar con la base de datos")
    print("Cerrando...")
    sys.exit()

# Preparando cursor
cursor = db.cursor()

# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
    print("Conectado - Codigo de resultado: "+str(rc))

    # Subscribing in on_connect() means that if we lose the connection and
    # reconnect then subscriptions will be renewed.
    client.subscribe("/#")

# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
    print(msg.topic+" "+str(msg.payload))
    lista = msg.topic.split("/")

    sql = """INSERT INTO `tutorial17`.`tabla` (`id`, `usuario`, `topic`, `mensaje`, `fecha`) VALUES (NULL, '""" + lista[1]+ """', '""" + lista[2] + """', '""" + str(msg.payload) + """', CURRENT_TIMESTAMP);"""

    try:
        # Ejecutar un comando SQL
        cursor.execute(sql)
        db.commit()
        print("Guardando en base de datos...OK")
    except:
        db.rollback()
        print("Guardando en base de datos...Falló")
        
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message

try:
    client.connect("m13.cloudmqtt.com", 11948, 60)
except:
    print("No se pudo conectar con el MQTT Broker...")
    print("Cerrando...")
    db.close()
    sys.exit()   
    
client.username_pw_set("lqeamtbn", "vh0cU_Vcszxp")

try:
    client.loop_forever()
except KeyboardInterrupt:  #precionar Crtl + C para salir
    print("Cerrando...")
    db.close()

Python y Librerías:


Enlaces externos:

http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html

sábado, 19 de agosto de 2017

Tutorial #16 ESP8266 (Parte6) – Gráficos en tu Dashboard MQTT usando Highcharts

En este nuevo tutorial sobre IOT (internet de las cosas) usando ESP8266, voy a explica como agregarle un gráfico al dashboard MQTT que vimos en el tutorial anterior. Para eso vamos a usar JavaScript y dos librerías de Highcharts.


tutorial16.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Tutorial 16</title>
    <script src='mqttws31.js' type='text/javascript'></script> 
    <!-- https://api.cloudmqtt.com/sso/js/mqttws31.js -->
    <script src="raphael-2.1.4.min.js"></script>
    <script src="justgage.js"></script>
    <script src="https://code.highcharts.com/stock/highstock.js"></script>
    <script src="https://code.highcharts.com/stock/modules/exporting.js"></script>    
  </head>
  <style type="text/css">
    .a200x160px {
      width: 200px;
      height: 160px;
    }
    img {
      margin-left: 75px;
    }

    .switch {
      position: relative;
      display: inline-block;
      width: 120px;
      height: 68px;
    }

    .switch input {display:none;}

    .slider {
      position: absolute;
      cursor: pointer;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: #ccc;
      -webkit-transition: .4s;
      transition: .4s;
    }

    .slider:before {
      position: absolute;
      content: "";
      height: 52px;
      width: 52px;
      left: 8px;
      bottom: 8px;
      background-color: white;
      -webkit-transition: .4s;
      transition: .4s;
    }

    input:checked + .slider {
      background-color: red;
    }

    input:focus + .slider {
      box-shadow: 0 0 1px #2196F3;
    }

    input:checked + .slider:before {
      -webkit-transform: translateX(52px);
      -ms-transform: translateX(52px);
      transform: translateX(52px);
    }

    .slider.round {
      border-radius: 68px;
    }

    .slider.round:before {
      border-radius: 50%;
    }

    input[type=range] {
      height: 26px;
      -webkit-appearance: none;
      margin: 10px 0;
      width: 120px;
    }
    input[type=range]:focus {
      outline: none;
    }
    input[type=range]::-webkit-slider-runnable-track {
      width: 100%;
      height: 14px;
      cursor: pointer;
      animate: 0.2s;
      box-shadow: 1px 1px 1px #50555C;
      background: #50555C;
      border-radius: 14px;
      border: 0px solid #000000;
    }
    input[type=range]::-webkit-slider-thumb {
      box-shadow: 0px 0px 0px #000000;
      border: 0px solid #000000;
      height: 20px;
      width: 40px;
      border-radius: 12px;
      background: green;
      cursor: pointer;
      -webkit-appearance: none;
      margin-top: -3px;
    }
    input[type=range]:focus::-webkit-slider-runnable-track {
      background: #50555C;
    }
   
    input[type=range]:focus::-ms-fill-lower {
      background: #50555C;
    }
    input[type=range]:focus::-ms-fill-upper {
      background: #50555C;
    }

    #container {
      width: 600px;
      height: 300px;
    }
  </style>
  <body>
      <div id="gauge" class="a200x160px"></div>
      <div>
        <img id="imgPulsador" src="nopresionado.png">
      </div>
      <div>
         <svg width="100" height="75" viewBox="0 0 640 480">
         <defs>
          <linearGradient id="svg_6" x1="0" y1="0" x2="1" y2="0">
           <stop stop-color="#bfbfbf" offset="0"/>
           <stop stop-color="#404040" offset="1"/>
          </linearGradient>
          <linearGradient id="svg_11" x1="0" y1="0" x2="1" y2="1" spreadMethod="pad">
           <stop id="led1" stop-color="#000000" stop-opacity="0.992188" offset="0"/>
           <stop stop-color="#820101" stop-opacity="0.988281" offset="1"/>
          </linearGradient>
          <linearGradient id="svg_14" x1="0" y1="0" x2="1" y2="1" spreadMethod="pad">
           <stop stop-color="#ffffff" stop-opacity="0.996094" offset="0"/>
           <stop stop-color="#d30606" stop-opacity="0.984375" offset="0.703125"/>
          </linearGradient>
         </defs>
         <g>
          <title>Layer 1</title>
          <circle fill="url(#svg_6)" stroke-width="17.5" stroke-linecap="round" cx="320" cy="240" r="196.125" id="svg_3" fill-opacity="0.77" transform="rotate(90, 320, 240)"/>
          <circle fill="url(#svg_6)" stroke-width="17.5" stroke-linecap="round" fill-opacity="0.64" cx="319.252837" cy="239.999045" r="160" id="svg_7"/>
          <circle fill="url(#svg_11)" stroke-width="17.5" stroke-linecap="round" cx="320.000535" cy="240.001698" r="150" id="svg_8"/>
          <ellipse fill="url(#svg_14)" stroke-width="17.5" stroke-linecap="round" cx="250.179609" cy="170.124194" rx="75.675959" ry="44.402987" id="svg_20" transform="rotate(-47.7626, 250.18, 170.125)"/>
         </g>
        </svg>
        <label class="switch">
          <input type="checkbox" onclick='OnOff2()'>
          <span class="slider round"></span>
        </label>
      </div>
      <div>
         <svg width="100" height="75" viewBox="0 0 640 480">
         <defs>
          <linearGradient id="svg_26" x1="0" y1="0" x2="1" y2="0">
           <stop stop-color="#bfbfbf" offset="0"/>
           <stop stop-color="#404040" offset="1"/>
          </linearGradient>
          <linearGradient id="svg_211" x1="0" y1="0" x2="1" y2="1" spreadMethod="pad">
           <stop id="led2" stop-color="rgb(0, 0, 0)" stop-opacity="0.992188" offset="0"/>
           <stop stop-color="#018201" stop-opacity="0.988281" offset="1"/>
          </linearGradient>
          <linearGradient id="svg_214" x1="0" y1="0" x2="1" y2="1" spreadMethod="pad">
           <stop stop-color="#ffffff" stop-opacity="0.996094" offset="0"/>
           <stop stop-color="#06d306" stop-opacity="0.984375" offset="0.703125"/>
          </linearGradient>
         </defs>
         <g>
          <title>Layer 1</title>
          <circle fill="url(#svg_6)" stroke-width="17.5" stroke-linecap="round" cx="320" cy="240" r="196.125" id="svg_23" fill-opacity="0.77" transform="rotate(90, 320, 240)"/>
          <circle fill="url(#svg_6)" stroke-width="17.5" stroke-linecap="round" fill-opacity="0.64" cx="319.252837" cy="239.999045" r="160" id="svg_27"/>
          <circle fill="url(#svg_211)" stroke-width="17.5" stroke-linecap="round" cx="320.000535" cy="240.001698" r="150" id="svg_28"/>
          <ellipse fill="url(#svg_214)" stroke-width="17.5" stroke-linecap="round" cx="250.179609" cy="170.124194" rx="75.675959" ry="44.402987" id="svg_220" transform="rotate(-47.7626, 250.18, 170.125)"/>
         </g>
        </svg>
        <input type="range" id="myRange" min="0" max="1023"  onmouseup="enviarSalidaAnalogica()">
      </div>
      <div id="container"></div>
    <script>      
      usuario = 'placa2';
      contrasena = '12345678';

      var g = new JustGage({
        id: "gauge",
        min: 0,
        max: 50,
        title: "Temperatura ºC"
      });

      estadoDigital = "OFF";
      temperaturas = -1;

      function OnOff2(){
        if (estadoDigital == "OFF"){
          message = new Paho.MQTT.Message("ON");
          message.destinationName = '/' + usuario + '/salidaDigital'
          client.send(message);
        }
        else if (estadoDigital == "ON"){
          message = new Paho.MQTT.Message("OFF");
          message.destinationName = '/' + usuario + '/salidaDigital'
          client.send(message);
        }
      };

      function enviarSalidaAnalogica(){
        var dato = document.getElementById("myRange").value;
        message = new Paho.MQTT.Message(dato);
        message.destinationName = '/' + usuario + '/salidaAnalogica'
        client.send(message);
      };
       
      // called when the client connects
      function onConnect() {
        // Once a connection has been made, make a subscription and send a message.
        console.log("onConnect");
        client.subscribe("#");
      }
        
      // called when the client loses its connection
      function onConnectionLost(responseObject) {
        if (responseObject.errorCode !== 0) {
          console.log("onConnectionLost:", responseObject.errorMessage);
          setTimeout(function() { client.connect() }, 5000);
        }
      }
        
      // called when a message arrives
      function onMessageArrived(message) {
        if (message.destinationName == '/' + usuario + '/' + 'temperatura') { //acá coloco el topic
            //document.getElementById("temperatura").textContent = message.payloadString  + " ºC";
            g.refresh(message.payloadString);
            temperaturas = parseFloat(message.payloadString);
        }
        if (message.destinationName == '/' + usuario + '/' + 'pulsador') { //acá coloco el topic
            //document.getElementById("pulsador").textContent = message.payloadString;
            if (message.payloadString == "presionado"){
              document.getElementById("imgPulsador").src = "presionado.png"
            }
            else if (message.payloadString == "NO presionado"){
              document.getElementById("imgPulsador").src = "nopresionado.png"
            }
         }
        if (message.destinationName == '/' + usuario + '/' + 'salidaDigital') { //acá coloco el topic
            //document.getElementById("salidaDigital").textContent = message.payloadString;
            estadoDigital = message.payloadString;

            if (estadoDigital == "OFF") {
              document.getElementById("led1").setAttribute("stop-color", "#110000");
            }
            else if (estadoDigital == "ON") {
              document.getElementById("led1").setAttribute("stop-color", "#ff0000");
            }
        }
        if (message.destinationName == '/' + usuario + '/' + 'salidaAnalogica') { //acá coloco el topic
            //document.getElementById("salidaAnalogica").textContent = message.payloadString;
            document.getElementById("led2").setAttribute("stop-color", "rgb(0, " + parseInt(message.payloadString/4) + ", 0)")
        }
      }

        function onFailure(invocationContext, errorCode, errorMessage) {
          var errDiv = document.getElementById("error");
          errDiv.textContent = "Could not connect to WebSocket server, most likely you're behind a firewall that doesn't allow outgoing connections to port 39627";
          errDiv.style.display = "block";
        }
        
        var clientId = "ws" + Math.random();
        // Create a client instance
        var client = new Paho.MQTT.Client("m13.cloudmqtt.com", 31948, clientId);
        
        // set callback handlers
        client.onConnectionLost = onConnectionLost;
        client.onMessageArrived = onMessageArrived;
        
        // connect the client
        client.connect({
          useSSL: true,
          userName: usuario,
          password: contrasena,
          onSuccess: onConnect,
          onFailure: onFailure
        });

        Highcharts.setOptions({
            global: {
                useUTC: false
            }
        });

        // Create the chart
        Highcharts.stockChart('container', {
            chart: {
                events: {
                    load: function () {

                        // set up the updating of the chart each second
                        var series = this.series[0];
                        setInterval(function () {
                            var x = (new Date()).getTime(), // current time
                                y =temperaturas;
                            series.addPoint([x, y]);
                        }, 1000);
                    }
                }
            },

            rangeSelector: {
                buttons: [{
                    count: 1,
                    type: 'minute',
                    text: '1M'
                }, {
                    count: 5,
                    type: 'minute',
                    text: '5M'
                }, {
                    type: 'all',
                    text: 'All'
                }],
                inputEnabled: false,
                selected: 0
            },

            title: {
                text: 'Temperatura ºC'
            },

            exporting: {
                enabled: false
            },

            series: [{
                name: 'ºC',
                data: (function () {
                    // generate an array of random data
                    var data = [],
                        time = (new Date()).getTime(),
                        i;

                    for (i = -300; i <= 0; i += 1) {
                        data.push([
                            time + i * 1000,
                            -1
                        ]);
                    }
                    return data;
                }())
            }]
        });

          
        
    </script>
  </body>
</html>

sábado, 12 de agosto de 2017

Tutorial #15 ESP8266 (Parte5) – Diseño en tu Dashboard MQTT usando CSS y JavaScript

Este es el quinto tutorial de IOT (internet de las cosas) usando MQTT en un ESP8266; en estos videos (Parte A y B) les muestro como agregarle diseño al dashboard (tablero) que vimos en el tutorial 13, para eso vamos a hacer uso de CSS y JavaScript.

Parte A:


Parte B:


tutorial15.html:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Tutorial 15</title>
    <script src='mqttws31.js' type='text/javascript'></script> 
    <!-- https://api.cloudmqtt.com/sso/js/mqttws31.js -->
    <script src="raphael-2.1.4.min.js"></script>
    <script src="justgage.js"></script>    
  </head>
  <style type="text/css">
    .a200x160px {
      width: 200px;
      height: 160px;
    }
    img {
      margin-left: 75px;
    }

    .switch {
      position: relative;
      display: inline-block;
      width: 120px;
      height: 68px;
    }

    .switch input {display:none;}

    .slider {
      position: absolute;
      cursor: pointer;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: #ccc;
      -webkit-transition: .4s;
      transition: .4s;
    }

    .slider:before {
      position: absolute;
      content: "";
      height: 52px;
      width: 52px;
      left: 8px;
      bottom: 8px;
      background-color: white;
      -webkit-transition: .4s;
      transition: .4s;
    }

    input:checked + .slider {
      background-color: red;
    }

    input:focus + .slider {
      box-shadow: 0 0 1px #2196F3;
    }

    input:checked + .slider:before {
      -webkit-transform: translateX(52px);
      -ms-transform: translateX(52px);
      transform: translateX(52px);
    }

    .slider.round {
      border-radius: 68px;
    }

    .slider.round:before {
      border-radius: 50%;
    }

    input[type=range] {
      height: 26px;
      -webkit-appearance: none;
      margin: 10px 0;
      width: 120px;
    }
    input[type=range]:focus {
      outline: none;
    }
    input[type=range]::-webkit-slider-runnable-track {
      width: 100%;
      height: 14px;
      cursor: pointer;
      animate: 0.2s;
      box-shadow: 1px 1px 1px #50555C;
      background: #50555C;
      border-radius: 14px;
      border: 0px solid #000000;
    }
    input[type=range]::-webkit-slider-thumb {
      box-shadow: 0px 0px 0px #000000;
      border: 0px solid #000000;
      height: 20px;
      width: 40px;
      border-radius: 12px;
      background: green;
      cursor: pointer;
      -webkit-appearance: none;
      margin-top: -3px;
    }
    input[type=range]:focus::-webkit-slider-runnable-track {
      background: #50555C;
    }
   
    input[type=range]:focus::-ms-fill-lower {
      background: #50555C;
    }
    input[type=range]:focus::-ms-fill-upper {
      background: #50555C;
    }
  </style>
  <body>
      <div id="gauge" class="a200x160px"></div>
      <div>
        <img id="imgPulsador" src="nopresionado.png">
      </div>
      <div>
         <svg width="100" height="75" viewBox="0 0 640 480">
         <defs>
          <linearGradient id="svg_6" x1="0" y1="0" x2="1" y2="0">
           <stop stop-color="#bfbfbf" offset="0"/>
           <stop stop-color="#404040" offset="1"/>
          </linearGradient>
          <linearGradient id="svg_11" x1="0" y1="0" x2="1" y2="1" spreadMethod="pad">
           <stop id="led1" stop-color="#000000" stop-opacity="0.992188" offset="0"/>
           <stop stop-color="#820101" stop-opacity="0.988281" offset="1"/>
          </linearGradient>
          <linearGradient id="svg_14" x1="0" y1="0" x2="1" y2="1" spreadMethod="pad">
           <stop stop-color="#ffffff" stop-opacity="0.996094" offset="0"/>
           <stop stop-color="#d30606" stop-opacity="0.984375" offset="0.703125"/>
          </linearGradient>
         </defs>
         <g>
          <title>Layer 1</title>
          <circle fill="url(#svg_6)" stroke-width="17.5" stroke-linecap="round" cx="320" cy="240" r="196.125" id="svg_3" fill-opacity="0.77" transform="rotate(90, 320, 240)"/>
          <circle fill="url(#svg_6)" stroke-width="17.5" stroke-linecap="round" fill-opacity="0.64" cx="319.252837" cy="239.999045" r="160" id="svg_7"/>
          <circle fill="url(#svg_11)" stroke-width="17.5" stroke-linecap="round" cx="320.000535" cy="240.001698" r="150" id="svg_8"/>
          <ellipse fill="url(#svg_14)" stroke-width="17.5" stroke-linecap="round" cx="250.179609" cy="170.124194" rx="75.675959" ry="44.402987" id="svg_20" transform="rotate(-47.7626, 250.18, 170.125)"/>
         </g>
        </svg>
        <label class="switch">
          <input type="checkbox" onclick='OnOff2()'>
          <span class="slider round"></span>
        </label>
      </div>
      <div>
         <svg width="100" height="75" viewBox="0 0 640 480">
         <defs>
          <linearGradient id="svg_26" x1="0" y1="0" x2="1" y2="0">
           <stop stop-color="#bfbfbf" offset="0"/>
           <stop stop-color="#404040" offset="1"/>
          </linearGradient>
          <linearGradient id="svg_211" x1="0" y1="0" x2="1" y2="1" spreadMethod="pad">
           <stop id="led2" stop-color="rgb(0, 0, 0)" stop-opacity="0.992188" offset="0"/>
           <stop stop-color="#018201" stop-opacity="0.988281" offset="1"/>
          </linearGradient>
          <linearGradient id="svg_214" x1="0" y1="0" x2="1" y2="1" spreadMethod="pad">
           <stop stop-color="#ffffff" stop-opacity="0.996094" offset="0"/>
           <stop stop-color="#06d306" stop-opacity="0.984375" offset="0.703125"/>
          </linearGradient>
         </defs>
         <g>
          <title>Layer 1</title>
          <circle fill="url(#svg_6)" stroke-width="17.5" stroke-linecap="round" cx="320" cy="240" r="196.125" id="svg_23" fill-opacity="0.77" transform="rotate(90, 320, 240)"/>
          <circle fill="url(#svg_6)" stroke-width="17.5" stroke-linecap="round" fill-opacity="0.64" cx="319.252837" cy="239.999045" r="160" id="svg_27"/>
          <circle fill="url(#svg_211)" stroke-width="17.5" stroke-linecap="round" cx="320.000535" cy="240.001698" r="150" id="svg_28"/>
          <ellipse fill="url(#svg_214)" stroke-width="17.5" stroke-linecap="round" cx="250.179609" cy="170.124194" rx="75.675959" ry="44.402987" id="svg_220" transform="rotate(-47.7626, 250.18, 170.125)"/>
         </g>
        </svg>
        <input type="range" id="myRange" min="0" max="1023"  onmouseup="enviarSalidaAnalogica()">
      </div>
    <script>      
      usuario = 'placa2';
      contrasena = '12345678';

      var g = new JustGage({
        id: "gauge",
        min: 0,
        max: 50,
        title: "Temperatura ºC"
      });

      estadoDigital = "OFF";

      function OnOff2(){
        if (estadoDigital == "OFF"){
          message = new Paho.MQTT.Message("ON");
          message.destinationName = '/' + usuario + '/salidaDigital'
          client.send(message);
        }
        else if (estadoDigital == "ON"){
          message = new Paho.MQTT.Message("OFF");
          message.destinationName = '/' + usuario + '/salidaDigital'
          client.send(message);
        }
      };

      function enviarSalidaAnalogica(){
        var dato = document.getElementById("myRange").value;
        message = new Paho.MQTT.Message(dato);
        message.destinationName = '/' + usuario + '/salidaAnalogica'
        client.send(message);
      };
       
      // called when the client connects
      function onConnect() {
        // Once a connection has been made, make a subscription and send a message.
        console.log("onConnect");
        client.subscribe("#");
      }
        
      // called when the client loses its connection
      function onConnectionLost(responseObject) {
        if (responseObject.errorCode !== 0) {
          console.log("onConnectionLost:", responseObject.errorMessage);
          setTimeout(function() { client.connect() }, 5000);
        }
      }
        
      // called when a message arrives
      function onMessageArrived(message) {
        if (message.destinationName == '/' + usuario + '/' + 'temperatura') { //acá coloco el topic
            //document.getElementById("temperatura").textContent = message.payloadString  + " ºC";
            g.refresh(message.payloadString);
        }
        if (message.destinationName == '/' + usuario + '/' + 'pulsador') { //acá coloco el topic
            //document.getElementById("pulsador").textContent = message.payloadString;
            if (message.payloadString == "presionado"){
              document.getElementById("imgPulsador").src = "presionado.png"
            }
            else if (message.payloadString == "NO presionado"){
              document.getElementById("imgPulsador").src = "nopresionado.png"
            }
         }
        if (message.destinationName == '/' + usuario + '/' + 'salidaDigital') { //acá coloco el topic
            //document.getElementById("salidaDigital").textContent = message.payloadString;
            estadoDigital = message.payloadString;

            if (estadoDigital == "OFF") {
              document.getElementById("led1").setAttribute("stop-color", "#110000");
            }
            else if (estadoDigital == "ON") {
              document.getElementById("led1").setAttribute("stop-color", "#ff0000");
            }
        }
        if (message.destinationName == '/' + usuario + '/' + 'salidaAnalogica') { //acá coloco el topic
            //document.getElementById("salidaAnalogica").textContent = message.payloadString;
            document.getElementById("led2").setAttribute("stop-color", "rgb(0, " + parseInt(message.payloadString/4) + ", 0)")
        }
      }

        function onFailure(invocationContext, errorCode, errorMessage) {
          var errDiv = document.getElementById("error");
          errDiv.textContent = "Could not connect to WebSocket server, most likely you're behind a firewall that doesn't allow outgoing connections to port 39627";
          errDiv.style.display = "block";
        }
        
        var clientId = "ws" + Math.random();
        // Create a client instance
        var client = new Paho.MQTT.Client("m13.cloudmqtt.com", 31948, clientId);
        
        // set callback handlers
        client.onConnectionLost = onConnectionLost;
        client.onMessageArrived = onMessageArrived;
        
        // connect the client
        client.connect({
          useSSL: true,
          userName: usuario,
          password: contrasena,
          onSuccess: onConnect,
          onFailure: onFailure
        });        
    </script>
  </body>
</html>

Enlaces externos:

http://justgage.com
https://www.flaticon.com/free-icon/one-finger-click-black-hand-symbol_56011
https://www.flaticon.com/free-icon/touch-of-one-finger-of-solid-black-hand-symbol_55421
http://www.downloadclipart.net/svg/18483-led-red-off-svg.svg
https://www.w3schools.com/howto/howto_css_switch.asp
http://www.cssportal.com/style-input-range/