ESP32 LoRa for dummys - TinyGS: Application Programming Interface (API)

Para probar las API de TinyGs puede usar su navegador, pero encontrará que es mas práctico usar cURL un navegador orientado a linea de comandos  (terminal), versiones compiladas apra la mayoria de sistemas operativos las encontrará en static-curl

Para probar la instalación de cURL puede ejecutar curl --help' o 'curl --manual'


Vamos a ver unos cuantos comandos del API de TinyGS (hasta el momento no he localizado el manual):

https://api.tinygs.com/v1/statistics

Lista las estadísticas del sistema

{"members":5919,"packets":16714523,"stations":1677}

https://api.tinygs.com/v1/stations?

Lista todas las estaciones TinyGS  con el siguiente formato (observe que el userID no tiene nada que ver con su usuario de acceso a TinyGS

{
    "name": "MalvaRosa",
    "userId": 5343680049,
    "autoTune": 433,
    "confirmedPackets": 953,
    "lastSeen": 1730394223267,
    "location": [39.48, -0.33],
    "satellite": "Tianqi",
    "status": 1,
    "telemetryPackets": 808,
    "test": false,
    "version": 2403242,
    "lastPacketTime": 1730387686501
  }, 

https://api.tinygs.com/v1/stations?userId=5343680049

Devuelve todas las estaciones de un usuario

[
  {
    "name": "MalvaRosa",
    "userId": 5343680049,
    "autoTune": 433,
    "confirmedPackets": 953,
    "lastSeen": 1730395183291,
    "location": [39.48, -0.33],
    "satellite": "Tianqi",
    "status": 1,
    "telemetryPackets": 808,
    "test": false,
    "version": 2403242,
    "cnt_pckt_conf_433_LoRa": 718,
    "cnt_pckt_tele_433_LoRa": 691,
    "lastPacketTime": 1730387686501,
    "cnt_pckt_conf_137_LoRa": 235,
    "cnt_pckt_tele_137_LoRa": 117
  },
  {
    "name": "MalvaRosa_Test",
    "userId": 5343680049,
    "autoTune": 900,
    "confirmedPackets": 0,
    "lastSeen": 1730395141293,
    "location": [39.48, -0.33],
    "satellite": "PY4-Unknown",
    "status": 1,
    "telemetryPackets": 0,
    "test": false,
    "version": 2403242
  }

https://api.tinygs.com/v1/station/MalvaRosa@5343680049

{
  "name": "MalvaRosa",
  "userId": 5343680049,
  "antennaBand": [90, 2500],
  "autoTune": 433,
  "autoUpdate": true,
  "beta": true,
  "confirmedPackets": 4083,
  "creationDate": 1728812735116,
  "elevation": 11,
  "lastSeen": 1731686791360,
  "local_ip": "192.168.1.47",
  "location": [39.48, -0.33],
  "modem_conf": "",
  "radioStatus": 0,
  "satellite": "ONDOSAT-OWL-1",
  "status": 1,
  "telemetryPackets": 3593,
  "test": false,
  "tx": true,
  "version": 2403242,
  "wifiRssi": -65,
  "recordDistance": 2955.53036241728,
  "frames": 4234,
  "lastPacketTime": 1731682167081,
  "antenna": "Yagi-Uda",
  "description": "137Band: Lilygo ESP32 LoRa433 autotuning  137 & Turnstile\n433Band: Lilygo ESP32 LoRa 433 autotuning  433 & logperiode vertical",
  "pictures": [
    {
      "src": "https://static.tinygs.com/stationImages/TinyGS_MalvaRosa_c89fe.jpg",
      "status": 1,
      "type": "image/jpeg",
      "size": 159128,
      "origName": "20241028_112548.jpg",
      "shortId": "c89fe1d31328cdc7"
    }
  ]

 

https://api.tinygs.com/v1/station/MalvaRosa@5343680049/statistics

Devuelve las estadísticas de un estación concreta TinyGS, en este caso Malvarosa@5343680049(da error)

https://api.tinygs.com/v1/tinygs_supported.txt

Lista lo satélites soportados que pueden ser cargados en aplicaciones como gpredict or Look4Sat 

Norby

1 46494U 20068J   24318.83421293  .00019065  00000-0  81076-3 0  9999

2 46494  97.8346 285.6840 0013552 145.1692 215.0433 15.22993847227170 

https://api.tinygs.com/v1/packets

https://api.tinygs.com/v2/packets

 "packets": [
    {
      "raw": "6/iADOxwPBr4BAzscDoAAEreHhw67t9kQjOPXUOgFAdCgXGNQpZWk+A2QAYAL7cVARgJGgEmDxGMoCsk3gldAABLRgKqBAEAsAEBACQAoUQeAAAAAAAAAAAAAAAAAAAAAAAAAA==",
      "serverTime": 1731586620341,
      "mode": "LoRa",
      "freq": 400.45,
      "sf": 9,
      "bw": 500,
      "cr": 5,
      "bitrate": null,
      "freqDev": null,
      "rxBw": null,
      "satellite": "Tianqi-26",
      "norad": 99999,
      "id": "3fb42333-6387-426b-ab8b-ee52c40a14aa",
      "satDisplayName": "Tianqi-26",
      "parsed": {
        "header": {
          "netId": 235,
          "msgTypeId": 248,
          "headUnk": 128,
          "msgTypeId1n": 15,
          "msgTypeId2n": 8,
          "headUnk1n": 2
        },
        "payload": {
          "timestamp": 216821820,
          "satId": 26,
          "unk248xF8": 248,
          "unk24x18": 4,
          "unk81x51": 12,
          "unk121x79": 236,
          "unk81xx51": 112,
          "unk121xx79": 58,
          "unk71x47": 0,
          "unk206CEx3": [0, 74, 222],
          "unkx211x110x172x142x150x38": [30, 28, 58, 238, 223, 100],
          "unkx150x37x150x20": [66, 51, 143, 93],
          "lastRXtime": 1134564359,
          "unkx00x00": [66, 129],
          "unkValue": 1.3989722830040528e+30,
          "eccentricity": 81295593963520,
          "inclination": 2.09376120567322,
          "ascending": -0.000008881346730049,
          "angleX": 1.85376157074446e-33,
          "angleY": 7.17613534340837e-30,
          "unkValue2": 5.85726400107595e-13,
          "eccentricity2": 576465906264178700,
          "inclination2": 8362.50390625,
          "ascending2": 2.36361636262599e-38,
          "angleX2": 2.35357157497402e-38,
          "angleY2": -6.64470877341397e-19,
          "tinygsTxPower": 9000,
          "satId1b": 1,
          "satId2n": 10,
          "tinygsSatellite": "Tianqi-26"
        },
        "telemetry": true,
        "type": "Beacon",
        "object": 0
      },
      "satPos": {
        "lat": 27.0680430552991,
        "lng": -128.527143810243,
        "alt": 901.236724880711
      },
      "stationNumber": 4
    },

Vamos a ver ahora algunos programas 

Crear un archivo de log o registro

El programa, escrito en Python (Necesitará tener instalado Python y un usar su IDE o un editor de progrmas para ponerlo a punto),  crea un archivo de registro para registrar los datos a intervalos regulares (Estoy probandolo)

#!/usr/bin/env python3
import requests 
import os
import time
__author__ = "Diarmuid O'Briain"
__copyright__ = "Copyright 2023, Limerick Clare Amateur Radio Club"
__licence__ = "European Union Public Licence v1.2"
__version__ = "1.0"
telegram_id = "<Telegram-ID>"
API = f"https://api.tinygs.com/v1/stations?userId={telegram_id}"
LOG = "/path/to/lora_station.log"
TIME=5 # espera 5 minutos
def access_api():
    """Acceso a la API de TinyGS"""
    # Descargando los datos de la URL
    response = requests.get(API)
    # Comprobando que la descarga ha ido bien
    if response.status_code == 200:
        # Append the data to the file
        with open(LOG, mode="a") as fh:
            fh.write(response.text)
            fh.write("\n\n")
    else:
        print(f"Error en la descarga de datos: {response.status_code}")

if __name__ == "__main__":
    while True:
        access_api()
        time.sleep(TIME*60)  # Time in seconds

 

Hay que reemplazar

      • /path/to/lora_station.log  por el directorio correcto
      •  <Telegram-ID> por el usuario correcto


Referencia:

Prohibida la reproducción parcial o total de este artículo sin permiso previo del autor

ESP32 Basic Starter Kit for dummys: Proyecto - 10 Sincronización de interruptores fisicos y virtuales


Para este proyecto el programa lo localizamos en  ESP32/ESP8266: Control Outputs with Web Server and a Physical Button Simultaneously

(En construcción)

Referencia 

  • LROBLRUYA Project 10 pp 102


Continúa en: Proyecto-11 



Prohibida la reproducción parcial o total de este artículo sin permiso previo del autor

ESP32 Basic Starter Kit for dummys: Proyecto - 9 Trabajando con relés

Un relé (relay) es un interruptor operado eléctricamente y, como cualquier otro interruptor, se puede encender (cerrado - closed) o apagar (abierto - open), dejando pasar la corriente o no a través suyo. 

Un relé puede ser controlado con voltajes bajos, como los 3.3V proporcionado por los GPIO ESP32 y eso nos permite controlar tensiones altas como 12V, 24V o tensión de red (220V), también permite controlar radiofrecuencia por lo que permite por ejemplo conmutar líneas de alimentación de antenas.

Un relé controlado por un microprocesador es la base, entre otras cosas, de la domótica (subir y bajar persianas, abrir puertas, encender o apagar luces, etc.)

En el Kit tenemos una placa con 2 relés, pero se pueden encontrar placas con:
La alimentación puede ser de 3,3 V,  5V (usar VIN pin), 12 o 24 V (Se necesita fuente externa para estos últimos voltajes)



Esquema del modulo con relés (2) cada uno de ellos tiene un terminal NO (Normalmente abierto, a falta de orden que diga lo contrario), uno COM (común, corriente a controlar) y uno NC (Normalmente cerrado, a falta de orden que diga lo contrario). Además tiene dos entradas para la alimentación  de tensión (VCC, +) y la de tierra (GND, -) y dos entradas de control IN1 y IN2. la segunda toma de alimentación cn el puente o jump permite aislar la alimentación del relé de la del ESP32.
(Observe que el tutorial del KIT aprovecha la imagen de https://randomnerdtutorials.com/)


Lógica:
  • Normally Closed  (NC) (p.e. router de wifi):
    • HIGH signal – La corriente no circula  (circuito abierto)
    • LOW signal – La corriente circula  (circuito cerrado)
  • Normally Open  (NO) (p.e una luz):
    • HIGH signal – La corriente circula  (circuito cerrado)
    • LOW signal – La corriente no circula (circuito abierto)

Circuiteria para probar los relés


Vamos a ver varios programas de ejemplo de más sencillos a mas complejos con el find e fijar los conceptos y abrir ventanas a su creatividad:

Programa de prueba del relé 

El programa enciende y apaga un LED siguiendo una secuencia, cada 5 segundos, y escribe en el monitor el estado. Observe que además de encenderse el LED se oye el "clic" del relé cuando actúa, pues es un componente mecánico y se enciende un microled en la placa ESP32

/*********
  Rui Santos
 Complete project details at https://RandomNerdTutorials.com/esp32-relay-module-ac-web-server/ 
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/
//  Define el GPIO 26 como salida, y 115200 la velocidad del monitor serie
const int relay = 26;

void setup() {
  Serial.begin(115200);
  pinMode(relay, OUTPUT);
}

void loop() {
  // NO Normalmente Open con LOW se cierra
  digitalWrite(relay, LOW);
  Serial.println("encendido");
  delay(5000); 
  
  // NO Normalmente Open con HIGTH  se abre
  digitalWrite(relay, HIGH);
  Serial.println("apagado");
  delay(5000);
}

Puede modificar:
    • el circuito con un segundo led conectado a la salida NC de forma que cuando un led se apague el otro se encienda y viceversa
    • el programa para incluir el control del GPIO26 y con ello el segundo relé

Programa para controlar un relé mediante un botón o dos botones

const int relay = 26;
const int buttonPin = 4; 

// Inicialización  de la salida serie y GPIO
int buttonState = 0;
void setup() {
  Serial.begin(115200);
  pinMode(buttonPin, INPUT); 
  pinMode(relay, OUTPUT);
}

void loop() 
{
  // Lee el estado del botón GPIO 4
  buttonState = digitalRead(buttonPin);
  Serial.println(buttonState); 
  // Si está HIGH entonces ... sino ... 
  if (buttonState == HIGH) 
  {
    // Enciende LED ON
    digitalWrite(relay, LOW);
    Serial.println("OFF"); 
  } else {
    // Apaga LED OFF
    digitalWrite(relay, HIGH);
    Serial.println("ON");  
}}

Programa para controlar un relé mediante un código (se puede mejorar para quen que no cominece el ciclo encendido, pero en otro momento más)

// Definiciones GPIO
const int relay = 26;
char val;

// Inicialización de la salida serie y GPIO
void setup() {
Serial.begin(115200);
pinMode(relay, OUTPUT);
}

void loop()
{
if (Serial.available() > 0)
{
val=Serial.read();
if ((val == '\n') || (val == '\r')) { }
else if (val == 'E') {
digitalWrite(relay, LOW);
Serial.println("Enciende");}
else {
digitalWrite(relay, HIGH);
Serial.println("Apaga");}
}}





Programa para controlar un relé mediante web

Estos programas de control mediante web server son muy potentes y muestran el potencial de los servidores web y trabajando en  microprocesadores, pero desde el punto de vista didáctico tienen poco valor pues la programación es compleja.

Para este segundo programa del proyecto en el que se controlan hasta 5 relés desde un navegador  se necesita instalar dos librerias nuevas:
  • https://codeload.github.com/me-no-dev/AsyncTCP/zip/refs/heads/master
  • https://codeload.github.com/me-no-dev/ESPAsyncWebServer/zip/refs/heads/master
El proceso de instalación es el siguiente:
  • Entrar en las URL que provocarán la descarga del ZIP correpondiente
  • Desde Android IDE hacer Sketch -> Incluir biblioteca (libreria) - > Añadir biblioteca ZIP
A estas alturas no decubriré nada nuevo si digo que no se localiza el fuente donde dice el manual asíe s que lo buscamos por Google "ESP32 Relay Module – Control AC Appliances (Web Server)"

/*********
Rui Santos
Complete project details at  
https://RandomNerdTutorials.com/esp32-relay-module-ac-web-server/
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*********/

// Incluye las bibliotecas
#include "WiFi.h"
#include "ESPAsyncWebServer.h"

// Set to true to define Relay as Normally Open (NO)
#define RELAY_NO true

// Numero de reles a controlar
#define NUM_RELAYS 5

// asignacion de GPIO para los 5 reles
int relayGPIOs[NUM_RELAYS] = {2, 26, 27, 25, 33};

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

const char* PARAM_INPUT_1 = "relay";
const char* PARAM_INPUT_2 = "state";

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

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html {font-family: Arial; display: inline-block; text-align: center;}
h2 {font-size: 3.0rem;}
p {font-size: 3.0rem;}
body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
.switch {position: relative; display: inline-block; width: 120px; height: 68px}
.switch input {display: none}
.slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0;  
background-color: #ccc; border-radius: 34px}
.slider:before {position: absolute; content: ""; height: 52px; width: 52px;  
left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s;  
transition: .4s; border-radius: 68px}
input:checked+.slider {background-color: #2196F3}
input:checked+.slider:before {-webkit-transform: translateX(52px);  
-ms-transform: translateX(52px); transform: translateX(52px)}
</style>
</head>
<body>
<h2>ESP Web Server</h2>
%BUTTONPLACEHOLDER%
<script>function toggleCheckbox(element) {
var xhr = new XMLHttpRequest();
if(element.checked){ xhr.open("GET", "/update?relay="+element.id+"&state=1", true); }
else { xhr.open("GET", "/update?relay="+element.id+"&state=0", true); }
xhr.send();
}</script>
</body>
</html>
)rawliteral";

// Replaces placeholder with button section in your web page
String processor(const String& var){
//Serial.println(var);
if(var == "BUTTONPLACEHOLDER"){
String buttons ="";
for(int i=1; i<=NUM_RELAYS; i++){
String relayStateValue = relayState(i);
buttons+= "<h4>Relay #" + String(i) + " - GPIO " + relayGPIOs[i-1] + "</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"" + String(i) + "\" "+ relayStateValue +"><span class=\"slider\"></span></label>";
}
return buttons;
}
return String();
}

String relayState(int numRelay){
if(RELAY_NO){
if(digitalRead(relayGPIOs[numRelay-1])){
return "";
}
else {
return "checked";
}
}
else {
if(digitalRead(relayGPIOs[numRelay-1])){
return "checked";
}
else {
return "";
}
}
return "";
}

void setup(){
// Serial port for debugging purposes
Serial.begin(115200);

// Set all relays to off when the program starts - if set to Normally Open (NO), the relay is off when you set the relay to HIGH
for(int i=1; i<=NUM_RELAYS; i++){
pinMode(relayGPIOs[i-1], OUTPUT);
if(RELAY_NO){
digitalWrite(relayGPIOs[i-1], HIGH);
}
else{
digitalWrite(relayGPIOs[i-1], LOW);
}
}
// Connect to Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}

// Print ESP32 Local IP Address
Serial.println(WiFi.localIP());

// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html, processor);
});

// Send a GET request to <ESP_IP>/update?relay=<inputMessage>&state=<inputMessage2>
server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage;
String inputParam;
String inputMessage2;
String inputParam2;
// GET input1 value on <ESP_IP>/update?relay=<inputMessage>
if (request->hasParam(PARAM_INPUT_1) & request->hasParam(PARAM_INPUT_2)) {
inputMessage = request->getParam(PARAM_INPUT_1)->value();
inputParam = PARAM_INPUT_1;
inputMessage2 = request->getParam(PARAM_INPUT_2)->value();
inputParam2 = PARAM_INPUT_2;
if(RELAY_NO){
Serial.print("NO ");
digitalWrite(relayGPIOs[inputMessage.toInt()-1], !inputMessage2.toInt());
}
else{
Serial.print("NC ");
digitalWrite(relayGPIOs[inputMessage.toInt()-1], inputMessage2.toInt());
}
}
else {
inputMessage = "No message sent";
inputParam = "none";
}
Serial.println(inputMessage + inputMessage2);
request->send(200, "text/plain", "OK");
});
// Start server
server.begin();
}
void loop() {

}

Pagina web de control de los relés





Prohibida la reproducción parcial o total de este artículo sin permiso previo del autor

WSPR para el estudio de los efectos de las tormentas solares en la propagación ionosférica

 (En construcción) Referencias What 7 Geomagnetic Storms Taught Me About HF Propagation