40 Commits

Author SHA1 Message Date
a6ac9183cc some tests with lint 2020-09-04 17:17:47 +01:00
d4ce143298 use 2400 Baud 2020-09-03 21:08:35 +01:00
33d715ca83 use 9600 Baud 2020-09-03 20:50:42 +01:00
fdcd37c125 termination handler 2020-09-03 21:41:12 +02:00
20d617a10d shorter loop enable period 2020-09-03 20:35:27 +01:00
c0556dfa1c fix 2020-09-03 17:09:25 +02:00
4a02773d6d fix 2020-09-03 16:09:51 +02:00
5b6a9fa1ad fix 2020-09-03 16:08:29 +02:00
ff515e202a fix 2020-09-03 16:07:09 +02:00
76db8066b9 rename 2020-09-03 16:05:14 +02:00
0371c2174e fix 2020-09-03 16:03:50 +02:00
d65b0b1203 fix 2020-09-03 16:01:52 +02:00
7e6e6d8df0 logging 2020-09-03 14:40:35 +02:00
fb44b8b237 logging 2020-09-03 14:20:41 +02:00
59f4c2d972 error handling and reporting 2020-09-03 14:05:25 +02:00
dfee439030 works also in line mode so far 2020-08-31 22:32:37 +01:00
fb1b6dbc3a works so far 2020-08-31 21:58:34 +01:00
132dd92b37 c translation initial 2020-08-31 18:29:37 +01:00
32f7e1c87c ignore o files and executable 2020-08-31 18:29:11 +01:00
cee0a5de71 count cycles 2020-08-30 21:21:20 +01:00
910be985b1 disable debugging 2020-08-30 21:18:48 +01:00
8c694753f7 debugging 2020-08-30 22:15:59 +02:00
93adb7a5d4 code beautifying 2020-08-30 22:13:30 +02:00
4e07e84ce7 pretty print 2020-08-30 10:54:02 +01:00
bbdd078da8 fix 2020-08-29 17:01:54 +01:00
13cced9c64 stats 2020-08-29 18:00:39 +02:00
4393f3747b decrease delay 2020-08-29 17:54:00 +02:00
11f963f911 fix 2020-08-29 17:52:14 +02:00
4e85de2225 error handling 2020-08-29 17:51:16 +02:00
1b03a1187e ord 2020-08-29 17:43:30 +02:00
aab02ca10b forgot to call open 2020-08-29 17:42:07 +02:00
436fbb720d timeout 2020-08-29 17:40:47 +02:00
8ebab69fca fix 2020-08-29 16:29:56 +01:00
f8e317bb09 cycling 2020-08-29 13:54:42 +01:00
3052af1de5 works so far 2020-08-29 13:38:34 +01:00
d3411ad48f Merge branch 'master' of ssh://home.hottis.de:2922/wolutator/mbus-gateway 2020-08-29 14:33:48 +02:00
0e01b0b1df wait for tx empty 2020-08-29 14:33:32 +02:00
6d7aaa3cb7 format 2020-08-29 12:52:16 +01:00
1ecc18dfee fix 2020-08-29 13:50:24 +02:00
a2717e1a2e use ord here too 2020-08-29 12:49:27 +01:00
5 changed files with 764 additions and 18 deletions

3
.gitignore vendored
View File

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

View File

@ -2,6 +2,12 @@ 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
@ -89,12 +95,13 @@ class MeterbusResponseStates(Enum):
START2 = 4 START2 = 4
C_FIELD = 5 C_FIELD = 5
A_FIELD = 6 A_FIELD = 6
C_FIELD = 7 CI_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 ]
@ -102,25 +109,34 @@ 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=None, xonxoff=0, rtscts=0) stopbits=1, timeout=1.0, 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)
# FIXME: Attention, the actual write time of the interface is not considered. buf = array.array('h', [0])
# This is a measured value. while True:
sleep(0.030) fcntl.ioctl(self.port.fileno(), termios.TIOCSERGETLSR, buf, 1)
if buf[0] & termios.TIOCSER_TEMT:
break
sleep(0.001)
frontendHold() frontendHold()
frontendRxEnable(True)
frameData = [] frameData = []
frame = { frame = {
'l': 0, 'l': 0,
@ -131,21 +147,132 @@ class MeterbusSerial(object):
} }
expectedUserDataOctets = 0 expectedUserDataOctets = 0
state = MeterbusResponseStates.START1 state = MeterbusResponseStates.START1
while (state not in [MeterbusResponseStates.DONE, MeterbusResponseStates.ERROR]): while (state not in [MeterbusResponseStates.DONE, MeterbusResponseStates.ERROR, MeterbusResponseStates.TIMEOUT]):
print("Waiting for input ... ") # print("Waiting for input ... ")
c = self.port.read() c = self.port.read(1)
if len(c) == 0:
state = MeterbusResponseStates.TIMEOUT
continue
c = ord(c)
print("State {}, Octet {:02X}".format(state, ord(c))) # print("State {}, Octet 0x{:02X}".format(state, 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(5.0) sleep(2.0)
loop(True)
sleep(5.0)
m = MeterbusSerial() m = MeterbusSerial()
m.shortFrameRequest(0x5b, 84) m.open()
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)

17
src/Makefile Normal file
View File

@ -0,0 +1,17 @@
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

551
src/mbusgw.c Normal file
View File

@ -0,0 +1,551 @@
#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;
}

48
src/mbusgw.h Normal file
View File

@ -0,0 +1,48 @@
#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