
Display of mateq84 FM80 system data using Grafana using the MQTT plugin. Yes, it's browser based and you can run the server locally.
https://grafana.com/



json = cJSON_CreateObject();
cJSON_AddStringToObject(json, "name", "mateq84");
cJSON_AddNumberToObject(json, "benergy", benergy);
cJSON_AddNumberToObject(json, "acenergy", acenergy);
cJSON_AddNumberToObject(json, "load", load);
cJSON_AddNumberToObject(json, "runtime", runtime);
cJSON_AddNumberToObject(json, "solar", solar);
cJSON_AddNumberToObject(json, "batenergykw", bat_energy_kw);
cJSON_AddNumberToObject(json, "batenergyscaled", bat_energy_scaled);
cJSON_AddNumberToObject(json, "bamps", bamps);
cJSON_AddNumberToObject(json, "bvolts", bvolts);
cJSON_AddNumberToObject(json, "pamps", pamps);
cJSON_AddNumberToObject(json, "pvolts", pvolts);
cJSON_AddNumberToObject(json, "pwatts", pwatts);
cJSON_AddStringToObject(json, "system", "FM80 solar monitor");
// convert the cJSON object to a JSON string
char *json_str = cJSON_Print(json);
Energy vs Power
It’s a common mistake to take Power as an Energy value, but the two are not alike.
Energy is a quantitative measurement of what it takes to produce work (e.g. heat water) while Power measures the speed at which energy is transferred.
Electrical Power is measured in Watts (W) and Electrical Energy is measured in kiloWatt-hour (kWh).
Think of this in a parallel to speed and distance: Power is the speed you are going and Energy is the distance driven.
Therefore Energy (kiloWatt-hour) is not an average of the Power you are consuming over a given period of time (the unit of the average power would be Watt or kiloWatt again). Energy is the integral (mathematical operation) of the Power function.
This difference is very important as you need to use the proper entities in our Energy Panel.















void gti_cmds(void)
{
if (Sready()) {
mqtt_r = Sread();
#ifdef GTI_ECHO
UART1_Write(mqtt_r); // debug echo
#endif
switch (mqtt_r) {
case 'Z': // zero power
cmd_value = 0;
break;
case '+': // incr power
cmd_value = gti_power + 100;
if (cmd_value > 1000) {
cmd_value = 1000;
}
break;
case '-': // decr power
cmd_value = gti_power - 100;
if (cmd_value < 0) {
cmd_value = 0;
}
break;
case 'I': // idle power
cmd_value = 5;
break;
case 'F': // normal operation
cmd_value = 600;
break;
case 'M': // max unit rated power testing
cmd_value = 1000;
break;
case '#': // execute command symbol
INTERRUPT_GlobalInterruptLowDisable(); // 16-bit atomic update
gti_power = cmd_value;
INTERRUPT_GlobalInterruptLowEnable();
break;
default: // eat extra characters
while (Sready()) {
mqtt_r = Sread();
}
break;
}
}
}




/*
* converted to PI for solar power control
*/
double UpdatePI(volatile struct SPid * const pid, double const error) {
double pTerm, iTerm;
pTerm = pid->pGain * error; // calculate the proportional term
// calculate the integral state with appropriate limiting
pid->iState += error;
if (pid->iState > pid->iMax) {
pid->iState = pid->iMax;
} else if (pid->iState < pid->iMin) {
pid->iState = pid->iMin;
} else {
}
iTerm = (pid->iGain * pid->iState); // calculate the integral term
if ((pTerm + iTerm) > pid->iMax) {
iTerm = 0.0f;
pTerm = pid->iMax;
}
return pTerm + iTerm;
}
if (bsoc_set_mode(PV_BIAS, true) && E.git_sw_status) {
char gti_str[16];
int32_t error_drive;
if (E.gti_delay++ >= GTI_DELAY) {
E.mode.in_control = true;
E.gti_delay = 0;
error_drive = (int32_t) E.mode.error; // PI feedback control signal
if (error_drive < 0) {
error_drive = PV_BIAS; // control wide power swings
}
snprintf(gti_str, 15, "V%04dX", error_drive); // format for dumpload controller gti power commands
mqtt_gti_power(client_p, TOPIC_P, gti_str);
}
} else {
if (E.mode.in_control) {
E.mode.in_control = false;
ramp_down_gti(client_p, true);
ramp_down_ac(client_p, true);
}
};
/*
* set mode to true to add GTI power to AC power for control power feedback
* target is the positive power bias to keep the battery(s) charged
*/
bool bsoc_set_mode(double target, bool mode) {
bool bsoc_mode = false;
if (E.mvar[V_PWA] >= PV_FULL_PWR) {
bsoc_mode = true;
}
E.mode.gti_dumpload = (E.print_vars[L3_P]* -1.0f) + E.mvar[V_DPPV];
E.mode.total_system = (E.mvar[V_FLO] - E.mode.gti_dumpload) + E.mvar[V_DPPV] +(E.print_vars[L3_P]* -1.0f);
E.mode.gti_dumpload = (E.print_vars[L3_P]* -1.0f) - E.mvar[V_DPPV];
/*
* look at system energy balance for power control drive
*/
if (mode) { // add GTI power from dumpload
E.mode.error = (int32_t) UpdatePI(&E.mode.pid, E.mvar[V_BEN] + E.mode.gti_dumpload + PBAL_OFFSET);
} else {
E.mode.error = (int32_t) UpdatePI(&E.mode.pid, E.mvar[V_BEN] + PBAL_OFFSET);
}
E.mode.target = target;
return bsoc_mode;
}
Hi there nsaspook,View attachment 322107
Battery BMS in float mode (FCM). System moving MAX energy to AC loads. A slightly negative control bias to slowly drain energy from the battery bank.
View attachment 322103
View attachment 322104
View attachment 322105
System back in BULK battery charge mode (BCM) where it keeps a slightly positive control bias of energy flow to the battery to keep it topped off.
View attachment 322106

Thanks for the quick reply. I was hoping for a quick fix to my issue, but alas, life offers no shortcuts.Nice.
I'm sure my interface hardware system could be hacked for the VFX3524 inverter as the MATE interface is the same but the controller software (in C) is custom for my configuration and is not intended for a general use library. Adapting pyMATE would likely be much easier to get going unless you're up to speed with PIC18 xc8 embedded programming. Feel free to use anything from the archive or to ask questions.
https://github.com/nsaspook/Q84vtouch/tree/q84








