Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pioneer send support #547

Merged
merged 15 commits into from
Oct 21, 2018
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions examples/IRMQTTServer/IRMQTTServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ void handleRoot() {
"<option selected='selected' value='3'>NEC</option>" // Default
"<option value='29'>Nikai</option>"
"<option value='5'>Panasonic</option>"
"<option value='50'>Pioneer</option>"
"<option value='1'>RC-5</option>"
"<option value='23'>RC-5X</option>"
"<option value='2'>RC-6</option>"
Expand Down Expand Up @@ -1448,6 +1449,14 @@ bool sendIRCode(int const ir_type, uint64_t const code, char const * code_str,
irsend.sendLutron(code, bits, repeat);
break;
#endif
#if SEND_PIONEER
case PIONEER: // 50
if (bits == 0)
bits = kPioneerBits;
irsend.sendPioneer(code, bits, repeat);
break;
#endif

#if SEND_LG
case LG2: // 51
if (bits == 0)
Expand Down
9 changes: 9 additions & 0 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,15 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
if (decodeCarrierAC(results))
return true;
#endif
#if DECODE_PIONEER
DPRINTLN("Attempting Pioneer decode");
// Try decodePioneer() before decodeNEC() because the protocols are
// similar in timings & structure, but the Pioneer one is much longer than the
// NEC protocol (2x32 bits vs 1x32 bits) so this one should be tried first to
// try to reduce false detection as a NEC packet.
if (decodePioneer(results))
return true;
#endif
#if DECODE_NEC
DPRINTLN("Attempting NEC decode");
if (decodeNEC(results))
Expand Down
5 changes: 5 additions & 0 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,11 @@ class IRrecv {
bool decodePanasonicAC(decode_results *results,
uint16_t nbits = kPanasonicAcBits, bool strict = true);
#endif
#if DECODE_PIONEER
bool decodePioneer(decode_results *results,
const uint16_t nbits = kPioneerBits,
const bool strict = true);
#endif
};

#endif // IRRECV_H_
4 changes: 4 additions & 0 deletions src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,9 @@
#define DECODE_PANASONIC_AC true
#define SEND_PANASONIC_AC true

#define DECODE_PIONEER true
#define SEND_PIONEER true
crankyoldgit marked this conversation as resolved.
Show resolved Hide resolved

#if (DECODE_ARGO || DECODE_DAIKIN || DECODE_FUJITSU_AC || DECODE_GREE || \
DECODE_KELVINATOR || DECODE_MITSUBISHI_AC || DECODE_TOSHIBA_AC || \
DECODE_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC || \
Expand Down Expand Up @@ -336,6 +339,7 @@ const uint16_t kPanasonicAcStateLength = 27;
const uint16_t kPanasonicAcStateShortLength = 16;
const uint16_t kPanasonicAcBits = kPanasonicAcStateLength * 8;
const uint16_t kPanasonicAcShortBits = kPanasonicAcStateShortLength * 8;
const uint16_t kPioneerBits = 64;
const uint16_t kProntoMinLength = 6;
const uint16_t kRC5RawBits = 14;
const uint16_t kRC5Bits = kRC5RawBits - 2;
Expand Down
3 changes: 3 additions & 0 deletions src/IRsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,9 @@ void IRsend::send(uint16_t type, uint64_t data, uint16_t nbits) {
#endif
#if SEND_GICABLE
case GICABLE: sendGICable(data, nbits); break;
#endif
#if SEND_PIONEER
case PIONEER: sendPioneer(data, nbits); break;
#endif
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,11 @@ void send(uint16_t type, uint64_t data, uint16_t nbits);
uint16_t nbytes = kPanasonicAcStateLength,
uint16_t repeat = kNoRepeat);
#endif
#if SEND_PIONEER
void sendPioneer(const uint64_t data, const uint16_t nbits = kPioneerBits,
const uint16_t repeat = kNoRepeat);
uint64_t encodePioneer(uint16_t address, uint16_t command);
#endif

protected:
#ifdef UNIT_TEST
Expand Down
1 change: 1 addition & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ std::string typeToString(const decode_type_t protocol,
case NIKAI: result = "NIKAI"; break;
case PANASONIC: result = "PANASONIC"; break;
case PANASONIC_AC: result = "PANASONIC_AC"; break;
case PIONEER: result = "PIONEER"; break;
case PRONTO: result = "PRONTO"; break;
case RAW: result = "RAW"; break;
case RC5: result = "RC5"; break;
Expand Down
31 changes: 3 additions & 28 deletions src/ir_NEC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright 2017 David Conran

#define __STDC_LIMIT_MACROS
#include "ir_NEC.h"
#include <stdint.h>
#include <algorithm>
#include "IRrecv.h"
Expand All @@ -16,34 +17,8 @@

// NEC originally added from https://github.com/shirriff/Arduino-IRremote/

// Constants
// Ref:
// http://www.sbprojects.com/knowledge/ir/nec.php
const uint16_t kNecTick = 560;
const uint16_t kNecHdrMarkTicks = 16;
const uint16_t kNecHdrMark = kNecHdrMarkTicks * kNecTick;
const uint16_t kNecHdrSpaceTicks = 8;
const uint16_t kNecHdrSpace = kNecHdrSpaceTicks * kNecTick;
const uint16_t kNecBitMarkTicks = 1;
const uint16_t kNecBitMark = kNecBitMarkTicks * kNecTick;
const uint16_t kNecOneSpaceTicks = 3;
const uint16_t kNecOneSpace = kNecOneSpaceTicks * kNecTick;
const uint16_t kNecZeroSpaceTicks = 1;
const uint16_t kNecZeroSpace = kNecZeroSpaceTicks * kNecTick;
const uint16_t kNecRptSpaceTicks = 4;
const uint16_t kNecRptSpace = kNecRptSpaceTicks * kNecTick;
const uint16_t kNecRptLength = 4;
const uint16_t kNecMinCommandLengthTicks = 193;
const uint32_t kNecMinCommandLength = kNecMinCommandLengthTicks * kNecTick;
const uint32_t kNecMinGap = kNecMinCommandLength -
(kNecHdrMark + kNecHdrSpace + kNECBits * (kNecBitMark + kNecOneSpace) +
kNecBitMark);
const uint16_t kNecMinGapTicks = kNecMinCommandLengthTicks -
(kNecHdrMarkTicks + kNecHdrSpaceTicks +
kNECBits * (kNecBitMarkTicks + kNecOneSpaceTicks) +
kNecBitMarkTicks);

#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO)
#if (SEND_NEC || SEND_SHERWOOD || SEND_AIWA_RC_T501 || SEND_SANYO || \
SEND_PIONEER)
// Send a raw NEC(Renesas) formatted message.
//
// Args:
Expand Down
45 changes: 45 additions & 0 deletions src/ir_NEC.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2009 Ken Shirriff
// Copyright 2017, 2018 David Conran

#ifndef IR_NEC_H_
#define IR_NEC_H_

#include <stdint.h>
#include "IRremoteESP8266.h"

// N N EEEEE CCCC
// NN N E C
// N N N EEE C
// N NN E C
// N N EEEEE CCCC

// NEC originally added from https://github.com/shirriff/Arduino-IRremote/

// Constants
// Ref:
// http://www.sbprojects.com/knowledge/ir/nec.php
const uint16_t kNecTick = 560;
const uint16_t kNecHdrMarkTicks = 16;
const uint16_t kNecHdrMark = kNecHdrMarkTicks * kNecTick;
const uint16_t kNecHdrSpaceTicks = 8;
const uint16_t kNecHdrSpace = kNecHdrSpaceTicks * kNecTick;
const uint16_t kNecBitMarkTicks = 1;
const uint16_t kNecBitMark = kNecBitMarkTicks * kNecTick;
const uint16_t kNecOneSpaceTicks = 3;
const uint16_t kNecOneSpace = kNecOneSpaceTicks * kNecTick;
const uint16_t kNecZeroSpaceTicks = 1;
const uint16_t kNecZeroSpace = kNecZeroSpaceTicks * kNecTick;
const uint16_t kNecRptSpaceTicks = 4;
const uint16_t kNecRptSpace = kNecRptSpaceTicks * kNecTick;
const uint16_t kNecRptLength = 4;
const uint16_t kNecMinCommandLengthTicks = 193;
const uint32_t kNecMinCommandLength = kNecMinCommandLengthTicks * kNecTick;
const uint32_t kNecMinGap = kNecMinCommandLength -
(kNecHdrMark + kNecHdrSpace + kNECBits * (kNecBitMark + kNecOneSpace) +
kNecBitMark);
const uint16_t kNecMinGapTicks = kNecMinCommandLengthTicks -
(kNecHdrMarkTicks + kNecHdrSpaceTicks +
kNECBits * (kNecBitMarkTicks + kNecOneSpaceTicks) +
kNecBitMarkTicks);

#endif // IR_NEC_H_
145 changes: 145 additions & 0 deletions src/ir_Pioneer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright 2009 Ken Shirriff
// Copyright 2017, 2018 David Conran
// Copyright 2018 Kamil Palczewski

#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include <algorithm>
#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"
crankyoldgit marked this conversation as resolved.
Show resolved Hide resolved
#include "ir_NEC.h"

// PPPP III OOO N N EEEE EEEE RRRR
// P P I O O NN N E E R R
// PPPP I O O N N N EEE EEE RRRR
// P I O O N NN E E R R
// P III OOO N N EEEE EEEE R RR

// Ref:
// http://adrian-kingston.com/IRFormatPioneer.htm

#if SEND_PIONEER
// Send a raw Pioneer formatted message.
//
// Args:
// data: The message to be sent.
// nbits: The number of bits of the message to be sent.
// Typically kPioneerBits.
// repeat: The number of times the command is to be repeated.
//
// Status: BETA / Expected to be working.
//
// Ref:
// http://adrian-kingston.com/IRFormatPioneer.htm
void IRsend::sendPioneer(const uint64_t data, const uint16_t nbits,
const uint16_t repeat) {
// If nbits is to big, or is odd, abort.
if (nbits > sizeof(data) * 8 || nbits % 2 == 1) return;

// send 1st part of the code
sendNEC(data >> (nbits / 2), nbits / 2, 0);
// send 2nd part of the code
sendNEC(data & ((1UL << (nbits / 2)) - 1), nbits / 2, repeat);
}

// Calculate the raw Pioneer data code based on two NEC sub-codes
// Args:
// address A 16-bit "published" NEC value.
// command: A 16-bit "published" NEC value.
// Returns:
// A raw 64-bit Pioneer message code.
//
// Status: BETA / Expected to work.
//
// Note:
// Address & Command can be take from a decode result OR from the spreadsheets
// located at:
// https://www.pioneerelectronics.com/PUSA/Support/Home-Entertainment-Custom-Install/IR+Codes/A+V+Receivers
// where the first part is considered the address,
// and the second the command.
// e.g.
// "A556+AF20" is an Address of 0xA556 & a Command of 0xAF20.
uint64_t IRsend::encodePioneer(const uint16_t address, const uint16_t command) {
return (((uint64_t) encodeNEC(address >> 8, address & 0xFF)) << 32) |
encodeNEC(command >> 8, command & 0xFF);
}
#endif // SEND_PIONEER

#if DECODE_PIONEER
// Decode the supplied Pioneer message.
//
// Args:
// results: Ptr to the data to decode and where to store the decode result.
// nbits: The number of data bits to expect. Typically kPioneerBits.
// strict: Flag indicating if we should perform strict matching.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: BETA / Should be working. (Self decodes & real examples)
//
bool IRrecv::decodePioneer(decode_results *results, const uint16_t nbits,
const bool strict) {
if (results->rawlen < 2 * (nbits + kHeader + kFooter) - 1)
return false; // Can't possibly be a valid Pioneer message.
if (strict && nbits != kPioneerBits)
return false; // Not strictly an Pioneer message.

uint64_t data = 0;
uint16_t offset = kStartOffset;

for (uint16_t section = 0; section < 2; section++) {
// Header
if (!matchMark(results->rawbuf[offset], kNecHdrMark)) return false;
// Calculate how long the lowest tick time is based on the header mark.
uint32_t mark_tick = results->rawbuf[offset++] * kRawTick /
kNecHdrMarkTicks;
if (!matchSpace(results->rawbuf[offset], kNecHdrSpace)) return false;
// Calculate how long the common tick time is based on the header space.
uint32_t space_tick = results->rawbuf[offset++] * kRawTick /
kNecHdrSpaceTicks;
//
// Data
match_result_t data_result = matchData(&(results->rawbuf[offset]),
nbits / 2,
kNecBitMarkTicks * mark_tick,
kNecOneSpaceTicks * space_tick,
kNecBitMarkTicks * mark_tick,
kNecZeroSpaceTicks * space_tick);
if (data_result.success == false) return false;
uint8_t command = data_result.data >> 8;
uint8_t command_inverted = data_result.data;
uint8_t address = data_result.data >> 24;
uint8_t address_inverted = data_result.data >> 16;
// Compliance
if (strict) {
if (command != (command_inverted ^ 0xFF))
return false; // Command integrity failed.
if (address != (address_inverted ^ 0xFF))
return false; // Address integrity failed.
}
data = (data << (nbits / 2)) + data_result.data;
offset += data_result.used;
// NEC-like commands and addresses are technically in LSB first order so the
// final versions have to be reversed.
uint16_t code = reverseBits((command << 8) + address, 16);
if (section)
results->command = code;
else
results->address = code;

// Footer
if (!matchMark(results->rawbuf[offset++], kNecBitMarkTicks * mark_tick))
return false;
if (offset < results->rawlen &&
!matchAtLeast(results->rawbuf[offset++], kNecMinGapTicks * space_tick))
return false;
}

// Success
results->bits = nbits;
results->value = data;
results->decode_type = PIONEER;
return true;
}
#endif // DECODE_PIONEER
27 changes: 19 additions & 8 deletions test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ TESTS = IRutils_test IRsend_test ir_NEC_test ir_GlobalCache_test \
ir_Aiwa_test ir_Denon_test ir_Sanyo_test ir_Daikin_test ir_Coolix_test \
ir_Gree_test IRrecv_test ir_Pronto_test ir_Fujitsu_test ir_Nikai_test \
ir_Toshiba_test ir_Midea_test ir_Magiquest_test ir_Lasertag_test \
ir_Carrier_test ir_Haier_test ir_Hitachi_test ir_GICable_test \
ir_Whirlpool_test ir_Lutron_test ir_Electra_test
ir_Carrier_test ir_Haier_test ir_Hitachi_test ir_GICable_test \
ir_Whirlpool_test ir_Lutron_test ir_Electra_test ir_Pioneer_test

# All Google Test headers. Usually you shouldn't change this
# definition.
Expand Down Expand Up @@ -74,11 +74,12 @@ GTEST_SRCS_ = $(GTEST_DIR)/src/*.cc $(GTEST_DIR)/src/*.h $(GTEST_HEADERS)

# All the IR protocol object files.
PROTOCOLS = ir_NEC.o ir_Sony.o ir_Samsung.o ir_JVC.o ir_RCMM.o ir_RC5_RC6.o \
ir_LG.o ir_Mitsubishi.o ir_Fujitsu.o ir_Sharp.o ir_Sanyo.o ir_Denon.o ir_Dish.o \
ir_Panasonic.o ir_Whynter.o ir_Coolix.o ir_Aiwa.o ir_Sherwood.o \
ir_Kelvinator.o ir_Daikin.o ir_Gree.o ir_Pronto.o ir_Nikai.o ir_Toshiba.o \
ir_Midea.o ir_Magiquest.o ir_Lasertag.o ir_Carrier.o ir_Haier.o \
ir_Hitachi.o ir_GICable.o ir_Whirlpool.o ir_Lutron.o ir_Electra.o
ir_LG.o ir_Mitsubishi.o ir_Fujitsu.o ir_Sharp.o ir_Sanyo.o ir_Denon.o ir_Dish.o \
ir_Panasonic.o ir_Whynter.o ir_Coolix.o ir_Aiwa.o ir_Sherwood.o \
ir_Kelvinator.o ir_Daikin.o ir_Gree.o ir_Pronto.o ir_Nikai.o ir_Toshiba.o \
ir_Midea.o ir_Magiquest.o ir_Lasertag.o ir_Carrier.o ir_Haier.o \
ir_Hitachi.o ir_GICable.o ir_Whirlpool.o ir_Lutron.o ir_Electra.o \
ir_Pioneer.o
# All the IR Protocol header files.
PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \
$(USER_DIR)/ir_Gree.h \
Expand All @@ -90,6 +91,7 @@ PROTOCOLS_H = $(USER_DIR)/ir_Argo.h \
$(USER_DIR)/ir_Daikin.h \
$(USER_DIR)/ir_Kelvinator.h \
$(USER_DIR)/ir_Mitsubishi.h \
$(USER_DIR)/ir_NEC.h \
$(USER_DIR)/ir_Samsung.h \
$(USER_DIR)/ir_Trotec.h \
$(USER_DIR)/ir_Fujitsu.h \
Expand Down Expand Up @@ -158,7 +160,7 @@ IRrecv_test.o : IRrecv_test.cpp $(USER_DIR)/IRsend.h $(USER_DIR)/IRrecv.h IRsend
IRrecv_test : IRrecv_test.o $(COMMON_OBJ)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@

ir_NEC.o : $(USER_DIR)/ir_NEC.cpp $(COMMON_DEPS)
ir_NEC.o : $(USER_DIR)/ir_NEC.cpp $(USER_DIR)/ir_NEC.h $(COMMON_DEPS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_NEC.cpp

ir_NEC_test.o : ir_NEC_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS)
Expand Down Expand Up @@ -472,3 +474,12 @@ ir_Electra_test.o : ir_Electra_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS)

ir_Electra_test : $(COMMON_OBJ) ir_Electra_test.o
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@

ir_Pioneer.o : $(USER_DIR)/ir_Pioneer.cpp $(COMMON_DEPS) $(GTEST_HEADERS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $(USER_DIR)/ir_Pioneer.cpp

ir_Pioneer_test.o : ir_Pioneer_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(USER_DIR) -c ir_Pioneer_test.cpp

ir_Pioneer_test : $(COMMON_OBJ) ir_Pioneer_test.o
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -lpthread $^ -o $@
Loading