Skip to content

Instantly share code, notes, and snippets.

@mherweg
Created April 23, 2025 12:50
Show Gist options
  • Save mherweg/83d0469e52f89884f8a9a10c36b617c2 to your computer and use it in GitHub Desktop.
Save mherweg/83d0469e52f89884f8a9a10c36b617c2 to your computer and use it in GitHub Desktop.
python script that reads energy & power from 2 SMA Inverters and sends it to MQTT and pvoutput.org
#!/usr/bin/env python3
#
# reads energy & power from 2 SMA Inverters and sends it to MQTT and pvoutput.org
#
from pymodbus.client import ModbusTcpClient
import struct
import time
import paho.mqtt.client as mqtt
# Modbus TCP connection parameters
MODBUS_IP = 'xx.xx.xx.xx' # Replace with your Modbus device IP
MODBUS_IP2 = 'xx.xx.xx.xx'
MODBUS_PORT = 502
MODBUS_ID = 126
# MQTT broker parameters
MQTT_BROKER = 'mqtt.example.org' # Replace with your MQTT broker address
MQTT_PORT = 1883
MQTT_TOPIC_64BIT = 'mytopic/total_energy'
MQTT_TOPIC_16BIT = 'mytopc/power'
# MQTT credentials
MQTT_USERNAME = ''
MQTT_PASSWORD = ''
import requests
from datetime import datetime
# Replace with your actual API key and system ID
API_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
SYSTEM_ID = 'nnnnn'
old_wh = 0
# Example data
DATE = datetime.now().strftime('%Y%m%d') # Format: yyyyMMdd
TIME = datetime.now().strftime('%H:%M') # Format: HH:mm
# PVOutput API endpoint
url = 'https://pvoutput.org/service/r2/addstatus.jsp'
headers = {
'X-Pvoutput-Apikey': API_KEY,
'X-Pvoutput-SystemId': SYSTEM_ID
}
# Modbus register addresses
# Assuming 64-bit value is stored in 4 consecutive 16-bit registers starting at address 0
# Assuming 16-bit value is stored in 1 register at address 4
REGISTER_64BIT_START = 40303-1
REGISTER_16BIT = 40200-1
def read_64bit_value(client, address):
# Read 4 registers (4 x 16 bits = 64 bits)
rr = client.read_holding_registers(address, 4, slave=MODBUS_ID )
if rr.isError():
print("Error reading 64-bit value")
return None
# Combine 4 registers into 64-bit integer (big endian)
registers = rr.registers
# Pack registers into bytes
bytes_value = struct.pack('>HHHH', *registers)
# Unpack as unsigned long long (64-bit)
value = struct.unpack('>Q', bytes_value)[0]
return value
def read_16bit_value(client, address):
rr = client.read_holding_registers(address, 1, slave=MODBUS_ID )
if rr.isError():
print("Error reading 16-bit value")
return None
return rr.registers[0]
def main():
old_wh = 0
# Connect to Modbus TCP
modbus_client = ModbusTcpClient(MODBUS_IP, port=MODBUS_PORT)
if not modbus_client.connect():
print("Failed to connect to Modbus server", MODBUS_IP )
return
modbus_client2 = ModbusTcpClient(MODBUS_IP2, port=MODBUS_PORT)
if not modbus_client2.connect():
print("Failed to connect to Modbus server", MODBUS_IP )
return
# Connect to MQTT broker
mqtt_client = mqtt.Client()
mqtt_client.username_pw_set(username=MQTT_USERNAME, password=MQTT_PASSWORD)
mqtt_client.connect(MQTT_BROKER, MQTT_PORT)
mqtt_client.loop_start()
try:
#while True:
val64 = read_64bit_value(modbus_client, REGISTER_64BIT_START)
val16 = read_16bit_value(modbus_client, REGISTER_16BIT)
scale =read_16bit_value(modbus_client, REGISTER_16BIT+1)
val64b = read_64bit_value(modbus_client2, REGISTER_64BIT_START)
val16b = read_16bit_value(modbus_client2, REGISTER_16BIT)
scaleb = read_16bit_value(modbus_client2, REGISTER_16BIT+1)
validation = 0
if val64 is not None and val64b is not None:
validation += 1
wh=(val64+val64b) #Wh
sum1 = wh/1000
#wh_diff = wh - old_wh
#old_wh = wh
mqtt_client.publish(MQTT_TOPIC_64BIT, str(sum1))
print(f"Published total production: {sum1}")
if val16 is not None and val16b is not None:
validation += 1
if val16 != 32768 and val16b != 32768:
sum2 = val16 * (scale*10) + val16b * (scaleb*10)
mqtt_client.publish(MQTT_TOPIC_16BIT, str(sum2))
print(f"Published active power: {sum2}")
if validation == 2:
payload = {
'd': datetime.now().strftime('%Y%m%d'), # Format: yyyyMMdd
't': datetime.now().strftime('%H:%M'),
'v1': wh,
'v2': sum2,
'c1': 1,
# 'v5': TEMPERATURE, # Optional
# 'v6': VOLTAGE # Optional
}
response = requests.post(url, headers=headers, data=payload)
if response.status_code == 200:
print('Data uploaded successfully.')
else:
print(f'Failed to upload data. Status code: {response.status_code}')
print(response.text)
# time.sleep(300) # 5 minuten
except KeyboardInterrupt:
print("Stopping...")
finally:
mqtt_client.loop_stop()
modbus_client.close()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment