Could look something like this:
class HammingStream : public Stream {
public:
HammingStream(Stream& ios) : m_ios(ios) {}
virtual size_t write(uint8_t byte);
virtual int read();
virtual int available();
virtual void flush();
protected:
Stream& m_ios;
uint8_t encode4(uint8_t nibble);
uint8_t decode8(uint8_t code);
};
size_t HammingStream::write(uint8_t byte)
{
m_ios.write(encode4(byte >> 4));
m_ios.write(encode4(byte & 0xf));
return (1);
}
int HammingStream::read()
{
if (available() == 0) return (-1);
uint8_t nibble = decode8(m_ios.read());
return ((nibble << 4) | decode8(m_ios.read());
}
int HammingStream::available()
{
return (m_ios.available() / 2);
}
void HammingStream::flush()
{
m_ios.flush();
}
Then you could:
HammingStream HammingSerial(SoftwareSerial);
HammingSerial.println(F("hello world"));
The above leaves out the encoding/decoding but also error detection. Hamming decoding can capture up to two bit errors and do one bit correction (on 4-bit data).
Cheers!