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 800 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,23 +0,0 @@
CC=gcc
CFLAGS=-Wall
LDFLAGS=-lwiringPi
mbusgw: mbusgw.o
$(CC) -o $@ $(LDFLAGS) $^
mbusgw.o: mbusgw.c mbusgw.h
%.o : %.c
$(CC) $(CFLAGS) -c $<
.PHONY: all
all: mbusgw
.PHONY: install
install: all
sudo cp mbusgw /usr/local/bin/
.PHONY: clean
clean:
-rm -f *.o mbusgw

View File

@ -1,569 +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;
int serialFd;
bool verbose = false;
bool loopActiveFlag = false;
bool loopEnabled = false;
void msleep(uint32_t t) {
usleep(t * 1000);
}
void infolog(const char *format, ...) {
va_list ap;
va_start(ap, format);
if (verbose) {
vfprintf(stderr, format, ap);
}
va_end(ap);
}
void errlog(const char *format, ...) {
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
}
void ledRed(bool v) {
if (v) {
digitalWrite(LED_RED, HIGH);
} else {
digitalWrite(LED_RED, LOW);
}
}
void ledGreen(bool v) {
if (v) {
digitalWrite(LED_GREEN, HIGH);
} else {
digitalWrite(LED_GREEN, LOW);
}
}
void frontendReset() {
digitalWrite(FRONTEND_RESET, LOW);
msleep(25);
digitalWrite(FRONTEND_RESET, HIGH);
msleep(100);
}
void loopControl(bool v) {
if (v) {
digitalWrite(LOOP_ENABLE, HIGH);
msleep(5);
digitalWrite(LOOP_ENABLE, LOW);
loopEnabled = true;
} else {
loopEnabled = false;
digitalWrite(LOOP_DISABLE, HIGH);
digitalWrite(LOOP_DISABLE, LOW);
}
}
void frontendSample() {
digitalWrite(FRONTEND_SAMPLE_HOLD, LOW);
}
void frontendHold() {
digitalWrite(FRONTEND_SAMPLE_HOLD, HIGH);
}
void loopStatusISR() {
loopActiveFlag = digitalRead(LOOP_STATUS) == LOW;
if ((! loopActiveFlag) && (loopEnabled)) {
loopEnabled = false;
ledRed(true);
}
}
void myExit(int e) {
ledRed(false);
ledGreen(false);
loopControl(false);
frontendSample();
exit(e);
}
void termHandler(int signum)
{
infolog("Termination requested via signal\n");
myExit(0);
}
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);
}
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;
}
void closeSerial(int fd) {
loopControl(false);
close(fd);
}
uint8_t request(int fd, uint8_t cmd, uint8_t addr, t_longframe **retFrame) {
uint8_t retCode = SUCCESS;
t_longframe *frame = (t_longframe*) malloc(sizeof(t_longframe));
if (! frame) {
errlog("unable to allocate memory for frame\n");
return ERROR_OUT_OF_MEMORY__FRAME;
}
frame->userdata = NULL;
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 (1) {
int r;
if (ioctl(fd, TIOCSERGETLSR, &r) == -1) {
errlog("error %d getting TIOCSERGETLSR for fd %d: %s\n",
errno, fd, strerror(errno));
free(frame);
frame = NULL;
return ERROR_TX_REG_UNACCESSIBLE;
}
if (r & TIOCSER_TEMT) {
break;
}
}
msleep(1);
frontendHold();
uint8_t userdataIdx = 0;
uint8_t calculatedChksum = 0;
t_state state = e_START1;
while ((state != e_DONE) &&
(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);
retCode = ERROR_STATE_ENGINE__START1;
state = e_ERROR;
}
break;
case e_LENGTH1:
if (c <= 3) {
errlog("length to small %02x\n", c);
retCode = ERROR_STATE_ENGINE__LENGTH1;
state = e_ERROR;
} else {
frame->length1 = c;
frame->userdata = (uint8_t*) malloc(frame->length1 - 3);
if (! frame->userdata) {
errlog("unable to allocate memory for userdata\n");
retCode = ERROR_OUT_OF_MEMORY__USERDATA;
state = e_ERROR;
}
state = e_LENGTH2;
}
break;
case e_LENGTH2:
if (frame->length1 != c) {
errlog("invalid length2 %02x vs. %02x\n", frame->length1, c);
retCode = ERROR_STATE_ENGINE__LENGTH2;
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);
retCode = ERROR_STATE_ENGINE__START2;
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);
retCode = ERROR_STATE_ENGINE__INVALID_CHKSUM;
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);
retCode = ERROR_STATE_ENGINE__STOP;
state = e_ERROR;
}
break;
case e_ERROR:
ledRed(true);
infolog("already error, read the rest (now: %02x) until timeout\n", c);
break;
default:
errlog("illegal state %d\n", state);
retCode = ERROR_STATE_ENGINE__ILLEGAL_STATE;
state = e_ERROR;
break;
}
}
if ((state == e_ERROR) || (state == e_TIMEOUT)) {
if (state == e_ERROR) {
if (retCode == SUCCESS) {
retCode = ERROR_STATE_ENGINE__UNKNOWN;
}
} else if (state == e_TIMEOUT) {
if (retCode == SUCCESS) {
retCode = ERROR_TIMEOUT;
}
}
if (frame->userdata) {
free(frame->userdata);
frame->userdata = NULL;
}
free(frame);
frame = NULL;
}
*retFrame = frame;
return retCode;
}
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);
for (uint8_t i = 0; i < (frame->length1 - 3); i++) {
if (i && !(i % 16)) {
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);
for (uint8_t 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, BAUDRATE);
if (fd == -1) {
errlog("unable to open device, fatal error\n");
myExit(-1);
}
while (1) {
if (lineMode) {
infolog("lineMode, waiting for input\n");
fread(&cmd, 1, 1, stdin);
fread(&addr, 1, 1, stdin);
}
if (lineMode && (cmd == LINEMODE_CMD_PREFIX)) {
if (addr == LINEMODE_CMD_TERMINATE) {
infolog("termination requested\n");
break;
}
if (addr == LINEMODE_CMD_LOOP_SHUTDOWN) {
infolog("loop shutdown requested\n");
loopControl(false);
continue;
}
}
if (! loopActiveFlag) {
errlog("loop is not active, enable it and delay\n");
loopControl(true);
msleep(2000);
}
infolog("sending request %02x %02x\n", cmd, addr);
t_longframe *frame = NULL;
uint8_t requestReturnCode = 0;
if (loopActiveFlag) {
ledRed(false);
requestReturnCode = request(fd, cmd, addr, &frame);
} else {
errlog("loop is currently inactive, no need to try\n");
requestReturnCode = ERROR_LOOP_FAILURE;
}
if (requestReturnCode == SUCCESS) {
infolog("received a valid frame\n");
printFrame(hexOut, frame);
free(frame->userdata);
frame->userdata = NULL;
free(frame);
frame = NULL;
} else {
ledRed(true);
if (! loopActiveFlag) {
requestReturnCode = ERROR_LOOP_FAILURE;
}
errlog("error %04x occured\n", requestReturnCode);
if (! hexOut) {
fprintf(stdout, "%c%c", requestReturnCode, 0);
fflush(stdout);
}
if (! lineMode) {
exitCode = -2;
}
}
if (! lineMode) {
break;
}
}
infolog("closing device\n");
closeSerial(fd);
myExit(exitCode);
}

View File

@ -1,60 +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 BAUDRATE 2400
#define LINEMODE_CMD_PREFIX 0
#define LINEMODE_CMD_TERMINATE 0
#define LINEMODE_CMD_LOOP_SHUTDOWN 1
#define SUCCESS 0
#define ERROR_TIMEOUT 1
#define ERROR_LOOP_FAILURE 2
#define ERROR_TX_REG_UNACCESSIBLE 3
#define ERROR_OUT_OF_MEMORY__FRAME 4
#define ERROR_OUT_OF_MEMORY__USERDATA 5
#define ERROR_STATE_ENGINE__START1 10
#define ERROR_STATE_ENGINE__LENGTH1 11
#define ERROR_STATE_ENGINE__LENGTH2 12
#define ERROR_STATE_ENGINE__START2 13
#define ERROR_STATE_ENGINE__INVALID_CHKSUM 14
#define ERROR_STATE_ENGINE__STOP 15
#define ERROR_STATE_ENGINE__ILLEGAL_STATE 16
#define ERROR_STATE_ENGINE__UNKNOWN 17
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