Skip to content

Commit

Permalink
Initial support of Hitachi A/C messages. (#445)
Browse files Browse the repository at this point in the history
* Initial support of Hitachi A/C messages.
* Add send and decode routines for Hitachi A/C protocol.
* There is no interpretation of the message as yet. i.e. Just dumb decoding.
* Unit tests for sendHitachiAC() and decodeHitachiAC()
* Add needed changes to example & tool code.
* Change new maximum state size to HITACHI_AC_STATE_LENGTH (28 bytes)

[Note] Assuming that this is a 38kHz protocol. Can't test as I don't have a device.
Ref: Issue #417
  • Loading branch information
crankyoldgit authored Apr 7, 2018
1 parent ab2b267 commit 5fcd9fe
Show file tree
Hide file tree
Showing 10 changed files with 486 additions and 7 deletions.
9 changes: 9 additions & 0 deletions examples/IRMQTTServer/IRMQTTServer.ino
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,9 @@ void parseStringAndSendAirCon(const uint16_t irType, const String str) {
case HAIER_AC:
stateSize = HAIER_AC_STATE_LENGTH;
break;
case HITACHI_AC:
stateSize = HITACHI_AC_STATE_LENGTH;
break;
default: // Not a protocol we expected. Abort.
debug("Unexpected AirCon protocol detected. Ignoring.");
return;
Expand Down Expand Up @@ -564,6 +567,11 @@ void parseStringAndSendAirCon(const uint16_t irType, const String str) {
case HAIER_AC:
irsend.sendHaierAC(reinterpret_cast<uint8_t *>(state));
break;
#endif
#if SEND_HITACHI_AC
case HITACHI_AC:
irsend.sendHitachiAC(reinterpret_cast<uint8_t *>(state));
break;
#endif
}
}
Expand Down Expand Up @@ -1082,6 +1090,7 @@ void sendIRCode(int const ir_type, uint64_t const code, char const * code_str,
case TOSHIBA_AC: // 32
case FUJITSU_AC: // 33
case HAIER_AC: // 38
case HITACHI_AC: // 40
parseStringAndSendAirCon(ir_type, code_str);
break;
#if SEND_DENON
Expand Down
5 changes: 5 additions & 0 deletions src/IRrecv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,11 @@ bool IRrecv::decode(decode_results *results, irparams_t *save) {
if (decodeHaierAC(results))
return true;
#endif
#if DECODE_HITACHI_AC
DPRINTLN("Attempting Hitachi AC decode");
if (decodeHitachiAC(results))
return true;
#endif
#if DECODE_HASH
// decodeHash returns a hash on any input.
// Thus, it needs to be last in the list.
Expand Down
8 changes: 6 additions & 2 deletions src/IRrecv.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
#define FNV_PRIME_32 16777619UL
#define FNV_BASIS_32 2166136261UL

// Daikin is the current largest state size (by far).
#define STATE_SIZE_MAX DAIKIN_COMMAND_LENGTH
// Hitachi is the current largest state size.
#define STATE_SIZE_MAX HITACHI_AC_STATE_LENGTH

// Types
// information for the interrupt handler
Expand Down Expand Up @@ -274,6 +274,10 @@ class IRrecv {
bool decodeHaierAC(decode_results *results,
uint16_t nbits = HAIER_AC_BITS, bool strict = true);
#endif
#if DECODE_HAIER_AC
bool decodeHitachiAC(decode_results *results,
uint16_t nbits = HITACHI_AC_BITS, bool strict = true);
#endif
};

#endif // IRRECV_H_
10 changes: 8 additions & 2 deletions src/IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,12 @@
#define DECODE_HAIER_AC true
#define SEND_HAIER_AC true

#define DECODE_HITACHI_AC true
#define SEND_HITACHI_AC 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_TROTEC || DECODE_HAIER_AC || DECODE_HITACHI_AC)
#define DECODE_AC true // We need some common infrastructure for decoding A/Cs.
#else
#define DECODE_AC false // We don't need that infrastructure.
Expand Down Expand Up @@ -217,7 +220,8 @@ enum decode_type_t {
LASERTAG,
CARRIER_AC,
HAIER_AC,
MITSUBISHI2
MITSUBISHI2,
HITACHI_AC
};

// Message lengths & required repeat values
Expand All @@ -239,6 +243,8 @@ enum decode_type_t {
#define GREE_BITS (GREE_STATE_LENGTH * 8)
#define HAIER_AC_STATE_LENGTH 9U
#define HAIER_AC_BITS (HAIER_AC_STATE_LENGTH * 8)
#define HITACHI_AC_STATE_LENGTH 28U
#define HITACHI_AC_BITS (HITACHI_AC_STATE_LENGTH * 8)
#define JVC_BITS 16U
#define KELVINATOR_STATE_LENGTH 16U
#define KELVINATOR_BITS (KELVINATOR_STATE_LENGTH * 8)
Expand Down
5 changes: 5 additions & 0 deletions src/IRsend.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ void send(uint16_t type, uint64_t data, uint16_t nbits);
uint16_t nbytes = HAIER_AC_STATE_LENGTH,
uint16_t repeat = 0);
#endif
#if SEND_HITACHI_AC
void sendHitachiAC(unsigned char data[],
uint16_t nbytes = HITACHI_AC_STATE_LENGTH,
uint16_t repeat = 0);
#endif

protected:
#ifdef UNIT_TEST
Expand Down
3 changes: 3 additions & 0 deletions src/IRutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ std::string typeToString(const decode_type_t protocol,
case GLOBALCACHE: result = "GLOBALCACHE"; break;
case GREE: result = "GREE"; break;
case HAIER_AC: result = "HAIER_AC"; break;
case HITACHI_AC: result = "HITACHI_AC"; break;
case JVC: result = "JVC"; break;
case KELVINATOR: result = "KELVINATOR"; break;
case LG: result = "LG"; break;
Expand Down Expand Up @@ -151,7 +152,9 @@ bool hasACState(const decode_type_t protocol) {
case FUJITSU_AC:
case GREE:
case HAIER_AC:
case HITACHI_AC:
case KELVINATOR:
case MITSUBISHI_AC:
case TOSHIBA_AC:
return true;
default:
Expand Down
119 changes: 119 additions & 0 deletions src/ir_Hitachi.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Copyright 2018 David Conran
//
// Code to emulate Hitachi protocol compatible devices.
// Should be compatible with:
// * Hitachi RAS-35THA6 remote
//

#include <algorithm>
#ifndef ARDUINO
#include <string>
#endif
#include "IRremoteESP8266.h"
#include "IRrecv.h"
#include "IRsend.h"
#include "IRutils.h"

// HH HH IIIII TTTTTTT AAA CCCCC HH HH IIIII
// HH HH III TTT AAAAA CC C HH HH III
// HHHHHHH III TTT AA AA CC HHHHHHH III
// HH HH III TTT AAAAAAA CC C HH HH III
// HH HH IIIII TTT AA AA CCCCC HH HH IIIII

// Constants
// Ref: https://github.com/markszabo/IRremoteESP8266/issues/417
#define HITACHI_AC_HDR_MARK 3300U
#define HITACHI_AC_HDR_SPACE 1700U
#define HITACHI_AC_BIT_MARK 400U
#define HITACHI_AC_ONE_SPACE 1250U
#define HITACHI_AC_ZERO_SPACE 500U
#define HITACHI_AC_MIN_GAP 100000U // Completely made up value.

#if SEND_HITACHI_AC
// Send a Hitachi A/C message.
//
// Args:
// data: An array of bytes containing the IR command.
// nbytes: Nr. of bytes of data in the array. (>=HITACHI_AC_STATE_LENGTH)
// repeat: Nr. of times the message is to be repeated. (Default = 0).
//
// Status: ALPHA / Untested.
//
// Ref:
// https://github.com/markszabo/IRremoteESP8266/issues/417
void IRsend::sendHitachiAC(unsigned char data[], uint16_t nbytes,
uint16_t repeat) {
if (nbytes < HITACHI_AC_STATE_LENGTH)
return; // Not enough bytes to send a proper message.
sendGeneric(HITACHI_AC_HDR_MARK, HITACHI_AC_HDR_SPACE,
HITACHI_AC_BIT_MARK, HITACHI_AC_ONE_SPACE,
HITACHI_AC_BIT_MARK, HITACHI_AC_ZERO_SPACE,
HITACHI_AC_BIT_MARK, HITACHI_AC_MIN_GAP,
data, nbytes, 38, true, repeat, 50);
}
#endif // SEND_HITACHI_AC

#if DECODE_HITACHI_AC
// Decode the supplied Hitachi A/C 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 HITACHI_AC_BITS.
// strict: Flag indicating if we should perform strict matching.
// Returns:
// boolean: True if it can decode it, false if it can't.
//
// Status: ALPHA / Untested.
bool IRrecv::decodeHitachiAC(decode_results *results, uint16_t nbits,
bool strict) {
if (results->rawlen < 2 * nbits + HEADER + FOOTER - 1)
return false; // Can't possibly be a valid HitachiAC message.
if (strict && nbits != HITACHI_AC_BITS)
return false; // Not strictly a HitachiAC message.

uint16_t offset = OFFSET_START;
uint16_t dataBitsSoFar = 0;
match_result_t data_result;

// Header
if (!matchMark(results->rawbuf[offset++], HITACHI_AC_HDR_MARK))
return false;
if (!matchSpace(results->rawbuf[offset++], HITACHI_AC_HDR_SPACE))
return false;

// Data
// Keep reading bytes until we either run out of message or state to fill.
for (uint16_t i = 0;
offset <= results->rawlen - 16 && i < HITACHI_AC_STATE_LENGTH;
i++, dataBitsSoFar += 8, offset += data_result.used) {
data_result = matchData(&(results->rawbuf[offset]), 8,
HITACHI_AC_BIT_MARK,
HITACHI_AC_ONE_SPACE,
HITACHI_AC_BIT_MARK,
HITACHI_AC_ZERO_SPACE);
if (data_result.success == false) break; // Fail
results->state[i] = (uint8_t) data_result.data;
}

// Footer.
if (!matchMark(results->rawbuf[offset++], HITACHI_AC_BIT_MARK))
return false;
if (offset <= results->rawlen &&
!matchAtLeast(results->rawbuf[offset], HITACHI_AC_MIN_GAP))
return false;

// Compliance
if (strict) {
// Correct size/length)
if (dataBitsSoFar / 8 != HITACHI_AC_STATE_LENGTH) return false;
}

// Success
results->decode_type = HITACHI_AC;
results->bits = dataBitsSoFar;
// No need to record the state as we stored it as we decoded it.
// As we use result->state, we don't record value, address, or command as it
// is a union data type.
return true;
}
#endif // DECODE_HITACHI_AC
13 changes: 11 additions & 2 deletions test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ 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_Carrier_test ir_Haier_test ir_Hitachi_test

# All Google Test headers. Usually you shouldn't change this
# definition.
Expand Down Expand Up @@ -74,7 +74,7 @@ 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_Midea.o ir_Magiquest.o ir_Lasertag.o ir_Carrier.o ir_Haier.o ir_Hitachi.o

# Common object files
COMMON_OBJ = IRutils.o IRtimer.o IRsend.o IRrecv.o ir_GlobalCache.o \
Expand Down Expand Up @@ -406,3 +406,12 @@ ir_Haier_test.o : ir_Haier_test.cpp $(COMMON_TEST_DEPS) $(GTEST_HEADERS)

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

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

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

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

0 comments on commit 5fcd9fe

Please sign in to comment.