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.

825 lines
21 KiB

  1. /*
  2. RF BRIDGE MODULE
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #include "rfbridge.h"
  6. #if RF_SUPPORT
  7. #include <queue>
  8. #include "api.h"
  9. #include "relay.h"
  10. #include "terminal.h"
  11. #include "mqtt.h"
  12. #include "ws.h"
  13. // -----------------------------------------------------------------------------
  14. // DEFINITIONS
  15. // -----------------------------------------------------------------------------
  16. // EFM8 Protocol
  17. #define RF_MESSAGE_SIZE 9
  18. #define RF_MAX_MESSAGE_SIZE (112+4)
  19. #define RF_CODE_START 0xAA
  20. #define RF_CODE_ACK 0xA0
  21. #define RF_CODE_LEARN 0xA1
  22. #define RF_CODE_LEARN_KO 0xA2
  23. #define RF_CODE_LEARN_OK 0xA3
  24. #define RF_CODE_RFIN 0xA4
  25. #define RF_CODE_RFOUT 0xA5
  26. #define RF_CODE_SNIFFING_ON 0xA6
  27. #define RF_CODE_SNIFFING_OFF 0xA7
  28. #define RF_CODE_RFOUT_NEW 0xA8
  29. #define RF_CODE_LEARN_NEW 0xA9
  30. #define RF_CODE_LEARN_KO_NEW 0xAA
  31. #define RF_CODE_LEARN_OK_NEW 0xAB
  32. #define RF_CODE_RFOUT_BUCKET 0xB0
  33. #define RF_CODE_STOP 0x55
  34. // Settings
  35. #define RF_MAX_KEY_LENGTH (9)
  36. // -----------------------------------------------------------------------------
  37. // GLOBALS TO THE MODULE
  38. // -----------------------------------------------------------------------------
  39. unsigned char _uartbuf[RF_MESSAGE_SIZE+3] = {0};
  40. unsigned char _uartpos = 0;
  41. unsigned char _learnId = 0;
  42. bool _learnStatus = true;
  43. bool _rfbin = false;
  44. struct rfb_message_t {
  45. uint8_t code[RF_MESSAGE_SIZE];
  46. uint8_t times;
  47. };
  48. static std::queue<rfb_message_t> _rfb_message_queue;
  49. #if RFB_DIRECT
  50. RCSwitch * _rfModem;
  51. bool _learning = false;
  52. #endif
  53. bool _rfb_receive = false;
  54. bool _rfb_transmit = false;
  55. unsigned char _rfb_repeat = RF_SEND_TIMES;
  56. // -----------------------------------------------------------------------------
  57. // PRIVATES
  58. // -----------------------------------------------------------------------------
  59. #if WEB_SUPPORT
  60. void _rfbWebSocketSendCodeArray(JsonObject& root, unsigned char start, unsigned char size) {
  61. JsonObject& rfb = root.createNestedObject("rfb");
  62. rfb["size"] = size;
  63. rfb["start"] = start;
  64. JsonArray& on = rfb.createNestedArray("on");
  65. JsonArray& off = rfb.createNestedArray("off");
  66. for (uint8_t id=start; id<start+size; id++) {
  67. on.add(rfbRetrieve(id, true));
  68. off.add(rfbRetrieve(id, false));
  69. }
  70. }
  71. void _rfbWebSocketOnVisible(JsonObject& root) {
  72. root["rfbVisible"] = 1;
  73. }
  74. void _rfbWebSocketOnConnected(JsonObject& root) {
  75. root["rfbRepeat"] = getSetting("rfbRepeat", RF_SEND_TIMES);
  76. root["rfbCount"] = relayCount();
  77. #if RFB_DIRECT
  78. root["rfbdirectVisible"] = 1;
  79. root["rfbRX"] = getSetting("rfbRX", RFB_RX_PIN);
  80. root["rfbTX"] = getSetting("rfbTX", RFB_TX_PIN);
  81. #endif
  82. }
  83. void _rfbWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
  84. if (strcmp(action, "rfblearn") == 0) rfbLearn(data["id"], data["status"]);
  85. if (strcmp(action, "rfbforget") == 0) rfbForget(data["id"], data["status"]);
  86. if (strcmp(action, "rfbsend") == 0) rfbStore(data["id"], data["status"], data["data"].as<const char*>());
  87. }
  88. bool _rfbWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
  89. return (strncmp(key, "rfb", 3) == 0);
  90. }
  91. void _rfbWebSocketOnData(JsonObject& root) {
  92. _rfbWebSocketSendCodeArray(root, 0, relayCount());
  93. }
  94. #endif // WEB_SUPPORT
  95. // From a byte array to an hexa char array ("A220EE...", double the size)
  96. static bool _rfbHexFromBytearray(uint8_t * in, size_t in_size, char * out, size_t out_size) {
  97. if ((2 * in_size + 1) > (out_size)) return false;
  98. static const char base16[] = "0123456789ABCDEF";
  99. unsigned char index = 0;
  100. for (;;) {
  101. if (index >= in_size) break;
  102. out[(index*2)] = base16[(in[index] & 0xf0) >> 4];
  103. out[(index*2)+1] = base16[(in[index] & 0xf)];
  104. ++index;
  105. }
  106. out[2*index] = '\0';
  107. return index <= out_size;
  108. }
  109. // From an hexa char array ("A220EE...") to a byte array (half the size)
  110. static bool _rfbBytearrayFromHex(const char* in, size_t in_size, uint8_t* out, uint8_t out_size) {
  111. if (out_size < (in_size / 2)) return false;
  112. unsigned char index = 0;
  113. unsigned char out_index = 0;
  114. auto char2byte = [](char ch) -> uint8_t {
  115. if ((ch >= '0') && (ch <= '9')) {
  116. return (ch - '0');
  117. } else if ((ch >= 'a') && (ch <= 'f')) {
  118. return 10 + (ch - 'a');
  119. } else if ((ch >= 'A') && (ch <= 'F')) {
  120. return 10 + (ch - 'A');
  121. } else {
  122. return 0;
  123. }
  124. };
  125. for (;;) {
  126. if (index >= in_size) break;
  127. out[out_index] = char2byte(in[index]) << 4;
  128. out[out_index] += char2byte(in[index + 1]);
  129. index += 2;
  130. out_index += 1;
  131. }
  132. return true;
  133. }
  134. void _rfbAckImpl();
  135. void _rfbLearnImpl();
  136. void _rfbSendImpl(uint8_t * message);
  137. void _rfbReceiveImpl();
  138. bool _rfbMatch(char* code, unsigned char& relayID, unsigned char& value, char* buffer = NULL) {
  139. if (strlen(code) != 18) return false;
  140. bool found = false;
  141. String compareto = String(&code[12]);
  142. compareto.toUpperCase();
  143. DEBUG_MSG_P(PSTR("[RF] Trying to match code %s\n"), compareto.c_str());
  144. for (unsigned char i=0; i<relayCount(); i++) {
  145. String code_on = rfbRetrieve(i, true);
  146. if (code_on.length() && code_on.endsWith(compareto)) {
  147. DEBUG_MSG_P(PSTR("[RF] Match ON code for relay %d\n"), i);
  148. value = 1;
  149. found = true;
  150. if (buffer) strcpy(buffer, code_on.c_str());
  151. }
  152. String code_off = rfbRetrieve(i, false);
  153. if (code_off.length() && code_off.endsWith(compareto)) {
  154. DEBUG_MSG_P(PSTR("[RF] Match OFF code for relay %d\n"), i);
  155. if (found) value = 2;
  156. found = true;
  157. if (buffer) strcpy(buffer, code_off.c_str());
  158. }
  159. if (found) {
  160. relayID = i;
  161. return true;
  162. }
  163. }
  164. return false;
  165. }
  166. void _rfbDecode() {
  167. static unsigned long last = 0;
  168. if (millis() - last < RF_RECEIVE_DELAY) return;
  169. last = millis();
  170. uint8_t action = _uartbuf[0];
  171. char buffer[RF_MESSAGE_SIZE * 2 + 1] = {0};
  172. DEBUG_MSG_P(PSTR("[RF] Action 0x%02X\n"), action);
  173. if (action == RF_CODE_LEARN_KO) {
  174. _rfbAckImpl();
  175. DEBUG_MSG_P(PSTR("[RF] Learn timeout\n"));
  176. }
  177. if (action == RF_CODE_LEARN_OK || action == RF_CODE_RFIN) {
  178. _rfbAckImpl();
  179. if (_rfbHexFromBytearray(&_uartbuf[1], RF_MESSAGE_SIZE, buffer, sizeof(buffer))) {
  180. DEBUG_MSG_P(PSTR("[RF] Received message '%s'\n"), buffer);
  181. }
  182. }
  183. if (action == RF_CODE_LEARN_OK) {
  184. DEBUG_MSG_P(PSTR("[RF] Learn success\n"));
  185. rfbStore(_learnId, _learnStatus, buffer);
  186. // Websocket update
  187. #if WEB_SUPPORT
  188. wsPost([](JsonObject& root) {
  189. _rfbWebSocketSendCodeArray(root, _learnId, 1);
  190. });
  191. #endif
  192. }
  193. if (action == RF_CODE_RFIN) {
  194. /* Look for the code, possibly replacing the code with the exact learned one on match
  195. * we want to do this on learn too to be sure that the learned code is the same if it
  196. * is equivalent
  197. */
  198. unsigned char id;
  199. unsigned char status;
  200. bool matched = _rfbMatch(buffer, id, status, buffer);
  201. if (matched) {
  202. DEBUG_MSG_P(PSTR("[RF] Matched message '%s'\n"), buffer);
  203. _rfbin = true;
  204. if (status == 2) {
  205. relayToggle(id);
  206. } else {
  207. relayStatus(id, status == 1);
  208. }
  209. }
  210. #if MQTT_SUPPORT
  211. mqttSend(MQTT_TOPIC_RFIN, buffer, false, false);
  212. #endif
  213. }
  214. }
  215. //
  216. // RF handler implementations
  217. //
  218. #if !RFB_DIRECT // Default for ITEAD SONOFF RFBRIDGE
  219. void _rfbSendRaw(const uint8_t *message, unsigned char size) {
  220. Serial.write(message, size);
  221. }
  222. void _rfbAckImpl() {
  223. DEBUG_MSG_P(PSTR("[RF] Sending ACK\n"));
  224. Serial.println();
  225. Serial.write(RF_CODE_START);
  226. Serial.write(RF_CODE_ACK);
  227. Serial.write(RF_CODE_STOP);
  228. Serial.flush();
  229. Serial.println();
  230. }
  231. void _rfbLearnImpl() {
  232. DEBUG_MSG_P(PSTR("[RF] Sending LEARN\n"));
  233. Serial.println();
  234. Serial.write(RF_CODE_START);
  235. Serial.write(RF_CODE_LEARN);
  236. Serial.write(RF_CODE_STOP);
  237. Serial.flush();
  238. Serial.println();
  239. }
  240. void _rfbSendImpl(uint8_t * message) {
  241. Serial.println();
  242. Serial.write(RF_CODE_START);
  243. Serial.write(RF_CODE_RFOUT);
  244. _rfbSendRaw(message, RF_MESSAGE_SIZE);
  245. Serial.write(RF_CODE_STOP);
  246. Serial.flush();
  247. Serial.println();
  248. }
  249. void _rfbReceiveImpl() {
  250. static bool receiving = false;
  251. while (Serial.available()) {
  252. yield();
  253. uint8_t c = Serial.read();
  254. //DEBUG_MSG_P(PSTR("[RF] Received 0x%02X\n"), c);
  255. if (receiving) {
  256. if (c == RF_CODE_STOP && (_uartpos == 1 || _uartpos == RF_MESSAGE_SIZE + 1)) {
  257. _rfbDecode();
  258. receiving = false;
  259. } else if (_uartpos <= RF_MESSAGE_SIZE) {
  260. _uartbuf[_uartpos++] = c;
  261. } else {
  262. // wrong message, should have received a RF_CODE_STOP
  263. receiving = false;
  264. }
  265. } else if (c == RF_CODE_START) {
  266. _uartpos = 0;
  267. receiving = true;
  268. }
  269. }
  270. }
  271. void _rfbParseRaw(char * raw) {
  272. int rawlen = strlen(raw);
  273. if (rawlen > (RF_MAX_MESSAGE_SIZE * 2)) return;
  274. if ((rawlen < 2) || (rawlen & 1)) return;
  275. DEBUG_MSG_P(PSTR("[RF] Sending RAW MESSAGE '%s'\n"), raw);
  276. uint8_t message[RF_MAX_MESSAGE_SIZE];
  277. _rfbBytearrayFromHex(raw, (size_t)rawlen, message, sizeof(message));
  278. _rfbSendRaw(message, (rawlen / 2));
  279. }
  280. #else // RFB_DIRECT
  281. void _rfbAckImpl() {}
  282. void _rfbLearnImpl() {
  283. DEBUG_MSG_P(PSTR("[RF] Entering LEARN mode\n"));
  284. _learning = true;
  285. }
  286. void _rfbSendImpl(uint8_t * message) {
  287. if (!_rfb_transmit) return;
  288. unsigned int protocol = message[1];
  289. unsigned int timing =
  290. (message[2] << 8) |
  291. (message[3] << 0) ;
  292. unsigned int bitlength = message[4];
  293. unsigned long rf_code =
  294. (message[5] << 24) |
  295. (message[6] << 16) |
  296. (message[7] << 8) |
  297. (message[8] << 0) ;
  298. _rfModem->setProtocol(protocol);
  299. if (timing > 0) {
  300. _rfModem->setPulseLength(timing);
  301. }
  302. _rfModem->send(rf_code, bitlength);
  303. _rfModem->resetAvailable();
  304. }
  305. void _rfbReceiveImpl() {
  306. if (!_rfb_receive) return;
  307. static long learn_start = 0;
  308. if (!_learning && learn_start) {
  309. learn_start = 0;
  310. }
  311. if (_learning) {
  312. if (!learn_start) {
  313. DEBUG_MSG_P(PSTR("[RF] Arming learn timeout\n"));
  314. learn_start = millis();
  315. }
  316. if (learn_start > 0 && millis() - learn_start > RF_LEARN_TIMEOUT) {
  317. DEBUG_MSG_P(PSTR("[RF] Learn timeout triggered\n"));
  318. memset(_uartbuf, 0, sizeof(_uartbuf));
  319. _uartbuf[0] = RF_CODE_LEARN_KO;
  320. _rfbDecode();
  321. _learning = false;
  322. }
  323. }
  324. if (_rfModem->available()) {
  325. static unsigned long last = 0;
  326. if (millis() - last > RF_DEBOUNCE) {
  327. last = millis();
  328. unsigned long rf_code = _rfModem->getReceivedValue();
  329. if ( rf_code > 0) {
  330. DEBUG_MSG_P(PSTR("[RF] Received code: %08X\n"), rf_code);
  331. unsigned int timing = _rfModem->getReceivedDelay();
  332. memset(_uartbuf, 0, sizeof(_uartbuf));
  333. unsigned char *msgbuf = _uartbuf + 1;
  334. _uartbuf[0] = _learning ? RF_CODE_LEARN_OK: RF_CODE_RFIN;
  335. msgbuf[0] = 0xC0;
  336. msgbuf[1] = _rfModem->getReceivedProtocol();
  337. msgbuf[2] = timing >> 8;
  338. msgbuf[3] = timing >> 0;
  339. msgbuf[4] = _rfModem->getReceivedBitlength();
  340. msgbuf[5] = rf_code >> 24;
  341. msgbuf[6] = rf_code >> 16;
  342. msgbuf[7] = rf_code >> 8;
  343. msgbuf[8] = rf_code >> 0;
  344. _rfbDecode();
  345. _learning = false;
  346. }
  347. }
  348. _rfModem->resetAvailable();
  349. }
  350. yield();
  351. }
  352. #endif // RFB_DIRECT
  353. void _rfbEnqueue(uint8_t * code, unsigned char times) {
  354. if (!_rfb_transmit) return;
  355. // rc-switch will repeat on its own
  356. #if RFB_DIRECT
  357. times = 1;
  358. #endif
  359. char buffer[RF_MESSAGE_SIZE];
  360. _rfbHexFromBytearray(code, RF_MESSAGE_SIZE, buffer, sizeof(buffer));
  361. DEBUG_MSG_P(PSTR("[RF] Enqueuing MESSAGE '%s' %d time(s)\n"), buffer, times);
  362. rfb_message_t message;
  363. memcpy(message.code, code, RF_MESSAGE_SIZE);
  364. message.times = times;
  365. _rfb_message_queue.push(message);
  366. }
  367. void _rfbSendQueued() {
  368. if (!_rfb_transmit) return;
  369. // Check if there is something in the queue
  370. if (_rfb_message_queue.empty()) return;
  371. static unsigned long last = 0;
  372. if (millis() - last < RF_SEND_DELAY) return;
  373. last = millis();
  374. // Pop the first message and send it
  375. rfb_message_t message = _rfb_message_queue.front();
  376. _rfb_message_queue.pop();
  377. _rfbSendImpl(message.code);
  378. // Push it to the stack again if we need to send it more than once
  379. if (message.times > 1) {
  380. message.times = message.times - 1;
  381. _rfb_message_queue.push(message);
  382. }
  383. yield();
  384. }
  385. bool _rfbCompare(const char * code1, const char * code2) {
  386. return strcmp(&code1[12], &code2[12]) == 0;
  387. }
  388. bool _rfbSameOnOff(unsigned char id) {
  389. return _rfbCompare(rfbRetrieve(id, true).c_str(), rfbRetrieve(id, false).c_str());
  390. }
  391. void _rfbParseCode(char * code) {
  392. // The payload may be a code in HEX format ([0-9A-Z]{18}) or
  393. // the code comma the number of times to transmit it.
  394. char * tok = strtok(code, ",");
  395. // Check if a switch is linked to that message
  396. unsigned char id;
  397. unsigned char status = 0;
  398. if (_rfbMatch(tok, id, status)) {
  399. if (status == 2) {
  400. relayToggle(id);
  401. } else {
  402. relayStatus(id, status == 1);
  403. }
  404. return;
  405. }
  406. uint8_t message[RF_MESSAGE_SIZE];
  407. if (_rfbBytearrayFromHex(tok, strlen(tok), message, sizeof(message))) {
  408. tok = strtok(nullptr, ",");
  409. uint8_t times = (tok != nullptr) ? atoi(tok) : 1;
  410. _rfbEnqueue(message, times);
  411. }
  412. }
  413. #if MQTT_SUPPORT
  414. void _rfbMqttCallback(unsigned int type, const char * topic, char * payload) {
  415. if (type == MQTT_CONNECT_EVENT) {
  416. char buffer[strlen(MQTT_TOPIC_RFLEARN) + 3];
  417. snprintf_P(buffer, sizeof(buffer), PSTR("%s/+"), MQTT_TOPIC_RFLEARN);
  418. mqttSubscribe(buffer);
  419. if (_rfb_transmit) {
  420. mqttSubscribe(MQTT_TOPIC_RFOUT);
  421. }
  422. #if !RFB_DIRECT
  423. mqttSubscribe(MQTT_TOPIC_RFRAW);
  424. #endif
  425. }
  426. if (type == MQTT_MESSAGE_EVENT) {
  427. // Match topic
  428. String t = mqttMagnitude((char *) topic);
  429. // Check if should go into learn mode
  430. if (t.startsWith(MQTT_TOPIC_RFLEARN)) {
  431. _learnId = t.substring(strlen(MQTT_TOPIC_RFLEARN)+1).toInt();
  432. if (_learnId >= relayCount()) {
  433. DEBUG_MSG_P(PSTR("[RF] Wrong learnID (%d)\n"), _learnId);
  434. return;
  435. }
  436. _learnStatus = (char)payload[0] != '0';
  437. _rfbLearnImpl();
  438. return;
  439. }
  440. if (t.equals(MQTT_TOPIC_RFOUT)) {
  441. _rfbParseCode(payload);
  442. }
  443. #if !RFB_DIRECT
  444. if (t.equals(MQTT_TOPIC_RFRAW)) {
  445. _rfbParseRaw(payload);
  446. }
  447. #endif
  448. }
  449. }
  450. #endif // MQTT_SUPPORT
  451. #if API_SUPPORT
  452. void _rfbAPISetup() {
  453. apiRegister(MQTT_TOPIC_RFOUT,
  454. [](char * buffer, size_t len) {
  455. snprintf_P(buffer, len, PSTR("OK"));
  456. },
  457. [](const char * payload) {
  458. _rfbParseCode((char *) payload);
  459. }
  460. );
  461. apiRegister(MQTT_TOPIC_RFLEARN,
  462. [](char * buffer, size_t len) {
  463. snprintf_P(buffer, len, PSTR("OK"));
  464. },
  465. [](const char * payload) {
  466. // The payload must be the relayID plus the mode (0 or 1)
  467. char * tok = strtok((char *) payload, ",");
  468. if (NULL == tok) return;
  469. if (!isNumber(tok)) return;
  470. _learnId = atoi(tok);
  471. if (_learnId >= relayCount()) {
  472. DEBUG_MSG_P(PSTR("[RF] Wrong learnID (%d)\n"), _learnId);
  473. return;
  474. }
  475. tok = strtok(NULL, ",");
  476. if (NULL == tok) return;
  477. _learnStatus = (char) tok[0] != '0';
  478. _rfbLearnImpl();
  479. }
  480. );
  481. #if !RFB_DIRECT
  482. apiRegister(MQTT_TOPIC_RFRAW,
  483. [](char * buffer, size_t len) {
  484. snprintf_P(buffer, len, PSTR("OK"));
  485. },
  486. [](const char * payload) {
  487. _rfbParseRaw((char *)payload);
  488. }
  489. );
  490. #endif
  491. }
  492. #endif // API_SUPPORT
  493. #if TERMINAL_SUPPORT
  494. void _rfbInitCommands() {
  495. terminalRegisterCommand(F("LEARN"), [](Embedis* e) {
  496. if (e->argc < 3) {
  497. terminalError(F("Wrong arguments"));
  498. return;
  499. }
  500. int id = String(e->argv[1]).toInt();
  501. if (id >= relayCount()) {
  502. DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
  503. return;
  504. }
  505. int status = String(e->argv[2]).toInt();
  506. rfbLearn(id, status == 1);
  507. terminalOK();
  508. });
  509. terminalRegisterCommand(F("FORGET"), [](Embedis* e) {
  510. if (e->argc < 3) {
  511. terminalError(F("Wrong arguments"));
  512. return;
  513. }
  514. int id = String(e->argv[1]).toInt();
  515. if (id >= relayCount()) {
  516. DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
  517. return;
  518. }
  519. int status = String(e->argv[2]).toInt();
  520. rfbForget(id, status == 1);
  521. terminalOK();
  522. });
  523. }
  524. #endif // TERMINAL_SUPPORT
  525. // -----------------------------------------------------------------------------
  526. // PUBLIC
  527. // -----------------------------------------------------------------------------
  528. void rfbStore(unsigned char id, bool status, const char * code) {
  529. DEBUG_MSG_P(PSTR("[RF] Storing %d-%s => '%s'\n"), id, status ? "ON" : "OFF", code);
  530. if (status) {
  531. setSetting({"rfbON", id}, code);
  532. } else {
  533. setSetting({"rfbOFF", id}, code);
  534. }
  535. }
  536. String rfbRetrieve(unsigned char id, bool status) {
  537. if (status) {
  538. return getSetting({"rfbON", id});
  539. } else {
  540. return getSetting({"rfbOFF", id});
  541. }
  542. }
  543. void rfbStatus(unsigned char id, bool status) {
  544. String value = rfbRetrieve(id, status);
  545. if (value.length() > 0) {
  546. uint8_t message[RF_MAX_MESSAGE_SIZE];
  547. int len = _rfbBytearrayFromHex(value.c_str(), value.length(), message, sizeof(message));
  548. if (len == RF_MESSAGE_SIZE && // probably a standard msg
  549. (message[0] != RF_CODE_START || // raw would start with 0xAA
  550. message[1] != RF_CODE_RFOUT_BUCKET || // followed by 0xB0,
  551. message[2] + 4 != len || // needs a valid length,
  552. message[len-1] != RF_CODE_STOP)) { // and finish with 0x55
  553. if (!_rfbin) {
  554. _rfbEnqueue(message, _rfbSameOnOff(id) ? 1 : _rfb_repeat);
  555. }
  556. } else {
  557. #if !RFB_DIRECT
  558. _rfbSendRaw(message, len); // send a raw message
  559. #endif
  560. }
  561. }
  562. _rfbin = false;
  563. }
  564. void rfbLearn(unsigned char id, bool status) {
  565. _learnId = id;
  566. _learnStatus = status;
  567. _rfbLearnImpl();
  568. }
  569. void rfbForget(unsigned char id, bool status) {
  570. char key[RF_MAX_KEY_LENGTH] = {0};
  571. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  572. delSetting(key);
  573. // Websocket update
  574. #if WEB_SUPPORT
  575. wsPost([id](JsonObject& root) {
  576. _rfbWebSocketSendCodeArray(root, id, 1);
  577. });
  578. #endif
  579. }
  580. // -----------------------------------------------------------------------------
  581. // SETUP & LOOP
  582. // -----------------------------------------------------------------------------
  583. void rfbSetup() {
  584. #if MQTT_SUPPORT
  585. mqttRegister(_rfbMqttCallback);
  586. #endif
  587. #if API_SUPPORT
  588. _rfbAPISetup();
  589. #endif
  590. #if WEB_SUPPORT
  591. wsRegister()
  592. .onVisible(_rfbWebSocketOnVisible)
  593. .onConnected(_rfbWebSocketOnConnected)
  594. .onData(_rfbWebSocketOnData)
  595. .onAction(_rfbWebSocketOnAction)
  596. .onKeyCheck(_rfbWebSocketOnKeyCheck);
  597. #endif
  598. #if TERMINAL_SUPPORT
  599. _rfbInitCommands();
  600. #endif
  601. _rfb_repeat = getSetting("rfbRepeat", RF_SEND_TIMES);
  602. #if RFB_DIRECT
  603. const auto rx = getSetting("rfbRX", RFB_RX_PIN);
  604. const auto tx = getSetting("rfbTX", RFB_TX_PIN);
  605. _rfb_receive = gpioValid(rx);
  606. _rfb_transmit = gpioValid(tx);
  607. if (!_rfb_transmit && !_rfb_receive) {
  608. DEBUG_MSG_P(PSTR("[RF] Neither RX or TX are set\n"));
  609. return;
  610. }
  611. _rfModem = new RCSwitch();
  612. if (_rfb_receive) {
  613. _rfModem->enableReceive(rx);
  614. DEBUG_MSG_P(PSTR("[RF] RF receiver on GPIO %u\n"), rx);
  615. }
  616. if (_rfb_transmit) {
  617. _rfModem->enableTransmit(tx);
  618. _rfModem->setRepeatTransmit(_rfb_repeat);
  619. DEBUG_MSG_P(PSTR("[RF] RF transmitter on GPIO %u\n"), tx);
  620. }
  621. #else
  622. _rfb_receive = true;
  623. _rfb_transmit = true;
  624. #endif
  625. // Register loop only when properly configured
  626. espurnaRegisterLoop([]() -> void {
  627. _rfbReceiveImpl();
  628. _rfbSendQueued();
  629. });
  630. }
  631. #endif