#include "Max31865PT1000Handler.h" #include #include #include "fsfw/datapool/PoolReadGuard.h" Max31865PT1000Handler::Max31865PT1000Handler(object_id_t objectId, object_id_t comIF, CookieIF *comCookie) : DeviceHandlerBase(objectId, comIF, comCookie), sensorDataset(this, MAX31865::REQUEST_RTD), sensorDatasetSid(sensorDataset.getSid()) { #if OBSW_VERBOSE_LEVEL >= 1 debugDivider = new PeriodicOperationDivider(10); #endif } Max31865PT1000Handler::~Max31865PT1000Handler() {} void Max31865PT1000Handler::doStartUp() { if (internalState == InternalState::NONE) { internalState = InternalState::WARMUP; Clock::getUptime(&startTime); } if (internalState == InternalState::WARMUP) { dur_millis_t timeNow = 0; Clock::getUptime(&timeNow); if (timeNow - startTime >= 100) { internalState = InternalState::CONFIGURE; } } if (internalState == InternalState::CONFIGURE) { if (commandExecuted) { commandExecuted = false; internalState = InternalState::REQUEST_CONFIG; } } if (internalState == InternalState::REQUEST_CONFIG) { if (commandExecuted) { commandExecuted = false; internalState = InternalState::CONFIG_HIGH_THRESHOLD; } } if (internalState == InternalState::CONFIG_HIGH_THRESHOLD) { if (commandExecuted) { internalState = InternalState::REQUEST_HIGH_THRESHOLD; commandExecuted = false; } } if (internalState == InternalState::REQUEST_HIGH_THRESHOLD) { if (commandExecuted) { internalState = InternalState::CONFIG_LOW_THRESHOLD; commandExecuted = false; } } if (internalState == InternalState::CONFIG_LOW_THRESHOLD) { if (commandExecuted) { internalState = InternalState::REQUEST_LOW_THRESHOLD; commandExecuted = false; } } if (internalState == InternalState::REQUEST_LOW_THRESHOLD) { if (commandExecuted) { internalState = InternalState::CLEAR_FAULT_BYTE; commandExecuted = false; } } if (internalState == InternalState::CLEAR_FAULT_BYTE) { if (commandExecuted) { commandExecuted = false; internalState = InternalState::RUNNING; if (instantNormal) { setMode(MODE_NORMAL); } else { setMode(_MODE_TO_ON); } } } } void Max31865PT1000Handler::doShutDown() { commandExecuted = false; warningSwitch = true; setMode(_MODE_POWER_DOWN); } ReturnValue_t Max31865PT1000Handler::buildNormalDeviceCommand(DeviceCommandId_t *id) { if (internalState == InternalState::RUNNING) { *id = MAX31865::REQUEST_RTD; return buildCommandFromCommand(*id, nullptr, 0); } else if (internalState == InternalState::REQUEST_FAULT_BYTE) { *id = MAX31865::REQUEST_FAULT_BYTE; return buildCommandFromCommand(*id, nullptr, 0); } else if (internalState == InternalState::CLEAR_FAULT_BYTE) { *id = MAX31865::CLEAR_FAULT_BYTE; return buildCommandFromCommand(*id, nullptr, 0); } else { return DeviceHandlerBase::NOTHING_TO_SEND; } } ReturnValue_t Max31865PT1000Handler::buildTransitionDeviceCommand(DeviceCommandId_t *id) { switch (internalState) { case (InternalState::NONE): case (InternalState::WARMUP): case (InternalState::RUNNING): return DeviceHandlerBase::NOTHING_TO_SEND; case (InternalState::CONFIGURE): { *id = MAX31865::CONFIG_CMD; uint8_t config[1] = {DEFAULT_CONFIG}; return buildCommandFromCommand(*id, config, 1); } case (InternalState::REQUEST_CONFIG): { *id = MAX31865::REQUEST_CONFIG; return buildCommandFromCommand(*id, nullptr, 0); } case (InternalState::CONFIG_HIGH_THRESHOLD): { *id = MAX31865::WRITE_HIGH_THRESHOLD; return buildCommandFromCommand(*id, nullptr, 0); } case (InternalState::REQUEST_HIGH_THRESHOLD): { *id = MAX31865::REQUEST_HIGH_THRESHOLD; return buildCommandFromCommand(*id, nullptr, 0); } case (InternalState::CONFIG_LOW_THRESHOLD): { *id = MAX31865::WRITE_LOW_THRESHOLD; return buildCommandFromCommand(*id, nullptr, 0); } case (InternalState::REQUEST_LOW_THRESHOLD): { *id = MAX31865::REQUEST_LOW_THRESHOLD; return buildCommandFromCommand(*id, nullptr, 0); } case (InternalState::CLEAR_FAULT_BYTE): { *id = MAX31865::CLEAR_FAULT_BYTE; return buildCommandFromCommand(*id, nullptr, 0); } default: #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "Max31865PT1000Handler: Invalid internal state" << std::endl; #else sif::printError("Max31865PT1000Handler: Invalid internal state\n"); #endif return HasReturnvaluesIF::RETURN_FAILED; } } ReturnValue_t Max31865PT1000Handler::buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t *commandData, size_t commandDataLen) { switch (deviceCommand) { case (MAX31865::CONFIG_CMD): { commandBuffer[0] = static_cast(MAX31865::CONFIG_CMD); if (commandDataLen == 1) { commandBuffer[1] = commandData[0]; currentCfg = commandData[0]; DeviceHandlerBase::rawPacketLen = 2; DeviceHandlerBase::rawPacket = commandBuffer.data(); return HasReturnvaluesIF::RETURN_OK; } else { return DeviceHandlerIF::NO_COMMAND_DATA; } } case (MAX31865::CLEAR_FAULT_BYTE): { commandBuffer[0] = static_cast(MAX31865::CONFIG_CMD); commandBuffer[1] = currentCfg | MAX31865::CLEAR_FAULT_BIT_VAL; DeviceHandlerBase::rawPacketLen = 2; DeviceHandlerBase::rawPacket = commandBuffer.data(); return HasReturnvaluesIF::RETURN_OK; } case (MAX31865::REQUEST_CONFIG): { commandBuffer[0] = static_cast(MAX31865::REQUEST_CONFIG); commandBuffer[1] = 0x00; // dummy byte DeviceHandlerBase::rawPacketLen = 2; DeviceHandlerBase::rawPacket = commandBuffer.data(); return HasReturnvaluesIF::RETURN_OK; } case (MAX31865::WRITE_HIGH_THRESHOLD): { commandBuffer[0] = static_cast(MAX31865::WRITE_HIGH_THRESHOLD); commandBuffer[1] = static_cast(HIGH_THRESHOLD >> 8); commandBuffer[2] = static_cast(HIGH_THRESHOLD & 0xFF); DeviceHandlerBase::rawPacketLen = 3; DeviceHandlerBase::rawPacket = commandBuffer.data(); return HasReturnvaluesIF::RETURN_OK; } case (MAX31865::REQUEST_HIGH_THRESHOLD): { commandBuffer[0] = static_cast(MAX31865::REQUEST_HIGH_THRESHOLD); commandBuffer[1] = 0x00; // dummy byte commandBuffer[2] = 0x00; // dummy byte DeviceHandlerBase::rawPacketLen = 3; DeviceHandlerBase::rawPacket = commandBuffer.data(); return HasReturnvaluesIF::RETURN_OK; } case (MAX31865::WRITE_LOW_THRESHOLD): { commandBuffer[0] = static_cast(MAX31865::WRITE_LOW_THRESHOLD); commandBuffer[1] = static_cast(LOW_THRESHOLD >> 8); commandBuffer[2] = static_cast(LOW_THRESHOLD & 0xFF); DeviceHandlerBase::rawPacketLen = 3; DeviceHandlerBase::rawPacket = commandBuffer.data(); return HasReturnvaluesIF::RETURN_OK; } case (MAX31865::REQUEST_LOW_THRESHOLD): { commandBuffer[0] = static_cast(MAX31865::REQUEST_LOW_THRESHOLD); commandBuffer[1] = 0x00; // dummy byte commandBuffer[2] = 0x00; // dummy byte DeviceHandlerBase::rawPacketLen = 3; DeviceHandlerBase::rawPacket = commandBuffer.data(); return HasReturnvaluesIF::RETURN_OK; } case (MAX31865::REQUEST_RTD): { commandBuffer[0] = static_cast(MAX31865::REQUEST_RTD); // two dummy bytes commandBuffer[1] = 0x00; commandBuffer[2] = 0x00; DeviceHandlerBase::rawPacketLen = 3; DeviceHandlerBase::rawPacket = commandBuffer.data(); return HasReturnvaluesIF::RETURN_OK; } case (MAX31865::REQUEST_FAULT_BYTE): { commandBuffer[0] = static_cast(MAX31865::REQUEST_FAULT_BYTE); commandBuffer[1] = 0x00; DeviceHandlerBase::rawPacketLen = 2; DeviceHandlerBase::rawPacket = commandBuffer.data(); return HasReturnvaluesIF::RETURN_OK; } default: // Unknown DeviceCommand return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED; } } void Max31865PT1000Handler::fillCommandAndReplyMap() { insertInCommandAndReplyMap(MAX31865::CONFIG_CMD, 3); insertInCommandAndReplyMap(MAX31865::REQUEST_CONFIG, 3); insertInCommandAndReplyMap(MAX31865::WRITE_LOW_THRESHOLD, 3); insertInCommandAndReplyMap(MAX31865::REQUEST_LOW_THRESHOLD, 3); insertInCommandAndReplyMap(MAX31865::WRITE_HIGH_THRESHOLD, 3); insertInCommandAndReplyMap(MAX31865::REQUEST_HIGH_THRESHOLD, 3); insertInCommandAndReplyMap(MAX31865::REQUEST_RTD, 3, &sensorDataset); insertInCommandAndReplyMap(MAX31865::REQUEST_FAULT_BYTE, 3); insertInCommandAndReplyMap(MAX31865::CLEAR_FAULT_BYTE, 3); } ReturnValue_t Max31865PT1000Handler::scanForReply(const uint8_t *start, size_t remainingSize, DeviceCommandId_t *foundId, size_t *foundLen) { size_t rtdReplySize = 3; size_t configReplySize = 2; if (remainingSize == rtdReplySize and internalState == InternalState::RUNNING) { *foundId = MAX31865::REQUEST_RTD; *foundLen = rtdReplySize; return RETURN_OK; } if (remainingSize == 3) { switch (internalState) { case (InternalState::CONFIG_HIGH_THRESHOLD): { *foundLen = 3; *foundId = MAX31865::WRITE_HIGH_THRESHOLD; commandExecuted = true; return RETURN_OK; } case (InternalState::REQUEST_HIGH_THRESHOLD): { *foundLen = 3; *foundId = MAX31865::REQUEST_HIGH_THRESHOLD; return RETURN_OK; } case (InternalState::CONFIG_LOW_THRESHOLD): { *foundLen = 3; *foundId = MAX31865::WRITE_LOW_THRESHOLD; commandExecuted = true; return RETURN_OK; } case (InternalState::REQUEST_LOW_THRESHOLD): { *foundLen = 3; *foundId = MAX31865::REQUEST_LOW_THRESHOLD; return RETURN_OK; } default: { sif::debug << "Max31865PT1000Handler::scanForReply: Unknown internal state" << std::endl; return RETURN_OK; } } } if (remainingSize == configReplySize) { if (internalState == InternalState::CONFIGURE) { commandExecuted = true; *foundLen = configReplySize; *foundId = MAX31865::CONFIG_CMD; } else if (internalState == InternalState::REQUEST_FAULT_BYTE) { *foundId = MAX31865::REQUEST_FAULT_BYTE; *foundLen = 2; internalState = InternalState::RUNNING; } else if (internalState == InternalState::CLEAR_FAULT_BYTE) { *foundId = MAX31865::CLEAR_FAULT_BYTE; *foundLen = 2; if (mode == _MODE_START_UP) { commandExecuted = true; } else { internalState = InternalState::RUNNING; } } else { *foundId = MAX31865::REQUEST_CONFIG; *foundLen = configReplySize; } } return RETURN_OK; } ReturnValue_t Max31865PT1000Handler::interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) { switch (id) { case (MAX31865::REQUEST_CONFIG): { if (packet[1] != DEFAULT_CONFIG) { if (warningSwitch) { #if FSFW_CPP_OSTREAM_ENABLED == 1 // it propably would be better if we at least try one restart.. sif::warning << "Max31865PT1000Handler: 0x" << std::hex << this->getObjectId() << ": Invalid configuration reply" << std::endl; #else sif::printWarning("Max31865PT1000Handler: %04x: Invalid configuration reply!\n", this->getObjectId()); #endif warningSwitch = false; } return HasReturnvaluesIF::RETURN_OK; } // set to true for invalid configs too for now. if (internalState == InternalState::REQUEST_CONFIG) { commandExecuted = true; } else if (internalState == InternalState::RUNNING) { // we should propably generate a telemetry with the config byte // as payload here. } break; } case (MAX31865::REQUEST_LOW_THRESHOLD): { uint16_t readLowThreshold = packet[1] << 8 | packet[2]; if (readLowThreshold != LOW_THRESHOLD) { #if FSFW_VERBOSE_LEVEL >= 1 #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "Max31865PT1000Handler::interpretDeviceReply: Object ID: " << std::hex << this->getObjectId() << ": Missmatch between " << "written and readback value of low threshold register" << std::endl; #else sif::printWarning( "Max31865PT1000Handler::interpretDeviceReply: Missmatch between " "written and readback value of low threshold register\n"); #endif #endif } commandExecuted = true; break; } case (MAX31865::REQUEST_HIGH_THRESHOLD): { uint16_t readHighThreshold = packet[1] << 8 | packet[2]; if (readHighThreshold != HIGH_THRESHOLD) { #if FSFW_VERBOSE_LEVEL >= 1 #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "Max31865PT1000Handler::interpretDeviceReply: Object ID: " << std::hex << this->getObjectId() << ": Missmatch between " << "written and readback value of high threshold register" << std::endl; #else sif::printWarning( "Max31865PT1000Handler::interpretDeviceReply: Missmatch between " "written and readback value of high threshold register\n"); #endif #endif } commandExecuted = true; break; } case (MAX31865::REQUEST_RTD): { // first bit of LSB reply byte is the fault bit uint8_t faultBit = packet[2] & 0b0000'0001; if (resetFaultBit) { internalState = InternalState::CLEAR_FAULT_BYTE; resetFaultBit = false; } else if (faultBit == 1) { // Maybe we should attempt to restart it? internalState = InternalState::REQUEST_FAULT_BYTE; resetFaultBit = true; } // RTD value consists of last seven bits of the LSB reply byte and // the MSB reply byte uint16_t adcCode = ((packet[1] << 8) | packet[2]) >> 1; // do something with rtd value, will propably be stored in // dataset. float rtdValue = adcCode * RTD_RREF_PT1000 / INT16_MAX; // calculate approximation float approxTemp = adcCode / 32.0 - 256.0; if (debugMode) { #if OBSW_VERBOSE_LEVEL >= 1 if (debugDivider->checkAndIncrement()) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::info << "Max31865: " << std::setw(28) << std::left << locString << std::right << " | R[Ohm] " << rtdValue << " Ohms | Approx T[C]: " << approxTemp << std::endl; #else sif::printInfo("Max31685: Measured resistance is %f Ohms\n", rtdValue); sif::printInfo("Approximated temperature is %f C\n", approxTemp); #endif } #endif } PoolReadGuard pg(&sensorDataset); if (pg.getReadResult() != HasReturnvaluesIF::RETURN_OK) { // Configuration error #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "Max31865PT1000Handler::interpretDeviceReply: Object ID: " << std::hex << this->getObjectId() << ": Error reading dataset!" << std::endl; #else sif::printWarning( "Max31865PT1000Handler::interpretDeviceReply: " "Error reading dataset!\n"); #endif return pg.getReadResult(); } if (not sensorDataset.isValid()) { sensorDataset.setValidity(true, false); sensorDataset.rtdValue.setValid(true); sensorDataset.temperatureCelcius.setValid(true); } sensorDataset.rtdValue = rtdValue; sensorDataset.temperatureCelcius = approxTemp; break; } case (MAX31865::REQUEST_FAULT_BYTE): { faultByte = packet[1]; bool faultStatusChanged = (faultByte != lastFaultStatus); // Spam protection if (faultStatusChanged or ((faultByte == lastFaultStatus) and (sameFaultStatusCounter < 3))) { // TODO: Think about triggering an event here #if OBSW_VERBOSE_LEVEL >= 1 #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "Max31865PT1000Handler::interpretDeviceReply: Object ID: " << std::hex << this->getObjectId() << ": Fault byte is: 0b" << std::bitset<8>(faultByte) << std::endl; #else sif::printWarning( "Max31865PT1000Handler::interpretDeviceReply: Fault byte" " is: 0b" BYTE_TO_BINARY_PATTERN "\n", BYTE_TO_BINARY(faultByte)); #endif #endif if (faultStatusChanged) { sameFaultStatusCounter = 0; } else { sameFaultStatusCounter++; } } lastFaultStatus = faultByte; PoolReadGuard pg(&sensorDataset); auto result = pg.getReadResult(); if (result != HasReturnvaluesIF::RETURN_OK) { // Configuration error #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "Max31865PT1000Handler::interpretDeviceReply: Object ID: " << std::hex << this->getObjectId() << ": Error reading dataset" << std::endl; #else sif::printWarning("Max31865PT1000Handler::interpretDeviceReply: Error reading dataset\n"); #endif return result; } if (faultStatusChanged) { sensorDataset.lastErrorByte.setValid(true); sensorDataset.lastErrorByte = faultByte; } sensorDataset.errorByte.setValid(true); sensorDataset.errorByte = faultByte; if (faultByte != 0) { sensorDataset.temperatureCelcius.setValid(false); } break; } default: break; } return HasReturnvaluesIF::RETURN_OK; } void Max31865PT1000Handler::debugInterface(uint8_t positionTracker, object_id_t objectId, uint32_t parameter) {} uint32_t Max31865PT1000Handler::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) { return 25000; } ReturnValue_t Max31865PT1000Handler::getSwitches(const uint8_t **switches, uint8_t *numberOfSwitches) { return DeviceHandlerBase::NO_SWITCH; } ReturnValue_t Max31865PT1000Handler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) { using namespace MAX31865; localDataPoolMap.emplace(static_cast(PoolIds::RTD_VALUE), new PoolEntry({0})); localDataPoolMap.emplace(static_cast(PoolIds::TEMPERATURE_C), new PoolEntry({0})); localDataPoolMap.emplace(static_cast(PoolIds::LAST_FAULT_BYTE), new PoolEntry({0})); localDataPoolMap.emplace(static_cast(PoolIds::FAULT_BYTE), new PoolEntry({0})); poolManager.subscribeForPeriodicPacket(sensorDataset.getSid(), false, 30.0, false); return HasReturnvaluesIF::RETURN_OK; } void Max31865PT1000Handler::setInstantNormal(bool instantNormal) { this->instantNormal = instantNormal; } void Max31865PT1000Handler::modeChanged() { if (mode == MODE_OFF) { internalState = InternalState::NONE; } } void Max31865PT1000Handler::setDeviceInfo(uint8_t idx, std::string locString_) { deviceIdx = idx; locString = std::move(locString_); } void Max31865PT1000Handler::setDebugMode(bool enable) { this->debugMode = enable; }