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.

705 lines
20 KiB

7 years ago
  1. /*
  2. ITEAD RF BRIDGE MODULE
  3. Copyright (C) 2017-2018 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #ifdef ITEAD_SONOFF_RFBRIDGE
  6. #include <queue>
  7. #include <Ticker.h>
  8. #if RFB_DIRECT
  9. #include <RCSwitch.h>
  10. #endif
  11. // -----------------------------------------------------------------------------
  12. // DEFINITIONS
  13. // -----------------------------------------------------------------------------
  14. // EFM8 Protocol
  15. #define RF_MESSAGE_SIZE 9
  16. #define RF_MAX_MESSAGE_SIZE (112+4)
  17. #define RF_CODE_START 0xAA
  18. #define RF_CODE_ACK 0xA0
  19. #define RF_CODE_LEARN 0xA1
  20. #define RF_CODE_LEARN_KO 0xA2
  21. #define RF_CODE_LEARN_OK 0xA3
  22. #define RF_CODE_RFIN 0xA4
  23. #define RF_CODE_RFOUT 0xA5
  24. #define RF_CODE_SNIFFING_ON 0xA6
  25. #define RF_CODE_SNIFFING_OFF 0xA7
  26. #define RF_CODE_RFOUT_NEW 0xA8
  27. #define RF_CODE_LEARN_NEW 0xA9
  28. #define RF_CODE_LEARN_KO_NEW 0xAA
  29. #define RF_CODE_LEARN_OK_NEW 0xAB
  30. #define RF_CODE_RFOUT_BUCKET 0xB0
  31. #define RF_CODE_STOP 0x55
  32. // Settings
  33. #define RF_MAX_KEY_LENGTH (9)
  34. // -----------------------------------------------------------------------------
  35. // GLOBALS TO THE MODULE
  36. // -----------------------------------------------------------------------------
  37. unsigned char _uartbuf[RF_MESSAGE_SIZE+3] = {0};
  38. unsigned char _uartpos = 0;
  39. unsigned char _learnId = 0;
  40. bool _learnStatus = true;
  41. bool _rfbin = false;
  42. typedef struct {
  43. byte code[RF_MESSAGE_SIZE];
  44. byte times;
  45. } rfb_message_t;
  46. static std::queue<rfb_message_t> _rfb_message_queue;
  47. Ticker _rfb_ticker;
  48. bool _rfb_ticker_active = false;
  49. #if RFB_DIRECT
  50. RCSwitch * _rfModem;
  51. bool _learning = false;
  52. #endif
  53. #if WEB_SUPPORT
  54. Ticker _rfb_sendcodes;
  55. #endif
  56. // -----------------------------------------------------------------------------
  57. // PRIVATES
  58. // -----------------------------------------------------------------------------
  59. /*
  60. From an hexa char array ("A220EE...") to a byte array (half the size)
  61. */
  62. static int _rfbToArray(const char * in, byte * out, int length = RF_MESSAGE_SIZE * 2) {
  63. int n = strlen(in);
  64. if (n > RF_MAX_MESSAGE_SIZE*2 || (length > 0 && n != length)) return 0;
  65. char tmp[3] = {0,0,0};
  66. n /= 2;
  67. for (unsigned char p = 0; p<n; p++) {
  68. memcpy(tmp, &in[p*2], 2);
  69. out[p] = strtol(tmp, NULL, 16);
  70. }
  71. return n;
  72. }
  73. /*
  74. From a byte array to an hexa char array ("A220EE...", double the size)
  75. */
  76. static bool _rfbToChar(byte * in, char * out, int n = RF_MESSAGE_SIZE) {
  77. for (unsigned char p = 0; p<n; p++) {
  78. sprintf_P(&out[p*2], PSTR("%02X"), in[p]);
  79. }
  80. return true;
  81. }
  82. #if WEB_SUPPORT
  83. void _rfbWebSocketSendCode(unsigned char id, bool status, const char * code) {
  84. char wsb[192]; // (32 * 5): 46 bytes for json , 116 bytes raw code, reserve
  85. snprintf_P(wsb, sizeof(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"%s\"}]}"), id, status ? 1 : 0, code);
  86. wsSend(wsb);
  87. }
  88. void _rfbWebSocketSendCodes() {
  89. for (unsigned char id=0; id<relayCount(); id++) {
  90. _rfbWebSocketSendCode(id, true, rfbRetrieve(id, true).c_str());
  91. _rfbWebSocketSendCode(id, false, rfbRetrieve(id, false).c_str());
  92. }
  93. }
  94. void _rfbWebSocketOnSend(JsonObject& root) {
  95. root["rfbVisible"] = 1;
  96. root["rfbCount"] = relayCount();
  97. #if RF_RAW_SUPPORT
  98. root["rfbrawVisible"] = 1;
  99. #endif
  100. _rfb_sendcodes.once_ms(1000, _rfbWebSocketSendCodes);
  101. }
  102. void _rfbWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
  103. if (strcmp(action, "rfblearn") == 0) rfbLearn(data["id"], data["status"]);
  104. if (strcmp(action, "rfbforget") == 0) rfbForget(data["id"], data["status"]);
  105. if (strcmp(action, "rfbsend") == 0) rfbStore(data["id"], data["status"], data["data"].as<const char*>());
  106. }
  107. #endif // WEB_SUPPORT
  108. void _rfbAck() {
  109. #if not RFB_DIRECT
  110. DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending ACK\n"));
  111. Serial.println();
  112. Serial.write(RF_CODE_START);
  113. Serial.write(RF_CODE_ACK);
  114. Serial.write(RF_CODE_STOP);
  115. Serial.flush();
  116. Serial.println();
  117. #endif
  118. }
  119. void _rfbLearn() {
  120. #if RFB_DIRECT
  121. DEBUG_MSG_P(PSTR("[RFBRIDGE] Entering LEARN mode\n"));
  122. _learning = true;
  123. #else
  124. DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending LEARN\n"));
  125. Serial.println();
  126. Serial.write(RF_CODE_START);
  127. Serial.write(RF_CODE_LEARN);
  128. Serial.write(RF_CODE_STOP);
  129. Serial.flush();
  130. Serial.println();
  131. #endif
  132. #if WEB_SUPPORT
  133. char buffer[100];
  134. snprintf_P(buffer, sizeof(buffer), PSTR("{\"action\": \"rfbLearn\", \"data\":{\"id\": %d, \"status\": %d}}"), _learnId, _learnStatus ? 1 : 0);
  135. wsSend(buffer);
  136. #endif
  137. }
  138. void _rfbSendRaw(const byte *message, const unsigned char n = RF_MESSAGE_SIZE) {
  139. for (unsigned char j=0; j<n; j++) {
  140. Serial.write(message[j]);
  141. }
  142. }
  143. void _rfbSend(byte * message) {
  144. #if RFB_DIRECT
  145. unsigned int protocol = message[1];
  146. unsigned int timing =
  147. (message[2] << 8) |
  148. (message[3] << 0) ;
  149. unsigned int bitlength = message[4];
  150. unsigned long rf_code =
  151. (message[5] << 24) |
  152. (message[6] << 16) |
  153. (message[7] << 8) |
  154. (message[8] << 0) ;
  155. _rfModem->setProtocol(protocol);
  156. if (timing > 0) {
  157. _rfModem->setPulseLength(timing);
  158. }
  159. _rfModem->send(rf_code, bitlength);
  160. _rfModem->resetAvailable();
  161. #else
  162. Serial.println();
  163. Serial.write(RF_CODE_START);
  164. Serial.write(RF_CODE_RFOUT);
  165. _rfbSendRaw(message);
  166. Serial.write(RF_CODE_STOP);
  167. Serial.flush();
  168. Serial.println();
  169. #endif
  170. }
  171. void _rfbSend() {
  172. // Check if there is something in the queue
  173. if (_rfb_message_queue.empty()) return;
  174. // Pop the first element
  175. rfb_message_t message = _rfb_message_queue.front();
  176. _rfb_message_queue.pop();
  177. // Send the message
  178. _rfbSend(message.code);
  179. // If it should be further sent, push it to the stack again
  180. if (message.times > 1) {
  181. message.times = message.times - 1;
  182. _rfb_message_queue.push(message);
  183. }
  184. // if there are still messages in the queue...
  185. if (_rfb_message_queue.empty()) {
  186. _rfb_ticker.detach();
  187. _rfb_ticker_active = false;
  188. }
  189. }
  190. void _rfbSend(byte * code, unsigned char times) {
  191. #if RFB_DIRECT
  192. times = 1;
  193. #endif
  194. char buffer[RF_MESSAGE_SIZE];
  195. _rfbToChar(code, buffer);
  196. DEBUG_MSG_P(PSTR("[RFBRIDGE] Enqueuing MESSAGE '%s' %d time(s)\n"), buffer, times);
  197. rfb_message_t message;
  198. memcpy(message.code, code, RF_MESSAGE_SIZE);
  199. message.times = times;
  200. _rfb_message_queue.push(message);
  201. // Enable the ticker if not running
  202. if (!_rfb_ticker_active) {
  203. _rfb_ticker_active = true;
  204. _rfb_ticker.attach_ms(RF_SEND_DELAY, _rfbSend);
  205. }
  206. }
  207. #if RF_RAW_SUPPORT
  208. void _rfbSendRawOnce(byte *code, unsigned char length) {
  209. char buffer[length*2];
  210. _rfbToChar(code, buffer, length);
  211. DEBUG_MSG_P(PSTR("[RFBRIDGE] Sending RAW MESSAGE '%s'\n"), buffer);
  212. _rfbSendRaw(code, length);
  213. }
  214. #endif // RF_RAW_SUPPORT
  215. bool _rfbMatch(char* code, unsigned char& relayID, unsigned char& value, char* buffer = NULL) {
  216. if (strlen(code) != 18) return false;
  217. bool found = false;
  218. String compareto = String(&code[12]);
  219. compareto.toUpperCase();
  220. DEBUG_MSG_P(PSTR("[RFBRIDGE] Trying to match code %s\n"), compareto.c_str());
  221. for (unsigned char i=0; i<relayCount(); i++) {
  222. String code_on = rfbRetrieve(i, true);
  223. if (code_on.length() && code_on.endsWith(compareto)) {
  224. DEBUG_MSG_P(PSTR("[RFBRIDGE] Match ON code for relay %d\n"), i);
  225. value = 1;
  226. found = true;
  227. if (buffer) strcpy(buffer, code_on.c_str());
  228. }
  229. String code_off = rfbRetrieve(i, false);
  230. if (code_off.length() && code_off.endsWith(compareto)) {
  231. DEBUG_MSG_P(PSTR("[RFBRIDGE] Match OFF code for relay %d\n"), i);
  232. if (found) value = 2;
  233. found = true;
  234. if (buffer) strcpy(buffer, code_off.c_str());
  235. }
  236. if (found) {
  237. relayID = i;
  238. return true;
  239. }
  240. }
  241. return false;
  242. }
  243. void _rfbDecode() {
  244. static unsigned long last = 0;
  245. if (millis() - last < RF_RECEIVE_DELAY) return;
  246. last = millis();
  247. byte action = _uartbuf[0];
  248. char buffer[RF_MESSAGE_SIZE * 2 + 1] = {0};
  249. DEBUG_MSG_P(PSTR("[RFBRIDGE] Action 0x%02X\n"), action);
  250. if (action == RF_CODE_LEARN_KO) {
  251. _rfbAck();
  252. DEBUG_MSG_P(PSTR("[RFBRIDGE] Learn timeout\n"));
  253. #if WEB_SUPPORT
  254. wsSend_P(PSTR("{\"action\": \"rfbTimeout\"}"));
  255. #endif
  256. }
  257. if (action == RF_CODE_LEARN_OK || action == RF_CODE_RFIN) {
  258. _rfbAck();
  259. _rfbToChar(&_uartbuf[1], buffer);
  260. DEBUG_MSG_P(PSTR("[RFBRIDGE] Received message '%s'\n"), buffer);
  261. }
  262. if (action == RF_CODE_LEARN_OK) {
  263. DEBUG_MSG_P(PSTR("[RFBRIDGE] Learn success\n"));
  264. rfbStore(_learnId, _learnStatus, buffer);
  265. // Websocket update
  266. #if WEB_SUPPORT
  267. _rfbWebSocketSendCode(_learnId, _learnStatus, buffer);
  268. #endif
  269. }
  270. if (action == RF_CODE_RFIN) {
  271. /* Look for the code, possibly replacing the code with the exact learned one on match
  272. * we want to do this on learn too to be sure that the learned code is the same if it
  273. * is equivalent
  274. */
  275. unsigned char id;
  276. unsigned char status;
  277. bool matched = _rfbMatch(buffer, id, status, buffer);
  278. if (matched) {
  279. DEBUG_MSG_P(PSTR("[RFBRIDGE] Matched message '%s'\n"), buffer);
  280. _rfbin = true;
  281. if (status == 2) {
  282. relayToggle(id);
  283. } else {
  284. relayStatus(id, status == 1);
  285. }
  286. }
  287. #if MQTT_SUPPORT
  288. mqttSend(MQTT_TOPIC_RFIN, buffer);
  289. #endif
  290. }
  291. }
  292. void _rfbReceive() {
  293. #if RFB_DIRECT
  294. static long learn_start = 0;
  295. if (!_learning && learn_start) {
  296. learn_start = 0;
  297. }
  298. if (_learning) {
  299. if (!learn_start) {
  300. DEBUG_MSG_P(PSTR("[RFBRIDGE] arming learn timeout\n"));
  301. learn_start = millis();
  302. }
  303. if (learn_start > 0 && millis() - learn_start > RF_LEARN_TIMEOUT) {
  304. DEBUG_MSG_P(PSTR("[RFBRIDGE] learn timeout triggered\n"));
  305. memset(_uartbuf, 0, sizeof(_uartbuf));
  306. _uartbuf[0] = RF_CODE_LEARN_KO;
  307. _rfbDecode();
  308. _learning = false;
  309. }
  310. }
  311. if (_rfModem->available()) {
  312. static unsigned long last = 0;
  313. if (millis() - last > RF_DEBOUNCE) {
  314. last = millis();
  315. unsigned long rf_code = _rfModem->getReceivedValue();
  316. if ( rf_code > 0) {
  317. DEBUG_MSG_P(PSTR("[RFBRIDGE] Received code: %08X\n"), rf_code);
  318. unsigned int timing = _rfModem->getReceivedDelay();
  319. memset(_uartbuf, 0, sizeof(_uartbuf));
  320. unsigned char *msgbuf = _uartbuf + 1;
  321. _uartbuf[0] = _learning ? RF_CODE_LEARN_OK: RF_CODE_RFIN;
  322. msgbuf[0] = 0xC0;
  323. msgbuf[1] = _rfModem->getReceivedProtocol();
  324. msgbuf[2] = timing >> 8;
  325. msgbuf[3] = timing >> 0;
  326. msgbuf[4] = _rfModem->getReceivedBitlength();
  327. msgbuf[5] = rf_code >> 24;
  328. msgbuf[6] = rf_code >> 16;
  329. msgbuf[7] = rf_code >> 8;
  330. msgbuf[8] = rf_code >> 0;
  331. _rfbDecode();
  332. _learning = false;
  333. }
  334. }
  335. _rfModem->resetAvailable();
  336. }
  337. #else
  338. static bool receiving = false;
  339. while (Serial.available()) {
  340. yield();
  341. byte c = Serial.read();
  342. //DEBUG_MSG_P(PSTR("[RFBRIDGE] Received 0x%02X\n"), c);
  343. if (receiving) {
  344. if (c == RF_CODE_STOP && (_uartpos == 1 || _uartpos == RF_MESSAGE_SIZE + 1)) {
  345. _rfbDecode();
  346. receiving = false;
  347. } else if (_uartpos <= RF_MESSAGE_SIZE) {
  348. _uartbuf[_uartpos++] = c;
  349. } else {
  350. // wrong message, should have received a RF_CODE_STOP
  351. receiving = false;
  352. }
  353. } else if (c == RF_CODE_START) {
  354. _uartpos = 0;
  355. receiving = true;
  356. }
  357. }
  358. #endif
  359. }
  360. bool _rfbCompare(const char * code1, const char * code2) {
  361. return strcmp(&code1[12], &code2[12]) == 0;
  362. }
  363. bool _rfbSameOnOff(unsigned char id) {
  364. return _rfbCompare(rfbRetrieve(id, true).c_str(), rfbRetrieve(id, false).c_str());
  365. }
  366. #if MQTT_SUPPORT
  367. void _rfbMqttCallback(unsigned int type, const char * topic, const char * payload) {
  368. if (type == MQTT_CONNECT_EVENT) {
  369. char buffer[strlen(MQTT_TOPIC_RFLEARN) + 3];
  370. snprintf_P(buffer, sizeof(buffer), PSTR("%s/+"), MQTT_TOPIC_RFLEARN);
  371. mqttSubscribe(buffer);
  372. mqttSubscribe(MQTT_TOPIC_RFOUT);
  373. #if RF_RAW_SUPPORT
  374. mqttSubscribe(MQTT_TOPIC_RFRAW);
  375. #endif
  376. }
  377. if (type == MQTT_MESSAGE_EVENT) {
  378. // Match topic
  379. String t = mqttMagnitude((char *) topic);
  380. // Check if should go into learn mode
  381. if (t.startsWith(MQTT_TOPIC_RFLEARN)) {
  382. _learnId = t.substring(strlen(MQTT_TOPIC_RFLEARN)+1).toInt();
  383. if (_learnId >= relayCount()) {
  384. DEBUG_MSG_P(PSTR("[RFBRIDGE] Wrong learnID (%d)\n"), _learnId);
  385. return;
  386. }
  387. _learnStatus = (char)payload[0] != '0';
  388. _rfbLearn();
  389. return;
  390. }
  391. bool isRFOut = t.equals(MQTT_TOPIC_RFOUT);
  392. #if RF_RAW_SUPPORT
  393. bool isRFRaw = !isRFOut && t.equals(MQTT_TOPIC_RFRAW);
  394. #else
  395. bool isRFRaw = false;
  396. #endif
  397. if (isRFOut || isRFRaw) {
  398. // The payload may be a code in HEX format ([0-9A-Z]{18}) or
  399. // the code comma the number of times to transmit it.
  400. char * tok = strtok((char *) payload, ",");
  401. // Check if a switch is linked to that message
  402. unsigned char id;
  403. unsigned char status = 0;
  404. if (_rfbMatch(tok, id, status)) {
  405. if (status == 2) {
  406. relayToggle(id);
  407. } else {
  408. relayStatus(id, status == 1);
  409. }
  410. return;
  411. }
  412. #if RF_RAW_SUPPORT
  413. byte message[RF_MAX_MESSAGE_SIZE];
  414. int len = _rfbToArray(tok, message, 0);
  415. if ((len > 0) && (isRFRaw || len != RF_MESSAGE_SIZE)) {
  416. _rfbSendRawOnce(message, len);
  417. } else {
  418. tok = strtok(NULL, ",");
  419. byte times = (tok != NULL) ? atoi(tok) : 1;
  420. _rfbSend(message, times);
  421. }
  422. #else // RF_RAW_SUPPORT
  423. byte message[RF_MESSAGE_SIZE];
  424. if (_rfbToArray(tok, message)) {
  425. tok = strtok(NULL, ",");
  426. byte times = (tok != NULL) ? atoi(tok) : 1;
  427. _rfbSend(message, times);
  428. }
  429. #endif // RF_RAW_SUPPORT
  430. }
  431. }
  432. }
  433. #endif
  434. #if TERMINAL_SUPPORT
  435. void _rfbInitCommands() {
  436. settingsRegisterCommand(F("LEARN"), [](Embedis* e) {
  437. if (e->argc < 3) {
  438. DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
  439. return;
  440. }
  441. int id = String(e->argv[1]).toInt();
  442. if (id >= relayCount()) {
  443. DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
  444. return;
  445. }
  446. int status = String(e->argv[2]).toInt();
  447. rfbLearn(id, status == 1);
  448. DEBUG_MSG_P(PSTR("+OK\n"));
  449. });
  450. settingsRegisterCommand(F("FORGET"), [](Embedis* e) {
  451. if (e->argc < 3) {
  452. DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n"));
  453. return;
  454. }
  455. int id = String(e->argv[1]).toInt();
  456. if (id >= relayCount()) {
  457. DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
  458. return;
  459. }
  460. int status = String(e->argv[2]).toInt();
  461. rfbForget(id, status == 1);
  462. DEBUG_MSG_P(PSTR("+OK\n"));
  463. });
  464. }
  465. #endif // TERMINAL_SUPPORT
  466. // -----------------------------------------------------------------------------
  467. // PUBLIC
  468. // -----------------------------------------------------------------------------
  469. void rfbStore(unsigned char id, bool status, const char * code) {
  470. DEBUG_MSG_P(PSTR("[RFBRIDGE] Storing %d-%s => '%s'\n"), id, status ? "ON" : "OFF", code);
  471. char key[RF_MAX_KEY_LENGTH] = {0};
  472. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  473. setSetting(key, code);
  474. }
  475. String rfbRetrieve(unsigned char id, bool status) {
  476. char key[RF_MAX_KEY_LENGTH] = {0};
  477. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  478. return getSetting(key);
  479. }
  480. void rfbStatus(unsigned char id, bool status) {
  481. String value = rfbRetrieve(id, status);
  482. if (value.length() > 0) {
  483. bool same = _rfbSameOnOff(id);
  484. #if RF_RAW_SUPPORT
  485. byte message[RF_MAX_MESSAGE_SIZE];
  486. int len = _rfbToArray(value.c_str(), message, 0);
  487. if (len == RF_MESSAGE_SIZE && // probably a standard msg
  488. (message[0] != RF_CODE_START || // raw would start with 0xAA
  489. message[1] != RF_CODE_RFOUT_BUCKET || // followed by 0xB0,
  490. message[2] + 4 != len || // needs a valid length,
  491. message[len-1] != RF_CODE_STOP)) { // and finish with 0x55
  492. if (!_rfbin) {
  493. unsigned char times = same ? 1 : RF_SEND_TIMES;
  494. _rfbSend(message, times);
  495. }
  496. } else {
  497. _rfbSendRawOnce(message, len); // send a raw message
  498. }
  499. #else // RF_RAW_SUPPORT
  500. if (!_rfbin) {
  501. byte message[RF_MESSAGE_SIZE];
  502. _rfbToArray(value.c_str(), message);
  503. unsigned char times = same ? 1 : RF_SEND_TIMES;
  504. _rfbSend(message, times);
  505. }
  506. #endif // RF_RAW_SUPPORT
  507. }
  508. _rfbin = false;
  509. }
  510. void rfbLearn(unsigned char id, bool status) {
  511. _learnId = id;
  512. _learnStatus = status;
  513. _rfbLearn();
  514. }
  515. void rfbForget(unsigned char id, bool status) {
  516. char key[RF_MAX_KEY_LENGTH] = {0};
  517. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  518. delSetting(key);
  519. // Websocket update
  520. #if WEB_SUPPORT
  521. char wsb[100];
  522. snprintf_P(wsb, sizeof(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"\"}]}"), id, status ? 1 : 0);
  523. wsSend(wsb);
  524. #endif
  525. }
  526. // -----------------------------------------------------------------------------
  527. // SETUP & LOOP
  528. // -----------------------------------------------------------------------------
  529. void rfbSetup() {
  530. #if MQTT_SUPPORT
  531. mqttRegister(_rfbMqttCallback);
  532. #endif
  533. #if WEB_SUPPORT
  534. wsOnSendRegister(_rfbWebSocketOnSend);
  535. wsOnActionRegister(_rfbWebSocketOnAction);
  536. #endif
  537. #if TERMINAL_SUPPORT
  538. _rfbInitCommands();
  539. #endif
  540. #if RFB_DIRECT
  541. _rfModem = new RCSwitch();
  542. _rfModem->enableReceive(RFB_RX_PIN);
  543. _rfModem->enableTransmit(RFB_TX_PIN);
  544. _rfModem->setRepeatTransmit(6);
  545. DEBUG_MSG_P(PSTR("[RFBRIDGE] RF receiver on GPIO %u\n"), RFB_RX_PIN);
  546. DEBUG_MSG_P(PSTR("[RFBRIDGE] RF transmitter on GPIO %u\n"), RFB_TX_PIN);
  547. #endif
  548. // Register loop
  549. espurnaRegisterLoop(rfbLoop);
  550. }
  551. void rfbLoop() {
  552. _rfbReceive();
  553. }
  554. #endif