#include #include #include #include #include #include #include #include #include #include #include #include #include static const uint8_t MBUS_QUERY_CMD = 0x5b; typedef enum { MBCR_SUCCESS = 0, MBCR_ERROR_TIMEOUT, MBCR_ERROR_LOOP_FAILURE, MBCR_ERROR_TX_REG_UNACCESSIBLE, MBCR_ERROR_OUT_OF_MEMORY__FRAME, MBCR_ERROR_OUT_OF_MEMORY__USERDATA, MBCR_ERROR_STATE_ENGINE__START1, MBCR_ERROR_STATE_ENGINE__LENGTH1, MBCR_ERROR_STATE_ENGINE__LENGTH2, MBCR_ERROR_STATE_ENGINE__START2, MBCR_ERROR_STATE_ENGINE__INVALID_CHKSUM, MBCR_ERROR_STATE_ENGINE__STOP, MBCR_ERROR_STATE_ENGINE__ILLEGAL_STATE, MBCR_ERROR_STATE_ENGINE__UNKNOWN } e_mbusCommResult; typedef enum { MBCS_IDLE, MBCS_SEND, MBCS_SEND_CONT, MBCS_SENDING_DONE, MBCS_ENABLE_FRONTEND, MBCS_START1, MBCS_LENGTH1, MBCS_LENGTH2, MBCS_START2, MBCS_C_FIELD, MBCS_A_FIELD, MBCS_CI_FIELD, MBCS_USERDATA, MBCS_CHKSUM, MBCS_STOP, MBCS_DONE, MBCS_TIMEOUT, MBCS_DISABLE_FRONTEND, MBCS_ERROR } e_mbusCommState; 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; typedef struct { e_mbusCommState state; uint8_t retryCnt; uint8_t cmd; uint8_t addr; uint8_t sendBuf[5]; uint8_t receiveCnt; uint8_t receivedOctet; bool receiving; e_mbusCommResult result; t_longframe frame; t_mbusDevice *device; } t_mbusCommHandle; static t_mbusCommHandle mbusCommHandle = { .state = MBCS_IDLE, .retryCnt = 0, .cmd = 0, .addr = 0, .receiveCnt = 0, .receivedOctet = 0, .receiving = false }; static void parseAndPrintFrame(t_mbusCommHandle *mbusCommHandle) { t_longframe *frame = &(mbusCommHandle->frame); mbus_frame reply; memset(&reply, 0, sizeof(reply)); //mbus_parse(&reply, buf, len); reply.start1 = frame->start1; reply.length1 = frame->length1; reply.length2 = frame->length2; reply.start2 = frame->start2; reply.control = frame->c; reply.address = frame->a; reply.control_information = frame->ci; memcpy(reply.data, frame->userdata, frame->length1 - 3); reply.checksum = frame->chksum; reply.stop = frame->stop; reply.type = MBUS_FRAME_TYPE_LONG; reply.data_size = frame->length1 - 3; mbus_frame_data frame_data; memset(&frame_data, 0, sizeof(frame_data)); int r = mbus_frame_data_parse(&reply, &frame_data); if (r == 0) { mbus_data_variable *data_var = &(frame_data.data_var); logMsg("papf sts: %02x", data_var->header.status); if ((data_var->header.status & 0x01)) { errMsg("papf sts: Application Busy"); } if ((data_var->header.status & 0x02)) { errMsg("papf sts: Any Application Error"); } if ((data_var->header.status & 0x04)) { errMsg("papf sts: Power Low"); } if ((data_var->header.status & 0x08)) { errMsg("papf sts: Permanent Error"); } if ((data_var->header.status & 0x10)) { errMsg("papf sts: Temporary Error"); } if ((data_var->header.status & 0x20)) { errMsg("papf sts: Specific to manufacturer Error 1"); } if ((data_var->header.status & 0x40)) { errMsg("papf sts: Specific to manufacturer Error 2"); } if ((data_var->header.status & 0x80)) { errMsg("papf sts: Specific to manufacturer Error 3"); } mbus_data_record *record; int i; for (record = data_var->record, i = 0; record; record = record->next, i++) { for (uint8_t j = 0; j < MBUSDEVICE_NUM_OF_CONSIDEREDFIELDS; j++) { if ((mbusCommHandle->device->consideredField[j].index == i) && (strlen(mbusCommHandle->device->consideredField[j].label) > 0)) { coloredMsg(LOG_YELLOW, "papf txt: I:%d, L:%s, U:%s V:%s", i, mbusCommHandle->device->consideredField[j].label, mbus_data_record_unit(record), mbus_data_record_value(record)); } } } mbus_data_record_free(data_var->record); } else { errMsg("papf err: unable to parse frame"); } } static void handleRequestEngine(void *handle); static void timeoutHandler(void *handle) { errMsg("mbc timeout"); t_mbusCommHandle *localMbusCommHandle = (t_mbusCommHandle*) handle; localMbusCommHandle->state = MBCS_TIMEOUT; localMbusCommHandle->receiving = false; handleRequestEngine(handle); } static void receiveNext(t_mbusCommHandle *localMbusCommHandle) { localMbusCommHandle->receiving = true; HAL_UART_Receive_IT(&mbusUart, &(localMbusCommHandle->receivedOctet), 1); } static void handleRequestEngine(void *handle) { t_mbusCommHandle *localMbusCommHandle = (t_mbusCommHandle*) handle; static uint8_t userdataIdx = 0; static uint8_t calculatedChksum = 0; switch (localMbusCommHandle->state) { case MBCS_IDLE: logMsg("hre state IDLE"); break; case MBCS_SEND: logMsg("hre state SEND"); localMbusCommHandle->sendBuf[0] = 0x10; localMbusCommHandle->sendBuf[1] = localMbusCommHandle->cmd; localMbusCommHandle->sendBuf[2] = localMbusCommHandle->addr; localMbusCommHandle->sendBuf[3] = localMbusCommHandle->cmd + localMbusCommHandle->addr; // checksum localMbusCommHandle->sendBuf[4] = 0x16; localMbusCommHandle->state = MBCS_SEND_CONT; // no break !! case MBCS_SEND_CONT: logMsg("hre state SEND_CONT"); show(LED_RED, OFF); if (! loopActive) { logMsg("hre enabling loop, try %d", localMbusCommHandle->retryCnt); localMbusCommHandle->retryCnt++; loopEnable(); schAdd(handleRequestEngine, handle, 10, 0); // give 10ms to settled the loop } else { localMbusCommHandle->retryCnt = 0; HAL_UART_Transmit_IT(&mbusUart, localMbusCommHandle->sendBuf, 5); // transition from here to SENDING_DONE is initiate by mbusCommTxCpltCallback // interrupt callback localMbusCommHandle->state = MBCS_SENDING_DONE; } break; case MBCS_SENDING_DONE: logMsg("hre state SENDING_DONE"); localMbusCommHandle->state = MBCS_ENABLE_FRONTEND; schAdd(handleRequestEngine, handle, 3, 0); break; case MBCS_ENABLE_FRONTEND: logMsg("hre state ENABLE_FRONTEND"); frontendEnable(); schAdd(timeoutHandler, handle, 2500, 0); calculatedChksum = 0; userdataIdx = 0; localMbusCommHandle->state = MBCS_START1; receiveNext(localMbusCommHandle); break; case MBCS_START1: //logMsg("hre state START1"); if (localMbusCommHandle->receivedOctet == 0x68) { localMbusCommHandle->frame.start1 = localMbusCommHandle->receivedOctet; localMbusCommHandle->state = MBCS_LENGTH1; } else { errMsg("hre err: invalid start1 symbol %02x", localMbusCommHandle->receivedOctet); localMbusCommHandle->result = MBCR_ERROR_STATE_ENGINE__START1; localMbusCommHandle->state = MBCS_ERROR; } receiveNext(localMbusCommHandle); break; case MBCS_LENGTH1: //logMsg("hre state LENGTH1"); if (localMbusCommHandle->receivedOctet <= 3) { errMsg("hre err: length to small %02x", localMbusCommHandle->receivedOctet); localMbusCommHandle->result = MBCR_ERROR_STATE_ENGINE__LENGTH1; localMbusCommHandle->state = MBCS_ERROR; } else { localMbusCommHandle->frame.length1 = localMbusCommHandle->receivedOctet; localMbusCommHandle->frame.userdata = (uint8_t*) malloc(localMbusCommHandle->frame.length1 - 3); if (! localMbusCommHandle->frame.userdata) { errMsg("hre err: unable to allocate memory for userdata"); localMbusCommHandle->result = MBCR_ERROR_OUT_OF_MEMORY__USERDATA; localMbusCommHandle->state = MBCS_ERROR; } else { localMbusCommHandle->state = MBCS_LENGTH2; } } receiveNext(localMbusCommHandle); break; case MBCS_LENGTH2: //logMsg("hre state LENGTH2"); if (localMbusCommHandle->frame.length1 != localMbusCommHandle->receivedOctet) { errMsg("hre err: invalid length2 %02x vs. %02x", localMbusCommHandle->frame.length1, localMbusCommHandle->receivedOctet); localMbusCommHandle->result = MBCR_ERROR_STATE_ENGINE__LENGTH2; localMbusCommHandle->state = MBCS_ERROR; } else { localMbusCommHandle->frame.length2 = localMbusCommHandle->receivedOctet; localMbusCommHandle->state = MBCS_START2; } receiveNext(localMbusCommHandle); break; case MBCS_START2: //logMsg("hre state START2"); if (localMbusCommHandle->receivedOctet == 0x68) { localMbusCommHandle->frame.start2 = localMbusCommHandle->receivedOctet; localMbusCommHandle->state = MBCS_C_FIELD; } else { errMsg("hre err: invalid start2 symbol %02x", localMbusCommHandle->receivedOctet); localMbusCommHandle->result = MBCR_ERROR_STATE_ENGINE__START2; localMbusCommHandle->state = MBCS_ERROR; } receiveNext(localMbusCommHandle); break; case MBCS_C_FIELD: //logMsg("hre state C_FIELD"); localMbusCommHandle->frame.c = localMbusCommHandle->receivedOctet; calculatedChksum += localMbusCommHandle->receivedOctet; localMbusCommHandle->state = MBCS_A_FIELD; receiveNext(localMbusCommHandle); break; case MBCS_A_FIELD: //logMsg("hre state A_FIELD"); localMbusCommHandle->frame.a = localMbusCommHandle->receivedOctet; calculatedChksum += localMbusCommHandle->receivedOctet; localMbusCommHandle->state = MBCS_CI_FIELD; receiveNext(localMbusCommHandle); break; case MBCS_CI_FIELD: //logMsg("hre state CI_FIELD"); localMbusCommHandle->frame.ci = localMbusCommHandle->receivedOctet; calculatedChksum += localMbusCommHandle->receivedOctet; localMbusCommHandle->state = MBCS_USERDATA; receiveNext(localMbusCommHandle); break; case MBCS_USERDATA: //logMsg("hre state USERDATA"); localMbusCommHandle->frame.userdata[userdataIdx] = localMbusCommHandle->receivedOctet; calculatedChksum += localMbusCommHandle->receivedOctet; userdataIdx++; if (userdataIdx == (localMbusCommHandle->frame.length1 - 3)) { localMbusCommHandle->state = MBCS_CHKSUM; } receiveNext(localMbusCommHandle); break; case MBCS_CHKSUM: //logMsg("hre state CHKSUM"); if (localMbusCommHandle->receivedOctet != calculatedChksum) { errMsg("hre err: invalid checksum %02x vs %02x", calculatedChksum, localMbusCommHandle->receivedOctet); localMbusCommHandle->result = MBCR_ERROR_STATE_ENGINE__INVALID_CHKSUM; localMbusCommHandle->state = MBCS_ERROR; } else { localMbusCommHandle->frame.chksum = localMbusCommHandle->receivedOctet; localMbusCommHandle->state = MBCS_STOP; } receiveNext(localMbusCommHandle); break; case MBCS_STOP: //logMsg("hre state STOP"); if (localMbusCommHandle->receivedOctet == 0x16) { localMbusCommHandle->frame.stop = localMbusCommHandle->receivedOctet; localMbusCommHandle->state = MBCS_DONE; schAdd(handleRequestEngine, handle, 0, 0); } else { errMsg("hre err: invalid stop symbol %02x", localMbusCommHandle->receivedOctet); localMbusCommHandle->result = MBCR_ERROR_STATE_ENGINE__STOP; localMbusCommHandle->state = MBCS_ERROR; receiveNext(localMbusCommHandle); } break; case MBCS_DONE: logMsg("hre state DONE"); parseAndPrintFrame(localMbusCommHandle); if (localMbusCommHandle->frame.userdata != NULL) { free(localMbusCommHandle->frame.userdata); localMbusCommHandle->frame.userdata = NULL; } localMbusCommHandle->result = MBCR_SUCCESS; localMbusCommHandle->state = MBCS_DISABLE_FRONTEND; schDel(timeoutHandler, handle); schAdd(handleRequestEngine, handle, 0, 0); break; case MBCS_ERROR: errMsg("hre state ERROR"); show(LED_RED, ON); errMsg("hre err: already error, read the rest (now: %02x) until timeout", localMbusCommHandle->receivedOctet); receiveNext(localMbusCommHandle); break; case MBCS_TIMEOUT: errMsg("hre state TIMEOUT"); localMbusCommHandle->device->failures += 1; localMbusCommHandle->receiving = false; if (localMbusCommHandle->frame.userdata != NULL) { free(localMbusCommHandle->frame.userdata); localMbusCommHandle->frame.userdata = NULL; } HAL_UART_AbortReceive(&mbusUart); uint8_t kitchenSink[16]; memset(kitchenSink, 0, 16); HAL_StatusTypeDef r = HAL_UART_Receive(&mbusUart, kitchenSink, 16, 100); errMsg("hre abort, last receive result: %02x", r); // no break case MBCS_DISABLE_FRONTEND: logMsg("hre state DISABLE_FRONTEND"); frontendDisable(); localMbusCommHandle->state = MBCS_IDLE; break; default: localMbusCommHandle->state = MBCS_IDLE; break; } } void mbusCommTxCpltCallback(UART_HandleTypeDef *huart) { schAdd(handleRequestEngine, (void*) &mbusCommHandle, 0, 0); } void mbusCommRxCpltCallback(UART_HandleTypeDef *huart) { if (mbusCommHandle.receiving) { schAdd(handleRequestEngine, (void*) &mbusCommHandle, 0, 0); mbusCommHandle.receiving = false; } else { errMsg("mcrx: received 0x%02x but not expected", mbusCommHandle.receivedOctet); } } e_mbusCommRequestResult mbusCommRequest(t_mbusDevice *mbusDevice) { static uint32_t cnt = 0; e_mbusCommRequestResult res = MBCRR_BUSY; if (mbusCommHandle.state == MBCS_IDLE) { coloredMsg(LOG_BLUE, "*** NEW REQUEST %s R:%d F:%d C:%d ***", mbusDevice->deviceName, mbusDevice->requests, mbusDevice->failures, cnt); cnt++; mbusCommHandle.state = MBCS_SEND; mbusCommHandle.retryCnt = 0; mbusCommHandle.cmd = MBUS_QUERY_CMD; mbusCommHandle.addr = mbusDevice->address; mbusCommHandle.device = mbusDevice; mbusDevice->requests += 1; schAdd(handleRequestEngine, (void*) &mbusCommHandle, 0, 0); res = MBCRR_TRIGGERED; } return res; }