323 lines
14 KiB
C++
323 lines
14 KiB
C++
/*
|
|
* ir_Kaseikyo.hpp
|
|
*
|
|
* Contains functions for receiving and sending Kaseikyo/Panasonic IR Protocol in "raw" and standard format with 16 bit address + 8 bit command
|
|
*
|
|
* This file is part of Arduino-IRremote https://github.com/Arduino-IRremote/Arduino-IRremote.
|
|
*
|
|
************************************************************************************
|
|
* MIT License
|
|
*
|
|
* Copyright (c) 2020-2023 Armin Joachimsmeyer
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is furnished
|
|
* to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
|
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
************************************************************************************
|
|
*/
|
|
#ifndef _IR_KASEIKYO_HPP
|
|
#define _IR_KASEIKYO_HPP
|
|
|
|
#if defined(DEBUG) && !defined(LOCAL_DEBUG)
|
|
#define LOCAL_DEBUG
|
|
#else
|
|
//#define LOCAL_DEBUG // This enables debug output only for this file
|
|
#endif
|
|
|
|
/** \addtogroup Decoder Decoders and encoders for different protocols
|
|
* @{
|
|
*/
|
|
//==============================================================================
|
|
// K K AA SSS EEEE III K K Y Y OOO
|
|
// K K A A S E I K K Y Y O O
|
|
// KK AAAA SSS EEE I KK Y O O
|
|
// K K A A S E I K K Y O O
|
|
// K K A A SSSS EEEE III K K Y OOO
|
|
//==============================================================================
|
|
//==============================================================================
|
|
// PPPP AAA N N AAA SSSS OOO N N IIIII CCCC
|
|
// P P A A NN N A A S O O NN N I C
|
|
// PPPP AAAAA N N N AAAAA SSS O O N N N I C
|
|
// P A A N NN A A S O O N NN I C
|
|
// P A A N N A A SSSS OOO N N IIIII CCCC
|
|
//==============================================================================
|
|
/*
|
|
Protocol=Panasonic Address=0xFF1 Command=0x76 Raw-Data=0x9976FF10 48 bits LSB first
|
|
+3450,-1700
|
|
+ 450,- 400 + 500,-1250 + 450,- 400 + 500,- 400
|
|
+ 450,- 400 + 400,- 450 + 500,- 350 + 450,- 450
|
|
+ 450,- 400 + 450,- 400 + 500,- 400 + 450,- 400
|
|
+ 450,- 400 + 500,-1250 + 450,- 400 + 500,- 350
|
|
+ 500,- 400 + 450,- 400 + 450,- 450 + 450,- 400
|
|
+ 450,-1250 + 500,- 400 + 450,- 400 + 450,- 400
|
|
+ 450,-1300 + 450,-1250 + 450,-1300 + 400,-1300
|
|
+ 450,-1300 + 450,-1250 + 450,-1250 + 500,-1250
|
|
+ 450,- 450 + 450,-1250 + 450,-1250 + 500,- 400
|
|
+ 450,-1250 + 450,-1300 + 450,-1250 + 450,- 450
|
|
+ 450,-1250 + 450,- 400 + 450,- 400 + 500,-1250
|
|
+ 450,-1250 + 450,- 400 + 500,- 400 + 450,-1250
|
|
+ 450
|
|
Sum: 64300
|
|
*/
|
|
// http://www.hifi-remote.com/johnsfine/DecodeIR.html#Panasonic
|
|
// http://www.hifi-remote.com/johnsfine/DecodeIR.html#Kaseikyo
|
|
// LSB first
|
|
// The first two (8-bit) bytes contains the vendor code.
|
|
// The next 4 bit is VendorID parity.
|
|
// The last byte is parity (XOR) of the 3 bytes before.
|
|
// There are multiple interpretations of the next fields:
|
|
// IRP: {37k,432}<1,-1|1,-3>(8,-4,M:8,N:8,X:4,D:4,S:8,F:8,G:8,1,-173)+ {X=M:4:0^M:4:4^N:4:0^N:4:4}
|
|
// 1. interpretation: 4 bit Device, 8 bitSubdevice and 8 bit function.
|
|
// 0_______ 1_______ 2______ 3_______ 4_______ 5_______
|
|
// 01234567 89ABCDEF 01234567 01234567 01234567 01234567
|
|
// 01000000 00100100 0110Dev_ Sub_Dev_ Fun____ XOR( B2, B3, B4) - Byte 0,1 and vendor parity showing Panasonic vendor code 0x2002.
|
|
// 1. interpretation: <start bit><VendorID:16><VendorID parity:4><Device:4><Subdevice:8><Function:8><Parity:8><stop bit>
|
|
// see: http://www.remotecentral.com/cgi-bin/mboard/rc-pronto/thread.cgi?26152
|
|
// 2. interpretation (Flipper Zero style): <start bit><VendorID:16><VendorID parity:4><Genre1:4><Genre2:4><Command:10><ID:2><Parity:8><stop bit>
|
|
// see: https://www.mikrocontroller.net/articles/IRMP_-_english#KASEIKYO
|
|
// Implemented is Samsung style: <start bit><VendorID:16><VendorID parity:4><Address:12><Command:8><Parity of VendorID parity, Address and Command:8><stop bit>
|
|
// which is derived from Samsung remotes and may not be optimal for Denon kind of Kaseikyo protokol usage.
|
|
//
|
|
#define KASEIKYO_VENDOR_ID_BITS 16
|
|
#define KASEIKYO_VENDOR_ID_PARITY_BITS 4
|
|
#define KASEIKYO_ADDRESS_BITS 12
|
|
#define KASEIKYO_COMMAND_BITS 8
|
|
#define KASEIKYO_PARITY_BITS 8
|
|
#define KASEIKYO_BITS (KASEIKYO_VENDOR_ID_BITS + KASEIKYO_VENDOR_ID_PARITY_BITS + KASEIKYO_ADDRESS_BITS + KASEIKYO_COMMAND_BITS + KASEIKYO_PARITY_BITS) // 48
|
|
#define KASEIKYO_UNIT 432 // 16 pulses of 37 kHz (432,432432) - Pronto 0x70 | 0x10
|
|
|
|
#define KASEIKYO_HEADER_MARK (8 * KASEIKYO_UNIT) // 3456
|
|
#define KASEIKYO_HEADER_SPACE (4 * KASEIKYO_UNIT) // 1728
|
|
|
|
#define KASEIKYO_BIT_MARK KASEIKYO_UNIT
|
|
#define KASEIKYO_ONE_SPACE (3 * KASEIKYO_UNIT) // 1296
|
|
#define KASEIKYO_ZERO_SPACE KASEIKYO_UNIT
|
|
|
|
#define KASEIKYO_AVERAGE_DURATION 56000
|
|
#define KASEIKYO_REPEAT_PERIOD 130000
|
|
#define KASEIKYO_REPEAT_DISTANCE (KASEIKYO_REPEAT_PERIOD - KASEIKYO_AVERAGE_DURATION) // 74 ms
|
|
#define KASEIKYO_MAXIMUM_REPEAT_DISTANCE (KASEIKYO_REPEAT_DISTANCE + (KASEIKYO_REPEAT_DISTANCE / 4)) // Just a guess
|
|
|
|
#define PANASONIC_VENDOR_ID_CODE 0x2002
|
|
#define DENON_VENDOR_ID_CODE 0x3254
|
|
#define MITSUBISHI_VENDOR_ID_CODE 0xCB23
|
|
#define SHARP_VENDOR_ID_CODE 0x5AAA
|
|
#define JVC_VENDOR_ID_CODE 0x0103
|
|
|
|
struct PulseDistanceWidthProtocolConstants KaseikyoProtocolConstants = { KASEIKYO, KASEIKYO_KHZ, KASEIKYO_HEADER_MARK,
|
|
KASEIKYO_HEADER_SPACE, KASEIKYO_BIT_MARK, KASEIKYO_ONE_SPACE, KASEIKYO_BIT_MARK, KASEIKYO_ZERO_SPACE, PROTOCOL_IS_LSB_FIRST
|
|
, (KASEIKYO_REPEAT_PERIOD / MICROS_IN_ONE_MILLI), NULL };
|
|
|
|
/************************************
|
|
* Start of send and decode functions
|
|
************************************/
|
|
|
|
/**
|
|
* Address can be interpreted as sub-device << 4 + 4 bit device
|
|
*/
|
|
void IRsend::sendKaseikyo(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats, uint16_t aVendorCode) {
|
|
// Set IR carrier frequency
|
|
enableIROut (KASEIKYO_KHZ); // 37 kHz
|
|
|
|
// Vendor Parity
|
|
uint8_t tVendorParity = aVendorCode ^ (aVendorCode >> 8);
|
|
tVendorParity = (tVendorParity ^ (tVendorParity >> 4)) & 0xF;
|
|
|
|
#if __INT_WIDTH__ < 32
|
|
LongUnion tSendValue;
|
|
// Compute parity
|
|
tSendValue.UWord.LowWord = (aAddress << KASEIKYO_VENDOR_ID_PARITY_BITS) | tVendorParity; // set low nibble with vendor parity
|
|
tSendValue.UBytes[2] = aCommand;
|
|
tSendValue.UBytes[3] = aCommand ^ tSendValue.UBytes[0] ^ tSendValue.UBytes[1]; // 8 bit parity of 3 bytes command, address and vendor parity
|
|
IRRawDataType tRawKaseikyoData[2];
|
|
tRawKaseikyoData[0] = (uint32_t) tSendValue.UWord.LowWord << 16 | aVendorCode; // LSB of tRawKaseikyoData[0] is sent first
|
|
tRawKaseikyoData[1] = tSendValue.UWord.HighWord;
|
|
sendPulseDistanceWidthFromArray(&KaseikyoProtocolConstants, &tRawKaseikyoData[0], KASEIKYO_BITS, aNumberOfRepeats);
|
|
#else
|
|
LongLongUnion tSendValue;
|
|
tSendValue.UWords[0] = aVendorCode;
|
|
// Compute parity
|
|
tSendValue.UWords[1] = (aAddress << KASEIKYO_VENDOR_ID_PARITY_BITS) | tVendorParity; // set low nibble to parity
|
|
tSendValue.UBytes[4] = aCommand;
|
|
tSendValue.UBytes[5] = aCommand ^ tSendValue.UBytes[2] ^ tSendValue.UBytes[3]; // Parity
|
|
sendPulseDistanceWidth(&KaseikyoProtocolConstants, tSendValue.ULongLong, KASEIKYO_BITS, aNumberOfRepeats);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Stub using Kaseikyo with PANASONIC_VENDOR_ID_CODE
|
|
*/
|
|
void IRsend::sendPanasonic(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
|
|
sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, PANASONIC_VENDOR_ID_CODE);
|
|
}
|
|
|
|
/**
|
|
* Stub using Kaseikyo with DENON_VENDOR_ID_CODE
|
|
*/
|
|
void IRsend::sendKaseikyo_Denon(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
|
|
sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, DENON_VENDOR_ID_CODE);
|
|
}
|
|
|
|
/**
|
|
* Stub using Kaseikyo with MITSUBISHI_VENDOR_ID_CODE
|
|
*/
|
|
void IRsend::sendKaseikyo_Mitsubishi(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
|
|
sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, MITSUBISHI_VENDOR_ID_CODE);
|
|
}
|
|
|
|
/**
|
|
* Stub using Kaseikyo with SHARP_VENDOR_ID_CODE
|
|
*/
|
|
void IRsend::sendKaseikyo_Sharp(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
|
|
sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, SHARP_VENDOR_ID_CODE);
|
|
}
|
|
|
|
/**
|
|
* Stub using Kaseikyo with JVC_VENDOR_ID_CODE
|
|
*/
|
|
void IRsend::sendKaseikyo_JVC(uint16_t aAddress, uint8_t aCommand, int_fast8_t aNumberOfRepeats) {
|
|
sendKaseikyo(aAddress, aCommand, aNumberOfRepeats, JVC_VENDOR_ID_CODE);
|
|
}
|
|
|
|
/*
|
|
* Tested with my Panasonic DVD/TV remote
|
|
*/
|
|
bool IRrecv::decodeKaseikyo() {
|
|
|
|
decode_type_t tProtocol;
|
|
// Check we have enough data (96 + 4) 4 for initial gap, start bit mark and space + stop bit mark
|
|
if (decodedIRData.rawlen != ((2 * KASEIKYO_BITS) + 4)) {
|
|
IR_DEBUG_PRINT(F("Kaseikyo: "));
|
|
IR_DEBUG_PRINT(F("Data length="));
|
|
IR_DEBUG_PRINT(decodedIRData.rawlen);
|
|
IR_DEBUG_PRINTLN(F(" is not 100"));
|
|
return false;
|
|
}
|
|
|
|
if (!checkHeader(&KaseikyoProtocolConstants)) {
|
|
return false;
|
|
}
|
|
|
|
// decode first 16 Vendor ID bits
|
|
if (!decodePulseDistanceWidthData(&KaseikyoProtocolConstants, KASEIKYO_VENDOR_ID_BITS)) {
|
|
#if defined(LOCAL_DEBUG)
|
|
Serial.print(F("Kaseikyo: "));
|
|
Serial.println(F("Vendor ID decode failed"));
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
uint16_t tVendorId = decodedIRData.decodedRawData;
|
|
if (tVendorId == PANASONIC_VENDOR_ID_CODE) {
|
|
tProtocol = PANASONIC;
|
|
} else if (tVendorId == SHARP_VENDOR_ID_CODE) {
|
|
tProtocol = KASEIKYO_SHARP;
|
|
} else if (tVendorId == DENON_VENDOR_ID_CODE) {
|
|
tProtocol = KASEIKYO_DENON;
|
|
} else if (tVendorId == JVC_VENDOR_ID_CODE) {
|
|
tProtocol = KASEIKYO_JVC;
|
|
} else if (tVendorId == MITSUBISHI_VENDOR_ID_CODE) {
|
|
tProtocol = KASEIKYO_MITSUBISHI;
|
|
} else {
|
|
tProtocol = KASEIKYO;
|
|
}
|
|
|
|
// Vendor Parity
|
|
uint8_t tVendorParity = tVendorId ^ (tVendorId >> 8);
|
|
tVendorParity = (tVendorParity ^ (tVendorParity >> 4)) & 0xF;
|
|
|
|
/*
|
|
* Decode next 32 bits, 8 VendorID parity parity + 12 address (device and subdevice) + 8 command + 8 parity
|
|
*/
|
|
if (!decodePulseDistanceWidthData(&KaseikyoProtocolConstants,
|
|
KASEIKYO_VENDOR_ID_PARITY_BITS + KASEIKYO_ADDRESS_BITS + KASEIKYO_COMMAND_BITS + KASEIKYO_PARITY_BITS,
|
|
3 + (2 * KASEIKYO_VENDOR_ID_BITS))) {
|
|
#if defined(LOCAL_DEBUG)
|
|
Serial.print(F("Kaseikyo: "));
|
|
Serial.println(F("VendorID parity, address, command + parity decode failed"));
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
// Success
|
|
// decodedIRData.flags = IRDATA_FLAGS_IS_LSB_FIRST; // Not required, since this is the start value
|
|
LongUnion tValue;
|
|
tValue.ULong = decodedIRData.decodedRawData;
|
|
#if __INT_WIDTH__ >= 32
|
|
// workaround until complete refactoring for 64 bit
|
|
decodedIRData.decodedRawData = (decodedIRData.decodedRawData << 16) | tVendorId; // store all 48 bits in decodedRawData
|
|
#endif
|
|
decodedIRData.address = (tValue.UWord.LowWord >> KASEIKYO_VENDOR_ID_PARITY_BITS); // remove 4 bit vendor parity
|
|
decodedIRData.command = tValue.UByte.MidHighByte;
|
|
uint8_t tParity = tValue.UByte.LowByte ^ tValue.UByte.MidLowByte ^ tValue.UByte.MidHighByte;
|
|
|
|
if (tVendorParity != (tValue.UByte.LowByte & 0xF)) {
|
|
decodedIRData.flags = IRDATA_FLAGS_PARITY_FAILED | IRDATA_FLAGS_IS_LSB_FIRST;
|
|
|
|
#if defined(LOCAL_DEBUG)
|
|
Serial.print(F("Kaseikyo: "));
|
|
Serial.print(F("4 bit VendorID parity is not correct. Expected=0x"));
|
|
Serial.print(tVendorParity, HEX);
|
|
Serial.print(F(" received=0x"));
|
|
Serial.print(decodedIRData.decodedRawData, HEX);
|
|
Serial.print(F(" VendorID=0x"));
|
|
Serial.println(tVendorId, HEX);
|
|
#endif
|
|
}
|
|
|
|
if (tProtocol == KASEIKYO) {
|
|
decodedIRData.flags |= IRDATA_FLAGS_EXTRA_INFO;
|
|
decodedIRData.extra = tVendorId; // Store (unknown) vendor ID
|
|
}
|
|
|
|
if (tValue.UByte.HighByte != tParity) {
|
|
decodedIRData.flags |= IRDATA_FLAGS_PARITY_FAILED;
|
|
|
|
#if defined(LOCAL_DEBUG)
|
|
Serial.print(F("Kaseikyo: "));
|
|
Serial.print(F("8 bit parity is not correct. Expected=0x"));
|
|
Serial.print(tParity, HEX);
|
|
Serial.print(F(" received=0x"));
|
|
Serial.print(decodedIRData.decodedRawData >> KASEIKYO_COMMAND_BITS, HEX);
|
|
Serial.print(F(" address=0x"));
|
|
Serial.print(decodedIRData.address, HEX);
|
|
Serial.print(F(" command=0x"));
|
|
Serial.println(decodedIRData.command, HEX);
|
|
#endif
|
|
}
|
|
|
|
decodedIRData.numberOfBits = KASEIKYO_BITS;
|
|
decodedIRData.protocol = tProtocol;
|
|
|
|
// check for repeat
|
|
checkForRepeatSpaceTicksAndSetFlag(KASEIKYO_MAXIMUM_REPEAT_DISTANCE / MICROS_PER_TICK);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Removed void IRsend::sendPanasonic(uint16_t aAddress, uint32_t aData)
|
|
* and bool IRrecv::decodePanasonicMSB(decode_results *aResults)
|
|
* since their implementations were wrong (wrong length), and nobody recognized it
|
|
*/
|
|
|
|
/** @}*/
|
|
#if defined(LOCAL_DEBUG)
|
|
#undef LOCAL_DEBUG
|
|
#endif
|
|
#endif // _IR_KASEIKYO_HPP
|