Our plans to upgrade our solar PV system this year are slowly progressing with installing a new Solis 1.5kW inverter and an EM112 Series Energy Meter, which have RS485 output and communicate using Modbus protocol.

Following being scammed by the Zero Home Bills solar panel supplier, we placed an order with another supplier whose website showed that they had the panels we required in stock, but after a few days, they contacted me to say they did not have them, and the website hadn't been updated for a long time. They are now looking for alternative panels which are similar dimensions to fit across our roof.

Solar Panel Sizes

The space available on our roof is 4 meters across and 3.8 meters high, and with the fixing hardware, we would not be able to fit four regular-size panels as they are typically 2,108mm x 1,048mm in size; this would limit us to a single row of 3 panels, and with 400W panels, this would only give us a theoretical maximum output of 1.2kW.

We found panels produced by JA Solar which are 1689 x 996mm and have a 330W output, and we could fit 5 or 6 of these on the roof between the chimneys. To minimise shadows over the top row of panels, we decided to mount 2 on the top row and 3 below, which will give us a theoretical maximum output of 1.6kW, which is suitable for the Solis Solar 1.5kW Mini 5G Inverter.

To mount the new panels, we ordered 4 x 3.3m mounting rails and associated fixings and brackets from Dragons Breath Solar, who have been very helpful when selecting the hardware needed.

New install in the basement

We decided to move our solar PV inverter installation from its previous location in the loft to the shed below the house. This has thick stone walls and a concrete ceiling and has space to install 2 inverters and a battery system later if required.

To get the solar PV power from the roof down to the basement shed, we purchased 20 meters of 6mm armoured cable; this was routed down the back of the house and into the shed through the stone wall.

This feeds into a DC isolator and onto the new solar PV inverter. 

The new inverter is connected to a double pole mains isolator. It has an EM112 Series Energy Meter fitted in the same box to monitor energy production and consumption of the inverter. This is connected to the fuse box and has a 16 amp RCBO to protect the circuit.

Reading Data for Home Assistant

Solis Cloud
Solis Cloud Screenshot

Using the Solis Data Logging Stick, which uses WiFi to communicate, we were able to set up the inverter to send data to the remote soliscloud service, but we could not find any way to get capture data apart from scraping the basic status page built into the WiFi adapter which only gave watts being produced and daily output.

As the Solis inverter uses RS485 to communicate with the data logging stick, we tried to connect to the inverter using one of our RS485 Raspberry Pi expansion boards from abelectronics.co.uk/p/77/rs485-pi and a Raspberry Pi 3. I found code on sequr.be/blog, which reads the data from the inverter and sends it to an MQTT broker. This is how we wanted to use the data with Home Assistant reading the MQTT data from the broker. We can also use this to upload to our long-term storage data logger online.

The data COM port on the base of the Solis inverter uses a small round 4-pin plug with a screw thread to hold it in place when connected. After a lot of searching, I found that the connector is an Exceedconn EC04681-2014-BF. Pin 3 on the connector is connected to A on the RS485 Pi, and pin 4 is connected to B.

RS485 uses two wires and sometimes a ground connection, so we used a twin-screen cable to go between the inverter to the EM112 Series Energy Meter and then onto the Raspberry Pi with a DB9 connector connected to the RS485 Pi board. This shields the inner data wires from external interference.

On the Raspberry Pi, we also installed a 1-Wire Pi Plus board, and this is used with a temperature sensor fitted in the shed to monitor the air temperature, which is also posted to the MQTT server and Home Assistant.

Setting up the Raspberry Pi

We installed a new Micro SD card with the latest Raspberry Pi OS, and after updating the software, the serial port and i2c bus were enabled.

The following packages are installed using:

sudo apt update
sudo apt install -y python3 python3-pip
python3 -m pip install -U pyserial
python3 -m pip install -U minimalmodbus
python3 -m pip install -U paho.mqtt
        

Troubleshooting connection issues with the Solis Inverter

With the Solis inverter connected to the Raspberry Pi, we were initially unable to communicate with the inverter, and the Python scripts returned connection errors.

We tried several RS485 Pi hats, but all had the same issue.

With the RS485 Pi connected to the energy meter, we could read the data but not from the inverter.

After several frustrating days of trying different combinations of RS485 hats, different wiring connections, with and without termination resistors and ordering a USB RS485 adapter which would also fail to connect, we tried to install the Data logging WiFi adapter. We connected the A and B wires to the pins on the adapter's circuit board. With the inverter set to address 2 we could then read the data from the Raspberry Pi, but the WiFi adapter could not read the data as it was expecting the inverter to be on address 1.

Solis WiFi Data Logger Adapter
Solis WiFi Data Logger Adapter

Removing the WiFi adapter and connecting directly to the inverter's COM port resulted in no data again so we knew that there was something on the WiFi adapter enabling the data from the inverter.

The adapter was placed under the microscope, and we traced the data A & B lines from the plug to the RS485 chip on the PCB. We found that there are 10K ohm resistors fitted on the A & B data lines with the A channel having a 10K pull-up resistor to 3.3 volts and the B channel having a 10K pull-down resistor to ground.

We then added the 10K pull-up and pull-down resistors to the RS485 Pi board and connected it directly to the Solis inverter. The RS498 bus was working correctly, and we could read values from the device.

I found several documents related to the available Modbus registers on the Solis inverters, but the list on https://www.aggsoft.com/serial-data-logger/tutorials/modbus-data-logging/ginlong-solis-pv.htm seems to be the most compressive.

Raspberry Pi
Raspberry Pi with RS485 and 1 Wire board
Troubleshooting
Troubleshooting the connection issues

Python Code and upload scripts

To test the Solis Inverter, we created the following Python script, which connects to the inverter with its address ID set as 2 and then reads the data from the registers:

import minimalmodbus
import socket
import serial
import time

solis1500 = minimalmodbus.Instrument('/dev/ttyAMA0', 2)
solis1500.serial.baudrate = 9600
solis1500.serial.bytesize = 8
solis1500.serial.parity = serial.PARITY_NONE
solis1500.serial.stopbits = 1
solis1500.serial.timeout = 3

# get data from solis mini 3000

print("SSW (Solis Mini 3000 4G)")
AlltimeEnergy_KW_z = solis1500.read_long(3008, functioncode=4, signed=False) # Read All Time Energy (KWH Total) as Unsigned 32-Bit
print("Generated (All time):" +str(AlltimeEnergy_KW_z) + "kWh")
Today_KW_z = solis1500.read_register(3014, functioncode=4, signed=False) # Read Today Energy (KWH Total) as 16-Bit
print("Generated (Today) :" + str(Today_KW_z/10) + "kWh")

Solis1500_ACW = solis1500.read_long(3004, functioncode=4, signed=False) # Read AC Watts as Unsigned 32-Bit

Solis1500_DCV = solis1500.read_register(3021, number_of_decimals=1, functioncode=4, signed=False) # Read DC Volts as Unsigned 16-Bit

Solis1500_DCI = solis1500.read_register(3022, number_of_decimals=0, functioncode=4, signed=False) # Read DC Current as Unsigned 16-Bit

Solis1500_ACV = solis1500.read_register(3035, number_of_decimals=1, functioncode=4, signed=False) # Read AC Volts as Unsigned 16-Bit

Solis1500_ACI = solis1500.read_register(3038, number_of_decimals=0, functioncode=4, signed=False) # Read AC Current as Unsigned 16-Bit

Solis1500_ACF = solis1500.read_register(3042, number_of_decimals=2, functioncode=4, signed=False) # Read AC Frequency as Unsigned 16-Bit

Solis1500_Temp = solis1500.read_register(3041, number_of_decimals=1, functioncode=4, signed=True) # Read Inverter Temperature as Signed 16-Bit

Solis1500_AlltimeEnergy_KW = solis1500.read_long(3008, functioncode=4, signed=False) # Read All Time Energy (KWH Total) as Unsigned 32-Bit

Solis1500_Today_KW = solis1500.read_register(3014, number_of_decimals=1, functioncode=4, signed=False) # Read Today Energy (KWH Total) as 16-Bit

print("{:<23s}{:10.2f} W".format("AC Watts", Solis1500_ACW))
                       print("{:<23s}{:10.2f} V".format("DC Volt", Solis1500_DCV))
print("{:<23s}{:10.2f} A".format("DC Current", Solis1500_DCI))
print("{:<23s}{:10.2f} V".format("AC Volt", Solis1500_ACV))
print("{:<23s}{:10.2f} A".format("AC Current", Solis1500_ACI))
print("{:<23s}{:10.2f} Hz".format("AC Frequency", Solis1500_ACF))
print("{:<23s}{:10.2f} °C".format("Inverter Temperature", Solis1500_Temp))
print("{:<23s}{:10.2f} kWh".format("Generated (All time)", Solis1500_AlltimeEnergy_KW))
print("{:<23s}{:10.2f} kWh".format("Generated (Today)", Solis1500_Today_KW))

To test the EM112 Series Energy Meter, we created the following Python script, which connects to the meter, which had its address ID set as 5 and then reads the registers data:

import minimalmodbus
import serial

### Registers

REG_VOLTS =         0x00  # 2 word INT32
REG_AMPS =          0x02  # 2 word INT32
REG_WATTS =         0x04  # 2 word INT32
REG_VA =            0x06  # 2 word INT32
REG_VAR =           0x08  # 2 word INT32
REG_W_DMD =         0x0A  # 2 word INT32
REG_W_DMD_PEAK =    0x0C  # 2 word INT32
REG_PF =            0x0E  # 1 word INT32
REG_HZ =            0x0F  # 1 word INT32
REG_KWP_TOTAL =     0x10  # 2 word INT32
REG_KVARH_TOTAL =   0x12  # 2 word INT32
REG_KWP_PARTIAL =   0x14  # 2 word INT32
REG_KVARH_PARTIAL = 0x16  # 2 word INT32
REG_KWN_TOTAL =     0x20  # 2 word INT32

instrument = minimalmodbus.Instrument('/dev/ttyAMA0', 5, debug = True)
instrument.serial.baudrate = 9600   # Baud
instrument.serial.bytesize = 8
instrument.serial.parity   = serial.PARITY_NONE
instrument.serial.stopbits = 1
instrument.clear_buffers_before_each_transaction = True
instrument.serial.timeout  = 0.5   # seconds

Realtime_Volts = instrument.read_register(REG_VOLTS, functioncode=4, signed=False)	 # Read Volts
Realtime_Amps = instrument.read_long(REG_AMPS, functioncode = 3, signed = True, byteorder = 3) / 1000	 # Read Volts
Realtime_Watts = instrument.read_long(REG_WATTS, functioncode = 3, signed = True, byteorder = 3) / 10	 # Read Volts
Realtime_KW_Partial = instrument.read_long(REG_KWP_PARTIAL, functioncode = 3, signed = True, byteorder = 3) / 10	 # Read Volts

print("Volts:               " + str(Realtime_Volts))
print("Amps:                " + str(Realtime_Amps))
print("Watts:               " + str(Realtime_Watts))
print("KW Partial:          " + str(Realtime_KW_Partial))

With the data now showing correctly from both devices, the next stage was to create a new Python script which reads the meter and inverter and uploads this to our MQTT server. To do this, we used the following code:

#!/usr/bin/python3
"""
Python script to monitor inverter electric meter and forward data to mqtt broker
"""
from __future__ import absolute_import, division, print_function, \
                                                    unicode_literals
import minimalmodbus
import serial
import sys  
import re
import paho.mqtt.client as mqtt
import sched, time

DEBUG = False

def trace(output):
    if DEBUG:
        print(output)

mqtt_broker = '192.168.1.10'
mqtt_user = 'user'
mqtt_pass = 'password'
mqtt_port = 1883

### Energy Meter Registers

### All registers use function code 4

METER_REG_VOLTS =         0x00  # 2 word INT32
METER_REG_AMPS =          0x02  # 2 word INT32
METER_REG_WATTS =         0x04  # 2 word INT32
METER_REG_VA =            0x06  # 2 word INT32
METER_REG_VAR =           0x08  # 2 word INT32
METER_REG_W_DMD =         0x0A  # 2 word INT32
METER_REG_W_DMD_PEAK =    0x0C  # 2 word INT32
METER_REG_PF =            0x0E  # 1 word INT32
METER_REG_HZ =            0x0F  # 1 word INT32
METER_REG_KWP_TOTAL =     0x10  # 2 word INT32
METER_REG_KVARH_TOTAL =   0x12  # 2 word INT32
METER_REG_KWP_PARTIAL =   0x14  # 2 word INT32
METER_REG_KVARH_PARTIAL = 0x16  # 2 word INT32
METER_REG_KWN_TOTAL =     0x20  # 2 word INT32

### Solis 1500 Registers

### All registers use function code 4

SOLIS1500_REG_ACWATTS =   3004  # Read AC Watts as Unsigned 32-Bit
SOLIS1500_REG_DCVOLTS =   3021  # Read DC Volts as Unsigned 16-Bit
SOLIS1500_REG_DCCURRENT = 3022  # Read DC Current as Unsigned 16-Bit
SOLIS1500_REG_ACVOLTS =   3035  # Read AC Volts as Unsigned 16-Bit
SOLIS1500_REG_ACCURRENT = 3038  # Read AC Current as Unsigned 16-Bit
SOLIS1500_REG_ACFREQ =    3042  # Read AC Frequency as Unsigned 16-Bit
SOLIS1500_REG_ALLTIMEKW = 3008  # Read All Time Energy (KWH Total) as Unsigned 32-Bit
SOLIS1500_REG_TODAYKW =   3014  # Read Today Energy (KWH Total) as 16-Bit

client = mqtt.Client("P1") #create new instance
energymeter = minimalmodbus.Instrument('/dev/ttyAMA0', 5, debug = False, close_port_after_each_call = True)
solis1500 = minimalmodbus.Instrument('/dev/ttyAMA0', 2, debug = False, close_port_after_each_call = True)

# The callback for when the client receives a CONNACK response from the server.
def on_connect(mqttc, obj, flags, rc):
    if rc!=0:
       trace("Bad connection Returned code=",rc)
       # trace("MQTT Connected with result code "+str(rc))
    #else:    

def on_disconnect(client, userdata, rc):
    trace("MQTT Disconnected with result code "+str(rc))

def data_loop(sc): 
    client.connect(mqtt_broker, mqtt_port, 60) # connect to mqtt broker

    # Get data from energy meter
    try:
        Realtime_Volts = energymeter.read_long(METER_REG_VOLTS, functioncode = 3, signed = True, byteorder = 3) / 10	 # Read Volts
        Realtime_Amps = energymeter.read_long(METER_REG_AMPS, functioncode = 3, signed = True, byteorder = 3) / 1000	 # Read Volts
        Realtime_Watts = energymeter.read_long(METER_REG_WATTS, functioncode = 3, signed = True, byteorder = 3) / 10	 # Read Volts
        Realtime_KW_Partial = energymeter.read_long(METER_REG_KWP_PARTIAL, functioncode = 3, signed = True, byteorder = 3) / 10	 # Read Volts

        client.publish("/home/solarpv/meter/volts",Realtime_Volts)
        client.publish("/home/solarpv/meter/amps",Realtime_Amps)
        client.publish("/home/solarpv/meter/watts",Realtime_Watts)
        client.publish("/home/solarpv/meter/kwtotal",Realtime_KW_Partial)
    except Exception as err:
        print(err)

    # Get data from Solis 1500 Inverter
    try:        
        Solis1500_DCV = solis1500.read_register(3021, number_of_decimals=1, functioncode=4, signed=False) # Read DC Volts as Unsigned 16-Bit
        Solis1500_DCI = solis1500.read_register(3022, number_of_decimals=0, functioncode=4, signed=False) / 10 # Read DC Current as Unsigned 16-Bit
        Solis1500_ACV = solis1500.read_register(3035, number_of_decimals=1, functioncode=4, signed=False) # Read AC Volts as Unsigned 16-Bit
        Solis1500_ACI = solis1500.read_register(3038, number_of_decimals=0, functioncode=4, signed=False) / 10 # Read AC Current as Unsigned 16-Bit
        Solis1500_ACW = solis1500.read_long(3004, functioncode=4, signed=False) # Read AC Watts as Unsigned 32-Bit
        Solis1500_ACF = solis1500.read_register(3042, number_of_decimals=2, functioncode=4, signed=False) # Read AC Frequency as Unsigned 16-Bit
        Solis1500_Temp = solis1500.read_register(3041, number_of_decimals=1, functioncode=4, signed=True) # Read Inverter Temperature as Signed 16-Bit
        Solis1500_AlltimeEnergy_KW = solis1500.read_long(3008, functioncode=4, signed=False) # Read All Time Energy (KWH Total) as Unsigned 32-Bit
        Solis1500_Today_KW = solis1500.read_register(3014, number_of_decimals=1, functioncode=4, signed=False) # Read Today Energy (KWH Total) as 16-Bit

        client.publish("/home/solarpv/pvvolt",Solis1500_DCV)
        client.publish("/home/solarpv/pvamp",Solis1500_DCI)
        client.publish("/home/solarpv/gridvolt",Solis1500_ACV)
        client.publish("/home/solarpv/gridcurrent",Solis1500_ACI)
        client.publish("/home/solarpv/gridpower",Solis1500_ACW)
        client.publish("/home/solarpv/gridfrequency",Solis1500_ACF)        
        client.publish("/home/solarpv/devicetemp",Solis1500_Temp)        
        client.publish("/home/solarpv/totalpower",Solis1500_AlltimeEnergy_KW)
        client.publish("/home/solarpv/todaypower",Solis1500_Today_KW)
        client.publish("/home/solarpv/online",1)

        trace("{:<23s}{:10.2f} W".format("AC Watts", Solis1500_ACW))
                               trace("{:<23s}{:10.2f} V".format("DC Volt", Solis1500_DCV))
        trace("{:<23s}{:10.2f} A".format("DC Current", Solis1500_DCI))
        trace("{:<23s}{:10.2f} V".format("AC Volt", Solis1500_ACV))
        trace("{:<23s}{:10.2f} A".format("AC Current", Solis1500_ACI))
        trace("{:<23s}{:10.2f} Hz".format("AC Frequency", Solis1500_ACF))
        trace("{:<23s}{:10.2f} °C".format("Inverter Temperature", Solis1500_Temp))
        trace("{:<23s}{:10.2f} kWh".format("Generated (All time)", Solis1500_AlltimeEnergy_KW))
        trace("{:<23s}{:10.2f} kWh".format("Generated (Today)", Solis1500_Today_KW))
    except Exception as err:
        trace(err)
        client.publish("/home/solarpv/pvvolt",0)
        client.publish("/home/solarpv/pvamp",0)
        client.publish("/home/solarpv/gridvolt",0)
        client.publish("/home/solarpv/gridcurrent",0)
        client.publish("/home/solarpv/gridpower",0)
        client.publish("/home/solarpv/gridfrequency",0)
        client.publish("/home/solarpv/devicetemp",0)
        client.publish("/home/solarpv/online",0)

    client.disconnect() # disconnect from mqtt broker

    sc.enter(60, 1, data_loop, (sc,))

def main():

    s = sched.scheduler(time.time, time.sleep)

    client.username_pw_set(mqtt_user, mqtt_pass)
    client.on_connect = on_connect
    client.on_disconnect = on_disconnect

    energymeter.serial.baudrate = 9600   # Baud
    energymeter.serial.bytesize = 8
    energymeter.serial.parity   = serial.PARITY_NONE
    energymeter.serial.stopbits = 1
    energymeter.clear_buffers_before_each_transaction = True
    energymeter.serial.timeout  = 0.5   # seconds
    energymeter.mode = minimalmodbus.MODE_RTU

    solis1500.serial.baudrate = 9600   # Baud
    solis1500.serial.bytesize = 8
    solis1500.serial.parity   = serial.PARITY_NONE
    solis1500.serial.stopbits = 1
    solis1500.clear_buffers_before_each_transaction = True
    solis1500.serial.timeout  = 0.5   # seconds
    solis1500.mode = minimalmodbus.MODE_RTU

    s.enter(60, 1, data_loop, (s,))
    s.run()

if __name__ == "__main__":
    main()

The script sends the data to an MQTT server on IP 192.168.1.10. This needs to be changed with the username and password to suit your MQTT server. The script runs every 60 seconds.

For the Maxim ds18b20 1-wire temperature sensor, we used the following Python script, which reads the connected sensor and the Raspberry Pi CPU temperature and uploads it to the MQTT server:

#!/usr/bin/env python3
"""
Python script to monitor serial port and forward sensor data to mqtt broker
"""
from __future__ import absolute_import, division, print_function, \
                                                    unicode_literals
import serial
import datetime
import sys  
import re
import paho.mqtt.client as mqtt
import sched, time
DEBUG = False

def trace(output):
    if DEBUG:
        print(output)

mqtt_topics = {
    "/sys/bus/w1/devices/28-000003c45227/temperature": "/home/shed/temperature", #shed temperature
    "/sys/class/thermal/thermal_zone0/temp": "/home/devices/solar_raspberry_pi/temperature", #solar raspberry pi temperature
}

mqtt_broker = '192.168.1.10'
mqtt_user = 'user'
mqtt_pass = 'password'
mqtt_port = 1883

client = mqtt.Client("P1") #create new instance

# The callback for when the client receives a CONNACK response from the server.
def on_connect(mqttc, obj, flags, rc):
    if rc!=0:
       trace("Bad connection Returned code=",rc)
       # trace("MQTT Connected with result code "+str(rc))
    #else:
        
def on_disconnect(client, userdata, rc):
    trace("MQTT Disconnected with result code "+str(rc))

def data_loop(sc): 
    try:
        # loop through sensors and get values
        for sensor, topic in mqtt_topics.items():
            with open(sensor, 'r') as file:
                data = file.read()
                    
                temperature = int(data) / 1000
                trace(temperature)
                # publish to mqtt
                client.connect(mqtt_broker, mqtt_port, 60)
                client.publish(topic,temperature)
                client.disconnect()
    except Exception as err:
        trace("Failed to open files: {0}".format(err))

    sc.enter(60, 1, data_loop, (sc,))

def main():
    s = sched.scheduler(time.time, time.sleep)

    client.username_pw_set(mqtt_user, mqtt_pass)
    client.on_connect = on_connect
    client.on_disconnect = on_disconnect

    s.enter(60, 1, data_loop, (s,))
    s.run()
    

if __name__ == "__main__":
    main()

Home Assistant

To add the new data to Home Assistant, new MQTT sensors were added to the configuration.yaml

# Start solar pv 

- platform: mqtt
  state_topic: "/home/solarpv/pvvolt"
  name: "SolarPV Panel Volts"
  unit_of_measurement: V
  scan_interval: 30

- platform: mqtt
  state_topic: "/home/solarpv/pvamp"
  name: "SolarPV Panel Amps"
  unit_of_measurement: A
  scan_interval: 30

- platform: mqtt
  state_topic: "/home/solarpv/gridpower"
  name: "SolarPV GridPower"
  unit_of_measurement: W
  scan_interval: 30

- platform: mqtt
  state_topic: "/home/solarpv/totalpower"
  name: "SolarPV TotalGenerated"
  unit_of_measurement: Kwh
  scan_interval: 30
  
- platform: mqtt
  state_topic: "/home/solarpv/devicetemp"
  name: "SolarPV Inverter Temp"
  unit_of_measurement: C
  scan_interval: 20

- platform: mqtt
  state_topic: "/home/solarpv/todaypower"
  name: "SolarPV TodayGenerated"
  unit_of_measurement: Kwh 
  scan_interval: 30

- platform: mqtt
  state_topic: "/home/solarpv/online"
  name: "SolarPV Inverter Online"
  scan_interval: 30


- platform: mqtt
  state_topic: "/home/solarpv/meter/amps"
  name: "SolarPV Total Meter Amps"
  unit_of_measurement: A
  scan_interval: 30
  
- platform: mqtt
  state_topic: "/home/solarpv/meter/watts"
  name: "SolarPV Total Meter GridPower"
  unit_of_measurement: W
  scan_interval: 30

- platform: mqtt
  state_topic: "/home/solarpv/meter/kwtotal"
  name: "SolarPV Total Meter TotalGenerated"
  unit_of_measurement: Kwh 
  scan_interval: 30


- platform: mqtt
  state_topic: "/home/shed/temperature"
  name: "Shed Temperature"
  unit_of_measurement: °C 
  scan_interval: 30

- platform: mqtt
  state_topic: "/home/devices/solar_raspberry_pi/temperature"
  name: "Shed RaspberryPi Temperature"
  unit_of_measurement: °C  
  scan_interval: 30


# End solar pv   

Home Assistant needs to be restarted to enable the new sensors, and after a reboot, they can be added to the user interface on a card of your choice.

Mains isolator and meter box
Mains isolator and meter
Inverter and Logging installed
Inverter and Logging installed

Parts Used:

Solis Solar Inverter 1.5kW Mini 5G www.itstechnologies.shop/products/solis-solar-inverter-1-5kw-mini-5g-single-tracker-with-dc-isolator

Solis Data Logging Stick – WiFi www.itstechnologies.shop/products/solis-data-logging-stick-wifi-gen-3

EM112 Series Energy Meter uk.farnell.com/carlo-gavazzi/em112dinav01xs1x/digital-energy-meter-1-phase-230vac/dp/2672858

1 Wire Pi Plus abelectronics.co.uk/p/60/1-wire-pi-plus

RS485 Pi abelectronics.co.uk/p/77/rs485-pi

Maxim ds18b20 uk.farnell.com/maxim-integrated-products/ds18b20/temperature-sensor/dp/2515553

WYLEX 4-module 2-way part populated the the main switch consumer unit: screwfix.com/p/wylex-4-module-2-way-part-populated-main-switch-consumer-unit/3056j