Como convertir ficheros m4a a mp3 sin morir en el intento


Em este post veremos cómo podemos grabar un CD de música directamente con Windows partiendo de ficheros m4a , sin usar ninguna aplicación adicional de conversión de mp4 a mp3 que no venga preinstalada, puesto que la mayoría de la aplicaciones comerciales de conversión , y que prometen ser gratuitas, en realidad tienen limitaciones en cuanto al numero de canciones , tamaño, etc. Para ello, usaremos un viejo conocido que lleva muchos años con nosotros: el Reproductor de Windows Media.

Aunque sin lugar a dudas hay mejores aplicaciones para grabar CD (al 99,9% de pago ), si lo hacemos muy esporádicamente, y quizás no queramos instalar nada adicional ( puesto que el 99.9% nos van a pedir que pasemos por caja camuflados en versiones gratuitas que necesitaremos actualizar para usar la funcionalidad requerida), el Reproductor de Windows Media incluido en Windows 10 y que no tenemos que pagar nada nos puede servir perfectamente en dichos casos.

En efecto ,con el reproductor de Windows Media incluido de serie en el sistema operativo, se pueden grabar tres tipos de discos: CDs de audio, CDs de datos y DVDs de datos. Para decidir qué tipo de disco debe usar, debe tener en cuenta qué quiere copiar, qué tamaño tiene y cómo quiere reproducir el disco.

Esta tabla le puede ayudar a decidir qué tipo de disco va a usar.

Tipo de discoDescripción
CD de audioElija esta opción si quiere crear un CD de música que pueda reproducir en un automóvil con solo viejo lector de CD ( sin capacidad de reproducir mp3) o en su viejo equipo estéreo con capacidad de leer solo Cd’s. Use esta opción por tanto solo para música «sin compresión» .Capacidad: hasta 80 minutos.
CD de datosPuede reproducir un CD de datos en PCs y en algunos reproductores de CDs y DVDs modernos. El dispositivo debe ser compatible con los tipos de archivos que va a agregar al disco, como WMA, MP3, JPEG o Windows Media Video (WMV).
Elija esta opción si tiene mucha música y un reproductor de CD o DVD que puede reproducir los tipos de archivos que va a agregar al disco o si quieres realizar una copia de seguridad de tu contenido multimedia. Use por ello esta opción para música, imágenes y vídeos. La capacidad es de aproximadamente 700 megabytes (MB) u ocho horas de música.
DVD de datosPuedes reproducir un DVD de datos en PCs y en algunos reproductores de DVDs. El dispositivo debe admitir los tipos de archivos que agregas al disco, como WMA, MP3, JPEG o WMV. Elija esta opción si tiene muchos archivos que no caben en un solo CD, ya que un DVD de datos contiene mucho espacio. Usa esta opción para música, imágenes y vídeos. La capacidad es de aproximadamente 4,7 gigabytes (GB) o 54 horas de música.

Puede crear un CD que almacene unos 80 minutos de música y se reproducirá en casi cualquier reproductor de CD o puede crear un CD o DVD de datos que contenga varias horas de música y que se reproduzca en PCs o reproductores modernos que admitan formatos de audio comprimidos. Un CD de datos funciona en reproductores de CD que admiten los tipos de archivo copiados en el disco.

Para grabar un CD de audio, sigue estos pasos:  

  1. Abra el Reproductor de Windows Media.
  2. En la biblioteca del reproductor, seleccione la pestaña grabar , seleccione el botón opciones de grabación Botón Opcionesy, a continuación, seleccione CD de audio o CD o DVD de datos.
  3. Inserte un disco en blanco en la grabadora de CD o DVD.

    Si su equipo tiene más de una unidad de CD o DVD, seleccione la unidad que desea usar.
  4. Busque los elementos que quieras grabar en el disco en la Biblioteca del reproductor y arrástrelos al panel de lista (en el margen derecho de la Biblioteca del reproductor) para crear una lista de grabación.
  5. Cuando termine con la lista, pulse o haz clic en Iniciar grabación.

Conversión de m4a a mp3

1- Abra los archivos M4A con Windows Media Player. Puede hacer clic en buscar en el archivo M4A y arrastrarlo y soltarlo en Windows Media Player y aparecerá en la biblioteca.

2- Vaya a la esquina superior izquierda del menú y haz clic en el botón «Organizar». Desde allí, haga clic en «Opciones» y luego en «Grabar Música».

3- En la nueva ventana que aparecerá, haga clic en «Copiar música» y luego desplácese hacia abajo y haga clic en «Cambiar» para seleccionar un destino para el archivo convertido.

Ahora vaya al botón «Formatear» y haga clic en el menú desplegable. Desde allí, desplaces hacia abajo y haga clic en el formato «MP3». También puedes establecer la tasa de bits del audio ( lo ideal es ponerlo a lo máximo, para que nos de la máxima calidad).

Una vez termine su lista de reproducción, si la capacidad libre resultante del CD es pequeña (este valor aparece en la esquina superior derecha en función del CD que se haya insertado ), pulse el botón Iniciar grabación .

El proceso de grabación se compone de varios pasos: primero, la aplicación prepara las canciones, lo cual puede tomar algo de tiempo, después empieza la grabación de verdad.

Si su PC va un poco justo de potencia le recomiendo que no lo use mientras se está grabando el CD, pues podría provocar un error durante la grabación que deje el disco virgen ilegible.

Una vez termine la grabación se expulsará el disco automáticamente.

Nota: en caso de usar una grabadora externa, en caso de que no aparezca como reconocida en el reproductor de windows media, simplemente con la grabadora conectada cierre la aplicación WM por completo y vuelva abrirla

Sugerencias: 

  • Si quiere cambiar el orden de los elementos de la lista de grabación, arrástralos arriba o abajo en la lista.
  • Si quiere quitar un elemento de la lista de grabación, haga clic con el botón derecho en él y seleccione Quitar de la lista. De este modo, quitará el elemento de la lista de grabación, pero no del equipo.
  • Si la lista tiene demasiados elementos para un solo disco, puede grabar el resto en otro disco.

Anuncio publicitario

Descargar ficheros desde un ESp32 vía ftp


A veces si en nuestro ESP32 o ESP866 vamos almacenando medidas varias en archivos diferentes en una SD conectada a nuestro ESPXX, necesitamos alguna vía de descargar o visualizar esos archivos sin tener desconectar la tarjeta SD , punto donde entra el servicio ftp, el cual nos va a facilitar esta tarea de un modo muy sencillo, como vamos a ver en este post.

El protocolo de transferencia de archivos (en inglés File Transfer Protocol o FTP) es un protocolo de red para la transferencia de archivos entre sistemas conectados a una red TCP (Transmission Control Protocol), basado en la arquitectura cliente-servidor. Desde un equipo cliente se puede conectar a un servidor para descargar archivos desde él o para enviarle archivos, independientemente del sistema operativo utilizado en cada equipo.

El servicio FTP es ofrecido por la capa de aplicación del modelo de capas de red TCP/IP al usuario, utilizando normalmente el puerto de red 20 y el 21. Un problema básico de FTP es que está pensado para ofrecer la máxima velocidad en la conexión, pero no la máxima seguridad, ya que todo el intercambio de información, desde el login y password del usuario en el servidor hasta la transferencia de cualquier archivo, se realiza en texto plano sin ningún tipo de cifrado, con lo que un posible atacante puede capturar este tráfico, acceder al servidor y/o apropiarse de los archivos transferidos.

Para solucionar este problema son de gran utilidad aplicaciones como SCP y SFTP, incluidas en el paquete SSH, que permiten transferir archivos pero cifrando todo el tráfico.

Biblioteca

La biblioteca está disponible directamente desde el Administrador de bibliotecas de Arduino IDE.

SimpleFTPServer en el administrador de bibliotecas de Arduino
SimpleFTPServer en el administrador de bibliotecas de Arduino

O el código fuente de GitHub.

Puede encontrar una biblioteca  aquí .Haga clic en el  botón DESCARGAS  en la esquina superior derecha y cambie el nombre de la carpeta sin comprimir SimpleFTPServer.

Verifique que SimpleFTPServer contenga FtpServer.cpp, FtpServer.h, FtpServerKey.he SimpleFTPServer.h .

Coloque la carpeta de la biblioteca SimpleFTPServer en su carpeta /libraries/.

Es posible que deba crear la subcarpeta de bibliotecas si es su primera biblioteca.

Reinicie el IDE.

Seleccione FS en esp8266

También puede habilitar LittleFS para esp8266 editando la línea en el archivo FtpServerKey.h

1#defineDEFAULT_STORAGE_TYPE_ESP8266 STORAGE_SPIFFS

en

1#defineDEFAULT_STORAGE_TYPE_ESP8266 STORAGE_LITTLEFS

Uso

Aquí hay un ejemplo para esp8266

/*
 *  WeMos D1 mini (esp8266)
 *  Start FTP server to upload data on SPIFFS
 *  by Mischianti Renzo <https://www.mischianti.org>
 *
 *  https://www.mischianti.org/2019/05/11/wemos-d1-mini-esp8266-integrated-spiffs-filesistem-part-2/
 *
 */
 
#include "Arduino.h"
 
#include <ESP8266WiFi.h>
#include <SimpleFtpServer.h>
 
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASS";
 
 
FtpServer ftpSrv;   //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial
 
 
void setup(void){
  Serial.begin(115200);
  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());
 
  /////FTP Setup, ensure SPIFFS is started before ftp;  /////////
  if (SPIFFS.begin()) {
//    SPIFFS.format();
      Serial.println("SPIFFS opened!");
      ftpSrv.begin("esp8266","esp8266");    //username, password for ftp.  set ports in ESP8266FtpServer.h  (default 21, 50009 for PASV)
  }
}
void loop(void){
  ftpSrv.handleFTP();        //make sure in loop you call handleFTP()!!
}

Seleccione FS en esp32

También puede habilitar LittleFS para esp32 editando la línea en el archivo FtpServerKey.h

1#defineDEFAULT_STORAGE_TYPE_ESP32 STORAGE_SPIFFS

en

1#defineDEFAULT_STORAGE_TYPE_ESP32 STORAGE_LITTLEFS

o esto para FFAT

1#defineDEFAULT_STORAGE_TYPE_ESP32 STORAGE_FFAT

Uso

Aquí para esp32

/*
 *  ESP32 Dev Kit (esp32)
 *  Start FTP server to upload data on SPIFFS
 *  by Mischianti Renzo <https://www.mischianti.org>
 *
 *  https://www.mischianti.org/2019/05/11/wemos-d1-mini-esp8266-integrated-spiffs-filesistem-part-2/
 *
 */
 
#include <WiFi.h>
#include "SPIFFS.h"
 
#include <SimpleFtpServer.h>
 
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASS";
 
 
FtpServer ftpSrv;   //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial
 
 
void setup(void){
  Serial.begin(115200);
  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());
 
  /////FTP Setup, ensure SPIFFS is started before ftp;  /////////
  if (SPIFFS.begin(true)) {
      Serial.println("SPIFFS opened!");
      ftpSrv.begin("esp8266","esp8266");    //username, password for ftp.  set ports in ESP8266FtpServer.h  (default 21, 50009 for PASV)
  }    
}
void loop(void){
  ftpSrv.handleFTP();        //make sure in loop you call handleFTP()!!  
}

Es importante destacar en el código anterior que debemos anotar el nombre de nuestra red wifi así como nuestra clave de acceso ( en el ejemplo esta en negrita) . Igualmente deberemos elegir un login y pwd para acceder al servicio ftp ( en el ejemplo es ESP8266/ESP866).

Esta biblioteca admite solo el modo pasivo y debe forzar solo una conexión a la vez.

Uso FileZilla como cliente, que puede descargarlo aquí ; Es bastante simple de usar y configurar.

Configuración de Filezilla para acceder a esp8266, seleccione FTP simple en Administrar sitio

Primero, debe continuar Manage site --> New sitey ahora configure este parámetro:

  • Seleccionar FTP como protocolo;
  • Seleccione Use plain FTP (insecure);
  • Establezca su nombre de usuario y contraseña (usted elige eso en el boceto);
  • Que en Trasfer settingsseleccionar Maximun number of connectionigual 1;
  • Ahora conéctese a su dispositivo.
Configuración de Filezilla para acceso esp8266, seleccione max num conexiones

Ahora puede administrar sus SPIFFS con arrastrar y soltar.

Recuerde que SPIFFS no administra carpetas, por lo que todos los archivos deben ser planos.

Prueba

Para verificar la carga, puede usar el boceto simple utilizado en el artículo de SPIFFS vinculado:

/*
 *  WeMos D1 mini (esp8266)
 *  SPIFFS get info, read dir and show all file uploaded
 *  add a data folder to use with esp8266 data uploader
 *  by Mischianti Renzo <https://www.mischianti.org>
 *
 *  https://www.mischianti.org/2019/05/11/wemos-d1-mini-esp8266-integrated-spiffs-filesistem-part-2/
 *
 */
 
#include "Arduino.h"
#include "FS.h"
 
void setup()
{
    Serial.begin(112500);
 
    delay(500);
 
    Serial.println(F("Inizializing FS..."));
    if (SPIFFS.begin()){
        Serial.println(F("done."));
    }else{
        Serial.println(F("fail."));
    }
 
    // To format all space in SPIFFS
    // SPIFFS.format()
 
    // Get all information of your SPIFFS
    FSInfo fs_info;
    SPIFFS.info(fs_info);
 
    Serial.println("File sistem info.");
 
    Serial.print("Total space:      ");
    Serial.print(fs_info.totalBytes);
    Serial.println("byte");
 
    Serial.print("Total space used: ");
    Serial.print(fs_info.usedBytes);
    Serial.println("byte");
 
    Serial.print("Block size:       ");
    Serial.print(fs_info.blockSize);
    Serial.println("byte");
 
    Serial.print("Page size:        ");
    Serial.print(fs_info.totalBytes);
    Serial.println("byte");
 
    Serial.print("Max open files:   ");
    Serial.println(fs_info.maxOpenFiles);
 
    Serial.print("Max path length:  ");
    Serial.println(fs_info.maxPathLength);
 
    Serial.println();
 
    // Open dir folder
    Dir dir = SPIFFS.openDir("/");
    // Cycle all the content
    while (dir.next()) {
        // get filename
        Serial.print(dir.fileName());
        Serial.print(" - ");
        // If element have a size display It else write 0
        if(dir.fileSize()) {
            File f = dir.openFile("r");
            Serial.println(f.size());
            f.close();
        }else{
            Serial.println("0");
        }
    }
}
 
void loop()
{
 
}

Prueba con devolución de llamada

Una característica interesante que agregué recientemente fue la devolución de llamada en alguna acción

/*
 * FtpServer esp8266 and esp32 with SPIFFS
 *
 * AUTHOR:  Renzo Mischianti
 *
 * https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32
 *
 */
 
#ifdef ESP8266
#include <ESP8266WiFi.h>
#elif defined ESP32
#include <WiFi.h>
#include "SPIFFS.h"
#endif
 
#include <SimpleFTPServer.h>
 
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASS";
 
 
FtpServer ftpSrv;   //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial
 
void _callback(FtpOperation ftpOperation, unsigned int freeSpace, unsigned int totalSpace){
  switch (ftpOperation) {
    case FTP_CONNECT:
      Serial.println(F("FTP: Connected!"));
      break;
    case FTP_DISCONNECT:
      Serial.println(F("FTP: Disconnected!"));
      break;
    case FTP_FREE_SPACE_CHANGE:
      Serial.printf("FTP: Free space change, free %u of %u!\n", freeSpace, totalSpace);
      break;
    default:
      break;
  }
};
void _transferCallback(FtpTransferOperation ftpOperation, const char* name, unsigned int transferredSize){
  switch (ftpOperation) {
    case FTP_UPLOAD_START:
      Serial.println(F("FTP: Upload start!"));
      break;
    case FTP_UPLOAD:
      Serial.printf("FTP: Upload of file %s byte %u\n", name, transferredSize);
      break;
    case FTP_TRANSFER_STOP:
      Serial.println(F("FTP: Finish transfer!"));
      break;
    case FTP_TRANSFER_ERROR:
      Serial.println(F("FTP: Transfer error!"));
      break;
    default:
      break;
  }
 
  /* FTP_UPLOAD_START = 0,
   * FTP_UPLOAD = 1,
   *
   * FTP_DOWNLOAD_START = 2,
   * FTP_DOWNLOAD = 3,
   *
   * FTP_TRANSFER_STOP = 4,
   * FTP_DOWNLOAD_STOP = 4,
   * FTP_UPLOAD_STOP = 4,
   *
   * FTP_TRANSFER_ERROR = 5,
   * FTP_DOWNLOAD_ERROR = 5,
   * FTP_UPLOAD_ERROR = 5
   */
};
 
void setup(void){
  Serial.begin(115200);
  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());
 
 
  /////FTP Setup, ensure SPIFFS is started before ftp;  /////////
   
  /////FTP Setup, ensure SPIFFS is started before ftp;  /////////
#ifdef ESP32       //esp32 we send true to format spiffs if cannot mount
  if (SPIFFS.begin(true)) {
#elif defined ESP8266
  if (SPIFFS.begin()) {
#endif
      ftpSrv.setCallback(_callback);
      ftpSrv.setTransferCallback(_transferCallback);
 
      Serial.println("SPIFFS opened!");
      ftpSrv.begin("esp8266","esp8266");    //username, password for ftp.   (default 21, 50009 for PASV)
  }    
}
void loop(void){
  ftpSrv.handleFTP();        //make sure in loop you call handleFTP()!!  
 // server.handleClient();   //example if running a webserver you still need to call .handleClient();
  
}

Es importante destacar en el código anterior que debemos anotar el nombre de nuestra red wifi así como nuestra clave de acceso ( en el ejemplo esta en negrita) . Igualmente deberemos elegir un login y pwd para acceder al servicio ftp ( en el ejemplo es ESP8266/ESP866).

Aquí hay una carga simple de un archivo README.md.

.......
Connected to reef-casa-sopra 
IP address: 192.168.1.127
LittleFS opened!
FTP: Connected!
FTP: Upload start!
FTP: Upload of file README.md byte 1072
FTP: Upload of file README.md byte 3120
FTP: Upload of file README.md byte 3559
FTP: Finish transfer!
FTP: Free space change, free 1019904 of 1036288!


Fuente https://www.mischianti.org/2020/02/08/ftp-server-on-esp8266-and-esp32/

Como gestionar una SD desde un ESP32


Módulo de tarjeta MicroSD

Existen diferentes módulos de tarjetas microSD compatibles con el ESP32. En este post vamos a ver una aplicación de estos económicos módulos que nos pueden servir para un sinfín de aplicaciones.

Este módulo de lector de tarjetas SD facilita las aplicaciones que implican el uso de una tarjeta SD proporcionando una interfaz sencilla con un microcontrolador y que se puede utilizar como unidades periféricas normales.

Programando el microcontrolador en consecuencia, es posible leer y escribir en una tarjeta SD.
Las señales procedentes de la tarjeta SD (MOSI, SCK, MISO y CS) son reportadas en el modulo y luego conectadas al conector de 2×8 pines pre-soldados. Esto facilita la integración del lector de tarjetas SD en diferentes tipos de proyectos.

La programación permite leer y escribir en la tarjeta SD usando su microcontrolador, y el modulo es adecuado para varios tipos de proyectos y requisitos, como reproductores MP3, sistemas de control MCU/ARM, etc.

En este post vamos a utilizar por tanto el módulo de tarjeta microSD que se muestra en la imagen superior (es un módulo de tarjeta microSD también es compatible con otros microcontroladores como Arduino y las placas ESP8266 NodeMCU así como el ESp32 y compatibles ), el cual se comunicará mediante el protocolo de comunicación SPI (aunque se puede usar cualquier otro módulo de tarjeta microSD con una interfaz SPI).

Asignación de pines del módulo de la tarjeta MicroSD – SPI

El módulo de la tarjeta microSD se comunica mediante el protocolo de comunicación SPI. Puede conectarlo al ESP32 usando los pines SPI predeterminados.

Módulo de tarjeta microSDESP32
VCC Vin (5v)
CSGPIO5
MOSIGPIO23
CLKGPIO18
MISOGPIO19
TIERRATIERRA

Piezas necesarias

Para este post, necesitamos las siguientes partes:

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

Diagrama de cableado del módulo de tarjeta microSD ESP32

Preparación de la tarjeta microSD

Antes de continuar con el tutorial, asegúrese de formatear su tarjeta microSD como FAT32 . Siga las siguientes instrucciones para formatear su tarjeta microSD o use una herramienta de software como SD Card Formater (compatible con Windows y Mac OS).

1.  Inserte la tarjeta microSD en su computadora. Vaya a  Mi PC  y haga clic derecho en la tarjeta SD. Seleccione  Formato  como se muestra en la siguiente figura.

Tarjeta MicroSD Módulo formatear tarjeta sd

2.  Aparece una nueva ventana. Seleccione  FAT32 , presione  Iniciar  para iniciar el proceso de formateo y siga las instrucciones en pantalla.

Tarjeta MicroSD Módulo formatear tarjeta sd

ESP32 Manejo de archivos con un módulo de tarjeta MicroSD

Hay dos bibliotecas diferentes para ESP32 (incluidas en el núcleo de Arduino para ESP32): la biblioteca SD y la biblioteca SDD_MMC.h .

Si usa la biblioteca SD, está usando el controlador SPI. Si usa la biblioteca SDD_MMC, está usando el controlador ESP32 SD/SDIO/MMC. Puede obtener más información sobre el controlador ESP32 SD/SDIO/MMC .

ESP32 Manejar archivos en tarjeta microSD Ejemplo Leer y escribir

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.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-microsd-card-arduino/
  
  This sketch can be found at: Examples > SD(esp32) > SD_Test
*/

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

const int chipSelect = SS;//10;

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
  Serial.println("LISTDIR.....");
  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.println("CREATEDIR.....");
  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.println("REMOVEDIR.....");
  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.println("READFILE.....");
  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);


Serial.println("WRITEFILE.....");
  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.println("APPENDFILE.....");
  
  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.println("RENAMEFILE.....");
  
  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.println("DELETEFILE.....");
  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){
  
  Serial.println("TESTFILEIO.....");
  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);

  delay(2000);
  if(!SD.begin(SS)){
 // 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;
  }

delay(2000);
 Serial.println("TIPO DE TARJETA...");
  Serial.print("SD Card Type: ");
  if(cardType == CARD_MMC){
    Serial.println("MMC");
  } else if(cardType == CARD_SD){
    Serial.println("SDSC");  //ESTA
  } else if(cardType == CARD_SDHC){
    Serial.println("SDHC");
  } else {
    Serial.println("UNKNOWN");
  }


delay(2000);
 Serial.println("TAMAÑO TARJETA...");
  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);  //1910MB

  listDir(SD, "/", 0);
  delay(2000);
  
  createDir(SD, "/mydir");
  delay(2000);

  
  listDir(SD, "/", 0);
  delay(2000);
  
  removeDir(SD, "/mydir");
  delay(2000);
  
  listDir(SD, "/", 2);
  delay(2000);
  
  writeFile(SD, "/hello.txt", "Hello ");
  delay(2000);

  
  appendFile(SD, "/hello.txt", "World!\n");
  delay(2000);
  
  readFile(SD, "/hello.txt");
  delay(2000);
  
  deleteFile(SD, "/foo.txt");
  delay(2000);
  
  renameFile(SD, "/hello.txt", "/foo.txt");
  delay(2000);

  
  readFile(SD, "/foo.txt");
  delay(2000);

  
  testFileIO(SD, "/test.txt");
  delay(2000);
  delay(2000);
  
 Serial.println("ESPACIO LIBRE TARJETA..");
  Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
  Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
}

void loop(){
//nada que hacer
delay(20000);
Serial.printf("OTRO BUCLE MAS");

}

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
  • Agregar contenido al archivo 
  • Cambiar el nombre de un archivo 
  • Eliminar un archivo 
  • Inicializar tarjeta microSD 
  • Obtenga el tipo de tarjeta microSD 
  • Obtenga el tamaño de la tarjeta microSD 

Alternativamente, puede usar los ejemplos de SD_MMC ; estos son similares a los ejemplos de SD, pero usan el controlador SDMMC. Para el controlador SDMMC, necesita un módulo de tarjeta microSD compatible. El módulo que estamos usando en este post no es compatible con SDMMC.

Cómo funciona el código

Primero, debe incluir las siguientes bibliotecas:FS.hpara manejar archivos,SD.hpara interactuar con la tarjeta microSD ySPI.hpara utilizar el protocolo de comunicación SPI.

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

El ejemplo proporciona varias funciones para manejar archivos en la tarjeta microSD.

Listar un directorio

loslistDir()función enumera los directorios en la tarjeta SD. Esta función acepta como argumentos el sistema de archivos (Dakota del Sur), 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();
  }
}

Aquí hay un ejemplo de cómo llamar a esta función. los/corresponde al directorio raíz de la tarjeta microSD.

listDir(SD, "/", 0);

Crear un Directorio

loscrearDir()La función crea un nuevo directorio. Pasar como argumento elDakota del Sursistema de archivos 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 llamadomidir.

createDir(SD, "/mydir");

Eliminar un directorio

Para eliminar un directorio de la tarjeta microSD, use eleliminarDir()y pase como argumento el sistema de archivos 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");
  }
}

Aquí hay un ejemplo:

removeDir(SD, "/mydir");

Leer contenido del archivo

losleerArchivo()La función lee el contenido de un archivo e imprime el contenido en el monitor serie. Al igual que con las funciones anteriores, pase como argumento elDakota del Sursistema de archivos y la ruta del archivo.

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 delhola.txtexpediente.

readFile(SD, "/hello.txt")

Escribir contenido en un archivo

Para escribir contenido en un archivo, puede utilizar elescribirArchivo()función. Pasar como argumento, elDakota del Sursistema de archivos, la ruta del archivo 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 escribeHolaen elhola.txtexpediente.

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

Agregar contenido a un archivo

De manera similar, puede agregar contenido a un archivo (sin sobrescribir el contenido anterior) usando elagregar archivo ()función.

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 agrega el mensaje¡Mundo!\nen elhola.txtexpediente. los\nortesignifica 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 cambiar el nombre de un archivo usando elrenombrar archivo()función. Pase como argumentos el sistema de archivos SD, el nombre de archivo original y el nuevo nombre de archivo.

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 delhola.txtarchivo afoo.txt.

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

Eliminar un archivo

Utilizar elborrar archivo()Función para eliminar un archivo. Pasar como argumento elDakota del Sursistema de archivos y la ruta del archivo que desea eliminar.

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 elimina elfoo.txtarchivo de la tarjeta microSD.

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

Probar un archivo

lostestFileIO()funciones muestra cuánto tiempo lleva leer el contenido de un archivo.

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 prueba laprueba.txtexpediente.

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

Inicializar la tarjeta microSD

En elconfiguración(), las siguientes líneas inicializan la tarjeta microSD conSD.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 laempezar()función, intentará inicializar la comunicación SPI con la tarjeta microSD en el pin predeterminado de selección de chip (CS). Si desea utilizar otro pin CS, puede pasarlo como argumento alempezar()función. Por ejemplo, si desea usar GPIO 17 como un pin CS, debe 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();

Si desea utilizar pines SPI personalizados con la tarjeta microSD, vaya a esta sección .

Obtener tipo de tarjeta microSD

Las siguientes líneas imprimen el tipo de tarjeta microSD en el monitor serie.

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 tamaño de tarjeta microSD

Puede obtener el tamaño de la tarjeta microSD llamando altamaño de tarjeta()método:

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

Prueba de 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));

Demostración

Sube el boceto anterior a tu placa ESP32. Después de eso, abra el monitor en serie y presione el botón RST integrado ESP32. Si la inicialización tiene éxito, obtendrá mensajes similares en el monitor serie.

Características de prueba del módulo de tarjeta MicroSD de la placa ESP32: lectura, escritura, eliminación
Características de prueba del módulo de tarjeta MicroSD de la placa ESP32: lectura, escritura, eliminación

Use pines SPI personalizados con la tarjeta MicroSD

losSD.h y SD_MMC.h son bibliotecas que utilizan los pines VSPI SPI (23, 19, 18, 5) de forma predeterminada. Puede configurar otros pines como pines SPI. El ESP32 presenta dos interfaces SPI: HSPI y VSPI en los siguientes pines:

SPIMOSIMISOCLKCS
VSPIGPIO23GPIO19GPIO18GPIO5
HSPIGPIO13GPIO12GPIO14GPIO15

Para usar otros pines SPI, puede proceder de la siguiente manera:

Al comienzo de su código, declare los pines que desea usar, por ejemplo:

#define SCK  18
#define MISO  19
#define MOSI  23
#define CS  5

En elconfiguración(), cree una nueva clase SPI en HSPI o VSPI. Estamos usando VSPI. Ambos funcionarán bien.

SPIClass spi = SPIClass(VSPI);

Inicialice el protocolo de comunicación SPI en los pines definidos anteriormente:

spi.begin(SCK, MISO, MOSI, CS);

Finalmente, inicialice la tarjeta microSD con elempezar()método. Pase como argumento el pin CS, la instancia SPI que desea usar y la frecuencia del bus.

if (!SD.begin(CS,spi,80000000)) {
  Serial.println("Card Mount Failed");
  return;
}

Aquí está el código de muestra modificado para usar pines SPI personalizados:

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-microsd-card-arduino/
  
  This sketch was mofidied from: Examples > SD(esp32) > SD_Test
*/

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

#define SCK  17
#define MISO  19
#define MOSI  23
#define CS  5

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);
  SPIClass spi = SPIClass(VSPI);
  spi.begin(SCK, MISO, MOSI, CS);

  if (!SD.begin(CS,spi,80000000)) {
    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(){

}

Fuente https://randomnerdtutorials.com/esp32-microsd-card-arduino/#sdcardcustompins


Ejemplo: Registro de datos ESP32 en tarjeta microSD

El uso de una tarjeta microSD es especialmente útil para proyectos de registro de datos. Como ejemplo, le mostraremos cómo guardar las lecturas del sensor de un sensor BME280 con marcas de tiempo ( tiempo de época ).

Descripción general del proyecto de tarjeta microSD BME280 de registro de datos ESP32

requisitos previos

Para este ejemplo, asegúrese de tener instaladas las siguientes bibliotecas:

Puede instalar estas bibliotecas utilizando el administrador de bibliotecas de Arduino. En tu IDE de Arduino, ve a Sketch > Incluir biblioteca > Administrar bibliotecas… Luego, busca los nombres de las bibliotecas e instálalos.

Diagrama esquemático

Para este ejemplo, debe conectar el módulo de la tarjeta microSD y el sensor BME280 al ESP32. Aquí hay una lista de las piezas necesarias:

Conecte el circuito siguiendo el siguiente diagrama esquemático.

Diagrama esquemático del circuito de la tarjeta microSD ESP32 BME280

También puedes echar un vistazo a las siguientes tablas:

BME280ESP32
VIN3V3
TIERRATIERRA
SCLGPIO22
ASDGPIO21
módulo de tarjeta microSDESP32
3V33,3 V
CSGPIO5
MOSIGPIO23
CLKGPIO18
MISOGPIO19
TIERRATIERRA

https://8d47f2cdfa0fea8c35311dc564e24b83.safeframe.googlesyndication.com/safeframe/1-0-38/html/container.html

Código

Copie el siguiente código en su IDE de Arduino. Este boceto obtiene lecturas del sensor BME280 (temperatura, humedad y presión) y las registra en un archivo en la tarjeta microSD cada 30 segundos. También registra la marca de tiempo (tiempo de época solicitado a un servidor NTP).

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-microsd-card-arduino/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

// Libraries for SD card
#include "FS.h"
#include "SD.h"
#include <SPI.h>

//Libraries for BME280 sensor
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

// Libraries to get time from NTP Server
#include <WiFi.h>
#include "time.h"

// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Timer variables
unsigned long lastTime = 0;
unsigned long timerDelay = 30000;

// BME280 I2C
Adafruit_BME280 bme;

// Variables to hold sensor readings
float temp;
float hum;
float pres;
String dataMessage;

// NTP server to request epoch time
const char* ntpServer = "pool.ntp.org";

// Variable to save current epoch time
unsigned long epochTime; 

// Function that gets current epoch time
unsigned long getTime() {
  time_t now;
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    //Serial.println("Failed to obtain time");
    return(0);
  }
  time(&now);
  return now;
}

// Initialize WiFi
void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

// Init BME280
void initBME(){
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }
}

// Initialize SD card
void initSDCard(){
   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;
  }
  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);
}

// Write to the SD card
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();
}

// Append data to the SD card
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 setup() {
  Serial.begin(115200);
  
  initWiFi();
  initBME();
  initSDCard();
  configTime(0, 0, ntpServer);
  
  // If the data.txt file doesn't exist
  // Create a file on the SD card and write the data labels
  File file = SD.open("/data.txt");
  if(!file) {
    Serial.println("File doesn't exist");
    Serial.println("Creating file...");
    writeFile(SD, "/data.txt", "Epoch Time, Temperature, Humidity, Pressure \r\n");
  }
  else {
    Serial.println("File already exists");  
  }
  file.close();
}

void loop() {
  if ((millis() - lastTime) > timerDelay) {
    //Get epoch time
    epochTime = getTime();
    
    //Get sensor readings
    temp = bme.readTemperature();
    //temp = 1.8*bme.readTemperature() + 32;
    hum = bme.readHumidity();
    pres = bme.readPressure()/100.0F;

    //Concatenate all info separated by commas
    dataMessage = String(epochTime) + "," + String(temp) + "," + String(hum) + "," + String(pres)+ "\r\n";
    Serial.print("Saving data: ");
    Serial.println(dataMessage);

    //Append the data to file
    appendFile(SD, "/data.txt", dataMessage.c_str());

    lastTime = millis();
  }
}

https://8d47f2cdfa0fea8c35311dc564e24b83.safeframe.googlesyndication.com/safeframe/1-0-38/html/container.html

Inserte sus credenciales de red en las siguientes variables y el código funcionará de inmediato:

const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Este ejemplo utiliza las funciones que hemos visto anteriormente para escribir y agregar datos a la tarjeta microSD (escribirArchivo()yagregar archivo ()funciones).

Demostración

Sube el código a tu tablero. Puede comprobar en el monitor serie si todo funciona como se esperaba.

ESP32 BME280 Registro de datos en tarjeta microSD Monitor serie

Deje que el proyecto funcione durante un tiempo para recopilar algunas lecturas. Luego, inserte la tarjeta microSD en su computadora, y debería tener un archivo llamado datos.txt con las lecturas del sensor.

ESP32 BME280 Registro de datos a archivo en tarjeta microSD

Fuente https://randomnerdtutorials.com/esp32-microsd-card-arduino/

Obtener la fecha y hora del servidor NTP con ESP32


De vez en cuando se encontrará con una idea en la que mantener el tiempo es una preocupación principal. Por ejemplo, imagine un relé que debe activarse en un momento determinado o un registrador de datos que debe almacenar valores en intervalos precisos.

Lo primero que se te viene a la mente es usar un chip RTC (Real Time Clock). Pero estos chips no son perfectamente precisos, por lo que debe realizar ajustes manuales una y otra vez para mantenerlos sincronizados.

La solución aquí es usar Network Time Protocol (NTP). Si su proyecto ESP32 tiene acceso a Internet, puede obtener la fecha y la hora (con una precisión de unos pocos milisegundos de UTC) GRATIS . No necesita ningún hardware adicional.

¿Qué es un NTP?

Un NTP significa Network Time Protocol . Es un Protocolo de Internet (IP) estándar para sincronizar los relojes de la computadora con alguna referencia a través de una red.

El protocolo se puede usar para sincronizar todos los dispositivos en red con el tiempo universal coordinado (UTC) en unos pocos milisegundos (50 milisegundos en la Internet pública y menos de 5 milisegundos en un entorno LAN).

El tiempo universal coordinado (UTC) es un estándar de tiempo mundial, estrechamente relacionado con GMT (hora del meridiano de Greenwich). UTC no varía, es el mismo en todo el mundo.

NTP establece los relojes de las computadoras en UTC, el cliente aplica cualquier compensación de zona horaria local o compensación de horario de verano. De esta manera, los clientes pueden sincronizarse con los servidores independientemente de las diferencias de ubicación y zona horaria.

Arquitectura NTP

NTP utiliza una arquitectura jerárquica. Cada nivel de la jerarquía se conoce como estrato .

En la parte superior se encuentran los dispositivos de cronometraje de alta precisión, como relojes atómicos, GPS o relojes de radio, conocidos como relojes de hardware de estrato

Los servidores Stratum 1 tienen una conexión directa a un reloj de hardware stratum 0 y, por lo tanto, tienen la hora más precisa.

Arquitectura jerárquica NTP con estratos

Cada estrato en la jerarquía se sincroniza con el estrato superior y actúa como servidor para las computadoras del estrato inferior.

¿Cómo funciona NTP?

NTP puede funcionar de varias maneras. La configuración más habitual es la de operar en modo cliente-servidor . El principio básico de funcionamiento es el siguiente:

  1. El dispositivo cliente, como ESP32, se conecta al servidor mediante el Protocolo de datagramas de usuario (UDP) en el puerto 123.
  2. Luego, un cliente transmite un paquete de solicitud a un servidor NTP.
  3. En respuesta a esta solicitud, el servidor NTP envía un paquete de marca de tiempo.
  4. Un paquete de marca de tiempo contiene información múltiple, como la marca de tiempo UNIX, la precisión, el retraso o la zona horaria.
  5. Luego, un cliente puede analizar los valores de fecha y hora actuales.
Funcionamiento del servidor NTP: transferencia de paquetes de solicitud y marca de tiempo

Obtener fecha y hora del servidor NTP

El siguiente esquema le brindará una comprensión completa de cómo obtener la fecha y la hora del servidor NTP.

Antes de subir el boceto, debe realizar algunos cambios para que funcione para usted.

  • Debe modificar las siguientes dos variables con sus credenciales de red, para que ESP32 pueda establecer una conexión con la red existente.const char* ssid = "YOUR_SSID"; const char* password = "YOUR_PASS";
  • Debe ajustar el desplazamiento UTC para su zona horaria en milisegundos. Consulte la lista de compensaciones de tiempo UTC . Aquí hay algunos ejemplos para diferentes zonas horarias:
    • Para UTC -5.00 : -5 * 60 * 60 : -18000
    • Para UTC +1.00 : 1 * 60 * 60 : 3600
    • Para UTC +0.00 : 0 * 60 * 60 : 0
    const long gmtOffset_sec = 3600;
  • Cambie la compensación de la luz del día en milisegundos. Si su país observa el horario de verano , configúrelo en 3600. De lo contrario, configúrelo en 0.const int daylightOffset_sec = 3600;

Una vez que haya terminado, continúe y pruebe el boceto.

#include <WiFi.h>
#include "time.h"

const char* ssid       = "YOUR_SSID";
const char* password   = "YOUR_PASS";

const char* ntpServer = "pool.ntp.org";
const long  gmtOffset_sec = 3600;
const int   daylightOffset_sec = 3600;

void printLocalTime()
{
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("Failed to obtain time");
    return;
  }
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}

void setup()
{
  Serial.begin(115200);
  
  //connect to WiFi
  Serial.printf("Connecting to %s ", ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
  }
  Serial.println(" CONNECTED");
  
  //init and get the time
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  printLocalTime();

  //disconnect WiFi as it's no longer needed
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
}

void loop()
{
  delay(1000);
  printLocalTime();
}

Después de cargar el boceto, presione el botón EN en su ESP32 y debería obtener la fecha y la hora cada segundo, como se muestra a continuación.

ESP32 lee la fecha y la hora de la salida del servidor NTP en el monitor serie

Código Explicación

Echemos un vistazo rápido al código para ver cómo funciona. Primero, incluimos las bibliotecas necesarias para este proyecto.

  • La biblioteca WiFi.h proporciona métodos WiFi específicos de ESP32 que llamamos para conectarse a la red.
  • time.h es la biblioteca de tiempo nativo de ESP32 que realiza una sincronización elegante del servidor NTP.
#include <WiFi.h>
#include "time.h"

A continuación, configuramos algunas constantes como SSID, contraseña WiFi, UTC Offset y Daylight offset que ya conoce.

Junto con eso, debemos especificar la dirección del servidor NTP que deseamos usar. pool.ntp.org es un proyecto NTP abierto ideal para cosas como esta.

const char* ntpServer = "pool.ntp.org";

El pool.ntp.org selecciona automáticamente los servidores de tiempo que están geográficamente cerca de usted. Pero si desea elegir explícitamente, use una de las subzonas de pool.ntp.org.

ÁreaNombre de host
En todo el mundopiscina.ntp.org
Asiaasia.pool.ntp.org
Europaeuropa.pool.ntp.org
América del nortenorteamerica.pool.ntp.org
Oceaníaoceania.pool.ntp.org
Sudamericasudamerica.pool.ntp.org

En la sección de configuración, primero inicializamos la comunicación en serie con la PC y nos unimos a la red WiFi usando la WiFi.begin()función.

Serial.begin(115200);

//connect to WiFi
Serial.printf("Connecting to %s ", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.print(".");
}
Serial.println(" CONNECTED");

Una vez que ESP32 está conectado a la red, inicializamos el cliente NTP usando la configTime()función para obtener la fecha y la hora de un servidor NTP.

//init and get the time
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

Ahora podemos simplemente llamar a la printLocalTime()función personalizada cada vez que queramos imprimir la fecha y hora actuales.

getLocalTime()La función se utiliza para transmitir un paquete de solicitud a un servidor NTP y analizar el paquete de marca de tiempo recibido en un formato legible. Toma la estructura del tiempo como parámetro.

Puede acceder a la información de fecha y hora accediendo a los miembros de esta estructura de tiempo.

%Adevuelve el día de la semana
%Bdevuelve el mes del año
%ddevuelve el día del mes
% Yaño de retorno
%Hhora de regreso
%Mdevuelve minutos
%Sdevuelve segundos
void printLocalTime()
{
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("Failed to obtain time");
    return;
  }
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}

Cómo usar un módulo de reloj en tiempo real (DS3231)


Antes de nada, debería saber qué es un RTC (Real Time Clock), o reloj de tiempo real. Estos chips son muy frecuentes en multitud de aplicaciones, de hecho, cualquier PC tiene uno de ellos en la placa base, y está alimentado también por una pila CR2032 también, que por cierto es la que mantiene la hora y la configuración en el BIOS/UEFI y del que la coge el sistema operativo al arrancar para estar en hora (aunque ahora, con Internet, se puede usar la sincronización con servidores para mayor precisión, pero esto es otra historia…).

El RTC pues ,lo que hace es obtener mediciones de tiempo, así de simple. La diferencia con otros tipos de relojes electrónicos es que simplemente mide el tiempo, y lo hace contabilizando pulsos de señal de reloj, sabiendo la frecuencia y periodos de la misma. Además de la hora, un RTC también permite llevar una contabilidad de los días, semanas, meses y años. Es decir, la fecha completa…

En el caso del Time Clock Module (o DS3231) es un módulo que mide el tiempo, dependiente o independientemente de su tarjeta Arduino ( o derivado ) a través de su propia electrónica especializada consistente en el DS3231 acompañado de un Xtal o cristal de cuarzo que actuará como resonador, el que aporta la frecuencia. Adicionalmente, tambien se necesita una circuitería electrónica capaz de llevar el recuento y almacenar la fecha en una memoria. La circuitería debe ser capaz de contabilizar segundos, minutos, horas, días, semanas, meses y años.

Esa memoria es volátil, por eso necesita de la pila, para tener alimentación constante. Si no tiene pila o se agota, se borrará… Eso es lo que le ocurre a los PCs cuando se le agota la pila, dan una hora incorrecta. Si la configuras mientras el PC está encendido, la hora se mantendrá, ya que el RTC se está alimentando, pero es durante los procesos durante los que está apagado cuando se necesita de esa batería.

En cuanto a la forma de comunicarse con el RTC DS3131 para obtener los valores de fecha que consigue, se hace mediante bus I2C. Y para la alimentación, puedes usar 2.3 a 5.5v para el DS3231, lo que es algo más bajo que los 4.5 a 5.5v del DS1307, por lo que puede ser más eficiente energéticamente y hacer que la pila dure más.

Además, debe saber que estos módulos suelen tener una EEPROM adicional AT24C32 para almacenar algunos registros y mediciones previas, lo que es bastante práctico.

En el caso de usarlo con Arduino, la tarjeta Arduino mide el tiempo transcurrido desde que se encendió el módulo (en ms) y el módulo viene ensamblado listo para usar, con batería (suministrada).

Código

El código ha sido realizado por Gilles Thebault: http://gilles.thebault.free.fr/spip.php?article53

Biblioteca

El código necesita la biblioteca «Wire», que ya está en la tarjeta. Así que no tiene que descargarlo.

También necesitará la biblioteca «ds3231»: https://github.com/rodan/ds3231 .

Conexiones

CÓDIGO

Arduino DS3231

Este código mostrará valores en el monitor serie.

#include <Wire.h>
#include <ds3231.h>
 
struct ts t; 
 
void setup ()  {
  Serial.begin ( 9600 ) ;
  Wire.begin () ;
  DS3231_init ( DS3231_CONTROL_INTCN ) ;
  /*------------------------------------------------ ----------------------------
  ¡Para sincronizar su módulo de reloj, inserte los valores de horario a continuación!
  -------------------------------------------------- --------------------------*/
  t.hour=12; 
  t.min=30;
  t.sec=0;
  t.mday=25;
  t.mon=12;
  t.year=2019;
 
  DS3231_set ( t ) ; 
}
 
void loop vacío ()  {
  DS3231_get ( & t ) ;
  Serial.print ( "Fecha:" ) ;
  Serial.print ( t.mday ) ;
  Serial.print ( "/" ) ;
  Serial.print ( t.mon ) ;
  Serial.print ( "/" ) ;
  Serial.print ( t.year ) ;
  Serial.print ( "\t Hora:" ) ;
  Serial.print ( t.hour ) ;
  Serial.print ( ":" ) ;
  Serial.print ( t.min ) ;
  Serial.print ( "." ) ;
  Serial.println ( t.seg ) ;
 
  delay ( 1000 ) ;
}


Archivos de alojamiento en el servidor web ESP32 desde la tarjeta MicroSD


En este post vamos a construir un servidor web ESP32 para servir archivos desde una tarjeta microSD usando un módulo de tarjeta microSD, el cual se comunicara usando el protocolo SPI sirviendo sus archivos HTML, CSS, JavaScript, imágenes y otros activos guardados en la tarjeta microSD. Esto puede ser especialmente útil si sus archivos son demasiado grandes para caber en el sistema de archivos (SPIFFS), o también puede ser más conveniente según su proyecto. Para construir el servidor web, usaremos la biblioteca ESPAsyncWebServer.

Archivos de alojamiento del servidor web ESP32 desde la tarjeta MicroSD

Descripción del proyecto

Para mostrarle cómo crear un servidor web con archivos de una tarjeta microSD, crearemos una página HTML simple que muestra texto formateado con CSS. También serviremos un favicon. La siguiente imagen muestra la página web que serviremos:

Servidor web ESP32 con archivos desde tarjeta microSD
  • El servidor web se construye utilizando la biblioteca ESPAsyncWebServer ;
  • Los archivos HTML, CSS y favicon se guardan en la tarjeta microSD;
  • La tarjeta microSD se comunica con el ESP32 a través del protocolo de comunicación SPI;
  • Cuando el cliente realiza una solicitud al ESP32, este sirve los archivos guardados en la tarjeta microSD;
Servidor web ESP32 con archivos de tarjeta microSD Cómo funciona

Módulo de tarjeta MicroSD

Existen diferentes módulos de tarjetas microSD compatibles con el ESP32. Estamos utilizando el módulo de tarjeta microSD que se muestra en la siguiente figura el cual se comunica mediante el protocolo de comunicación SPI. Puede usar cualquier otro módulo de tarjeta microSD con una interfaz SPI.

Módulo de tarjeta microSD para ESP32 ESP8266 Arduino SPI

Asignación de pines del módulo de la tarjeta MicroSD

El módulo de la tarjeta microSD se comunica mediante el protocolo de comunicación SPI. Usaremos los pines SPI ESP32 predeterminados como se muestra en la siguiente tabla:

módulo de tarjeta microSDESP32
3V33,3 V
CSGPIO5
MOSIGPIO23
CLKGPIO18
MISOGPIO19
TIERRATIERRA

Preparación de la tarjeta MicroSD

Antes de continuar , asegúrese de formatear su tarjeta microSD como FAT32 . Siga las siguientes instrucciones para formatear su tarjeta microSD o use una herramienta de software como SD Card Formater (compatible con Windows y Mac OS).

1.  Inserte la tarjeta microSD en su computadora. Vaya a  Mi PC  y haga clic derecho en la tarjeta SD. Seleccione  Formato  como se muestra en la figura a continuación.

Preparación de la tarjeta MicroSD Formato de tarjeta microSD

2.  Aparece una nueva ventana. Seleccione  FAT32 , presione  Iniciar  para iniciar el proceso de formateo y siga las instrucciones en pantalla.

Preparación de la tarjeta MicroSD Formato de tarjeta microSD

Después de formatear la tarjeta microSD, puede pegar allí los archivos utilizados para construir el servidor web, moveremos los ficheros index.html,estilo.css, y favicon.png a allí. Siga la siguiente sección para obtener sus archivos.

Archivo HTML

Crea un archivo llamado index.html con el siguiente contenido:

<!DOCTYPE HTML><html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" type="text/css" href="style.css">
  <link rel="icon"  type="image/png" href="favicon.png">
  <title>ESP Web Server</title>
</head>
<body>
  <h1>Hello World!</h1>
  <p>This page was built with files from a microSD card.</p>
</body>
</html>

Archivo CSS

Crea un archivo llamado style.css con el siguiente contenido:

html {
    font-family: Arial; 
    text-align: center;
}
body {
    max-width: 400px; 
    margin:0px auto;
}

icono de favoritos

También serviremos un favicon. Es una imagen png de 15×15 píxeles. Puede usar el mismo favicon que el nuestro, o su propio ícono, o ningún favicon.

Puede hacer clic en el siguiente enlace para descargar el favicon:

Copiar archivos a la tarjeta MicroSD

Después de tener todos los archivos preparados, abra el directorio de la tarjeta microSD y pegue los archivos.

Carpeta de tarjeta microSD con archivos para construir servidor web

Piezas necesarias

Placa de pruebas de cableado del módulo de tarjeta microSD ESP32

Para este cometido necesitamos las siguientes partes:

  • ESP32  
  • Módulo de tarjeta microSD
  • Tarjeta micro SD
  • Tablero de circuitos
  • Cables puente

Diagrama esquemático

Conecte el módulo de la tarjeta microSD al ESP32 como se muestra en el siguiente diagrama esquemático, donde como vemos alimentamos desde el ESP32 al lector de tarjetas y usaremos los pines SPI del ESP32 predeterminados ( GPIOS 5,23,18 Y 19) tal y como se ha expuesto en la sección anterior.

Diagrama de cableado del módulo de tarjeta microSD ESP32

Código

Copie el siguiente código en su IDE de Arduino.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-web-server-microsd-card/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

void initSDCard(){
  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;
  }

  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);
}

void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

void setup() {
  Serial.begin(115200);
  initWiFi();
  initSDCard();

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SD, "/index.html", "text/html");
  });

  server.serveStatic("/", SD, "/");

  server.begin();
}

void loop() {
  
}

Obviamente debe insertar sus credenciales de red en las siguientes variables y el código debería funcionar de inmediato:

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Cómo funciona el código

Primero, asegúrese de incluir las bibliotecas FS.h,SD.h y SPI. h para poder comunicarse con la tarjeta microSD y manejar archivos.

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

Inicializar tarjeta MicroSD

losinitSDCard() es la función que inicializa la tarjeta microSD en los pines SPI predeterminados.

void initSDCard(){
  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;
  }
  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);
}

Luego, debe llamar a esta función en la configuración():

initSDCard();

Servir archivos desde la tarjeta microSD

Cuando accede a la dirección IP ESP32 en la raíz (/) URL, envíe el archivo HTML guardado en la tarjeta microSD.

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(SD, "/index.html", "text/html");
});

El primer argumento de la función de enviar() es el sistema de archivos donde se guardan los archivos. En este caso, se guarda en la tarjeta SD. El segundo argumento es la ruta donde se encuentra el archivo.(/index.html). El tercer argumento se refiere al tipo de contenido (texto/html).

Cuando el archivo HTML se cargue en su navegador, solicitará los archivos CSS y favicon. Estos son archivos estáticos guardados en el mismo directorio . Podemos agregar la siguiente línea para servir archivos estáticos en un directorio cuando lo solicite la URL raíz. Sirve los archivos CSS y favicon automáticamente.

server.serveStatic("/", SD, "/");

Si su servidor web necesita manejar más rutas, puede agregarlas a la configuración(). No olvide configurar SD como primer argumento de la función enviar(). De esta forma, buscará los archivos en la tarjeta microSD.

Es tan simple como esto. Esto se puede aplicar a cualquier otro proyecto de servidor web.

Demostración

Después de cargar el código, abra el monitor en serie a una velocidad de transmisión de 115200. Presione el botón RST integrado.

Debería obtener algo similar en el Serial Monitor: la dirección IP ESP32 e información sobre la tarjeta microSD.

Servidor web con archivos de la tarjeta MicroSD Dirección IP del monitor serie

Abra un navegador en su red local y pegue la dirección IP ESP32. Cargará la siguiente página web con los archivos guardados en la tarjeta microSD. Observe el icono de favoritos en la barra de título del navegador web.

Servidor web ESP32 con archivos desde tarjeta microSD

Medidas eléctricas para 6 canales (hardware)


El medidor de energía ESP32 de 6 canales expandible puede leer 6 canales de corriente y 2 canales de voltaje a la vez. Utiliza transformadores de corriente y un transformador de CA para medir el voltaje y alimentar la(s) placa(s)/ESP32. La placa principal incluye un convertidor reductor para alimentar la electrónica y la placa de desarrollo ESP32, que se conecta directamente a la placa. Se pueden apilar hasta 6 placas adicionales encima de la placa principal para permitirle monitorear hasta 42 canales actuales en resolución de 16 bits, en tiempo real, ¡todo a la vez!

El uso de esta placa puede ser bien Monofásico (Split norteamericano monofásico 120V/240V 60Hz – red y/o circuitos individuales o Monofásico europeo 240V 50Hz debiéndose proporcionar transformador AC-AC 9V o 12V con al menos 500mA de salida) o Trifásico: se recomienda medir los 3 voltajes con 3 transformadores de voltaje. Esto se puede hacer usando una placa principal con 1 placa adicional pudiendose usar un solo medidor para medir trifásico, pero la potencia (vataje) no se calculará correctamente. La potencia se puede calcular en el software, pero el factor de potencia deberá estimarse ((voltaje*corriente)*power_factor)).

Características:

  • Muestras de 6 canales de corriente y 1 canal de voltaje (ampliable a 2 voltajes)
  • Las placas complementarias (hasta 6) pueden expandir el medidor hasta 42 canales de corriente y 8 canales de voltaje
  • Utiliza 2  Microchip ATM90E32AS – 3 canales de corriente y 1 voltaje por IC
  • Para cada canal, el medidor también puede calcular lo siguiente:
    • Poder activo
    • Poder reactivo
    • Poder aparente
    • Factor de potencia
    • Frecuencia
    • La temperatura
  • Utiliza abrazaderas de transformador de corriente estándar para probar la corriente
  • Resistencias de carga de 22 ohmios por canal de corriente
  • Incluye convertidor reductor incorporado para alimentar ESP32 y electrónica
  • 2 interrupciones IRQ y 1 salida de advertencia conectada a ESP32
  • Salidas de cruce por cero
  • Salidas de pulso de energía por IC (4 por IC x2)
  • Interfaz SPI
  • Error de medición IC: 0,1%
  • Rango dinámico IC: 6000:1
  • Selección de ganancia actual: hasta 4x
  • Deriva de referencia de voltaje típica (ppm/°C): 6
  • Resolución ADC (bits): 16

Lo que necesitará:

  • Transformadores de corriente (cualquier combinación de los siguientes, o cualquier transformador de corriente que no supere los 720 mV RMS o la salida de 33 mA)
    • SCT-006 20A/25mA Micro (apertura de 6 mm – conectores de 3,5 mm)
    • SCT-010 80A/26.6mA Mini (apertura 10mm – conectores 3.5mm)
    • SCT-013-000 100A/50mA (apertura 13mm – conectores 3.5mm)
    • SCT-016 120A/40mA (apertura 16mm – conectores 3.5mm)
    • Magnelab SCT-0750-100 (conectores de tornillo: debe cortar la conexión de la resistencia de carga en la parte posterior de la placa, ya que tienen una resistencia de carga incorporada).
    • SCT-024 200A/100mA (apertura 24mm – salida terminal)
    • También se pueden usar otros, siempre que estén clasificados para la cantidad de energía que desea medir y tengan una salida de corriente de no más de 720 mV RMS, o 33 mA en la salida máxima.
  • Transformador de CA (NO CC):
    • América del Norte: Jameco Reliapro 120V a 9V AC-AC o 12v. El pin positivo debe ser de 2,5 mm (algunos son de 2,1)
    • Europa: 240 V a 9 V o 12 V CA-CA al menos 500 mA
  • ESP32 (elija uno):
    • NodoMCU 32s
    • Espressif DevKitC
    • DevKitC-32U si necesita una mejor recepción wifi (no olvide la antena )
    • Cualquier otra cosa con los mismos pines que el anterior, que generalmente son 19 pines por lado con 3v3 en la parte superior izquierda y CLK en la parte inferior derecha
  • Software (elija uno):

Configuración del medidor

Diagrama del medidor de energía ESP32 de 6 canales expandible
Diagrama posterior del medidor de energía ESP32 de 6 canales expandible

Conectando el ESP32

El medidor de energía ESP32 de 6 canales expandible está hecho para que una placa de desarrollo ESP32 se pueda conectar directamente al medidor. Consulte la lista anterior para conocer las placas de desarrollo ESP32 compatibles. Siempre inserte el ESP32 con el pin 3V3 en la parte superior izquierda del medidor . Los pines inferiores se utilizan para conectar la señal de voltaje (del enchufe de alimentación) a las placas adicionales. Si el ESP32 se inserta en los pines inferiores, lo más probable es que haga un cortocircuito en el ESP32.

Comunicarse con el ESP32

El medidor de energía ESP32 de 6 canales expandible utiliza SPI para comunicarse con el ESP32. Cada placa utiliza 2 pines CS.

La placa principal utiliza los siguientes pines SPI:

  • CLK – 18
  • miso – 19
  • MOSI – 23
  • CS1 – 5 (CT1-CT3 y voltaje 1)
  • CS2 – 4 (CT4-CT6 y voltaje 2)

Placas complementarios

Las placas adicionales (hasta 6) pueden expandir el medidor de energía principal hasta 42 canales de corriente y 8 canales de voltaje. Las placas complementarias se conectan directamente a la placa principal como se ve aquí.

La placa adicional permite seleccionar el pin CS en función de la configuración del puente en la parte inferior de la placa. Esto es para que se puedan usar varias placas complementarias, hasta un máximo de 6. NO seleccione más de un pin CS por banco. Los pines CS pueden ser:

  • CT1-CT3 (CS):
    • Para v1.3 y anteriores:
      • 0
      • 2 (*asegúrese de que no se utilice un LED integrado para IO2 en el ESP32)
      • 12 (*hará que ESP32 no arranque si se usa)
      • 13
      • 14
      • 15
    • Para v1.4 y superior:
      • 0
      • 27
      • 35 (no usar, no funcionará correctamente)
      • 13
      • 14
      • 15
    • Para v1.4 rev1 y superior:
      • 35 se cambia a 2 (puede evitar que se programe el ESP32; si es así, desconecte el puente)
  • CT4-CT6 (CS2):
    • dieciséis
    • 17
    • 21
    • 22
    • 25
    • 26

Calibración de sensores de corriente y voltaje (transformador de CA)

Valores de calibración comunes
  • Transformadores de corriente:
    • 20A/25mA SCT-006: 11143
    • 30A/1V SCT-013-030: 8650
    • 50A/1V SCT-013-050: 15420
    • 80A/26,6mA SCT-010: 41660
    • 100A/50mA SCT-013-000: 27518
    • 120A/40mA: SCT-016: 41787
    • 200A/100mA SCT-024: 27518
  • Transformadores de CA
    • Transformador Jameco 9VAC 157041:
      • Para versiones de contador:
        • v1.3 o superior: 7305
        • v1.2: 42620

Medición de potencia y otros valores de medición

El medidor de energía ESP32 de 6 canales expandible utiliza 2 circuitos integrados ATM90E32AS. Cada IC tiene 3 canales de voltaje y 3 canales de corriente. Para que los datos de medición de potencia se calculen internamente, cada canal de corriente debe tener un voltaje de referencia. Si el voltaje está desfasado con la corriente, entonces la corriente y la potencia se leerán como negativas, lo que afectará el factor de potencia y los cálculos de potencia. Si tiene una configuración dividida monofásica o bifásica, la solución es dar la vuelta al transformador de corriente en el cable.

v1.1 del medidor usó 1 de los canales de voltaje para cada IC. Esto significa que los datos de potencia y medición tendrían que calcularse en el software, o los canales de voltaje tendrían que mapearse a través de registros cambiantes en el IC para obtener datos de potencia y medición de CT2, CT3, CT5, CT6.

v1.2 y v1.3 tienen JP8-JP11 en la parte posterior de la placa, lo que permitiría conectar todos los canales de voltaje, lo que permitiría calcular la potencia y otros valores de medición. La mayor parte de v1.3 vino soldada.

v1.4 eliminó JP8-JP11 y tiene canales de voltaje conectados internamente en la pcb.

Medición de circuitos de dos polos (240 V)

Para aplicaciones monofásicas divididas, los circuitos de dos polos tienen 2 cables activos que totalizan 240 V (generalmente rojo y negro en los edificios más nuevos). En la mayoría de los casos, ambos polos se usan por igual, pero en otros puede haber componentes electrónicos en el aparato que usan solo 1 polo. Hay 3 opciones diferentes para medir estos circuitos:

  • Monitoree 1 fase con 1 CT y duplique la salida de corriente en el software (menos preciso) Vea los detalles sobre cómo hacer esto en ESPHome aquí .
  • Use 2 CT para monitorear cada cable vivo en el circuito (si está monitoreando 1 voltaje, deben estar en direcciones opuestas entre sí)
  • Si tiene suficiente cable y el CT es lo suficientemente grande, pase ambos cables calientes a través de 1 CT en direcciones opuestas

Medición de un segundo voltaje

Los orificios etiquetados como VA2 al lado del enchufe de alimentación en la placa principal del medidor y en la parte inferior derecha de la placa adicional son para medir un segundo voltaje. Para hacer esto debes:

  • Corte (con un cuchillo) JP12 y JP13 en la parte posterior de la placa para v1.3+, o JP7 para versiones anteriores
  • Use un segundo transformador de CA, idealmente uno idéntico al primario
  • Enchufe el segundo transformador de CA a un tomacorriente en la fase opuesta al primario
  • Suelde en un cabezal de clavija, un conector de tornillo de 3,5 mm (2,54 mm para v1.3 y anteriores) o un conector flexible estilo CC en VA2+ y VA2- (junto al conector principal de alimentación/voltaje)

Cuando se cortan los puentes de voltaje, la referencia de voltaje para CT4-CT6 será de VA2. Esto significa que los transformadores de corriente para CT4-CT6 deben conectarse a circuitos que estén en la misma fase que VA2, y CT1-CT3 deben conectarse a circuitos que estén en fase con el voltaje primario. Si un CT no está en fase con el voltaje, sus lecturas de corriente y potencia serán negativas. Si, por ejemplo, tiene 4 circuitos en fase con el primario y 2 en fase con VA2, puede invertir el transformador de corriente en el cable para ponerlo en fase con el voltaje (suponiendo que se divide en monofásico o bifásico)

Para tableros complementarios, el voltaje principal provendrá del tablero principal. La medición de voltaje secundario opcional (también pines VA2), estará en fase con CT4-CT6.

Medición de electricidad trifásica

Lo que necesitará para medir las 3 fases correctamente:

  • Placa principal de 6 canales (v1.4 y superior)
  • Tablero adicional de 6 canales
  • 3 transformadores de tensión, uno para cada fase. Estos pueden ser de pared, de tipo enchufable (si tiene un tomacorriente conectado a cada fase) o transformadores independientes conectados directamente a los disyuntores. Deben bajar el voltaje a entre 9 y 14 V CA. El primero que se conecta a la placa principal también alimenta el ESP32 y la electrónica, por lo que debe generar al menos 500 mA.
  • Cabeceras soldadas a los terminales VA2 en la placa principal y la placa complementaria
  • JP12 y JP13 cortados en la placa principal y las placas complementarias Similar a lo anterior para medir un segundo voltaje, una vez que se cortan JP12 y JP13:
  • CT1-CT3 en la placa principal y CT1-CT3 en la placa adicional estarán en fase con la primera fase
  • CT3-CT4 en el tablero principal con la 2da fase
  • CT3-CT4 en la placa adicional con la 3.ª fase.

Los transformadores deben calibrarse individualmente para una mayor precisión.

Como alternativa, puede utilizar 2 placas complementarias y conectar 1 transformador a cada placa:

  • Ubique los pines más inferiores (es decir, aquellos en los que el ESP32 no se conecta), marcados como «VA-» y «VA+» para cada placa adicional. Estos normalmente proporcionan la referencia de voltaje a la placa adicional desde la placa principal.
  • Corta estos alfileres
  • Suelde un conector, o los cables de un transformador, en los orificios inferiores derechos marcados como «VA2» en las placas complementarias. La polaridad no importa (si obtiene lecturas de corriente negativas, invierta el CT en el cable)
  • No corte JP12 y JP13

En los sistemas trifásicos, un transformador de corriente que está conectado a una fase que no está en fase con la referencia de voltaje siempre dará como resultado una potencia activa y un factor de potencia cercanos a cero.

A continuación podemos ver un video que resume tanto la electrónica necesaria como las opciones de configuración posibles que veremos en un segundo post.


Fuente: https://github.com/CircuitSetup/Expandable-6-Channel-ESP32-Energy-Meter

Monitor de energía de precisión con pantalla y salida de datos


En este interesante proyecto de radiopench , utilizaremos un Arduino NANO, un medidor PZEM004T v3 y una pantalla OLED de 0.96 pulgadas , y dos pulsadores de operación, que son lo más pequeños posibles.

El esquema de conexiones podemos resumirlo del siguiente modo:

  • Pin 10 : select.
  • Pin 9 :enter.
  • Pin 11 Rx (conectado a Tx pin en el PZEM).
  • Pin 12 Tx (conectado a Rx pin en the PZEM).
  • Pin 4 a SDA de la pantalla amoled.
  • Pin5a SCL de la pantalla amoled.

En este blog se ha tratado en un articulo anterior el modulo medidor PZEM004T v3 (ver PZEM-004Tv3 del fabricante Peacefair
). Esta vez leeremos el resultado de la medición del Arduino pero además lo mostraremos en una pequeña pantalla amoled de 6 líneas. Además, como no es interesante solo mostrarlo, dejaremos que el resultado fluya hacia el puerto serial como un registro pues de esta manera podemos recibir datos en una PC, etc. y crear gráficos fácilmente.

Asimismo al incluir dos pulsadores podemos variar el periodo de muestreo o resetear los resultados.

En este esquema podemos ver más en detalle todas las conexiones (la fuente se puede suprimir en caso de que lo conectemos permanentemente a un ordenador vía USB)

Hay dos pulsadores de seleccionar e ingresar en la parte superior izquierda. El Arduino NANO (compatible) se puede reemplazar por cualquier variante de la familia Arduino (incluido el ESP32) y la pantalla OLED de 0.96 pulgadas no deberían costar demasiado

Descripción de la funcionalidad de la operación

La operación es simple y el resultado de la medición de potencia se muestra en el OLED a intervalos de 1 segundo. Además, el resultado de la medición se envía al puerto serie (salida de registro) en el intervalo especificado.

Vamos a ver el significado de cada línea mostrada en el panel amoled (el orden es el correspondiente a empezar desde la parte superior de la pantalla hacia abajo):

  • Línea 1: El valor de la potencia activa se muestra como un carácter con un cuadrado doble (unidad W). El borde derecho de la primera línea es el tiempo restante hasta la salida del registro. Cuando llega a 0, el registro se genera y, al mismo tiempo, el registro se muestra en la parte superior derecha de la pantalla.
  • Línea 2: Muestra los valores de tensión y corriente (unidades V, A). El valor es un valor efectivo
  • Línea 3: Mostrar el valor de la potencia aparente (unidad VA)
  • Línea 4: Mostrar la cantidad de electricidad (energy) (unidad: kWh)
  • Línea 5: Frecuencia (unidad Hz)
  • Línea 6: Muestra el valor del factor de potencia (unidad %), y además como un gráfico de barras.

Puede ser difícil de leer porque está lleno de información en una pantalla pequeña. Sin embargo, dado que la potencia activa más importante se muestra en letras grandes en la fila superior y el factor de potencia también se muestra en el gráfico de barras en la parte inferior, debería ser relativamente fácil de ver incluso desde la distancia.

Al restablecer mientras presiona el botón de configuración del ciclo de grabación, ingresará a la pantalla de configuración del ciclo de grabación. El período de medición se puede cambiar presionando el botón SELECT y se puede especificar como 1, 2, 5, 10, 30, 60, 120, 300 segundos. El valor se confirma presionando el botón Enter y se inicia el modo de medición normal. Además, el valor establecido se registra en la EEPROM, por lo que se recuerda incluso si la alimentación está apagada.

Al presionar el botón Enter durante la operación de restablecimiento de energía, el valor de potencia integrado (valor de kWh) se puede borrar a cero. Para indicar que se ha aceptado la operación del botón, la pantalla se apaga una vez .

Dado que se emite en formato CSV separado por comas, puede leerlo tal como está en Excel. Los datos son desde el principio hasta el segundo, en el siguiente orden: segundo, voltaje (V), corriente (A), potencia aparente (VA), potencia activa (W), factor de potencia, consumo de energía (kWh) y frecuencia (Hz).
Además, se inserta la coma inicial para aclarar el delimitador porque si se inserta una cadena de marca de tiempo en el software del terminal, se puede perder el límite con el segundo valor.

A continuación podemos ver el código completo para gestionar la pantalla , los dos pulsadores y el monitor PZEM.

/* AC power meter with text log
  Measuring instrument:PZEM-004T v3.0
  Display 0.96 inch OLED (SSD1306, 128x64)

   Pin 10 energy clear button
   Pin 11 Rx (Connects to the Tx pin on the PZEM)
   Pin 12 Tx (Connects to the Rx pin on the PZEM)
  
*/

#include <EEPROM.h>
#include <PZEM004Tv30.h>                     // PZEM004Tv3 library
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>                // 0.96inch OLED
#include <MsTimer2.h>

#define vCal 1.006                           // voltage calibration coefficient(dafault=1.0)
#define iCal 0.9205                          // current calibration coefficient(dafault=1.0)

#define entPin   9                           // enter button
#define selPin  10                           // select button
#define pzemRX  11                           // recieve data (connect to PZEM TX)
#define pzemTX  12                           // send data    (connect to PZEM RX)

Adafruit_SSD1306 oled(128, 64, &Wire, 4);    // OLED setup
PZEM004Tv30 pzem(pzemRX, pzemTX);            // Pin11 to TX, Pin12 to RX

unsigned int rangeTable[8] = {1, 2, 5, 10, 30, 60, 120, 300}; // time Inerval Table
int rangeNumber;
int timeToLog;                               // remaining time to output log

float Vx, Ax, VAx, Wx, kWhx, PFx, Hzx, VARx; // measured value
char buff[8];                                // character format buffer
unsigned int logInterval;                    // log interval
volatile boolean t2Flag = false;             // timing sync variable

void setup() {
  oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);    // set OLED I2C Address 0x3C or 0x3D
  Serial.begin(115200);
  pinMode(entPin, INPUT_PULLUP);             // enter button
  pinMode(selPin, INPUT_PULLUP);             // select button

  rangeNumber = EEPROM.read(0);
  if (rangeNumber > 7) {                     // abnormal range number will be
    rangeNumber = 0;                         // set to 0
  }
  logInterval = rangeTable[rangeNumber];

  oled.clearDisplay();
  oled.display();
  oled.setTextColor(WHITE);                  // write with white character

  if (digitalRead(entPin) == LOW) {
    recIntervalSet();
  }

  startScreen();                             // show start screen
  Serial.println();                          // start message
  Serial.println(F(" , PZEM-004V3 Power meter start V0.5"));
  Serial.println(F(" , sec, V(rms), I(rms), VA, W, PF, kWh, Hz"));
  Serial.flush();

  timeToLog = logInterval;                   // set interval
  measure();                                 // get value from PZEM-004Tv3
  logPrint();                                // data out serial port
  oledDisplay();                             // display data on OLED

  MsTimer2::set(1000, timer2IRQ);            // IRQ interval 1sec
  MsTimer2::start();
}

void loop() {
  if (digitalRead(selPin) == LOW) {          // if select button pushed during running
    resetKWh();                              // clear the energy value
  }
  while (t2Flag == false) {                  // wait MsTimer2 IRQ
  }
  t2Flag = false;

  measure();                                 // get value from PZEM-004Tv3
  timeToLog --;                              // decrement log timer
  oledDisplay();                             // display present data to OLED
  if (timeToLog == 0 ) {                     // if log interval time is up,
    logPrint();                              // output log data to serial port
    timeToLog = logInterval;                 // set value for next cycle
  }
}            // end of loop

void measure() {                             // read values from PZEM-004T v3
  Vx = vCal * pzem.voltage();                // voltage(apply calibration correction)
  Ax = iCal * pzem.current();                // current(apply calibration correction)
  VAx = Vx * Ax;                             // calculate apparent power
  Wx = vCal * iCal * pzem.power();           // effective power(Use the value after calibration correction)
  PFx = pzem.pf();                           // power factor
  kWhx = vCal * iCal * pzem.energy();        // sum of energy(Use the value after calibration correction)
  Hzx = pzem.frequency();                    // line frequency
}

void logPrint() {                               // serial out for log
  static long t = 0;
  Serial.print(F(" , "));                       // this for the separator for terminal software timestamp text
  Serial.print(t); Serial.print(F(", "));       // time in second
  Serial.print(Vx, 1); Serial.print(F(", "));   // voltage
  Serial.print(Ax, 3); Serial.print(F(", "));   // current amps
  Serial.print(VAx, 2); Serial.print(F(", "));  // VA value
  Serial.print(Wx, 1); Serial.print(F(", "));   // wattage
  Serial.print(PFx, 2); Serial.print(F(", "));  // powewr factor
  Serial.print(kWhx, 3); Serial.print(F(", ")); // totall energy
  Serial.print(Hzx, 1);                         // frequency
  Serial.println();
  t += logInterval;                             // increment accumurate time
}

void oledDisplay() {                                // display information on OLED
  oled.clearDisplay();                              // clear display buffer
  oled.setTextSize(2);                              // double size character

  dtostrf(Wx, 6, 1, buff);                          // format nnnn.n
  oled.setCursor(0, 0);
  oled.print(buff); oled.print(F("W"));             // display power value

  oled.setTextSize(1);                              // standerd size character
  oled.setCursor(110, 8);
  sprintf(buff, "%3d", timeToLog);                  // format decimal 3-digit
  oled.print(buff);                                 // remainig time display
  if (timeToLog == 0) {                             // if time up
    oled.setCursor(110, 0); oled.print(F("Log"));   // display "Log" at top right
  }

  dtostrf(Vx, 5, 1, buff); oled.setCursor(0, 16); oled.print(F("V:"));
  oled.print(buff); oled.print(F("V"));             // voltage nnn.n

  dtostrf(Ax, 6, 3, buff); oled.setCursor(56, 16); oled.print(F("I: "));
  oled.print(buff); oled.print(F("A"));             // amps nn.nnn

  dtostrf(VAx, 7, 2, buff); oled.setCursor(0, 26); oled.print(F("Pappa:"));
  oled.print(buff); oled.print(F(" VA"));           // volt amps nnnn.nn

  dtostrf(kWhx, 7, 3, buff); oled.setCursor(0, 36); oled.print("Energ");
  oled.print(buff); oled.print(F(" kWh"));          // watt hours nnn.nnn


  dtostrf(Hzx, 4, 1, buff); oled.setCursor(0, 46); oled.print(F("Freq :   "));
  oled.print(buff); oled.print(F(" Hz"));           // fequency nn.n

  dtostrf(PFx * 100.0, 3, 0, buff); oled.setCursor(0, 56); oled.print(F("PF:"));
  oled.print(buff); oled.print(F("% Y    Y    Y")); // power factor nnn
  oled.fillRect(50, 61, (60 * PFx) + 1, 3, WHITE);  // Power factor bar

  oled.display();                                   // actual display will be done at here
}

void recIntervalSet() {                             // Log interval time setting by button swith
  while (digitalRead(entPin) == LOW) {              // wait till the enter button is released
    oled.clearDisplay(); oled.setCursor(0, 0);
    oled.print(F("Log interval setting"));          // start message
    oled.display();
  }
  delay(30);

  while (digitalRead(entPin) == HIGH) {
    oled.clearDisplay();
    oled.setTextSize(2);                            // double size character
    oled.setCursor(0, 0);
    oled.println(F("Set Int."));                    // display set interaval value

    oled.setCursor(35, 30);
    oled.print(logInterval); oled.print(F("sec"));  // display the value
    oled.display();

    if (digitalRead(selPin) == LOW) {
      rangeNumber ++;                               // increment range number
      if (rangeNumber > 7) {                        // if beyond upper limit
        rangeNumber = 0;                            // circulate the number
      }
      logInterval = rangeTable[rangeNumber];        // set interval time from table
    }
    while (digitalRead(selPin) == LOW) {            // wait select botton relesed
    }
    delay(30);
  }

  EEPROM.write(0, rangeNumber);                     // save value to EEPROM
}

void startScreen() {                                // start message
  oled.clearDisplay(); oled.setTextSize(2);         // double size character
  oled.setCursor(0, 0);
  oled.println(F("PZEM-004T"));
  oled.println();
  oled.print(F("int.="));
  oled.print(logInterval); oled.print(F("s"));      // display log interval
  oled.display();
  delay(2000);
}

void resetKWh() {                   // clear accumulated energy value
  Serial.println(F("Reset kWh"));   // write message on the log
  pzem.resetEnergy();               // reset value
  oled.clearDisplay();              // erase disp. to show command accepted
  oled.display();
  delay(50);
}

void timer2IRQ() {                  // MsTimer2 IRQ
  t2Flag = true;                    // set flag for loop
}

Se ha omitido la descripción del programa, pues no hay nada complicado. Sin embargo, dado que el coeficiente de corrección se define a continuación y corrige el resultado de la medición de voltaje y corriente, es mejor ajustarlo para cada dispositivo.

Si no conoce el valor de corrección, está bien establecerlo en 1.0.

  • define vCal 1.006 // coeficiente de calibración de voltaje(dafault=1.0)
  • define iCal 0.9205 // coeficiente de calibración de corriente(dafault=1.0)

En el siguiente video podemos ver una demostración práctica del dispositivo:

Video del proyecto en inglés

Fuente :El diagrama de circuito y el código del programa están disponibles en el siguiente siguiente sitio escrito en japonés http://radiopench.blog96.fc2.com/blog-entry-995.html