diff --git a/.gitmodules b/.gitmodules index 419ce256..44ec0cee 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "libraries/DHT-sensor-library"] path = libraries/DHT-sensor-library url = https://github.com/adafruit/DHT-sensor-library +[submodule "libraries/RFM69"] + path = libraries/RFM69 + url = https://github.com/LowPowerLab/RFM69 diff --git a/.travis.yml b/.travis.yml index 54e86dcc..24ce2f95 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,7 @@ install: - git clone https://github.com/adafruit/DHT-sensor-library /usr/local/share/arduino/libraries/DHT_sensor_library - git clone https://github.com/adafruit/Adafruit_Sensor /usr/local/share/arduino/libraries/Adafruit_Sensor - git clone https://github.com/sparkfun/SparkFun_BME280_Arduino_Library /usr/local/share/arduino/libraries/SparkFun_BME280 + - git clone https://github.com/LowPowerLab/RFM69 /usr/local/share/arduino/libraries/RFM69 - arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --save-prefs - arduino --install-boards esp8266:esp8266 - arduino --board $BD --save-prefs diff --git a/OpenMQTTGateway.ino b/OpenMQTTGateway.ino index 1da7ce18..246e6c72 100644 --- a/OpenMQTTGateway.ino +++ b/OpenMQTTGateway.ino @@ -120,7 +120,7 @@ void callback(char* topic, byte* payload, unsigned int length) { void setup() { //Launch serial for debugging purposes - Serial.begin(115200); + Serial.begin(SERIAL_BAUD); #ifdef ESP8266 //Begining wifi connection in case of ESP8266 @@ -176,6 +176,9 @@ void setup() #ifdef ZgatewayBT setupBT(); #endif + #ifdef ZgatewayRFM69 + setupRFM69(); + #endif } @@ -257,6 +260,12 @@ void loop() if(resultBT) trc(F("BT sent by MQTT")); #endif + #ifdef ZgatewayRFM69 + boolean resultRFM69 = RFM69toMQTT(); + if(resultRFM69) + trc(F("RFM69 data sent by MQTT")); + #endif + } @@ -324,6 +333,10 @@ void receivingMQTT(char * topicOri, char * datacallback) { #ifdef ZgatewayIR MQTTtoIR(topicOri, datacallback); #endif +#ifdef ZgatewayRFM69 + MQTTtoRFM69(topicOri, datacallback); +#endif + } //trace diff --git a/User_config.h b/User_config.h index 75437027..3f9e5f4d 100644 --- a/User_config.h +++ b/User_config.h @@ -30,12 +30,13 @@ */ /*----------------------------USER PARAMETERS-----------------------------*/ +#define SERIAL_BAUD 115200 /*-------------DEFINE YOUR WIRING TYPE BELOW----------------*/ // Choose between "I2C_Wiring" OR "Classic_Wiring" // Please Note: I2C Wiring and Classic Wiring uses two complete different Pins for all Modules, see PIN definitions at the end of this file //#define I2C_Wiring // With Support for I2C Modules and the associated libraries BH1750 and BME280 -#define Classic_Wiring // Without Support for I2C Modules, legacy wiring V0.3.1 and below - +//#define RFM69_Wiring // following file img/OpenMQTTGateway_IR_RF_RFM69.png +#define Classic_Wiring // following file img/OpenMQTTGateway_IR_RF_BT.png /*-------------DEFINE YOUR NETWORK PARAMETERS BELOW----------------*/ //MQTT Parameters definition #define mqtt_server "192.168.1.17" @@ -74,14 +75,20 @@ const byte subnet[] = { 255, 255, 255, 0 }; //ip adress #ifdef ESP8266 // for nodemcu, weemos and esp8266 //#define ZsensorDHT #define ZgatewayRF + #ifdef RFM69_Wiring + #define ZgatewayRFM69 + #endif #define ZgatewayIR - #define ZgatewayBT + //#define ZgatewayBT #ifdef I2C_Wiring // to use the sensor below the gateway should wired with I2CWiring, see PIN DEFINITIONS below #define ZsensorBH1750 #define ZsensorBME280 #endif #else // for arduino + W5100 #define ZgatewayRF + #ifdef RFM69_Wiring + //#define ZgatewayRFM69 not tested + #endif #define ZgatewayIR #define ZgatewayBT //#define ZsensorDHT @@ -167,6 +174,30 @@ RF supported protocols /*----------------------BT topics-------------------------*/ #define subjectBTtoMQTT "home/BTtoMQTT/" +/*----------------------RFM69 topics & parameters -------------------------*/ +#define subjectRFM69toMQTT "home/RFM69toMQTT" +#define subjectRFM69toMQTTrssi "home/RFM69toMQTT/rssi" +#define subjectRFM69toMQTTsender "home/RFM69toMQTT/sender" +#define subjectMQTTtoRFM69 "home/commands/MQTTtoRFM69" +#define RFM69receiverKey "RCV_" // receiver id will be defined if a subject contains RFM69receiverKey followed by a value of 3 digits +#define subjectGTWRFM69toMQTT "home/RFM69toMQTT" +#define defaultRFM69ReceiverId 99 + +// Default values +const char PROGMEM ENCRYPTKEY[] = "sampleEncryptKey"; +const char PROGMEM MDNS_NAME[] = "rfm69gw1"; +const char PROGMEM MQTT_BROKER[] = "raspi2"; +const char PROGMEM RFM69AP_NAME[] = "RFM69-AP"; +#define NETWORKID 200 //the same on all nodes that talk to each other +#define NODEID 10 + +//Match frequency to the hardware version of the radio +#define FREQUENCY RF69_433MHZ +//#define FREQUENCY RF69_868MHZ +//#define FREQUENCY RF69_915MHZ +#define IS_RFM69HCW true // set to 'true' if you are using an RFM69HCW module +#define POWER_LEVEL 31 + /*-------------------PIN DEFINITIONS----------------------*/ #ifdef I2C_Wiring // With Support for I2C Modules #define DHT_RECEIVER_PIN 14 //on nodeMCU this is D5 GPIO14 @@ -208,6 +239,32 @@ RF supported protocols #endif #endif +#ifdef RFM69_Wiring // Without Support for I2C Modules + #define DHT_RECEIVER_PIN 0 //on nodeMCU this is D3 GPIO0 + #define IR_RECEIVER_PIN 2 // put 2 = D4 on nodemcu, 2 = D2 on arduino + #define RF_EMITTER_PIN 10 //put 4 = D2 on nodemcu, 4 = D4 on arduino + + #ifdef ESP8266 + #define IR_EMITTER_PIN 16 // 14 = D5 on nodemcu #define only usefull for ESP8266 + //RF PIN definition + #define RF_RECEIVER_PIN 0 // 5 = D1 on nodemcu + #define RFM69_CS D1 // GPIO15/HCS/D8 + #define RFM69_IRQ D8 // GPIO04/D2 + #define RFM69_IRQN digitalPinToInterrupt(RFM69_IRQ) + #define RFM69_RST D4 // GPIO02/D4 + #else + //IMPORTANT NOTE: On arduino UNO connect IR emitter pin to D9 , comment #define IR_USE_TIMER2 and uncomment #define IR_USE_TIMER1 on library IRremote/IRremoteInt.h so as to free pin D3 for RF RECEIVER PIN + //RF PIN definition + #define RF_RECEIVER_PIN 1 // 1 = D3 on arduino + #define BT_RX 5 //arduino RX connect HM-10 TX + #define BT_TX 6 //arduino TX connect HM-10 RX + #define RFM69_CS 10 + #define RFM69_IRQ 2 + #define RFM69_IRQN digitalPinToInterrupt(RFM69_IRQ) + #define RFM69_RST 9 + #endif +#endif + //RF number of signal repetition #define RF_EMITTER_REPEAT 20 diff --git a/ZgatewayRFM69.ino b/ZgatewayRFM69.ino new file mode 100644 index 00000000..bda25c27 --- /dev/null +++ b/ZgatewayRFM69.ino @@ -0,0 +1,238 @@ +/* + OpenMQTTGateway - ESP8266 or Arduino program for home automation + + Act as a wifi or ethernet gateway between your RF/infrared IR signal and a MQTT broker + Send and receiving command by MQTT + + This gateway enables to: + - receive MQTT data from a topic and send RF signal corresponding to the received MQTT data with an RFM69 module + - publish MQTT data to a different topic related to received signal from an RFM69 module + + Copyright: (c)Felix Rusu LowPowerLab.com + Library and code by Felix Rusu - felix@lowpowerlab.com + Modification of the code nanohab from bbx10 https://github.com/bbx10/nanohab + Copyright: (c)Florian ROBERT + + This file is part of OpenMQTTGateway. + + OpenMQTTGateway is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenMQTTGateway is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifdef ZgatewayRFM69 + +#include //https://www.github.com/lowpowerlab/rfm69 +#include +#include + +char RadioConfig[128]; + +// vvvvvvvvv Global Configuration vvvvvvvvvvv + +struct _GLOBAL_CONFIG { + uint32_t checksum; + char rfmapname[32]; + char encryptkey[16+1]; + uint8_t networkid; + uint8_t nodeid; + uint8_t powerlevel; // bits 0..4 power leve, bit 7 RFM69HCW 1=true + uint8_t rfmfrequency; +}; + +#define GC_POWER_LEVEL (pGC->powerlevel & 0x1F) +#define GC_IS_RFM69HCW ((pGC->powerlevel & 0x80) != 0) +#define SELECTED_FREQ(f) ((pGC->rfmfrequency==f)?"selected":"") + +struct _GLOBAL_CONFIG *pGC; + +// vvvvvvvvv Global Configuration vvvvvvvvvvv +uint32_t gc_checksum() { + uint8_t *p = (uint8_t *)pGC; + uint32_t checksum = 0; + p += sizeof(pGC->checksum); + for (size_t i = 0; i < (sizeof(*pGC) - 4); i++) { + checksum += *p++; + } + return checksum; +} + +#ifdef ESP8266 +void eeprom_setup() { + EEPROM.begin(4096); + pGC = (struct _GLOBAL_CONFIG *)EEPROM.getDataPtr(); + // if checksum bad init GC else use GC values + if (gc_checksum() != pGC->checksum) { + trc("Factory reset"); + memset(pGC, 0, sizeof(*pGC)); + strcpy_P(pGC->encryptkey, ENCRYPTKEY); + strcpy_P(pGC->rfmapname, RFM69AP_NAME); + pGC->networkid = NETWORKID; + pGC->nodeid = NODEID; + pGC->powerlevel = ((IS_RFM69HCW)?0x80:0x00) | POWER_LEVEL; + pGC->rfmfrequency = FREQUENCY; + pGC->checksum = gc_checksum(); + EEPROM.commit(); + } +} +#endif + +RFM69 radio; + +void setupRFM69(void) { + #ifdef ESP8266 + eeprom_setup(); + #endif + int freq; + static const char PROGMEM JSONtemplate[] = + R"({"msgType":"config","freq":%d,"rfm69hcw":%d,"netid":%d,"power":%d})"; + char payload[128]; + + radio = RFM69(RFM69_CS, RFM69_IRQ, GC_IS_RFM69HCW, RFM69_IRQN); + + // Initialize radio + if (!radio.initialize(pGC->rfmfrequency, pGC->nodeid, pGC->networkid)) + { + trc(F("RFM69 initialization failed")); + } + + if (GC_IS_RFM69HCW) { + radio.setHighPower(); // Only for RFM69HCW & HW! + } + radio.setPowerLevel(GC_POWER_LEVEL); // power output ranges from 0 (5dBm) to 31 (20dBm) + + if (pGC->encryptkey[0] != '\0') radio.encrypt(pGC->encryptkey); + + trc(F("RFM69 Listening and transmitting at")); + switch (pGC->rfmfrequency) { + case RF69_433MHZ: + freq = 433; + break; + case RF69_868MHZ: + freq = 868; + break; + case RF69_915MHZ: + freq = 915; + break; + case RF69_315MHZ: + freq = 315; + break; + default: + freq = -1; + break; + } + trc(String(freq)); + + size_t len = snprintf_P(RadioConfig, sizeof(RadioConfig), JSONtemplate, + freq, GC_IS_RFM69HCW, pGC->networkid, GC_POWER_LEVEL); + if (len >= sizeof(RadioConfig)) { + trc("\n\n*** RFM69 config truncated ***\n"); + } +} + +boolean RFM69toMQTT(void) { + //check if something was received (could be an interrupt from the radio) + if (radio.receiveDone()) + { + + uint8_t data[RF69_MAX_DATA_LEN]; + + //save packet because it may be overwritten + memcpy(data, (void *)radio.DATA, radio.DATALEN); + client.publish(subjectRFM69toMQTT,(char *)data); + + trc(F("Data received")); + trc((const char *)data); + + //check if sender wanted an ACK + if (radio.ACKRequested()) + { + radio.sendACK(); + } + radio.receiveDone(); //put radio in RX mode + //updateClients(senderId, rssi, (const char *)data); + + return true; + + } else { + radio.receiveDone(); //put radio in RX mode + return false; + } +} + +boolean MQTTtoRFM69(char * topicOri, char * datacallback) { + int loops; + uint32_t startMillis; + static uint32_t deltaMillis = 0; + // RF DATA ANALYSIS + //We look into the subject to see if a special RF protocol is defined + String topic = topicOri; + int valueRCV = defaultRFM69ReceiverId; //default receiver id value + int pos = topic.lastIndexOf(RFM69receiverKey); + if (pos != -1){ + pos = pos + +strlen(RFM69receiverKey); + valueRCV = (topic.substring(pos,pos + 3)).toInt(); + trc(F("RFM69 receiver ID:")); + trc(String(valueRCV)); + } +if ((topic == subjectMQTTtoRFM69) && (valueRCV == defaultRFM69ReceiverId)){ + trc(F("MQTTtoRFM69 default")); + loops = 10; + startMillis = millis(); + while (loops--) { + if(radio.sendWithRetry(valueRCV, datacallback, strlen(datacallback)+1)) { + deltaMillis = millis() - startMillis; + Serial.print(" OK "); + Serial.println(deltaMillis); + // Acknowledgement to the GTWRF topic + boolean result = client.publish(subjectGTWRFM69toMQTT, datacallback); + if (result)trc(F("Ack published")); + return true; + break; + } + else { + Serial.print("!"); + } + delay(50); + } + if (loops <= 0) { + deltaMillis = 0; + trc(F("RFM69 sending failed")); + return false; + } + } else if (valueRCV != defaultRFM69ReceiverId) { + trc(F("MQTTtoRFM69 user parameters")); + loops = 10; + startMillis = millis(); + while (loops--) { + if(radio.sendWithRetry(valueRCV, datacallback, strlen(datacallback)+1)) { + deltaMillis = millis() - startMillis; + Serial.print(" OK "); + Serial.println(deltaMillis); + // Acknowledgement to the GTWRF topic + boolean result = client.publish(subjectGTWRFM69toMQTT, datacallback); + if (result)trc(F("Ack published")); + return true; + break; + } + else { + Serial.print("!"); + } + delay(50); + } + if (loops <= 0) { + deltaMillis = 0; + trc(F("RFM69 sending failed")); + return false; + } + } +} +#endif diff --git a/img/OpenMQTTGateway_IR_RF_RFM69.png b/img/OpenMQTTGateway_IR_RF_RFM69.png new file mode 100644 index 00000000..1f2019cc Binary files /dev/null and b/img/OpenMQTTGateway_IR_RF_RFM69.png differ diff --git a/libraries/RFM69 b/libraries/RFM69 new file mode 160000 index 00000000..928de01a --- /dev/null +++ b/libraries/RFM69 @@ -0,0 +1 @@ +Subproject commit 928de01a2b0ffc21d0dff935694641c04d6b9041