/** * 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 * 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 - Changed for MySensors usage by Bart Eversdijk * Version 1.1 - Added option to record manual presets up to 240 * Version 2.0 - Migrated to MySensrors version 2.0 * * DESCRIPTION * * IRrecord: record and play back IR signals as a minimal * An IR detector/demodulator must be connected to the input RECV_PIN. * An IR LED must be connected to the output PWM pin 3. * * * The logic is: * If a V_IR_RECORD is received the node enters in record mode and once a valid IR message has been received * it is stored in EEPROM. The first byte of the V_IR_RECORD message will be used as preset ID * * If a V_IR_SEND the IR message beloning to the preset number of the first message byte is broadcasted * * * Version 0.11 September, 2009 * Copyright 2009 Ken Shirriff * http://arcfn.com */ // Enable debug prints #define MY_DEBUG // Enable and select radio type attached #define MY_RADIO_RF24 #define MY_NODE_ID 55 #define MY_RF24_CE_PIN 10 #define MY_RF24_CS_PIN 9 #include #include #include // https://github.com/z3t0/Arduino-IRremote/releases // OR install IRRemote via "Sketch" -> "Include Library" -> "Manage Labraries..." // Search for IRRemote b shirif and press the install button // Arduino pin to connect the IR receiver to int RECV_PIN = 7; //Include RF #include RCSwitch mySwitch = RCSwitch(); #define CHILD_ID 2 #define CANDLES_CHILD_ID 5 #define LED_CHILD_ID 6 #define HAARD_CHILD_ID 7 #define MY_RAWBUF 50 const char * TYPE2STRING[] = { "UNKONWN", "RC5", "RC6", "NEC", "Sony", "Panasonic", "JVC", "SAMSUNG", "Whynter", "AIWA RC T501", "LG", "Sanyo", "Mitsubishi", "Dish", "Sharp", "Denon" }; #define Type2String(x) TYPE2STRING[x < 0 ? 0 : x] #define AddrTxt F(" addres: 0x") #define ValueTxt F(" value: 0x") #define NATxt F(" - not implemented/found") // Raw or unknown codes requires an Arduino with a larger memory like a MEGA and some changes to store in EEPROM (now max 255 bytes) // #define IR_SUPPORT_UNKNOWN_CODES typedef union { struct { decode_type_t type; // The type of code unsigned long value; // The data bits if type is not raw int len; // The length of the code in bits unsigned int address; // Used by Panasonic & Sharp [16-bits] } code; #ifdef IR_SUPPORT_UNKNOWN_CODES struct { decode_type_t type; // The type of code unsigned int codes[MY_RAWBUF]; byte count; // The number of interval samples } raw; #endif } IRCode; #define MAX_STORED_IR_CODES 10 IRCode StoredIRCodes[MAX_STORED_IR_CODES]; IRrecv irrecv(RECV_PIN); IRsend irsend; decode_results ircode; #define NO_PROG_MODE 0xFF byte progModeId = NO_PROG_MODE; // Manual Preset IR values -- these are working demo values // VERA call: luup.call_action("urn:schemas-arduino-cc:serviceId:ArduinoIr1", "SendIrCode", {Index=15}, ) // One can add up to 240 preset codes (if your memory lasts) to see to correct data connect the Arduino with this plug in and // look at the serial monitor while pressing the desired RC button // ON = NEC FF00FF (32 bits) //OFF = NEC FF807F (32 bits) /** Duni Warm White LED Candle / Duni Warmweiß LED Kerzen~ ON={"Protocol":"NEC","Bits":32,"Data":"0x00FF00FF"} OFF={"Protocol":"NEC","Bits":32,"Data":"0x00FF807F"} 4h={"Protocol":"NEC","Bits":32,"Data":"0x00FF40BF"} 8h={"Protocol":"NEC","Bits":32,"Data":"0x00FFC03F"} ModeCandle={"Protocol":"NEC","Bits":32,"Data":"0x00FF20DF"} ModeLight={"Protocol":"NEC","Bits":32,"Data":"0x00FFA05F"} ModeDark={"Protocol":"NEC","Bits":32,"Data":"0x00FF906F"} ModeBright={"Protocol":"NEC","Bits":32,"Data":"0x00FFE01F"} ModeMoon={"Protocol":"NEC","Bits":32,"Data":"0x00FF10EF"} ModeNightLight={"Protocol":"NEC","Bits":32,"Data":"0x00FF609F"} **/ IRCode PresetIRCodes[] = { { { NEC, 0xFF00FF, 32, 0 }}, // 11 - NEC ON { { NEC, 0xFF807F, 32, 0 }}, // 12 - NEC OFF { { NEC, 0xFFA05F, 32, 0 }}, // 13 - NEC Passive Mode" { { NEC, 0xFF20DF, 32, 0 }}, // 14 - NEC Active Mode { { NEC, 0xFF906F, 32, 0 }}, // 15 - NEC DIM - { { NEC, 0xFFE01F, 32, 0 }}, // 16 - NEC DIM + { { NEC, 0xFF10EF, 32, 0 }}, // 17 - NEC key "4" { { NEC, 0xFF38C7, 32, 0 }}, // 18 - NEC key "5" { { RC6, 0x800F2401, 36, 0 }}, // 19 - RC6 key "1" MicroSoft Mulitmedia RC { { RC6, 0x800F2402, 36, 0 }} // 20 - RC6 key "2" MicroSoft Mulitmedia RC }; #define MAX_PRESET_IR_CODES (sizeof(PresetIRCodes)/sizeof(IRCode)) #define MAX_IR_CODES (MAX_STORED_IR_CODES + MAX_PRESET_IR_CODES) //MyMessage msgIrReceive(CHILD_ID, V_IR_RECEIVE); //MyMessage msgIrRecord(CHILD_ID, V_IR_RECORD); void setup() { // Tell MYS Controller that we're NOT recording // send(msgIrRecord.set(0)); Serial.println(F("Recall EEPROM settings")); recallEeprom(sizeof(StoredIRCodes), (byte *)&StoredIRCodes); // Start the ir receiver // irrecv.enableIRIn(); // Transmitter is connected to Arduino Pin #8 mySwitch.enableTransmit(8); // Optional set protocol (default is 1, will work for most outlets) mySwitch.setProtocol(1); // Optional set pulse length. mySwitch.setPulseLength(390); // Optional set number of transmission repetitions. mySwitch.setRepeatTransmit(5); Serial.println(F("Init done...")); } void presentation () { // Send the sketch version information to the gateway and Controller sendSketchInfo("Kaarsen (IR en RF)", "3.0"); // Register a sensors to gw. Use binary light for test purposes. present(CHILD_ID, S_IR); present(CANDLES_CHILD_ID, S_LIGHT); present(LED_CHILD_ID, S_LIGHT); present(HAARD_CHILD_ID, S_LIGHT); } void loop() { /* if (irrecv.decode(&ircode)) { dump(&ircode); if (progModeId != NO_PROG_MODE) { // If we are in PROG mode (Recording) store the new IR code and end PROG mode if (storeRCCode(progModeId)) { Serial.println(F("Stored ")); // If sucessfull RC decode and storage --> also update the EEPROM storeEeprom(sizeof(StoredIRCodes), (byte *)&StoredIRCodes); progModeId = NO_PROG_MODE; // Tell MYS Controller that we're done recording send(msgIrRecord.set(0)); } } else { // If we are in Playback mode just tell the MYS Controller we did receive an IR code if (ircode.decode_type != UNKNOWN) { if (ircode.value != REPEAT) { // Look if we found a stored preset 0 => not found byte num = lookUpPresetCode(&ircode); if (num) { // Send IR decode result to the MYS Controller Serial.print(F("Found code for preset #")); Serial.println(num); send(msgIrReceive.set(num)); } } } } // Wait a while before receive next IR-code (also block MySensors receiver so it will not interfere with a new message) delay(500); // Start receiving again irrecv.resume(); } */ } void receive(const MyMessage &message) { //Serial.print(F("New message: ")); //Serial.println(message.type); //Message of type V_LIGHT ? if (message.type==V_LIGHT) { //Lamp 1 if (message.sensor==CANDLES_CHILD_ID) { if(message.getBool()==1) { Serial.println(" --> ON"); sendRCCode(11); delay(100); sendRCCode(13); delay(100); sendRCCode(15); delay(100); sendRCCode(15); delay(100); sendRCCode(15); delay(100); sendRCCode(15); delay(100); sendRCCode(15); } else { Serial.println(" --> OFF"); sendRCCode(12); } } } //Message of type V_LIGHT ? if (message.type==V_LIGHT) { //Lamp 1 if (message.sensor==LED_CHILD_ID) { if(message.getBool()==1) { Serial.println(" --> ON"); mySwitch.send(14944537, 24); //ON } else { Serial.println(" --> OFF"); mySwitch.send(14944539, 24); //OFF } } } //Message of type V_LIGHT ? if (message.type==V_LIGHT) { //Lamp 1 if (message.sensor==HAARD_CHILD_ID) { if(message.getBool()==1) { Serial.println(" --> HAARD ON"); IrSender.sendNEC(0x0, 0x81, 5); } else { Serial.println(" --> HAARD OFF"); IrSender.sendNEC(0x0, 0x81, 5); } } } /* if (message.type == V_IR_RECORD) { // IR_RECORD V_VAR1 // Get IR record requets for index : paramvalue progModeId = message.getByte() % MAX_STORED_IR_CODES; // Tell MYS Controller that we're now in recording mode send(msgIrRecord.set(1)); Serial.print(F("Record new IR for: ")); Serial.println(progModeId); } if (message.type == V_IR_SEND) { // Send an IR code from offset: paramvalue - no check for legal value Serial.print(F("Send IR preset: ")); byte code = message.getByte() % MAX_IR_CODES; if (code == 0) { code = MAX_IR_CODES; } Serial.print(code); sendRCCode(code); } // Start receiving ir again... irrecv.enableIRIn(); */ } /* byte lookUpPresetCode (decode_results *ircode) { // Get rit of the RC5/6 toggle bit when looking up if (ircode->decode_type == RC5) { ircode->value = ircode->value & 0x7FF; } if (ircode->decode_type == RC6) { ircode->value = ircode->value & 0xFFFF7FFF; } for (byte index = 0; index < MAX_STORED_IR_CODES; index++) { if ( StoredIRCodes[index].code.type == ircode->decode_type && StoredIRCodes[index].code.value == ircode->value && StoredIRCodes[index].code.len == ircode->bits) { // The preset number starts with 1 so the last is stored as 0 -> fix this when looking up the correct index return (index == 0) ? MAX_STORED_IR_CODES : index; } } for (byte index = 0; index < MAX_PRESET_IR_CODES; index++) { if ( PresetIRCodes[index].code.type == ircode->decode_type && PresetIRCodes[index].code.value == ircode->value && PresetIRCodes[index].code.len == ircode->bits) { // The preset number starts with 1 so the last is stored as 0 -> fix this when looking up the correct index return ((index == 0) ? MAX_PRESET_IR_CODES : index) + MAX_STORED_IR_CODES; } } // not found so return 0 return 0; } // Stores the code for later playback bool storeRCCode(byte index) { if (ircode.decode_type == UNKNOWN) { #ifdef IR_SUPPORT_UNKNOWN_CODES Serial.println(F("Received unknown code, saving as raw")); // To store raw codes: // Drop first value (gap) // As of v1.3 of IRLib global values are already in microseconds rather than ticks // They have also been adjusted for overreporting/underreporting of marks and spaces byte rawCount = min(ircode.rawlen - 1, MY_RAWBUF); for (int i = 1; i <= rawCount; i++) { StoredIRCodes[index].raw.codes[i - 1] = ircode.rawbuf[i]; // Drop the first value }; return true; #else return false; } #endif if (ircode.value == REPEAT) { // Don't record a NEC repeat value as that's useless. Serial.println(F("repeat; ignoring.")); return false; } // Get rit of the toggle bit when storing RC5/6 if (ircode.decode_type == RC5) { ircode.value = ircode.value & 0x07FF; } if (ircode.decode_type == RC6) { ircode.value = ircode.value & 0xFFFF7FFF; } StoredIRCodes[index].code.type = ircode.decode_type; StoredIRCodes[index].code.value = ircode.value; StoredIRCodes[index].code.address = ircode.address; // Used by Panasonic & Sharp [16-bits] StoredIRCodes[index].code.len = ircode.bits; Serial.print(F(" value: 0x")); Serial.println(ircode.value, HEX); return true; } */ void sendRCCode(byte index) { IRCode *pIr = ((index <= MAX_STORED_IR_CODES) ? &StoredIRCodes[index % MAX_STORED_IR_CODES] : &PresetIRCodes[index - MAX_STORED_IR_CODES - 1]); #ifdef IR_SUPPORT_UNKNOWN_CODES if(pIr->code.type == UNKNOWN) { // Assume 38 KHz irsend.sendRaw(pIr->raw.codes, pIr->raw.count, 38); Serial.println(F("Sent raw")); return; } #endif Serial.print(F(" - sent ")); Serial.print(Type2String(pIr->code.type)); if (pIr->code.type == RC5) { // For RC5 and RC6 there is a toggle bit for each succesor IR code sent alway toggle this bit, needs to repeat the command 3 times with 100 mS pause pIr->code.value ^= 0x0800; for (byte i=0; i < 3; i++) { if (i > 0) { delay(100); } irsend.sendRC5(pIr->code.value, pIr->code.len); } } else if (pIr->code.type == RC6) { // For RC5 and RC6 there is a toggle bit for each succesor IR code sent alway toggle this bit, needs to repeat the command 3 times with 100 mS pause if (pIr->code.len == 20) { pIr->code.value ^= 0x10000; } for (byte i=0; i < 3; i++) { if (i > 0) { delay(100); } irsend.sendRC6(pIr->code.value, pIr->code.len); } } else if (pIr->code.type == NEC) { irsend.sendNEC(pIr->code.value, pIr->code.len); } else if (pIr->code.type == SONY) { irsend.sendSony(pIr->code.value, pIr->code.len); } else if (pIr->code.type == DENON) { irsend.sendDenon(pIr->code.value, pIr->code.len); } else { // No valid IR type, found it does not make sense to broadcast Serial.println(NATxt); return; } Serial.print(" "); Serial.println(pIr->code.value, HEX); } /* // Dumps out the decode_results structure. void dump(decode_results *results) { int count = results->rawlen; Serial.print(F("Received : ")); Serial.print(results->decode_type, DEC); Serial.print(F(" ")); Serial.print(Type2String(results->decode_type)); if (results->decode_type == PANASONIC) { Serial.print(AddrTxt); Serial.print(results->address,HEX); Serial.print(ValueTxt); } Serial.print(F(" ")); Serial.print(results->value, HEX); Serial.print(F(" (")); Serial.print(results->bits, DEC); Serial.println(F(" bits)")); if (results->decode_type == UNKNOWN) { Serial.print(F("Raw (")); Serial.print(count, DEC); Serial.print(F("): ")); for (int i = 0; i < count; i++) { if ((i % 2) == 1) { Serial.print(results->rawbuf[i]*USECPERTICK, DEC); } else { Serial.print(-(int)results->rawbuf[i]*USECPERTICK, DEC); } Serial.print(" "); } Serial.println(""); } } */ // Store IR record struct in EEPROM void storeEeprom(byte len, byte *buf) { saveState(0, len); for (byte i = 1; i < min(len, 100); i++, buf++) { saveState(i, *buf); } } void recallEeprom(byte len, byte *buf) { if (loadState(0) != len) { Serial.print(F("Corrupt EEPROM preset values and Clear EEPROM")); for (byte i = 1; i < min(len, 100); i++, buf++) { *buf = 0; storeEeprom(len, buf); } return; } for (byte i = 1; i < min(len, 100); i++, buf++) { *buf = loadState(i); } }