No description
Find a file
2026-08-17 10:42:25 -03:00
README.md first commit 2026-08-17 10:42:25 -03:00

UpsBox

Firmware para ESP32-C3 SuperMini (board lolin_c3_mini, framework Arduino / PlatformIO) que actúa como puente WiFi ↔ serie transparente para depurar la comunicación con un UPS APC BX1100CI.

El C3 se conecta por UART TTL 3.3V directo a la placa del UPS (en los pines que iban al conversor USB, ya retirado): no hay RS232 ni level shifter. El puente pasa bytes crudos en ambos sentidos — no parsea el protocolo — para poder descubrir a mano qué habla el UPS (APC "smart" de comandos de una letra + CR, o Microlink binario).

Conexionado (UART1)

C3 SuperMini UPS Notas
GPIO0 (RX) TX del UPS entra al C3
GPIO1 (TX) RX del UPS sale del C3 hacia el UPS
GND GND del UPS masa común imprescindible

Parámetros: 2400 8N1. Pines y baudios son configurables en include/config.h (UPS_RX_PIN, UPS_TX_PIN, UPS_BAUD).

El UART1 del UPS es independiente del USB CDC (Serial) que se usa para el log de diagnóstico: nunca se mezclan.

Puertos de red

  • :23 → puente Telnet crudo ↔ UART del UPS (el núcleo). telnet upsbox.local.
  • :2323 → consola de logs de diagnóstico (WiFi/IP, OTA, cliente conectado…). nc upsbox.local 2323. También sale por el USB CDC nativo.
  • :80 → web de estado (http://upsbox.local/).

Arquitectura del código

  • Puente serie (serial_bridge): TCP:23 ↔ UART1. Filtra la negociación Telnet (IAC/0xFF) para que no contamine el stream serie y pide al cliente modo carácter/raw (sin line-buffering ni eco local). Un cliente a la vez (el más nuevo gana). Reconexión sin resetear la placa.
  • WiFi (wifi_conn): station, única fase bloqueante al boot + setAutoReconnect(true); si se cuelga reinicia la radio, no el chip.
  • OTA (ota): ArduinoOTA, upsbox.local con password. Se atiende en cada loop() aunque haya un cliente Telnet conectado.
  • Log remoto (remote_log) y web (web).
  • Secretos: secrets.yml cifrado con ansible-vault, inyectado como macros -D en el build (scripts/inject_secrets.py). Las credenciales no viven en el fuente.

Setup de secretos

cp secrets.yml.example secrets.yml
# ...editar wifi_ssid / wifi_pass / ota_password (tg_* opcional)...
ansible-vault encrypt secrets.yml
# password del vault: archivo .vault_pass (gitignoreado) o ANSIBLE_VAULT_PASSWORD_FILE

Flashear la primera vez (por USB)

pio run -t upload            # usa upload_port = /dev/ttyACM0 (ajustar si hace falta)
pio device monitor           # ver el log por USB CDC (115200): IP, OTA listo, etc.

El platformio.ini ya trae ARDUINO_USB_CDC_ON_BOOT=1 para que el USB CDC del C3 hable desde el arranque.

Flashear las siguientes (por OTA)

Una vez que la placa está en la red (mirá la IP en el monitor o en http://upsbox.local/):

pio run -t upload --upload-port upsbox.local     # o la IP; pide la ota_password

espota/ArduinoOTA queda anunciado como upsbox.local. Podés fijarlo en el platformio.ini con upload_protocol = espota y upload_port = upsbox.local.

Conectarse a la consola del UPS (Telnet)

telnet upsbox.local        # o telnet <ip>

El firmware negocia modo carácter, así que cada tecla se manda al UPS al toque. Todo lo que llega del UPS se imprime crudo.

Mandar comandos con CR intacto

Los comandos APC "smart" terminan en CR (\r, 0x0D), no en LF. El método más determinístico para probar un comando y ver la respuesta es un disparo con nc:

printf 'Q\r' | nc -w2 upsbox.local 23      # manda exactamente Q + CR, espera 2s la respuesta
printf 'Y\r' | nc -w2 upsbox.local 23      # "smart mode" (respuesta esperada: SM)
printf 'A\r' | nc -w2 upsbox.local 23      # self-test de front panel

nc no negocia Telnet, así que el byte sale tal cual: no hay IAC de por medio.

Interactivo con telnet: al presionar Enter, muchos clientes mandan CR-LF o CR-NUL. El puente lo pasa crudo por defecto (TELNET_EOL_MODE 0) — el CR llega, pero puede ir seguido de un LF/NUL. Si querés que cualquier Enter mande un solo CR limpio (cómodo para tantear a mano, también hace que el LF de nc interactivo se traduzca a CR), poné en config.h:

#define TELNET_EOL_MODE 1

Si el UPS resulta hablar binario, TELNET_ESCAPE_IAC 1 (default) escapa los 0xFF para no romper el cliente Telnet. Para verlo tal cual con nc, poné TELNET_ESCAPE_IAC 0 en config.h.

Tips de diagnóstico

  • Si no ves nada del UPS: revisá GND común y probá cruzar RX/TX (TX del UPS debe entrar al RX=GPIO0 del C3).
  • El log (nc upsbox.local 2323 o USB) te avisa cuándo se conecta/desconecta el cliente Telnet y el estado de WiFi/OTA.