xdrv_06_snfbridge.ino 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. /*
  2. xdrv_06_snfbridge.ino - sonoff RF bridge 433 support for Sonoff-Tasmota
  3. Copyright (C) 2018 Theo Arends and Erik Andrén Zachrisson (fw update)
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. /*********************************************************************************************\
  16. Sonoff RF Bridge 433
  17. \*********************************************************************************************/
  18. #define XDRV_06 6
  19. #define SFB_TIME_AVOID_DUPLICATE 2000 // Milliseconds
  20. enum SonoffBridgeCommands {
  21. CMND_RFSYNC, CMND_RFLOW, CMND_RFHIGH, CMND_RFHOST, CMND_RFCODE, CMND_RFKEY, CMND_RFRAW };
  22. const char kSonoffBridgeCommands[] PROGMEM =
  23. D_CMND_RFSYNC "|" D_CMND_RFLOW "|" D_CMND_RFHIGH "|" D_CMND_RFHOST "|" D_CMND_RFCODE "|" D_CMND_RFKEY "|" D_CMND_RFRAW;
  24. uint8_t sonoff_bridge_receive_flag = 0;
  25. uint8_t sonoff_bridge_receive_raw_flag = 0;
  26. uint8_t sonoff_bridge_learn_key = 1;
  27. uint8_t sonoff_bridge_learn_active = 0;
  28. uint8_t sonoff_bridge_expected_bytes = 0;
  29. uint32_t sonoff_bridge_last_received_id = 0;
  30. uint32_t sonoff_bridge_last_send_code = 0;
  31. unsigned long sonoff_bridge_last_time = 0;
  32. unsigned long sonoff_bridge_last_learn_time = 0;
  33. #ifdef USE_RF_FLASH
  34. /*********************************************************************************************\
  35. * EFM8BB1 RF microcontroller in-situ firmware update
  36. *
  37. * Enables upload of EFM8BB1 firmware provided by https://github.com/Portisch/RF-Bridge-EFM8BB1 using the web gui.
  38. * Based on source by Erik Andrén Zachrisson (https://github.com/arendst/Sonoff-Tasmota/pull/2886)
  39. \*********************************************************************************************/
  40. #include "ihx.h"
  41. #include "c2.h"
  42. #define RF_RECORD_NO_START_FOUND -1
  43. #define RF_RECORD_NO_END_FOUND -2
  44. ssize_t rf_find_hex_record_start(uint8_t *buf, size_t size)
  45. {
  46. for (int i = 0; i < size; i++) {
  47. if (buf[i] == ':') {
  48. return i;
  49. }
  50. }
  51. return RF_RECORD_NO_START_FOUND;
  52. }
  53. ssize_t rf_find_hex_record_end(uint8_t *buf, size_t size)
  54. {
  55. for (ssize_t i = 0; i < size; i++) {
  56. if (buf[i] == '\n') {
  57. return i;
  58. }
  59. }
  60. return RF_RECORD_NO_END_FOUND;
  61. }
  62. ssize_t rf_glue_remnant_with_new_data_and_write(const uint8_t *remnant_data, uint8_t *new_data, size_t new_data_len)
  63. {
  64. ssize_t record_start;
  65. ssize_t record_end;
  66. ssize_t glue_record_sz;
  67. uint8_t *glue_buf;
  68. ssize_t result;
  69. if (remnant_data[0] != ':') { return -8; } // File invalid - RF Remnant data did not start with a start token
  70. // Find end token in new data
  71. record_end = rf_find_hex_record_end(new_data, new_data_len);
  72. record_start = rf_find_hex_record_start(new_data, new_data_len);
  73. // Be paranoid and check that there is no start marker before the end record
  74. // If so this implies that there was something wrong with the last start marker saved
  75. // in the last upload part
  76. if ((record_start != RF_RECORD_NO_START_FOUND) && (record_start < record_end)) {
  77. return -8; // File invalid - Unexpected RF start marker found before RF end marker
  78. }
  79. glue_record_sz = strlen((const char *) remnant_data) + record_end;
  80. glue_buf = (uint8_t *) malloc(glue_record_sz);
  81. if (glue_buf == NULL) { return -2; } // Not enough space
  82. // Assemble new glue buffer
  83. memcpy(glue_buf, remnant_data, strlen((const char *) remnant_data));
  84. memcpy(glue_buf + strlen((const char *) remnant_data), new_data, record_end);
  85. result = rf_decode_and_write(glue_buf, glue_record_sz);
  86. free(glue_buf);
  87. return result;
  88. }
  89. ssize_t rf_decode_and_write(uint8_t *record, size_t size)
  90. {
  91. uint8_t err = ihx_decode(record, size);
  92. if (err != IHX_SUCCESS) { return -13; } // Failed to decode RF firmware
  93. ihx_t *h = (ihx_t *) record;
  94. if (h->record_type == IHX_RT_DATA) {
  95. int retries = 5;
  96. uint16_t address = h->address_high * 0x100 + h->address_low;
  97. do {
  98. err = c2_programming_init();
  99. err = c2_block_write(address, h->data, h->len);
  100. } while (err != C2_SUCCESS && retries--);
  101. } else if (h->record_type == IHX_RT_END_OF_FILE) {
  102. // RF firmware upgrade done, restarting RF chip
  103. err = c2_reset();
  104. }
  105. if (err != C2_SUCCESS) { return -12; } // Failed to write to RF chip
  106. return 0;
  107. }
  108. ssize_t rf_search_and_write(uint8_t *buf, size_t size)
  109. {
  110. // Binary contains a set of commands, decode and program each one
  111. ssize_t rec_end;
  112. ssize_t rec_start;
  113. ssize_t err;
  114. for (size_t i = 0; i < size; i++) {
  115. // Find starts and ends of commands
  116. rec_start = rf_find_hex_record_start(buf + i, size - i);
  117. if (rec_start == RF_RECORD_NO_START_FOUND) {
  118. // There is nothing left to save in this buffer
  119. return -8; // File invalid
  120. }
  121. // Translate rec_start from local buffer position to chunk position
  122. rec_start += i;
  123. rec_end = rf_find_hex_record_end(buf + rec_start, size - rec_start);
  124. if (rec_end == RF_RECORD_NO_END_FOUND) {
  125. // We have found a start but not an end, save remnant
  126. return rec_start;
  127. }
  128. // Translate rec_end from local buffer position to chunk position
  129. rec_end += rec_start;
  130. err = rf_decode_and_write(buf + rec_start, rec_end - rec_start);
  131. if (err < 0) { return err; }
  132. i = rec_end;
  133. }
  134. // Buffer was perfectly aligned, start and end found without any remaining trailing characters
  135. return 0;
  136. }
  137. uint8_t rf_erase_flash(void)
  138. {
  139. uint8_t err;
  140. for (int i = 0; i < 4; i++) { // HACK: Try multiple times as the command sometimes fails (unclear why)
  141. err = c2_programming_init();
  142. if (err != C2_SUCCESS) {
  143. return 10; // Failed to init RF chip
  144. }
  145. err = c2_device_erase();
  146. if (err != C2_SUCCESS) {
  147. if (i < 3) {
  148. c2_reset(); // Reset RF chip and try again
  149. } else {
  150. return 11; // Failed to erase RF chip
  151. }
  152. } else {
  153. break;
  154. }
  155. }
  156. return 0;
  157. }
  158. uint8_t SnfBrUpdateInit(void)
  159. {
  160. pinMode(PIN_C2CK, OUTPUT);
  161. pinMode(PIN_C2D, INPUT);
  162. return rf_erase_flash(); // 10, 11
  163. }
  164. #endif // USE_RF_FLASH
  165. /********************************************************************************************/
  166. void SonoffBridgeReceivedRaw(void)
  167. {
  168. // Decoding according to https://github.com/Portisch/RF-Bridge-EFM8BB1
  169. uint8_t buckets = 0;
  170. if (0xB1 == serial_in_buffer[1]) { buckets = serial_in_buffer[2] << 1; } // Bucket sniffing
  171. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_CMND_RFRAW "\":{\"" D_JSON_DATA "\":\""));
  172. for (int i = 0; i < serial_in_byte_counter; i++) {
  173. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s%02X"), mqtt_data, serial_in_buffer[i]);
  174. if (0xB1 == serial_in_buffer[1]) {
  175. if ((i > 3) && buckets) { buckets--; }
  176. if ((i < 3) || (buckets % 2) || (i == serial_in_byte_counter -2)) {
  177. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s "), mqtt_data);
  178. }
  179. }
  180. }
  181. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s\"}}"), mqtt_data);
  182. MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_CMND_RFRAW));
  183. XdrvRulesProcess();
  184. }
  185. /********************************************************************************************/
  186. void SonoffBridgeLearnFailed(void)
  187. {
  188. sonoff_bridge_learn_active = 0;
  189. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, sonoff_bridge_learn_key, D_JSON_LEARN_FAILED);
  190. MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RFKEY));
  191. }
  192. void SonoffBridgeReceived(void)
  193. {
  194. uint16_t sync_time = 0;
  195. uint16_t low_time = 0;
  196. uint16_t high_time = 0;
  197. uint32_t received_id = 0;
  198. char rfkey[8];
  199. char stemp[16];
  200. AddLogSerial(LOG_LEVEL_DEBUG);
  201. if (0xA2 == serial_in_buffer[0]) { // Learn timeout
  202. SonoffBridgeLearnFailed();
  203. }
  204. else if (0xA3 == serial_in_buffer[0]) { // Learned A3 20 F8 01 18 03 3E 2E 1A 22 55
  205. sonoff_bridge_learn_active = 0;
  206. low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; // Low time in uSec
  207. high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; // High time in uSec
  208. if (low_time && high_time) {
  209. for (byte i = 0; i < 9; i++) {
  210. Settings.rf_code[sonoff_bridge_learn_key][i] = serial_in_buffer[i +1];
  211. }
  212. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, sonoff_bridge_learn_key, D_JSON_LEARNED);
  213. MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR(D_CMND_RFKEY));
  214. } else {
  215. SonoffBridgeLearnFailed();
  216. }
  217. }
  218. else if (0xA4 == serial_in_buffer[0]) { // Received RF data A4 20 EE 01 18 03 3E 2E 1A 22 55
  219. if (sonoff_bridge_learn_active) {
  220. SonoffBridgeLearnFailed();
  221. } else {
  222. sync_time = serial_in_buffer[1] << 8 | serial_in_buffer[2]; // Sync time in uSec
  223. low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; // Low time in uSec
  224. high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; // High time in uSec
  225. received_id = serial_in_buffer[7] << 16 | serial_in_buffer[8] << 8 | serial_in_buffer[9];
  226. unsigned long now = millis();
  227. if (!((received_id == sonoff_bridge_last_received_id) && (now - sonoff_bridge_last_time < SFB_TIME_AVOID_DUPLICATE))) {
  228. sonoff_bridge_last_received_id = received_id;
  229. sonoff_bridge_last_time = now;
  230. strncpy_P(rfkey, PSTR("\"" D_JSON_NONE "\""), sizeof(rfkey));
  231. for (byte i = 1; i <= 16; i++) {
  232. if (Settings.rf_code[i][0]) {
  233. uint32_t send_id = Settings.rf_code[i][6] << 16 | Settings.rf_code[i][7] << 8 | Settings.rf_code[i][8];
  234. if (send_id == received_id) {
  235. snprintf_P(rfkey, sizeof(rfkey), PSTR("%d"), i);
  236. break;
  237. }
  238. }
  239. }
  240. if (Settings.flag.rf_receive_decimal) {
  241. snprintf_P(stemp, sizeof(stemp), PSTR("%u"), received_id);
  242. } else {
  243. snprintf_P(stemp, sizeof(stemp), PSTR("\"%06X\""), received_id);
  244. }
  245. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_RFRECEIVED "\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":%s,\"" D_CMND_RFKEY "\":%s}}"),
  246. sync_time, low_time, high_time, stemp, rfkey);
  247. MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_RFRECEIVED));
  248. XdrvRulesProcess();
  249. #ifdef USE_DOMOTICZ
  250. DomoticzSensor(DZ_COUNT, received_id); // Send rid as Domoticz Counter value
  251. #endif // USE_DOMOTICZ
  252. }
  253. }
  254. }
  255. }
  256. boolean SonoffBridgeSerialInput(void)
  257. {
  258. // iTead Rf Universal Transceiver Module Serial Protocol Version 1.0 (20170420)
  259. static int8_t receive_len = 0;
  260. if (sonoff_bridge_receive_flag) {
  261. if (sonoff_bridge_receive_raw_flag) {
  262. if (!serial_in_byte_counter) {
  263. serial_in_buffer[serial_in_byte_counter++] = 0xAA;
  264. }
  265. serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
  266. if (serial_in_byte_counter == 3) {
  267. if ((0xA6 == serial_in_buffer[1]) || (0xAB == serial_in_buffer[1])) { // AA A6 06 023908010155 55 - 06 is receive_len
  268. receive_len = serial_in_buffer[2] + 4; // Get at least receive_len bytes
  269. }
  270. }
  271. if ((!receive_len && (0x55 == serial_in_byte)) || (receive_len && (serial_in_byte_counter == receive_len))) { // 0x55 - End of text
  272. SonoffBridgeReceivedRaw();
  273. sonoff_bridge_receive_flag = 0;
  274. return 1;
  275. }
  276. }
  277. else if (!((0 == serial_in_byte_counter) && (0 == serial_in_byte))) { // Skip leading 0
  278. if (0 == serial_in_byte_counter) {
  279. sonoff_bridge_expected_bytes = 2; // 0xA0, 0xA1, 0xA2
  280. if (serial_in_byte >= 0xA3) {
  281. sonoff_bridge_expected_bytes = 11; // 0xA3, 0xA4, 0xA5
  282. }
  283. if (serial_in_byte == 0xA6) {
  284. sonoff_bridge_expected_bytes = 0; // 0xA6 and up supported by Portisch firmware only
  285. serial_in_buffer[serial_in_byte_counter++] = 0xAA;
  286. sonoff_bridge_receive_raw_flag = 1;
  287. }
  288. }
  289. serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
  290. if ((sonoff_bridge_expected_bytes == serial_in_byte_counter) && (0x55 == serial_in_byte)) { // 0x55 - End of text
  291. SonoffBridgeReceived();
  292. sonoff_bridge_receive_flag = 0;
  293. return 1;
  294. }
  295. }
  296. serial_in_byte = 0;
  297. }
  298. if (0xAA == serial_in_byte) { // 0xAA - Start of text
  299. serial_in_byte_counter = 0;
  300. serial_in_byte = 0;
  301. sonoff_bridge_receive_flag = 1;
  302. receive_len = 0;
  303. }
  304. return 0;
  305. }
  306. void SonoffBridgeSendCommand(byte code)
  307. {
  308. Serial.write(0xAA); // Start of Text
  309. Serial.write(code); // Command or Acknowledge
  310. Serial.write(0x55); // End of Text
  311. }
  312. void SonoffBridgeSendAck(void)
  313. {
  314. Serial.write(0xAA); // Start of Text
  315. Serial.write(0xA0); // Acknowledge
  316. Serial.write(0x55); // End of Text
  317. }
  318. void SonoffBridgeSendCode(uint32_t code)
  319. {
  320. Serial.write(0xAA); // Start of Text
  321. Serial.write(0xA5); // Send following code
  322. for (byte i = 0; i < 6; i++) {
  323. Serial.write(Settings.rf_code[0][i]);
  324. }
  325. Serial.write((code >> 16) & 0xff);
  326. Serial.write((code >> 8) & 0xff);
  327. Serial.write(code & 0xff);
  328. Serial.write(0x55); // End of Text
  329. Serial.flush();
  330. }
  331. void SonoffBridgeSend(uint8_t idx, uint8_t key)
  332. {
  333. uint8_t code;
  334. key--; // Support 1 to 16
  335. Serial.write(0xAA); // Start of Text
  336. Serial.write(0xA5); // Send following code
  337. for (byte i = 0; i < 8; i++) {
  338. Serial.write(Settings.rf_code[idx][i]);
  339. }
  340. if (0 == idx) {
  341. code = (0x10 << (key >> 2)) | (1 << (key & 3)); // 11,12,14,18,21,22,24,28,41,42,44,48,81,82,84,88
  342. } else {
  343. code = Settings.rf_code[idx][8];
  344. }
  345. Serial.write(code);
  346. Serial.write(0x55); // End of Text
  347. Serial.flush();
  348. #ifdef USE_DOMOTICZ
  349. // uint32_t rid = Settings.rf_code[idx][6] << 16 | Settings.rf_code[idx][7] << 8 | code;
  350. // DomoticzSensor(DZ_COUNT, rid); // Send rid as Domoticz Counter value
  351. #endif // USE_DOMOTICZ
  352. }
  353. void SonoffBridgeLearn(uint8_t key)
  354. {
  355. sonoff_bridge_learn_key = key;
  356. sonoff_bridge_learn_active = 1;
  357. sonoff_bridge_last_learn_time = millis();
  358. Serial.write(0xAA); // Start of Text
  359. Serial.write(0xA1); // Start learning
  360. Serial.write(0x55); // End of Text
  361. }
  362. /*********************************************************************************************\
  363. * Commands
  364. \*********************************************************************************************/
  365. boolean SonoffBridgeCommand(void)
  366. {
  367. char command [CMDSZ];
  368. boolean serviced = true;
  369. int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kSonoffBridgeCommands);
  370. if (-1 == command_code) {
  371. serviced = false; // Unknown command
  372. }
  373. else if ((command_code >= CMND_RFSYNC) && (command_code <= CMND_RFCODE)) { // RfSync, RfLow, RfHigh, RfHost and RfCode
  374. char *p;
  375. char stemp [10];
  376. uint32_t code = 0;
  377. uint8_t radix = 10;
  378. uint8_t set_index = command_code *2;
  379. if (XdrvMailbox.data[0] == '#') {
  380. XdrvMailbox.data++;
  381. XdrvMailbox.data_len--;
  382. radix = 16;
  383. }
  384. if (XdrvMailbox.data_len) {
  385. code = strtol(XdrvMailbox.data, &p, radix);
  386. if (code) {
  387. if (CMND_RFCODE == command_code) {
  388. sonoff_bridge_last_send_code = code;
  389. SonoffBridgeSendCode(code);
  390. } else {
  391. if (1 == XdrvMailbox.payload) {
  392. code = pgm_read_byte(kDefaultRfCode + set_index) << 8 | pgm_read_byte(kDefaultRfCode + set_index +1);
  393. }
  394. uint8_t msb = code >> 8;
  395. uint8_t lsb = code & 0xFF;
  396. if ((code > 0) && (code < 0x7FFF) && (msb != 0x55) && (lsb != 0x55)) { // Check for End of Text codes
  397. Settings.rf_code[0][set_index] = msb;
  398. Settings.rf_code[0][set_index +1] = lsb;
  399. }
  400. }
  401. }
  402. }
  403. if (CMND_RFCODE == command_code) {
  404. code = sonoff_bridge_last_send_code;
  405. } else {
  406. code = Settings.rf_code[0][set_index] << 8 | Settings.rf_code[0][set_index +1];
  407. }
  408. if (10 == radix) {
  409. snprintf_P(stemp, sizeof(stemp), PSTR("%d"), code);
  410. } else {
  411. snprintf_P(stemp, sizeof(stemp), PSTR("\"#%X\""), code);
  412. }
  413. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_XVALUE, command, stemp);
  414. }
  415. else if ((CMND_RFKEY == command_code) && (XdrvMailbox.index > 0) && (XdrvMailbox.index <= 16)) {
  416. unsigned long now = millis();
  417. if ((!sonoff_bridge_learn_active) || (now - sonoff_bridge_last_learn_time > 60100)) {
  418. sonoff_bridge_learn_active = 0;
  419. if (2 == XdrvMailbox.payload) { // Learn RF data
  420. SonoffBridgeLearn(XdrvMailbox.index);
  421. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, D_JSON_START_LEARNING);
  422. }
  423. else if (3 == XdrvMailbox.payload) { // Unlearn RF data
  424. Settings.rf_code[XdrvMailbox.index][0] = 0; // Reset sync_time MSB
  425. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, D_JSON_SET_TO_DEFAULT);
  426. }
  427. else if (4 == XdrvMailbox.payload) { // Save RF data provided by RFSync, RfLow, RfHigh and last RfCode
  428. for (byte i = 0; i < 6; i++) {
  429. Settings.rf_code[XdrvMailbox.index][i] = Settings.rf_code[0][i];
  430. }
  431. Settings.rf_code[XdrvMailbox.index][6] = (sonoff_bridge_last_send_code >> 16) & 0xff;
  432. Settings.rf_code[XdrvMailbox.index][7] = (sonoff_bridge_last_send_code >> 8) & 0xff;
  433. Settings.rf_code[XdrvMailbox.index][8] = sonoff_bridge_last_send_code & 0xff;
  434. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, D_JSON_SAVED);
  435. } else if (5 == XdrvMailbox.payload) { // Show default or learned RF data
  436. uint8_t key = XdrvMailbox.index;
  437. uint8_t index = (0 == Settings.rf_code[key][0]) ? 0 : key; // Use default if sync_time MSB = 0
  438. uint16_t sync_time = (Settings.rf_code[index][0] << 8) | Settings.rf_code[index][1];
  439. uint16_t low_time = (Settings.rf_code[index][2] << 8) | Settings.rf_code[index][3];
  440. uint16_t high_time = (Settings.rf_code[index][4] << 8) | Settings.rf_code[index][5];
  441. uint32_t code = (Settings.rf_code[index][6] << 16) | (Settings.rf_code[index][7] << 8);
  442. if (0 == index) {
  443. key--;
  444. code |= (uint8_t)((0x10 << (key >> 2)) | (1 << (key & 3)));
  445. } else {
  446. code |= Settings.rf_code[index][8];
  447. }
  448. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"%s%d\":{\"" D_JSON_SYNC "\":%d,\"" D_JSON_LOW "\":%d,\"" D_JSON_HIGH "\":%d,\"" D_JSON_DATA "\":\"%06X\"}}"),
  449. command, XdrvMailbox.index, sync_time, low_time, high_time, code);
  450. } else {
  451. if ((1 == XdrvMailbox.payload) || (0 == Settings.rf_code[XdrvMailbox.index][0])) { // Test sync_time MSB
  452. SonoffBridgeSend(0, XdrvMailbox.index); // Send default RF data
  453. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, D_JSON_DEFAULT_SENT);
  454. } else {
  455. SonoffBridgeSend(XdrvMailbox.index, 0); // Send learned RF data
  456. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, XdrvMailbox.index, D_JSON_LEARNED_SENT);
  457. }
  458. }
  459. } else {
  460. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, sonoff_bridge_learn_key, D_JSON_LEARNING_ACTIVE);
  461. }
  462. }
  463. else if (CMND_RFRAW == command_code) {
  464. if (XdrvMailbox.data_len) {
  465. if (XdrvMailbox.data_len < 6) { // On, Off
  466. switch (XdrvMailbox.payload) {
  467. case 0: // Receive Raw Off
  468. SonoffBridgeSendCommand(0xA7); // Stop reading RF signals enabling iTead default RF handling
  469. case 1: // Receive Raw On
  470. sonoff_bridge_receive_raw_flag = XdrvMailbox.payload;
  471. break;
  472. case 166: // 0xA6 - Start reading RF signals disabling iTead default RF handling
  473. case 167: // 0xA7 - Stop reading RF signals enabling iTead default RF handling
  474. case 169: // 0xA9 - Start learning predefined protocols
  475. case 176: // 0xB0 - Stop sniffing
  476. case 177: // 0xB1 - Start sniffing
  477. case 255: // 0xFF - Show firmware version
  478. SonoffBridgeSendCommand(XdrvMailbox.payload);
  479. sonoff_bridge_receive_raw_flag = 1;
  480. break;
  481. case 192: // 0xC0 - Beep
  482. char beep[] = "AAC000C055\0";
  483. SerialSendRaw(beep);
  484. break;
  485. }
  486. } else {
  487. SerialSendRaw(RemoveSpace(XdrvMailbox.data));
  488. sonoff_bridge_receive_raw_flag = 1;
  489. }
  490. }
  491. snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, GetStateText(sonoff_bridge_receive_raw_flag));
  492. } else serviced = false; // Unknown command
  493. return serviced;
  494. }
  495. /*********************************************************************************************/
  496. void SonoffBridgeInit(void)
  497. {
  498. sonoff_bridge_receive_raw_flag = 0;
  499. SonoffBridgeSendCommand(0xA7); // Stop reading RF signals enabling iTead default RF handling
  500. }
  501. /*********************************************************************************************\
  502. * Interface
  503. \*********************************************************************************************/
  504. boolean Xdrv06(byte function)
  505. {
  506. boolean result = false;
  507. if (SONOFF_BRIDGE == Settings.module) {
  508. switch (function) {
  509. case FUNC_INIT:
  510. SonoffBridgeInit();
  511. break;
  512. case FUNC_COMMAND:
  513. result = SonoffBridgeCommand();
  514. break;
  515. case FUNC_SERIAL:
  516. result = SonoffBridgeSerialInput();
  517. break;
  518. }
  519. }
  520. return result;
  521. }