mmu2_protocol.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. /// @file protocol.h
  2. #pragma once
  3. #include <stdint.h>
  4. #include "mmu2_crc.h"
  5. namespace modules {
  6. /// @brief The MMU communication protocol implementation and related stuff.
  7. ///
  8. /// See description of the new protocol in the MMU 2021 doc
  9. namespace protocol {
  10. /// Definition of request message codes
  11. enum class RequestMsgCodes : uint8_t {
  12. unknown = 0,
  13. Query = 'Q',
  14. Tool = 'T',
  15. Load = 'L',
  16. Mode = 'M',
  17. Unload = 'U',
  18. Reset = 'X',
  19. Finda = 'P',
  20. Version = 'S',
  21. Button = 'B',
  22. Eject = 'E',
  23. Write = 'W',
  24. Cut = 'K',
  25. FilamentType = 'F',
  26. FilamentSensor = 'f',
  27. Home = 'H',
  28. Read = 'R'
  29. };
  30. /// Definition of response message parameter codes
  31. enum class ResponseMsgParamCodes : uint8_t {
  32. unknown = 0,
  33. Processing = 'P',
  34. Error = 'E',
  35. Finished = 'F',
  36. Accepted = 'A',
  37. Rejected = 'R',
  38. Button = 'B' // the MMU registered a button press and is sending it to the printer for processing
  39. };
  40. /// A request message - requests are being sent by the printer into the MMU.
  41. struct RequestMsg {
  42. RequestMsgCodes code; ///< code of the request message
  43. uint8_t value; ///< value of the request message or address of variable to read/write
  44. uint16_t value2; ///< in case or write messages - value to be written into the register
  45. /// CRC8 check - please note we abuse this byte for CRC of ResponseMsgs as well.
  46. /// The crc8 byte itself is not added into the CRC computation (obviously ;) )
  47. /// Beware - adding any members of this data structure may need changing the way CRC is being computed!
  48. uint8_t crc8;
  49. constexpr uint8_t ComputeCRC8() const {
  50. uint8_t crc = 0;
  51. crc = modules::crc::CRC8::CCITT_updateCX(0, (uint8_t)code);
  52. crc = modules::crc::CRC8::CCITT_updateCX(crc, value);
  53. crc = modules::crc::CRC8::CCITT_updateW(crc, value2);
  54. return crc;
  55. }
  56. /// @param code of the request message
  57. /// @param value of the request message
  58. inline constexpr RequestMsg(RequestMsgCodes code, uint8_t value)
  59. : code(code)
  60. , value(value)
  61. , value2(0)
  62. , crc8(ComputeCRC8()) {
  63. }
  64. /// Intended for write requests
  65. /// @param code of the request message ('W')
  66. /// @param address of the register
  67. /// @param value to write into the register
  68. inline constexpr RequestMsg(RequestMsgCodes code, uint8_t address, uint16_t value)
  69. : code(code)
  70. , value(address)
  71. , value2(value)
  72. , crc8(ComputeCRC8()) {
  73. }
  74. constexpr uint8_t CRC() const { return crc8; }
  75. };
  76. /// A response message - responses are being sent from the MMU into the printer as a response to a request message.
  77. struct ResponseMsg {
  78. RequestMsg request; ///< response is always preceeded by the request message
  79. ResponseMsgParamCodes paramCode; ///< code of the parameter
  80. uint16_t paramValue; ///< value of the parameter
  81. constexpr uint8_t ComputeCRC8() const {
  82. uint8_t crc = request.ComputeCRC8();
  83. crc = modules::crc::CRC8::CCITT_updateCX(crc, (uint8_t)paramCode);
  84. crc = modules::crc::CRC8::CCITT_updateW(crc, paramValue);
  85. return crc;
  86. }
  87. /// @param request the source request message this response is a reply to
  88. /// @param paramCode code of the parameter
  89. /// @param paramValue value of the parameter
  90. inline constexpr ResponseMsg(RequestMsg request, ResponseMsgParamCodes paramCode, uint16_t paramValue)
  91. : request(request)
  92. , paramCode(paramCode)
  93. , paramValue(paramValue) {
  94. this->request.crc8 = ComputeCRC8();
  95. }
  96. constexpr uint8_t CRC() const { return request.crc8; }
  97. };
  98. /// Combined commandStatus and its value into one data structure (optimization purposes)
  99. struct ResponseCommandStatus {
  100. ResponseMsgParamCodes code;
  101. uint16_t value;
  102. inline constexpr ResponseCommandStatus(ResponseMsgParamCodes code, uint16_t value)
  103. : code(code)
  104. , value(value) {}
  105. };
  106. /// Message decoding return values
  107. enum class DecodeStatus : uint_fast8_t {
  108. MessageCompleted, ///< message completed and successfully lexed
  109. NeedMoreData, ///< message incomplete yet, waiting for another byte to come
  110. Error, ///< input character broke message decoding
  111. };
  112. /// Protocol class is responsible for creating/decoding messages in Rx/Tx buffer
  113. ///
  114. /// Beware - in the decoding more, it is meant to be a statefull instance which works through public methods
  115. /// processing one input byte per call.
  116. class Protocol {
  117. public:
  118. inline Protocol()
  119. : rqState(RequestStates::Code)
  120. , requestMsg(RequestMsgCodes::unknown, 0)
  121. , rspState(ResponseStates::RequestCode)
  122. , responseMsg(RequestMsg(RequestMsgCodes::unknown, 0), ResponseMsgParamCodes::unknown, 0) {
  123. }
  124. /// Takes the input byte c and steps one step through the state machine
  125. /// @returns state of the message being decoded
  126. DecodeStatus DecodeRequest(uint8_t c);
  127. /// Decodes response message in rxbuff
  128. /// @returns decoded response message structure
  129. DecodeStatus DecodeResponse(uint8_t c);
  130. /// Encodes request message msg into txbuff memory
  131. /// It is expected the txbuff is large enough to fit the message
  132. /// @returns number of bytes written into txbuff
  133. static uint8_t EncodeRequest(const RequestMsg &msg, uint8_t *txbuff);
  134. /// Encodes Write request message msg into txbuff memory
  135. /// It is expected the txbuff is large enough to fit the message
  136. /// @returns number of bytes written into txbuff
  137. static uint8_t EncodeWriteRequest(uint8_t address, uint16_t value, uint8_t *txbuff);
  138. /// @returns the maximum byte length necessary to encode a request message
  139. /// Beneficial in case of pre-allocating a buffer for enconding a RequestMsg.
  140. static constexpr uint8_t MaxRequestSize() { return 13; }
  141. /// @returns the maximum byte length necessary to encode a response message
  142. /// Beneficial in case of pre-allocating a buffer for enconding a ResponseMsg.
  143. static constexpr uint8_t MaxResponseSize() { return 14; }
  144. /// Encode generic response Command Accepted or Rejected
  145. /// @param msg source request message for this response
  146. /// @param ar code of response parameter
  147. /// @param txbuff where to format the message
  148. /// @returns number of bytes written into txbuff
  149. static uint8_t EncodeResponseCmdAR(const RequestMsg &msg, ResponseMsgParamCodes ar, uint8_t *txbuff);
  150. /// Encode response to Read FINDA query
  151. /// @param msg source request message for this response
  152. /// @param findaValue 1/0 (on/off) status of FINDA
  153. /// @param txbuff where to format the message
  154. /// @returns number of bytes written into txbuff
  155. static uint8_t EncodeResponseReadFINDA(const RequestMsg &msg, uint8_t findaValue, uint8_t *txbuff);
  156. /// Encode response to Query operation status
  157. /// @param msg source request message for this response
  158. /// @param code status of operation (Processing, Error, Finished)
  159. /// @param value related to status of operation(e.g. error code or progress)
  160. /// @param txbuff where to format the message
  161. /// @returns number of bytes written into txbuff
  162. static uint8_t EncodeResponseQueryOperation(const RequestMsg &msg, ResponseCommandStatus rcs, uint8_t *txbuff);
  163. /// Encode response to Read query
  164. /// @param msg source request message for this response
  165. /// @param accepted true if the read query was accepted
  166. /// @param value2 variable value
  167. /// @param txbuff where to format the message
  168. /// @returns number of bytes written into txbuff
  169. static uint8_t EncodeResponseRead(const RequestMsg &msg, bool accepted, uint16_t value2, uint8_t *txbuff);
  170. /// @returns the most recently lexed request message
  171. inline const RequestMsg GetRequestMsg() const { return requestMsg; }
  172. /// @returns the most recently lexed response message
  173. inline const ResponseMsg GetResponseMsg() const { return responseMsg; }
  174. /// resets the internal request decoding state (typically after an error)
  175. void ResetRequestDecoder() {
  176. rqState = RequestStates::Code;
  177. }
  178. /// resets the internal response decoding state (typically after an error)
  179. void ResetResponseDecoder() {
  180. rspState = ResponseStates::RequestCode;
  181. }
  182. #ifndef UNITTEST
  183. private:
  184. #endif
  185. enum class RequestStates : uint8_t {
  186. Code, ///< starting state - expects message code
  187. Value, ///< expecting code value
  188. Address, ///< expecting address for Write command
  189. WriteValue, ///< value to be written (Write command)
  190. CRC, ///< CRC
  191. Error ///< automaton in error state
  192. };
  193. RequestStates rqState;
  194. RequestMsg requestMsg;
  195. enum class ResponseStates : uint8_t {
  196. RequestCode, ///< starting state - expects message code
  197. RequestValue, ///< expecting code value
  198. ParamCode, ///< expecting param code
  199. ParamValue, ///< expecting param value
  200. CRC, ///< expecting CRC value
  201. Error ///< automaton in error state
  202. };
  203. ResponseStates rspState;
  204. ResponseMsg responseMsg;
  205. static constexpr bool IsNewLine(uint8_t c) {
  206. return c == '\n' || c == '\r';
  207. }
  208. static constexpr bool IsDigit(uint8_t c) {
  209. return c >= '0' && c <= '9';
  210. }
  211. static constexpr bool IsCRCSeparator(uint8_t c) {
  212. return c == '*';
  213. }
  214. static constexpr bool IsHexDigit(uint8_t c) {
  215. return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
  216. }
  217. static constexpr uint8_t Char2Nibble(uint8_t c) {
  218. switch (c) {
  219. case '0':
  220. case '1':
  221. case '2':
  222. case '3':
  223. case '4':
  224. case '5':
  225. case '6':
  226. case '7':
  227. case '8':
  228. case '9':
  229. return c - '0';
  230. case 'a':
  231. case 'b':
  232. case 'c':
  233. case 'd':
  234. case 'e':
  235. case 'f':
  236. return c - 'a' + 10;
  237. default:
  238. return 0;
  239. }
  240. }
  241. static constexpr uint8_t Nibble2Char(uint8_t n) {
  242. switch (n) {
  243. case 0:
  244. case 1:
  245. case 2:
  246. case 3:
  247. case 4:
  248. case 5:
  249. case 6:
  250. case 7:
  251. case 8:
  252. case 9:
  253. return n + '0';
  254. case 0xa:
  255. case 0xb:
  256. case 0xc:
  257. case 0xd:
  258. case 0xe:
  259. case 0xf:
  260. return n - 10 + 'a';
  261. default:
  262. return 0;
  263. }
  264. }
  265. /// @returns number of characters written
  266. static uint8_t UInt8ToHex(uint8_t value, uint8_t *dst);
  267. /// @returns number of characters written
  268. static uint8_t UInt16ToHex(uint16_t value, uint8_t *dst);
  269. static uint8_t BeginEncodeRequest(const RequestMsg &msg, uint8_t *dst);
  270. static uint8_t AppendCRC(uint8_t crc, uint8_t *dst);
  271. };
  272. } // namespace protocol
  273. } // namespace modules
  274. namespace mp = modules::protocol;