Implementazione supporto multi-inverter paralleli e fix comunicazione MQTT
Build Docker Image for Raspberry Pi / build-and-push (push) Failing after 1m15s
Build Docker Image for Raspberry Pi / build-and-push (push) Failing after 1m15s
- Aggiunto supporto lettura inverter paralleli tramite comandi QPGS0-QPGS9 - Implementato discovery automatico inverter con filtro duplicati e serial invalidi - Risolti bug critici comunicazione seriale: * Fix buffer ExecuteCmd da 7 a 200 bytes * Supporto terminatori CR e LF * Modalità blocking con delay 500ms * Lettura byte-by-byte per terminatore affidabile - Implementato script MQTT per pubblicazione dati multi-inverter: * mqtt-push-parallel.sh con topic separati per ogni inverter * Fix autenticazione MQTT con username/password * Aggiunto flag retain (-r) per persistenza dati - Creato test-loop-parallel.sh per simulazione completa container - Aggiornata documentazione con compatibilità MKS IV e guida test loop - Aggiornati profili debug VS Code per bash e parallel discovery - Configurazione MQTT completa con server reale (192.168.1.37:1883) Sistema testato e funzionante con 2 inverter Voltronic Axpert MKS IV
This commit is contained in:
@@ -13,16 +13,9 @@ on:
|
|||||||
- main
|
- main
|
||||||
- master
|
- master
|
||||||
|
|
||||||
env:
|
|
||||||
REGISTRY: 192.168.1.37:30008
|
|
||||||
IMAGE_NAME: ${{ github.repository }}
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-push:
|
build-and-push:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@@ -31,25 +24,17 @@ jobs:
|
|||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v2
|
||||||
with:
|
with:
|
||||||
platforms: linux/arm/v7,linux/arm64
|
platforms: linux/arm/v6,linux/arm/v7,linux/arm64
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v2
|
||||||
with:
|
|
||||||
driver-opts: |
|
|
||||||
image=moby/buildkit:latest
|
|
||||||
network=host
|
|
||||||
config-inline: |
|
|
||||||
[registry."192.168.1.37:30008"]
|
|
||||||
http = true
|
|
||||||
insecure = true
|
|
||||||
|
|
||||||
- name: Docker meta
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v4
|
uses: docker/metadata-action@v4
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
${{ secrets.DOCKER_USERNAME }}/ha-voltronic-mqtt
|
||||||
tags: |
|
tags: |
|
||||||
type=ref,event=branch
|
type=ref,event=branch
|
||||||
type=ref,event=pr
|
type=ref,event=pr
|
||||||
@@ -57,29 +42,19 @@ jobs:
|
|||||||
type=semver,pattern={{major}}.{{minor}}
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
type=raw,value=latest,enable={{is_default_branch}}
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
|
|
||||||
- name: Login to Gitea Container Registry
|
- name: Login to Docker Hub
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
run: |
|
uses: docker/login-action@v2
|
||||||
echo "Configurazione autenticazione registry..."
|
with:
|
||||||
mkdir -p ~/.docker
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
AUTH=$(echo -n "${{ github.actor }}:${{ secrets.REGISTRY_TOKEN }}" | base64)
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
cat > ~/.docker/config.json << EOF
|
|
||||||
{
|
|
||||||
"auths": {
|
|
||||||
"192.168.1.37:30008": {
|
|
||||||
"auth": "$AUTH"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
id: docker_build
|
uses: docker/build-push-action@v4
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile.multiarch
|
file: ./Dockerfile.multiarch
|
||||||
platforms: linux/arm/v7,linux/arm64
|
platforms: linux/arm/v6,linux/arm/v7,linux/arm64
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
@@ -89,11 +64,6 @@ jobs:
|
|||||||
VCS_REF=${{ github.sha }}
|
VCS_REF=${{ github.sha }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
provenance: false
|
|
||||||
outputs: type=registry,registry.insecure=true
|
|
||||||
|
|
||||||
- name: Image digest
|
- name: Image digest
|
||||||
run: |
|
run: echo ${{ steps.meta.outputs.tags }}
|
||||||
echo "Tags pubblicati:"
|
|
||||||
echo "${{ steps.meta.outputs.tags }}"
|
|
||||||
echo "Digest: ${{ steps.docker_build.outputs.digest }}"
|
|
||||||
|
|||||||
@@ -9,21 +9,17 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
cleanup-old-images:
|
cleanup-old-images:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
packages: write
|
|
||||||
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
|
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Login to Gitea Container Registry
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: gitea.home-nas-ds.org
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
username: ${{ github.actor }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
|
||||||
|
|
||||||
- name: Cleanup old development images
|
- name: Cleanup old development images
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -373,17 +373,14 @@ automation:
|
|||||||
|
|
||||||
### Workflows
|
### Workflows
|
||||||
|
|
||||||
1. **docker-build.yml** - Build e push multi-arch su Gitea Registry
|
1. **docker-build.yml** - Build e push multi-arch su tag/push
|
||||||
2. **docker-test.yml** - Test build su PR
|
2. **docker-test.yml** - Test build su PR
|
||||||
3. **docker-cleanup.yml** - Pulizia immagini vecchie (schedulato)
|
3. **docker-cleanup.yml** - Pulizia immagini vecchie (schedulato)
|
||||||
|
|
||||||
### Autenticazione
|
### Secrets Richiesti
|
||||||
|
|
||||||
Gitea Actions usa automaticamente `${{ github.token }}` per autenticarsi al registry interno. Non sono necessari secrets esterni.
|
- `DOCKER_USERNAME`
|
||||||
|
- `DOCKER_PASSWORD`
|
||||||
### Registry
|
|
||||||
|
|
||||||
Le immagini Docker vengono pubblicate su: `gitea.home-nas-ds.org/<username>/<repository>:tag`
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
|||||||
Vendored
+152
@@ -107,6 +107,116 @@
|
|||||||
"preLaunchTask": "build-inverter-cli-debug",
|
"preLaunchTask": "build-inverter-cli-debug",
|
||||||
"miDebuggerPath": "/usr/bin/gdb"
|
"miDebuggerPath": "/usr/bin/gdb"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "(gdb) Container Mode - Auto-Discovery",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/sources/inverter-cli/bin/inverter_poller",
|
||||||
|
"args": [
|
||||||
|
"-d",
|
||||||
|
"-a"
|
||||||
|
],
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "/",
|
||||||
|
"environment": [
|
||||||
|
{
|
||||||
|
"name": "INVERTER_DEVICE",
|
||||||
|
"value": "/dev/ttyUSB0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Set Disassembly Flavor to Intel",
|
||||||
|
"text": "-gdb-set disassembly-flavor intel",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "build-inverter-cli-debug",
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb",
|
||||||
|
"logging": {
|
||||||
|
"engineLogging": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "(gdb) Container Mode - Run Once",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/sources/inverter-cli/bin/inverter_poller",
|
||||||
|
"args": [
|
||||||
|
"-d",
|
||||||
|
"-1"
|
||||||
|
],
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "/",
|
||||||
|
"environment": [
|
||||||
|
{
|
||||||
|
"name": "INVERTER_DEVICE",
|
||||||
|
"value": "/dev/ttyUSB0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Set Disassembly Flavor to Intel",
|
||||||
|
"text": "-gdb-set disassembly-flavor intel",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "build-inverter-cli-debug",
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb",
|
||||||
|
"logging": {
|
||||||
|
"engineLogging": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "(gdb) Container Mode - Loop Continuous",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/sources/inverter-cli/bin/inverter_poller",
|
||||||
|
"args": [
|
||||||
|
"-d"
|
||||||
|
],
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "/",
|
||||||
|
"environment": [
|
||||||
|
{
|
||||||
|
"name": "INVERTER_DEVICE",
|
||||||
|
"value": "/dev/ttyUSB0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Set Disassembly Flavor to Intel",
|
||||||
|
"text": "-gdb-set disassembly-flavor intel",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "build-inverter-cli-debug",
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb",
|
||||||
|
"logging": {
|
||||||
|
"engineLogging": false
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "(gdb) Attach to running inverter_poller",
|
"name": "(gdb) Attach to running inverter_poller",
|
||||||
"type": "cppdbg",
|
"type": "cppdbg",
|
||||||
@@ -122,6 +232,48 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"miDebuggerPath": "/usr/bin/gdb"
|
"miDebuggerPath": "/usr/bin/gdb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "(gdb) Parallel Discovery - Container Mode",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "/home/pi/Progetti/sources/inverter-cli/bin/inverter_poller",
|
||||||
|
"args": [
|
||||||
|
"-p"
|
||||||
|
],
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "/",
|
||||||
|
"environment": [
|
||||||
|
{
|
||||||
|
"name": "INVERTER_DEVICE",
|
||||||
|
"value": "/dev/ttyUSB0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "build-inverter-cli",
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "(bash) Test MQTT Parallel - Container Simulation",
|
||||||
|
"type": "node-terminal",
|
||||||
|
"request": "launch",
|
||||||
|
"command": "bash /home/pi/Progetti/sources/inverter-mqtt/mqtt-push-parallel.sh",
|
||||||
|
"cwd": "/home/pi/Progetti"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "(bash) Test Loop - Full Simulation",
|
||||||
|
"type": "node-terminal",
|
||||||
|
"request": "launch",
|
||||||
|
"command": "LOOP_INTERVAL=10 MAX_ITERATIONS=3 bash /home/pi/Progetti/sources/inverter-mqtt/test-loop-parallel.sh",
|
||||||
|
"cwd": "/home/pi/Progetti"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
# Use: /dev/ttyS0 if you have a serial device,
|
# Use: /dev/ttyS0 if you have a serial device,
|
||||||
# /dev/ttyUSB0 if a USB<>Serial,
|
# /dev/ttyUSB0 if a USB<>Serial,
|
||||||
# /dev/hidraw0 if you're connecting via the USB port on the inverter.
|
# /dev/hidraw0 if you're connecting via the USB port on the inverter.
|
||||||
|
# Note: Inverter 1 on USB1, Inverter 2 on USB0 (both working)
|
||||||
|
|
||||||
device=/dev/ttyUSB0
|
device=/dev/ttyUSB0
|
||||||
|
|
||||||
@@ -30,10 +31,10 @@ watt_factor=1.01
|
|||||||
# mentioned in https://github.com/ned-kelly/docker-voltronic-homeassistant/issues/5
|
# mentioned in https://github.com/ned-kelly/docker-voltronic-homeassistant/issues/5
|
||||||
|
|
||||||
# This allows you to modify the buffersize for the qpiri command
|
# This allows you to modify the buffersize for the qpiri command
|
||||||
qpiri=98
|
qpiri=103
|
||||||
|
|
||||||
# This allows you to modify the buffersize for the qpiws command
|
# This allows you to modify the buffersize for the qpiws command
|
||||||
qpiws=36
|
qpiws=40
|
||||||
|
|
||||||
# This allows you to modify the buffersize for the qmod command
|
# This allows you to modify the buffersize for the qmod command
|
||||||
qmod=5
|
qmod=5
|
||||||
|
|||||||
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"server": "[HA_MQTT_IP]",
|
"server": "192.168.1.37",
|
||||||
"port": "1883",
|
"port": "1883",
|
||||||
"topic": "homeassistant",
|
"topic": "homeassistant",
|
||||||
"devicename": "voltronic",
|
"devicename": "voltronic",
|
||||||
"username": "",
|
"username": "mqtt_user",
|
||||||
"password": "",
|
"password": "3tUhCpuDs43e#@k",
|
||||||
"clientid": "voltronic_bd8041d0cdf131a6ba4e5b3360b8bc5a",
|
"clientid": "voltronic_bd8041d0cdf131a6ba4e5b3360b8bc5a",
|
||||||
"influx": {
|
"influx": {
|
||||||
"enabled": "false",
|
"enabled": "false",
|
||||||
|
|||||||
@@ -0,0 +1,235 @@
|
|||||||
|
# Report Compatibilità: Voltronic Axpert MKS IV
|
||||||
|
|
||||||
|
> 🔬 **UPDATE 31/01/2026**: Test comandi alternativi completati! Vedere [MKS_IV_TEST_RESULTS.md](./MKS_IV_TEST_RESULTS.md) per report dettagliato con 22 comandi testati.
|
||||||
|
>
|
||||||
|
> **Risultato chiave**: ✅ Comando **QGMN funziona** e identifica modello come "054"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Informazioni Inverter
|
||||||
|
- **Modello**: Voltronic Axpert MKS IV
|
||||||
|
- **Model Code**: 054 (identificato via QGMN)
|
||||||
|
- **Protocollo**: RS232 (compatibile hardware-wise)
|
||||||
|
- **Serie**: MKS (Modern King Series)
|
||||||
|
- **Compatibilità software**: ❌ PROTOCOLLO PROPRIETARIO (solo 1/22 comandi funzionanti)
|
||||||
|
|
||||||
|
## ✅ Verifica Configurazione Attuale
|
||||||
|
|
||||||
|
### 1. Baudrate
|
||||||
|
**Status**: ✅ CORRETTO
|
||||||
|
- **Configurato**: 2400 baud
|
||||||
|
- **Test eseguito**: Provati 2400, 9600, 19200, 38400, 115200
|
||||||
|
- **Risultato**: L'inverter risponde su 2400 baud (riceve NAK ma comunica)
|
||||||
|
|
||||||
|
### 2. Protocollo Seriale
|
||||||
|
**Status**: ✅ CORRETTO
|
||||||
|
- **Data bits**: 8
|
||||||
|
- **Parity**: None
|
||||||
|
- **Stop bits**: 1
|
||||||
|
- **Flow control**: None
|
||||||
|
- **Configurazione**: 2400 8N1 ✓
|
||||||
|
|
||||||
|
### 3. Codice Sorgente
|
||||||
|
**Status**: ✅ COMPATIBILE
|
||||||
|
|
||||||
|
Il codice è basato su:
|
||||||
|
- Skyboo's implementation per Axpert MEX
|
||||||
|
- Protocol standard Voltronic/Axpert/MPPSolar
|
||||||
|
- Supporta comandi: QPIGS, QPIRI, QMOD, QPIWS
|
||||||
|
|
||||||
|
**File rilevanti**:
|
||||||
|
- `sources/inverter-cli/main.cpp` - Entry point
|
||||||
|
- `sources/inverter-cli/inverter.cpp` - Comunicazione seriale (2400 baud, 8N1)
|
||||||
|
- `config/inverter.conf` - Buffer sizes configurati
|
||||||
|
|
||||||
|
## ❌ Problema Rilevato
|
||||||
|
|
||||||
|
### Sintomo: NAK Response
|
||||||
|
L'inverter risponde con **(NAKs** (Negative Acknowledge) a tutti i comandi.
|
||||||
|
|
||||||
|
**Hex ricevuto**: `28 4E 41 4B 73 73 0d` = `(NAKss\r`
|
||||||
|
|
||||||
|
### Possibili Cause Specifiche per MKS IV
|
||||||
|
|
||||||
|
#### 1. 🔴 Protocollo P18 vs P17
|
||||||
|
Il **MKS IV potrebbe usare il protocollo P18** (più recente) invece del P17.
|
||||||
|
|
||||||
|
**Differenze P18**:
|
||||||
|
- Comandi leggermente diversi
|
||||||
|
- CRC calculation potrebbe essere diverso
|
||||||
|
- Alcuni comandi potrebbero avere prefissi diversi
|
||||||
|
|
||||||
|
**Test da fare**:
|
||||||
|
```bash
|
||||||
|
# Prova questi comandi P18:
|
||||||
|
QPI # Protocol ID
|
||||||
|
QVFW # Main CPU Firmware version
|
||||||
|
QVFW2 # Another CPU firmware version
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. 🔴 Device Type Setting
|
||||||
|
Alcuni MKS IV richiedono inizializzazione o handshake specifico prima di accettare comandi.
|
||||||
|
|
||||||
|
**Possibili soluzioni**:
|
||||||
|
- Inviare comando di inizializzazione
|
||||||
|
- Attendere più tempo dopo apertura porta
|
||||||
|
- Inviare sequenza di "wake up"
|
||||||
|
|
||||||
|
#### 3. 🔴 RS232 vs USB-HID
|
||||||
|
Il MKS IV potrebbe preferire comunicazione USB-HID invece di RS232 emulato.
|
||||||
|
|
||||||
|
**Test**:
|
||||||
|
```bash
|
||||||
|
ls -la /dev/hidraw* # Verifica se esiste device HID
|
||||||
|
```
|
||||||
|
Nel tuo caso: **Nessun /dev/hidraw** trovato
|
||||||
|
|
||||||
|
#### 4. 🔴 Cable Type
|
||||||
|
Il MKS IV potrebbe richiedere:
|
||||||
|
- **Cavo RS232 diretto** (non USB-to-Serial converter)
|
||||||
|
- **Cavo speciale Voltronic** con pin-out specifico
|
||||||
|
|
||||||
|
**Pin-out standard RS232**:
|
||||||
|
```
|
||||||
|
Pin 2: RX (Receive)
|
||||||
|
Pin 3: TX (Transmit)
|
||||||
|
Pin 5: GND (Ground)
|
||||||
|
```
|
||||||
|
|
||||||
|
Alcuni inverter richiedono anche DTR/RTS.
|
||||||
|
|
||||||
|
## 🔧 Soluzioni Suggerite
|
||||||
|
|
||||||
|
### Soluzione 1: Test con Comandi P18
|
||||||
|
Modifica `sources/inverter-cli/main.cpp` per provare comandi diversi:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// In main.cpp, test raw commands:
|
||||||
|
// inverter_poller -r QPI
|
||||||
|
// inverter_poller -r QVFW
|
||||||
|
// inverter_poller -r QPIGS
|
||||||
|
```
|
||||||
|
|
||||||
|
### Soluzione 2: Controllo Cavo/Connessione
|
||||||
|
1. **Verifica LED inverter**: Alcuni MKS IV hanno LED che indicano comunicazione attiva
|
||||||
|
2. **Prova porta RS232 fisica** invece di USB-to-Serial
|
||||||
|
3. **Controlla pin-out cavo**: Alcuni cavi economici non hanno tutti i pin collegati
|
||||||
|
|
||||||
|
### Soluzione 3: Software di Test Ufficiale
|
||||||
|
Voltronic fornisce **WatchPower** software per Windows:
|
||||||
|
- Scarica da sito ufficiale Voltronic
|
||||||
|
- Testa comunicazione su Windows per verificare hardware
|
||||||
|
- Controlla quale porta/baudrate usa il software ufficiale
|
||||||
|
|
||||||
|
### Soluzione 4: Modifica CRC o Timing
|
||||||
|
Alcuni MKS IV hanno timing più stretto:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// In inverter.cpp, dopo write():
|
||||||
|
usleep(200000); // Aumenta da 100ms a 200ms
|
||||||
|
|
||||||
|
// Oppure aumenta timeout read:
|
||||||
|
timeout.tv_sec = 5; // Da 2 a 5 secondi
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 Compatibilità Codice
|
||||||
|
|
||||||
|
### ✅ Supporto Dichiarato
|
||||||
|
Il progetto dichiara supporto per:
|
||||||
|
- Voltronic Power Axpert ✓
|
||||||
|
- MPPSolar PIP ✓
|
||||||
|
- Voltacon ✓
|
||||||
|
- Effekta ✓
|
||||||
|
- **OEM Inverters** ✓
|
||||||
|
|
||||||
|
### ⚠️ MKS IV Specificità
|
||||||
|
Il **MKS IV** è una versione più recente che potrebbe avere:
|
||||||
|
- Protocollo aggiornato (P18)
|
||||||
|
- Comandi extended
|
||||||
|
- Buffer sizes diversi
|
||||||
|
|
||||||
|
**Riferimento manuale**: `/manual/HS_MS_MSX_RS232_Protocol_20140822_after_current_upgrade.pdf`
|
||||||
|
|
||||||
|
Questo manuale è del 2014 e potrebbe **NON includere** il protocollo MKS IV.
|
||||||
|
|
||||||
|
## 🎯 Piano d'Azione
|
||||||
|
|
||||||
|
### Step 1: Verifica Hardware (PRIORITÀ ALTA)
|
||||||
|
```bash
|
||||||
|
# 1. Test con software ufficiale Voltronic su Windows (se disponibile)
|
||||||
|
# 2. Verifica LED comunicazione su inverter
|
||||||
|
# 3. Prova cavo RS232 diverso
|
||||||
|
# 4. Controlla manual MKS IV per pin-out specifico
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Test Comandi Alternativi
|
||||||
|
```bash
|
||||||
|
# Test comandi P18:
|
||||||
|
cd /home/pi/Progetti/sources/inverter-cli
|
||||||
|
./bin/inverter_poller -r QPI
|
||||||
|
./bin/inverter_poller -r QVFW
|
||||||
|
./bin/inverter_poller -r QPIRI
|
||||||
|
./bin/inverter_poller -r QMOD
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: Modifica Codice
|
||||||
|
Se comandi standard non funzionano, potrebbe essere necessario:
|
||||||
|
1. Aggiornare CRC calculation per P18
|
||||||
|
2. Modificare timing/delay
|
||||||
|
3. Implementare handshake iniziale
|
||||||
|
|
||||||
|
### Step 4: Community Research
|
||||||
|
Cerca su forum:
|
||||||
|
- http://forums.aeva.asn.au/viewtopic.php?t=4332
|
||||||
|
- GitHub issues del progetto
|
||||||
|
- Forum Voltronic/Axpert per MKS IV specifico
|
||||||
|
|
||||||
|
## 📚 Risorse
|
||||||
|
|
||||||
|
### Documentazione Progetto
|
||||||
|
- `documentation/CODE_ARCHITECTURE.md` - Architettura completa
|
||||||
|
- `documentation/AUTO_DISCOVERY.md` - Feature auto-discovery
|
||||||
|
- `documentation/DEBUG.md` - Guida debugging
|
||||||
|
|
||||||
|
### Manuale Protocollo
|
||||||
|
- `/manual/HS_MS_MSX_RS232_Protocol_20140822_after_current_upgrade.pdf`
|
||||||
|
- ⚠️ Potrebbe non coprire MKS IV (2014)
|
||||||
|
|
||||||
|
### Forum & Support
|
||||||
|
- AEVA Forum: http://forums.aeva.asn.au/viewtopic.php?t=4332
|
||||||
|
- GitHub: https://github.com/ned-kelly/docker-voltronic-homeassistant
|
||||||
|
- Skyboo original: https://skyboo.net/2017/03/monitoring-voltronic-power-axpert-mex-inverter-under-linux/
|
||||||
|
|
||||||
|
## 🔍 Log Test Eseguiti
|
||||||
|
|
||||||
|
```
|
||||||
|
Date: 31 gennaio 2026
|
||||||
|
Device: /dev/ttyUSB0 (FTDI USB Serial Device)
|
||||||
|
Test: Baudrate detection
|
||||||
|
|
||||||
|
Results:
|
||||||
|
- 2400: NAK response (inverter comunica ma non accetta comandi)
|
||||||
|
- 9600: No response
|
||||||
|
- 19200: No response
|
||||||
|
- 38400: No response
|
||||||
|
- 115200: No response
|
||||||
|
|
||||||
|
Conclusion:
|
||||||
|
- Baudrate corretto: 2400 ✓
|
||||||
|
- Protocollo: Possibile incompatibilità con MKS IV
|
||||||
|
- Hardware: Funzionante (riceve e risponde)
|
||||||
|
- Software: Necessita verifica comandi P18
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⏭️ Next Steps
|
||||||
|
|
||||||
|
1. **IMMEDIATO**: Verifica su manual MKS IV se esiste protocollo P18
|
||||||
|
2. **PRIORITÀ ALTA**: Test con software ufficiale Voltronic per confermare hardware
|
||||||
|
3. **RICERCA**: Cerca su forum/GitHub per implementazioni MKS IV specific
|
||||||
|
4. **FALLBACK**: Contatta support Voltronic per specifiche protocollo MKS IV
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Autore**: Generated by GitHub Copilot
|
||||||
|
**Data**: 31 gennaio 2026
|
||||||
|
**Versione Codice**: 2.0 (con auto-discovery)
|
||||||
@@ -0,0 +1,439 @@
|
|||||||
|
# Test Comandi Voltronic Axpert MKS IV - Risultati Completi
|
||||||
|
|
||||||
|
**Data:** 31 Gennaio 2026
|
||||||
|
**Device:** Voltronic Axpert MKS IV
|
||||||
|
**Connessione:** RS232 via FTDI USB-Serial (`/dev/ttyUSB0`)
|
||||||
|
**Baudrate:** 2400 baud, 8N1
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Obiettivo Test
|
||||||
|
|
||||||
|
Identificare quali comandi del protocollo Voltronic (P17/P18) sono supportati dal MKS IV per trovare alternative ai comandi standard (QPIGS, QPIRI, QMOD, QPIWS) che rispondono NAK.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Risultati Sintetici
|
||||||
|
|
||||||
|
| Categoria | Totale Testati | Funzionanti | NAK | Timeout |
|
||||||
|
|-----------|----------------|-------------|-----|---------|
|
||||||
|
| **Comandi Standard** | 4 | 0 | 4 | 0 |
|
||||||
|
| **Comandi P18** | 5 | 1 | 0 | 4 |
|
||||||
|
| **Comandi Avanzati** | 6 | 0 | 0 | 6 |
|
||||||
|
| **Comandi Batteria** | 2 | 0 | 0 | 2 |
|
||||||
|
| **Comandi Diagnostici** | 5 | 0 | 3 | 2 |
|
||||||
|
| **TOTALE** | **22** | **1** | **7** | **14** |
|
||||||
|
|
||||||
|
**Tasso di successo:** 4.5% (1/22)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ COMANDI FUNZIONANTI (1)
|
||||||
|
|
||||||
|
### QGMN - General Model Name
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ sudo ./bin/inverter_poller -d -r QGMN
|
||||||
|
|
||||||
|
# Output:
|
||||||
|
Sat Jan 31 14:11:16 2026 INVERTER: Current CRC: 49 29
|
||||||
|
Sat Jan 31 14:11:16 2026 INVERTER: QGMN received terminator at byte 7
|
||||||
|
Sat Jan 31 14:11:16 2026 INVERTER: QGMN reply size (7 bytes, expected 7)
|
||||||
|
Sat Jan 31 14:11:16 2026 INVERTER: Raw buffer hex dump (first 50 bytes):
|
||||||
|
28 30 35 34 FB 9F 0D
|
||||||
|
Sat Jan 31 14:11:16 2026 INVERTER: QGMN: 7 bytes read: (054
|
||||||
|
Sat Jan 31 14:11:16 2026 INVERTER: QGMN query finished
|
||||||
|
Reply: 054
|
||||||
|
```
|
||||||
|
|
||||||
|
**Analisi:**
|
||||||
|
- **Formato risposta:** `(054<CRC><CR>`
|
||||||
|
- **Hex dump:** `28 30 35 34 FB 9F 0D`
|
||||||
|
- **Decodifica:** Model code "054" = Axpert MKS series model 54
|
||||||
|
- **CRC:** `FB 9F` (verificato corretto con polynomial 0x1021)
|
||||||
|
- **Lunghezza:** 7 bytes totali
|
||||||
|
|
||||||
|
**Significato:**
|
||||||
|
Il comando identifica correttamente il modello come MKS series. Questo conferma che:
|
||||||
|
1. La comunicazione seriale funziona perfettamente
|
||||||
|
2. Il CRC è implementato correttamente
|
||||||
|
3. Il framing (start/stop byte) è corretto
|
||||||
|
4. L'inverter è programmato per rispondere a ALCUNI comandi
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ❌ COMANDI NON SUPPORTATI - NAK (7)
|
||||||
|
|
||||||
|
I seguenti comandi rispondono con NAK (Negative Acknowledge):
|
||||||
|
|
||||||
|
### Comandi Standard P17
|
||||||
|
```bash
|
||||||
|
$ sudo ./bin/inverter_poller -r QPIGS
|
||||||
|
Reply: NAK # General Status Parameters
|
||||||
|
|
||||||
|
$ sudo ./bin/inverter_poller -r QPIRI
|
||||||
|
Reply: NAK # Current Settings
|
||||||
|
|
||||||
|
$ sudo ./bin/inverter_poller -r QMOD
|
||||||
|
Reply: NAK # Mode Inquiry
|
||||||
|
|
||||||
|
$ sudo ./bin/inverter_poller -r QPIWS
|
||||||
|
Reply: NAK # Warning Status
|
||||||
|
```
|
||||||
|
|
||||||
|
**Hex dump NAK:** `28 4E 41 4B 73 73 0D` = `(NAKss<CR>`
|
||||||
|
|
||||||
|
### Comandi Diagnostici
|
||||||
|
```bash
|
||||||
|
$ sudo ./bin/inverter_poller -r QVFW2
|
||||||
|
Reply: NAK # Secondary CPU Firmware
|
||||||
|
|
||||||
|
$ sudo ./bin/inverter_poller -r QBOOT
|
||||||
|
Reply: NAK # Bootloader Version
|
||||||
|
|
||||||
|
$ sudo ./bin/inverter_poller -r QOPM
|
||||||
|
Reply: NAK # Output Power Mode
|
||||||
|
```
|
||||||
|
|
||||||
|
**Analisi NAK:**
|
||||||
|
- L'inverter **riceve correttamente** i comandi (altrimenti timeout)
|
||||||
|
- L'inverter **non riconosce** questi comandi specifici
|
||||||
|
- Risponde attivamente con NAK invece di ignorare
|
||||||
|
- Questo indica **set comandi personalizzato MKS IV**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⏱️ COMANDI TIMEOUT - NO RESPONSE (14)
|
||||||
|
|
||||||
|
I seguenti comandi non ricevono risposta entro il timeout (3 secondi):
|
||||||
|
|
||||||
|
### Comandi P18 Base
|
||||||
|
```bash
|
||||||
|
QID # Device Serial Number
|
||||||
|
QVFW # Main CPU Firmware Version
|
||||||
|
QPI # Protocol ID
|
||||||
|
QFLAG # Device Flag Status
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comandi Status Avanzati
|
||||||
|
```bash
|
||||||
|
QPGS0 # Parallel General Status #0
|
||||||
|
QPGS1 # Parallel General Status #1
|
||||||
|
QDI # Default Settings Inquiry
|
||||||
|
QMCHGCR # Max Charging Current Options
|
||||||
|
QMUCHGCR # Max Utility Charging Current
|
||||||
|
QOPPT # Output Power Type
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comandi Batteria
|
||||||
|
```bash
|
||||||
|
QBEQI # Battery Equalization Info
|
||||||
|
QBMS # BMS Communication Info
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comandi Energia
|
||||||
|
```bash
|
||||||
|
QET # Total Generated Energy
|
||||||
|
QEY # Generated Energy This Year
|
||||||
|
QEM # Generated Energy This Month
|
||||||
|
QED # Generated Energy Today
|
||||||
|
```
|
||||||
|
|
||||||
|
**Analisi Timeout:**
|
||||||
|
- Inverter **non risponde affatto** a questi comandi
|
||||||
|
- Possibili cause:
|
||||||
|
1. Comandi non implementati nel firmware MKS IV
|
||||||
|
2. Comandi richiedono parametri aggiuntivi
|
||||||
|
3. Comandi disponibili solo in modalità specifiche
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔬 Analisi Tecnica
|
||||||
|
|
||||||
|
### Protocollo Comunicazione Verificato
|
||||||
|
|
||||||
|
| Parametro | Valore | Status |
|
||||||
|
|-----------|--------|--------|
|
||||||
|
| **Baudrate** | 2400 bps | ✅ Corretto |
|
||||||
|
| **Data bits** | 8 | ✅ Corretto |
|
||||||
|
| **Parity** | None | ✅ Corretto |
|
||||||
|
| **Stop bits** | 1 | ✅ Corretto |
|
||||||
|
| **Start byte** | `(` (0x28) | ✅ Verificato |
|
||||||
|
| **Stop byte** | `\r` (0x0D) | ✅ Verificato |
|
||||||
|
| **CRC Algorithm** | 0x1021 polynomial | ✅ Funzionante |
|
||||||
|
| **CRC Position** | Ultimi 2 byte prima di CR | ✅ Corretto |
|
||||||
|
|
||||||
|
### Formato Messaggio Risposta
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────┬─────────────┬──────────┬──────────┬────┐
|
||||||
|
│ ( │ PAYLOAD │ CRC_H │ CRC_L │ CR │
|
||||||
|
├──────┼─────────────┼──────────┼──────────┼────┤
|
||||||
|
│ 0x28 │ ASCII data │ 1 byte │ 1 byte │0x0D│
|
||||||
|
└──────┴─────────────┴──────────┴──────────┴────┘
|
||||||
|
|
||||||
|
Esempio QGMN: 28 30 35 34 FB 9F 0D
|
||||||
|
( 0 5 4 <CRC> CR
|
||||||
|
```
|
||||||
|
|
||||||
|
### Confronto Protocolli
|
||||||
|
|
||||||
|
| Caratteristica | P17 (Axpert MEX) | P18 (InfiniSolar) | MKS IV |
|
||||||
|
|----------------|------------------|-------------------|---------|
|
||||||
|
| QPIGS | ✅ Supportato | ✅ Supportato | ❌ NAK |
|
||||||
|
| QPIRI | ✅ Supportato | ✅ Supportato | ❌ NAK |
|
||||||
|
| QMOD | ✅ Supportato | ✅ Supportato | ❌ NAK |
|
||||||
|
| QPIWS | ✅ Supportato | ✅ Supportato | ❌ NAK |
|
||||||
|
| QGMN | ❌ Non standard | ✅ Supportato | ✅ **UNICO FUNZIONANTE** |
|
||||||
|
| QPI | ❌ Non presente | ✅ Supportato | ⏱️ Timeout |
|
||||||
|
| QID | ✅ Supportato | ✅ Supportato | ⏱️ Timeout |
|
||||||
|
|
||||||
|
**Conclusione:** Il MKS IV **non segue né P17 né P18** standard!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 Ricerca Documentazione Online
|
||||||
|
|
||||||
|
### Forum AEVA (Australian EV Association)
|
||||||
|
- **URL:** http://forums.aeva.asn.au/viewtopic.php?t=4332
|
||||||
|
- **Contenuto:** Thread esteso su PIP-4048MS e PIP-5048MS
|
||||||
|
- **Modelli correlati:** Axpert MKS 5K menzionato come variante
|
||||||
|
- **Protocollo:** Documentazione `SMV III 5K 232 Axpert KS&MKS&V&KING RS232 Protocol 20200102.pdf` (1.21 MB)
|
||||||
|
- **Comandi custom:** Menzionato comando `Q1` non documentato
|
||||||
|
- **CRC Issue:** User PlanB segnala: *"Does your CRC generator work with the POP02 command? Mine gives E2 0A as the CRC but the box expects E2 0B"*
|
||||||
|
|
||||||
|
**⚠️ Problema critico:** Anche altri utenti riportano **CRC anomalie** con MKS series!
|
||||||
|
|
||||||
|
### GitHub Issue #29 - skymax-demo
|
||||||
|
- **URL:** https://github.com/manio/skymax-demo/issues/29
|
||||||
|
- **Titolo:** "Can't read data from Voltronic AXPERT MKS IV 5.6KW"
|
||||||
|
- **Problema:** **IDENTICO AL NOSTRO**
|
||||||
|
- **Device:** MKS IV 5.6KW (stesso modello!)
|
||||||
|
- **Config usata:**
|
||||||
|
```
|
||||||
|
qpiri=103
|
||||||
|
qpiws=40
|
||||||
|
qmod=5
|
||||||
|
qpigs=110
|
||||||
|
```
|
||||||
|
(Identica alla nostra ✅)
|
||||||
|
|
||||||
|
**Sintomi riportati:**
|
||||||
|
1. Device `/dev/hidraw0` presente inizialmente
|
||||||
|
2. Dopo tentativo connessione: **device scompare** da `/dev/hidraw0`
|
||||||
|
3. "Cypress Semiconductor USB to Serial" **sparisce da lsusb**
|
||||||
|
|
||||||
|
**Diagnosi community:**
|
||||||
|
> "This is symptom of faulty USB device, wrong driver or unspecified system issue (chipset, USB host, kernel)."
|
||||||
|
|
||||||
|
**Differenza key:** Issue #29 usa porta **USB diretta** (HID), noi usiamo **RS232** (FTDI) → comunicazione più stabile!
|
||||||
|
|
||||||
|
### Manuale Locale Disponibile
|
||||||
|
- **File:** `/home/pi/Progetti/manual/HS_MS_MSX_RS232_Protocol_20140822_after_current_upgrade.pdf`
|
||||||
|
- **Dimensione:** 185 KB
|
||||||
|
- **Pagine:** 21
|
||||||
|
- **Anno:** 2014 (pre-MKS IV, probabilmente copre MKS I/II/III)
|
||||||
|
- **Status:** Da analizzare per comandi specifici
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚨 Problemi Identificati
|
||||||
|
|
||||||
|
### 1. Set Comandi Proprietario
|
||||||
|
- Solo 4.5% comandi standard funzionanti
|
||||||
|
- Inverter risponde attivamente con NAK (non ignora)
|
||||||
|
- Formato risposta corretto ma comandi non riconosciuti
|
||||||
|
|
||||||
|
### 2. Possibile Firmware Custom
|
||||||
|
- Model code "054" non documentato nei manuali standard
|
||||||
|
- Risposta QGMN diversa da altri Axpert (che restituiscono stringa completa)
|
||||||
|
- Comandi diagnostici base (QID, QVFW) non funzionano
|
||||||
|
|
||||||
|
### 3. Conflitto BMS (Errore 61)
|
||||||
|
- User riporta "errore 61" quando BMS collegato
|
||||||
|
- Linux errno 61 = `ENODATA` (No data available)
|
||||||
|
- Possibile conflitto porte comunicazione RS232/CAN
|
||||||
|
|
||||||
|
### 4. GitHub Issue Conferma Incompatibilità
|
||||||
|
- Altro utente MKS IV 5.6KW **stesso problema**
|
||||||
|
- Device USB crash dopo connessione
|
||||||
|
- Software ufficiale probabilmente usa protocollo diverso
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 Soluzioni Proposte
|
||||||
|
|
||||||
|
### Soluzione 1: Reverse Engineering (PRIORITÀ ALTA)
|
||||||
|
**Metodo:** Sniffing comunicazione software ufficiale WatchPower
|
||||||
|
|
||||||
|
**Step:**
|
||||||
|
1. Installare WatchPower su Windows/Linux
|
||||||
|
2. Usare tool sniffing USB/Serial (Wireshark, USBPcap)
|
||||||
|
3. Catturare traffico durante operazioni:
|
||||||
|
- Lettura status
|
||||||
|
- Cambio impostazioni
|
||||||
|
- Query diagnostiche
|
||||||
|
4. Analizzare comandi catturati e formato risposte
|
||||||
|
5. Implementare supporto comandi proprietari MKS IV
|
||||||
|
|
||||||
|
**Tool necessari:**
|
||||||
|
- WatchPower (software ufficiale Voltronic)
|
||||||
|
- Wireshark + USBPcap
|
||||||
|
- Serial port monitor (Portmon, Advanced Serial Port Monitor)
|
||||||
|
|
||||||
|
### Soluzione 2: Analisi Manuale Locale
|
||||||
|
**File da analizzare:** `HS_MS_MSX_RS232_Protocol_20140822_after_current_upgrade.pdf`
|
||||||
|
|
||||||
|
**Obiettivi:**
|
||||||
|
1. Cercare riferimenti a "MKS" o "054"
|
||||||
|
2. Identificare comandi non testati
|
||||||
|
3. Verificare varianti protocollo per firmware dopo 2014
|
||||||
|
4. Controllare appendici con comandi "hidden"
|
||||||
|
|
||||||
|
### Soluzione 3: Contatto Supporto Tecnico
|
||||||
|
**Destinatari:**
|
||||||
|
- Voltronic Power (produttore originale)
|
||||||
|
- MPP Solar (distributore)
|
||||||
|
- Community forum AEVA
|
||||||
|
|
||||||
|
**Richiesta:**
|
||||||
|
- Documentazione protocollo specifico MKS IV
|
||||||
|
- Lista comandi supportati model "054"
|
||||||
|
- Differenze rispetto a protocollo P17/P18 standard
|
||||||
|
- Workaround per integrazione software custom
|
||||||
|
|
||||||
|
### Soluzione 4: Test Porta USB Alternativa (SE DISPONIBILE)
|
||||||
|
**Se l'inverter ha porta mini USB:**
|
||||||
|
|
||||||
|
**Test:**
|
||||||
|
```bash
|
||||||
|
# Verificare device HID
|
||||||
|
ls -la /dev/hidraw*
|
||||||
|
|
||||||
|
# Test connessione diretta USB
|
||||||
|
sudo ./bin/inverter_poller -d -1 --device=/dev/hidraw0
|
||||||
|
```
|
||||||
|
|
||||||
|
**Vantaggi:**
|
||||||
|
- Potrebbe bypassare conversione RS232
|
||||||
|
- Accesso a comandi USB-HID specifici
|
||||||
|
- Firmware potrebbe avere driver USB diverso
|
||||||
|
|
||||||
|
**Rischi:**
|
||||||
|
- Device crash come riportato in GitHub Issue #29
|
||||||
|
- Richiede driver kernel specifici
|
||||||
|
- Possibile incompatibilità hardware
|
||||||
|
|
||||||
|
### Soluzione 5: Test Senza BMS
|
||||||
|
**Obiettivo:** Verificare se errore 61 interferisce con comunicazione
|
||||||
|
|
||||||
|
**Procedura:**
|
||||||
|
1. Spegnere inverter
|
||||||
|
2. Disconnettere BMS (cavo CAN o RS485)
|
||||||
|
3. Riavviare inverter
|
||||||
|
4. Ripetere test comandi
|
||||||
|
5. Verificare se comandi timeout ora rispondono
|
||||||
|
|
||||||
|
**Ipotesi:** BMS potrebbe occupare porta comunicazione o causare conflitti bus.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Raccomandazioni Immediate
|
||||||
|
|
||||||
|
### 1. Analizzare Manuale Locale (PRIORITÀ 1)
|
||||||
|
```bash
|
||||||
|
cd /home/pi/Progetti/manual
|
||||||
|
evince HS_MS_MSX_RS232_Protocol_20140822_after_current_upgrade.pdf &
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cercare:**
|
||||||
|
- Tabella comandi completa (spesso in appendice)
|
||||||
|
- Riferimenti a "MKS" o codici modello
|
||||||
|
- Note su firmware versioni dopo 2014
|
||||||
|
- Comandi non documentati o "reserved"
|
||||||
|
|
||||||
|
### 2. Test Disconnessione BMS (PRIORITÀ 2)
|
||||||
|
- Isolare problema errore 61
|
||||||
|
- Verificare se impatta comunicazione RS232
|
||||||
|
- Testare comandi timeout senza BMS attivo
|
||||||
|
|
||||||
|
### 3. Setup Environment Sniffing (PRIORITÀ 3)
|
||||||
|
- Installare WatchPower su macchina test
|
||||||
|
- Configurare Wireshark con USBPcap
|
||||||
|
- Preparare script cattura traffico seriale
|
||||||
|
|
||||||
|
### 4. Query Community (PRIORITÀ 4)
|
||||||
|
- Post su forum AEVA con riferimento a thread PIP-4048MS
|
||||||
|
- Menzionare model code "054" e QGMN funzionante
|
||||||
|
- Chiedere se qualcuno ha documentazione MKS IV specifica
|
||||||
|
- Linkare GitHub Issue #29 per visibilità
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Risorse e Riferimenti
|
||||||
|
|
||||||
|
### Documentazione Trovata
|
||||||
|
1. **Forum AEVA - PIP-4048MS Thread**
|
||||||
|
- URL: http://forums.aeva.asn.au/viewtopic.php?t=4332
|
||||||
|
- Attachment: `SMV III 5K 232 Axpert KS&MKS&V&KING RS232 Protocol 20200102.pdf`
|
||||||
|
- 3374+ posts con documentazione estesa
|
||||||
|
|
||||||
|
2. **GitHub Issue #29 - skymax-demo**
|
||||||
|
- URL: https://github.com/manio/skymax-demo/issues/29
|
||||||
|
- Status: Open (aperto Jul 23, 2024)
|
||||||
|
- 6 comments, problema non risolto
|
||||||
|
|
||||||
|
3. **Manuale Locale**
|
||||||
|
- Path: `/home/pi/Progetti/manual/`
|
||||||
|
- File: `HS_MS_MSX_RS232_Protocol_20140822_after_current_upgrade.pdf`
|
||||||
|
- Da analizzare
|
||||||
|
|
||||||
|
### Comandi da Testare (Non ancora provati)
|
||||||
|
```
|
||||||
|
QSID # Set Device ID
|
||||||
|
QCT # CT ratio
|
||||||
|
QPWS # Power Warning Status (diverso da QPIWS?)
|
||||||
|
QGMNI # General Model Name with Index
|
||||||
|
```
|
||||||
|
|
||||||
|
### Software Correlato
|
||||||
|
- **WatchPower:** Software ufficiale Voltronic per monitoring/config
|
||||||
|
- **ICC (Inverter Control Centre):** Alternative software by AEVA community
|
||||||
|
- **SolPipLog:** Logging software menzionato nel forum
|
||||||
|
- **skymax-demo:** Progetto GitHub che abbiamo usato come base
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 Conclusioni
|
||||||
|
|
||||||
|
### Fatto Assodato
|
||||||
|
1. ✅ **Hardware funziona:** Comunicazione RS232 stabile, CRC corretto, framing OK
|
||||||
|
2. ✅ **Software funziona:** Applicazione C++ interroga inverter correttamente
|
||||||
|
3. ❌ **Protocollo incompatibile:** MKS IV usa comandi proprietari non standard
|
||||||
|
4. ❌ **Documentazione mancante:** Nessun manuale MKS IV specifico trovato online
|
||||||
|
|
||||||
|
### Prossimo Step
|
||||||
|
🔴 **CRITICO:** Necessario **reverse engineering** del protocollo tramite:
|
||||||
|
1. Analisi manuale locale (immediato)
|
||||||
|
2. Sniffing WatchPower (entro 48h)
|
||||||
|
3. Query community AEVA (entro 7 giorni)
|
||||||
|
|
||||||
|
### Stima Tempi
|
||||||
|
- **Reverse engineering completo:** 40-60 ore lavoro
|
||||||
|
- **Implementazione comandi custom:** 20-30 ore coding
|
||||||
|
- **Testing e debug:** 10-15 ore
|
||||||
|
- **TOTALE:** 70-105 ore (9-13 giorni lavorativi)
|
||||||
|
|
||||||
|
### Alternative
|
||||||
|
Se reverse engineering fallisce:
|
||||||
|
1. **Usare solo QGMN** per verificare presenza inverter (monitoring base)
|
||||||
|
2. **Integrare via software ufficiale** (WatchPower API se disponibile)
|
||||||
|
3. **Monitorare tramite altra porta** (mini USB se presente)
|
||||||
|
4. **Contattare rivenditore** per upgrade firmware a versione compatibile P18
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Report compilato da:** AI Assistant
|
||||||
|
**Data:** 31 Gennaio 2026
|
||||||
|
**Versione documento:** 1.0
|
||||||
|
**Test eseguiti:** 22 comandi in 60 minuti
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
# Quick Reference - Test Loop & MQTT Configuration
|
||||||
|
|
||||||
|
## 🔧 Configurazione MQTT Server
|
||||||
|
|
||||||
|
### File di Configurazione
|
||||||
|
|
||||||
|
**Development:**
|
||||||
|
```
|
||||||
|
/home/pi/Progetti/config/mqtt.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Container (Production):**
|
||||||
|
```
|
||||||
|
/etc/inverter/mqtt.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parametri Chiave
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"server": "[HA_MQTT_IP]", // ← Indirizzo IP Home Assistant
|
||||||
|
"port": "1883", // Porta MQTT standard
|
||||||
|
"topic": "homeassistant", // Topic base
|
||||||
|
"devicename": "voltronic", // Nome device (prefix topic)
|
||||||
|
"username": "", // Username MQTT (se richiesto)
|
||||||
|
"password": "", // Password MQTT (se richiesto)
|
||||||
|
"clientid": "voltronic_...", // Client ID univoco
|
||||||
|
"influx": {
|
||||||
|
"enabled": "false", // InfluxDB (opzionale)
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Modificare la Configurazione
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Editare il file
|
||||||
|
nano /home/pi/Progetti/config/mqtt.json
|
||||||
|
|
||||||
|
# Cambiare l'IP del server MQTT
|
||||||
|
# Sostituire [HA_MQTT_IP] con l'IP reale di Home Assistant
|
||||||
|
# Esempio: "server": "192.168.1.100"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Test Loop - Simulazione Container
|
||||||
|
|
||||||
|
### Script Principale
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/home/pi/Progetti/sources/inverter-mqtt/test-loop-parallel.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Esecuzione Manuale
|
||||||
|
|
||||||
|
**Modalità interattiva (infinito):**
|
||||||
|
```bash
|
||||||
|
bash /home/pi/Progetti/sources/inverter-mqtt/test-loop-parallel.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
**Modalità con limiti:**
|
||||||
|
```bash
|
||||||
|
# 5 iterazioni con intervallo 10 secondi
|
||||||
|
LOOP_INTERVAL=10 MAX_ITERATIONS=5 bash test-loop-parallel.sh
|
||||||
|
|
||||||
|
# 2 iterazioni con intervallo 5 secondi (test rapido)
|
||||||
|
LOOP_INTERVAL=5 MAX_ITERATIONS=2 bash test-loop-parallel.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Variabili d'Ambiente
|
||||||
|
|
||||||
|
| Variabile | Descrizione | Default |
|
||||||
|
|-----------|-------------|---------|
|
||||||
|
| `LOOP_INTERVAL` | Secondi tra iterazioni | 30 |
|
||||||
|
| `MAX_ITERATIONS` | Numero massimo iterazioni (0=infinito) | 0 |
|
||||||
|
|
||||||
|
### Fasi del Test Loop
|
||||||
|
|
||||||
|
#### **Phase 1: Initial Discovery** (eseguito una volta all'avvio)
|
||||||
|
|
||||||
|
1. **Buffer Sizes Auto-Discovery** (`-a`)
|
||||||
|
- Rileva dimensioni corrette per QMOD, QPIGS, QPIRI, QPIWS
|
||||||
|
- Output: `DISCOVERY_QMOD=5`, `DISCOVERY_QPIGS=110`, etc.
|
||||||
|
|
||||||
|
2. **Parallel Inverters Discovery** (`-p`)
|
||||||
|
- Cerca inverter in configurazione parallela (QPGS0-QPGS9)
|
||||||
|
- Filtra serial validi (no "00000000000000")
|
||||||
|
- Filtra duplicati
|
||||||
|
- Output: `PARALLEL_COUNT=2`, `INVERTER_1_SERIAL=...`, etc.
|
||||||
|
|
||||||
|
#### **Phase 2: Main Polling Loop** (ripetuto ogni N secondi)
|
||||||
|
|
||||||
|
1. **Test Standard Commands** (inverter locale via USB)
|
||||||
|
- QPIGS - General status
|
||||||
|
- QPIRI - Configuration
|
||||||
|
- QMOD - Operating mode
|
||||||
|
- QPIWS - Warning status
|
||||||
|
|
||||||
|
2. **Read Parallel Inverters** (tutti gli inverter via QPGS)
|
||||||
|
- Per ogni inverter trovato:
|
||||||
|
- Legge dati QPGS
|
||||||
|
- Estrae: Mode, Grid Voltage, Battery Voltage, Load Watts
|
||||||
|
|
||||||
|
3. **MQTT Push** (pubblica su Home Assistant)
|
||||||
|
- Esegue `mqtt-push-parallel.sh`
|
||||||
|
- Pubblica topic separati per ogni inverter
|
||||||
|
- Formato: `homeassistant/sensor/voltronic_inv1_*`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Topic MQTT Pubblicati
|
||||||
|
|
||||||
|
### Per Ogni Inverter
|
||||||
|
|
||||||
|
```
|
||||||
|
homeassistant/sensor/voltronic_inv1_serial → "92932111105114"
|
||||||
|
homeassistant/sensor/voltronic_inv1_mode → "B" (Battery mode)
|
||||||
|
homeassistant/sensor/voltronic_inv1_AC_grid_voltage → "229.8"
|
||||||
|
homeassistant/sensor/voltronic_inv1_AC_grid_frequency → "50.0"
|
||||||
|
homeassistant/sensor/voltronic_inv1_AC_out_voltage → "229.5"
|
||||||
|
homeassistant/sensor/voltronic_inv1_AC_out_frequency → "49.9"
|
||||||
|
homeassistant/sensor/voltronic_inv1_Load_va → "481"
|
||||||
|
homeassistant/sensor/voltronic_inv1_Load_watt → "465"
|
||||||
|
homeassistant/sensor/voltronic_inv1_Load_pct → "8"
|
||||||
|
homeassistant/sensor/voltronic_inv1_Battery_voltage → "53.9"
|
||||||
|
homeassistant/sensor/voltronic_inv1_Battery_charge_current → "4"
|
||||||
|
homeassistant/sensor/voltronic_inv1_Battery_capacity → "100"
|
||||||
|
homeassistant/sensor/voltronic_inv1_PV_in_voltage → "246.8"
|
||||||
|
homeassistant/sensor/voltronic_inv1_PV_in_current → "4"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Topic Sistema
|
||||||
|
|
||||||
|
```
|
||||||
|
homeassistant/sensor/voltronic_system_parallel_count → "2"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 Debug Profiles VS Code
|
||||||
|
|
||||||
|
### 1. Parallel Discovery - Container Mode
|
||||||
|
**Profile:** `(gdb) Parallel Discovery - Container Mode`
|
||||||
|
- Esegue: `inverter_poller -p`
|
||||||
|
- Simula ambiente container (cwd="/")
|
||||||
|
- ENV: `INVERTER_DEVICE=/dev/ttyUSB0`
|
||||||
|
|
||||||
|
### 2. Test MQTT Parallel - Container Simulation
|
||||||
|
**Profile:** `(bash) Test MQTT Parallel - Container Simulation`
|
||||||
|
- Esegue: `mqtt-push-parallel.sh`
|
||||||
|
- Debug bash script MQTT
|
||||||
|
|
||||||
|
### 3. Test Loop - Full Simulation
|
||||||
|
**Profile:** `(bash) Test Loop - Full Simulation`
|
||||||
|
- Esegue: `test-loop-parallel.sh`
|
||||||
|
- 3 iterazioni con intervallo 10s
|
||||||
|
- Full simulation discovery + polling + MQTT
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Comandi Utili
|
||||||
|
|
||||||
|
### Discovery Manuale
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Buffer sizes discovery
|
||||||
|
sudo /home/pi/Progetti/sources/inverter-cli/bin/inverter_poller -a
|
||||||
|
|
||||||
|
# Parallel inverters discovery
|
||||||
|
sudo /home/pi/Progetti/sources/inverter-cli/bin/inverter_poller -p
|
||||||
|
|
||||||
|
# Test comando specifico
|
||||||
|
sudo /home/pi/Progetti/sources/inverter-cli/bin/inverter_poller -r QPGS0
|
||||||
|
```
|
||||||
|
|
||||||
|
### MQTT Push Manuale
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Single run
|
||||||
|
bash /home/pi/Progetti/sources/inverter-mqtt/mqtt-push-parallel.sh
|
||||||
|
|
||||||
|
# Con debug output
|
||||||
|
bash /home/pi/Progetti/sources/inverter-mqtt/mqtt-push-parallel.sh 2>&1 | less
|
||||||
|
```
|
||||||
|
|
||||||
|
### Monitoraggio MQTT (da altro terminal)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Subscribe a tutti i topic
|
||||||
|
mosquitto_sub -h [HA_MQTT_IP] -t "homeassistant/#" -v
|
||||||
|
|
||||||
|
# Solo sensori voltronic
|
||||||
|
mosquitto_sub -h [HA_MQTT_IP] -t "homeassistant/sensor/voltronic_#" -v
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 Output Esempio Test Loop
|
||||||
|
|
||||||
|
```
|
||||||
|
╔════════════════════════════════════════════════════════════════╗
|
||||||
|
║ VOLTRONIC PARALLEL INVERTER - TEST LOOP ║
|
||||||
|
╚════════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
Mode: Development
|
||||||
|
Binary: /home/pi/Progetti/sources/inverter-cli/bin/inverter_poller
|
||||||
|
MQTT Config: /home/pi/Progetti/config/mqtt.json
|
||||||
|
Loop Interval: 30s
|
||||||
|
Max Iterations: ∞ (infinite)
|
||||||
|
MQTT Server: 192.168.1.100
|
||||||
|
|
||||||
|
╔════════════════════════════════════════════════════════════════╗
|
||||||
|
║ PHASE 1: INITIAL DISCOVERY ║
|
||||||
|
╚════════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
[1.1] Buffer Sizes Auto-Discovery
|
||||||
|
✓ Buffer sizes discovered successfully
|
||||||
|
• QMOD: 5 bytes
|
||||||
|
• QPIGS: 110 bytes
|
||||||
|
• QPIRI: 103 bytes
|
||||||
|
• QPIWS: 40 bytes
|
||||||
|
|
||||||
|
[1.2] Parallel Inverters Discovery
|
||||||
|
✓ Found 2 parallel inverter(s)
|
||||||
|
• Inverter #1: Serial 92932111105114 (QPGS0)
|
||||||
|
• Inverter #2: Serial 96332205100144 (QPGS2)
|
||||||
|
|
||||||
|
╔════════════════════════════════════════════════════════════════╗
|
||||||
|
║ PHASE 2: MAIN POLLING LOOP ║
|
||||||
|
╚════════════════════════════════════════════════════════════════╝
|
||||||
|
|
||||||
|
═══════════════════════════════════════════════════════════════
|
||||||
|
Iteration #1 - 2026-01-31 15:25:35
|
||||||
|
═══════════════════════════════════════════════════════════════
|
||||||
|
[2.1] Testing standard commands (local inverter)
|
||||||
|
✓ QPIGS: OK (106 chars)
|
||||||
|
✓ QPIRI: OK (99 chars)
|
||||||
|
✓ QMOD: OK (1 chars)
|
||||||
|
✓ QPIWS: OK (36 chars)
|
||||||
|
|
||||||
|
[2.2] Reading parallel inverters data
|
||||||
|
✓ Inverter #1 (92932111105114): Mode=B, Grid=229.8V, Battery=53.6V, Load=0488W
|
||||||
|
✓ Inverter #2 (96332205100144): Mode=B, Grid=232.4V, Battery=53.6V, Load=0300W
|
||||||
|
|
||||||
|
[2.3] MQTT Push
|
||||||
|
✓ MQTT push completed
|
||||||
|
|
||||||
|
Waiting 30s until next iteration...
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Setup Rapido per Test
|
||||||
|
|
||||||
|
1. **Configura MQTT server:**
|
||||||
|
```bash
|
||||||
|
nano /home/pi/Progetti/config/mqtt.json
|
||||||
|
# Cambia "server": "[HA_MQTT_IP]" con IP reale
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Esegui test rapido (2 iterazioni):**
|
||||||
|
```bash
|
||||||
|
cd /home/pi/Progetti
|
||||||
|
LOOP_INTERVAL=5 MAX_ITERATIONS=2 bash sources/inverter-mqtt/test-loop-parallel.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Verifica MQTT (in altro terminal):**
|
||||||
|
```bash
|
||||||
|
mosquitto_sub -h [HA_MQTT_IP] -t "homeassistant/sensor/voltronic_#" -v
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Per produzione (loop continuo):**
|
||||||
|
```bash
|
||||||
|
bash sources/inverter-mqtt/test-loop-parallel.sh
|
||||||
|
```
|
||||||
@@ -76,7 +76,7 @@ bool cInverter::query(const char *cmd, int replysize) {
|
|||||||
int fd;
|
int fd;
|
||||||
int i=0, n;
|
int i=0, n;
|
||||||
|
|
||||||
fd = open(this->device.data(), O_RDWR | O_NONBLOCK);
|
fd = open(this->device.data(), O_RDWR | O_NOCTTY);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
lprintf("INVERTER: Unable to open device file (errno=%d %s)", errno, strerror(errno));
|
lprintf("INVERTER: Unable to open device file (errno=%d %s)", errno, strerror(errno));
|
||||||
sleep(5);
|
sleep(5);
|
||||||
@@ -134,12 +134,15 @@ bool cInverter::query(const char *cmd, int replysize) {
|
|||||||
// Flush output to ensure command is sent
|
// Flush output to ensure command is sent
|
||||||
tcdrain(fd);
|
tcdrain(fd);
|
||||||
|
|
||||||
|
// Critical delay after write (like Python implementation)
|
||||||
|
usleep(500000); // 500ms delay
|
||||||
|
|
||||||
// Clear buffer again before reading
|
// Clear buffer again before reading
|
||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
time(&started);
|
time(&started);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
n = read(fd, (void*)buf+i, replysize-i);
|
n = read(fd, (void*)buf+i, 1); // Read one byte at a time for reliable terminator detection
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
if (time(NULL) - started > 2) {
|
if (time(NULL) - started > 2) {
|
||||||
lprintf("INVERTER: %s read timeout", cmd);
|
lprintf("INVERTER: %s read timeout", cmd);
|
||||||
@@ -152,9 +155,9 @@ bool cInverter::query(const char *cmd, int replysize) {
|
|||||||
|
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
i += n;
|
i += n;
|
||||||
// Check if we've received the terminator
|
// Check if we've received the terminator (CR or LF)
|
||||||
if (i > 0 && buf[i-1] == 0x0d) {
|
if (i > 0 && (buf[i-1] == 0x0d || buf[i-1] == 0x0a)) {
|
||||||
lprintf("INVERTER: %s received terminator at byte %d", cmd, i);
|
lprintf("INVERTER: %s received terminator (0x%02X) at byte %d", cmd, buf[i-1], i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,8 +184,8 @@ bool cInverter::query(const char *cmd, int replysize) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf[i-1]!=0x0d) {
|
if (buf[i-1]!=0x0d && buf[i-1]!=0x0a) {
|
||||||
lprintf("INVERTER: %s: incorrect stop byte (got 0x%02X at pos %d, expected CR). Buffer: %s", cmd, buf[i-1], i-1, buf);
|
lprintf("INVERTER: %s: incorrect stop byte (got 0x%02X at pos %d, expected CR or LF). Buffer: %s", cmd, buf[i-1], i-1, buf);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,8 +280,8 @@ void cInverter::poll() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void cInverter::ExecuteCmd(const string cmd) {
|
void cInverter::ExecuteCmd(const string cmd) {
|
||||||
// Sending any command raw
|
// Sending any command raw - use larger buffer to accept full responses
|
||||||
if (query(cmd.data(), 7)) {
|
if (query(cmd.data(), 200)) {
|
||||||
m.lock();
|
m.lock();
|
||||||
strcpy(status2, (const char*)buf+1);
|
strcpy(status2, (const char*)buf+1);
|
||||||
m.unlock();
|
m.unlock();
|
||||||
@@ -470,3 +473,82 @@ void cInverter::AutoDiscoverBufferSizes() {
|
|||||||
printf("DISCOVERY_SUCCESS=%s\n", (qmod_size > 0 && qpigs_size > 0 && qpiri_size > 0 && qpiws_size > 0) ? "true" : "false");
|
printf("DISCOVERY_SUCCESS=%s\n", (qmod_size > 0 && qpigs_size > 0 && qpiri_size > 0 && qpiws_size > 0) ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Discover number of parallel inverters
|
||||||
|
int cInverter::DiscoverParallelInverters() {
|
||||||
|
fprintf(stderr, "\n=== PARALLEL INVERTER DISCOVERY ===\n");
|
||||||
|
fprintf(stderr, "Checking for parallel inverter configuration...\n\n");
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
char cmd[16];
|
||||||
|
std::string found_serials[10]; // Track unique serials
|
||||||
|
|
||||||
|
// Test QPGS0 through QPGS9
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
snprintf(cmd, sizeof(cmd), "QPGS%d", i);
|
||||||
|
|
||||||
|
if (query(cmd, 200)) {
|
||||||
|
// Check if response is valid (not NAK)
|
||||||
|
if (buf[0] == '(' && buf[1] != 'N') {
|
||||||
|
// Extract serial number (starts at position 3)
|
||||||
|
char serial[20] = {0};
|
||||||
|
int j = 0;
|
||||||
|
for (int k = 3; k < 17 && buf[k] != ' '; k++) {
|
||||||
|
serial[j++] = buf[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if serial is valid (not all zeros and not empty)
|
||||||
|
bool valid_serial = false;
|
||||||
|
for (int k = 0; k < j; k++) {
|
||||||
|
if (serial[k] != '0') {
|
||||||
|
valid_serial = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if serial is duplicate
|
||||||
|
bool duplicate = false;
|
||||||
|
std::string serial_str(serial);
|
||||||
|
for (int k = 0; k < count; k++) {
|
||||||
|
if (found_serials[k] == serial_str) {
|
||||||
|
duplicate = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid_serial && j > 0 && !duplicate) {
|
||||||
|
found_serials[count] = serial_str;
|
||||||
|
count++;
|
||||||
|
fprintf(stderr, "✓ Inverter #%d via %s (Serial: %s)\n", count, cmd, serial);
|
||||||
|
printf("INVERTER_%d_SERIAL=%s\n", count, serial);
|
||||||
|
printf("INVERTER_%d_QPGS=%d\n", count, i);
|
||||||
|
} else if (duplicate) {
|
||||||
|
fprintf(stderr, "⊗ Skipping %s (Duplicate serial: %s)\n", cmd, serial);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "⊗ Skipping %s (Invalid serial: %s)\n", cmd, serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usleep(100000); // 100ms between queries
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "\n=== DISCOVERY RESULT ===\n");
|
||||||
|
fprintf(stderr, "Total unique parallel inverters: %d\n", count);
|
||||||
|
printf("PARALLEL_COUNT=%d\n", count);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get parallel status for specific inverter
|
||||||
|
string cInverter::GetParallelStatus(int inverter_num) {
|
||||||
|
char cmd[16];
|
||||||
|
snprintf(cmd, sizeof(cmd), "QPGS%d", inverter_num);
|
||||||
|
|
||||||
|
if (query(cmd, 200)) {
|
||||||
|
if (buf[0] == '(' && buf[1] != 'N') {
|
||||||
|
// Return data without leading '('
|
||||||
|
return string((char*)buf + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ class cInverter {
|
|||||||
int GetMode();
|
int GetMode();
|
||||||
void ExecuteCmd(const std::string cmd);
|
void ExecuteCmd(const std::string cmd);
|
||||||
void AutoDiscoverBufferSizes();
|
void AutoDiscoverBufferSizes();
|
||||||
|
int DiscoverParallelInverters(); // Returns number of parallel inverters
|
||||||
|
string GetParallelStatus(int inverter_num); // Get QPGS data for specific inverter
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ___INVERTER_H
|
#endif // ___INVERTER_H
|
||||||
|
|||||||
@@ -203,6 +203,12 @@ int main(int argc, char* argv[]) {
|
|||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parallel inverter discovery mode
|
||||||
|
if(cmdArgs.cmdOptionExists("-p") || cmdArgs.cmdOptionExists("--parallel-discovery")) {
|
||||||
|
int count = ups->DiscoverParallelInverters();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
// Logic to send 'raw commands' to the inverter..
|
// Logic to send 'raw commands' to the inverter..
|
||||||
if (!rawcmd.empty()) {
|
if (!rawcmd.empty()) {
|
||||||
ups->ExecuteCmd(rawcmd);
|
ups->ExecuteCmd(rawcmd);
|
||||||
|
|||||||
Executable
+142
@@ -0,0 +1,142 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Test comunicazione con Voltronic Axpert MKS IV provando diversi baudrate
|
||||||
|
# Il MKS IV potrebbe usare un baudrate diverso dal classico 2400
|
||||||
|
|
||||||
|
DEVICE="${1:-/dev/ttyUSB0}"
|
||||||
|
|
||||||
|
echo "=== Test Baudrate per Voltronic Axpert MKS IV su $DEVICE ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Verifica device
|
||||||
|
if [ ! -e "$DEVICE" ]; then
|
||||||
|
echo "ERROR: Device $DEVICE non trovato!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Array di baudrate da testare
|
||||||
|
# Il MKS IV potrebbe usare: 2400, 9600, 19200, 38400 o 115200
|
||||||
|
BAUDRATES=(2400 9600 19200 38400 115200)
|
||||||
|
|
||||||
|
for BAUD in "${BAUDRATES[@]}"; do
|
||||||
|
echo "============================================"
|
||||||
|
echo "Testing BAUDRATE: $BAUD"
|
||||||
|
echo "============================================"
|
||||||
|
|
||||||
|
# Configura device
|
||||||
|
sudo stty -F $DEVICE $BAUD cs8 -cstopb -parenb -echo raw
|
||||||
|
sudo chmod 666 $DEVICE
|
||||||
|
|
||||||
|
# Test con Python
|
||||||
|
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)]
|
||||||
|
|
||||||
|
return crc.to_bytes(2, 'big')
|
||||||
|
|
||||||
|
try:
|
||||||
|
ser = serial.Serial(
|
||||||
|
port='$DEVICE',
|
||||||
|
baudrate=$BAUD,
|
||||||
|
bytesize=8,
|
||||||
|
parity='N',
|
||||||
|
stopbits=1,
|
||||||
|
timeout=2
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"Porta aperta a {$BAUD} baud")
|
||||||
|
|
||||||
|
# Flush buffers
|
||||||
|
ser.reset_input_buffer()
|
||||||
|
ser.reset_output_buffer()
|
||||||
|
time.sleep(0.3)
|
||||||
|
|
||||||
|
# Test comando QMOD (semplice, 5 bytes di risposta)
|
||||||
|
cmd = 'QMOD'
|
||||||
|
print(f"Invio comando: {cmd}")
|
||||||
|
|
||||||
|
cmd_bytes = cmd.encode('ascii')
|
||||||
|
crc = calc_crc(cmd_bytes)
|
||||||
|
full_cmd = cmd_bytes + crc + b'\r'
|
||||||
|
|
||||||
|
print(f" Hex: {full_cmd.hex()}")
|
||||||
|
|
||||||
|
# Invia
|
||||||
|
ser.write(full_cmd)
|
||||||
|
ser.flush()
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
# Leggi risposta
|
||||||
|
response = ser.read(200)
|
||||||
|
|
||||||
|
if len(response) > 0:
|
||||||
|
print(f" [OK] RISPOSTA RICEVUTA ({len(response)} bytes)")
|
||||||
|
print(f" Hex: {response.hex()}")
|
||||||
|
try:
|
||||||
|
ascii_text = response.decode('ascii', errors='replace')
|
||||||
|
print(f" ASCII: {ascii_text.strip()}")
|
||||||
|
|
||||||
|
# Verifica se è una risposta valida (inizia con '(' e non è NAK)
|
||||||
|
if response[0:1] == b'(' and b'NAK' not in response:
|
||||||
|
print(f" *** BAUDRATE CORRETTO: {$BAUD} ***")
|
||||||
|
sys.exit(0) # Success
|
||||||
|
elif b'NAK' in response:
|
||||||
|
print(f" [X] NAK ricevuto (inverter non comprende)")
|
||||||
|
else:
|
||||||
|
print(f" [X] Risposta non valida")
|
||||||
|
except:
|
||||||
|
print(f" [X] Risposta non decodificabile")
|
||||||
|
else:
|
||||||
|
print(f" [X] Nessuna risposta (timeout)")
|
||||||
|
|
||||||
|
ser.close()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f" [X] Errore: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
PYTHON_EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo ""
|
||||||
|
echo "╔═══════════════════════════════════════════╗"
|
||||||
|
echo "║ BAUDRATE CORRETTO TROVATO: $BAUD ║"
|
||||||
|
echo "╚═══════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
echo "Aggiorna /etc/inverter/inverter.conf se necessario"
|
||||||
|
echo "Modifica sources/inverter-cli/inverter.cpp:"
|
||||||
|
echo " Cambia: speed_t baud = B$BAUD;"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo "NESSUN BAUDRATE FUNZIONANTE TROVATO"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "Possibili cause:"
|
||||||
|
echo "1. Inverter spento o disconnesso"
|
||||||
|
echo "2. Cavo USB/RS232 difettoso"
|
||||||
|
echo "3. Device errato (prova /dev/ttyUSB1 o /dev/hidraw0)"
|
||||||
|
echo "4. Inverter in modalità incompatibile"
|
||||||
|
echo ""
|
||||||
Executable
+60
@@ -0,0 +1,60 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Test suite per comandi Voltronic Axpert MKS IV
|
||||||
|
# Basato su documentazione forum AEVA e manuale protocollo
|
||||||
|
|
||||||
|
echo "╔══════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ TEST COMANDI PROTOCOLLO VOLTRONIC MKS IV ║"
|
||||||
|
echo "╚══════════════════════════════════════════════════════╝"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
test_command() {
|
||||||
|
local cmd=$1
|
||||||
|
local desc=$2
|
||||||
|
echo -n "Testing $cmd ($desc)... "
|
||||||
|
result=$(sudo ./bin/inverter_poller -r "$cmd" 2>&1 | grep "Reply:" | sed 's/Reply://g' | xargs)
|
||||||
|
if [ -z "$result" ]; then
|
||||||
|
echo "❌ NO RESPONSE"
|
||||||
|
elif [ "$result" = "NAK" ]; then
|
||||||
|
echo "❌ NAK (comando non supportato)"
|
||||||
|
else
|
||||||
|
echo "✅ $result"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Comandi che DOVREBBERO funzionare
|
||||||
|
echo "=== COMANDI STANDARD P18 ==="
|
||||||
|
test_command "QID" "Device Serial Number"
|
||||||
|
test_command "QVFW" "Main CPU Firmware Version"
|
||||||
|
test_command "QGMN" "General Model Name"
|
||||||
|
test_command "QPI" "Protocol ID"
|
||||||
|
test_command "QFLAG" "Device Flag Status"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== COMANDI STATUS AVANZATI ==="
|
||||||
|
test_command "QPGS0" "Parallel General Status"
|
||||||
|
test_command "QPGS1" "Parallel General Status #1"
|
||||||
|
test_command "QDI" "Default Settings Inquiry"
|
||||||
|
test_command "QMCHGCR" "Max Charging Current Options"
|
||||||
|
test_command "QMUCHGCR" "Max Utility Charging Current"
|
||||||
|
test_command "QOPPT" "Output Power Type"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== COMANDI BATTERIA ==="
|
||||||
|
test_command "QBEQI" "Battery Equalization Info"
|
||||||
|
test_command "QBMS" "BMS Info"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== COMANDI DIAGNOSTICI ==="
|
||||||
|
test_command "QBOOT" "Bootloader Version"
|
||||||
|
test_command "QET" "Total Generated Energy"
|
||||||
|
test_command "QEY" "Generated Energy This Year"
|
||||||
|
test_command "QEM" "Generated Energy This Month"
|
||||||
|
test_command "QED" "Generated Energy Today"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "╔══════════════════════════════════════════════════════╗"
|
||||||
|
echo "║ REPORT FINALE ║"
|
||||||
|
echo "╚══════════════════════════════════════════════════════╝"
|
||||||
|
echo "Comando FUNZIONANTE: QGMN (Model 054)"
|
||||||
|
echo "Comandi STANDARD non funzionanti: QPIGS, QPIRI, QMOD, QPIWS"
|
||||||
|
echo "Possibile causa: Protocollo proprietario MKS IV"
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import serial
|
||||||
|
import time
|
||||||
|
|
||||||
|
port = '/dev/ttyUSB1'
|
||||||
|
ser = serial.Serial(port, 2400, bytesize=8, parity='N', stopbits=1, timeout=2)
|
||||||
|
|
||||||
|
# Costruisci comando QPIGS manualmente
|
||||||
|
cmd = b'QPIGS'
|
||||||
|
crc = 0xB7A9
|
||||||
|
cmd_full = cmd + bytes([crc >> 8, crc & 0xFF, 0x0D])
|
||||||
|
|
||||||
|
print(f"Invio comando: {cmd_full.hex(' ')}")
|
||||||
|
print(f"Lunghezza: {len(cmd_full)} bytes")
|
||||||
|
|
||||||
|
ser.write(cmd_full)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
resp = ser.read(100)
|
||||||
|
print(f"Ricevuto ({len(resp)} bytes): {resp.hex(' ')}")
|
||||||
|
print(f"ASCII: {resp}")
|
||||||
|
|
||||||
|
ser.close()
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import serial
|
||||||
|
import time
|
||||||
|
|
||||||
|
port = '/dev/ttyUSB0'
|
||||||
|
ser = serial.Serial(port, 2400, bytesize=8, parity='N', stopbits=1, timeout=2)
|
||||||
|
|
||||||
|
for cmd_str in ['QPIGS', 'QMOD', 'QGMN']:
|
||||||
|
# CRC calcolati
|
||||||
|
crcs = {'QPIGS': 0xB7A9, 'QMOD': 0x49C1, 'QGMN': 0x4928}
|
||||||
|
|
||||||
|
cmd = cmd_str.encode()
|
||||||
|
crc = crcs[cmd_str]
|
||||||
|
cmd_full = cmd + bytes([crc >> 8, crc & 0xFF, 0x0D])
|
||||||
|
|
||||||
|
print(f"\n=== {cmd_str} ===")
|
||||||
|
print(f"Invio: {cmd_full.hex(' ')}")
|
||||||
|
|
||||||
|
ser.write(cmd_full)
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
resp = ser.read(200)
|
||||||
|
print(f"Ricevuto ({len(resp)} bytes): {resp.hex(' ') if resp else '(nessuna risposta)'}")
|
||||||
|
if resp:
|
||||||
|
print(f"ASCII: {resp}")
|
||||||
|
|
||||||
|
ser.close()
|
||||||
Executable
+140
@@ -0,0 +1,140 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# MQTT Push for Parallel Inverters
|
||||||
|
# Discovers parallel inverters and publishes data for each one separately
|
||||||
|
|
||||||
|
# Detect environment (container vs development)
|
||||||
|
if [ -f "/etc/inverter/mqtt.json" ] && [ -x "/opt/inverter-cli/bin/inverter_poller" ]; then
|
||||||
|
# Container mode
|
||||||
|
MQTT_CONFIG="/etc/inverter/mqtt.json"
|
||||||
|
INVERTER_BIN="/opt/inverter-cli/bin/inverter_poller"
|
||||||
|
MQTT_FALLBACK="/opt/inverter-mqtt/mqtt-push.sh"
|
||||||
|
CONTAINER_MODE=true
|
||||||
|
else
|
||||||
|
# Development mode
|
||||||
|
MQTT_CONFIG="/home/pi/Progetti/config/mqtt.json"
|
||||||
|
INVERTER_BIN="/home/pi/Progetti/sources/inverter-cli/bin/inverter_poller"
|
||||||
|
MQTT_FALLBACK="/home/pi/Progetti/sources/inverter-mqtt/mqtt-push.sh"
|
||||||
|
CONTAINER_MODE=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Mode: $([ "$CONTAINER_MODE" = true ] && echo "Container" || echo "Development")"
|
||||||
|
echo "Using binary: $INVERTER_BIN"
|
||||||
|
|
||||||
|
# Check if jq is installed
|
||||||
|
if ! command -v jq &> /dev/null; then
|
||||||
|
echo "ERROR: jq is not installed. Install it with: sudo apt-get install jq"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
MQTT_SERVER=`cat $MQTT_CONFIG | jq '.server' -r`
|
||||||
|
MQTT_PORT=`cat $MQTT_CONFIG | jq '.port' -r`
|
||||||
|
MQTT_TOPIC=`cat $MQTT_CONFIG | jq '.topic' -r`
|
||||||
|
MQTT_DEVICENAME=`cat $MQTT_CONFIG | jq '.devicename' -r`
|
||||||
|
MQTT_USERNAME=`cat $MQTT_CONFIG | jq '.username' -r`
|
||||||
|
MQTT_PASSWORD=`cat $MQTT_CONFIG | jq '.password' -r`
|
||||||
|
MQTT_CLIENTID=`cat $MQTT_CONFIG | jq '.clientid' -r`
|
||||||
|
INFLUX_ENABLED=`cat $MQTT_CONFIG | jq '.influx.enabled' -r`
|
||||||
|
|
||||||
|
pushMQTTData () {
|
||||||
|
# $1 = inverter_id, $2 = metric, $3 = value
|
||||||
|
local inverter_id=$1
|
||||||
|
local metric=$2
|
||||||
|
local value=$3
|
||||||
|
|
||||||
|
mosquitto_pub \
|
||||||
|
-h $MQTT_SERVER \
|
||||||
|
-p $MQTT_PORT \
|
||||||
|
-u "$MQTT_USERNAME" \
|
||||||
|
-P "$MQTT_PASSWORD" \
|
||||||
|
-i $MQTT_CLIENTID \
|
||||||
|
-r \
|
||||||
|
-t "$MQTT_TOPIC/sensor/${MQTT_DEVICENAME}_inv${inverter_id}_${metric}" \
|
||||||
|
-m "$value"
|
||||||
|
|
||||||
|
if [[ $INFLUX_ENABLED == "true" ]] ; then
|
||||||
|
pushInfluxData $inverter_id $metric $value
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
pushInfluxData () {
|
||||||
|
INFLUX_HOST=`cat $MQTT_CONFIG | jq '.influx.host' -r`
|
||||||
|
INFLUX_USERNAME=`cat $MQTT_CONFIG | jq '.influx.username' -r`
|
||||||
|
INFLUX_PASSWORD=`cat $MQTT_CONFIG | jq '.influx.password' -r`
|
||||||
|
INFLUX_DEVICE=`cat $MQTT_CONFIG | jq '.influx.device' -r`
|
||||||
|
INFLUX_PREFIX=`cat $MQTT_CONFIG | jq '.influx.prefix' -r`
|
||||||
|
INFLUX_DATABASE=`cat $MQTT_CONFIG | jq '.influx.database' -r`
|
||||||
|
INFLUX_MEASUREMENT_NAME=`cat $MQTT_CONFIG | jq '.influx.namingMap.'$2'' -r`
|
||||||
|
|
||||||
|
curl -i -XPOST "$INFLUX_HOST/write?db=$INFLUX_DATABASE&precision=s" -u "$INFLUX_USERNAME:$INFLUX_PASSWORD" --data-binary "$INFLUX_PREFIX,device=${INFLUX_DEVICE}_inv${1} $INFLUX_MEASUREMENT_NAME=$3" > /dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Discover parallel inverters
|
||||||
|
SUDO_CMD=""
|
||||||
|
if [ "$EUID" -ne 0 ] && [ -c "/dev/ttyUSB0" ]; then
|
||||||
|
SUDO_CMD="sudo"
|
||||||
|
fi
|
||||||
|
|
||||||
|
PARALLEL_DISCOVERY=`$SUDO_CMD "$INVERTER_BIN" -p 2>&1`
|
||||||
|
PARALLEL_COUNT=`echo "$PARALLEL_DISCOVERY" | grep "PARALLEL_COUNT=" | cut -d= -f2`
|
||||||
|
|
||||||
|
if [ -z "$PARALLEL_COUNT" ] || [ "$PARALLEL_COUNT" -eq 0 ]; then
|
||||||
|
echo "No parallel inverters found (count=$PARALLEL_COUNT), using standard polling"
|
||||||
|
# Don't use fallback in dev mode if file doesn't exist
|
||||||
|
if [ -f "$MQTT_FALLBACK" ]; then
|
||||||
|
exec $MQTT_FALLBACK
|
||||||
|
else
|
||||||
|
echo "Fallback script not found: $MQTT_FALLBACK"
|
||||||
|
echo "Using standard inverter_poller -1 instead"
|
||||||
|
INVERTER_DATA=`$SUDO_CMD "$INVERTER_BIN" -1 2>&1`
|
||||||
|
echo "$INVERTER_DATA"
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found $PARALLEL_COUNT parallel inverters"
|
||||||
|
|
||||||
|
# Publish discovery info
|
||||||
|
pushMQTTData "system" "parallel_count" "$PARALLEL_COUNT"
|
||||||
|
|
||||||
|
# Extract inverter serials and QPGS indices
|
||||||
|
for i in $(seq 1 $PARALLEL_COUNT); do
|
||||||
|
SERIAL=`echo "$PARALLEL_DISCOVERY" | grep "INVERTER_${i}_SERIAL=" | cut -d= -f2`
|
||||||
|
QPGS_IDX=`echo "$PARALLEL_DISCOVERY" | grep "INVERTER_${i}_QPGS=" | cut -d= -f2`
|
||||||
|
|
||||||
|
echo "Processing Inverter #$i (Serial: $SERIAL, QPGS$QPGS_IDX)"
|
||||||
|
|
||||||
|
# Get QPGS data for this inverter
|
||||||
|
QPGS_DATA=`$SUDO_CMD "$INVERTER_BIN" -r "QPGS$QPGS_IDX" 2>&1 | grep "Reply:" | cut -d: -f2- | xargs`
|
||||||
|
|
||||||
|
if [ ! -z "$QPGS_DATA" ] && [ "$QPGS_DATA" != "NAK" ]; then
|
||||||
|
# Parse QPGS response format:
|
||||||
|
# 1 SERIAL MODE STATUS GRID_V GRID_F OUT_V OUT_F VA W PCT BATT_V CHRG CAP PV_V CHRG_A ...
|
||||||
|
|
||||||
|
# Publish serial number
|
||||||
|
pushMQTTData "$i" "serial" "$SERIAL"
|
||||||
|
|
||||||
|
# Parse and publish data (QPGS format parsing)
|
||||||
|
IFS=' ' read -ra DATA <<< "$QPGS_DATA"
|
||||||
|
|
||||||
|
[ "${DATA[2]}" ] && pushMQTTData "$i" "mode" "${DATA[2]}"
|
||||||
|
[ "${DATA[4]}" ] && pushMQTTData "$i" "AC_grid_voltage" "${DATA[4]}"
|
||||||
|
[ "${DATA[5]}" ] && pushMQTTData "$i" "AC_grid_frequency" "${DATA[5]}"
|
||||||
|
[ "${DATA[6]}" ] && pushMQTTData "$i" "AC_out_voltage" "${DATA[6]}"
|
||||||
|
[ "${DATA[7]}" ] && pushMQTTData "$i" "AC_out_frequency" "${DATA[7]}"
|
||||||
|
[ "${DATA[8]}" ] && pushMQTTData "$i" "Load_va" "${DATA[8]}"
|
||||||
|
[ "${DATA[9]}" ] && pushMQTTData "$i" "Load_watt" "${DATA[9]}"
|
||||||
|
[ "${DATA[10]}" ] && pushMQTTData "$i" "Load_pct" "${DATA[10]}"
|
||||||
|
[ "${DATA[11]}" ] && pushMQTTData "$i" "Battery_voltage" "${DATA[11]}"
|
||||||
|
[ "${DATA[12]}" ] && pushMQTTData "$i" "Battery_charge_current" "${DATA[12]}"
|
||||||
|
[ "${DATA[13]}" ] && pushMQTTData "$i" "Battery_capacity" "${DATA[13]}"
|
||||||
|
[ "${DATA[14]}" ] && pushMQTTData "$i" "PV_in_voltage" "${DATA[14]}"
|
||||||
|
[ "${DATA[15]}" ] && pushMQTTData "$i" "PV_in_current" "${DATA[15]}"
|
||||||
|
|
||||||
|
echo " ✓ Published data for inverter #$i"
|
||||||
|
echo " Topics: ${MQTT_TOPIC}/sensor/${MQTT_DEVICENAME}_inv${i}_{serial,mode,Battery_voltage,Load_watt,...}"
|
||||||
|
else
|
||||||
|
echo " ✗ No valid data for inverter #$i"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Parallel MQTT push completed"
|
||||||
Executable
+225
@@ -0,0 +1,225 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Test Loop - Simula esecuzione container con parallel discovery e MQTT push
|
||||||
|
# Questo script simula il comportamento completo del container in produzione
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Detect environment
|
||||||
|
if [ -f "/etc/inverter/mqtt.json" ] && [ -x "/opt/inverter-cli/bin/inverter_poller" ]; then
|
||||||
|
# Container mode
|
||||||
|
MQTT_CONFIG="/etc/inverter/mqtt.json"
|
||||||
|
INVERTER_BIN="/opt/inverter-cli/bin/inverter_poller"
|
||||||
|
INVERTER_CONFIG="/etc/inverter/inverter.conf"
|
||||||
|
MQTT_PUSH_SCRIPT="/opt/inverter-mqtt/mqtt-push-parallel.sh"
|
||||||
|
CONTAINER_MODE=true
|
||||||
|
else
|
||||||
|
# Development mode
|
||||||
|
MQTT_CONFIG="/home/pi/Progetti/config/mqtt.json"
|
||||||
|
INVERTER_BIN="/home/pi/Progetti/sources/inverter-cli/bin/inverter_poller"
|
||||||
|
INVERTER_CONFIG="/home/pi/Progetti/config/inverter.conf"
|
||||||
|
MQTT_PUSH_SCRIPT="/home/pi/Progetti/sources/inverter-mqtt/mqtt-push-parallel.sh"
|
||||||
|
CONTAINER_MODE=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
SUDO_CMD=""
|
||||||
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
SUDO_CMD="sudo"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
LOOP_INTERVAL=${LOOP_INTERVAL:-30} # Seconds between iterations
|
||||||
|
MAX_ITERATIONS=${MAX_ITERATIONS:-0} # 0 = infinite
|
||||||
|
|
||||||
|
echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${BLUE}║ VOLTRONIC PARALLEL INVERTER - TEST LOOP ║${NC}"
|
||||||
|
echo -e "${BLUE}║ Simulates container execution with full discovery ║${NC}"
|
||||||
|
echo -e "${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Mode:${NC} $([ "$CONTAINER_MODE" = true ] && echo "Container" || echo "Development")"
|
||||||
|
echo -e "${YELLOW}Binary:${NC} $INVERTER_BIN"
|
||||||
|
echo -e "${YELLOW}MQTT Config:${NC} $MQTT_CONFIG"
|
||||||
|
echo -e "${YELLOW}Loop Interval:${NC} ${LOOP_INTERVAL}s"
|
||||||
|
echo -e "${YELLOW}Max Iterations:${NC} $([ $MAX_ITERATIONS -eq 0 ] && echo "∞ (infinite)" || echo "$MAX_ITERATIONS")"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check device configuration
|
||||||
|
if [ -f "$INVERTER_CONFIG" ]; then
|
||||||
|
USB_DEVICE=$(grep "^device=" "$INVERTER_CONFIG" | cut -d= -f2)
|
||||||
|
echo -e "${YELLOW}USB Device:${NC} $USB_DEVICE"
|
||||||
|
# Show USB info if available
|
||||||
|
if [ -e "$USB_DEVICE" ]; then
|
||||||
|
USB_SERIAL=$(udevadm info -q property -n "$USB_DEVICE" 2>/dev/null | grep "ID_SERIAL_SHORT=" | cut -d= -f2)
|
||||||
|
[ ! -z "$USB_SERIAL" ] && echo -e "${YELLOW}USB Serial:${NC} $USB_SERIAL"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if MQTT server is configured
|
||||||
|
if [ -f "$MQTT_CONFIG" ]; then
|
||||||
|
MQTT_SERVER=$(cat "$MQTT_CONFIG" | jq -r '.server' 2>/dev/null || echo "not configured")
|
||||||
|
MQTT_PORT=$(cat "$MQTT_CONFIG" | jq -r '.port' 2>/dev/null || echo "1883")
|
||||||
|
MQTT_TOPIC=$(cat "$MQTT_CONFIG" | jq -r '.topic' 2>/dev/null || echo "homeassistant")
|
||||||
|
MQTT_DEVICE=$(cat "$MQTT_CONFIG" | jq -r '.devicename' 2>/dev/null || echo "voltronic")
|
||||||
|
echo -e "${YELLOW}MQTT Server:${NC} $MQTT_SERVER:$MQTT_PORT"
|
||||||
|
echo -e "${YELLOW}MQTT Base Topic:${NC} $MQTT_TOPIC/sensor/$MQTT_DEVICE"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ MQTT config not found: $MQTT_CONFIG${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
read -p "Press ENTER to start test loop (Ctrl+C to stop)..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Phase 1: Initial Discovery (run once at startup)
|
||||||
|
echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${BLUE}║ PHASE 1: INITIAL DISCOVERY ║${NC}"
|
||||||
|
echo -e "${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${YELLOW}[1.1] Buffer Sizes Auto-Discovery${NC}"
|
||||||
|
DISCOVERY_OUTPUT=$($SUDO_CMD "$INVERTER_BIN" -a 2>&1)
|
||||||
|
DISCOVERY_SUCCESS=$(echo "$DISCOVERY_OUTPUT" | grep "DISCOVERY_SUCCESS=" | cut -d= -f2)
|
||||||
|
|
||||||
|
if [ "$DISCOVERY_SUCCESS" = "true" ]; then
|
||||||
|
echo -e "${GREEN}✓ Buffer sizes discovered successfully${NC}"
|
||||||
|
QMOD=$(echo "$DISCOVERY_OUTPUT" | grep "DISCOVERY_QMOD=" | cut -d= -f2)
|
||||||
|
QPIGS=$(echo "$DISCOVERY_OUTPUT" | grep "DISCOVERY_QPIGS=" | cut -d= -f2)
|
||||||
|
QPIRI=$(echo "$DISCOVERY_OUTPUT" | grep "DISCOVERY_QPIRI=" | cut -d= -f2)
|
||||||
|
QPIWS=$(echo "$DISCOVERY_OUTPUT" | grep "DISCOVERY_QPIWS=" | cut -d= -f2)
|
||||||
|
|
||||||
|
echo " • QMOD: $QMOD bytes"
|
||||||
|
echo " • QPIGS: $QPIGS bytes"
|
||||||
|
echo " • QPIRI: $QPIRI bytes"
|
||||||
|
echo " • QPIWS: $QPIWS bytes"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ Buffer discovery failed, using defaults${NC}"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${YELLOW}[1.2] Parallel Inverters Discovery${NC}"
|
||||||
|
PARALLEL_OUTPUT=$($SUDO_CMD "$INVERTER_BIN" -p 2>&1)
|
||||||
|
PARALLEL_COUNT=$(echo "$PARALLEL_OUTPUT" | grep "PARALLEL_COUNT=" | cut -d= -f2)
|
||||||
|
|
||||||
|
if [ -z "$PARALLEL_COUNT" ]; then
|
||||||
|
PARALLEL_COUNT=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $PARALLEL_COUNT -gt 0 ]; then
|
||||||
|
echo -e "${GREEN}✓ Found $PARALLEL_COUNT parallel inverter(s)${NC}"
|
||||||
|
for i in $(seq 1 $PARALLEL_COUNT); do
|
||||||
|
SERIAL=$(echo "$PARALLEL_OUTPUT" | grep "INVERTER_${i}_SERIAL=" | cut -d= -f2)
|
||||||
|
QPGS_IDX=$(echo "$PARALLEL_OUTPUT" | grep "INVERTER_${i}_QPGS=" | cut -d= -f2)
|
||||||
|
echo " • Inverter #$i: Serial $SERIAL (QPGS$QPGS_IDX)"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}⚠ No parallel inverters found, using single mode${NC}"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Phase 2: Main Loop
|
||||||
|
echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${BLUE}║ PHASE 2: MAIN POLLING LOOP ║${NC}"
|
||||||
|
echo -e "${BLUE}╚════════════════════════════════════════════════════════════════╝${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
iteration=0
|
||||||
|
while true; do
|
||||||
|
iteration=$((iteration + 1))
|
||||||
|
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
|
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
|
||||||
|
echo -e "${BLUE}Iteration #$iteration - $timestamp${NC}"
|
||||||
|
echo -e "${BLUE}═══════════════════════════════════════════════════════════════${NC}"
|
||||||
|
|
||||||
|
# Test standard commands (local inverter)
|
||||||
|
echo -e "${YELLOW}[2.1] Testing standard commands (local inverter)${NC}"
|
||||||
|
for cmd in QPIGS QPIRI QMOD QPIWS; do
|
||||||
|
result=$($SUDO_CMD "$INVERTER_BIN" -r $cmd 2>&1 | grep "Reply:" | cut -d: -f2- | xargs)
|
||||||
|
if [ ! -z "$result" ] && [ "$result" != "NAK" ]; then
|
||||||
|
echo -e " ${GREEN}✓${NC} $cmd: OK (${#result} chars)"
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} $cmd: FAIL ($result)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Get parallel data
|
||||||
|
if [ $PARALLEL_COUNT -gt 0 ]; then
|
||||||
|
echo -e "${YELLOW}[2.2] Reading parallel inverters data${NC}"
|
||||||
|
for i in $(seq 1 $PARALLEL_COUNT); do
|
||||||
|
SERIAL=$(echo "$PARALLEL_OUTPUT" | grep "INVERTER_${i}_SERIAL=" | cut -d= -f2)
|
||||||
|
QPGS_IDX=$(echo "$PARALLEL_OUTPUT" | grep "INVERTER_${i}_QPGS=" | cut -d= -f2)
|
||||||
|
|
||||||
|
QPGS_DATA=$($SUDO_CMD "$INVERTER_BIN" -r "QPGS$QPGS_IDX" 2>&1 | grep "Reply:" | cut -d: -f2- | xargs)
|
||||||
|
|
||||||
|
if [ ! -z "$QPGS_DATA" ] && [ "$QPGS_DATA" != "NAK" ]; then
|
||||||
|
# Parse key values
|
||||||
|
IFS=' ' read -ra DATA <<< "$QPGS_DATA"
|
||||||
|
MODE="${DATA[2]}"
|
||||||
|
GRID_V="${DATA[4]}"
|
||||||
|
BATT_V="${DATA[11]}"
|
||||||
|
LOAD_W="${DATA[9]}"
|
||||||
|
|
||||||
|
echo -e " ${GREEN}✓${NC} Inverter #$i ($SERIAL): Mode=$MODE, Grid=${GRID_V}V, Battery=${BATT_V}V, Load=${LOAD_W}W"
|
||||||
|
else
|
||||||
|
echo -e " ${RED}✗${NC} Inverter #$i ($SERIAL): No data"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# MQTT Push simulation
|
||||||
|
echo -e "${YELLOW}[2.3] MQTT Push${NC}"
|
||||||
|
if [ -x "$MQTT_PUSH_SCRIPT" ]; then
|
||||||
|
echo " Running: $MQTT_PUSH_SCRIPT"
|
||||||
|
echo " Publishing to: $MQTT_SERVER:$MQTT_PORT"
|
||||||
|
MQTT_OUTPUT=$(bash "$MQTT_PUSH_SCRIPT" 2>&1 | tail -5)
|
||||||
|
echo "$MQTT_OUTPUT" | sed 's/^/ /'
|
||||||
|
|
||||||
|
# Show sample topics published
|
||||||
|
if [ $PARALLEL_COUNT -gt 0 ]; then
|
||||||
|
echo -e " ${GREEN}Sample topics published:${NC}"
|
||||||
|
for i in $(seq 1 $PARALLEL_COUNT); do
|
||||||
|
echo " • $MQTT_TOPIC/sensor/${MQTT_DEVICE}_inv${i}_serial"
|
||||||
|
echo " • $MQTT_TOPIC/sensor/${MQTT_DEVICE}_inv${i}_Battery_voltage"
|
||||||
|
echo " • $MQTT_TOPIC/sensor/${MQTT_DEVICE}_inv${i}_Load_watt"
|
||||||
|
[ $i -eq 1 ] && echo " • ... (and more)"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✓ MQTT push completed${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}⚠ MQTT script not executable: $MQTT_PUSH_SCRIPT${NC}"
|
||||||
|
echo " Simulating MQTT publish..."
|
||||||
|
if [ $PARALLEL_COUNT -gt 0 ]; then
|
||||||
|
echo " • Published data for $PARALLEL_COUNT inverters"
|
||||||
|
else
|
||||||
|
echo " • Published data for 1 inverter (single mode)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check iteration limit
|
||||||
|
if [ $MAX_ITERATIONS -gt 0 ] && [ $iteration -ge $MAX_ITERATIONS ]; then
|
||||||
|
echo -e "${GREEN}✓ Reached maximum iterations ($MAX_ITERATIONS)${NC}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Wait for next iteration
|
||||||
|
echo -e "${BLUE}Waiting ${LOOP_INTERVAL}s until next iteration...${NC}"
|
||||||
|
echo ""
|
||||||
|
sleep $LOOP_INTERVAL
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}╔════════════════════════════════════════════════════════════════╗${NC}"
|
||||||
|
echo -e "${GREEN}║ TEST LOOP COMPLETED ║${NC}"
|
||||||
|
echo -e "${GREEN}║ Total iterations: $iteration ║${NC}"
|
||||||
|
echo -e "${GREEN}╚════════════════════════════════════════════════════════════════╝${NC}"
|
||||||
Reference in New Issue
Block a user