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

Add initial support for Mitsubishi A/C IR remote emulation. #82

Merged
merged 4 commits into from
Mar 1, 2017
Merged
Show file tree
Hide file tree
Changes from all 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
148 changes: 148 additions & 0 deletions IRMitsubishiAC.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/*
Code to emulate Mitsubishi A/C IR remote control unit.
Inspired and derived from the work done at:
https://github.com/r45635/HVAC-IR-Control

Warning: Consider this very alpha code. Seems to work, but not validated.

Equipment it seems compatible with:
* <Add models (A/C & remotes) you've gotten it working with here>
*/

#include <IRMitsubishiAC.h>

// Initialise the object.
IRMitsubishiAC::IRMitsubishiAC(int pin) : _irsend(pin) {
stateReset();
}

// Reset the state of the remote to a known good state/sequence.
void IRMitsubishiAC::stateReset() {
for (uint8_t i = 0; i < MITSUBISHI_AC_STATE_LENGTH; i++)
remote_state[i] = known_good_state[i];
checksum(); // Calculate the checksum
}

// Configure the pin for output.
void IRMitsubishiAC::begin() {
_irsend.begin();
}

// Send the current desired state to the IR LED.
void IRMitsubishiAC::send() {
checksum(); // Ensure correct checksum before sending.
_irsend.sendMitsubishiAC(remote_state);
}

// Return a pointer to the internal state date of the remote.
uint8_t* IRMitsubishiAC::getRaw() {
checksum();
return remote_state;
}

// Calculate the checksum for the current internal state of the remote.
void IRMitsubishiAC::checksum() {
uint8_t sum = 0;
// Checksum is simple addition of all previous bytes stored
// as a 8 bit value.
for (uint8_t i = 0; i < 17; i++)
sum += remote_state[i];
remote_state[17] = sum & 0xFFU;
}

// Set the requested power state of the A/C to off.
void IRMitsubishiAC::on() {
//state = ON;
remote_state[5] |= MITSUBISHI_AC_POWER;
}

// Set the requested power state of the A/C to off.
void IRMitsubishiAC::off() {
//state = OFF;
remote_state[5] &= ~MITSUBISHI_AC_POWER;
}

// Set the requested power state of the A/C.
void IRMitsubishiAC::setPower(bool state) {
if (state)
on();
else
off();
}

// Return the requested power state of the A/C.
bool IRMitsubishiAC::getPower() {
return((remote_state[5] & MITSUBISHI_AC_POWER) != 0);
}

// Set the temp. in deg C
void IRMitsubishiAC::setTemp(uint8_t temp) {
temp = max(MITSUBISHI_AC_MIN_TEMP, temp);
temp = min(MITSUBISHI_AC_MAX_TEMP, temp);
remote_state[7] = temp - MITSUBISHI_AC_MIN_TEMP;
}

// Return the set temp. in deg C
uint8_t IRMitsubishiAC::getTemp() {
return(remote_state[7] + MITSUBISHI_AC_MIN_TEMP);
}

// Set the speed of the fan, 0-6.
// 0 is auto, 1-5 is the speed, 6 is silent.
void IRMitsubishiAC::setFan(uint8_t fan) {
// Bounds check
if (fan > MITSUBISHI_AC_FAN_SILENT)
fan = MITSUBISHI_AC_FAN_MAX; // Set the fan to maximum if out of range.
if (fan == MITSUBISHI_AC_FAN_AUTO) { // Automatic is a special case.
remote_state[9] = B10000000 | (remote_state[9] & B01111000);
return;
} else if (fan >= MITSUBISHI_AC_FAN_MAX) {
fan--; // There is no spoon^H^H^Heed 5 (max), pretend it doesn't exist.
}
remote_state[9] |= fan;
}

// Return the requested state of the unit's fan.
uint8_t IRMitsubishiAC::getFan() {
uint8_t fan = remote_state[9] & B111;
if (fan == MITSUBISHI_AC_FAN_MAX)
return MITSUBISHI_AC_FAN_SILENT;
return fan;
}

// Return the requested climate operation mode of the a/c unit.
uint8_t IRMitsubishiAC::getMode() {
/*
MITSUBISHI_AC_AUTO
MITSUBISHI_AC_COOL
MITSUBISHI_AC_DRY
MITSUBISHI_AC_HEAT
*/
return(remote_state[6]);
}

// Set the requested climate operation mode of the a/c unit.
void IRMitsubishiAC::setMode(uint8_t mode) {
// If we get an unexpected mode, default to AUTO.
switch (mode) {
case MITSUBISHI_AC_AUTO: break;
case MITSUBISHI_AC_COOL: break;
case MITSUBISHI_AC_DRY: break;
case MITSUBISHI_AC_HEAT: break;
default: mode = MITSUBISHI_AC_AUTO;
}
remote_state[6] = mode;
}

// Set the requested vane operation mode of the a/c unit.
void IRMitsubishiAC::setVane(uint8_t mode) {
mode = max(mode, B111); // bounds check
mode |= B1000;
mode <<= 3;
remote_state[9] |= mode;
}

// Return the requested vane operation mode of the a/c unit.
uint8_t IRMitsubishiAC::getVane() {
return ((remote_state[9] & B00111000) >> 3);
}
52 changes: 52 additions & 0 deletions IRMitsubishiAC.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@

#include <IRremoteESP8266.h>
#include <Arduino.h>

#define MITSUBISHI_AC_AUTO 0x20U
#define MITSUBISHI_AC_COOL 0x18U
#define MITSUBISHI_AC_DRY 0x10U
#define MITSUBISHI_AC_HEAT 0x08U
#define MITSUBISHI_AC_POWER 0x20U
#define MITSUBISHI_AC_FAN_AUTO 0U
#define MITSUBISHI_AC_FAN_MAX 5U
#define MITSUBISHI_AC_FAN_SILENT 6U
#define MITSUBISHI_AC_MIN_TEMP 16U // 16C
#define MITSUBISHI_AC_MAX_TEMP 31U // 31C
#define MITSUBISHI_AC_VANE_AUTO 0U
#define MITSUBISHI_AC_VANE_AUTO_MOVE 7U
#define MITSUBISHI_AC_STATE_LENGTH 18

class IRMitsubishiAC
{
public:
IRMitsubishiAC(int pin);

void stateReset();
void send();

void begin();
void on();
void off();
void setPower(bool state);
bool getPower();
void setTemp(uint8_t temp);
uint8_t getTemp();
void setFan(uint8_t fan);
uint8_t getFan();
void setMode(uint8_t mode);
uint8_t getMode();
void setVane(uint8_t mode);
uint8_t getVane();
uint8_t* getRaw();


private:
// The state of the IR remote in IR code form.
// Known good state obtained from:
// https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266.ino#L108
uint8_t known_good_state[MITSUBISHI_AC_STATE_LENGTH] = { 0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x08, 0x06, 0x30, 0x45, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F };
uint8_t remote_state[MITSUBISHI_AC_STATE_LENGTH];

void checksum();
IRsend _irsend;
};
32 changes: 32 additions & 0 deletions IRremoteESP8266.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
* Denon: sendDenon, decodeDenon added by Massimiliano Pinto
* (from https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp)
* Kelvinator A/C and Sherwood added by crankyoldgit
* Mitsubishi A/C added by crankyoldgit
* (derived from https://github.com/r45635/HVAC-IR-Control)
*
* Updated by markszabo (https://github.com/markszabo/IRremoteESP8266) for
* sending IR code on ESP8266
Expand All @@ -40,6 +42,7 @@
#include "IRremoteESP8266.h"
#include "IRremoteInt.h"
#include "IRKelvinator.h"
#include "IRMitsubishiAC.h"

// These versions of MATCH, MATCH_MARK, and MATCH_SPACE are only for debugging.
// To use them, set DEBUG in IRremoteInt.h
Expand Down Expand Up @@ -647,6 +650,35 @@ void IRsend::sendSherwood(unsigned long data, int nbits) {
sendSherwood(data, nbits, 1);
}

void IRsend::sendMitsubishiACChunk(uint8_t data) {
// send a chunk(byte) of Mitsubishi AC data
for (uint8_t bit = 0; bit < 8; bit++, data >>= 1) {
if (data & B1) { // 1
mark(MITSUBISHI_AC_BIT_MARK);
space(MITSUBISHI_AC_ONE_SPACE);
} else { // 0
mark(MITSUBISHI_AC_BIT_MARK);
space(MITSUBISHI_AC_ZERO_SPACE);
}
}
}

void IRsend::sendMitsubishiAC(unsigned char data[]) {
// Set IR carrier frequency
enableIROut(38);
// Mitsubishi AC remote sends the packet twice.
for (uint8_t count = 0; count < 2; count++) {
// Header
mark(MITSUBISHI_AC_HDR_MARK);
space(MITSUBISHI_AC_HDR_SPACE);
// Data
for (uint8_t i = 0; i < MITSUBISHI_AC_STATE_LENGTH; i++)
sendMitsubishiACChunk(data[i]);
// Footer
mark(MITSUBISHI_AC_RPT_MARK);
space(MITSUBISHI_AC_RPT_SPACE);
}
}
// ---------------------------------------------------------------


Expand Down
6 changes: 5 additions & 1 deletion IRremoteESP8266.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ enum decode_type_t {
DAIKIN,
DENON,
KELVINATOR,
SHERWOOD
SHERWOOD,
MITSUBISHI_AC
};

// Results returned from the decoder
Expand Down Expand Up @@ -102,6 +103,7 @@ class decode_results {
#define DENON 17
#define KELVINATOR 18 // Currently not implemented
#define SHERWOOD 19 // Not implemented. It decodes as an NEC code.
#define MITSUBISHI_AC 20 // Not implemented.
#define UNKNOWN -1

// Decoded value for NEC when a repeat code is received
Expand Down Expand Up @@ -203,13 +205,15 @@ class IRsend
void sendKelvinator(unsigned char data[]);
void sendSherwood(unsigned long data, int nbits);
void sendSherwood(unsigned long data, int nbits, int repeats);
void sendMitsubishiAC(unsigned char data[]);
void enableIROut(int khz);
VIRTUAL void mark(int usec);
VIRTUAL void space(unsigned long usec);
private:
int halfPeriodicTime;
int IRpin;
void sendKelvinatorChunk(unsigned char data, unsigned char nbits);
void sendMitsubishiACChunk(unsigned char data);
} ;

// Some useful constants
Expand Down
13 changes: 13 additions & 0 deletions IRremoteInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
* Denon: sendDenon, decodeDenon added by Massimiliano Pinto
(from https://github.com/z3t0/Arduino-IRremote/blob/master/ir_Denon.cpp)
* Kelvinator A/C added by crankyoldgit
* Mitsubishi A/C added by crankyoldgit
* (based on https://github.com/r45635/HVAC-IR-Control)
*
* 09/23/2015 : Samsung pulse parameters updated by Sebastien Warin to be compatible with EUxxD6200
*
Expand Down Expand Up @@ -84,6 +86,17 @@
// #define MITSUBISHI_DOUBLE_SPACE_USECS 800 // usually ssee 713 - not using ticks as get number wrapround
// #define MITSUBISHI_RPT_LENGTH 45000

// Mitsubishi A/C
// Values were initially obtained from:
// https://github.com/r45635/HVAC-IR-Control/blob/master/HVAC_ESP8266/HVAC_ESP8266.ino#L84
#define MITSUBISHI_AC_HDR_MARK 3400
#define MITSUBISHI_AC_HDR_SPACE 1750
#define MITSUBISHI_AC_BIT_MARK 450
#define MITSUBISHI_AC_ONE_SPACE 1300
#define MITSUBISHI_AC_ZERO_SPACE 420
#define MITSUBISHI_AC_RPT_MARK 440
#define MITSUBISHI_AC_RPT_SPACE 17100L


#define RC5_T1 889
#define RC5_RPT_LENGTH 46000
Expand Down
42 changes: 42 additions & 0 deletions examples/TurnOnMitsubishiAC/TurnOnMitsubishiAC.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

#include <IRMitsubishiAC.h>

IRMitsubishiAC mitsubir(D1); // IR led controlled by Pin D1.

void printState() {
// Display the settings.
Serial.println("Mitsubishi A/C remote is in the following state:");
Serial.printf(" Power: %d, Mode: %d, Temp: %dC, Fan Speed: %d, Vane Mode: %d\n",
mitsubir.getPower(), mitsubir.getMode(), mitsubir.getTemp(),
mitsubir.getFan(), mitsubir.getVane());
// Display the encoded IR sequence.
unsigned char* ir_code = mitsubir.getRaw();
Serial.print("IR Code: 0x");
for (int i = 0; i < MITSUBISHI_AC_STATE_LENGTH; i++)
Serial.printf("%02X", ir_code[i]);
Serial.println();
}

void setup(){
mitsubir.begin();
Serial.begin(115200);
delay(200);

// Set up what we want to send. See IRMitsubishiAC.cpp for all the options.
Serial.println("Default state of the remote.");
printState();
Serial.println("Setting desired state for A/C.");
mitsubir.on();
mitsubir.setFan(1);
mitsubir.setMode(MITSUBISHI_AC_COOL);
mitsubir.setTemp(26);
mitsubir.setVane(MITSUBISHI_AC_VANE_AUTO);
}

void loop() {
// Now send the IR signal.
Serial.println("Sending IR command to A/C ...");
mitsubir.send();
printState();
delay(5000);
}