/* TUYA MODULE Copyright (C) 2019 by Maxim Prokhorov */ #pragma once #include #include #include #include namespace tuya { class PrintRaw { public: static void write(Print& printer, uint8_t data) { printer.write(data); } static void write(Print& printer, const uint8_t* data, size_t size) { printer.write(data, size); } }; class PrintHex { public: static void write(Print& printer, uint8_t data) { char buffer[3] = {0}; snprintf(buffer, sizeof(buffer), "%02x", data); printer.write(buffer, 2); } static void write(Print& printer, const uint8_t* data, size_t size) { for (size_t n = 0; n < size; ++n) { write(printer, data[n]); } } }; class StreamWrapper { protected: Stream& _stream; public: StreamWrapper(Stream& stream) : _stream(stream) {} int available() { return _stream.available(); } void rewind() { while(_stream.read() != -1); } }; class Output : public virtual StreamWrapper { public: Output(Stream& stream) : StreamWrapper(stream) {} Output(StreamString& stream, size_t length) : Output(stream) { stream.reserve((length * 2) + 1); } template void _write(const T& data) { const uint8_t header[2] = {0x55, 0xaa}; uint8_t checksum = 0xff; PrintType::write(_stream, header, 2); for (auto it = data.cbegin(); it != data.cend(); ++it) { checksum += *it; PrintType::write(_stream, *it); } PrintType::write(_stream, checksum); } template void write(const T& data) { _write(data); } template void writeHex(const T& data) { _write(data); } }; class Input : public virtual StreamWrapper { // Buffer depth based on the SDK recommendations constexpr static size_t LIMIT = 256; // 9600 baud ~= 1.04 bytes per second // 256 * 1.04 = 266.24 constexpr static size_t TIME_LIMIT = 267; public: using const_iterator = std::vector::const_iterator; Input(Stream& stream) : StreamWrapper(stream) { _buffer.reserve(LIMIT); } bool full() { return (_index >= LIMIT); } bool done() { return _done; } size_t size() { return _index; } uint8_t operator[](size_t i) const { if (i > LIMIT) return 0; return _buffer[i]; } const_iterator cbegin() const { return _buffer.cbegin(); } const_iterator cend() const { return _buffer.cend(); } void read() { if (_done) return; if (full()) return; if (_last && (millis() - _last > TIME_LIMIT)) reset(); _last = millis(); int byte = _stream.read(); if (byte < 0) return; //DEBUG_MSG("i=%u byte=%u chk=%u:%u\n", // _index, byte, _checksum, uint8_t(_checksum + byte)); // check that header value is 0x55aa if (0 == _index) { if (0x55 != byte) return; } else if (1 == _index) { if (0xaa != byte) return; } // set read boundary from received length if (4 == _index) { _read_until = byte << 8; } if (5 == _index) { _read_until += byte + _index + 1; //DEBUG_MSG("read_until=%u\n", _read_until); } // verify that the checksum byte is the same that we got so far if ((_index > 5) && (_index >= _read_until)) { if (_checksum != byte) { //DEBUG_MSG("chk err, recv=%u calc=%u\n", byte, _checksum); reset(); return; } //DEBUG_MSG("chk ok\n"); _done = true; return; } _buffer[_index] = byte; _checksum += byte; ++_index; } void reset() { std::fill(_buffer.begin(), _buffer.end(), 0); _read_until = LIMIT; _checksum = 0; _index = 0; _done = false; _last = 0; } private: bool _done = false; size_t _index = 0; size_t _read_until = LIMIT; uint8_t _checksum = 0; std::vector _buffer; unsigned long _last = 0; }; class Transport : public Input, public Output, public virtual StreamWrapper { public: Transport(Stream& stream) : StreamWrapper(stream), Input(stream), Output(stream) {} }; }