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.

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