Merge branch 'development' of github.com:tbowmo/Arduino into development

This commit is contained in:
Thomas Mørch
2015-10-03 10:40:28 +02:00
72 changed files with 5439 additions and 285 deletions
+4
View File
@@ -8,3 +8,7 @@ Please visit www.mysensors.org for more information
Current build status of master branch: [![Build Status](http://ci.mysensors.org/job/MySensorsArduino/branch/master/badge/icon)](http://ci.mysensors.org/job/MySensorsArduino/branch/master/)
Current build status of development branch: [![Build Status](http://ci.mysensors.org/job/MySensorsArduino/branch/development/badge/icon)](http://ci.mysensors.org/job/MySensorsArduino/branch/development/)
Current build status of master branch (nightly build of Arduino IDE): [![Build Status](http://ci.mysensors.org/job/MySensorsArduinoNightly/branch/master/badge/icon)](http://ci.mysensors.org/job/MySensorsArduinoNightly/branch/master/)
Current build status of development branch (nightly build of Arduino IDE): [![Build Status](http://ci.mysensors.org/job/MySensorsArduinoNightly/branch/development/badge/icon)](http://ci.mysensors.org/job/MySensorsArduinoNightly/branch/development/)
+6
View File
@@ -79,6 +79,12 @@ recipe.size.regex=^(?:\.text|\.data|\.bootloader)\s+([0-9]+).*
recipe.size.regex.data=^(?:\.data|\.bss|\.noinit)\s+([0-9]+).*
recipe.size.regex.eeprom=^(?:\.eeprom)\s+([0-9]+).*
## Preprocessor
preproc.includes.flags=-w -x c++ -M -MG -MP
recipe.preproc.includes="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} {preproc.includes.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}"
preproc.macros.flags=-w -x c++ -E -CC
recipe.preproc.macros="{compiler.path}{compiler.cpp.cmd}" {compiler.cpp.flags} {preproc.macros.flags} -mmcu={build.mcu} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}"
# AVR Uploader/Programmers tools
# ------------------------------
+170
View File
@@ -0,0 +1,170 @@
#include <CarrierHeatpumpIR.h>
CarrierHeatpumpIR::CarrierHeatpumpIR() : HeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "carrier";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"carrier\",\"dn\":\"Carrier\",\"mT\":17,\"xT\":30,\"fs\":6}";
_model = model;
_info = info;
}
void CarrierHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd)
{
(void)swingVCmd;
(void)swingHCmd;
// Sensible defaults for the heat pump mode
uint8_t operatingMode = CARRIER_AIRCON1_MODE_HEAT;
uint8_t fanSpeed = CARRIER_AIRCON1_FAN_AUTO;
uint8_t temperature = 23;
if (powerModeCmd == POWER_OFF)
{
operatingMode = CARRIER_AIRCON1_MODE_OFF;
}
else
{
switch (operatingModeCmd)
{
case MODE_AUTO:
operatingMode = CARRIER_AIRCON1_MODE_AUTO;
break;
case MODE_HEAT:
operatingMode = CARRIER_AIRCON1_MODE_HEAT;
break;
case MODE_COOL:
operatingMode = CARRIER_AIRCON1_MODE_COOL;
break;
case MODE_DRY:
operatingMode = CARRIER_AIRCON1_MODE_DRY;
fanSpeedCmd = FAN_AUTO; // Fan speed is always 'AUTO' in DRY mode
break;
case MODE_FAN:
operatingMode = CARRIER_AIRCON1_MODE_FAN;
temperatureCmd = 22; // Temperature is always 22 in FAN mode
break;
}
}
switch (fanSpeedCmd)
{
case FAN_AUTO:
fanSpeed = CARRIER_AIRCON1_FAN_AUTO;
break;
case FAN_1:
fanSpeed = CARRIER_AIRCON1_FAN1;
break;
case FAN_2:
fanSpeed = CARRIER_AIRCON1_FAN2;
break;
case FAN_3:
fanSpeed = CARRIER_AIRCON1_FAN3;
break;
case FAN_4:
fanSpeed = CARRIER_AIRCON1_FAN4;
break;
case FAN_5:
fanSpeed = CARRIER_AIRCON1_FAN5;
break;
}
if ( temperatureCmd > 16 && temperatureCmd < 31)
{
temperature = temperatureCmd;
}
sendCarrier(IR, operatingMode, fanSpeed, temperature);
}
// Send the Carrier code
// Carrier has the LSB and MSB in different format than Panasonic
void CarrierHeatpumpIR::sendCarrier(IRSender& IR, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature)
{
uint8_t sendBuffer[9] = { 0x4f, 0xb0, 0xc0, 0x3f, 0x80, 0x00, 0x00, 0x00, 0x00 }; // The data is on the last four uint8_ts
static const uint8_t temperatures[] PROGMEM = { 0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e, 0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b };
uint8_t checksum = 0;
// PROGMEM arrays cannot be addressed directly, see http://forum.arduino.cc/index.php?topic=106603.0
sendBuffer[5] = pgm_read_byte(&(temperatures[(temperature-17)]));
sendBuffer[6] = operatingMode | fanSpeed;
// Checksum
for (int i=0; i<8; i++) {
checksum += IR.bitReverse(sendBuffer[i]);
}
// There's something really strange with the checksum calculation...
// With these many of the codes matchs with the code from the real Carrier remote
// Still certain temperatures do not work with fan speeds 1, 2 or 5
switch (sendBuffer[6] & 0xF0) {
case 0x00: // MODE_AUTO - certain temperature / fan speed combinations do not work
checksum += 0x02;
switch (sendBuffer[6] & 0x0F) {
case 0x02: // FAN1
case 0x03: // FAN5
case 0x06: // FAN2
checksum += 0x80;
break;
}
break;
case 0x40: // MODE_DRY - all settings should work
checksum += 0x02;
break;
case 0xC0: // MODE_HEAT - certain temperature / fan speed combinations do not work
switch (sendBuffer[6] & 0x0F) {
case 0x05: // FAN4
case 0x06: // FAN2
checksum += 0xC0;
break;
}
break;
case 0x20: // MODE_FAN - all settings should work
checksum += 0x02;
switch (sendBuffer[6] & 0x0F) {
case 0x02: // FAN1
case 0x03: // FAN5
case 0x06: // FAN2
checksum += 0x80;
break;
}
break;
}
sendBuffer[8] = IR.bitReverse(checksum);
// 40 kHz PWM frequency
IR.setFrequency(40);
// Header
IR.mark(CARRIER_AIRCON1_HDR_MARK);
IR.space(CARRIER_AIRCON1_HDR_SPACE);
// Payload
for (size_t i=0; i<sizeof(sendBuffer); i++) {
IR.sendIRbyte(sendBuffer[i], CARRIER_AIRCON1_BIT_MARK, CARRIER_AIRCON1_ZERO_SPACE, CARRIER_AIRCON1_ONE_SPACE);
}
// Pause + new header
IR.mark(CARRIER_AIRCON1_BIT_MARK);
IR.space(CARRIER_AIRCON1_MSG_SPACE);
IR.mark(CARRIER_AIRCON1_HDR_MARK);
IR.space(CARRIER_AIRCON1_HDR_SPACE);
// Payload again
for (size_t i=0; i<sizeof(sendBuffer); i++) {
IR.sendIRbyte(sendBuffer[i], CARRIER_AIRCON1_BIT_MARK, CARRIER_AIRCON1_ZERO_SPACE, CARRIER_AIRCON1_ONE_SPACE);
}
// End mark
IR.mark(CARRIER_AIRCON1_BIT_MARK);
IR.space(0);
}
+42
View File
@@ -0,0 +1,42 @@
/*
Carrier 42NQV035G / 38NYV035H2 heatpump control (remote control P/N WH-L05SE)
*/
#ifndef CarrierHeatpumpIR_h
#define CarrierHeatpumpIR_h
#include <HeatpumpIR.h>
// Carrier (42NQV035G / 38NYV035H2) timing constants (remote control P/N WH-L05SE)
#define CARRIER_AIRCON1_HDR_MARK 4320
#define CARRIER_AIRCON1_HDR_SPACE 4350
#define CARRIER_AIRCON1_BIT_MARK 500
#define CARRIER_AIRCON1_ONE_SPACE 1650
#define CARRIER_AIRCON1_ZERO_SPACE 550
#define CARRIER_AIRCON1_MSG_SPACE 7400
// Carrier codes
#define CARRIER_AIRCON1_MODE_AUTO 0x00 // Operating mode
#define CARRIER_AIRCON1_MODE_HEAT 0xC0
#define CARRIER_AIRCON1_MODE_COOL 0x80
#define CARRIER_AIRCON1_MODE_DRY 0x40
#define CARRIER_AIRCON1_MODE_FAN 0x20
#define CARRIER_AIRCON1_MODE_OFF 0xE0 // Power OFF
#define CARRIER_AIRCON1_FAN_AUTO 0x00 // Fan speed
#define CARRIER_AIRCON1_FAN1 0x02
#define CARRIER_AIRCON1_FAN2 0x06
#define CARRIER_AIRCON1_FAN3 0x01
#define CARRIER_AIRCON1_FAN4 0x05
#define CARRIER_AIRCON1_FAN5 0x03
class CarrierHeatpumpIR : public HeatpumpIR
{
public:
CarrierHeatpumpIR();
void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd);
private:
void sendCarrier(IRSender& IR, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature);
};
#endif
+193
View File
@@ -0,0 +1,193 @@
#include <FujitsuHeatpumpIR.h>
FujitsuHeatpumpIR::FujitsuHeatpumpIR() : HeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "fujitsu_awyz";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"fujitsu_awyz\",\"dn\":\"Fujitsu AWYZ\",\"mT\":16,\"xT\":30,\"fs\":5}";
_model = model;
_info = info;
}
void FujitsuHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd)
{
send(IR, powerModeCmd, operatingModeCmd, fanSpeedCmd, temperatureCmd, swingVCmd, swingHCmd, false);
}
void FujitsuHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool ecoModeCmd)
{
// Sensible defaults for the heat pump mode
uint8_t operatingMode = FUJITSU_AIRCON1_MODE_HEAT;
uint8_t fanSpeed = FUJITSU_AIRCON1_FAN_AUTO;
uint8_t temperature = 23;
uint8_t swingV = FUJITSU_AIRCON1_VDIR_MANUAL;
uint8_t swingH = FUJITSU_AIRCON1_HDIR_MANUAL;
uint8_t ecoMode = FUJITSU_AIRCON1_ECO_OFF;
if (powerModeCmd == POWER_OFF)
{
operatingMode = FUJITSU_AIRCON1_MODE_OFF;
}
else
{
switch (operatingModeCmd)
{
case MODE_AUTO:
operatingMode = FUJITSU_AIRCON1_MODE_AUTO;
break;
case MODE_HEAT:
operatingMode = FUJITSU_AIRCON1_MODE_HEAT;
break;
case MODE_COOL:
operatingMode = FUJITSU_AIRCON1_MODE_COOL;
break;
case MODE_DRY:
operatingMode = FUJITSU_AIRCON1_MODE_DRY;
break;
case MODE_FAN:
operatingMode = FUJITSU_AIRCON1_MODE_FAN;
// When Fujitsu goes to FAN mode, it sets the low bit of the uint8_t with the temperature. What is the meaning of that?
break;
}
}
switch (fanSpeedCmd)
{
case FAN_AUTO:
fanSpeed = FUJITSU_AIRCON1_FAN_AUTO;
break;
case FAN_1:
fanSpeed = FUJITSU_AIRCON1_FAN1;
break;
case FAN_2:
fanSpeed = FUJITSU_AIRCON1_FAN2;
break;
case FAN_3:
fanSpeed = FUJITSU_AIRCON1_FAN3;
break;
case FAN_4:
fanSpeed = FUJITSU_AIRCON1_FAN4;
break;
}
if ( temperatureCmd > 15 && temperatureCmd < 31)
{
temperature = temperatureCmd;
}
// Only support 'don't move' or 'swing' at the moment, not any specific position
if ( swingVCmd == VDIR_SWING) {
swingV = FUJITSU_AIRCON1_VDIR_SWING;
}
if ( swingHCmd == HDIR_SWING) {
swingH = FUJITSU_AIRCON1_HDIR_SWING;
}
if (ecoModeCmd) {
ecoMode = FUJITSU_AIRCON1_ECO_ON;
}
sendFujitsu(IR, operatingMode, fanSpeed, temperature, swingV, swingH, ecoMode);
}
void FujitsuHeatpumpIR::sendFujitsu(IRSender& IR, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH, uint8_t ecoMode)
{
// ON, HEAT, AUTO FAN, +24 degrees
uint8_t FujitsuTemplate[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, 0x80, 0x04, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00 };
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
uint8_t OFF_msg[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0x02, 0xFD };
uint8_t checksum = 0x00;
/*
Fujitsu does not have codes to set the air direction to any specific position, but just go to the next position:
uint8_t nextVerticalPosition_msg[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0x6C, 0x93 };
uint8_t nextHorizontalPosition_msg[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0x79,0x86 };
These would need to be sent separately...
*/
// Set the operatingmode on the template message
FujitsuTemplate[9] = operatingMode;
// Set the eco mode on the template message
FujitsuTemplate[14] = ecoMode;
// Set the temperature on the template message. The least significant bit should be set to '1'
FujitsuTemplate[8] = (temperature - 16) << 4 | 0x01;
// Set the fan speed and air direction on the template message
FujitsuTemplate[10] = fanSpeed + swingV + swingH;
// Calculate the checksum
for (int i=0; i<15; i++) {
checksum += FujitsuTemplate[i];
}
FujitsuTemplate[15] = (uint8_t)(0x9E - checksum);
if (operatingMode == FUJITSU_AIRCON1_MODE_OFF) {
// OFF
sendFujitsuMsg(IR, sizeof(OFF_msg), OFF_msg);
} else {
sendFujitsuMsg(IR, sizeof(FujitsuTemplate), FujitsuTemplate);
}
}
void FujitsuHeatpumpIR::sendFujitsuHiPower(IRSender& IR)
{
uint8_t HiPower_msg[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0x39, 0xC6 };
sendFujitsuMsg(IR, sizeof(HiPower_msg), HiPower_msg);
}
void FujitsuHeatpumpIR::sendFujitsuFilterClean(IRSender& IR)
{
uint8_t FilterClean_msg[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0x02, 0xFD };
sendFujitsuMsg(IR, sizeof(FilterClean_msg), FilterClean_msg);
}
void FujitsuHeatpumpIR::sendFujitsuSuperQuiet(IRSender& IR)
{
uint8_t SuperQuiet_msg[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0x02, 0xFD };
sendFujitsuMsg(IR, sizeof(SuperQuiet_msg), SuperQuiet_msg);
}
void FujitsuHeatpumpIR::sendFujitsuTestRun(IRSender& IR)
{
uint8_t TestRun_msg[] = { 0x14, 0x63, 0x00, 0x10, 0x10, 0x02, 0xFD };
sendFujitsuMsg(IR, sizeof(TestRun_msg), TestRun_msg);
}
void FujitsuHeatpumpIR::sendFujitsuMsg(IRSender& IR, uint8_t msgSize, uint8_t *msg)
{
// 40 kHz PWM frequency
IR.setFrequency(40);
// Header
IR.mark(FUJITSU_AIRCON1_HDR_MARK);
IR.space(FUJITSU_AIRCON1_HDR_SPACE);
// Data
for (uint8_t i=0; i<msgSize; i++) {
IR.sendIRbyte(msg[i], FUJITSU_AIRCON1_BIT_MARK, FUJITSU_AIRCON1_ZERO_SPACE, FUJITSU_AIRCON1_ONE_SPACE);
}
// End mark
IR.mark(FUJITSU_AIRCON1_BIT_MARK);
IR.space(0);
}
+54
View File
@@ -0,0 +1,54 @@
/*
Fujitsu Nocria (AWYZ14) heatpump control (remote control P/N AR-PZ2)
*/
#ifndef FujitsuHeatpumpIR_h
#define FujitsuHeatpumpIR_h
#include <HeatpumpIR.h>
// Fujitsu Nocria (AWYZ14) timing constants (remote control P/N AR-PZ2)
#define FUJITSU_AIRCON1_HDR_MARK 3210
#define FUJITSU_AIRCON1_HDR_SPACE 1680
#define FUJITSU_AIRCON1_BIT_MARK 410
#define FUJITSU_AIRCON1_ONE_SPACE 1230
#define FUJITSU_AIRCON1_ZERO_SPACE 440
// Fujitsu codes
#define FUJITSU_AIRCON1_MODE_AUTO 0x00 // Operating mode
#define FUJITSU_AIRCON1_MODE_HEAT 0x04
#define FUJITSU_AIRCON1_MODE_COOL 0x01
#define FUJITSU_AIRCON1_MODE_DRY 0x02
#define FUJITSU_AIRCON1_MODE_FAN 0x03
#define FUJITSU_AIRCON1_MODE_OFF 0xFF // Power OFF - not real codes, but we need something...
#define FUJITSU_AIRCON1_FAN_AUTO 0x00 // Fan speed
#define FUJITSU_AIRCON1_FAN1 0x04
#define FUJITSU_AIRCON1_FAN2 0x03
#define FUJITSU_AIRCON1_FAN3 0x02
#define FUJITSU_AIRCON1_FAN4 0x01
#define FUJITSU_AIRCON1_VDIR_MANUAL 0x00 // Air direction modes
#define FUJITSU_AIRCON1_VDIR_SWING 0x10
#define FUJITSU_AIRCON1_HDIR_MANUAL 0x00
#define FUJITSU_AIRCON1_HDIR_SWING 0x20
#define FUJITSU_AIRCON1_ECO_OFF 0x20
#define FUJITSU_AIRCON1_ECO_ON 0x00
class FujitsuHeatpumpIR : public HeatpumpIR
{
public:
FujitsuHeatpumpIR();
void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd);
void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd, bool ecoModeCmd);
void sendFujitsuHiPower(IRSender& IR);
void sendFujitsuFilterClean(IRSender& IR);
void sendFujitsuSuperQuiet(IRSender& IR);
void sendFujitsuTestRun(IRSender& IR);
private:
void sendFujitsu(IRSender& IR, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH, uint8_t ecoMode);
void sendFujitsuMsg(IRSender& IR, uint8_t msgSize, uint8_t *msg);
};
#endif
+21
View File
@@ -0,0 +1,21 @@
#include <HeatpumpIR.h>
HeatpumpIR::HeatpumpIR()
{
}
// This is a virtual function, i.e. never called
void HeatpumpIR::send(IRSender&, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t)
{
}
// Heatpump model and info getters
const char PROGMEM* HeatpumpIR::model()
{
return _model;
}
const char PROGMEM* HeatpumpIR::info()
{
return _info;
}
+68
View File
@@ -0,0 +1,68 @@
/*
The generic heatpump interface
* Constants for modes
* The constructor and the 'send' command interface
*/
#ifndef HeatpumpIR_h
#define HeatpumpIR_h
#include <Arduino.h>
#include <IRSender.h>
// Power state
#define POWER_OFF 0
#define POWER_ON 1
// Operating modes
#define MODE_AUTO 1
#define MODE_HEAT 2
#define MODE_COOL 3
#define MODE_DRY 4
#define MODE_FAN 5
#define MODE_MAINT 6
// Fan speeds. Note that some heatpumps have less than 5 fan speeds
#define FAN_AUTO 0
#define FAN_1 1
#define FAN_2 2
#define FAN_3 3
#define FAN_4 4
#define FAN_5 5
// Vertical air directions. Note that these cannot be set on all heat pumps
#define VDIR_AUTO 0
#define VDIR_MANUAL 0
#define VDIR_SWING 1
#define VDIR_UP 2
#define VDIR_MUP 3
#define VDIR_MIDDLE 4
#define VDIR_MDOWN 5
#define VDIR_DOWN 6
// Horizontal air directions. Note that these cannot be set on all heat pumps
#define HDIR_AUTO 0
#define HDIR_MANUAL 0
#define HDIR_SWING 1
#define HDIR_MIDDLE 2
#define HDIR_LEFT 3
#define HDIR_MLEFT 4
#define HDIR_MRIGHT 5
#define HDIR_RIGHT 6
class HeatpumpIR
{
protected:
HeatpumpIR(); // Cannot create generic heatpump instances
const char * _model;
const char * _info;
public:
virtual void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd);
const char PROGMEM* model();
const char PROGMEM* info();
};
#endif
+129
View File
@@ -0,0 +1,129 @@
#include <HisenseHeatpumpIR.h>
HisenseHeatpumpIR::HisenseHeatpumpIR() : HeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "hisense_aud";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"hisense_aud\",\"dn\":\"Hisense AUD\",\"mT\":18,\"xT\":32,\"fs\":3}";
_model = model;
_info = info;
}
void HisenseHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd , uint8_t fanSpeedCmd , uint8_t temperatureCmd , uint8_t swingVCmd , uint8_t swingHCmd )
{
(void)swingVCmd;
(void)swingHCmd;
// Sensible defaults for the heat pump mode
uint8_t powerMode = HISENSE_AIRCON1_POWER_ON;
uint8_t operatingMode = HISENSE_AIRCON1_MODE_HEAT;
uint8_t fanSpeed = HISENSE_AIRCON1_FAN_AUTO;
uint8_t temperature = 21;
uint8_t swingV=0;
uint8_t swingH=0;
if (powerModeCmd == POWER_OFF)
{
powerMode = HISENSE_AIRCON1_POWER_OFF;
}
else
{
switch (operatingModeCmd)
{
case MODE_AUTO:
operatingMode = HISENSE_AIRCON1_MODE_AUTO;
fanSpeedCmd = FAN_AUTO; // Fan speed is always 'AUTO' in AUTO mode
break;
case MODE_HEAT:
operatingMode = HISENSE_AIRCON1_MODE_HEAT;
break;
case MODE_COOL:
operatingMode = HISENSE_AIRCON1_MODE_COOL;
break;
case MODE_DRY:
operatingMode = HISENSE_AIRCON1_MODE_DRY;
fanSpeedCmd = FAN_AUTO; // Fan speed is always 'AUTO' in DRY mode
break;
case MODE_FAN:
operatingMode = HISENSE_AIRCON1_MODE_FAN;
if ( fanSpeedCmd == FAN_AUTO ) {
fanSpeedCmd = FAN_1; // Fan speed cannot be 'AUTO' in FAN mode
temperatureCmd = 25; // Fixed temperature FAN mode
}
break;
}
}
switch (fanSpeedCmd)
{
case FAN_AUTO:
fanSpeed = HISENSE_AIRCON1_FAN_AUTO;
break;
case FAN_1:
fanSpeed = HISENSE_AIRCON1_FAN1;
break;
case FAN_2:
fanSpeed = HISENSE_AIRCON1_FAN2;
break;
case FAN_3:
fanSpeed = HISENSE_AIRCON1_FAN3;
break;
}
if ( temperatureCmd > 17 && temperatureCmd < 33)
{
temperature = temperatureCmd;
}
sendHisense(IR, powerMode, operatingMode, fanSpeed, temperature, swingV, swingH);
}
// Send the Hisense code
void HisenseHeatpumpIR::sendHisense(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV ,uint8_t swingH)
{
(void)swingV;
(void)swingH;
uint8_t HisenseTemplate[] = { 0x87, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, // Header uint8_t 0-1
0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00 }; //
uint8_t i;
// Set the Fan speed, On/Off.
HisenseTemplate[2] = fanSpeed | powerMode;
HisenseTemplate[3] = (((temperature - 18) << 4) | operatingMode ) ;
// Calculate the uint8_t checksum EXOR uint8_t 2 to 12
HisenseTemplate[13] = HisenseTemplate[2];
for (i=3; i<13; i++) {
HisenseTemplate[13]= HisenseTemplate[i] ^ HisenseTemplate[13];
}
// 38 kHz PWM frequency
IR.setFrequency(38);
// Send Header mark
IR.mark(HISENSE_AIRCON1_HDR_MARK);
IR.space(HISENSE_AIRCON1_HDR_SPACE);
// Payload header part
for (i=0; i<7; i++) {
IR.sendIRbyte(HisenseTemplate[i], HISENSE_AIRCON1_BIT_MARK, HISENSE_AIRCON1_ZERO_SPACE, HISENSE_AIRCON1_ONE_SPACE);
}
// Mesage space
IR.mark(HISENSE_AIRCON1_BIT_MARK);
IR.space(HISENSE_AIRCON1_MSG_SPACE);
// Payload message part
for (; i<14; i++) {
IR.sendIRbyte(HisenseTemplate[i], HISENSE_AIRCON1_BIT_MARK, HISENSE_AIRCON1_ZERO_SPACE, HISENSE_AIRCON1_ONE_SPACE);
}
// End mark
IR.mark(HISENSE_AIRCON1_BIT_MARK);
IR.space(0);
}
+73
View File
@@ -0,0 +1,73 @@
/*
Hisense AQV12PSBN / AQV09ASA heatpump control (remote control P/N zzz)
*/
#ifndef HisenseHeatpumpIR_h
#define HisenseHeatpumpIR_h
#include <HeatpumpIR.h>
// Hisense timing constants
#define HISENSE_AIRCON1_HDR_MARK 9060
#define HISENSE_AIRCON1_HDR_SPACE 4550
#define HISENSE_AIRCON1_BIT_MARK 520
#define HISENSE_AIRCON1_ONE_SPACE 1700
#define HISENSE_AIRCON1_ZERO_SPACE 630
#define HISENSE_AIRCON1_MSG_SPACE 8140
// Power state
#define HISENSE_AIRCON1_POWER_OFF 0x04 //**
#define HISENSE_AIRCON1_POWER_ON 0x00 //**
// Operating modes
// Hisense codes
#define HISENSE_AIRCON1_MODE_AUTO 0x04 // Not available 0x00
#define HISENSE_AIRCON1_MODE_HEAT 0x00
#define HISENSE_AIRCON1_MODE_COOL 0x02
#define HISENSE_AIRCON1_MODE_DRY 0x03
#define HISENSE_AIRCON1_MODE_FAN 0x04
#define HISENSE_AIRCON1_MODE_MAINT 0x04 // Power OFF
// Fan speeds. Note that some heatpumps have less than 5 fan speeds
#define HISENSE_AIRCON1_FAN_AUTO 0x00 // Fan speed
#define HISENSE_AIRCON1_FAN1 0x03 // * low
#define HISENSE_AIRCON1_FAN2 0x02 // * med
#define HISENSE_AIRCON1_FAN3 0x01 // * high
#define HISENSE_AIRCON1_FAN4 0x01 // * high Not available
#define HISENSE_AIRCON1_FAN5 0x01 // * high Not available
// Not available in this model.
// Vertical air directions. Note that these cannot be set on all heat pumps
#define HISENSE_VDIR_AUTO 0
#define HISENSE_VDIR_MANUAL 0
#define HISENSE_VDIR_SWING 0
#define HISENSE_VDIR_UP 0
#define HISENSE_VDIR_MUP 0
#define HISENSE_VDIR_MIDDLE 0
#define HISENSE_VDIR_MDOWN 0
#define HISENSE_VDIR_DOWN 0
// Not available in this model.
// Horizontal air directions. Note that these cannot be set on all heat pumps
#define HISENSE_HDIR_AUTO 0
#define HISENSE_HDIR_MANUAL 0
#define HISENSE_HDIR_SWING 0
#define HISENSE_HDIR_MIDDLE 0
#define HISENSE_HDIR_LEFT 0
#define HISENSE_HDIR_MLEFT 0
#define HISENSE_HDIR_MRIGHT 0
#define HISENSE_HDIR_RIGHT 0
class HisenseHeatpumpIR : public HeatpumpIR
{
public:
HisenseHeatpumpIR();
void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd);
private:
void sendHisense(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH);
};
#endif
+206
View File
@@ -0,0 +1,206 @@
#include <Arduino.h>
#include <IRSender.h>
// Heavily based on Ken Shirriff's IRRemote library:
// https://github.com/shirriff/Arduino-IRremote
//
// For PWM on Arduino, see http://playground.arduino.cc/Main/TimerPWMCheatsheet
IRSender::IRSender(uint8_t pin)
{
_pin = pin;
}
// Set the PWM frequency. The selected pin determines which timer to use
void IRSender::setFrequency(int frequency)
{
uint8_t pwmval8 = F_CPU / 2000 / (frequency);
uint16_t pwmval16 = F_CPU / 2000 / (frequency);
pinMode(_pin, OUTPUT);
digitalWrite(_pin, LOW); // When not sending PWM, we want it low
switch (_pin)
{
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// Arduino Mega
case 9:
// Fall-through to 10, timer2 controls both 9 and 10
case 10:
TCCR2A = _BV(WGM20);
TCCR2B = _BV(WGM22) | _BV(CS20);
OCR2A = pwmval8;
OCR2B = pwmval8 / 3;
break;
case 11:
// Fall-through to 12, timer1 controls both 11 and 12
case 12:
TCCR1A = _BV(WGM11);
TCCR1B = _BV(WGM13) | _BV(CS10);
ICR1 = pwmval16;
OCR1A = pwmval16 / 3;
OCR1B = pwmval16 / 3;
break;
case 44:
// Fall-through to 46, timer 5 controls pins 44, 45 and 46 on Arduino Mega
case 45:
case 46:
TCCR5A = _BV(WGM51) | _BV(WGM50);
TCCR5B = _BV(WGM53) | _BV(CS50);
ICR5 = pwmval16;
OCR5A = pwmval16 / 3;
#else
// Arduino Duemilanove etc
case 3:
// Fall-through to 11, timer2 controls both 3 and 11
case 11:
TCCR2A = _BV(WGM20);
TCCR2B = _BV(WGM22) | _BV(CS20);
OCR2A = pwmval8;
OCR2B = pwmval8 / 3;
break;
case 9:
// Fall-through to 10, timer1 controls both 9 and 10
case 10:
TCCR1A = _BV(WGM11);
TCCR1B = _BV(WGM13) | _BV(CS10);
ICR1 = pwmval16;
OCR1A = pwmval16 / 3;
OCR1B = pwmval16 / 3;
break;
#endif
}
}
// Send a uint8_t (8 bits) over IR
void IRSender::sendIRbyte(uint8_t sendByte, int bitMarkLength, int zeroSpaceLength, int oneSpaceLength)
{
for (int i=0; i<8 ; i++)
{
if (sendByte & 0x01)
{
mark(bitMarkLength);
space(oneSpaceLength);
}
else
{
mark(bitMarkLength);
space(zeroSpaceLength);
}
sendByte >>= 1;
}
}
// The Carrier IR protocol has the bits in a reverse order (compared to the other heatpumps)
// See http://www.nrtm.org/index.php/2013/07/25/reverse-bits-in-a-uint8_t/
uint8_t IRSender::bitReverse(uint8_t x)
{
// 01010101 | 10101010
x = ((x >> 1) & 0x55) | ((x << 1) & 0xaa);
// 00110011 | 11001100
x = ((x >> 2) & 0x33) | ((x << 2) & 0xcc);
// 00001111 | 11110000
x = ((x >> 4) & 0x0f) | ((x << 4) & 0xf0);
return x;
}
// Send an IR 'mark' symbol, i.e. transmitter ON
void IRSender::mark(int markLength)
{
switch (_pin)
{
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// Arduino Mega
case 9:
(TCCR2A |= _BV(COM2B1)); // Enable pin 3 PWM output
break;
case 11:
(TCCR1A |= _BV(COM1A1)); // Enable pin 9 PWM output
break;
case 12:
(TCCR1A |= _BV(COM1B1)); // Enable pin 10 PWM output
break;
case 10:
(TCCR2A |= _BV(COM2A1)); // Enable pin 11 PWM output
break;
case 44:
(TCCR5A |= _BV(COM5C1)); // Enable pin 44 PWM output on Arduino Mega
break;
case 45:
(TCCR5A |= _BV(COM5B1)); // Enable pin 45 PWM output on Arduino Mega
break;
case 46:
(TCCR5A |= _BV(COM5A1)); // Enable pin 46 PWM output on Arduino Mega
break;
#else
// Arduino Duemilanove etc
case 3:
(TCCR2A |= _BV(COM2B1)); // Enable pin 3 PWM output
break;
case 9:
(TCCR1A |= _BV(COM1A1)); // Enable pin 9 PWM output
break;
case 10:
(TCCR1A |= _BV(COM1B1)); // Enable pin 10 PWM output
break;
case 11:
(TCCR2A |= _BV(COM2A1)); // Enable pin 11 PWM output
break;
#endif
}
delayMicroseconds(markLength);
}
// Send an IR 'space' symbol, i.e. transmitter OFF
void IRSender::space(int spaceLength)
{
switch (_pin)
{
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// Arduino Mega
case 9:
(TCCR2A &= ~(_BV(COM2B1))); // Disable pin 3 PWM output
break;
case 11:
(TCCR1A &= ~(_BV(COM1A1))); // Disable pin 9 PWM output
break;
case 12:
(TCCR1A &= ~(_BV(COM1B1))); // Disable pin 10 PWM output
break;
case 10:
(TCCR2A &= ~(_BV(COM2A1))); // Disable pin 11 PWM output
break;
case 44:
(TCCR5A &= ~(_BV(COM5C1))); // Disable pin 44 PWM output on Arduino Mega
case 45:
(TCCR5A &= ~(_BV(COM5B1))); // Disable pin 45 PWM output on Arduino Mega
case 46:
(TCCR5A &= ~(_BV(COM5A1))); // Disable pin 46 PWM output on Arduino Mega
#else
// Arduino Duemilanove etc
case 3:
(TCCR2A &= ~(_BV(COM2B1))); // Disable pin 3 PWM output
break;
case 9:
(TCCR1A &= ~(_BV(COM1A1))); // Disable pin 9 PWM output
break;
case 10:
(TCCR1A &= ~(_BV(COM1B1))); // Disable pin 10 PWM output
break;
case 11:
(TCCR2A &= ~(_BV(COM2A1))); // Disable pin 11 PWM output
break;
#endif
}
// Mitsubishi heatpump uses > 16383us spaces, and delayMicroseconds only works up to 2^14 - 1 us
// Use the less accurate milliseconds delay for longer delays
if (spaceLength < 16383) {
delayMicroseconds(spaceLength);
} else {
delay(spaceLength/1000);
}
}
+23
View File
@@ -0,0 +1,23 @@
/*
Class to send IR signals using the Arduino PWM
*/
#ifndef IRSender_h
#define IRSender_h
#include <Arduino.h>
class IRSender
{
public:
IRSender(uint8_t pin);
void setFrequency(int frequency);
void sendIRbyte(uint8_t sendByte, int bitMarkLength, int zeroSpaceLength, int oneSpaceLength);
uint8_t bitReverse(uint8_t x);
void space(int spaceLength);
void mark(int markLength);
private:
uint8_t _pin;
};
#endif
+339
View File
@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{description}
Copyright (C) {year} {fullname}
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
{signature of Ty Coon}, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
+150
View File
@@ -0,0 +1,150 @@
#include <MideaHeatpumpIR.h>
MideaHeatpumpIR::MideaHeatpumpIR() : HeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "midea"; // The basic and inverter Midea's are the same, i.e. at least Pro Plus 10FP, Pro Plus 13Fp, Pro Plus 9 Inverter and Pro Plus 12 Inverter should work
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"midea\",\"dn\":\"Ultimate Pro Plus\",\"mT\":16,\"xT\":30,\"fs\":4,\"maint\":[10]}";
_model = model;
_info = info;
}
void MideaHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd)
{
(void)swingVCmd;
(void)swingHCmd;
// Sensible defaults for the heat pump mode
uint8_t operatingMode = MIDEA_AIRCON1_MODE_HEAT;
uint8_t fanSpeed = MIDEA_AIRCON1_FAN_AUTO;
uint8_t temperature = 23;
switch (powerModeCmd)
{
case 0:
// OFF is a special case
operatingMode = MIDEA_AIRCON1_MODE_OFF;
sendMidea(IR, operatingMode, fanSpeed, temperature);
return;
}
switch (operatingModeCmd)
{
case MODE_AUTO:
operatingMode = MIDEA_AIRCON1_MODE_AUTO;
break;
case MODE_HEAT:
operatingMode = MIDEA_AIRCON1_MODE_HEAT;
break;
case MODE_COOL:
operatingMode = MIDEA_AIRCON1_MODE_COOL;
break;
case MODE_DRY:
operatingMode = MIDEA_AIRCON1_MODE_DRY;
break;
case MODE_FAN:
operatingMode = MIDEA_AIRCON1_MODE_FAN;
break;
case MODE_MAINT:
// Maintenance mode ('FP' on the remote) is a special mode on Midea
// Also, this is a switch between 'normal' operation and 'maintenance' operation,
// i.e. if already running on maintenance, the heatpump will go back to normal operation
operatingMode = MIDEA_AIRCON1_MODE_FP;
break;
}
switch (fanSpeedCmd)
{
case FAN_AUTO:
fanSpeed = MIDEA_AIRCON1_FAN_AUTO;
break;
case FAN_1:
fanSpeed = MIDEA_AIRCON1_FAN1;
break;
case FAN_2:
fanSpeed = MIDEA_AIRCON1_FAN2;
break;
case FAN_3:
fanSpeed = MIDEA_AIRCON1_FAN3;
break;
}
if ( temperatureCmd > 16 && temperatureCmd < 31)
{
temperature = temperatureCmd;
}
sendMidea(IR, operatingMode, fanSpeed, temperature);
}
// Send the Midea code
void MideaHeatpumpIR::sendMidea(IRSender& IR, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature)
{
uint8_t sendBuffer[3] = { 0x4D, 0x00, 0x00 }; // First uint8_t is always 0x4D
static const uint8_t temperatures[] PROGMEM = {0, 8, 12, 4, 6, 14, 10, 2, 3, 11, 9, 1, 5, 13 };
static const uint8_t OffMsg[] PROGMEM = {0x4D, 0xDE, 0x07 };
static const uint8_t FPMsg[] PROGMEM = {0xAD, 0xAF, 0xB5 };
if (operatingMode == MIDEA_AIRCON1_MODE_OFF)
{
memcpy_P(sendBuffer, OffMsg, sizeof(sendBuffer));
}
else if (operatingMode == MIDEA_AIRCON1_MODE_FP)
{
memcpy_P(sendBuffer, FPMsg, sizeof(sendBuffer));
}
else
{
sendBuffer[1] = ~fanSpeed;
if ( operatingMode == MIDEA_AIRCON1_MODE_FAN )
{
sendBuffer[2] = MIDEA_AIRCON1_MODE_DRY | 0x07;
}
else
{
sendBuffer[2] = operatingMode | pgm_read_byte(&(temperatures[(temperature-17)]));
}
}
// Send the code
sendMidearaw(IR, sendBuffer);
}
// Send the Midea raw code
void MideaHeatpumpIR::sendMidearaw(IRSender& IR, uint8_t sendBuffer[])
{
// 40 kHz PWM frequency
IR.setFrequency(40);
// Header
IR.mark(MIDEA_AIRCON1_HDR_MARK);
IR.space(MIDEA_AIRCON1_HDR_SPACE);
// Six uint8_ts, every second uint8_t is a bitwise not of the previous uint8_t
for (int i=0; i<3; i++) {
IR.sendIRbyte(sendBuffer[i], MIDEA_AIRCON1_BIT_MARK, MIDEA_AIRCON1_ZERO_SPACE, MIDEA_AIRCON1_ONE_SPACE);
IR.sendIRbyte(~sendBuffer[i], MIDEA_AIRCON1_BIT_MARK, MIDEA_AIRCON1_ZERO_SPACE, MIDEA_AIRCON1_ONE_SPACE);
}
// Pause
IR.mark(MIDEA_AIRCON1_BIT_MARK);
IR.space(MIDEA_AIRCON1_MSG_SPACE);
// Header, two last uint8_ts repeated
IR.mark(MIDEA_AIRCON1_HDR_MARK);
IR.space(MIDEA_AIRCON1_HDR_SPACE);
// Six uint8_ts, every second uint8_t is a bitwise not of the previous uint8_t
for (int i=0; i<3; i++) {
IR.sendIRbyte(sendBuffer[i], MIDEA_AIRCON1_BIT_MARK, MIDEA_AIRCON1_ZERO_SPACE, MIDEA_AIRCON1_ONE_SPACE);
IR.sendIRbyte(~sendBuffer[i], MIDEA_AIRCON1_BIT_MARK, MIDEA_AIRCON1_ZERO_SPACE, MIDEA_AIRCON1_ONE_SPACE);
}
// End mark
IR.mark(MIDEA_AIRCON1_BIT_MARK);
IR.space(0);
}
+47
View File
@@ -0,0 +1,47 @@
/*
Midea MSR1-12HRN1-QC2 + MOA1-12HN1-QC2 heatpump control (remote control P/N RG51M1/E)
This heatpump is sold as 'Ultimate Pro Plus 13FP' in Finland, by www.ultimatemarket.com
Midea MSR1U-12HRDN1-QRC4W + MOB-12HFN1-QRC4W (remote control P/N RG51I20/BGE)
This heatpump is sold as 'Ultimate 12 Pro Plus Inverter' in Finland, by www.ultimatemarket.com
*/
#ifndef MideaHeatpumpIR_h
#define MideaHeatpumpIR_h
#include <HeatpumpIR.h>
// Midea timing constants
#define MIDEA_AIRCON1_HDR_MARK 4420
#define MIDEA_AIRCON1_HDR_SPACE 4300
#define MIDEA_AIRCON1_BIT_MARK 620
#define MIDEA_AIRCON1_ONE_SPACE 1560
#define MIDEA_AIRCON1_ZERO_SPACE 480
#define MIDEA_AIRCON1_MSG_SPACE 5100
// MIDEA codes
#define MIDEA_AIRCON1_MODE_AUTO 0x10 // Operating mode
#define MIDEA_AIRCON1_MODE_HEAT 0x30
#define MIDEA_AIRCON1_MODE_COOL 0x00
#define MIDEA_AIRCON1_MODE_DRY 0x20
#define MIDEA_AIRCON1_MODE_FAN 0x60
#define MIDEA_AIRCON1_MODE_FP 0x70 // Not a real mode...
#define MIDEA_AIRCON1_MODE_OFF 0xFE // Power OFF - not real codes, but we need something...
#define MIDEA_AIRCON1_MODE_ON 0xFF // Power ON
#define MIDEA_AIRCON1_FAN_AUTO 0x02 // Fan speed
#define MIDEA_AIRCON1_FAN1 0x06
#define MIDEA_AIRCON1_FAN2 0x05
#define MIDEA_AIRCON1_FAN3 0x03
class MideaHeatpumpIR : public HeatpumpIR
{
public:
MideaHeatpumpIR();
void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd);
private:
void sendMidea(IRSender& IR, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature);
void sendMidearaw(IRSender& IR, uint8_t sendBuffer[]);
};
#endif
@@ -0,0 +1,214 @@
#include <MitsubishiHeatpumpIR.h>
// These are protected methods, i.e. generic Mitsubishi instances cannot be created directly
MitsubishiHeatpumpIR::MitsubishiHeatpumpIR() : HeatpumpIR()
{
}
// The different models just set the model accordingly
MitsubishiFDHeatpumpIR::MitsubishiFDHeatpumpIR() : MitsubishiHeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "mitsubishi_fd";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"mitsubishi_fd\",\"dn\":\"Mitsubishi FD\",\"mT\":16,\"xT\":31,\"fs\":5}";
_model = model;
_info = info;
_mitsubishiModel = MITSUBISHI_FD;
}
MitsubishiFEHeatpumpIR::MitsubishiFEHeatpumpIR() : MitsubishiHeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "mitsubishi_fe";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"mitsubishi_fe\",\"dn\":\"Mitsubishi FE\",\"mT\":16,\"xT\":31,\"fs\":5,\"maint\":[10]}";
_model = model;
_info = info;
_mitsubishiModel = MITSUBISHI_FE;
}
void MitsubishiHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd)
{
// Sensible defaults for the heat pump mode
uint8_t powerMode = MITSUBISHI_AIRCON1_MODE_ON;
uint8_t operatingMode = MITSUBISHI_AIRCON1_MODE_HEAT;
uint8_t fanSpeed = MITSUBISHI_AIRCON1_FAN_AUTO;
uint8_t temperature = 23;
uint8_t swingV = MITSUBISHI_AIRCON1_VS_AUTO;
uint8_t swingH = MITSUBISHI_AIRCON1_HS_SWING;
if (powerModeCmd == 0)
{
powerMode = MITSUBISHI_AIRCON1_MODE_OFF;
}
switch (operatingModeCmd)
{
case MODE_AUTO:
operatingMode = MITSUBISHI_AIRCON1_MODE_AUTO;
break;
case MODE_HEAT:
operatingMode = MITSUBISHI_AIRCON1_MODE_HEAT;
break;
case MODE_COOL:
operatingMode = MITSUBISHI_AIRCON1_MODE_COOL;
break;
case MODE_DRY:
operatingMode = MITSUBISHI_AIRCON1_MODE_DRY;
break;
case MODE_FAN:
if (_mitsubishiModel == MITSUBISHI_FE) {
operatingMode = MITSUBISHI_AIRCON1_MODE_FAN;
temperatureCmd = 24;
} else {
operatingMode = MITSUBISHI_AIRCON1_MODE_COOL;
// Temperature needs to be set to 31 degrees for 'simulated' FAN mode
temperatureCmd = 31;
}
break;
case MODE_MAINT: // Maintenance mode is just the heat mode at +10, FAN5
if (_mitsubishiModel == MITSUBISHI_FE) {
operatingMode |= MITSUBISHI_AIRCON1_MODE_HEAT;
temperature = 10; // Default to +10 degrees
fanSpeedCmd = FAN_AUTO;
}
break;
}
switch (fanSpeedCmd)
{
case FAN_AUTO:
fanSpeed = MITSUBISHI_AIRCON1_FAN_AUTO;
break;
case FAN_1:
fanSpeed = MITSUBISHI_AIRCON1_FAN1;
break;
case FAN_2:
fanSpeed = MITSUBISHI_AIRCON1_FAN2;
break;
case FAN_3:
fanSpeed = MITSUBISHI_AIRCON1_FAN3;
break;
case FAN_4:
fanSpeed = MITSUBISHI_AIRCON1_FAN4;
break;
}
if ( temperatureCmd > 16 && temperatureCmd < 32)
{
temperature = temperatureCmd;
}
switch (swingVCmd)
{
case VDIR_AUTO:
swingV = MITSUBISHI_AIRCON1_VS_AUTO;
break;
case VDIR_SWING:
swingV = MITSUBISHI_AIRCON1_VS_SWING;
break;
case VDIR_UP:
swingV = MITSUBISHI_AIRCON1_VS_UP;
break;
case VDIR_MUP:
swingV = MITSUBISHI_AIRCON1_VS_MUP;
break;
case VDIR_MIDDLE:
swingV = MITSUBISHI_AIRCON1_VS_MIDDLE;
break;
case VDIR_MDOWN:
swingV = MITSUBISHI_AIRCON1_VS_MDOWN;
break;
case VDIR_DOWN:
swingV = MITSUBISHI_AIRCON1_VS_DOWN;
break;
}
switch (swingHCmd)
{
case HDIR_AUTO:
case HDIR_SWING:
swingH = MITSUBISHI_AIRCON1_HS_SWING;
break;
case HDIR_MIDDLE:
swingH = MITSUBISHI_AIRCON1_HS_MIDDLE;
break;
case HDIR_LEFT:
swingH = MITSUBISHI_AIRCON1_HS_LEFT;
break;
case HDIR_MLEFT:
swingH = MITSUBISHI_AIRCON1_HS_MLEFT;
break;
case HDIR_RIGHT:
swingH = MITSUBISHI_AIRCON1_HS_RIGHT;
break;
case HDIR_MRIGHT:
swingH = MITSUBISHI_AIRCON1_HS_MRIGHT;
break;
}
sendMitsubishi(IR, powerMode, operatingMode, fanSpeed, temperature, swingV, swingH);
}
void MitsubishiHeatpumpIR::sendMitsubishi(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH)
{
uint8_t MitsubishiTemplate[] = { 0x23, 0xCB, 0x26, 0x01, 0x00, 0x20, 0x48, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00 };
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
uint8_t checksum = 0x00;
// Set the operatingmode on the template message
MitsubishiTemplate[5] = powerMode;
MitsubishiTemplate[6] = operatingMode;
// Set the temperature on the template message
if (temperature == 10) {
MitsubishiTemplate[7] = 0x00; // Maintenance mode
MitsubishiTemplate[15] = 0x20; // This seems to be set to 0x20 on maintenance mode
} else {
MitsubishiTemplate[7] = temperature - 16;
}
// Set the horizontal air direction on the template message
MitsubishiTemplate[8] = swingH;
// Set the fan speed and vertical air direction on the template message
MitsubishiTemplate[9] = fanSpeed | swingV;
// Calculate the checksum
for (int i=0; i<17; i++) {
checksum += MitsubishiTemplate[i];
}
MitsubishiTemplate[17] = checksum;
// 40 kHz PWM frequency
IR.setFrequency(40);
// The Mitsubishi data is repeated twice
for (int j=0; j<2; j++) {
// Header
IR.mark(MITSUBISHI_AIRCON1_HDR_MARK);
IR.space(MITSUBISHI_AIRCON1_HDR_SPACE);
// Data
for (unsigned int i=0; i<sizeof(MitsubishiTemplate); i++) {
IR.sendIRbyte(MitsubishiTemplate[i], MITSUBISHI_AIRCON1_BIT_MARK, MITSUBISHI_AIRCON1_ZERO_SPACE, MITSUBISHI_AIRCON1_ONE_SPACE);
}
// Pause between the first and the second data burst
if (j == 0) {
IR.mark(MITSUBISHI_AIRCON1_BIT_MARK);
IR.space(MITSUBISHI_AIRCON1_MSG_SPACE);
}
}
// End mark
IR.mark(MITSUBISHI_AIRCON1_BIT_MARK);
IR.space(0);
}
@@ -0,0 +1,79 @@
/*
Mitsubishi MSZ FD-25 heatpump control (remote control P/N KM09D 0052376)
*/
#ifndef MitsubishiHeatpumpIR_h
#define MitsubishiHeatpumpIR_h
#include <HeatpumpIR.h>
// Mitsubishi MSZ FD-25 timing constants (remote control P/N KM09D 0052376)
#define MITSUBISHI_AIRCON1_HDR_MARK 3500
#define MITSUBISHI_AIRCON1_HDR_SPACE 1700
#define MITSUBISHI_AIRCON1_BIT_MARK 430
#define MITSUBISHI_AIRCON1_ONE_SPACE 1250
#define MITSUBISHI_AIRCON1_ZERO_SPACE 390
#define MITSUBISHI_AIRCON1_MSG_SPACE 17500
// Mitsubishi codes
#define MITSUBISHI_AIRCON1_MODE_AUTO 0x60 // Operating mode
#define MITSUBISHI_AIRCON1_MODE_HEAT 0x48
#define MITSUBISHI_AIRCON1_MODE_COOL 0x58
#define MITSUBISHI_AIRCON1_MODE_DRY 0x50
#define MITSUBISHI_AIRCON1_MODE_FAN 0x38 // EF 'FAN' mode
#define MITSUBISHI_AIRCON1_MODE_OFF 0x00 // Power OFF
#define MITSUBISHI_AIRCON1_MODE_ON 0x20 // Power ON
#define MITSUBISHI_AIRCON1_FAN_AUTO 0x00 // Fan speed - mixed with vertical swing...
#define MITSUBISHI_AIRCON1_FAN1 0x01
#define MITSUBISHI_AIRCON1_FAN2 0x02
#define MITSUBISHI_AIRCON1_FAN3 0x03
#define MITSUBISHI_AIRCON1_FAN4 0x04
#define MITSUBISHI_AIRCON1_VS_SWING 0x78 // Vertical swing
#define MITSUBISHI_AIRCON1_VS_AUTO 0x40 // Automatic setting
#define MITSUBISHI_AIRCON1_VS_UP 0x48
#define MITSUBISHI_AIRCON1_VS_MUP 0x50
#define MITSUBISHI_AIRCON1_VS_MIDDLE 0x58
#define MITSUBISHI_AIRCON1_VS_MDOWN 0x60
#define MITSUBISHI_AIRCON1_VS_DOWN 0x68
#define MITSUBISHI_AIRCON1_VS_AUTO2 0x80 // ???
#define MITSUBISHI_AIRCON1_VS_AUTO3 0xB8 // ???
#define MITSUBISHI_AIRCON1_HS_SWING 0xC0 // Horizontal swing
#define MITSUBISHI_AIRCON1_HS_MIDDLE 0x30
#define MITSUBISHI_AIRCON1_HS_LEFT 0x10
#define MITSUBISHI_AIRCON1_HS_MLEFT 0x20
#define MITSUBISHI_AIRCON1_HS_MRIGHT 0x40
#define MITSUBISHI_AIRCON1_HS_RIGHT 0x50
// Mitsubishi model codes
#define MITSUBISHI_FD 0
#define MITSUBISHI_FE 1
class MitsubishiHeatpumpIR : public HeatpumpIR
{
protected: // Cannot create generic Mitsubishi heatpump instances
MitsubishiHeatpumpIR();
uint8_t _mitsubishiModel; // Tells whether this is FD or EF (or other supported model...)
public:
void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd);
private:
void sendMitsubishi(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingVCmd, uint8_t swingHCmd);
};
class MitsubishiFDHeatpumpIR : public MitsubishiHeatpumpIR
{
public:
MitsubishiFDHeatpumpIR();
};
class MitsubishiFEHeatpumpIR : public MitsubishiHeatpumpIR
{
public:
MitsubishiFEHeatpumpIR();
};
#endif
@@ -0,0 +1,262 @@
#include <PanasonicCKPHeatpumpIR.h>
PanasonicCKPHeatpumpIR::PanasonicCKPHeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "panasonic_ckp";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"panasonic_ckp\",\"dn\":\"Panasonic CKP\",\"mT\":16,\"xT\":30,\"fs\":6}";
_model = model;
_info = info;
}
// Panasonic CKP numeric values to command uint8_ts
void PanasonicCKPHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd)
{
// Sensible defaults for the heat pump mode
uint8_t powerMode = false;
uint8_t operatingMode = PANASONIC_AIRCON1_MODE_KEEP;
uint8_t fanSpeed = PANASONIC_AIRCON1_FAN_AUTO;
uint8_t temperature = 23;
uint8_t swingV = PANASONIC_AIRCON1_VS_UP;
uint8_t swingH = PANASONIC_AIRCON1_HS_SWING;
switch (powerModeCmd)
{
case POWER_ON:
powerMode = true;
break;
}
switch (operatingModeCmd)
{
case MODE_AUTO:
operatingMode |= PANASONIC_AIRCON1_MODE_AUTO;
break;
case MODE_HEAT:
operatingMode |= PANASONIC_AIRCON1_MODE_HEAT;
break;
case MODE_COOL:
operatingMode |= PANASONIC_AIRCON1_MODE_COOL;
break;
case MODE_DRY:
operatingMode |= PANASONIC_AIRCON1_MODE_DRY;
break;
case MODE_FAN:
operatingMode |= PANASONIC_AIRCON1_MODE_FAN;
temperatureCmd = 27; // Temperature is always 27 in FAN mode
break;
default:
operatingMode |= PANASONIC_AIRCON1_MODE_HEAT;
break;
}
switch (fanSpeedCmd)
{
case FAN_AUTO:
fanSpeed = PANASONIC_AIRCON1_FAN_AUTO;
break;
case FAN_1:
fanSpeed = PANASONIC_AIRCON1_FAN1;
break;
case FAN_2:
fanSpeed = PANASONIC_AIRCON1_FAN2;
break;
case FAN_3:
fanSpeed = PANASONIC_AIRCON1_FAN3;
break;
case FAN_4:
fanSpeed = PANASONIC_AIRCON1_FAN4;
break;
case FAN_5:
fanSpeed = PANASONIC_AIRCON1_FAN5;
break;
}
if ( temperatureCmd > 15 && temperatureCmd < 31)
{
temperature = temperatureCmd;
}
switch (swingVCmd)
{
case VDIR_SWING:
swingV = PANASONIC_AIRCON1_VS_SWING;
break;
case VDIR_UP:
swingV = PANASONIC_AIRCON1_VS_UP;
break;
case VDIR_MUP:
swingV = PANASONIC_AIRCON1_VS_MUP;
break;
case VDIR_MIDDLE:
swingV = PANASONIC_AIRCON1_VS_MIDDLE;
break;
case VDIR_MDOWN:
swingV = PANASONIC_AIRCON1_VS_MDOWN;
break;
case VDIR_DOWN:
swingV = PANASONIC_AIRCON1_VS_DOWN;
break;
}
switch (swingHCmd)
{
case HDIR_SWING:
swingH = PANASONIC_AIRCON1_HS_SWING;
break;
case HDIR_AUTO: // Well, just set it to manual
swingH = PANASONIC_AIRCON1_HS_MANUAL;
break;
}
sendPanasonicCKP(IR, operatingMode, fanSpeed, temperature, swingV, swingH);
delay(1000); // Sleep 1 second between the messages
// This will change the power state in one minute from now
sendPanasonicCKPOnOffTimerCancel(IR, powerMode, false);
/*
// Send the 'timer cancel' signal 2 minutes later
if (panasonicCancelTimer != 0)
{
timer.stop(panasonicCancelTimer);
panasonicCancelTimer = 0;
}
// Note that the argument to 'timer.after' has to be explicitly cast into 'long'
panasonicCancelTimer = timer.after(2L*60L*1000L, sendPanasonicCKPCancelTimer);
Serial.print(F("'Timer cancel' timer ID: "));
Serial.println(panasonicCancelTimer);
*/
}
// Send the Panasonic CKP code
void PanasonicCKPHeatpumpIR::sendPanasonicCKP(IRSender& IR, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH)
{
uint8_t sendBuffer[4];
// Fan speed & temperature, temperature needs to be 27 in FAN mode
if (operatingMode == PANASONIC_AIRCON1_MODE_FAN || operatingMode == (PANASONIC_AIRCON1_MODE_FAN | PANASONIC_AIRCON1_MODE_KEEP ))
{
temperature = 27;
}
sendBuffer[0] = fanSpeed | (temperature - 15);
// Power toggle & operation mode
sendBuffer[1] = operatingMode;
// Swings
sendBuffer[2] = swingV | swingH;
// Always 0x36
sendBuffer[3] = 0x36;
// Send the code
sendPanasonicCKPraw(IR, sendBuffer);
}
// Send the Panasonic CKP raw code
void PanasonicCKPHeatpumpIR::sendPanasonicCKPraw(IRSender& IR, uint8_t sendBuffer[])
{
// 40 kHz PWM frequency
IR.setFrequency(40);
// Header, two first uint8_ts repeated
IR.mark(PANASONIC_AIRCON1_HDR_MARK);
IR.space(PANASONIC_AIRCON1_HDR_SPACE);
for (int i=0; i<2; i++) {
IR.sendIRbyte(sendBuffer[0], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
IR.sendIRbyte(sendBuffer[0], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
IR.sendIRbyte(sendBuffer[1], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
IR.sendIRbyte(sendBuffer[1], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
IR.mark(PANASONIC_AIRCON1_HDR_MARK);
IR.space(PANASONIC_AIRCON1_HDR_SPACE);
}
// Pause
IR.mark(PANASONIC_AIRCON1_BIT_MARK);
IR.space(PANASONIC_AIRCON1_MSG_SPACE);
// Header, two last uint8_ts repeated
IR.mark(PANASONIC_AIRCON1_HDR_MARK);
IR.space(PANASONIC_AIRCON1_HDR_SPACE);
for (int i=0; i<2; i++) {
IR.sendIRbyte(sendBuffer[2], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
IR.sendIRbyte(sendBuffer[2], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
IR.sendIRbyte(sendBuffer[3], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
IR.sendIRbyte(sendBuffer[3], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
IR.mark(PANASONIC_AIRCON1_HDR_MARK);
IR.space(PANASONIC_AIRCON1_HDR_SPACE);
}
IR.mark(PANASONIC_AIRCON1_BIT_MARK);
IR.space(0);
}
// Send the Panasonic CKP On/Off code
//
// CKP does not have discrete ON/OFF commands, but this can be emulated by using the timer
// The side-effects of using the timer are:
// * ONE minute delay before the power state changes
// * the 'TIMER' led (orange) is lit
// * a timer event is scheduled to cancel the timer after TWO minutes (the 'TIMER' led turns off
void PanasonicCKPHeatpumpIR::sendPanasonicCKPOnOffTimerCancel(IRSender& IR, boolean powerState, boolean cancelTimer)
{
static const uint8_t ON_msg[] PROGMEM = { 0x7F, 0x38, 0xBF, 0x38, 0x10, 0x3D, 0x80, 0x3D, 0x09, 0x34, 0x80, 0x34 }; // ON at 00:10, time now 00:09, no OFF timing
static const uint8_t OFF_msg[] PROGMEM = { 0x10, 0x38, 0x80, 0x38, 0x7F, 0x3D, 0xBF, 0x3D, 0x09, 0x34, 0x80, 0x34 }; // OFF at 00:10, time now 00:09, no ON timing
static const uint8_t CANCEL_msg[] PROGMEM = { 0x7F, 0x38, 0xBF, 0x38, 0x7F, 0x3D, 0xBF, 0x3D, 0x17, 0x34, 0x80, 0x34 }; // Timer CANCEL
// Save some SRAM by only having one copy of the template on the SRAM
uint8_t sendBuffer[sizeof(ON_msg)];
if ( cancelTimer == true ) {
memcpy_P(sendBuffer, CANCEL_msg, sizeof(ON_msg));
} else if ( powerState == true ) {
memcpy_P(sendBuffer, ON_msg, sizeof(ON_msg));
} else {
memcpy_P(sendBuffer, OFF_msg, sizeof(ON_msg));
}
// 40 kHz PWM frequency
IR.setFrequency(40);
for (int i=0; i<6; i++) {
IR.mark(PANASONIC_AIRCON1_HDR_MARK);
IR.space(PANASONIC_AIRCON1_HDR_SPACE);
IR.sendIRbyte(sendBuffer[i*2 + 0], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
IR.sendIRbyte(sendBuffer[i*2 + 0], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
IR.sendIRbyte(sendBuffer[i*2 + 1], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
IR.sendIRbyte(sendBuffer[i*2 + 1], PANASONIC_AIRCON1_BIT_MARK, PANASONIC_AIRCON1_ZERO_SPACE, PANASONIC_AIRCON1_ONE_SPACE);
IR.mark(PANASONIC_AIRCON1_HDR_MARK);
IR.space(PANASONIC_AIRCON1_HDR_SPACE);
if ( i < 5 ) {
IR.mark(PANASONIC_AIRCON1_BIT_MARK);
IR.space(PANASONIC_AIRCON1_MSG_SPACE);
}
}
IR.mark(PANASONIC_AIRCON1_BIT_MARK);
IR.space(0);
}
// Send the Panasonic CKP timer cancel
void PanasonicCKPHeatpumpIR::sendPanasonicCKPCancelTimer(IRSender& IR)
{
Serial.println(F("Sending Panasonic CKP timer cancel"));
sendPanasonicCKPOnOffTimerCancel(IR, false, true);
}
@@ -0,0 +1,55 @@
/*
Panasonic CKP heatpump control (remote control P/N A75C2295)
*/
#ifndef PanasonicCKPHeatpumpIR_h
#define PanasonicCKPHeatpumpIR_h
#include <HeatpumpIR.h>
// Panasonic CKP timing constants (remote control P/N A75C2295)
#define PANASONIC_AIRCON1_HDR_MARK 3400
#define PANASONIC_AIRCON1_HDR_SPACE 3500
#define PANASONIC_AIRCON1_BIT_MARK 800
#define PANASONIC_AIRCON1_ONE_SPACE 2700
#define PANASONIC_AIRCON1_ZERO_SPACE 1000
#define PANASONIC_AIRCON1_MSG_SPACE 14000
// Panasonic CKP codes
#define PANASONIC_AIRCON1_MODE_AUTO 0x06 // Operating mode
#define PANASONIC_AIRCON1_MODE_HEAT 0x04
#define PANASONIC_AIRCON1_MODE_COOL 0x02
#define PANASONIC_AIRCON1_MODE_DRY 0x03
#define PANASONIC_AIRCON1_MODE_FAN 0x01
#define PANASONIC_AIRCON1_MODE_ONOFF 0x00 // Toggle ON/OFF
#define PANASONIC_AIRCON1_MODE_KEEP 0x08 // Do not toggle ON/OFF
#define PANASONIC_AIRCON1_FAN_AUTO 0xF0 // Fan speed
#define PANASONIC_AIRCON1_FAN1 0x20
#define PANASONIC_AIRCON1_FAN2 0x30
#define PANASONIC_AIRCON1_FAN3 0x40
#define PANASONIC_AIRCON1_FAN4 0x50
#define PANASONIC_AIRCON1_FAN5 0x60
#define PANASONIC_AIRCON1_VS_SWING 0xF0 // Vertical swing
#define PANASONIC_AIRCON1_VS_UP 0x90
#define PANASONIC_AIRCON1_VS_MUP 0xA0
#define PANASONIC_AIRCON1_VS_MIDDLE 0xB0
#define PANASONIC_AIRCON1_VS_MDOWN 0xC0
#define PANASONIC_AIRCON1_VS_DOWN 0xD0
#define PANASONIC_AIRCON1_HS_SWING 0x08 // Horizontal swing
#define PANASONIC_AIRCON1_HS_MANUAL 0x00
class PanasonicCKPHeatpumpIR : public HeatpumpIR
{
public:
PanasonicCKPHeatpumpIR();
void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd);
void sendPanasonicCKPCancelTimer(IRSender& IR);
private:
void sendPanasonicCKP(IRSender& IR, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH);
void sendPanasonicCKPraw(IRSender& IR, uint8_t sendBuffer[]);
void sendPanasonicCKPOnOffTimerCancel(IRSender& IR, boolean powerState, boolean cancelTimer);
};
#endif
@@ -0,0 +1,237 @@
#include <PanasonicHeatpumpIR.h>
// This is a protected method, i.e. generic Panasonic instances cannot be created
PanasonicHeatpumpIR::PanasonicHeatpumpIR() : HeatpumpIR()
{
}
// The different models just set the model accordingly
PanasonicDKEHeatpumpIR::PanasonicDKEHeatpumpIR() : PanasonicHeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "panasonic_dke";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"panasonic_dke\",\"dn\":\"Panasonic DKE\",\"mT\":16,\"xT\":30,\"fs\":6}";
_model = model;
_info = info;
_panasonicModel = PANASONIC_DKE;
}
PanasonicJKEHeatpumpIR::PanasonicJKEHeatpumpIR() : PanasonicHeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "panasonic_jke";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"panasonic_jke\",\"dn\":\"Panasonic JKE\",\"mT\":16,\"xT\":30,\"fs\":6}";
_model = model;
_info = info;
_panasonicModel = PANASONIC_JKE;
}
PanasonicNKEHeatpumpIR::PanasonicNKEHeatpumpIR() : PanasonicHeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "panasonic_nke";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"panasonic_nke\",\"dn\":\"Panasonic NKE\",\"mT\":16,\"xT\":30,\"fs\":6,\"maint\":[8,10]}";
_model = model;
_info = info;
_panasonicModel = PANASONIC_NKE;
}
// Panasonic DKE/NKE/JKE numeric values to command uint8_ts
void PanasonicHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd)
{
// Sensible defaults for the heat pump mode
uint8_t operatingMode = PANASONIC_AIRCON2_TIMER_CNL;
uint8_t fanSpeed = PANASONIC_AIRCON2_FAN_AUTO;
uint8_t temperature = 23;
uint8_t swingV = PANASONIC_AIRCON2_VS_UP;
uint8_t swingH = PANASONIC_AIRCON2_HS_AUTO;
switch (powerModeCmd)
{
case POWER_ON:
operatingMode |= PANASONIC_AIRCON2_MODE_ON;
break;
}
switch (operatingModeCmd)
{
case MODE_AUTO:
operatingMode |= PANASONIC_AIRCON2_MODE_AUTO;
break;
case MODE_HEAT:
operatingMode |= PANASONIC_AIRCON2_MODE_HEAT;
break;
case MODE_COOL:
operatingMode |= PANASONIC_AIRCON2_MODE_COOL;
break;
case MODE_DRY:
operatingMode |= PANASONIC_AIRCON2_MODE_DRY;
break;
case MODE_FAN:
operatingMode |= PANASONIC_AIRCON2_MODE_FAN;
temperatureCmd = 27; // Temperature is always 27 in FAN mode
break;
case MODE_MAINT: // Maintenance mode is just the heat mode at +8 or +10, FAN5
operatingMode |= PANASONIC_AIRCON2_MODE_HEAT;
temperature = 10; // Default to +10 degrees
fanSpeedCmd = FAN_5;
break;
}
switch (fanSpeedCmd)
{
case FAN_AUTO:
fanSpeed = PANASONIC_AIRCON2_FAN_AUTO;
break;
case FAN_1:
fanSpeed = PANASONIC_AIRCON2_FAN1;
break;
case FAN_2:
fanSpeed = PANASONIC_AIRCON2_FAN2;
break;
case FAN_3:
fanSpeed = PANASONIC_AIRCON2_FAN3;
break;
case FAN_4:
fanSpeed = PANASONIC_AIRCON2_FAN4;
break;
case FAN_5:
fanSpeed = PANASONIC_AIRCON2_FAN5;
break;
}
if ( temperatureCmd > 15 && temperatureCmd < 31)
{
temperature = temperatureCmd;
}
switch (swingVCmd)
{
case VDIR_AUTO:
case VDIR_SWING:
swingV = PANASONIC_AIRCON2_VS_AUTO;
break;
case VDIR_UP:
swingV = PANASONIC_AIRCON2_VS_UP;
break;
case VDIR_MUP:
swingV = PANASONIC_AIRCON2_VS_MUP;
break;
case VDIR_MIDDLE:
swingV = PANASONIC_AIRCON2_VS_MIDDLE;
break;
case VDIR_MDOWN:
swingV = PANASONIC_AIRCON2_VS_MDOWN;
break;
case VDIR_DOWN:
swingV = PANASONIC_AIRCON2_VS_DOWN;
break;
}
switch (swingHCmd)
{
case HDIR_AUTO:
case HDIR_SWING:
swingH = PANASONIC_AIRCON2_HS_AUTO;
break;
case HDIR_MIDDLE:
swingH = PANASONIC_AIRCON2_HS_MIDDLE;
break;
case HDIR_LEFT:
swingH = PANASONIC_AIRCON2_HS_LEFT;
break;
case HDIR_MLEFT:
swingH = PANASONIC_AIRCON2_HS_MLEFT;
break;
case HDIR_RIGHT:
swingH = PANASONIC_AIRCON2_HS_RIGHT;
break;
case HDIR_MRIGHT:
swingH = PANASONIC_AIRCON2_HS_MRIGHT;
break;
}
// NKE has +8 / + 10 maintenance heating, which also means MAX fanspeed
if ( _panasonicModel == PANASONIC_NKE )
{
if ( temperatureCmd == 8 || temperatureCmd == 10 )
{
temperature = temperatureCmd;
fanSpeed = PANASONIC_AIRCON2_FAN5;
}
}
sendPanasonic(IR, operatingMode, fanSpeed, temperature, swingV, swingH);
}
// Send the Panasonic DKE/JKE/NKE code
void PanasonicHeatpumpIR::sendPanasonic(IRSender& IR, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH)
{
// Only uint8_ts 13, 14, 16, 17 and 26 are modified, DKE and JKE seem to share the same template?
static const uint8_t panasonicProgmemTemplate[][27] PROGMEM = {
// DKE, model 0
{ 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x06, 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x0E, 0xE0, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00 },
// JKE, model 1
{ 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x06, 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x0E, 0xE0, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00 },
// NKE, model 2
{ 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x06, 0x02, 0x20, 0xE0, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x00, 0x0E, 0xE0, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00 }
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
};
// Save some SRAM by only having one copy of the template on the SRAM
uint8_t panasonicTemplate[27];
memcpy_P(panasonicTemplate, panasonicProgmemTemplate[_panasonicModel], sizeof(panasonicTemplate));
panasonicTemplate[13] = operatingMode;
panasonicTemplate[14] = temperature << 1;
panasonicTemplate[16] = fanSpeed | swingV;
// Only the DKE model has a setting for the horizontal air flow
if ( _panasonicModel == PANASONIC_DKE) {
panasonicTemplate[17] = swingH;
}
// Checksum calculation
uint8_t checksum = 0xF4;
for (int i=0; i<26; i++) {
checksum += panasonicTemplate[i];
}
panasonicTemplate[26] = checksum;
// 40 kHz PWM frequency
IR.setFrequency(40);
// Header
IR.mark(PANASONIC_AIRCON2_HDR_MARK);
IR.space(PANASONIC_AIRCON2_HDR_SPACE);
// First 8 uint8_ts
for (int i=0; i<8; i++) {
IR.sendIRbyte(panasonicTemplate[i], PANASONIC_AIRCON2_BIT_MARK, PANASONIC_AIRCON2_ZERO_SPACE, PANASONIC_AIRCON2_ONE_SPACE);
}
// Pause
IR.mark(PANASONIC_AIRCON2_BIT_MARK);
IR.space(PANASONIC_AIRCON2_MSG_SPACE);
// Header
IR.mark(PANASONIC_AIRCON2_HDR_MARK);
IR.space(PANASONIC_AIRCON2_HDR_SPACE);
// Last 19 uint8_ts
for (int i=8; i<27; i++) {
IR.sendIRbyte(panasonicTemplate[i], PANASONIC_AIRCON2_BIT_MARK, PANASONIC_AIRCON2_ZERO_SPACE, PANASONIC_AIRCON2_ONE_SPACE);
}
IR.mark(PANASONIC_AIRCON2_BIT_MARK);
IR.space(0);
}
@@ -0,0 +1,83 @@
/*
Panasonic DKE/JKE/NKE heatpump control (DKE remote control P/N A75C2616 etc)
*/
#ifndef PanasonicHeatpumpIR_h
#define PanasonicHeatpumpIR_h
#include <HeatpumpIR.h>
// Panasonic DKE, JKE & NKE timing constants (DKE remote control P/N A75C2616)
#define PANASONIC_AIRCON2_HDR_MARK 3500
#define PANASONIC_AIRCON2_HDR_SPACE 1800
#define PANASONIC_AIRCON2_BIT_MARK 420
#define PANASONIC_AIRCON2_ONE_SPACE 1350
#define PANASONIC_AIRCON2_ZERO_SPACE 470
#define PANASONIC_AIRCON2_MSG_SPACE 10000
// Panasonic DKE, JNE & NKE codes
#define PANASONIC_AIRCON2_MODE_AUTO 0x00 // Operating mode
#define PANASONIC_AIRCON2_MODE_HEAT 0x40
#define PANASONIC_AIRCON2_MODE_COOL 0x30
#define PANASONIC_AIRCON2_MODE_DRY 0x20
#define PANASONIC_AIRCON2_MODE_FAN 0x60
#define PANASONIC_AIRCON2_MODE_OFF 0x00 // Power OFF
#define PANASONIC_AIRCON2_MODE_ON 0x01
#define PANASONIC_AIRCON2_TIMER_CNL 0x08
#define PANASONIC_AIRCON2_FAN_AUTO 0xA0 // Fan speed
#define PANASONIC_AIRCON2_FAN1 0x30
#define PANASONIC_AIRCON2_FAN2 0x40
#define PANASONIC_AIRCON2_FAN3 0x50
#define PANASONIC_AIRCON2_FAN4 0x60
#define PANASONIC_AIRCON2_FAN5 0x70
#define PANASONIC_AIRCON2_VS_AUTO 0x0F // Vertical swing
#define PANASONIC_AIRCON2_VS_UP 0x01
#define PANASONIC_AIRCON2_VS_MUP 0x02
#define PANASONIC_AIRCON2_VS_MIDDLE 0x03
#define PANASONIC_AIRCON2_VS_MDOWN 0x04
#define PANASONIC_AIRCON2_VS_DOWN 0x05
#define PANASONIC_AIRCON2_HS_AUTO 0x0D // Horizontal swing
#define PANASONIC_AIRCON2_HS_MIDDLE 0x06
#define PANASONIC_AIRCON2_HS_LEFT 0x09
#define PANASONIC_AIRCON2_HS_MLEFT 0x0A
#define PANASONIC_AIRCON2_HS_MRIGHT 0x0B
#define PANASONIC_AIRCON2_HS_RIGHT 0x0C
// Panasonic model codes
#define PANASONIC_DKE 0
#define PANASONIC_JKE 1
#define PANASONIC_NKE 2
class PanasonicHeatpumpIR : public HeatpumpIR
{
protected: // Cannot create generic Panasonic heatpump instances
PanasonicHeatpumpIR();
uint8_t _panasonicModel; // Tells whether this is DKE, NKE or JKE (or other supported model...)
public:
void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd);
private:
void sendPanasonic(IRSender& IR, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH);
};
class PanasonicDKEHeatpumpIR : public PanasonicHeatpumpIR
{
public:
PanasonicDKEHeatpumpIR();
};
class PanasonicJKEHeatpumpIR : public PanasonicHeatpumpIR
{
public:
PanasonicJKEHeatpumpIR();
};
class PanasonicNKEHeatpumpIR : public PanasonicHeatpumpIR
{
public:
PanasonicNKEHeatpumpIR();
};
#endif
+22
View File
@@ -0,0 +1,22 @@
# arduino-heatpumpir
Original location: https://github.com/ToniA/arduino-heatpumpir
An Arduino library to control a Panasonic, Midea, Carrier, Fujitsu or Mitsubishi heat pump/split unit air conditioner.
Currently supports at least these models
* Panasonic E9/E12-CKP (Panasonic remote control P/N A75C2295)
* Panasonic E9/E12-DKE (Panasonic remote control P/N A75C2616)
* Panasonic E9/E12-JKE and E9/E12-NKE
* Midea MSR1-12HRN1-QC2 + MOA1-12HN1-QC2, sold as Ultimate Pro Plus Basic 13FP in Finland (Midea remote control P/N RG51M1/E)
* Carrier 42NQV035G / 38NYV035H2 (Carrier remote control P/N WH-L05SE)
* Fujitsu Nocria AWYZ14 (remote control P/N AR-PZ2)
* Mitsubishi MSZ FD-25, probably also FD-35 (remote control P/N KM09D 0052376)
* Hisense AUD (remote control Y-H1-01, Y-H1-02(E), Y-J1, Y-E4-07) probably AUC model
## Instructions
* Download the library, and place it under your personal Arduino 'libraries' directory, under directory 'HeatpumpIR'
* See the example sketches
![Schema](https://raw.github.com/ToniA/arduino-heatpumpir/master/arduino_irsender.png)
+175
View File
@@ -0,0 +1,175 @@
#include <SamsungHeatpumpIR.h>
SamsungHeatpumpIR::SamsungHeatpumpIR() : HeatpumpIR()
{
static const char PROGMEM model[] PROGMEM = "samsung";
static const char PROGMEM info[] PROGMEM = "{\"mdl\":\"samsung\",\"dn\":\"Samsung\",\"mT\":16,\"xT\":27,\"fs\":4}";
_model = model;
_info = info;
}
void SamsungHeatpumpIR::send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd)
{
(void)swingVCmd;
(void)swingHCmd;
// Sensible defaults for the heat pump mode
uint8_t powerMode = SAMSUNG_AIRCON1_MODE_ON;
uint8_t operatingMode = SAMSUNG_AIRCON1_MODE_HEAT;
uint8_t fanSpeed = SAMSUNG_AIRCON1_FAN_AUTO;
uint8_t temperature = 23;
uint8_t swingV = SAMSUNG_AIRCON1_VS_AUTO;
if (powerModeCmd == POWER_OFF)
{
powerMode = SAMSUNG_AIRCON1_MODE_OFF;
}
else
{
switch (operatingModeCmd)
{
case MODE_AUTO:
operatingMode = SAMSUNG_AIRCON1_MODE_AUTO;
fanSpeedCmd = FAN_AUTO; // Fan speed is always 'AUTO' in AUTO mode
break;
case MODE_HEAT:
operatingMode = SAMSUNG_AIRCON1_MODE_HEAT;
break;
case MODE_COOL:
operatingMode = SAMSUNG_AIRCON1_MODE_COOL;
break;
case MODE_DRY:
operatingMode = SAMSUNG_AIRCON1_MODE_DRY;
fanSpeedCmd = FAN_AUTO; // Fan speed is always 'AUTO' in DRY mode
break;
case MODE_FAN:
operatingMode = SAMSUNG_AIRCON1_MODE_FAN;
if ( fanSpeedCmd == FAN_AUTO ) {
fanSpeedCmd = FAN_1; // Fan speed cannot be 'AUTO' in FAN mode
}
break;
}
}
switch (fanSpeedCmd)
{
case FAN_AUTO:
fanSpeed = SAMSUNG_AIRCON1_FAN_AUTO;
break;
case FAN_1:
fanSpeed = SAMSUNG_AIRCON1_FAN1;
break;
case FAN_2:
fanSpeed = SAMSUNG_AIRCON1_FAN2;
break;
case FAN_3:
fanSpeed = SAMSUNG_AIRCON1_FAN3;
break;
}
if ( temperatureCmd > 15 && temperatureCmd < 28)
{
temperature = temperatureCmd;
}
switch (swingVCmd)
{
case VDIR_SWING:
swingV = SAMSUNG_AIRCON1_VS_SWING;
break;
}
sendSamsung(IR, powerMode, operatingMode, fanSpeed, temperature, swingV);
}
// Send the Samsung code
void SamsungHeatpumpIR::sendSamsung(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV)
{
uint8_t SamsungTemplate[] = { 0x02, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, // Header part
0x01, 0xD2, 0x0F, 0x00, 0x00, 0x00, 0x00, // Always the same data on POWER messages
0x01, 0x00, 0xFE, 0x71, 0x00, 0x00, 0x00 }; // The actual data is in this part, on uint8_ts 14-20
uint8_t SamsungChecksum = 0;
// Set the power mode on the template message, also add the first part checksum
SamsungTemplate[6] = powerMode;
if ( powerMode == SAMSUNG_AIRCON1_MODE_ON ) {
SamsungTemplate[1] = 0x92;
} else {
SamsungTemplate[1] = 0xB2;
}
SamsungTemplate[20] = powerMode;
// Set the fan speed and the operating mode on the template message
SamsungTemplate[19] = operatingMode | fanSpeed;
// Set the temperature on the template message
SamsungTemplate[18] = (temperature - 16) << 4;
// Set the vertical swing mode on the template message
SamsungTemplate[16] = swingV;
// Calculate the uint8_t 15 checksum
// Count the number of ONE bits on message uint8_ts 15-20
for (uint8_t j=15; j<21; j++) {
uint8_t Samsunguint8_t = SamsungTemplate[j];
for (uint8_t i=0; i<8; i++) {
if ( (Samsunguint8_t & 0x01) == 0x01 ) {
SamsungChecksum++;
}
Samsunguint8_t >>= 1;
}
}
// Transform the number of ONE bits to the actual checksum
SamsungChecksum = 32 - SamsungChecksum;
SamsungChecksum <<= 4;
SamsungChecksum += 0x02;
SamsungTemplate[15] = SamsungChecksum;
// 38 kHz PWM frequency
IR.setFrequency(38);
// Header
IR.mark(SAMSUNG_AIRCON1_HDR_MARK);
IR.space(SAMSUNG_AIRCON1_HDR_SPACE);
// Payload header part
for (int i=0; i<7; i++) {
IR.sendIRbyte(SamsungTemplate[i], SAMSUNG_AIRCON1_BIT_MARK, SAMSUNG_AIRCON1_ZERO_SPACE, SAMSUNG_AIRCON1_ONE_SPACE);
}
// Pause + new header
IR.mark(SAMSUNG_AIRCON1_BIT_MARK);
IR.space(SAMSUNG_AIRCON1_MSG_SPACE);
IR.mark(SAMSUNG_AIRCON1_HDR_MARK);
IR.space(SAMSUNG_AIRCON1_HDR_SPACE);
// Payload power message part
for (int i=7; i<14; i++) {
IR.sendIRbyte(SamsungTemplate[i], SAMSUNG_AIRCON1_BIT_MARK, SAMSUNG_AIRCON1_ZERO_SPACE, SAMSUNG_AIRCON1_ONE_SPACE);
}
// Pause + new header
IR.mark(SAMSUNG_AIRCON1_BIT_MARK);
IR.space(SAMSUNG_AIRCON1_MSG_SPACE);
IR.mark(SAMSUNG_AIRCON1_HDR_MARK);
IR.space(SAMSUNG_AIRCON1_HDR_SPACE);
// Payload data message part
for (int i=14; i<21; i++) {
IR.sendIRbyte(SamsungTemplate[i], SAMSUNG_AIRCON1_BIT_MARK, SAMSUNG_AIRCON1_ZERO_SPACE, SAMSUNG_AIRCON1_ONE_SPACE);
}
// End mark
IR.mark(SAMSUNG_AIRCON1_BIT_MARK);
IR.space(0);
}
+42
View File
@@ -0,0 +1,42 @@
/*
Samsung AQV12PSBN / AQV09ASA heatpump control (remote control P/N zzz)
*/
#ifndef SamsungHeatpumpIR_h
#define SamsungHeatpumpIR_h
#include <HeatpumpIR.h>
// Samsung timing constants
#define SAMSUNG_AIRCON1_HDR_MARK 3000
#define SAMSUNG_AIRCON1_HDR_SPACE 9000
#define SAMSUNG_AIRCON1_BIT_MARK 500
#define SAMSUNG_AIRCON1_ONE_SPACE 1500
#define SAMSUNG_AIRCON1_ZERO_SPACE 500
#define SAMSUNG_AIRCON1_MSG_SPACE 2000
// Samsung codes
#define SAMSUNG_AIRCON1_MODE_AUTO 0x00 // Operating mode
#define SAMSUNG_AIRCON1_MODE_HEAT 0x40
#define SAMSUNG_AIRCON1_MODE_COOL 0x10
#define SAMSUNG_AIRCON1_MODE_DRY 0x20
#define SAMSUNG_AIRCON1_MODE_FAN 0x30
#define SAMSUNG_AIRCON1_MODE_OFF 0xC0 // Power OFF
#define SAMSUNG_AIRCON1_MODE_ON 0xF0 // Power ON
#define SAMSUNG_AIRCON1_FAN_AUTO 0x01 // Fan speed
#define SAMSUNG_AIRCON1_FAN1 0x05 // * low
#define SAMSUNG_AIRCON1_FAN2 0x09 // * med
#define SAMSUNG_AIRCON1_FAN3 0x0B // * high
#define SAMSUNG_AIRCON1_VS_SWING 0xAE // Vertical swing
#define SAMSUNG_AIRCON1_VS_AUTO 0xFE
class SamsungHeatpumpIR : public HeatpumpIR
{
public:
SamsungHeatpumpIR();
void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd);
private:
void sendSamsung(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV);
};
#endif
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

@@ -0,0 +1,48 @@
#include <Arduino.h>
#include <HisenseHeatpumpIR.h>
IRSender irSender(3); // IR led on Duemilanove digital pin 3
HisenseHeatpumpIR *heatpumpIR;
void setup()
{
Serial.begin(9600);
delay(500);
heatpumpIR = new HisenseHeatpumpIR();
Serial.println(F("Starting"));
}
void loop()
{
const char* buf;
Serial.print(F("Sending IR to "));
// Print the model
buf = heatpumpIR->model();
// 'model' is a PROGMEM pointer, so need to write a byte at a time
while (char modelChar = pgm_read_byte(buf++))
{
Serial.print(modelChar);
}
Serial.print(F(", info: "));
// Print the info
buf = heatpumpIR->info();
// 'info' is a PROGMEM pointer, so need to write a byte at a time
while (char infoChar = pgm_read_byte(buf++))
{
Serial.print(infoChar);
}
Serial.println();
heatpumpIR->send(irSender, POWER_ON, MODE_HEAT, FAN_3, 30, VDIR_AUTO, HDIR_AUTO);
delay(1500);
heatpumpIR->send(irSender, POWER_ON, MODE_DRY, FAN_2, 20, VDIR_AUTO, HDIR_AUTO);
delay(1500);
heatpumpIR->send(irSender, POWER_ON, MODE_FAN, FAN_AUTO, 18, VDIR_AUTO, HDIR_AUTO);
delay(1500);
heatpumpIR->send(irSender, POWER_ON, MODE_COOL, FAN_1, 18, VDIR_AUTO, HDIR_AUTO);
delay(2500);
heatpumpIR->send(irSender, POWER_OFF, MODE_COOL, FAN_1, 18, VDIR_AUTO, HDIR_AUTO);
delay(1500);
}
@@ -0,0 +1,93 @@
#include <Arduino.h>
#include <MideaHeatpumpIR.h> // https://github.com/ToniA/arduino-heatpumpir
#include <Button.h> // http://playground.arduino.cc/Code/Button
/*
This is a project I did for my my friend's summer cottage. He has an alarm system
with some extra relays which can be controlled via GSM SMS messages. With two relays
we can have four heatpump states:
* relay 1 OFF and relay 2 in any state: heatpump OFF
* relay 1 ON and relay 2 OFF: heatpump ON, normal heating state
* relay 1 ON and relay 2 ON: heatpump ON, maintenance heating (FP mode on Midea's remote)
The heatpump is 'Ultimate 12 Pro Plus Inverter', sold by ultimatemarket.com:
Midea MSR1U-12HRDN1-QRC4W + MOB-12HFN1-QRC4W, with remote control RG51I20/BGE
This same sketch would also control the older 'Ultimate Pro Plus 13FP':
Midea MSR1-12HRN1-QC2 + MOA1-12HN1-QC2 heatpump control, with remote control RG51M1/E
... and very likely many other Midea heatpumps...
The Midea 'FP', or 'maintenance mode', is not really a mode even if the HeatpumpIR library
interface makes it look like a mode. Instead it's a switch between the 'normal' and 'FP'
mode, so to be absolutely sure about the operating mode, we need to turn the heatpump OFF
first, and then turn it on (goes into 'normal' mode), and finally switch into 'FP'.
The IR led is driven by a transistor amplifier. I used 2 ohms as the resistor. I might sound
very small, but also the led operates on very short bursts. For example Panasonic uses 2 ohm
resistor for the IR led on its remotes.
*/
// Heatpump states
const byte heatpumpOff = 0;
const byte heatpumpNormal = 1;
const byte heatpumpMaintenance = 2;
Button relay1 = Button(11, INPUT_PULLUP); // Heatpump ON-OFF state
Button relay2 = Button(12, INPUT_PULLUP); // FP mode (maintenance heating at 8 degrees C) ON-OFF state
IRSender irSender(9); // IR led on Duemilanove digital pin 9
byte heatpumpState = heatpumpOff;
HeatpumpIR *heatpumpIR = new MideaHeatpumpIR();
void setup(){
Serial.begin(9600);
delay(500);
Serial.println(F("Starting..."));
}
void loop(){
if (relay1.isPressed()) {
// Normal heating operation
if (!relay2.isPressed() && heatpumpState != heatpumpNormal) {
Serial.println(F("Normal heating operation"));
Serial.println(F("* Sending power OFF"));
heatpumpIR->send(irSender, POWER_OFF, MODE_HEAT, FAN_2, 22, VDIR_UP, HDIR_AUTO);
delay(15 * 1000); // 15 seconds, to allow full shutdown
Serial.println(F("* Sending normal heat command"));
heatpumpIR->send(irSender, POWER_ON, MODE_HEAT, FAN_2, 22, VDIR_UP, HDIR_AUTO);
heatpumpState = heatpumpNormal;
// Maintenance heating operation
} else if (relay2.isPressed() && heatpumpState != heatpumpMaintenance) {
Serial.println(F("Maintenance heating operation"));
Serial.println(F("* Sending power OFF"));
heatpumpIR->send(irSender, POWER_OFF, MODE_HEAT, FAN_2, 22, VDIR_UP, HDIR_AUTO);
delay(15 * 1000); // 15 seconds, to allow full shutdown
Serial.println(F("* Sending normal heat command"));
heatpumpIR->send(irSender, POWER_ON, MODE_HEAT, FAN_2, 22, VDIR_UP, HDIR_AUTO);
delay(5 * 1000); // 15 seconds, to allow full start before switching to maintenance
Serial.println(F("* Switching to maintenance heating"));
heatpumpIR->send(irSender, POWER_ON, MODE_MAINT, FAN_3, 10, VDIR_UP, HDIR_AUTO);
heatpumpState = heatpumpMaintenance;
}
// Power OFF
} else if (!relay1.isPressed() && heatpumpState != heatpumpOff) {
heatpumpState = heatpumpOff;
Serial.println(F("Sending power OFF"));
heatpumpIR->send(irSender, POWER_OFF, MODE_HEAT, FAN_2, 22, VDIR_UP, HDIR_AUTO);
}
delay(500); // 0.5 seconds delay
}
@@ -0,0 +1,205 @@
#include <Arduino.h>
// HeatpumpIR libraries
#include <FujitsuHeatpumpIR.h>
#include <PanasonicCKPHeatpumpIR.h>
#include <PanasonicHeatpumpIR.h>
#include <CarrierHeatpumpIR.h>
#include <MideaHeatpumpIR.h>
#include <MitsubishiHeatpumpIR.h>
#include <SamsungHeatpumpIR.h>
// Timer library, https://github.com/ToniA/Timer
#include <Timer.h>
// MySensors libraries
#include <MySensor.h>
#include <SPI.h>
// This MySensors node has three childs, with ID's 1, 2 & 3
#define CHILD_1 1
#define CHILD_2 2
#define CHILD_3 3
// MySensors definitions
MySensor gw;
MyMessage irMsg(CHILD_1, V_IR_RECEIVE);
// Domoticz does not yet properly support the S_IR sensor type
MyMessage textMsg(CHILD_2, V_TEXT);
MyMessage sendMsg(CHILD_3, V_LIGHT);
// Array with all supported heatpump models
HeatpumpIR *heatpumpIR[] = { new PanasonicCKPHeatpumpIR(), // 0, keep this if you don't remove the timer for cancelling Panasonic CKP messages
new PanasonicDKEHeatpumpIR(), // 1
new PanasonicJKEHeatpumpIR(), // 2
new PanasonicNKEHeatpumpIR(), // 3
new CarrierHeatpumpIR(), // 4
new MideaHeatpumpIR(), // 5
new FujitsuHeatpumpIR(), // 6
new MitsubishiFDHeatpumpIR(), // 7
new MitsubishiFEHeatpumpIR(), // 8
new SamsungHeatpumpIR() // 9
};
// IR led on PWM output-capable digital pin 3
IRSender irSender(3);
// Timer for sending Panasonic CKP series timer cancellation commands
Timer timer;
int8_t panasonicCKPTimer = 0;
// Number of supported models
byte models = 0;
void setup()
{
Serial.begin(115200);
delay(1000);
Serial.println(F("HeatpumpIR sensor starting up..."));
models = sizeof(heatpumpIR) / sizeof(HeatpumpIR*);
Serial.print(F("Number of supported models: ")); Serial.println(models);
// Auto-assign the node ID, do not forward messages
gw.begin(incomingMessage, AUTO, false);
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Heatpump Sensor", "1.0");
// Register a sensors to the MySensors Gateway
gw.present(CHILD_1, S_IR, "IR sender");
// For Domoticz
gw.present(CHILD_2, S_INFO, "IR data");
gw.present(CHILD_3, S_LIGHT, "IR send");
// The TEXT sensor is not created in Domoticz before data is sent
gw.send(textMsg.setSensor(CHILD_2).set("00000000"));
}
void loop()
{
gw.process();
timer.update();
}
// Handle incoming messages from the MySensors Gateway
void incomingMessage(const MyMessage &message) {
const char *irData;
// V_IR type message
if (message.type==V_IR_SEND) {
Serial.println(F("Received IR send command..."));
irData = message.getString();
Serial.print(F("Code: 0x"));
Serial.println(irData);
sendHeatpumpIRCommand(irData);
}
// Domoticz
else if (message.type==V_LIGHT) {
// When the button is pressed on Domoticz, request the value of the TEXT sensor
Serial.println(F("Requesting IR code from Domoticz..."));
gw.request(CHILD_2, V_TEXT, 0);
} else if (message.type==V_TEXT) {
// TEXT sensor value is received as a result of the previous step
Serial.println(F("IR code received from Domoticz..."));
Serial.print(F("Code: 0x"));
irData = message.getString();
Serial.println(irData);
sendHeatpumpIRCommand(irData);
// Set the Domoticz switch back to 'OFF' state
gw.send(sendMsg.setSensor(CHILD_3).set(0));
}
}
// Decode the IR command and send the IR command to the heatpump
void sendHeatpumpIRCommand(const char *irCommandString) {
// irCommandString is an 8-digit hex digit
long irCommand = 0;
int sscanfStatus = sscanf(irCommandString, "%lx", &irCommand);
if (sscanfStatus == 1) {
Serial.print(F("IR code conversion OK: 0x"));
Serial.println(irCommand, HEX);
} else {
Serial.println(F("Failed to convert IR hex code to number"));
}
/*
The heatpump command is packed into a 32-bit hex number, see
libraries\HeatpumpIR\HeatpumpIR.h for the constants
12345678
3 MODEL
4 POWER
5 OPERATING MODE
6 FAN SPEED
78 TEMPERATURE IN HEX
00213416 (as an example of a valid code)
2 = PanasonicJKE
1 = Power ON
3 = COOL
4 = FAN 4
16 = Temperature 22 degrees (0x16 = 22)
*/
byte model = (irCommand & 0x00F00000) >> 20;
byte power = (irCommand & 0x00010000) >> 16;
byte mode = (irCommand & 0x0000F000) >> 12;
byte fan = (irCommand & 0x00000F00) >> 8;
byte temp = (irCommand & 0x000000FF);
const char* buf;
Serial.print(F("Model: "));
buf = heatpumpIR[model]->model();
// 'model' is a PROGMEM pointer, so need to write a byte at a time
while (char modelChar = pgm_read_byte(buf++))
{
Serial.print(modelChar);
}
Serial.println();
Serial.print(F("Model #: ")); Serial.println(model);
Serial.print(F("Power: ")); Serial.println(power);
Serial.print(F("Mode: ")); Serial.println(mode);
Serial.print(F("Fan: ")); Serial.println(fan);
Serial.print(F("Temp: ")); Serial.println(temp);
// Heatpump models start from 0, i.e. model number is always less than the number of different models
if (model < models) {
// This is a supported model
// Cancel the timer on Panasonic CKP heatpump
if (model == 0) {
Serial.println(F("Cancelling timer on Panasonic CKP heatpump..."));
timer.stop(panasonicCKPTimer);
}
Serial.println(F("All OK - sending IR command to heatpump..."));
heatpumpIR[model]->send(irSender, power, mode, fan, temp, VDIR_UP, HDIR_AUTO);
if (model == 0) {
Serial.println(F("Scheduling timer cancellation on Panasonic CKP heatpump..."));
panasonicCKPTimer = timer.after(120000, panasonicCancelTimer); // Called after 2 minutes
}
}
}
// Cancel the timer on the Panasonic CKP heatpump
void panasonicCancelTimer()
{
PanasonicCKPHeatpumpIR *panasonicCKPHeatpumpIR = new PanasonicCKPHeatpumpIR();
panasonicCKPHeatpumpIR->sendPanasonicCKPCancelTimer(irSender);
Serial.println(F("The TIMER led should now be OFF"));
}
@@ -0,0 +1,73 @@
# MySensors example
This example shows how HeatpumpIR could be integrated with the MySensors sensor network. As this sensor sends infrared signals,
the natural sensor type is 'S_IR'. The heatpump/air conditioner signals are really long, so the signals need to be encoded somehow.
In this example the idea is to encode just this information, using 24 bits of data
* model code (see the index of the array heatpumpIR in the code)
* power state (for the rest, see the HeatpumpIR.h file for the constants)
* operating mode
* fan speed
* temperature (the temperature is directly the temperature in HEX)
```
An example:
00213416 (as an example of a valid code)
2 = PanasonicJKE
1 = Power ON
3 = COOL
4 = FAN 4
16 = Temperature 22 degrees (0x16 = 22)
```
This sketch creates three sensors:
* S_IR
* S_INFO
* S_LIGHT
The S_INFO and S_LIGHT sensors are for Domoticz, as it does not currently (as of V2.3093) support infrared code sending through the S_IR sensor type.
## Sending the code
### MYSController
* Select the IR sensor on the 'Heatpump Sensor 1.0' node
* Select 'IR_SEND' as the subtype
* Enter for example '00213416' as the payload
### Domoticz
As Domoticz does not really support sending IR code yet, the workaround is to use two sensors. When the node is connected, Domoticz should find two new devices
* A 'Lighting 2 / AC' type sensor with name 'IR send'
* A 'General / Text' type sensor with name 'Unknown' and Data '00000000'
To send a command, first add the sensors. Then update the data of the text sensor to contain the IR code to be sent, and then flip the switch to trigger the sending.
The data can be updated through the JSON REST API, or through the Lua event scripts.
REST API example (assuming that the idx of the text sensor is 105 and the switch is 104, and Domoticz is at 192.168.0.4:8080):
```
http://192.168.0.4:8080/json.htm?type=command&param=udevice&idx=105&nvalue=0&svalue=00213416
http://192.168.0.4:8080/json.htm?type=command&param=switchlight&idx=104&switchcmd=On
```
Lua example:
* Seems that the text sensor can't be updated through the commandArray...
* So now when a device called 'trigger' changes, the text sensor value is first updated to '00213416', and then the IR signal is sent
```
commandArray = {}
for key, value in pairs(devicechanged) do
if (key == 'trigger') then
print("Heatpump script")
commandArray['OpenURL']='http://192.168.0.4:8080/json.htm?type=command&param=udevice&idx=105&nvalue=0&svalue=00213416'
commandArray['IR send']='On'
end
end
return commandArray
```
## Hardware
Use any ATMega328-based Arduino, connect the radio, and finally connect an infrared led (in series with a resistor, like 100 Ohm) between digital pin 3 and GND.
@@ -0,0 +1,58 @@
#include <Arduino.h>
#include <PanasonicCKPHeatpumpIR.h>
#include <Timer.h> // https://github.com/JChristensen/Timer
/*
This schema demonstrates how to control the Panasonic CKP power state change. The CKP does not have discrete
'ON' and 'OFF' commands, but only a state switch. So, if the initial power state is now known, switching the
state does not help much.
Luckily this can be implemented by using the timer:
* The 'send' command will send all the settings AND program the timer so that the pump will turn ON in a minute
* The 'sendPanasonicCKPCancelTimer' will cancel the timer
The 'turn OFF' must be implemented the same way.
Of course you can choose to not turn off the timer, but that means that the heatpump will attempt to switch ON
(or OFF) every day at the same time, as the timer will still be active.
*/
IRSender irSender(3); // IR led on Duemilanove digital pin 3
PanasonicCKPHeatpumpIR *heatpumpIR;
Timer timer;
void setup()
{
Serial.begin(9600);
delay(500);
heatpumpIR = new PanasonicCKPHeatpumpIR();
Serial.println("Turning the Panasonic CKP heatpump ON by using the timer");
heatpumpIR->send(irSender, POWER_ON, MODE_HEAT, FAN_2, 24, VDIR_UP, HDIR_AUTO);
Serial.println("The heatpump should have beeped, and the TIMER led should be ON");
timer.after(60000, panasonicIsOn); // Called after 1 minute
timer.after(120000, panasonicCancelTimer); // Called after 2 minutes
}
void loop()
{
timer.update();
}
void panasonicIsOn()
{
Serial.println("The heatpump should should turn ON by now, the TIMER led is still ON");
}
void panasonicCancelTimer()
{
heatpumpIR->sendPanasonicCKPCancelTimer(irSender);
Serial.println("The TIMER led should now be OFF");
}
@@ -0,0 +1,59 @@
#include <Arduino.h>
#include <FujitsuHeatpumpIR.h>
#include <PanasonicCKPHeatpumpIR.h>
#include <PanasonicHeatpumpIR.h>
#include <CarrierHeatpumpIR.h>
#include <MideaHeatpumpIR.h>
#include <MitsubishiHeatpumpIR.h>
#include <SamsungHeatpumpIR.h>
IRSender irSender(3); // IR led on Duemilanove digital pin 3
// Array with all supported heatpumps
HeatpumpIR *heatpumpIR[] = {new PanasonicCKPHeatpumpIR(), new PanasonicDKEHeatpumpIR(), new PanasonicJKEHeatpumpIR(),
new PanasonicNKEHeatpumpIR(), new CarrierHeatpumpIR(), new MideaHeatpumpIR(),
new FujitsuHeatpumpIR(), new MitsubishiFDHeatpumpIR(), new MitsubishiFEHeatpumpIR(),
new SamsungHeatpumpIR(), NULL};
void setup()
{
Serial.begin(9600);
delay(500);
Serial.println(F("Starting"));
}
void loop()
{
int i = 0;
const char* buf;
do {
// Send the same IR command to all supported heatpumps
Serial.print(F("Sending IR to "));
buf = heatpumpIR[i]->model();
// 'model' is a PROGMEM pointer, so need to write a byte at a time
while (char modelChar = pgm_read_byte(buf++))
{
Serial.print(modelChar);
}
Serial.print(F(", info: "));
buf = heatpumpIR[i]->info();
// 'info' is a PROGMEM pointer, so need to write a byte at a time
while (char infoChar = pgm_read_byte(buf++))
{
Serial.print(infoChar);
}
Serial.println();
// Send the IR command
heatpumpIR[i]->send(irSender, POWER_ON, MODE_HEAT, FAN_2, 24, VDIR_UP, HDIR_AUTO);
delay(500);
}
while (heatpumpIR[++i] != NULL);
delay(2000);
}
+26
View File
@@ -0,0 +1,26 @@
HeatpumpIR KEYWORD1
PanasonicHeatpumpIR KEYWORD1
PanasonicCKPHeatpumpIR KEYWORD1
PanasonicDKEHeatpumpIR KEYWORD1
PanasonicJKEHeatpumpIR KEYWORD1
PanasonicNKEHeatpumpIR KEYWORD1
CarrierHeatpumpIR KEYWORD1
MideaHeatpumpIR KEYWORD1
FujitsuHeatpumpIR KEYWORD1
MitsubishiHeatpumpIR KEYWORD1
MitsubishiFDHeatpumpIR KEYWORD1
MitsubishiFEHeatpumpIR KEYWORD1
SamsungHeatpumpIR KEYWORD1
IRSender KEYWORD1
setFrequency KEYWORD2
sendIRByte KEYWORD2
bitReverse KEYWORD2
space KEYWORD2
mark KEYWORD2
send KEYWORD2
model KEYWORD2
info KEYWORD2
sendPanasonicCKPCancelTimer KEYWORD2
+3
View File
@@ -26,6 +26,9 @@
// to see what is actually is happening when developing
#define DEBUG
// Disable this line, If you are using TX(1), RX(0) as normal I/O pin
#define ENABLED_SERIAL
// Serial output baud rate (for debug prints and serial gateway)
#define BAUD_RATE 115200
+12 -3
View File
@@ -17,6 +17,8 @@
* version 2 as published by the Free Software Foundation.
*/
#ifdef ARDUINO_ARCH_AVR
#include "MyHw.h"
#include "MyHwATMega328.h"
@@ -119,7 +121,9 @@ void powerDown(period_t period) {
void MyHwATMega328::internalSleep(unsigned long ms) {
// Let serial prints finish (debug, log etc)
Serial.flush();
#ifdef ENABLED_SERIAL
Serial.flush();
#endif
pinIntTrigger = 0;
while (!pinIntTrigger && ms >= 8000) { powerDown(SLEEP_8S); ms -= 8000; }
if (!pinIntTrigger && ms >= 4000) { powerDown(SLEEP_4S); ms -= 4000; }
@@ -148,7 +152,9 @@ bool MyHwATMega328::sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
pinTriggeredWakeup = false;
}
} else {
Serial.flush();
#ifdef ENABLED_SERIAL
Serial.flush();
#endif
powerDown(SLEEP_FOREVER);
}
detachInterrupt(interrupt);
@@ -166,7 +172,9 @@ inline uint8_t MyHwATMega328::sleep(uint8_t interrupt1, uint8_t mode1, uint8_t i
retVal = -1;
}
} else {
Serial.flush();
#ifdef ENABLED_SERIAL
Serial.flush();
#endif
powerDown(SLEEP_FOREVER);
}
detachInterrupt(interrupt1);
@@ -209,3 +217,4 @@ void MyHwATMega328::debugPrint(bool isGW, const char *fmt, ... ) {
}
#endif
#endif // #ifdef ARDUINO_ARCH_AVR
+3
View File
@@ -20,6 +20,8 @@
#ifndef MyHwATMega328_h
#define MyHwATMega328_h
#ifdef ARDUINO_ARCH_AVR
#include "MyHw.h"
#include "MyConfig.h"
#include "MyMessage.h"
@@ -116,3 +118,4 @@ private:
void internalSleep(unsigned long ms);
};
#endif
#endif // #ifdef ARDUINO_ARCH_AVR
+184
View File
@@ -0,0 +1,184 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2015 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#ifdef ARDUINO_ARCH_ESP8266
#include "MyHw.h"
#include "MyHwESP8266.h"
#include <EEPROM.h>
/*
int8_t pinIntTrigger = 0;
void wakeUp() //place to send the interrupts
{
pinIntTrigger = 1;
}
void wakeUp2() //place to send the second interrupts
{
pinIntTrigger = 2;
}
// Watchdog Timer interrupt service routine. This routine is required
// to allow automatic WDIF and WDIE bit clearance in hardware.
ISR (WDT_vect)
{
// WDIE & WDIF is cleared in hardware upon entering this ISR
wdt_disable();
}
*/
static void hw_initConfigBlock( size_t length = 1024 /*ATMega328 has 1024 bytes*/ )
{
static bool initDone = false;
if (!initDone)
{
EEPROM.begin(length);
initDone = true;
}
}
void hw_readConfigBlock(void* buf, void* adr, size_t length)
{
hw_initConfigBlock();
uint8_t* dst = static_cast<uint8_t*>(buf);
int offs = reinterpret_cast<int>(adr);
while (length-- > 0)
{
*dst++ = EEPROM.read(offs++);
}
}
void hw_writeConfigBlock(void* buf, void* adr, size_t length)
{
hw_initConfigBlock();
uint8_t* src = static_cast<uint8_t*>(buf);
int offs = reinterpret_cast<int>(adr);
while (length-- > 0)
{
EEPROM.write(offs++, *src++);
}
EEPROM.commit();
}
uint8_t hw_readConfig(int adr)
{
uint8_t value;
hw_readConfigBlock(&value, reinterpret_cast<void*>(adr), 1);
return value;
}
void hw_writeConfig(int adr, uint8_t value)
{
uint8_t curr = hw_readConfig(adr);
if (curr != value)
{
hw_writeConfigBlock(&value, reinterpret_cast<void*>(adr), 1);
}
}
MyHwESP8266::MyHwESP8266() : MyHw()
{
}
/*
// The following was redifined as macros to save space
inline uint8_t MyHwATMega328::readConfig(uint8_t pos) {
return eeprom_read_byte((uint8_t*)pos);
}
inline void MyHwATMega328::writeConfig(uint8_t pos, uint8_t value) {
eeprom_update_byte((uint8_t*)pos, value);
}
inline void MyHwATMega328::readConfigBlock(void* buf, void * pos, size_t length) {
eeprom_read_block(buf, (void*)pos, length);
}
inline void MyHwATMega328::writeConfigBlock(void* pos, void* buf, size_t length) {
eeprom_write_block((void*)pos, (void*)buf, length);
}
inline void MyHwATMega328::init() {
Serial.begin(BAUD_RATE);
}
inline void MyHwATMega328::watchdogReset() {
wdt_reset();
}
inline void MyHwATMega328::reboot() {
wdt_enable(WDTO_15MS); for (;;);
}
inline unsigned long MyHwATMega328::millis() {
return ::millis();
}
*/
void MyHwESP8266::sleep(unsigned long ms) {
// TODO: Not supported!
}
bool MyHwESP8266::sleep(uint8_t interrupt, uint8_t mode, unsigned long ms) {
// TODO: Not supported!
return false;
}
inline uint8_t MyHwESP8266::sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms) {
// TODO: Not supported!
return 0;
}
#ifdef DEBUG
void MyHwESP8266::debugPrint(bool isGW, const char *fmt, ... ) {
char fmtBuffer[300];
if (isGW) {
// prepend debug message to be handled correctly by controller (C_INTERNAL, I_LOG_MESSAGE)
snprintf_P(fmtBuffer, 299, PSTR("0;0;%d;0;%d;"), C_INTERNAL, I_LOG_MESSAGE);
Serial.print(fmtBuffer);
}
va_list args;
va_start (args, fmt );
va_end (args);
if (isGW) {
// Truncate message if this is gateway node
vsnprintf_P(fmtBuffer, 60, fmt, args);
fmtBuffer[59] = '\n';
fmtBuffer[60] = '\0';
} else {
vsnprintf_P(fmtBuffer, 299, fmt, args);
}
va_end (args);
Serial.print(fmtBuffer);
Serial.flush();
//Serial.write(freeRam());
}
#endif
#endif // #ifdef ARDUINO_ARCH_ESP8266
+86
View File
@@ -0,0 +1,86 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2015 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#ifdef ARDUINO_ARCH_ESP8266
#ifndef MyHwESP8266_h
#define MyHwESP8266_h
#include "MyHw.h"
#include "MyConfig.h"
#include "MyMessage.h"
#ifdef __cplusplus
#include <Arduino.h>
#include <SPI.h>
#endif
// Define these as macros to save valuable space
#define hw_digitalWrite(__pin, __value) (digitalWrite(__pin, __value))
#define hw_init() Serial.begin(BAUD_RATE)
#define hw_watchdogReset() wdt_reset()
#define hw_reboot() wdt_enable(WDTO_15MS); while (1)
#define hw_millis() millis()
void hw_readConfigBlock(void* buf, void* adr, size_t length);
void hw_writeConfigBlock(void* buf, void* adr, size_t length);
void hw_writeConfig(int adr, uint8_t value);
uint8_t hw_readConfig(int adr);
enum period_t
{
SLEEP_15Ms,
SLEEP_30MS,
SLEEP_60MS,
SLEEP_120MS,
SLEEP_250MS,
SLEEP_500MS,
SLEEP_1S,
SLEEP_2S,
SLEEP_4S,
SLEEP_8S,
SLEEP_FOREVER
};
class MyHwESP8266 : public MyHw
{
public:
MyHwESP8266();
/* void init();
void watchdogReset();
void reboot();
unsigned long millis();
uint8_t readConfig(uint8_t pos);
void writeConfig(uint8_t pos, uint8_t value);
void readConfigBlock(void* buf, void * pos, size_t length);
void writeConfigBlock(void* pos, void* buf, size_t length); */
void sleep(unsigned long ms);
bool sleep(uint8_t interrupt, uint8_t mode, unsigned long ms);
uint8_t sleep(uint8_t interrupt1, uint8_t mode1, uint8_t interrupt2, uint8_t mode2, unsigned long ms);
#ifdef DEBUG
void debugPrint(bool isGW, const char *fmt, ... );
#endif
};
#endif
#endif // #ifdef ARDUINO_ARCH_ESP8266
+2
View File
@@ -167,7 +167,9 @@ typedef enum {
#ifndef BIT
#define BIT(n) ( 1<<(n) )
#endif
// Create a bitmask of length len.
#define BIT_MASK(len) ( BIT(len)-1 )
// Create a bitfield mask of length starting at bit 'start'.
+6 -6
View File
@@ -95,14 +95,12 @@ bool MySensor::isValidFirmware() {
#ifdef WITH_LEDS_BLINKING
void MySensor::handleLedsBlinking() {
static unsigned long next_time = hw_millis() + ledBlinkPeriod;
// Just return if it is not the time...
// http://playground.arduino.cc/Code/TimingRollover
if ((long)(hw_millis() - next_time) < 0)
if ((long)(hw_millis() - blink_next_time) < 0)
return;
else
next_time = hw_millis() + ledBlinkPeriod;
blink_next_time = hw_millis() + ledBlinkPeriod;
// do the actual blinking
if(countRx && countRx != 255) {
@@ -153,7 +151,9 @@ void MySensor::errBlink(uint8_t cnt) {
#endif
void MySensor::begin(void (*_msgCallback)(const MyMessage &), uint8_t _nodeId, boolean _repeaterMode, uint8_t _parentNodeId) {
hw_init();
#ifdef ENABLED_SERIAL
hw_init();
#endif
repeaterMode = _repeaterMode;
msgCallback = _msgCallback;
failedTransmissions = 0;
@@ -567,7 +567,7 @@ boolean MySensor::process() {
#ifdef MY_SIGNING_FEATURE
// Before processing message, reject unsigned messages if signing is required and check signature (if it is signed and addressed to us)
// Note that we do not care at all about any signature found if we do not require signing, nor do we care about ACKs (they are never signed)
if (signer.requestSignatures() && msg.destination == nc.nodeId && mGetLength(msg) && !mGetAck(msg) &&
if ((!isGateway || DO_SIGN(msg.sender)) && signer.requestSignatures() && msg.destination == nc.nodeId && mGetLength(msg) && !mGetAck(msg) &&
(mGetCommand(msg) != C_INTERNAL ||
(msg.type != I_GET_NONCE_RESPONSE && msg.type != I_GET_NONCE && msg.type != I_REQUEST_SIGNING &&
msg.type != I_ID_REQUEST && msg.type != I_ID_RESPONSE &&
+6
View File
@@ -41,8 +41,13 @@
// Set the hardware driver to use (initialized by MySensor-class)
//#if defined __AVR_ATmega328P__
#if defined(ARDUINO_ARCH_ESP8266)
#include "MyHwESP8266.h"
typedef MyHwESP8266 MyHwDriver;
#elif defined(ARDUINO_ARCH_AVR)
#include "MyHwATMega328.h"
typedef MyHwATMega328 MyHwDriver;
#endif
//#endif
@@ -354,6 +359,7 @@ class MySensor
unsigned long ledBlinkPeriod;
void handleLedsBlinking(); // do the actual blinking
unsigned long blink_next_time;
#endif
MyTransport& radio;
@@ -24,6 +24,7 @@
* HMAC-SHA256 authentication with a readout-protected key.
*
*/
#if defined(ARDUINO_ARCH_AVR)
#include "MySigning.h"
#include "MySigningAtsha204.h"
@@ -266,3 +267,4 @@ uint8_t* MySigningAtsha204::sha256(const uint8_t* data, size_t sz) {
DEBUG_SIGNING_PRINTBUF(F("SHA:"), &rx_buffer[SHA204_BUFFER_POS_DATA], 32);
return &rx_buffer[SHA204_BUFFER_POS_DATA];
}
#endif // #if defined(ARDUINO_ARCH_AVR)
+1 -1
View File
@@ -33,7 +33,7 @@ bool MyTransportRFM69::init() {
// Start up the radio library (_address will be set later by the MySensors library)
radio.initialize(_freqBand, _address, _networkId);
#ifdef RFM69_ENABLE_ENCRYPTION
radio.encrypt(ENCRYPTKEY);
radio.encrypt(RFM69_ENCRYPTKEY);
#endif
return true;
}
@@ -75,7 +75,7 @@ void setup()
digitalWrite(SECONDARY_BUTTON_PIN, HIGH);
// Send the sketch version information to the gateway and Controller
sensor_node.sendSketchInfo(SKETCH_NAME, SKETCH_MAJOR_VER"."SKETCH_MINOR_VER);
sensor_node.sendSketchInfo(SKETCH_NAME, SKETCH_MAJOR_VER "." SKETCH_MINOR_VER);
// Register binary input sensor to sensor_node (they will be created as child devices)
// You can use S_DOOR, S_MOTION or S_LIGHT here depending on your usage.
@@ -0,0 +1,265 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2015 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
*******************************
*
* REVISION HISTORY
* Version 1.0 - Henrik EKblad
* Contribution by a-lurker and Anticimex,
* Contribution by Norbert Truchsess <norbert.truchsess@t-online.de>
* Contribution by Ivo Pullens (ESP8266 support)
*
* DESCRIPTION
* The EthernetGateway sends data received from sensors to the WiFi link.
* The gateway also accepts input on ethernet interface, which is then sent out to the radio network.
*
* VERA CONFIGURATION:
* Enter "ip-number:port" in the ip-field of the Arduino GW device. This will temporarily override any serial configuration for the Vera plugin.
* E.g. If you want to use the defualt values in this sketch enter: 192.168.178.66:5003
*
* LED purposes:
* - To use the feature, uncomment WITH_LEDS_BLINKING in MyConfig.h
* - RX (green) - blink fast on radio message recieved. In inclusion mode will blink fast only on presentation recieved
* - TX (yellow) - blink fast on radio message transmitted. In inclusion mode will blink slowly
* - ERR (red) - fast blink on error during transmission error or recieve crc error
*
* See http://www.mysensors.org/build/ethernet_gateway for wiring instructions.
* The ESP8266 however requires different wiring:
* nRF24L01+ ESP8266
* VCC VCC
* CE GPIO4
* CSN/CS GPIO15
* SCK GPIO14
* MISO GPIO12
* MOSI GPIO13
*
* Not all ESP8266 modules have all pins available on their external interface.
* This code has been tested on an ESP-12 module.
* The ESP8266 requires a certain pin configuration to download code, and another one to run code:
* - Connect REST (reset) via 10K pullup resistor to VCC, and via switch to GND ('reset switch')
* - Connect GPIO15 via 10K pulldown resistor to GND
* - Connect CH_PD via 10K resistor to VCC
* - Connect GPIO2 via 10K resistor to VCC
* - Connect GPIO0 via 10K resistor to VCC, and via switch to GND ('bootload switch')
*
* Inclusion mode button:
* - Connect GPIO5 via switch to GND ('inclusion switch')
*
* Hardware SHA204 signing is currently not supported!
*
* Make sure to fill in your ssid and WiFi password below for ssid & pass.
*/
#define NO_PORTB_PINCHANGES
#include <SPI.h>
#include <MySigningNone.h>
#include <MySigningAtsha204Soft.h>
#include <MyTransportNRF24.h>
#include <MyTransportRFM69.h>
#include <EEPROM.h>
#include <MyHwESP8266.h>
#include <ESP8266WiFi.h>
#include <MyParserSerial.h>
#include <MySensor.h>
#include <stdarg.h>
#include "GatewayUtil.h"
const char *ssid = "MySSID"; // cannot be longer than 32 characters!
const char *pass = "MyVerySecretPassword"; //
#define INCLUSION_MODE_TIME 1 // Number of minutes inclusion mode is enabled
#define INCLUSION_MODE_PIN 5 // Digital pin used for inclusion mode button
#define RADIO_CE_PIN 4 // radio chip enable
#define RADIO_SPI_SS_PIN 15 // radio SPI serial select
#ifdef WITH_LEDS_BLINKING
#define RADIO_ERROR_LED_PIN 7 // Error led pin
#define RADIO_RX_LED_PIN 8 // Receive led pin
#define RADIO_TX_LED_PIN 9 // the PCB, on board LED
#endif
// NRFRF24L01 radio driver (set low transmit power by default)
MyTransportNRF24 transport(RADIO_CE_PIN, RADIO_SPI_SS_PIN, RF24_PA_LEVEL_GW);
//MyTransportRFM69 transport;
// Message signing driver (signer needed if MY_SIGNING_FEATURE is turned on in MyConfig.h)
#ifdef MY_SIGNING_FEATURE
MySigningNone signer;
//MySigningAtsha204Soft signer;
#endif
// Hardware profile
MyHwESP8266 hw;
// Construct MySensors library (signer needed if MY_SIGNING_FEATURE is turned on in MyConfig.h)
// To use LEDs blinking, uncomment WITH_LEDS_BLINKING in MyConfig.h
MySensor gw(transport, hw
#ifdef MY_SIGNING_FEATURE
, signer
#endif
#ifdef WITH_LEDS_BLINKING
, RADIO_RX_LED_PIN, RADIO_TX_LED_PIN, RADIO_ERROR_LED_PIN
#endif
);
#define IP_PORT 5003 // The port you want to open
#define MAX_SRV_CLIENTS 5 // how many clients should be able to telnet to this ESP8266
// a R/W server on the port
static WiFiServer server(IP_PORT);
static WiFiClient clients[MAX_SRV_CLIENTS];
static bool clientsConnected[MAX_SRV_CLIENTS];
static inputBuffer inputString[MAX_SRV_CLIENTS];
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
void output(const char *fmt, ... )
{
char serialBuffer[MAX_SEND_LENGTH];
va_list args;
va_start (args, fmt );
vsnprintf_P(serialBuffer, MAX_SEND_LENGTH, fmt, args);
va_end (args);
Serial.print(serialBuffer);
for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++)
{
if (clients[i] && clients[i].connected())
{
// Serial.print("Client "); Serial.print(i); Serial.println(" write");
clients[i].write((uint8_t*)serialBuffer, strlen(serialBuffer));
}
}
}
void setup()
{
// Setup console
hw_init();
Serial.println(); Serial.println();
Serial.println("ESP8266 MySensors Gateway");
Serial.print("Connecting to "); Serial.println(ssid);
(void)WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("Connected!");
Serial.print("IP: "); Serial.println(WiFi.localIP());
Serial.flush();
setupGateway(INCLUSION_MODE_PIN, INCLUSION_MODE_TIME, output);
// Initialize gateway at maximum PA level, channel 70 and callback for write operations
gw.begin(incomingMessage, 0, true, 0);
// start listening for clients
server.begin();
server.setNoDelay(true);
}
void loop() {
gw.process();
checkButtonTriggeredInclusion();
checkInclusionFinished();
// Go over list of clients and stop any that are no longer connected.
// If the server has a new client connection it will be assigned to a free slot.
bool allSlotsOccupied = true;
for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++)
{
if (!clients[i].connected())
{
if (clientsConnected[i])
{
Serial.print("Client "); Serial.print(i); Serial.println(" disconnected");
clients[i].stop();
}
//check if there are any new clients
if (server.hasClient())
{
clients[i] = server.available();
inputString[i].idx = 0;
Serial.print("Client "); Serial.print(i); Serial.println(" connected");
output(PSTR("0;0;%d;0;%d;Gateway startup complete.\n"), C_INTERNAL, I_GATEWAY_READY);
}
}
bool connected = clients[i].connected();
clientsConnected[i] = connected;
allSlotsOccupied &= connected;
}
if (allSlotsOccupied && server.hasClient())
{
//no free/disconnected spot so reject
Serial.println("No free slot available");
WiFiClient c = server.available();
c.stop();
}
// Loop over clients connect and read available data
for (uint8_t i = 0; i < ARRAY_SIZE(clients); i++)
{
while(clients[i].connected() && clients[i].available())
{
char inChar = clients[i].read();
if ( inputString[i].idx < MAX_RECEIVE_LENGTH - 1 )
{
// if newline then command is complete
if (inChar == '\n')
{
// a command was issued by the client
// we will now try to send it to the actuator
inputString[i].string[inputString[i].idx] = 0;
// echo the string to the serial port
Serial.print("Client "); Serial.print(i); Serial.print(": "); Serial.println(inputString[i].string);
parseAndSend(gw, inputString[i].string);
// clear the string:
inputString[i].idx = 0;
// Finished with this client's message. Next loop() we'll see if there's more to read.
break;
} else {
// add it to the inputString:
inputString[i].string[inputString[i].idx++] = inChar;
}
} else {
// Incoming message too long. Throw away
Serial.print("Client "); Serial.print(i); Serial.println(": Message too long");
inputString[i].idx = 0;
// Finished with this client's message. Next loop() we'll see if there's more to read.
break;
}
}
}
}
@@ -0,0 +1,136 @@
#ifndef __GATEWAYUTIL_H__
#define __GATEWAYUTIL_H__
#ifdef ARDUINO
static uint8_t inclusionTime = 1; // Number of minutes inclusion mode is enabled
static uint8_t pinInclusion = 3; // Input pin that should trigger inclusion mode
#define MAX_RECEIVE_LENGTH 100 // Max buffersize needed for messages coming from controller
#define MAX_SEND_LENGTH 120 // Max buffersize needed for messages destined for controller
typedef struct
{
char string[MAX_RECEIVE_LENGTH];
uint8_t idx;
} inputBuffer;
static volatile boolean buttonTriggeredInclusion;
static boolean inclusionMode; // Keeps track on inclusion mode
bool inclusionButtonSupported = false;
void (*serial)(const char *fmt, ... );
MyParserSerial parser;
void setInclusionMode(boolean newMode);
char convBuf[MAX_PAYLOAD*2+1];
unsigned long inclusionStartTime;
void startInclusionInterrupt() {
buttonTriggeredInclusion = true;
}
void setupGateway(uint8_t _inc, uint8_t _incTime, void (* _serial)(const char *, ... )) {
inclusionMode = 0;
buttonTriggeredInclusion = false;
serial = _serial;
inclusionTime = _incTime;
inclusionButtonSupported = (_inc != 255);
if (inclusionButtonSupported)
{
pinInclusion = _inc;
// Setup digital in that triggers inclusion mode
pinMode(pinInclusion, INPUT_PULLUP);
// Add interrupt for inclusion button to pin
attachInterrupt(pinInclusion, startInclusionInterrupt, FALLING);
}
}
void incomingMessage(const MyMessage &message) {
// if (mGetCommand(message) == C_PRESENTATION && inclusionMode) {
// gw.rxBlink(3);
// } else {
// gw.rxBlink(1);
// }
// Pass along the message from sensors to serial line
serial(PSTR("%d;%d;%d;%d;%d;%s\n"),message.sender, message.sensor, mGetCommand(message), mGetAck(message), message.type, message.getString(convBuf));
}
void checkButtonTriggeredInclusion() {
if (inclusionButtonSupported)
{
if (buttonTriggeredInclusion) {
// Ok, someone pressed the inclusion button on the gateway
// start inclusion mode for 1 munute.
#ifdef DEBUG
serial(PSTR("0;0;%d;0;%d;Inclusion started by button.\n"), C_INTERNAL, I_LOG_MESSAGE);
#endif
buttonTriggeredInclusion = false;
setInclusionMode(true);
}
}
}
void checkInclusionFinished() {
if (inclusionMode && millis()-inclusionStartTime>60000UL*inclusionTime) {
// inclusionTimeInMinutes minute(s) has passed.. stop inclusion mode
setInclusionMode(false);
}
}
void parseAndSend(MySensor &gw, char *commandBuffer) {
boolean ok;
MyMessage &msg = gw.getLastMessage();
if (parser.parse(msg, commandBuffer)) {
uint8_t command = mGetCommand(msg);
if (msg.destination==GATEWAY_ADDRESS && command==C_INTERNAL) {
// Handle messages directed to gateway
if (msg.type == I_VERSION) {
// Request for version
serial(PSTR("0;0;%d;0;%d;%s\n"), C_INTERNAL, I_VERSION, LIBRARY_VERSION);
} else if (msg.type == I_INCLUSION_MODE) {
// Request to change inclusion mode
setInclusionMode(atoi(msg.data) == 1);
}
} else {
#ifdef WITH_LEDS_BLINKING
gw.txBlink(1);
#endif
ok = gw.sendRoute(msg);
if (!ok) {
#ifdef WITH_LEDS_BLINKING
gw.errBlink(1);
#endif
}
}
}
}
void setInclusionMode(boolean newMode) {
if (newMode != inclusionMode) {
inclusionMode = newMode;
// Send back mode change on serial line to ack command
serial(PSTR("0;0;%d;0;%d;%d\n"), C_INTERNAL, I_INCLUSION_MODE, inclusionMode?1:0);
if (inclusionMode) {
inclusionStartTime = millis();
}
}
}
#else
#error This example is only for use on Arduino.
#endif // ARDUINO
#endif
@@ -0,0 +1,210 @@
#include <Arduino.h>
// HeatpumpIR libraries, https://github.com/ToniA/arduino-heatpumpir
#include <FujitsuHeatpumpIR.h>
#include <PanasonicCKPHeatpumpIR.h>
#include <PanasonicHeatpumpIR.h>
#include <CarrierHeatpumpIR.h>
#include <MideaHeatpumpIR.h>
#include <MitsubishiHeatpumpIR.h>
#include <SamsungHeatpumpIR.h>
// Timer library, https://github.com/JChristensen/Timer
#include <Timer.h>
// MySensors libraries
#include <MySensor.h>
#include <SPI.h>
// Child ID's of this node
#define CHILD_1 1
// MySensors definitions
MySensor gw;
MyMessage irMsg(CHILD_1, V_IR_RECEIVE);
// -- Hacks for Domoticz start
#define CHILD_2 2
#define CHILD_3 3
// Domoticz does not yet properly support the S_IR sensor type
MyMessage textMsg(CHILD_2, V_TEXT);
MyMessage sendMsg(CHILD_3, V_LIGHT);
// -- Hacks for Domoticz end
// Array with all supported heatpump models
HeatpumpIR *heatpumpIR[] = { new PanasonicCKPHeatpumpIR(), // 0, keep this if you don't remove the timer for cancelling Panasonic CKP messages
new PanasonicDKEHeatpumpIR(), // 1
new PanasonicJKEHeatpumpIR(), // 2
new PanasonicNKEHeatpumpIR(), // 3
new CarrierHeatpumpIR(), // 4
new MideaHeatpumpIR(), // 5
new FujitsuHeatpumpIR(), // 6
new MitsubishiFDHeatpumpIR(), // 7
new MitsubishiFEHeatpumpIR(), // 8
new SamsungHeatpumpIR() // 9
};
// IR led on PWM output-capable digital pin 3
IRSender irSender(3);
// Timer for sending Panasonic CKP series timer cancellation commands
Timer timer;
int8_t panasonicCKPTimer = 0;
// Number of supported models
byte models = 0;
void setup()
{
Serial.begin(115200);
delay(1000);
Serial.println(F("HeatpumpIR sensor starting up..."));
models = sizeof(heatpumpIR) / sizeof(HeatpumpIR*);
Serial.print(F("Number of supported models: ")); Serial.println(models);
// Auto-assign the node ID, do not forward messages
gw.begin(incomingMessage, AUTO, false);
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Heatpump Sensor", "1.0");
// Register a sensors to the MySensors Gateway
gw.present(CHILD_1, S_IR, "IR sender");
// -- Hacks for Domoticz start
gw.present(CHILD_2, S_INFO, "IR data");
gw.present(CHILD_3, S_LIGHT, "IR send");
// The TEXT sensor is not created in Domoticz before data is sent
gw.send(textMsg.setSensor(CHILD_2).set("00000000"));
// -- Hacks for Domoticz end
}
void loop()
{
gw.process();
timer.update();
}
// Handle incoming messages from the MySensors Gateway
void incomingMessage(const MyMessage &message) {
const char *irData;
// V_IR type message
if (message.type==V_IR_SEND) {
Serial.println(F("Received IR send command..."));
irData = message.getString();
Serial.print(F("Code: 0x"));
Serial.println(irData);
sendHeatpumpIRCommand(irData);
}
// -- Hacks for Domoticz start
else if (message.type==V_LIGHT) {
// When the button is pressed on Domoticz, request the value of the TEXT sensor
Serial.println(F("Requesting IR code from Domoticz..."));
gw.request(CHILD_2, V_TEXT, 0);
} else if (message.type==V_TEXT) {
// TEXT sensor value is received as a result of the previous step
Serial.println(F("IR code received from Domoticz..."));
Serial.print(F("Code: 0x"));
irData = message.getString();
Serial.println(irData);
sendHeatpumpIRCommand(irData);
// Set the Domoticz switch back to 'OFF' state
gw.send(sendMsg.setSensor(CHILD_3).set(0));
}
// -- Hacks for Domoticz end
}
// Decode the IR command and send the IR command to the heatpump
void sendHeatpumpIRCommand(const char *irCommandString) {
// irCommandString is an 8-digit hex digit
long irCommand = 0;
int sscanfStatus = sscanf(irCommandString, "%lx", &irCommand);
if (sscanfStatus == 1) {
Serial.print(F("IR code conversion OK: 0x"));
Serial.println(irCommand, HEX);
} else {
Serial.println(F("Failed to convert IR hex code to number"));
}
/*
The heatpump command is packed into a 32-bit hex number, see
libraries\HeatpumpIR\HeatpumpIR.h for the constants
12345678
3 MODEL
4 POWER
5 OPERATING MODE
6 FAN SPEED
78 TEMPERATURE IN HEX
00213416 (as an example of a valid code)
2 = PanasonicJKE
1 = Power ON
3 = COOL
4 = FAN 4
16 = Temperature 22 degrees (0x16 = 22)
*/
byte model = (irCommand & 0x00F00000) >> 20;
byte power = (irCommand & 0x00010000) >> 16;
byte mode = (irCommand & 0x0000F000) >> 12;
byte fan = (irCommand & 0x00000F00) >> 8;
byte temp = (irCommand & 0x000000FF);
const char* buf;
Serial.print(F("Model: "));
buf = heatpumpIR[model]->model();
// 'model' is a PROGMEM pointer, so need to write a byte at a time
while (char modelChar = pgm_read_byte(buf++))
{
Serial.print(modelChar);
}
Serial.println();
Serial.print(F("Model #: ")); Serial.println(model);
Serial.print(F("Power: ")); Serial.println(power);
Serial.print(F("Mode: ")); Serial.println(mode);
Serial.print(F("Fan: ")); Serial.println(fan);
Serial.print(F("Temp: ")); Serial.println(temp);
// Heatpump models start from 0, i.e. model number is always less than the number of different models
if (model < models) {
// This is a supported model
// Cancel the timer on Panasonic CKP heatpump
if (model == 0) {
Serial.println(F("Cancelling timer on Panasonic CKP heatpump..."));
timer.stop(panasonicCKPTimer);
}
Serial.println(F("All OK - sending IR command to heatpump..."));
heatpumpIR[model]->send(irSender, power, mode, fan, temp, VDIR_UP, HDIR_AUTO);
if (model == 0) {
Serial.println(F("Scheduling timer cancellation on Panasonic CKP heatpump..."));
panasonicCKPTimer = timer.after(120000, panasonicCancelTimer); // Called after 2 minutes
}
}
}
// Cancel the timer on the Panasonic CKP heatpump
void panasonicCancelTimer()
{
PanasonicCKPHeatpumpIR *panasonicCKPHeatpumpIR = new PanasonicCKPHeatpumpIR();
panasonicCKPHeatpumpIR->sendPanasonicCKPCancelTimer(irSender);
Serial.println(F("The TIMER led should now be OFF"));
}
@@ -0,0 +1,73 @@
# MySensors example
This example shows how HeatpumpIR could be integrated with the MySensors sensor network. As this sensor sends infrared signals,
the natural sensor type is 'S_IR'. The heatpump/air conditioner signals are really long, so the signals need to be encoded somehow.
In this example the idea is to encode just this information, using 24 bits of data
* model code (see the index of the array heatpumpIR in the code)
* power state (for the rest, see the HeatpumpIR.h file for the constants)
* operating mode
* fan speed
* temperature (the temperature is directly the temperature in HEX)
```
An example:
00213416 (as an example of a valid code)
2 = PanasonicJKE
1 = Power ON
3 = COOL
4 = FAN 4
16 = Temperature 22 degrees (0x16 = 22)
```
This sketch creates three sensors:
* S_IR
* S_INFO
* S_LIGHT
The S_INFO and S_LIGHT sensors are for Domoticz, as it does not currently (as of V2.3093) support infrared code sending through the S_IR sensor type.
## Sending the code
### MYSController
* Select the IR sensor on the 'Heatpump Sensor 1.0' node
* Select 'IR_SEND' as the subtype
* Enter for example '00213416' as the payload
### Domoticz
As Domoticz does not really support sending IR code yet, the workaround is to use two sensors. When the node is connected, Domoticz should find two new devices
* A 'Lighting 2 / AC' type sensor with name 'IR send'
* A 'General / Text' type sensor with name 'Unknown' and Data '00000000'
To send a command, first add the sensors. Then update the data of the text sensor to contain the IR code to be sent, and then flip the switch to trigger the sending.
The data can be updated through the JSON REST API, or through the Lua event scripts.
REST API example (assuming that the idx of the text sensor is 105 and the switch is 104, and Domoticz is at 192.168.0.4:8080):
```
http://192.168.0.4:8080/json.htm?type=command&param=udevice&idx=105&nvalue=0&svalue=00213416
http://192.168.0.4:8080/json.htm?type=command&param=switchlight&idx=104&switchcmd=On
```
Lua example:
* Seems that the text sensor can't be updated through the commandArray...
* So now when a device called 'trigger' changes, the text sensor value is first updated to '00213416', and then the IR signal is sent
```
commandArray = {}
for key, value in pairs(devicechanged) do
if (key == 'trigger') then
print("Heatpump script")
commandArray['OpenURL']='http://192.168.0.4:8080/json.htm?type=command&param=udevice&idx=105&nvalue=0&svalue=00213416'
commandArray['IR send']='On'
end
end
return commandArray
```
## Hardware
Use any ATMega328-based Arduino, connect the radio, and finally connect an infrared led (in series with a resistor, like 100 Ohm) between digital pin 3 and GND.
@@ -1,81 +1,9 @@
/**
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2015 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
*******************************
*
* REVISION HISTORY
* Version 1.0 - idefix
*
* DESCRIPTION
* Arduino BH1750FVI Light sensor
* communicate using I2C Protocol
* this library enable 2 slave device addresses
* Main address 0x23
* secondary address 0x5C
* connect the sensor as follows :
*
* VCC >>> 5V
* Gnd >>> Gnd
* ADDR >>> NC or GND
* SCL >>> A5
* SDA >>> A4
* http://www.mysensors.org/build/light
*/
#include <MySensor.h>
#include <SPI.h>
#include <MySensor.h>
#include <BH1750.h>
#include <Wire.h>
#define CHILD_ID_LIGHT 0
unsigned long SLEEP_TIME = 30000; // Sleep time between reads (in milliseconds)
MyTransportNRF24 transport;
MyHwATMega328 hw;
MySensor gw(transport, hw);
BH1750 lightSensor;
MySensor gw;
// V_LIGHT_LEVEL should only be used for uncalibrated light level 0-100%.
// If your controller supports the new V_LEVEL variable, use this instead for
// transmitting LUX light level.
MyMessage msg(CHILD_ID_LIGHT, V_LIGHT_LEVEL);
// MyMessage msg(CHILD_ID_LIGHT, V_LEVEL);
uint16_t lastlux;
void setup()
{
gw.begin();
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Light Lux Sensor", "1.0");
// Register all sensors to gateway (they will be created as child devices)
gw.present(CHILD_ID_LIGHT, S_LIGHT_LEVEL);
lightSensor.begin();
}
void loop()
{
uint16_t lux = lightSensor.readLightLevel();// Get Lux value
Serial.println(lux);
if (lux != lastlux) {
gw.send(msg.set(lux));
lastlux = lux;
}
gw.sleep(SLEEP_TIME);
}
void setup() {}
void loop() {}
@@ -162,6 +162,8 @@ volatile uint8_t countErr;
void processMQTTMessages(char* topic, byte* payload, unsigned int length);
PubSubClient client(remote_ip, remote_port, processMQTTMessages, ethClient);
inline MyMessage& build(MyMessage &msg, uint8_t sender, uint8_t destination, uint8_t sensor,
uint8_t command, uint8_t type, bool enableAck);
////////////////////////////////////////////////////////////////
@@ -132,6 +132,7 @@ MyHwATMega328 hw;
// Construct MySensors library (signer needed if MY_SIGNING_FEATURE is turned on in MyConfig.h)
MySensor gw(transport, hw /*, signer*/);
inline MyMessage& build (MyMessage &msg, uint8_t destination, uint8_t sensor, uint8_t command, uint8_t type, bool enableAck);
EthernetServer server = EthernetServer(TCP_PORT);
EthernetClient *currentClient = NULL;
@@ -26,7 +26,7 @@
#define SHORT_WAIT 50
#define SKETCH_NAME "MockMySensors "
#define SKETCH_VERSION "v0.4"
#define SKETCH_VERSION "v0.5"
// Define Sensors ids
/* S_DOOR, S_MOTION, S_SMOKE, S_LIGHT, S_DIMMER, S_COVER, S_TEMP, S_HUM, S_BARO, S_WIND,
@@ -42,34 +42,49 @@
// Some of these ID's have not been updated for v1.5. Uncommenting too many of them
// will make the sketch too large for a pro mini's memory so it's probably best to try
// one at a time.
#define ID_S_ARMED 0 // dummy to controll armed stated for several sensors
#define ID_S_DOOR 1
//#define ID_S_MOTION 2
//#define ID_S_SMOKE 3
//#define ID_S_LIGHT 4
//#define ID_S_DIMMER 5
//#define ID_S_COVER 6
//#define ID_S_TEMP 7
//#define ID_S_HUM 8
//#define ID_S_BARO 9
//#define ID_S_WIND 10
//#define ID_S_RAIN 11
//#define ID_S_UV 12
//#define ID_S_WEIGHT 13
//#define ID_S_POWER 14
//#define ID_S_HEATER 15
//#define ID_S_DISTANCE 16
//#define ID_S_LIGHT_LEVEL 17
//#define ID_S_LOCK 18
//#define ID_S_IR 19
//#define ID_S_WATER 20
//#define ID_S_AIR_QUALITY 21
//#define ID_S_DUST 22
//#define ID_S_SCENE_CONTROLLER 23
//#define ID_S_CUSTOM 99
#define ID_S_ARMED 0 // dummy to controll armed stated for several sensors
#define ID_S_DOOR 1
//#define ID_S_MOTION 2
//#define ID_S_SMOKE 3
//#define ID_S_LIGHT 4
//#define ID_S_DIMMER 5
//#define ID_S_COVER 6
//#define ID_S_TEMP 7
//#define ID_S_HUM 8
//#define ID_S_BARO 9
//#define ID_S_WIND 10
//#define ID_S_RAIN 11
//#define ID_S_UV 12
//#define ID_S_WEIGHT 13
//#define ID_S_POWER 14
//#define ID_S_HEATER 15
//#define ID_S_DISTANCE 16
//#define ID_S_LIGHT_LEVEL 17
//#define ID_S_LOCK 18
//#define ID_S_IR 19
//#define ID_S_WATER 20
//#define ID_S_AIR_QUALITY 21
//#define ID_S_DUST 22
//#define ID_S_SCENE_CONTROLLER 23
//// Lib 1.5 sensors
//#define ID_S_RGB_LIGHT 24
//#define ID_S_RGBW_LIGHT 25
//#define ID_S_COLOR_SENSOR 26
//#define ID_S_HVAC 27
//#define ID_S_MULTIMETER 28
//#define ID_S_SPRINKLER 29
//#define ID_S_WATER_LEAK 30
//#define ID_S_SOUND 31
//#define ID_S_VIBRATION 32
//#define ID_S_MOISTURE 33
//
//#define ID_S_CUSTOM 99
// Global Vars
unsigned long SLEEP_TIME = 300000; // Sleep time between reads (in milliseconds)
unsigned long SLEEP_TIME = 900000; // Sleep time between reads (in milliseconds)
boolean metric = true;
long randNumber;
@@ -130,7 +145,8 @@ MyHwATMega328 hw;
MyMessage msg_S_COVER_U(ID_S_COVER,V_UP);
MyMessage msg_S_COVER_D(ID_S_COVER,V_DOWN);
MyMessage msg_S_COVER_S(ID_S_COVER,V_STOP);
const char* coverVal = "V_STOP"; //compiler warnings
MyMessage msg_S_COVER_V(ID_S_COVER,V_VAR1);
int coverState=0; //0=Stop; 1=up; -1=down
#endif
#ifdef ID_S_TEMP
@@ -168,16 +184,31 @@ MyHwATMega328 hw;
#ifdef ID_S_POWER
MyMessage msg_S_POWER_W(ID_S_POWER,V_WATT);
MyMessage msg_S_POWER_K(ID_S_POWER,V_KWH);
// MyMessage msg_S_POWER_I(ID_S_POWER,V_IMPEDANCE);
// MyMessage msg_S_POWER_V(ID_S_POWER,V_VOLTAGE);
// MyMessage msg_S_POWER_C(ID_S_POWER,V_CURRENT);
#endif
#ifdef ID_S_HEATER //V_HVAC_SETPOINT_HEAT, V_HVAC_FLOW_STATE, V_TEMP
MyMessage msg_S_HEATER_S(ID_S_HEATER,V_HVAC_SETPOINT_HEAT); // HVAC/Heater setpoint (Integer between 0-100). S_HEATER, S_HVAC
MyMessage msg_S_HEATER_F(ID_S_HEATER,V_HVAC_FLOW_STATE); // HVAC flow state ("Off", "HeatOn", "CoolOn", or "AutoChangeOver"). S_HEATER, S_HVAC
int heatTemp=18;
const char* heatState="Off";
#ifdef ID_S_HEATER
//////// REVIEW IMPLEMENTATION ////////////
MyMessage msg_S_HEATER_SET_POINT(ID_S_HEATER,V_HVAC_SETPOINT_HEAT); // HVAC/Heater setpoint (Integer between 0-100). S_HEATER, S_HVAC
MyMessage msg_S_HEATER_FLOW_STATE(ID_S_HEATER,V_HVAC_FLOW_STATE); // Mode of header. One of "Off", "HeatOn", "CoolOn", or "AutoChangeOver" // S_HVAC, S_HEATER
//MyMessage msg_S_HEATER_STATUS(ID_S_HEATER,V_STATUS);
//MyMessage msg_S_HEATER_TEMP(ID_S_HEATER,V_TEMP);
float heater_setpoint=21.5;
String heater_flow_state="Off";
// float heater_temp=23.5;
// bool heater_status=false;
// V_TEMP // Temperature
// V_STATUS // Binary status. 0=off 1=on
// V_HVAC_FLOW_STATE // Mode of header. One of "Off", "HeatOn", "CoolOn", or "AutoChangeOver"
// V_HVAC_SPEED // HVAC/Heater fan speed ("Min", "Normal", "Max", "Auto")
// V_HVAC_SETPOINT_HEAT // HVAC/Heater setpoint
#endif
#ifdef ID_S_DISTANCE
@@ -196,7 +227,7 @@ MyHwATMega328 hw;
#ifdef ID_S_IR
MyMessage msg_S_IR_S(ID_S_IR,V_IR_SEND);
MyMessage msg_S_IR_R(ID_S_IR,V_IR_RECEIVE);
const char* irVal = "";
long irVal = 0;
#endif
#ifdef ID_S_WATER
@@ -218,17 +249,95 @@ MyHwATMega328 hw;
// not sure if scene controller sends int or chars
// betting on ints as Touch Display Scen by Hek // compiler warnings
char *scenes[] = {
"All off",
"Good Morning",
"Clean Up!",
"All Lights Off",
"Music On/Off"
(char *)"Good Morning",
(char *)"Clean Up!",
(char *)"All Lights Off",
(char *)"Music On/Off"
};
int sceneVal=0;
int sceneValPrevious=0;
#endif
#ifdef ID_S_RGB_LIGHT
MyMessage msg_S_RGB_LIGHT_V_RGB(ID_S_RGB_LIGHT,V_RGB);
MyMessage msg_S_RGB_LIGHT_V_WATT(ID_S_RGB_LIGHT,V_WATT);
String rgbState="000000";
//RGB light V_RGB, V_WATT
//RGB value transmitted as ASCII hex string (I.e "ff0000" for red)
#endif
#ifdef ID_S_RGBW_LIGHT
MyMessage msg_S_RGBW_LIGHT_V_RGBW(ID_S_RGBW_LIGHT,V_RGBW);
MyMessage msg_S_RGBW_LIGHT_V_WATT(ID_S_RGBW_LIGHT,V_WATT);
String rgbwState="00000000";
//RGBW light (with separate white component) V_RGBW, V_WATT
//RGBW value transmitted as ASCII hex string (I.e "ff0000ff" for red + full white) S_RGBW_LIGHT
#endif
#ifdef ID_S_COLOR_SENSOR
MyMessage msg_S_COLOR_SENSOR_V_RGB(ID_S_COLOR_SENSOR,V_RGB);
//Color sensor V_RGB
//RGB value transmitted as ASCII hex string (I.e "ff0000" for red) S_RGB_LIGHT, S_COLOR_SENSOR
#endif
#ifdef ID_S_HVAC
MyMessage msg_S_HVAC_V_HVAC_SETPOINT_HEAT(ID_S_HVAC,V_HVAC_SETPOINT_HEAT);
MyMessage msg_S_HVAC_V_HVAC_SETPOINT_COOL(ID_S_HVAC,V_HVAC_SETPOINT_COOL);
MyMessage msg_S_HVAC_V_HVAC_FLOW_STATET(ID_S_HVAC,V_HVAC_FLOW_STATE);
MyMessage msg_S_HVAC_V_HVAC_FLOW_MODE(ID_S_HVAC,V_HVAC_FLOW_MODE);
MyMessage msg_S_HVAC_V_HVAC_SPEED(ID_S_HVAC,V_HVAC_SPEED);
float hvac_SetPointHeat = 16.5;
float hvac_SetPointCool = 25.5;
String hvac_FlowState = "AutoChangeOver";
String hvac_FlowMode = "Auto";
String hvac_Speed = "Normal";
//Thermostat/HVAC device
//V_HVAC_SETPOINT_HEAT, // HVAC/Heater setpoint
//V_HVAC_SETPOINT_COOL, // HVAC cold setpoint
//V_HVAC_FLOW_STATE, // Mode of header. One of "Off", "HeatOn", "CoolOn", or "AutoChangeOver"
//V_HVAC_FLOW_MODE, // Flow mode for HVAC ("Auto", "ContinuousOn", "PeriodicOn")
//V_HVAC_SPEED // HVAC/Heater fan speed ("Min", "Normal", "Max", "Auto")
// NOT IMPLEMENTED YET
//V_TEMP // Temperature
//V_STATUS // Binary status. 0=off 1=on
#endif
#ifdef ID_S_MULTIMETER
MyMessage msg_S_MULTIMETER_V_IMPEDANCE(ID_S_MULTIMETER,V_IMPEDANCE);
MyMessage msg_S_MULTIMETER_V_VOLTAGE(ID_S_MULTIMETER,V_VOLTAGE);
MyMessage msg_S_MULTIMETER_V_CURRENT(ID_S_MULTIMETER,V_CURRENT);
// Multimeter device V_VOLTAGE, V_CURRENT, V_IMPEDANCE
// V_IMPEDANCE 14 Impedance value
// V_VOLTAGE 38 Voltage level
// V_CURRENT 39 Current level
#endif
#ifdef ID_S_SPRINKLER
// S_SPRINKLER 31 Sprinkler device V_STATUS (turn on/off), V_TRIPPED (if fire detecting device)
// V_STATUS 2 Binary status. 0=off 1=on
// V_ARMED 15 Armed status of a security sensor. 1=Armed, 0=Bypassed
// V_TRIPPED 16 Tripped status of a security sensor. 1=Tripped, 0=Untripped
#endif
#ifdef ID_S_WATER_LEAK
#endif
#ifdef ID_S_SOUND
#endif
#ifdef ID_S_VIBRATION
#endif
#ifdef ID_S_MOISTURE
#endif
#ifdef ID_S_MOISTURE
MyMessage msg_S_MOISTURE(ID_S_MOISTURE,V_LEVEL);
#endif
#ifdef ID_S_CUSTOM
MyMessage msg_S_CUSTOM_1(ID_S_CUSTOM,V_VAR1);
MyMessage msg_S_CUSTOM_2(ID_S_CUSTOM,V_VAR2);
@@ -274,145 +383,192 @@ void setup()
#ifdef ID_S_DOOR
Serial.println(" S_DOOR");
gw.present(ID_S_DOOR,S_DOOR);
gw.present(ID_S_DOOR,S_DOOR,"Outside Door");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_MOTION
Serial.println(" S_MOTION");
gw.present(ID_S_MOTION,S_MOTION);
gw.present(ID_S_MOTION,S_MOTION,"Outside Motion");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_SMOKE
Serial.println(" S_SMOKE");
gw.present(ID_S_SMOKE,S_SMOKE);
gw.present(ID_S_SMOKE,S_SMOKE,"Kitchen Smoke");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_LIGHT
Serial.println(" S_LIGHT");
gw.present(ID_S_LIGHT,S_LIGHT);
gw.present(ID_S_LIGHT,S_LIGHT,"Hall Light");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_DIMMER
Serial.println(" S_DIMMER");
gw.present(ID_S_DIMMER,S_DIMMER);
gw.present(ID_S_DIMMER,S_DIMMER,"Living room dimmer");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_COVER
Serial.println(" S_COVER");
gw.present(ID_S_COVER,S_COVER);
gw.present(ID_S_COVER,S_COVER,"Window cover");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_TEMP
Serial.println(" S_TEMP");
gw.present(ID_S_TEMP,S_TEMP);
gw.present(ID_S_TEMP,S_TEMP,"House Temperarue");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_HUM
Serial.println(" S_HUM");
gw.present(ID_S_HUM,S_HUM);
gw.present(ID_S_HUM,S_HUM,"Current Humidity");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_BARO
Serial.println(" S_BARO");
gw.present(ID_S_BARO,S_BARO);
gw.present(ID_S_BARO,S_BARO," Air pressure");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_WIND
Serial.println(" S_WIND");
gw.present(ID_S_WIND,S_WIND);
gw.present(ID_S_WIND,S_WIND,"Wind Station");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_RAIN
Serial.println(" S_RAIN");
gw.present(ID_S_RAIN,S_RAIN);
gw.present(ID_S_RAIN,S_RAIN,"Rain Station");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_UV
Serial.println(" S_UV");
gw.present(ID_S_UV,S_UV);
gw.present(ID_S_UV,S_UV,"Ultra Violet");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_WEIGHT
Serial.println(" S_WEIGHT");
gw.present(ID_S_WEIGHT,S_WEIGHT);
gw.present(ID_S_WEIGHT,S_WEIGHT,"Outdoor Scale");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_POWER
Serial.println(" S_POWER");
gw.present(ID_S_POWER,S_POWER);
gw.present(ID_S_POWER,S_POWER,"Power Metric");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_HEATER
Serial.println(" S_HEATER");
gw.present(ID_S_HEATER,S_HEATER);
gw.present(ID_S_HEATER,S_HEATER,"Garage Heater");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_DISTANCE
Serial.println(" S_DISTANCE");
gw.present(ID_S_DISTANCE,S_DISTANCE);
gw.present(ID_S_DISTANCE,S_DISTANCE,"Distance Measure");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_LIGHT_LEVEL
Serial.println(" S_LIGHT_LEVEL");
gw.present(ID_S_LIGHT_LEVEL,S_LIGHT_LEVEL);
gw.present(ID_S_LIGHT_LEVEL,S_LIGHT_LEVEL,"Outside Light Level");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_LOCK
Serial.println(" S_LOCK");
gw.present(ID_S_LOCK,S_LOCK);
gw.present(ID_S_LOCK,S_LOCK,"Front Door Lock");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_IR
Serial.println(" S_IR");
gw.present(ID_S_IR,S_IR);
gw.present(ID_S_IR,S_IR,"Univeral Command");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_WATER
Serial.println(" S_WATER");
gw.present(ID_S_WATER,S_WATER);
gw.present(ID_S_WATER,S_WATER,"Water Level");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_AIR_QUALITY
Serial.println(" S_AIR_QUALITY");
gw.present(ID_S_AIR_QUALITY,S_AIR_QUALITY);
gw.present(ID_S_AIR_QUALITY,S_AIR_QUALITY,"Air Station");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_DUST
Serial.println(" S_DUST");
gw.present(ID_S_DUST,S_DUST);
gw.present(ID_S_DUST,S_DUST,"Dust Level");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_SCENE_CONTROLLER
Serial.println(" S_SCENE_CONTROLLER");
gw.present(ID_S_SCENE_CONTROLLER,S_SCENE_CONTROLLER);
gw.present(ID_S_SCENE_CONTROLLER,S_SCENE_CONTROLLER,"Scene Controller");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_RGB_LIGHT
Serial.println(" RGB_LIGHT");
gw.present(ID_S_RGB_LIGHT,S_RGB_LIGHT,"Mood Light");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_RGBW_LIGHT
Serial.println(" RGBW_LIGHT");
gw.present(ID_S_RGBW_LIGHT,S_RGBW_LIGHT,"Mood Light 2");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_COLOR_SENSOR
Serial.println(" COLOR_SENSOR");
gw.present(ID_S_COLOR_SENSOR,S_COLOR_SENSOR,"Hall Painting");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_HVAC
Serial.println(" HVAC");
gw.present(ID_S_HVAC,S_HVAC,"HVAC");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_MULTIMETER
Serial.println(" MULTIMETER");
gw.present(ID_S_MULTIMETER,S_MULTIMETER,"Electric Staion");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_SPRINKLER
#endif
#ifdef ID_S_WATER_LEAK
#endif
#ifdef ID_S_SOUND
#endif
#ifdef ID_S_VIBRATION
#endif
#ifdef ID_S_MOISTURE
#endif
#ifdef ID_S_MOISTURE
Serial.println(" S_MOISTURE");
gw.present(ID_S_MOISTURE,S_MOISTURE,"Basement Sensor");
gw.wait(SHORT_WAIT);
#endif
#ifdef ID_S_CUSTOM
Serial.println(" S_CUSTOM");
gw.present(ID_S_CUSTOM,S_CUSTOM);
gw.present(ID_S_CUSTOM,S_CUSTOM,"Other Stuff");
gw.wait(SHORT_WAIT);
#endif
@@ -535,6 +691,41 @@ void loop()
scene();
#endif
#ifdef ID_S_RGB_LIGHT
rgbLight();
#endif
#ifdef ID_S_RGBW_LIGHT
rgbwLight();
#endif
#ifdef ID_S_COLOR_SENSOR
color();
#endif
#ifdef ID_S_HVAC
hvac();
#endif
#ifdef ID_S_MULTIMETER
multimeter();
#endif
#ifdef ID_S_SPRINKLER
#endif
#ifdef ID_S_WATER_LEAK
#endif
#ifdef ID_S_SOUND
#endif
#ifdef ID_S_VIBRATION
#endif
#ifdef ID_S_MOISTURE
#endif
#ifdef ID_S_MOISTURE
moisture();
#endif
#ifdef ID_S_CUSTOM
custom();
#endif
@@ -646,17 +837,17 @@ void cover(){
Serial.print("Cover is : " );
if (coverVal == "V_UP"){
if (coverState == 1){
Serial.println("Opening");
gw.send(msg_S_COVER_U.set(V_UP));
}else if (coverVal == "V_DOWN"){
gw.send(msg_S_COVER_U.set(1));
}else if (coverState == -1){
Serial.println("Closing");
gw.send(msg_S_COVER_D.set(V_DOWN));
gw.send(msg_S_COVER_D.set(0));
}else{
Serial.println("Idle");
gw.send(msg_S_COVER_S.set(V_STOP));
gw.send(msg_S_COVER_S.set(-1));
}
gw.send(msg_S_COVER_V.set(coverState));
}
#endif
@@ -760,46 +951,38 @@ void weight(){
void power(){
Serial.print("Watt is: " );
Serial.println(map(randNumber,1,100,0,150));
Serial.println(map(randNumber,1,100,0,150));
gw.send(msg_S_POWER_W.set(map(randNumber,1,100,0,150)));
Serial.print("KWH is: " );
Serial.println(map(randNumber,1,100,0,150));
Serial.println(map(randNumber,1,100,0,150));
gw.send(msg_S_POWER_K.set(map(randNumber,1,100,0,150)));
// Serial.print("Impedance is: " );
// Serial.println(map(randNumber,1,100,0,150));
//
// gw.send(msg_S_POWER_I.set(map(randNumber,1,100,0,150)));
// Serial.print("Voltage is: " );
// Serial.println(map(randNumber,1,100,0,150));
//
// gw.send(msg_S_POWER_V.set(map(randNumber,1,100,0,150)));
// Serial.print("Current is: " );
// Serial.println(map(randNumber,1,100,0,150));
//
// gw.send(msg_S_POWER_C.set(map(randNumber,1,100,0,150)));
}
#endif
#ifdef ID_S_HEATER
void heater(){
// float heater_setpoint=21.5;
// float heater_temp=23.5;
// bool heater_status=false;
// String heatState="Off";
Serial.print("Heater flow state is: " );
Serial.println(heater_flow_state);
gw.send(msg_S_HEATER_FLOW_STATE.set(heater_flow_state.c_str()));
Serial.print("Heater mode is: " );
Serial.println(heatState);
// Serial.print("Heater on/off is: " );
// Serial.println((heater_status==true)?"On":"Off");
// gw.send(msg_S_HEATER_STATUS.set(heater_status));
gw.send(msg_S_HEATER_F.set(heatState));
// Serial.print("Heater Temperature is: " );
// Serial.println(heater_temp,1);
// gw.send(msg_S_HEATER_TEMP.set(heater_temp,1));
Serial.print("Heater Set Point is: " );
Serial.println(heatTemp);
gw.send(msg_S_HEATER_S.set(heatTemp));
Serial.print("Heater Setpoint: " );
Serial.println(heater_setpoint,1);
gw.send(msg_S_HEATER_SET_POINT.set(heater_setpoint,1));
}
#endif
@@ -842,6 +1025,7 @@ void ir(){
Serial.println(irVal);
gw.send(msg_S_IR_S.set(irVal));
gw.send(msg_S_IR_R.set(irVal));
}
#endif
@@ -899,6 +1083,125 @@ void scene(){
}
#endif
#ifdef ID_S_RGB_LIGHT
void rgbLight(){
Serial.print("RGB Light state is: " );
Serial.println(rgbState);
gw.send(msg_S_RGB_LIGHT_V_RGB.set(rgbState.c_str()));
Serial.print("RGB Light Watt is: " );
Serial.println(map(randNumber,1,100,0,150));
gw.send(msg_S_RGB_LIGHT_V_WATT.set(map(randNumber,1,100,0,150)));
}
#endif
#ifdef ID_S_RGBW_LIGHT
void rgbwLight(){
Serial.print("RGBW Light state is: " );
Serial.println(rgbwState);
gw.send(msg_S_RGBW_LIGHT_V_RGBW.set(rgbwState.c_str()));
Serial.print("RGBW Light Watt is: " );
Serial.println(map(randNumber,1,100,0,150));
gw.send(msg_S_RGBW_LIGHT_V_WATT.set(map(randNumber,1,100,0,150)));
}
#endif
#ifdef ID_S_COLOR_SENSOR
void color(){
String colorState;
String red = String(random(0,256),HEX);
String green = String(random(0,256),HEX);
String blue = String(random(0,256),HEX);
colorState=String(red + green + blue);
Serial.print("Color state is: " );
Serial.println(colorState);
gw.send(msg_S_COLOR_SENSOR_V_RGB.set(colorState.c_str()));
}
#endif
#ifdef ID_S_HVAC
void hvac(){
// float hvac_SetPointHeat = 16.5;
// float hvac_SetPointCool = 25.5;
// String hvac_FlowState = "AutoChangeOver";
// String hvac_FlowMode = "Auto";
// String hvac_Speed = "Normal";
Serial.print("HVAC Set Point Heat is: " );
Serial.println(hvac_SetPointHeat);
gw.send(msg_S_HVAC_V_HVAC_SETPOINT_HEAT.set(hvac_SetPointHeat,1));
Serial.print("HVAC Set Point Cool is: " );
Serial.println(hvac_SetPointCool);
gw.send(msg_S_HVAC_V_HVAC_SETPOINT_COOL.set(hvac_SetPointCool,1));
Serial.print("HVAC Flow State is: " );
Serial.println(hvac_FlowState);
gw.send(msg_S_HVAC_V_HVAC_FLOW_STATET.set(hvac_FlowState.c_str()));
Serial.print("HVAC Flow Mode is: " );
Serial.println(hvac_FlowMode);
gw.send(msg_S_HVAC_V_HVAC_FLOW_MODE.set(hvac_FlowMode.c_str()));
Serial.print("HVAC Speed is: " );
Serial.println(hvac_Speed);
gw.send(msg_S_HVAC_V_HVAC_SPEED.set(hvac_Speed.c_str()));
}
#endif
#ifdef ID_S_MULTIMETER
void multimeter(){
int impedance=map(randNumber,1,100,0,15000);
int volt=map(randNumber,1,100,0,380);
int amps=map(randNumber,1,100,0,16);
Serial.print("Impedance is: " );
Serial.println(impedance);
gw.send(msg_S_MULTIMETER_V_IMPEDANCE.set(impedance));
Serial.print("Voltage is: " );
Serial.println(volt);
gw.send(msg_S_MULTIMETER_V_VOLTAGE.set(volt));
Serial.print("Current is: " );
Serial.println(amps);
gw.send(msg_S_MULTIMETER_V_CURRENT.set(amps));
}
#endif
#ifdef ID_S_SPRINKLER
#endif
#ifdef ID_S_WATER_LEAK
#endif
#ifdef ID_S_SOUND
#endif
#ifdef ID_S_VIBRATION
#endif
#ifdef ID_S_MOISTURE
#endif
#ifdef ID_S_MOISTURE
void moisture(){
Serial.print("Moisture level is: " );
Serial.println(randNumber);
gw.send(msg_S_MOISTURE.set(randNumber));
}
#endif
#ifdef ID_S_CUSTOM
void custom(){
@@ -924,20 +1227,42 @@ void incomingMessage(const MyMessage &message) {
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println((isArmed ? "Armed":"Disarmed" ));
door();//temp ack for door
#ifdef ID_S_DOOR
door();//temp ack for door
#endif
#ifdef ID_S_MOTION
motion();//temp ack
#endif
#ifdef ID_S_SMOKE
smoke();//temp ack
#endif
break;
#endif
case V_STATUS: // V_LIGHT:
#ifdef ID_S_LIGHT
case V_LIGHT:
if(message.sensor==ID_S_LIGHT){
isLightOn = message.getBool();
Serial.print("Incoming change for ID_S_LIGHT:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println((isLightOn ? "On":"Off"));
light(); // temp ack
break;
}
#endif
// #ifdef ID_S_HEATER
// if(message.sensor == ID_S_HEATER){
// heater_status = message.getBool();
// Serial.print("Incoming change for ID_S_HEATER:");
// Serial.print(message.sensor);
// Serial.print(", New status: ");
// Serial.println(heater_status);
// heater();//temp ack
// }
// #endif
break;
#ifdef ID_S_DIMMER
case V_DIMMER:
@@ -950,53 +1275,90 @@ void incomingMessage(const MyMessage &message) {
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(message.getInt());
dimmer();// temp ack
break;
#endif
#ifdef ID_S_COVER
case V_UP:
coverVal="V_UP";
coverState=1;
Serial.print("Incoming change for ID_S_COVER:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println("V_UP");
cover(); // temp ack
break;
case V_DOWN:
coverVal="V_DOWN";
coverState=-1;
Serial.print("Incoming change for ID_S_COVER:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println("V_DOWN");
cover(); //temp ack
break;
case V_STOP:
coverVal="V_STOP";
coverState=0;
Serial.print("Incoming change for ID_S_COVER:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println("V_STOP");
cover(); //temp ack
break;
#endif
#ifdef ID_S_HEATER
case V_HVAC_FLOW_STATE:
heatState = message.getString();
Serial.print("Incoming change for ID_S_HEATER:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(heatState);
break;
case V_HVAC_SETPOINT_HEAT:
heatTemp = message.getInt();
Serial.print("Incoming change for ID_S_HEATER:");
#ifdef ID_S_HEATER
if(message.sensor == ID_S_HEATER){
heater_setpoint=message.getFloat();
Serial.print("Incoming set point for ID_S_HEATER:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(heatTemp);
Serial.println(heater_setpoint,1);
heater();//temp ack
}
#endif
#ifdef ID_S_HVAC
if(message.sensor == ID_S_HVAC){
hvac_SetPointHeat=message.getFloat();
Serial.print("Incoming set point for ID_S_HVAC:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(hvac_SetPointHeat,1);
hvac();//temp ack
}
#endif
break;
case V_HVAC_FLOW_STATE:
#ifdef ID_S_HEATER
if(message.sensor == ID_S_HEATER){
heater_flow_state=message.getString();
Serial.print("Incoming flow state change for ID_S_HEATER:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(heater_flow_state);
heater();//temp ack
}
#endif
#ifdef ID_S_HVAC
if(message.sensor == ID_S_HVAC){
hvac_FlowState=message.getString();
Serial.print("Incoming set point for ID_S_HVAC:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(hvac_FlowState);
hvac();//temp ack
}
#endif
break;
#endif
#ifdef ID_S_LOCK
case V_LOCK_STATUS:
@@ -1010,12 +1372,21 @@ void incomingMessage(const MyMessage &message) {
#endif
#ifdef ID_S_IR
case V_IR_RECEIVE:
irVal = message.getString();
case V_IR_SEND:
irVal = message.getLong();
Serial.print("Incoming change for ID_S_IR:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(irVal);
ir(); // temp ack
break;
case V_IR_RECEIVE:
irVal = message.getLong();
Serial.print("Incoming change for ID_S_IR:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(irVal);
ir(); // temp ack
break;
#endif
@@ -1027,7 +1398,7 @@ void incomingMessage(const MyMessage &message) {
Serial.print(", New status: ");
Serial.print(scenes[sceneVal]);
Serial.println(" On");
scene();
scene();// temp ack
break;
case V_SCENE_OFF:
sceneVal = message.getInt();
@@ -1036,10 +1407,71 @@ void incomingMessage(const MyMessage &message) {
Serial.print(", New status: ");
Serial.print(scenes[sceneVal]);
Serial.println(" Off");
scene();
scene();// temp ack
break;
#endif
#ifdef ID_S_RGB_LIGHT
case V_RGB:
rgbState=message.getString();
Serial.print("Incoming flow state change for ID_S_RGB_LIGHT:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(rgbState);
rgbLight(); // temp ack
break;
#endif
#ifdef ID_S_RGBW_LIGHT
case V_RGBW:
rgbwState=message.getString();
Serial.print("Incoming flow state change for ID_S_RGBW_LIGHT:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(rgbwState);
rgbwLight();
break;
#endif
#ifdef ID_S_HVAC
// hvac_SetPointHeat
// hvac_SetPointCool
// hvac_FlowState
// hvac_FlowMode
// hvac_Speed
case V_HVAC_SETPOINT_COOL:
hvac_SetPointCool=message.getFloat();
Serial.print("Incoming set point for ID_S_HVAC:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(hvac_SetPointCool,1);
hvac();//temp ack
break;
case V_HVAC_FLOW_MODE:
hvac_Speed=message.getString();
Serial.print("Incoming set point for ID_S_HVAC:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(hvac_Speed);
hvac();//temp ack
break;
case V_HVAC_SPEED:
hvac_FlowMode=message.getString();
Serial.print("Incoming set point for ID_S_HVAC:");
Serial.print(message.sensor);
Serial.print(", New status: ");
Serial.println(hvac_FlowMode);
hvac();//temp ack
break;
#endif
default:
Serial.print("Unknown/UnImplemented message type: ");
Serial.println(message.type);
@@ -17,7 +17,23 @@ With a Mega you can have them all
Changes Log
-----------------
2015-09-07
Five more sensors
S_RGB_LIGHT
S_RGBW_LIGHT
S_COLOR_SENSOR
S_HVAC
S_MULTIMETER
Consolidated HVAC and HEATER incoming message
2015-09-06
Merge with Gizmocuz (implementation of S_HEATER & S_MOISTURE
Implementation of S_COVER, S_IR, S_SCENE_CONTROLLER
2015-08-19
Removed the load/save to eeprom
Touched the heater sensor
Created a pseudo sensor to handle the V_ARMED message.
@@ -56,6 +56,8 @@
#define PANIC_DISTANCE 5 // Mix distance we red warning indication should be active (in cm)
#define PARKED_DISTANCE 20 // Distance when "parked signal" should be sent to controller (in cm)
#define PARK_OFF_TIMEOUT 20000 // Number of milliseconds until turning off light when parked.
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
// example for more information on possible values.
@@ -67,10 +69,11 @@ NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and
#define CHILD_ID 1
MySensor gw;
MyMessage msg(CHILD_ID,V_TRIPPED);
int oldParkedStatus=-1;
#endif
unsigned long sendInterval = 5000; // Send park status at maximum every 5 second.
unsigned long lastSend;
#endif
int oldParkedStatus=-1;
unsigned long blinkInterval = 100; // blink interval (milliseconds)
unsigned long lastBlinkPeriod;
@@ -109,7 +112,6 @@ void loop() {
if (now-lastDebouncePeriod > distDebounce) {
lastDebouncePeriod = now;
#ifdef SEND_STATUS_TO_CONTROLLER
// Update parked status
int parked = displayDist != 0 && displayDist<PARKED_DISTANCE;
if (parked != oldParkedStatus && now-lastSend > sendInterval) {
@@ -117,48 +119,56 @@ void loop() {
Serial.println("Car Parked");
else
Serial.println("Car Gone");
#ifdef SEND_STATUS_TO_CONTROLLER
gw.send(msg.set(parked));
#endif
oldParkedStatus = parked;
lastSend = now;
}
#endif
if (displayDist == 0) {
// No reading from sensor, assume no object found
numLightPixels--;
} else {
skipZero = 0;
int newLightPixels = NUMPIXELS - (NUMPIXELS*(displayDist-PANIC_DISTANCE)/MAX_DISTANCE);
if (newLightPixels>numLightPixels) {
// Fast raise
numLightPixels += max((newLightPixels - numLightPixels) / 2, 1);
} else if (newLightPixels<numLightPixels) {
// Slow decent
numLightPixels--;
}
}
if (numLightPixels>=NUMPIXELS) {
// Do some intense red blinking
if (now-lastBlinkPeriod > blinkInterval) {
blinkColor = !blinkColor;
lastBlinkPeriod = now;
}
for(int i=0;i<numLightPixels;i++){
pixels.setPixelColor(i, pixels.Color(blinkColor?255*MAX_INTESITY/100:0,0,0));
}
} else {
for(int i=0;i<numLightPixels;i++){
int r = 255 * i/NUMPIXELS;
int g = 255 - r;
// pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
pixels.setPixelColor(i, pixels.Color(r*MAX_INTESITY/100,g*MAX_INTESITY/100,0));
}
// Turn off the rest
for(int i=numLightPixels;i<NUMPIXELS;i++){
if (parked && now-lastSend > PARK_OFF_TIMEOUT) {
// We've been parked for a while now. Turn off all pixels
for(int i=0;i<NUMPIXELS;i++){
pixels.setPixelColor(i, pixels.Color(0,0,0));
}
} else {
if (displayDist == 0) {
// No reading from sensor, assume no object found
numLightPixels--;
} else {
skipZero = 0;
int newLightPixels = NUMPIXELS - (NUMPIXELS*(displayDist-PANIC_DISTANCE)/MAX_DISTANCE);
if (newLightPixels>numLightPixels) {
// Fast raise
numLightPixels += max((newLightPixels - numLightPixels) / 2, 1);
} else if (newLightPixels<numLightPixels) {
// Slow decent
numLightPixels--;
}
}
if (numLightPixels>=NUMPIXELS) {
// Do some intense red blinking
if (now-lastBlinkPeriod > blinkInterval) {
blinkColor = !blinkColor;
lastBlinkPeriod = now;
}
for(int i=0;i<numLightPixels;i++){
pixels.setPixelColor(i, pixels.Color(blinkColor?255*MAX_INTESITY/100:0,0,0));
}
} else {
for(int i=0;i<numLightPixels;i++){
int r = 255 * i/NUMPIXELS;
int g = 255 - r;
// pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
pixels.setPixelColor(i, pixels.Color(r*MAX_INTESITY/100,g*MAX_INTESITY/100,0));
}
// Turn off the rest
for(int i=numLightPixels;i<NUMPIXELS;i++){
pixels.setPixelColor(i, pixels.Color(0,0,0));
}
}
}
pixels.show(); // This sends the updated pixel color to the hardware.
}
}
}
@@ -20,6 +20,7 @@
*
* REVISION HISTORY
* Version 1.0 - Henrik Ekblad
* Version 1.1 - GizMoCuz
*
* DESCRIPTION
* Use this sensor to measure volume and flow of your house watermeter.
@@ -33,8 +34,8 @@
* http://www.mysensors.org/build/pulse_water
*/
#include <SPI.h>
#include <MySensor.h>
#include <SPI.h>
#define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your sensor. (Only 2 and 3 generates interrupt!)
#define SENSOR_INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway)
@@ -47,7 +48,7 @@
#define CHILD_ID 1 // Id of the sensor child
unsigned long SEND_FREQUENCY = 20000; // Minimum time between send (in milliseconds). We don't want to spam the gateway.
unsigned long SEND_FREQUENCY = 30000; // Minimum time between send (in milliseconds). We don't want to spam the gateway.
MySensor gw;
MyMessage flowMsg(CHILD_ID,V_FLOW);
@@ -72,8 +73,11 @@ void setup()
{
gw.begin(incomingMessage);
// initialize our digital pins internal pullup resistor so one pulse switches from high to low (less distortion)
pinMode(DIGITAL_INPUT_SENSOR, INPUT_PULLUP);
// Send the sketch version information to the gateway and Controller
gw.sendSketchInfo("Water Meter", "1.2");
gw.sendSketchInfo("Water Meter", "1.1");
// Register this device as Waterflow sensor
gw.present(CHILD_ID, S_WATER);
@@ -85,7 +89,7 @@ void setup()
lastSend = lastPulse = millis();
attachInterrupt(SENSOR_INTERRUPT, onPulse, RISING);
attachInterrupt(SENSOR_INTERRUPT, onPulse, FALLING);
}
@@ -93,7 +97,7 @@ void loop()
{
gw.process();
unsigned long currentTime = millis();
// Only send values at a maximum frequency or woken up from sleep
if (SLEEP_MODE || (currentTime - lastSend > SEND_FREQUENCY))
{
@@ -124,7 +128,7 @@ void loop()
}
// Pulse count has changed
if (pulseCount != oldPulseCount) {
if ((pulseCount != oldPulseCount)||(!SLEEP_MODE)) {
oldPulseCount = pulseCount;
Serial.print("pulsecount:");
@@ -133,7 +137,7 @@ void loop()
gw.send(lastCounterMsg.set(pulseCount)); // Send pulsecount value to gw in VAR1
double volume = ((double)pulseCount/((double)PULSE_FACTOR));
if (volume != oldvolume) {
if ((volume != oldvolume)||(!SLEEP_MODE)) {
oldvolume = volume;
Serial.print("volume:");
@@ -152,6 +156,7 @@ void incomingMessage(const MyMessage &message) {
if (message.type==V_VAR1) {
unsigned long gwPulseCount=message.getULong();
pulseCount += gwPulseCount;
flow=oldflow=0;
Serial.print("Received last pulse count from gw:");
Serial.println(pulseCount);
pcReceived = true;
@@ -178,4 +183,3 @@ void onPulse()
}
pulseCount++;
}
+4
View File
@@ -1,3 +1,5 @@
#if defined(ARDUINO_ARCH_AVR)
#include "Arduino.h"
#include "ATSHA204.h"
@@ -572,3 +574,5 @@ uint8_t ATSHA204Class::sha204c_check_crc(uint8_t *response)
return (crc[0] == response[count] && crc[1] == response[count + 1])
? SHA204_SUCCESS : SHA204_BAD_CRC;
}
#endif // defined(ARDUINO_ARCH_AVR)
+32 -19
View File
@@ -498,7 +498,7 @@ void RF24::begin(void)
// Reset current status
// Notice reset and flush is the last thing we do
write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
write_register(RF24_STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
// Set up default configuration. Callers can always change it later.
// This channel should be universally safe and not bleed over into adjacent
@@ -525,7 +525,7 @@ void RF24::startListening(void)
powerUp();
#endif
write_register(CONFIG, read_register(CONFIG) | _BV(PRIM_RX));
write_register(STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
write_register(RF24_STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
// Restore the pipe0 adddress, if exists
if (pipe0_reading_address[0] > 0){
@@ -546,22 +546,35 @@ void RF24::startListening(void)
}
/****************************************************************************/
static const uint8_t child_pipe_enable[] PROGMEM =
static uint8_t get_child_pipe_mask(const uint8_t idx)
{
ERX_P0, ERX_P1, ERX_P2, ERX_P3, ERX_P4, ERX_P5
};
#ifdef ESP8266
static const uint8_t child_pipe_enable[] =
{
_BV(ERX_P0), _BV(ERX_P1), _BV(ERX_P2), _BV(ERX_P3), _BV(ERX_P4), _BV(ERX_P5)
};
return child_pipe_enable[idx];
#else
static const uint8_t child_pipe_enable[] PROGMEM =
{
_BV(ERX_P0), _BV(ERX_P1), _BV(ERX_P2), _BV(ERX_P3), _BV(ERX_P4), _BV(ERX_P5)
};
return pgm_read_byte(&child_pipe_enable[idx]);
#endif
}
void RF24::stopListening(void)
{
ce(LOW);
#if defined(__arm__)
delayMicroseconds(300);
#endif
delayMicroseconds(130);
if(read_register(FEATURE) & _BV(EN_ACK_PAY)){
flush_tx();
}
//flush_rx();
write_register(CONFIG, ( read_register(CONFIG) ) & ~_BV(PRIM_RX) );
@@ -569,13 +582,13 @@ void RF24::stopListening(void)
// for 3 pins solution TX mode is only left with additonal powerDown/powerUp cycle
if (ce_pin == csn_pin) {
powerDown();
powerUp();
powerUp();
}
#endif
write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(pgm_read_byte(&child_pipe_enable[0]))); // Enable RX on pipe0
write_register(EN_RXADDR,read_register(EN_RXADDR) | get_child_pipe_mask(0)); // Enable RX on pipe0
delayMicroseconds(100);
}
/****************************************************************************/
@@ -636,7 +649,7 @@ bool RF24::write( const void* buf, uint8_t len, const bool multicast )
ce(LOW);
uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
uint8_t status = write_register(RF24_STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
//Max retries exceeded
if( status & _BV(MAX_RT)){
@@ -686,7 +699,7 @@ bool RF24::writeBlocking( const void* buf, uint8_t len, uint32_t timeout )
/****************************************************************************/
void RF24::reUseTX(){
write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag
write_register(RF24_STATUS,_BV(MAX_RT) ); //Clear max retry flag
spiTrans( REUSE_TX_PL );
ce(LOW); //Re-Transfer packet
ce(HIGH);
@@ -709,7 +722,7 @@ bool RF24::writeFast( const void* buf, uint8_t len, const bool multicast )
if( get_status() & _BV(MAX_RT)){
//reUseTX(); //Set re-transmit
write_register(STATUS,_BV(MAX_RT) ); //Clear max retry flag
write_register(RF24_STATUS,_BV(MAX_RT) ); //Clear max retry flag
return 0; //Return 0. The previous payload has been retransmitted
//From the user perspective, if you get a 0, just keep trying to send the same payload
}
@@ -777,7 +790,7 @@ bool RF24::txStandBy(){
#endif
while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){
if( get_status() & _BV(MAX_RT)){
write_register(STATUS,_BV(MAX_RT) );
write_register(RF24_STATUS,_BV(MAX_RT) );
ce(LOW);
flush_tx(); //Non blocking, flush the data
return 0;
@@ -802,7 +815,7 @@ bool RF24::txStandBy(uint32_t timeout){
while( ! (read_register(FIFO_STATUS) & _BV(TX_EMPTY)) ){
if( get_status() & _BV(MAX_RT)){
write_register(STATUS,_BV(MAX_RT) );
write_register(RF24_STATUS,_BV(MAX_RT) );
ce(LOW); //Set re-transmit
ce(HIGH);
if(millis() - start >= timeout){
@@ -891,7 +904,7 @@ void RF24::read( void* buf, uint8_t len ){
read_payload( buf, len );
//Clear the two possible interrupt flags with one command
write_register(STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS) );
write_register(RF24_STATUS,_BV(RX_DR) | _BV(MAX_RT) | _BV(TX_DS) );
}
@@ -901,7 +914,7 @@ void RF24::whatHappened(bool& tx_ok,bool& tx_fail,bool& rx_ready)
{
// Read the status & reset the status in one easy call
// Or is that such a good idea?
uint8_t status = write_register(STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
uint8_t status = write_register(RF24_STATUS,_BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT) );
// Report to the user what happened
tx_ok = status & _BV(TX_DS);
@@ -972,7 +985,7 @@ void RF24::openReadingPipe(uint8_t child, uint64_t address)
// Note it would be more efficient to set all of the bits for all open
// pipes at once. However, I thought it would make the calling code
// more simple to do it this way.
write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(pgm_read_byte(&child_pipe_enable[child])));
write_register(EN_RXADDR,read_register(EN_RXADDR) | get_child_pipe_mask(child));
}
}
@@ -1009,7 +1022,7 @@ void RF24::openReadingPipe(uint8_t child, const uint8_t *address)
// Note it would be more efficient to set all of the bits for all open
// pipes at once. However, I thought it would make the calling code
// more simple to do it this way.
write_register(EN_RXADDR,read_register(EN_RXADDR) | _BV(pgm_read_byte(&child_pipe_enable[child])));
write_register(EN_RXADDR,read_register(EN_RXADDR) | get_child_pipe_mask(child));
}
}
@@ -1018,7 +1031,7 @@ void RF24::openReadingPipe(uint8_t child, const uint8_t *address)
void RF24::closeReadingPipe( uint8_t pipe )
{
write_register(EN_RXADDR,read_register(EN_RXADDR) & ~_BV(pgm_read_byte(&child_pipe_enable[pipe])));
write_register(EN_RXADDR,read_register(EN_RXADDR) & ~get_child_pipe_mask(pipe));
}
/****************************************************************************/
+7 -4
View File
@@ -87,7 +87,7 @@
// Avoid spurious warnings
// Arduino DUE is arm and uses traditional PROGMEM constructs
#if 1
#if ! defined( NATIVE ) && defined( ARDUINO ) && ! defined(__arm__) && ! defined( CORE_TEENSY3 )
#if ! defined( NATIVE ) && defined( ARDUINO ) && ! defined(__arm__) && ! defined( CORE_TEENSY3 ) && ! defined(ESP8266)
#undef PROGMEM
#define PROGMEM __attribute__(( section(".progmem.data") ))
#undef PSTR
@@ -97,18 +97,21 @@
// Progmem is Arduino-specific
// Arduino DUE is arm and does not include avr/pgmspace
#if defined(ARDUINO) && ! defined(__arm__)
#if defined(__AVR__)
#include <avr/pgmspace.h>
#define PRIPSTR "%S"
#else
#if defined(ESP8266)
#include <pgmspace.h>
#else
#if ! defined(ARDUINO) // This doesn't work on Arduino DUE
typedef char const char;
#else // Fill in pgm_read_byte that is used, but missing from DUE
#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#endif
#endif
#if !defined ( CORE_TEENSY )
#if !defined ( CORE_TEENSY ) && ! defined(ESP8266)
typedef uint16_t prog_uint16_t;
#define PSTR(x) (x)
#define printf_P printf
+4
View File
@@ -384,8 +384,10 @@ void RFM69::writeReg(byte addr, byte value)
void RFM69::select() {
noInterrupts();
//save current SPI settings
#ifndef ESP8266
_SPCR = SPCR;
_SPSR = SPSR;
#endif
//set RFM69 SPI settings
SPI.setDataMode(SPI_MODE0);
SPI.setBitOrder(MSBFIRST);
@@ -397,8 +399,10 @@ void RFM69::select() {
void RFM69::unselect() {
digitalWrite(_slaveSelectPin, HIGH);
//restore SPI settings to what they were before talking to RFM69
#ifndef ESP8266
SPCR = _SPCR;
SPSR = _SPSR;
#endif
interrupts();
}
+3
View File
@@ -47,10 +47,13 @@
#define RF69_IRQ_PIN 3
#define RF69_IRQ_NUM 0
#else
// ESP8266 should fall into this category now
// TODO !! Untested code! Entering unknown territory...
#define RF69_IRQ_PIN 2
#define RF69_IRQ_NUM 0
#endif
#define CSMA_LIMIT -90 // upper RX signal sensitivity threshold in dBm for carrier sense access
#define RF69_MODE_SLEEP 0 // XTAL OFF
#define RF69_MODE_STANDBY 1 // XTAL ON
+6
View File
@@ -56,8 +56,10 @@ void SPIFlash::select() {
#ifndef SPI_HAS_TRANSACTION
noInterrupts();
#endif
#ifndef ESP8266
_SPCR = SPCR;
_SPSR = SPSR;
#endif
#ifdef SPI_HAS_TRANSACTION
SPI.beginTransaction(_settings);
@@ -80,15 +82,19 @@ void SPIFlash::unselect() {
#else
interrupts();
#endif
#ifndef ESP8266
SPCR = _SPCR;
SPSR = _SPSR;
#endif
}
/// setup SPI, read device ID etc...
boolean SPIFlash::initialize()
{
#ifndef ESP8266
_SPCR = SPCR;
_SPSR = SPSR;
#endif
pinMode(_slaveSelectPin, OUTPUT);
#ifdef SPI_HAS_TRANSACTION
_settings = SPISettings(4000000, MSBFIRST, SPI_MODE0);
+1 -1
View File
@@ -31,7 +31,7 @@
#define SETUP_RETR 0x04
#define RF_CH 0x05
#define RF_SETUP 0x06
#define STATUS 0x07
#define RF24_STATUS 0x07
#define OBSERVE_TX 0x08
#define CD 0x09
#define RX_ADDR_P0 0x0A
+6 -2
View File
@@ -1,6 +1,10 @@
#include <string.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#if defined(__AVR__)
#include <avr/pgmspace.h>
#define PRIPSTR "%S"
#elif defined(ESP8266)
#include <pgmspace.h>
#endif
#include "sha256.h"
+65
View File
@@ -0,0 +1,65 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
Code by Simon Monk
http://www.simonmonk.org
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// For Arduino 1.0 and earlier
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "Event.h"
Event::Event(void)
{
eventType = EVENT_NONE;
}
void Event::update(void)
{
unsigned long now = millis();
update(now);
}
void Event::update(unsigned long now)
{
if (now - lastEventTime >= period)
{
switch (eventType)
{
case EVENT_EVERY:
(*callback)();
break;
case EVENT_OSCILLATE:
pinState = ! pinState;
digitalWrite(pin, pinState);
break;
}
lastEventTime = now;
count++;
}
if (repeatCount > -1 && count >= repeatCount)
{
eventType = EVENT_NONE;
}
}
+49
View File
@@ -0,0 +1,49 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
Code by Simon Monk
http://www.simonmonk.org
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef Event_h
#define Event_h
#include <inttypes.h>
#define EVENT_NONE 0
#define EVENT_EVERY 1
#define EVENT_OSCILLATE 2
class Event
{
public:
Event(void);
void update(void);
void update(unsigned long now);
int8_t eventType;
unsigned long period;
int repeatCount;
uint8_t pin;
uint8_t pinState;
void (*callback)(void);
unsigned long lastEventTime;
int count;
};
#endif
+29
View File
@@ -0,0 +1,29 @@
1.0 by Simon Monk
Library as downloaded 02Feb2012 22:55 UTC from http://srmonk.blogspot.com/2012/01/arduino-timer-library.html
1.1 by Jack Christensen
Changed data types of variables and functions:
o event types and indexes changed from int to int8_t.
o periods and durations changed from lon to unsigned long.
o update() and stop() functions typed as void, since they return nothing.
o pin numbers and pin values changed from int to uint8_t, this agrees with digitalWrite().
o added return values to Timer::pulse() and Timer::oscillate(uint8_t, unsigned long, uint8_t).
o changed test in Event::update() to use subtraction to avoid rollover issues.
o Updated keywords.txt file to include all functions.
1.2 by Damian Philipp
o Added a range check to Timer::stop() to avoid memory corruption.
o Added constants to <Timer.h>:
- NO_TIMER_AVAILABLE: Signals that while an event was to be queued, no free timer could be found.
- TIMER_NOT_AN_EVENT: Can be used to flag a variable that *might* contain a timer ID as
*not* containing a timer ID
o Replaced a bunch of magic numbers in <Timer.cpp> by the above constants
o Added several comments
o Added Timer::pulseImmediate(). pulseImmediate sets the pin to the specified value for the given
duration. After the duration, the pin is set to !value.
1.3 by Jack Christensen
o Added "blink2" example illustrating flashing two LEDs at different rates.
o 19Oct2013: This is the last v1.x release. It will continue to be available on GitHub
as a branch named v1.3. Future development will continue with Sandy Walsh's v2.0 which
can pass context (timer ID, etc.) to the callback functions.
+138
View File
@@ -0,0 +1,138 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
Code by Simon Monk
http://www.simonmonk.org
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// For Arduino 1.0 and earlier
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "Timer.h"
Timer::Timer(void)
{
}
int8_t Timer::every(unsigned long period, void (*callback)(), int repeatCount)
{
int8_t i = findFreeEventIndex();
if (i == -1) return -1;
_events[i].eventType = EVENT_EVERY;
_events[i].period = period;
_events[i].repeatCount = repeatCount;
_events[i].callback = callback;
_events[i].lastEventTime = millis();
_events[i].count = 0;
return i;
}
int8_t Timer::every(unsigned long period, void (*callback)())
{
return every(period, callback, -1); // - means forever
}
int8_t Timer::after(unsigned long period, void (*callback)())
{
return every(period, callback, 1);
}
int8_t Timer::oscillate(uint8_t pin, unsigned long period, uint8_t startingValue, int repeatCount)
{
int8_t i = findFreeEventIndex();
if (i == NO_TIMER_AVAILABLE) return NO_TIMER_AVAILABLE;
_events[i].eventType = EVENT_OSCILLATE;
_events[i].pin = pin;
_events[i].period = period;
_events[i].pinState = startingValue;
digitalWrite(pin, startingValue);
_events[i].repeatCount = repeatCount * 2; // full cycles not transitions
_events[i].lastEventTime = millis();
_events[i].count = 0;
return i;
}
int8_t Timer::oscillate(uint8_t pin, unsigned long period, uint8_t startingValue)
{
return oscillate(pin, period, startingValue, -1); // forever
}
/**
* This method will generate a pulse of !startingValue, occuring period after the
* call of this method and lasting for period. The Pin will be left in !startingValue.
*/
int8_t Timer::pulse(uint8_t pin, unsigned long period, uint8_t startingValue)
{
return oscillate(pin, period, startingValue, 1); // once
}
/**
* This method will generate a pulse of startingValue, starting immediately and of
* length period. The pin will be left in the !startingValue state
*/
int8_t Timer::pulseImmediate(uint8_t pin, unsigned long period, uint8_t pulseValue)
{
int8_t id(oscillate(pin, period, pulseValue, 1));
// now fix the repeat count
if (id >= 0 && id < MAX_NUMBER_OF_EVENTS) {
_events[id].repeatCount = 1;
}
return id;
}
void Timer::stop(int8_t id)
{
if (id >= 0 && id < MAX_NUMBER_OF_EVENTS) {
_events[id].eventType = EVENT_NONE;
}
}
void Timer::update(void)
{
unsigned long now = millis();
update(now);
}
void Timer::update(unsigned long now)
{
for (int8_t i = 0; i < MAX_NUMBER_OF_EVENTS; i++)
{
if (_events[i].eventType != EVENT_NONE)
{
_events[i].update(now);
}
}
}
int8_t Timer::findFreeEventIndex(void)
{
for (int8_t i = 0; i < MAX_NUMBER_OF_EVENTS; i++)
{
if (_events[i].eventType == EVENT_NONE)
{
return i;
}
}
return NO_TIMER_AVAILABLE;
}
+67
View File
@@ -0,0 +1,67 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
Code by Simon Monk
http://www.simonmonk.org
* * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef Timer_h
#define Timer_h
#include <inttypes.h>
#include "Event.h"
#define MAX_NUMBER_OF_EVENTS (10)
#define TIMER_NOT_AN_EVENT (-2)
#define NO_TIMER_AVAILABLE (-1)
class Timer
{
public:
Timer(void);
int8_t every(unsigned long period, void (*callback)(void));
int8_t every(unsigned long period, void (*callback)(void), int repeatCount);
int8_t after(unsigned long duration, void (*callback)(void));
int8_t oscillate(uint8_t pin, unsigned long period, uint8_t startingValue);
int8_t oscillate(uint8_t pin, unsigned long period, uint8_t startingValue, int repeatCount);
/**
* This method will generate a pulse of !startingValue, occuring period after the
* call of this method and lasting for period. The Pin will be left in !startingValue.
*/
int8_t pulse(uint8_t pin, unsigned long period, uint8_t startingValue);
/**
* This method will generate a pulse of pulseValue, starting immediately and of
* length period. The pin will be left in the !pulseValue state
*/
int8_t pulseImmediate(uint8_t pin, unsigned long period, uint8_t pulseValue);
void stop(int8_t id);
void update(void);
void update(unsigned long now);
protected:
Event _events[MAX_NUMBER_OF_EVENTS];
int8_t findFreeEventIndex(void);
};
#endif
@@ -0,0 +1,29 @@
//Flash two LEDs at different rates using Simon Monk's Timer library
//http://www.doctormonk.com/2012/01/arduino-timer-library.html
//
//Jack Christensen 30Sep2013
//
//Beerware license: Free for any and all purposes, but if you find it
//useful AND we actually meet someday, you can buy me a beer!
#include "Timer.h" //http://github.com/JChristensen/Timer
const int LED1 = 8; //connect one LED to this pin (with appropriate current-limiting resistor of course)
const int LED2 = 9; //connect another LED to this pin (don't forget the resistor)
const unsigned long PERIOD1 = 1000; //one second
const unsigned long PERIOD2 = 10000; //ten seconds
Timer t; //instantiate the timer object
void setup(void)
{
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
t.oscillate(LED1, PERIOD1, HIGH);
t.oscillate(LED2, PERIOD2, HIGH);
}
void loop(void)
{
t.update();
}
@@ -0,0 +1,42 @@
#include "Timer.h"
Timer t;
int ledEvent;
void setup()
{
Serial.begin(9600);
int tickEvent = t.every(2000, doSomething);
Serial.print("2 second tick started id=");
Serial.println(tickEvent);
pinMode(13, OUTPUT);
ledEvent = t.oscillate(13, 50, HIGH);
Serial.print("LED event started id=");
Serial.println(ledEvent);
int afterEvent = t.after(10000, doAfter);
Serial.print("After event started id=");
Serial.println(afterEvent);
}
void loop()
{
t.update();
}
void doSomething()
{
Serial.print("2 second tick: millis()=");
Serial.println(millis());
}
void doAfter()
{
Serial.println("stop the led event");
t.stop(ledEvent);
t.oscillate(13, 500, HIGH, 5);
}
@@ -0,0 +1,17 @@
#include "Timer.h"
Timer t;
int pin = 13;
void setup()
{
pinMode(pin, OUTPUT);
t.pulse(pin, 10 * 1000, HIGH); // 10 seconds
// t.pulse(pin, 10 * 60 * 1000, HIGH); // 10 minutes
}
void loop()
{
t.update();
}
@@ -0,0 +1,22 @@
#include "Timer.h"
Timer t;
int pin = 13;
void setup()
{
Serial.begin(9600);
pinMode(pin, OUTPUT);
t.oscillate(pin, 100, LOW);
t.every(1000, takeReading);
}
void loop()
{
t.update();
}
void takeReading()
{
Serial.println(analogRead(0));
}
+31
View File
@@ -0,0 +1,31 @@
#######################################
# Syntax Coloring Map For Timer Library
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
Timer KEYWORD1
Event KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
every KEYWORD2
after KEYWORD2
oscillate KEYWORD2
pulse KEYWORD2
pulseImmediate KEYWORD2
stop KEYWORD2
update KEYWORD2
findFreeEventIndex KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
#######################################
# Constants (LITERAL1)
#######################################