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

Fix modbus CRC and shorter resp len (FW after 2020), fix serial receive, fix maximum #1

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ Remote utility programs for Kunkin KP184 electronic load for Linux/Cygwin.
**kp184cmd** interactive command line utility
**battery** battery discharge utility
**kp184.py** battery capacity calculator and graph plotter based on discharge utility CSV data

NOTE: KP184 communication CRC is different from 2020 firmware and above. You should check and try -O program option.
31 changes: 21 additions & 10 deletions battery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,15 +167,16 @@ void usage(const char prog[])
{
printf("usage: %s <-t tty|-s host[:port]> <-l load> <-v Volt> [-B conf] [-a addr]"
" [-V Volt] [-c Amp] [-C Amp] [-i interval] [-N samples] [-n samples]"
" [-f path] [-o] [-q]\n", prog);
" [-f path] [-o] [-O] [-q]\n", prog);
printf(" -O: older KP184 firmware before 2020\n");
printf(" -t: communicate via TTY port\n");
printf(" -s: communicate via socket\n");
printf(" -B: serial configuration string [%s]\n", defconf_serial);
printf(" -a: device address [%hhu]\n", KP184::defAddress());
printf(" -l: load mode and value: val[m]<A|R|W>\n");
printf(" -v: voltage threshold, V\n");
printf(" -V: voltage threshold to set half load, V\n");
printf(" -c: cuurent low threshold, A\n");
printf(" -c: current low threshold, A\n");
printf(" -C: current high threshold, load is immediately off, A\n");
printf(" -T: maximum load time, h:m:s\n");
printf(" -i: sample interval, s [%g s]\n",
Expand All @@ -187,6 +188,8 @@ void usage(const char prog[])
printf(" -q: produce no additional information\n");
}

#define SIGTIMEEND (SIGRTMIN+1)

int main(int argc, char *argv[])
{
int rc = 0, op;
Expand All @@ -205,13 +208,16 @@ int main(int argc, char *argv[])
struct itimerspec tsint, tsend = {};
sigset_t timset;
timer_t tintid = 0, tendid = 0;
struct sigevent sevEnd;
static struct sigaction sigact;
siginfo_t sinfo;
struct winsize ws;
bool isOldFirmware = false;


opterr = 0;
while ((op = getopt(argc, argv, "t:s:B:a:l:v:V:c:C:T:i:N:n:f:oq")) != -1) {
while ((op = getopt(argc, argv, "Ot:s:B:a:l:v:V:c:C:T:i:N:n:f:oq")) != -1) {
switch(op) {
case 'O': isOldFirmware = true; break;
case 't': ltype = Link::SERIAL; link = optarg; break;
case 's': ltype = Link::SOCKET; link = optarg; break;
case 'B': lconf = optarg; break;
Expand Down Expand Up @@ -361,6 +367,7 @@ int main(int argc, char *argv[])
sigaction(SIGINT, &sigact, NULL);
sigaction(SIGQUIT, &sigact, NULL);

kp184.setCRCLSBfirst(!isOldFirmware); // new 2020 FW and above have swapped CRC
rc = kp184.open(ltype, link, lconf);
if (rc)
return rc;
Expand All @@ -371,6 +378,7 @@ int main(int argc, char *argv[])

sigemptyset(&timset);
sigaddset(&timset, SIGALRM);
sigaddset(&timset, SIGTIMEEND);
sigprocmask(SIG_BLOCK, &timset, NULL);
rc = timer_create(CLOCK_MONOTONIC, NULL, &tintid);
if (rc == EAGAIN)
Expand All @@ -381,9 +389,12 @@ int main(int argc, char *argv[])
}

if (ts_cmp(tsend.it_value, { 0, 0 }) > 0) {
rc = timer_create(CLOCK_MONOTONIC, NULL, &tendid);
memset(&sevEnd, 0x00, sizeof(sevEnd));
sevEnd.sigev_notify = SIGEV_SIGNAL;
sevEnd.sigev_signo = SIGTIMEEND;
rc = timer_create(CLOCK_MONOTONIC, &sevEnd, &tendid);
if (rc == EAGAIN)
rc = timer_create(CLOCK_MONOTONIC, NULL, &tendid);
rc = timer_create(CLOCK_MONOTONIC, &sevEnd, &tendid);
if (rc) {
perror("ERR Can't create termination timer");
goto close;
Expand Down Expand Up @@ -521,11 +532,11 @@ int main(int argc, char *argv[])

// wait for timers
while (term == TERM_NONE) {
int sret = sigwaitinfo(&timset, &sinfo);
if ((sret != SIGALRM) || (sinfo.si_value.sival_ptr == 0))
continue;
if ((timer_t)sinfo.si_value.sival_ptr != tintid)
int sret = sigwaitinfo(&timset, NULL);
if (sret == SIGTIMEEND)
term = TERM_TIME;
if (sret != SIGALRM)
continue;
break;
}

Expand Down
9 changes: 6 additions & 3 deletions cmdUI/cmdUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@ static int process_command(int fd, char *line, int len)

void usage(const char *prog)
{
printf("usage: %s <-t tty|-s host[:port]> [-B conf] [\"cmd 1\"] ...\n", prog);
printf("usage: %s <-t tty|-s host[:port]> [-o] [-B conf] [\"cmd 1\"] ...\n", prog);
printf(" -O: older KP184 firmware before 2020\n");
printf(" -t: communicate via TTY port\n");
printf(" -s: communicate via socket\n");
printf(" -B: serial configuration string [%s]\n", getDefaultConfig(Link::SERIAL));
Expand All @@ -247,10 +248,12 @@ int main(int argc, char *argv[])
Link::linktype_t ltype = Link::SERIAL;
const char *link = NULL, *lconf = NULL, *prog = basename(argv[0]);
char c;
bool isOldFirmware = false;

opterr = 0;
while ((c = getopt(argc, argv, "t:s:B:")) != -1) {
while ((c = getopt(argc, argv, "Ot:s:B:")) != -1) {
switch(c) {
case 'O': isOldFirmware = true; break;
case 't': ltype = Link::SERIAL; link = optarg; break;
case 's': ltype = Link::SOCKET; link = optarg; break;
case 'B': lconf = optarg; break;
Expand All @@ -275,7 +278,7 @@ int main(int argc, char *argv[])
sigaction(SIGINT, &_sigact, NULL);
sigaction(SIGQUIT, &_sigact, NULL);

if (openDevice(ltype, link, lconf))
if (openDevice(ltype, link, lconf, isOldFirmware))
return -ENOTCONN;

#ifdef HAVE_READLINE
Expand Down
3 changes: 2 additions & 1 deletion cmdUI/dev_KP184.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,9 @@ cmd_t devcmds[] = {
CMD_END
};

int openDevice(Link::linktype_t type, const char *link, const char *config)
int openDevice(Link::linktype_t type, const char *link, const char *config, bool isOldFW)
{
kp184.setCRCLSBfirst(!isOldFW); // new 2020 FW and above have swapped CRC
return kp184.open(type, link, config);
}

Expand Down
2 changes: 1 addition & 1 deletion cmdUI/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ typedef struct _cmd_t {
extern cmd_t devcmds[];

// misc
int openDevice(Link::linktype_t type, const char link[], const char config[]);
int openDevice(Link::linktype_t type, const char link[], const char config[], bool isOldFW);
int reOpenDevice();
const char *getDefaultConfig(Link::linktype_t type);
const char *getPrompt();
Expand Down
5 changes: 3 additions & 2 deletions include/KP184.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,14 +255,15 @@ class KP184: public mbRTU<24, 1, 1, 250> {
rc = (int)doIO(sbuf, slen, rbuf, sizeof(rbuf));
if (rc < 0)
return rc;
if (rc != 7)
if (rc < 6)
return -ENODATA;
if (rbuf[0] != getAddress())
return -EFAULT;
if (rbuf[1] != OP_WRITE1AO)
return -ENOMSG;
if (memcmp(rbuf + 2, sbuf + 2, 4) != 0) // addr + code + reg[2] + val[2]
if (memcmp(rbuf + rc - 2, sbuf + slen -2, 2) != 0) { // check only part of rec value. newer fw return shorter answers (6B) ?
return -ENODATA;
}

return 0;
}
Expand Down
30 changes: 22 additions & 8 deletions include/link.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class Link
m_fd(-1)
, m_type(NONE)
, m_timeout_send({ 2, 0 })
, m_timeout_recv({ 0, 500000L }) {
, m_timeout_recv({ 0, 1500000L }) {
}

~Link() {
Expand Down Expand Up @@ -252,8 +252,8 @@ class Link
goto serfail;
}

// default config
sattr.c_cflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// default config - sets the terminal to something like "raw" mode
cfmakeraw(&sattr);
// set to 8-bits, no parity, 1 stop bit
sattr.c_cflag &= ~(PARENB | CSTOPB | CRTSCTS);
sattr.c_cflag &= ~CSIZE;
Expand Down Expand Up @@ -470,25 +470,39 @@ class Link
if (m_fd < 0)
return -ENXIO;

int rlen = 0;

// because we don't know datalen of received data,
// just read until timeout occur
memcpy(&timeout, &m_timeout_recv, sizeof(timeout));
do {
FD_ZERO (&read_fd);
FD_SET (m_fd, &read_fd);

if ((rc = select (m_fd + 1, &read_fd, NULL, NULL, &timeout)) < 0) {
rc = select(m_fd + 1, &read_fd, NULL, NULL, &timeout);
if (rc < 0) {
if (errno == EINTR)
continue;
rc = -errno;
perror ("select");
return rc;
} else if (rc == 0) { // timeout
rc = -ETIMEDOUT;
return (rlen > 0) ? rlen : -ETIMEDOUT;
} else { // we have only one descriptor
rc = read(m_fd, buf, size);
if (rc < 0)
rc = read(m_fd, buf+rlen, size-rlen);

if(rc > 0) {
rlen += rc;
// after first rec data, next select timeout can be shorter
timeout.tv_sec = 0;
timeout.tv_usec = 50000L;
}

if (rc < 0) {
return -errno;
}
}
} while(0);
} while(1);

return rc;
}
Expand Down
21 changes: 15 additions & 6 deletions include/mbrtu.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ template <size_t max_msglen_val = 260,
class mbRTU : public Link {
public:
mbRTU(): m_devaddr(def_devaddr)
, m_recvdelay(10000)
, crcLSBfirst(false)
#ifdef MBDEBUG
, m_debug(false)
#endif
Expand All @@ -39,7 +39,9 @@ class mbRTU : public Link {

virtual devaddr_t getAddress() { return m_devaddr; }

virtual void setRecvDelay(useconds_t delay) { m_recvdelay = delay; }
virtual void setCRCLSBfirst(bool on) { crcLSBfirst = on; }

virtual bool getCRCLSBfirst() { return crcLSBfirst; }

#ifdef MBDEBUG
virtual void setDebug(bool on) { m_debug = on; }
Expand Down Expand Up @@ -134,21 +136,29 @@ class mbRTU : public Link {

// len is length of payload in the frame (excl. CRC)
// returns total frame length (inc. CRC)
static size_t addCRC(uint8_t buf[], size_t len) {
virtual size_t addCRC(uint8_t buf[], size_t len) {
uint16_t crc = CRC16(buf, len);

if(crcLSBfirst)
crc = ((crc << 8) & 0xff00) | ((crc >> 8) & 0x00ff);

buf[len++] = (uint8_t)((crc >> 8) & 0xFF);
buf[len++] = (uint8_t)(crc & 0xFF);
return len;
}

// len is full length of the frame (inc. CRC)
// returns payload length on match (excl. CRC), -1 if no match
static ssize_t checkCRC(const uint8_t buf[], size_t len) {
virtual ssize_t checkCRC(const uint8_t buf[], size_t len) {
if (len <= 2)
return -ENODATA;

len -= 2;
uint16_t crc = CRC16(buf, len);

if(crcLSBfirst)
crc = ((crc << 8) & 0xff00) | ((crc >> 8) & 0x00ff);

if (buf[len] == (uint8_t)((crc >> 8) & 0xFF) &&
buf[len + 1] == (uint8_t)(crc & 0xFF))
return len;
Expand Down Expand Up @@ -198,7 +208,6 @@ class mbRTU : public Link {
if (ret < 0)
return ret;
if ((size_t)ret == len) {
usleep(m_recvdelay);
if ((ret = recv(rbuf, size)) >= 0) {
#ifdef MBDEBUG
if (m_debug)
Expand All @@ -216,7 +225,7 @@ class mbRTU : public Link {

private:
devaddr_t m_devaddr;
useconds_t m_recvdelay;
bool crcLSBfirst; // CRC LSB first - New 2020 FW and above
#ifdef MBDEBUG
bool m_debug;
#endif
Expand Down