Kaarsen/Kaarsen.ino
2023-04-08 09:39:10 +02:00

567 lines
16 KiB
C++

/**
* 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 - 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 <SPI.h>
#include <MySensors.h>
#include <IRremote.h> // 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.h>
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}, <device number>)
// 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);
}
}