Ir al contenido

← Web API - GET | Contenido | Siguiente: Calibración de sensores →

9. Web API - Puntos finales POST

Version: 1.0.0
Date: 2025-01-15

Documentación detallada de los puntos finales POST de la API REST para el control y la configuración del sistema ACRouter.




Tabla de contenidos




9.1 Introducción

Los puntos finales POST de la API REST del ACRouter están diseñados para:

  • Control del router – cambiar modo de operación, nivel del dimmer
  • Configuración – actualizar parámetros del sistema
  • Gestión WiFi – conectar/desconectar de redes
  • Configuración de hardware – configurar GPIOs, sensores, dimmers
  • Control del sistema – reiniciar dispositivo

Todos los cambios se aplican inmediatamente y se guardan en NVS (Non-Volatile Storage).

Implementación: components/comm/src/WebServerManager.cpp




9.2 Formato de solicitud


9.2.1 Content-Type

Todas las solicitudes POST deben usar:

http
Content-Type: application/json


9.2.2 Estructura de la solicitud

http
POST /api/endpoint HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "parameter1": "value1",
  "parameter2": 123
}


9.2.3 Formato de respuesta exitosa

Estado HTTP: 200 OK

json
{
  "success": true,
  "message": "Operation completed successfully"
}


9.2.4 Formato de respuesta de error

Estado HTTP: 400, 500, 501

json
{
  "error": "Error message description"
}



9.3 Punto final: POST /api/config

Actualizar parámetros de configuración del sistema. Permite actualizar uno o varios parámetros en una sola solicitud.


9.3.1 Solicitud

http
POST /api/config HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "voltage_coef": 1.02,
  "current_coef": 30.0,
  "current_threshold": 0.12,
  "power_threshold": 12.0,
  "control_gain": 180.0,
  "balance_threshold": 40.0
}


9.3.2 Parámetros (opcionales)

Todos los parámetros son opcionales. Solo se actualizan los campos especificados.

Parámetro Tipo Rango Descripción
voltage_coef float 0.1 .. 10.0 Coeficiente de calibración de voltaje
current_coef float 0.1 .. 100.0 Coeficiente de conversión de corriente (A/V)
current_threshold float 0.01 .. 10.0 Umbral de detección de corriente (A)
power_threshold float 1.0 .. 1000.0 Umbral de detección de potencia (W)
control_gain float 1.0 .. 1000.0 Ganancia del lazo de control
balance_threshold float 0.0 .. 100.0 Umbral de balance para el modo AUTO (W)


9.3.3 Respuesta (éxito)

Estado HTTP: 200 OK

json
{
  "success": true,
  "message": "Configuration updated"
}

Si no se modificó ningún parámetro:

json
{
  "success": true,
  "message": "No changes"
}


9.3.4 Respuesta (error)

Estado HTTP: 400 Bad Request

json
{
  "error": "Missing request body"
}
json
{
  "error": "Invalid JSON"
}


9.3.5 Ejemplos de uso

Actualizar un solo parámetro:

bash
curl -X POST http://192.168.4.1/api/config \
  -H "Content-Type: application/json" \
  -d '{"control_gain": 200.0}'

Actualizar varios parámetros:

bash
curl -X POST http://192.168.4.1/api/config \
  -H "Content-Type: application/json" \
  -d '{
    "control_gain": 180.0,
    "balance_threshold": 40.0,
    "voltage_coef": 1.02
  }'

Python:

python
import requests

config = {
    "control_gain": 200.0,
    "balance_threshold": 35.0
}

response = requests.post(
    "http://192.168.4.1/api/config",
    json=config
)

print(response.json())
# {'success': True, 'message': 'Configuration updated'}

JavaScript:

javascript
const config = {
  control_gain: 200.0,
  balance_threshold: 35.0
};

fetch('http://192.168.4.1/api/config', {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify(config)
})
  .then(r => r.json())
  .then(data => console.log(data));



9.4 Punto final: POST /api/config/reset

Restablecer todos los parámetros de configuración a los valores de fábrica.


9.4.1 Solicitud

http
POST /api/config/reset HTTP/1.1
Host: 192.168.4.1

Cuerpo de la solicitud: No requerido


9.4.2 Valores de fábrica

cpp
control_gain        = 150.0
balance_threshold   = 50.0 W
voltage_coef        = 1.0
current_coef        = 30.0 A/V
current_threshold   = 0.1 A
power_threshold     = 10.0 W


9.4.3 Respuesta (éxito)

Estado HTTP: 200 OK

json
{
  "success": true,
  "message": "Configuration reset to defaults"
}


9.4.4 Ejemplos de uso

Bash:

bash
curl -X POST http://192.168.4.1/api/config/reset

Python:

python
import requests

response = requests.post("http://192.168.4.1/api/config/reset")
print(response.json())
# {'success': True, 'message': 'Configuration reset to defaults'}

⚠️ Advertencia: Esta operación es irreversible. Todos los ajustes personalizados se perderán.




9.5 Punto final: POST /api/mode

Establecer el modo de operación del router.


9.5.1 Solicitud

http
POST /api/mode HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "mode": "auto"
}


9.5.2 Parámetros

Parámetro Tipo Requerido Valores válidos
mode string "off", "auto", "eco", "offgrid", "manual", "boost"

Descripción de los modos:

Modo Descripción
"off" Router desactivado, dimmer 0%
"auto" Enrutador solar – minimizar importación/exportación de red
"eco" Económico – evitar importación, permitir exportación
"offgrid" Offgrid – solar/batería, uso mínimo de red
"manual" Manual – nivel de dimmer fijo
"boost" Potencia máxima – dimmer 100%


9.5.3 Respuesta (éxito)

Estado HTTP: 200 OK

json
{
  "success": true,
  "message": "Mode set to AUTO"
}


9.5.4 Respuesta (error)

Estado HTTP: 400 Bad Request

json
{
  "error": "Missing 'mode' field"
}
json
{
  "error": "Invalid mode (use: off, auto, eco, offgrid, manual, boost)"
}


9.5.5 Ejemplos de uso

Activar modo AUTO (enrutador solar):

bash
curl -X POST http://192.168.4.1/api/mode \
  -H "Content-Type: application/json" \
  -d '{"mode": "auto"}'

Activar modo ECO:

bash
curl -X POST http://192.168.4.1/api/mode \
  -H "Content-Type: application/json" \
  -d '{"mode": "eco"}'

Python – alternar entre modos:

python
import requests
import time

def set_mode(mode):
    response = requests.post(
        "http://192.168.4.1/api/mode",
        json={"mode": mode}
    )
    return response.json()

# Set AUTO mode
print(set_mode("auto"))
time.sleep(60)

# Switch to MANUAL for testing
print(set_mode("manual"))
time.sleep(30)

# Return to AUTO
print(set_mode("auto"))



9.6 Punto final: POST /api/dimmer

Establecer el nivel del dimmer. Cambia automáticamente el router al modo MANUAL.


9.6.1 Solicitud

http
POST /api/dimmer HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "value": 75
}


9.6.2 Parámetros

Parámetro Tipo Requerido Rango Descripción
value integer 0 .. 100 Nivel del dimmer en porcentaje


9.6.3 Respuesta (éxito)

Estado HTTP: 200 OK

json
{
  "success": true,
  "message": "Dimmer value set"
}

⚠️ Importante: El router cambia automáticamente al modo MANUAL cuando se establece el nivel del dimmer.


9.6.4 Respuesta (error)

Estado HTTP: 400 Bad Request

json
{
  "error": "Missing 'value' field"
}
json
{
  "error": "Value must be 0-100"
}


9.6.5 Ejemplos de uso

Establecer dimmer al 50%:

bash
curl -X POST http://192.168.4.1/api/dimmer \
  -H "Content-Type: application/json" \
  -d '{"value": 50}'

Python – rampa de potencia gradual:

python
import requests
import time

def set_dimmer(value):
    response = requests.post(
        "http://192.168.4.1/api/dimmer",
        json={"value": value}
    )
    return response.json()

# Gradually increase from 0% to 100%
for level in range(0, 101, 10):
    print(f"Setting dimmer to {level}%")
    set_dimmer(level)
    time.sleep(5)

# Gradually decrease back
for level in range(100, -1, -10):
    print(f"Setting dimmer to {level}%")
    set_dimmer(level)
    time.sleep(5)

JavaScript – control por deslizador:

javascript
// HTML: 

const slider = document.getElementById('dimmerSlider');

slider.addEventListener('change', (e) => {
  const value = parseInt(e.target.value);

  fetch('http://192.168.4.1/api/dimmer', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({value: value})
  })
    .then(r => r.json())
    .then(data => console.log(`Dimmer set to ${value}%`));
});



9.7 Punto final: POST /api/manual

Punto final alternativo para establecer el modo manual con un nivel de dimmer especificado. Equivalente a POST /api/mode (mode=manual) + POST /api/dimmer.


9.7.1 Solicitud

http
POST /api/manual HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "value": 60
}


9.7.2 Parámetros

Parámetro Tipo Requerido Rango Descripción
value integer 0 .. 100 Nivel del dimmer en porcentaje


9.7.3 Comportamiento

  1. Cambia el router al modo MANUAL
  2. Establece el nivel del dimmer al valor especificado


9.7.4 Respuesta (éxito)

Estado HTTP: 200 OK

json
{
  "success": true,
  "message": "Manual control set"
}


9.7.5 Ejemplos de uso

Bash:

bash
curl -X POST http://192.168.4.1/api/manual \
  -H "Content-Type: application/json" \
  -d '{"value": 60}'

Diferencia entre /api/dimmer y /api/manual:

python
import requests

# Option 1: Use /api/dimmer
# Automatically switches to MANUAL
requests.post("http://192.168.4.1/api/dimmer", json={"value": 60})

# Option 2: Use /api/manual
# Explicitly switches to MANUAL and sets level
requests.post("http://192.168.4.1/api/manual", json={"value": 60})

# Result is identical



9.8 Punto final: POST /api/hardware/voltage/calibrate

Calibración automática del sensor de voltaje (disponible desde la versión 2.0).

El sistema mide automáticamente la salida del sensor (VDC RMS) y calcula el multiplicador de calibración basándose en el voltaje de red medido.


9.8.1 Solicitud

http
POST /api/hardware/voltage/calibrate HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "measured_vac": 232.5
}


9.8.2 Parámetros

Parámetro Tipo Requerido Descripción
measured_vac float Voltaje de red medido con multímetro (VAC RMS)

Rango: 100.0 .. 300.0 (según el estándar de red)


9.8.3 Proceso de calibración

  1. PowerMeterADC mide la salida actual del sensor (VDC RMS)
  2. Calcula el multiplicador: multiplier = measured_vac / sensor_vdc
  3. Actualiza nominal_vdc con el valor realmente medido
  4. La configuración se guarda en NVS
  5. ⚠️ Se requiere reinicio para aplicar los cambios


9.8.4 Respuesta (éxito)

Estado HTTP: 200 OK

json
{
  "success": true,
  "message": "Voltage sensor calibrated",
  "sensor_vdc": 0.72,
  "multiplier": 322.92,
  "sensor_type": "ZMPT107"
}

Campos de la respuesta:

Campo Tipo Descripción
sensor_vdc float Salida medida del sensor (VDC RMS)
multiplier float Multiplicador calculado
sensor_type string Tipo de sensor (ZMPT107, ZMPT101B, CUSTOM)


9.8.5 Respuesta (error)

Estado HTTP: 400 Bad Request

json
{
  "error": "Missing 'measured_vac' field"
}
json
{
  "error": "PowerMeterADC is not running"
}
json
{
  "error": "No voltage sensor configured"
}

Estado HTTP: 500 Internal Server Error

json
{
  "error": "Failed to save calibration to NVS"
}


9.8.6 Ejemplos de uso

Calibración básica:

bash
curl -X POST http://192.168.4.1/api/hardware/voltage/calibrate \
  -H "Content-Type: application/json" \
  -d '{"measured_vac": 232.5}'

Ejemplo de respuesta:

json
{
  "success": true,
  "message": "Voltage sensor calibrated",
  "sensor_vdc": 0.72,
  "multiplier": 322.92,
  "sensor_type": "ZMPT107"
}

Python – procedimiento de calibración completo:

python
import requests
import time

def calibrate_voltage_sensor(measured_vac, base_url="http://192.168.4.1"):
    """
    Automatic voltage sensor calibration

    Args:
        measured_vac: Grid voltage measured with multimeter (VAC)
        base_url: Router URL

    Returns:
        bool: True if calibration successful
    """

    print(f"Starting voltage sensor calibration...")
    print(f"Measured grid voltage: {measured_vac} VAC")

    # 1. Send calibration request
    response = requests.post(
        f"{base_url}/api/hardware/voltage/calibrate",
        json={"measured_vac": measured_vac},
        timeout=10
    )

    if response.status_code != 200:
        error = response.json().get('error', 'Unknown error')
        print(f"✗ Calibration failed: {error}")
        return False

    # 2. Get results
    result = response.json()

    if not result.get('success'):
        print(f"✗ Calibration failed")
        return False

    print(f"✓ Calibration successful:")
    print(f"  Sensor VDC: {result['sensor_vdc']:.3f} V")
    print(f"  Multiplier: {result['multiplier']:.2f}")
    print(f"  Sensor type: {result['sensor_type']}")

    # 3. Reboot device
    print("\nRebooting device to apply calibration...")
    requests.post(f"{base_url}/api/system/reboot")

    # 4. Wait for reboot
    time.sleep(15)

    # 5. Verify result
    print("Verifying calibration...")
    for attempt in range(10):
        try:
            status = requests.get(f"{base_url}/api/status", timeout=2).json()
            voltage = status.get('voltage', 0)

            # Check voltage is close to measured value
            if abs(voltage - measured_vac) < 5.0:
                print(f"✓ Calibration verified: {voltage:.1f} V")
                return True
            else:
                print(f"  Current reading: {voltage:.1f} V (expected {measured_vac:.1f} V)")
        except:
            pass

        time.sleep(2)

    print("⚠ Could not verify calibration (device may still be rebooting)")
    return False

# Usage
calibrate_voltage_sensor(232.5)

JavaScript – calibración desde la interfaz web:

javascript
async function calibrateVoltageSensor(measuredVAC) {
    try {
        // 1. Send calibration request
        const response = await fetch('http://192.168.4.1/api/hardware/voltage/calibrate', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({measured_vac: measuredVAC})
        });

        const result = await response.json();

        if (!result.success) {
            alert(`Calibration failed: ${result.error || 'Unknown error'}`);
            return false;
        }

        // 2. Show results
        alert(`Calibration successful!\n` +
              `Sensor VDC: ${result.sensor_vdc.toFixed(3)} V\n` +
              `Multiplier: ${result.multiplier.toFixed(2)}\n` +
              `Sensor: ${result.sensor_type}\n\n` +
              `Device will reboot now...`);

        // 3. Reboot device
        await fetch('http://192.168.4.1/api/system/reboot', {method: 'POST'});

        return true;
    } catch (error) {
        alert(`Error: ${error.message}`);
        return false;
    }
}

// Usage in HTML form
// 
// 


9.8.7 Notas importantes

⚠️ Requisitos:

  • PowerMeterADC debe estar en ejecución (isRunning() == true)
  • Debe haber un sensor de voltaje configurado (tipo VOLTAGE_AC)
  • Se requiere reinicio después de la calibración

Ventajas de la calibración automática:

  • No es necesario ajustar manualmente el potenciómetro del sensor
  • Funciona con cualquier voltaje de salida del sensor (0,5 V, 0,7 V, 1,0 V, etc.)
  • Actualiza automáticamente nominal_vdc con el valor realmente medido
  • Calibración más precisa y rápida

📖 Ver también:




9.9 Punto final: POST /api/wifi/connect

Conectar a una red WiFi. Las credenciales se guardan automáticamente en NVS tras una conexión exitosa.


9.9.1 Solicitud

http
POST /api/wifi/connect HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "ssid": "MyHomeNetwork",
  "password": "MySecurePassword123"
}


9.9.2 Parámetros

Parámetro Tipo Requerido Descripción
ssid string Nombre de la red WiFi (máx. 32 caracteres)
password string No Contraseña de la red (vacío para redes abiertas)


9.9.3 Comportamiento

  1. Se desconecta de la red STA actual (si está conectado)
  2. Intenta conectar a la nueva red
  3. En caso de éxito:
  4. Las credenciales se guardan automáticamente en NVS
  5. Obtiene dirección IP vía DHCP
  6. Inicializa NTP (sincronización de hora)
  7. El modo AP permanece activo (modo AP+STA)
  8. En el siguiente arranque, el router se conectará automáticamente a esta red


9.9.4 Respuesta (éxito)

Estado HTTP: 200 OK

json
{
  "success": true,
  "message": "Connecting to WiFi... Credentials will be saved on success",
  "ssid": "MyHomeNetwork"
}

⚠️ Importante: La respuesta se envía inmediatamente después de iniciar la conexión. La conexión real tarda de 5 a 10 segundos.


9.9.5 Respuesta (error)

Estado HTTP: 400 Bad Request

json
{
  "error": "Missing 'ssid' field"
}

Estado HTTP: 500 Internal Server Error

json
{
  "error": "Failed to initiate WiFi connection"
}


9.9.6 Verificar la conexión

Después de 10–15 segundos, verificar el estado:

bash
curl -s http://192.168.4.1/api/wifi/status | jq '.sta_connected, .sta_ip'


9.9.7 Ejemplos de uso

Conectar a red protegida:

bash
curl -X POST http://192.168.4.1/api/wifi/connect \
  -H "Content-Type: application/json" \
  -d '{
    "ssid": "MyHomeNetwork",
    "password": "SecurePass2024"
  }'

Conectar a red abierta:

bash
curl -X POST http://192.168.4.1/api/wifi/connect \
  -H "Content-Type: application/json" \
  -d '{"ssid": "PublicWiFi"}'

Python – conectar con verificación:

python
import requests
import time

def connect_wifi(ssid, password=None):
    """Connect to WiFi and wait for result"""

    # Send connection request
    payload = {"ssid": ssid}
    if password:
        payload["password"] = password

    response = requests.post(
        "http://192.168.4.1/api/wifi/connect",
        json=payload
    )

    if response.status_code != 200:
        print(f"Failed to initiate connection: {response.json()}")
        return False

    print(f"Connecting to {ssid}...")

    # Wait 10 seconds
    time.sleep(10)

    # Check result
    status = requests.get("http://192.168.4.1/api/wifi/status").json()

    if status['sta_connected'] and status['sta_ssid'] == ssid:
        print(f"✓ Connected to {ssid}")
        print(f"  IP: {status['sta_ip']}")
        print(f"  RSSI: {status['rssi']} dBm")
        return True
    else:
        print(f"✗ Failed to connect to {ssid}")
        return False

# Usage
connect_wifi("MyHomeNetwork", "MyPassword123")

⚠️ Seguridad: La contraseña se transmite en texto plano. Usar solo a través de conexión AP directa.




9.10 Punto final: POST /api/wifi/disconnect

Desconectar de la red WiFi (modo STA). El punto de acceso (AP) permanece activo.


9.10.1 Solicitud

http
POST /api/wifi/disconnect HTTP/1.1
Host: 192.168.4.1

Cuerpo de la solicitud: No requerido


9.10.2 Comportamiento

  • Se desconecta de la red STA
  • Cambia al modo AP_ONLY
  • Las credenciales guardadas permanecen en NVS (para conexión automática al reiniciar)


9.10.3 Respuesta (éxito)

Estado HTTP: 200 OK

json
{
  "success": true,
  "message": "Disconnected from WiFi network"
}


9.10.4 Ejemplos de uso

Bash:

bash
curl -X POST http://192.168.4.1/api/wifi/disconnect

Python:

python
import requests

response = requests.post("http://192.168.4.1/api/wifi/disconnect")
print(response.json())
# {'success': True, 'message': 'Disconnected from WiFi network'}

# Check status
status = requests.get("http://192.168.4.1/api/wifi/status").json()
print(f"State: {status['state']}")  # AP_ONLY
print(f"Saved credentials: {status['has_saved_credentials']}")  # True (remain in NVS)



9.11 Punto final: POST /api/wifi/forget

Eliminar las credenciales WiFi guardadas del NVS.


9.11.1 Solicitud

http
POST /api/wifi/forget HTTP/1.1
Host: 192.168.4.1

Cuerpo de la solicitud: No requerido


9.11.2 Comportamiento

  • Elimina las credenciales WiFi del NVS
  • La conexión actual se mantiene (si está conectado)
  • En el siguiente arranque, el router NO se conectará automáticamente
  • Permanecerá en modo AP_ONLY


9.11.3 Respuesta (éxito)

Estado HTTP: 200 OK

json
{
  "success": true,
  "message": "WiFi credentials cleared from NVS"
}


9.11.4 Respuesta (error)

Estado HTTP: 500 Internal Server Error

json
{
  "error": "Failed to clear WiFi credentials"
}


9.11.5 Ejemplos de uso

Bash:

bash
curl -X POST http://192.168.4.1/api/wifi/forget

Python – reinicio completo de WiFi:

python
import requests

def reset_wifi():
    """Complete WiFi reset"""

    # 1. Disconnect from network
    print("Disconnecting from WiFi...")
    requests.post("http://192.168.4.1/api/wifi/disconnect")

    # 2. Clear saved credentials
    print("Clearing saved credentials...")
    response = requests.post("http://192.168.4.1/api/wifi/forget")

    if response.json()['success']:
        print("✓ WiFi reset complete")
        print("  Router will be in AP-only mode after reboot")
    else:
        print("✗ Failed to clear credentials")

reset_wifi()

Escenario de uso común:

python
# Change WiFi network

# 1. Clear old credentials
requests.post("http://192.168.4.1/api/wifi/forget")

# 2. Connect to new network
requests.post("http://192.168.4.1/api/wifi/connect", json={
    "ssid": "NewNetwork",
    "password": "NewPassword"
})



9.12 Punto final: POST /api/hardware/config

Actualizar la configuración de hardware: canales ADC, dimmers, cruce por cero, relés, LEDs.

⚠️ Importante: Los cambios se aplican solo después de reiniciar el dispositivo.


9.12.1 Solicitud

http
POST /api/hardware/config HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "adc_channels": [
    {
      "gpio": 34,
      "type": 1,
      "multiplier": 320.0,
      "offset": 0.0,
      "enabled": true
    },
    ...
  ],
  "dimmer_ch1": {
    "gpio": 19,
    "enabled": true
  },
  "zerocross_gpio": 18,
  "zerocross_enabled": true,
  ...
}


9.12.2 Parámetros

Canales ADC (array de 4 elementos):

Parámetro Tipo Requerido Descripción Rango recomendado
gpio integer Pin GPIO 32–39 (¡solo ADC1!)
type integer Tipo de sensor 0=NONE, 1=VOLTAGE_AC, 2=CURRENT_LOAD, 3=CURRENT_GRID, 4=CURRENT_SOLAR
multiplier float No Coeficiente de calibración Auto: 328,57 (voltaje 230V), 30,0 (corriente). Ver Calibración de sensores. Rango: 0,1–1000,0
offset float No Desplazamiento de calibración Por defecto: 0,0. Rango: -100,0 a +100,0
enabled boolean Habilitado true/false

Notas:

  • GPIO: Solo ADC1 (32–39). ¡ADC2 no es compatible en modo de lectura continua!
  • Multiplicador: Se selecciona automáticamente según el tipo de sensor si no se especifica:
  • VOLTAGE_AC → 230,0 (ZMPT107)
  • CURRENT_* → 30,0 (SCT013-030)
  • Desplazamiento: Se usa para compensar el error sistemático del sensor

Dimmers:

Parámetro Tipo Descripción
dimmer_ch1.gpio integer Pin GPIO para dimmer 1
dimmer_ch1.enabled boolean Dimmer 1 habilitado
dimmer_ch2.gpio integer Pin GPIO para dimmer 2
dimmer_ch2.enabled boolean Dimmer 2 habilitado

Detector de cruce por cero:

Parámetro Tipo Descripción
zerocross_gpio integer Pin GPIO
zerocross_enabled boolean Habilitado

Relés:

Parámetro Tipo Descripción
relay_ch1.gpio integer Pin GPIO para relé 1
relay_ch1.active_high boolean Lógica: true = activo en ALTO
relay_ch1.enabled boolean Relé 1 habilitado
relay_ch2.* ... Igual para relé 2

Indicadores LED:

Parámetro Tipo Descripción
led_status_gpio integer GPIO para LED de estado
led_load_gpio integer GPIO para LED de carga


9.12.3 Validación

La configuración se valida automáticamente antes de guardar:

  • Verificación de conflictos GPIO (un GPIO no puede usarse dos veces)
  • Verificación de pines GPIO válidos (0–39)
  • Verificación de tipos de sensor


9.12.4 Respuesta (éxito)

Estado HTTP: 200 OK

json
{
  "success": true,
  "message": "Configuration saved to NVS (reboot required)"
}

⚠️ Importante: ¡Se requiere reinicio para aplicar los cambios!


9.12.5 Respuesta (error de validación)

Estado HTTP: 400 Bad Request

json
{
  "success": false,
  "error": "GPIO 19 conflict: used by Dimmer 1 and ADC channel 0"
}


9.12.6 Ejemplos de uso

Actualizar canales ADC:

bash
curl -X POST http://192.168.4.1/api/hardware/config \
  -H "Content-Type: application/json" \
  -d '{
    "adc_channels": [
      {
        "gpio": 34,
        "type": 1,
        "multiplier": 320.0,
        "offset": 0.0,
        "enabled": true
      },
      {
        "gpio": 35,
        "type": 2,
        "multiplier": 30.0,
        "offset": 0.0,
        "enabled": true
      }
    ]
  }'

Python – cambiar GPIO del dimmer:

python
import requests

config = {
    "dimmer_ch1": {
        "gpio": 21,  # Change from 19 to 21
        "enabled": True
    }
}

response = requests.post(
    "http://192.168.4.1/api/hardware/config",
    json=config
)

if response.json()['success']:
    print("Configuration saved. Rebooting device...")
    # Reboot device
    requests.post("http://192.168.4.1/api/system/reboot")



9.13 Punto final: POST /api/hardware/validate

Validar la configuración de hardware sin guardar. Útil para verificar antes de aplicar.


9.13.1 Solicitud

http
POST /api/hardware/validate HTTP/1.1
Host: 192.168.4.1
Content-Type: application/json

{
  "dimmer_ch1": {
    "gpio": 34,
    "enabled": true
  }
}


9.13.2 Respuesta (configuración válida)

Estado HTTP: 200 OK

json
{
  "valid": true
}


9.13.3 Respuesta (configuración inválida)

Estado HTTP: 200 OK

json
{
  "valid": false,
  "error": "GPIO 34 conflict: already used by ADC channel 0"
}


9.13.4 Ejemplos de uso

Python – validar antes de aplicar:

python
import requests

new_config = {
    "dimmer_ch1": {"gpio": 21, "enabled": True},
    "zerocross_gpio": 18
}

# 1. Validate first
validate_response = requests.post(
    "http://192.168.4.1/api/hardware/validate",
    json=new_config
)

if validate_response.json()['valid']:
    print("✓ Configuration valid, applying...")

    # 2. Apply configuration
    apply_response = requests.post(
        "http://192.168.4.1/api/hardware/config",
        json=new_config
    )

    if apply_response.json()['success']:
        print("✓ Configuration saved, rebooting...")
        requests.post("http://192.168.4.1/api/system/reboot")
else:
    print(f"✗ Invalid configuration: {validate_response.json()['error']}")



9.14 Punto final: POST /api/system/reboot

Reiniciar el dispositivo.


9.14.1 Solicitud

http
POST /api/system/reboot HTTP/1.1
Host: 192.168.4.1

Cuerpo de la solicitud: No requerido


9.14.2 Comportamiento

  1. Envía la respuesta al cliente
  2. Espera 500 ms (para transmitir la respuesta)
  3. Espera 3 segundos adicionales
  4. Ejecuta el reinicio vía ESP.restart()

⚠️ Importante: Las tareas críticas deben detenerse antes del reinicio (previsto para versiones futuras).


9.14.3 Respuesta

Estado HTTP: 200 OK

json
{
  "success": true,
  "message": "Rebooting in 3 seconds..."
}


9.14.4 Ejemplos de uso

Bash:

bash
curl -X POST http://192.168.4.1/api/system/reboot

Python – reiniciar y esperar:

python
import requests
import time

def reboot_and_wait(url="http://192.168.4.1"):
    """Reboot device and wait for it to come back online"""

    print("Rebooting device...")
    try:
        requests.post(f"{url}/api/system/reboot", timeout=5)
    except requests.exceptions.RequestException:
        pass  # Connection may be dropped

    print("Waiting for device to reboot...")
    time.sleep(10)  # Wait 10 seconds

    # Wait until device comes back online
    for attempt in range(30):
        try:
            response = requests.get(f"{url}/api/info", timeout=2)
            if response.status_code == 200:
                info = response.json()
                print(f"✓ Device online (uptime: {info['uptime_sec']}s)")
                return True
        except requests.exceptions.RequestException:
            pass

        print(f"  Attempt {attempt + 1}/30...")
        time.sleep(2)

    print("✗ Device did not come back online")
    return False

# Usage
reboot_and_wait()



9.15 Manejo de errores


9.15.1 Códigos de error HTTP

Código Estado Descripción
400 Bad Request Formato de solicitud inválido, campos faltantes, valores inválidos
500 Internal Server Error Error interno del servidor (error NVS, etc.)
501 Not Implemented Funcionalidad no implementada


9.15.2 Ejemplos de errores

Cuerpo de solicitud faltante:

json
{
  "error": "Missing request body"
}

JSON inválido:

json
{
  "error": "Invalid JSON"
}

Campo requerido faltante:

json
{
  "error": "Missing 'mode' field"
}

Valor inválido:

json
{
  "error": "Value must be 0-100"
}

Error de validación:

json
{
  "error": "GPIO 19 conflict: used by Dimmer 1 and ADC channel 0"
}


9.15.3 Manejo de errores en el código

Python:

python
import requests

def safe_post(url, json_data):
    """Safe POST request with error handling"""
    try:
        response = requests.post(url, json=json_data, timeout=10)

        if response.status_code == 200:
            return response.json()
        else:
            error_msg = response.json().get('error', 'Unknown error')
            print(f"Error {response.status_code}: {error_msg}")
            return None

    except requests.exceptions.Timeout:
        print("Request timeout")
        return None

    except requests.exceptions.ConnectionError:
        print("Connection error")
        return None

    except Exception as e:
        print(f"Unexpected error: {e}")
        return None

# Usage
result = safe_post(
    "http://192.168.4.1/api/mode",
    {"mode": "auto"}
)

if result and result.get('success'):
    print("Success!")
else:
    print("Failed to set mode")



9.16 Ejemplos de uso


9.16.1 Configuración completa del router (Python)

python
import requests
import time

class ACRouterConfig:
    def __init__(self, base_url="http://192.168.4.1"):
        self.base_url = base_url

    def set_mode(self, mode):
        """Set operating mode"""
        return requests.post(
            f"{self.base_url}/api/mode",
            json={"mode": mode}
        ).json()

    def set_dimmer(self, value):
        """Set dimmer level"""
        return requests.post(
            f"{self.base_url}/api/dimmer",
            json={"value": value}
        ).json()

    def update_config(self, **params):
        """Update configuration parameters"""
        return requests.post(
            f"{self.base_url}/api/config",
            json=params
        ).json()

    def connect_wifi(self, ssid, password=None):
        """Connect to WiFi"""
        payload = {"ssid": ssid}
        if password:
            payload["password"] = password

        response = requests.post(
            f"{self.base_url}/api/wifi/connect",
            json=payload
        ).json()

        if response['success']:
            time.sleep(10)  # Wait for connection
            status = requests.get(f"{self.base_url}/api/wifi/status").json()
            return status['sta_connected']

        return False

    def setup_solar_router(self):
        """Complete Solar Router setup"""

        # 1. Configure control parameters
        print("Configuring control parameters...")
        self.update_config(
            control_gain=180.0,
            balance_threshold=40.0
        )

        # 2. Set AUTO mode
        print("Setting AUTO mode...")
        self.set_mode("auto")

        # 3. Check status
        status = requests.get(f"{self.base_url}/api/status").json()
        print(f"Mode: {status['mode']}")
        print(f"Dimmer: {status['dimmer']}%")
        print(f"Power: {status['power_grid']}W")

# Usage
router = ACRouterConfig()
router.setup_solar_router()


9.16.2 Monitoreo y corrección automática (Python)

python
import requests
import time

def monitor_and_adjust():
    """Monitor power and automatically adjust mode"""

    base_url = "http://192.168.4.1"

    while True:
        # Get current status
        status = requests.get(f"{base_url}/api/status").json()

        power = status['power_grid']
        mode = status['mode']

        print(f"[{time.strftime('%H:%M:%S')}] Power: {power:+7.1f}W | Mode: {mode}")

        # If high import from grid in AUTO mode
        if mode == 'auto' and power > 500:
            print("  High import detected, increasing gain...")
            requests.post(f"{base_url}/api/config", json={"control_gain": 250.0})

        # If high export to grid
        elif mode == 'auto' and power < -500:
            print("  High export detected, switching to BOOST mode...")
            requests.post(f"{base_url}/api/mode", json={"mode": "boost"})

        time.sleep(10)

monitor_and_adjust()


9.16.3 Panel de control web (HTML + JavaScript)

html



    ACRouter Control Panel
    


    

ACRouter Control Panel

Mode Control

Dimmer Control

0%

Quick Actions




  • 07_COMMANDS.md – Referencia de comandos (RU)
  • 07_COMMANDS_EN.md – Referencia de comandos (EN)
  • 08_WEB_API_GET.md – Puntos finales Web API GET (RU)
  • 08_WEB_API_GET_EN.md – Puntos finales Web API GET (EN)
  • 09_WEB_API_POST.md – Puntos finales Web API POST (RU)
  • 10_WEB_INTERFACE.md – Navegación de la interfaz web (siguiente sección)

Firmware Version: 1.0.0
Last Updated: 2025-01-15

← Web API - GET | Contenido | Siguiente: Calibración de sensores →