SQLite se ejecuta directamente en cualquier ESP32 PLC de Industrial Shields equipado con una tarjeta SD, dándote una base de datos relacional completa sin un servidor. Esta guía muestra cómo crear el esquema en tu PC, cargarlo en la tarjeta SD y registrar lecturas de pines analógicos con marcas de tiempo usando el esp32_arduino_sqlite3_lib biblioteca.
La arquitectura sin servidor y basada en archivos de SQLite la hace ideal para aplicaciones en el borde: registro ligero, consultas indexadas a través de millones de registros y persistencia de datos a través de reinicios, todo dentro de las limitaciones del ESP32.
¿Qué es SQLite y por qué usarlo en un ESP32 PLC?
SQLite es un popular sistema de gestión de bases de datos relacional embebido, sin servidor y de código abierto que se utiliza ampliamente en diversas aplicaciones de software. Es un motor de base de datos autónomo y sin configuración que opera directamente desde archivos en disco, eliminando la necesidad de un proceso de servidor de base de datos separado.
A pesar de su pequeño tamaño, SQLite proporciona muchas características potentes que se encuentran en sistemas de bases de datos más grandes. Soporta consultas SQL estándar, transacciones, tipos de datos, índices y varias otras operaciones de bases de datos. Además, SQLite ofrece propiedades ACID (Atomicidad, Consistencia, Aislamiento, Durabilidad), asegurando la integridad y fiabilidad de los datos.
Debido a que SQLite tiene un enfoque sin servidor, puedes integrar SQLite en proyectos basados en ESP32. Esto te permite aprovechar el poder de un sistema de gestión de bases de datos relacional directamente en el dispositivo ESP32, para cosas como registro ligero, consultas de datos avanzadas y almacenamiento de variables a través de reinicios.
Hardware y bibliotecas requeridas para usar SQLite en un PLC ESP32
Para esta demostración, estamos usando el PLC Industrial ESP32 21+, pero cualquier PLC ESP32 con una tarjeta SD será compatible.
Necesitaremos instalar la herramienta de línea de comandos sqlite3 en tu PC, utilizando un gestor de paquetes (como "apt") o descargándola desde el sitio web oficial de SQLite en https://sqlite.org/cli.html.
Finalmente, para instalar la biblioteca esp32_arduino_sqlite3_lib tendrás que usar el Administrador de Bibliotecas en el IDE de Arduino (busca "esp32_sqlite").
Cómo registrar datos de sensores analógicos en SQLite en una tarjeta SD de un PLC ESP32
Este ejemplo registra lecturas de dos pines analógicos en una base de datos SQLite en una tarjeta SD, con marcas de tiempo RTC en cada entrada para que puedas consultar el historial completo desde tu PC.
El esquema crea dos tablas: analog_pins almacena los identificadores de los pines, y analog_log registra cada lectura con su valor y marca de tiempo. Se preinsertan dos pines: I0.12 y I0.11.
Primero que nada, tenemos que crear la base de datos en la PC con un esquema SQL. Abre un símbolo del sistema o terminal y navega al directorio donde deseas crear el archivo de la base de datos. Crea un archivo schema.sql y pega este código:
PRAGMA foreign_keys = ON;
CREATE TABLE IF NOT EXISTS analog_pins (
id INTEGER PRIMARY KEY,
pin VARCHAR(5) UNIQUE
);
CREATE TABLE IF NOT EXISTS analog_log (
id_pin INTEGER,
value INTEGER,
timestamp_log DATETIME,
FOREIGN KEY (id_pin) REFERENCES analog_pins(id)
);
INSERT INTO analog_pins (pin) VALUES("I0.12");
INSERT INTO analog_pins (pin) VALUES("I0.11");Con el esquema definido, puedes crear la base de datos con el comando sqlite3 web.db < schema.sql. Ahora, formatea una tarjeta SD utilizando el sistema de archivos FAT32 y coloca el archivo web.db en la raíz de la tarjeta SD. Inserta la SD en el PLC ESP32 y sube el siguiente programa:
#include "SD.h"
#include <sqlite3.h>
#define DB_NAME "web.db"
#include <RTC2.h>
int openDb(sqlite3** db) {
int rc = sqlite3_open("/sd/" DB_NAME, db);
if (rc) {
Serial.print("No se puede abrir la base de datos: ");
Serial.println(sqlite3_errmsg(*db));
} else {
Serial.println("Base de datos abierta con éxito");
}
return rc;
}
void closeDb(sqlite3* db) { sqlite3_close(db); }
int execDb(sqlite3* db, const char* query) {
char *zErrMsg = NULL;
int rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg);
if (rc != SQLITE_OK) {
Serial.print("Error SQL: ");
Serial.println(zErrMsg);
sqlite3_free(zErrMsg);
} else {
Serial.printf("Operación realizada con éxito\n");
}
return rc;
}
#define START_QUERY "INSERT INTO analog_log SELECT (SELECT id FROM analog_pins WHERE pin='"
#define MAX_LENGTH_QUERY sizeof(START_QUERY)+32
template <const int PIN>
void read_analog(void* pvParameter) {
pinMode(PIN, INPUT);
sqlite3 *db;
char query[MAX_LENGTH_QUERY];
const int occupied_query_bytes = snprintf(query, sizeof(query), START_QUERY "%s'),", (const char* const)pvParameter);
char* parameters = query + occupied_query_bytes;
while (1) {
int r = analogRead(PIN);
auto timestamp = RTC.getTime();
snprintf(parameters, MAX_LENGTH_QUERY-occupied_query_bytes, "%d,%ld;", r, timestamp);
if (openDb(&db) != SQLITE_OK) break;
execDb(db, query);
closeDb(db);
vTaskDelay(5000);
}
}
const char* const sI0_12 = "I0.12";
const char* const sI0_11 = "I0.11";
void setup() {
Serial.begin(115200);
SD.begin(13);
if (!RTC.read()) { RTC.setTime(0); }
sqlite3_initialize();
xTaskCreate(read_analog<I0_12>, "TaskI0.12", 8192, (void*)sI0_12, 0, NULL);
xTaskCreate(read_analog<I0_11>, "TaskI0.11", 8192, (void*)sI0_11, 0, NULL);
}
void loop() {}El programa genera dos tareas de FreeRTOS — una por pin analógico — cada una funcionando de manera independiente. Cada 5 segundos, una tarea lee el voltaje del pin, recupera la marca de tiempo actual del RTC e inserta una fila en analog_log. La base de datos se abre y se cierra en cada escritura para asegurar la integridad de los datos a través de ciclos de energía.
Para verificar los datos registrados, retira la tarjeta SD, conéctala a tu PC y consulta la base de datos:
sqlite3 web.db "SELECT * FROM analog_log ORDER BY timestamp_log DESC LIMIT 10;"