Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for 2mppt #2

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ This is a fork from ned-kelly version with improved changes from kchiem, dilyan
- now you can transmit commands to inverter bigger then 5 characters by sending them in batch of 5 characters .
- Battery redicharge voltage - now show good values.
- many changes witch I don't remember :)
- Add support for 2 MPPT
- Fix the bulk command write to work since the inverter requires full 8 byte blocks at a time

**The instructions to use this fork are at the end of readme and I have taking in consideration that you allready have installed and followed the intructions of original repository of ned-kelly. If you start from zero, then install docker and docker-compose and then skip to no 5.**

Expand Down
2 changes: 2 additions & 0 deletions config/inverter.conf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ amperage_factor=1.0
# reading compared to measurement tools. Normally this will remain '1'
watt_factor=1.0

# Indicate how many mppt the inverter has. For a second PV controller set it to 2. Logic currently support up to two mppts
mppt_count=1

# The following settings allow you to modify runtime buffers.
# N.B. These values may not be applicable to all inverter types, as such you will
Expand Down
7 changes: 6 additions & 1 deletion config/mqtt.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@
"Max_charge_current": "max_charge_current",
"Out_source_priority": "output_source_priority",
"Charger_source_priority": "charger_source_priority",
"Battery_redischarge_voltage": "battery_re_discharge_voltage"
"Battery_redischarge_voltage": "battery_re_discharge_voltage",
"PV2_in_current": "pv2_in_current",
"PV2_in_voltage": "pv2_in_voltage",
"PV2_in_watts": "pv2_in_watts",
"PV2_charging_power": "pv2_charging_power",
"PV_total_charging_power": "pv_total_charging_power"
}
}
}
4 changes: 4 additions & 0 deletions homeassistant/lovelace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ views:
- entity: sensor.pv_in_watts
- entity: sensor.pv_in_current
- entity: sensor.pv_in_voltage
- entity: sensor.pv2_in_watts
- entity: sensor.pv2_in_current
- entity: sensor.pv2_in_voltage
- entity: sensor.pv_total_charging_power
- entity: sensor.battery_charge_current
- entity: sensor.battery_discharge_current
- entity: sensor.battery_voltage
Expand Down
39 changes: 31 additions & 8 deletions sources/inverter-cli/inverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,30 @@

cInverter::cInverter(std::string devicename) {
device = devicename;
status1[0] = 0;
status2[0] = 0;
status_qpigs[0] = 0;
status_qpiri[0] = 0;
status_qpigs2[0] = 0;
warnings[0] = 0;
mode = 0;
}

string *cInverter::GetQpigsStatus() {
m.lock();
string *result = new string(status1);
string *result = new string(status_qpigs);
m.unlock();
return result;
}

string *cInverter::GetQpigs2Status() {
m.lock();
string *result = new string(status_qpigs2);
m.unlock();
return result;
}

string *cInverter::GetQpiriStatus() {
m.lock();
string *result = new string(status2);
string *result = new string(status_qpiri);
m.unlock();
return result;
}
Expand Down Expand Up @@ -93,7 +101,7 @@ bool cInverter::query(const char *cmd) {
tcflush(fd, TCOFLUSH);

// ---------------------------------------------------------------

lprintf("DEBUG: Execute command %s", cmd);
// Generating CRC for a command
uint16_t crc = cal_crc_half((uint8_t*)cmd, strlen(cmd));
n = strlen(cmd);
Expand All @@ -103,6 +111,10 @@ bool cInverter::query(const char *cmd) {
buf[n++] = crc & 0xff;
buf[n++] = 0x0d;

while (n % 8 != 0) {
buf[n++] = 0x00;
}

// Send buffer in hex
char messagestart[128];
char* messageptr = messagestart;
Expand Down Expand Up @@ -213,18 +225,29 @@ void cInverter::poll() {
if (query("QPIGS") &&
strcmp((char *)&buf[1], "NAK") != 0) {
m.lock();
strcpy(status1, (const char*)buf+1);
strcpy(status_qpigs, (const char*)buf+1);
m.unlock();
ups_qpigs_changed = true;
}
}

// Reading QPIGS2 status
if (!ups_qpigs2_changed) {
if (query("QPIGS2") &&
strcmp((char *)&buf[1], "NAK") != 0) {
m.lock();
strcpy(status_qpigs2, (const char*)buf+1);
m.unlock();
ups_qpigs2_changed = true;
}
}

// Reading QPIRI status
if (!ups_qpiri_changed) {
if (query("QPIRI") &&
strcmp((char *)&buf[1], "NAK") != 0) {
m.lock();
strcpy(status2, (const char*)buf+1);
strcpy(status_qpiri, (const char*)buf+1);
m.unlock();
ups_qpiri_changed = true;
}
Expand All @@ -249,7 +272,7 @@ void cInverter::ExecuteCmd(const string cmd) {
// Sending any command raw
if (query(cmd.data())) {
m.lock();
strcpy(status2, (const char*)buf+1);
strcpy(status_qpiri, (const char*)buf+1);
m.unlock();
}
}
Expand Down
6 changes: 4 additions & 2 deletions sources/inverter-cli/inverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ using namespace std;
class cInverter {
unsigned char buf[1024]; //internal work buffer
char warnings[1024];
char status1[1024];
char status2[1024];
char status_qpigs[1024];
char status_qpiri[1024];
char status_qpigs2[1024];
char mode;
std::string device;
std::mutex m;
Expand All @@ -38,6 +39,7 @@ uint16_t cal_crc_half(uint8_t *pin, uint8_t len);

string *GetQpiriStatus();
string *GetQpigsStatus();
string *GetQpigs2Status();
string *GetWarnings();

int GetMode();
Expand Down
70 changes: 60 additions & 10 deletions sources/inverter-cli/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ atomic_bool ups_status_changed(false);
atomic_bool ups_qmod_changed(false);
atomic_bool ups_qpiri_changed(false);
atomic_bool ups_qpigs_changed(false);
atomic_bool ups_qpigs2_changed(false);
atomic_bool ups_qpiws_changed(false);
atomic_bool ups_cmd_executed(false);

Expand All @@ -43,6 +44,7 @@ string devicename;
// int runinterval;
float ampfactor;
float wattfactor;
int mppt_count;

// ---------------------------------------

Expand Down Expand Up @@ -85,6 +87,8 @@ void getSettingsFile(string filename) {
attemptAddSetting(&ampfactor, linepart2);
else if(linepart1 == "watt_factor")
attemptAddSetting(&wattfactor, linepart2);
else if(linepart1 == "mppt_count")
attemptAddSetting(&mppt_count, linepart2);
else
continue;
}
Expand All @@ -97,7 +101,7 @@ void getSettingsFile(string filename) {

int main(int argc, char* argv[]) {

// Reply1
// Reply_qpigs
float voltage_grid;
float freq_grid;
float voltage_out;
Expand All @@ -123,7 +127,14 @@ int eeprom_version;
int pv_charging_power;
char device_status2[3];

// Reply2
// Reply_qpigs2
float pv2_input_current;
float pv2_input_voltage;
float pv2_input_watts;
int pv2_charging_power;
int pv_total_charging_power;

// Reply_qpiri
float grid_voltage_rating;
float grid_current_rating;
float out_voltage_rating;
Expand Down Expand Up @@ -182,6 +193,9 @@ float batt_redischarge_voltage;
exit(0);
}

if (mppt_count < 2) {
ups_qpigs2_changed = true;
}

if (runOnce)
ups->poll();
Expand All @@ -200,21 +214,35 @@ float batt_redischarge_voltage;
ups_status_changed = false;
}

if (mppt_count < 2) {
ups_qpigs2_changed = true;
}

lprintf("DEBUG: ups_qmod_changed %s, ups_qpiri_changed %s, ups_qpigs_changed %s, ups_qpigs2_changed %s",
ups_qmod_changed ? "true" : "false",
ups_qpiri_changed ? "true" : "false",
ups_qpigs_changed ? "true" : "false",
ups_qpigs2_changed ? "true": "false");
// Once we receive all queries print it to screen
if (ups_qmod_changed && ups_qpiri_changed && ups_qpigs_changed) {
if (ups_qmod_changed && ups_qpiri_changed && ups_qpigs_changed && ups_qpigs2_changed) {
ups_qmod_changed = false;
ups_qpiri_changed = false;
ups_qpigs_changed = false;
if (mppt_count >= 2) {
// We only read QPIGS2 if it is enabled.
ups_qpigs2_changed = false;
}

int mode = ups->GetMode();
string *reply1 = ups->GetQpigsStatus();
string *reply2 = ups->GetQpiriStatus();
string *reply_qpigs = ups->GetQpigsStatus();
string *reply_qpiri = ups->GetQpiriStatus();
string *reply_qpigs2 = ups->GetQpigs2Status();
string *warnings = ups->GetWarnings();

if (reply1 && reply2 && warnings) {
if (reply_qpigs && reply_qpiri && reply_qpigs2 && warnings) {

// Parse and display values, QPIGS, * means contained in output, ^ is not included in output
sscanf(reply1->c_str(), "%f %f %f %f %d %d %d %d %f %d %d %d %f %f %f %d %s %d %d %d %s",
sscanf(reply_qpigs->c_str(), "%f %f %f %f %d %d %d %d %f %d %d %d %f %f %f %d %s %d %d %d %s",
&voltage_grid, // * Grid voltage
&freq_grid, // * Grid frequency
&voltage_out, // * AC output voltage
Expand All @@ -238,7 +266,7 @@ float batt_redischarge_voltage;
&device_status2);

char parallel_max_num; //QPIRI
sscanf(reply2->c_str(), "%f %f %f %f %f %d %d %f %f %f %f %f %d %d %d %d %d %d %c %d %d %d %f",
sscanf(reply_qpiri->c_str(), "%f %f %f %f %f %d %d %f %f %f %f %f %d %d %d %d %d %d %c %d %d %d %f",
&grid_voltage_rating, // ^ Grid rating voltage
&grid_current_rating, // ^ Grid rating current per protocol, frequency in practice
&out_voltage_rating, // ^ AC output rating voltage
Expand All @@ -265,20 +293,34 @@ float batt_redischarge_voltage;
// ^ PV OK condition for parallel
// ^ PV power balance

// QPIGS2 (MPPT2)
if (mppt_count >= 2) {
sscanf(reply_qpigs2->c_str(), "%f %f %d",
&pv2_input_current, // * PV2 Input current
&pv2_input_voltage, // * PV2 Input voltage
&pv2_charging_power // * PV2 Charging power
);
}

pv_total_charging_power = pv_charging_power + pv2_charging_power;

// There appears to be a discrepancy in actual DMM measured current vs what the meter is
// telling me it's getting, so lets add a variable we can multiply/divide by to adjust if
// needed. This should be set in the config so it can be changed without program recompile.
if (debugFlag) {
printf("INVERTER: ampfactor from config is %.2f\n", ampfactor);
printf("INVERTER: wattfactor from config is %.2f\n", wattfactor);
printf("INVERTER: MPPT count from config is %d\n", mppt_count);
}

pv_input_current = pv_input_current * ampfactor;
pv2_input_current = pv2_input_current * ampfactor;

// It appears on further inspection of the documentation, that the input current is actually
// current that is going out to the battery at battery voltage (NOT at PV voltage). This
// would explain the larger discrepancy we saw before.
pv_input_watts = (scc_voltage * pv_input_current) * wattfactor;
pv2_input_watts = (scc_voltage * pv2_input_current) * wattfactor;

// Calculate watt-hours generated per run interval period (given as program argument)
// pv_input_watthour = pv_input_watts / (3600000 / runinterval);
Expand Down Expand Up @@ -325,12 +367,20 @@ float batt_redischarge_voltage;
printf(" \"Out_source_priority\":%d,\n", out_source_priority); // QPIRI
printf(" \"Charger_source_priority\":%d,\n", charger_source_priority); // QPIRI
printf(" \"Battery_redischarge_voltage\":%.1f,\n", batt_redischarge_voltage); // QPIRI
if (mppt_count >= 2) {
printf(" \"PV2_in_voltage\":%.1f,\n", pv2_input_voltage); // QPIGS2
printf(" \"PV2_in_current\":%.1f,\n", pv2_input_current); // QPIGS2
printf(" \"PV2_in_watts\":%.1f,\n", pv2_input_watts); // = (scc_voltage * pv2_input_current) * wattfactor;
printf(" \"PV2_charging_power\":%d,\n", pv2_charging_power); // QPIGS2
}
printf(" \"PV_total_charging_power\":%d,\n", pv_total_charging_power);
printf(" \"Warnings\":\"%s\"\n", warnings->c_str()); //
printf("}\n");

// Delete reply string so we can update with new data when polled again...
delete reply1;
delete reply2;
delete reply_qpigs;
delete reply_qpiri;
delete reply_qpigs2;

if(runOnce) {
// there is no thread -- ups->terminateThread();
Expand Down
1 change: 1 addition & 0 deletions sources/inverter-cli/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ extern atomic_bool ups_qpiws_changed;
extern atomic_bool ups_qmod_changed;
extern atomic_bool ups_qpiri_changed;
extern atomic_bool ups_qpigs_changed;
extern atomic_bool ups_qpigs2_changed;

#endif // ___MAIN_H
6 changes: 5 additions & 1 deletion sources/inverter-mqtt/mqtt-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ registerModeTopic "SCC_charge_on" "" "power" "None" "SCC charge on"
registerTopic "SCC_voltage" "V" "current-dc" "voltage" "SCC voltage"
registerModeTopic "Switch_On" "" "power" "None" "Switch On"
registerModeTopic "Warnings" "" "power" "None" "Warnings"

registerTopic "PV2_in_current" "A" "solar-panel-large" "current" "PV2 in current"
registerTopic "PV2_in_voltage" "V" "solar-panel-large" "voltage" "PV2 in voltage"
registerTopic "PV2_in_watts" "W" "solar-panel-large" "power" "PV2 in watts"
registerTopic "PV2_charging_power" "W" "solar-panel-large" "power" "PV2 charging power"
registerTopic "PV_total_charging_power" "W" "solar-panel-large" "power" "PV Total charging power"
# Add in a separate topic so we can send raw commands from assistant back to the inverter via MQTT (such as changing power modes etc)...
registerInverterRawCMD