Implementazione supporto multi-inverter paralleli e fix comunicazione MQTT
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:
Pi Developer
2026-01-31 16:15:26 +01:00
parent 8863c77f6f
commit 547537e761
18 changed files with 1842 additions and 70 deletions
+140
View File
@@ -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"
+225
View File
@@ -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}"