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.

802 lines
22 KiB

7 years ago
  1. /*
  2. RF MODULE
  3. Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
  4. */
  5. #if defined(ITEAD_SONOFF_RFBRIDGE) || RF_SUPPORT
  6. #include <queue>
  7. #include <Ticker.h>
  8. #if RFB_DIRECT || RF_SUPPORT
  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. #if not RF_SUPPORT
  43. typedef struct {
  44. byte code[RF_MESSAGE_SIZE];
  45. byte times;
  46. } rfb_message_t;
  47. static std::queue<rfb_message_t> _rfb_message_queue;
  48. Ticker _rfb_ticker;
  49. bool _rfb_ticker_active = false;
  50. #endif
  51. #if RFB_DIRECT || RF_SUPPORT
  52. RCSwitch * _rfModem;
  53. bool _learning = false;
  54. #endif
  55. #if WEB_SUPPORT
  56. Ticker _rfb_sendcodes;
  57. #endif
  58. // -----------------------------------------------------------------------------
  59. // PRIVATES
  60. // -----------------------------------------------------------------------------
  61. /*
  62. From a byte array to an hexa char array ("A220EE...", double the size)
  63. */
  64. static bool _rfbToChar(byte * in, char * out, int n = RF_MESSAGE_SIZE) {
  65. for (unsigned char p = 0; p<n; p++) {
  66. sprintf_P(&out[p*2], PSTR("%02X"), in[p]);
  67. }
  68. return true;
  69. }
  70. #if WEB_SUPPORT
  71. void _rfbWebSocketSendCodeArray(unsigned char start, unsigned char size) {
  72. DynamicJsonBuffer jsonBuffer;
  73. JsonObject& root = jsonBuffer.createObject();
  74. JsonObject& rfb = root.createNestedObject("rfb");
  75. rfb["size"] = size;
  76. rfb["start"] = start;
  77. JsonArray& on = rfb.createNestedArray("on");
  78. JsonArray& off = rfb.createNestedArray("off");
  79. for (byte id=start; id<start+size; id++) {
  80. on.add(rfbRetrieve(id, true));
  81. off.add(rfbRetrieve(id, false));
  82. }
  83. wsSend(root);
  84. }
  85. void _rfbWebSocketSendCode(unsigned char id) {
  86. _rfbWebSocketSendCodeArray(id, 1);
  87. }
  88. void _rfbWebSocketSendCodes() {
  89. _rfbWebSocketSendCodeArray(0, relayCount());
  90. }
  91. void _rfbWebSocketOnSend(JsonObject& root) {
  92. root["rfbVisible"] = 1;
  93. root["rfbCount"] = relayCount();
  94. #if RF_RAW_SUPPORT
  95. root["rfbrawVisible"] = 1;
  96. #endif
  97. _rfb_sendcodes.once_ms(1000, _rfbWebSocketSendCodes);
  98. }
  99. void _rfbWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
  100. if (strcmp(action, "rfblearn") == 0) rfbLearn(data["id"], data["status"]);
  101. if (strcmp(action, "rfbforget") == 0) rfbForget(data["id"], data["status"]);
  102. if (strcmp(action, "rfbsend") == 0) rfbStore(data["id"], data["status"], data["data"].as<const char*>());
  103. }
  104. #endif // WEB_SUPPORT
  105. void _rfbAck() {
  106. #if (not RFB_DIRECT) && (not RF_SUPPORT)
  107. DEBUG_MSG_P(PSTR("[RF] Sending ACK\n"));
  108. Serial.println();
  109. Serial.write(RF_CODE_START);
  110. Serial.write(RF_CODE_ACK);
  111. Serial.write(RF_CODE_STOP);
  112. Serial.flush();
  113. Serial.println();
  114. #endif
  115. }
  116. void _rfbLearn() {
  117. #if RFB_DIRECT || RF_SUPPORT
  118. DEBUG_MSG_P(PSTR("[RF] Entering LEARN mode\n"));
  119. _learning = true;
  120. #else
  121. DEBUG_MSG_P(PSTR("[RF] Sending LEARN\n"));
  122. Serial.println();
  123. Serial.write(RF_CODE_START);
  124. Serial.write(RF_CODE_LEARN);
  125. Serial.write(RF_CODE_STOP);
  126. Serial.flush();
  127. Serial.println();
  128. #endif
  129. #if WEB_SUPPORT
  130. char buffer[100];
  131. snprintf_P(buffer, sizeof(buffer), PSTR("{\"action\": \"rfbLearn\", \"data\":{\"id\": %d, \"status\": %d}}"), _learnId, _learnStatus ? 1 : 0);
  132. wsSend(buffer);
  133. #endif
  134. }
  135. /*
  136. From an hexa char array ("A220EE...") to a byte array (half the size)
  137. */
  138. static int _rfbToArray(const char * in, byte * out, int length = RF_MESSAGE_SIZE * 2) {
  139. int n = strlen(in);
  140. if (n > RF_MAX_MESSAGE_SIZE*2 || (length > 0 && n != length)) return 0;
  141. char tmp[3] = {0,0,0};
  142. n /= 2;
  143. for (unsigned char p = 0; p<n; p++) {
  144. memcpy(tmp, &in[p*2], 2);
  145. out[p] = strtol(tmp, NULL, 16);
  146. }
  147. return n;
  148. }
  149. #if not RF_SUPPORT
  150. void _rfbSendRaw(const byte *message, const unsigned char n = RF_MESSAGE_SIZE) {
  151. for (unsigned char j=0; j<n; j++) {
  152. Serial.write(message[j]);
  153. }
  154. }
  155. void _rfbSend(byte * message) {
  156. #if RFB_DIRECT
  157. unsigned int protocol = message[1];
  158. unsigned int timing =
  159. (message[2] << 8) |
  160. (message[3] << 0) ;
  161. unsigned int bitlength = message[4];
  162. unsigned long rf_code =
  163. (message[5] << 24) |
  164. (message[6] << 16) |
  165. (message[7] << 8) |
  166. (message[8] << 0) ;
  167. _rfModem->setProtocol(protocol);
  168. if (timing > 0) {
  169. _rfModem->setPulseLength(timing);
  170. }
  171. _rfModem->send(rf_code, bitlength);
  172. _rfModem->resetAvailable();
  173. #else
  174. Serial.println();
  175. Serial.write(RF_CODE_START);
  176. Serial.write(RF_CODE_RFOUT);
  177. _rfbSendRaw(message);
  178. Serial.write(RF_CODE_STOP);
  179. Serial.flush();
  180. Serial.println();
  181. #endif
  182. }
  183. void _rfbSend() {
  184. // Check if there is something in the queue
  185. if (_rfb_message_queue.empty()) return;
  186. // Pop the first element
  187. rfb_message_t message = _rfb_message_queue.front();
  188. _rfb_message_queue.pop();
  189. // Send the message
  190. _rfbSend(message.code);
  191. // If it should be further sent, push it to the stack again
  192. if (message.times > 1) {
  193. message.times = message.times - 1;
  194. _rfb_message_queue.push(message);
  195. }
  196. // if there are still messages in the queue...
  197. if (_rfb_message_queue.empty()) {
  198. _rfb_ticker.detach();
  199. _rfb_ticker_active = false;
  200. }
  201. }
  202. void _rfbSend(byte * code, unsigned char times) {
  203. #if RFB_DIRECT
  204. times = 1;
  205. #endif
  206. char buffer[RF_MESSAGE_SIZE];
  207. _rfbToChar(code, buffer);
  208. DEBUG_MSG_P(PSTR("[RF] Enqueuing MESSAGE '%s' %d time(s)\n"), buffer, times);
  209. rfb_message_t message;
  210. memcpy(message.code, code, RF_MESSAGE_SIZE);
  211. message.times = times;
  212. _rfb_message_queue.push(message);
  213. // Enable the ticker if not running
  214. if (!_rfb_ticker_active) {
  215. _rfb_ticker_active = true;
  216. _rfb_ticker.attach_ms(RF_SEND_DELAY, _rfbSend);
  217. }
  218. }
  219. #endif // not RF_SUPPORT
  220. #if RF_RAW_SUPPORT
  221. void _rfbSendRawOnce(byte *code, unsigned char length) {
  222. char buffer[length*2];
  223. _rfbToChar(code, buffer, length);
  224. DEBUG_MSG_P(PSTR("[RF] Sending RAW MESSAGE '%s'\n"), buffer);
  225. _rfbSendRaw(code, length);
  226. }
  227. #endif // RF_RAW_SUPPORT
  228. bool _rfbMatch(char* code, unsigned char& relayID, unsigned char& value, char* buffer = NULL) {
  229. if (strlen(code) != 18) return false;
  230. bool found = false;
  231. String compareto = String(&code[12]);
  232. compareto.toUpperCase();
  233. DEBUG_MSG_P(PSTR("[RF] Trying to match code %s\n"), compareto.c_str());
  234. for (unsigned char i=0; i<relayCount(); i++) {
  235. String code_on = rfbRetrieve(i, true);
  236. if (code_on.length() && code_on.endsWith(compareto)) {
  237. DEBUG_MSG_P(PSTR("[RF] Match ON code for relay %d\n"), i);
  238. value = 1;
  239. found = true;
  240. if (buffer) strcpy(buffer, code_on.c_str());
  241. }
  242. String code_off = rfbRetrieve(i, false);
  243. if (code_off.length() && code_off.endsWith(compareto)) {
  244. DEBUG_MSG_P(PSTR("[RF] Match OFF code for relay %d\n"), i);
  245. if (found) value = 2;
  246. found = true;
  247. if (buffer) strcpy(buffer, code_off.c_str());
  248. }
  249. if (found) {
  250. relayID = i;
  251. return true;
  252. }
  253. }
  254. return false;
  255. }
  256. void _rfbDecode() {
  257. static unsigned long last = 0;
  258. if (millis() - last < RF_RECEIVE_DELAY) return;
  259. last = millis();
  260. byte action = _uartbuf[0];
  261. char buffer[RF_MESSAGE_SIZE * 2 + 1] = {0};
  262. DEBUG_MSG_P(PSTR("[RF] Action 0x%02X\n"), action);
  263. if (action == RF_CODE_LEARN_KO) {
  264. _rfbAck();
  265. DEBUG_MSG_P(PSTR("[RF] Learn timeout\n"));
  266. #if WEB_SUPPORT
  267. wsSend_P(PSTR("{\"action\": \"rfbTimeout\"}"));
  268. #endif
  269. }
  270. if (action == RF_CODE_LEARN_OK || action == RF_CODE_RFIN) {
  271. _rfbAck();
  272. _rfbToChar(&_uartbuf[1], buffer);
  273. DEBUG_MSG_P(PSTR("[RF] Received message '%s'\n"), buffer);
  274. }
  275. if (action == RF_CODE_LEARN_OK) {
  276. DEBUG_MSG_P(PSTR("[RF] Learn success\n"));
  277. rfbStore(_learnId, _learnStatus, buffer);
  278. // Websocket update
  279. #if WEB_SUPPORT
  280. _rfbWebSocketSendCode(_learnId);
  281. #endif
  282. }
  283. if (action == RF_CODE_RFIN) {
  284. /* Look for the code, possibly replacing the code with the exact learned one on match
  285. * we want to do this on learn too to be sure that the learned code is the same if it
  286. * is equivalent
  287. */
  288. unsigned char id;
  289. unsigned char status;
  290. bool matched = _rfbMatch(buffer, id, status, buffer);
  291. if (matched) {
  292. DEBUG_MSG_P(PSTR("[RF] Matched message '%s'\n"), buffer);
  293. _rfbin = true;
  294. if (status == 2) {
  295. relayToggle(id);
  296. } else {
  297. relayStatus(id, status == 1);
  298. }
  299. }
  300. #if MQTT_SUPPORT
  301. mqttSend(MQTT_TOPIC_RFIN, buffer);
  302. #endif
  303. }
  304. }
  305. void _rfbReceive() {
  306. #if RFB_DIRECT || RF_SUPPORT
  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. #else
  351. static bool receiving = false;
  352. while (Serial.available()) {
  353. yield();
  354. byte c = Serial.read();
  355. //DEBUG_MSG_P(PSTR("[RF] Received 0x%02X\n"), c);
  356. if (receiving) {
  357. if (c == RF_CODE_STOP && (_uartpos == 1 || _uartpos == RF_MESSAGE_SIZE + 1)) {
  358. _rfbDecode();
  359. receiving = false;
  360. } else if (_uartpos <= RF_MESSAGE_SIZE) {
  361. _uartbuf[_uartpos++] = c;
  362. } else {
  363. // wrong message, should have received a RF_CODE_STOP
  364. receiving = false;
  365. }
  366. } else if (c == RF_CODE_START) {
  367. _uartpos = 0;
  368. receiving = true;
  369. }
  370. }
  371. #endif
  372. }
  373. bool _rfbCompare(const char * code1, const char * code2) {
  374. return strcmp(&code1[12], &code2[12]) == 0;
  375. }
  376. bool _rfbSameOnOff(unsigned char id) {
  377. return _rfbCompare(rfbRetrieve(id, true).c_str(), rfbRetrieve(id, false).c_str());
  378. }
  379. void _rfbParseCode(char * code) {
  380. // The payload may be a code in HEX format ([0-9A-Z]{18}) or
  381. // the code comma the number of times to transmit it.
  382. char * tok = strtok(code, ",");
  383. // Check if a switch is linked to that message
  384. unsigned char id;
  385. unsigned char status = 0;
  386. if (_rfbMatch(tok, id, status)) {
  387. if (status == 2) {
  388. relayToggle(id);
  389. } else {
  390. relayStatus(id, status == 1);
  391. }
  392. return;
  393. }
  394. #if RF_RAW_SUPPORT
  395. byte message[RF_MAX_MESSAGE_SIZE];
  396. int len = _rfbToArray(tok, message, 0);
  397. if ((len > 0) && (isRFRaw || len != RF_MESSAGE_SIZE)) {
  398. _rfbSendRawOnce(message, len);
  399. } else {
  400. tok = strtok(NULL, ",");
  401. byte times = (tok != NULL) ? atoi(tok) : 1;
  402. _rfbSend(message, times);
  403. }
  404. #else // RF_RAW_SUPPORT
  405. byte message[RF_MESSAGE_SIZE];
  406. if (_rfbToArray(tok, message)) {
  407. tok = strtok(NULL, ",");
  408. byte times = (tok != NULL) ? atoi(tok) : 1;
  409. _rfbSend(message, times);
  410. }
  411. #endif // RF_RAW_SUPPORT
  412. }
  413. #if MQTT_SUPPORT
  414. void _rfbMqttCallback(unsigned int type, const char * topic, const 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 not RF_SUPPORT
  420. mqttSubscribe(MQTT_TOPIC_RFOUT);
  421. #endif
  422. #if RF_RAW_SUPPORT
  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. _rfbLearn();
  438. return;
  439. }
  440. #if not RF_SUPPORT
  441. bool isRFOut = t.equals(MQTT_TOPIC_RFOUT);
  442. #endif
  443. #if RF_RAW_SUPPORT
  444. bool isRFRaw = !isRFOut && t.equals(MQTT_TOPIC_RFRAW);
  445. #elif not RF_SUPPORT
  446. bool isRFRaw = false;
  447. #endif
  448. #if not RF_SUPPORT
  449. if (isRFOut || isRFRaw) {
  450. _rfbParseCode((char *) payload);
  451. }
  452. #endif // not RF_SUPPORT
  453. }
  454. }
  455. #endif // MQTT_SUPPORT
  456. #if API_SUPPORT
  457. void _rfbAPISetup() {
  458. #if not RF_SUPPORT
  459. apiRegister(MQTT_TOPIC_RFOUT,
  460. [](char * buffer, size_t len) {
  461. snprintf_P(buffer, len, PSTR("OK"));
  462. },
  463. [](const char * payload) {
  464. _rfbParseCode((char *) payload);
  465. }
  466. );
  467. #endif // RF_SUPPORT
  468. apiRegister(MQTT_TOPIC_RFLEARN,
  469. [](char * buffer, size_t len) {
  470. snprintf_P(buffer, len, PSTR("OK"));
  471. },
  472. [](const char * payload) {
  473. // The payload must be the relayID plus the mode (0 or 1)
  474. char * tok = strtok((char *) payload, ",");
  475. if (NULL == tok) return;
  476. if (!isNumber(tok)) return;
  477. _learnId = atoi(tok);
  478. if (_learnId >= relayCount()) {
  479. DEBUG_MSG_P(PSTR("[RF] Wrong learnID (%d)\n"), _learnId);
  480. return;
  481. }
  482. tok = strtok(NULL, ",");
  483. if (NULL == tok) return;
  484. _learnStatus = (char) tok[0] != '0';
  485. _rfbLearn();
  486. }
  487. );
  488. #if RF_RAW_SUPPORT
  489. apiRegister(MQTT_TOPIC_RFRAW,
  490. [](char * buffer, size_t len) {
  491. snprintf_P(buffer, len, PSTR("OK"));
  492. },
  493. [](const char * payload) {
  494. _rfbParseCode(payload);
  495. }
  496. );
  497. #endif // RF_RAW_SUPPORT
  498. }
  499. #endif // API_SUPPORT
  500. #if TERMINAL_SUPPORT
  501. void _rfbInitCommands() {
  502. terminalRegisterCommand(F("LEARN"), [](Embedis* e) {
  503. if (e->argc < 3) {
  504. terminalError(F("Wrong arguments"));
  505. return;
  506. }
  507. int id = String(e->argv[1]).toInt();
  508. if (id >= relayCount()) {
  509. DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
  510. return;
  511. }
  512. int status = String(e->argv[2]).toInt();
  513. rfbLearn(id, status == 1);
  514. terminalOK();
  515. });
  516. terminalRegisterCommand(F("FORGET"), [](Embedis* e) {
  517. if (e->argc < 3) {
  518. terminalError(F("Wrong arguments"));
  519. return;
  520. }
  521. int id = String(e->argv[1]).toInt();
  522. if (id >= relayCount()) {
  523. DEBUG_MSG_P(PSTR("-ERROR: Wrong relayID (%d)\n"), id);
  524. return;
  525. }
  526. int status = String(e->argv[2]).toInt();
  527. rfbForget(id, status == 1);
  528. terminalOK();
  529. });
  530. }
  531. #endif // TERMINAL_SUPPORT
  532. // -----------------------------------------------------------------------------
  533. // PUBLIC
  534. // -----------------------------------------------------------------------------
  535. void rfbStore(unsigned char id, bool status, const char * code) {
  536. DEBUG_MSG_P(PSTR("[RF] Storing %d-%s => '%s'\n"), id, status ? "ON" : "OFF", code);
  537. char key[RF_MAX_KEY_LENGTH] = {0};
  538. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  539. setSetting(key, code);
  540. }
  541. String rfbRetrieve(unsigned char id, bool status) {
  542. char key[RF_MAX_KEY_LENGTH] = {0};
  543. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  544. return getSetting(key);
  545. }
  546. #if not RF_SUPPORT
  547. void rfbStatus(unsigned char id, bool status) {
  548. String value = rfbRetrieve(id, status);
  549. if (value.length() > 0) {
  550. bool same = _rfbSameOnOff(id);
  551. #if RF_RAW_SUPPORT
  552. byte message[RF_MAX_MESSAGE_SIZE];
  553. int len = _rfbToArray(value.c_str(), message, 0);
  554. if (len == RF_MESSAGE_SIZE && // probably a standard msg
  555. (message[0] != RF_CODE_START || // raw would start with 0xAA
  556. message[1] != RF_CODE_RFOUT_BUCKET || // followed by 0xB0,
  557. message[2] + 4 != len || // needs a valid length,
  558. message[len-1] != RF_CODE_STOP)) { // and finish with 0x55
  559. if (!_rfbin) {
  560. unsigned char times = same ? 1 : RF_SEND_TIMES;
  561. _rfbSend(message, times);
  562. }
  563. } else {
  564. _rfbSendRawOnce(message, len); // send a raw message
  565. }
  566. #else // RF_RAW_SUPPORT
  567. if (!_rfbin) {
  568. byte message[RF_MESSAGE_SIZE];
  569. _rfbToArray(value.c_str(), message);
  570. unsigned char times = same ? 1 : RF_SEND_TIMES;
  571. _rfbSend(message, times);
  572. }
  573. #endif // RF_RAW_SUPPORT
  574. }
  575. _rfbin = false;
  576. }
  577. #endif // not RF_SUPPORT
  578. void rfbLearn(unsigned char id, bool status) {
  579. _learnId = id;
  580. _learnStatus = status;
  581. _rfbLearn();
  582. }
  583. void rfbForget(unsigned char id, bool status) {
  584. char key[RF_MAX_KEY_LENGTH] = {0};
  585. snprintf_P(key, sizeof(key), PSTR("rfb%s%d"), status ? "ON" : "OFF", id);
  586. delSetting(key);
  587. // Websocket update
  588. #if WEB_SUPPORT
  589. char wsb[100];
  590. snprintf_P(wsb, sizeof(wsb), PSTR("{\"rfb\":[{\"id\": %d, \"status\": %d, \"data\": \"\"}]}"), id, status ? 1 : 0);
  591. wsSend(wsb);
  592. #endif
  593. }
  594. // -----------------------------------------------------------------------------
  595. // SETUP & LOOP
  596. // -----------------------------------------------------------------------------
  597. void rfbSetup() {
  598. #if MQTT_SUPPORT
  599. mqttRegister(_rfbMqttCallback);
  600. #endif
  601. #if API_SUPPORT
  602. _rfbAPISetup();
  603. #endif
  604. #if WEB_SUPPORT
  605. wsOnSendRegister(_rfbWebSocketOnSend);
  606. wsOnActionRegister(_rfbWebSocketOnAction);
  607. #endif
  608. #if TERMINAL_SUPPORT
  609. _rfbInitCommands();
  610. #endif
  611. #if RFB_DIRECT || RF_SUPPORT
  612. _rfModem = new RCSwitch();
  613. #if RF_SUPPORT
  614. _rfModem->enableReceive(RF_PIN);
  615. DEBUG_MSG_P(PSTR("[RF] RF receiver on GPIO %u\n"), RF_PIN);
  616. #else
  617. _rfModem->enableReceive(RFB_RX_PIN);
  618. _rfModem->enableTransmit(RFB_TX_PIN);
  619. _rfModem->setRepeatTransmit(6);
  620. DEBUG_MSG_P(PSTR("[RF] RF receiver on GPIO %u\n"), RFB_RX_PIN);
  621. DEBUG_MSG_P(PSTR("[RF] RF transmitter on GPIO %u\n"), RFB_TX_PIN);
  622. #endif
  623. #endif
  624. // Register loop
  625. espurnaRegisterLoop(rfbLoop);
  626. }
  627. void rfbLoop() {
  628. _rfbReceive();
  629. }
  630. #endif