diff --git a/Firmware/mmu2_crc.cpp b/Firmware/mmu2_crc.cpp new file mode 100644 index 000000000..20c68f7c5 --- /dev/null +++ b/Firmware/mmu2_crc.cpp @@ -0,0 +1,22 @@ +/// @file +#include "mmu2_crc.h" + +#ifdef __AVR__ +#include +#endif + +namespace modules { +namespace crc { + +#ifdef __AVR__ +uint8_t CRC8::CCITT_update(uint8_t crc, uint8_t b) { + return _crc8_ccitt_update(crc, b); +} +#else +uint8_t CRC8::CCITT_update(uint8_t crc, uint8_t b) { + return CCITT_updateCX(crc, b); +} +#endif + +} // namespace crc +} // namespace modules diff --git a/Firmware/mmu2_crc.h b/Firmware/mmu2_crc.h new file mode 100644 index 000000000..cc8f06dab --- /dev/null +++ b/Firmware/mmu2_crc.h @@ -0,0 +1,43 @@ +/// @file +#pragma once +#include + +namespace modules { + +/// Contains all the necessary functions for computation of CRC +namespace crc { + +class CRC8 { +public: + /// Compute/update CRC8 CCIIT from 8bits. + /// Details: https://www.nongnu.org/avr-libc/user-manual/group__util__crc.html + static uint8_t CCITT_update(uint8_t crc, uint8_t b); + + static constexpr uint8_t CCITT_updateCX(uint8_t crc, uint8_t b) { + uint8_t data = crc ^ b; + for (uint8_t i = 0; i < 8; i++) { + if ((data & 0x80U) != 0) { + data <<= 1U; + data ^= 0x07U; + } else { + data <<= 1U; + } + } + return data; + } + + /// Compute/update CRC8 CCIIT from 16bits (convenience wrapper) + static constexpr uint8_t CCITT_updateW(uint8_t crc, uint16_t w) { + union U { + uint8_t b[2]; + uint16_t w; + explicit constexpr inline U(uint16_t w) + : w(w) {} + } u(w); + return CCITT_updateCX(CCITT_updateCX(crc, u.b[0]), u.b[1]); + } +}; + +} // namespace crc + +} // namespace modules diff --git a/Firmware/mmu2_protocol.cpp b/Firmware/mmu2_protocol.cpp index 140c4e49b..91c645dd4 100644 --- a/Firmware/mmu2_protocol.cpp +++ b/Firmware/mmu2_protocol.cpp @@ -9,7 +9,7 @@ // any of the running operation statuses: OID: [T|L|U|E|C|W|K][0-4] // P[0-9] : command being processed i.e. operation running, may contain a state number // E[0-9][0-9] : error 1-9 while doing a tool change -// F : operation finished - will be repeated to "Q" messages until a new command is issued +// F[0-9] : operation finished - will be repeated to "Q" messages until a new command is issued namespace modules { namespace protocol { @@ -40,14 +40,17 @@ DecodeStatus Protocol::DecodeRequest(uint8_t c) { case 'S': case 'B': case 'E': - case 'W': + case 'W': // write is gonna be a special one case 'K': case 'F': case 'f': case 'H': + case 'R': requestMsg.code = (RequestMsgCodes)c; requestMsg.value = 0; - rqState = RequestStates::Value; + requestMsg.value2 = 0; + requestMsg.crc8 = 0; + rqState = (c == 'W') ? RequestStates::Address : RequestStates::Value; // prepare special automaton path for Write commands return DecodeStatus::NeedMoreData; default: requestMsg.code = RequestMsgCodes::unknown; @@ -55,18 +58,61 @@ DecodeStatus Protocol::DecodeRequest(uint8_t c) { return DecodeStatus::Error; } case RequestStates::Value: - if (IsDigit(c)) { - requestMsg.value *= 10; - requestMsg.value += c - '0'; + if (IsHexDigit(c)) { + requestMsg.value <<= 4U; + requestMsg.value |= Char2Nibble(c); + return DecodeStatus::NeedMoreData; + } else if (IsCRCSeparator(c)) { + rqState = RequestStates::CRC; return DecodeStatus::NeedMoreData; - } else if (IsNewLine(c)) { - rqState = RequestStates::Code; - return DecodeStatus::MessageCompleted; } else { requestMsg.code = RequestMsgCodes::unknown; rqState = RequestStates::Error; return DecodeStatus::Error; } + case RequestStates::Address: + if (IsHexDigit(c)) { + requestMsg.value <<= 4U; + requestMsg.value |= Char2Nibble(c); + return DecodeStatus::NeedMoreData; + } else if (c == ' ') { // end of address, value coming + rqState = RequestStates::WriteValue; + return DecodeStatus::NeedMoreData; + } else { + requestMsg.code = RequestMsgCodes::unknown; + rqState = RequestStates::Error; + return DecodeStatus::Error; + } + case RequestStates::WriteValue: + if (IsHexDigit(c)) { + requestMsg.value2 <<= 4U; + requestMsg.value2 |= Char2Nibble(c); + return DecodeStatus::NeedMoreData; + } else if (IsCRCSeparator(c)) { + rqState = RequestStates::CRC; + return DecodeStatus::NeedMoreData; + } else { + requestMsg.code = RequestMsgCodes::unknown; + rqState = RequestStates::Error; + return DecodeStatus::Error; + } + case RequestStates::CRC: + if (IsHexDigit(c)) { + requestMsg.crc8 <<= 4U; + requestMsg.crc8 |= Char2Nibble(c); + return DecodeStatus::NeedMoreData; + } else if (IsNewLine(c)) { + // check CRC at this spot + if (requestMsg.crc8 != requestMsg.ComputeCRC8()) { + // CRC mismatch + requestMsg.code = RequestMsgCodes::unknown; + rqState = RequestStates::Error; + return DecodeStatus::Error; + } else { + rqState = RequestStates::Code; + return DecodeStatus::MessageCompleted; + } + } default: //case error: if (IsNewLine(c)) { rqState = RequestStates::Code; @@ -80,12 +126,28 @@ DecodeStatus Protocol::DecodeRequest(uint8_t c) { } uint8_t Protocol::EncodeRequest(const RequestMsg &msg, uint8_t *txbuff) { - constexpr uint8_t reqSize = 3; txbuff[0] = (uint8_t)msg.code; - txbuff[1] = msg.value + '0'; - txbuff[2] = '\n'; - return reqSize; - static_assert(reqSize <= MaxRequestSize(), "Request message length exceeded the maximum size, increase the magic constant in MaxRequestSize()"); + uint8_t i = 1 + UInt8ToHex(msg.value, txbuff + 1); + + i += AppendCRC(msg.CRC(), txbuff + i); + + txbuff[i] = '\n'; + ++i; + return i; + static_assert(7 <= MaxRequestSize(), "Request message length exceeded the maximum size, increase the magic constant in MaxRequestSize()"); +} + +uint8_t Protocol::EncodeWriteRequest(uint8_t address, uint16_t value, uint8_t *txbuff) { + const RequestMsg msg(RequestMsgCodes::Write, address, value); + uint8_t i = BeginEncodeRequest(msg, txbuff); + // dump the value + i += UInt16ToHex(value, txbuff + i); + + i += AppendCRC(msg.CRC(), txbuff + i); + + txbuff[i] = '\n'; + ++i; + return i; } DecodeStatus Protocol::DecodeResponse(uint8_t c) { @@ -107,8 +169,11 @@ DecodeStatus Protocol::DecodeResponse(uint8_t c) { case 'F': case 'f': case 'H': + case 'R': responseMsg.request.code = (RequestMsgCodes)c; responseMsg.request.value = 0; + responseMsg.request.value2 = 0; + responseMsg.request.crc8 = 0; rspState = ResponseStates::RequestValue; return DecodeStatus::NeedMoreData; case 0x0a: @@ -120,9 +185,9 @@ DecodeStatus Protocol::DecodeResponse(uint8_t c) { return DecodeStatus::Error; } case ResponseStates::RequestValue: - if (IsDigit(c)) { - responseMsg.request.value *= 10; - responseMsg.request.value += c - '0'; + if (IsHexDigit(c)) { + responseMsg.request.value <<= 4U; + responseMsg.request.value += Char2Nibble(c); return DecodeStatus::NeedMoreData; } else if (c == ' ') { rspState = ResponseStates::ParamCode; @@ -149,13 +214,34 @@ DecodeStatus Protocol::DecodeResponse(uint8_t c) { return DecodeStatus::Error; } case ResponseStates::ParamValue: - if (IsDigit(c)) { - responseMsg.paramValue *= 10; - responseMsg.paramValue += c - '0'; + if (IsHexDigit(c)) { + responseMsg.paramValue <<= 4U; + responseMsg.paramValue += Char2Nibble(c); + return DecodeStatus::NeedMoreData; + } else if (IsCRCSeparator(c)) { + rspState = ResponseStates::CRC; + return DecodeStatus::NeedMoreData; + } else { + responseMsg.paramCode = ResponseMsgParamCodes::unknown; + rspState = ResponseStates::Error; + return DecodeStatus::Error; + } + case ResponseStates::CRC: + if (IsHexDigit(c)) { + responseMsg.request.crc8 <<= 4U; + responseMsg.request.crc8 += Char2Nibble(c); return DecodeStatus::NeedMoreData; } else if (IsNewLine(c)) { - rspState = ResponseStates::RequestCode; - return DecodeStatus::MessageCompleted; + // check CRC at this spot + if (responseMsg.request.crc8 != responseMsg.ComputeCRC8()) { + // CRC mismatch + responseMsg.paramCode = ResponseMsgParamCodes::unknown; + rspState = ResponseStates::Error; + return DecodeStatus::Error; + } else { + rspState = ResponseStates::RequestCode; + return DecodeStatus::MessageCompleted; + } } else { responseMsg.paramCode = ResponseMsgParamCodes::unknown; rspState = ResponseStates::Error; @@ -173,75 +259,103 @@ DecodeStatus Protocol::DecodeResponse(uint8_t c) { } uint8_t Protocol::EncodeResponseCmdAR(const RequestMsg &msg, ResponseMsgParamCodes ar, uint8_t *txbuff) { - txbuff[0] = (uint8_t)msg.code; - txbuff[1] = msg.value + '0'; - txbuff[2] = ' '; - txbuff[3] = (uint8_t)ar; - txbuff[4] = '\n'; - return 5; + const ResponseMsg rsp(msg, ar, 0); // this needs some cleanup @@TODO - check assembly how bad is it + uint8_t i = BeginEncodeRequest(rsp.request, txbuff); + txbuff[i] = (uint8_t)ar; + ++i; + i += AppendCRC(rsp.CRC(), txbuff + i); + txbuff[i] = '\n'; + ++i; + return i; } uint8_t Protocol::EncodeResponseReadFINDA(const RequestMsg &msg, uint8_t findaValue, uint8_t *txbuff) { - txbuff[0] = (uint8_t)msg.code; - txbuff[1] = msg.value + '0'; - txbuff[2] = ' '; - txbuff[3] = (uint8_t)ResponseMsgParamCodes::Accepted; - txbuff[4] = findaValue + '0'; - txbuff[5] = '\n'; - return 6; + return EncodeResponseRead(msg, true, findaValue, txbuff); } -uint8_t Protocol::EncodeResponseVersion(const RequestMsg &msg, uint8_t value, uint8_t *txbuff) { - txbuff[0] = (uint8_t)msg.code; - txbuff[1] = msg.value + '0'; - txbuff[2] = ' '; - txbuff[3] = (uint8_t)ResponseMsgParamCodes::Accepted; - uint8_t *dst = txbuff + 4; - if (value < 10) { - *dst++ = value + '0'; - } else if (value < 100) { - *dst++ = value / 10 + '0'; - *dst++ = value % 10 + '0'; - } else { - *dst++ = value / 100 + '0'; - *dst++ = (value / 10) % 10 + '0'; - *dst++ = value % 10 + '0'; - } - *dst = '\n'; - return dst - txbuff + 1; +uint8_t Protocol::EncodeResponseVersion(const RequestMsg &msg, uint16_t value, uint8_t *txbuff) { + return EncodeResponseRead(msg, true, value, txbuff); } -uint8_t Protocol::EncodeResponseQueryOperation(const RequestMsg &msg, ResponseMsgParamCodes code, uint16_t value, uint8_t *txbuff) { - txbuff[0] = (uint8_t)msg.code; - txbuff[1] = msg.value + '0'; - txbuff[2] = ' '; - txbuff[3] = (uint8_t)code; - uint8_t *dst = txbuff + 4; - if (code != ResponseMsgParamCodes::Finished) { - if (value < 10) { - *dst++ = value + '0'; - } else if (value < 100) { - *dst++ = value / 10 + '0'; - *dst++ = value % 10 + '0'; - } else if (value < 1000) { - *dst++ = value / 100 + '0'; - *dst++ = (value / 10) % 10 + '0'; - *dst++ = value % 10 + '0'; - } else if (value < 10000) { - *dst++ = value / 1000 + '0'; - *dst++ = (value / 100) % 10 + '0'; - *dst++ = (value / 10) % 10 + '0'; - *dst++ = value % 10 + '0'; - } else { - *dst++ = value / 10000 + '0'; - *dst++ = (value / 1000) % 10 + '0'; - *dst++ = (value / 100) % 10 + '0'; - *dst++ = (value / 10) % 10 + '0'; - *dst++ = value % 10 + '0'; - } +uint8_t Protocol::EncodeResponseQueryOperation(const RequestMsg &msg, ResponseCommandStatus rcs, uint8_t *txbuff) { + const ResponseMsg rsp(msg, rcs.code, rcs.value); + uint8_t i = BeginEncodeRequest(msg, txbuff); + txbuff[i] = (uint8_t)rsp.paramCode; + ++i; + i += UInt16ToHex(rsp.paramValue, txbuff + i); + i += AppendCRC(rsp.CRC(), txbuff + i); + txbuff[i] = '\n'; + return i + 1; +} + +uint8_t Protocol::EncodeResponseRead(const RequestMsg &msg, bool accepted, uint16_t value2, uint8_t *txbuff) { + const ResponseMsg rsp(msg, + accepted ? ResponseMsgParamCodes::Accepted : ResponseMsgParamCodes::Rejected, + accepted ? value2 : 0 // be careful about this value for CRC computation - rejected status doesn't have any meaningful value which could be reconstructed from the textual form of the message + ); + uint8_t i = BeginEncodeRequest(msg, txbuff); + txbuff[i] = (uint8_t)rsp.paramCode; + ++i; + if (accepted) { + // dump the value + i += UInt16ToHex(value2, txbuff + i); } - *dst = '\n'; - return dst - txbuff + 1; + i += AppendCRC(rsp.CRC(), txbuff + i); + txbuff[i] = '\n'; + return i + 1; +} + +uint8_t Protocol::UInt8ToHex(uint8_t value, uint8_t *dst) { + if (value == 0) { + *dst = '0'; + return 1; + } + + uint8_t v = value >> 4U; + uint8_t charsOut = 1; + if (v != 0) { // skip the first '0' if any + *dst = Nibble2Char(v); + ++dst; + charsOut = 2; + } + v = value & 0xfU; + *dst = Nibble2Char(v); + return charsOut; +} + +uint8_t Protocol::UInt16ToHex(uint16_t value, uint8_t *dst) { + constexpr uint16_t topNibbleMask = 0xf000; + if (value == 0) { + *dst = '0'; + return 1; + } + // skip initial zeros + uint8_t charsOut = 4; + while ((value & topNibbleMask) == 0) { + value <<= 4U; + --charsOut; + } + for (uint8_t i = 0; i < charsOut; ++i) { + uint8_t n = (value & topNibbleMask) >> (8U + 4U); + value <<= 4U; + *dst = Nibble2Char(n); + ++dst; + } + return charsOut; +} + +uint8_t Protocol::BeginEncodeRequest(const RequestMsg &msg, uint8_t *dst) { + dst[0] = (uint8_t)msg.code; + + uint8_t i = 1 + UInt8ToHex(msg.value, dst + 1); + + dst[i] = ' '; + return i + 1; +} + +uint8_t Protocol::AppendCRC(uint8_t crc, uint8_t *dst) { + dst[0] = '*'; // reprap-style separator of CRC + return 1 + UInt8ToHex(crc, dst + 1); } } // namespace protocol diff --git a/Firmware/mmu2_protocol.h b/Firmware/mmu2_protocol.h index 21170b131..870999df9 100644 --- a/Firmware/mmu2_protocol.h +++ b/Firmware/mmu2_protocol.h @@ -1,13 +1,13 @@ /// @file protocol.h #pragma once #include +#include "mmu2_crc.h" namespace modules { /// @brief The MMU communication protocol implementation and related stuff. /// /// See description of the new protocol in the MMU 2021 doc -/// @@TODO possibly add some checksum to verify the correctness of messages namespace protocol { /// Definition of request message codes @@ -23,11 +23,12 @@ enum class RequestMsgCodes : uint8_t { Version = 'S', Button = 'B', Eject = 'E', - Wait = 'W', + Write = 'W', Cut = 'K', FilamentType = 'F', FilamentSensor = 'f', - Home = 'H' + Home = 'H', + Read = 'R' }; /// Definition of response message parameter codes @@ -37,20 +38,50 @@ enum class ResponseMsgParamCodes : uint8_t { Error = 'E', Finished = 'F', Accepted = 'A', - Rejected = 'R', + Rejected = 'R', Button = 'B' // the MMU registered a button press and is sending it to the printer for processing }; /// A request message - requests are being sent by the printer into the MMU. struct RequestMsg { RequestMsgCodes code; ///< code of the request message - uint8_t value; ///< value of the request message + uint8_t value; ///< value of the request message or address of variable to read/write + uint16_t value2; ///< in case or write messages - value to be written into the register + + /// CRC8 check - please note we abuse this byte for CRC of ResponseMsgs as well. + /// The crc8 byte itself is not added into the CRC computation (obviously ;) ) + /// Beware - adding any members of this data structure may need changing the way CRC is being computed! + uint8_t crc8; + + constexpr uint8_t ComputeCRC8() const { + uint8_t crc = 0; + crc = modules::crc::CRC8::CCITT_updateCX(0, (uint8_t)code); + crc = modules::crc::CRC8::CCITT_updateCX(crc, value); + crc = modules::crc::CRC8::CCITT_updateCX(crc, value2); + return crc; + } /// @param code of the request message /// @param value of the request message - inline RequestMsg(RequestMsgCodes code, uint8_t value) + inline constexpr RequestMsg(RequestMsgCodes code, uint8_t value) : code(code) - , value(value) {} + , value(value) + , value2(0) + , crc8(ComputeCRC8()) { + } + + /// Intended for write requests + /// @param code of the request message ('W') + /// @param address of the register + /// @param value to write into the register + inline constexpr RequestMsg(RequestMsgCodes code, uint8_t address, uint16_t value) + : code(code) + , value(address) + , value2(value) + , crc8(ComputeCRC8()) { + } + + constexpr uint8_t CRC() const { return crc8; } }; /// A response message - responses are being sent from the MMU into the printer as a response to a request message. @@ -59,13 +90,33 @@ struct ResponseMsg { ResponseMsgParamCodes paramCode; ///< code of the parameter uint16_t paramValue; ///< value of the parameter + constexpr uint8_t ComputeCRC8() const { + uint8_t crc = request.ComputeCRC8(); + crc = modules::crc::CRC8::CCITT_updateCX(crc, (uint8_t)paramCode); + crc = modules::crc::CRC8::CCITT_updateW(crc, paramValue); + return crc; + } + /// @param request the source request message this response is a reply to /// @param paramCode code of the parameter /// @param paramValue value of the parameter - inline ResponseMsg(RequestMsg request, ResponseMsgParamCodes paramCode, uint16_t paramValue) + inline constexpr ResponseMsg(RequestMsg request, ResponseMsgParamCodes paramCode, uint16_t paramValue) : request(request) , paramCode(paramCode) - , paramValue(paramValue) {} + , paramValue(paramValue) { + this->request.crc8 = ComputeCRC8(); + } + + constexpr uint8_t CRC() const { return request.crc8; } +}; + +/// Combined commandStatus and its value into one data structure (optimization purposes) +struct ResponseCommandStatus { + ResponseMsgParamCodes code; + uint16_t value; + inline constexpr ResponseCommandStatus(ResponseMsgParamCodes code, uint16_t value) + : code(code) + , value(value) {} }; /// Message decoding return values @@ -101,9 +152,18 @@ public: /// @returns number of bytes written into txbuff static uint8_t EncodeRequest(const RequestMsg &msg, uint8_t *txbuff); + /// Encodes Write request message msg into txbuff memory + /// It is expected the txbuff is large enough to fit the message + /// @returns number of bytes written into txbuff + static uint8_t EncodeWriteRequest(uint8_t address, uint16_t value, uint8_t *txbuff); + /// @returns the maximum byte length necessary to encode a request message /// Beneficial in case of pre-allocating a buffer for enconding a RequestMsg. - static constexpr uint8_t MaxRequestSize() { return 3; } + static constexpr uint8_t MaxRequestSize() { return 13; } + + /// @returns the maximum byte length necessary to encode a response message + /// Beneficial in case of pre-allocating a buffer for enconding a ResponseMsg. + static constexpr uint8_t MaxResponseSize() { return 14; } /// Encode generic response Command Accepted or Rejected /// @param msg source request message for this response @@ -124,7 +184,7 @@ public: /// @param value version number (0-255) /// @param txbuff where to format the message /// @returns number of bytes written into txbuff - static uint8_t EncodeResponseVersion(const RequestMsg &msg, uint8_t value, uint8_t *txbuff); + static uint8_t EncodeResponseVersion(const RequestMsg &msg, uint16_t value, uint8_t *txbuff); /// Encode response to Query operation status /// @param msg source request message for this response @@ -132,7 +192,15 @@ public: /// @param value related to status of operation(e.g. error code or progress) /// @param txbuff where to format the message /// @returns number of bytes written into txbuff - static uint8_t EncodeResponseQueryOperation(const RequestMsg &msg, ResponseMsgParamCodes code, uint16_t value, uint8_t *txbuff); + static uint8_t EncodeResponseQueryOperation(const RequestMsg &msg, ResponseCommandStatus rcs, uint8_t *txbuff); + + /// Encode response to Read query + /// @param msg source request message for this response + /// @param accepted true if the read query was accepted + /// @param value2 variable value + /// @param txbuff where to format the message + /// @returns number of bytes written into txbuff + static uint8_t EncodeResponseRead(const RequestMsg &msg, bool accepted, uint16_t value2, uint8_t *txbuff); /// @returns the most recently lexed request message inline const RequestMsg GetRequestMsg() const { return requestMsg; } @@ -150,10 +218,15 @@ public: rspState = ResponseStates::RequestCode; } +#ifndef UNITTEST private: +#endif enum class RequestStates : uint8_t { Code, ///< starting state - expects message code Value, ///< expecting code value + Address, ///< expecting address for Write command + WriteValue, ///< value to be written (Write command) + CRC, ///< CRC Error ///< automaton in error state }; @@ -165,18 +238,84 @@ private: RequestValue, ///< expecting code value ParamCode, ///< expecting param code ParamValue, ///< expecting param value + CRC, ///< expecting CRC value Error ///< automaton in error state }; ResponseStates rspState; ResponseMsg responseMsg; - static bool IsNewLine(uint8_t c) { + static constexpr bool IsNewLine(uint8_t c) { return c == '\n' || c == '\r'; } - static bool IsDigit(uint8_t c) { + static constexpr bool IsDigit(uint8_t c) { return c >= '0' && c <= '9'; } + static constexpr bool IsCRCSeparator(uint8_t c) { + return c == '*'; + } + static constexpr bool IsHexDigit(uint8_t c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'); + } + static constexpr uint8_t Char2Nibble(uint8_t c) { + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return c - '0'; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + return c - 'a' + 10; + default: + return 0; + } + } + + static constexpr uint8_t Nibble2Char(uint8_t n) { + switch (n) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + return n + '0'; + case 0xa: + case 0xb: + case 0xc: + case 0xd: + case 0xe: + case 0xf: + return n - 10 + 'a'; + default: + return 0; + } + } + + /// @returns number of characters written + static uint8_t UInt8ToHex(uint8_t value, uint8_t *dst); + + /// @returns number of characters written + static uint8_t UInt16ToHex(uint16_t value, uint8_t *dst); + + static uint8_t BeginEncodeRequest(const RequestMsg &msg, uint8_t *dst); + + static uint8_t AppendCRC(uint8_t crc, uint8_t *dst); }; } // namespace protocol diff --git a/Firmware/mmu2_protocol_logic.cpp b/Firmware/mmu2_protocol_logic.cpp index 401f28f12..6fa7ff9b3 100644 --- a/Firmware/mmu2_protocol_logic.cpp +++ b/Firmware/mmu2_protocol_logic.cpp @@ -7,8 +7,8 @@ namespace MMU2 { static constexpr uint8_t supportedMmuFWVersionMajor = 2; -static constexpr uint8_t supportedMmuFWVersionMinor = 0; -static constexpr uint8_t supportedMmuFWVersionBuild = 19; +static constexpr uint8_t supportedMmuFWVersionMinor = 1; +static constexpr uint8_t supportedMmuFWVersionBuild = 1; StepStatus ProtocolLogicPartBase::ProcessFINDAReqSent(StepStatus finishedRV, State nextState){ if (auto expmsg = logic->ExpectingMessage(linkLayerTimeout); expmsg != MessageReady)