3 Commits

Author SHA1 Message Date
67035fe249 use ord 2020-08-29 12:48:01 +01:00
f842b2a03b dumb test fix 1 2020-08-29 13:42:39 +02:00
aa7556e950 dumb test 2020-08-29 13:39:29 +02:00
5 changed files with 18 additions and 764 deletions

3
.gitignore vendored
View File

@ -1,6 +1,3 @@
*.pyc *.pyc
.*.sw? .*.sw?
*.o
mbusgw

View File

@ -2,12 +2,6 @@ import wiringpi
from time import sleep from time import sleep
import serial import serial
from enum import Enum from enum import Enum
import array
import fcntl
import termios
import pprint
LOOP_ENABLE = 18 LOOP_ENABLE = 18
LOOP_DISABLE = 23 LOOP_DISABLE = 23
@ -95,13 +89,12 @@ class MeterbusResponseStates(Enum):
START2 = 4 START2 = 4
C_FIELD = 5 C_FIELD = 5
A_FIELD = 6 A_FIELD = 6
CI_FIELD = 7 C_FIELD = 7
USERDATA = 8 USERDATA = 8
CHKSUM = 9 CHKSUM = 9
STOP = 10 STOP = 10
DONE = 99 DONE = 99
ERROR = 100 ERROR = 100
TIMEOUT = 101
def a2h(a): def a2h(a):
return [ hex(x) for x in a ] return [ hex(x) for x in a ]
@ -109,33 +102,24 @@ def a2h(a):
class MeterbusSerial(object): class MeterbusSerial(object):
def __init__(self): def __init__(self):
self.port = serial.Serial('/dev/ttyAMA0', baudrate=2400, bytesize=8, parity='E', self.port = serial.Serial('/dev/ttyAMA0', baudrate=2400, bytesize=8, parity='E',
stopbits=1, timeout=1.0, xonxoff=0, rtscts=0) stopbits=1, timeout=None, xonxoff=0, rtscts=0)
def open(self):
loop(True)
sleep(0.5)
def close(self):
loop(False)
def shortFrameRequest(self, cmd, addr): def shortFrameRequest(self, cmd, addr):
chksum = (cmd + addr) & 0x00ff chksum = (cmd + addr) & 0x00ff
msg = bytearray([0x10, cmd, addr, chksum, 0x16]) msg = bytearray([0x10, cmd, addr, chksum, 0x16])
# print(a2h(msg)) print(a2h(msg))
frontendSample() frontendSample()
frontendRxEnable(False)
self.port.write(msg) self.port.write(msg)
buf = array.array('h', [0]) # FIXME: Attention, the actual write time of the interface is not considered.
while True: # This is a measured value.
fcntl.ioctl(self.port.fileno(), termios.TIOCSERGETLSR, buf, 1) sleep(0.030)
if buf[0] & termios.TIOCSER_TEMT:
break
sleep(0.001)
frontendHold() frontendHold()
frontendRxEnable(True)
frameData = [] frameData = []
frame = { frame = {
@ -147,132 +131,21 @@ class MeterbusSerial(object):
} }
expectedUserDataOctets = 0 expectedUserDataOctets = 0
state = MeterbusResponseStates.START1 state = MeterbusResponseStates.START1
while (state not in [MeterbusResponseStates.DONE, MeterbusResponseStates.ERROR, MeterbusResponseStates.TIMEOUT]): while (state not in [MeterbusResponseStates.DONE, MeterbusResponseStates.ERROR]):
# print("Waiting for input ... ") print("Waiting for input ... ")
c = self.port.read(1) c = self.port.read()
if len(c) == 0:
state = MeterbusResponseStates.TIMEOUT
continue
c = ord(c)
# print("State {}, Octet 0x{:02X}".format(state, c)) print("State {}, Octet {:02X}".format(state, ord(c)))
if state == MeterbusResponseStates.START1:
if c == 0x68:
frameData.append(c)
state = MeterbusResponseStates.LENGTH1
else:
print('Invalid start1 symbol')
state = MeterbusResponseStates.ERROR
elif state == MeterbusResponseStates.LENGTH1:
frameData.append(c)
frame['l'] = c
expectedUserDataOctets = frame['l'] - 3
state = MeterbusResponseStates.LENGTH2
elif state == MeterbusResponseStates.LENGTH2:
frameData.append(c)
if frame['l'] == c:
state = MeterbusResponseStates.START2
else:
print('Invalid length 2 octet')
state = MeterbusResponseStates.ERROR
elif state == MeterbusResponseStates.START2:
frameData.append(c)
if c == 0x68:
frameData.append(c)
state = MeterbusResponseStates.C_FIELD
else:
print('Invalid start2 symbol')
state = MeterbusResponseStates.ERROR
elif state == MeterbusResponseStates.C_FIELD:
frameData.append(c)
frame['c'] = c
state = MeterbusResponseStates.A_FIELD
elif state == MeterbusResponseStates.A_FIELD:
frameData.append(c)
frame['a'] = c
state = MeterbusResponseStates.CI_FIELD
elif state == MeterbusResponseStates.CI_FIELD:
frameData.append(c)
frame['ci'] = c
state = MeterbusResponseStates.USERDATA
elif state == MeterbusResponseStates.USERDATA:
frameData.append(c)
frame['userdata'].append(c)
expectedUserDataOctets -= 1
if expectedUserDataOctets == 0:
state = MeterbusResponseStates.CHKSUM
elif state == MeterbusResponseStates.CHKSUM:
frameData.append(c)
responseChksum = c
calculatedChksum = (frame['c'] + frame['a'] + frame['ci'] + sum(frame['userdata'])) & 0x00ff
if responseChksum != calculatedChksum:
print('Invalid checksum {} {}'.format(responseChksum, calculatedChksum))
state = MeterbusResponseStates.ERROR
else:
state = MeterbusResponseStates.STOP
elif state == MeterbusResponseStates.STOP:
frameData.append(c)
if c == 0x16:
state = MeterbusResponseStates.DONE
else:
print('Invalid stop symbol')
state = MeterbusResponseStates.ERROR
else:
print('Invalid state')
state = MeterbusResponseStates.ERROR
# print(a2h(frameData))
res = {}
if state == MeterbusResponseStates.DONE:
res = {'status': 'OK', 'frame': frameData, 'c': frame['c'], 'a': frame['a'], 'ci': frame['ci'], 'userdata': frame['userdata']}
else:
res = {'status': 'ERROR', 'code': state }
return res
if __name__ == "__main__": if __name__ == "__main__":
init() init()
loop(False) loop(False)
sleep(2.0) sleep(5.0)
loop(True)
sleep(5.0)
m = MeterbusSerial() m = MeterbusSerial()
m.open() m.shortFrameRequest(0x5b, 84)
devices = [ 84, 87, 86, 85, 82, 81, 83, 80 ]
stats = {
'total': {
'cycles': 0,
'ok': 0,
'error': 0
},
'devices': { x: { 'ok': 0, 'error': 0 } for x in devices }
}
pp = pprint.PrettyPrinter(indent=4)
while True:
for a in devices:
r = m.shortFrameRequest(0x5b, a)
stats['total']['cycles'] += 1
if r['status'] == 'ERROR':
stats['total']['error'] += 1
stats['devices'][a]['error'] += 1
print("Error for {}, last state was {}, restarting loop".format(a, r['code']))
m.close()
sleep(5)
m.open()
else:
stats['total']['ok'] += 1
stats['devices'][a]['ok'] += 1
sleep(1)
pp.pprint(stats)
print("")
sleep(15)

View File

@ -1,17 +0,0 @@
CC=gcc
CFLAGS=-Wall
LDFLAGS=-lwiringPi
mbusgw: mbusgw.o
$(CC) -o $@ $(LDFLAGS) $^
.c.o:
$(CC) $(CFLAGS) -c $<
.PHONY: all
all: mbusgw
.PHONY: clean
clean:
-rm -f *.o mbusgw

View File

@ -1,551 +0,0 @@
#include <wiringPi.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <stdarg.h>
#include <signal.h>
#include "mbusgw.h"
typedef enum {
e_START1,
e_LENGTH1,
e_LENGTH2,
e_START2,
e_C_FIELD,
e_A_FIELD,
e_CI_FIELD,
e_USERDATA,
e_CHKSUM,
e_STOP,
e_DONE,
e_ERROR,
e_TIMEOUT
} t_state;
static bool verbose = false;
static bool loopActiveFlag = false;
static void msleep(uint32_t t) {
usleep((useconds_t)(t * 1000));
}
static void infolog(const char *format, ...) {
va_list ap;
va_start(ap, format);
if (verbose) {
vfprintf(stderr, format, ap);
}
va_end(ap);
}
static void errlog(const char *format, ...) {
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
}
static void ledRed(bool v) {
if (v) {
digitalWrite(LED_RED, HIGH);
} else {
digitalWrite(LED_RED, LOW);
}
}
static void ledGreen(bool v) {
if (v) {
digitalWrite(LED_GREEN, HIGH);
} else {
digitalWrite(LED_GREEN, LOW);
}
}
static void frontendReset() {
digitalWrite(FRONTEND_RESET, LOW);
msleep(25);
digitalWrite(FRONTEND_RESET, HIGH);
msleep(100);
}
static void loopControl(bool v) {
if (v) {
digitalWrite(LOOP_ENABLE, HIGH);
msleep(5);
digitalWrite(LOOP_ENABLE, LOW);
} else {
digitalWrite(LOOP_DISABLE, HIGH);
digitalWrite(LOOP_DISABLE, LOW);
}
}
static void frontendSample() {
digitalWrite(FRONTEND_SAMPLE_HOLD, LOW);
}
static void frontendHold() {
digitalWrite(FRONTEND_SAMPLE_HOLD, HIGH);
}
static void loopStatusISR() {
loopActiveFlag = digitalRead(LOOP_STATUS) == LOW;
if (! loopActiveFlag) {
ledRed(true);
}
}
static void deinit() {
ledRed(false);
ledGreen(false);
loopControl(false);
frontendSample();
}
static void termHandler(/*@unused@*/ int signum)
{
infolog("Termination requested via signal\n");
deinit();
exit(EXIT_SUCCESS);
}
static void init() {
infolog("Register termination handler\n");
signal(SIGTERM, termHandler);
signal(SIGINT, termHandler);
infolog("setting up gpios\n");
wiringPiSetupGpio();
pinMode(LOOP_ENABLE, OUTPUT);
digitalWrite(LOOP_ENABLE, LOW);
pinMode(LOOP_DISABLE, OUTPUT);
digitalWrite(LOOP_DISABLE, LOW);
pinMode(FRONTEND_RESET, OUTPUT);
digitalWrite(FRONTEND_RESET, LOW);
pinMode(FRONTEND_SAMPLE_HOLD, OUTPUT);
digitalWrite(FRONTEND_SAMPLE_HOLD, LOW);
pinMode(LED_RED, OUTPUT);
digitalWrite(LED_RED, LOW);
pinMode(LED_GREEN, OUTPUT);
digitalWrite(LED_GREEN, LOW);
pinMode(LOOP_STATUS, INPUT);
pullUpDnControl(LOOP_STATUS, PUD_UP);
wiringPiISR(LOOP_STATUS, INT_EDGE_BOTH, loopStatusISR);
frontendReset();
loopControl(false);
}
static int openSerial(char *serialDevice, uint32_t speedNum) {
int fd = open(serialDevice, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
errlog("error %d opening serial device %s: %s\n",
errno, serialDevice, strerror(errno));
return -1;
}
struct termios tty;
if (tcgetattr(fd, &tty) != 0) {
errlog("error %d getting attributes for serial device %s: %s\n",
errno, serialDevice, strerror(errno));
return -1;
}
int speed;
switch (speedNum) {
case 1200:
speed = B1200;
break;
case 2400:
speed = B2400;
break;
case 4800:
speed = B4800;
break;
case 9600:
speed = B9600;
break;
default:
errlog("speed %d not supported\n", speedNum);
return -1;
}
cfsetospeed(&tty, speed);
cfsetispeed(&tty, speed);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
tty.c_cflag &= ~(CRTSCTS | CSTOPB | PARODD);
tty.c_cflag |= (CLOCAL | CREAD | PARENB);
tty.c_lflag = 0;
tty.c_oflag = 0;
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 50;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_iflag |= IGNBRK;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
errlog("error %d setting attributes for serial device %s: %s\n",
errno, serialDevice, strerror(errno));
return -1;
}
loopControl(true);
msleep(2000);
return fd;
}
static void closeSerial(int fd) {
loopControl(false);
close(fd);
}
static /*@null@*/ t_longframe *request(int fd, uint8_t cmd, uint8_t addr) {
errno = 0;
t_longframe *frame = (t_longframe*) malloc(sizeof(t_longframe));
if (! frame) {
errlog("unable to allocate memory for frame\n");
return NULL;
}
memset(frame, 0, sizeof(t_longframe));
uint8_t chksum = cmd + addr;
frontendSample();
uint8_t sendBuf[5];
sendBuf[0] = 0x10;
sendBuf[1] = cmd;
sendBuf[2] = addr;
sendBuf[3] = chksum;
sendBuf[4] = 0x16;
write(fd, sendBuf, 5);
while (true) {
int r = 0;
if (ioctl(fd, TIOCSERGETLSR, &r) == -1) {
errlog("error %d getting TIOCSERGETLSR for fd %d: %s\n",
errno, fd, strerror(errno));
errno = ERROR_APP_SPECIFIC_ERROR_FLAG | ERROR_TX_REG_UNACCESSIBLE;
if (frame->userdata) {
free(frame->userdata);
}
free(frame);
frame = NULL;
return NULL;
}
if ((r & TIOCSER_TEMT) != 0) {
break;
}
}
msleep(1);
frontendHold();
uint8_t userdataIdx = 0;
uint8_t calculatedChksum = 0;
t_state state = e_START1;
while ((state != e_DONE) &&
(state != e_ERROR) &&
(state != e_TIMEOUT)) {
infolog("waiting for input ...\n");
uint8_t c;
ssize_t s = read(fd, &c, 1);
if (s == 0) {
errlog("timeout waiting for input\n");
state = e_TIMEOUT;
continue;
}
infolog("state %d, Octet %02x\n", state, c);
switch(state) {
case e_START1:
if (c == 0x68) {
frame->start1 = c;
state = e_LENGTH1;
} else {
errlog("invalid start1 symbol %02x\n", c);
state = e_ERROR;
}
break;
case e_LENGTH1:
if (c <= 3) {
errlog("length to small %02x\n", c);
state = e_ERROR;
} else {
frame->length1 = c;
if (frame->userdata) {
free(frame->userdata);
}
frame->userdata = (uint8_t*) malloc(frame->length1 - 3);
if (! frame->userdata) {
errlog("unable to allocate memory for userdata\n");
state = e_ERROR;
}
state = e_LENGTH2;
}
break;
case e_LENGTH2:
if (frame->length1 != c) {
errlog("invalid length2 %02x vs. %02x\n", frame->length1, c);
state = e_ERROR;
} else {
frame->length2 = c;
state = e_START2;
}
break;
case e_START2:
if (c == 0x68) {
frame->start2 = c;
state = e_C_FIELD;
} else {
errlog("invalid start2 symbol %02x\n", c);
state = e_ERROR;
}
break;
case e_C_FIELD:
frame->c = c;
calculatedChksum += c;
state = e_A_FIELD;
break;
case e_A_FIELD:
frame->a = c;
calculatedChksum += c;
state = e_CI_FIELD;
break;
case e_CI_FIELD:
frame->ci = c;
calculatedChksum += c;
state = e_USERDATA;
break;
case e_USERDATA:
frame->userdata[userdataIdx] = c;
calculatedChksum += c;
userdataIdx++;
if (userdataIdx == (frame->length1 - 3)) {
state = e_CHKSUM;
}
break;
case e_CHKSUM:
if (c != calculatedChksum) {
errlog("invalid checksum %02x vs %02x\n", calculatedChksum, c);
state = e_ERROR;
} else {
frame->chksum = c;
state = e_STOP;
}
break;
case e_STOP:
if (c == 0x16) {
frame->stop = c;
state = e_DONE;
} else {
errlog("invalid stop symbol %02x\n", c);
state = e_ERROR;
}
break;
default:
errlog("illegal state %d\n", state);
state = e_ERROR;
break;
}
}
if ((state == e_ERROR) || (state == e_TIMEOUT)) {
if (state == e_ERROR) {
errno = ERROR_TX_REG_UNACCESSIBLE | ERROR_STATE_ENGINE;
} else if (state == e_TIMEOUT) {
errno = ERROR_TX_REG_UNACCESSIBLE | ERROR_TIMEOUT;
}
if (frame->userdata) {
free(frame->userdata);
frame->userdata = NULL;
}
free(frame);
frame = NULL;
}
return frame;
}
static void printFrame(bool hexOut, t_longframe *frame) {
if (hexOut) {
fprintf(stderr, "%02x %02x %02x %02x %02x %02x %02x\n",
frame->start1, frame->length1, frame->length2, frame->start2,
frame->c, frame->a, frame->ci);
uint8_t i = 0;
for (i = 0; i < (frame->length1 - 3); i++) {
if ((i != 0) && ((i % 16) == 0)) {
fprintf(stderr, "\n");
}
fprintf(stderr, "%02x ", frame->userdata[i]);
}
fprintf(stderr, "\n");
fprintf(stderr, "%02x %02x\n", frame->chksum, frame->stop);
} else {
fprintf(stdout, "%c%c", 0, frame->length1 + 6);
fprintf(stdout, "%c%c%c%c%c%c%c",
frame->start1, frame->length1, frame->length2, frame->start2,
frame->c, frame->a, frame->ci);
uint8_t i = 0;
for (i = 0; i < (frame->length1 - 3); i++) {
fprintf(stdout, "%c", frame->userdata[i]);
}
fprintf(stdout, "%c%c", frame->chksum, frame->stop);
fflush(stdout);
}
}
int main(int argc, char *argv[]) {
init();
ledGreen(true);
int exitCode = 0;
bool hexOut = false;
bool lineMode = false;
uint8_t addr = 0;
uint8_t cmd = 0x5b;
int opt;
while ((opt = getopt(argc, argv, "lhvxc:a:")) != -1) {
switch(opt) {
case 'h':
errlog("mbusgw - interface access tool for meterbus gateway\n");
errlog("-h ... Show this help page\n");
errlog("-v ... Verbose output\n");
errlog("-x ... Output as hex string in 'human-readable' form\n");
errlog("-a addr ... Address of device to be queried\n");
errlog("-c cmd ... Command to be sent, default is 0x5b\n");
errlog("-l ... Read cmd and addr from stdin unless\n");
errlog(" 0x00 0x00 is provided and return the\n");
errlog(" result on stdout.\n");
errlog(" It is preceeds by 0x00 for okay and the\n");
errlog(" length.\n");
errlog(" In case of an error it will by 0xff followed\n");
errlog(" by one octet with an error code\n");
errlog("\n");
break;
case 'v':
verbose = true;
break;
case 'l':
lineMode = true;
break;
case 'x':
hexOut = true;
break;
case 'a':
addr = (uint8_t) strtol(optarg, NULL, 0);
break;
case 'c':
cmd = (uint8_t) strtol(optarg, NULL, 0);
break;
}
}
infolog("opening device\n");
int fd = openSerial(DEFAULT_SERIAL_DEVICE, 2400);
if (fd == -1) {
errlog("unable to open device, fatal error\n");
deinit();
exit(EXIT_FAILURE);
}
while (true) {
if (! loopActiveFlag) {
errlog("loop is not active, enable it and delay\n");
loopControl(true);
msleep(2000);
}
if (lineMode) {
infolog("lineMode, waiting for input\n");
fread(&cmd, 1, 1, stdin);
fread(&addr, 1, 1, stdin);
}
if ((cmd == 0) && (addr == 0)) {
errlog("termination requested\n");
break;
}
infolog("sending request %02x %02x\n", cmd, addr);
t_longframe *frame = NULL;
if (loopActiveFlag) {
ledRed(false);
frame = request(fd, cmd, addr);
} else {
errlog("loop is currently inactive, no need to try\n");
}
if (frame) {
infolog("received a valid frame\n");
printFrame(hexOut, frame);
free(frame->userdata);
frame->userdata = NULL;
free(frame);
frame = NULL;
} else {
ledRed(true);
if (! loopActiveFlag) {
errno = ERROR_APP_SPECIFIC_ERROR_FLAG | ERROR_LOOP_FAILURE;
}
errlog("error %04x occured\n", errno);
if (! hexOut) {
uint8_t maskedError = (uint8_t)(errno & ~ERROR_APP_SPECIFIC_ERROR_FLAG);
fprintf(stdout, "%c%c", maskedError, 0);
fflush(stdout);
}
if (! lineMode) {
exitCode = -2;
}
}
if (! lineMode) {
break;
}
}
infolog("closing device\n");
closeSerial(fd);
deinit();
return exitCode;
}

View File

@ -1,48 +0,0 @@
#ifndef _MBUSGW_H_
#define _MBUSGW_H_
#include <stdint.h>
#define LOOP_ENABLE 18
#define LOOP_DISABLE 23
#define LOOP_STATUS 22
#define FRONTEND_RESET 26
#define FRONTEND_SAMPLE_HOLD 19
#define LED_RED 5
#define LED_GREEN 6
#define DEFAULT_SERIAL_DEVICE "/dev/ttyAMA0"
#define ERROR_TIMEOUT 1
#define ERROR_STATE_ENGINE 2
#define ERROR_LOOP_FAILURE 3
#define ERROR_TX_REG_UNACCESSIBLE 4
#define ERROR_APP_SPECIFIC_ERROR_FLAG 0x4000
typedef struct {
uint8_t start1;
uint8_t length1;
uint8_t length2;
uint8_t start2;
uint8_t l;
uint8_t c;
uint8_t a;
uint8_t ci;
uint8_t *userdata;
uint8_t chksum;
uint8_t stop;
} t_longframe;
#endif