diff --git a/data/index.html b/data/index.html index 9db118e5..608ded1e 100644 --- a/data/index.html +++ b/data/index.html @@ -1,7 +1,7 @@ - SONOFF Configuration + {appname} {appversion} - Configuration @@ -17,9 +17,6 @@ 'dataType': 'json', 'data': $(this).serializeArray() }).done(function(data) { - if (data.ip) $("span.ip").html(data.ip); - if (data.network) $("span.network").html(data.network); - if (data.status) $("span.status").html(data.status); $(":submit").prop("value", "Connect").removeAttr("disabled"); }).fail(function() { $(":submit").prop("value", "Connect").removeAttr("disabled"); @@ -27,6 +24,22 @@ event.preventDefault(); }); + function update() { + $.ajax({ + 'method': 'GET', + 'url': '/status', + 'dataType': 'json' + }).done(function(data) { + $("span.network").html(data.wifi ? data.network.toUpperCase() : "NOT CONNECTED"); + $("span.ip").html(data.wifi ? "("+data.ip+")" : ""); + $("span.mqtt").html(data.mqtt ? "CONNECTED" : "NOT CONNECTED"); + $("span.relay").html(data.relay ? "ON" : "OFF"); + }); + } + + update(); + var updateTimer = setInterval(update, 5000); + }); @@ -36,13 +49,14 @@
-

SONOFF Configuration

-
This form lets you define the WIFI network this SONOFF should try to join.
+

{appname} {appversion} - Configuration

+
This form lets you define the WIFI network this device should try to join.
-

Connection: {status}

-

Network: {network} ({ip})

+

Wifi:

+

MQTT:

+

Relay:

@@ -108,6 +122,20 @@
+
+ +
+ +
+
+ +
+ +
+ +
+
+
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 (durationmax1Period) { + _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); + }