/**
* (c) Jochen Dolze, 2019
*
* as1440.c is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* as1440.c is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with as1440.c. If not, see .
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
int tty_fd;
bool verbose;
bool nounit;
bool trim0;
bool debug;
int hangup(int);
int msleep(long msec)
{
struct timespec ts;
int res;
if (msec < 0) {
errno = EINVAL;
return -1;
}
ts.tv_sec = msec / 1000;
ts.tv_nsec = (msec % 1000) * 1000000;
do {
res = nanosleep(&ts, &ts);
}
while (res && errno == EINTR);
return res;
}
int init_tty(int fd, struct termios *tio)
{
if (verbose)
fprintf(stderr, "init tty\n");
memset(tio, 0, sizeof(struct termios));
tio->c_cflag = CS7 | CREAD | CLOCAL | PARENB;
tio->c_cc[VTIME] = 40; // 4 seconds;
if (cfsetospeed(tio, B300) == -1) {
perror("cfsetospeed");
return -2;
}
if (cfsetispeed(tio, B300) == -1) {
perror("cfsetispeed");
return -2;
}
if (tcsetattr(fd, TCSANOW, tio) == -1) {
perror("tcsetattr");
return -2;
}
if (tcflush(fd, TCIOFLUSH) == -1) {
perror("tcflush");
return -2;
}
if (verbose)
fprintf(stderr, "init tty ok\n");
return 0;
}
int init(int fd, char *maxbaud)
{
if (verbose)
fprintf(stderr, "init\n");
if (write(fd, "/?!\r\n", 5) == -1) {
perror("write");
return -2;
}
char rbuf[25];
int l = 0;
unsigned char c = 'X';
while (c != 10) {
if (read(fd, &c, 1) <= 0) {
fprintf(stderr, "timeout waiting for init response\n");
return -1;
}
if ((c != 10) && (c != 13))
rbuf[l++] = c;
if (l > 21) {
rbuf[l] = 0;
fprintf(stderr, "too many chars (%i>21) in init response: '%s'\n", l, rbuf);
return -1;
}
}
rbuf[l] = 0;
if (l != 21) {
fprintf(stderr, "response hasn't 21 characters: '%s'\n", rbuf);
return -1;
}
if (strncmp(rbuf, "/ELS", 4)) {
fprintf(stderr, "response doesn't start with /ELS: '%s'", rbuf);
return -1;
}
if ((rbuf[5] != '\\') || (rbuf[6] != '@')) {
fprintf(stderr, "response doesn't contain \\@: '%s'", rbuf);
return -1;
}
*maxbaud = rbuf[4];
msleep(800); // mandatory!
if (verbose)
fprintf(stderr, "init ok\n");
return 0;
}
int changebaud(int fd, struct termios *tio, char baudrate)
{
int newbaud = (1 << (baudrate - 48)) * 300;
if (verbose)
fprintf(stderr, "change baudrate to B%i\n", newbaud);
if (write(fd, "\x06", 1) == -1) {
perror("write");
return -2;
}
if (write(fd, "0", 1) == -1) {
perror("write");
return -2;
}
if (write(fd, &baudrate, 1) == -1) {
perror("write");
return -2;
}
if (write(fd, "1\r\n", 3) == -1) {
perror("write");
return -2;
}
msleep(400); // mandatory!
int reto, reti;
switch (baudrate) {
case '0':
break;
case '1':
reto = cfsetospeed(tio, B600);
reti = cfsetispeed(tio, B600);
break;
case '2':
reto = cfsetospeed(tio, B1200);
reti = cfsetispeed(tio, B1200);
break;
case '3':
reto = cfsetospeed(tio, B2400);
reti = cfsetispeed(tio, B2400);
break;
case '4':
reto = cfsetospeed(tio, B4800);
reti = cfsetispeed(tio, B4800);
break;
case '5':
reto = cfsetospeed(tio, B9600);
reti = cfsetispeed(tio, B9600);
break;
case '6':
reto = cfsetospeed(tio, B19200);
reti = cfsetispeed(tio, B19200);
break;
default:
fprintf(stderr, "unknown baudrate '%c' (%i)\n", baudrate, newbaud);
return -1;
}
if (baudrate != '0') {
if (reto == -1) {
perror("cfsetospeed");
return -2;
}
if (reti == -1) {
perror("cfsetispeed");
return -2;
}
if (tcsetattr(fd, TCSANOW, tio) == -1) {
perror("tcsetattr");
return -2;
}
if (tcflush(fd, TCIOFLUSH) == -1) {
perror("tcflush");
return -2;
}
}
if (verbose)
fprintf(stderr, "change baudrate to B%i ok\n", newbaud);
return 0;
}
int readanswer(int fd, bool dontprint)
{
unsigned char c = 'x';
char rbuf[50];
int l = 0;
while (c != 3) {
if (read(fd, &c, 1) <= 0) {
fprintf(stderr, "timeout waiting for answer\n");
return -2;
}
if (c > 13)
rbuf[l++] = c;
if (l > 48) {
rbuf[l] = 0;
fprintf(stderr, "answer too long (%i): '%s'\n", l, rbuf);
return -1;
}
}
rbuf[l] = 0;
read(fd, &c, 1); // read BCC
if (dontprint)
return 0;
if ((rbuf[0] < 48) || (rbuf[0] > 57)) {
fprintf(stderr, "answer with wrong format: '%s'\n", rbuf);
return -1;
}
if (debug) fprintf(stderr,"received '%s'\n",rbuf);
int i;
bool inval = false;
bool dotrim = trim0;
char *p = rbuf;
while (*p) {
if ((inval) && (dotrim)) {
if ((*p == '0') && (*(p + 1)) != '.') {
memmove(p, p + 1, strlen(p + 1));
continue;
} else {
dotrim = false;
}
}
if (*p == '(') {
*p = 9;
inval = true;
}
if (*p == ')') {
*p = 0;
}
if (*p == '*') {
if (inval) {
if (nounit) {
*p = 0;
} else {
*p = 32;
}
} else {
*p = '_';
}
}
p++;
}
char *s = strchr(rbuf, ':');
if (s) {
fprintf(stdout, "%s\n", ++s);
} else {
fprintf(stdout, "%snotfound\n", rbuf);
}
fflush(stdout);
return 0;
}
char calcBCC(unsigned char *buf)
{
int i;
char BCC = buf[0];
for (i = 1; i < strlen(buf); i++) {
BCC = BCC ^ buf[i];
if (buf[i] == 3)
break;
}
return BCC;
}
int sendpwd(int fd, char *pwd)
{
if (verbose)
fprintf(stderr, "sendpwd\n");
if (strlen(pwd) != 8) {
fprintf(stderr, "password not 8 characters: '%s'\n", pwd);
hangup(fd);
return -1;
}
char buf[20];
sprintf(buf, "%cP1%c(%s)%c ", 1, 2, pwd, 3);
int len = strlen(buf);
buf[len - 1] = calcBCC(&buf[1]);
if (write(fd, buf, len) == -1) {
perror("write");
return -2;
}
if (verbose)
fprintf(stderr, "sendpwd ok\n");
return 0;
}
int getr5(int fd, char *value)
{
char buf[20];
sprintf(buf, "%cR5%c%s()%c ", 1, 2, value, 3);
int len = strlen(buf);
buf[len - 1] = calcBCC(&buf[1]);
/*
int i;
for (i = 0; i < len; i++) {
if (buf[i] > 10) {
fprintf(stderr, "%c", buf[i]);
} else {
fprintf(stderr, " %x ", buf[i]);
}
}
fprintf(stderr, "\n");
*/
if (write(fd, buf, len) == -1) {
perror("write");
return -2;
}
return 0;
}
int hangup(int fd)
{
if (verbose)
fprintf(stderr, "hangup\n");
msleep(200);
char buf[] = { 1, 'B', '0', 3, 'q' };
if (write(fd, buf, strlen(buf)) == -1) {
perror("write");
return -2;
}
if (verbose)
fprintf(stderr, "hangup ok\n");
return 0;
}
int readack(int fd)
{
if (verbose)
fprintf(stderr, "readack\n");
unsigned char c;
if (read(fd, &c, 1) == -1) {
perror("read");
return -2;
}
if (c == 1) {
fprintf(stderr, "got hangup instead of ACK\n");
return -1;
}
if (c == 21) {
fprintf(stderr, "got NAK instead of ACK\n");
return -1;
}
if (c != 6) {
fprintf(stderr, "got %i instead of ACK\n", c);
return -1;
}
if (verbose)
fprintf(stderr, "readack ok\n");
return 0;
}
void cleanup(void)
{
if (tty_fd != -1) {
hangup(tty_fd);
close(tty_fd);
tty_fd = -1;
}
}
void ctrlc(int sig)
{
exit(EXIT_SUCCESS);
}
void usage(char *name)
{
fprintf(stderr,
"usage: %s -F device -O obisval1[:obisval2...] [-p password ] [-v] [-n] [-e] [-d]\n"
" -v\tverbose\n"
" -d\tinclude debug output\n"
" -t\tleft trim zeros\n"
" -n\tdon't add units\n"
" -e\tendless loop (end with ctrl-c)\n\n", name);
}
int main(int argc, char **argv)
{
if (argc == 1) {
usage(argv[0]);
exit(EXIT_FAILURE);
}
int opt;
char *dev = NULL;
char *pwd = "00000000";
char *obis = NULL;
bool endless = false;
debug = false;
verbose = false;
nounit = false;
trim0 = false;
while ((opt = getopt(argc, argv, "F:O:p:vtned")) != -1) {
switch (opt) {
case 'F':
dev = optarg;
break;
case 'O':
obis = optarg;
break;
case 'v':
verbose = true;
break;
case 'p':
pwd = optarg;
break;
case 't':
trim0 = true;
case 'n':
nounit = true;
break;
case 'e':
endless = true;
break;
case 'd':
debug = true;
break;
default:
usage(argv[0]);
exit(EXIT_FAILURE);
}
}
if (!obis) {
usage(argv[0]);
exit(EXIT_FAILURE);
}
if (!dev) {
usage(argv[0]);
exit(EXIT_FAILURE);
}
if (strlen(pwd) != 8) {
fprintf(stderr, "password must contain 8 characters (%lu): '%s'\n", strlen(pwd), pwd);
exit(EXIT_FAILURE);
}
tty_fd = -1;
atexit(cleanup);
signal(SIGINT, ctrlc);
signal(SIGPIPE, SIG_IGN);
bool first = true;
while (1) {
if (!first) {
if (verbose)
fprintf(stderr, "waiting 30 seconds\n");
sleep(30);
}
first = false;
if (tty_fd == -1) {
if (verbose)
fprintf(stderr, "opening %s\n", dev);
tty_fd = open(dev, O_RDWR);
if (tty_fd == -1) {
if (verbose)
perror(dev);
if (endless)
continue;
return -1;
}
}
struct termios tio;
int ret = init_tty(tty_fd, &tio);
if (ret < 0) {
if (ret == -2)
cleanup();
continue;
}
unsigned char maxbaud;
ret = init(tty_fd, &maxbaud);
if (ret < 0) {
if (ret == -2)
cleanup();
continue;
}
ret = changebaud(tty_fd, &tio, maxbaud);
if (ret < 0) {
if (ret == -2)
cleanup();
continue;
}
ret = readanswer(tty_fd, true);
if (ret < 0) {
if (ret == -2)
cleanup();
continue;
}
ret = sendpwd(tty_fd, pwd);
if (ret < 0) {
if (ret == -2)
cleanup();
continue;
}
ret = readack(tty_fd);
if (ret < 0) {
if (ret == -2)
cleanup();
continue;
}
bool noerror = true;
int tc = 0;
do {
char *cobis = strdup(obis);
char *ptr = strtok(cobis, ":");
while (ptr) {
ret = getr5(tty_fd, ptr);
if (ret < 0) {
if (ret == -2)
cleanup();
noerror = false;
ptr = NULL;
continue;
}
ret = readanswer(tty_fd, false);
if (ret == 0)
tc = 0;
if (ret == -1) {
noerror = false;
ptr = NULL;
continue;
}
if (ret == -2) {
tc = tc + 1;
if (tc > 8) {
noerror = false;
ptr = NULL;
continue;
}
}
ptr = strtok(NULL, ":");
}
free(cobis);
} while (noerror && endless);
if (!endless)
break;
}
exit(EXIT_SUCCESS);
}