Secuenciador aleatorio con Arduino


Arduino Uno es una placa de microcontrolador basada en el microcontrolador ATmega328P de la compañía Atmel (ahora adquirida por Microchip Technology). Fue lanzada en 2010 y se convirtió en una de las placas más populares en la plataforma Arduino.

La placa Arduino Uno es una plataforma de hardware de código abierto que permite a los usuarios crear proyectos electrónicos interactivos y controlar dispositivos electrónicos usando software y hardware personalizado.

La placa Arduino Uno incluye pines de entrada y salida digitales y analógicos que permiten a los usuarios conectar sensores, actuadores y otros componentes electrónicos para crear proyectos personalizados. La placa se puede programar utilizando el entorno de desarrollo integrado (IDE) de Arduino, que se puede descargar gratuitamente desde el sitio web de Arduino, pero actualmente ya existen otras herramientas de desarrollo.

Vamos a ver una simple aplicación de una placa Arduino Uno conectada una placa de relés, lo cual puede servir como secuenciador por ejemplo para maquetas , juegos de luces, medidas eléctricas, escaparates , escenarios , SIMULAR LA ACTIVIDAD EN UNA VIVIENDA cuando estamos fuera, y un largo etc.

El aspecto del conjunto es el siguiente:

Como vemos en la iamgen anterior, solo usaremos 5 salidas binarias correspondientes a los pines d2, d3, d4, d5 las cuales conectaremos directaemente a las entradas de la placa de relés ( no olvidar la masa ).

Las conexiones no pueden ser mas simples ,porque se conectan las salidas binarias de la placa Arduino pin a pin a la placa de relés sin olvidar la masa o GND.

No recomendamos para nada alimentar desde la propia placa Arduino la placa de relés, porque esta puede consumir según el modelo de placa de relés mas energia de la que puede aportar la salida de Arduino de 5v y además casi todas las placas de relés de cierta calidad se alimentan a 12V, por lo que se recomienda alimentar la placa de relés externamente.

Y ahora presentamos el código Arduino del ejemplo , donde también sacamos por consola los nombres de las cargas que vamos encendiendo y el tiempo de conexión total.

Por supuesto los nombre de los aplicativos a conectar se deberían cambiar con el nombre de los dispositivos eléctricos que conectemos

Es de interes destacar que calculamos cualquier combinación de 5 bits , la cual sacamos por los puertos binarios.

Además del combinación de salidas activadas que es aleatoria, también permanece en un estado de tiempo aleatorio entre 6000 y 100.000 ( la variable se llama tiempoEncendido) así como también hay una breve pausa de todo apagado entre 10.000 y 60000 ( la variable se llama tiempo_pausa), ambas por supuesto que debemos cambiar en función de la utilidad que se desee.

El resto de la funcionalidad del programa es sacar por consola el / o los electrodomésticos a encender , el tiempo que se va a encender y luego apagar así como el tiempo total de funcionamiento

El código completo de todo el programa es el siguiente, el cual se debería adaptar y personalizar a las necesidades de cada caso concreto:




int soldador = 2;
int luz_led = 3;
int luz_halogena = 4;
int ordenador = 5;
int ventilador = 6;

unsigned long tiempoEncendidoTotal = 0; // tiempo total que se va invirtiendo en milisegundos

void setup() {
  Serial.begin(115200);
  Serial.println("Iniciando ..");
 
  pinMode(soldador, OUTPUT);
  pinMode(luz_led, OUTPUT);
  pinMode(luz_halogena, OUTPUT);
  pinMode(ordenador, OUTPUT);
  pinMode(ventilador, OUTPUT);
  
  randomSeed(analogRead(A0));


  digitalWrite(soldador, HIGH); // apaga el soldador
    digitalWrite(luz_led, HIGH); // apaga la luz LED
 digitalWrite(luz_halogena, HIGH); // apaga la luz halógena
   digitalWrite(ordenador, HIGH); // apaga el ordenador
digitalWrite(ventilador, HIGH); // apaga el ventilador


 delay (1000);
}

void loop() {
  long tiempo_pausa = random(10000, 60000); // tiempo aleatorio entre 10 y 60 segundos en milisegundos
  int dispositivo = random(0, 33); // número aleatorio entre 0 y 32
  Serial.print("Numero generado: ");
  Serial.println(dispositivo);
  String binaryString = String(dispositivo, BIN);
     Serial.print("Cadena:");
      Serial.println(binaryString);
      
  

   

  if (dispositivo & (1 << 0)) { // verifica si el bit 0 está activado
    digitalWrite(soldador, LOW); // enciende el soldador
    Serial.println("Soldador encendido");
  } else {
    digitalWrite(soldador, HIGH); // apaga el soldador
  }

  if (dispositivo & (1 << 1)) { // verifica si el bit 1 está activado
    digitalWrite(luz_led, LOW); // enciende la luz LED
    Serial.println("Luz LED encendida");
  } else {
    digitalWrite(luz_led, HIGH); // apaga la luz LED
  }

  if (dispositivo & (1 << 2)) { // verifica si el bit 2 está activado
    digitalWrite(luz_halogena, LOW); // enciende la luz halógena
    Serial.println("Luz halógena encendida");
  } else {
    digitalWrite(luz_halogena, HIGH); // apaga la luz halógena
  }

  if (dispositivo & (1 << 3)) { // verifica si el bit 3 está activado
    digitalWrite(ordenador, LOW); // enciende el ordenador
        Serial.println("Ordenador encendido");
  } else {
    digitalWrite(ordenador, HIGH); // apaga el ordenador
  }

  if (dispositivo & (1 << 4)) { // verifica si el bit 4 está activado
    digitalWrite(ventilador, LOW); // enciende el ventilador
       Serial.println("Ventilador encendido");
  } else {
    digitalWrite(ventilador, HIGH); // apaga el ventilador
  }



 
long tiempoEncendido = random(6000, 300000); //genera un número aleatorio entre 1 minuto (60000ms) y 5 minutos (300000ms)
  
Serial.print("Tiempo encendido nuevo estado: ");
Serial.println(tiempoEncendido/1000);


 Serial.print(" Tiempo de pausa tras nuevo estado: ");
  Serial.print(tiempo_pausa/1000); // muestra el tiempo en segundos
  Serial.println(" segundos");

  tiempoEncendidoTotal += tiempoEncendido;
  unsigned long tiempoAcumulado = tiempoEncendidoTotal / 1000;
  unsigned int horas = tiempoAcumulado / 3600;
  unsigned int minutos = (tiempoAcumulado % 3600) / 60;
  unsigned int segundos = tiempoAcumulado % 60;

 

delay(tiempoEncendido); //espera el tiempo aleatorio generado


  

  
  digitalWrite(soldador, HIGH);
  digitalWrite(luz_led, HIGH);
  digitalWrite(luz_halogena, HIGH);
  digitalWrite(ordenador, HIGH);
  digitalWrite(ventilador, HIGH);

  




  delay(tiempo_pausa);
   Serial.print("Tiempo encendido total: ");
  Serial.print(horas);
  Serial.print(":");
  if (minutos < 10) {
    Serial.print("0");
  }
  Serial.print(minutos);
  Serial.print(":");
  if (segundos < 10) {
    Serial.print("0");
  }
  Serial.println(segundos);
}

Extracto

Este código, escrito en el IDE de Arduino , controla el estado de encendido/apagado de varios dispositivos (soldador, luz_led, luz_halogena, ordenador, ventilador) basándose en números binarios generados aleatoriamente. Los dispositivos están conectados a pines digitales específicos de la placa Arduino y se encienden/apagan cambiando la señal digital de estos pines de alta a baja (encendido) o de baja a alta (apagado).

El programa también realiza un seguimiento del tiempo total de encendido de todos los dispositivos en segundos y lo muestra en horas, minutos y segundos. El tiempo de encendido de cada dispositivo se genera aleatoriamente y varía entre 1 minuto (6000ms) y 5 minutos (300000ms), mientras que el tiempo de pausa entre los tiempos de encendido de cada dispositivo también se genera aleatoriamente entre 10 y 60 segundos.

El programa comienza inicializando todos los dispositivos en el estado de apagado, luego entra en un bucle infinito que genera un nuevo número binario y enciende/apaga los dispositivos en consecuencia, basándose en el estado de cada bit en el número binario. Después de cada período de encendido, todos los dispositivos se apagan y se inserta un tiempo de pausa antes de que comience el siguiente ciclo.

Anuncio publicitario

Gestion de tarjetas SD con ESP32


Es obvio que las SD se han convertido en algo indispensable en nuestra vida digital , estando presentes en infinidad de dispositivos electrónicos como smartphones, cámaras digitales, cámaras de seguridad, reproductores multimedia, ordenadores, microcontroladores, y un larguísimo etcétera.

Por otro lado, de vez en cuando nos encontramos con proyectos basados en ESP32 ( y todas sus variantes) que necesitan una forma de almacenar una gran cantidad de datos de forma escalable y eficiente (es decir, necesitamos construir lo que viene a llamarse un registrador de datos o en ingle «datalogger»), siendo lo ideal por tanto usar con nuestro microntrolador precisamente estas tarjetas SD o micro SD, dada su gran capacidad para empaquetar GigaBytes de datos en un espacio más pequeño que una moneda.

SPI es un acrónimo que significa «Serial Peripheral Interface». Es un protocolo de comunicación serie que se utiliza para transferir datos entre dispositivos electrónicos, como microcontroladores, sensores, dispositivos de almacenamiento, etc. SPI permite a varios dispositivos compartir un canal de comunicación y cada dispositivo puede actuar como esclavo o maestro. El protocolo SPI es conocido por ser rápido y flexible, y por tener una arquitectura sencilla que permite una fácil integración en una amplia gama de aplicaciones electrónicas. Además, el protocolo SPI es compatible con una amplia gama de velocidades de transmisión de datos, lo que lo hace adecuado para una variedad de aplicaciones con diferentes requisitos de velocidad y capacidad de procesamiento.

Esp32

El ESP32 es un microcontrolador que incluye una interfaz de comunicación SPI, lo que significa que puede utilizar el protocolo SPI para comunicarse con otros dispositivos electrónicos. Aquí hay algunos de los usos más comunes de la interfaz SPI en el ESP32:

  1. Comunicación con sensores: El ESP32 puede utilizar la interfaz SPI para comunicarse con sensores, como acelerómetros, giroscopios, sensores de temperatura, etc.
  2. Comunicación con dispositivos de almacenamiento: El ESP32 puede utilizar la interfaz SPI para comunicarse con dispositivos de almacenamiento, como tarjetas SD o dispositivos Flash, para leer y escribir datos.
  3. Comunicación con pantallas táctiles: El ESP32 puede utilizar la interfaz SPI para comunicarse con pantallas táctiles y controlar la visualización y la entrada de datos.
  4. Comunicación con dispositivos de radiofrecuencia: El ESP32 puede utilizar la interfaz SPI para comunicarse con dispositivos de radiofrecuencia, como módulos Bluetooth o Wi-Fi, para transferir datos a larga distancia.

En resumen, la interfaz SPI en el ESP32 se puede utilizar para una amplia gama de aplicaciones, lo que lo hace una herramienta valiosa para desarrollar proyectos electrónicos y de Internet de las cosas.

En este post vamos a ver que en realidad leer o escribir datos en una SD ( o micro-sd) en el entorno de la familia Arduino es en realidad muy sencillo gracias a las librería SD y también al protocolo SPI para la comunicación con el lector de SD.

Descripción general del hardware.

Para este proyecto se ha probado el lector de sd de AZDelivery el cual nos proporciona una expansión fácil y económica del espacio de almacenamiento mediante la ranura SD. La comunicación es muy fácil con el microcontrolador a través del protocolo SPI (como vamos a ver ) y soporta tarjetas Micro SD (2G), tarjetas Micro SDHC (32G) (tarjeta de alta velocidad). Además el módulo lee todos los datos contenidos en la tarjeta SD y se puede conectar fácilmente a varios tipos de microcontroladores ( además si lo compramos a este fabricante incluye un E-Book que proporciona información útil sobre cómo comenzar su proyecto, ayuda con una configuración rápida y ahorra tiempo en el proceso de configuración proporcionándonos una serie de ejemplos de aplicación, guías de instalación completas y bibliotecas, etc.).

aspecto lector sd

El módulo de la tarjeta micro SD contiene dos componentes principales que, sin duda, hacen que sea fácil agregar el registro de datos a su próximo proyecto Arduino:

  • El voltaje de funcionamiento de cualquier tarjeta micro SD estándar es de 3,3 V. Por lo tanto, no podemos conectarlo directamente a circuitos que usan lógica de 5V. De hecho, cualquier voltaje que supere los 3,6 V dañará permanentemente la tarjeta micro SD. Es por eso; el módulo tiene un regulador de caída ultrabaja incorporado que convertirá los voltajes de 3,3 V a 6 V a ~3,3 V.
  • También hay un chip 74LVC125A en el módulo que convierte la lógica de la interfaz de 3,3 V-5 V a 3,3 V. Esto se llama cambio de nivel lógico. Eso significa que puede usar esta placa para interactuar con microcontroladores de 3,3 V y 5 V como Arduino.

En realidad, hay dos formas de interactuar con tarjetas micro SD: modo SPI y modo SDIO. El modo SDIO es mucho más rápido y se usa en teléfonos móviles, cámaras digitales, etc, pero desgraciadamente este modo es más complejo y requiere la firma de documentos de confidencialidad( por esa razón, es probable que los aficionados como nosotros nunca encuentren el código de interfaz del modo SDIO). En su lugar, cada módulo de tarjeta SD se basa en el modo SPI de «menor velocidad y menos sobrecarga» que es fácil de usar para cualquier microcontrolador (como Arduino).

Asignación de pines del módulo de la tarjeta Micro SD

El módulo de la tarjeta micro SD que vamos a usar es bastante simple de conectar. Tiene seis pines:

parte atras lector sd
  • VCC :El pin que suministra energía para el módulo y debe conectarse al pin de 5V en el Arduino.
  • GND (TIERRA): debe estar conectado a tierra de Arduino.
  • MISO (Master In Slave Out): es la salida SPI del módulo de tarjeta Micro SD.
  • MOSI (Salida maestra Entrada esclava): es la entrada SPI al módulo de tarjeta Micro SD.
  • SCK (reloj serie): pin acepta pulsos de reloj que sincronizan la transmisión de datos generada por Arduino.
  • CS (Selección de esclavo): es utilizado por Arduino (Master) para habilitar y deshabilitar dispositivos específicos en el bus SPI.

Preparación de la tarjeta micro SD

Antes de insertar la tarjeta micro SD en el módulo y conectarla al Arduino, debe formatear correctamente la tarjeta. Para la biblioteca Arduino que discutiremos, y casi todas las demás bibliotecas SD, la tarjeta debe estar formateada FAT16 o FAT32.

Si tiene una tarjeta SD nueva, es probable que ya esté formateada previamente con un sistema de archivos FAT. Sin embargo, es posible que tenga problemas con el formato de fábrica de la tarjeta o, si es una tarjeta antigua, debe reformatearse. De cualquier manera, siempre es una buena idea formatear la tarjeta antes de usarla, ¡incluso si es nueva!

Es recomendable utilizar la utilidad oficial de formateo de tarjetas SD : escrita por la asociación SD , ¡resuelve muchos problemas que surgen con un mal formateo! Descargue el formateador y ejecútelo en su ordenador, simplemente seleccione la unidad correcta y haga clic en FORMATEAR.

Captura de pantalla del formateador SD

Cableado: conexión del módulo de tarjeta Micro SD a Arduino

Ahora que su tarjeta está lista para usar, ¡podemos conectar la placa de conexión micro SD!

Para empezar, inserte el módulo de la tarjeta micro SD en una placa de pruebas. Conecte el pin VCC en el módulo a 5V en el pin Arduino y GND a tierra. Ahora nos quedamos con los pines que se usan para la comunicación SPI. Como las tarjetas micro SD requieren una gran cantidad de transferencia de datos, brindarán el mejor rendimiento cuando se conecten a los pines SPI del hardware en un microcontrolador. Los pines SPI del hardware son mucho más rápidos que «bit-banging» del código de la interfaz usando otro conjunto de pines.

Tenga en cuenta que cada placa Arduino tiene diferentes pines SPI que deben conectarse en consecuencia. Para placas ESP32 (En el ejemplo DOIT ESP32 DEVKIT1) esos pines son digitales 18 (SCK), 19 (MISO) y 23 (MOSI). También necesitará un cuarto pin para la línea ‘chip/slave select’ (SS). Por lo general, este es el pin 5, pero en realidad puede usar cualquier pin que desee (siempre que lo inicialize en el código).

Si tiene una placa Mega, ¡los pines son diferentes! Querrá usar digital 50 (MISO), 51 (MOSI), 52 (SCK) y 53 (SS). Consulte la siguiente tabla para una comprensión rápida.

MicroSD card moduleESP32
VCC+5v
CSGPIO 5
MOSIGPIO 23
CLKGPIO 18
MISOGPIO 19
GNDGND

En caso de que esté utilizando una placa Arduino diferente a la mencionada anteriormente, es recomendable consultar la documentación oficial de Arduino antes de continuar.

Para cablear el módulo de la tarjeta microSD a la placa ESP32, puede seguir el siguiente diagrama esquemático (para los pines SPI por defecto de ESP32):

esquema conexiones lector sd

Realmente dadas las poquísimas conexiones necesarias, el montaje se puede hacer directamente con un cable de cinta de 6 hilos como podemos ver en la imagen .

aspecto del montaje del lector de sd

Código Arduino: prueba del módulo de la tarjeta SD con CardInfo

Existen dos librerías diferentes para el ESP32 (incluidas en el núcleo de Arduino para el ESP32): la librería SD y la librería SDD_MMC.h.

Si utiliza la biblioteca SD, está utilizando el controlador SPI. Si usa la librería SDD_MMC estás usando el controlador SD/SDIO/MMC del ESP32.

Hay varios ejemplos en Arduino IDE que muestran cómo manejar archivos en la tarjeta microSD usando el ESP32. En el IDE de Arduino, vaya a Archivo > Ejemplos > SD(esp32) > SD_Test, o copie el siguiente código:


/*
    This sketch can be found at: Examples > SD(esp32) > SD_Test
*/

#include "FS.h"
#include "SD.h"
#include "SPI.h"

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
  Serial.printf("Listing directory: %s\n", dirname);

  File root = fs.open(dirname);
  if(!root){
    Serial.println("Failed to open directory");
    return;
  }
  if(!root.isDirectory()){
    Serial.println("Not a directory");
    return;
  }

  File file = root.openNextFile();
  while(file){
    if(file.isDirectory()){
      Serial.print("  DIR : ");
      Serial.println(file.name());
      if(levels){
        listDir(fs, file.name(), levels -1);
      }
    } else {
      Serial.print("  FILE: ");
      Serial.print(file.name());
      Serial.print("  SIZE: ");
      Serial.println(file.size());
    }
    file = root.openNextFile();
  }
}

void createDir(fs::FS &fs, const char * path){
  Serial.printf("Creating Dir: %s\n", path);
  if(fs.mkdir(path)){
    Serial.println("Dir created");
  } else {
    Serial.println("mkdir failed");
  }
}

void removeDir(fs::FS &fs, const char * path){
  Serial.printf("Removing Dir: %s\n", path);
  if(fs.rmdir(path)){
    Serial.println("Dir removed");
  } else {
    Serial.println("rmdir failed");
  }
}

void readFile(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\n", path);

  File file = fs.open(path);
  if(!file){
    Serial.println("Failed to open file for reading");
    return;
  }

  Serial.print("Read from file: ");
  while(file.available()){
    Serial.write(file.read());
  }
  file.close();
}

void writeFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Writing file: %s\n", path);

  File file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file for writing");
    return;
  }
  if(file.print(message)){
    Serial.println("File written");
  } else {
    Serial.println("Write failed");
  }
  file.close();
}

void appendFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Appending to file: %s\n", path);

  File file = fs.open(path, FILE_APPEND);
  if(!file){
    Serial.println("Failed to open file for appending");
    return;
  }
  if(file.print(message)){
      Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}

void renameFile(fs::FS &fs, const char * path1, const char * path2){
  Serial.printf("Renaming file %s to %s\n", path1, path2);
  if (fs.rename(path1, path2)) {
    Serial.println("File renamed");
  } else {
    Serial.println("Rename failed");
  }
}

void deleteFile(fs::FS &fs, const char * path){
  Serial.printf("Deleting file: %s\n", path);
  if(fs.remove(path)){
    Serial.println("File deleted");
  } else {
    Serial.println("Delete failed");
  }
}

void testFileIO(fs::FS &fs, const char * path){
  File file = fs.open(path);
  static uint8_t buf[512];
  size_t len = 0;
  uint32_t start = millis();
  uint32_t end = start;
  if(file){
    len = file.size();
    size_t flen = len;
    start = millis();
    while(len){
      size_t toRead = len;
      if(toRead > 512){
        toRead = 512;
      }
      file.read(buf, toRead);
      len -= toRead;
    }
    end = millis() - start;
    Serial.printf("%u bytes read for %u ms\n", flen, end);
    file.close();
  } else {
    Serial.println("Failed to open file for reading");
  }


  file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file for writing");
    return;
  }

  size_t i;
  start = millis();
  for(i=0; i<2048; i++){
    file.write(buf, 512);
  }
  end = millis() - start;
  Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
  file.close();
}

void setup(){
  Serial.begin(115200);
  if(!SD.begin(5)){
    Serial.println("Card Mount Failed");
    return;
  }
  uint8_t cardType = SD.cardType();

  if(cardType == CARD_NONE){
    Serial.println("No SD card attached");
    return;
  }

  Serial.print("SD Card Type: ");
  if(cardType == CARD_MMC){
    Serial.println("MMC");
  } else if(cardType == CARD_SD){
    Serial.println("SDSC");
  } else if(cardType == CARD_SDHC){
    Serial.println("SDHC");
  } else {
    Serial.println("UNKNOWN");
  }

  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);

  listDir(SD, "/", 0);
  createDir(SD, "/mydir");
  listDir(SD, "/", 0);
  removeDir(SD, "/mydir");
  listDir(SD, "/", 2);
  writeFile(SD, "/hello.txt", "Hello ");
  appendFile(SD, "/hello.txt", "World!\n");
  readFile(SD, "/hello.txt");
  deleteFile(SD, "/foo.txt");
  renameFile(SD, "/hello.txt", "/foo.txt");
  readFile(SD, "/foo.txt");
  testFileIO(SD, "/test.txt");
  Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
  Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
}

void loop(){

}

Este ejemplo muestra cómo realizar casi cualquier tarea que pueda necesitar con la tarjeta microSD:

  • Listar un directorio;
  • Crear un directorio;
  • Eliminar un directorio;
  • Leer el contenido de un archivo;
  • Escribir contenido en un archivo
  • Añadir contenido a un archivo;
  • Renombrar un fichero;
  • Eliminar un archivo;
  • Inicializar tarjeta microSD;
  • Obtener tipo de tarjeta microSD;
  • Obtener el tamaño de la tarjeta microSD;


Alternativamente, puede utilizar los ejemplos SD_MMC – son similares a los ejemplos SD, pero utilizan el controlador SDMMC. Para el controlador SDMMC, necesita un módulo de tarjeta microSD compatible. El módulo que utilizamos en este post no es compatible con SDMMC.

Todos los ejemplos que vamos a a ver por tanto proporciona varias funciones para manejar archivos en la tarjeta microSD.

Listar un directorio


La función listDir() lista los directorios de la tarjeta SD. Esta función acepta como argumentos el sistema de archivos (SD), el nombre del directorio principal y los niveles para entrar en el directorio.

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
  Serial.printf("Listing directory: %s\n", dirname);

  File root = fs.open(dirname);
  if(!root){
    Serial.println("Failed to open directory");
    return;
  }
  if(!root.isDirectory()){
    Serial.println("Not a directory");
    return;
  }

  File file = root.openNextFile();
  while(file){
    if(file.isDirectory()){
      Serial.print("  DIR : ");
      Serial.println(file.name());
      if(levels){
        listDir(fs, file.name(), levels -1);
      }
    } else {
      Serial.print("  FILE: ");
      Serial.print(file.name());
      Serial.print("  SIZE: ");
      Serial.println(file.size());
    }
     file = root.openNextFile();
  }
}

He aquí un ejemplo de cómo llamar a esta función .

listDir(SD, "/", 0);

Obsérvese que es muy importante la barra invertida antes del directorio pues eso indica que el directorio esta en el directorio raiz .

Crear un directorio
La función createDir() crea un nuevo directorio. Pasa como argumento el sistema de ficheros SD y la ruta del nombre del directorio.

void createDir(fs::FS &fs, const char * path){
  Serial.printf("Creating Dir: %s\n", path);
  if(fs.mkdir(path)){
    Serial.println("Dir created");
  } else {
    Serial.println("mkdir failed");
  }
}

Por ejemplo, el siguiente comando crea un nuevo directorio en la raíz llamado mydir

createDir(SD, "/mydir");

Obsérvese que es muy importante la barra invertida antes del directorio pues eso indica que el directorio esta en el directorio raiz (y se llama mydir).

Eliminar un directorio
Para eliminar un directorio de la tarjeta microSD, utilice la función removeDir() y pase como argumento el sistema de archivos de la SD y la ruta del nombre del directorio.

void removeDir(fs::FS &fs, const char * path){
  Serial.printf("Removing Dir: %s\n", path);
  if(fs.rmdir(path)){
    Serial.println("Dir removed");
  } else {
    Serial.println("rmdir failed");
  }
}

He aquí un ejemplo:

removeDir(SD, "/mydir");

Obsérvese que es muy importante la barra invertida antes del directorio pues eso indica que el directorio esta en el directorio raiz (y se llama mydir).

Leer el contenido de un archivo
La función readFile() lee el contenido de un archivo e imprime el contenido en el Monitor Serial. Como en las funciones anteriores, pase como argumento el sistema de ficheros SD y la ruta del fichero.

void readFile(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\n", path);

  File file = fs.open(path);
  if(!file){
    Serial.println("Failed to open file for reading");
    return;
  }

  Serial.print("Read from file: ");
  while(file.available()){
    Serial.write(file.read());
  }
  file.close();
}

Por ejemplo, la siguiente línea lee el contenido del archivo hello.txt.

readFile(SD, "/hello.txt")

Obsérvese que es muy importante la barra invertidas antes del fichero pues eso indica que el fichero esta en el directorio raiz (y se llama hello.txt).

Escribir contenido en un archivo
Para escribir contenido en un fichero, puede utilizar la función writeFile(). Pase como argumento el sistema de ficheros SD, la ruta del fichero y el mensaje.

void writeFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Writing file: %s\n", path);

  File file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file for writing");
    return;
  }
  if(file.print(message)){
    Serial.println("File written");
  } else {
    Serial.println("Write failed");
  }
  file.close();
}

La siguiente línea escribe Hola en el archivo hola.txt.

writeFile(SD, "/hello.txt", "Hello ");

Obsérvese que es muy importante la barra invertidas antes del fichero pues eso indica que el fichero esta en el directorio raiz (y se llama hello.txt).

Añadir contenido a un archivo
Del mismo modo, puede añadir contenido a un archivo (sin sobrescribir el contenido anterior) utilizando la función appendFile().

void appendFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Appending to file: %s\n", path);

  File file = fs.open(path, FILE_APPEND);
  if(!file){
    Serial.println("Failed to open file for appending");
    return;
  }
  if(file.print(message)){
    Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}

La siguiente línea añade el mensaje ¡Mundo!\n en el archivo hola.txt. El \n significa que la próxima vez que escriba algo en el archivo, se escribirá en una nueva línea.

appendFile(SD, "/hello.txt", "World!\n");

Cambiar el nombre de un archivo
Puede renombrar un fichero utilizando la función renameFile(). Pasa como argumentos el sistema de ficheros SD, el nombre original del fichero y el nuevo nombre.

void renameFile(fs::FS &fs, const char * path1, const char * path2){
  Serial.printf("Renaming file %s to %s\n", path1, path2);
  if (fs.rename(path1, path2)) {
    Serial.println("File renamed");
  } else {
    Serial.println("Rename failed");
  }
}

La siguiente línea cambia el nombre del archivo hello.txt a foo.txt.

renameFile(SD, "/hello.txt", "/foo.txt");

Borrar un fichero
Utilice la función deleteFile() para borrar un fichero. Pase como argumento el sistema de ficheros SD y la ruta del fichero que desea borrar.

void deleteFile(fs::FS &fs, const char * path){
  Serial.printf("Deleting file: %s\n", path);
  if(fs.remove(path)){
    Serial.println("File deleted");
  } else {
    Serial.println("Delete failed");
  }
}

La siguiente línea borra el archivo foo.txt de la tarjeta microSD.

deleteFile(SD, "/foo.txt");

Probar un archivo
Por último la función testFileIO() muestra el tiempo que se tarda en leer el contenido de un fichero.

void testFileIO(fs::FS &fs, const char * path){
  File file = fs.open(path);
  static uint8_t buf[512];
  size_t len = 0;
  uint32_t start = millis();
  uint32_t end = start;
  if(file){
    len = file.size();
    size_t flen = len;
    start = millis();
    while(len){
      size_t toRead = len;
      if(toRead > 512){
        toRead = 512;
      }
      file.read(buf, toRead);
      len -= toRead;
    }
    end = millis() - start;
    Serial.printf("%u bytes read for %u ms\n", flen, end);
    file.close();
  } 
  else {
    Serial.println("Failed to open file for reading");
  }

  file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file for writing");
    return;
  }

  size_t i;
  start = millis();
  for(i=0; i<2048; i++){
    file.write(buf, 512);
  }
  end = millis() - start;
  Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
  file.close();
}

La siguiente función comprueba el archivo test.txt.

testFileIO(SD, "/test.txt");

Inicializar la tarjeta microSD
En setup(), las siguientes líneas inicializan la tarjeta microSD con SD.begin().

Serial.begin(115200);
if(!SD.begin()){
  Serial.println("Card Mount Failed");
  return;
}
uint8_t cardType = SD.cardType();

if(cardType == CARD_NONE){
  Serial.println("No SD card attached");
  return;
}

Si no pasa ningún argumento a la función begin(), intentará inicializar la comunicación SPI con la tarjeta microSD en el pin por defecto de selección de chip (CS). Si desea utilizar otro pin CS, puede pasarlo como argumento a la función begin(). Por ejemplo, si quiere usar GPIO 17 como pin CS, debería usar las siguientes líneas de código:

Serial.begin(115200);
if(!SD.begin(17)){
  Serial.println("Card Mount Failed");
  return;
}
uint8_t cardType = SD.cardType();

Obtener el tipo de tarjeta microSD
Las siguientes líneas imprimen el tipo de tarjeta microSD en el Monitor Serial.

Serial.print("SD Card Type: ");
if(cardType == CARD_MMC){
  Serial.println("MMC");
} else if(cardType == CARD_SD){
  Serial.println("SDSC");
} else if(cardType == CARD_SDHC){
  Serial.println("SDHC");
} else {
  Serial.println("UNKNOWN");
}

Obtener el tamaño de la tarjeta microSD
Puedes obtener el tamaño de la tarjeta microSD llamando al método cardSize():

uint64_t cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);

Para probar las funciones de la tarjeta MicroSD,las siguientes líneas llaman a las funciones que hemos visto anteriormente:

listDir(SD, "/", 0);
createDir(SD, "/mydir");
listDir(SD, "/", 0);
removeDir(SD, "/mydir");
listDir(SD, "/", 2);
writeFile(SD, "/hello.txt", "Hello ");
appendFile(SD, "/hello.txt", "World!\n");
readFile(SD, "/hello.txt");
deleteFile(SD, "/foo.txt");
renameFile(SD, "/hello.txt", "/foo.txt");
readFile(SD, "/foo.txt");
testFileIO(SD, "/test.txt");
Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));


Suba el sketch anterior a tu placa ESP32. Después de eso, abra el Monitor Serie y pulse el botón RST de la placa ESP32. Si la inicialización tiene éxito, obtendrá mensajes similares a los siguientes en el Monitor Serie.

Funciones utilizadas con el objeto Archivo

Además, hay algunas funciones que puede usar con objetos de archivo :

  • Puede  usar seek()en un archivo. Esto moverá el cursor de lectura/escritura a una nueva ubicación. Por ejemplo seek(0) , lo llevará al principio del archivo, ¡lo cual puede ser muy útil!
  • Así mismo puedes llamar al position()que te indicará en qué parte del expediente te encuentras.
  • Si desea saber el tamaño de un archivo, llame size() para obtener la cantidad de bytes en el archivo.
  • Los directorios/carpetas son archivos especiales, puede determinar si un archivo es un directorio llamando isDirectory()
  • Una vez que tenga un directorio, puede comenzar a revisar todos los archivos en el directorio llamando openNextFile()
  • Puede terminar necesitando saber el nombre de un archivo, por ejemplo, si llamó openNextFile()a un directorio. En este caso, llame al name()que devolverá un puntero a la matriz de caracteres con formato 8.3 que puede hacer directamente Serial.print() si lo desea.
Por cierto , aunque pueda parecer extraño, es más económico comprar varios módulos que un único ( por unos 2€ en Amazon )

Como gestionar un LCD con Arduino


Tradicionalmente para visualizar información con microcontroladores se han usado leds, displays de leds de 7segmentos, «tubos nixie!,LCD , pantallas OLED, etc

Hay varias ventajas para usar pantallas LCD frente a otros medios:

  1. Fácil visualización de datos: La pantalla LCD permite una fácil visualización de los datos generados o recibidos por el Arduino, lo que simplifica la tarea de monitoreo y análisis.
  2. Interfaz de usuario: Las pantallas LCD pueden ser utilizadas para crear interfaces de usuario amigables para proyectos con Arduino, lo que hace más fácil para los usuarios interactuar con el sistema.
  3. Versatilidad: Las pantallas LCD pueden ser utilizadas en una amplia variedad de proyectos, desde monitoreo de sensores hasta juegos y aplicaciones de tiempo real.
  4. Ahorro de espacio: Las pantallas LCD son más compactas y requieren menos espacio que otras opciones de visualización, lo que las hace ideales para proyectos con restricciones de espacio.
  5. Fácil conexión: La conexión de una pantalla LCD con una placa Arduino es generalmente sencilla y requiere pocos componentes adicionales.
  6. Bajo consumo de energía: Las pantallas LCD utilizan poca energía y son adecuadas para proyectos con restricciones de energía o con requisitos de larga duración.

El principio detrás de las pantallas LCD para Arduino es el uso de la tecnología de cristal líquido para desplegar información en una pantalla. Una pantalla LCD para Arduino funciona mediante la aplicación de una tensión a los cristales líquidos en la pantalla, lo que los hace inclinarse y permitir o bloquear la luz detrás de ellos. De esta manera, se pueden crear diferentes patrones de luz y oscuridad en la pantalla para formar caracteres o gráficos.

Una placa Arduino (o cualquier variante) controla la tensión aplicada a los cristales líquidos a través de un controlador LCD específico, que envía señales a los pines de la pantalla LCD. El controlador recibe y procesa los datos enviados por el Arduino y los convierte en una señal que controla la tensión aplicada a los cristales líquidos. De esta manera, se puede desplegar información en tiempo real en la pantalla LCD.

Una pantalla LCD con una placa Arduino puede ser utilizada para desplegar información o datos en tiempo real que sean generados o recibidos por el Arduino. Esto permite una visualización más clara y accesible de los datos y puede ser utilizado en una amplia variedad de proyectos, como monitoreo de sensores, interfaces de usuario, juegos, entre otros.

La placa GeekPi ( LCD1602)

Para que sirva de ejemplo, vamos a estudiar la placa GeekPi que puede comprarse en Amazon por unos 5€/unidad aqui. Esta placa utiliza el protocolo I2C para reducir la ocupación de los puertos de E / S , lo que facilita la adición al proyecto y menos cableado lo cual es fantástico ya que tradicionalmente se usaban en función del bus de 4 o 8 bits al menos 7 pines de E/S. Esta placa es comúnmente utilizado en IoT, proyectos de bricolaje, animación doméstica, construcción inteligente, proyecto de bricolaje del fabricante, etc.

Asimismo esta placa es compatible con todas las placas de desarrollo actuales, como Arduino (y todas sus variantes incluyendo ESP32, ESP8266, ECT ) , Raspberry pi, Tinkerboard, Nano pi, Banana pi, stm32, etc.

Respecto a ajustes, tan solo necesita un potenciómetro que hay detrás del lcd donde podemos ajustar la retroiluminación (color: azul) y el contraste.

La alimentación: 5v y la dirección I2C es: 0x27 y sus dimensiones son : 80 mm x 35 mm x 11 mm

En caso de conectar este lcd a una placa Arduino (o similares como ESP32, ESP8266, etc. ), las conexiones son las siguientes

Para otras placas como la Raspberry, como vemos en la imagen la conexión es similar:

Como vemos el LCD1602 usa I²C utilizando sólo dos líneas bidireccionales open-drain, Serial Data Line (SDA) y Serial Clock Line (SCL),con resistencias. Los voltajes típicos utilizados son +5 V o +3,3 V, aunque se permiten sistemas con otros voltajes. Puede funcionar siempre que sea compatible con la placa de desarrollo I2C como ya se ha comentado (las comunes Arduino, Raspberry pi, Stm32, etc.).

Este es un resumen de sus características:

  • Ocupa menos puertos de E/S.
  • Soporta el protocolo IIC.
  • La biblioteca I2C LCD1602 es fácil de conseguir
  • Con un potenciómetro para ajustar la retroiluminación y el contraste
  • Retroiluminación azul
  • uente de alimentación 5v
  • La dirección I2C es: la nueva versión es 0x3F, la versión antigua es 0x27

Cómo conectarlo a Arduino


Conecte el módulo LCD1602 a la placa Arduino Uno como se muestra en la siguiente imagen:
I2C LCD1602 placa Arduino Uno
GND GND
VCC 5V
SDA A4 /pin 20 mega2560
SCL A5 /pin 21 mega2560

Cómo programarlo en Arduino


1.Descarga la librería LiquidCrystal_I2C
2.Abre el IDE de Arduino,Selecciona Sketch -> Include Library -> Add ZIP Library
3.Encuentra el archivo LiquidCrystal_I2C que acabas de descargar.
4.Haga clic en abrir y, a continuación, se le pedirá por «Biblioteca añadido a sus bibliotecas.
5.Marque ‘Importar bibliotecas'».
6.Abra el archivo ->Sketch->Include Library->LiquidCrystal_I2C.
7.Copia el siguiente código en el IDE de Arduino, haz click en el icono upload para subir el código a la placa de control:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27, if new version please use 0x3F instead.

void setup()
{
  lcd.init();  //initialize the lcd
  lcd.backlight();  //open the backlight
}

void loop()
{
  lcd.setCursor(3, 0); // set the cursor to column 3, line 0
  lcd.print("Hello GeeekPi");  // Print a message to the LCD
  
  lcd.setCursor(2, 1); // set the cursor to column 2, line 1
  lcd.print("hello world");  // Print a message to the LCD.
}

POSICIONAR EL TEXTO
La función mylcd.lcd_display_string() imprime texto en la pantalla y también permite elegir dónde posicionarlo.
La función se usa como mylcd.lcd_display_string(«TEXTO A IMPRIMIR», FILA, COLUMNA).
El siguiente código imprime «¡Hola Mundo!» en la fila 2, columna 3:
En un LCD de 16×2, las filas están numeradas del 1 al 2, mientras que las columnas están numeradas del 0 al 15. Por lo tanto, para imprimir «¡Hola Mundo!
Así que para imprimir «¡Hola Mundo!» en la primera columna de la fila superior, usted usaría

mylcd.lcd_display_string(“Hello World!”, 1, 0)

BORRAR LA PANTALLA
La función mylcd.lcd_clear() borra la pantalla

import I2C_LCD_driver
from time import *
mylcd = I2C_LCD_driver.lcd()
mylcd.lcd_display_string("This is how you", 1)
sleep(1)
mylcd.lcd_clear()
mylcd.lcd_display_string("clear the screen", 1)
sleep(1)
mylcd.lcd_clear()

TEXTO PARPADEANTE
Podemos utilizar un simple bucle while con las funciones mylcd.lcd_display_string() y mylcd.lcd_clear() para crear un efecto de texto parpadeante continuo:

import time
import I2C_LCD_driver
mylcd = I2C_LCD_driver.lcd()

while True:
    mylcd.lcd_display_string(u"Hello world!")
    time.sleep(1)
    mylcd.lcd_clear()
    time.sleep(1)


Puede usar la función time.sleep() en la línea 7 para cambiar el tiempo (en segundos) que el texto permanece encendido.
El tiempo que el texto permanece apagado se puede cambiar en la función time.sleep() en la línea 9.
Para finalizar el programa, pulse Ctrl-C.

IMPRIMIR LA FECHA Y LA HORA
El siguiente programa imprime la fecha y hora actual en la pantalla LCD:

import I2C_LCD_driver
import time
mylcd = I2C_LCD_driver.lcd()


while True:
    mylcd.lcd_display_string("Time: %s" %time.strftime("%H:%M:%S"), 1)
    
    mylcd.lcd_display_string("Date: %s" %time.strftime("%m/%d/%Y"), 2)

IMPRIME TU DIRECCIÓN IP
Este código imprime la dirección IP de tu conexión ethernet (eth0).
Para imprimir la IP de tu conexión WiFi, cambia eth0 por wlan0 en la línea 18:

import I2C_LCD_driver
import socket
import fcntl
import struct

mylcd = I2C_LCD_driver.lcd()

def get_ip_address(ifname):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    return socket.inet_ntoa(fcntl.ioctl(
        s.fileno(),
        0x8915, 
        struct.pack('256s', ifname[:15])
    )[20:24])

mylcd.lcd_display_string("IP Address:", 1) 

mylcd.lcd_display_string(get_ip_address('eth0'), 2)

DESPLAZAR TEXTO DE DERECHA A IZQUIERDA CONTINUAMENTE
Este programa desplazará una cadena de texto desde el lado derecho de la pantalla LCD hacia el lado izquierdo y hará un bucle continuo:

import I2C_LCD_driver
from time import *

mylcd = I2C_LCD_driver.lcd()

str_pad = " " * 16
my_long_string = "This is a string that needs to scroll"
my_long_string = str_pad + my_long_string

while True:
    for i in range (0, len(my_long_string)):
        lcd_text = my_long_string[i:(i+16)]
        mylcd.lcd_display_string(lcd_text,1)
        sleep(0.4)
        mylcd.lcd_display_string(str_pad,1)

DESPLAZAR TEXTO DE DERECHA A IZQUIERDA UNA VEZ


El siguiente código desliza texto en la pantalla de derecha a izquierda una vez, luego se detiene y deja una pantalla limpia.

import I2C_LCD_driver
from time import *

mylcd = I2C_LCD_driver.lcd()

str_pad = " " * 16
my_long_string = "This is a string that needs to scroll"
my_long_string = str_pad + my_long_string

for i in range (0, len(my_long_string)):
    lcd_text = my_long_string[i:(i+16)]
    mylcd.lcd_display_string(lcd_text,1)
    sleep(0.4)
    mylcd.lcd_display_string(str_pad,1)

DESPLAZAR TEXTO DE IZQUIERDA A DERECHA UNA VEZ
Este programa desliza texto en la pantalla de izquierda a derecha una vez, luego se detiene y deja los primeros 16 caracteres de la cadena de texto en la pantalla.

import I2C_LCD_driver
from time import *

mylcd = I2C_LCD_driver.lcd()

padding = " " * 16
my_long_string = "This is a string that needs to scroll"
padded_string = my_long_string + padding

for i in range (0, len(my_long_string)):
   lcd_text = padded_string[((len(my_long_string)-1)-i):-i]
   mylcd.lcd_display_string(lcd_text,1)
   sleep(0.4)
   mylcd.lcd_display_string(padding[(15+i):i], 1)

CARACTERES PERSONALIZADOS
Puede crear cualquier patrón que desee e imprimirlo en la pantalla como un carácter personalizado.
Cada carácter es una matriz de 5 x 8 píxeles.
Hasta 8 caracteres personalizados pueden ser definidos y almacenados en la memoria del LCD.
Este generador de caracteres personalizados le ayudará a crear la matriz de bits necesaria para definir los caracteres en la memoria de la LCD.
enlace: [ https://omerk.github.io/lcdchargen/ |generador de caracteres personalizados ]


IMPRIMIENDO UN CARACTER PERSONALIZADO
El siguiente código genera un caracter «<«:

import I2C_LCD_driver
from time import *

mylcd = I2C_LCD_driver.lcd()

fontdata1 = [      
        [ 0b00010, 
          0b00100, 
          0b01000, 
          0b10000, 
          0b01000, 
          0b00100, 
          0b00010, 
          0b00000 ],
]

mylcd.lcd_load_custom_chars(fontdata1)
mylcd.lcd_write(0x80)
mylcd.lcd_write_char(0)

IMPRIMIR VARIOS CARACTERES PERSONALIZADOS
Este programa imprime una flecha grande hacia la derecha (→) en la pantalla:

import I2C_LCD_driver
from time import *

mylcd = I2C_LCD_driver.lcd()

fontdata1 = [
        # char(0) - Upper-left character
        [ 0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b11111, 
          0b11111 ],

        # char(1) - Upper-middle character
        [ 0b00000, 
          0b00000, 
          0b00100, 
          0b00110, 
          0b00111, 
          0b00111, 
          0b11111, 
          0b11111 ],
        
        # char(2) - Upper-right character
        [ 0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b10000, 
          0b11000 ],
        
        # char(3) - Lower-left character
        [ 0b11111, 
          0b11111, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000 ],
       
        # char(4) - Lower-middle character
        [ 0b11111, 
          0b11111, 
          0b00111, 
          0b00111, 
          0b00110, 
          0b00100, 
          0b00000, 
          0b00000 ],
        
        # char(5) - Lower-right character
        [ 0b11000, 
          0b10000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000, 
          0b00000 ],
]

mylcd.lcd_load_custom_chars(fontdata1)

mylcd.lcd_write(0x80)
mylcd.lcd_write_char(0)
mylcd.lcd_write_char(1)
mylcd.lcd_write_char(2)

mylcd.lcd_write(0xC0)
mylcd.lcd_write_char(3)
mylcd.lcd_write_char(4)
mylcd.lcd_write_char(5)

Fuente https://wiki.52pi.com/index.php?title=Z-0234

Sistema de domótica Alexa con Arduino IoT y ESP8266


En este post vamos a ver un sistema de automatización del hogar Alexa con la nube Arduino IoT.  Esta vez usaremos, una vez más, el famoso módulo ESP8266 NodeMCU, el cual es algo más económico que el ESP32, y es mas que suficiente para esta aplicación que únicamente trata de operar 4 electrodomésticos, aunque pueden modificarse según sus necesidades, todo ello a través del panel de Arduino IoT Cloud, Alexa e interruptores mecánicos. 

esp8266 por arriba

La placa AZDelivery NodeMCU Amica V2 es la clásica placa de desarrollo ESP caracterizándose por su sencillo uso (se maneja fácilmente desde el IDE de Arduino) contando con un potente procesador ESP8266-12F de Espressif y una WLAN integrada, por lo que este controlador ofrece una sencilla introducción al desarrollo del IoT. Hay disponibles varias versiones de firmware del fabricante, que se pueden flashear cómodamente a través del puerto micro-USB.A diferencia de modelos anteriores, si usa el chipset CP2102 modificado (por ejemplo la azdelivery) se permite una mayor velocidad de transferencia. Además, este modulo cuenta con una relativa gran memoria y también con una reserva de potencia del procesador.

Este es el resumen de las características mas significativos:

Voltaje de alimentación (USB)5V DC
Voltaje de Entrada/Salida3.3V DC
Frecuencia de reloj80MHz / 160MHz
Instrucción RAM32kB
Datos RAM96kB
UART2
Pines digitales GPIO17 (configurable como PWM a 3,3V)
Pin analógico ADC1 (el rango de voltaje es: 0 – 1V)
Memoria flash externa4MB

Es interesante destacar que gracias a la interactividad con la nube de Amazon, también podemos operar este sistema controlando los relés desde la app de Alexa desde cualquier lugar del mundo siempre que haya conectividad a Internet (aun así, también se ha previsto para esos casos, operar esos electrodomésticos con interruptores manuales).

En efecto en este diseño , puede controlar los aparatos con pulsadores de modo que si no tiene WiFi o conexión a Internet, puede controlarlo manualmente usando este método. Estas son resumidamente algunas características de este circuito:

  • Puede encender/apagar interruptores y enchufes inteligentes usando su voz.
  • Aumente o disminuya el brillo de las luces de su habitación.
  • También puede cambiar el color de las luces.
  • Puede comprobar la temperatura ambiente mediante sensores.
  • Vea la actividad de movimiento de los sensores en su habitación.

El circuito

El circuito propuesto es bastante sencillo utilizando los pines GPIO digitales D1, D2, D5 y D6 para controlar el módulo de relé de 4 canales. El SD3, D3, D7 y RX están conectados con los botones para controlar este proyecto manualmente. Obviamente el modulo de relés se puede realizar por ejemplo con una placa de puntos siguiendo el esquema de mas bajo o bien comprarlo ya montado.( por unos 11€)

modulo de reles

Los relés los conectamos al GPiO4, GPIO 5, GPIO 14 y GPIO 12 . En caso de realizar nosotros mismos el circuito usaremos cuatro transistores típicos de pequeña señal BC547B con sus correspondientes resistencias de base y diodos de protección en las bobinas de los relés. Además el circuito de los relés se puede complementar con sendos leds indicadores de estado, así como de leds de presencia de energía con sus respectivas resistencias limitadoras de 100 ohmios.

La función INPUT_PULLUP en Arduino IDE se usa aquí en lugar de usar resistencias pull-up con cada interruptor. Desde el código fuente, cuando los pines de control de los módulos de relé reciben una señal BAJA, el relé se encenderá y para la señal ALTA se apagará.

Por último el suministro de alimentación para el circuito es de de 5V 2Amp bien mediante el propio conector micro-usb del ESP32 o bien mediante alimentación común.

Este es el simple diagrama de circuito:

Sistema de domótica 4 reles , esp8266 con Alexa

Este el listado de componentes:

  • ESP8266 NodeMCU
  • Relé de 5V (x4)
  • Transistores BC547 (x4)
  • Diodo 1N4007 PN (x4)
  • LED verde (x4)
  • LED rojo (x4)
  • Resistencia 1K (x4)
  • Resistencia de 100 ohmios (x8)
  • Pulsador (x4)
  • Fuente de 5 V CC

Esta es la imagen del circuito completo donde el circuito completo se han montado sobre una PCB a medida:

Foto del montaje

Configuración y software

En resumen estos son los pasos para construir el sistema de automatización del hogar Alexa

  • Al principio, cree una cuenta en Arduino IoT Cloud.
  • Configure todo para Dashboard.
  • Configure Arduino IoT Cloud para ESP8266.
  • Programa NodeMCU con Arduino IDE.
  • Conexión de la aplicación IoT Cloud y Alexa.

A continuación veamos en mas detalle los pasos anteriormente enunciados:

1-Regístrese para obtener una cuenta gratuita de Arduino IoT Cloud

Para este proyecto de domótica, tenemos que configurar una cuenta gratuita de Arduino IoT Cloud. Gracias a esta configuración conecta ESP8266 para controlar dispositivos con Alexa de modo que crearemos una nube Arduino IoT. Estos son los pasos a seguir:

  1.  Al principio, debe hacer clic en el siguiente enlace
    https://create.arduino.cc/iot/things .
  2. Haga clic en Crear uno.
  3. Ingrese todos los detalles requeridos que quiere. Luego haga clic en Siguiente.
  4. En la página siguiente, requiere correo electrónico, nombre de usuario y contraseña para su
    cuenta Arduino IoT Cloud. Después de completar todos estos, debe aceptar todos los términos y condiciones.
  5. Ahora haga clic en el botón Registrarse.
  6. Luego revise su correo electrónico. El correo electrónico de verificación será enviado a su cuenta. Haga clic en el enlace del correo electrónico y verifíquelo.
  7. Luego regrese a Arduino IoT Cloud y actualice la página.
  8. Por último, haga clic en IoT Cloud.

2-Agregar un dispositivo

Estos son los pasos a seguir para agregar un dispositivo a la nube de Arduino:

  1. Haga clic en seleccionar dispositivo.
  2. Luego haga clic en configurar un dispositivo de terceros.
  3. Ahora seleccione el dispositivo (ESP8266) y seleccione el modelo (NodeMCU 1.0).
  4. Allí puede ver todas las placas compatibles con Arduino Cloud en el menú.
  5. Haga clic en Continuar.
  6. Ingrese un nombre para su dispositivo y haga clic en Siguiente.
  7. Después de eso, obtiene la identificación y la clave secreta de su dispositivo. Puede guardar los detalles haciendo clic en descargar el PDF.
  8. Por último, haga clic en Continuar y se agregará su dispositivo.

3- Agregar variables en proyectos Arduino IoT Cloud

Para controlar sensores o relés, debe obtener datos de estos. Estos datos pueden ser capturados por variables. En este proyecto, necesitamos cuatro variables. Para un plan gratuito, puede agregar hasta cinco variables. Estos son los pasos a seguir:

  1. Haga clic en Agregar variables.
  2. Asigne un nombre a la variable y seleccione el tipo como interruptor compatible con Alexa.
  3. Haga clic en Permiso variable como Lectura y escritura y actualice la política como Al cambiar
  4. Haga clic en Agregar variables.
  5. En este proceso puede agregar todo tipo de Variables.

4-Creación de un panel web para Arduino IoT Cloud

Estos son los pasos a seguir para crear un panel web en la nube de Arduino :

  1. Al principio, haga clic en Tablero y luego en ‘Crear tablero‘.
  2. Haga clic en el botón editar. Luego haga clic en Agregar y allí puede seleccionar el widget de cambio.
  3. Luego asigne un nombre al interruptor y haga clic en el botón Vincular variable que está visible a la derecha.
  4. Como ya creamos una variable en el paso anterior, enlaze este con el widget.
  5. Ahora haga clic en Vincular variable. Luego haga clic en Listo.
  6. Ahora puede agregar todos los widgets necesarios. En este proyecto, necesitamos cuatro widgets Switch. Después de agregar esto, haga clic en Cosas para salir del tablero.

5-Instalar boceto para Arduino IoT Cloud

Después de agregar cualquier variable en esto, se guardará automáticamente en la pestaña de boceto. Si desea editar esto pulse en más donde puede abrir un editor completo. Desde allí puede descargar el boceto del programa para el microcontrolador. Para este proyecto, el boceto descargado se abre en Arduino IDE.

Instalación de bibliotecas

Estos son los pasos a seguir en caso desde que no tenga instaladas las bibliotecas del ESP8266:

  1. En este paso, vamos a instalar todas las bibliotecas y otras dependencias. En este proyecto, necesitamos una biblioteca para ESP8266.
  2. Al principio, ve al botón de boceto.
  3. Luego haga clic en incluir bibliotecas.
  4. Haga clic en administrar bibliotecas en Arduino IDE.
  5. Allí pedirá instalar todas las dependencias. Haga clic en Instalar todo.

Actualizar el boceto del proyecto

Para actualizar Sketch, abra el archivo .ino en el IDE de Arduino. Allí puede ingresar la ID de la cosa, la ID del dispositivo, las credenciales de WiFi y la clave secreta.

Puede copiar el Thing ID desde la esquina inferior derecha de esta ventana. Ahora pegue la ID de la cosa y la ID del dispositivo en el archivo thingProperties.h en el IDE de Arduino. Luego pegue las credenciales de WiFi y la clave secreta en el archivo arduino_secrets.h .
Ahora cargue el código para ESP8266 en Arduino IDE.

Cómo usar el control remoto en la nube de Arduino

Primero, instale ‘Arduino IoT Cloud Remote‘ desde Google Play Store. Luego inicie sesión en la aplicación con el correo electrónico que usa en la cuenta de Arduino IoT Cloud. Introduzca el nombre de la cosa para abrir el panel.

Programar ESP8266 NodeMCU

  1. Al principio, actualice las preferencias -> URL del administrador de tableros adicionales: https://dl.espressif.com/dl/package_esp32_index.json y http://arduino.esp8266.com/stable/package_esp8266com_index.json
  2. Luego instale la placa ESP8266 desde el administrador de la placa.
  3. Luego instale todas las bibliotecas y dependencias requeridas.

Código para NodeMCU

Cuando cargue el código a ESP8266, puede conectar la aplicación Alexa con él. Los pasos para conectar la cuenta de Arduino IoT Cloud con la aplicación Amazon Alexa los veremos mas abajo.

#include "thingProperties.h"
 
// define the GPIO connected pins with relays and switches
#define RelayPin1 5  //D1
#define RelayPin2 4  //D2
#define RelayPin3 14 //D5
#define RelayPin4 12 //D6
 
#define SwitchPin1 10  //SD3
#define SwitchPin2 0   //D3 
#define SwitchPin3 13  //D7
#define SwitchPin4 3   //RX
 
#define wifiLed   16   //D0
 
int toggleState_1 = 0; //Define integer to remember the toggle state for relay 1
int toggleState_2 = 0; //Define integer to remember the toggle state for relay 2
int toggleState_3 = 0; //Define integer to remember the toggle state for relay 3
int toggleState_4 = 0; //Define integer to remember the toggle state for relay 4
 
void relayOnOff(int relay) {
 
  switch (relay) {
    case 1:
      if (toggleState_1 == 0) {
        digitalWrite(RelayPin1, LOW); // Turn on relay 1
        toggleState_1 = 1;
        Serial.println("Device1 ON");
      }
      else {
        digitalWrite(RelayPin1, HIGH); // Turn off relay 1
        toggleState_1 = 0;
        Serial.println("Device1 OFF");
      }
      delay(100);
      break;
    case 2:
      if (toggleState_2 == 0) {
        digitalWrite(RelayPin2, LOW); // Turn on relay 2
        toggleState_2 = 1;
        Serial.println("Device2 ON");
      }
      else {
        digitalWrite(RelayPin2, HIGH); // Turn off relay 2
        toggleState_2 = 0;
        Serial.println("Device2 OFF");
      }
      delay(100);
      break;
    case 3:
      if (toggleState_3 == 0) {
        digitalWrite(RelayPin3, LOW); // Turn on relay 3
        toggleState_3 = 1;
        Serial.println("Device3 ON");
      } else {
        digitalWrite(RelayPin3, HIGH); // Turn off relay 3
        toggleState_3 = 0;
        Serial.println("Device3 OFF");
      }
      delay(100);
      break;
    case 4:
      if (toggleState_4 == 0) {
        digitalWrite(RelayPin4, LOW); // Turn on relay 4
        toggleState_4 = 1;
        Serial.println("Device4 ON");
      }
      else {
        digitalWrite(RelayPin4, HIGH); // Turn off relay 4
        toggleState_4 = 0;
        Serial.println("Device4 OFF");
      }
      delay(100);
      break;
    default : break;
  }
}
 
void manual_control() {
  //Manual Switch Control
  if (digitalRead(SwitchPin1) == LOW) {
    delay(200);
    relayOnOff(1);
  }
  else if (digitalRead(SwitchPin2) == LOW) {
    delay(200);
    relayOnOff(2);
  }
  else if (digitalRead(SwitchPin3) == LOW) {
    delay(200);
    relayOnOff(3);
  }
  else if (digitalRead(SwitchPin4) == LOW) {
    delay(200);
    relayOnOff(4);
  }
}
 
void setup() {
  // Initialize serial and wait for port to open:
  Serial.begin(9600);
 
  delay(1500);
 
  // Define thingProperties.h
  initProperties();
 
  // Connect to Arduino IoT Cloud
  
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
 
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();
 
  pinMode(RelayPin1, OUTPUT);
  pinMode(RelayPin2, OUTPUT);
  pinMode(RelayPin3, OUTPUT);
  pinMode(RelayPin4, OUTPUT);
 
  pinMode(wifiLed, OUTPUT);
 
  pinMode(SwitchPin1, INPUT_PULLUP);
  pinMode(SwitchPin2, INPUT_PULLUP);
  pinMode(SwitchPin3, INPUT_PULLUP);
  pinMode(SwitchPin4, INPUT_PULLUP);
 
  //During Starting all Relays should TURN OFF
  digitalWrite(RelayPin1, HIGH);
  digitalWrite(RelayPin2, HIGH);
  digitalWrite(RelayPin3, HIGH);
  digitalWrite(RelayPin4, HIGH);
 
  digitalWrite(wifiLed, HIGH);  //Turn OFF WiFi LED
}
 
void loop() {
  ArduinoCloud.update();
  
  manual_control(); //Control relays manually
 
  if (WiFi.status() != WL_CONNECTED)
  {
    digitalWrite(wifiLed, HIGH); //Turn OFF WiFi LED
  }
  else{
    digitalWrite(wifiLed, LOW); //Turn ON WiFi LED
  }
}
 
void onSwitch1Change() {
  if (switch1 == 1)
  {
    digitalWrite(RelayPin1, LOW);
    Serial.println("Device1 ON");
    toggleState_1 = 1;
  }
  else
  {
    digitalWrite(RelayPin1, HIGH);
    Serial.println("Device1 OFF");
    toggleState_1 = 0;
  }
}
 
void onSwitch2Change() {
  if (switch2 == 1)
  {
    digitalWrite(RelayPin2, LOW);
    Serial.println("Device2 ON");
    toggleState_2 = 1;
  }
  else
  {
    digitalWrite(RelayPin2, HIGH);
    Serial.println("Device2 OFF");
    toggleState_2 = 0;
  }
}
 
void onSwitch3Change() {
  if (switch3 == 1)
  {
    digitalWrite(RelayPin3, LOW);
    Serial.println("Device2 ON");
    toggleState_3 = 1;
  }
  else
  {
    digitalWrite(RelayPin3, HIGH);
    Serial.println("Device3 OFF");
    toggleState_3 = 0;
  }
}
 
void onSwitch4Change() {
  if (switch4 == 1)
  {
    digitalWrite(RelayPin4, LOW);
    Serial.println("Device4 ON");
    toggleState_4 = 1;
  }
  else
  {
    digitalWrite(RelayPin4, HIGH);
    Serial.println("Device4 OFF");
    toggleState_4 = 0;
  }
}

6-Conectar la aplicación Amazon Alexa con ESP8266

Para conectar nuestro circuito con la aplicación Alexa, en la app móvil Amazon Alexa realizaremos los siguientes pasos:

  1. Al principio, haga clic en Más en la aplicación Amazon Alexa.
  2. Luego seleccione Habilidades y juegos.
  3. En la opción de búsqueda, puede encontrar Arduino.
  4. Haga clic en Habilitar para usar.

Ahora necesitemos agregar un dispositivo en la aplicación Alexa

  1. En la aplicación Alexa, inicie sesión con el correo electrónico y la contraseña que utilizó para iniciar sesión en la cuenta Arduino IoT Cloud.
  2. Luego ciérralo.
  3. Haga clic en Descubrir dispositivos. El nombre de su dispositivo es visible allí en unos segundos.
  4. Luego haga clic en su dispositivo. A continuación, seleccione todos los interruptores.
  5. Si ESP8266 está conectado con su WiFi, puede controlar todos los dispositivos desde la aplicación Alexa.
  6. Ahora solo tiene que decir «Alexa, enciende la luz». La luz esta encendida. Con este comando de voz, puede controlar los electrodomésticos con su voz.

Fuente https://circuitdiagrams.in/alexa-home-automation-system/

Automatización del hogar inteligente con control de humedad mediante Blynk


Para el control de dispositivos electrónicos, gracias a las redes inalámbricas se potencia aún más el concepto de “Smart Home Automation” entendiendo la automatización del hogar como una red de hardware que funciona para comunicar los dispositivos cotidianos entre sí a través del servicio de Internet (IoT). Por ello conecta todos los dispositivos con una «única unidad de control central común» que depende de la entrada del usuario. Estos dispositivos pueden tener conectividad wifi para interactuar con teléfonos inteligentes o tabletas, ya sea que los usuarios estén en casa o en cualquier parte del mundo.

Por otro lado, tenemos a Blynk, una plataforma de Internet de las cosas que permite a los desarrolladores conectar sus dispositivos IoT (Internet de las cosas) a una variedad de servicios en la nube. La plataforma ofrece una aplicación móvil y un servidor de nube que se pueden utilizar para controlar y monitorear dispositivos conectados. Los desarrolladores pueden utilizar una variedad de lenguajes de programación para crear aplicaciones que se comuniquen con el servidor de Blynk y controlen sus dispositivos IoT.

En este post mostraremos un sistema de automatización del hogar inteligente con funciones de LDR, temperatura y humedad.  Además, cuenta con pulsador externo y funciones de interruptor táctil. Este sistema funciona a través del software Blynk, que como vamos a ver es fácil de controlar.

Automatización del hogar inteligente con control de humedad mediante Blynk

Este proyecto incluye dos partes principales: primero está el hardware, que nos permite controlar los sensores y actuadores del sistema de automatización del hogar inteligente, el segundo es el servidor, que gestiona, controla y monitoriza el domicilio del usuario.

Un sistema IoT se construye con sensores básicos, actuadores, sistema integrado, red, interfaz de usuario y almacenamiento de datos. Estos se discuten en detalle a continuación.

Sensores

Hay tres tipos de sensores que usamos para construir este sistema usando el software Blynk. El primero es el sensor DHT11 que puede detectar tanto la temperatura como la humedad, el cual hemos visto en este blog en numerosas aplicaciones. 

El DHT11 es un sensor de humedad y temperatura que se utiliza comúnmente en proyectos de IoT (Internet de las cosas) y automatización del hogar. Este sensor utiliza un chip capacitivo para medir la humedad y un termistor para medir la temperatura. El DHT11 es un sensor de bajo costo y bajo consumo de energía que es fácil de usar y se comunica con un microcontrolador a través de un protocolo de comunicación digital. Es un sensor bastante simple, pero es bastante preciso en las mediciones de humedad y temperatura en un rango de 0 a 50 grados Celsius y de 20 a 90% de humedad relativa.

dht11

El segundo es un simple LDR, que controla las funciones de encendido/apagado automático del sistema. Y el tercer sensor es un sensor táctil, que se utiliza para controlar los actuadores manualmente a través del tacto.

Actuadores

El módulo de relé de dos canales de 5 V se utiliza aquí como actuador. Básicamente, los pines de salida que están conectados directamente a la entrada del relé se denominan actuadores. Las cargas eléctricas como luces de CA, ventiladores, etc. están conectadas a los terminales comunes y normalmente abiertos de los relés. Este módulo de relé es controlado por la aplicación Blynk a través de teléfonos inteligentes.

Sistema Integrado

Cada dispositivo inteligente de automatización del hogar necesita un sistema integrado adecuado que pueda manejar todo el proceso. ESP8266 NodeMCU juega el papel aquí como un controlador integrado. Es programable por el software Arduino IDE. El NodeMCU controla los datos digitales y analógicos del receptor y los transmite a través de un servidor web. Al mismo tiempo, sus comandos de proceso son dados por sensores y actuadores.

Red

Para vincular todos los dispositivos con el sistema integrado, necesitamos una red adecuada para comunicarnos entre nosotros.

Interfaz de usuario

Para enviar datos del receptor al transmisor, necesitamos una interfaz de usuario para controlar los dispositivos desde cualquier lugar. En este proyecto, presentamos la plataforma Blynk para hacer este sistema inteligente de automatización del hogar. Es demasiado simple de usar. También proporciona botones no programables para ahorrar tiempo y esfuerzos de programación.

Almacenamiento de datos

El almacenamiento de datos utilizado aquí también lo proporciona la plataforma en la nube Blynk. El almacenamiento de big data es básicamente una gran cantidad de datos que se recopilan de todos los dispositivos. Este tipo de datos siempre varía de un dispositivo a otro. Al ser un servidor en la nube, la velocidad de datos es muy alta.

Diagrama de circuito

El circuito es realmente sencillo como podemos ver en el siguiente esquema siendo el «corazón» el ESP8266. En el circuito el DHT11 esta conectado al GPI10 mediante el protocolo one-wire. También tenemos cuatro pulsadores, tres conectados al GPIO16, GPIO13 y GPIO15 ( pueden usarse en su lugar también sensores táctiles ) y un cuarto al pin de reset. Asimismo no podemos olvidar el LDR conectado a la único puerto analógico disponible en el ESP8266 ( el ADCO).

Para terminar tenemos dos relés conectados al GPiO14 y GPIO12 mediante dos transistores de pequeña señal BC547B con sus correspondientes resistencias de base y diodos de protección en las bobinas de los relés.

esquema circuito para blynk

Esta es la lista de componentes del circuito:

  • ESP8266 NodeMCU
  • Sensor de humedad DHT11
  • Sensor LDR
  • Sensor táctil (x3)
  • Relé de 5V (x2)
  • Acoplador óptico 817 (x2)
  • Transistor BC547 NPN (x2)
  • Pulsador (x3)
  • LED rojo (x2)
  • LED verde (x2)
  • 1N4007 PN Diodo (x2)
  • Terminal de tornillo (x2)
  • Resistencia de 330Ω (x2)
  • Resistencia de 1KΩ (2)
  • alambres

Diseño de circuitos con software de PCB para la automatización del hogar inteligente

Para hacer el circuito compacto y darle un aspecto profesional, podemos usar una a PCB después de probar todas las características del proyecto de domótica inteligente utilizando el módulo wifi ESP8266 en la placa de pruebas. 

Automatización del hogar inteligente con PCB Blynk

Para el archivo Gerber, consulte este enlace Automatización inteligente del hogar usando ESP8266 y Blynk .

Software necesario para la automatización del hogar inteligente

La comunicación entre los dispositivos y la plataforma Blynk IoT se establecerá cuando haya una conexión de red presente.

Para cargar el código a ESP8266 NodeMCU, necesitamos el software Arduino IDE.

  • Primer software Arduino IDE abierto
  • Vaya a Archivo y seleccione Preferencias y pegue el enlace » https://arduino.esp8266.com/stable/package_esp8266com_index.json » en las URL de Administrador de placa adicional para agregar la placa ESP8266. Abra Boards Manager desde el menú Herramientas y escriba ESP8266 para instalar la plataforma ESP8266.
  • Para programar el módulo ESP8266, se requiere una identidad única del software Blynk. Ese es el código de autenticación. Después de conectarse a una red, esta identificación ayuda a comunicarse con el dispositivo.
  • Para conectarse a la red WiFi local se requiere WiFi SSID y contraseña.
  • Antes de compilar y cargar el código en este módulo, debemos instalar la  biblioteca de sensores de Adafruit , la biblioteca Blynk , la biblioteca DHT11 y la biblioteca OLED .

Para agregar estas bibliotecas al IDE de Arduino, vaya a Sketch – Incluir biblioteca – Agregar biblioteca .zip – busque e instale archivos.

Preparación del panel de control de automatización del hogar inteligente con Blynk

Abra la aplicación oficial de Blynk y cree una cuenta para usted. Después de crear un nuevo proyecto, recibirá el token de autenticación a través de su correo electrónico.

    Copie este token de autenticación y péguelo en los campos correspondientes del código Arduino ESP8266. Después de eso, cargue el código IDE de Arduino en NodeMCU y espere a que se conecte con el dispositivo en la red.

    La página siguiente mostrará un espacio en blanco para agregar botones para el sistema. Toque el espacio en blanco y elija la opción de botón y agregue esto. Nuevamente haga clic en esto y elija un pin virtual y seleccione el pin deseado que incluye en su código Arduino. Luego haga clic en Aceptar y presione Atrás.

    Continuar con el mismo proceso con el otro. Después de eso, elija el botón de indicador de temperatura y humedad y elija sus pines virtuales. Finalmente, regrese a la página de inicio y haga clic en el botón de reproducción en la parte superior derecha. Puede controlar sus dispositivos a través de esta aplicación Blynk.

    Principio de funcionamiento del sistema de automatización del hogar inteligente

    El sistema de automatización del hogar inteligente basado en ESP8266 se basa en el módulo wifi ESP8266, LDR, sensor DHT11, tres sensores táctiles, pantalla OLED y dos relés.

    Este circuito tiene dos partes de sensores:

    •  La primera parte es una entrada analógica para medir la intensidad de la luz a través del sensor LDR
    •  La segunda parte es una entrada digital para leer valores de temperatura y humedad a través del sensor de humedad DHT11. Para medir una amplia gama de temperatura y humedad, puede usar DHT22 como reemplazo de DHT11.

    Necesitamos un suministro de 5 V CC para encender este circuito, ya que necesita controlar los relés. Luego, también tenemos que alimentar el ESP8266 NodeMCU. ESP8266 NodeMCU V1.0 tiene 11 pines GPIO y un pin ADC con resolución de 10 bits. En la figura, explicamos la configuración de pines de ESP8266. Este módulo tiene un regulador de voltaje incorporado de 3.3V. También tiene un convertidor de USB a serie basado en CP2102 que brinda una interfaz fácil con el PC para cargar el código Arduino en el módulo.

    El sensor LDR se usa aquí para detectar la intensidad de la luz a su alrededor. Puede controlar el encendido o apagado de las luces según la intensidad de la luz. Esto está conectado al pin A0 del ESP8266 NodeMCU para leer el voltaje analógico según la luz ambiental. Se puede cambiar manualmente a través del botón CMOD en el sistema.

    El sensor DHT11 se usa aquí para leer la temperatura y la humedad a través del pin de entrada digital SD3. Los datos dados se dividen en temperatura y humedad por DHT11 a través del mismo pin de entrada. Los pines digitales D5 y D6 están conectados a las entradas del relé que actúan como actuadores. Estos pines controlan los dispositivos de CA a través de la aplicación. Tres sensores táctiles se conectan a través de tres botones y luego se conectan a los pines digitales D0, D7 y D8 respectivamente. Cuando el sistema se desconecta, estos funcionarán como interruptores normales. Entonces, podríamos usar este proyecto de automatización del hogar inteligente tanto en modo en línea como fuera de línea.

    Por último, necesitamos conectar la pantalla OLED con el sistema a través de los pines I2C de la pantalla NodeMCU y OLED. Esta pantalla OLED es opcional.

    Código NodeMCU

    //ESP8266 Based Smart Home Automation System Using Blynk
    #define BLYNK_PRINT Serial            
    #include <BlynkSimpleEsp8266.h>  
    #include <DHT.h>
    #include <SH1106Wire.h> 
    void checkPhysicalButton();
    int toggleState_1 = 0;
    int pushButton1State = LOW;
    int toggleState_2 = 0;
    int pushButton2State = LOW;
    float temperature1 = 0;
    float humidity1   = 0;
    int   ldrVal;
    int   switchMode = 0;
    //Set values for Auto Control Mode
    const float maxTemp = 34.10;
    const float minTemp = 33.8;
    const int maxLight = 1000;
    const int minLight = 200;
    #define AUTH "4w8iA4UCRADMzqEt60NoviM3Ntno0oHP"                 // You should get Auth Token in the Blynk App.  
    #define WIFI_SSID "CyBorg"             //Enter Wifi Name
    #define WIFI_PASS "Tanmoy#[email protected]"         //Enter wifi Password
    #define PUSH_BUTTON_CMODE  15 //D8
    #define LDR_PIN            A0 //A0
    #define DHTPIN             10 //SD3  pin connected with DHT
    #define RELAY_PIN_1      14   //D5
    #define RELAY_PIN_2      12   //D6
    #define PUSH_BUTTON_1    16   //D0
    #define PUSH_BUTTON_2    13   //D7
    #define VPIN_BUTTON_1    V1 
    #define VPIN_BUTTON_2    V2
    #define VPIN_BUTTON_C    V3
    #define VPIN_HUMIDITY    V5
    #define VPIN_TEMPERATURE V6
    // Declaration for an SH1106 display connected to I2C (SDA, SCL pins)
    SH1106Wire display(0x3c, D2, D1);
    // Uncomment whatever type you're using!
    #define DHTTYPE DHT11     // DHT 11
    //#define DHTTYPE DHT22   // DHT 22, AM2302, AM2321
    //#define DHTTYPE DHT21   // DHT 21, AM2301
    DHT dht(DHTPIN, DHTTYPE);
    BlynkTimer timer;
    void changeMode(){      
      delay(200);
      if (switchMode == 0){
        switchMode = 1;
      }
      else if (switchMode == 1) {
        switchMode = 0;
      }
      display.clear();
      display.setFont(ArialMT_Plain_24);
      display.drawString(10, 5, "Set Mode: ");
      display.setFont(ArialMT_Plain_24);
      display.drawString(20, 35, String(modeDecode(switchMode)));    
      display.display();
      delay(500);
      Blynk.virtualWrite(VPIN_BUTTON_C, switchMode);
      //display.clear();
    }
    void relayOnOff(int relay){
        switch(relay){
          case 1: 
                 if(toggleState_1 == 0){
                  digitalWrite(RELAY_PIN_1, HIGH); // turn on relay 1
                  toggleState_1 = 1;
                  }
                 else{
                  digitalWrite(RELAY_PIN_1, LOW); // turn off relay 1
                  toggleState_1 = 0;
                  }
                 delay(100);
          break;
          case 2: 
                 if(toggleState_2 == 0){
                  digitalWrite(RELAY_PIN_2, HIGH); // turn on relay 2
                  toggleState_2 = 1;
                  }
                 else{
                  digitalWrite(RELAY_PIN_2, LOW); // turn off relay 2
                  toggleState_2 = 0;
                  }
                 delay(100);
          break;
          default : break;      
          }
      
    }
    String modeDecode(int count){
      if (count == 0){
        return " Manual ";
      }
      else if (count == 1){
        return " Auto ";
      }
    }
    void displayData(){
      display.clear();
      display.setFont(ArialMT_Plain_24);
      display.drawString(20, 0, String(temperature1) + " 'C");
      display.drawString(20, 25, String(humidity1) + " %");
      display.setFont(ArialMT_Plain_16);
      display.drawString(10, 48, "Mode -> ");
      display.drawString(68, 48, String(modeDecode(switchMode)));
      display.display();
      Serial.print(F("Temperature: "));
      Serial.print(temperature1);
      Serial.print("   ");
      Serial.print(F("humidity: "));
      Serial.print(humidity1);
      Serial.print("   ");
      Serial.println(ldrVal); 
      Serial.println("");
    }
    void readSensor(){
        
      ldrVal = analogRead(LDR_PIN);
      
      float h = dht.readHumidity();
      float t = dht.readTemperature(); // or dht.readTemperature(true) for Fahrenheit
      
      if (isnan(h) || isnan(t)) {
        Serial.println("Failed to read from DHT sensor!");
        return;
      }
      else {
        humidity1 = h;
        temperature1 = t;
      }  
    }
    void sendSensor()
    {
      readSensor();
      // You can send any value at any time.
      // Please don't send more that 10 values per second.
      Blynk.virtualWrite(VPIN_HUMIDITY, humidity1);
      Blynk.virtualWrite(VPIN_TEMPERATURE, temperature1);
    }
    BLYNK_CONNECTED() {
      // Request the latest state from the server
      Blynk.syncVirtual(VPIN_BUTTON_1);
      Blynk.syncVirtual(VPIN_BUTTON_2);
      Blynk.syncVirtual(VPIN_BUTTON_C);
    }
    // When App button is pushed - switch the state
    BLYNK_WRITE(VPIN_BUTTON_1) {
      toggleState_1 = param.asInt();
      digitalWrite(RELAY_PIN_1, toggleState_1);
    }
    BLYNK_WRITE(VPIN_BUTTON_2) {
      toggleState_2 = param.asInt();
      digitalWrite(RELAY_PIN_2, toggleState_2);
    }
    BLYNK_WRITE(VPIN_BUTTON_C) {
      switchMode = param.asInt();
    }
    void checkPhysicalButton()
    {
      if (digitalRead(PUSH_BUTTON_1) == HIGH) {
          relayOnOff(1);
          // Update Button Widget
          Blynk.virtualWrite(VPIN_BUTTON_1, toggleState_1);
        }
      if (digitalRead(PUSH_BUTTON_2) == HIGH) {
          relayOnOff(2);
          // Update Button Widget
          Blynk.virtualWrite(VPIN_BUTTON_2, toggleState_2);
        }
    }
    void setup()
    {
      Serial.begin(9600);
      
      // Initialising the UI will init the display too.
      display.init();
      display.flipScreenVertically();
      display.setFont(ArialMT_Plain_16);
      display.setTextAlignment(TEXT_ALIGN_LEFT);
      delay(1000);  
      display.setColor(WHITE);
      display.clear();
      pinMode(RELAY_PIN_1, OUTPUT);
      pinMode(PUSH_BUTTON_1, INPUT);
      digitalWrite(RELAY_PIN_1, toggleState_1);
      pinMode(RELAY_PIN_2, OUTPUT);
      pinMode(PUSH_BUTTON_2, INPUT);
      digitalWrite(RELAY_PIN_2, toggleState_2);
      Blynk.begin(AUTH, WIFI_SSID, WIFI_PASS);  
      // Setup a function to be called every 500 ms
      timer.setInterval(500L, checkPhysicalButton);
      
      dht.begin();
      // Setup a function to be called every second
      timer.setInterval(1000L, sendSensor);
      // Setup a function to be called every 2 second
      timer.setInterval(2000L, displayData);
    }
    void loop()
    {
      if (digitalRead(PUSH_BUTTON_CMODE) == HIGH){
        changeMode();
      }   
      else{    
        if(switchMode == 1){ //if Auto Mode
          //DHT11 control Relay 1
          if(temperature1 > maxTemp){
            if(toggleState_1 == 0){
              digitalWrite(RELAY_PIN_1, HIGH); // turn ON relay 1
              toggleState_1 = 1;
              // Update Button Widget
              Blynk.virtualWrite(VPIN_BUTTON_1, toggleState_1);
            }
          }
          else if (temperature1 < minTemp){
            if(toggleState_1 == 1){
               digitalWrite(RELAY_PIN_1, LOW); // turn OFF relay 1
               toggleState_1 = 0;
               // Update Button Widget
               Blynk.virtualWrite(VPIN_BUTTON_1, toggleState_1);
              }
          }
          //LDR control Relay 2
          if( ldrVal < minLight){
            if(toggleState_2 == 0){
              digitalWrite(RELAY_PIN_2, HIGH); // turn ON relay 2
              toggleState_2 = 1;
              // Update Button Widget
              Blynk.virtualWrite(VPIN_BUTTON_2, toggleState_2);
            }
          }
          else if (ldrVal > maxLight){
            if(toggleState_2 == 1){
               digitalWrite(RELAY_PIN_2, LOW); // turn OFF relay 2
               toggleState_2 = 0;
               // Update Button Widget
               Blynk.virtualWrite(VPIN_BUTTON_2, toggleState_2);
              }
          } 
        }
        timer.run();
        Blynk.run();
      }
    }

    Fuente https://circuitdiagrams.in/smart-home-automation-using-blynk/

    Primeros pasos con la nube de Arduino


    Los dispositivos conectados en todo el mundo aumentan en miles de millones cada año. Arduino IoT Cloud es una plataforma que permite a cualquier persona crear proyectos IoT, con una interfaz fácil de usar y una solución todo en uno para configuración, escritura de código, carga y visualización.

    En este post, veremos algunos componentes diferentes de Arduino IoT Cloud y brindaremos una descripción general porque además hay basta información en Arduino IoT Cloud, ya que tiene varias páginas de documentación disponibles. A continuación encontrará una lista de algunas páginas populares:

    A continuación se muestra una lista de las funciones de Arduino IoT Cloud.

    • Monitoreo de datos : aprenda cómo monitorear fácilmente los valores del sensor de su Arduino a través de un tablero.
    • Sincronización de variables: la sincronización de variables le permite sincronizar variables entre dispositivos, lo que permite la comunicación entre dispositivos con una codificación mínima.
    • Programador : programe trabajos para que se activen o desactiven durante un período de tiempo específico (segundos, minutos, horas).
    • Cargas por aire (OTA) : cargue el código en dispositivos que no estén conectados a su computadora.
    • Webhooks : integre su proyecto con otro servicio, como IFTTT.
    • Soporte de Amazon Alexa : haga que su proyecto sea controlado por voz con la integración de Amazon Alexa.
    • Uso compartido del panel : comparta sus datos con otras personas de todo el mundo.

    Hardware compatible

    Para usar Arduino IoT Cloud, se requiere una placa compatible con la nube . Se puede elegir entre utilizar una placa Arduino oficial, o una placa basada en el microcontrolador ESP32/ESP8266. Además Arduino IoT Cloud actualmente admite la conexión a través de Wi-Fi, LoRaWAN® (a través de The Things Network) y redes móviles.

    Todas las placas Arduino compatibles con la nube vienen con un elemento seguro de hardware (como el criptochip ECC508 ), donde puede almacenar sus claves de seguridad.

    Wifi

    Las placas Arduino oficiales solo admiten la banda de frecuencia de 2,4 GHz para transmitir datos. Las siguientes placas se conectan a Arduino IoT Cloud a través de Wi-Fi.

    • MKR 1000 Wi-Fi :Arduino MKR1000 ha sido diseñado para ofrecer una solución práctica y rentable para cualquiera que busque añadir conectividad WiFi a sus proyectos con una mínima experiencia en redes. El diseño incluye un circuito de carga Li-Po que permite al Arduino MKR1000 funcionar con alimentación de batería o 5V externos, cargando la batería Li-Po mientras funciona con alimentación externa. El cambio de una fuente a otra se realiza automáticamente. El MKR1000 tiene un procesador Arm Cortex-M0+ de 32 bits, el habitual rico conjunto de interfaces de E/S, y WiFi de bajo consumo con un chip criptográfico para una comunicación segura. Se puede programar utilizando el conocido y fácil de usar software Arduino (IDE). Todas estas características hacen de esta placa la opción preferida para los emergentes proyectos IoT alimentados por batería en un factor de forma compacto. Su precio ronda los 60€ en Amazon.
    MKR 1000 Wi-Fi :
    • MKR Wi-Fi 1010 :MKR 1010 simplifica la creación de prototipos de aplicaciones IoT basadas en WiFi, fácil de conectar y configurar a otros productos de Arduino. El módulo híbrido de conectividad WiFi / BLE tiene un modo de bajo consumo de energía, mejorando la vida útil de las baterías .Ideal para principiantes y profesionales en IoT para la construcción de redes de sensores inalámbricos en minutos o dispositivos de control remoto. La placa puede conectarse a cualquier tipo de red WiFi existente o puedes usarla para crear tu propio Punto de Acceso Arduino. También es posible conectar su placa a diferentes servicios de la Nube, ¡incluyendo Arduino IoT Cloud! Fácil de alimentar con un cable USB o una batería Li-Po externa de 3.7V. Recarga la batería a través del puerto USB. Cuesta unos 40€ en Amazon
    MKR 1010
    • Conexión Nano RP2040 : La placa Raspberry Pi Pico incorpora el chip microcontrolador RP2040 propio de Raspberry Pi, con procesador Arm Cortex M0+ de doble núcleo que funciona a 133 MHz, 264 KB de SRAM y 2 MB de memoria Flash integrados, así como 26 pines GPIO multifunción .Es una placa microcontroladora de bajo coste y alto rendimiento con interfaces digitales flexibles con Pre-Soldered Header, es más conveniente para los ingenieros a utilizar. Para el desarrollo de software, está disponible el SDK C/C++ de Raspberry Pi, o el MicroPython. También hay recursos completos de desarrollo y tutoriales para ayudarle a empezar fácilmente, e integrarlo en los productos finales rápidamente. Esta placa es mas económica rondando los 16€ en Amazon.
    Raspberry Pi Pico
    • Nano 33 Internet de las Cosas :Diseñado para interacciones de corta distancia de BT y proyectos de ahorro de energía. Esta placa es pequeña (48mm x 18mm) y robusta estando construida alrededor del módulo NINA B306, basado en el nRF 52840 nórdico y que contiene un poderoso Cortex M4F.La arquitectura de la placa es totalmente compatible con Arduino IDE Online y Offline; tiene un sensor inercial de 9 ejes (IMU) y un bajo consumo de energía en comparación con otras placas del mismo tamaño.Clock 64MHz, Flask 1MB, RAM 256KB.Dos conectores de 15 pines, uno a cada lado de la placa. Cuesta unos 28€ en Amazon

    nano-33
    • Portenta H7 : es una placa que permite ejecutar simultáneamente código de alto nivel junto con tareas en tiempo real. El diseño incluye dos procesadores que pueden ejecutar tareas en paralelo. Por ejemplo, es posible ejecutar código compilado de Arduino junto con uno de MicroPython, y tener ambos núcleos para comunicarse entre sí. La funcionalidad de Portenta es doble, puede ejecutarse como cualquier otra placa de microcontrolador integrada o como el procesador principal de una computadora integrada. Portenta puede ejecutar fácilmente procesos creados con TensorFlow™ Lite, podría tener uno de los núcleos que calcula un algoritmo de visión por computadora sobre la marcha, mientras que el otro podría realizar operaciones de bajo nivel como controlar un motor o actuar como una interfaz de usuario.Utilice Portenta cuando el rendimiento sea clave, entre otros casos, lo imaginamos como parte de:Maquinaria industrial de alta gama, Equipo de laboratorio,Visión por computador, PLC,Interfaces de usuario preparadas para la industria, Controlador de robótica , Dispositivos de misión crítica,Computadora estacionaria dedicada,Cálculo de arranque de alta velocidad (ms). Cuesta unso 100€ en la pagina oficial de Arduino
    portenta-h7
    • Portenta H7 Lite conectado :El Portenta H7 Lite Connected está diseñado para proporcionar la potencia computacional del Portenta H7 para aplicaciones de IA y soluciones de control de baja latencia, con un módulo que se vuelve más rentable al eliminar la función de interfaz de video de alta resolución. Llena la brecha entre las versiones H7 completa y H7 Lite al integrar conectividad inalámbrica, agregando otra opción para que los clientes de Arduino Pro construyan la solución perfecta con la combinación correcta de rendimiento y simplicidad. El Portenta H7 Lite Connected ejecuta simultáneamente código de alto nivel y tareas en tiempo real gracias a sus dos procesadores. Por ejemplo, puede ejecutar código compilado por Arduino y MicroPython al mismo tiempo, y hacer que los dos núcleos se comuniquen a través de un mecanismo de llamada de procedimiento remoto que permite llamar a funciones en el otro procesador sin problemas para realizar múltiples tareas reales. Sus algoritmos de aprendizaje automático ahora pueden ejecutarse simultáneamente junto con la interacción de sensor/actuador de baja latencia. Cuesta unos 89€ en la tienda oficial
    • Control de la máquina Portenta: Portenta Machine Control es una unidad de control industrial totalmente centralizada y de bajo consumo capaz de controlar equipos y maquinaria. Se puede programar utilizando el marco Arduino u otras plataformas de desarrollo integradas.Gracias a su poder de cómputo, Portenta Machine Control permite una amplia gama de casos de uso de inteligencia artificial y mantenimiento predictivo. Permite la recopilación de datos en tiempo real desde la planta de producción y admite el control remoto de equipos, incluso desde la nube, cuando se desee. Cuesta unos 300€ en la tienda de Arduino.
    portenta-induistrial
    • Nicla Visión : combina un potente procesador IC STM32H747AII6 Dual ARM® Cortex® M7/M4 con una cámara a color de 2 MP compatible con TinyML, así como un sensor de movimiento inteligente de 6 ejes, un micrófono integrado y un sensor de distancia. Puede incluirlo fácilmente en cualquier proyecto porque está diseñado para ser compatible con todos los productos Arduino Portenta y MKR, se integra completamente con OpenMV, es compatible con MicroPython y también ofrece conectividad WiFi y Bluetooth® Low Energy. Es tan compacto, con su factor de forma de 22,86 x 22,86 mm, que puede adaptarse físicamente a la mayoría de los escenarios y requiere tan poca energía que puede funcionar con batería para aplicaciones independientes.Todo esto convierte a Nicla Vision en la solución ideal para desarrollar o crear prototipos con procesamiento de imágenes en el dispositivo y visión artificial en el borde, para seguimiento de activos, reconocimiento de objetos, mantenimiento predictivo y más, más fácil y rápido que nunca. Entrénelo para detectar detalles, de modo que pueda concentrarse en el panorama general. Cuesta unos 95€

    nicla-vision
    • Opta .   Gracias a su poder de cómputo (STM32H747XI dual Cortex®-M7+M4 MCU Arm® de baja potencia de 32 bits: incluye un Cortex® M7 que funciona a 480 MHz y un Cortex® M4 que funciona a 240 MHz comunicandose los dos núcleos a través de un mecanismo de llamada a procedimiento remoto que permite llamar a funciones en el otro procesador sin problemas) Arduino Opta permite una amplia gama de aplicaciones de control, monitoreo y mantenimiento predictivo en tiempo real.Permite a los profesionales ampliar los proyectos de automatización mientras aprovechan el ecosistema abierto y ampliamente conocido de Arduino. Póngalo a trabajar rápidamente, aprovechando las numerosas bibliotecas de software disponibles. El elemento seguro integrado garantiza actualizaciones de firmware inalámbricas y control remoto a través de Arduino Cloud o servicios de terceros. Arduino Opta está disponible en tres variantes:Opta Lite: puertos Ethernet y USB-C® integrados, Opta RS485: puertos Ethernet y USB-C® integrados, además de conectividad RS485 y Opta WiFi: puertos Ethernet y USB-C® integrados, además de RS485 y Wi-Fi/Bluetooth® Low Energy.El Opta está programado para ser lanzado pronto, junto con la documentación sobre cómo usarlo.

    opta

    La conexión a través de Wi-Fi es una alternativa fácil y sus credenciales se pueden ingresar de manera segura durante la configuración de un proyecto. Este tipo de conexión es más adecuado para proyectos de bajo alcance, en los que conecta su placa a la nube a través del enrutador de su hogar/trabajo/escuela. Ahora veremos otros tipos de conectividad:

    LoRaWAN®

    Las siguientes placas se conectan a Arduino IoT Cloud a través de The Things Stack , un servidor de red LoRaWAN® conectado a miles de puertas de enlace LoRa® públicas.

    • MKR WAN 1300
    • MKR WAN 1310

    Se recomienda la conexión a través de LoRaWAN® para proyectos de bajo consumo tanto en áreas remotas como urbanas, donde Wi-Fi u otros tipos de conectividad populares no están disponibles. Las placas MKR WAN 1300/1310 están equipadas con un módulo de radio LoRa y tienen una ranura para una antena. Con la configuración adecuada de bajo consumo, la placa puede enviar datos a la nube durante meses con una sola batería.

    Placas GSM / NB-IoT

    El MKR GSM 1400 y el MKR NB 1500 requieren una tarjeta SIM para conectarse a la nube, ya que se comunican a través de las redes móviles.

    • MKR GSM 1400
    • MKR NB 1500

    La conexión a través de redes móviles se puede considerar en áreas remotas donde no hay Wi-Fi o en proyectos móviles (como el seguimiento de carga). Tenga en cuenta que una conexión segura es una operación que consume mucha memoria, por lo que no hay mucha memoria para la aplicación del usuario (por ejemplo, alrededor de 2,6 kB en el MKR GSM 1400). El uso de muchas variables de IoT Cloud puede hacer que el boceto se quede sin memoria en las placas que no descargan la pila SSL y hacen que se bloquee.

    ESP32 / ESP8266

    Arduino IoT Cloud es compatible con una amplia gama de placas de terceros basadas en los microcontroladores ESP32 y ESP8266 con soporte para Wi-Fi. Para configurarlos, simplemente elija la opción de terceros en la configuración del dispositivo.

    Configuración de placas de terceros.
    Configuración de placas de terceros.

    Ethernet

    Arduino IoT Cloud admite la conexión a través de Ethernet en varios dispositivos. Las opciones para conectarse vía Ethernet son las siguientes:

    • Conéctese con el Portenta H7 en combinación con un portador/escudo compatible con Ethernet .Para conectarse con la placa Portenta H7 , necesitará uno de los siguientes protectores/portadores :Escudo de visión Portenta Ethernet o Control de la máquina Portenta
    • Conéctese con la Opta . *

    Para habilitar la comunicación a través de Ethernet con el Portenta H7, mientras configura su dispositivo, debe seleccionar la opción «Ethernet». Si su dispositivo ya está configurado como un dispositivo Wi-Fi, debe quitarlo antes de configurarlo para la comunicación Ethernet.

    Elija la opción Ethernet.
    Elija la opción Ethernet.

    Tenga en cuenta que el hardware más antiguo, como Ethernet Shield Rev2 y MKR ETH Shield , actualmente no es compatible con Arduino IoT Cloud.

    Primeros pasos

    Configurar Arduino IoT Cloud y acceder a las diferentes funciones disponibles implica unos simples pasos. ¡Así que echemos un vistazo a cómo ir de principio a fin!

    1. Crear una cuenta de Arduino

    Para comenzar a usar la nube Arduino IoT, primero debemos iniciar sesión o registrarnos en Arduino .

    2. Vaya a la nube Arduino IoT

    Una vez que nos hayamos registrado, puede acceder a Arduino IoT Cloud desde cualquier página en arduino.cc haciendo clic en el menú de cuatro puntos en la esquina superior derecha. También puede ir directamente a Arduino IoT Cloud .

    Navegando a la nube.
    Navegando a la nube.

    3. Crear una cosa

    El viaje siempre comienza creando una Cosa nueva . En la descripción general de Thing, podemos elegir qué dispositivo usar, a qué red Wi-Fi queremos conectarnos y crear variables que podemos monitorear y controlar. Este es el espacio de configuración principal, donde todos los cambios que hacemos se generan automáticamente en un archivo de boceto especial .

    Resumen de la Cosa.
    Resumen de la Cosa.

    4. Configuración de un dispositivo

    Los dispositivos se pueden agregar y vincular fácilmente a una Cosa. Arduino IoT Cloud requiere que su ordenador tenga instalado Arduino Agent . El proceso de configuración es rápido y fácil, y se puede hacer haciendo clic en el botón «Seleccionar dispositivo» en la descripción general de Thing. Aquí podemos elegir entre cualquier placa que se haya configurado, o seleccionar la opción “Configurar nuevo dispositivo” .

    Configuración de un dispositivo.
    Configuración de un dispositivo.

    «Dispositivos»

    La pestaña del dispositivo.
    La pestaña del dispositivo.

    5. Creando Variables

    Las variables que creamos se generan automáticamente en un archivo de boceto. Hay varios tipos de datos entre los que podemos elegir, como int, float, boolean, long, char . También hay variables especiales, como Temperatura, Velocidad, Luminancia que se pueden usar. Al hacer clic en el botón «Agregar variable» , podemos elegir el nombre, el tipo de datos, la configuración de actualización y el modo de interacción.

    Creación de variables.
    Creación de variables.

    6. Conexión a una red

    Para conectarse a una red Wi-Fi, simplemente haga clic en el botón «Configurar» en la sección de red. Ingrese las credenciales y haga clic en «Guardar» . ¡Esta información también se genera en su archivo de boceto!

    Ingreso de credenciales de red.
    Ingreso de credenciales de red.

    5. Creando Variables

    Ahora que hemos configurado las variables, los dispositivos y los ajustes de red, ¡podemos comenzar a programar nuestros dispositivos!

    Un archivo de boceto generado automáticamente se puede encontrar en la pestaña «Bosquejo» . Tiene la misma estructura que un típico archivo *.ino , pero con algún código adicional para hacer la conexión a su red y a la nube.

    Un boceto que, por ejemplo, lee un sensor analógico y usa la variable de nube para almacenarlo. Cuando se haya cargado el boceto, funcionará como un boceto normal, ¡pero también actualizará las variables de la nube que usamos!

    Además, cada vez que creamos una variable que tiene habilitado el permiso de lectura y escritura , también se genera una función en la parte inferior de su archivo de boceto. ¡Cada vez que esta variable cambie, ejecutará el código dentro de esta función! Esto significa que podemos dejar la mayor parte del código fuera del loop() y solo ejecutar código cuando sea necesario.

    Para subir el programa a nuestra placa, simplemente haga clic en el botón «Subir» .

    Edición de un boceto en el editor de la nube.
    Edición de un boceto en el editor de la nube.

    El editor también tiene una herramienta Serial Monitor , que se puede abrir haciendo clic en la lupa en la barra de herramientas. Aquí puede ver información sobre su conexión o comandos impresos a través de

    Serial.print()

    .

    La herramienta de monitor en serie.
    La herramienta de monitor en serie.

    Una vez que hayamos cargado correctamente el código, podemos abrir la pestaña «Serial Monitor» para ver información sobre nuestra conexión. Si tiene éxito, imprimirá «conectado a network_name» y «conectado a la nube» . Si no se conecta, también imprimirá los errores aquí.

    El editor en la nube es una versión «mínima» reflejada del Editor web. Cualquier cambio que realice también se reflejará en el Editor web, que es más adecuado para desarrollar bocetos más avanzados.

    8. Creación de un panel

    Ahora que configuramos el dispositivo y la red, creamos variables, completamos el boceto y cargamos correctamente el código, podemos pasar a la parte divertida: ¡crear tableros!

    Los tableros son una interfaz de usuario visual para interactuar con sus tableros en la nube, y podemos configurar muchas configuraciones diferentes según lo que necesite su proyecto de IoT. Podemos acceder a nuestros tableros haciendo clic en la pestaña «Tableros» en la parte superior de la interfaz de Arduino IoT Cloud, donde podemos crear nuevos tableros y ver una lista de tableros creados para otras cosas.

    Navegación a los tableros.
    Navegación a los tableros.

    Si hacemos clic en “Crear nuevo tablero” , entramos en un editor de tableros. Aquí, podemos crear algo llamado widgets . Los widgets son la representación visual de nuestras variables que creamos, y hay muchos diferentes para elegir. A continuación se muestra un ejemplo que utiliza varios tipos de widgets.

    Los diferentes widgets disponibles.
    Los diferentes widgets disponibles.

    Cuando creamos widgets, también debemos vincularlos a nuestras variables . Esto se hace haciendo clic en un widget que creamos, seleccionando una cosa y seleccionando una variable que queremos vincular. Una vez que está vinculado, podemos interactuar con él, por ejemplo, un botón, o podemos monitorear un valor de un sensor. ¡Mientras nuestra placa esté conectada a la nube, los valores se actualizarán!

    Digamos que tenemos un widget de temperatura que queremos vincular a la variable de temperatura dentro del proyecto de la nube .

    Vincular una variable a un widget.
    Vincular una variable a un widget.

    Tenga en cuenta que no todos los widgets y variables son compatibles. Un interruptor y un número entero, por ejemplo, no se pueden vincular y no serán una opción al configurar su tablero.

    También podemos tener varias cosas funcionando a la vez, dependiendo de tu plan Arduino IoT Cloud, que podemos incluir en el mismo dashboard. Esta es una excelente característica para rastrear múltiples tableros en, por ejemplo, una red de sensores más grande, donde los tableros se pueden conectar a diferentes redes en todo el mundo, pero se pueden monitorear desde el mismo tablero.

    zz

    Integración Arduino IoT Cloud Amazon Alexa


    En este post vamos integrar Arduino con Alexa para lo cual vamos a utilizar Arduino IoT Cloud, es decir usaremos el Editor web Arduino , Arduino IoT Cloud asi como Alexa Skills para Arduino .

    En cuanto al hardware se usaran los siguientes componentes:

    • Arduino MKR WiFi 1010 × 1
    • Escudo Arduino MKR ENV × 1
    • Protoboard (genérico) × 1
    • LED de alto brillo, blanco × 1
    • Cables de puente (genéricos) × 1
    • Cátodo común LED RGB × 1
    • Resistencia 220 ohmios

    1: Arduino IoT Cloud y montaje inicial

    El esquema que se va a usar es bastante simple, como se puede ver en la imagen de abajo, pues se basa en dos simples leds ( uno tricolor) con sus correspondientes resistencias limitadoras todo ello conectado a un arduino MKR

    Desde la página principal de IoT Cloud , crearemos algo nuevo y le asignaremos un nombre significativo Luego seleccionaremos el tablero que vamos a usar. Si no puede ver su tablero, es posible que haya omitido agregar un nuevo dispositivo siguiendo el procedimiento que comienza después de hacer clic en el botón AGREGAR DISPOSITIVO , en la sección Dispositivos .

    Una vez hecho esto, añadiremos tres propiedades a nuestra «Thing» (cosa) . Los dos primeros representarán las lámparas , el último la temperatura.

    El tipo ColouredLight nos permite controlar una Luz RGB , permitiéndonos controlar su brillo y color. Configúrelo como ReadandWrite porque necesitaremos controlar su comportamiento usando Alexa.

    El tipo DimmedLight es similar al anterior, pero solo nos permite controlar el brillo de una luz.

    La última propiedad a agregar es la temperatura . Agreguemos este como ReadOnly, ya que solo queremos saber su valor, no establecerlo. Como tipo podemos usar el sensor de temperatura (Celsius), pero siéntase libre de configurarlo como Fº si le conviene más.

    Así es como debería verse la vista del Panel de control de su cosa en este punto:

    Al agregar propiedades, asegúrese de usar solo los tipos enumerados en la sección Hogar inteligente ; de lo contrario, no funcionarán con Alexa.

    La sección Smart Home

    Ahora vaya al Editor web haciendo clic en el botón Editar boceto en la vista de edición de su cosa .

    2: Editor web Arduino

    En el Editor web necesitamos agregar algún código al Sketch generado automáticamente para nosotros.

    Debido a que queremos leer algunos datos ambientales (es decir, la temperatura, pero no dude en agregar luz, humedad y presión si lo desea), importaremos la biblioteca Arduino_MKRENV en la parte superior de nuestro Sketch.

    #include <Arduino_MKRENV.h>
    

    Luego definimos algunas constantes para los pines de la placa que se usarán para R, G, Bandwhitelight , de esta manera el código es más legible:

    #define PIN_MEETING_ROOM 5
    #define PIN_LOUNGE_AREA_R 2
    #define PIN_LOUNGE_AREA_B 3
    #define PIN_LOUNGE_AREA_G 4
    

    Ahora, en la función de configuración , podemos inicializar el Arduino MKR ENV Shield con:

    if (!ENV.begin()) {
       Serial.println("Failed to initialize MKR ENV shield!");
       while (1);
    }
    

    De esta forma si el escudo no está instalado se bloqueará la ejecución del Sketch .

    No necesitamos especificar que pinMode debe ser OUTPUT (saldia ) porque lusaremos analogWrite() para establecer la intensidad de nuestro LED blanco o los colores individuales del RGB.

    En la función de bucle vamos a leer la temperatura cada segundo:

    temperature = ENV.readTemperature();
    delay(1000);
    

    Finalmente, es hora de implementar nuestras funciones de devolución de llamada: las que se ejecutarán cada vez que se extraiga un cambio en el valor de una propiedad de IoT Cloud.

    Tenemos que implementar onMeetingRoomChangeonLoungeAreaChange:

    void onMeetingRoomChange() {
     uint8_t brightness = map(meetingRoom.getBrightness(), 0, 100, 0, 255);
     if (meetingRoom.getSwitch()) {
       Serial.println(brightness);
       analogWrite(PIN_MEETING_ROOM, brightness);    
     }
     else{
       analogWrite(PIN_MEETING_ROOM, LOW);
     }
    }
    

    Con el código de arriba primero leemos el valor de brillo de la nube y lo asignamos a un valor utilizable, luego verificamos si el interruptor de la luz está encendido, si lo está podemos encender la luz, usando el brightnessr ojo antes. De lo contrario apagamos la luz

    El principio de funcionamiento es el mismo para la otra devolución de llamada:

    void onLoungeAreaChange() {
     uint8_t r, g, b;
     loungeArea.getValue().getRGB(r, g, b);
     if (loungeArea.getSwitch()) {
       Serial.println("R:"+String(r)+" G:"+String(g)+ " B:"+String(b));
       analogWrite(PIN_LOUNGE_AREA_R, r);
       analogWrite(PIN_LOUNGE_AREA_B, b);
       analogWrite(PIN_LOUNGE_AREA_G, g);
     }
     else{
        Serial.println("Lamp Off");
        analogWrite(PIN_LOUNGE_AREA_R, 0);
        analogWrite(PIN_LOUNGE_AREA_B, 0);
        analogWrite(PIN_LOUNGE_AREA_G, 0);
     }
    }
    

    La única diferencia notable es el hecho de que en lugar del brillo solo, tenemos tres componentes diferentes: son la representación del color RGB de la luz. Por supuesto, podemos definir colores personalizados por nombre en la aplicación Alexa, por lo que no tendremos que decir manualmente qué cantidades de rojo, verde o azul queremos configurar.

    Este es el código al completo:

    /* 
      Sketch generated by the Arduino IoT Cloud Thing "Office"
      https://create.arduino.cc/cloud/things/f2c77d46-5abb-41e2-a8bb-3990155f342c 
    
      Arduino IoT Cloud Properties description
    
      The following variables are automatically generated and updated when changes are made to the Thing properties
    
      CloudTemperature temperature;
      CloudDimmedLight meetingRoom;
      CloudColoredLight loungeArea;
    
      Properties which are marked as READ/WRITE in the Cloud Thing will also have functions
      which are called when their values are changed from the Dashboard.
      These functions are generated with the Thing and added at the end of this sketch.
    */
    
    #include "thingProperties.h"
    #include <Arduino_MKRENV.h>
    
    #define PIN_MEETING_ROOM 5
    #define PIN_LOUNGE_AREA_R 2
    #define PIN_LOUNGE_AREA_B 3
    #define PIN_LOUNGE_AREA_G 4
    
    void setup() {
      // Initialize serial and wait for port to open:
      Serial.begin(9600);
      // This delay gives the chance to wait for a Serial Monitor without blocking if none is found
      delay(1500);
      
      if (!ENV.begin()) {
        Serial.println("Failed to initialize MKR ENV shield!");
        while (1);
      }
    
      // Defined in thingProperties.h
      initProperties();
    
      // Connect to Arduino IoT Cloud
      ArduinoCloud.begin(ArduinoIoTPreferredConnection);
      
      /*
         The following function allows you to obtain more information
         related to the state of network and IoT Cloud connection and errors
         the higher number the more granular information you’ll get.
         The default is 0 (only errors).
         Maximum is 4
     */
      setDebugMessageLevel(2);
      ArduinoCloud.printDebugInfo();
    }
    
    void loop() {
      ArduinoCloud.update();
      // Your code here 
      temperature = ENV.readTemperature();
      delay(1000);
    }
    
    
    void onMeetingRoomChange() {
      uint8_t brightness = map(meetingRoom.getBrightness(), 0, 100, 0, 255);
      if (meetingRoom.getSwitch()) {
        Serial.println(brightness);
        analogWrite(PIN_MEETING_ROOM, brightness);    
      }
      else{
        analogWrite(PIN_MEETING_ROOM, LOW);
      }
    }
    
    
    void onLoungeAreaChange() {
      uint8_t r, g, b;
      loungeArea.getValue().getRGB(r, g, b);
      if (loungeArea.getSwitch()) {
        Serial.println("R:"+String(r)+" G:"+String(g)+ " B:"+String(b));
        analogWrite(PIN_LOUNGE_AREA_R, r);
        analogWrite(PIN_LOUNGE_AREA_B, b);
        analogWrite(PIN_LOUNGE_AREA_G, g);
      }
      else{
         Serial.println("Lamp Off");
         analogWrite(PIN_LOUNGE_AREA_R, 0);
         analogWrite(PIN_LOUNGE_AREA_B, 0);
         analogWrite(PIN_LOUGE_AREA_G, 0);
      }
    }
    

    3: Configuración de habilidades de Amazon Alexa

    Ahora necesitaremos la aplicación Amazon Alexa, que se puede descargar desde la App Store de Apple o la Google Play Store . Una vez instalado, inicie sesión con su cuenta existente o cree una nueva.

    Repasemos los pasos necesarios para instalar Arduino Alexa Skill. Seguiremos la secuencia de imágenes a continuación para ver todos los pasos requeridos.

    1/9: toca "Dispositivos"
    Toque «Dispositivos»
    2/9: Ahora habilitemos nuestras habilidades para el hogar inteligente
    Ahora habilitemos nuestras habilidades para el hogar inteligente (smart Home Skills)

    Tecleando «Arduino» y tocando «buscar» obtendremos una lista. ¡Ese es el que necesitamos!

    Una vez seleccionado, tendremos que habilitarlo

    Inicie sesión con nuestras credenciales de cuenta Arduino Create

    Nuestra Alexa y Arduino IoT Cloud ahora pueden hablar entre sí

    Es hora de descubrir nuestros dispositivos

    Un poco de paciencia no hará daño, así que habrá que esperar hasta que Alexa reconozca los nuevos dispositivos que nos hemos montado.

    4: Configuración de dispositivos Amazon Alexa

    Después de esperar un poco, debería ver que se han encontrado algunos dispositivos.

    Después de tocar en Elegir dispositivo, se le presentará la página de configuración que enumera todos los dispositivos disponibles (se nombrarán de acuerdo con el nombre de nuestras propiedades en Arduino IoT Cloud).

    Vamos a elegir un dispositivo y toque Set Hasta dispositivo . Si lo desea, también puede agregarlo a un grupo (de esta manera puede controlar y organizar sus dispositivos más fácilmente), de lo contrario, omita esta parte.

    Repita el proceso de configuración para cada dispositivo que desee controlar.

    Finalmente, la vista del dispositivo debería verse así:

    Finalmente podemos empezar a preguntar cosas como «Alexa, ¿cuál es la temperatura en la oficina?» o «Alexa, enciende la luz de la sala de reuniones» .

    Más información en https://create.arduino.cc/projecthub/303628/arduino-iot-cloud-amazon-alexa-integration-4e6078

    Creacion de un aplicacion movil para Iot


    A veces crear una APP móvil para nuestro proyecto de IOT puede ser una labor muy compleja dependiendo muchísimo de la tecnologia que usemos para desarrollarla. Para intentar simplificar todo esto en este blog hemos hablado del entorno Cayenne que dispone también de infraestructura en la nube y también está disponible tanto versión web como móvil, pero en esta ocasión vamos a tratar una solución mucho más sencilla conocida como Blynk.  Si es la primera vez que conoce Blynk, ¡bienvenido! Blynk está aquí para ayudar a todos aquellos desarrolladores y emprendedores con talento que buscan crear prototipos, desplegar y gestionar remotamente dispositivos electrónicos conectados a cualquier escala. Esta  nueva plataforma le permitirá conectar el hardware a la nube y utilizar módulos de aplicación preconfeccionados para construir aplicaciones iOS, Android y web listas para los usuarios finales.

    Obtención de la placa Arduino adecuada para el trabajo

    Si desea realizar un proyecto Arduino que está conectado a Internet a través de WiFi, necesita una placa a que puede conectarse a Wi-Fi. Es así de simple. El Arduino UNO, nano, micro, Mega … no se puede conectar a Wi-Fi sin la adición de escudos caros y obsoletos. Así mismo puede ahorrar mucho tiempo y dinero y no hacer para esto a menos que tenga una buena razón para hacerlo. Hay afortunadamente placas mucho más baratas con conectividad wifi como pueden ser  ESP8266  y su sucesor el ESP32 ( asi como todos sus derivados).

    Una placa muy barata es  Wemos D1 o Wemos mini tablero de www.wemos.cc. Los Wemos D1 es un clon de la Arduino UNO  que utiliza el chip esp8266, cuenta con capacidades WiFi completo fuera de la caja y cuesta sólo $ 4,00 (aunque hoy en dia una placa basada es ESP32 es mucho más potente y versatil por muy poco mas)  .Esa  placa que vemos abajo es muy barata contando con un microprocesador que se conecta a Wi-Fi. Cuenta con todas las cosas que sabe y gusta  como: pines digitales, pines analógicos, Vin, GND, 3,3 V de salida, conector de alimentación, salida de 5 V y un conector micro USB rápido y fácil de cargar sus bocetos.

    Si necesita una placa  más pequeña, un ejemplo es la  Wemos mini (basada también en el ESP8266). 

    Getting the Right Arduino Board for the Job

    EJEMPLO INICIAL

    Inscribirse

    ‍ Su primer paso será crear una cuenta. Puede hacerlo descargando  la app para iOS y Android o en Blynk.Console . El proceso de registro es bastante sencillo, pero aquí puede consultar una guía más detallada si es necesario

    Nota: Si es usuario de Blynk 1.0, deberá crear una nueva cuenta para acceder a la nueva plataforma.

    Habilitar el modo de desarrollador

    ‍ El desarrollador Por lo general, es alguien que construye el hardware, desarrolla el firmware y realiza todas las configuraciones del dispositivo.

    Habilitar el modo de desarrollador:
    1. En la aplicación Blynk o en Blynk.Console
    2. Vaya a Mi perfil / Perfil de usuario en el menú de la izquierda
    3. Verifique que el interruptor del modo de desarrollador esté en ON

    Nota: actualmente, solo se permite un desarrollador por organización para evitar problemas de sincronización. Este límite se puede cambiar más adelante.

    Configuración rápida de plantilla

    ‍ Una vez que esté en modo desarrollador, puede comenzar a trabajar en su primera plantilla de dispositivo. Plantilla de dispositivo es un conjunto de configuraciones heredadas por dispositivos de un tipo similar. Piense en los interruptores domésticos inteligentes. Todos realizan una función similar y es seguro asumir que deberían tener el mismo modelo de datos, GPIO, código de firmware, etc. Si necesitara introducir cambios en todos estos dispositivos, en lugar de editar cada uno de ellos, simplemente podría editar se actualizará una plantilla de dispositivo y todos los dispositivos.

    Active su primer dispositivo

    ‍ Ahora que ha creado una o más plantillas de dispositivos, puede comenzar a poner sus dispositivos en línea.

    Para comenzar a usar Blynk.Cloud, debe asignar un AuthToken único a cada dispositivo. El objetivo de AuthToken es identificar el dispositivo en Blynk Cloud. Hay algunas formas de obtener tokens de autenticación para su dispositivo y pueden variar según el hardware, la conectividad y el caso de uso de IoT en el que esté trabajando.

    Estas son las dos formas principales de poner AuthTokens en sus dispositivos:

    A. Aprovisionamiento de WiFi mediante Blynk.Edgent

    ‍ Para los dispositivos que pueden conectarse a Internet a través de WIFI, recomendamos utilizar el método de aprovisionamiento WiFi integrado en la aplicación Blynk. Este método también se denomina aprovisionamiento de tokens de autenticación dinámicos.

    La aplicación Blynk y Blynk.Edgent se encargarán de decirle a sus dispositivos cómo conectarse a la red WiFi de su hogar u oficina. El nuevo token de autenticación se generará y almacenará automáticamente en el dispositivo. No necesita especificar las credenciales de WiFi y el token de autenticación en el código de firmware.

    Recomendamos enfáticamente utilizar el aprovisionamiento WiFi si está trabajando en un producto WiFi comercial. Beneficios del método de aprovisionamiento WiFi para aplicaciones comerciales:

    • No puede predecir a qué red WiFi sus clientes conectarán sus productos.
    • Simplifica el proceso de fabricación a escala, ya que puede usar un código de firmware similar sin necesidad de agregar manualmente el token de autenticación a cada dispositivo al final de la línea de fabricación.
    B. Activación del dispositivo con un token de autenticación estático (para Ethernet, celular y otros tipos de conexión)

    ‍ Este método se recomienda para dispositivos que pueden conectarse a Internet mediante Ethernet, celular (2G, 3G, 4G, LTE) u otros tipos de conexión (que no requieren credenciales WiFi personalizadas, por ejemplo).

    La principal diferencia con el aprovisionamiento de WiFi es que AuthToken debe generarse manualmente y actualizarse en el dispositivo antes de que pueda usarse.

    El token de autenticación estático se usa a menudo durante la etapa de creación de prototipos. Sin embargo, Blynk también ofrece una solución completa para trabajar con tokens estáticos en las aplicaciones comerciales.

    Enviar datos del dispositivo a Blynk

    ‍ Para enviar mensajes desde la aplicación al código que se ejecuta en su placa (a través del servidor Blynk), utilizará pines virtuales.

    • Los pines virtuales son independientes del hardware. Esto significa que es mucho más fácil transferir su código de una plataforma de hardware a otra en el futuro (por ejemplo, puede darse cuenta de que NodeMCU es mejor que Arduino Uno + ESP-01 con el que comenzó).
    • Tienes mucho más control sobre lo que hace tu widget cuando usas pines virtuales. Por ejemplo, si desea que un solo botón de aplicación active o desactive varios relés al mismo tiempo, es muy fácil hacerlo con pines virtuales.
    • Los pines virtuales son más predecibles (estables si lo desea) que manipular pines digitales.
    Configurar notificaciones para un solo dispositivo o para una selección de dispositivos

    ‍ Nota : las notificaciones funcionan además de Eventos . Antes de continuar, asegúrese de que su producto tenga al menos un evento configurado en sus notificaciones de configuración de plantilla para una selección de dispositivos

    1. Abrir búsqueda
    2. Ir a Dispositivos
    3. Seleccione varios dispositivos
    4. Pase el cursor sobre el menú Acciones […]
    5. Haga clic en Configuración de notificaciones

    En el cajón abierto, seleccione los eventos deseados para editar

    1. Haga clic en Editar configuración

    En la ventana modal, active los canales deseados (Correo electrónico, Push o SMS), seleccione los destinatarios . Si el destinatario no está en la lista, puede buscar escribiendo el nombre o la dirección de correo electrónico.

    Tenga en cuenta que está la entrada «Todos los miembros». Significa que todos los usuarios de la organización a la que pertenece el dispositivo serán los destinatarios de una notificación.

    EJEMPLO  CON WEMOS

    1. Instale la última versión del IDE de Arduino www.arduino.cc

    2. Siga los pasos en el sitio Wemos para configurar el IDE de Arduino para apoyar la placa Wemos  http:? Id = //www.wemos.cc/wiki/doku.php en: arduino_g …

    3. Descargue los ejemplos de  Wemos D1 desde https: //github.com/wemos/D1_mini_Examples/archive / …

    4. Descomprima el archivo descargado  de https://github.com/wemos/D1_mini_Examples/archive/master.zip
    5. Copie todos los archivos en el directorio de ejemplos de Arduino: (normalmente C: \ Archivos de programa (x86) \ Arduino \ ejemplos)
    6. IDE Arduino abierto, seleccione Archivo – ejemplos – Mini ejemplos D1 – Bases – abrir y cerrar
    7. En Herramientas, seleccione su tablero Wemos y el puerto COM correcto (éste es el puerto USB que está utilizando, por lo general sólo una opción estará disponible)
    8. Suba su boceto. Si  su tarjeta está parpadeando, lo hizo bien, si no, se ha perdido uno de los pasos anteriores.

    Creación de su  aplicación con Blynk

    Para hacer  nuestra aplicación vamos a utilizar Blynk. Blynk es una sencilla herramienta que te permite crear aplicaciones en su teléfono y compartir esas aplicaciones con otras personas. Es un sistema de arrastrar y soltar lo que no requiere lenguaje de programación y toma el control directo de los pines del Arduino . Comenzó como un pedal de arranque en 2015, pero creció rápidamente para ser una herramienta profesional y de gran utilidad. la mejor parte: la aplicación se creará en sólo un minuto y estará totalmente adaptable! (Únase a los foros Blynk si necesita ayuda o quiere contribuir).

    1. Descargue la última biblioteca de Blynk http://www.blynk.cc/getting-started/,descomprimir y copiarlo en el directorio de la biblioteca Arduino (normalmente: C: \ Archivos de programa (x86) \ Arduino \ bibliotecas).

    2. Descargue la aplicación Blynk para Android o Iphone  y hacer su primera aplicación. Vaya a la configuración y elija Esp8266.

    3. En «configuración» en la aplicación que se encuentra «token de autenticación» por email a si mismo, que lo necesitará más adelante .

    4. Abra el IDE Arduino.
    5. Bajo el item  ejemplos, elija Blynk, «independiente esp8266»

    6. En el boceto, agregue el token de autenticación de la etapa 3, donde dice token de autenticación. SSID es el  nombre de la conexión Wi-Fi y la contraseña es la contraseña de su red wifi. Llene estos tres en el croquis en sus respectivos lugares . No hay necesidad de seguir elaborando el boceto. Sólo con este código tendrá un control total sobre todos los pasadores de su tarjeta a través de la app.

    Creating Your Android / Iphone App With Blynk

    Prueba de su aplicación Blynk!

    1. Añadir un LED a GND y el pin 13 en su tablero Wemos (Quiero mencionar que la forma correcta de hacer esto, es con una resistencia entre el pin 13 y el LED. Su LED no va a vivir una vida muy larga y sin)
    2. En la aplicación en su teléfono o tableta, agregue un control deslizante conectado al pin gpO14.(Nótese que las patas de la placa no son los mismos que los pasadores en la aplicación. Para una descripción completa, consulte el diagrama anterior, Observe también que pines I / O en Wemos tableros son de 3,3 voltios, no de 5 voltios al igual que en la Arduino UNO. no enviar 5 voltios a través de ellos!)
    3. En el IDE de Arduino pulsar  el botón de reproducción en la esquina superior derecha de la aplicación Blynk. FELICITACIONES !!!!

    Acabamos de realizar  un proyecto de electrónica TOTALMENTE CONTROLADO INTELIGENTE !!!!, para ello mueva el control deslizante hacia la derecha para hacer que el led ir más brillante, a la izquierda del atenuador de luminosidad.

    TEST Your Blynk App!

    Algunas observaciones importantes

    – Como se mencionado antes en el paso 4 los pines de la placa no coinciden con la aplicación, consulte el sitio Wemos para la disposición de las clavijas de la placa específica. Es probable que en un futuro próximo Blynk añadirá soporte Wemos por supuesto, a continuación, seleccione su tablero de aplicación y los pasadores coincidirán.

    – 3,3 v es todo el i / o pasadores de apoyo Wemos. Va a romper su tablero si envía más a través de él. Un circuito divisor de resistencia o tensión simple puede ayudar si usted fuera a ser pegado con 5v entrada que necesita convertir.

    – Los Wemos sólo tiene un pin analógico. Si necesita mucho más, necesitara usar otra placa como puede ser el ESP32

    Fuente aqui 

    Programación OTA en ESP32


    Una de las mejores cosas de ESP32 es que su firmware se puede actualizar de forma inalámbrica. Este tipo de programación se llama «Over-The-Air» (OTA) permitiendo actualizar/cargar un nuevo programa al ESP32 a través de Wi-Fi sin tener que conectar el ESP32 a un ordenador a través de USB.

    La funcionalidad OTA es útil cuando no hay acceso físico al módulo ESP pero además, reduce el tiempo necesario para actualizar cada módulo ESP durante el mantenimiento.

    Una ventaja clave de OTA es que una única ubicación central puede enviar una actualización a varios ESP en la misma red.

    La única desventaja es que debe incluir un código OTA con cada boceto que cargue para poder usar OTA en la próxima actualización.

    Hay dos formas de implementar la funcionalidad OTA en el ESP32:

    • OTA básico : las actualizaciones se envían mediante el IDE de Arduino.
    • Web Updater OTA : las actualizaciones se entregan a través de un navegador web.

    Cada uno tiene sus propios beneficios, por lo que puede usar el que funcione mejor para su proyecto.

    OTA Básico

    En este primer apartado veremos el proceso de implementación de OTA básico.  En resumen son 3 pasos simples para usar OTA básico con el ESP32

    1. Instalación de la serie Python 2.7.x: el primer paso es instalar la serie Python 2.7.x en su computadora.
    2. Carga de firmware OTA básico en serie: cargue el boceto que contiene el firmware OTA en serie. Este es un paso necesario para realizar las actualizaciones posteriores de forma inalámbrica.
    3. Carga de nuevos bocetos por aire: ahora puede cargar nuevos bocetos al ESP32 desde Arduino IDE por aire.

    1: Instalación de Python 2.7.x

    Para usar la funcionalidad OTA, primero debe instalar Python 2.7.x, si aún no está instalado en su máquina. Descargue Python 2.7.x para Windows (instalador MSI) desde el sitio web oficial de Python .

    Inicie el instalador y continúe con el asistente de instalación

    Asegúrese de que la opción «Agregar python.exe a la ruta» esté habilitada en la sección Personalizar Python 2.7.X.

    2: Carga del firmware OTA básico en serie

    Debido a que la imagen de fábrica del ESP32 carece de la capacidad de actualización OTA, primero debe cargar el firmware OTA en el ESP32 a través de la interfaz serial.

    Es necesario actualizar primero el firmware para poder realizar actualizaciones inalámbricas posteriores.

    El complemento ESP32 para Arduino IDE incluye una biblioteca OTA, así como un ejemplo de BasicOTA. Simplemente navegue a Archivo > Ejemplos > ArduinoOTA > BasicOTA .

    Antes de comenzar a cargar el boceto, debe modificar las siguientes dos variables con sus credenciales de red para que el ESP32 pueda conectarse a una red existente.

    const char* ssid = "..........";
    const char* password = "..........";

    Cuando haya terminado, siga adelante y cargue el boceto.

    #include <WiFi.h>
    #include <ESPmDNS.h>
    #include <WiFiUdp.h>
    #include <ArduinoOTA.h>
    
    const char* ssid = "..........";
    const char* password = "..........";
    
    void setup() {
      Serial.begin(115200);
      Serial.println("Booting");
      WiFi.mode(WIFI_STA);
      WiFi.begin(ssid, password);
      while (WiFi.waitForConnectResult() != WL_CONNECTED) {
        Serial.println("Connection Failed! Rebooting...");
        delay(5000);
        ESP.restart();
      }
    
      // Port defaults to 3232
      // ArduinoOTA.setPort(3232);
    
      // Hostname defaults to esp3232-[MAC]
      // ArduinoOTA.setHostname("myesp32");
    
      // No authentication by default
      // ArduinoOTA.setPassword("admin");
    
      // Password can be set with it's md5 value as well
      // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
      // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
    
      ArduinoOTA
        .onStart([]() {
          String type;
          if (ArduinoOTA.getCommand() == U_FLASH)
            type = "sketch";
          else // U_SPIFFS
            type = "filesystem";
    
          // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
          Serial.println("Start updating " + type);
        })
        .onEnd([]() {
          Serial.println("\nEnd");
        })
        .onProgress([](unsigned int progress, unsigned int total) {
          Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
        })
        .onError([](ota_error_t error) {
          Serial.printf("Error[%u]: ", error);
          if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
          else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
          else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
          else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
          else if (error == OTA_END_ERROR) Serial.println("End Failed");
        });
    
      ArduinoOTA.begin();
    
      Serial.println("Ready");
      Serial.print("IP address: ");
      Serial.println(WiFi.localIP());
    }
    
    void loop() {
      ArduinoOTA.handle();
    }

    Ahora, abra el Serial Monitor a 115200 baudios y presione el botón EN en el ESP32. Si todo está bien, debería ver la dirección IP dinámica asignada por su enrutador. Tome nota de ello.

    Paso 3: Carga del nuevo boceto por aire

    Ahora, subamos un nuevo boceto por aire. Recuerde que debes incluir el código OTA en cada sketch que subas. De lo contrario, perderá la capacidad OTA y no podrá realizar la próxima carga inalámbrica. Por lo tanto, se recomienda que modifique el código anterior para incluir su nuevo código.

    Como ejemplo, incluiremos un boceto Blink simple en el código OTA básico. Recuerda modificar las variables SSID y contraseña con sus credenciales de red.

    Los cambios en el programa Basic OTA están resaltados con comentarios //******.

    #include <WiFi.h>
    #include <ESPmDNS.h>
    #include <WiFiUdp.h>
    #include <ArduinoOTA.h>
    
    const char* ssid = "..........";
    const char* password = "..........";
    
    
    //******codigo programa*******
    //variabls for blinking an LED with Millis
    const int led = 2; // ESP32 Pin to which onboard LED is connected
    unsigned long previousMillis = 0;  // will store last time LED was updated
    const long interval = 1000;  // interval at which to blink (milliseconds)
    int ledState = LOW;  // ledState used to set the LED
    
    //******fin codigo programa*******
    
    void setup() {
    
    //******codigo programa*******
    pinMode(led, OUTPUT);
    //******fin codigo programa*******
      
      Serial.begin(115200);
      Serial.println("Booting");
      WiFi.mode(WIFI_STA);
      WiFi.begin(ssid, password);
      while (WiFi.waitForConnectResult() != WL_CONNECTED) {
        Serial.println("Connection Failed! Rebooting...");
        delay(5000);
        ESP.restart();
      }
    
      // Port defaults to 3232
      // ArduinoOTA.setPort(3232);
    
      // Hostname defaults to esp3232-[MAC]
      // ArduinoOTA.setHostname("myesp32");
    
      // No authentication by default
      // ArduinoOTA.setPassword("admin");
    
      // Password can be set with it's md5 value as well
      // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
      // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
    
      ArduinoOTA
        .onStart([]() {
          String type;
          if (ArduinoOTA.getCommand() == U_FLASH)
            type = "sketch";
          else // U_SPIFFS
            type = "filesystem";
    
          // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
          Serial.println("Start updating " + type);
        })
        .onEnd([]() {
          Serial.println("\nEnd");
        })
        .onProgress([](unsigned int progress, unsigned int total) {
          Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
        })
        .onError([](ota_error_t error) {
          Serial.printf("Error[%u]: ", error);
          if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
          else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
          else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
          else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
          else if (error == OTA_END_ERROR) Serial.println("End Failed");
        });
    
      ArduinoOTA.begin();
    
      Serial.println("Ready");
      Serial.print("IP address: ");
      Serial.println(WiFi.localIP());
    }
    
    void loop() {
      ArduinoOTA.handle();  
    
    //******codigo programa*******
    //loop to blink without delay
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis >= interval) {
      // save the last time you blinked the LED
      previousMillis = currentMillis;
      // if the LED is off turn it on and vice-versa:
      ledState = not(ledState);
      // set the LED with the ledState of the variable:
      digitalWrite(led,  ledState);
     //******fin codigo programa*******
      }
    
    }

    Tenga en cuenta que no hemos utilizado la delay()función para hacer que el LED parpadee. Esto se debe a que la delay()función pausa el programa. Si se genera la siguiente solicitud OTA mientras el ESP32 está en pausa esperando a delay()que se complete, su programa perderá esa solicitud.

    Después de copiar el boceto anterior en su IDE de Arduino, navegue hasta la opción Herramientas > Puerto . Busque algo como: esp32-xxxxxx en your_esp_ip_address . Si no puede localizarlo, es posible que deba reiniciar su IDE.

    Elija el puerto y presione el botón Cargar. El nuevo boceto se cargará en cuestión de segundos. El LED integrado debería comenzar a parpadear.

    Actualizador OTA via web

    La programación OTA es útil cuando necesita actualizar el código de las placas ESP32 a las que no se puede acceder fácilmente. El ejemplo que mostraremos aquí funciona cuando el ESP32 y su navegador están en su red local.

    La única desventaja de OTA Web Updater es que debe agregar el código para OTA en cada boceto que cargue, para que pueda usar OTA en el futuro.

    ¿Cómo funciona el Actualizador web OTA?

    • El primer boceto debe cargarse a través del puerto serie. Este boceto debe contener el código para crear el actualizador web OTA, de modo que pueda cargar el código más tarde con su navegador.
    • El boceto de OTA Web Updater crea un servidor web al que puede acceder para cargar un nuevo boceto a través del navegador web.
    • Luego, debe implementar rutinas OTA en cada boceto que cargue, de modo que pueda realizar las próximas actualizaciones/cargas por aire.
    • Si carga un código sin una rutina OTA, ya no podrá acceder al servidor web y cargar un nuevo boceto por aire.

    Antes de continuar , debe tener instalado el complemento ESP32 en su IDE de Arduino. Veamos los pasos a seguir

    1-Carga de OTAWebUpdater

    Cuando instala el complemento ESP32 para Arduino IDE, instalará automáticamente la biblioteca ArduinoOTA. Vaya a Archivo > Ejemplos > ArduinoOTA > OTAWebUpdater .

    otawebupdater-arduino-ide

    Debería cargarse el siguiente código.

    /*
     * OTAWebUpdater.ino Example from ArduinoOTA Library
     * Rui Santos 
     * Complete Project Details https://randomnerdtutorials.com
     */
    
    #include <WiFi.h>
    #include <WiFiClient.h>
    #include <WebServer.h>
    #include <ESPmDNS.h>
    #include <Update.h>
    
    const char* host = "esp32";
    const char* ssid = "REPLACE_WITH_YOUR_SSID";
    const char* password = "REPLACE_WITH_YOUR_PASSWORD";
    
    WebServer server(80);
    
    /*
     * Login page
     */
    const char* loginIndex = 
     "<form name='loginForm'>"
        "<table width='20%' bgcolor='A09F9F' align='center'>"
            "<tr>"
                "<td colspan=2>"
                    "<center><font size=4><b>ESP32 Login Page</b></font></center>"
                    "<br>"
                "</td>"
                "<br>"
                "<br>"
            "</tr>"
            "<td>Username:</td>"
            "<td><input type='text' size=25 name='userid'><br></td>"
            "</tr>"
            "<br>"
            "<br>"
            "<tr>"
                "<td>Password:</td>"
                "<td><input type='Password' size=25 name='pwd'><br></td>"
                "<br>"
                "<br>"
            "</tr>"
            "<tr>"
                "<td><input type='submit' onclick='check(this.form)' value='Login'></td>"
            "</tr>"
        "</table>"
    "</form>"
    "<script>"
        "function check(form)"
        "{"
        "if(form.userid.value=='admin' && form.pwd.value=='admin')"
        "{"
        "window.open('/serverIndex')"
        "}"
        "else"
        "{"
        " alert('Error Password or Username')/*displays error message*/"
        "}"
        "}"
    "</script>";
     
    /*
     * Server Index Page
     */
     
    const char* serverIndex = 
    "https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"
    "<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
       "<input type='file' name='update'>"
            "<input type='submit' value='Update'>"
        "</form>"
     "<div id='prg'>progress: 0%</div>"
     "<script>"
      "$('form').submit(function(e){"
      "e.preventDefault();"
      "var form = $('#upload_form')[0];"
      "var data = new FormData(form);"
      " $.ajax({"
      "url: '/update',"
      "type: 'POST',"
      "data: data,"
      "contentType: false,"
      "processData:false,"
      "xhr: function() {"
      "var xhr = new window.XMLHttpRequest();"
      "xhr.upload.addEventListener('progress', function(evt) {"
      "if (evt.lengthComputable) {"
      "var per = evt.loaded / evt.total;"
      "$('#prg').html('progress: ' + Math.round(per*100) + '%');"
      "}"
      "}, false);"
      "return xhr;"
      "},"
      "success:function(d, s) {"
      "console.log('success!')" 
     "},"
     "error: function (a, b, c) {"
     "}"
     "});"
     "});"
     "</script>";
    
    /*
     * setup function
     */
    void setup(void) {
      Serial.begin(115200);
    
      // Connect to WiFi network
      WiFi.begin(ssid, password);
      Serial.println("");
    
      // Wait for connection
      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
      }
      Serial.println("");
      Serial.print("Connected to ");
      Serial.println(ssid);
      Serial.print("IP address: ");
      Serial.println(WiFi.localIP());
    
      /*use mdns for host name resolution*/
      if (!MDNS.begin(host)) { //http://esp32.local
        Serial.println("Error setting up MDNS responder!");
        while (1) {
          delay(1000);
        }
      }
      Serial.println("mDNS responder started");
      /*return index page which is stored in serverIndex */
      server.on("/", HTTP_GET, []() {
        server.sendHeader("Connection", "close");
        server.send(200, "text/html", loginIndex);
      });
      server.on("/serverIndex", HTTP_GET, []() {
        server.sendHeader("Connection", "close");
        server.send(200, "text/html", serverIndex);
      });
      /*handling uploading firmware file */
      server.on("/update", HTTP_POST, []() {
        server.sendHeader("Connection", "close");
        server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
        ESP.restart();
      }, []() {
        HTTPUpload& upload = server.upload();
        if (upload.status == UPLOAD_FILE_START) {
          Serial.printf("Update: %s\n", upload.filename.c_str());
          if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
            Update.printError(Serial);
          }
        } else if (upload.status == UPLOAD_FILE_WRITE) {
          /* flashing firmware to ESP*/
          if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
            Update.printError(Serial);
          }
        } else if (upload.status == UPLOAD_FILE_END) {
          if (Update.end(true)) { //true to set the size to the current progress
            Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
          } else {
            Update.printError(Serial);
          }
        }
      });
      server.begin();
    }
    
    void loop(void) {
      server.handleClient();
      delay(1);
    }
    

    Debe cambiar las siguientes líneas en el código para incluir sus propias credenciales de red:

    const char* ssid = "";
    const char* contraseña = "";

    El ejemplo de OTAWebUpdater para ESP32 crea un servidor web asíncrono donde puede cargar código nuevo en su placa sin necesidad de una conexión en serie.Suba el código anterior a tu placa ESP32. No olvide ingresar sus credenciales de red y seleccionar la placa y el puerto serial correctos.

    Después de cargar el código, abra el monitor serie a una velocidad de transmisión de 115200, presione el botón de activación de ESP32 y obtendrá la dirección IP de ESP32:

    Ahora, puede cargar el código a su ESP32 por aire usando un navegador en su red local.

    Para probar el Actualizador web OTA, puede desconectar el ESP32 de su computadora y encenderlo con un banco de energía, por ejemplo (esto es opcional, lo sugerimos para imitar una situación en la que el ESP32 no está conectado a su computadora).

    2-Actualización del nuevo código usando el actualizador web OTA

    Abra un navegador en su red e ingrese la dirección IP ESP32. Deberías obtener lo siguiente:

    otawebupdater-usuario-contraseña

    Introduzca el nombre de usuario y la contraseña:

    • Nombre de usuario : admin
    • contraseña : admin

    Puede cambiar el nombre de usuario y la contraseña en el código.

    Nota:  Después de ingresar el nombre de usuario y la contraseña, será redirigido a la URL /serverIndex . No necesita ingresar el nombre de usuario y la contraseña para acceder a la URL /serverIndex . Por lo tanto, si alguien conoce la URL para cargar el nuevo código, el nombre de usuario y la contraseña no protegen la página web para que otros no puedan acceder a ella.

    Debería abrirse una nueva pestaña en la URL /serverIndex . Esta página le permite cargar un nuevo código a su ESP32. Debería cargar archivos .bin (veremos cómo hacerlo en un momento).

    ota-web-updater-esp32

    3-Preparación del nuevo sketch

    Al cargar un nuevo boceto por aire, debe tener en cuenta que debe agregar código para OTA en su nuevo boceto, de modo que siempre pueda sobrescribir cualquier boceto con uno nuevo en el futuro. Por lo tanto, le recomendamos que modifique el boceto de OTAWebUpdater para incluir su propio código.

    Con fines de aprendizaje, subamos un nuevo código que parpadee un LED (sin demora). Copie el siguiente código en su IDE de Arduino.

    /*
     * Rui Santos 
     * Complete Project Details https://randomnerdtutorials.com
     */
    
    #include <WiFi.h>
    #include <WiFiClient.h>
    #include <WebServer.h>
    #include <ESPmDNS.h>
    #include <Update.h>
    
    const char* host = "esp32";
    const char* ssid = "REPLACE_WITH_YOUR_SSID";
    const char* password = "REPLACE_WITH_YOUR_PASSWORD";
    
    //variabls to blink without delay:
    const int led = 2;
    unsigned long previousMillis = 0;        // will store last time LED was updated
    const long interval = 1000;           // interval at which to blink (milliseconds)
    int ledState = LOW;             // ledState used to set the LED
    
    WebServer server(80);
    
    /*
     * Login page
     */
    
    const char* loginIndex = 
     "<form name='loginForm'>"
        "<table width='20%' bgcolor='A09F9F' align='center'>"
            "<tr>"
                "<td colspan=2>"
                    "<center><font size=4><b>ESP32 Login Page</b></font></center>"
                    "<br>"
                "</td>"
                "<br>"
                "<br>"
            "</tr>"
            "<td>Username:</td>"
            "<td><input type='text' size=25 name='userid'><br></td>"
            "</tr>"
            "<br>"
            "<br>"
            "<tr>"
                "<td>Password:</td>"
                "<td><input type='Password' size=25 name='pwd'><br></td>"
                "<br>"
                "<br>"
            "</tr>"
            "<tr>"
                "<td><input type='submit' onclick='check(this.form)' value='Login'></td>"
            "</tr>"
        "</table>"
    "</form>"
    "<script>"
        "function check(form)"
        "{"
        "if(form.userid.value=='admin' && form.pwd.value=='admin')"
        "{"
        "window.open('/serverIndex')"
        "}"
        "else"
        "{"
        " alert('Error Password or Username')/*displays error message*/"
        "}"
        "}"
    "</script>";
     
    /*
     * Server Index Page
     */
     
    const char* serverIndex = 
    "https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"
    "<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
       "<input type='file' name='update'>"
            "<input type='submit' value='Update'>"
        "</form>"
     "<div id='prg'>progress: 0%</div>"
     "<script>"
      "$('form').submit(function(e){"
      "e.preventDefault();"
      "var form = $('#upload_form')[0];"
      "var data = new FormData(form);"
      " $.ajax({"
      "url: '/update',"
      "type: 'POST',"
      "data: data,"
      "contentType: false,"
      "processData:false,"
      "xhr: function() {"
      "var xhr = new window.XMLHttpRequest();"
      "xhr.upload.addEventListener('progress', function(evt) {"
      "if (evt.lengthComputable) {"
      "var per = evt.loaded / evt.total;"
      "$('#prg').html('progress: ' + Math.round(per*100) + '%');"
      "}"
      "}, false);"
      "return xhr;"
      "},"
      "success:function(d, s) {"
      "console.log('success!')" 
     "},"
     "error: function (a, b, c) {"
     "}"
     "});"
     "});"
     "</script>";
    
    /*
     * setup function
     */
    void setup(void) {
      pinMode(led, OUTPUT);
      
      Serial.begin(115200);
    
      // Connect to WiFi network
      WiFi.begin(ssid, password);
      Serial.println("");
    
      // Wait for connection
      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
      }
      Serial.println("");
      Serial.print("Connected to ");
      Serial.println(ssid);
      Serial.print("IP address: ");
      Serial.println(WiFi.localIP());
    
      /*use mdns for host name resolution*/
      if (!MDNS.begin(host)) { //http://esp32.local
        Serial.println("Error setting up MDNS responder!");
        while (1) {
          delay(1000);
        }
      }
      Serial.println("mDNS responder started");
      /*return index page which is stored in serverIndex */
      server.on("/", HTTP_GET, []() {
        server.sendHeader("Connection", "close");
        server.send(200, "text/html", loginIndex);
      });
      server.on("/serverIndex", HTTP_GET, []() {
        server.sendHeader("Connection", "close");
        server.send(200, "text/html", serverIndex);
      });
      /*handling uploading firmware file */
      server.on("/update", HTTP_POST, []() {
        server.sendHeader("Connection", "close");
        server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
        ESP.restart();
      }, []() {
        HTTPUpload& upload = server.upload();
        if (upload.status == UPLOAD_FILE_START) {
          Serial.printf("Update: %s\n", upload.filename.c_str());
          if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
            Update.printError(Serial);
          }
        } else if (upload.status == UPLOAD_FILE_WRITE) {
          /* flashing firmware to ESP*/
          if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
            Update.printError(Serial);
          }
        } else if (upload.status == UPLOAD_FILE_END) {
          if (Update.end(true)) { //true to set the size to the current progress
            Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
          } else {
            Update.printError(Serial);
          }
        }
      });
      server.begin();
    }
    
    void loop(void) {
      server.handleClient();
      delay(1);
    
      //loop to blink without delay
      unsigned long currentMillis = millis();
    
      if (currentMillis - previousMillis >= interval) {
        // save the last time you blinked the LED
        previousMillis = currentMillis;
    
        // if the LED is off turn it on and vice-versa:
        ledState = not(ledState);
    
        // set the LED with the ledState of the variable:
        digitalWrite(led, ledState);
      }
    }
    

    Como puede ver, hemos agregado el código «parpadeo sin demora» al código OTAWebUpdater, para que podamos realizar actualizaciones más adelante.

    Después de copiar el código en su IDE de Arduino, debe generar un archivo .bin .

    4-Generación del archivo .bin en Arduino IDE

    Guarde su boceto como LED_Web_Updater .

    Para generar un archivo .bin a partir de su boceto, vaya a Boceto > Exportar binario compilado

    exportar-archivo-bin-arduino-ide

    Se debe crear un nuevo archivo en el boceto de la carpeta. Vaya a Bosquejo > Mostrar carpeta de bosquejo . Debe tener dos archivos en su carpeta Sketch: el archivo .ino y el archivo .bin . Debe cargar el archivo .bin mediante el actualizador web de OTA.

    5-Carga del nuevo sketch por aire al ESP32

    En su navegador, en la página ESP32 OTA Web Updater, haga clic en el botón Elegir archivo . Seleccione el archivo .bin generado anteriormente y luego haga clic en Actualizar .

    Después de unos segundos, el código debería cargarse correctamente.

    El LED incorporado ESP32 debe estar parpadeando.¡Felicidades! Ha subido un nuevo código a tu ESP32 por aire.

    Las actualizaciones inalámbricas hemos visto que son útiles para cargar un nuevo código en su placa ESP32 cuando no es fácilmente accesible. En este segundo caso hemos visto el código OTA Web Updater como crea un servidor web al que puede acceder para cargar un nuevo código en su placa ESP32 utilizando un navegador web en su red local.

    Fuentes

    https://lastminuteengineers.com/esp32-ota-updates-arduino-ide/

    ESP32 Over-the-air (OTA) Programming – Web Updater Arduino IDE

    Ejecutando código en paralelo con el ESP32


    El objetivo de esta post es en primer lugar comprender como se puede ejecutar la multitarea en el ESP32 para después ver como se implementa un algoritmo de cálculo de potencia simple en el ESP32 y probar la aceleración ejecutándolo en los dos núcleos del microcontrolador.

    El ESP32 viene con 2 microprocesadores Xtensa LX6 de 32 bits: core 0 y core 1. Entonces, es dual core. Cuando ejecutamos código en Arduino IDE, de forma predeterminada, se ejecuta en el núcleo 1. En esta publicación, le mostraremos cómo ejecutar código en el segundo núcleo ESP32 mediante la creación de tareas. Puede ejecutar piezas de código simultáneamente en ambos núcleos y hacer que su ESP32 sea multitarea. Como nota imporante  no necesariamente necesita ejecutar doble núcleo para lograr la multitarea.

    El ESP32 por tanto tiene dos núcleos Tensilica LX6 [1] que podemos usar para ejecutar código. Al momento de escribir, la forma más fácil de controlar la ejecución de código en los diferentes núcleos del ESP32 es usando FreeRTOS y asignar una tarea a cada CPU

    Aunque de forma genérica son muchos los beneficios de tener más de un núcleo disponible para ejecutar código, uno de los más importantes es aumentar el rendimiento de nuestros programas. Entonces, aunque veremos en este post cómo ejecutar código en los dos núcleos del ESP32, también comprobaremos el aumento de rendimiento que podemos obtener de eso mediante una aplicación simple que sea capaz de calcular una potencia de los números de una matriz. Lo ejecutaremos en un solo núcleo y luego dividiremos el procesamiento por los dos núcleos y verificaremos si hay una ganancia en el tiempo de ejecución. Finalmente, solo para comparar, también dividiremos la ejecución entre cuatro tareas (dos asignadas a cada núcleo) solo para verificar si hay alguna aceleración para generar más tareas que núcleos.

    Introducción

    El ESP32 viene con 2 microprocesadores Xtensa LX6 de 32 bits, por lo que es de doble núcleo:

    • Núcleo 0
    • Núcleo 1

    Cuando subimos el código al ESP32 usando el IDE de Arduino, simplemente se ejecuta; no tenemos que preocuparnos de qué núcleo ejecuta el código.

    Hay una función que puede usar para identificar en qué núcleo se está ejecutando el código:

    xPortGetCoreID()

    Si usa esa función en un boceto de Arduino, verá que tanto setup() para la configuración() como el loopo()se ejecutan en el núcleo 1. Pruébelo usted mismo cargando el siguiente boceto en su ESP32.

    
    void setup() {
      Serial.begin(115200);
      Serial.print("setup() running on core ");
      Serial.println(xPortGetCoreID());
    }
    
    void loop() {
      Serial.print("loop() running on core ");
      Serial.println(xPortGetCoreID());
    }
    

    Abra el Serial Monitor a una velocidad de transmisión de 115200 y verifique el núcleo en el que se ejecuta el boceto de Arduino.

    Crear tareas

    Arduino IDE es compatible con FreeRTOS para ESP32, que es un sistema operativo en tiempo real. Esto nos permite manejar varias tareas en paralelo que se ejecutan de forma independiente. Las tareas son fragmentos de código que ejecutan algo. Por ejemplo, puede hacer parpadear un LED, realizar una solicitud de red, medir lecturas de sensores, publicar lecturas de sensores, etc.

    Para asignar partes específicas de código a un núcleo específico, debe crear tareas. Al crear una tarea, puede elegir en qué núcleo se ejecutará, así como su prioridad. Los valores de prioridad comienzan en 0, en el que 0 es la prioridad más baja. El procesador ejecutará primero las tareas con mayor prioridad.

    Para crear tareas necesita seguir los siguientes pasos:

    1. Crear un identificador de tarea. Un ejemplo para Task1:

    TaskHandle_t Task1;

    2. En la configuración()crear una tarea asignada a un núcleo específico usando elxTaskCreatePinnedToCorefunción. Esa función toma varios argumentos, incluida la prioridad y el núcleo donde se debe ejecutar la tarea (el último parámetro).

    xTaskCreatePinnedToCore(
          Task1code, /* Function to implement the task */
          "Task1", /* Name of the task */
          10000,  /* Stack size in words */
          NULL,  /* Task input parameter */
          0,  /* Priority of the task */
          &Task1,  /* Task handle. */
          0); /* Core where the task should run */

    3. Después de crear la tarea, debe crear una función que contenga el código para la tarea creada. En este ejemplo, debe crear la tarea e1código()función. Así es como se ve la función de tarea:

    Void Task1code( void * parameter) {
      for(;;) {
        Code for task 1 - infinite loop
        (...)
      }
    }

    los for(;;)crean un bucle infinito. Por lo tanto, esta función se ejecuta de manera similar a la función loop(). Puede usarlo como un segundo ciclo en su código, por ejemplo.

    Si durante la ejecución de su código desea eliminar la tarea creada, puede utilizar la función vTareaEliminar(), que acepta el identificador de tareas (Tarea 1) como argumento:

    vTaskDelete(Task1);

    Veamos cómo funcionan estos conceptos con un ejemplo sencillo.

    Crear tareas en diferentes núcleos: ejemplo

    Para seguir este ejemplo usaremos las siguientes partes:

    Para crear diferentes tareas que se ejecuten en diferentes núcleos, crearemos dos tareas que parpadeen los LED con diferentes tiempos de retraso . Conectaremos mediante dos resistencias en serie de 330 ohmios el cátodo de un led rojo al puerto 4 (GPIO4) y el otro cátodo del led verde al GPIO2 uniendo ambas masas y conectando estas al ping GND del ESP32 .

    El plano resultante seria similar al siguiente diagrama:

    Crearemos dos tareas ejecutándose en diferentes núcleos:

    • Task1 se ejecuta en el núcleo 0;
    • Task2 se ejecuta en el núcleo 1;

    Cargue el siguiente boceto en su ESP32 para hacer parpadear cada LED en un núcleo diferente:

    
    
    TaskHandle_t Task1;
    TaskHandle_t Task2;
    
    // LED pins
    const int led1 = 2;
    const int led2 = 4;
    
    void setup() {
      Serial.begin(115200); 
      pinMode(led1, OUTPUT);
      pinMode(led2, OUTPUT);
    
      //create a task that will be executed in the Task1code() function, with priority 1 and executed on core 0
      xTaskCreatePinnedToCore(
                        Task1code,   /* Task function. */
                        "Task1",     /* name of task. */
                        10000,       /* Stack size of task */
                        NULL,        /* parameter of the task */
                        1,           /* priority of the task */
                        &Task1,      /* Task handle to keep track of created task */
                        0);          /* pin task to core 0 */                  
      delay(500); 
    
      //create a task that will be executed in the Task2code() function, with priority 1 and executed on core 1
      xTaskCreatePinnedToCore(
                        Task2code,   /* Task function. */
                        "Task2",     /* name of task. */
                        10000,       /* Stack size of task */
                        NULL,        /* parameter of the task */
                        1,           /* priority of the task */
                        &Task2,      /* Task handle to keep track of created task */
                        1);          /* pin task to core 1 */
        delay(500); 
    }
    
    //Task1code: blinks an LED every 1000 ms
    void Task1code( void * pvParameters ){
      Serial.print("Task1 running on core ");
      Serial.println(xPortGetCoreID());
    
      for(;;){
        digitalWrite(led1, HIGH);
        delay(1000);
        digitalWrite(led1, LOW);
        delay(1000);
      } 
    }
    
    //Task2code: blinks an LED every 700 ms
    void Task2code( void * pvParameters ){
      Serial.print("Task2 running on core ");
      Serial.println(xPortGetCoreID());
    
      for(;;){
        digitalWrite(led2, HIGH);
        delay(700);
        digitalWrite(led2, LOW);
        delay(700);
      }
    }
    
    void loop() {
      
    }
    

    Cómo funciona el código

    Nota: en el código creamos dos tareas y asignamos una tarea al núcleo 0 y otra al núcleo 1. Los bocetos de Arduino se ejecutan en el núcleo 1 de forma predeterminada. Por lo tanto, podría escribir el código para Task2 en el loop()(no hubo necesidad de crear otra tarea). En este caso, creamos dos tareas diferentes con fines de aprendizaje.

    Sin embargo, según los requisitos de su proyecto, puede ser más práctico organizar su código en tareas como se muestra en este ejemplo.

    El código comienza creando un identificador de tarea para Task1 y Task2 llamadoTarea 1yTarea 2.

    TaskHandle_t Task1;
    TaskHandle_t Task2;

    Asigne GPIO 2 y GPIO 4 a los LED:

    const int led1 = 2; 
    const int led2 = 4;

    En la configuración(), inicialice el monitor serie a una velocidad en baudios de 115200:

    Serial.begin(115200);

    Declare los LED como salidas:

    pinMode(led1, OUTPUT); 
    pinMode(led2, OUTPUT);

    Luego, cree Task1 usando la función  xTaskCreatePinnedToCore() :

    xTaskCreatePinnedToCore(
                 Task1code, /* Task function. */
                 "Task1",   /* name of task. */
                 10000,     /* Stack size of task */
                 NULL,      /* parameter of the task */
                 1,         /* priority of the task */
                 &Task1,    /* Task handle to keep track of created task */
                 0);        /* pin task to core 0 */

    Task1 se implementará con eltarea1código()función. Entonces, necesitamos crear esa función más adelante en el código. Le damos a la tarea la prioridad 1 y la anclamos al núcleo 0.

    Creamos Task2 usando el mismo método:

    xTaskCreatePinnedToCore(
                 Task2code,  /* Task function. */
                 "Task2",    /* name of task. */
                 10000,      /* Stack size of task */
                 NULL,       /* parameter of the task */
                 1,          /* priority of the task */
                 &Task2,     /* Task handle to keep track of created task */
                 1);         /* pin task to core 0 */

    Después de crear las tareas, necesitamos crear las funciones que ejecutarán esas tareas.

    void Task1code( void * pvParameters ){
      Serial.print("Task1 running on core ");
      Serial.println(xPortGetCoreID());
    
      for(;;){
        digitalWrite(led1, HIGH);
        delay(1000);
        digitalWrite(led1, LOW);
        delay(1000);
      }
    }

    La función para Task1 se llamatarea1código()(Puedes llamarlo como quieras). Para fines de depuración, primero imprimimos el núcleo en el que se ejecuta la tarea:

    Serial.print("Task1 running on core ");
    Serial.println(xPortGetCoreID());

    Entonces, tenemos un ciclo infinito similar alcírculo()en el boceto de Arduino. En ese ciclo, parpadeamos el LED1 cada segundo.

    Lo mismo sucede con Task2, pero parpadeamos el LED con un tiempo de retraso diferente.

    void Task2code( void * pvParameters ){
      Serial.print("Task2 running on core ");
      Serial.println(xPortGetCoreID());
    
      for(;;){
        digitalWrite(led2, HIGH);
        delay(700);
        digitalWrite(led2, LOW);
        delay(700);
      }
    }

    Finalmente, el bucle loop tiene ()la función está vacía:

    void loop() { }

    Nota: como se mencionó anteriormente, el bucle loop ()se ejecuta en el núcleo 1. Entonces, en lugar de crear una tarea para ejecutar en el núcleo 1, simplemente puede escribir su código dentro del loop().

    Demostración

    Suba el código a su ESP32. Asegúrese de tener la placa y el puerto COM correctos seleccionados.

    Abra el monitor serie a una velocidad en baudios de 115200. Debería recibir los siguientes mensajes:

    Como era de esperar, Task1 se ejecuta en el núcleo 0, mientras que Task2 se ejecuta en el núcleo 1.

    En su circuito, un LED debe parpadear cada 1 segundo y el otro debe parpadear cada 700 milisegundos.

    En resumen:

    • El ESP32 es de doble núcleo;
    • Los bocetos de Arduino se ejecutan en el núcleo 1 de forma predeterminada;
    • Para usar core 0 necesitas crear tareas;
    • Puede usar la función TaskCreatePinnedToCore() para fijar una tarea específica a un núcleo específico;
    • Con este método, puede ejecutar dos tareas diferentes de forma independiente y simultánea utilizando los dos núcleos.

    Hemos visto un ejemplo simple con LED. La idea es utilizar este método con proyectos más avanzados con aplicaciones del mundo real. Por ejemplo, puede ser útil usar un núcleo para tomar lecturas de sensores y otro para publicar esas lecturas en un sistema de automatización del hogar. Ahora a continuación usando la multitarea vamos a calcular como aumenta la aceleración usando ambos núcleos


    Cálculo de aceleración

    Básicamente, lo que queremos comprobar es cuánto aumenta la velocidad de ejecución de nuestro programa cuando pasamos de una ejecución de un solo núcleo a una ejecución de doble núcleo. Entonces, una de las formas más fáciles de hacerlo es calcular la relación entre el tiempo de ejecución del programa que se ejecuta en un solo núcleo y el que se ejecuta en los dos núcleos del ESP32 [2].

    Mediremos el tiempo de ejecución para cada enfoque utilizando la función Arduino micros , que devuelve la cantidad de microsegundos desde que la placa comenzó a ejecutar el programa [3].

    Entonces, para medir un bloque de ejecución de código, haremos algo similar a lo que se indica a continuación.

    start = micros();
    //Run code
    end = micros();
    execTime = end - start;
    
    

    Podríamos haber usado la función FreeRTOS xTaskGetTickCount  para una mayor precisión, pero la función micros será suficiente para lo que queremos mostrar y es una función muy conocida de Arduino.

    Variables globales

    Comenzaremos nuestro código declarando algunas variables globales auxiliares. No vamos a necesitar ningún include.

    Como vamos a comparar aceleraciones en diferentes situaciones, vamos a especificar una matriz con múltiples exponentes para probar. Además, vamos a tener una variable con el tamaño de la matriz, para que podamos iterarla. Primero vamos a probar valores pequeños para el exponente y luego comenzaremos a aumentarlos mucho.

    int n[10] = {2, 3, 4, 5, 10, 50, 100, 1000, 2000, 10000 };
    int nArraySize = 10;
    

    También vamos a tener algunas variables para almacenar el tiempo de ejecución del código en los diferentes casos de uso. Reutilizaremos las  variables de inicio y finalización  de la ejecución , pero usaremos una variable diferente para cada caso de uso: ejecución de una tarea, ejecución de dos tareas y ejecución de cuatro tareas. De esta forma, almacenaremos los valores para una última comparación.

    unsigned long start;
    unsigned long end;
    unsigned long execTimeOneTask, execTimeTwoTask, execTimeFourTask ;

    Luego, declararemos un semáforo de conteo para poder sincronizar la función de configuración (que se ejecuta en una tarea de FreeRTOS) con las tareas que vamos a ejecutar. Consulte esta publicación para obtener una explicación detallada sobre cómo lograr este tipo de sincronización.

    Tenga en cuenta que la función xSemaphoreCreateCounting recibe como entrada el recuento máximo y el recuento inicial del semáforo. Como tendremos como máximo 4 tareas para sincronizar, su valor máximo será 4.

    SemaphoreHandle_t barrierSemaphore = xSemaphoreCreateCounting( 4, 0 );
    

    Finalmente, declararemos dos arreglos: uno con los valores iniciales (llamado bigArray ) y otro para almacenar los resultados (llamado resultArray ). Haremos sus tallas grandes.

    int bigArray[10000], resultArray[10000];


    La tarea de FreeRTOS

    Definiremos la función que implementará nuestro algoritmo de cálculo de potencia, para ser lanzada como tarea. Si necesita ayuda con los detalles sobre cómo definir una tarea de FreeRTOS, consulte este tutorial anterior.

    Lo primero que debemos tener en cuenta es que nuestra función recibirá algunos parámetros de configuración. Dado que en algunos de los casos de uso vamos a dividir la ejecución del algoritmo en varias tareas, necesitamos controlar la parte de la matriz que cubrirá cada tarea.

    Para crear solo una tarea genérica que pueda responder a todos nuestros casos de uso, pasaremos como parámetro los índices de la matriz de la que será responsable la tarea. Además, vamos a pasar el exponente, así que ahora sabemos cuántas multiplicaciones necesitamos realizar. Puede consultar con más detalle cómo pasar un argumento a una tarea de FreeRTOS en este tutorial anterior.

    Entonces, primero definiremos una estructura con estos 3 parámetros, para poder pasarla a nuestra función. Puedes leer más sobre estructuras aquí . Tenga en cuenta que esta estructura se declara fuera de cualquier función. Entonces, el código se puede colocar cerca de la declaración de variables globales.

    struct argsStruct {
      int arrayStart;
      int arrayEnd;
      int n;
    };

    Como ya tenemos declarada nuestra estructura, dentro de la función declararemos una variable del tipo de esta estructura. Luego, asignaremos el parámetro de entrada de la función a esta variable. Recuerde que los parámetros se pasan a las funciones de FreeRTOS como un puntero a nulo ( void * ) y es nuestra responsabilidad devolverlo al tipo original.

    Tenga en cuenta también que le pasamos a la función un puntero a la variable original y no a la variable real, por lo que necesitamos usar el puntero para acceder al valor. 

    	
    argsStruct myArgs = *((argsStruct*)parameters);

    Ahora, implementaremos la función de cálculo de potencia. Tenga en cuenta que podríamos haber usado la función Arduino pow , pero luego explicaré por qué no lo hicimos. Entonces, implementaremos la función con un ciclo donde multiplicaremos un valor por sí mismo n veces.

    Comenzamos declarando una variable para contener el producto parcial. Luego, haremos un ciclo for para iterar todos los elementos de la matriz asignada a la tarea. Recuerda que este era nuestro objetivo inicial. Luego, para cada elemento, calcularemos su potencia y al final le asignaremos el valor a la matriz de resultados. Esto asegura que usaremos los mismos datos en todas nuestras pruebas y no cambiaremos la matriz original.

    Para un código más limpio, podríamos haber implementado el algoritmo pow en una función dedicada, pero se intenta minimizar las llamadas a funciones auxiliares para un código más compacto.

    Verifique el código a continuación. Dejo ahí comentada una línea de código para calcular la potencia usando la función pow , así que puedes probarlo si quieres. Tenga en cuenta que los resultados de aceleración serán considerablemente diferentes. Además, para acceder a un elemento de una variable de estructura, usamos el nombre de la variable punto («.») el nombre del elemento.

    int product;
    for (int i = myArgs.arrayStart; i < myArgs.arrayEnd; i++) {
     
        product = 1;
        for (int j = 0; j < myArgs.n; j++) {
          product =  product * bigArray[i];
        }
        resultArray[i]=product;
        //resultArray [i] = pow(bigArray[i], myArgs.n);
    }

    Compruebe el código de función completo a continuación. Tenga en cuenta que al final del código estamos aumentando el semáforo de conteo global en una unidad. Esto se hace para asegurar la sincronización con la función de configuración, ya que contaremos el tiempo de ejecución a partir de ahí.  Además, al final, estamos eliminando la tarea.

    void powerTask( void * parameters ) {
     
      argsStruct  myArgs = *((argsStruct*)parameters);
     
      int product;
      for (int i = myArgs.arrayStart; i < myArgs.arrayEnd; i++) {
     
        product = 1;
        for (int j = 0; j < myArgs.n; j++) {
          product =  product * bigArray[i];
        }
        resultArray[i]=product;
        //resultArray [i] = pow(bigArray[i], myArgs.n);
      }
     
      xSemaphoreGive(barrierSemaphore);
     
      vTaskDelete(NULL);
     
    }
    


    La función de configuración

    Vamos a hacer todo el código restante en la función de configuración, por lo que nuestro ciclo principal estará vacío. Comenzaremos abriendo una conexión en serie para generar los resultados de nuestras pruebas.

    Serial.begin(115200);
    Serial.println();

    Ahora, inicializaremos nuestra matriz con algunos valores para aplicar el cálculo. Tenga en cuenta que no nos preocupa el contenido de la matriz ni el resultao real, sino los tiempos de ejecución. Entonces, vamos a inicializar la matriz con valores aleatorios solo para mostrar esas funciones, ya que no vamos a imprimir la matriz que contendrá los resultados.

    Primero vamos a llamar a la función randomSeed , por lo que los valores aleatorios generados diferirán en diferentes ejecuciones del programa [4]. Si el pin analógico está desconectado, devolverá un valor correspondiente a ruido aleatorio, lo cual es ideal para pasar como entrada de la función randomSeed .

    Después de eso, podemos simplemente llamar a la función aleatoria  , pasando como argumentos los valores mínimo y máximo que se pueden devolver [4]. Nuevamente, estamos haciendo esto solo con fines ilustrativos, ya que no vamos a imprimir el contenido de las matrices.

    randomSeed(analogRead(0));
    for (int i = 0; i < 10000; i++) {
      bigArray[i] = random(1, 10);
    }

    Ahora, vamos a definir las variables que se utilizarán como argumentos para nuestras tareas. Recuerda la estructura declarada anteriormente, que contendrá los índices del arreglo que procesará cada tarea y el exponente.

    Entonces, el primer elemento de la estructura es el índice inicial, el segundo es el índice final y el tercero es el exponente. Declararemos estructuras para cada caso de uso (una tarea, dos tareas y cuatro tareas) como se puede ver en el código a continuación. Tenga en cuenta que en el último elemento de la estructura estamos pasando un elemento de la matriz global que declaramos con los exponentes. Entonces, como veremos en el código final, iteraremos toda la matriz, pero por ahora mantengamos las cosas simples.

    argsStruct oneTask = { 0 , 1000 , n[i] };
     
    argsStruct twoTasks1 = { 0 , 1000 / 2 , n[i] };
    argsStruct twoTasks2 = { 1000 / 2 , 1000 , n[i] };
     
    argsStruct fourTasks1 = { 0 , 1000 / 4 , n[i] };
    argsStruct fourTasks2 = { 1000 / 4 , 1000 / 4 * 2, n[i]};
    argsStruct fourTasks3 = { 1000 / 4 * 2, 1000 / 4 * 3, n[i]};
    argsStruct fourTasks4 = { 1000 / 4 * 3 , 1000, n[i]};

    Ahora, vamos a hacer la prueba usando una sola tarea. Empezamos por obtener el tiempo de ejecución con la función micros. Luego, usaremos la función  xTaskCreatePinnedToCore para crear una tarea de FreeRTOS anclada a uno de los núcleos. Elegiremos el núcleo 1.

    Vamos a pasar como parámetro la dirección de la variable de estructura oneTask , que contiene los argumentos necesarios para que se ejecute la función. No olvides el yeso al vacío* .

    Una cosa muy importante a tener en cuenta es que aún no hemos analizado qué tareas pueden haber sido lanzadas por el núcleo de Arduino y que pueden influir en el tiempo de ejecución. Entonces, para garantizar que nuestra tarea se ejecutará con mayor prioridad, le asignaremos un valor de 20. Recuerde, los números más altos significan una mayor prioridad de ejecución para el programador de FreeRTOS.

    Después de iniciar la tarea, solicitaremos una unidad del semáforo, asegurándonos de que la función de configuración se mantendrá hasta que la nueva tarea termine de ejecutarse. Finalmente, imprimiremos el tiempo de ejecución.

    Serial.println("");
    Serial.println("------One task-------");
     
    start = micros();
     
    xTaskCreatePinnedToCore(
      powerTask,               /* Function to implement the task */
      "powerTask",              /* Name of the task */
      10000,                   /* Stack size in words */
      (void*)&oneTask,         /* Task input parameter */
      20,                      /* Priority of the task */
      NULL,                    /* Task handle. */
      1);                      /* Core where the task should run */
     
    xSemaphoreTake(barrierSemaphore, portMAX_DELAY);
     
    end = micros();
    execTimeOneTask = end - start;
    Serial.print("Exec time: ");
    Serial.println(execTimeOneTask);
    Serial.print("Start: ");
    Serial.println(start);
    Serial.print("end: ");
    Serial.println(end);

    Esto es más o menos lo que vamos a hacer para el resto de los casos de uso. La única diferencia es que vamos a lanzar más tareas y vamos a intentar sacar más unidades del semáforo (tantas como tareas lanzadas para ese caso de uso).

    Consulte el código fuente completo a continuación, que ya incluye los casos de uso para ejecutar el código con dos tareas (una por núcleo ESP32) y cuatro tareas (dos por núcleo ESP32). Además, al final de la función de configuración, incluye la impresión de los resultados de aceleración para cada iteración.

    Tenga en cuenta que el parámetro extra de la función Serial.println indica el número de lugares decimales para el número de punto flotante.

    int n[10] = {2, 3, 4, 5, 10, 50, 100, 1000, 2000, 10000  };
    int nArraySize = 10;
     
    struct argsStruct {
      int arrayStart;
      int arrayEnd;
      int n;
    };
     
    unsigned long start;
    unsigned long end;
    unsigned long execTimeOneTask, execTimeTwoTask, execTimeFourTask ;
     
    SemaphoreHandle_t barrierSemaphore = xSemaphoreCreateCounting( 4, 0 );
     
    int bigArray[10000], resultArray[10000];
     
    void setup() {
     
      Serial.begin(115200);
      Serial.println();
     
      randomSeed(analogRead(0));
     
      for (int i = 0; i < 10000; i++) {
        bigArray[i] = random(1, 10);
      }
     
      for (int i = 0; i < nArraySize; i++) {
     
        Serial.println("#############################");
        Serial.print("Starting test for n= ");
        Serial.println(n[i]);
     
        argsStruct oneTask = { 0 , 1000 , n[i] };
     
        argsStruct twoTasks1 = { 0 , 1000 / 2 , n[i] };
        argsStruct twoTasks2 = { 1000 / 2 , 1000 , n[i] };
     
        argsStruct fourTasks1 = { 0 , 1000 / 4 , n[i] };
        argsStruct fourTasks2 = { 1000 / 4 , 1000 / 4 * 2,   n[i]};
        argsStruct fourTasks3 = { 1000 / 4 * 2, 1000 / 4 * 3, n[i]};
        argsStruct fourTasks4 = { 1000 / 4 * 3 , 1000,     n[i]};
     
        Serial.println("");
        Serial.println("------One task-------");
     
        start = micros();
     
        xTaskCreatePinnedToCore(
          powerTask,               /* Function to implement the task */
          "powerTask",              /* Name of the task */
          10000,                   /* Stack size in words */
          (void*)&oneTask,         /* Task input parameter */
          20,                      /* Priority of the task */
          NULL,                    /* Task handle. */
          1);                      /* Core where the task should run */
     
        xSemaphoreTake(barrierSemaphore, portMAX_DELAY);
     
        end = micros();
        execTimeOneTask = end - start;
        Serial.print("Exec time: ");
        Serial.println(execTimeOneTask);
        Serial.print("Start: ");
        Serial.println(start);
        Serial.print("end: ");
        Serial.println(end);
     
        Serial.println("");
        Serial.println("------Two tasks-------");
     
        start = micros();
     
        xTaskCreatePinnedToCore(
          powerTask,                /* Function to implement the task */
          "powerTask",              /* Name of the task */
          10000,                    /* Stack size in words */
          (void*)&twoTasks1,        /* Task input parameter */
          20,                       /* Priority of the task */
          NULL,                     /* Task handle. */
          0);                       /* Core where the task should run */
     
        xTaskCreatePinnedToCore(
          powerTask,               /* Function to implement the task */
          "coreTask",              /* Name of the task */
          10000,                   /* Stack size in words */
          (void*)&twoTasks2,       /* Task input parameter */
          20,                      /* Priority of the task */
          NULL,                    /* Task handle. */
          1);                      /* Core where the task should run */
     
        for (int i = 0; i < 2; i++) {
          xSemaphoreTake(barrierSemaphore, portMAX_DELAY);
        }
     
        end = micros();
        execTimeTwoTask = end - start;
        Serial.print("Exec time: ");
        Serial.println(execTimeTwoTask);
        Serial.print("Start: ");
        Serial.println(start);
        Serial.print("end: ");
        Serial.println(end);
     
        Serial.println("");
        Serial.println("------Four tasks-------");
     
        start = micros();
     
        xTaskCreatePinnedToCore(
          powerTask,                /* Function to implement the task */
          "powerTask",              /* Name of the task */
          10000,                    /* Stack size in words */
          (void*)&fourTasks1,       /* Task input parameter */
          20,                       /* Priority of the task */
          NULL,                     /* Task handle. */
          0);                       /* Core where the task should run */
     
        xTaskCreatePinnedToCore(
          powerTask,                /* Function to implement the task */
          "powerTask",              /* Name of the task */
          10000,                    /* Stack size in words */
          (void*)&fourTasks2,       /* Task input parameter */
          20,                       /* Priority of the task */
          NULL,                     /* Task handle. */
          0);                       /* Core where the task should run */
     
        xTaskCreatePinnedToCore(
          powerTask,                /* Function to implement the task */
          "powerTask",              /* Name of the task */
          10000,                    /* Stack size in words */
          (void*)&fourTasks3,       /* Task input parameter */
          20,                       /* Priority of the task */
          NULL,                     /* Task handle. */
          1);                       /* Core where the task should run */
     
        xTaskCreatePinnedToCore(
          powerTask,                /* Function to implement the task */
          "powerTask",              /* Name of the task */
          10000,                    /* Stack size in words */
          (void*)&fourTasks4,       /* Task input parameter */
          20,                       /* Priority of the task */
          NULL,                     /* Task handle. */
          1);                       /* Core where the task should run */
     
        for (int i = 0; i < 4; i++) {
          xSemaphoreTake(barrierSemaphore, portMAX_DELAY);
        }
     
        end = micros();
        execTimeFourTask = end - start;
        Serial.print("Exec time: ");
        Serial.println(execTimeFourTask);
        Serial.print("Start: ");
        Serial.println(start);
        Serial.print("end: ");
        Serial.println(end);
     
        Serial.println();
        Serial.println("------Results-------");
     
        Serial.print("Speedup two tasks: ");
        Serial.println((float) execTimeOneTask / execTimeTwoTask, 4 );
     
        Serial.print("Speedup four tasks: ");
        Serial.println((float)execTimeOneTask / execTimeFourTask, 4 );
     
        Serial.print("Speedup four tasks vs two tasks: ");
        Serial.println((float)execTimeTwoTask / execTimeFourTask, 4 );
     
        Serial.println("#############################");
        Serial.println();
      }
     
    }
     
    void loop() {
     
    }
     
    void powerTask( void * parameters ) {
     
      argsStruct  myArgs = *((argsStruct*)parameters);
     
      int product;
      for (int i = myArgs.arrayStart; i < myArgs.arrayEnd; i++) {
     
        product = 1;
        for (int j = 0; j < myArgs.n; j++) {
          product =  product * bigArray[i];
        }
     
        resultArray[i]=product;
        //resultArray [i] = pow(bigArray[i], myArgs.n);
      }
     
      xSemaphoreGive(barrierSemaphore);
     
      vTaskDelete(NULL);
     
    }


    Probando el código

    Para probar el código, simplemente cárguelo con el IDE de Arduino y abra el monitor serie. Debería obtener un resultado similar a la figura 1. Naturalmente, los tiempos de ejecución pueden variar.

    Análisis de aceleración de doble núcleo ESP32

    Figura 1 : Salida del programa de prueba de aceleración.

    Los resultados para cada exponente se muestran en la siguiente tabla, en la tabla 1.

    Exponente1 tarea [µs]2 tareas [µs]4 tareas [µs]Acelerar 1 tarea frente a 2 tareasAcelerar 1 tarea frente a 4 tareasAcelerar 2 tareas vs 4 tareas
    22291832961.25140.77360.6182
    32712073251.30920.83380.6369
    43122243401.39290.91760.6588
    53542493671.42170.96460.6785
    105563474511.60231.23280.7694
    502235118813051.88131.71260.9103
    1004331223423431.93871.84850.9535
    10004207221108212121.99321.98340.9951
    20008399242138421901.99331.99080.9988
    100004194002102832103101.99451.99420.9999

    Tabla 1 – Resultados de aceleración para los exponentes definidos en el código.


    Analizando los resultados

    Para comprender los resultados, primero debemos tener en cuenta que no se puede paralelizar completamente todo el programa. Entonces, siempre habrá partes del código que se ejecuten en un solo núcleo, como el lanzamiento de las tareas o los mecanismos de sincronización.

    Aunque no teníamos este caso de uso, muchos algoritmos de paralelización también tienen una parte en la que cada resultado parcial se agrega secuencialmente (por ejemplo, si tenemos varias tareas calculando el valor máximo de su parte de la matriz y luego la tarea principal calcula el valor máximo entre todos los resultados parciales).

    Entonces, hagamos lo que hagamos, teóricamente no podemos lograr una aceleración igual a la cantidad de núcleos (hay algunas excepciones en, por ejemplo, algoritmos que buscan un valor específico y salen cuando lo encuentran, pero no nos compliquemos).

    Entonces, mientras más cómputos ejecutemos en paralelo versus la porción que ejecutamos en secuencia, más aceleración tendremos. Y eso es precisamente lo que vemos en nuestros resultados. Si comenzamos con un exponente de 2 , nuestra aceleración de la ejecución de una tarea a dos tareas es solo de  1.2514 .

    Pero a medida que aumentamos el valor del exponente, hacemos más iteraciones del ciclo más interno y, por lo tanto, más cálculos podemos realizar en paralelo. Así, a medida que aumentamos el exponente, vemos un aumento de la aceleración. Por ejemplo, con un exponente de 1000 , nuestra aceleración es  1.9933 . Lo cual es un valor muy alto en comparación con el anterior.

    Entonces, esto significa que la parte secuencial se diluye y para grandes exponentes la paralelización compensa. Tenga en cuenta que podemos lograr este tipo de valores altos porque el algoritmo es muy simple y fácil de paralelizar. Además, debido a la forma en que FreeRTOS maneja las prioridades y los cambios de contexto de ejecución de tareas, no hay mucha sobrecarga después de que se ejecutan las tareas. Una computadora normal tiende a tener más cambios de contexto entre subprocesos y, por lo tanto, la sobrecarga secuencial es mayor.

    Tenga en cuenta que no usamos la función pow para mostrar esta progresión en los valores de aceleración. La función pow usa flotantes, lo que significa que su implementación es más intensiva en computación. Entonces, obtendríamos aceleraciones mucho mejores en exponentes más bajos porque la parte paralela sería mucho más relevante que la secuencial. Puede comentar nuestro algoritmo implementado y usar la función pow  para comparar los resultados.

    Además, es importante tener en cuenta que lanzar más tareas que la cantidad de núcleos no aumenta la velocidad. De hecho, tiene el efecto contrario. Como podemos ver en los resultados, la aceleración de ejecutar con 4 tareas siempre es menor que la de ejecutar con 2 tareas. De hecho, para exponentes bajos, en realidad es más lento ejecutar el código con 4 tareas (aunque estén divididas entre los dos núcleos del ESP32) que ejecutar con una tarea en un solo núcleo.

    Esto es normal porque cada CPU solo puede ejecutar una tarea en un momento dado y, por lo tanto, lanzar más tareas que CPU significa más tiempo secuencial para manejarlas y más sobrecarga en los puntos de sincronización.

    Sin embargo, tenga en cuenta que esto no siempre es blanco y negro. Si las tareas tuvieran algún tipo de punto de rendimiento en el que dejarían de esperar algo, entonces tener más tareas que núcleos podría ser beneficioso para usar los ciclos de CPU libres. Sin embargo, en nuestro caso, las CPU nunca rinden y siempre se están ejecutando, por lo que no hay ningún beneficio en lanzar más de 2 tareas, en cuanto a la aceleración.


    La ley de Amdhal

    Para complementar los resultados, veremos la ley de Amdahl . Entonces, vamos a aplicar algunas transformaciones a la fórmula de aceleración que estábamos usando. La fórmula inicial era que la aceleración es igual al tiempo de ejecución secuencial (lo llamaremos T ) dividido por el tiempo de ejecución en paralelo (lo llamaremos T Paralelo ).

    fórmula de aceleración

    Pero, como vimos, hay una parte del tiempo de ejecución que siempre es secuencial y otra que puede ser paralelizada. Llamemos p a la fracción del programa que podemos ejecutar en paralelo. Como es una fracción, su valor estará entre 0 y 1.

    Entonces, podemos representar el tiempo de ejecución secuencial T como la porción que puede ejecutarse en paralelo ( p*T ) más la porción que no puede (1-p)*T .

    ejecución secuencial

    Como solo podemos dividir la parte paralela entre los núcleos de nuestra máquina, el tiempo T Parallel es similar a la fórmula anterior, excepto que la parte paralela aparece dividida por la cantidad de núcleos que tenemos disponibles.

    ejecución en paralelo

    Entonces, nuestra fórmula de aceleración se convierte en:

    Fórmula modificada de aceleración

    Ahora tenemos la aceleración escrita en función de T , el tiempo de ejecución original sin mejoras. Entonces, podemos dividir cada término por T :

    acelerar la ley de amdahl

    Entonces, ahora tenemos la aceleración escrita en función de la parte que puede ejecutarse en paralelo y la cantidad de núcleos. Para terminar, supongamos que tenemos una cantidad infinita de núcleos disponibles para ejecutar (el caso de uso real sería un número muy grande, pero analizaremos matemáticamente la fórmula).

    Acelera un número infinito de núcleos

    Como una constante dividida por infinito es igual a 0, terminamos con:

    Acelerar un número infinito de núcleos expresión final

    Así que, aunque la aceleración aumenta con la cantidad de recursos disponibles para la paralelización (la cantidad de núcleos, en nuestro caso), lo cierto es que la máxima aceleración teórica posible está limitada por la parte no paralela, que no podemos optimizar.

    Esta es una conclusión interesante para que decidamos si vamos o no a la paralelización. Algunas veces, los algoritmos pueden paralelizarse fácilmente y tenemos una tremenda aceleración, otras el esfuerzo necesario no justifica la ganancia.

    Además, otra cosa importante a tener en cuenta es que el mejor algoritmo secuencial no siempre es el mejor después de la paralelización. Entonces, a veces, es mejor usar un algoritmo que tiene menos rendimiento en la ejecución secuencial, pero termina siendo mucho mejor que el mejor secuencial en la paralelización.


    Resumen

    Con este post, confirmamos que las funciones disponibles para la ejecución multinúcleo funcionan bien y podemos aprovecharlas para obtener beneficios de rendimiento.

    La última parte más teórica tenía el objetivo de mostrar que la paralelización no es un tema trivial, y uno no puede saltar directamente a un enfoque paralelo sin un análisis previo y esperar una aceleración igual a la cantidad de núcleos disponibles. Este razonamiento se extiende más allá del alcance del ESP32 y se aplica a la computación paralela en general.

    Entonces, para aquellos que van a comenzar con la computación paralela utilizando el ESP32 para mejorar el rendimiento, es un buen comienzo para aprender primero algunos de los conceptos más teóricos.

    Finalmente, tenga en cuenta que el código está orientado a mostrar los resultados, por lo que se podrían haber hecho muchas optimizaciones, como lanzar las tareas en un ciclo en lugar de repetir el código o condensar el algoritmo en una función.

    Además, no buscábamos los resultados de la ejecución para confirmar que la matriz de salida era la misma porque ese no era nuestro enfoque principal. Naturalmente, pasar de secuencial a paralelo requiere una implementación cuidadosa del código y la ejecución de muchas pruebas para garantizar que el resultado sea el mismo.

    Mas información en

    [1] https://espressif.com/en/products/hardware/esp32/overview

    [2] http://www.dcc.fc.up.pt/~fds/aulas/PPD/1112/metrics_en.pdf

    [3] https://www.arduino.cc/en/reference/micros

    [4] https://www.arduino.cc/en/reference/random

    [5] https://techtutorialsx.com/2017/05/16/esp32-dual-core-execution-speedup/