/* * meterBusClient.cpp * * Created on: 08.03.2014 * Author: wn */ #include "meterBusClient.h" const bool MBC_COMPILE_TIME_DEBUG = false; String MeterBusClientConfig::exec(String params) { String res = "done"; int space = params.indexOf(' '); String p1 = ""; char pb1[128]; if (space != -1) { params.toCharArray(pb1, 128, space+1); } if (params.startsWith("a ") && (space != -1)) { unsigned int a = atoi(pb1); m_meterBusClient->setAddress(a); Serial.print("Address is now "); Serial.println(a); Serial.print("MBC is at "); Serial.println((long) m_meterBusClient, 16); } else if (params.startsWith("debug ") && (space != -1)) { bool b = (strcmp(pb1, "on") == 0); m_meterBusClient->setDebug(b); } else if (params.startsWith("info ") && (space != -1)) { bool b = (strcmp(pb1, "on") == 0); m_meterBusClient->setInfo(b); } else if (params.startsWith("show")) { Serial.print(getResource(MBC_FRAMES_KEY)); Serial.print(m_meterBusClient->m_frameCnt); Serial.println(); Serial.print(getResource(MBC_MYFRAMES_KEY)); Serial.print(m_meterBusClient->m_myFrameCnt); Serial.println(); Serial.print(getResource(MBC_INVALID_FRAMES_KEY)); Serial.print(m_meterBusClient->m_invalidFrameCnt); Serial.println(); Serial.print(getResource(MBC_INVALID_CHECKSUM_CNT_KEY)); Serial.print(m_meterBusClient->m_invalidChecksum); Serial.println(); Serial.print(getResource(MBC_COLLISION_CNT_KEY)); Serial.print(m_meterBusClient->m_collisionCnt); Serial.println(); Serial.print(getResource(MBC_ADDRESS_LONG_KEY)); Serial.print(m_meterBusClient->getAddress()); Serial.println(); } else { res = "subcommand not found"; } return res; } MeterBusClient::MeterBusClient() : m_meterBusClientConfig(this), m_address(0), m_frameCnt(0), m_accessCnt(0), m_myFrameCnt(0), m_invalidFrameCnt(0), m_invalidChecksum(0), m_collisionCnt(0) { } void MeterBusClient::begin(CmdServer *cmdServer, Thermometer *thermometer, Uptime *uptime) { Serial.print("I'm at "); Serial.println((long)this, 16); m_meterBusClientConfig.registerYourself(cmdServer); m_thermometer = thermometer; m_uptime = uptime; //Serial3.begin(1200); //Serial3.begin(2400, SERIAL_8E1); Serial3.begin(2400); UART2_C1 |= UART_C1_PE | UART_C1_M; setAddress(Config::getUChar(Config::METERBUSCLIENT_ADDRESS)); } void MeterBusClient::setAddress(unsigned char a) { Config::setUChar(Config::METERBUSCLIENT_ADDRESS, a); m_address = a; } unsigned char MeterBusClient::getAddress() { return m_address; } void MeterBusClient::setDebug(bool b) { Config::setBool(Config::METERBUSCLIENT_DEBUG, b); m_debug = b; } bool MeterBusClient::getDebug() { return m_debug; } void MeterBusClient::setInfo(bool b) { Config::setBool(Config::METERBUSCLIENT_INFO, b); m_info = b; } bool MeterBusClient::getInfo() { return m_info; } unsigned char MeterBusClient::getStatus() { return 0; } unsigned char MeterBusClient::getAccessCnt() { return m_accessCnt; } /* * Single Character: E5h * * Short Frame: * Start 10h * C-Field * A-Field * Check-Sum * Stop 16h * * Control Frame: * Start 68h * Length = 3 * Length * Start 68h * C-Field * A-Field * CI-Field * Check-Sum * Stop 16h * * Long Frame: * Start 68h * Length * Length * Start 68h * C-Field * A-Field * CI-Field * User-Data * Check-Sum * Stop 16h * * Frames: * SND_NKE * Short Frame, C: 40h * * REQ_UD2 * Short Frame, C: 5Bh */ bool isChecksumValid(MeterBusFrame frame) { unsigned char ctrlsum = frame.aField + frame.cField + frame.ciField; for (int i = 0; i < (frame.length - 3); i++) { ctrlsum += frame.userData[i]; } return (ctrlsum == frame.checksum); } bool MeterBusClient::handleFrame() { bool res = false; m_frameCnt++; if (! isChecksumValid(m_frame)) { Serial.println(getResource(MBC_INVALID_CHECKSUM_KEY)); m_invalidChecksum++; } else if (m_frame.aField != getAddress()) { if (MBC_COMPILE_TIME_DEBUG && getDebug()) { Serial.print(getResource(MBC_NOT_FOR_ME_KEY)); Serial.print((int)m_frame.aField, 16); Serial.println();} } else { m_myFrameCnt++; // handle the frame if (MBC_COMPILE_TIME_DEBUG && getDebug()) { Serial.print(getResource(MBC_START_DELIMITER_KEY)); Serial.println(m_frame.startDelimiter, 16);} if (MBC_COMPILE_TIME_DEBUG && getDebug()) { Serial.print(getResource(MBC_LENGTH_KEY)); Serial.println(m_frame.length, 16);} if (MBC_COMPILE_TIME_DEBUG && getDebug()) { Serial.print(getResource(MBC_CFIELD_KEY)); Serial.println(m_frame.cField, 16);} if (MBC_COMPILE_TIME_DEBUG && getDebug()) { Serial.print(getResource(MBC_ADDRESS_KEY)); Serial.println(m_frame.aField, 16);} if (MBC_COMPILE_TIME_DEBUG && getDebug()) { Serial.print(getResource(MBC_CIFIELD_KEY)); Serial.println(m_frame.ciField, 16);} if (m_frame.length > 3) { if (MBC_COMPILE_TIME_DEBUG && getDebug()) { Serial.print(getResource(MBC_USERDATA_KEY));} for (unsigned char i = 0; i < (m_frame.length - 3); i++) { if (MBC_COMPILE_TIME_DEBUG && getDebug()) { Serial.print(m_frame.userData[i], 16);} if (MBC_COMPILE_TIME_DEBUG && getDebug()) { Serial.print(getResource(SPACE_KEY));} } if (MBC_COMPILE_TIME_DEBUG && getDebug()) { Serial.println();} } if (m_frame.startDelimiter == 0x10) { if (m_frame.cField == 0x40) { SND_NKE(); res = true; } else if (m_frame.cField == 0x5b) { m_accessCnt++; REQ_UD2(); res = true; } else { Serial.println(getResource(MBC_UNHANDLED_FRAME_KEY)); } } } return res; } typedef enum { STATE_START, STATE_IDLE, STATE_SHORT_FRAME, STATE_LONG_CTRL_FRAME, STATE_HANDLE, STATE_INVALID, STATE_DONE, STATE_DELAYED_RESPONSE, STATE_DELAY, STATE_RESPONSE, STATE_RECEIVE_ECHO } e_meterBusClientState; typedef enum { SUBSTATE_LENGTH, SUBSTATE_LENGTH_REPEAT, SUBSTATE_START_REPEAT, SUBSTATE_C_FIELD, SUBSTATE_A_FIELD, SUBSTATE_CI_FIELD, SUBSTATE_USERDATA, SUBSTATE_CHECKSUM, SUBSTATE_STOP } e_meterBusClientSubState; void MeterBusClient::exec() { static e_meterBusClientState state = STATE_IDLE; static e_meterBusClientSubState subState = SUBSTATE_LENGTH; static unsigned char userDataCnt = 0; static unsigned int echoReceiveCnt = 0; static unsigned long receivedDoneTimestamp; int chi = Serial3.read(); //if (chi != -1) { // Serial.print("Received: "); Serial.println(chi, 16); //} char ch = (char) chi; switch (state) { case STATE_START: m_frame.startDelimiter = 0; m_frame.length = 0; m_frame.cField = 0; m_frame.aField = 0; m_frame.ciField = 0; //memset(m_frame.userData, 0, sizeof(m_frame.userData)); m_frame.valid = false; state = STATE_IDLE; break; case STATE_IDLE: if (chi != -1) { if (ch == 0x10) { m_frame.startDelimiter = 0x10; state = STATE_SHORT_FRAME; subState = SUBSTATE_C_FIELD; } else if (ch == 0x68) { m_frame.startDelimiter = 0x68; state = STATE_LONG_CTRL_FRAME; subState = SUBSTATE_LENGTH; } else { state = STATE_INVALID; } } break; case STATE_SHORT_FRAME: if (chi != -1) { if (subState == SUBSTATE_C_FIELD) { m_frame.cField = ch; subState = SUBSTATE_A_FIELD; } else if (subState == SUBSTATE_A_FIELD) { m_frame.aField = ch; subState = SUBSTATE_CHECKSUM; } else if (subState == SUBSTATE_CHECKSUM) { m_frame.checksum = ch; subState = SUBSTATE_STOP; } else if (subState == SUBSTATE_STOP) { if (ch == 0x16) { m_frame.valid = true; state = STATE_HANDLE; } else { state = STATE_INVALID; } } } break; case STATE_LONG_CTRL_FRAME: if (chi != -1) { if (subState == SUBSTATE_LENGTH) { m_frame.length = ch; if ((m_frame.length < 3) || (m_frame.length > 252)) { state = STATE_INVALID; } else { subState = SUBSTATE_LENGTH_REPEAT; } } else if (subState == SUBSTATE_LENGTH_REPEAT) { if (ch == m_frame.length) { subState = SUBSTATE_START_REPEAT; } else { state = STATE_INVALID; } } else if (subState == SUBSTATE_START_REPEAT) { if (ch == 0x68) { subState = SUBSTATE_C_FIELD; } else { state = STATE_INVALID; } } else if (subState == SUBSTATE_C_FIELD) { m_frame.cField = ch; subState = SUBSTATE_A_FIELD; } else if (subState == SUBSTATE_A_FIELD) { m_frame.aField = ch; subState = SUBSTATE_CI_FIELD; } else if (subState == SUBSTATE_CI_FIELD) { m_frame.ciField = ch; if (m_frame.length == 3) { subState = SUBSTATE_CHECKSUM; } else { subState = SUBSTATE_USERDATA; userDataCnt = 0; } } else if (subState == SUBSTATE_USERDATA) { m_frame.userData[userDataCnt] = ch; userDataCnt++; if (userDataCnt >= (m_frame.length - 3)) { subState = SUBSTATE_CHECKSUM; } } else if (subState == SUBSTATE_CHECKSUM) { m_frame.checksum = ch; subState = SUBSTATE_STOP; } else if (subState == SUBSTATE_STOP) { if (ch == 0x16) { m_frame.valid = true; state = STATE_HANDLE; } else { state = STATE_INVALID; } } } break; case STATE_INVALID: Serial.println("INVALID FRAME"); m_invalidFrameCnt++; state = STATE_START; break; case STATE_HANDLE: state = STATE_DONE; if (m_frame.valid) { receivedDoneTimestamp = millis(); if (handleFrame()) { state = STATE_DELAY; } } break; case STATE_DELAY: if ((receivedDoneTimestamp + RESPONSE_DELAY) < millis()) { state = STATE_DELAYED_RESPONSE; } break; case STATE_DELAYED_RESPONSE: for (unsigned int i = 0; i < m_sendBufferLen; i++) { Serial3.write(m_sendBuffer[i]); } echoReceiveCnt = 0; state = STATE_RECEIVE_ECHO; break; case STATE_RECEIVE_ECHO: if (chi != -1) { //Serial.println("echo character"); if (ch == m_sendBuffer[echoReceiveCnt]) { echoReceiveCnt++; if (echoReceiveCnt == m_sendBufferLen) { state = STATE_DONE; } } else { m_collisionCnt++; state = STATE_DONE; } } break; case STATE_DONE: // may be another useful message state = STATE_START; break; } }