diff --git a/lib/RemoteSwitch/RemoteReceiver.cpp b/lib/RemoteSwitch/RemoteReceiver.cpp
new file mode 100644
index 00000000..6195cf4c
--- /dev/null
+++ b/lib/RemoteSwitch/RemoteReceiver.cpp
@@ -0,0 +1,158 @@
+/*
+ * RemoteSwitch library v2.0.0 made by Randy Simons http://randysimons.nl
+ * See RemoteSwitchSender.h for details.
+ *
+ * License: "Free BSD license". See license.txt
+ */
+
+#include "RemoteReceiver.h"
+
+
+/************
+* RemoteReceiver
+************/
+
+unsigned short RemoteReceiver::_interrupt;
+volatile int RemoteReceiver::_state;
+unsigned short RemoteReceiver::_minRepeats;
+RemoteReceiverCallBack RemoteReceiver::_callback;
+boolean RemoteReceiver::_inCallback = false;
+
+void RemoteReceiver::init(unsigned short interrupt, unsigned short minRepeats, RemoteReceiverCallBack callback) {
+ _interrupt = interrupt;
+ _minRepeats = minRepeats;
+ _callback = callback;
+
+ //enable();
+}
+
+void RemoteReceiver::enable() {
+ _state = -1;
+ attachInterrupt(_interrupt, interruptHandler, CHANGE);
+}
+
+void RemoteReceiver::disable() {
+ detachInterrupt(_interrupt);
+}
+
+
+void RemoteReceiver::interruptHandler() {
+ static unsigned int period; //Calculated duration of 1 period
+ static unsigned short receivedBit; //Contains "bit" currently receiving
+ static unsigned long receivedCode; //Contains received code
+ static unsigned long previousCode; //Contains previous received code
+ static unsigned short repeats = 0; //The number of times the an identical code is received in a row.
+ static unsigned long lastChange=0; //Timestamp of previous edge
+ static unsigned int min1Period, max1Period, min3Period, max3Period;
+
+ unsigned long currentTime=micros();
+ unsigned int duration=currentTime-lastChange; //Duration = Time between edges
+ lastChange=currentTime;
+
+
+ if (_state==-1) { //Waiting for sync-signal
+ if (duration>3720) { //==31*120 minimal time between two edges before decoding starts.
+ //Sync signal received.. Preparing for decoding
+ period=duration/31;
+ receivedCode=previousCode=repeats=0;
+
+ //Allow for large error-margin. ElCheapo-hardware :(
+ min1Period=period*4/10; //Avoid floating point math; saves memory.
+ max1Period=period*16/10;
+ min3Period=period*23/10;
+ max3Period=period*37/10;
+ }
+ else {
+ return;
+ }
+ } else if (_state<48) { //Decoding message
+ //bit part durations can ONLY be 1 or 3 periods.
+ if (duration>=min1Period && duration<=max1Period) {
+ bitClear(receivedBit,_state%4); //Note: this sets the bits in reversed order! Correct order would be: 3-(_state%4), but that's overhead we don't want.
+ }
+ else if (duration>=min3Period && duration<=max3Period) {
+ bitSet(receivedBit,_state%4); //Note: this sets the bits in reversed order!
+ }
+ else { //Otherwise the entire sequence is invalid
+ _state=-1;
+ return;
+ }
+
+ if ((_state%4)==3) { //Last bit part?
+ //Shift
+ receivedCode*=3;
+ //Decode bit.
+ if (receivedBit==B1010) { //short long short long == B0101, but bits are set in reversed order, so compare to B1010
+ //bit "0" received
+ receivedCode+=0; //I hope the optimizer handles this ;)
+ }
+ else if (receivedBit==B0101) { //long short long short == B101, but bits are set in reversed order, so compare to B0101
+ //bit "1" received
+ receivedCode+=1;
+ }
+ else if (receivedBit==B0110) { //short long long short. Reversed too, but makes no difference.
+ //bit "f" received
+ receivedCode+=2;
+ }
+ else {
+ //Bit was rubbish. Abort.
+ _state=-1;
+ return;
+ }
+ }
+ } else if (_state==48) { //Waiting for sync bit part 1
+ //Must be 1 period.
+ if (duration
max1Period) {
+ _state=-1;
+ return;
+ }
+ } else { //Waiting for sync bit part 2
+ //Must be 31 periods.
+ if (durationperiod*36) {
+ _state=-1;
+ return;
+ }
+
+ //receivedCode is a valid code!
+
+ if (receivedCode!=previousCode) {
+ repeats=0;
+ previousCode=receivedCode;
+ }
+
+ repeats++;
+
+ if (repeats>=_minRepeats) {
+ if (!_inCallback) {
+ _inCallback = true;
+ (_callback)(receivedCode, period);
+ _inCallback = false;
+ }
+ //Reset after callback.
+ _state=-1;
+ return;
+ }
+
+ //Reset for next round
+ receivedCode = 0;
+ _state=0; //no need to wait for another sync-bit!
+ return;
+ }
+
+ _state++;
+ return;
+}
+
+boolean RemoteReceiver::isReceiving(int waitMillis) {
+ unsigned long startTime=millis();
+
+ int waited; //signed int!
+ do {
+ if (_state!=-1) {
+ return true;
+ }
+ waited = (millis()-startTime);
+ } while(waited>=0 && waited <= waitMillis); //Yes, clock wraps every 50 days. And then you'd have to wait for a looooong time.
+
+ return false;
+}
diff --git a/lib/RemoteSwitch/RemoteReceiver.h b/lib/RemoteSwitch/RemoteReceiver.h
new file mode 100644
index 00000000..357d92db
--- /dev/null
+++ b/lib/RemoteSwitch/RemoteReceiver.h
@@ -0,0 +1,75 @@
+/*
+ * RemoteSwitch library v2.0.0 made by Randy Simons http://randysimons.nl
+ *
+ * License: "Free BSD license". See license.txt
+ */
+
+#ifndef RemoteReceiver_h
+#define RemoteReceiver_h
+
+//#include "WProgram.h"
+#include "Arduino.h"
+
+typedef void (*RemoteReceiverCallBack)(unsigned long, unsigned int);
+
+/**
+* See RemoteSwitch for introduction.
+*
+* RemoteReceiver decodes the signal received from a 433MHz-receiver, like the "KlikAanKlikUit"-system
+* as well as the signal sent by the RemoteSwtich class. When a correct signal is received,
+* a user-defined callback function is called.
+*
+* Note that in the callback function, the interrupts are still disabled. You can enabled them, if needed.
+* A call to the callback must b finished before RemoteReceiver will call the callback function again, thus
+* there is no re-entrant problem.
+*
+* When sending your own code using RemoteSwich, disable() the receiver first.
+*
+* This is a pure static class, for simplicity and to limit memory-use.
+*/
+
+class RemoteReceiver {
+ public:
+ /**
+ * Initializes the decoder.
+ *
+ * @param interrupt The interrupt as is used by Arduino's attachInterrupt function. See attachInterrupt for details.
+ * @param minRepeats The number of times the same code must be received in a row before the callback is calles
+ * @param callback Pointer to a callback function, with signature void (*func)(unsigned long, unsigned int). First parameter is the decoded data, the second the period of the timing.
+ */
+ static void init(unsigned short interrupt, unsigned short minRepeats, RemoteReceiverCallBack callback);
+
+ /**
+ * Enabled decoding. No need to call enable() after init().
+ */
+ static void enable();
+
+ /**
+ * Disable decoding. You can enable decoding by calling enable();
+ */
+ static void disable();
+
+ /**
+ * Tells wether a signal is being received. Since it makes no sense to transmit while another transmitter is active,
+ * it's best to wait for isReceiving() to false.
+ *
+ * Note: isReceiving() depends on interrupts enabled. Thus, when disabled()'ed, or when interrupts are disabled (as is
+ * the case in the callback), isReceiving() will not work properly.
+
+ * @param waitMillis number of milliseconds to monitor for signal.
+ * @return boolean If after waitMillis no signal was being processed, returns false. If before expiration a signal was being processed, returns true.
+ */
+ static boolean isReceiving(int waitMillis = 50);
+
+ private:
+ static void interruptHandler();
+
+ static unsigned short _interrupt; //Radio input interrupt
+ volatile static int _state; //State of decoding process. There are 49 states, 1 for "waiting for signal" and 48 for decoding the 48 edges in a valid code.
+ static unsigned short _minRepeats;
+ static RemoteReceiverCallBack _callback;
+ static boolean _inCallback; //When true, the callback function is being executed; prevents re-entrance.
+
+};
+
+#endif
diff --git a/lib/RemoteSwitch/RemoteSwitch.cpp b/lib/RemoteSwitch/RemoteSwitch.cpp
new file mode 100644
index 00000000..96828012
--- /dev/null
+++ b/lib/RemoteSwitch/RemoteSwitch.cpp
@@ -0,0 +1,263 @@
+/*
+ * RemoteSwitch library v2.0.0 made by Randy Simons http://randysimons.nl
+ * See RemoteSwitchSender.h for details.
+ *
+ * License: "Free BSD license". See license.txt
+ */
+
+#include "RemoteSwitch.h"
+
+
+/************
+* RemoteSwitch
+************/
+
+RemoteSwitch::RemoteSwitch(unsigned short pin, unsigned int periodusec, unsigned short repeats) {
+ _pin=pin;
+ _periodusec=periodusec;
+ _repeats=repeats;
+
+ pinMode(_pin, OUTPUT);
+}
+
+unsigned long RemoteSwitch::encodeTelegram(unsigned short trits[]) {
+ unsigned long data = 0;
+
+ //Encode data
+ for (unsigned short i=0;i<12;i++) {
+ data*=3;
+ data+=trits[i];
+ }
+
+ //Encode period duration
+ data |= (unsigned long)_periodusec << 23;
+
+ //Encode repeats
+ data |= (unsigned long)_repeats << 20;
+
+ return data;
+}
+
+void RemoteSwitch::sendTelegram(unsigned short trits[]) {
+ sendTelegram(encodeTelegram(trits),_pin);
+}
+
+/**
+* Format data:
+* pppppppp|prrrdddd|dddddddd|dddddddd (32 bit)
+* p = perioud (9 bit unsigned int
+* r = repeats as 2log. Thus, if r = 3, then signal is sent 2^3=8 times
+* d = data
+*/
+void RemoteSwitch::sendTelegram(unsigned long data, unsigned short pin) {
+ unsigned int periodusec = (unsigned long)data >> 23;
+ unsigned short repeats = 1 << (((unsigned long)data >> 20) & B111);
+ data = data & 0xfffff; //truncate to 20 bit
+
+ //Convert the base3-code to base4, to avoid lengthy calculations when transmitting.. Messes op timings.
+ unsigned long dataBase4 = 0;
+
+ for (unsigned short i=0; i<12; i++) {
+ dataBase4<<=2;
+ dataBase4|=(data%3);
+ data/=3;
+ }
+
+ for (unsigned short int j=0;j>=2;
+ }
+
+ //Send termination/synchronisation-signal. Total length: 32 periods
+ digitalWrite(pin, HIGH);
+ delayMicroseconds(periodusec);
+ digitalWrite(pin, LOW);
+ delayMicroseconds(periodusec*31);
+ }
+}
+
+boolean RemoteSwitch::isSameCode(unsigned long encodedTelegram, unsigned long receivedData) {
+ return (receivedData==(encodedTelegram & 0xFFFFF)); //Compare the 20 LSB's
+}
+
+
+/************
+* ActionSwitch
+************/
+
+ActionSwitch::ActionSwitch(unsigned short pin, unsigned int periodusec) : RemoteSwitch(pin,periodusec,3) {
+ //Call contructor
+}
+
+
+void ActionSwitch::sendSignal(unsigned short systemCode, char device, boolean on) {
+ sendTelegram(getTelegram(systemCode,device,on), _pin);
+}
+
+unsigned long ActionSwitch::getTelegram(unsigned short systemCode, char device, boolean on) {
+ unsigned short trits[12];
+
+ device-=65;
+
+ for (unsigned short i=0; i<5; i++) {
+ //bits 0-4 contain address (2^5=32 addresses)
+ trits[i]=(systemCode & 1)?1:2;
+ systemCode>>=1;
+
+ //bits 5-9 contain device. Only one trit has value 0, others have 2 (float)!
+ trits[i+5]=(i==device?0:2);
+ }
+
+ //switch on or off
+ trits[10]=(!on?0:2);
+ trits[11]=(on?0:2);
+
+ return encodeTelegram(trits);
+}
+
+/************
+* BlokkerSwitch
+************/
+
+BlokkerSwitch::BlokkerSwitch(unsigned short pin, unsigned int periodusec) : RemoteSwitch(pin,periodusec,3) {
+ //Call contructor
+}
+
+
+void BlokkerSwitch::sendSignal(unsigned short device, boolean on) {
+ sendTelegram(getTelegram(device,on), _pin);
+}
+
+unsigned long BlokkerSwitch::getTelegram(unsigned short device, boolean on) {
+ unsigned short trits[12]={0};
+
+ device--;
+
+ for (unsigned short i=1; i<4; i++) {
+ //Bits 1-3 contain device
+ trits[i]=(device & 1)?0:1;
+ device>>=1;
+ }
+
+ //switch on or off
+ trits[8]=(on?1:0);
+
+ return encodeTelegram(trits);
+}
+
+/************
+* KaKuSwitch
+************/
+
+KaKuSwitch::KaKuSwitch(unsigned short pin, unsigned int periodusec) : RemoteSwitch(pin,periodusec,3) {
+ //Call contructor
+}
+
+void KaKuSwitch::sendSignal(char address, unsigned short device, boolean on) {
+ sendTelegram(getTelegram(address, device, on), _pin);
+}
+
+unsigned long KaKuSwitch::getTelegram(char address, unsigned short device, boolean on) {
+ unsigned short trits[12];
+
+ address-=65;
+ device-=1;
+
+ for (unsigned short i=0; i<4; i++) {
+ //bits 0-3 contain address (2^4 = 16 addresses)
+ trits[i]=(address & 1)?2:0;
+ address>>=1;
+
+ //bits 4-8 contain device (2^4 = 16 addresses)
+ trits[i+4]=(device & 1)?2:0;
+ device>>=1;
+ }
+
+ //bits 8-10 seem to be fixed
+ trits[8]=0;
+ trits[9]=2;
+ trits[10]=2;
+
+ //switch on or off
+ trits[11]=(on?2:0);
+
+ return encodeTelegram(trits);
+}
+
+void KaKuSwitch::sendSignal(char address, unsigned short group, unsigned short device, boolean on) {
+ sendTelegram(getTelegram(address, group, on), _pin);
+}
+
+unsigned long KaKuSwitch::getTelegram(char address, unsigned short group, unsigned short device, boolean on) {
+ unsigned short trits[12], i;
+
+ address-=65;
+ group-=1;
+ device-=1;
+
+ //address. M3E Pin A0-A3
+ for (i=0; i<4; i++) {
+ //bits 0-3 contain address (2^4 = 16 addresses)
+ trits[i]=(address & 1)?2:0;
+ address>>=1;
+ }
+
+ //device. M3E Pin A4-A5
+ for (; i<6; i++) {
+ trits[i]=(device & 1)?2:0;
+ device>>=1;
+ }
+
+ //group. M3E Pin A6-A7
+ for (; i<8; i++) {
+ trits[i]=(group & 1)?2:0;
+ group>>=1;
+ }
+
+ //bits 8-10 are be fixed. M3E Pin A8/D0-A10/D2
+ trits[8]=0;
+ trits[9]=2;
+ trits[10]=2;
+
+ //switch on or off, M3E Pin A11/D3
+ trits[11]=(on?2:0);
+
+ return encodeTelegram(trits);
+}
diff --git a/lib/RemoteSwitch/RemoteSwitch.h b/lib/RemoteSwitch/RemoteSwitch.h
new file mode 100644
index 00000000..762ae979
--- /dev/null
+++ b/lib/RemoteSwitch/RemoteSwitch.h
@@ -0,0 +1,197 @@
+/*
+ * RemoteSwitch library v2.0.0 made by Randy Simons http://randysimons.nl
+ *
+ * License: "Free BSD license". See license.txt
+ */
+
+#ifndef RemoteSwitch_h
+#define RemoteSwitch_h
+
+//#include "WProgram.h"
+#include "Arduino.h"
+
+/**
+* RemoteSwitch provides a generic class for simulation of common RF remote controls, like the 'Klik aan Klik uit'-system
+* (http://www.klikaanklikuit.nl/), used to remotely switch lights etc.
+*
+* Many of these remotes seem to use a 433MHz SAW resonator and one of these chips: LP801B, HX2262, PT2262, M3E.
+* Datasheet for the HX2262/PT2262 ICs:
+* http://www.princeton.com.tw/downloadprocess/downloadfile.asp?mydownload=PT2262_1.pdf
+*
+* Hardware required for this library: a 433MHz SAW oscillator transmitter, e.g.
+* http://www.sparkfun.com/commerce/product_info.php?products_id=7815
+* http://www.conrad.nl/goto/?product=130428
+*
+* Notes:
+* - Since these chips use (and send!) tri-state inputs (low, high and floating) I use 'trits' instead of 'bits',
+* when appropriate.
+* - I measured the period lengths with a scope. Thus: they work for my remotes, but may fail for yours...
+* A better way would be to calculate the 'target'-timings using the datasheets and the resistor-values on the remotes.
+*/
+class RemoteSwitch {
+ public:
+ /**
+ * Constructor.
+ *
+ * To obtain the correct period length, an oscilloscope is convenient, but you can also read the
+ * datasheet of the transmitter, measure the resistor for the oscillator and calculate the freqency.
+ *
+ * @param pin output pin on Arduino to which the transmitter is connected
+ * @param periodsec [0..511] Duration of one period, in microseconds. A trit is 6 periods.
+ * @param repeats [0..7] The 2log-Number of times the signal is repeated. The actual number of repeats will be 2^repeats. 3 would be a good start.
+ */
+ RemoteSwitch(unsigned short pin, unsigned int periodusec, unsigned short repeats);
+
+ /**
+ * Encodes the data base on the current object and the given trits. The data can be reused, e.g.
+ * for use with the static version of sendTelegram, so you won't need to instantiate costly objects!
+ *
+ * @return The data suited for use with RemoteSwitch::sendTelegram.
+ */
+ unsigned long encodeTelegram(unsigned short trits[]);
+
+ /**
+ * Send a telegram, including synchronisation-part.
+ *
+ * @param trits Array of size 12. "trits" should be either 0, 1 or 2, where 2 indicaties "float"
+ */
+ void sendTelegram(unsigned short trits[]);
+
+ /**
+ * Send a telegram, including synchronisation-part. The data-param encodes the period duration, number of repeats and the actual data.
+ * Note: static method, which allows for use in low-mem situations.
+ *
+ * Format data:
+ * pppppppp|prrrdddd|dddddddd|dddddddd (32 bit)
+ * p = perioud (9 bit unsigned int
+ * r = repeats as 2log. Thus, if r = 3, then signal is sent 2^3=8 times
+ * d = data
+ *
+ * @param data data, period and repeats.
+ * @param pin Pin number of the transmitter.
+ */
+ static void sendTelegram(unsigned long data, unsigned short pin);
+
+ /**
+ * Compares the data received with RemoteReceive with the data obtained by one of the getTelegram-functions.
+ * Period duration and repetitions are ignored by this function; only the data-payload is compared.
+ *
+ * @return true, if the codes are identical (the 20 least significant bits match)
+ */
+ static boolean isSameCode(unsigned long encodedTelegram, unsigned long receivedData);
+
+ protected:
+ unsigned short _pin; //Radio output pin
+ unsigned int _periodusec; //oscillator period in microseconds
+ unsigned short _repeats; //Number over repetitions of one telegram
+};
+
+/**
+* ActionSwitch simulatos a remote, as sold in the Dutch 'Action' stores. But there are many similar systems on the market.
+* If your remote has setting for 5 address bits, and can control 5 devices on or off, then you can try to use the ActionSwitch
+*/
+class ActionSwitch: RemoteSwitch {
+ public:
+ /**
+ * Constructor
+ *
+ * @param pin output pin on Arduino to which the transmitter is connected
+ * @param periodsec Duration of one period, in microseconds. Default is 190usec
+ * @see RemoteSwitch
+ */
+ ActionSwitch(unsigned short pin, unsigned int periodusec=190);
+
+ /**
+ * Send a on or off signal to a device.
+ *
+ * @param systemCode 5-bit addres (dip switches in remote). Range [0..31]
+ * @param device Device to switch. Range: [A..E] (case sensitive!)
+ * @param on True, to switch on. False to switch off,
+ */
+ void sendSignal(unsigned short systemCode, char device, boolean on);
+
+ /**
+ * Generates the telegram (data) which can be used for RemoteSwitch::sendTelegram.
+ * See sendSignal for details on the parameters
+ *
+ * @return Encoded data, including repeats and period duration.
+ */
+ unsigned long getTelegram(unsigned short systemCode, char device, boolean on);
+};
+
+/**
+* BlokkerSwitch simulatos a remote, as sold in the Dutch 'Blokker' stores. But there are many similar systems on the market.
+* These remotes have 4 on, 4 off buttons and a switch to switch between device 1-4 and 5-8. No futher configuration
+* possible.
+*/
+class BlokkerSwitch: RemoteSwitch {
+ public:
+ /**
+ * Constructor
+ *
+ * @param pin output pin on Arduino to which the transmitter is connected
+ * @param periodsec Duration of one period, in microseconds. Default is 307usec
+ * @see RemoteSwitch
+ */
+ BlokkerSwitch(unsigned short pin, unsigned int periodusec=230);
+
+ /**
+ * Send a on or off signal to a device.
+ *
+ * @param device Device to switch. Range: [1..8]
+ * @param on True, to switch on. False to switch off,
+ */
+ void sendSignal(unsigned short device, boolean on);
+
+ /**
+ * @see RemoteSwitch::getTelegram
+ */
+ unsigned long getTelegram(unsigned short device, boolean on);
+};
+
+/**
+* KaKuSwitch simulates a KlikAanKlikUit-remote, but there are many clones.
+* If your transmitter has a address dial with the characters A till P, you can try this class.
+*/
+class KaKuSwitch: RemoteSwitch {
+ public:
+ /**
+ * Constructor
+ *
+ * @param pin output pin on Arduino to which the transmitter is connected
+ * @param periodsec Duration of one period, in microseconds. Default is 375usec
+ * @see RemoteSwitch
+ */
+ KaKuSwitch(unsigned short pin, unsigned int periodusec=375);
+
+ /**
+ * Send a on or off signal to a device.
+ *
+ * @param address addres (dial switches in remote). Range [A..P] (case sensitive!)
+ * @param group Group to switch. Range: [1..4]
+ * @param device Device to switch. Range: [1..4]
+ * @param on True, to switch on. False to switch off,
+ */
+ void sendSignal(char address, unsigned short group, unsigned short device, boolean on);
+
+ /**
+ * Send a on or off signal to a device.
+ *
+ * @param address addres (dip switches in remote). Range [A..P] (case sensitive!)
+ * @param device device (dial switches in remote). Range [1..16]
+ * @param on True, to switch on. False to switch off,
+ */
+ void sendSignal(char address, unsigned short device, boolean on);
+
+ /**
+ * @see RemoteSwitch::getTelegram
+ */
+ unsigned long getTelegram(char address, unsigned short group, unsigned short device, boolean on);
+
+ /**
+ * @see RemoteSwitch::getTelegram
+ */
+ unsigned long getTelegram(char address, unsigned short device, boolean on);
+};
+
+#endif
diff --git a/lib/RemoteSwitch/examples/Light_show/Light_show.pde b/lib/RemoteSwitch/examples/Light_show/Light_show.pde
new file mode 100644
index 00000000..20077ff9
--- /dev/null
+++ b/lib/RemoteSwitch/examples/Light_show/Light_show.pde
@@ -0,0 +1,53 @@
+#include
+
+/*
+* Demo for RF remote switch transmitter.
+* For details, see RemoteSwitch.h!
+*
+* This sketch switches some devices on and off in a loop.
+*/
+
+//Intantiate a new ActionSwitch remote, use pin 11
+ActionSwitch actionSwitch(11);
+
+//Intantiate a new KaKuSwitch remote, also use pin 11 (same transmitter!)
+KaKuSwitch kaKuSwitch(11);
+
+//Intantiate a new Blokker remote, also use pin 11 (same transmitter!)
+BlokkerSwitch blokkerSwitch(11);
+
+
+void setup() {
+}
+
+void loop() {
+ //Switch off KaKu-device 10 on address M
+ kaKuSwitch.sendSignal('M',10,false);
+
+ //Switch on Action-device B on system code 1.
+ actionSwitch.sendSignal(1,'B',true);
+
+ //Switch on Blokker-device 7.
+ blokkerSwitch.sendSignal(7,true);
+
+
+
+ //wait 2 seconds
+ delay(2000);
+
+
+
+ //Switch on KaKu-device 2 of group 3 on address M (which is the same as device 10 on address M!)
+ kaKuSwitch.sendSignal('M',3,2,true);
+
+ //Switch off Action-device B on system code 1.
+ actionSwitch.sendSignal(1,'B',false);
+
+ //Switch off Blokker-device 7.
+ blokkerSwitch.sendSignal(7,false);
+
+
+
+ //wait 4 seconds
+ delay(4000);
+}
diff --git a/lib/RemoteSwitch/examples/Remote_translator/Remote_translator.pde b/lib/RemoteSwitch/examples/Remote_translator/Remote_translator.pde
new file mode 100644
index 00000000..5f0188bf
--- /dev/null
+++ b/lib/RemoteSwitch/examples/Remote_translator/Remote_translator.pde
@@ -0,0 +1,58 @@
+#include
+#include
+
+/*
+* Demo for RF remote switch receiver.
+* For details, see RemoteReceiver.h!
+*
+* This sketch "translates" a Action-remote to a Blokker-remote.
+* When the A-On-button of the Action-remote is pressed, the Blokker-devices
+* 5, 6 and 7 are switched on. The A-Off-button switches the devices off again.
+*
+* Connect the transmitter to digital pin 11, and the receiver to digital pin 2.
+*/
+
+ActionSwitch actionSwitch(11);
+BlokkerSwitch blokkerSwitch(11);
+
+//Prepare the code for switch A (system code 1) on and off, for easy comparision later.
+unsigned long actionAOn = actionSwitch.getTelegram(1,'A',true);
+unsigned long actionAOff = actionSwitch.getTelegram(1,'A',false);
+
+void setup() {
+ //See example Show_received_code for info on this
+ RemoteReceiver::init(0, 3, translateCode);
+}
+
+void loop() {
+}
+
+//Callback function is called only when a valid code is received.
+void translateCode(unsigned long receivedCode, unsigned int period) {
+ //Enabled interrupts, so RemoteReceiver::isReceiving() can be used.
+ interrupts();
+
+ //Compare the signals
+ if (RemoteSwitch::isSameCode(actionAOn, receivedCode)) {
+ //A-On-button pressed!
+
+ //Wait for a free ether
+ while(RemoteReceiver::isReceiving());
+
+ //Switch devices on
+ blokkerSwitch.sendSignal(5,true);
+ blokkerSwitch.sendSignal(6,true);
+ blokkerSwitch.sendSignal(7,true);
+
+ } else if (RemoteSwitch::isSameCode(actionAOff, receivedCode)) {
+ //A-Off-button pressed!
+
+ //Wait for a free ether
+ while(RemoteReceiver::isReceiving());
+
+ //Switch devices off
+ blokkerSwitch.sendSignal(5,false);
+ blokkerSwitch.sendSignal(6,false);
+ blokkerSwitch.sendSignal(7,false);
+ }
+}
diff --git a/lib/RemoteSwitch/examples/Retransmitter/Retransmitter.pde b/lib/RemoteSwitch/examples/Retransmitter/Retransmitter.pde
new file mode 100644
index 00000000..a1197d75
--- /dev/null
+++ b/lib/RemoteSwitch/examples/Retransmitter/Retransmitter.pde
@@ -0,0 +1,52 @@
+#include
+#include
+
+/*
+* Demo for RF remote switch receiver.
+* For details, see RemoteReceiver.h!
+*
+* This sketch demonstrates how to use the static version of
+* RemoteReceiver::sendTelegram, which can be used in low-memory
+* situations.
+*
+* Connect the transmitter to digital pin 11, and the receiver to digital pin 2.
+*
+* When run, this sketch waits for a valid code from the receiver, decodes it,
+* and retransmits it after 5 seconds.
+*/
+
+void setup() {
+ //See example Show_received_code for info on this
+ RemoteReceiver::init(0, 3, showCode);
+}
+
+void loop() {
+}
+
+void showCode(unsigned long receivedCode, unsigned int period) {
+ //Disable the receiver; otherwise it might pick up the retransmit as well.
+ RemoteReceiver::disable();
+
+ //Need interrupts for delay
+ interrupts();
+
+ unsigned long code;
+
+ //Copy the received code.
+ code = receivedCode & 0xFFFFF; //truncate to 20 bits for show; receivedCode is never more than 20 bits..
+
+ //Add the period duration to the code. Range: [0..511] (9 bit)
+ code |= (unsigned long)period << 23;
+
+ //Add the number of repeats to the code. Range: [0..7] (3 bit). The actual number of repeats will be 2^(repeats),
+ //in this case 8
+ code |= 3L << 20;
+
+ //Wait 5 seconds before sending.
+ delay(5000);
+
+ //Retransmit the signal on pin 11. Note: no object was created!
+ RemoteSwitch::sendTelegram(code,11);
+
+ RemoteReceiver::enable();
+}
diff --git a/lib/RemoteSwitch/examples/Show_received_code/Show_received_code.pde b/lib/RemoteSwitch/examples/Show_received_code/Show_received_code.pde
new file mode 100644
index 00000000..6a4d8dbc
--- /dev/null
+++ b/lib/RemoteSwitch/examples/Show_received_code/Show_received_code.pde
@@ -0,0 +1,39 @@
+#include
+
+/*
+* Demo for RF remote switch receiver.
+* For details, see RemoteReceiver.h!
+*
+* This sketch shows the received signals on the serial port.
+* Connect the receiver to digital pin 2.
+*/
+
+
+void setup() {
+ Serial.begin(115200);
+
+ //Initialize receiver on interrupt 0 (= digital pin 2), calls the callback "showCode"
+ //after 3 identical codes have been received in a row. (thus, keep the button pressed
+ //for a moment)
+ //
+ //See the interrupt-parameter of attachInterrupt for possible values (and pins)
+ //to connect the receiver.
+ RemoteReceiver::init(0, 3, showCode);
+}
+
+void loop() {
+}
+
+//Callback function is called only when a valid code is received.
+void showCode(unsigned long receivedCode, unsigned int period) {
+ //Note: interrupts are disabled. You can re-enable them if needed.
+
+ //Print the received code.
+ Serial.print("Code: ");
+ Serial.print(receivedCode);
+ Serial.print(", period duration: ");
+ Serial.print(period);
+ Serial.println("us.");
+}
+
+
diff --git a/lib/RemoteSwitch/keywords.txt b/lib/RemoteSwitch/keywords.txt
new file mode 100644
index 00000000..bf1c60ff
--- /dev/null
+++ b/lib/RemoteSwitch/keywords.txt
@@ -0,0 +1,12 @@
+RemoteSwitch KEYWORD1
+ActionSwitch KEYWORD1
+BlokkerSwitch KEYWORD1
+KaKuSwitch KEYWORD1
+sendTelegram KEYWORD2
+sendSignal KEYWORD2
+isSameCode KEYWORD2
+RemoteReceiver KEYWORD1
+init KEYWORD2
+enable KEYWORD2
+disable KEYWORD2
+isReceiving KEYWORD2
diff --git a/lib/RemoteSwitch/license.txt b/lib/RemoteSwitch/license.txt
new file mode 100644
index 00000000..fd9cb841
--- /dev/null
+++ b/lib/RemoteSwitch/license.txt
@@ -0,0 +1,25 @@
+Copyright 2010 Randy Simons. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY RANDY SIMONS ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RANDY SIMONS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Randy Simons.
\ No newline at end of file
diff --git a/platformio.ini b/platformio.ini
index f88924cf..c0419f55 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -26,11 +26,17 @@ framework = arduino
board = esp01_1m
lib_install = 89
+[env:node]
+platform = espressif
+framework = arduino
+board = nodemcuv2
+lib_install = 89
+
[env:ota]
platform = espressif
framework = arduino
board = esp01_1m
lib_install = 89
upload_speed = 115200
-upload_port = "192.168.4.1"
+upload_port = "192.168.1.109"
upload_flags = --auth=fibonacci --port 8266
diff --git a/src/code.ino b/src/code.ino
index f9ccb6fd..597cbef3 100644
--- a/src/code.ino
+++ b/src/code.ino
@@ -25,6 +25,7 @@ along with this program. If not, see .
#include
#include
#include
+#include
#include "FS.h"
// -----------------------------------------------------------------------------
@@ -33,6 +34,11 @@ along with this program. If not, see .
#define DEBUG
+#define ENABLE_RF 1
+#define ENABLE_OTA 1
+#define ENABLE_MQTT 1
+#define ENABLE_WEBSERVER 1
+
#define APP_NAME "Espurna"
#define MAX_VERSION 0
#define MIN_VERSION 9
@@ -44,59 +50,85 @@ along with this program. If not, see .
#define RELAY_PIN 12
#define LED_PIN 13
-#define AP_PASS "fibonacci"
-#define OTA_PASS "fibonacci"
-#define BUFFER_SIZE 1024
+#define ADMIN_PASS "fibonacci"
#define CONFIG_PATH "/.config"
#define WIFI_CONNECT_TIMEOUT 5000
#define WIFI_RECONNECT_DELAY 5000
#define MQTT_RECONNECT_DELAY 10000
#define NETWORK_BUFFER 3
+#define BUFFER_SIZE 1024
+
+#define RF_PIN 14
+#define RF_CHANNEL 31
+#define RF_DEVICE 1
+
+#define MQTT_RETAIN true
// -----------------------------------------------------------------------------
// Globals
// -----------------------------------------------------------------------------
-ESP8266WebServer server(80);
-WiFiClient client;
-PubSubClient mqtt(client);
-
char identifier[20] = {0};
byte network = 0;
String config_ssid[NETWORK_BUFFER];
String config_pass[NETWORK_BUFFER];
-String mqtt_server = "192.168.1.100";
-String mqtt_topic = "/test/switch/{identifier}";
-String mqtt_port = "1883";
-
-char mqtt_subscribe_to[30];
-char mqtt_publish_to[30];
DebounceEvent button1 = false;
+#if ENABLE_WEBSERVER
+ ESP8266WebServer server(80);
+#endif
+
+#if ENABLE_MQTT
+ WiFiClient client;
+ PubSubClient mqtt(client);
+ String mqtt_server = "192.168.1.100";
+ String mqtt_topic = "/test/switch/{identifier}";
+ String mqtt_port = "1883";
+ char mqtt_subscribe_status[30];
+ char mqtt_publish_status[30];
+ char mqtt_publish_ip[30];
+#endif
+
+#if ENABLE_RF
+ unsigned long rf_code = 0;
+ unsigned long rf_code_on = 0;
+ unsigned long rf_code_off = 0;
+ String rf_channel = String(RF_CHANNEL);
+ String rf_device = String(RF_DEVICE);
+#endif
+
// -----------------------------------------------------------------------------
// Relay
// -----------------------------------------------------------------------------
void switchRelayOn() {
- #ifdef DEBUG
- Serial.println("Turning the relay ON");
- #endif
- if (mqtt.connected()) {
- mqtt.publish(mqtt_publish_to, "1");
+ if (!digitalRead(RELAY_PIN)) {
+ #ifdef DEBUG
+ Serial.println("Turning the relay ON");
+ #endif
+ #if ENABLE_MQTT
+ if (mqtt.connected()) {
+ mqtt.publish(mqtt_publish_status, "1", MQTT_RETAIN);
+ }
+ #endif
+ digitalWrite(RELAY_PIN, HIGH);
}
- digitalWrite(RELAY_PIN, HIGH);
}
void switchRelayOff() {
- #ifdef DEBUG
- Serial.println("Turning the relay OFF");
- #endif
- if (mqtt.connected()) {
- mqtt.publish(mqtt_publish_to, "0");
+ if (digitalRead(RELAY_PIN)) {
+ #ifdef DEBUG
+ Serial.println("Turning the relay OFF");
+ #endif
+ #if ENABLE_MQTT
+ if (mqtt.connected()) {
+ mqtt.publish(mqtt_publish_status, "0", MQTT_RETAIN);
+ }
+ #endif
+ digitalWrite(RELAY_PIN, LOW);
}
- digitalWrite(RELAY_PIN, LOW);
}
void toggleRelay() {
@@ -111,186 +143,213 @@ void toggleRelay() {
// WebServer
// -----------------------------------------------------------------------------
-String getContentType(String filename) {
- if (server.hasArg("download")) return "application/octet-stream";
- else if (filename.endsWith(".htm")) return "text/html";
- else if (filename.endsWith(".html")) return "text/html";
- else if (filename.endsWith(".css")) return "text/css";
- else if (filename.endsWith(".js")) return "application/javascript";
- else if (filename.endsWith(".png")) return "image/png";
- else if (filename.endsWith(".gif")) return "image/gif";
- else if (filename.endsWith(".jpg")) return "image/jpeg";
- else if (filename.endsWith(".ico")) return "image/x-icon";
- else if (filename.endsWith(".xml")) return "text/xml";
- else if (filename.endsWith(".pdf")) return "application/x-pdf";
- else if (filename.endsWith(".zip")) return "application/x-zip";
- else if (filename.endsWith(".gz")) return "application/x-gzip";
- return "text/plain";
-}
+#if ENABLE_WEBSERVER
+
+ String getContentType(String filename) {
+ if (server.hasArg("download")) return "application/octet-stream";
+ else if (filename.endsWith(".htm")) return "text/html";
+ else if (filename.endsWith(".html")) return "text/html";
+ else if (filename.endsWith(".css")) return "text/css";
+ else if (filename.endsWith(".js")) return "application/javascript";
+ else if (filename.endsWith(".png")) return "image/png";
+ else if (filename.endsWith(".gif")) return "image/gif";
+ else if (filename.endsWith(".jpg")) return "image/jpeg";
+ else if (filename.endsWith(".ico")) return "image/x-icon";
+ else if (filename.endsWith(".xml")) return "text/xml";
+ else if (filename.endsWith(".pdf")) return "application/x-pdf";
+ else if (filename.endsWith(".zip")) return "application/x-zip";
+ else if (filename.endsWith(".gz")) return "application/x-gzip";
+ return "text/plain";
+ }
-void handleRelayOn() {
- #ifdef DEBUG
- Serial.println("Request: /on");
- #endif
- switchRelayOn();
- server.send(200, "text/plain", "ON");
-}
+ void handleRelayOn() {
+ #ifdef DEBUG
+ Serial.println("Request: /on");
+ #endif
+ switchRelayOn();
+ server.send(200, "text/plain", "ON");
+ }
-void handleRelayOff() {
- #ifdef DEBUG
- Serial.println("Request: /off");
- #endif
- switchRelayOff();
- server.send(200, "text/plain", "OFF");
-}
+ void handleRelayOff() {
+ #ifdef DEBUG
+ Serial.println("Request: /off");
+ #endif
+ switchRelayOff();
+ server.send(200, "text/plain", "OFF");
+ }
-bool handleFileRead(String path) {
+ bool handleFileRead(String path) {
- #ifdef DEBUG
- Serial.println("Request: " + path);
- #endif
+ #ifdef DEBUG
+ Serial.println("Request: " + path);
+ #endif
- if (path.endsWith("/")) path += "index.html";
- String contentType = getContentType(path);
- String pathWithGz = path + ".gz";
+ if (path.endsWith("/")) path += "index.html";
+ String contentType = getContentType(path);
+ String pathWithGz = path + ".gz";
+
+ if (SPIFFS.exists(pathWithGz)) path = pathWithGz;
+ if (SPIFFS.exists(path)) {
+ File file = SPIFFS.open(path, "r");
+ size_t sent = server.streamFile(file, contentType);
+ size_t contentLength = file.size();
+ file.close();
+ return true;
+ }
+
+ return false;
- if (SPIFFS.exists(pathWithGz)) path = pathWithGz;
- if (SPIFFS.exists(path)) {
- File file = SPIFFS.open(path, "r");
- size_t sent = server.streamFile(file, contentType);
- size_t contentLength = file.size();
- file.close();
- return true;
}
- return false;
+ void handleHome() {
-}
+ #ifdef DEBUG
+ Serial.println("Request: /index.html");
+ #endif
-void handleHome() {
+ String filename = "/index.html";
+ String content = "";
+ char buffer[BUFFER_SIZE];
+
+ // Read file in chunks
+ File file = SPIFFS.open(filename, "r");
+ int size = file.size();
+ while (size > 0) {
+ size_t len = std::min(BUFFER_SIZE-1, size);
+ file.read((uint8_t *) buffer, len);
+ buffer[len] = 0;
+ content += buffer;
+ size -= len;
+ }
+ file.close();
- #ifdef DEBUG
- Serial.println("Request: /index.html");
- #endif
+ // Replace placeholders
+ content.replace("{appname}", APP_NAME);
+ content.replace("{appversion}", String(MAX_VERSION) + String(".") + String(MIN_VERSION));
+ content.replace("{ssid0}", config_ssid[0]);
+ content.replace("{pass0}", config_pass[0]);
+ content.replace("{ssid1}", config_ssid[1]);
+ content.replace("{pass1}", config_pass[1]);
+ content.replace("{ssid2}", config_ssid[2]);
+ content.replace("{pass2}", config_pass[2]);
+ #if ENABLE_MQTT
+ content.replace("{mqtt_server}", mqtt_server);
+ content.replace("{mqtt_port}", mqtt_port);
+ content.replace("{mqtt_topic}", mqtt_topic);
+ #endif
+ #if ENABLE_RF
+ content.replace("{rf_channel}", rf_channel);
+ content.replace("{rf_device}", rf_device);
+ #endif
- String filename = "/index.html";
- String content = "";
- char buffer[BUFFER_SIZE];
-
- // Read file in chunks
- File file = SPIFFS.open(filename, "r");
- int size = file.size();
- while (size > 0) {
- size_t len = std::min(BUFFER_SIZE-1, size);
- file.read((uint8_t *) buffer, len);
- buffer[len] = 0;
- content += buffer;
- size -= len;
- }
- file.close();
+ // Serve content
+ String contentType = getContentType(filename);
+ server.send(200, contentType, content);
- // Replace placeholders
- if (WiFi.status() == WL_CONNECTED) {
- content.replace("{status}", "Client + Acces Point");
- content.replace("{network}", config_ssid[network]);
- content.replace("{ip}", WiFi.localIP().toString());
- } else {
- content.replace("{status}", "Acces Point");
- content.replace("{network}", "");
- content.replace("{ip}", "");
}
- content.replace("{ssid0}", config_ssid[0]);
- content.replace("{pass0}", config_pass[0]);
- content.replace("{ssid1}", config_ssid[1]);
- content.replace("{pass1}", config_pass[1]);
- content.replace("{ssid2}", config_ssid[2]);
- content.replace("{pass2}", config_pass[2]);
- content.replace("{mqtt_server}", mqtt_server);
- content.replace("{mqtt_port}", mqtt_port);
- content.replace("{mqtt_topic}", mqtt_topic);
-
- // Serve content
- String contentType = getContentType(filename);
- server.send(200, contentType, content);
-}
+ void handleSave() {
-void handleSave() {
+ #ifdef DEBUG
+ Serial.println("Request: /save");
+ #endif
- #ifdef DEBUG
- Serial.println("Request: /save");
- #endif
+ config_ssid[0] = server.arg("ssid0");
+ config_pass[0] = server.arg("pass0");
+ config_ssid[1] = server.arg("ssid1");
+ config_pass[1] = server.arg("pass1");
+ config_ssid[2] = server.arg("ssid2");
+ config_pass[2] = server.arg("pass2");
+ #if ENABLE_MQTT
+ mqtt_server = server.arg("mqtt_server");
+ mqtt_port = server.arg("mqtt_port");
+ mqtt_topic = server.arg("mqtt_topic");
+ #endif
+ #if ENABLE_RF
+ rf_channel = server.arg("rf_channel");
+ rf_device = server.arg("rf_device");
+ #endif
+
+ server.send(202, "text/json", "{}");
+
+ saveConfig();
+ #if ENABLE_RF
+ rfBuildCodes();
+ #endif
+ network = 0;
+ wifiSetup(true);
- config_ssid[0] = server.arg("ssid0");
- config_pass[0] = server.arg("pass0");
- config_ssid[1] = server.arg("ssid1");
- config_pass[1] = server.arg("pass1");
- config_ssid[2] = server.arg("ssid2");
- config_pass[2] = server.arg("pass2");
- mqtt_server = server.arg("mqtt_server");
- mqtt_port = server.arg("mqtt_port");
- mqtt_topic = server.arg("mqtt_topic");
-
- saveConfig();
- network = 0;
- wifiSetup();
- delay(100);
-
- String output = "{";
- output += "\"status\": \"";
- if (WiFi.status() == WL_CONNECTED) {
- output += "Client + Acces Point";
- } else {
- output += "Acces Point";
}
- output += "\", \"ip\": \"";
- if (WiFi.status() == WL_CONNECTED) {
- output += WiFi.localIP().toString();
+
+ void handleStatus() {
+
+ #ifdef DEBUG
+ //Serial.println("Request: /status");
+ #endif
+
+ String output = "{ ";
+ output += "\"wifi\": ";
+ output += (WiFi.status() == WL_CONNECTED) ? "1": "0";
+ if ((WiFi.status() == WL_CONNECTED)) {
+ output += ", \"network\": \"";
+ output += WiFi.SSID();
+ output += "\", \"ip\": \"";
+ output += WiFi.localIP().toString();
+ output += "\"";
+ }
+ #if ENABLE_MQTT
+ output += ", \"mqtt\": ";
+ output += (mqtt.connected()) ? "1": "0";
+ #endif
+ output += ", \"relay\": ";
+ output += (digitalRead(RELAY_PIN)) ? "1": "0";
+ output += " }";
+
+ server.send(200, "text/json", output);
}
- output += "\" }";
- server.send(200, "text/json", output);
-}
+ void webServerSetup() {
-void webServerSetup() {
+ // Relay control
+ server.on("/relay/on", HTTP_GET, handleRelayOn);
+ server.on("/relay/off", HTTP_GET, handleRelayOff);
- // Relay control
- server.on("/on", HTTP_GET, handleRelayOn);
- server.on("/off", HTTP_GET, handleRelayOff);
+ // Configuration page
+ server.on("/save", HTTP_POST, handleSave);
+ server.on("/status", HTTP_GET, handleStatus);
+ server.on("/", HTTP_GET, handleHome);
+ server.on("/index.html", HTTP_GET, handleHome);
- // Configuration page
- server.on("/save", HTTP_POST, handleSave);
- server.on("/", HTTP_GET, handleHome);
- server.on("/index.html", HTTP_GET, handleHome);
+ // Anything else
+ server.onNotFound([]() {
- // Anything else
- server.onNotFound([]() {
+ // Hidden files
+ if (server.uri().startsWith("/.")) {
+ server.send(403, "text/plain", "Forbidden");
+ return;
+ }
- // Hidden files
- if (server.uri().startsWith("/.")) {
- server.send(403, "text/plain", "Forbidden");
- return;
- }
+ // Existing files in SPIFFS
+ if (!handleFileRead(server.uri())) {
+ server.send(404, "text/plain", "NotFound");
+ return;
+ }
- // Existing files in SPIFFS
- if (!handleFileRead(server.uri())) {
- server.send(404, "text/plain", "NotFound");
- return;
- }
+ });
- });
+ // Run server
+ server.begin();
- // Run server
- server.begin();
+ }
-}
+ void webServerLoop() {
+ server.handleClient();
+ }
-void webServerLoop() {
- server.handleClient();
-}
+#endif
// -----------------------------------------------------------------------------
-// Wifi modes
+// Wifi
// -----------------------------------------------------------------------------
char * getIdentifier() {
@@ -305,13 +364,35 @@ char * getIdentifier() {
return identifier;
}
-void wifiSetup() {
+void wifiSetup(bool force) {
- // Disconnect MQTT
- if (mqtt.connected()) mqtt.disconnect();
+ if ((!force) && (WiFi.status() == WL_CONNECTED)) return;
+
+ #if ENABLE_MQTT
+ if (mqtt.connected()) mqtt.disconnect();
+ #endif
+
+ #if ENABLE_RF
+ RemoteReceiver::disable();
+ #endif
- // STA mode
WiFi.mode(WIFI_AP_STA);
+ #ifdef DEBUG
+ WiFi.printDiag(Serial);
+ #endif
+
+ // SoftAP mode
+ WiFi.softAP(getIdentifier(), ADMIN_PASS);
+ #ifdef DEBUG
+ Serial.print("AP Mode: ");
+ Serial.print(getIdentifier());
+ Serial.print("/");
+ Serial.print(ADMIN_PASS);
+ Serial.print(", IP address: ");
+ Serial.println(WiFi.softAPIP());
+ #endif
+
+ // STA mode
if (config_ssid[network].length() > 0) {
char ssid[config_ssid[network].length()+1];
@@ -341,6 +422,7 @@ void wifiSetup() {
#endif
if (WiFi.status() == WL_CONNECTED) {
+ WiFi.setAutoConnect(true);
#ifdef DEBUG
Serial.println(WiFi.localIP());
#endif
@@ -353,16 +435,8 @@ void wifiSetup() {
}
- if (WiFi.status() != WL_CONNECTED) WiFi.mode(WIFI_AP);
- WiFi.softAP(getIdentifier(), AP_PASS);
-
- #ifdef DEBUG
- Serial.print("AP Mode: ");
- Serial.print(getIdentifier());
- Serial.print("/");
- Serial.print(AP_PASS);
- Serial.print(", IP address: ");
- Serial.println(WiFi.softAPIP());
+ #if ENABLE_RF
+ RemoteReceiver::enable();
#endif
}
@@ -371,7 +445,10 @@ void wifiLoop() {
static unsigned long timeout = millis();
if (WiFi.status() != WL_CONNECTED) {
if (timeout < millis()) {
- wifiSetup();
+ #if ENABLE_RF
+ //RemoteReceiver::disable();
+ #endif
+ wifiSetup(false);
timeout = millis() + WIFI_RECONNECT_DELAY;
}
}
@@ -381,97 +458,169 @@ void wifiLoop() {
// MQTT
// -----------------------------------------------------------------------------
-void buildTopics() {
+#if ENABLE_MQTT
- // Replace identifier
- String base = mqtt_topic;
- base.replace("{identifier}", getIdentifier());
+ void buildTopics() {
- // Get publish topic
- base.toCharArray(mqtt_publish_to, base.length()+1);
- mqtt_publish_to[base.length()+1] = 0;
+ String tmp;
- // Get subscribe topic
- String subscribe = base + "/set";
- subscribe.toCharArray(mqtt_subscribe_to, subscribe.length()+1);
- mqtt_subscribe_to[subscribe.length()+1] = 0;
+ // Replace identifier
+ String base = mqtt_topic;
+ base.replace("{identifier}", getIdentifier());
-}
+ // Get publish status topic
+ base.toCharArray(mqtt_publish_status, base.length()+1);
+ mqtt_publish_status[base.length()+1] = 0;
-void mqttCallback(char* topic, byte* payload, unsigned int length) {
+ // Get publish ip topic
+ tmp = base + "/ip";
+ tmp.toCharArray(mqtt_publish_ip, tmp.length()+1);
+ mqtt_publish_ip[tmp.length()+1] = 0;
- #ifdef DEBUG
- Serial.print("MQTT message ");
- Serial.print(topic);
- Serial.print(" => ");
- for (int i = 0; i < length; i++) {
- Serial.print((char)payload[i]);
- }
- Serial.println();
- #endif
+ // Get subscribe status topic
+ tmp = base + "/set";
+ tmp.toCharArray(mqtt_subscribe_status, tmp.length()+1);
+ mqtt_subscribe_status[tmp.length()+1] = 0;
- if ((char)payload[0] == '1') {
- switchRelayOn();
- } else {
- switchRelayOff();
}
-}
+ void mqttCallback(char* topic, byte* payload, unsigned int length) {
-void mqttConnect() {
+ #ifdef DEBUG
+ Serial.print("MQTT message ");
+ Serial.print(topic);
+ Serial.print(" => ");
+ for (int i = 0; i < length; i++) {
+ Serial.print((char)payload[i]);
+ }
+ Serial.println();
+ #endif
- if (!mqtt.connected()) {
+ if ((char)payload[0] == '1') {
+ switchRelayOn();
+ } else {
+ switchRelayOff();
+ }
- char buffer[mqtt_server.length()+1];
- mqtt_server.toCharArray(buffer, mqtt_server.length()+1);
- mqtt.setServer(buffer, mqtt_port.toInt());
+ }
- #ifdef DEBUG
- Serial.print("Connecting to MQTT broker: ");
- #endif
+ void mqttConnect() {
- if (mqtt.connect(getIdentifier())) {
+ if (!mqtt.connected()) {
- buildTopics();
+ char buffer[mqtt_server.length()+1];
+ mqtt_server.toCharArray(buffer, mqtt_server.length()+1);
+ mqtt.setServer(buffer, mqtt_port.toInt());
#ifdef DEBUG
- Serial.println("connected!");
- Serial.print("Subscribing to ");
- Serial.println(mqtt_subscribe_to);
+ Serial.print("Connecting to MQTT broker: ");
#endif
- mqtt.publish(mqtt_publish_to, "HOLA");
- mqtt.subscribe(mqtt_subscribe_to);
+ if (mqtt.connect(getIdentifier())) {
+ buildTopics();
- } else {
+ #ifdef DEBUG
+ Serial.println("connected!");
+ Serial.print("Subscribing to ");
+ Serial.println(mqtt_subscribe_status);
+ #endif
- #ifdef DEBUG
- Serial.print("failed, rc=");
- Serial.println(mqtt.state());
- #endif
+ String ipString = WiFi.localIP().toString();
+ char ip[ipString.length()+1];
+ ipString.toCharArray(ip, ipString.length()+1);
+ mqtt.publish(mqtt_publish_ip, ip, MQTT_RETAIN);
+ mqtt.subscribe(mqtt_subscribe_status);
+
+ } else {
+
+ #ifdef DEBUG
+ Serial.print("failed, rc=");
+ Serial.println(mqtt.state());
+ #endif
+
+ }
}
- }
-}
+ }
-void mqttSetup() {
- mqtt.setCallback(mqttCallback);
-}
+ void mqttSetup() {
+ mqtt.setCallback(mqttCallback);
+ }
-void mqttLoop() {
- static unsigned long timeout = millis();
- if (WiFi.status() == WL_CONNECTED) {
- if (!mqtt.connected()) {
- if (timeout < millis()) {
- mqttConnect();
- timeout = millis() + MQTT_RECONNECT_DELAY;
+ void mqttLoop() {
+ static unsigned long timeout = millis();
+ if (WiFi.status() == WL_CONNECTED) {
+ if (!mqtt.connected()) {
+ if (timeout < millis()) {
+ mqttConnect();
+ timeout = millis() + MQTT_RECONNECT_DELAY;
+ }
}
+ if (mqtt.connected()) mqtt.loop();
}
- if (mqtt.connected()) mqtt.loop();
}
-}
+
+#endif
+
+// -----------------------------------------------------------------------------
+// RF
+// -----------------------------------------------------------------------------
+
+#if ENABLE_RF
+
+ void rfLoop() {
+ if (rf_code == 0) return;
+ Serial.print("RF code: ");
+ Serial.println(rf_code);
+ if (rf_code == rf_code_on ) switchRelayOn();
+ if (rf_code == rf_code_off ) switchRelayOff();
+ rf_code = 0;
+ }
+
+ void rfBuildCodes() {
+
+ unsigned long code = 0;
+
+ // channel
+ unsigned int channel = rf_channel.toInt();
+ for (byte i = 0; i < 5; i++) {
+ code *= 3;
+ if (channel & 1) code += 1;
+ channel >>= 1;
+ }
+
+ // device
+ unsigned int device = rf_device.toInt();
+ for (byte i = 0; i < 5; i++) {
+ code *= 3;
+ if (device != i) code += 2;
+ }
+
+ // status
+ code *= 9;
+ rf_code_off = code + 2;
+ rf_code_on = code + 6;
+
+ Serial.print("RF code ON: ");
+ Serial.println(rf_code_on);
+ Serial.print("RF code OFF: ");
+ Serial.println(rf_code_off);
+
+ }
+
+ void rfCallback(unsigned long code, unsigned int period) {
+ rf_code = code;
+ }
+
+ void rfSetup() {
+ rfBuildCodes();
+ RemoteReceiver::init(RF_PIN, 3, rfCallback);
+ RemoteReceiver::enable();
+ }
+
+#endif
// -----------------------------------------------------------------------------
// Configuration
@@ -486,9 +635,15 @@ bool saveConfig() {
file.println("pass1=" + config_pass[1]);
file.println("ssid2=" + config_ssid[2]);
file.println("pass2=" + config_pass[2]);
- file.println("mqtt_server=" + mqtt_server);
- file.println("mqtt_port=" + mqtt_port);
- file.println("mqtt_topic=" + mqtt_topic);
+ #if ENABLE_MQTT
+ file.println("mqtt_server=" + mqtt_server);
+ file.println("mqtt_port=" + mqtt_port);
+ file.println("mqtt_topic=" + mqtt_topic);
+ #endif
+ #if ENABLE_RF
+ file.println("rf_channel=" + rf_channel);
+ file.println("rf_device=" + rf_device);
+ #endif
file.close();
return true;
}
@@ -525,9 +680,15 @@ bool loadConfig() {
else if (line.startsWith("pass1=")) config_pass[1] = line.substring(6);
else if (line.startsWith("ssid2=")) config_ssid[2] = line.substring(6);
else if (line.startsWith("pass2=")) config_pass[2] = line.substring(6);
- else if (line.startsWith("mqtt_server=")) mqtt_server = line.substring(12);
- else if (line.startsWith("mqtt_port=")) mqtt_port = line.substring(10);
- else if (line.startsWith("mqtt_topic=")) mqtt_topic = line.substring(11);
+ #if ENABLE_MQTT
+ else if (line.startsWith("mqtt_server=")) mqtt_server = line.substring(12);
+ else if (line.startsWith("mqtt_port=")) mqtt_port = line.substring(10);
+ else if (line.startsWith("mqtt_topic=")) mqtt_topic = line.substring(11);
+ #endif
+ #if ENABLE_RF
+ else if (line.startsWith("rf_channel=")) rf_channel = line.substring(11);
+ else if (line.startsWith("rf_device=")) rf_device = line.substring(10);
+ #endif
if (end < 0) break;
start = end + 1;
end = content.indexOf("\n", start);
@@ -543,53 +704,63 @@ bool loadConfig() {
// OTA
// -----------------------------------------------------------------------------
-void OTASetup() {
+#if ENABLE_OTA
- // Port defaults to 8266
- ArduinoOTA.setPort(8266);
+ void OTASetup() {
- // Hostname defaults to esp8266-[ChipID]
- ArduinoOTA.setHostname(getIdentifier());
+ // Port defaults to 8266
+ ArduinoOTA.setPort(8266);
- // No authentication by default
- ArduinoOTA.setPassword((const char *) OTA_PASS);
+ // Hostname defaults to esp8266-[ChipID]
+ ArduinoOTA.setHostname(getIdentifier());
- ArduinoOTA.onStart([]() {
- #ifdef DEBUG
- Serial.println("OTA - Start");
- #endif
- });
+ // No authentication by default
+ ArduinoOTA.setPassword((const char *) ADMIN_PASS);
- ArduinoOTA.onEnd([]() {
- #ifdef DEBUG
- Serial.println("OTA - End");
- #endif
- });
+ ArduinoOTA.onStart([]() {
+ #ifdef ENABLE_RF
+ RemoteReceiver::disable();
+ #endif
+ #ifdef DEBUG
+ Serial.println("OTA - Start");
+ #endif
+ });
- ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
- #ifdef DEBUG
- Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
- #endif
- });
+ ArduinoOTA.onEnd([]() {
+ #ifdef DEBUG
+ Serial.println("OTA - End");
+ #endif
+ #ifdef ENABLE_RF
+ RemoteReceiver::enable();
+ #endif
+ });
- ArduinoOTA.onError([](ota_error_t error) {
- #ifdef DEBUG
- Serial.printf("Error[%u]: ", error);
- if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
- else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
- else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
- else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
- else if (error == OTA_END_ERROR) Serial.println("End Failed");
- #endif
- });
+ ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
+ #ifdef DEBUG
+ Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
+ #endif
+ });
- ArduinoOTA.begin();
+ ArduinoOTA.onError([](ota_error_t error) {
+ #ifdef DEBUG
+ Serial.printf("Error[%u]: ", error);
+ if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
+ else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
+ else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
+ else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
+ else if (error == OTA_END_ERROR) Serial.println("End Failed");
+ #endif
+ });
-}
+ ArduinoOTA.begin();
-void OTALoop() {
- ArduinoOTA.handle();
-}
+ }
+
+ void OTALoop() {
+ ArduinoOTA.handle();
+ }
+
+#endif
// -----------------------------------------------------------------------------
// Hardware (buttons, LEDs,...)
@@ -599,7 +770,10 @@ void hardwareSetup() {
Serial.begin(115200);
pinMode(RELAY_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
+ pinMode(RF_PIN, INPUT_PULLUP);
button1 = DebounceEvent(BUTTON_PIN);
+ //switchRelayOff();
+ SPIFFS.begin();
}
void blink(unsigned long delayOff, unsigned long delayOn) {
@@ -647,23 +821,44 @@ void welcome() {
}
void setup() {
+
hardwareSetup();
- SPIFFS.begin();
delay(5000);
welcome();
- OTASetup();
- switchRelayOff();
+
+ #if ENABLE_OTA
+ OTASetup();
+ #endif
loadConfig();
- wifiSetup();
- webServerSetup();
- mqttSetup();
+ wifiSetup(false);
+ #if ENABLE_WEBSERVER
+ webServerSetup();
+ #endif
+ #if ENABLE_MQTT
+ mqttSetup();
+ #endif
+ #if ENABLE_RF
+ rfSetup();
+ #endif
+
}
void loop() {
- OTALoop();
+
+ #if ENABLE_OTA
+ OTALoop();
+ #endif
wifiLoop();
- webServerLoop();
- mqttLoop();
+ #if ENABLE_MQTT
+ mqttLoop();
+ #endif
+ #if ENABLE_RF
+ rfLoop();
+ #endif
+ #if ENABLE_WEBSERVER
+ webServerLoop();
+ #endif
hardwareLoop();
- delay(1);
+ delay(10);
+
}