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.
 
 
 
 
 
 

216 lines
5.1 KiB

/*
TUYA MODULE
Copyright (C) 2019 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
*/
#pragma once
#include <Print.h>
#include <StreamString.h>
#include <iterator>
#include <vector>
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 <typename T, typename PrintType>
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 <typename T>
void write(const T& data) {
_write<T, PrintRaw>(data);
}
template <typename T>
void writeHex(const T& data) {
_write<T, PrintHex>(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<uint8_t>::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<uint8_t> _buffer;
unsigned long _last = 0;
};
class Transport : public Input, public Output, public virtual StreamWrapper {
public:
Transport(Stream& stream) :
StreamWrapper(stream), Input(stream), Output(stream)
{}
};
}