feat(v2.0): Auto-discovery, correzione bug critici, e documentazione completa
Build Docker Image for Raspberry Pi / build-and-push (push) Failing after 4m42s

🆕 Funzionalità Auto-Discovery
- Aggiunto metodo AutoDiscoverBufferSizes() per rilevamento automatico QPIGS/QPIRI/QMOD/QPIWS
- Supporto variabili d'ambiente (INVERTER_DEVICE, MQTT_SERVER, etc.)
- Caching persistente buffer sizes in /cache/inverter.conf.cache
- Flag -a/--auto-discover per modalità auto-detection

🐛 Bug Fixes Critici
- **Parsing interi**: Aggiunta attemptAddSettingInt() con stoi() invece di stof()
  - Fix: stof('98') = 98.0f → 97 (int), ora stoi('98') = 98 direttamente
  - Applicato a: qpiri, qpiws, qmod, qpigs
- **Thread sync**: Aggiunto ups_qpiws_changed a main loop e condizione exit poll()
  - Fix: loop principale controllava solo 3 flag su 4, causava hang
  - Fix: thread poll() non usciva in runOnce perché mancava controllo QPIWS
- **Config accuracy**: Corretti buffer sizes (qpiri: 98→103, qpiws: 36→40)
  - Rimosso sources/inverter-cli/inverter.conf che sovrascriveva config globale
  - Validato con test: inverter_poller -1 completa in 6s con JSON completo

📚 Documentazione Completa
- Creato documentation/CODE_ARCHITECTURE.md (38KB)
  - Mappa logica variabili globali
  - Flusso esecuzione main() con diagrammi ASCII
  - Sequence diagram classe cInverter (poll, query, auto-discovery)
  - Thread synchronization diagrams
  - MQTT integration bash scripts flow
  - Mappa concettuale 5-layer system architecture
  - Error handling e performance optimizations
- Organizzati file .md in documentation/ (AUTO_DISCOVERY, IMPLEMENTATION, QUICKSTART, DEBUG)
- Aggiornato README.md con sezione v2.0 e indice documentazione
- Aggiornato .github/copilot-instructions.md con novità v2.0

🔧 Miglioramenti Build & CI/CD
- Gitea Actions per build multi-arch (arm/v6, arm/v7, arm64, amd64, 386)
- Configurazione VS Code completa (tasks, launch, debug GDB)
- Script test-autodiscovery.sh e test-device.sh

 Testing Validato
- inverter_poller -1 completa in 6 secondi
- Output JSON completo con tutte le metriche
- Exit pulito senza timeout (exit code 0)
- Tutte le 4 query QMOD/QPIGS/QPIRI/QPIWS funzionanti
This commit is contained in:
Pi Developer
2026-01-25 15:00:48 +01:00
parent ac2642639f
commit 6af9fcad7e
30 changed files with 4503 additions and 139 deletions
+17 -1
View File
@@ -1,8 +1,24 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
PROJECT("inverter_poller")
set (CMAKE_CXX_FLAGS "-O2 --std=c++0x ${CMAKE_CXX_FLAGS}")
# Set default build type to Release if not specified
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build (Debug or Release)" FORCE)
endif()
# Compiler flags for different build types
set(CMAKE_CXX_FLAGS "--std=c++0x ${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG")
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DDEBUG -Wall -Wextra")
# Print build type
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
file(GLOB SOURCES *.cpp)
ADD_EXECUTABLE(inverter_poller ${SOURCES})
target_link_libraries(inverter_poller -lpthread)
# Set output directory for binary
set_target_properties(inverter_poller PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin"
)
-25
View File
@@ -1,25 +0,0 @@
# Basic configuration options for the actual inverter polling process...
# The device to read from...
# Use: /dev/ttyS0 if you have a serial device,
# /dev/ttyUSB0 if a USB<>Serial,
# /dev/hidraw0 if you're connecting via the USB port on the inverter.
device=/dev/ttyUSB0
# How many times per hour is the program going to run...
# This is used to calculate the PV & Load Watt Hours between runs...
# If unsure, leave as default - it will run every minute...
# (120 = every 30 seconds)...
run_interval=120
# This allows you to modify the amperage in case the inverter is giving an incorrect
# reading compared to measurement tools. Normally this will remain '1'
amperage_factor=1.0
# This allos you to modify the wattage in case the inverter is giving an incorrect
# reading compared to measurement tools. Normally this will remain '1'
watt_factor=1.01
qpiri=97
+236 -21
View File
@@ -1,6 +1,7 @@
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <unistd.h>
#include "inverter.h"
#include "tools.h"
@@ -15,10 +16,12 @@ cInverter::cInverter(std::string devicename, int qpiri, int qpiws, int qmod, int
status2[0] = 0;
warnings[0] = 0;
mode = 0;
qpiri = qpiri;
qpiws = qpiws;
qmod = qmod;
qpigs = qpigs;
buf_qpiri = qpiri;
buf_qpiws = qpiws;
buf_qmod = qmod;
buf_qpigs = qpigs;
lprintf("INVERTER: Initialized with buffer sizes - QPIRI:%d QPIWS:%d QMOD:%d QPIGS:%d",
buf_qpiri, buf_qpiws, buf_qmod, buf_qpigs);
}
string *cInverter::GetQpigsStatus() {
@@ -89,6 +92,7 @@ bool cInverter::query(const char *cmd, int replysize) {
tcgetattr(fd, &settings);
cfsetospeed(&settings, baud); // baud rate
cfsetispeed(&settings, baud); // input baud rate
settings.c_cflag &= ~PARENB; // no parity
settings.c_cflag &= ~CSTOPB; // 1 stop bit
settings.c_cflag &= ~CSIZE;
@@ -97,7 +101,15 @@ bool cInverter::query(const char *cmd, int replysize) {
settings.c_oflag &= ~OPOST; // raw output
tcsetattr(fd, TCSANOW, &settings); // apply the settings
tcflush(fd, TCOFLUSH);
// CRITICAL: Flush both input and output buffers to clear any residual data
tcflush(fd, TCIOFLUSH);
// Wait a bit for the device to settle after flush
usleep(100000); // 100ms delay
// Clear internal buffer
memset(buf, 0, sizeof(buf));
// ---------------------------------------------------------------
@@ -112,7 +124,18 @@ bool cInverter::query(const char *cmd, int replysize) {
buf[n++] = 0x0d;
//send a command
write(fd, &buf, n);
int written = write(fd, &buf, n);
if (written != n) {
lprintf("INVERTER: %s write failed (wrote %d of %d bytes)", cmd, written, n);
close(fd);
return false;
}
// Flush output to ensure command is sent
tcdrain(fd);
// Clear buffer again before reading
memset(buf, 0, sizeof(buf));
time(&started);
do {
@@ -122,25 +145,49 @@ bool cInverter::query(const char *cmd, int replysize) {
lprintf("INVERTER: %s read timeout", cmd);
break;
} else {
usleep(10);
usleep(10000); // 10ms
continue;
}
}
if (n > 0) {
i += n;
// Check if we've received the terminator
if (i > 0 && buf[i-1] == 0x0d) {
lprintf("INVERTER: %s received terminator at byte %d", cmd, i);
break;
}
}
i += n;
} while (i<replysize);
} while (i<replysize && (time(NULL) - started < 3));
close(fd);
if (i==replysize) {
if (i > 0) {
lprintf("INVERTER: %s reply size (%d bytes)", cmd, i);
lprintf("INVERTER: %s reply size (%d bytes, expected %d)", cmd, i, replysize);
if (buf[0]!='(' || buf[replysize-1]!=0x0d) {
lprintf("INVERTER: %s: incorrect start/stop bytes. Buffer: %s", cmd, buf);
// Dump raw buffer for debugging
if (debugFlag) {
lprintf("INVERTER: Raw buffer hex dump (first 50 bytes):");
for(int x = 0; x < (i < 50 ? i : 50); x++) {
fprintf(stderr, "%02X ", buf[x]);
if ((x+1) % 16 == 0) fprintf(stderr, "\n");
}
fprintf(stderr, "\n");
}
if (buf[0]!='(' ) {
lprintf("INVERTER: %s: incorrect start byte (got 0x%02X, expected '('). Buffer: %s", cmd, buf[0], buf);
return false;
}
if (!(CheckCRC(buf, replysize))) {
lprintf("INVERTER: %s: CRC Failed! Reply size: %d Buffer: %s", cmd, replysize, buf);
if (buf[i-1]!=0x0d) {
lprintf("INVERTER: %s: incorrect stop byte (got 0x%02X at pos %d, expected CR). Buffer: %s", cmd, buf[i-1], i-1, buf);
return false;
}
if (!(CheckCRC(buf, i))) {
lprintf("INVERTER: %s: CRC Failed! Reply size: %d Buffer: %s", cmd, i, buf);
return false;
}
@@ -148,6 +195,13 @@ bool cInverter::query(const char *cmd, int replysize) {
lprintf("INVERTER: %s: %d bytes read: %s", cmd, i, buf);
lprintf("INVERTER: %s query finished", cmd);
// If expected size doesn't match actual size, log it
if (i != replysize) {
lprintf("INVERTER: WARNING - %s actual size (%d) differs from configured size (%d)", cmd, i, replysize);
lprintf("INVERTER: SUGGESTION - Update inverter.conf to set the buffer size to %d", i);
}
return true;
} else {
lprintf("INVERTER: %s reply too short (%d bytes)", cmd, i);
@@ -157,49 +211,68 @@ bool cInverter::query(const char *cmd, int replysize) {
void cInverter::poll() {
int n,j;
extern const int qpiri, qpiws, qmod, qpigs;
fprintf(stderr, "[POLL] Thread started, runOnce=%s\n", runOnce ? "true" : "false");
while (true) {
// Reading mode
if (!ups_qmod_changed) {
if (query("QMOD", qmod)) {
fprintf(stderr, "[POLL] Reading QMOD...\n");
if (query("QMOD", buf_qmod)) {
SetMode(buf[1]);
ups_qmod_changed = true;
fprintf(stderr, "[POLL] QMOD completed\n");
}
}
// reading status (QPIGS)
if (!ups_qpigs_changed) {
if (query("QPIGS", qpigs)) {
fprintf(stderr, "[POLL] Reading QPIGS...\n");
if (query("QPIGS", buf_qpigs)) {
m.lock();
strcpy(status1, (const char*)buf+1);
m.unlock();
ups_qpigs_changed = true;
fprintf(stderr, "[POLL] QPIGS completed\n");
}
}
// Reading QPIRI status
if (!ups_qpiri_changed) {
if (query("QPIRI", qpiri)) {
fprintf(stderr, "[POLL] Reading QPIRI...\n");
if (query("QPIRI", buf_qpiri)) {
m.lock();
strcpy(status2, (const char*)buf+1);
m.unlock();
ups_qpiri_changed = true;
fprintf(stderr, "[POLL] QPIRI completed\n");
}
}
// Get any device warnings...
if (!ups_qpiws_changed) {
if (query("QPIWS", qpiws)) {
fprintf(stderr, "[POLL] Reading QPIWS...\n");
if (query("QPIWS", buf_qpiws)) {
m.lock();
strcpy(warnings, (const char*)buf+1);
m.unlock();
ups_qpiws_changed = true;
fprintf(stderr, "[POLL] QPIWS completed\n");
}
}
sleep(5);
// If runOnce mode and all data collected, exit the thread
if (runOnce && ups_qmod_changed && ups_qpigs_changed && ups_qpiri_changed && ups_qpiws_changed) {
fprintf(stderr, "[POLL] All data collected, exiting (run-once mode)\n");
return;
}
fprintf(stderr, "[POLL] Flags: QMOD=%d QPIGS=%d QPIRI=%d QPIWS=%d, sleeping...\n",
ups_qmod_changed.load(), ups_qpigs_changed.load(),
ups_qpiri_changed.load(), ups_qpiws_changed.load());
sleep(2);
}
}
@@ -255,3 +328,145 @@ bool cInverter::CheckCRC(unsigned char *data, int len) {
uint16_t crc = cal_crc_half(data, len-3);
return data[len-3]==(crc>>8) && data[len-2]==(crc&0xff);
}
// Auto-discover the correct buffer size for a command by reading until CR
int cInverter::query_auto(const char *cmd, int max_size) {
time_t started;
int fd;
int i=0, n;
unsigned char temp_buf[1024];
memset(temp_buf, 0, sizeof(temp_buf));
fd = open(this->device.data(), O_RDWR | O_NONBLOCK);
if (fd == -1) {
lprintf("INVERTER: Unable to open device file for auto-discovery");
return -1;
}
// Configure serial port
speed_t baud = B2400;
struct termios settings;
tcgetattr(fd, &settings);
cfsetospeed(&settings, baud);
cfsetispeed(&settings, baud);
settings.c_cflag &= ~PARENB;
settings.c_cflag &= ~CSTOPB;
settings.c_cflag &= ~CSIZE;
settings.c_cflag |= CS8 | CLOCAL;
settings.c_oflag &= ~OPOST;
tcsetattr(fd, TCSANOW, &settings);
// Flush all buffers
tcflush(fd, TCIOFLUSH);
usleep(200000); // 200ms delay to ensure clean state
// Generate and send command with CRC
uint16_t crc = cal_crc_half((uint8_t*)cmd, strlen(cmd));
n = strlen(cmd);
memcpy(&temp_buf, cmd, n);
temp_buf[n++] = crc >> 8;
temp_buf[n++] = crc & 0xff;
temp_buf[n++] = 0x0d;
write(fd, &temp_buf, n);
tcdrain(fd);
// Clear buffer for reading
memset(temp_buf, 0, sizeof(temp_buf));
time(&started);
// Read until we find CR (0x0d) or timeout
while (i < max_size && (time(NULL) - started < 5)) {
n = read(fd, temp_buf+i, 1); // Read one byte at a time
if (n > 0) {
i += n;
// Found the terminator
if (temp_buf[i-1] == 0x0d) {
lprintf("INVERTER: Auto-discovery for %s: found CR at byte %d", cmd, i);
break;
}
} else {
usleep(10000); // 10ms between reads
}
}
close(fd);
// Validate the response
if (i > 0 && temp_buf[0] == '(' && temp_buf[i-1] == 0x0d) {
lprintf("INVERTER: Auto-discovery for %s successful: %d bytes", cmd, i);
return i;
} else {
lprintf("INVERTER: Auto-discovery for %s failed (read %d bytes)", cmd, i);
// Dump what we received for debugging
if (i > 0) {
lprintf("INVERTER: Received data (hex):");
for(int x = 0; x < i; x++) {
fprintf(stderr, "%02X ", temp_buf[x]);
}
fprintf(stderr, "\n");
lprintf("INVERTER: Received data (ascii):");
for(int x = 0; x < i; x++) {
fprintf(stderr, "%c", (temp_buf[x] >= 32 && temp_buf[x] < 127) ? temp_buf[x] : '.');
}
fprintf(stderr, "\n");
}
return -1;
}
}
// Auto-discover buffer sizes for all commands
void cInverter::AutoDiscoverBufferSizes() {
printf("\n=== AUTO-DISCOVERY MODE ===\n");
printf("Testing inverter to find correct buffer sizes...\n\n");
int qmod_size = query_auto("QMOD", 20);
if (qmod_size > 0) {
printf("✓ QMOD buffer size: %d\n", qmod_size);
} else {
printf("✗ QMOD auto-discovery failed\n");
}
sleep(1);
int qpigs_size = query_auto("QPIGS", 150);
if (qpigs_size > 0) {
printf("✓ QPIGS buffer size: %d\n", qpigs_size);
} else {
printf("✗ QPIGS auto-discovery failed\n");
}
sleep(1);
int qpiri_size = query_auto("QPIRI", 150);
if (qpiri_size > 0) {
printf("✓ QPIRI buffer size: %d\n", qpiri_size);
} else {
printf("✗ QPIRI auto-discovery failed\n");
}
sleep(1);
int qpiws_size = query_auto("QPIWS", 100);
if (qpiws_size > 0) {
printf("✓ QPIWS buffer size: %d\n", qpiws_size);
} else {
printf("✗ QPIWS auto-discovery failed\n");
}
printf("\n=== SUGGESTED CONFIGURATION ===\n");
printf("Update your /etc/inverter/inverter.conf with these values:\n\n");
if (qmod_size > 0) printf("qmod=%d\n", qmod_size);
if (qpigs_size > 0) printf("qpigs=%d\n", qpigs_size);
if (qpiri_size > 0) printf("qpiri=%d\n", qpiri_size);
if (qpiws_size > 0) printf("qpiws=%d\n", qpiws_size);
printf("\n");
// Output in parsable format for scripts
printf("DISCOVERY_QMOD=%d\n", qmod_size > 0 ? qmod_size : 5);
printf("DISCOVERY_QPIGS=%d\n", qpigs_size > 0 ? qpigs_size : 110);
printf("DISCOVERY_QPIRI=%d\n", qpiri_size > 0 ? qpiri_size : 98);
printf("DISCOVERY_QPIWS=%d\n", qpiws_size > 0 ? qpiws_size : 36);
printf("DISCOVERY_SUCCESS=%s\n", (qmod_size > 0 && qpigs_size > 0 && qpiri_size > 0 && qpiws_size > 0) ? "true" : "false");
}
+9
View File
@@ -1,6 +1,7 @@
#ifndef ___INVERTER_H
#define ___INVERTER_H
#include <string>
#include <thread>
#include <mutex>
@@ -16,10 +17,17 @@ class cInverter {
std::string device;
std::mutex m;
// Buffer sizes for different commands
int buf_qpiri;
int buf_qpiws;
int buf_qmod;
int buf_qpigs;
void SetMode(char newmode);
bool CheckCRC(unsigned char *buff, int len);
bool query(const char *cmd, int replysize);
int query_auto(const char *cmd, int max_size);
uint16_t cal_crc_half(uint8_t *pin, uint8_t len);
public:
@@ -36,6 +44,7 @@ class cInverter {
int GetMode();
void ExecuteCmd(const std::string cmd);
void AutoDiscoverBufferSizes();
};
#endif // ___INVERTER_H
+33 -9
View File
@@ -65,6 +65,15 @@ void attemptAddSetting(float *addTo, string addFrom) {
}
}
void attemptAddSettingInt(int *addTo, string addFrom) {
try {
*addTo = stoi(addFrom);
} catch (exception e) {
cout << e.what() << '\n';
cout << "There's probably a string in the settings file where an int should be.\n";
}
}
void getSettingsFile(string filename) {
try {
@@ -89,14 +98,22 @@ void getSettingsFile(string filename) {
attemptAddSetting(&ampfactor, linepart2);
else if(linepart1 == "watt_factor")
attemptAddSetting(&wattfactor, linepart2);
else if(linepart1 == "qpiri")
attemptAddSetting(&qpiri, linepart2);
else if(linepart1 == "qpiws")
attemptAddSetting(&qpiws, linepart2);
else if(linepart1 == "qmod")
attemptAddSetting(&qmod, linepart2);
else if(linepart1 == "qpigs")
attemptAddSetting(&qpigs, linepart2);
else if(linepart1 == "qpiri") {
attemptAddSettingInt(&qpiri, linepart2);
if(debugFlag) printf("Parsed qpiri=%d from '%s'\n", qpiri, linepart2.c_str());
}
else if(linepart1 == "qpiws") {
attemptAddSettingInt(&qpiws, linepart2);
if(debugFlag) printf("Parsed qpiws=%d from '%s'\n", qpiws, linepart2.c_str());
}
else if(linepart1 == "qmod") {
attemptAddSettingInt(&qmod, linepart2);
if(debugFlag) printf("Parsed qmod=%d from '%s'\n", qmod, linepart2.c_str());
}
else if(linepart1 == "qpigs") {
attemptAddSettingInt(&qpigs, linepart2);
if(debugFlag) printf("Parsed qpigs=%d from '%s'\n", qpigs, linepart2.c_str());
}
else
continue;
}
@@ -180,6 +197,12 @@ int main(int argc, char* argv[]) {
bool ups_status_changed(false);
ups = new cInverter(devicename,qpiri,qpiws,qmod,qpigs);
// Auto-discovery mode to find correct buffer sizes
if(cmdArgs.cmdOptionExists("-a") || cmdArgs.cmdOptionExists("--auto-discover")) {
ups->AutoDiscoverBufferSizes();
exit(0);
}
// Logic to send 'raw commands' to the inverter..
if (!rawcmd.empty()) {
ups->ExecuteCmd(rawcmd);
@@ -200,11 +223,12 @@ int main(int argc, char* argv[]) {
ups_status_changed = false;
}
if (ups_qmod_changed && ups_qpiri_changed && ups_qpigs_changed) {
if (ups_qmod_changed && ups_qpiri_changed && ups_qpigs_changed && ups_qpiws_changed) {
ups_qmod_changed = false;
ups_qpiri_changed = false;
ups_qpigs_changed = false;
ups_qpiws_changed = false;
int mode = ups->GetMode();
string *reply1 = ups->GetQpigsStatus();
+1
View File
@@ -5,6 +5,7 @@
#include "inverter.h"
extern bool debugFlag;
extern bool runOnce;
extern atomic_bool ups_data_changed;
extern atomic_bool ups_status_changed;
+133
View File
@@ -0,0 +1,133 @@
#!/bin/bash
# Script per testare comunicazione diretta con inverter
# Usa questo per verificare se il device risponde correttamente
DEVICE="${1:-/dev/ttyUSB0}"
echo "=== Test Comunicazione Inverter su $DEVICE ==="
echo ""
# Verifica device esiste
if [ ! -e "$DEVICE" ]; then
echo "ERROR: Device $DEVICE non trovato!"
exit 1
fi
# Verifica permessi
if [ ! -r "$DEVICE" ] || [ ! -w "$DEVICE" ]; then
echo "ERROR: Permessi insufficienti su $DEVICE"
echo "Esegui: sudo chmod 666 $DEVICE"
exit 1
fi
echo "1. Configurazione device seriale..."
stty -F $DEVICE 2400 cs8 -cstopb -parenb -echo raw
echo "2. Test con cat (premi Ctrl+C dopo 5 secondi)..."
echo " Questo mostrerà eventuali dati inviati dall'inverter..."
timeout 5 cat $DEVICE | od -A x -t x1z -v | head -20
echo ""
echo "3. Invio comando raw QMOD all'inverter..."
# Crea script Python per inviare comando con CRC corretto
python3 << 'PYTHON_EOF'
import sys
import serial
import time
def calc_crc(data):
"""Calcola CRC secondo protocollo Voltronic"""
crc_ta = [
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef
]
crc = 0
for byte in data:
da = ((crc >> 8) >> 4)
crc = (crc << 4) & 0xFFFF
crc ^= crc_ta[da ^ (byte >> 4)]
da = ((crc >> 8) >> 4)
crc = (crc << 4) & 0xFFFF
crc ^= crc_ta[da ^ (byte & 0x0f)]
crc_high = (crc >> 8) & 0xFF
crc_low = crc & 0xFF
# Adjust for special bytes
if crc_low in [0x28, 0x0d, 0x0a]:
crc_low += 1
if crc_high in [0x28, 0x0d, 0x0a]:
crc_high += 1
return bytes([crc_high, crc_low])
try:
ser = serial.Serial(
port=sys.argv[1] if len(sys.argv) > 1 else '/dev/ttyUSB0',
baudrate=2400,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=3
)
# Flush buffers
ser.reset_input_buffer()
ser.reset_output_buffer()
time.sleep(0.2)
# Test comandi
commands = ['QMOD', 'QPIGS', 'QPIRI', 'QPIWS']
for cmd in commands:
print(f"\n--- Testing {cmd} ---")
# Prepare command
cmd_bytes = cmd.encode('ascii')
crc = calc_crc(cmd_bytes)
full_cmd = cmd_bytes + crc + b'\r'
print(f"Sending: {full_cmd.hex()}")
# Send
ser.write(full_cmd)
ser.flush()
time.sleep(0.3)
# Read response
response = ser.read(200)
if len(response) > 0:
print(f"Received {len(response)} bytes")
print(f"Hex: {response.hex()}")
try:
print(f"ASCII: {response.decode('ascii', errors='replace')}")
except:
print(f"ASCII: {response}")
if response[0:1] == b'(':
print(f"[OK] Valid start byte")
if response[-1:] == b'\r':
print(f"[OK] Valid end byte")
print(f">>> Buffer size for {cmd}: {len(response)}")
else:
print(f"[ERROR] Invalid end byte: {response[-1]:02x}")
else:
print(f"[ERROR] Invalid start byte: {response[0]:02x}")
else:
print("✗ No response received")
time.sleep(1)
ser.close()
except Exception as e:
print(f"Error: {e}")
sys.exit(1)
PYTHON_EOF
echo ""
echo "=== Test completato ===="
+2 -1
View File
@@ -54,7 +54,8 @@ int print_help() {
printf(" -r <raw-command> TX 'raw' command to the inverter\n");
printf(" -h | --help This Help Message\n");
printf(" -1 | --run-once Runs one iteration on the inverter, and then exits\n");
printf(" -d Additional debugging\n\n");
printf(" -d Additional debugging\n");
printf(" -a | --auto-discover Auto-detect correct buffer sizes for your inverter\n\n");
printf("RAW COMMAND EXAMPLES (see protocol manual for complete list):\n");
printf("Set output source priority POP00 (Utility first)\n");