Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
5.3 KiB

  1. /*
  2. TUYA MODULE
  3. Copyright (C) 2019 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
  4. */
  5. #pragma once
  6. #include <Print.h>
  7. #include <StreamString.h>
  8. #include <iterator>
  9. #include <vector>
  10. namespace Tuya {
  11. class PrintRaw {
  12. public:
  13. static void write(Print& printer, uint8_t data) {
  14. printer.write(data);
  15. }
  16. static void write(Print& printer, const uint8_t* data, size_t size) {
  17. printer.write(data, size);
  18. }
  19. };
  20. class PrintHex {
  21. public:
  22. static void write(Print& printer, uint8_t data) {
  23. char buffer[3] = {0};
  24. snprintf(buffer, sizeof(buffer), "%02x", data);
  25. printer.write(buffer, 2);
  26. }
  27. static void write(Print& printer, const uint8_t* data, size_t size) {
  28. for (size_t n=0; n<size; ++n) {
  29. char buffer[3] = {0};
  30. snprintf(buffer, sizeof(buffer), "%02x", data[n]);
  31. printer.write(buffer, 2);
  32. }
  33. }
  34. };
  35. class StreamWrapper {
  36. protected:
  37. Stream& _stream;
  38. public:
  39. StreamWrapper(Stream& stream) :
  40. _stream(stream)
  41. {}
  42. int available() {
  43. return _stream.available();
  44. }
  45. void rewind() {
  46. while(_stream.read() != -1);
  47. }
  48. };
  49. class Output : public virtual StreamWrapper {
  50. public:
  51. Output(Stream& stream) :
  52. StreamWrapper(stream)
  53. {}
  54. Output(StreamString& stream, size_t length) :
  55. Output(stream)
  56. {
  57. stream.reserve((length * 2) + 1);
  58. }
  59. template <typename T, typename PrintType>
  60. void _write(const T& data) {
  61. const uint8_t header[2] = {0x55, 0xaa};
  62. uint8_t checksum = 0xff;
  63. PrintType::write(_stream, header, 2);
  64. for (auto it = data.cbegin(); it != data.cend(); ++it) {
  65. checksum += *it;
  66. PrintType::write(_stream, *it);
  67. }
  68. PrintType::write(_stream, checksum);
  69. }
  70. template <typename T>
  71. void write(const T& data) {
  72. _write<T, PrintRaw>(data);
  73. }
  74. template <typename T>
  75. void writeHex(const T& data) {
  76. _write<T, PrintHex>(data);
  77. }
  78. };
  79. class Input : public virtual StreamWrapper {
  80. // Buffer depth based on the SDK recommendations
  81. constexpr static size_t LIMIT = 256;
  82. // 9600 baud ~= 1.04 bytes per second
  83. // 256 * 1.04 = 266.24
  84. constexpr static size_t TIME_LIMIT = 267;
  85. using const_iterator = std::vector<uint8_t>::const_iterator;
  86. public:
  87. Input(Stream& stream) :
  88. StreamWrapper(stream)
  89. {
  90. _buffer.reserve(LIMIT);
  91. }
  92. bool full() { return (_index >= LIMIT); }
  93. bool done() { return _done; }
  94. size_t size() { return _index; }
  95. uint8_t operator[](size_t i) {
  96. if (i > LIMIT) return 0;
  97. return _buffer[i];
  98. }
  99. const_iterator cbegin() const {
  100. return _buffer.cbegin();
  101. }
  102. const_iterator cend() const {
  103. return _buffer.cend();
  104. }
  105. void read() {
  106. if (_done) return;
  107. if (full()) return;
  108. if (_last && (millis() - _last > TIME_LIMIT)) reset();
  109. _last = millis();
  110. int byte = _stream.read();
  111. if (byte < 0) return;
  112. //DEBUG_MSG("i=%u byte=%u chk=%u:%u\n",
  113. // _index, byte, _checksum, uint8_t(_checksum + byte));
  114. // check that header value is 0x55aa
  115. if (0 == _index) {
  116. if (0x55 != byte) return;
  117. } else if (1 == _index) {
  118. if (0xaa != byte) return;
  119. }
  120. // set read boundary from received length
  121. if (4 == _index) {
  122. _read_until = byte << 8;
  123. }
  124. if (5 == _index) {
  125. _read_until += byte + _index + 1;
  126. //DEBUG_MSG("read_until=%u\n", _read_until);
  127. }
  128. // verify that the checksum byte is the same that we got so far
  129. if ((_index > 5) && (_index >= _read_until)) {
  130. if (_checksum != byte) {
  131. //DEBUG_MSG("chk err, recv=%u calc=%u\n", byte, _checksum);
  132. reset();
  133. return;
  134. }
  135. //DEBUG_MSG("chk ok\n");
  136. _done = true;
  137. return;
  138. }
  139. _buffer[_index] = byte;
  140. _checksum += byte;
  141. ++_index;
  142. }
  143. void reset() {
  144. std::fill(_buffer.begin(), _buffer.end(), 0);
  145. _read_until = LIMIT;
  146. _checksum = 0;
  147. _index = 0;
  148. _done = false;
  149. _last = 0;
  150. }
  151. private:
  152. bool _done = false;
  153. size_t _index = 0;
  154. size_t _read_until = LIMIT;
  155. uint8_t _checksum = 0;
  156. std::vector<uint8_t> _buffer;
  157. unsigned long _last = 0;
  158. };
  159. class Transport : public Input, public Output, public virtual StreamWrapper {
  160. public:
  161. Transport(Stream& stream) :
  162. StreamWrapper(stream), Input(stream), Output(stream)
  163. {}
  164. };
  165. }