diff --git a/IRelectra.cpp b/IRelectra.cpp index a990d1b..14ad165 100644 --- a/IRelectra.cpp +++ b/IRelectra.cpp @@ -1,106 +1,59 @@ /* * IRelectra - * Version 0.8 - * Copyrights 2014 Barak Weiss + * Copyrights 2016 Barak Weiss * * Many thanks to Chris from AnalysIR */ #include "IRelectra.h" #include +#include -#define UNIT 1000 +using std::vector; + +#define UNIT 992 #define NUM_BITS 34 IRelectra::IRelectra(IRsend* remote) : _remote(remote) {} -// Add bit b to array p at index i -// This function is used to convert manchester encoding to MARKs and SPACEs -// p is the pointer to the start of the MARK, SPACE array -// i is the current index -// b is the bit we want to add to the MARK, SPACE array -// A zero bit is one unit MARK and one unit SPACE -// a one bit is one unit SPACE and one unit MARK -void IRelectra::addBit(unsigned int* p, int* i, char b) +// Sends the specified configuration to the IR led +bool IRelectra::sendElectra(bool power, IRElectraMode mode, IRElectraFan fan, int temperature, bool swing, bool sleep) { - if (((*i) & 1) == 1) - { - // current index is SPACE - if ((b & 1) == 1) - { - // one is one unit low, then one unit up - // since we're pointing at SPACE, we should increase it byte a unit - // then add another MARK unit - *(p+*i) += UNIT; - (*i)++; - *(p+*i) = UNIT; - } - if ((b & 1) == 0) - { - // we need a MARK unit, then SPACE unit - (*i)++; - *(p+*i) = UNIT; - (*i)++; - *(p+*i) = UNIT; - } - } - else if (((*i) & 1) == 0) - { - // current index is MARK - if ((b & 1) == 1) - { - (*i)++; - *(p+*i) = UNIT; - (*i)++; - *(p+*i) = UNIT; - } - if ((b & 1) == 0) - { - *(p+*i) += UNIT; - (*i)++; - *(p+*i) = UNIT; - } - } - + // get the data representing the configuration + uint64_t code = encodeElectra(power, mode, fan, temperature, swing, sleep); + + // get the raw data itself with headers, repetition, etc. + std::vector data = generateSignal(code); + + // send using HW. + _remote->sendRaw(data.data(), data.size(), 33); + + return true; } -// Sends the specified configuration to the IR led using IRremote -// 1. Get the numeric value of the configuration -// 2. Convert to IRremote compatible array (MARKS and SPACES) -// 3. Send to IRremote -bool IRelectra::SendElectra(int power, int mode, int fan, int temperature, int swing, int sleep) +std::vector IRelectra::generateSignal(uint64_t code) { - unsigned int data[200]; //~maximum size of the IR packet - int i = 0; - - // get the data representing the configuration - uint64_t code = EncodeElectra(power, mode, fan, temperature, swing, sleep); - + MarkSpaceArray markspace(UNIT); + // The whole packet looks this: // 3 Times: // 3000 usec MARK // 3000 used SPACE // Maxchester encoding of the data, clock is ~1000usec // 4000 usec MARK - for (int k = 0; k<3; k++) + for (int k =0; k<3; k++) { - data[i] = 3 * UNIT; //mark - i++; - data[i] = 3 * UNIT; - for (int j = NUM_BITS - 1; j >= 0; j--) - { - addBit(data, &i, (code >> j) & 1); - } - i++; + markspace.addMark(3); //mark + markspace.addSpace(3); //space + markspace.addNumberWithManchesterCode(code, NUM_BITS); } - data[i] = 4 * UNIT; - - _remote->sendRaw(data, i + 1, 38); - return true; + markspace.addMark(4); + return markspace.data(); } -// Encodes specific A/C configuration to a number that describes +#pragma pack(1) + // That configuration has a total of 34 bits // 33: Power bit, if this bit is ON, the A/C will toggle it's power. // 32-30: Mode - Cool, heat etc. @@ -113,7 +66,42 @@ bool IRelectra::SendElectra(int power, int mode, int fan, int temperature, int s // 17- 2: Zeros // 1: One // 0: Zero -uint64_t IRelectra::EncodeElectra(int power, int mode, int fan, int temperature, int swing, int sleep) +typedef union ElectraCode { + uint64_t num; + struct { + uint8_t zeros1 : 1; + uint8_t ones1 : 1; + uint16_t zeros2 : 16; + uint8_t sleep : 1; + uint8_t temperature : 4; + uint8_t zeros3 : 2; + uint8_t swing : 1; + uint8_t zeros4 : 2; + uint8_t fan : 2; + uint8_t mode : 3; + uint8_t power : 1; + }; +} ElectraUnion; + +#pragma pack() + + +uint64_t IRelectra::encodeElectra(bool power, IRElectraMode mode, IRElectraFan fan, int temperature, bool swing, bool sleep) +{ + temperature -= 15; + ElectraCode code = { 0 }; + code.ones1 = 1; + code.sleep = sleep ? 1 : 0; + code.temperature = temperature; + code.swing = swing ? 1 : 0; + code.fan = fan; + code.mode = mode; + code.power = power ? 1 : 0; + + return code.num; +} + +uint64_t encodeElectra2(bool power, IRElectraMode mode, IRElectraFan fan, int temperature, bool swing, bool sleep) { uint64_t num = 0; uint64_t power64 = power; @@ -136,3 +124,73 @@ uint64_t IRelectra::EncodeElectra(int power, int mode, int fan, int temperature, return num; } + +/// +/// Mark Space Array +/// +MarkSpaceArray::MarkSpaceArray(uint16_t unitLengthInUsec) : _unitLength(unitLengthInUsec) +{ } + +void MarkSpaceArray::MarkSpaceArray::addMark(uint16_t units) +{ + if (currentState()) + { + addUnitsToCurrentState(units); + } + else + { + addUnitsToNextState(units); + } +} + +void MarkSpaceArray::addSpace(uint16_t units) +{ + if (!currentState()) + { + addUnitsToCurrentState(units); + } + else + { + addUnitsToNextState(units); + }} + +void MarkSpaceArray::addBitWithManchesterCode(uint8_t bit) +{ + if (currentState() == (bit & 1)) + { + addUnitsToNextState(1); + } + else + { + addUnitsToCurrentState(1); + } + addUnitsToNextState(1); +} + +void MarkSpaceArray::addNumberWithManchesterCode(uint64_t code, uint8_t numberOfBits) +{ + for (int j = numberOfBits - 1; j>=0; j--) + { + addBitWithManchesterCode((code >> j) & 1); + } +} + +void MarkSpaceArray::addUnitsToCurrentState(uint16_t units) +{ + _data.back() += _unitLength * units; +} + +void MarkSpaceArray::addUnitsToNextState(uint16_t units) +{ + _data.emplace_back(_unitLength * units); +} + +const std::vector MarkSpaceArray::data() +{ + return _data; +} + +uint8_t MarkSpaceArray::currentState() +{ + return _data.size() % 2; +} diff --git a/IRelectra.h b/IRelectra.h index 298876c..50c6a8f 100644 --- a/IRelectra.h +++ b/IRelectra.h @@ -1,7 +1,6 @@ /* * IRelectra -* Version 0.8 -* Copyrights 2014 Barak Weiss +* Copyrights 2016 Barak Weiss * * Many thanks to Chris from AnalysIR */ @@ -9,43 +8,95 @@ #ifndef IRelectra_h #define IRelectra_h -#include - #include "IRremote.h" -#define POWER_OFF 0 -#define POWER_ON 1 - -#define MODE_COOL 0b001 -#define MODE_HEAT 0b010 -#define MODE_AUTO 0b011 -#define MODE_DRY 0b100 -#define MODE_FAN 0b101 - -#define FAN_LOW 0b00 -#define FAN_MED 0b01 -#define FAN_HIGH 0b10 -#define FAN_AUTO 0b11 +#include +#include -#define SWING_OFF 0b0 -#define SWING_ON 0b1 +typedef enum IRElectraMode { + IRElectraModeCool = 0b001, + IRElectraModeHeat = 0b010, + IRElectraModeAuto = 0b011, + IRElectraModeDry = 0b100, + IRElectraModeFan = 0b101 +} IRElectraMode; -#define SLEEP_OFF 0b0 -#define SLEEP_ON 0b1 +typedef enum IRElectraFan { + IRElectraFanLow = 0b00, + IRElectraFanMedium = 0b01, + IRElectraFanHigh = 0b10, + IRElectraFanAuto = 0b11 +} IRElectraFan; class IRelectra { public: - // Ctor, remote will be used to send the raw IR data + // Initialize IRelectra(IRsend* remote); - // Sends the specified configuration to the IR led using IRremote - bool SendElectra(int power, int mode, int fan, int temperature, int swing, int sleep); + // Send an IR packet with the given parameters. + bool sendElectra(bool power, IRElectraMode mode, IRElectraFan fan, int temperature, bool swing, bool sleep); private: IRsend* _remote; - uint64_t EncodeElectra(int power, int mode, int fan, int temperature, int swing, int sleep); - void addBit(unsigned int* p, int* i, char b); + + // Encodes specific A/C configuration to a number that describes + uint64_t encodeElectra(bool power, IRElectraMode mode, IRElectraFan fan, int temperature, bool swing, bool sleep); + + // Create the entire MARK-SPACE array containing the entire packet to send to the A/C + std::vector generateSignal(uint64_t code); +}; + +// Class to create MARK-SPACE array. An IR code is a digital signal, which +// means it's made out of 0's (space) and 1's (mark). This class helps +// create these kinds of signals. It has the ability to add marks and spaces +// at any time, and to add single bit using Manchester code to the signal. +// Once you added enough data to the array use the data() methods to get +// the raw data. Make sure that the first think you add to the array is +// at least one mark. +class MarkSpaceArray +{ +public: + // Initialize the array with a specific unit length. This is the clock used + // in the Manchester code. + MarkSpaceArray(uint16_t unitLengthInUsec); + + // Add a number of time units with mark. + void addMark(uint16_t units); + + // Add a number of time units with space. + void addSpace(uint16_t units); + + // Encodes the bit with IEEE 802.3 Manchester coding and adds it to the array + // A zero bit is one unit MARK and one unit SPACE + // a one bit is one unit SPACE and one unit MARK + void addBitWithManchesterCode(uint8_t bit); + + // Encodes a given number of bits from the given number bit by bit with + // IEEE 802.3 Manchester coding and adds it to the array. MSB first. + void addNumberWithManchesterCode(uint64_t bit, uint8_t numberOfBits); + + // Array containing timing for marks and spaces, starts with marks. + const std::vector data(); + +private: + // Add more time units to the current state. For example, if the array + // looks like this: { 1*UNIT, 1*UNIT } (equal to calling addMark(1) + // followed by addSpace(1), the current state is SPACE (currentState()==0) + // calling this function will change the array to { 1*UNIT, 2*UNIT }. + void addUnitsToCurrentState(uint16_t units); + + // Add more time to the other state. For example, if the array + // looks like this: { 1*UNIT, 1*UNIT } (equal to calling addMark(1) + // followed by addSpace(1), the current state is SPACE (currentState()==0) + // calling this function will change the array to { 1*UNIT, 1*UNIT, 1*UNIT } + void addUnitsToNextState(uint16_t units); + + // Returns 1 for mark, 0 for space. + uint8_t currentState(); + + uint16_t _unitLength; + std::vector _data; }; #endif \ No newline at end of file