Skip to content

Commit

Permalink
Pioneer support (#547)
Browse files Browse the repository at this point in the history
Support for sending & decoding Pioneer 64bit codes.
  Pioneer codes are essentially two NEC 32-bit codes back to back.

* Unit tests
* Examples updated.
* Tests on real devices. Appears to work fine.
  • Loading branch information
kpalczewski authored and crankyoldgit committed Oct 21, 2018
1 parent ec75c50 commit 75c7fe3
Show file tree
Hide file tree
Showing 13 changed files with 400 additions and 41 deletions.
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 @@ -1471,6 +1472,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 @@ -328,6 +328,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
#if DECODE_MWM
bool decodeMWM(decode_results *results, uint16_t nbits = 24,
bool strict = true);
Expand Down
4 changes: 4 additions & 0 deletions src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@
#define DECODE_MWM true
#define SEND_MWM true

#define DECODE_PIONEER true
#define SEND_PIONEER true

#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 @@ -340,6 +343,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
#if SEND_MWM
void sendMWM(unsigned char data[], uint16_t nbytes,
uint16_t repeat = kNoRepeat);
Expand Down
1 change: 1 addition & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,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"
#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 & (((uint64_t) 1 << (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
Loading

0 comments on commit 75c7fe3

Please sign in to comment.