![]() |
rasProject_01 / weSweetHome
R. 146 2026-09-26
process control / RasPi software by weinert-automation
|
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. | |
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.)
Revision history
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 ...:
Holding registers read: 22 starting at [9]:
was read: 20 starting at [9] and 15 starting at [59]:
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 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
Timing
The program has a cyclic process control (in SPS manner). We have
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:
program respectively transfer to target machine by:
Due to idiosyncrasies of newer windows versions the make file can't execute the command (as was done for may years and variants)
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.
| void chgFilNames | ( | void | ) |
| int mqttInit | ( | ) |
Initialise as MQTT client.
On success only: subscribe, loop and publish.
| void mqttClean | ( | ) |
| 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.
| 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
| int main | ( | int | argc, |
| char ** | argv ) |
The program.
run by: growattLink [options
For options see longOptions and :: optHlpTxt.
| argc | number of parameters + 1 |
| argv | optional 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
| char const prgNamPure[] |
The pure program name.
To be provided in the application's / program's source.
| char const prgSVNrev[] |
The complete SVN revision string.
To be provided in the application's / program's source.
| char const prgSVNdat[] |
The complete SVN date string.
To be provided in the application's / program's source.
| char mqttHost[68] |
| char const topicSubs[20] |
| char const subTopGrow[14] |
Relative topic for Growatt readings.
| 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.
| char messSttOnPlg[6] |
State message: a S20 plug is now on.
| char messSttOffPlg[6] |
State message: a S20 plug is now off.
| char clientId[38] |
MQTT client ID.
default value: sweetHomeControl; length: 15; max. length: 36
May be changed before mqttInit().
| struct mosquitto* mosq |
MQTT interface state.
Defined in <mosquitto.h> without description. MQTT interface (state)