Browse Source

MQTT to RF bridge

fastled
Xose Pérez 6 years ago
parent
commit
63b02da68f
10 changed files with 336 additions and 25 deletions
  1. +1
    -0
      code/espurna/config/arduino.h
  2. +1
    -1
      code/espurna/config/debug.h
  3. +6
    -2
      code/espurna/config/general.h
  4. +18
    -0
      code/espurna/config/hardware.h
  5. +12
    -3
      code/espurna/espurna.ino
  6. +9
    -0
      code/espurna/hardware.ino
  7. +2
    -3
      code/espurna/mqtt.ino
  8. +22
    -12
      code/espurna/relay.ino
  9. +242
    -0
      code/espurna/rfbridge.ino
  10. +23
    -4
      code/platformio.ini

+ 1
- 0
code/espurna/config/arduino.h View File

@ -30,6 +30,7 @@
//#define LED_CONTROLLER
//#define H801_LED_CONTROLLER
//#define ESPURNA_H
//#define SONOFF_RFBRIDGE
//--------------------------------------------------------------------------------
// Features (values below are non-default values)


+ 1
- 1
code/espurna/config/debug.h View File

@ -1,6 +1,6 @@
#define DEBUG_MESSAGE_MAX_LENGTH 80
#ifdef SONOFF_DUAL
#if defined(SONOFF_DUAL) | defined(SONOFF_RFBRIDGE)
#undef DEBUG_PORT
#endif


+ 6
- 2
code/espurna/config/general.h View File

@ -20,8 +20,8 @@
// To receive the message son the destination computer use nc:
// nc -ul 8111
//#define DEBUG_UDP_IP IPAddress(192, 168, 1, 100)
//#define DEBUG_UDP_PORT 8111
#define DEBUG_UDP_IP IPAddress(192, 168, 1, 100)
#define DEBUG_UDP_PORT 8113
//--------------------------------------------------------------------------------
// EEPROM
@ -111,6 +111,7 @@ PROGMEM const char* const custom_reset_string[] = {
#define RELAY_PROVIDER_RELAY 0
#define RELAY_PROVIDER_DUAL 1
#define RELAY_PROVIDER_LIGHT 2
#define RELAY_PROVIDER_RFBRIDGE 3
// Pulse time in milliseconds
#define RELAY_PULSE_TIME 1.0
@ -225,6 +226,9 @@ PROGMEM const char* const custom_reset_string[] = {
#define MQTT_TOPIC_HOSTNAME "host"
#define MQTT_TOPIC_TIME "time"
#define MQTT_TOPIC_ANALOG "analog"
#define MQTT_TOPIC_RFOUT "rfout"
#define MQTT_TOPIC_RFIN "rfin"
#define MQTT_TOPIC_RFLEARN "rflearn"
// Periodic reports
#define MQTT_REPORT_STATUS 1


+ 18
- 0
code/espurna/config/hardware.h View File

@ -161,6 +161,7 @@
#define SERIAL_BAUDRATE 19230
#undef RELAY_PROVIDER
#define RELAY_PROVIDER RELAY_PROVIDER_DUAL
#define DUMMY_RELAY_COUNT 2
#elif defined(SONOFF_4CH)
@ -222,6 +223,20 @@
#define LED1_PIN 13
#define LED1_PIN_INVERSE 1
#elif defined(SONOFF_RFBRIDGE)
#define MANUFACTURER "ITEAD"
#define DEVICE "RFBRIDGE"
#define BUTTON1_PIN 0
#define BUTTON1_MODE BUTTON_PUSHBUTTON | BUTTON_DEFAULT_HIGH
#define LED1_PIN 13
#define LED1_PIN_INVERSE 1
#undef SERIAL_BAUDRATE
#define SERIAL_BAUDRATE 19200
#undef RELAY_PROVIDER
#define RELAY_PROVIDER RELAY_PROVIDER_RFBRIDGE
#define DUMMY_RELAY_COUNT 4
// -----------------------------------------------------------------------------
// Electrodragon boards
// -----------------------------------------------------------------------------
@ -269,6 +284,7 @@
#define DEVICE "AI_LIGHT"
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
#define LIGHT_PROVIDER LIGHT_PROVIDER_MY9192
#define DUMMY_RELAY_COUNT 1
// -----------------------------------------------------------------------------
// LED Controller
@ -282,6 +298,7 @@
#define LED1_PIN_INVERSE 1
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
#define LIGHT_PROVIDER LIGHT_PROVIDER_RGB
#define DUMMY_RELAY_COUNT 1
#undef RGBW_INVERSE_LOGIC
#undef RGBW_RED_PIN
@ -307,6 +324,7 @@
#define LED1_PIN_INVERSE 1
#define RELAY_PROVIDER RELAY_PROVIDER_LIGHT
#define LIGHT_PROVIDER LIGHT_PROVIDER_RGB2W
#define DUMMY_RELAY_COUNT 1
#undef RGBW_INVERSE_LOGIC
#undef RGBW_RED_PIN


+ 12
- 3
code/espurna/espurna.ino View File

@ -120,19 +120,22 @@ unsigned char customReset() {
}
void hardwareSetup() {
EEPROM.begin(4096);
#ifdef DEBUG_PORT
DEBUG_PORT.begin(SERIAL_BAUDRATE);
if (customReset() == CUSTOM_RESET_HARDWARE) {
DEBUG_PORT.setDebugOutput(true);
}
#endif
#ifdef SONOFF_DUAL
#elif defined(SERIAL_BAUDRATE)
Serial.begin(SERIAL_BAUDRATE);
#endif
#if not EMBEDDED_WEB
SPIFFS.begin();
#endif
}
void hardwareLoop() {
@ -214,6 +217,9 @@ void setup() {
mqttSetup();
ntpSetup();
#ifdef SONOFF_RFBRIDGE
rfbSetup();
#endif
#if ENABLE_I2C
i2cSetup();
#endif
@ -264,9 +270,12 @@ void loop() {
#if ENABLE_FAUXMO
fauxmoLoop();
#endif
#ifndef SONOFF_DUAL
#if !defined(SONOFF_DUAL) & !defined(SONOFF_RFBRIDGE)
settingsLoop();
#endif
#ifdef SONOFF_RFBRIDGE
rfbLoop();
#endif
#if ENABLE_NOFUSS
nofussLoop();
#endif


+ 9
- 0
code/espurna/hardware.ino View File

@ -17,6 +17,7 @@ the migration to future version 2 will be straigh forward.
#define RELAY_PROVIDER_RELAY 0
#define RELAY_PROVIDER_DUAL 1
#define RELAY_PROVIDER_LIGHT 2
#define RELAY_PROVIDER_RFBRIDGE 3
#define LIGHT_PROVIDER_NONE 0
#define LIGHT_PROVIDER_WS2812 1
@ -278,6 +279,14 @@ void hwUpwardsCompatibility() {
setSetting("lightLogic", 1);
#endif
#ifdef SONOFF_RFBRIDGE
setSetting("board", 25);
setSetting("ledGPIO", 1, 13);
setSetting("ledLogic", 1, 1);
setSetting("btnGPIO", 1, 0);
setSetting("relayProvider", RELAY_PROVIDER_RFBRIDGE);
#endif
saveSettings();
}

+ 2
- 3
code/espurna/mqtt.ino View File

@ -208,14 +208,13 @@ void _mqttOnMessage(char* topic, char* payload, unsigned int len) {
char message[len + 1];
strlcpy(message, (char *) payload, len + 1);
DEBUG_MSG_P(PSTR("[MQTT] Received %s => %s"), topic, message);
#if MQTT_SKIP_RETAINED
if (millis() - mqttConnectedAt < MQTT_SKIP_TIME) {
DEBUG_MSG_P(PSTR(" - SKIPPED\n"));
DEBUG_MSG_P(PSTR("[MQTT] Received %s => %s - SKIPPED\n"), topic, message);
return;
}
#endif
DEBUG_MSG_P(PSTR("\n"));
DEBUG_MSG_P(PSTR("[MQTT] Received %s => %s\n"), topic, message);
// Check system topics
String t = mqttSubtopic((char *) topic);


+ 22
- 12
code/espurna/relay.ino View File

@ -18,6 +18,7 @@ typedef struct {
unsigned char led;
unsigned int floodWindowStart;
unsigned char floodWindowChanges;
bool scheduled;
unsigned int scheduledStatusTime;
bool scheduledStatus;
bool scheduledReport;
@ -38,6 +39,10 @@ void relayProviderStatus(unsigned char id, bool status) {
if (id >= _relays.size()) return;
#if RELAY_PROVIDER == RELAY_PROVIDER_RFBRIDGE
rfbState(id, status);
#endif
#if RELAY_PROVIDER == RELAY_PROVIDER_DUAL
_dual_status ^= (1 << id);
Serial.flush();
@ -62,6 +67,10 @@ bool relayProviderStatus(unsigned char id) {
if (id >= _relays.size()) return false;
#if RELAY_PROVIDER == RELAY_PROVIDER_RFBRIDGE
return _relays[id].scheduledStatus;
#endif
#if RELAY_PROVIDER == RELAY_PROVIDER_DUAL
return ((_dual_status & (1 << id)) > 0);
#endif
@ -156,7 +165,7 @@ bool relayStatus(unsigned char id, bool status, bool report) {
bool changed = false;
if (relayStatus(id) != status) {
//if (relayStatus(id) != status) {
unsigned int floodWindowEnd = _relays[id].floodWindowStart + 1000 * RELAY_FLOOD_WINDOW;
unsigned int currentTime = millis();
@ -171,6 +180,7 @@ bool relayStatus(unsigned char id, bool status, bool report) {
_relays[id].scheduledStatusTime = floodWindowEnd;
}
_relays[id].scheduled = true;
_relays[id].scheduledStatus = status;
_relays[id].scheduledReport = (report ? true : _relays[id].scheduledReport);
@ -180,7 +190,7 @@ bool relayStatus(unsigned char id, bool status, bool report) {
changed = true;
}
//}
return changed;
}
@ -456,16 +466,14 @@ void relaySetupMQTT() {
void relaySetup() {
#if defined(SONOFF_DUAL)
// Two dummy relays for the dual
_relays.push_back((relay_t) {0, 0});
_relays.push_back((relay_t) {0, 0});
// Dummy relays for AI Light, Magic Home LED Controller, H801,
// Sonoff Dual and Sonoff RF Bridge
#ifdef DUMMY_RELAY_COUNT
#elif defined(AI_LIGHT) | defined(LED_CONTROLLER) | defined(H801_LED_CONTROLLER)
// One dummy relay for the AI Thinker Light & Magic Home and H801 led controllers
_relays.push_back((relay_t) {0, 0});
for (unsigned char i=0; i < DUMMY_RELAY_COUNT; i++) {
_relays.push_back((relay_t) {0, 0});
_relays[i].scheduled = false;
}
#else
@ -513,7 +521,8 @@ void relayLoop(void) {
unsigned int currentTime = millis();
bool status = _relays[id].scheduledStatus;
if (relayStatus(id) != status && currentTime >= _relays[id].scheduledStatusTime) {
//if (relayStatus(id) != status && currentTime >= _relays[id].scheduledStatusTime) {
if (_relays[id].scheduled && currentTime >= _relays[id].scheduledStatusTime) {
DEBUG_MSG_P(PSTR("[RELAY] %d => %s\n"), id, status ? "ON" : "OFF");
@ -541,6 +550,7 @@ void relayLoop(void) {
relayInfluxDB(id);
#endif
_relays[id].scheduled = false;
_relays[id].scheduledReport = false;
}


+ 242
- 0
code/espurna/rfbridge.ino View File

@ -0,0 +1,242 @@
/*
ITEAD RF BRIDGE MODULE
Copyright (C) 2017 by Xose Pérez <xose dot perez at gmail dot com>
*/
#ifdef SONOFF_RFBRIDGE
#define RF_MESSAGE_SIZE 9
#define RF_BUFFER_SIZE 12
#define RF_SEND_TIMES 3
#define RF_SEND_DELAY 50
#define RF_CODE_START 0xAA
#define RF_CODE_ACK 0xA0
#define RF_CODE_LEARN 0xA1
#define RF_CODE_LEARN_KO 0xA2
#define RF_CODE_LEARN_OK 0xA3
#define RF_CODE_RFIN 0xA4
#define RF_CODE_RFOUT 0xA5
#define RF_CODE_STOP 0x55
unsigned char _uartbuf[RF_BUFFER_SIZE] = {0};
unsigned char _uartpos = 0;
unsigned char _learnId = 0;
bool _learnState = true;
// -----------------------------------------------------------------------------
// PRIVATES
// -----------------------------------------------------------------------------
void _rfbAck() {
DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending ACK\n"));
Serial.write(RF_CODE_START);
Serial.write(RF_CODE_ACK);
Serial.write(RF_CODE_STOP);
Serial.flush();
}
void _rfbLearn() {
DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending LEARN\n"));
Serial.write(RF_CODE_START);
Serial.write(RF_CODE_LEARN);
Serial.write(RF_CODE_STOP);
Serial.flush();
}
void _rfbSend(byte * message) {
Serial.write(RF_CODE_START);
Serial.write(RF_CODE_RFOUT);
for (unsigned char j=0; j<RF_MESSAGE_SIZE; j++) {
Serial.write(message[j]);
}
Serial.write(RF_CODE_STOP);
Serial.flush();
}
void _rfbSend(byte * message, int times) {
char buffer[RF_MESSAGE_SIZE];
_rfbToChar(message, buffer);
DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending MESSAGE '%s' %d time(s)\n"), buffer, times);
for (int i=0; i<times; i++) {
if (i>0) {
unsigned long start = millis();
while (millis() - start < RF_SEND_DELAY) delay(1);
}
_rfbSend(message);
}
}
void _rfbDecode() {
byte action = _uartbuf[0];
char buffer[RF_MESSAGE_SIZE * 2 + 1] = {0};
DEBUG_MSG_P(PSTR("[RFBRIDGE] Action 0x%02X\n"), action);
if (action == RF_CODE_LEARN_KO) {
_rfbAck();
DEBUG_MSG_P(PSTR("[RFBRIDGE] Learn timeout\n"));
}
if (action == RF_CODE_LEARN_OK || action == RF_CODE_RFIN) {
_rfbToChar(&_uartbuf[1], buffer);
mqttSend(MQTT_TOPIC_RFIN, buffer);
_rfbAck();
}
if (action == RF_CODE_LEARN_OK) {
// TODO: notify websocket
_rfbStore(_learnId, _learnState, buffer);
DEBUG_MSG_P(PSTR("[RFBRIDGE] Learn success. Storing %d-%s => '%s'\n"), _learnId, _learnState ? "ON" : "OFF", buffer);
}
if (action == RF_CODE_RFIN) {
DEBUG_MSG_P(PSTR("[RFBRIDGE] Forward message '%s'\n"), buffer);
}
}
void _rfbReceive() {
static bool receiving = false;
while (Serial.available()) {
yield();
byte c = Serial.read();
//DEBUG_MSG_P(PSTR("[RFBRIDGE] Received 0x%02X\n"), c);
if (receiving) {
if (c == RF_CODE_STOP) {
_rfbDecode();
receiving = false;
} else {
_uartbuf[_uartpos++] = c;
}
} else if (c == RF_CODE_START) {
_uartpos = 0;
receiving = true;
}
}
}
/*
From an hexa char array ("A220EE...") to a byte array (half the size)
*/
bool _rfbToArray(const char * in, byte * out) {
if (strlen(in) != RF_MESSAGE_SIZE * 2) return false;
char tmp[3] = {0};
for (unsigned char p = 0; p<RF_MESSAGE_SIZE; p++) {
memcpy(tmp, &in[p*2], 2);
out[p] = strtol(tmp, NULL, 16);
}
return true;
}
/*
From a byte array to an hexa char array ("A220EE...", double the size)
*/
bool _rfbToChar(byte * in, char * out) {
for (unsigned char p = 0; p<RF_MESSAGE_SIZE; p++) {
sprintf(&out[p*2], "%02X", in[p]);
}
return true;
}
void _rfbMqttCallback(unsigned int type, const char * topic, const char * payload) {
if (type == MQTT_CONNECT_EVENT) {
char buffer[strlen(MQTT_TOPIC_RFLEARN) + 3];
sprintf(buffer, "%s/+", MQTT_TOPIC_RFLEARN);
mqttSubscribe(buffer);
mqttSubscribe(MQTT_TOPIC_RFOUT);
}
if (type == MQTT_MESSAGE_EVENT) {
// Match topic
String t = mqttSubtopic((char *) topic);
// Check if should go into learn mode
if (t.startsWith(MQTT_TOPIC_RFLEARN)) {
_learnId = t.substring(strlen(MQTT_TOPIC_RFLEARN)+1).toInt();
if (_learnId >= relayCount()) {
DEBUG_MSG_P(PSTR("[RFBRIDGE] Wrong learnID (%d)\n"), _learnId);
return;
}
_learnState = (char)payload[0] != '0';
_rfbLearn();
}
if (t.equals(MQTT_TOPIC_RFOUT)) {
byte message[RF_MESSAGE_SIZE];
if (_rfbToArray(payload, message)) {
_rfbSend(message, RF_SEND_TIMES);
}
}
}
}
void _rfbStore(unsigned char id, bool status, char * code) {
char key[8] = {0};
sprintf(key, "rfb%d%s", id, status ? "on" : "off");
setSetting(key, code);
}
String _rfbRetrieve(unsigned char id, bool status) {
char key[8] = {0};
sprintf(key, "rfb%d%s", id, status ? "on" : "off");
return getSetting(key);
}
// -----------------------------------------------------------------------------
// PUBLIC
// -----------------------------------------------------------------------------
void rfbState(unsigned char id, bool status) {
String value = _rfbRetrieve(id, status);
DEBUG_MSG_P(PSTR("[RFBRIDGE] Retrieving value for %d-%s => %s\n"), id, status ? "ON" : "OFF", value.c_str());
if (value.length() > 0) {
byte message[RF_MESSAGE_SIZE];
_rfbToArray(value.c_str(), message);
_rfbSend(message, RF_SEND_TIMES);
}
}
void rfbLearn(unsigned char id, bool status) {
_learnId = id;
_learnState = status;
_rfbLearn();
}
void rfbForget(unsigned char id, bool status) {
char key[8] = {0};
sprintf(key, "rfb%d%s", id, status ? "on" : "off");
delSetting(key);
}
// -----------------------------------------------------------------------------
// SETUP & LOOP
// -----------------------------------------------------------------------------
void rfbSetup() {
mqttRegister(_rfbMqttCallback);
}
void rfbLoop() {
_rfbReceive();
}
#endif

+ 23
- 4
code/platformio.ini View File

@ -182,8 +182,7 @@ upload_flags = --auth=fibonacci --port 8266
[env:sonoff-4ch-debug]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
board = esp8285
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m} -DSONOFF_4CH
@ -191,8 +190,7 @@ build_flags = ${common.build_flags_1m} -DSONOFF_4CH
[env:sonoff-4ch-debug-ota]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
board = esp8285
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m} -DSONOFF_4CH
@ -263,6 +261,27 @@ upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=fibonacci --port 8266
[env:rfbridge-debug]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m} -DSONOFF_RFBRIDGE
[env:rfbridge-debug-ota]
platform = espressif8266
framework = arduino
board = esp01_1m
board_flash_mode = dout
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
build_flags = ${common.build_flags_1m} -DSONOFF_RFBRIDGE
upload_speed = 115200
upload_port = "192.168.4.1"
upload_flags = --auth=Algernon1 --port 8266
[env:1ch-inching-debug]
platform = espressif8266
framework = arduino


Loading…
Cancel
Save