This commit is contained in:
2026-06-16 04:59:02 +10:00
parent e139079a3c
commit 436d452deb
14 changed files with 399 additions and 135 deletions
+58 -21
View File
@@ -10,38 +10,69 @@ PyAware seems to already decode Serial Data. Look at porting that to portable C
* Parse Serial Data
* A simple C parser, portable to allow parsing
This repo now contains a **portable C parser** for the SP PRO serial protocol, ported
from [neerolyte/selpi](https://github.com/neerolyte/selpi) (PyAware itself is not open
source). Full protocol notes are in [docs/PROTOCOL.md](docs/PROTOCOL.md) and the ESP32
This repo is a **portable C parser** for the SP PRO serial protocol (ported from
[neerolyte/selpi](https://github.com/neerolyte/selpi) PyAware itself is not open
source), packaged as a **PlatformIO / Arduino library** with an optional thin C++
wrapper. Full protocol notes are in [docs/PROTOCOL.md](docs/PROTOCOL.md) and the ESP32
wiring in [docs/HARDWARE.md](docs/HARDWARE.md).
## Layout
```
src/ sppro.h / sppro.c portable core: CRC, frame build/parse,
src/ sppro.h / sppro.c portable C core: CRC, frame build/parse,
md5.h / md5.c register map + scaling, MD5 login, session layer
host/ main.c Linux serial-console dashboard (termios)
esp32/ sppro_esp32.ino ESP32 (Arduino) example, reuses the core unchanged
tests/ test_sppro.c host known-answer tests (no hardware)
Sppro.h optional header-only C++/Arduino wrapper (any Stream)
examples/ Esp32SerialMonitor/ ESP32 example (.ino + platformio.ini)
docs/ PROTOCOL.md wire protocol, CRC, login, register map + scaling
HARDWARE.md RS-232 pinout and MAX3232 -> ESP32 wiring
extras/ host/main.c Linux serial-console dashboard (termios)
tests/test_sppro.c host known-answer tests (no hardware)
Makefile host build (test / console)
library.json, library.properties, keywords.txt PlatformIO + Arduino manifests
```
The core (`src/`) is C99, **no dynamic allocation and no I/O** — it builds/parses byte
buffers and converts raw words to units. I/O is supplied by the caller through a small
`sppro_transport_t` read/write callback, so the same code runs on a host or an MCU.
The core (`src/sppro.*`) is C99, **no dynamic allocation and no I/O** — it builds/parses
byte buffers and converts raw words to units. I/O is supplied by the caller through a
small `sppro_transport_t` read/write callback, so the same code runs on a host, an MCU,
or anything with a serial port. Only `src/` and `examples/` are part of the published
library; `extras/` is development tooling (Arduino ignores it).
## Build & test
## Use on Arduino / ESP32 (PlatformIO or Arduino IDE)
Add the library (`lib_deps = SPPro` in PlatformIO once published, or *Sketch → Include
Library* in the Arduino IDE), then use the `Sppro` wrapper, which works over **any**
Arduino `Stream` (HardwareSerial, SoftwareSerial, WiFiClient, …):
```cpp
#include <Sppro.h>
Sppro sp(Serial2); // wraps any Stream
void setup() {
Serial2.begin(57600, SERIAL_8N1, 16, 17); // via a MAX3232 level shifter
sp.login(""); // "" = no serial password
sp.readScales();
}
void loop() {
Serial.println(sp.value("BatteryVolts")); // scaled value, or NAN on error
delay(5000);
}
```
A complete ESP32 example is in
[`examples/Esp32SerialMonitor/`](examples/Esp32SerialMonitor); build it standalone with
`cd examples/Esp32SerialMonitor && pio run` (it links the parent library via
`lib_deps = symlink://../..`). See [docs/HARDWARE.md](docs/HARDWARE.md) — the SP PRO port
is RS-232 (±12 V) and **needs a MAX3232 level shifter**; never wire it straight to a GPIO.
## Build & test on a host (no hardware)
```sh
make test # build and run the host unit tests (no hardware needed)
cd extras
make test # build and run the host unit tests
make host # build ./build/sppro-console
```
## Run against a real SP PRO
Wire it up per [docs/HARDWARE.md](docs/HARDWARE.md) (RS-232, **needs a MAX3232 level
shifter**, 57600 8N1), then:
Then, with a real SP PRO + USB-RS232 adapter:
```sh
./build/sppro-console /dev/ttyUSB0 [serial_password] [interval_seconds]
@@ -52,7 +83,7 @@ voltage, state of charge, load/solar/generator power, and energy totals. The ser
password is set on the inverter (front panel → Settings → Communications); pass `""` if
none is configured, or set `SPPRO_PASSWORD`.
## Using the core in another project
## Use the pure C core (non-Arduino: ESP-IDF, STM32, Linux, Zephyr …)
```c
#include "sppro.h"
@@ -68,15 +99,21 @@ sppro_session_read(&t, sppro_reg_by_name("BattSocPercent"), &scales, &soc);
sppro_session_read(&t, sppro_reg_by_name("BatteryVolts"), &scales, &vbat);
```
For environments with their own protocol loop, the pure helpers (`sppro_build_query`,
`extras/host/main.c` is a full worked example of the two callbacks (termios). For
environments with their own protocol loop, the pure helpers (`sppro_build_query`,
`sppro_parse_query_response`, `sppro_decode`, `sppro_crc16`, `sppro_login_response`) can
be used without the session/transport layer.
## Status & caveats
- Verified by host unit tests: CRC against selpi's documented `0xa000` frame, MD5
known-answers, frame round-trips, and the scaling formulas. **Not yet verified against
physical hardware** — confirm decoded values against the inverter's own display.
- Verified by host unit tests (`cd extras && make test`): CRC against selpi's documented
`0xa000` frame, MD5 known-answers, frame round-trips, and the scaling formulas. The
ESP32 example + C++ wrapper are verified to compile/link via PlatformIO. **Not yet
verified against physical hardware** — confirm decoded values against the inverter's own
display.
- On 8-bit AVR, `double` is 32-bit float, so very large `Wh` accumulators lose some
precision (fine for display). Use the 32-bit-int register types directly if you need
exact counts.
- Read-only monitoring is the focus. Write framing (`sppro_build_write` /
`sppro_session_write`) exists because login needs it, but no inverter-setting writes
are wired into the demos.