This commit is contained in:
Edward Emelianov 2025-09-26 14:57:24 +03:00
parent 730629e933
commit 27a7388164
3 changed files with 147 additions and 30 deletions

View File

@ -1,5 +1,5 @@
# run `make DEF=...` to add extra defines # run `make DEF=...` to add extra defines
PROGRAM := getparams PROGRAM := invertercmd
LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all LDFLAGS := -fdata-sections -ffunction-sections -Wl,--gc-sections -Wl,--discard-all
LDFLAGS += -lusefull_macros LDFLAGS += -lusefull_macros
SRCS := $(wildcard *.c) SRCS := $(wildcard *.c)

Binary file not shown.

View File

@ -30,6 +30,7 @@ typedef struct{
char *getstatus; char *getstatus;
int baudrate; int baudrate;
int status; int status;
int settershelp;
} globopts; } globopts;
typedef struct{ typedef struct{
@ -39,6 +40,9 @@ typedef struct{
int flag; int flag;
int status; int status;
int mode; int mode;
int warning;
int deflt;
int bateq;
} statusinfo; } statusinfo;
typedef void (*parsefn)(const char *answer); typedef void (*parsefn)(const char *answer);
@ -55,7 +59,8 @@ static sl_option_t opts[] = {
{"devpath", NEED_ARG, NULL, 'd', arg_string, APTR(&G.path), "path to device (default: /dev/ttyS0)"}, {"devpath", NEED_ARG, NULL, 'd', arg_string, APTR(&G.path), "path to device (default: /dev/ttyS0)"},
{"baudrate",NEED_ARG, NULL, 'b', arg_int, APTR(&G.baudrate), "baudrate (default: 2400)"}, {"baudrate",NEED_ARG, NULL, 'b', arg_int, APTR(&G.baudrate), "baudrate (default: 2400)"},
{"cmd", NEED_ARG, NULL, 'c', arg_string, APTR(&G.customcmd), "custom command to send"}, {"cmd", NEED_ARG, NULL, 'c', arg_string, APTR(&G.customcmd), "custom command to send"},
{"status", NEED_ARG, NULL, 's', arg_string, APTR(&G.getstatus), "get status (type 'help' for options)"}, {"status", NEED_ARG, NULL, 's', arg_string, APTR(&G.getstatus), "get status: comma-separated options (type 'help' for options)"},
{"helpsetters",NO_ARGS, NULL, 0, arg_int, APTR(&G.settershelp),"show help about setters"},
end_option end_option
}; };
@ -66,6 +71,9 @@ static sl_suboption_t sopts[] = {
{"flag", NO_ARGS, arg_int, APTR(&S.flag)}, {"flag", NO_ARGS, arg_int, APTR(&S.flag)},
{"status", NO_ARGS, arg_int, APTR(&S.status)}, {"status", NO_ARGS, arg_int, APTR(&S.status)},
{"mode", NO_ARGS, arg_int, APTR(&S.mode)}, {"mode", NO_ARGS, arg_int, APTR(&S.mode)},
{"warning", NO_ARGS, arg_int, APTR(&S.warning)},
{"default", NO_ARGS, arg_int, APTR(&S.deflt)},
{"bateq", NO_ARGS, arg_int, APTR(&S.bateq)},
end_suboption end_suboption
}; };
@ -76,16 +84,20 @@ static void gettershelp(){
fprintf(stderr, "flag - device flag status (QFLAG)\n"); fprintf(stderr, "flag - device flag status (QFLAG)\n");
fprintf(stderr, "status - device general status parameters (QPIGS)\n"); fprintf(stderr, "status - device general status parameters (QPIGS)\n");
fprintf(stderr, "mode - device mode (QMOD)\n"); fprintf(stderr, "mode - device mode (QMOD)\n");
fprintf(stderr, "warning - warning status (QPIWS)\n");
fprintf(stderr, "default - default settings (QDI)\n");
fprintf(stderr, "bateq - battery equalization parameters (QBEQI)\n");
// fprintf(stderr, "\n"); // fprintf(stderr, "\n");
} }
static uint8_t *cal_crc(const char *cmd, int len){ static uint8_t *cal_crc(const char *cmd, int len){
static uint8_t CRC[3]; // 0 - hi, 1 - low static uint8_t CRC[4]; // 0 - hi, 1 - low, four bytes for quick zeroing
if(!cmd || len < 1) return 0; if(!cmd || len < 1) return 0;
const uint16_t crc_table[16] = { const uint16_t crc_table[16] = {
0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef
}; };
*((uint32_t*)CRC) = 0;
uint16_t crc = 0; uint16_t crc = 0;
uint8_t *ptr = (uint8_t*)cmd; uint8_t *ptr = (uint8_t*)cmd;
while(len--){ while(len--){
@ -131,6 +143,7 @@ static char *rd(){
if(!dev) return NULL; if(!dev) return NULL;
int got = sl_tty_read(dev); int got = sl_tty_read(dev);
if(got < 0) ERR("Can't read"); if(got < 0) ERR("Can't read");
DBG("got %d bytes, buflen: %zd", got, dev->buflen);
if(dev->buflen < 3) return NULL; if(dev->buflen < 3) return NULL;
uint8_t *CRC = cal_crc(dev->buf, dev->buflen - 3); uint8_t *CRC = cal_crc(dev->buf, dev->buflen - 3);
DBG("GOT CRC: 0x%02X 0x%02X", (uint8_t)dev->buf[dev->buflen-3], (uint8_t)dev->buf[dev->buflen-2]); DBG("GOT CRC: 0x%02X 0x%02X", (uint8_t)dev->buf[dev->buflen-3], (uint8_t)dev->buf[dev->buflen-2]);
@ -152,7 +165,7 @@ static void ratingparsing(const char *str){
&b, &c, &d, &e, &f, &h, &i, &j1, &k1, &j2, &k2, &l, &o1, &p1, &q, &o2, &p2, &q2, &r, &s, &t, &b, &c, &d, &e, &f, &h, &i, &j1, &k1, &j2, &k2, &l, &o1, &p1, &q, &o2, &p2, &q2, &r, &s, &t,
&u, &v, &w, &x); &u, &v, &w, &x);
if(N != 25){ WARNX("Got not full answer (%d instead of 25): '%s'", N, str); return; } if(N != 25){ WARNX("Got not full answer (%d instead of 25): '%s'", N, str); return; }
printf("\nGrid rating voltage: %g\nGrid rating current: %g\nAC optuput rating voltage: %g\n", b, c, d); printf("Grid rating voltage: %g\nGrid rating current: %g\nAC optuput rating voltage: %g\n", b, c, d);
printf("AC output rating frequency: %g\nAC output rating current: %g\nAC output rating apparent power: %d\n", e, f, h); printf("AC output rating frequency: %g\nAC output rating current: %g\nAC output rating apparent power: %d\n", e, f, h);
printf("AC output rating active power: %d\nBattery rating voltage: %g\nBattery recharge voltage: %g\n", i, j1, k1); printf("AC output rating active power: %d\nBattery rating voltage: %g\nBattery recharge voltage: %g\n", i, j1, k1);
printf("Battery undervoltage: %g\nBattery bulk voltage: %g\nBattery float voltage: %g\n", j2, k2, l); printf("Battery undervoltage: %g\nBattery bulk voltage: %g\nBattery float voltage: %g\n", j2, k2, l);
@ -175,27 +188,26 @@ static void ratingparsing(const char *str){
printf("\nTopology: %s\n", (t) ? "transformer" : "transformerless"); printf("\nTopology: %s\n", (t) ? "transformer" : "transformerless");
printf("Output mode: %d\nBattery redischarge voltage: %g\n", u, v); printf("Output mode: %d\nBattery redischarge voltage: %g\n", u, v);
printf("PV OK condition for parallel: %s\n", (w) ? "Only all connected" : "At least one connected"); printf("PV OK condition for parallel: %s\n", (w) ? "Only all connected" : "At least one connected");
printf("PV power balance: %s\n\n", (x) ? "Sum of powers" : "Max charged current"); printf("PV power balance: %s\n", (x) ? "Sum of powers" : "Max charged current");
} }
static const char *DEflags = "abjkuvxyz";
static const char *DEmeanings[] = {
"buzzer", "bypass", "power saving", "LCD display escape 1min", "overload restart",
"over temperature restart", "backlight on", "alarm on interrupt", "fault code record",
NULL
};
static void flagparsing(const char *str){ static void flagparsing(const char *str){
char c; char c;
int first = 0; int first = 0;
while((c = *str++)){ while((c = *str++)){
if(c == 'D'){ red("\nDISABLED: "); first = 1; } if(c == 'D'){ green("\nDISABLED: "); first = 1; }
else if(c == 'E'){ green("\nENABLED: "); first = 1; } else if(c == 'E'){ red("ENABLED: "); first = 1; }
else{ else{
const char *field = "unknown"; const char *field = "unknown";
switch(c){ for(int i = 0; DEflags[i]; ++i){
case 'a': field = "buzzer"; break; if(DEflags[i] == c){ field = DEmeanings[i]; break; }
case 'b': field = "bypass"; break;
case 'j': field = "power saving"; break;
case 'k': field = "LCD display escape 1min"; break;
case 'u': field = "overload restart"; break;
case 'v': field = "over temperature restart"; break;
case 'x': field = "backlight on"; break;
case 'y': field = "alarm on interrupt"; break;
case 'z': field = "fault code record"; break;
} }
printf("%s%s", (first) ? "" : ", ", field); printf("%s%s", (first) ? "" : ", ", field);
first = 0; first = 0;
@ -204,9 +216,19 @@ static void flagparsing(const char *str){
printf("\n"); printf("\n");
} }
/**
* @brief showflags - display bitflags
* @param flags - string with flags ('1' / '0')
* @param meaning - array with meaning of each flag, starting from H bit, i.e. meaning[0] is lesser bit
* @param nfields - strlen of flags (or less), H first
*/
static void showflags(const char *flags, const char **meaning, int nfields){ static void showflags(const char *flags, const char **meaning, int nfields){
for(int i = 0; i < nfields; ++i) for(int i = 0; i < nfields; ++i){
printf("\t%s: %s\n", meaning[i], flags[i]=='1' ? "on/yes" : "off/no"); if(!meaning[i]) continue;
printf("\t%s: ", meaning[i]);
if(flags[i] == '1') red("on/yes\n");
else green("off/no\n");
}
} }
// 230.0 50.0 232.0 50.0 0000 0000 000 409 26.99 000 100 0489 0000 000.0 00.00 00000 10011101 00 03 00000 100 // 230.0 50.0 232.0 50.0 0000 0000 000 409 26.99 000 100 0489 0000 000.0 00.00 00000 10011101 00 03 00000 100
@ -214,8 +236,6 @@ static void showflags(const char *flags, const char **meaning, int nfields){
static void statusparsing(const char *str){ static void statusparsing(const char *str){
float b, c, d, e, j, u, w; float b, c, d, e, j, u, w;
int f, g, h, i, k, o, t, e1, p, S, H, I; int f, g, h, i, k, o, t, e1, p, S, H, I;
//int l = strlen(str);
//for(int i = 0; i < l; ++i){char c = str[i]; if(isalnum(c)||c==' '||c=='.') printf("%c", c); else printf("\\x%02X", c); }
char x[9], T[4]; char x[9], T[4];
int N = sscanf(str, "%f %f %f %f %d %d %d %d %f %d %d %d %d %f %f %d %8s %d %d %d %3s", int N = sscanf(str, "%f %f %f %f %d %d %d %d %f %d %d %d %d %f %f %d %8s %d %d %d %3s",
&b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &o, &t, &e1, &u, &w, &p, x, &S, &H, &I, T); &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &o, &t, &e1, &u, &w, &p, x, &S, &H, &I, T);
@ -227,8 +247,8 @@ static void statusparsing(const char *str){
printf("Bus voltage: %d\nBattery voltage: %g\nBattery charging current: %d\nBattery capacity: %d\n", i, j, k, o); printf("Bus voltage: %d\nBattery voltage: %g\nBattery charging current: %d\nBattery capacity: %d\n", i, j, k, o);
printf("Inverter heat sink temperature: %d\nPV input current for battery: %d\nPV input voltage 1: %g\n", t, e1, u); printf("Inverter heat sink temperature: %d\nPV input current for battery: %d\nPV input voltage 1: %g\n", t, e1, u);
printf("Battery voltage from SCC: %g\nBattery discharge current: %d\nDevice status:\n", w, p); printf("Battery voltage from SCC: %g\nBattery discharge current: %d\nDevice status:\n", w, p);
static const char *sf[] = {"AC charging", "SCC charging", "Charging", "Steady batt voltage while charging", static const char *sf[] = {"SBU priority version", "configuration changed", "SCC firmware updated",
"Load status", "SCC firmware updated", "configuration changed", "SBU priority version"}; "Load status", "Steady batt voltage while charging", "Charging", "SCC charging", "AC charging"};
showflags(x, sf, 8); showflags(x, sf, 8);
if(N > 17){ if(N > 17){
printf("Battery offset for fans on: %d\nEEPROM version: %d\nPV charging power: %d\n", S, H, I); printf("Battery offset for fans on: %d\nEEPROM version: %d\nPV charging power: %d\n", S, H, I);
@ -242,29 +262,125 @@ static void statusparsing(const char *str){
static void modeparsing(const char *str){ static void modeparsing(const char *str){
printf("Device mode: "); printf("Device mode: ");
switch(*str){ switch(*str){
case 'B': printf("Battery\n"); break; case 'B': printf("Battery"); break;
case 'F': printf("Fault\n"); break; case 'F': printf("Fault"); break;
case 'H': printf("Power saving\n"); break; case 'H': printf("Power saving"); break;
case 'L': printf("Line\n"); break; case 'L': printf("Line"); break;
case 'P': printf("Power on\n"); break; case 'P': printf("Power on"); break;
case 'S': printf("Standby\n"); break; case 'S': printf("Standby"); break;
default: printf("Unknown"); default: printf("Unknown");
} }
printf("\n"); printf("\n");
} }
static void warningparsing(const char *str){
int l = strlen(str);
if(l < 32) WARNX("Non-full status! Data could be wrong");
static const char *wmsgs[] = {
NULL, "Inverter fault", "Bus over", "Bus under", "Bus soft fail", "Line fail", "OPV short",
"Inv voltage too low", "Inv voltage too high", "Over temperature", "Fan locked", "Battery voltage high",
"Battery low alarm", "Overcharge", "Battery under shutdown", "Battery derating", "Overload", "EEPROM fault",
"Inverter overcurrent", "Inverter soft fail", "Self test fail", "OP DC voltage over", "Bat open",
"Current sensor fail", "Battery short", "Power limit", "PV voltage high", "MPPT overload fault",
"MPPT overload warning", "Battery too low to charge", NULL, NULL
};
printf("Warning status:\n");
showflags(str, wmsgs, l);
}
static const char *endis(int val){
return (val) ? COLOR_RED "enable" COLOR_OLD : COLOR_GREEN "disable" COLOR_OLD;
}
// 230.0 50.0 0025 21.0 27.0 28.2 23.0 50 0 0 2 0 1 0 0 0 1 1 1 0 1 0 27.0 0 0 ()
// b c d e f g h i j k l m n o p q r s t u v w y x z a
static void defaultparsing(const char *str){
float b, c, e, f, g, h, y;
int d, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, z, a;
int N = sscanf(str, "%f %f %d %f %f %f %f %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %f %d %d %d",
&b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r, &s, &t, &u, &v, &w, &y, &x, &z, &a);
if(N < 25){WARNX("Wrong data format: %d fields instead of 25", N); return;}
printf("AC output voltage: %g\nAC output frequency: %g\nMax AC charging current: %d\n", b, c, d);
printf("Battery undervoltage: %g\nCharging float voltage: %g\nCharging bulk voltage: %g\n", e, f, g);
printf("Battery default recharge voltage: %g\nMax charging current: %d\nAC input voltage range: %s\n", h, i, (j) ? "UPS" : "appliance");
printf("Output source priority: %s\nCharger source priority: %s\n", (k) ? "solar first": "utility first", (l) ? "solar first" : "utility first");
printf("Battery type: %s\nBuzzer: %s\nPover saving: %s\n", (m) ? "other" : "AGM", endis(n), endis(o));
printf("Overload restart: %s\nOver temperature restart: %s\n", endis(p), endis(q));
printf("Backlight: %s\nAlarm on interrupt: %s\nFault code record: %s\n", endis(r), endis(s), endis(t));
printf("Overload bypass: %s\nLCD timeout escape: %s\nOutput mode: %d\n", endis(u), endis(v), w);
printf("Battery re-discharge voltage: %g\nPV OK condition for parallel: %s\n", y, (x) ? "all" : "any");
printf("PV power balance: %s\n", (z) ? "?" : "PV max current is charged current");
if(N == 26) printf("Max charging time @ CV stage: %s\n", (a) ? "?" : "automatically");
}
// 0 060 030 050 030 29.20 000 120 0 0000
// b c d e f g h i j k
static void equparsing(const char *str){
float g;
int b, c, d, e, f, h, i, j, k;
int N = sscanf(str, "%d %d %d %d %d %f%d %d %d %d", &b, &c, &d, &e, &f, &g, &h, &i, &j, &k);
if(N != 10){ WARNX("Not enougn parameters: got %d instead of 10\n", N); return; }
printf("Equalization: %s\n", endis(b));
printf("Eq. time: %d minutes\nEq. period: %d days\nEq. max current: %d\n", c, d, e);
printf("Eq. voltage: %g\nEq. over time: %d minutes\nEq. active status: %d\n", g, i, j);
}
static void printpar(const char *str){
printf("%s\n", str);
}
static void runparsing(const char *cmd, parsefn f){ static void runparsing(const char *cmd, parsefn f){
if(sendcmd(cmd)){ if(sendcmd(cmd)){
char *got = rd(); char *got = rd();
if(got) f(got); if(got && *got){
f(got);
printf("\n");
} else red("Can't get data\n\n");
} }
} }
static void showstatus(){ static void showstatus(){
green("\nModel name: "); runparsing("QMN", printpar);
if(S.all || S.rating) runparsing("QPIRI", ratingparsing); if(S.all || S.rating) runparsing("QPIRI", ratingparsing);
if(S.all || S.flag) runparsing("QFLAG", flagparsing); if(S.all || S.flag) runparsing("QFLAG", flagparsing);
if(S.all || S.status) runparsing("QPIGS", statusparsing); if(S.all || S.status) runparsing("QPIGS", statusparsing);
if(S.all || S.mode) runparsing("QMOD", modeparsing); if(S.all || S.mode) runparsing("QMOD", modeparsing);
if(S.all || S.warning) runparsing("QPIWS", warningparsing);
if(S.all || S.deflt) runparsing("QDI", defaultparsing);
if(S.all || S.bateq) runparsing("QBEQI", equparsing);
}
static void showsettershelp(){
printf("\n\n");
red("Be carefull with setters! Think twice before changing something!!!\n\n");
printf("Here are setters...\n");
printf("PEx - enable status / DEx - disable status, where 'x':\n");
for(int i = 0; DEflags[i]; ++i) printf("\t%c - %s\n", DEflags[i], DEmeanings[i]);
printf("PF - set all control parameters to default\n");
printf("MCHGCx - max charging current (Amps)\n");
printf("MUCHGCx - utility max charging current\n");
printf("Fx - invertere output frequency\n");
printf("POPx - output source priority (0 - utility, 1 - solar, 2 - SBU)\n");
printf("PBCVx - battery re-charge voltage\n");
printf("PBDVx - battery re-discharge voltage\n");
printf("PCPx - inverter charging priority (0-3: utility first/solar first/solar+utility/solar only\n");
printf("PGRx - inverter grid voltage range (0 - appliance, 1 - UPS)\n");
printf("PBTx - battery type (0 - AGM, 1 - flooded)\n");
printf("PSDVx - battery cut-off voltage\n");
printf("PCVVx - CV (constant voltage) charging voltage\n");
printf("PBFTx - battery float charging voltage\n");
printf("PBEQEx - enable (1) or disable (0) battery equalization\n");
printf("PBEQT x - battery equalization time (minutes)\n");
printf("PBEQPx - battery equalization period (days)\n");
printf("PBEQVx - battery equalization voltage\n");
printf("PBEQOTx - battery equalization overtime (minutes)\n");
printf("PBEQAx - activate (1) or disactivate (0) battery equalization now\n");
printf("PCVTx - max charging time an CV stage\n");
printf("\n\nAnd some getters that aren't in `status` variants:\n");
printf("QID - inverter's serial\nQVFW - firmware version\nQMCHGCR - max charging currents available\n");
printf("QMUCHGCR - max utility charging currents available\n");
printf("\n\n");
} }
int main(int argc, char **argv){ int main(int argc, char **argv){
@ -293,5 +409,6 @@ int main(int argc, char **argv){
} }
} }
sl_tty_close(&dev); sl_tty_close(&dev);
if(G.settershelp) showsettershelp();
return 0; return 0;
} }