Skip to content

Latest commit

 

History

22 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Serial-to-Modbus Bridge (ESP32 + RS485)

USB Serial bridge for dual-channel Modbus RTU DC power supplies (e.g. SK120X-style modules): 36 V / 6 A per channel. Uses ESP32 or ESP32-C3 and RS485.

No WiFi, no web UI—just a text-based serial protocol for direct computer control.

SK120X Module


Features

  • USB Serial — Text-based command/response protocol
  • Dual channel — Two independent outputs (Modbus slave IDs 1 & 2), 36 V / 6 A per channel
  • Fast streaming — Continuous DATA output at configurable rate (50-1000ms)
  • Board abstraction (HAL) — One codebase for ESP32 and ESP32-C3

Hardware Setup

  • MCU: ESP32 or ESP32-C3 dev board
  • RS485 transceiver (DE/RE optional; enable via build flag)
  • Two Modbus RTU power supply units (slave IDs 1 and 2)

Pin mapping

Board UART RX TX RS485 DE/RE
ESP32 (e.g. devkit) Serial2 16 17 4
ESP32-C3 Serial1 20 21 3

Baud rate: 115200 (both USB CDC and Modbus).


Software Setup

Build & upload (PlatformIO)

# Build both environments
pio run

# Build for a specific board
pio run -e esp32dev
pio run -e esp32-c3-devkitm-1

# Upload
pio run -t upload

Open Serial Monitor (115200) after upload. The device sends !READY v1.0.0 on boot.


Serial Protocol

The ESP32 streams data continuously. GUI/scripts send commands to control behavior.

Output format (ESP32 → Host)

DATA <ch> setV=<v> setA=<a> outV=<v> outA=<a> outP=<p> outE=<e> inV=<v> temp=<t> time=<s> cap=<c> ovp=<v> ocp=<a> opp=<p> status=<s> cvcc=<m> out=<0|1> mppt=<0|1>
  • ch: Channel number (1 or 2)
  • Values are raw register values (see Register Map below)
  • On read error: DATA <ch> ... ERR

Commands (Host → ESP32)

Command Description
WRITE <ch> <reg> <val> Write single register. Response: OK or ERR
LINK Copy Ch1 settings (V, I, output, MPPT) to Ch2. Response: OK or ERR
STOP Stop streaming. Response: OK stopped
START Resume streaming. Response: OK started
POLL <ms> Set poll interval (50-1000ms). Response: OK poll_ms=<ms>
POLL Query current poll interval. Response: OK poll_ms=<ms>
RAW <ch> Dump all 32 registers. Response: RAW <ch> <v0> <v1> ... <v31>
PING Connectivity check. Response: PONG
REGS Print register map
VERSION Firmware version. Response: OK 1.0.0
  • ch: 1 or 2
  • reg/val: decimal or hex (0x prefix)

Example session

!READY v1.0.0
DATA 1 setV=1200 setA=3000 outV=1198 outA=1500 outP=1797 outE=0 inV=2400 temp=381 time=0 cap=0 ovp=3700 ocp=6100 opp=25000 status=0 cvcc=0 out=1 mppt=0
DATA 2 setV=1200 setA=3000 outV=1195 outA=1480 outP=1768 outE=0 inV=2400 temp=378 time=0 cap=0 ovp=3700 ocp=6100 opp=25000 status=0 cvcc=0 out=1 mppt=0
> STOP
OK stopped
> WRITE 1 0x0000 1400
OK
DATA 1 setV=1400 ...
> POLL 200
OK poll_ms=200
> START
OK started

Register Map

Reg Name Unit R/W
0x0000 setV V * 100 R/W
0x0001 setA A * 1000 R/W
0x0002 outV V * 100 R
0x0003 outA A * 1000 R
0x0004 outP W * 100 R
0x0005 outE_H Wh (high) R
0x0006 outE_L Wh (low) R
0x0007 inV V * 100 R
0x000D temp °C * 10 R
0x0009 time_H sec (high) R
0x000A time_L sec (low) R
0x000B cap_H Ah*1000 (h) R
0x000C cap_L Ah*1000 (l) R
0x000E OVP V * 100 R/W
0x000F OCP A * 1000 R/W
0x0010 OPP W * 100 R/W
0x0012 out 0/1 R/W
0x0013 status flags R
0x0014 cvcc 0=CV, 1=CC R
0x001F mppt 0/1 (exp) R/W

GUI Application (psu-gui)

Standalone SDL2 control panel for the PSU with:

  • VFD display — Voltage, current, power readouts with green-on-black style
  • Bar meters & temperature — Voltage/current bars and temp strip (full GUI)
  • Scope traces — Real-time voltage/current waveform per channel
  • Controls — OUTPUT toggle, voltage/current setpoints, keypad (full GUI)
  • Dual vs singlepsu_gui / psu_gui_toolbar (two channels); psu_gui_single / psu_gui_toolbar_single (channel 1 only)
  • TRACKING — Link Ch1 settings to Ch2 (dual full GUI only)
  • Demo mode — Runs with simulated data if serial port unavailable

Screenshots (canonical copies in docs/images/psu-gui/; duplicates in psu-gui/screenshots/). More detail: psu-gui/README.md.

Dual full GUI (psu_gui)

Dual-channel full GUI

Single full GUI (psu_gui_single)

Single-channel full GUI

Dual toolbar (psu_gui_toolbar)

Dual-channel toolbar GUI

Dependencies

# Ubuntu/Debian
sudo apt install libsdl2-dev libsdl2-ttf-dev

# Fedora
sudo dnf install SDL2-devel SDL2_ttf-devel

# Arch
sudo pacman -S sdl2 sdl2_ttf

Also needs a monospace font (DejaVu Sans Mono or Liberation Mono).

Build & Run

cd psu-gui
make

# With ESP32 connected
./psu_gui /dev/ttyUSB0

# Demo mode (no device)
./psu_gui

# Compact toolbar-style GUI (dual channel)
./psu_gui_toolbar /dev/ttyUSB0
./psu_gui_toolbar

# Single-channel variants (hardware channel 1 only)
./psu_gui_single /dev/ttyUSB0
./psu_gui_toolbar_single /dev/ttyUSB0

GUI Controls

  • OUTPUT button — Toggle output on/off
  • VOLTAGE/CURRENT inputs — Click to edit, type value, press SET or Enter
  • TRACKING — When enabled, copies Ch1 settings to Ch2
  • REFRESH — Force status poll

Toolbar GUI (psu_gui_toolbar): Compact strip with large V/A readouts, ON/OFF and CV/CC status. SET opens a modal to edit setpoints (click outside, CANCEL, or Esc to close)


Code Structure

  • src/main.cpp — Serial protocol, DATA streaming, command parser
  • src/hal/ — Board HAL: UART/RS485 pins, init
  • src/modbus_psu.h / modbus_psu.cpp — Register map, Modbus read/write
  • psu-gui/ — SDL2 GUI application
    • main.c — Full dual-channel GUI
    • main_single.c — Full single-channel GUI
    • main_toolbar.c — Compact dual-channel toolbar
    • main_toolbar_single.c — Compact single-channel toolbar
    • serial_port.c/h — Linux serial port abstraction
    • psu_protocol.c/h — PSU command/response parsing

Scripting Example (Python)

import serial
import time
import re

ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=1)
time.sleep(2)  # wait for ESP32 boot

# Wait for READY
while True:
    line = ser.readline().decode().strip()
    if line == '!READY':
        break

# Stop streaming to send commands
ser.write(b'STOP\n')
ser.readline()  # OK stopped

# Set voltage to 15.00V on channel 1
ser.write(b'WRITE 1 0x0000 1500\n')
print(ser.readline().decode().strip())  # OK

# Resume streaming and read data
ser.write(b'START\n')
ser.readline()  # OK started

# Parse DATA lines
for _ in range(10):
    line = ser.readline().decode().strip()
    if line.startswith('DATA'):
        m = re.search(r'outV=(\d+)', line)
        if m:
            print(f"Output voltage: {int(m.group(1)) / 100:.2f} V")

ser.close()

Build Options

RS485 Direction Control

By default, RS485 DE/RE control is disabled. To enable:

# In platformio.ini, add to build_flags:
build_flags =
    -DUSE_RS485_DIR=1

Notes

  • MPPT — Experimental; support depends on PSU firmware.
  • Values are raw register values (setV 1200 = 12.00 V, setA 3000 = 3.000 A, etc.)
  • Single-threaded Arduino environment; modbus_psu_meaning() / modbus_psu_interpret() use static buffers.

Resources


License

MIT — use, modify, and share.

About

This is a web UI for SK120X 120W DC DC Buck Boost Converter CC CV 6.0-36V 5V 6V 12V 24V 36V Power Module Solar Charging Module

Topics

Resources

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages