Cada tantos meses aparece la misma historia en los grupos de mascotas: alguien dejó la reja entreabierta un segundo y el perro salió detrás de un gato. En una ciudad, esos primeros minutos son los que importan. Un collar con GPS no evita que se arranque, pero te avisa en el momento en que cruza el límite de la casa, con la ubicación exacta y un link a Google Maps, antes de que la búsqueda se transforme en un problema serio.
En este tutorial vas a armar ese collar desde cero con módulos que se consiguen sueltos en Chile: un ESP32 como cerebro, un GPS NEO-6M para la posición, un módulo GSM SIM800L para mandar los avisos por la red celular (sin depender de WiFi ni de una app externa) y una pantalla e-paper que muestra el nombre de la mascota y tu teléfono aunque la batería quede en cero. Al terminar vas a entender cómo funciona el geofencing, cómo se calcula la distancia entre dos coordenadas y cómo encajar todo dentro de una caja impresa en 3D montable en un collar estándar.
Nota sobre el hardware: el proyecto original de CircuitDigest usa una placa propietaria (GeoLinker GL868) que integra ESP32-S3, GPS y GSM en un solo PCB. Acá lo replicamos con los tres módulos por separado, que es la forma más común y económica de conseguirlo localmente. La lógica del firmware. zonas, alertas, nube. es la misma; solo cambia que en vez de una placa todo en-uno cableás los módulos entre sí.

Cómo funciona un rastreador con geofencing
Antes del cableado conviene entender la máquina de estados, porque el firmware
gira en torno a tres etapas que se repiten en el loop():
- Inicialización y lectura de posición. Al encender, el sistema levanta el GPS, el GSM/GPRS, la pantalla e-paper y la conexión a la nube. Un detalle fino: restaura desde la memoria flash el estado previo de cada zona. Sin esto, cada vez que reinicias el collar el sistema "cree" que la mascota acaba de entrar a la casa y dispara una alerta falsa. Guardar el estado en NVS evita ese ruido.
- Comparación contra las zonas. Con la latitud y longitud actuales, se mide la distancia a cada zona definida (Casa, Jardín, Parque, Zona restringida) y se decide si la mascota entró o salió de alguna.
- Alertas y nube. Si cruza un límite, sale un SMS con ubicación, velocidad, porcentaje de batería y link a Google Maps. Si entra a una zona restringida, además se dispara una llamada de voz, porque un SMS se puede pasar por alto pero un teléfono sonando no. En paralelo, la posición se sube periódicamente a la nube para ver el recorrido en un mapa en vivo.
Materiales y conexiones

La gran ventaja de resolver esto con un ESP32 es que sobran pines y potencia para manejar el GPS por UART, el SIM800L por otra UART y la e-paper por SPI al mismo tiempo. Tres puntos que marcan la diferencia entre "funciona a veces" y "funciona siempre":
- Alimentación del SIM800L. El módulo GSM pega peaks de corriente de hasta 2 A al transmitir. No lo alimentes desde el pin 3V3 del ESP32: usa la batería Li Ion (3.7 a 4.2 V entra justo en su rango) con un capacitor grande cerca, o vas a ver reinicios aleatorios cada vez que intente conectarse a la red.
- GPS al aire libre. El NEO-6M necesita ver cielo para el primer fix. En interior puede tardar minutos o no fijar nunca; para probar, dejalo cerca de una ventana o afuera.
- E-paper por SPI. Comparte el bus con cuidado y respeta los pines de la librería GxEPD2. Es lento (~1 a 2 s por refresco) pero consume casi nada y, lo mejor, mantiene la imagen sin energía: si la batería muere, tu número sigue visible en la pantalla para quien encuentre a la mascota.

Las librerías y cabeceras que usa el firmware de referencia:
#include <GL868_ESP32.h>
#include <math.h>
#include <Preferences.h>
#include <SPI.h>
#include <GxEPD2_BW.h>
#include <Fonts/FreeMonoBold9pt7b.h>
#include <Fonts/FreeMonoBold12pt7b.h>
#include <Fonts/FreeMonoBold18pt7b.h>
Firmware: el corazón del geofencing
Cada zona es simplemente un centro (lat/lon), un radio en metros y una bandera de
si es restringida. La bandera insideNow se persiste en NVS en cada cambio, que
es lo que evita las falsas alarmas al reiniciar:
struct GeofenceZone {
const char *name;
double latitude;
double longitude;
double radiusMeters;
bool restricted;
bool insideNow; // runtime — persisted to NVS on every change
};
GeofenceZone zones[] = {
{ "Home", 11.024934042701, 77.010585083681, 100.0, false, false },
{ "park", 11.019119891223, 77.006775393646, 150.0, false, false },
{ "Garden", 11.010967, 77.012959, 80.0, false, false },
{ "Restricted Area", 11.011156458988, 77.017023144466, 50.0, true, false },
};
La detección de entrada/salida se basa en la fórmula de Haversine, que calcula
la distancia sobre la superficie de la Tierra entre la posición actual y el centro
de cada zona. Si esa distancia es menor al radio, la mascota está dentro. Un
detalle importante que trae el firmware es la histéresis (HYSTERESIS_METERS 20.0): sin ese margen, cuando la mascota camina justo sobre el borde de una zona
el GPS "rebota" un par de metros para cada lado y dispararías decenas de alertas
entrar/salir por minuto. Los 20 m de histéresis absorben ese jitter.
Cuando se confirma un cruce, esta función arma y manda el aviso, y agenda la llamada solo si la zona es restringida:
static void sendGeofenceAlert(const char *zoneName, bool entered,
bool restricted, GPSData *gps, bool hasLoc) {
char msg[256];
buildAlertMessage(msg, sizeof(msg), entered ? "ENTERED" : "LEFT",
zoneName, gps, hasLoc);
sendReliableSMS(ALERT_NUMBER, msg);
if (restricted && !callInProgress && !pendingCall) {
pendingCall = true;
pendingCallTimer = millis();
}
}
La pantalla e-paper se actualiza solo cuando cambia el estado, no en cada vuelta del loop. Los refrescos de e-paper son lentos y tienen una vida útil finita de ciclos, así que re dibujar la misma pantalla cada segundo la desgasta sin sentido:
static void epdUpdateIfNeeded() {
if (petIsLost == displayShowsLost)
return;
if (petIsLost)
epdShowLost();
else
epdShowHealthy();
displayShowsLost = petIsLost;
}
Y todo se orquesta en el loop principal, que actualiza el módulo, revisa los SMS
entrantes (el dueño puede mandar LOCATION para pedir la posición al instante),
chequea las zonas y sube a la nube a intervalos regulares:
void loop() {
GeoLinker.update();
checkIncomingSMS();
bool hasLoc = getValidLocation(&gps);
checkGeofences(&gps, hasLoc);
if (!pendingCall && !callInProgress &&
millis() - lastCloudPush >= CLOUD_SEND_INTERVAL * 1000UL) {
lastCloudPush = millis();
cloudPush(&gps, hasLoc);
}
}

La caja impresa en 3D

La carcasa se diseñó en dos partes: una base que aloja la placa, la batería Li Ion y el cableado, y una tapa con un recorte a la medida de la pantalla e-paper para que quede visible pero protegida. A los costados van dos ranuras para pasar un collar estándar. Imprimila en PLA o PETG con relleno bajo (15 a 20 %) para que quede liviana; el PETG aguanta mejor el sol y la humedad si tu mascota sale seguido.

Pruebas y resultado

Con todo cargado, define tus zonas con las coordenadas reales de tu casa y patio (las del código son de ejemplo). Camina con el collar en la mano cruzando el límite de una zona: deberías recibir el SMS en segundos. Para la zona restringida, verifica que además entre la llamada. La pantalla e-paper debería alternar entre el nombre de la mascota y el mensaje de "perdida" con tu teléfono.

Problemas comunes:
- El GSM no conecta a la red. Revisa que la SIM esté bien puesta, que tenga saldo/plan activo y que sea 2G: el SIM800L solo habla 2G. En Chile confirma que tu operador aún dé cobertura 2G en tu zona.
- La batería se descarga muy rápido. El rastreo continuo y el GSM consumen. Sube
el intervalo de actualización (
CLOUD_SEND_INTERVAL) y activa modos de bajo consumo cuando la mascota está quieta. - La pantalla queda en blanco. Casi siempre son las conexiones SPI o la alimentación; verifica cada cable y que el código de init corra.
Variantes y mejoras
Tres formas de llevar el proyecto más lejos, ninguna en el tutorial original:
- Detección de collar removido con un IMU. Sumando un MPU6050 por I²C puedes detectar si el collar se sacó (queda inmóvil y horizontal por mucho rato) y mandar una alerta distinta. Útil si te preocupa que alguien le quite el collar.
- Posicionamiento en interiores por WiFi/BLE. El GPS no sirve dentro de la casa. El ESP32 ya trae WiFi y Bluetooth: puedes complementar el GPS con trilateración por RSSI de tus redes conocidas para saber en qué pieza está.
- Cerca virtual dinámica desde la nube. En vez de zonas fijas en el código, puedes recibir las coordenadas del geofence por MQTT y cambiarlas desde el celular sin recompilar el firmware, ideal si viajas con la mascota.
Personalización para Chile
Lista de materiales real y completa para armarlo con módulos sueltos que se consiguen en el mercado local:
- Placa ESP32 DevKit. cerebro del sistema (WiFi/BLE + pines de sobra).
- Módulo GPS NEO-6M (con antena cerámica). geolocalización.
- Módulo GSM/GPRS SIM800L. envío de SMS y llamadas por red celular.
- Pantalla e-paper 1.54" 200x200 (SPI). muestra estado y teléfono, retiene sin energía.
- Batería Li Ion 18650 3.7V (o LiPo con conector). alimentación portátil.
- Cable USB-C. programación y carga.
- SIM 2G / M2M. necesaria para el SIM800L (confirma cobertura 2G).
- Caja impresa en 3D. la imprimes tú con el STL del proyecto.
Si el tutorial original usa la placa integrada GeoLinker GL868, acá el ESP32 DevKit + SIM800L + NEO-6M cumplen exactamente la misma función armando los módulos por separado, con la ventaja de que consigues cada pieza por su cuenta y reemplazas solo la que falle.
Recursos
- Tutorial original (inglés): DIY IoT Pet Tracker with GPS, Geofencing & GSM Alerts
- Documentación adicional: librería
GxEPD2para pantallas e-paper y datasheet delNEO-6Mpara la configuración UART.
Versión chilena inspirada en el proyecto de CircuitDigest, re armada con módulos en stock local en MechatronicStore.




