Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

253 lines
6.2 KiB

6 years ago
  1. /*
  2. RF MODULE
  3. Copyright (C) 2016-2018 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if RF_SUPPORT
  6. #include <RCSwitch.h>
  7. RCSwitch * _rfModem;
  8. unsigned long _rf_learn_start = 0;
  9. unsigned char _rf_learn_id = 0;
  10. bool _rf_learn_status = true;
  11. bool _rf_learn_active = false;
  12. #if WEB_SUPPORT
  13. #include <Ticker.h>
  14. Ticker _rfb_sendcodes;
  15. #endif
  16. // -----------------------------------------------------------------------------
  17. // RF
  18. // -----------------------------------------------------------------------------
  19. unsigned long _rfRetrieve(unsigned char id, bool status) {
  20. String code = getSetting(status ? "rfbON" : "rfbOFF", id, "0");
  21. return strtoul(code.c_str(), 0, 16);
  22. }
  23. void _rfStore(unsigned char id, bool status, unsigned long code) {
  24. DEBUG_MSG_P(PSTR("[RF] Storing %d-%s => %X\n"), id, status ? "ON" : "OFF", code);
  25. char buffer[20];
  26. snprintf_P(buffer, sizeof(buffer), PSTR("%X"), code);
  27. setSetting(status ? "rfbON" : "rfbOFF", id, buffer);
  28. }
  29. void _rfLearn(unsigned char id, bool status) {
  30. _rf_learn_start = millis();
  31. _rf_learn_id = id;
  32. _rf_learn_status = status;
  33. _rf_learn_active = true;
  34. }
  35. void _rfForget(unsigned char id, bool status) {
  36. delSetting(status ? "rfbON" : "rfbOFF", id);
  37. // Websocket update
  38. #if WEB_SUPPORT
  39. char wsb[100];
  40. snprintf_P(wsb, sizeof(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"\"}]}"), id, status ? 1 : 0);
  41. wsSend(wsb);
  42. #endif
  43. }
  44. bool _rfMatch(unsigned long code, unsigned char& relayID, unsigned char& value) {
  45. bool found = false;
  46. DEBUG_MSG_P(PSTR("[RF] Trying to match code %X\n"), code);
  47. for (unsigned char i=0; i<relayCount(); i++) {
  48. unsigned long code_on = _rfRetrieve(i, true);
  49. unsigned long code_off = _rfRetrieve(i, false);
  50. if (code == code_on) {
  51. DEBUG_MSG_P(PSTR("[RF] Match ON code for relay %d\n"), i);
  52. value = 1;
  53. found = true;
  54. }
  55. if (code == code_off) {
  56. DEBUG_MSG_P(PSTR("[RF] Match OFF code for relay %d\n"), i);
  57. if (found) value = 2;
  58. found = true;
  59. }
  60. if (found) {
  61. relayID = i;
  62. return true;
  63. }
  64. }
  65. return false;
  66. }
  67. #if TERMINAL_SUPPORT
  68. void _rfInitCommands() {
  69. settingsRegisterCommand(F("LEARN"), [](Embedis* e) {
  70. if (e->argc < 3) {
  71. DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
  72. return;
  73. }
  74. int id = String(e->argv[1]).toInt();
  75. if (id >= relayCount()) {
  76. DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
  77. return;
  78. }
  79. int status = String(e->argv[2]).toInt();
  80. _rfLearn(id, status == 1);
  81. DEBUG_MSG_P(PSTR("+OK\n"));
  82. });
  83. settingsRegisterCommand(F("FORGET"), [](Embedis* e) {
  84. if (e->argc < 3) {
  85. DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
  86. return;
  87. }
  88. int id = String(e->argv[1]).toInt();
  89. if (id >= relayCount()) {
  90. DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
  91. return;
  92. }
  93. int status = String(e->argv[2]).toInt();
  94. _rfForget(id, status == 1);
  95. DEBUG_MSG_P(PSTR("+OK\n"));
  96. });
  97. }
  98. #endif // TERMINAL_SUPPORT
  99. // -----------------------------------------------------------------------------
  100. // WEB
  101. // -----------------------------------------------------------------------------
  102. #if WEB_SUPPORT
  103. void _rfWebSocketSendCode(unsigned char id, bool status, unsigned long code) {
  104. char wsb[100];
  105. snprintf_P(wsb, sizeof(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"%X\"}]}"), id, status ? 1 : 0, code);
  106. wsSend(wsb);
  107. }
  108. void _rfWebSocketSendCodes() {
  109. for (unsigned char id=0; id<relayCount(); id++) {
  110. _rfWebSocketSendCode(id, true, _rfRetrieve(id, true));
  111. _rfWebSocketSendCode(id, false, _rfRetrieve(id, false));
  112. }
  113. }
  114. void _rfWebSocketOnSend(JsonObject& root) {
  115. char buffer[20];
  116. root["rfbVisible"] = 1;
  117. root["rfbCount"] = relayCount();
  118. _rfb_sendcodes.once_ms(1000, _rfWebSocketSendCodes);
  119. }
  120. void _rfWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
  121. if (strcmp(action, "rfblearn") == 0) _rfLearn(data["id"], data["status"]);
  122. if (strcmp(action, "rfbforget") == 0) _rfForget(data["id"], data["status"]);
  123. if (strcmp(action, "rfbsend") == 0) _rfStore(data["id"], data["status"], strtoul(data["data"], NULL, 16));
  124. }
  125. #endif
  126. // -----------------------------------------------------------------------------
  127. void rfLoop() {
  128. if (_rfModem->available()) {
  129. static unsigned long last = 0;
  130. if (millis() - last > RF_DEBOUNCE) {
  131. last = millis();
  132. if (_rfModem->getReceivedValue() > 0) {
  133. unsigned long rf_code = _rfModem->getReceivedValue();
  134. DEBUG_MSG_P(PSTR("[RF] Received code: %X\n"), rf_code);
  135. if (_rf_learn_active) {
  136. _rf_learn_active = false;
  137. _rfStore(_rf_learn_id, _rf_learn_status, rf_code);
  138. // Websocket update
  139. #if WEB_SUPPORT
  140. _rfWebSocketSendCode(_rf_learn_id, _rf_learn_status, rf_code);
  141. #endif
  142. } else {
  143. unsigned char id;
  144. unsigned char value;
  145. if (_rfMatch(rf_code, id, value)) {
  146. if (2 == value) {
  147. relayToggle(id);
  148. } else {
  149. relayStatus(id, 1 == value);
  150. }
  151. }
  152. }
  153. }
  154. }
  155. _rfModem->resetAvailable();
  156. }
  157. if (_rf_learn_active && (millis() - _rf_learn_start > RF_LEARN_TIMEOUT)) {
  158. _rf_learn_active = false;
  159. }
  160. }
  161. void rfSetup() {
  162. _rfModem = new RCSwitch();
  163. _rfModem->enableReceive(RF_PIN);
  164. DEBUG_MSG_P(PSTR("[RF] RF receiver on GPIO %u\n"), RF_PIN);
  165. #if WEB_SUPPORT
  166. wsOnSendRegister(_rfWebSocketOnSend);
  167. wsOnActionRegister(_rfWebSocketOnAction);
  168. #endif
  169. #if TERMINAL_SUPPORT
  170. _rfInitCommands();
  171. #endif
  172. // Register loop
  173. espurnaRegisterLoop(rfLoop);
  174. }
  175. #endif