Tasmota/lib/lib_basic/IRremoteESP8266/src/ir_MWM.cpp
2021-03-25 08:40:27 +01:00

198 lines
5.8 KiB
C++

// Copyright 2018 Brett T. Warden
/// @file
/// @brief Disney Made With Magic (MWM) Support
/// derived from ir_Lasertag.cpp
/// @see https://github.com/crankyoldgit/IRremoteESP8266/pull/557
// Supports:
// Brand: Disney, Model: Made With Magic (Glow With The Show) wand
#include <algorithm>
#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"
// Constants
const uint16_t kMWMMinSamples = 6; // Msgs are >=3 bytes, bytes have >=2
// samples
const uint16_t kMWMTick = 417;
const uint32_t kMWMMinGap = 30000; // Typical observed delay b/w commands
const uint8_t kMWMTolerance = 0; // Percentage error margin.
const uint16_t kMWMExcess = 0; // See kMarkExcess.
const uint16_t kMWMDelta = 150; // Use instead of Excess and Tolerance.
const uint8_t kMWMMaxWidth = 9; // Maximum number of successive bits at a
// single level - worst case
const int16_t kSpace = 1;
const int16_t kMark = 0;
#if SEND_MWM
/// Send a MWM packet/message.
/// Status: Implemented.
/// @param[in] data The message to be sent.
/// @param[in] nbytes The number of bytes of message to be sent.
/// @param[in] repeat The number of times the command is to be repeated.
/// @note This protocol is 2400 bps serial, 1 start bit (mark),
/// 1 stop bit (space), no parity
void IRsend::sendMWM(const uint8_t data[], const uint16_t nbytes,
const uint16_t repeat) {
if (nbytes < 3) return; // Shortest possible message is 3 bytes
// Set 38kHz IR carrier frequency & a 1/4 (25%) duty cycle.
// NOTE: duty cycle is not confirmed. Just guessing based on RC5/6 protocols.
enableIROut(38, 25);
for (uint16_t r = 0; r <= repeat; r++) {
// Data
for (uint16_t i = 0; i < nbytes; i++) {
uint8_t byte = data[i];
// Start bit
mark(kMWMTick);
// LSB first, space=1
for (uint8_t mask = 0x1; mask; mask <<= 1) {
if (byte & mask) { // 1
space(kMWMTick);
} else { // 0
mark(kMWMTick);
}
}
// Stop bit
space(kMWMTick);
}
// Footer
space(kMWMMinGap);
}
}
#endif // SEND_MWM
#if DECODE_MWM
/// Decode the supplied MWM message.
/// Status: Implemented.
/// @param[in,out] results Ptr to the data to decode & where to store the result
/// @param[in] offset The starting index to use when attempting to decode the
/// raw data. Typically/Defaults to kStartOffset.
/// @param[in] nbits The number of data bits to expect.
/// @param[in] strict Flag indicating if we should perform strict matching.
/// @return True if it can decode it, false if it can't.
/// @note This protocol is 2400 bps serial, 1 start bit (mark),
/// 1 stop bit (space), no parity
bool IRrecv::decodeMWM(decode_results *results, uint16_t offset,
const uint16_t nbits, const bool strict) {
DPRINTLN("DEBUG: decodeMWM");
// Compliance
if (results->rawlen <= kMWMMinSamples + offset) {
DPRINTLN("DEBUG: decodeMWM: too few samples");
return false;
}
uint16_t used = 0;
uint64_t data = 0;
uint16_t frame_bits = 0;
uint16_t data_bits = 0;
// No Header
// Data
uint8_t bits_per_frame = 10;
for (; offset < results->rawlen && results->bits < 8 * kStateSizeMax;
frame_bits++) {
DPRINT("DEBUG: decodeMWM: offset = ");
DPRINTLN(offset);
int16_t level = getRClevel(results, &offset, &used, kMWMTick, kMWMTolerance,
kMWMExcess, kMWMDelta, kMWMMaxWidth);
if (level < 0) {
DPRINTLN("DEBUG: decodeMWM: getRClevel returned error");
break;
}
switch (frame_bits % bits_per_frame) {
case 0:
// Start bit
if (level != kMark) {
DPRINTLN("DEBUG: decodeMWM: framing error - invalid start bit");
goto done;
}
break;
case 9:
// Stop bit
if (level != kSpace) {
DPRINTLN("DEBUG: decodeMWM: framing error - invalid stop bit");
return false;
} else {
DPRINT("DEBUG: decodeMWM: data_bits = ");
DPRINTLN(data_bits);
DPRINT("DEBUG: decodeMWM: Finished byte: ");
DPRINTLN(uint64ToString(data));
results->state[data_bits / 8 - 1] = data & 0xFF;
results->bits = data_bits;
data = 0;
}
break;
default:
// Data bits
DPRINT("DEBUG: decodeMWM: Storing bit: ");
DPRINTLN((level == kSpace));
// Transmission is LSB-first, space=1
data |= ((level == kSpace)) << 8;
data >>= 1;
data_bits++;
break;
}
}
done:
// Footer (None)
// Compliance
DPRINT("DEBUG: decodeMWM: frame_bits = ");
DPRINTLN(frame_bits);
DPRINT("DEBUG: decodeMWM: data_bits = ");
DPRINTLN(data_bits);
if (data_bits < nbits) {
DPRINT("DEBUG: decodeMWM: too few bits; expected ");
DPRINTLN(nbits);
return false; // Less data than we expected.
}
uint16_t payload_length = 0;
switch (results->state[0] & 0xf0) {
case 0x90:
case 0xf0:
// Normal commands
payload_length = results->state[0] & 0x0f;
DPRINT("DEBUG: decodeMWM: payload_length = ");
DPRINTLN(payload_length);
break;
default:
if (strict) {
// Show commands
if (results->state[0] != 0x55 && results->state[1] != 0xAA) {
return false;
}
}
break;
}
if (data_bits < (payload_length + 3) * 8) {
DPRINT("DEBUG: decodeMWM: too few bytes; expected ");
DPRINTLN((payload_length + 3));
return false;
}
if (strict) {
if (payload_length && (data_bits > (payload_length + 3) * 8)) {
DPRINT("DEBUG: decodeMWM: too many bytes; expected ");
DPRINTLN((payload_length + 3));
return false;
}
}
// Success
results->decode_type = MWM;
results->repeat = false;
return true;
}
#endif // DECODE_MWM
// vim: et:ts=2:sw=2