rasProject_01 / weSweetHome  R. 146 2026-09-26
process control       /     RasPi software         by   weinert-automation
Loading...
Searching...
No Matches
growattLink.c File Reference

Communication with a Growatt inverter. More...

#include "arch/config.h"
#include <stdint.h>
#include "mqttHome.h"
#include "growattHome.h"
#include "weUtil.h"
#include "weStateM.h"
#include "weLockWatch.h"
#include "weShareMem.h"
#include <errno.h>
#include <sys/sem.h>
#include <getopt.h>
#include "weModbus.h"
#include "weGPIOd.h"

Macros

#define LEDgn
 green LED
#define LEDrd
 red LED
#define LEDye
 yellow LED

Functions

void chgFilNames (void)
 Set the current date in event & retain file paths.
int main (int argc, char **argv)
 The program.
void mqttClean ()
 End as MQTT client.
int mqttInit ()
 Initialise as MQTT client.
void * processIOthread (void *args)
 The task of controlling process IO.
void * rs232ModThread (void *args)

Variables

char clientId [38]
 MQTT client ID.
char messSttOffPlg [6]
 State message: a S20 plug is now off.
char messSttOnPlg [6]
 State message: a S20 plug is now on.
modBvals_t modBvals
 modbus respectively process values
struct mosquitto * mosq
 MQTT interface state.
int mosqLoopError
 Return value of mosquitto_loop.
char mqttHost [68]
 MQTT broker address or name.
int mqttPort
 MQTT broker port.
char const prgNamPure []
 The pure program name.
char const prgSVNdat []
 The complete SVN date string.
char const prgSVNrev []
 The complete SVN revision string.
char const subTopGrow [14]
 Relative topic for Growatt readings.
char subTopStPlg01 [14]
 State sub topic of S20 plug Number 01 to 09.
char const topicSubs [20]
 The MQTT topic to subscribe to.

Detailed Description

Communication with a Growatt inverter.

The program's task is to communicate with a Growatt PV (photovoltaic) inverter via its RS232 [sic!] Modbus interface. Data are requested on a regular basis from the input and holding registers. Hint: While PV input vanishes the Growatt inverter produces many Modbus errors. Their report is suppressed.

The program runs on a Raspberry with GUI-less (no graphics) Raspbian lite. GUI/HMI is featured as Web-Interface (Apache, C CGI) in the (W)LAN.

This program was originally implemented quick and dirty = non-structured programming. This sin is to be repaired, soon! (But has not, yet.)

Copyright (c) 2019 2026 Albrecht Weinert
weinert-automation.de a-weinert.de
/ / /\
/ /___ / \ |
\ /____\ /____\ | _|__
\ /\ / \ / \| |
\/ \/ \__/ \__/|_

Revision history

Rev. 146 20.09.2026
Rev. 213 13.08.2019 : new; derived from hometersControl.c
Rev. 216 31.08.2019 : first operable with Modbus outages (hopefully)
Rev. 219 06.11.2019 : Growatt MQTT all important values in one message
Rev. 270 07.11.2024 : use old includes before meterpi crash ("BC")
Rev. 76 22.02.2025 : from pi to sweet. Rev. jump due to 1&1's server close.
Rev. 81 15.04.2025 : serial gone, now /dev/ttyS0
Rev. 82 18.04.2025 : mosquitto_loop 3 times in a second en lieu de once
Rev. 83 04.05.2025 : no new log file when short; suppr. Modbus erropr flood
Rev. 83b 18.05.2025 : read recovery log corrected. vvvv
2025-05-18 06:05:34.129 ## read recovered after -2055142704 bad
Rev. 85 15.06.2025 : inhibit endless semaphore lock error logs
Rev. 114 17.04.2026 : typo, comments
Rev. 144 08.09.2026 : transfer hold registers only once, comments

Modbus usage

This program acts as client for one Growatt inverter 1500P "GEAD1018" with firmware G.2.0 and Modbus protocol version 3.01. It is a single line phase, single PV array and zero battery type.
Communication setting will be 'RTU / RS232 9600, none'.

Our Growatt inverter's Modbus protocol version seems to be V3.04 – or at least our software implemented against this specification works. But beware, other versions are totally incompatible by having many variables at different register addresses. Our implementation is not for "Protocol_II".

Input registers read: 34 starting at [0] and ...:

[0 ] Inverter state 0=waiting, 1=normal, 3:fault
[1 2 ] Ppv Input power (high low) 0.1W
[3 ] Ppv1 PV1 voltage 0.1V
[4 ] PV1Curr PV1 input current 0.1A
[5 6 ] PV1 input power (high low) 0.1W
[7...10] dto. for PV2, not present in our one panel only inverter
[11 12] Pac Output power (high low) 0.1W
[13] Fac Grid frequency 0.01Hz
[14] Vac1 one / 3phase grid U 0.1V
[15] Iac1 one / 3phase grid I 0.1A
[16 17] Pac1 dto. (high low) P 0.1VA
[18..25] dto. for L2 and L3, not present in our one phase only inverter
[26 27] Energy today (high low) Today generated energy 0.1KWH
[28 29] Energy total (high low) Totally generated energy 0.1KWH
[30 31] Work time total (high low) 0.5 kWH
[32] T Inverter temperature 0.1 grdC
[33] ISO fault Value 0.1V (isolation fault in V??)

Holding registers read: 22 starting at [9]:

[0 ] OnOff Bit 0x0001 = On; Bit 0x0100 = autoStart;
09. Fw version H Firmware version (high) ASCII
10. Fw version M Firmware version (middle)
11. Fw version L Firmware version (low)
12. Fw version2 H Control Firmware version (high)
13. Fw version2 M Control Firmware version (middle)
14. Fw version2 L Control Firmware version (low)
15. LCD language 0-5 0: Italian; 1: English; 2: German; 3: Spanish;
4: French; 5: Chinese; 6:Polish; 7:Portugues; 8:Hungary
16. Country Selected (1) 0 mean not selected
17. Vpv start Input start voltage 0.1V
18. Time start Start time 1s
19. RestartDelayTime Restart Delay Time after fault back; 1s
20. Power Start Slope Power start slope 1-1000 0.1%
21. Power Restart Slope Power restart slope 1-1000 0.1%
22. Select Baud rate Select communication baud rate 0-1
0: 9600bps 1:38400bps default 0
23. Serial NO Serial number 1-2 ASCII
24-27 Serial NO Serial number 3-10
28-29 Module H Inverter Module (high low) &*5 [sic!] (what the hell?)
30. Com Address Communicate address 1-254 default 1
tcflag_t baud
The UART's baud rate as flag bits.
Definition weSerial.c:154

was read: 20 starting at [9] and 15 starting at [59]:

[59...]: "GrowattInverterGEAD1018", 0, 0, 301 (ModbusVer), 1, 0 ...

Common memory

Common memory is used to communicate with other programs. At present this is a a small CGI program handling the AJAX link to the Apache based Web interface.

The Modbus data and made available to other programs on the same Pi, like e.g. the GCI program growattRead.c, by shared memory. Locking and signalling for the shared memory is done by a semaphore set. This is the same approach as in hometersControl.c.

MQTT

The most important panel/inverter values are signalled to programs on other Pis (hometersControl) by MQTT. The MQTT message has the form: Up: 119.6V P: 65.0W L: 7.2W T: 26.5gdC Wd/t: 1.2 / 2316.2kWh featuring Panel DC voltage, line Power (to grid), inverter loss and temperature, day's and total work. Currently retain is set to false. Test by:

mqtt-CLI.exe sub --host=meterPi --topic=labExp/sweetHome/grow01/meas

MQTT protocol via Ethernet or WLAN is used to publish essential inverter values and data. The MQTT broker or host defaults to 192.168.178.87, the port is 1883.

A backlink via HTTP parameter and shared memory and/or MQTT to control the inverter (via holding registers) is not implemented but may be in the future.

GPIO usage

This communication program currently uses no GPIO pins but includes all libraries etc. to do so.
As Test it uses three pins as output assuming LEDs connected to defined in include/target_growPi.h as Hi=On

Pi 1 / Pi 3 Pin
GPIO17/ 17 : red 11
GPIO21/ 27 : green 13
GPIO26 : yellow 47

Timing

The program has a cyclic process control (in SPS manner). We have

a) a 100 ms cycle for process control, process I/O, SFCs etc.
b) an 1s cycle for Modbus communication, timer handling and some SFCs

Server functions

Shared memory and a set of three semaphores is provided to share current values with other (C) programs as well as (in future) for receiving command and status information. This interface is also used to provide web interfaces in a flexible way.

Client functions

The program acts as (see above) as Modbus client. And it acts as MQTT publisher currently using a mosquitto broker on another Raspberry in the same private (W)LAN.

Library usage

The program uses the standard libraries pthread, pigpiod_if2, modbus, shm, sem and mosquitto. The own libraries in weRasp/..c and include/..h, namely weModbus, weGPIOd, sysUtil, weShareMem, weCGIajax etc. are compiled and linked in.

Build the program

cross-compile by:

make PROGRAM=growattLink TARGET=growPi clean all

program respectively transfer to target machine by:

make PROGRAM=growattLink TARGET=growPi progapp

Due to idiosyncrasies of newer windows versions the make file can't execute the command (as was done for may years and variants)

winscp.com /script=progTransWin /parameter us:pw growPi bin growattLink

Instead it will be output and must be copied and executed by hand.

The building of this application is governed by the make include files makeProg_growattLink_settings.mk and makeTarg_growPi_ettings.mk. The latter will lead to including include/arch/target_growPi.h.

This programme was originally / is quick and dirty. Outstanding task: make it structured.

Function Documentation

◆ chgFilNames()

void chgFilNames ( void )

Set the current date in event & retain file paths.

The current date from actRTmTxt is set into the file names evtFilNam and retFilNam

◆ mqttInit()

int mqttInit ( )

Initialise as MQTT client.

On success only: subscribe, loop and publish.

Returns
0: success the common mosq is set and usable; else: errno
See also
mqttClean

◆ mqttClean()

void mqttClean ( )

End as MQTT client.

Stop and cleanup.

See also
mqttInit

◆ processIOthread()

void * processIOthread ( void * args)

The task of controlling process IO.

Except for the Modbus communication, this thread controls all processIO: at present this is just three LEDs. Which means: it does nothing quite useful as this is just a "read Modbus" programme.

It is a 100 ms cycle thread.
The cyclic 100 ms task then distinguishes 10 steps within a second. This ten (0..9) steps serve for the synchronisation with the other thread and act as state machine framework.

◆ rs232ModThread()

void * rs232ModThread ( void * args)

if (modBvals.retRimp0 == retRead) goto case9; // old error

out char formIfix[60] = // form P 2 l7 Wd 16 l4 T 27 l5 Up 40 l 6 0123456789x123456789v123456789t123456789q123456789c1 2 3 4 "P: 1299.4W, Wd: 12.4kWh, T: 29.7gdC, Up: 114.3V\0\0\0"; formFixed16(formIfix + 2, 7, // form P 2 l7 modBvals.imp0Regs.regs[12], 1); // lo line power / .1W formFixed16(formIfix + 16, 4, modBvals.imp0Regs.regs[27], 1); // lo work day / .1 kWh formFixed16(formIfix + 27, 5, modBvals.imp0Regs.regs[32], 1); // Temp / .grdC formFixed16(formIfix + 40, 6, modBvals.imp0Regs.regs[3], 1); // Upanel / .1V = out

◆ main()

int main ( int argc,
char ** argv )

The program.

run by: growattLink [options
For options see longOptions and :: optHlpTxt.

Parameters
argcnumber of parameters + 1
argvoptional options

xxxxx for(;commonRun;) { // use main thread for async. tasks like MQTT pthread_yield(); break; // no asyn tasks yet if (!commonRun) break; pthread_yield(); if (!commonRun) break; if (!commonRun) break; pthread_yield(); if (!commonRun) break; } // commonRun async tasks xxxxx

Variable Documentation

◆ prgNamPure

char const prgNamPure[]

The pure program name.

To be provided in the application's / program's source.

See also
progNam() progNamB()

◆ prgSVNrev

char const prgSVNrev[]

The complete SVN revision string.

To be provided in the application's / program's source.

See also
progRev()

◆ prgSVNdat

char const prgSVNdat[]

The complete SVN date string.

To be provided in the application's / program's source.

See also
progDat()

◆ mqttHost

char mqttHost[68]

MQTT broker address or name.

It is either the hostname or the IP address as text. like e.g. 192.168.178.86 or mqttpi.

See also
mqttPort topicSubs

◆ mqttPort

int mqttPort

MQTT broker port.

It is usually the standard port 1883.

See also
mqttHost topicSubs

◆ topicSubs

char const topicSubs[20]

The MQTT topic to subscribe to.

The array's lenght is 20, fitting SUBTOP_I.

See also
mqttHost mqttPort SUBTOP_I

◆ subTopGrow

char const subTopGrow[14]

Relative topic for Growatt readings.

See also
subTopEco3 subTopBatU

◆ subTopStPlg01

char subTopStPlg01[14]

State sub topic of S20 plug Number 01 to 09.

It's preset as plug01/POWER for 01, but the digit at index [5] will be set before each use accordingly.

See also
messSttOnPlg messSttOffPlg

◆ messSttOnPlg

char messSttOnPlg[6]

State message: a S20 plug is now on.

See also
messSttOffPlg subTopStPlg01

◆ messSttOffPlg

char messSttOffPlg[6]

State message: a S20 plug is now off.

See also
messSttOnPlg subTopStPlg01

◆ clientId

char clientId[38]

MQTT client ID.

default value: sweetHomeControl; length: 15; max. length: 36
May be changed before mqttInit().

◆ mosq

struct mosquitto* mosq

MQTT interface state.

Defined in <mosquitto.h> without description. MQTT interface (state)